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
301 static struct LevelFileConfigInfo chunk_config_ELEM[] =
303 // (these values are the same for each player)
306 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
307 &li.block_last_field, FALSE // default case for EM levels
311 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
312 &li.sp_block_last_field, TRUE // default case for SP levels
316 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
317 &li.instant_relocation, FALSE
321 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
322 &li.can_pass_to_walkable, FALSE
326 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
327 &li.block_snap_field, TRUE
331 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
332 &li.continuous_snapping, TRUE
336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
337 &li.shifted_relocation, FALSE
341 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
342 &li.lazy_relocation, FALSE
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
347 &li.finish_dig_collect, TRUE
351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
352 &li.keep_walkable_ce, FALSE
355 // (these values are different for each player)
358 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
359 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
363 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
364 &li.initial_player_gravity[0], FALSE
368 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
369 &li.use_start_element[0], FALSE
373 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
374 &li.start_element[0], EL_PLAYER_1
378 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
379 &li.use_artwork_element[0], FALSE
383 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
384 &li.artwork_element[0], EL_PLAYER_1
388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
389 &li.use_explosion_element[0], FALSE
393 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
394 &li.explosion_element[0], EL_PLAYER_1
398 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
399 &li.use_initial_inventory[0], FALSE
403 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
404 &li.initial_inventory_size[0], 1
408 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
409 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
410 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
415 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
416 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
420 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
421 &li.initial_player_gravity[1], FALSE
425 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
426 &li.use_start_element[1], FALSE
430 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
431 &li.start_element[1], EL_PLAYER_2
435 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
436 &li.use_artwork_element[1], FALSE
440 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
441 &li.artwork_element[1], EL_PLAYER_2
445 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
446 &li.use_explosion_element[1], FALSE
450 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
451 &li.explosion_element[1], EL_PLAYER_2
455 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
456 &li.use_initial_inventory[1], FALSE
460 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
461 &li.initial_inventory_size[1], 1
465 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
466 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
467 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
472 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
473 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
477 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
478 &li.initial_player_gravity[2], FALSE
482 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
483 &li.use_start_element[2], FALSE
487 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
488 &li.start_element[2], EL_PLAYER_3
492 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
493 &li.use_artwork_element[2], FALSE
497 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
498 &li.artwork_element[2], EL_PLAYER_3
502 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
503 &li.use_explosion_element[2], FALSE
507 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
508 &li.explosion_element[2], EL_PLAYER_3
512 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
513 &li.use_initial_inventory[2], FALSE
517 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
518 &li.initial_inventory_size[2], 1
522 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
523 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
524 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
529 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
530 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
534 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
535 &li.initial_player_gravity[3], FALSE
539 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
540 &li.use_start_element[3], FALSE
544 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
545 &li.start_element[3], EL_PLAYER_4
549 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
550 &li.use_artwork_element[3], FALSE
554 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
555 &li.artwork_element[3], EL_PLAYER_4
559 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
560 &li.use_explosion_element[3], FALSE
564 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
565 &li.explosion_element[3], EL_PLAYER_4
569 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
570 &li.use_initial_inventory[3], FALSE
574 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
575 &li.initial_inventory_size[3], 1
579 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
580 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
581 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
584 // (these values are only valid for BD style levels)
587 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
588 &li.bd_diagonal_movements, FALSE
593 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
594 &li.score[SC_DIAMOND_EXTRA], 20
597 // (the following values are related to various game elements)
601 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
602 &li.score[SC_EMERALD], 10
607 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
608 &li.score[SC_DIAMOND], 10
613 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
614 &li.score[SC_BUG], 10
619 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
620 &li.score[SC_SPACESHIP], 10
625 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
626 &li.score[SC_PACMAN], 10
631 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
632 &li.score[SC_NUT], 10
637 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
638 &li.score[SC_DYNAMITE], 10
643 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
644 &li.score[SC_KEY], 10
649 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
650 &li.score[SC_PEARL], 10
655 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
656 &li.score[SC_CRYSTAL], 10
661 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
662 &li.amoeba_content, EL_DIAMOND
666 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
671 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
672 &li.grow_into_diggable, TRUE
677 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
678 &li.yamyam_content, EL_ROCK, NULL,
679 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
683 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
684 &li.score[SC_YAMYAM], 10
689 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
690 &li.score[SC_ROBOT], 10
694 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
700 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
706 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
707 &li.time_magic_wall, 10
712 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
713 &li.game_of_life[0], 2
717 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
718 &li.game_of_life[1], 3
722 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
723 &li.game_of_life[2], 3
727 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
728 &li.game_of_life[3], 3
732 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
733 &li.use_life_bugs, FALSE
738 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
743 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
748 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
753 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
758 EL_TIMEGATE_SWITCH, -1,
759 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
760 &li.time_timegate, 10
764 EL_LIGHT_SWITCH_ACTIVE, -1,
765 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
770 EL_SHIELD_NORMAL, -1,
771 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
772 &li.shield_normal_time, 10
775 EL_SHIELD_NORMAL, -1,
776 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
777 &li.score[SC_SHIELD], 10
781 EL_SHIELD_DEADLY, -1,
782 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
783 &li.shield_deadly_time, 10
786 EL_SHIELD_DEADLY, -1,
787 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
788 &li.score[SC_SHIELD], 10
793 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
798 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
799 &li.extra_time_score, 10
803 EL_TIME_ORB_FULL, -1,
804 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
805 &li.time_orb_time, 10
808 EL_TIME_ORB_FULL, -1,
809 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
810 &li.use_time_orb_bug, FALSE
815 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
816 &li.use_spring_bug, FALSE
821 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
822 &li.android_move_time, 10
826 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
827 &li.android_clone_time, 10
830 EL_EMC_ANDROID, SAVE_CONF_NEVER,
831 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
832 &li.android_clone_element[0], EL_EMPTY, NULL,
833 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
837 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
838 &li.android_clone_element[0], EL_EMPTY, NULL,
839 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
844 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
849 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
854 EL_EMC_MAGNIFIER, -1,
855 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
856 &li.magnify_score, 10
859 EL_EMC_MAGNIFIER, -1,
860 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
865 EL_EMC_MAGIC_BALL, -1,
866 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
870 EL_EMC_MAGIC_BALL, -1,
871 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
872 &li.ball_random, FALSE
875 EL_EMC_MAGIC_BALL, -1,
876 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
877 &li.ball_active_initial, FALSE
880 EL_EMC_MAGIC_BALL, -1,
881 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
882 &li.ball_content, EL_EMPTY, NULL,
883 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
887 EL_SOKOBAN_FIELD_EMPTY, -1,
888 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
889 &li.sb_fields_needed, TRUE
893 EL_SOKOBAN_OBJECT, -1,
894 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
895 &li.sb_objects_needed, TRUE
900 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
901 &li.mm_laser_red, FALSE
905 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
906 &li.mm_laser_green, FALSE
910 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
911 &li.mm_laser_blue, TRUE
916 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
917 &li.df_laser_red, TRUE
921 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
922 &li.df_laser_green, TRUE
926 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
927 &li.df_laser_blue, FALSE
931 EL_MM_FUSE_ACTIVE, -1,
932 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
937 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
943 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
948 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
949 &li.mm_ball_choice_mode, ANIM_RANDOM
953 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
954 &li.mm_ball_content, EL_EMPTY, NULL,
955 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
959 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
960 &li.rotate_mm_ball_content, TRUE
964 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
965 &li.explode_mm_ball, FALSE
969 EL_MM_STEEL_BLOCK, -1,
970 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
971 &li.mm_time_block, 75
975 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
976 &li.score[SC_ELEM_BONUS], 10
986 static struct LevelFileConfigInfo chunk_config_NOTE[] =
990 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
991 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
995 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
996 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1001 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1002 &xx_envelope.autowrap, FALSE
1006 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1007 &xx_envelope.centered, FALSE
1012 TYPE_STRING, CONF_VALUE_BYTES(1),
1013 &xx_envelope.text, -1, NULL,
1014 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1015 &xx_default_string_empty[0]
1025 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1029 TYPE_STRING, CONF_VALUE_BYTES(1),
1030 &xx_ei.description[0], -1,
1031 &yy_ei.description[0],
1032 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1033 &xx_default_description[0]
1038 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1039 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1040 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1042 #if ENABLE_RESERVED_CODE
1043 // (reserved for later use)
1046 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1047 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1048 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1054 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1055 &xx_ei.use_gfx_element, FALSE,
1056 &yy_ei.use_gfx_element
1060 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1061 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1062 &yy_ei.gfx_element_initial
1067 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1068 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1069 &yy_ei.access_direction
1074 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1075 &xx_ei.collect_score_initial, 10,
1076 &yy_ei.collect_score_initial
1080 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1081 &xx_ei.collect_count_initial, 1,
1082 &yy_ei.collect_count_initial
1087 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1088 &xx_ei.ce_value_fixed_initial, 0,
1089 &yy_ei.ce_value_fixed_initial
1093 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1094 &xx_ei.ce_value_random_initial, 0,
1095 &yy_ei.ce_value_random_initial
1099 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1100 &xx_ei.use_last_ce_value, FALSE,
1101 &yy_ei.use_last_ce_value
1106 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1107 &xx_ei.push_delay_fixed, 8,
1108 &yy_ei.push_delay_fixed
1112 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1113 &xx_ei.push_delay_random, 8,
1114 &yy_ei.push_delay_random
1118 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1119 &xx_ei.drop_delay_fixed, 0,
1120 &yy_ei.drop_delay_fixed
1124 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1125 &xx_ei.drop_delay_random, 0,
1126 &yy_ei.drop_delay_random
1130 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1131 &xx_ei.move_delay_fixed, 0,
1132 &yy_ei.move_delay_fixed
1136 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1137 &xx_ei.move_delay_random, 0,
1138 &yy_ei.move_delay_random
1142 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1143 &xx_ei.step_delay_fixed, 0,
1144 &yy_ei.step_delay_fixed
1148 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1149 &xx_ei.step_delay_random, 0,
1150 &yy_ei.step_delay_random
1155 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1156 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1161 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1162 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1163 &yy_ei.move_direction_initial
1167 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1168 &xx_ei.move_stepsize, TILEX / 8,
1169 &yy_ei.move_stepsize
1174 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1175 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1176 &yy_ei.move_enter_element
1180 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1181 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1182 &yy_ei.move_leave_element
1186 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1187 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1188 &yy_ei.move_leave_type
1193 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1194 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1195 &yy_ei.slippery_type
1200 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1201 &xx_ei.explosion_type, EXPLODES_3X3,
1202 &yy_ei.explosion_type
1206 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1207 &xx_ei.explosion_delay, 16,
1208 &yy_ei.explosion_delay
1212 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1213 &xx_ei.ignition_delay, 8,
1214 &yy_ei.ignition_delay
1219 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1220 &xx_ei.content, EL_EMPTY_SPACE,
1222 &xx_num_contents, 1, 1
1225 // ---------- "num_change_pages" must be the last entry ---------------------
1228 -1, SAVE_CONF_ALWAYS,
1229 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1230 &xx_ei.num_change_pages, 1,
1231 &yy_ei.num_change_pages
1242 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1244 // ---------- "current_change_page" must be the first entry -----------------
1247 -1, SAVE_CONF_ALWAYS,
1248 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1249 &xx_current_change_page, -1
1252 // ---------- (the remaining entries can be in any order) -------------------
1256 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1257 &xx_change.can_change, FALSE
1262 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1263 &xx_event_bits[0], 0
1267 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1268 &xx_event_bits[1], 0
1273 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1274 &xx_change.trigger_player, CH_PLAYER_ANY
1278 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1279 &xx_change.trigger_side, CH_SIDE_ANY
1283 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1284 &xx_change.trigger_page, CH_PAGE_ANY
1289 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1290 &xx_change.target_element, EL_EMPTY_SPACE
1295 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1296 &xx_change.delay_fixed, 0
1300 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1301 &xx_change.delay_random, 0
1305 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1306 &xx_change.delay_frames, FRAMES_PER_SECOND
1311 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1312 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1317 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1318 &xx_change.explode, FALSE
1322 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1323 &xx_change.use_target_content, FALSE
1327 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1328 &xx_change.only_if_complete, FALSE
1332 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1333 &xx_change.use_random_replace, FALSE
1337 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1338 &xx_change.random_percentage, 100
1342 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1343 &xx_change.replace_when, CP_WHEN_EMPTY
1348 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1349 &xx_change.has_action, FALSE
1353 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1354 &xx_change.action_type, CA_NO_ACTION
1358 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1359 &xx_change.action_mode, CA_MODE_UNDEFINED
1363 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1364 &xx_change.action_arg, CA_ARG_UNDEFINED
1369 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1370 &xx_change.action_element, EL_EMPTY_SPACE
1375 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1376 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1377 &xx_num_contents, 1, 1
1387 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1391 TYPE_STRING, CONF_VALUE_BYTES(1),
1392 &xx_ei.description[0], -1, NULL,
1393 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1394 &xx_default_description[0]
1399 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1400 &xx_ei.use_gfx_element, FALSE
1404 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1405 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1410 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1411 &xx_group.choice_mode, ANIM_RANDOM
1416 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1417 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1418 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1428 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1432 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1433 &xx_ei.use_gfx_element, FALSE
1437 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1438 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1448 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1452 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1453 &li.block_snap_field, TRUE
1457 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1458 &li.continuous_snapping, TRUE
1462 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1463 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1467 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1468 &li.use_start_element[0], FALSE
1472 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1473 &li.start_element[0], EL_PLAYER_1
1477 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1478 &li.use_artwork_element[0], FALSE
1482 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1483 &li.artwork_element[0], EL_PLAYER_1
1487 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1488 &li.use_explosion_element[0], FALSE
1492 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1493 &li.explosion_element[0], EL_PLAYER_1
1508 filetype_id_list[] =
1510 { LEVEL_FILE_TYPE_RND, "RND" },
1511 { LEVEL_FILE_TYPE_BD, "BD" },
1512 { LEVEL_FILE_TYPE_EM, "EM" },
1513 { LEVEL_FILE_TYPE_SP, "SP" },
1514 { LEVEL_FILE_TYPE_DX, "DX" },
1515 { LEVEL_FILE_TYPE_SB, "SB" },
1516 { LEVEL_FILE_TYPE_DC, "DC" },
1517 { LEVEL_FILE_TYPE_MM, "MM" },
1518 { LEVEL_FILE_TYPE_MM, "DF" },
1523 // ============================================================================
1524 // level file functions
1525 // ============================================================================
1527 static boolean check_special_flags(char *flag)
1529 if (strEqual(options.special_flags, flag) ||
1530 strEqual(leveldir_current->special_flags, flag))
1536 static struct DateInfo getCurrentDate(void)
1538 time_t epoch_seconds = time(NULL);
1539 struct tm *now = localtime(&epoch_seconds);
1540 struct DateInfo date;
1542 date.year = now->tm_year + 1900;
1543 date.month = now->tm_mon + 1;
1544 date.day = now->tm_mday;
1546 date.src = DATE_SRC_CLOCK;
1551 static void resetEventFlags(struct ElementChangeInfo *change)
1555 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1556 change->has_event[i] = FALSE;
1559 static void resetEventBits(void)
1563 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1564 xx_event_bits[i] = 0;
1567 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1571 /* important: only change event flag if corresponding event bit is set
1572 (this is because all xx_event_bits[] values are loaded separately,
1573 and all xx_event_bits[] values are set back to zero before loading
1574 another value xx_event_bits[x] (each value representing 32 flags)) */
1576 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1577 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1578 change->has_event[i] = TRUE;
1581 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1585 /* in contrast to the above function setEventFlagsFromEventBits(), it
1586 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1587 depending on the corresponding change->has_event[i] values here, as
1588 all xx_event_bits[] values are reset in resetEventBits() before */
1590 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1591 if (change->has_event[i])
1592 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1595 static char *getDefaultElementDescription(struct ElementInfo *ei)
1597 static char description[MAX_ELEMENT_NAME_LEN + 1];
1598 char *default_description = (ei->custom_description != NULL ?
1599 ei->custom_description :
1600 ei->editor_description);
1603 // always start with reliable default values
1604 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1605 description[i] = '\0';
1607 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1608 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1610 return &description[0];
1613 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1615 char *default_description = getDefaultElementDescription(ei);
1618 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1619 ei->description[i] = default_description[i];
1622 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1626 for (i = 0; conf[i].data_type != -1; i++)
1628 int default_value = conf[i].default_value;
1629 int data_type = conf[i].data_type;
1630 int conf_type = conf[i].conf_type;
1631 int byte_mask = conf_type & CONF_MASK_BYTES;
1633 if (byte_mask == CONF_MASK_MULTI_BYTES)
1635 int default_num_entities = conf[i].default_num_entities;
1636 int max_num_entities = conf[i].max_num_entities;
1638 *(int *)(conf[i].num_entities) = default_num_entities;
1640 if (data_type == TYPE_STRING)
1642 char *default_string = conf[i].default_string;
1643 char *string = (char *)(conf[i].value);
1645 strncpy(string, default_string, max_num_entities);
1647 else if (data_type == TYPE_ELEMENT_LIST)
1649 int *element_array = (int *)(conf[i].value);
1652 for (j = 0; j < max_num_entities; j++)
1653 element_array[j] = default_value;
1655 else if (data_type == TYPE_CONTENT_LIST)
1657 struct Content *content = (struct Content *)(conf[i].value);
1660 for (c = 0; c < max_num_entities; c++)
1661 for (y = 0; y < 3; y++)
1662 for (x = 0; x < 3; x++)
1663 content[c].e[x][y] = default_value;
1666 else // constant size configuration data (1, 2 or 4 bytes)
1668 if (data_type == TYPE_BOOLEAN)
1669 *(boolean *)(conf[i].value) = default_value;
1671 *(int *) (conf[i].value) = default_value;
1676 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1680 for (i = 0; conf[i].data_type != -1; i++)
1682 int data_type = conf[i].data_type;
1683 int conf_type = conf[i].conf_type;
1684 int byte_mask = conf_type & CONF_MASK_BYTES;
1686 if (byte_mask == CONF_MASK_MULTI_BYTES)
1688 int max_num_entities = conf[i].max_num_entities;
1690 if (data_type == TYPE_STRING)
1692 char *string = (char *)(conf[i].value);
1693 char *string_copy = (char *)(conf[i].value_copy);
1695 strncpy(string_copy, string, max_num_entities);
1697 else if (data_type == TYPE_ELEMENT_LIST)
1699 int *element_array = (int *)(conf[i].value);
1700 int *element_array_copy = (int *)(conf[i].value_copy);
1703 for (j = 0; j < max_num_entities; j++)
1704 element_array_copy[j] = element_array[j];
1706 else if (data_type == TYPE_CONTENT_LIST)
1708 struct Content *content = (struct Content *)(conf[i].value);
1709 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1712 for (c = 0; c < max_num_entities; c++)
1713 for (y = 0; y < 3; y++)
1714 for (x = 0; x < 3; x++)
1715 content_copy[c].e[x][y] = content[c].e[x][y];
1718 else // constant size configuration data (1, 2 or 4 bytes)
1720 if (data_type == TYPE_BOOLEAN)
1721 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1723 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1728 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1732 xx_ei = *ei_from; // copy element data into temporary buffer
1733 yy_ei = *ei_to; // copy element data into temporary buffer
1735 copyConfigFromConfigList(chunk_config_CUSX_base);
1740 // ---------- reinitialize and copy change pages ----------
1742 ei_to->num_change_pages = ei_from->num_change_pages;
1743 ei_to->current_change_page = ei_from->current_change_page;
1745 setElementChangePages(ei_to, ei_to->num_change_pages);
1747 for (i = 0; i < ei_to->num_change_pages; i++)
1748 ei_to->change_page[i] = ei_from->change_page[i];
1750 // ---------- copy group element info ----------
1751 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1752 *ei_to->group = *ei_from->group;
1754 // mark this custom element as modified
1755 ei_to->modified_settings = TRUE;
1758 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1760 int change_page_size = sizeof(struct ElementChangeInfo);
1762 ei->num_change_pages = MAX(1, change_pages);
1765 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1767 if (ei->current_change_page >= ei->num_change_pages)
1768 ei->current_change_page = ei->num_change_pages - 1;
1770 ei->change = &ei->change_page[ei->current_change_page];
1773 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1775 xx_change = *change; // copy change data into temporary buffer
1777 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1779 *change = xx_change;
1781 resetEventFlags(change);
1783 change->direct_action = 0;
1784 change->other_action = 0;
1786 change->pre_change_function = NULL;
1787 change->change_function = NULL;
1788 change->post_change_function = NULL;
1791 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1795 li = *level; // copy level data into temporary buffer
1796 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1797 *level = li; // copy temporary buffer back to level data
1799 setLevelInfoToDefaults_BD();
1800 setLevelInfoToDefaults_EM();
1801 setLevelInfoToDefaults_SP();
1802 setLevelInfoToDefaults_MM();
1804 level->native_bd_level = &native_bd_level;
1805 level->native_em_level = &native_em_level;
1806 level->native_sp_level = &native_sp_level;
1807 level->native_mm_level = &native_mm_level;
1809 level->file_version = FILE_VERSION_ACTUAL;
1810 level->game_version = GAME_VERSION_ACTUAL;
1812 level->creation_date = getCurrentDate();
1814 level->encoding_16bit_field = TRUE;
1815 level->encoding_16bit_yamyam = TRUE;
1816 level->encoding_16bit_amoeba = TRUE;
1818 // clear level name and level author string buffers
1819 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1820 level->name[i] = '\0';
1821 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1822 level->author[i] = '\0';
1824 // set level name and level author to default values
1825 strcpy(level->name, NAMELESS_LEVEL_NAME);
1826 strcpy(level->author, ANONYMOUS_NAME);
1828 // set level playfield to playable default level with player and exit
1829 for (x = 0; x < MAX_LEV_FIELDX; x++)
1830 for (y = 0; y < MAX_LEV_FIELDY; y++)
1831 level->field[x][y] = EL_SAND;
1833 level->field[0][0] = EL_PLAYER_1;
1834 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1836 BorderElement = EL_STEELWALL;
1838 // detect custom elements when loading them
1839 level->file_has_custom_elements = FALSE;
1841 // set all bug compatibility flags to "false" => do not emulate this bug
1842 level->use_action_after_change_bug = FALSE;
1844 if (leveldir_current)
1846 // try to determine better author name than 'anonymous'
1847 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1849 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1850 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1854 switch (LEVELCLASS(leveldir_current))
1856 case LEVELCLASS_TUTORIAL:
1857 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1860 case LEVELCLASS_CONTRIB:
1861 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1862 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1865 case LEVELCLASS_PRIVATE:
1866 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1867 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1871 // keep default value
1878 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1880 static boolean clipboard_elements_initialized = FALSE;
1883 InitElementPropertiesStatic();
1885 li = *level; // copy level data into temporary buffer
1886 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1887 *level = li; // copy temporary buffer back to level data
1889 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1892 struct ElementInfo *ei = &element_info[element];
1894 if (element == EL_MM_GRAY_BALL)
1896 struct LevelInfo_MM *level_mm = level->native_mm_level;
1899 for (j = 0; j < level->num_mm_ball_contents; j++)
1900 level->mm_ball_content[j] =
1901 map_element_MM_to_RND(level_mm->ball_content[j]);
1904 // never initialize clipboard elements after the very first time
1905 // (to be able to use clipboard elements between several levels)
1906 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1909 if (IS_ENVELOPE(element))
1911 int envelope_nr = element - EL_ENVELOPE_1;
1913 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1915 level->envelope[envelope_nr] = xx_envelope;
1918 if (IS_CUSTOM_ELEMENT(element) ||
1919 IS_GROUP_ELEMENT(element) ||
1920 IS_INTERNAL_ELEMENT(element))
1922 xx_ei = *ei; // copy element data into temporary buffer
1924 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1929 setElementChangePages(ei, 1);
1930 setElementChangeInfoToDefaults(ei->change);
1932 if (IS_CUSTOM_ELEMENT(element) ||
1933 IS_GROUP_ELEMENT(element))
1935 setElementDescriptionToDefault(ei);
1937 ei->modified_settings = FALSE;
1940 if (IS_CUSTOM_ELEMENT(element) ||
1941 IS_INTERNAL_ELEMENT(element))
1943 // internal values used in level editor
1945 ei->access_type = 0;
1946 ei->access_layer = 0;
1947 ei->access_protected = 0;
1948 ei->walk_to_action = 0;
1949 ei->smash_targets = 0;
1952 ei->can_explode_by_fire = FALSE;
1953 ei->can_explode_smashed = FALSE;
1954 ei->can_explode_impact = FALSE;
1956 ei->current_change_page = 0;
1959 if (IS_GROUP_ELEMENT(element) ||
1960 IS_INTERNAL_ELEMENT(element))
1962 struct ElementGroupInfo *group;
1964 // initialize memory for list of elements in group
1965 if (ei->group == NULL)
1966 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1970 xx_group = *group; // copy group data into temporary buffer
1972 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1977 if (IS_EMPTY_ELEMENT(element) ||
1978 IS_INTERNAL_ELEMENT(element))
1980 xx_ei = *ei; // copy element data into temporary buffer
1982 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
1988 clipboard_elements_initialized = TRUE;
1991 static void setLevelInfoToDefaults(struct LevelInfo *level,
1992 boolean level_info_only,
1993 boolean reset_file_status)
1995 setLevelInfoToDefaults_Level(level);
1997 if (!level_info_only)
1998 setLevelInfoToDefaults_Elements(level);
2000 if (reset_file_status)
2002 level->no_valid_file = FALSE;
2003 level->no_level_file = FALSE;
2006 level->changed = FALSE;
2009 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2011 level_file_info->nr = 0;
2012 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2013 level_file_info->packed = FALSE;
2015 setString(&level_file_info->basename, NULL);
2016 setString(&level_file_info->filename, NULL);
2019 int getMappedElement_SB(int, boolean);
2021 static void ActivateLevelTemplate(void)
2025 if (check_special_flags("load_xsb_to_ces"))
2027 // fill smaller playfields with padding "beyond border wall" elements
2028 if (level.fieldx < level_template.fieldx ||
2029 level.fieldy < level_template.fieldy)
2031 short field[level.fieldx][level.fieldy];
2032 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2033 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2034 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2035 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2037 // copy old playfield (which is smaller than the visible area)
2038 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2039 field[x][y] = level.field[x][y];
2041 // fill new, larger playfield with "beyond border wall" elements
2042 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2043 level.field[x][y] = getMappedElement_SB('_', TRUE);
2045 // copy the old playfield to the middle of the new playfield
2046 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2047 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2049 level.fieldx = new_fieldx;
2050 level.fieldy = new_fieldy;
2054 // Currently there is no special action needed to activate the template
2055 // data, because 'element_info' property settings overwrite the original
2056 // level data, while all other variables do not change.
2058 // Exception: 'from_level_template' elements in the original level playfield
2059 // are overwritten with the corresponding elements at the same position in
2060 // playfield from the level template.
2062 for (x = 0; x < level.fieldx; x++)
2063 for (y = 0; y < level.fieldy; y++)
2064 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2065 level.field[x][y] = level_template.field[x][y];
2067 if (check_special_flags("load_xsb_to_ces"))
2069 struct LevelInfo level_backup = level;
2071 // overwrite all individual level settings from template level settings
2072 level = level_template;
2074 // restore level file info
2075 level.file_info = level_backup.file_info;
2077 // restore playfield size
2078 level.fieldx = level_backup.fieldx;
2079 level.fieldy = level_backup.fieldy;
2081 // restore playfield content
2082 for (x = 0; x < level.fieldx; x++)
2083 for (y = 0; y < level.fieldy; y++)
2084 level.field[x][y] = level_backup.field[x][y];
2086 // restore name and author from individual level
2087 strcpy(level.name, level_backup.name);
2088 strcpy(level.author, level_backup.author);
2090 // restore flag "use_custom_template"
2091 level.use_custom_template = level_backup.use_custom_template;
2095 static boolean checkForPackageFromBasename_BD(char *basename)
2097 // check for native BD level file extensions
2098 if (!strSuffixLower(basename, ".bd") &&
2099 !strSuffixLower(basename, ".bdr") &&
2100 !strSuffixLower(basename, ".brc") &&
2101 !strSuffixLower(basename, ".gds"))
2104 // check for standard single-level BD files (like "001.bd")
2105 if (strSuffixLower(basename, ".bd") &&
2106 strlen(basename) == 6 &&
2107 basename[0] >= '0' && basename[0] <= '9' &&
2108 basename[1] >= '0' && basename[1] <= '9' &&
2109 basename[2] >= '0' && basename[2] <= '9')
2112 // this is a level package in native BD file format
2116 static char *getLevelFilenameFromBasename(char *basename)
2118 static char *filename = NULL;
2120 checked_free(filename);
2122 filename = getPath2(getCurrentLevelDir(), basename);
2127 static int getFileTypeFromBasename(char *basename)
2129 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2131 static char *filename = NULL;
2132 struct stat file_status;
2134 // ---------- try to determine file type from filename ----------
2136 // check for typical filename of a Supaplex level package file
2137 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2138 return LEVEL_FILE_TYPE_SP;
2140 // check for typical filename of a Diamond Caves II level package file
2141 if (strSuffixLower(basename, ".dc") ||
2142 strSuffixLower(basename, ".dc2"))
2143 return LEVEL_FILE_TYPE_DC;
2145 // check for typical filename of a Sokoban level package file
2146 if (strSuffixLower(basename, ".xsb") &&
2147 strchr(basename, '%') == NULL)
2148 return LEVEL_FILE_TYPE_SB;
2150 // check for typical filename of a Boulder Dash (GDash) level package file
2151 if (checkForPackageFromBasename_BD(basename))
2152 return LEVEL_FILE_TYPE_BD;
2154 // ---------- try to determine file type from filesize ----------
2156 checked_free(filename);
2157 filename = getPath2(getCurrentLevelDir(), basename);
2159 if (stat(filename, &file_status) == 0)
2161 // check for typical filesize of a Supaplex level package file
2162 if (file_status.st_size == 170496)
2163 return LEVEL_FILE_TYPE_SP;
2166 return LEVEL_FILE_TYPE_UNKNOWN;
2169 static int getFileTypeFromMagicBytes(char *filename, int type)
2173 if ((file = openFile(filename, MODE_READ)))
2175 char chunk_name[CHUNK_ID_LEN + 1];
2177 getFileChunkBE(file, chunk_name, NULL);
2179 if (strEqual(chunk_name, "MMII") ||
2180 strEqual(chunk_name, "MIRR"))
2181 type = LEVEL_FILE_TYPE_MM;
2189 static boolean checkForPackageFromBasename(char *basename)
2191 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2192 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2194 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2197 static char *getSingleLevelBasenameExt(int nr, char *extension)
2199 static char basename[MAX_FILENAME_LEN];
2202 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2204 sprintf(basename, "%03d.%s", nr, extension);
2209 static char *getSingleLevelBasename(int nr)
2211 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2214 static char *getPackedLevelBasename(int type)
2216 static char basename[MAX_FILENAME_LEN];
2217 char *directory = getCurrentLevelDir();
2219 DirectoryEntry *dir_entry;
2221 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2223 if ((dir = openDirectory(directory)) == NULL)
2225 Warn("cannot read current level directory '%s'", directory);
2230 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2232 char *entry_basename = dir_entry->basename;
2233 int entry_type = getFileTypeFromBasename(entry_basename);
2235 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2237 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2240 strcpy(basename, entry_basename);
2247 closeDirectory(dir);
2252 static char *getSingleLevelFilename(int nr)
2254 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2257 #if ENABLE_UNUSED_CODE
2258 static char *getPackedLevelFilename(int type)
2260 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2264 char *getDefaultLevelFilename(int nr)
2266 return getSingleLevelFilename(nr);
2269 #if ENABLE_UNUSED_CODE
2270 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2274 lfi->packed = FALSE;
2276 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2277 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2281 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2282 int type, char *format, ...)
2284 static char basename[MAX_FILENAME_LEN];
2287 va_start(ap, format);
2288 vsprintf(basename, format, ap);
2292 lfi->packed = FALSE;
2294 setString(&lfi->basename, basename);
2295 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2298 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2304 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2305 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2308 static int getFiletypeFromID(char *filetype_id)
2310 char *filetype_id_lower;
2311 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2314 if (filetype_id == NULL)
2315 return LEVEL_FILE_TYPE_UNKNOWN;
2317 filetype_id_lower = getStringToLower(filetype_id);
2319 for (i = 0; filetype_id_list[i].id != NULL; i++)
2321 char *id_lower = getStringToLower(filetype_id_list[i].id);
2323 if (strEqual(filetype_id_lower, id_lower))
2324 filetype = filetype_id_list[i].filetype;
2328 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2332 free(filetype_id_lower);
2337 char *getLocalLevelTemplateFilename(void)
2339 return getDefaultLevelFilename(-1);
2342 char *getGlobalLevelTemplateFilename(void)
2344 // global variable "leveldir_current" must be modified in the loop below
2345 LevelDirTree *leveldir_current_last = leveldir_current;
2346 char *filename = NULL;
2348 // check for template level in path from current to topmost tree node
2350 while (leveldir_current != NULL)
2352 filename = getDefaultLevelFilename(-1);
2354 if (fileExists(filename))
2357 leveldir_current = leveldir_current->node_parent;
2360 // restore global variable "leveldir_current" modified in above loop
2361 leveldir_current = leveldir_current_last;
2366 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2370 // special case: level number is negative => check for level template file
2373 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2374 getSingleLevelBasename(-1));
2376 // replace local level template filename with global template filename
2377 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2379 // no fallback if template file not existing
2383 // special case: check for file name/pattern specified in "levelinfo.conf"
2384 if (leveldir_current->level_filename != NULL)
2386 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2388 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2389 leveldir_current->level_filename, nr);
2391 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2393 if (fileExists(lfi->filename))
2396 else if (leveldir_current->level_filetype != NULL)
2398 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2400 // check for specified native level file with standard file name
2401 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2402 "%03d.%s", nr, LEVELFILE_EXTENSION);
2403 if (fileExists(lfi->filename))
2407 // check for native Rocks'n'Diamonds level file
2408 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2409 "%03d.%s", nr, LEVELFILE_EXTENSION);
2410 if (fileExists(lfi->filename))
2413 // check for native Boulder Dash level file
2414 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2415 if (fileExists(lfi->filename))
2418 // check for Emerald Mine level file (V1)
2419 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2420 'a' + (nr / 10) % 26, '0' + nr % 10);
2421 if (fileExists(lfi->filename))
2423 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2424 'A' + (nr / 10) % 26, '0' + nr % 10);
2425 if (fileExists(lfi->filename))
2428 // check for Emerald Mine level file (V2 to V5)
2429 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2430 if (fileExists(lfi->filename))
2433 // check for Emerald Mine level file (V6 / single mode)
2434 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2435 if (fileExists(lfi->filename))
2437 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2438 if (fileExists(lfi->filename))
2441 // check for Emerald Mine level file (V6 / teamwork mode)
2442 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2443 if (fileExists(lfi->filename))
2445 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2446 if (fileExists(lfi->filename))
2449 // check for various packed level file formats
2450 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2451 if (fileExists(lfi->filename))
2454 // no known level file found -- use default values (and fail later)
2455 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2456 "%03d.%s", nr, LEVELFILE_EXTENSION);
2459 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2461 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2462 lfi->type = getFileTypeFromBasename(lfi->basename);
2464 if (lfi->type == LEVEL_FILE_TYPE_RND)
2465 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2468 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2470 // always start with reliable default values
2471 setFileInfoToDefaults(level_file_info);
2473 level_file_info->nr = nr; // set requested level number
2475 determineLevelFileInfo_Filename(level_file_info);
2476 determineLevelFileInfo_Filetype(level_file_info);
2479 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2480 struct LevelFileInfo *lfi_to)
2482 lfi_to->nr = lfi_from->nr;
2483 lfi_to->type = lfi_from->type;
2484 lfi_to->packed = lfi_from->packed;
2486 setString(&lfi_to->basename, lfi_from->basename);
2487 setString(&lfi_to->filename, lfi_from->filename);
2490 // ----------------------------------------------------------------------------
2491 // functions for loading R'n'D level
2492 // ----------------------------------------------------------------------------
2494 int getMappedElement(int element)
2496 // remap some (historic, now obsolete) elements
2500 case EL_PLAYER_OBSOLETE:
2501 element = EL_PLAYER_1;
2504 case EL_KEY_OBSOLETE:
2508 case EL_EM_KEY_1_FILE_OBSOLETE:
2509 element = EL_EM_KEY_1;
2512 case EL_EM_KEY_2_FILE_OBSOLETE:
2513 element = EL_EM_KEY_2;
2516 case EL_EM_KEY_3_FILE_OBSOLETE:
2517 element = EL_EM_KEY_3;
2520 case EL_EM_KEY_4_FILE_OBSOLETE:
2521 element = EL_EM_KEY_4;
2524 case EL_ENVELOPE_OBSOLETE:
2525 element = EL_ENVELOPE_1;
2533 if (element >= NUM_FILE_ELEMENTS)
2535 Warn("invalid level element %d", element);
2537 element = EL_UNKNOWN;
2545 static int getMappedElementByVersion(int element, int game_version)
2547 // remap some elements due to certain game version
2549 if (game_version <= VERSION_IDENT(2,2,0,0))
2551 // map game font elements
2552 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2553 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2554 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2555 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2558 if (game_version < VERSION_IDENT(3,0,0,0))
2560 // map Supaplex gravity tube elements
2561 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2562 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2563 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2564 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2571 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2573 level->file_version = getFileVersion(file);
2574 level->game_version = getFileVersion(file);
2579 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2581 level->creation_date.year = getFile16BitBE(file);
2582 level->creation_date.month = getFile8Bit(file);
2583 level->creation_date.day = getFile8Bit(file);
2585 level->creation_date.src = DATE_SRC_LEVELFILE;
2590 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2592 int initial_player_stepsize;
2593 int initial_player_gravity;
2596 level->fieldx = getFile8Bit(file);
2597 level->fieldy = getFile8Bit(file);
2599 level->time = getFile16BitBE(file);
2600 level->gems_needed = getFile16BitBE(file);
2602 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2603 level->name[i] = getFile8Bit(file);
2604 level->name[MAX_LEVEL_NAME_LEN] = 0;
2606 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2607 level->score[i] = getFile8Bit(file);
2609 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2610 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2611 for (y = 0; y < 3; y++)
2612 for (x = 0; x < 3; x++)
2613 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2615 level->amoeba_speed = getFile8Bit(file);
2616 level->time_magic_wall = getFile8Bit(file);
2617 level->time_wheel = getFile8Bit(file);
2618 level->amoeba_content = getMappedElement(getFile8Bit(file));
2620 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2623 for (i = 0; i < MAX_PLAYERS; i++)
2624 level->initial_player_stepsize[i] = initial_player_stepsize;
2626 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2628 for (i = 0; i < MAX_PLAYERS; i++)
2629 level->initial_player_gravity[i] = initial_player_gravity;
2631 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2632 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2634 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2636 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2637 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2638 level->can_move_into_acid_bits = getFile32BitBE(file);
2639 level->dont_collide_with_bits = getFile8Bit(file);
2641 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2642 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2644 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2645 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2646 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2648 level->game_engine_type = getFile8Bit(file);
2650 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2655 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2659 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2660 level->name[i] = getFile8Bit(file);
2661 level->name[MAX_LEVEL_NAME_LEN] = 0;
2666 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2670 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2671 level->author[i] = getFile8Bit(file);
2672 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2677 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2680 int chunk_size_expected = level->fieldx * level->fieldy;
2682 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2683 stored with 16-bit encoding (and should be twice as big then).
2684 Even worse, playfield data was stored 16-bit when only yamyam content
2685 contained 16-bit elements and vice versa. */
2687 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2688 chunk_size_expected *= 2;
2690 if (chunk_size_expected != chunk_size)
2692 ReadUnusedBytesFromFile(file, chunk_size);
2693 return chunk_size_expected;
2696 for (y = 0; y < level->fieldy; y++)
2697 for (x = 0; x < level->fieldx; x++)
2698 level->field[x][y] =
2699 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2704 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2707 int header_size = 4;
2708 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2709 int chunk_size_expected = header_size + content_size;
2711 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2712 stored with 16-bit encoding (and should be twice as big then).
2713 Even worse, playfield data was stored 16-bit when only yamyam content
2714 contained 16-bit elements and vice versa. */
2716 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2717 chunk_size_expected += content_size;
2719 if (chunk_size_expected != chunk_size)
2721 ReadUnusedBytesFromFile(file, chunk_size);
2722 return chunk_size_expected;
2726 level->num_yamyam_contents = getFile8Bit(file);
2730 // correct invalid number of content fields -- should never happen
2731 if (level->num_yamyam_contents < 1 ||
2732 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2733 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2735 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2736 for (y = 0; y < 3; y++)
2737 for (x = 0; x < 3; x++)
2738 level->yamyam_content[i].e[x][y] =
2739 getMappedElement(level->encoding_16bit_field ?
2740 getFile16BitBE(file) : getFile8Bit(file));
2744 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2749 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2751 element = getMappedElement(getFile16BitBE(file));
2752 num_contents = getFile8Bit(file);
2754 getFile8Bit(file); // content x size (unused)
2755 getFile8Bit(file); // content y size (unused)
2757 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2759 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2760 for (y = 0; y < 3; y++)
2761 for (x = 0; x < 3; x++)
2762 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2764 // correct invalid number of content fields -- should never happen
2765 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2766 num_contents = STD_ELEMENT_CONTENTS;
2768 if (element == EL_YAMYAM)
2770 level->num_yamyam_contents = num_contents;
2772 for (i = 0; i < num_contents; i++)
2773 for (y = 0; y < 3; y++)
2774 for (x = 0; x < 3; x++)
2775 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2777 else if (element == EL_BD_AMOEBA)
2779 level->amoeba_content = content_array[0][0][0];
2783 Warn("cannot load content for element '%d'", element);
2789 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2795 int chunk_size_expected;
2797 element = getMappedElement(getFile16BitBE(file));
2798 if (!IS_ENVELOPE(element))
2799 element = EL_ENVELOPE_1;
2801 envelope_nr = element - EL_ENVELOPE_1;
2803 envelope_len = getFile16BitBE(file);
2805 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2806 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2808 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2810 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2811 if (chunk_size_expected != chunk_size)
2813 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2814 return chunk_size_expected;
2817 for (i = 0; i < envelope_len; i++)
2818 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2823 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2825 int num_changed_custom_elements = getFile16BitBE(file);
2826 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2829 if (chunk_size_expected != chunk_size)
2831 ReadUnusedBytesFromFile(file, chunk_size - 2);
2832 return chunk_size_expected;
2835 for (i = 0; i < num_changed_custom_elements; i++)
2837 int element = getMappedElement(getFile16BitBE(file));
2838 int properties = getFile32BitBE(file);
2840 if (IS_CUSTOM_ELEMENT(element))
2841 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2843 Warn("invalid custom element number %d", element);
2845 // older game versions that wrote level files with CUS1 chunks used
2846 // different default push delay values (not yet stored in level file)
2847 element_info[element].push_delay_fixed = 2;
2848 element_info[element].push_delay_random = 8;
2851 level->file_has_custom_elements = TRUE;
2856 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2858 int num_changed_custom_elements = getFile16BitBE(file);
2859 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2862 if (chunk_size_expected != chunk_size)
2864 ReadUnusedBytesFromFile(file, chunk_size - 2);
2865 return chunk_size_expected;
2868 for (i = 0; i < num_changed_custom_elements; i++)
2870 int element = getMappedElement(getFile16BitBE(file));
2871 int custom_target_element = getMappedElement(getFile16BitBE(file));
2873 if (IS_CUSTOM_ELEMENT(element))
2874 element_info[element].change->target_element = custom_target_element;
2876 Warn("invalid custom element number %d", element);
2879 level->file_has_custom_elements = TRUE;
2884 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2886 int num_changed_custom_elements = getFile16BitBE(file);
2887 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2890 if (chunk_size_expected != chunk_size)
2892 ReadUnusedBytesFromFile(file, chunk_size - 2);
2893 return chunk_size_expected;
2896 for (i = 0; i < num_changed_custom_elements; i++)
2898 int element = getMappedElement(getFile16BitBE(file));
2899 struct ElementInfo *ei = &element_info[element];
2900 unsigned int event_bits;
2902 if (!IS_CUSTOM_ELEMENT(element))
2904 Warn("invalid custom element number %d", element);
2906 element = EL_INTERNAL_DUMMY;
2909 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2910 ei->description[j] = getFile8Bit(file);
2911 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2913 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2915 // some free bytes for future properties and padding
2916 ReadUnusedBytesFromFile(file, 7);
2918 ei->use_gfx_element = getFile8Bit(file);
2919 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2921 ei->collect_score_initial = getFile8Bit(file);
2922 ei->collect_count_initial = getFile8Bit(file);
2924 ei->push_delay_fixed = getFile16BitBE(file);
2925 ei->push_delay_random = getFile16BitBE(file);
2926 ei->move_delay_fixed = getFile16BitBE(file);
2927 ei->move_delay_random = getFile16BitBE(file);
2929 ei->move_pattern = getFile16BitBE(file);
2930 ei->move_direction_initial = getFile8Bit(file);
2931 ei->move_stepsize = getFile8Bit(file);
2933 for (y = 0; y < 3; y++)
2934 for (x = 0; x < 3; x++)
2935 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2937 // bits 0 - 31 of "has_event[]"
2938 event_bits = getFile32BitBE(file);
2939 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2940 if (event_bits & (1u << j))
2941 ei->change->has_event[j] = TRUE;
2943 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2945 ei->change->delay_fixed = getFile16BitBE(file);
2946 ei->change->delay_random = getFile16BitBE(file);
2947 ei->change->delay_frames = getFile16BitBE(file);
2949 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2951 ei->change->explode = getFile8Bit(file);
2952 ei->change->use_target_content = getFile8Bit(file);
2953 ei->change->only_if_complete = getFile8Bit(file);
2954 ei->change->use_random_replace = getFile8Bit(file);
2956 ei->change->random_percentage = getFile8Bit(file);
2957 ei->change->replace_when = getFile8Bit(file);
2959 for (y = 0; y < 3; y++)
2960 for (x = 0; x < 3; x++)
2961 ei->change->target_content.e[x][y] =
2962 getMappedElement(getFile16BitBE(file));
2964 ei->slippery_type = getFile8Bit(file);
2966 // some free bytes for future properties and padding
2967 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2969 // mark that this custom element has been modified
2970 ei->modified_settings = TRUE;
2973 level->file_has_custom_elements = TRUE;
2978 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2980 struct ElementInfo *ei;
2981 int chunk_size_expected;
2985 // ---------- custom element base property values (96 bytes) ----------------
2987 element = getMappedElement(getFile16BitBE(file));
2989 if (!IS_CUSTOM_ELEMENT(element))
2991 Warn("invalid custom element number %d", element);
2993 ReadUnusedBytesFromFile(file, chunk_size - 2);
2998 ei = &element_info[element];
3000 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3001 ei->description[i] = getFile8Bit(file);
3002 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3004 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3006 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3008 ei->num_change_pages = getFile8Bit(file);
3010 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3011 if (chunk_size_expected != chunk_size)
3013 ReadUnusedBytesFromFile(file, chunk_size - 43);
3014 return chunk_size_expected;
3017 ei->ce_value_fixed_initial = getFile16BitBE(file);
3018 ei->ce_value_random_initial = getFile16BitBE(file);
3019 ei->use_last_ce_value = getFile8Bit(file);
3021 ei->use_gfx_element = getFile8Bit(file);
3022 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3024 ei->collect_score_initial = getFile8Bit(file);
3025 ei->collect_count_initial = getFile8Bit(file);
3027 ei->drop_delay_fixed = getFile8Bit(file);
3028 ei->push_delay_fixed = getFile8Bit(file);
3029 ei->drop_delay_random = getFile8Bit(file);
3030 ei->push_delay_random = getFile8Bit(file);
3031 ei->move_delay_fixed = getFile16BitBE(file);
3032 ei->move_delay_random = getFile16BitBE(file);
3034 // bits 0 - 15 of "move_pattern" ...
3035 ei->move_pattern = getFile16BitBE(file);
3036 ei->move_direction_initial = getFile8Bit(file);
3037 ei->move_stepsize = getFile8Bit(file);
3039 ei->slippery_type = getFile8Bit(file);
3041 for (y = 0; y < 3; y++)
3042 for (x = 0; x < 3; x++)
3043 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3045 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3046 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3047 ei->move_leave_type = getFile8Bit(file);
3049 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3050 ei->move_pattern |= (getFile16BitBE(file) << 16);
3052 ei->access_direction = getFile8Bit(file);
3054 ei->explosion_delay = getFile8Bit(file);
3055 ei->ignition_delay = getFile8Bit(file);
3056 ei->explosion_type = getFile8Bit(file);
3058 // some free bytes for future custom property values and padding
3059 ReadUnusedBytesFromFile(file, 1);
3061 // ---------- change page property values (48 bytes) ------------------------
3063 setElementChangePages(ei, ei->num_change_pages);
3065 for (i = 0; i < ei->num_change_pages; i++)
3067 struct ElementChangeInfo *change = &ei->change_page[i];
3068 unsigned int event_bits;
3070 // always start with reliable default values
3071 setElementChangeInfoToDefaults(change);
3073 // bits 0 - 31 of "has_event[]" ...
3074 event_bits = getFile32BitBE(file);
3075 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3076 if (event_bits & (1u << j))
3077 change->has_event[j] = TRUE;
3079 change->target_element = getMappedElement(getFile16BitBE(file));
3081 change->delay_fixed = getFile16BitBE(file);
3082 change->delay_random = getFile16BitBE(file);
3083 change->delay_frames = getFile16BitBE(file);
3085 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3087 change->explode = getFile8Bit(file);
3088 change->use_target_content = getFile8Bit(file);
3089 change->only_if_complete = getFile8Bit(file);
3090 change->use_random_replace = getFile8Bit(file);
3092 change->random_percentage = getFile8Bit(file);
3093 change->replace_when = getFile8Bit(file);
3095 for (y = 0; y < 3; y++)
3096 for (x = 0; x < 3; x++)
3097 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3099 change->can_change = getFile8Bit(file);
3101 change->trigger_side = getFile8Bit(file);
3103 change->trigger_player = getFile8Bit(file);
3104 change->trigger_page = getFile8Bit(file);
3106 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3107 CH_PAGE_ANY : (1 << change->trigger_page));
3109 change->has_action = getFile8Bit(file);
3110 change->action_type = getFile8Bit(file);
3111 change->action_mode = getFile8Bit(file);
3112 change->action_arg = getFile16BitBE(file);
3114 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3115 event_bits = getFile8Bit(file);
3116 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3117 if (event_bits & (1u << (j - 32)))
3118 change->has_event[j] = TRUE;
3121 // mark this custom element as modified
3122 ei->modified_settings = TRUE;
3124 level->file_has_custom_elements = TRUE;
3129 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3131 struct ElementInfo *ei;
3132 struct ElementGroupInfo *group;
3136 element = getMappedElement(getFile16BitBE(file));
3138 if (!IS_GROUP_ELEMENT(element))
3140 Warn("invalid group element number %d", element);
3142 ReadUnusedBytesFromFile(file, chunk_size - 2);
3147 ei = &element_info[element];
3149 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3150 ei->description[i] = getFile8Bit(file);
3151 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3153 group = element_info[element].group;
3155 group->num_elements = getFile8Bit(file);
3157 ei->use_gfx_element = getFile8Bit(file);
3158 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3160 group->choice_mode = getFile8Bit(file);
3162 // some free bytes for future values and padding
3163 ReadUnusedBytesFromFile(file, 3);
3165 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3166 group->element[i] = getMappedElement(getFile16BitBE(file));
3168 // mark this group element as modified
3169 element_info[element].modified_settings = TRUE;
3171 level->file_has_custom_elements = TRUE;
3176 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3177 int element, int real_element)
3179 int micro_chunk_size = 0;
3180 int conf_type = getFile8Bit(file);
3181 int byte_mask = conf_type & CONF_MASK_BYTES;
3182 boolean element_found = FALSE;
3185 micro_chunk_size += 1;
3187 if (byte_mask == CONF_MASK_MULTI_BYTES)
3189 int num_bytes = getFile16BitBE(file);
3190 byte *buffer = checked_malloc(num_bytes);
3192 ReadBytesFromFile(file, buffer, num_bytes);
3194 for (i = 0; conf[i].data_type != -1; i++)
3196 if (conf[i].element == element &&
3197 conf[i].conf_type == conf_type)
3199 int data_type = conf[i].data_type;
3200 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3201 int max_num_entities = conf[i].max_num_entities;
3203 if (num_entities > max_num_entities)
3205 Warn("truncating number of entities for element %d from %d to %d",
3206 element, num_entities, max_num_entities);
3208 num_entities = max_num_entities;
3211 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3212 data_type == TYPE_CONTENT_LIST))
3214 // for element and content lists, zero entities are not allowed
3215 Warn("found empty list of entities for element %d", element);
3217 // do not set "num_entities" here to prevent reading behind buffer
3219 *(int *)(conf[i].num_entities) = 1; // at least one is required
3223 *(int *)(conf[i].num_entities) = num_entities;
3226 element_found = TRUE;
3228 if (data_type == TYPE_STRING)
3230 char *string = (char *)(conf[i].value);
3233 for (j = 0; j < max_num_entities; j++)
3234 string[j] = (j < num_entities ? buffer[j] : '\0');
3236 else if (data_type == TYPE_ELEMENT_LIST)
3238 int *element_array = (int *)(conf[i].value);
3241 for (j = 0; j < num_entities; j++)
3243 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3245 else if (data_type == TYPE_CONTENT_LIST)
3247 struct Content *content= (struct Content *)(conf[i].value);
3250 for (c = 0; c < num_entities; c++)
3251 for (y = 0; y < 3; y++)
3252 for (x = 0; x < 3; x++)
3253 content[c].e[x][y] =
3254 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3257 element_found = FALSE;
3263 checked_free(buffer);
3265 micro_chunk_size += 2 + num_bytes;
3267 else // constant size configuration data (1, 2 or 4 bytes)
3269 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3270 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3271 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3273 for (i = 0; conf[i].data_type != -1; i++)
3275 if (conf[i].element == element &&
3276 conf[i].conf_type == conf_type)
3278 int data_type = conf[i].data_type;
3280 if (data_type == TYPE_ELEMENT)
3281 value = getMappedElement(value);
3283 if (data_type == TYPE_BOOLEAN)
3284 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3286 *(int *) (conf[i].value) = value;
3288 element_found = TRUE;
3294 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3299 char *error_conf_chunk_bytes =
3300 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3301 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3302 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3303 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3304 int error_element = real_element;
3306 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3307 error_conf_chunk_bytes, error_conf_chunk_token,
3308 error_element, EL_NAME(error_element));
3311 return micro_chunk_size;
3314 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3316 int real_chunk_size = 0;
3318 li = *level; // copy level data into temporary buffer
3320 while (!checkEndOfFile(file))
3322 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3324 if (real_chunk_size >= chunk_size)
3328 *level = li; // copy temporary buffer back to level data
3330 return real_chunk_size;
3333 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3335 int real_chunk_size = 0;
3337 li = *level; // copy level data into temporary buffer
3339 while (!checkEndOfFile(file))
3341 int element = getMappedElement(getFile16BitBE(file));
3343 real_chunk_size += 2;
3344 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3346 if (real_chunk_size >= chunk_size)
3350 *level = li; // copy temporary buffer back to level data
3352 return real_chunk_size;
3355 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3357 int real_chunk_size = 0;
3359 li = *level; // copy level data into temporary buffer
3361 while (!checkEndOfFile(file))
3363 int element = getMappedElement(getFile16BitBE(file));
3365 real_chunk_size += 2;
3366 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3368 if (real_chunk_size >= chunk_size)
3372 *level = li; // copy temporary buffer back to level data
3374 return real_chunk_size;
3377 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3379 int element = getMappedElement(getFile16BitBE(file));
3380 int envelope_nr = element - EL_ENVELOPE_1;
3381 int real_chunk_size = 2;
3383 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3385 while (!checkEndOfFile(file))
3387 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3390 if (real_chunk_size >= chunk_size)
3394 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3396 return real_chunk_size;
3399 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3401 int element = getMappedElement(getFile16BitBE(file));
3402 int real_chunk_size = 2;
3403 struct ElementInfo *ei = &element_info[element];
3406 xx_ei = *ei; // copy element data into temporary buffer
3408 xx_ei.num_change_pages = -1;
3410 while (!checkEndOfFile(file))
3412 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3414 if (xx_ei.num_change_pages != -1)
3417 if (real_chunk_size >= chunk_size)
3423 if (ei->num_change_pages == -1)
3425 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3428 ei->num_change_pages = 1;
3430 setElementChangePages(ei, 1);
3431 setElementChangeInfoToDefaults(ei->change);
3433 return real_chunk_size;
3436 // initialize number of change pages stored for this custom element
3437 setElementChangePages(ei, ei->num_change_pages);
3438 for (i = 0; i < ei->num_change_pages; i++)
3439 setElementChangeInfoToDefaults(&ei->change_page[i]);
3441 // start with reading properties for the first change page
3442 xx_current_change_page = 0;
3444 while (!checkEndOfFile(file))
3446 // level file might contain invalid change page number
3447 if (xx_current_change_page >= ei->num_change_pages)
3450 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3452 xx_change = *change; // copy change data into temporary buffer
3454 resetEventBits(); // reset bits; change page might have changed
3456 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3459 *change = xx_change;
3461 setEventFlagsFromEventBits(change);
3463 if (real_chunk_size >= chunk_size)
3467 level->file_has_custom_elements = TRUE;
3469 return real_chunk_size;
3472 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3474 int element = getMappedElement(getFile16BitBE(file));
3475 int real_chunk_size = 2;
3476 struct ElementInfo *ei = &element_info[element];
3477 struct ElementGroupInfo *group = ei->group;
3482 xx_ei = *ei; // copy element data into temporary buffer
3483 xx_group = *group; // copy group data into temporary buffer
3485 while (!checkEndOfFile(file))
3487 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3490 if (real_chunk_size >= chunk_size)
3497 level->file_has_custom_elements = TRUE;
3499 return real_chunk_size;
3502 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3504 int element = getMappedElement(getFile16BitBE(file));
3505 int real_chunk_size = 2;
3506 struct ElementInfo *ei = &element_info[element];
3508 xx_ei = *ei; // copy element data into temporary buffer
3510 while (!checkEndOfFile(file))
3512 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3515 if (real_chunk_size >= chunk_size)
3521 level->file_has_custom_elements = TRUE;
3523 return real_chunk_size;
3526 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3527 struct LevelFileInfo *level_file_info,
3528 boolean level_info_only)
3530 char *filename = level_file_info->filename;
3531 char cookie[MAX_LINE_LEN];
3532 char chunk_name[CHUNK_ID_LEN + 1];
3536 if (!(file = openFile(filename, MODE_READ)))
3538 level->no_valid_file = TRUE;
3539 level->no_level_file = TRUE;
3541 if (level_info_only)
3544 Warn("cannot read level '%s' -- using empty level", filename);
3546 if (!setup.editor.use_template_for_new_levels)
3549 // if level file not found, try to initialize level data from template
3550 filename = getGlobalLevelTemplateFilename();
3552 if (!(file = openFile(filename, MODE_READ)))
3555 // default: for empty levels, use level template for custom elements
3556 level->use_custom_template = TRUE;
3558 level->no_valid_file = FALSE;
3561 getFileChunkBE(file, chunk_name, NULL);
3562 if (strEqual(chunk_name, "RND1"))
3564 getFile32BitBE(file); // not used
3566 getFileChunkBE(file, chunk_name, NULL);
3567 if (!strEqual(chunk_name, "CAVE"))
3569 level->no_valid_file = TRUE;
3571 Warn("unknown format of level file '%s'", filename);
3578 else // check for pre-2.0 file format with cookie string
3580 strcpy(cookie, chunk_name);
3581 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3583 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3584 cookie[strlen(cookie) - 1] = '\0';
3586 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3588 level->no_valid_file = TRUE;
3590 Warn("unknown format of level file '%s'", filename);
3597 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3599 level->no_valid_file = TRUE;
3601 Warn("unsupported version of level file '%s'", filename);
3608 // pre-2.0 level files have no game version, so use file version here
3609 level->game_version = level->file_version;
3612 if (level->file_version < FILE_VERSION_1_2)
3614 // level files from versions before 1.2.0 without chunk structure
3615 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3616 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3624 int (*loader)(File *, int, struct LevelInfo *);
3628 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3629 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3630 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3631 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3632 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3633 { "INFO", -1, LoadLevel_INFO },
3634 { "BODY", -1, LoadLevel_BODY },
3635 { "CONT", -1, LoadLevel_CONT },
3636 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3637 { "CNT3", -1, LoadLevel_CNT3 },
3638 { "CUS1", -1, LoadLevel_CUS1 },
3639 { "CUS2", -1, LoadLevel_CUS2 },
3640 { "CUS3", -1, LoadLevel_CUS3 },
3641 { "CUS4", -1, LoadLevel_CUS4 },
3642 { "GRP1", -1, LoadLevel_GRP1 },
3643 { "CONF", -1, LoadLevel_CONF },
3644 { "ELEM", -1, LoadLevel_ELEM },
3645 { "NOTE", -1, LoadLevel_NOTE },
3646 { "CUSX", -1, LoadLevel_CUSX },
3647 { "GRPX", -1, LoadLevel_GRPX },
3648 { "EMPX", -1, LoadLevel_EMPX },
3653 while (getFileChunkBE(file, chunk_name, &chunk_size))
3657 while (chunk_info[i].name != NULL &&
3658 !strEqual(chunk_name, chunk_info[i].name))
3661 if (chunk_info[i].name == NULL)
3663 Warn("unknown chunk '%s' in level file '%s'",
3664 chunk_name, filename);
3666 ReadUnusedBytesFromFile(file, chunk_size);
3668 else if (chunk_info[i].size != -1 &&
3669 chunk_info[i].size != chunk_size)
3671 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3672 chunk_size, chunk_name, filename);
3674 ReadUnusedBytesFromFile(file, chunk_size);
3678 // call function to load this level chunk
3679 int chunk_size_expected =
3680 (chunk_info[i].loader)(file, chunk_size, level);
3682 if (chunk_size_expected < 0)
3684 Warn("error reading chunk '%s' in level file '%s'",
3685 chunk_name, filename);
3690 // the size of some chunks cannot be checked before reading other
3691 // chunks first (like "HEAD" and "BODY") that contain some header
3692 // information, so check them here
3693 if (chunk_size_expected != chunk_size)
3695 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3696 chunk_size, chunk_name, filename);
3708 // ----------------------------------------------------------------------------
3709 // functions for loading BD level
3710 // ----------------------------------------------------------------------------
3712 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3714 struct LevelInfo_BD *level_bd = level->native_bd_level;
3715 GdCave *cave = NULL; // will be changed below
3716 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3717 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3720 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3722 // cave and map newly allocated when set to defaults above
3723 cave = level_bd->cave;
3725 for (i = 0; i < 5; i++)
3727 cave->level_time[i] = level->time;
3728 cave->level_diamonds[i] = level->gems_needed;
3729 cave->level_magic_wall_time[i] = level->time_magic_wall;
3730 cave->level_timevalue[i] = level->score[SC_TIME_BONUS];
3733 cave->diamond_value = level->score[SC_EMERALD];
3734 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3736 cave->level_speed[0] = 160; // set cave speed
3738 cave->scheduling = level->bd_scheduling_type;
3739 cave->pal_timing = level->bd_pal_timing;
3740 cave->intermission = level->bd_intermission;
3741 cave->diagonal_movements = level->bd_diagonal_movements;
3743 strncpy(cave->name, level->name, sizeof(GdString));
3744 cave->name[sizeof(GdString) - 1] = '\0';
3746 for (x = 0; x < cave->w; x++)
3747 for (y = 0; y < cave->h; y++)
3748 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3751 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3753 struct LevelInfo_BD *level_bd = level->native_bd_level;
3754 GdCave *cave = level_bd->cave;
3755 int bd_level_nr = level_bd->level_nr;
3758 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3759 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3761 level->time = cave->level_time[bd_level_nr];
3762 level->gems_needed = cave->level_diamonds[bd_level_nr];
3763 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3765 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3766 level->score[SC_EMERALD] = cave->diamond_value;
3767 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
3769 level->bd_scheduling_type = cave->scheduling;
3770 level->bd_pal_timing = cave->pal_timing;
3771 level->bd_intermission = cave->intermission;
3772 level->bd_diagonal_movements = cave->diagonal_movements;
3774 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
3776 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
3777 level->name[MAX_LEVEL_NAME_LEN] = '\0';
3779 for (x = 0; x < level->fieldx; x++)
3780 for (y = 0; y < level->fieldy; y++)
3781 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3783 checked_free(cave_name);
3786 static void setTapeInfoToDefaults(void);
3788 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
3790 struct LevelInfo_BD *level_bd = level->native_bd_level;
3791 GdCave *cave = level_bd->cave;
3792 GdReplay *replay = level_bd->replay;
3798 // always start with reliable default values
3799 setTapeInfoToDefaults();
3801 tape.level_nr = level_nr; // (currently not used)
3802 tape.random_seed = replay->seed;
3804 TapeSetDateFromIsoDateString(replay->date);
3807 tape.pos[tape.counter].delay = 0;
3809 tape.bd_replay = TRUE;
3811 // all time calculations only used to display approximate tape time
3812 int cave_speed = cave->speed;
3813 int milliseconds_game = 0;
3814 int milliseconds_elapsed = 20;
3816 for (i = 0; i < replay->movements->len; i++)
3818 int replay_action = replay->movements->data[i];
3819 int tape_action = map_action_BD_to_RND(replay_action);
3820 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3821 boolean success = 0;
3825 success = TapeAddAction(action);
3827 milliseconds_game += milliseconds_elapsed;
3829 if (milliseconds_game >= cave_speed)
3831 milliseconds_game -= cave_speed;
3838 tape.pos[tape.counter].delay = 0;
3839 tape.pos[tape.counter].action[0] = 0;
3843 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
3849 TapeHaltRecording();
3853 // ----------------------------------------------------------------------------
3854 // functions for loading EM level
3855 // ----------------------------------------------------------------------------
3857 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3859 static int ball_xy[8][2] =
3870 struct LevelInfo_EM *level_em = level->native_em_level;
3871 struct CAVE *cav = level_em->cav;
3874 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3875 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3877 cav->time_seconds = level->time;
3878 cav->gems_needed = level->gems_needed;
3880 cav->emerald_score = level->score[SC_EMERALD];
3881 cav->diamond_score = level->score[SC_DIAMOND];
3882 cav->alien_score = level->score[SC_ROBOT];
3883 cav->tank_score = level->score[SC_SPACESHIP];
3884 cav->bug_score = level->score[SC_BUG];
3885 cav->eater_score = level->score[SC_YAMYAM];
3886 cav->nut_score = level->score[SC_NUT];
3887 cav->dynamite_score = level->score[SC_DYNAMITE];
3888 cav->key_score = level->score[SC_KEY];
3889 cav->exit_score = level->score[SC_TIME_BONUS];
3891 cav->num_eater_arrays = level->num_yamyam_contents;
3893 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3894 for (y = 0; y < 3; y++)
3895 for (x = 0; x < 3; x++)
3896 cav->eater_array[i][y * 3 + x] =
3897 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3899 cav->amoeba_time = level->amoeba_speed;
3900 cav->wonderwall_time = level->time_magic_wall;
3901 cav->wheel_time = level->time_wheel;
3903 cav->android_move_time = level->android_move_time;
3904 cav->android_clone_time = level->android_clone_time;
3905 cav->ball_random = level->ball_random;
3906 cav->ball_active = level->ball_active_initial;
3907 cav->ball_time = level->ball_time;
3908 cav->num_ball_arrays = level->num_ball_contents;
3910 cav->lenses_score = level->lenses_score;
3911 cav->magnify_score = level->magnify_score;
3912 cav->slurp_score = level->slurp_score;
3914 cav->lenses_time = level->lenses_time;
3915 cav->magnify_time = level->magnify_time;
3917 cav->wind_time = 9999;
3918 cav->wind_direction =
3919 map_direction_RND_to_EM(level->wind_direction_initial);
3921 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3922 for (j = 0; j < 8; j++)
3923 cav->ball_array[i][j] =
3924 map_element_RND_to_EM_cave(level->ball_content[i].
3925 e[ball_xy[j][0]][ball_xy[j][1]]);
3927 map_android_clone_elements_RND_to_EM(level);
3929 // first fill the complete playfield with the empty space element
3930 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3931 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3932 cav->cave[x][y] = Cblank;
3934 // then copy the real level contents from level file into the playfield
3935 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3937 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3939 if (level->field[x][y] == EL_AMOEBA_DEAD)
3940 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3942 cav->cave[x][y] = new_element;
3945 for (i = 0; i < MAX_PLAYERS; i++)
3947 cav->player_x[i] = -1;
3948 cav->player_y[i] = -1;
3951 // initialize player positions and delete players from the playfield
3952 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3954 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3956 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3958 cav->player_x[player_nr] = x;
3959 cav->player_y[player_nr] = y;
3961 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3966 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3968 static int ball_xy[8][2] =
3979 struct LevelInfo_EM *level_em = level->native_em_level;
3980 struct CAVE *cav = level_em->cav;
3983 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3984 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3986 level->time = cav->time_seconds;
3987 level->gems_needed = cav->gems_needed;
3989 sprintf(level->name, "Level %d", level->file_info.nr);
3991 level->score[SC_EMERALD] = cav->emerald_score;
3992 level->score[SC_DIAMOND] = cav->diamond_score;
3993 level->score[SC_ROBOT] = cav->alien_score;
3994 level->score[SC_SPACESHIP] = cav->tank_score;
3995 level->score[SC_BUG] = cav->bug_score;
3996 level->score[SC_YAMYAM] = cav->eater_score;
3997 level->score[SC_NUT] = cav->nut_score;
3998 level->score[SC_DYNAMITE] = cav->dynamite_score;
3999 level->score[SC_KEY] = cav->key_score;
4000 level->score[SC_TIME_BONUS] = cav->exit_score;
4002 level->num_yamyam_contents = cav->num_eater_arrays;
4004 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4005 for (y = 0; y < 3; y++)
4006 for (x = 0; x < 3; x++)
4007 level->yamyam_content[i].e[x][y] =
4008 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4010 level->amoeba_speed = cav->amoeba_time;
4011 level->time_magic_wall = cav->wonderwall_time;
4012 level->time_wheel = cav->wheel_time;
4014 level->android_move_time = cav->android_move_time;
4015 level->android_clone_time = cav->android_clone_time;
4016 level->ball_random = cav->ball_random;
4017 level->ball_active_initial = cav->ball_active;
4018 level->ball_time = cav->ball_time;
4019 level->num_ball_contents = cav->num_ball_arrays;
4021 level->lenses_score = cav->lenses_score;
4022 level->magnify_score = cav->magnify_score;
4023 level->slurp_score = cav->slurp_score;
4025 level->lenses_time = cav->lenses_time;
4026 level->magnify_time = cav->magnify_time;
4028 level->wind_direction_initial =
4029 map_direction_EM_to_RND(cav->wind_direction);
4031 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4032 for (j = 0; j < 8; j++)
4033 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4034 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4036 map_android_clone_elements_EM_to_RND(level);
4038 // convert the playfield (some elements need special treatment)
4039 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4041 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4043 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4044 new_element = EL_AMOEBA_DEAD;
4046 level->field[x][y] = new_element;
4049 for (i = 0; i < MAX_PLAYERS; i++)
4051 // in case of all players set to the same field, use the first player
4052 int nr = MAX_PLAYERS - i - 1;
4053 int jx = cav->player_x[nr];
4054 int jy = cav->player_y[nr];
4056 if (jx != -1 && jy != -1)
4057 level->field[jx][jy] = EL_PLAYER_1 + nr;
4060 // time score is counted for each 10 seconds left in Emerald Mine levels
4061 level->time_score_base = 10;
4065 // ----------------------------------------------------------------------------
4066 // functions for loading SP level
4067 // ----------------------------------------------------------------------------
4069 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4071 struct LevelInfo_SP *level_sp = level->native_sp_level;
4072 LevelInfoType *header = &level_sp->header;
4075 level_sp->width = level->fieldx;
4076 level_sp->height = level->fieldy;
4078 for (x = 0; x < level->fieldx; x++)
4079 for (y = 0; y < level->fieldy; y++)
4080 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4082 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4084 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4085 header->LevelTitle[i] = level->name[i];
4086 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4088 header->InfotronsNeeded = level->gems_needed;
4090 header->SpecialPortCount = 0;
4092 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4094 boolean gravity_port_found = FALSE;
4095 boolean gravity_port_valid = FALSE;
4096 int gravity_port_flag;
4097 int gravity_port_base_element;
4098 int element = level->field[x][y];
4100 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4101 element <= EL_SP_GRAVITY_ON_PORT_UP)
4103 gravity_port_found = TRUE;
4104 gravity_port_valid = TRUE;
4105 gravity_port_flag = 1;
4106 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4108 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4109 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4111 gravity_port_found = TRUE;
4112 gravity_port_valid = TRUE;
4113 gravity_port_flag = 0;
4114 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4116 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4117 element <= EL_SP_GRAVITY_PORT_UP)
4119 // change R'n'D style gravity inverting special port to normal port
4120 // (there are no gravity inverting ports in native Supaplex engine)
4122 gravity_port_found = TRUE;
4123 gravity_port_valid = FALSE;
4124 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4127 if (gravity_port_found)
4129 if (gravity_port_valid &&
4130 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4132 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4134 port->PortLocation = (y * level->fieldx + x) * 2;
4135 port->Gravity = gravity_port_flag;
4137 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4139 header->SpecialPortCount++;
4143 // change special gravity port to normal port
4145 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4148 level_sp->playfield[x][y] = element - EL_SP_START;
4153 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4155 struct LevelInfo_SP *level_sp = level->native_sp_level;
4156 LevelInfoType *header = &level_sp->header;
4157 boolean num_invalid_elements = 0;
4160 level->fieldx = level_sp->width;
4161 level->fieldy = level_sp->height;
4163 for (x = 0; x < level->fieldx; x++)
4165 for (y = 0; y < level->fieldy; y++)
4167 int element_old = level_sp->playfield[x][y];
4168 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4170 if (element_new == EL_UNKNOWN)
4172 num_invalid_elements++;
4174 Debug("level:native:SP", "invalid element %d at position %d, %d",
4178 level->field[x][y] = element_new;
4182 if (num_invalid_elements > 0)
4183 Warn("found %d invalid elements%s", num_invalid_elements,
4184 (!options.debug ? " (use '--debug' for more details)" : ""));
4186 for (i = 0; i < MAX_PLAYERS; i++)
4187 level->initial_player_gravity[i] =
4188 (header->InitialGravity == 1 ? TRUE : FALSE);
4190 // skip leading spaces
4191 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4192 if (header->LevelTitle[i] != ' ')
4196 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4197 level->name[j] = header->LevelTitle[i];
4198 level->name[j] = '\0';
4200 // cut trailing spaces
4202 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4203 level->name[j - 1] = '\0';
4205 level->gems_needed = header->InfotronsNeeded;
4207 for (i = 0; i < header->SpecialPortCount; i++)
4209 SpecialPortType *port = &header->SpecialPort[i];
4210 int port_location = port->PortLocation;
4211 int gravity = port->Gravity;
4212 int port_x, port_y, port_element;
4214 port_x = (port_location / 2) % level->fieldx;
4215 port_y = (port_location / 2) / level->fieldx;
4217 if (port_x < 0 || port_x >= level->fieldx ||
4218 port_y < 0 || port_y >= level->fieldy)
4220 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4225 port_element = level->field[port_x][port_y];
4227 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4228 port_element > EL_SP_GRAVITY_PORT_UP)
4230 Warn("no special port at position (%d, %d)", port_x, port_y);
4235 // change previous (wrong) gravity inverting special port to either
4236 // gravity enabling special port or gravity disabling special port
4237 level->field[port_x][port_y] +=
4238 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4239 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4242 // change special gravity ports without database entries to normal ports
4243 for (x = 0; x < level->fieldx; x++)
4244 for (y = 0; y < level->fieldy; y++)
4245 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4246 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4247 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4249 level->time = 0; // no time limit
4250 level->amoeba_speed = 0;
4251 level->time_magic_wall = 0;
4252 level->time_wheel = 0;
4253 level->amoeba_content = EL_EMPTY;
4255 // original Supaplex does not use score values -- rate by playing time
4256 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4257 level->score[i] = 0;
4259 level->rate_time_over_score = TRUE;
4261 // there are no yamyams in supaplex levels
4262 for (i = 0; i < level->num_yamyam_contents; i++)
4263 for (x = 0; x < 3; x++)
4264 for (y = 0; y < 3; y++)
4265 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4268 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4270 struct LevelInfo_SP *level_sp = level->native_sp_level;
4271 struct DemoInfo_SP *demo = &level_sp->demo;
4274 // always start with reliable default values
4275 demo->is_available = FALSE;
4278 if (TAPE_IS_EMPTY(tape))
4281 demo->level_nr = tape.level_nr; // (currently not used)
4283 level_sp->header.DemoRandomSeed = tape.random_seed;
4287 for (i = 0; i < tape.length; i++)
4289 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4290 int demo_repeat = tape.pos[i].delay;
4291 int demo_entries = (demo_repeat + 15) / 16;
4293 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4295 Warn("tape truncated: size exceeds maximum SP demo size %d",
4301 for (j = 0; j < demo_repeat / 16; j++)
4302 demo->data[demo->length++] = 0xf0 | demo_action;
4304 if (demo_repeat % 16)
4305 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4308 demo->is_available = TRUE;
4311 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4313 struct LevelInfo_SP *level_sp = level->native_sp_level;
4314 struct DemoInfo_SP *demo = &level_sp->demo;
4315 char *filename = level->file_info.filename;
4318 // always start with reliable default values
4319 setTapeInfoToDefaults();
4321 if (!demo->is_available)
4324 tape.level_nr = demo->level_nr; // (currently not used)
4325 tape.random_seed = level_sp->header.DemoRandomSeed;
4327 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4330 tape.pos[tape.counter].delay = 0;
4332 for (i = 0; i < demo->length; i++)
4334 int demo_action = demo->data[i] & 0x0f;
4335 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4336 int tape_action = map_key_SP_to_RND(demo_action);
4337 int tape_repeat = demo_repeat + 1;
4338 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4339 boolean success = 0;
4342 for (j = 0; j < tape_repeat; j++)
4343 success = TapeAddAction(action);
4347 Warn("SP demo truncated: size exceeds maximum tape size %d",
4354 TapeHaltRecording();
4358 // ----------------------------------------------------------------------------
4359 // functions for loading MM level
4360 // ----------------------------------------------------------------------------
4362 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4364 struct LevelInfo_MM *level_mm = level->native_mm_level;
4367 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4368 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4370 level_mm->time = level->time;
4371 level_mm->kettles_needed = level->gems_needed;
4372 level_mm->auto_count_kettles = level->auto_count_gems;
4374 level_mm->mm_laser_red = level->mm_laser_red;
4375 level_mm->mm_laser_green = level->mm_laser_green;
4376 level_mm->mm_laser_blue = level->mm_laser_blue;
4378 level_mm->df_laser_red = level->df_laser_red;
4379 level_mm->df_laser_green = level->df_laser_green;
4380 level_mm->df_laser_blue = level->df_laser_blue;
4382 strcpy(level_mm->name, level->name);
4383 strcpy(level_mm->author, level->author);
4385 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4386 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4387 level_mm->score[SC_KEY] = level->score[SC_KEY];
4388 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4389 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4391 level_mm->amoeba_speed = level->amoeba_speed;
4392 level_mm->time_fuse = level->mm_time_fuse;
4393 level_mm->time_bomb = level->mm_time_bomb;
4394 level_mm->time_ball = level->mm_time_ball;
4395 level_mm->time_block = level->mm_time_block;
4397 level_mm->num_ball_contents = level->num_mm_ball_contents;
4398 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4399 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4400 level_mm->explode_ball = level->explode_mm_ball;
4402 for (i = 0; i < level->num_mm_ball_contents; i++)
4403 level_mm->ball_content[i] =
4404 map_element_RND_to_MM(level->mm_ball_content[i]);
4406 for (x = 0; x < level->fieldx; x++)
4407 for (y = 0; y < level->fieldy; y++)
4409 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4412 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4414 struct LevelInfo_MM *level_mm = level->native_mm_level;
4417 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4418 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4420 level->time = level_mm->time;
4421 level->gems_needed = level_mm->kettles_needed;
4422 level->auto_count_gems = level_mm->auto_count_kettles;
4424 level->mm_laser_red = level_mm->mm_laser_red;
4425 level->mm_laser_green = level_mm->mm_laser_green;
4426 level->mm_laser_blue = level_mm->mm_laser_blue;
4428 level->df_laser_red = level_mm->df_laser_red;
4429 level->df_laser_green = level_mm->df_laser_green;
4430 level->df_laser_blue = level_mm->df_laser_blue;
4432 strcpy(level->name, level_mm->name);
4434 // only overwrite author from 'levelinfo.conf' if author defined in level
4435 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4436 strcpy(level->author, level_mm->author);
4438 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4439 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4440 level->score[SC_KEY] = level_mm->score[SC_KEY];
4441 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4442 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4444 level->amoeba_speed = level_mm->amoeba_speed;
4445 level->mm_time_fuse = level_mm->time_fuse;
4446 level->mm_time_bomb = level_mm->time_bomb;
4447 level->mm_time_ball = level_mm->time_ball;
4448 level->mm_time_block = level_mm->time_block;
4450 level->num_mm_ball_contents = level_mm->num_ball_contents;
4451 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4452 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4453 level->explode_mm_ball = level_mm->explode_ball;
4455 for (i = 0; i < level->num_mm_ball_contents; i++)
4456 level->mm_ball_content[i] =
4457 map_element_MM_to_RND(level_mm->ball_content[i]);
4459 for (x = 0; x < level->fieldx; x++)
4460 for (y = 0; y < level->fieldy; y++)
4461 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4465 // ----------------------------------------------------------------------------
4466 // functions for loading DC level
4467 // ----------------------------------------------------------------------------
4469 #define DC_LEVEL_HEADER_SIZE 344
4471 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4474 static int last_data_encoded;
4478 int diff_hi, diff_lo;
4479 int data_hi, data_lo;
4480 unsigned short data_decoded;
4484 last_data_encoded = 0;
4491 diff = data_encoded - last_data_encoded;
4492 diff_hi = diff & ~0xff;
4493 diff_lo = diff & 0xff;
4497 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4498 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4499 data_hi = data_hi & 0xff00;
4501 data_decoded = data_hi | data_lo;
4503 last_data_encoded = data_encoded;
4505 offset1 = (offset1 + 1) % 31;
4506 offset2 = offset2 & 0xff;
4508 return data_decoded;
4511 static int getMappedElement_DC(int element)
4519 // 0x0117 - 0x036e: (?)
4522 // 0x042d - 0x0684: (?)
4538 element = EL_CRYSTAL;
4541 case 0x0e77: // quicksand (boulder)
4542 element = EL_QUICKSAND_FAST_FULL;
4545 case 0x0e99: // slow quicksand (boulder)
4546 element = EL_QUICKSAND_FULL;
4550 element = EL_EM_EXIT_OPEN;
4554 element = EL_EM_EXIT_CLOSED;
4558 element = EL_EM_STEEL_EXIT_OPEN;
4562 element = EL_EM_STEEL_EXIT_CLOSED;
4565 case 0x0f4f: // dynamite (lit 1)
4566 element = EL_EM_DYNAMITE_ACTIVE;
4569 case 0x0f57: // dynamite (lit 2)
4570 element = EL_EM_DYNAMITE_ACTIVE;
4573 case 0x0f5f: // dynamite (lit 3)
4574 element = EL_EM_DYNAMITE_ACTIVE;
4577 case 0x0f67: // dynamite (lit 4)
4578 element = EL_EM_DYNAMITE_ACTIVE;
4585 element = EL_AMOEBA_WET;
4589 element = EL_AMOEBA_DROP;
4593 element = EL_DC_MAGIC_WALL;
4597 element = EL_SPACESHIP_UP;
4601 element = EL_SPACESHIP_DOWN;
4605 element = EL_SPACESHIP_LEFT;
4609 element = EL_SPACESHIP_RIGHT;
4613 element = EL_BUG_UP;
4617 element = EL_BUG_DOWN;
4621 element = EL_BUG_LEFT;
4625 element = EL_BUG_RIGHT;
4629 element = EL_MOLE_UP;
4633 element = EL_MOLE_DOWN;
4637 element = EL_MOLE_LEFT;
4641 element = EL_MOLE_RIGHT;
4649 element = EL_YAMYAM_UP;
4653 element = EL_SWITCHGATE_OPEN;
4657 element = EL_SWITCHGATE_CLOSED;
4661 element = EL_DC_SWITCHGATE_SWITCH_UP;
4665 element = EL_TIMEGATE_CLOSED;
4668 case 0x144c: // conveyor belt switch (green)
4669 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4672 case 0x144f: // conveyor belt switch (red)
4673 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4676 case 0x1452: // conveyor belt switch (blue)
4677 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4681 element = EL_CONVEYOR_BELT_3_MIDDLE;
4685 element = EL_CONVEYOR_BELT_3_LEFT;
4689 element = EL_CONVEYOR_BELT_3_RIGHT;
4693 element = EL_CONVEYOR_BELT_1_MIDDLE;
4697 element = EL_CONVEYOR_BELT_1_LEFT;
4701 element = EL_CONVEYOR_BELT_1_RIGHT;
4705 element = EL_CONVEYOR_BELT_4_MIDDLE;
4709 element = EL_CONVEYOR_BELT_4_LEFT;
4713 element = EL_CONVEYOR_BELT_4_RIGHT;
4717 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4721 element = EL_EXPANDABLE_WALL_VERTICAL;
4725 element = EL_EXPANDABLE_WALL_ANY;
4728 case 0x14ce: // growing steel wall (left/right)
4729 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4732 case 0x14df: // growing steel wall (up/down)
4733 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4736 case 0x14e8: // growing steel wall (up/down/left/right)
4737 element = EL_EXPANDABLE_STEELWALL_ANY;
4741 element = EL_SHIELD_DEADLY;
4745 element = EL_EXTRA_TIME;
4753 element = EL_EMPTY_SPACE;
4756 case 0x1578: // quicksand (empty)
4757 element = EL_QUICKSAND_FAST_EMPTY;
4760 case 0x1579: // slow quicksand (empty)
4761 element = EL_QUICKSAND_EMPTY;
4771 element = EL_EM_DYNAMITE;
4774 case 0x15a1: // key (red)
4775 element = EL_EM_KEY_1;
4778 case 0x15a2: // key (yellow)
4779 element = EL_EM_KEY_2;
4782 case 0x15a3: // key (blue)
4783 element = EL_EM_KEY_4;
4786 case 0x15a4: // key (green)
4787 element = EL_EM_KEY_3;
4790 case 0x15a5: // key (white)
4791 element = EL_DC_KEY_WHITE;
4795 element = EL_WALL_SLIPPERY;
4802 case 0x15a8: // wall (not round)
4806 case 0x15a9: // (blue)
4807 element = EL_CHAR_A;
4810 case 0x15aa: // (blue)
4811 element = EL_CHAR_B;
4814 case 0x15ab: // (blue)
4815 element = EL_CHAR_C;
4818 case 0x15ac: // (blue)
4819 element = EL_CHAR_D;
4822 case 0x15ad: // (blue)
4823 element = EL_CHAR_E;
4826 case 0x15ae: // (blue)
4827 element = EL_CHAR_F;
4830 case 0x15af: // (blue)
4831 element = EL_CHAR_G;
4834 case 0x15b0: // (blue)
4835 element = EL_CHAR_H;
4838 case 0x15b1: // (blue)
4839 element = EL_CHAR_I;
4842 case 0x15b2: // (blue)
4843 element = EL_CHAR_J;
4846 case 0x15b3: // (blue)
4847 element = EL_CHAR_K;
4850 case 0x15b4: // (blue)
4851 element = EL_CHAR_L;
4854 case 0x15b5: // (blue)
4855 element = EL_CHAR_M;
4858 case 0x15b6: // (blue)
4859 element = EL_CHAR_N;
4862 case 0x15b7: // (blue)
4863 element = EL_CHAR_O;
4866 case 0x15b8: // (blue)
4867 element = EL_CHAR_P;
4870 case 0x15b9: // (blue)
4871 element = EL_CHAR_Q;
4874 case 0x15ba: // (blue)
4875 element = EL_CHAR_R;
4878 case 0x15bb: // (blue)
4879 element = EL_CHAR_S;
4882 case 0x15bc: // (blue)
4883 element = EL_CHAR_T;
4886 case 0x15bd: // (blue)
4887 element = EL_CHAR_U;
4890 case 0x15be: // (blue)
4891 element = EL_CHAR_V;
4894 case 0x15bf: // (blue)
4895 element = EL_CHAR_W;
4898 case 0x15c0: // (blue)
4899 element = EL_CHAR_X;
4902 case 0x15c1: // (blue)
4903 element = EL_CHAR_Y;
4906 case 0x15c2: // (blue)
4907 element = EL_CHAR_Z;
4910 case 0x15c3: // (blue)
4911 element = EL_CHAR_AUMLAUT;
4914 case 0x15c4: // (blue)
4915 element = EL_CHAR_OUMLAUT;
4918 case 0x15c5: // (blue)
4919 element = EL_CHAR_UUMLAUT;
4922 case 0x15c6: // (blue)
4923 element = EL_CHAR_0;
4926 case 0x15c7: // (blue)
4927 element = EL_CHAR_1;
4930 case 0x15c8: // (blue)
4931 element = EL_CHAR_2;
4934 case 0x15c9: // (blue)
4935 element = EL_CHAR_3;
4938 case 0x15ca: // (blue)
4939 element = EL_CHAR_4;
4942 case 0x15cb: // (blue)
4943 element = EL_CHAR_5;
4946 case 0x15cc: // (blue)
4947 element = EL_CHAR_6;
4950 case 0x15cd: // (blue)
4951 element = EL_CHAR_7;
4954 case 0x15ce: // (blue)
4955 element = EL_CHAR_8;
4958 case 0x15cf: // (blue)
4959 element = EL_CHAR_9;
4962 case 0x15d0: // (blue)
4963 element = EL_CHAR_PERIOD;
4966 case 0x15d1: // (blue)
4967 element = EL_CHAR_EXCLAM;
4970 case 0x15d2: // (blue)
4971 element = EL_CHAR_COLON;
4974 case 0x15d3: // (blue)
4975 element = EL_CHAR_LESS;
4978 case 0x15d4: // (blue)
4979 element = EL_CHAR_GREATER;
4982 case 0x15d5: // (blue)
4983 element = EL_CHAR_QUESTION;
4986 case 0x15d6: // (blue)
4987 element = EL_CHAR_COPYRIGHT;
4990 case 0x15d7: // (blue)
4991 element = EL_CHAR_UP;
4994 case 0x15d8: // (blue)
4995 element = EL_CHAR_DOWN;
4998 case 0x15d9: // (blue)
4999 element = EL_CHAR_BUTTON;
5002 case 0x15da: // (blue)
5003 element = EL_CHAR_PLUS;
5006 case 0x15db: // (blue)
5007 element = EL_CHAR_MINUS;
5010 case 0x15dc: // (blue)
5011 element = EL_CHAR_APOSTROPHE;
5014 case 0x15dd: // (blue)
5015 element = EL_CHAR_PARENLEFT;
5018 case 0x15de: // (blue)
5019 element = EL_CHAR_PARENRIGHT;
5022 case 0x15df: // (green)
5023 element = EL_CHAR_A;
5026 case 0x15e0: // (green)
5027 element = EL_CHAR_B;
5030 case 0x15e1: // (green)
5031 element = EL_CHAR_C;
5034 case 0x15e2: // (green)
5035 element = EL_CHAR_D;
5038 case 0x15e3: // (green)
5039 element = EL_CHAR_E;
5042 case 0x15e4: // (green)
5043 element = EL_CHAR_F;
5046 case 0x15e5: // (green)
5047 element = EL_CHAR_G;
5050 case 0x15e6: // (green)
5051 element = EL_CHAR_H;
5054 case 0x15e7: // (green)
5055 element = EL_CHAR_I;
5058 case 0x15e8: // (green)
5059 element = EL_CHAR_J;
5062 case 0x15e9: // (green)
5063 element = EL_CHAR_K;
5066 case 0x15ea: // (green)
5067 element = EL_CHAR_L;
5070 case 0x15eb: // (green)
5071 element = EL_CHAR_M;
5074 case 0x15ec: // (green)
5075 element = EL_CHAR_N;
5078 case 0x15ed: // (green)
5079 element = EL_CHAR_O;
5082 case 0x15ee: // (green)
5083 element = EL_CHAR_P;
5086 case 0x15ef: // (green)
5087 element = EL_CHAR_Q;
5090 case 0x15f0: // (green)
5091 element = EL_CHAR_R;
5094 case 0x15f1: // (green)
5095 element = EL_CHAR_S;
5098 case 0x15f2: // (green)
5099 element = EL_CHAR_T;
5102 case 0x15f3: // (green)
5103 element = EL_CHAR_U;
5106 case 0x15f4: // (green)
5107 element = EL_CHAR_V;
5110 case 0x15f5: // (green)
5111 element = EL_CHAR_W;
5114 case 0x15f6: // (green)
5115 element = EL_CHAR_X;
5118 case 0x15f7: // (green)
5119 element = EL_CHAR_Y;
5122 case 0x15f8: // (green)
5123 element = EL_CHAR_Z;
5126 case 0x15f9: // (green)
5127 element = EL_CHAR_AUMLAUT;
5130 case 0x15fa: // (green)
5131 element = EL_CHAR_OUMLAUT;
5134 case 0x15fb: // (green)
5135 element = EL_CHAR_UUMLAUT;
5138 case 0x15fc: // (green)
5139 element = EL_CHAR_0;
5142 case 0x15fd: // (green)
5143 element = EL_CHAR_1;
5146 case 0x15fe: // (green)
5147 element = EL_CHAR_2;
5150 case 0x15ff: // (green)
5151 element = EL_CHAR_3;
5154 case 0x1600: // (green)
5155 element = EL_CHAR_4;
5158 case 0x1601: // (green)
5159 element = EL_CHAR_5;
5162 case 0x1602: // (green)
5163 element = EL_CHAR_6;
5166 case 0x1603: // (green)
5167 element = EL_CHAR_7;
5170 case 0x1604: // (green)
5171 element = EL_CHAR_8;
5174 case 0x1605: // (green)
5175 element = EL_CHAR_9;
5178 case 0x1606: // (green)
5179 element = EL_CHAR_PERIOD;
5182 case 0x1607: // (green)
5183 element = EL_CHAR_EXCLAM;
5186 case 0x1608: // (green)
5187 element = EL_CHAR_COLON;
5190 case 0x1609: // (green)
5191 element = EL_CHAR_LESS;
5194 case 0x160a: // (green)
5195 element = EL_CHAR_GREATER;
5198 case 0x160b: // (green)
5199 element = EL_CHAR_QUESTION;
5202 case 0x160c: // (green)
5203 element = EL_CHAR_COPYRIGHT;
5206 case 0x160d: // (green)
5207 element = EL_CHAR_UP;
5210 case 0x160e: // (green)
5211 element = EL_CHAR_DOWN;
5214 case 0x160f: // (green)
5215 element = EL_CHAR_BUTTON;
5218 case 0x1610: // (green)
5219 element = EL_CHAR_PLUS;
5222 case 0x1611: // (green)
5223 element = EL_CHAR_MINUS;
5226 case 0x1612: // (green)
5227 element = EL_CHAR_APOSTROPHE;
5230 case 0x1613: // (green)
5231 element = EL_CHAR_PARENLEFT;
5234 case 0x1614: // (green)
5235 element = EL_CHAR_PARENRIGHT;
5238 case 0x1615: // (blue steel)
5239 element = EL_STEEL_CHAR_A;
5242 case 0x1616: // (blue steel)
5243 element = EL_STEEL_CHAR_B;
5246 case 0x1617: // (blue steel)
5247 element = EL_STEEL_CHAR_C;
5250 case 0x1618: // (blue steel)
5251 element = EL_STEEL_CHAR_D;
5254 case 0x1619: // (blue steel)
5255 element = EL_STEEL_CHAR_E;
5258 case 0x161a: // (blue steel)
5259 element = EL_STEEL_CHAR_F;
5262 case 0x161b: // (blue steel)
5263 element = EL_STEEL_CHAR_G;
5266 case 0x161c: // (blue steel)
5267 element = EL_STEEL_CHAR_H;
5270 case 0x161d: // (blue steel)
5271 element = EL_STEEL_CHAR_I;
5274 case 0x161e: // (blue steel)
5275 element = EL_STEEL_CHAR_J;
5278 case 0x161f: // (blue steel)
5279 element = EL_STEEL_CHAR_K;
5282 case 0x1620: // (blue steel)
5283 element = EL_STEEL_CHAR_L;
5286 case 0x1621: // (blue steel)
5287 element = EL_STEEL_CHAR_M;
5290 case 0x1622: // (blue steel)
5291 element = EL_STEEL_CHAR_N;
5294 case 0x1623: // (blue steel)
5295 element = EL_STEEL_CHAR_O;
5298 case 0x1624: // (blue steel)
5299 element = EL_STEEL_CHAR_P;
5302 case 0x1625: // (blue steel)
5303 element = EL_STEEL_CHAR_Q;
5306 case 0x1626: // (blue steel)
5307 element = EL_STEEL_CHAR_R;
5310 case 0x1627: // (blue steel)
5311 element = EL_STEEL_CHAR_S;
5314 case 0x1628: // (blue steel)
5315 element = EL_STEEL_CHAR_T;
5318 case 0x1629: // (blue steel)
5319 element = EL_STEEL_CHAR_U;
5322 case 0x162a: // (blue steel)
5323 element = EL_STEEL_CHAR_V;
5326 case 0x162b: // (blue steel)
5327 element = EL_STEEL_CHAR_W;
5330 case 0x162c: // (blue steel)
5331 element = EL_STEEL_CHAR_X;
5334 case 0x162d: // (blue steel)
5335 element = EL_STEEL_CHAR_Y;
5338 case 0x162e: // (blue steel)
5339 element = EL_STEEL_CHAR_Z;
5342 case 0x162f: // (blue steel)
5343 element = EL_STEEL_CHAR_AUMLAUT;
5346 case 0x1630: // (blue steel)
5347 element = EL_STEEL_CHAR_OUMLAUT;
5350 case 0x1631: // (blue steel)
5351 element = EL_STEEL_CHAR_UUMLAUT;
5354 case 0x1632: // (blue steel)
5355 element = EL_STEEL_CHAR_0;
5358 case 0x1633: // (blue steel)
5359 element = EL_STEEL_CHAR_1;
5362 case 0x1634: // (blue steel)
5363 element = EL_STEEL_CHAR_2;
5366 case 0x1635: // (blue steel)
5367 element = EL_STEEL_CHAR_3;
5370 case 0x1636: // (blue steel)
5371 element = EL_STEEL_CHAR_4;
5374 case 0x1637: // (blue steel)
5375 element = EL_STEEL_CHAR_5;
5378 case 0x1638: // (blue steel)
5379 element = EL_STEEL_CHAR_6;
5382 case 0x1639: // (blue steel)
5383 element = EL_STEEL_CHAR_7;
5386 case 0x163a: // (blue steel)
5387 element = EL_STEEL_CHAR_8;
5390 case 0x163b: // (blue steel)
5391 element = EL_STEEL_CHAR_9;
5394 case 0x163c: // (blue steel)
5395 element = EL_STEEL_CHAR_PERIOD;
5398 case 0x163d: // (blue steel)
5399 element = EL_STEEL_CHAR_EXCLAM;
5402 case 0x163e: // (blue steel)
5403 element = EL_STEEL_CHAR_COLON;
5406 case 0x163f: // (blue steel)
5407 element = EL_STEEL_CHAR_LESS;
5410 case 0x1640: // (blue steel)
5411 element = EL_STEEL_CHAR_GREATER;
5414 case 0x1641: // (blue steel)
5415 element = EL_STEEL_CHAR_QUESTION;
5418 case 0x1642: // (blue steel)
5419 element = EL_STEEL_CHAR_COPYRIGHT;
5422 case 0x1643: // (blue steel)
5423 element = EL_STEEL_CHAR_UP;
5426 case 0x1644: // (blue steel)
5427 element = EL_STEEL_CHAR_DOWN;
5430 case 0x1645: // (blue steel)
5431 element = EL_STEEL_CHAR_BUTTON;
5434 case 0x1646: // (blue steel)
5435 element = EL_STEEL_CHAR_PLUS;
5438 case 0x1647: // (blue steel)
5439 element = EL_STEEL_CHAR_MINUS;
5442 case 0x1648: // (blue steel)
5443 element = EL_STEEL_CHAR_APOSTROPHE;
5446 case 0x1649: // (blue steel)
5447 element = EL_STEEL_CHAR_PARENLEFT;
5450 case 0x164a: // (blue steel)
5451 element = EL_STEEL_CHAR_PARENRIGHT;
5454 case 0x164b: // (green steel)
5455 element = EL_STEEL_CHAR_A;
5458 case 0x164c: // (green steel)
5459 element = EL_STEEL_CHAR_B;
5462 case 0x164d: // (green steel)
5463 element = EL_STEEL_CHAR_C;
5466 case 0x164e: // (green steel)
5467 element = EL_STEEL_CHAR_D;
5470 case 0x164f: // (green steel)
5471 element = EL_STEEL_CHAR_E;
5474 case 0x1650: // (green steel)
5475 element = EL_STEEL_CHAR_F;
5478 case 0x1651: // (green steel)
5479 element = EL_STEEL_CHAR_G;
5482 case 0x1652: // (green steel)
5483 element = EL_STEEL_CHAR_H;
5486 case 0x1653: // (green steel)
5487 element = EL_STEEL_CHAR_I;
5490 case 0x1654: // (green steel)
5491 element = EL_STEEL_CHAR_J;
5494 case 0x1655: // (green steel)
5495 element = EL_STEEL_CHAR_K;
5498 case 0x1656: // (green steel)
5499 element = EL_STEEL_CHAR_L;
5502 case 0x1657: // (green steel)
5503 element = EL_STEEL_CHAR_M;
5506 case 0x1658: // (green steel)
5507 element = EL_STEEL_CHAR_N;
5510 case 0x1659: // (green steel)
5511 element = EL_STEEL_CHAR_O;
5514 case 0x165a: // (green steel)
5515 element = EL_STEEL_CHAR_P;
5518 case 0x165b: // (green steel)
5519 element = EL_STEEL_CHAR_Q;
5522 case 0x165c: // (green steel)
5523 element = EL_STEEL_CHAR_R;
5526 case 0x165d: // (green steel)
5527 element = EL_STEEL_CHAR_S;
5530 case 0x165e: // (green steel)
5531 element = EL_STEEL_CHAR_T;
5534 case 0x165f: // (green steel)
5535 element = EL_STEEL_CHAR_U;
5538 case 0x1660: // (green steel)
5539 element = EL_STEEL_CHAR_V;
5542 case 0x1661: // (green steel)
5543 element = EL_STEEL_CHAR_W;
5546 case 0x1662: // (green steel)
5547 element = EL_STEEL_CHAR_X;
5550 case 0x1663: // (green steel)
5551 element = EL_STEEL_CHAR_Y;
5554 case 0x1664: // (green steel)
5555 element = EL_STEEL_CHAR_Z;
5558 case 0x1665: // (green steel)
5559 element = EL_STEEL_CHAR_AUMLAUT;
5562 case 0x1666: // (green steel)
5563 element = EL_STEEL_CHAR_OUMLAUT;
5566 case 0x1667: // (green steel)
5567 element = EL_STEEL_CHAR_UUMLAUT;
5570 case 0x1668: // (green steel)
5571 element = EL_STEEL_CHAR_0;
5574 case 0x1669: // (green steel)
5575 element = EL_STEEL_CHAR_1;
5578 case 0x166a: // (green steel)
5579 element = EL_STEEL_CHAR_2;
5582 case 0x166b: // (green steel)
5583 element = EL_STEEL_CHAR_3;
5586 case 0x166c: // (green steel)
5587 element = EL_STEEL_CHAR_4;
5590 case 0x166d: // (green steel)
5591 element = EL_STEEL_CHAR_5;
5594 case 0x166e: // (green steel)
5595 element = EL_STEEL_CHAR_6;
5598 case 0x166f: // (green steel)
5599 element = EL_STEEL_CHAR_7;
5602 case 0x1670: // (green steel)
5603 element = EL_STEEL_CHAR_8;
5606 case 0x1671: // (green steel)
5607 element = EL_STEEL_CHAR_9;
5610 case 0x1672: // (green steel)
5611 element = EL_STEEL_CHAR_PERIOD;
5614 case 0x1673: // (green steel)
5615 element = EL_STEEL_CHAR_EXCLAM;
5618 case 0x1674: // (green steel)
5619 element = EL_STEEL_CHAR_COLON;
5622 case 0x1675: // (green steel)
5623 element = EL_STEEL_CHAR_LESS;
5626 case 0x1676: // (green steel)
5627 element = EL_STEEL_CHAR_GREATER;
5630 case 0x1677: // (green steel)
5631 element = EL_STEEL_CHAR_QUESTION;
5634 case 0x1678: // (green steel)
5635 element = EL_STEEL_CHAR_COPYRIGHT;
5638 case 0x1679: // (green steel)
5639 element = EL_STEEL_CHAR_UP;
5642 case 0x167a: // (green steel)
5643 element = EL_STEEL_CHAR_DOWN;
5646 case 0x167b: // (green steel)
5647 element = EL_STEEL_CHAR_BUTTON;
5650 case 0x167c: // (green steel)
5651 element = EL_STEEL_CHAR_PLUS;
5654 case 0x167d: // (green steel)
5655 element = EL_STEEL_CHAR_MINUS;
5658 case 0x167e: // (green steel)
5659 element = EL_STEEL_CHAR_APOSTROPHE;
5662 case 0x167f: // (green steel)
5663 element = EL_STEEL_CHAR_PARENLEFT;
5666 case 0x1680: // (green steel)
5667 element = EL_STEEL_CHAR_PARENRIGHT;
5670 case 0x1681: // gate (red)
5671 element = EL_EM_GATE_1;
5674 case 0x1682: // secret gate (red)
5675 element = EL_EM_GATE_1_GRAY;
5678 case 0x1683: // gate (yellow)
5679 element = EL_EM_GATE_2;
5682 case 0x1684: // secret gate (yellow)
5683 element = EL_EM_GATE_2_GRAY;
5686 case 0x1685: // gate (blue)
5687 element = EL_EM_GATE_4;
5690 case 0x1686: // secret gate (blue)
5691 element = EL_EM_GATE_4_GRAY;
5694 case 0x1687: // gate (green)
5695 element = EL_EM_GATE_3;
5698 case 0x1688: // secret gate (green)
5699 element = EL_EM_GATE_3_GRAY;
5702 case 0x1689: // gate (white)
5703 element = EL_DC_GATE_WHITE;
5706 case 0x168a: // secret gate (white)
5707 element = EL_DC_GATE_WHITE_GRAY;
5710 case 0x168b: // secret gate (no key)
5711 element = EL_DC_GATE_FAKE_GRAY;
5715 element = EL_ROBOT_WHEEL;
5719 element = EL_DC_TIMEGATE_SWITCH;
5723 element = EL_ACID_POOL_BOTTOM;
5727 element = EL_ACID_POOL_TOPLEFT;
5731 element = EL_ACID_POOL_TOPRIGHT;
5735 element = EL_ACID_POOL_BOTTOMLEFT;
5739 element = EL_ACID_POOL_BOTTOMRIGHT;
5743 element = EL_STEELWALL;
5747 element = EL_STEELWALL_SLIPPERY;
5750 case 0x1695: // steel wall (not round)
5751 element = EL_STEELWALL;
5754 case 0x1696: // steel wall (left)
5755 element = EL_DC_STEELWALL_1_LEFT;
5758 case 0x1697: // steel wall (bottom)
5759 element = EL_DC_STEELWALL_1_BOTTOM;
5762 case 0x1698: // steel wall (right)
5763 element = EL_DC_STEELWALL_1_RIGHT;
5766 case 0x1699: // steel wall (top)
5767 element = EL_DC_STEELWALL_1_TOP;
5770 case 0x169a: // steel wall (left/bottom)
5771 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5774 case 0x169b: // steel wall (right/bottom)
5775 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5778 case 0x169c: // steel wall (right/top)
5779 element = EL_DC_STEELWALL_1_TOPRIGHT;
5782 case 0x169d: // steel wall (left/top)
5783 element = EL_DC_STEELWALL_1_TOPLEFT;
5786 case 0x169e: // steel wall (right/bottom small)
5787 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5790 case 0x169f: // steel wall (left/bottom small)
5791 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5794 case 0x16a0: // steel wall (right/top small)
5795 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5798 case 0x16a1: // steel wall (left/top small)
5799 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5802 case 0x16a2: // steel wall (left/right)
5803 element = EL_DC_STEELWALL_1_VERTICAL;
5806 case 0x16a3: // steel wall (top/bottom)
5807 element = EL_DC_STEELWALL_1_HORIZONTAL;
5810 case 0x16a4: // steel wall 2 (left end)
5811 element = EL_DC_STEELWALL_2_LEFT;
5814 case 0x16a5: // steel wall 2 (right end)
5815 element = EL_DC_STEELWALL_2_RIGHT;
5818 case 0x16a6: // steel wall 2 (top end)
5819 element = EL_DC_STEELWALL_2_TOP;
5822 case 0x16a7: // steel wall 2 (bottom end)
5823 element = EL_DC_STEELWALL_2_BOTTOM;
5826 case 0x16a8: // steel wall 2 (left/right)
5827 element = EL_DC_STEELWALL_2_HORIZONTAL;
5830 case 0x16a9: // steel wall 2 (up/down)
5831 element = EL_DC_STEELWALL_2_VERTICAL;
5834 case 0x16aa: // steel wall 2 (mid)
5835 element = EL_DC_STEELWALL_2_MIDDLE;
5839 element = EL_SIGN_EXCLAMATION;
5843 element = EL_SIGN_RADIOACTIVITY;
5847 element = EL_SIGN_STOP;
5851 element = EL_SIGN_WHEELCHAIR;
5855 element = EL_SIGN_PARKING;
5859 element = EL_SIGN_NO_ENTRY;
5863 element = EL_SIGN_HEART;
5867 element = EL_SIGN_GIVE_WAY;
5871 element = EL_SIGN_ENTRY_FORBIDDEN;
5875 element = EL_SIGN_EMERGENCY_EXIT;
5879 element = EL_SIGN_YIN_YANG;
5883 element = EL_WALL_EMERALD;
5887 element = EL_WALL_DIAMOND;
5891 element = EL_WALL_PEARL;
5895 element = EL_WALL_CRYSTAL;
5899 element = EL_INVISIBLE_WALL;
5903 element = EL_INVISIBLE_STEELWALL;
5907 // EL_INVISIBLE_SAND
5910 element = EL_LIGHT_SWITCH;
5914 element = EL_ENVELOPE_1;
5918 if (element >= 0x0117 && element <= 0x036e) // (?)
5919 element = EL_DIAMOND;
5920 else if (element >= 0x042d && element <= 0x0684) // (?)
5921 element = EL_EMERALD;
5922 else if (element >= 0x157c && element <= 0x158b)
5924 else if (element >= 0x1590 && element <= 0x159f)
5925 element = EL_DC_LANDMINE;
5926 else if (element >= 0x16bc && element <= 0x16cb)
5927 element = EL_INVISIBLE_SAND;
5930 Warn("unknown Diamond Caves element 0x%04x", element);
5932 element = EL_UNKNOWN;
5937 return getMappedElement(element);
5940 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5942 byte header[DC_LEVEL_HEADER_SIZE];
5944 int envelope_header_pos = 62;
5945 int envelope_content_pos = 94;
5946 int level_name_pos = 251;
5947 int level_author_pos = 292;
5948 int envelope_header_len;
5949 int envelope_content_len;
5951 int level_author_len;
5953 int num_yamyam_contents;
5956 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5958 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5960 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5962 header[i * 2 + 0] = header_word >> 8;
5963 header[i * 2 + 1] = header_word & 0xff;
5966 // read some values from level header to check level decoding integrity
5967 fieldx = header[6] | (header[7] << 8);
5968 fieldy = header[8] | (header[9] << 8);
5969 num_yamyam_contents = header[60] | (header[61] << 8);
5971 // do some simple sanity checks to ensure that level was correctly decoded
5972 if (fieldx < 1 || fieldx > 256 ||
5973 fieldy < 1 || fieldy > 256 ||
5974 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5976 level->no_valid_file = TRUE;
5978 Warn("cannot decode level from stream -- using empty level");
5983 // maximum envelope header size is 31 bytes
5984 envelope_header_len = header[envelope_header_pos];
5985 // maximum envelope content size is 110 (156?) bytes
5986 envelope_content_len = header[envelope_content_pos];
5988 // maximum level title size is 40 bytes
5989 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5990 // maximum level author size is 30 (51?) bytes
5991 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5995 for (i = 0; i < envelope_header_len; i++)
5996 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5997 level->envelope[0].text[envelope_size++] =
5998 header[envelope_header_pos + 1 + i];
6000 if (envelope_header_len > 0 && envelope_content_len > 0)
6002 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6003 level->envelope[0].text[envelope_size++] = '\n';
6004 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6005 level->envelope[0].text[envelope_size++] = '\n';
6008 for (i = 0; i < envelope_content_len; i++)
6009 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6010 level->envelope[0].text[envelope_size++] =
6011 header[envelope_content_pos + 1 + i];
6013 level->envelope[0].text[envelope_size] = '\0';
6015 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6016 level->envelope[0].ysize = 10;
6017 level->envelope[0].autowrap = TRUE;
6018 level->envelope[0].centered = TRUE;
6020 for (i = 0; i < level_name_len; i++)
6021 level->name[i] = header[level_name_pos + 1 + i];
6022 level->name[level_name_len] = '\0';
6024 for (i = 0; i < level_author_len; i++)
6025 level->author[i] = header[level_author_pos + 1 + i];
6026 level->author[level_author_len] = '\0';
6028 num_yamyam_contents = header[60] | (header[61] << 8);
6029 level->num_yamyam_contents =
6030 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6032 for (i = 0; i < num_yamyam_contents; i++)
6034 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6036 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6037 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6039 if (i < MAX_ELEMENT_CONTENTS)
6040 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6044 fieldx = header[6] | (header[7] << 8);
6045 fieldy = header[8] | (header[9] << 8);
6046 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6047 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6049 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6051 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6052 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6054 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6055 level->field[x][y] = getMappedElement_DC(element_dc);
6058 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6059 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6060 level->field[x][y] = EL_PLAYER_1;
6062 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6063 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6064 level->field[x][y] = EL_PLAYER_2;
6066 level->gems_needed = header[18] | (header[19] << 8);
6068 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6069 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6070 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6071 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6072 level->score[SC_NUT] = header[28] | (header[29] << 8);
6073 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6074 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6075 level->score[SC_BUG] = header[34] | (header[35] << 8);
6076 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6077 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6078 level->score[SC_KEY] = header[40] | (header[41] << 8);
6079 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6081 level->time = header[44] | (header[45] << 8);
6083 level->amoeba_speed = header[46] | (header[47] << 8);
6084 level->time_light = header[48] | (header[49] << 8);
6085 level->time_timegate = header[50] | (header[51] << 8);
6086 level->time_wheel = header[52] | (header[53] << 8);
6087 level->time_magic_wall = header[54] | (header[55] << 8);
6088 level->extra_time = header[56] | (header[57] << 8);
6089 level->shield_normal_time = header[58] | (header[59] << 8);
6091 // shield and extra time elements do not have a score
6092 level->score[SC_SHIELD] = 0;
6093 level->extra_time_score = 0;
6095 // set time for normal and deadly shields to the same value
6096 level->shield_deadly_time = level->shield_normal_time;
6098 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6099 // can slip down from flat walls, like normal walls and steel walls
6100 level->em_slippery_gems = TRUE;
6102 // time score is counted for each 10 seconds left in Diamond Caves levels
6103 level->time_score_base = 10;
6106 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6107 struct LevelFileInfo *level_file_info,
6108 boolean level_info_only)
6110 char *filename = level_file_info->filename;
6112 int num_magic_bytes = 8;
6113 char magic_bytes[num_magic_bytes + 1];
6114 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6116 if (!(file = openFile(filename, MODE_READ)))
6118 level->no_valid_file = TRUE;
6120 if (!level_info_only)
6121 Warn("cannot read level '%s' -- using empty level", filename);
6126 // fseek(file, 0x0000, SEEK_SET);
6128 if (level_file_info->packed)
6130 // read "magic bytes" from start of file
6131 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6132 magic_bytes[0] = '\0';
6134 // check "magic bytes" for correct file format
6135 if (!strPrefix(magic_bytes, "DC2"))
6137 level->no_valid_file = TRUE;
6139 Warn("unknown DC level file '%s' -- using empty level", filename);
6144 if (strPrefix(magic_bytes, "DC2Win95") ||
6145 strPrefix(magic_bytes, "DC2Win98"))
6147 int position_first_level = 0x00fa;
6148 int extra_bytes = 4;
6151 // advance file stream to first level inside the level package
6152 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6154 // each block of level data is followed by block of non-level data
6155 num_levels_to_skip *= 2;
6157 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6158 while (num_levels_to_skip >= 0)
6160 // advance file stream to next level inside the level package
6161 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6163 level->no_valid_file = TRUE;
6165 Warn("cannot fseek in file '%s' -- using empty level", filename);
6170 // skip apparently unused extra bytes following each level
6171 ReadUnusedBytesFromFile(file, extra_bytes);
6173 // read size of next level in level package
6174 skip_bytes = getFile32BitLE(file);
6176 num_levels_to_skip--;
6181 level->no_valid_file = TRUE;
6183 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6189 LoadLevelFromFileStream_DC(file, level);
6195 // ----------------------------------------------------------------------------
6196 // functions for loading SB level
6197 // ----------------------------------------------------------------------------
6199 int getMappedElement_SB(int element_ascii, boolean use_ces)
6207 sb_element_mapping[] =
6209 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6210 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6211 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6212 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6213 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6214 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6215 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6216 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6223 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6224 if (element_ascii == sb_element_mapping[i].ascii)
6225 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6227 return EL_UNDEFINED;
6230 static void SetLevelSettings_SB(struct LevelInfo *level)
6234 level->use_step_counter = TRUE;
6237 level->score[SC_TIME_BONUS] = 0;
6238 level->time_score_base = 1;
6239 level->rate_time_over_score = TRUE;
6242 level->auto_exit_sokoban = TRUE;
6245 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6246 struct LevelFileInfo *level_file_info,
6247 boolean level_info_only)
6249 char *filename = level_file_info->filename;
6250 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6251 char last_comment[MAX_LINE_LEN];
6252 char level_name[MAX_LINE_LEN];
6255 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6256 boolean read_continued_line = FALSE;
6257 boolean reading_playfield = FALSE;
6258 boolean got_valid_playfield_line = FALSE;
6259 boolean invalid_playfield_char = FALSE;
6260 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6261 int file_level_nr = 0;
6262 int x = 0, y = 0; // initialized to make compilers happy
6264 last_comment[0] = '\0';
6265 level_name[0] = '\0';
6267 if (!(file = openFile(filename, MODE_READ)))
6269 level->no_valid_file = TRUE;
6271 if (!level_info_only)
6272 Warn("cannot read level '%s' -- using empty level", filename);
6277 while (!checkEndOfFile(file))
6279 // level successfully read, but next level may follow here
6280 if (!got_valid_playfield_line && reading_playfield)
6282 // read playfield from single level file -- skip remaining file
6283 if (!level_file_info->packed)
6286 if (file_level_nr >= num_levels_to_skip)
6291 last_comment[0] = '\0';
6292 level_name[0] = '\0';
6294 reading_playfield = FALSE;
6297 got_valid_playfield_line = FALSE;
6299 // read next line of input file
6300 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6303 // cut trailing line break (this can be newline and/or carriage return)
6304 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6305 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6308 // copy raw input line for later use (mainly debugging output)
6309 strcpy(line_raw, line);
6311 if (read_continued_line)
6313 // append new line to existing line, if there is enough space
6314 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6315 strcat(previous_line, line_ptr);
6317 strcpy(line, previous_line); // copy storage buffer to line
6319 read_continued_line = FALSE;
6322 // if the last character is '\', continue at next line
6323 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6325 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6326 strcpy(previous_line, line); // copy line to storage buffer
6328 read_continued_line = TRUE;
6334 if (line[0] == '\0')
6337 // extract comment text from comment line
6340 for (line_ptr = line; *line_ptr; line_ptr++)
6341 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6344 strcpy(last_comment, line_ptr);
6349 // extract level title text from line containing level title
6350 if (line[0] == '\'')
6352 strcpy(level_name, &line[1]);
6354 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6355 level_name[strlen(level_name) - 1] = '\0';
6360 // skip lines containing only spaces (or empty lines)
6361 for (line_ptr = line; *line_ptr; line_ptr++)
6362 if (*line_ptr != ' ')
6364 if (*line_ptr == '\0')
6367 // at this point, we have found a line containing part of a playfield
6369 got_valid_playfield_line = TRUE;
6371 if (!reading_playfield)
6373 reading_playfield = TRUE;
6374 invalid_playfield_char = FALSE;
6376 for (x = 0; x < MAX_LEV_FIELDX; x++)
6377 for (y = 0; y < MAX_LEV_FIELDY; y++)
6378 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6383 // start with topmost tile row
6387 // skip playfield line if larger row than allowed
6388 if (y >= MAX_LEV_FIELDY)
6391 // start with leftmost tile column
6394 // read playfield elements from line
6395 for (line_ptr = line; *line_ptr; line_ptr++)
6397 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6399 // stop parsing playfield line if larger column than allowed
6400 if (x >= MAX_LEV_FIELDX)
6403 if (mapped_sb_element == EL_UNDEFINED)
6405 invalid_playfield_char = TRUE;
6410 level->field[x][y] = mapped_sb_element;
6412 // continue with next tile column
6415 level->fieldx = MAX(x, level->fieldx);
6418 if (invalid_playfield_char)
6420 // if first playfield line, treat invalid lines as comment lines
6422 reading_playfield = FALSE;
6427 // continue with next tile row
6435 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6436 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6438 if (!reading_playfield)
6440 level->no_valid_file = TRUE;
6442 Warn("cannot read level '%s' -- using empty level", filename);
6447 if (*level_name != '\0')
6449 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6450 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6452 else if (*last_comment != '\0')
6454 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6455 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6459 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6462 // set all empty fields beyond the border walls to invisible steel wall
6463 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6465 if ((x == 0 || x == level->fieldx - 1 ||
6466 y == 0 || y == level->fieldy - 1) &&
6467 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6468 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6469 level->field, level->fieldx, level->fieldy);
6472 // set special level settings for Sokoban levels
6473 SetLevelSettings_SB(level);
6475 if (load_xsb_to_ces)
6477 // special global settings can now be set in level template
6478 level->use_custom_template = TRUE;
6483 // -------------------------------------------------------------------------
6484 // functions for handling native levels
6485 // -------------------------------------------------------------------------
6487 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6488 struct LevelFileInfo *level_file_info,
6489 boolean level_info_only)
6493 // determine position of requested level inside level package
6494 if (level_file_info->packed)
6495 pos = level_file_info->nr - leveldir_current->first_level;
6497 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6498 level->no_valid_file = TRUE;
6501 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6502 struct LevelFileInfo *level_file_info,
6503 boolean level_info_only)
6505 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6506 level->no_valid_file = TRUE;
6509 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6510 struct LevelFileInfo *level_file_info,
6511 boolean level_info_only)
6515 // determine position of requested level inside level package
6516 if (level_file_info->packed)
6517 pos = level_file_info->nr - leveldir_current->first_level;
6519 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6520 level->no_valid_file = TRUE;
6523 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6524 struct LevelFileInfo *level_file_info,
6525 boolean level_info_only)
6527 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6528 level->no_valid_file = TRUE;
6531 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6533 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6534 CopyNativeLevel_RND_to_BD(level);
6535 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6536 CopyNativeLevel_RND_to_EM(level);
6537 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6538 CopyNativeLevel_RND_to_SP(level);
6539 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6540 CopyNativeLevel_RND_to_MM(level);
6543 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6545 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6546 CopyNativeLevel_BD_to_RND(level);
6547 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6548 CopyNativeLevel_EM_to_RND(level);
6549 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6550 CopyNativeLevel_SP_to_RND(level);
6551 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6552 CopyNativeLevel_MM_to_RND(level);
6555 void SaveNativeLevel(struct LevelInfo *level)
6557 // saving native level files only supported for some game engines
6558 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6559 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6562 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6563 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6564 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6565 char *filename = getLevelFilenameFromBasename(basename);
6567 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6570 boolean success = FALSE;
6572 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6574 CopyNativeLevel_RND_to_BD(level);
6575 // CopyNativeTape_RND_to_BD(level);
6577 success = SaveNativeLevel_BD(filename);
6579 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6581 CopyNativeLevel_RND_to_SP(level);
6582 CopyNativeTape_RND_to_SP(level);
6584 success = SaveNativeLevel_SP(filename);
6588 Request("Native level file saved!", REQ_CONFIRM);
6590 Request("Failed to save native level file!", REQ_CONFIRM);
6594 // ----------------------------------------------------------------------------
6595 // functions for loading generic level
6596 // ----------------------------------------------------------------------------
6598 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6599 struct LevelFileInfo *level_file_info,
6600 boolean level_info_only)
6602 // always start with reliable default values
6603 setLevelInfoToDefaults(level, level_info_only, TRUE);
6605 switch (level_file_info->type)
6607 case LEVEL_FILE_TYPE_RND:
6608 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6611 case LEVEL_FILE_TYPE_BD:
6612 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6613 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6616 case LEVEL_FILE_TYPE_EM:
6617 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6618 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6621 case LEVEL_FILE_TYPE_SP:
6622 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6623 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6626 case LEVEL_FILE_TYPE_MM:
6627 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6628 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6631 case LEVEL_FILE_TYPE_DC:
6632 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6635 case LEVEL_FILE_TYPE_SB:
6636 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6640 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6644 // if level file is invalid, restore level structure to default values
6645 if (level->no_valid_file)
6646 setLevelInfoToDefaults(level, level_info_only, FALSE);
6648 if (check_special_flags("use_native_bd_game_engine"))
6649 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6651 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6652 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6654 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6655 CopyNativeLevel_Native_to_RND(level);
6658 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6660 static struct LevelFileInfo level_file_info;
6662 // always start with reliable default values
6663 setFileInfoToDefaults(&level_file_info);
6665 level_file_info.nr = 0; // unknown level number
6666 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6668 setString(&level_file_info.filename, filename);
6670 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6673 static void LoadLevel_InitVersion(struct LevelInfo *level)
6677 if (leveldir_current == NULL) // only when dumping level
6680 // all engine modifications also valid for levels which use latest engine
6681 if (level->game_version < VERSION_IDENT(3,2,0,5))
6683 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6684 level->time_score_base = 10;
6687 if (leveldir_current->latest_engine)
6689 // ---------- use latest game engine --------------------------------------
6691 /* For all levels which are forced to use the latest game engine version
6692 (normally all but user contributed, private and undefined levels), set
6693 the game engine version to the actual version; this allows for actual
6694 corrections in the game engine to take effect for existing, converted
6695 levels (from "classic" or other existing games) to make the emulation
6696 of the corresponding game more accurate, while (hopefully) not breaking
6697 existing levels created from other players. */
6699 level->game_version = GAME_VERSION_ACTUAL;
6701 /* Set special EM style gems behaviour: EM style gems slip down from
6702 normal, steel and growing wall. As this is a more fundamental change,
6703 it seems better to set the default behaviour to "off" (as it is more
6704 natural) and make it configurable in the level editor (as a property
6705 of gem style elements). Already existing converted levels (neither
6706 private nor contributed levels) are changed to the new behaviour. */
6708 if (level->file_version < FILE_VERSION_2_0)
6709 level->em_slippery_gems = TRUE;
6714 // ---------- use game engine the level was created with --------------------
6716 /* For all levels which are not forced to use the latest game engine
6717 version (normally user contributed, private and undefined levels),
6718 use the version of the game engine the levels were created for.
6720 Since 2.0.1, the game engine version is now directly stored
6721 in the level file (chunk "VERS"), so there is no need anymore
6722 to set the game version from the file version (except for old,
6723 pre-2.0 levels, where the game version is still taken from the
6724 file format version used to store the level -- see above). */
6726 // player was faster than enemies in 1.0.0 and before
6727 if (level->file_version == FILE_VERSION_1_0)
6728 for (i = 0; i < MAX_PLAYERS; i++)
6729 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6731 // default behaviour for EM style gems was "slippery" only in 2.0.1
6732 if (level->game_version == VERSION_IDENT(2,0,1,0))
6733 level->em_slippery_gems = TRUE;
6735 // springs could be pushed over pits before (pre-release version) 2.2.0
6736 if (level->game_version < VERSION_IDENT(2,2,0,0))
6737 level->use_spring_bug = TRUE;
6739 if (level->game_version < VERSION_IDENT(3,2,0,5))
6741 // time orb caused limited time in endless time levels before 3.2.0-5
6742 level->use_time_orb_bug = TRUE;
6744 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6745 level->block_snap_field = FALSE;
6747 // extra time score was same value as time left score before 3.2.0-5
6748 level->extra_time_score = level->score[SC_TIME_BONUS];
6751 if (level->game_version < VERSION_IDENT(3,2,0,7))
6753 // default behaviour for snapping was "not continuous" before 3.2.0-7
6754 level->continuous_snapping = FALSE;
6757 // only few elements were able to actively move into acid before 3.1.0
6758 // trigger settings did not exist before 3.1.0; set to default "any"
6759 if (level->game_version < VERSION_IDENT(3,1,0,0))
6761 // correct "can move into acid" settings (all zero in old levels)
6763 level->can_move_into_acid_bits = 0; // nothing can move into acid
6764 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6766 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6767 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6768 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6769 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6771 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6772 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6774 // correct trigger settings (stored as zero == "none" in old levels)
6776 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6778 int element = EL_CUSTOM_START + i;
6779 struct ElementInfo *ei = &element_info[element];
6781 for (j = 0; j < ei->num_change_pages; j++)
6783 struct ElementChangeInfo *change = &ei->change_page[j];
6785 change->trigger_player = CH_PLAYER_ANY;
6786 change->trigger_page = CH_PAGE_ANY;
6791 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6793 int element = EL_CUSTOM_256;
6794 struct ElementInfo *ei = &element_info[element];
6795 struct ElementChangeInfo *change = &ei->change_page[0];
6797 /* This is needed to fix a problem that was caused by a bugfix in function
6798 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6799 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6800 not replace walkable elements, but instead just placed the player on it,
6801 without placing the Sokoban field under the player). Unfortunately, this
6802 breaks "Snake Bite" style levels when the snake is halfway through a door
6803 that just closes (the snake head is still alive and can be moved in this
6804 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6805 player (without Sokoban element) which then gets killed as designed). */
6807 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6808 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6809 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6810 change->target_element = EL_PLAYER_1;
6813 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6814 if (level->game_version < VERSION_IDENT(3,2,5,0))
6816 /* This is needed to fix a problem that was caused by a bugfix in function
6817 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6818 corrects the behaviour when a custom element changes to another custom
6819 element with a higher element number that has change actions defined.
6820 Normally, only one change per frame is allowed for custom elements.
6821 Therefore, it is checked if a custom element already changed in the
6822 current frame; if it did, subsequent changes are suppressed.
6823 Unfortunately, this is only checked for element changes, but not for
6824 change actions, which are still executed. As the function above loops
6825 through all custom elements from lower to higher, an element change
6826 resulting in a lower CE number won't be checked again, while a target
6827 element with a higher number will also be checked, and potential change
6828 actions will get executed for this CE, too (which is wrong), while
6829 further changes are ignored (which is correct). As this bugfix breaks
6830 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6831 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6832 behaviour for existing levels and tapes that make use of this bug */
6834 level->use_action_after_change_bug = TRUE;
6837 // not centering level after relocating player was default only in 3.2.3
6838 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6839 level->shifted_relocation = TRUE;
6841 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6842 if (level->game_version < VERSION_IDENT(3,2,6,0))
6843 level->em_explodes_by_fire = TRUE;
6845 // levels were solved by the first player entering an exit up to 4.1.0.0
6846 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6847 level->solved_by_one_player = TRUE;
6849 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6850 if (level->game_version < VERSION_IDENT(4,1,1,1))
6851 level->use_life_bugs = TRUE;
6853 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6854 if (level->game_version < VERSION_IDENT(4,1,1,1))
6855 level->sb_objects_needed = FALSE;
6857 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6858 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6859 level->finish_dig_collect = FALSE;
6861 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6862 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6863 level->keep_walkable_ce = TRUE;
6866 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6868 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6871 // check if this level is (not) a Sokoban level
6872 for (y = 0; y < level->fieldy; y++)
6873 for (x = 0; x < level->fieldx; x++)
6874 if (!IS_SB_ELEMENT(Tile[x][y]))
6875 is_sokoban_level = FALSE;
6877 if (is_sokoban_level)
6879 // set special level settings for Sokoban levels
6880 SetLevelSettings_SB(level);
6884 static void LoadLevel_InitSettings(struct LevelInfo *level)
6886 // adjust level settings for (non-native) Sokoban-style levels
6887 LoadLevel_InitSettings_SB(level);
6889 // rename levels with title "nameless level" or if renaming is forced
6890 if (leveldir_current->empty_level_name != NULL &&
6891 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6892 leveldir_current->force_level_name))
6893 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6894 leveldir_current->empty_level_name, level_nr);
6897 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6901 // map elements that have changed in newer versions
6902 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6903 level->game_version);
6904 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6905 for (x = 0; x < 3; x++)
6906 for (y = 0; y < 3; y++)
6907 level->yamyam_content[i].e[x][y] =
6908 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6909 level->game_version);
6913 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6917 // map custom element change events that have changed in newer versions
6918 // (these following values were accidentally changed in version 3.0.1)
6919 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6920 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6922 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6924 int element = EL_CUSTOM_START + i;
6926 // order of checking and copying events to be mapped is important
6927 // (do not change the start and end value -- they are constant)
6928 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6930 if (HAS_CHANGE_EVENT(element, j - 2))
6932 SET_CHANGE_EVENT(element, j - 2, FALSE);
6933 SET_CHANGE_EVENT(element, j, TRUE);
6937 // order of checking and copying events to be mapped is important
6938 // (do not change the start and end value -- they are constant)
6939 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6941 if (HAS_CHANGE_EVENT(element, j - 1))
6943 SET_CHANGE_EVENT(element, j - 1, FALSE);
6944 SET_CHANGE_EVENT(element, j, TRUE);
6950 // initialize "can_change" field for old levels with only one change page
6951 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6953 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6955 int element = EL_CUSTOM_START + i;
6957 if (CAN_CHANGE(element))
6958 element_info[element].change->can_change = TRUE;
6962 // correct custom element values (for old levels without these options)
6963 if (level->game_version < VERSION_IDENT(3,1,1,0))
6965 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6967 int element = EL_CUSTOM_START + i;
6968 struct ElementInfo *ei = &element_info[element];
6970 if (ei->access_direction == MV_NO_DIRECTION)
6971 ei->access_direction = MV_ALL_DIRECTIONS;
6975 // correct custom element values (fix invalid values for all versions)
6978 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6980 int element = EL_CUSTOM_START + i;
6981 struct ElementInfo *ei = &element_info[element];
6983 for (j = 0; j < ei->num_change_pages; j++)
6985 struct ElementChangeInfo *change = &ei->change_page[j];
6987 if (change->trigger_player == CH_PLAYER_NONE)
6988 change->trigger_player = CH_PLAYER_ANY;
6990 if (change->trigger_side == CH_SIDE_NONE)
6991 change->trigger_side = CH_SIDE_ANY;
6996 // initialize "can_explode" field for old levels which did not store this
6997 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6998 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7000 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7002 int element = EL_CUSTOM_START + i;
7004 if (EXPLODES_1X1_OLD(element))
7005 element_info[element].explosion_type = EXPLODES_1X1;
7007 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7008 EXPLODES_SMASHED(element) ||
7009 EXPLODES_IMPACT(element)));
7013 // correct previously hard-coded move delay values for maze runner style
7014 if (level->game_version < VERSION_IDENT(3,1,1,0))
7016 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7018 int element = EL_CUSTOM_START + i;
7020 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7022 // previously hard-coded and therefore ignored
7023 element_info[element].move_delay_fixed = 9;
7024 element_info[element].move_delay_random = 0;
7029 // set some other uninitialized values of custom elements in older levels
7030 if (level->game_version < VERSION_IDENT(3,1,0,0))
7032 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7034 int element = EL_CUSTOM_START + i;
7036 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7038 element_info[element].explosion_delay = 17;
7039 element_info[element].ignition_delay = 8;
7043 // set mouse click change events to work for left/middle/right mouse button
7044 if (level->game_version < VERSION_IDENT(4,2,3,0))
7046 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7048 int element = EL_CUSTOM_START + i;
7049 struct ElementInfo *ei = &element_info[element];
7051 for (j = 0; j < ei->num_change_pages; j++)
7053 struct ElementChangeInfo *change = &ei->change_page[j];
7055 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7056 change->has_event[CE_PRESSED_BY_MOUSE] ||
7057 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7058 change->has_event[CE_MOUSE_PRESSED_ON_X])
7059 change->trigger_side = CH_SIDE_ANY;
7065 static void LoadLevel_InitElements(struct LevelInfo *level)
7067 LoadLevel_InitStandardElements(level);
7069 if (level->file_has_custom_elements)
7070 LoadLevel_InitCustomElements(level);
7072 // initialize element properties for level editor etc.
7073 InitElementPropertiesEngine(level->game_version);
7074 InitElementPropertiesGfxElement();
7077 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7081 // map elements that have changed in newer versions
7082 for (y = 0; y < level->fieldy; y++)
7083 for (x = 0; x < level->fieldx; x++)
7084 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7085 level->game_version);
7087 // clear unused playfield data (nicer if level gets resized in editor)
7088 for (x = 0; x < MAX_LEV_FIELDX; x++)
7089 for (y = 0; y < MAX_LEV_FIELDY; y++)
7090 if (x >= level->fieldx || y >= level->fieldy)
7091 level->field[x][y] = EL_EMPTY;
7093 // copy elements to runtime playfield array
7094 for (x = 0; x < MAX_LEV_FIELDX; x++)
7095 for (y = 0; y < MAX_LEV_FIELDY; y++)
7096 Tile[x][y] = level->field[x][y];
7098 // initialize level size variables for faster access
7099 lev_fieldx = level->fieldx;
7100 lev_fieldy = level->fieldy;
7102 // determine border element for this level
7103 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7104 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7109 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7111 struct LevelFileInfo *level_file_info = &level->file_info;
7113 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7114 CopyNativeLevel_RND_to_Native(level);
7117 static void LoadLevelTemplate_LoadAndInit(void)
7119 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7121 LoadLevel_InitVersion(&level_template);
7122 LoadLevel_InitElements(&level_template);
7123 LoadLevel_InitSettings(&level_template);
7125 ActivateLevelTemplate();
7128 void LoadLevelTemplate(int nr)
7130 if (!fileExists(getGlobalLevelTemplateFilename()))
7132 Warn("no level template found for this level");
7137 setLevelFileInfo(&level_template.file_info, nr);
7139 LoadLevelTemplate_LoadAndInit();
7142 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7144 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7146 LoadLevelTemplate_LoadAndInit();
7149 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7151 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7153 if (level.use_custom_template)
7155 if (network_level != NULL)
7156 LoadNetworkLevelTemplate(network_level);
7158 LoadLevelTemplate(-1);
7161 LoadLevel_InitVersion(&level);
7162 LoadLevel_InitElements(&level);
7163 LoadLevel_InitPlayfield(&level);
7164 LoadLevel_InitSettings(&level);
7166 LoadLevel_InitNativeEngines(&level);
7169 void LoadLevel(int nr)
7171 SetLevelSetInfo(leveldir_current->identifier, nr);
7173 setLevelFileInfo(&level.file_info, nr);
7175 LoadLevel_LoadAndInit(NULL);
7178 void LoadLevelInfoOnly(int nr)
7180 setLevelFileInfo(&level.file_info, nr);
7182 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7185 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7187 SetLevelSetInfo(network_level->leveldir_identifier,
7188 network_level->file_info.nr);
7190 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7192 LoadLevel_LoadAndInit(network_level);
7195 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7199 chunk_size += putFileVersion(file, level->file_version);
7200 chunk_size += putFileVersion(file, level->game_version);
7205 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7209 chunk_size += putFile16BitBE(file, level->creation_date.year);
7210 chunk_size += putFile8Bit(file, level->creation_date.month);
7211 chunk_size += putFile8Bit(file, level->creation_date.day);
7216 #if ENABLE_HISTORIC_CHUNKS
7217 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7221 putFile8Bit(file, level->fieldx);
7222 putFile8Bit(file, level->fieldy);
7224 putFile16BitBE(file, level->time);
7225 putFile16BitBE(file, level->gems_needed);
7227 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7228 putFile8Bit(file, level->name[i]);
7230 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7231 putFile8Bit(file, level->score[i]);
7233 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7234 for (y = 0; y < 3; y++)
7235 for (x = 0; x < 3; x++)
7236 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7237 level->yamyam_content[i].e[x][y]));
7238 putFile8Bit(file, level->amoeba_speed);
7239 putFile8Bit(file, level->time_magic_wall);
7240 putFile8Bit(file, level->time_wheel);
7241 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7242 level->amoeba_content));
7243 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7244 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7245 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7246 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7248 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7250 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7251 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7252 putFile32BitBE(file, level->can_move_into_acid_bits);
7253 putFile8Bit(file, level->dont_collide_with_bits);
7255 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7256 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7258 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7259 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7260 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7262 putFile8Bit(file, level->game_engine_type);
7264 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7268 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7273 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7274 chunk_size += putFile8Bit(file, level->name[i]);
7279 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7284 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7285 chunk_size += putFile8Bit(file, level->author[i]);
7290 #if ENABLE_HISTORIC_CHUNKS
7291 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7296 for (y = 0; y < level->fieldy; y++)
7297 for (x = 0; x < level->fieldx; x++)
7298 if (level->encoding_16bit_field)
7299 chunk_size += putFile16BitBE(file, level->field[x][y]);
7301 chunk_size += putFile8Bit(file, level->field[x][y]);
7307 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7312 for (y = 0; y < level->fieldy; y++)
7313 for (x = 0; x < level->fieldx; x++)
7314 chunk_size += putFile16BitBE(file, level->field[x][y]);
7319 #if ENABLE_HISTORIC_CHUNKS
7320 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7324 putFile8Bit(file, EL_YAMYAM);
7325 putFile8Bit(file, level->num_yamyam_contents);
7326 putFile8Bit(file, 0);
7327 putFile8Bit(file, 0);
7329 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7330 for (y = 0; y < 3; y++)
7331 for (x = 0; x < 3; x++)
7332 if (level->encoding_16bit_field)
7333 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7335 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7339 #if ENABLE_HISTORIC_CHUNKS
7340 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7343 int num_contents, content_xsize, content_ysize;
7344 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7346 if (element == EL_YAMYAM)
7348 num_contents = level->num_yamyam_contents;
7352 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7353 for (y = 0; y < 3; y++)
7354 for (x = 0; x < 3; x++)
7355 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7357 else if (element == EL_BD_AMOEBA)
7363 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7364 for (y = 0; y < 3; y++)
7365 for (x = 0; x < 3; x++)
7366 content_array[i][x][y] = EL_EMPTY;
7367 content_array[0][0][0] = level->amoeba_content;
7371 // chunk header already written -- write empty chunk data
7372 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7374 Warn("cannot save content for element '%d'", element);
7379 putFile16BitBE(file, element);
7380 putFile8Bit(file, num_contents);
7381 putFile8Bit(file, content_xsize);
7382 putFile8Bit(file, content_ysize);
7384 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7386 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7387 for (y = 0; y < 3; y++)
7388 for (x = 0; x < 3; x++)
7389 putFile16BitBE(file, content_array[i][x][y]);
7393 #if ENABLE_HISTORIC_CHUNKS
7394 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7396 int envelope_nr = element - EL_ENVELOPE_1;
7397 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7401 chunk_size += putFile16BitBE(file, element);
7402 chunk_size += putFile16BitBE(file, envelope_len);
7403 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7404 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7406 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7407 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7409 for (i = 0; i < envelope_len; i++)
7410 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7416 #if ENABLE_HISTORIC_CHUNKS
7417 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7418 int num_changed_custom_elements)
7422 putFile16BitBE(file, num_changed_custom_elements);
7424 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7426 int element = EL_CUSTOM_START + i;
7428 struct ElementInfo *ei = &element_info[element];
7430 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7432 if (check < num_changed_custom_elements)
7434 putFile16BitBE(file, element);
7435 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7442 if (check != num_changed_custom_elements) // should not happen
7443 Warn("inconsistent number of custom element properties");
7447 #if ENABLE_HISTORIC_CHUNKS
7448 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7449 int num_changed_custom_elements)
7453 putFile16BitBE(file, num_changed_custom_elements);
7455 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7457 int element = EL_CUSTOM_START + i;
7459 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7461 if (check < num_changed_custom_elements)
7463 putFile16BitBE(file, element);
7464 putFile16BitBE(file, element_info[element].change->target_element);
7471 if (check != num_changed_custom_elements) // should not happen
7472 Warn("inconsistent number of custom target elements");
7476 #if ENABLE_HISTORIC_CHUNKS
7477 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7478 int num_changed_custom_elements)
7480 int i, j, x, y, check = 0;
7482 putFile16BitBE(file, num_changed_custom_elements);
7484 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7486 int element = EL_CUSTOM_START + i;
7487 struct ElementInfo *ei = &element_info[element];
7489 if (ei->modified_settings)
7491 if (check < num_changed_custom_elements)
7493 putFile16BitBE(file, element);
7495 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7496 putFile8Bit(file, ei->description[j]);
7498 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7500 // some free bytes for future properties and padding
7501 WriteUnusedBytesToFile(file, 7);
7503 putFile8Bit(file, ei->use_gfx_element);
7504 putFile16BitBE(file, ei->gfx_element_initial);
7506 putFile8Bit(file, ei->collect_score_initial);
7507 putFile8Bit(file, ei->collect_count_initial);
7509 putFile16BitBE(file, ei->push_delay_fixed);
7510 putFile16BitBE(file, ei->push_delay_random);
7511 putFile16BitBE(file, ei->move_delay_fixed);
7512 putFile16BitBE(file, ei->move_delay_random);
7514 putFile16BitBE(file, ei->move_pattern);
7515 putFile8Bit(file, ei->move_direction_initial);
7516 putFile8Bit(file, ei->move_stepsize);
7518 for (y = 0; y < 3; y++)
7519 for (x = 0; x < 3; x++)
7520 putFile16BitBE(file, ei->content.e[x][y]);
7522 putFile32BitBE(file, ei->change->events);
7524 putFile16BitBE(file, ei->change->target_element);
7526 putFile16BitBE(file, ei->change->delay_fixed);
7527 putFile16BitBE(file, ei->change->delay_random);
7528 putFile16BitBE(file, ei->change->delay_frames);
7530 putFile16BitBE(file, ei->change->initial_trigger_element);
7532 putFile8Bit(file, ei->change->explode);
7533 putFile8Bit(file, ei->change->use_target_content);
7534 putFile8Bit(file, ei->change->only_if_complete);
7535 putFile8Bit(file, ei->change->use_random_replace);
7537 putFile8Bit(file, ei->change->random_percentage);
7538 putFile8Bit(file, ei->change->replace_when);
7540 for (y = 0; y < 3; y++)
7541 for (x = 0; x < 3; x++)
7542 putFile16BitBE(file, ei->change->content.e[x][y]);
7544 putFile8Bit(file, ei->slippery_type);
7546 // some free bytes for future properties and padding
7547 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7554 if (check != num_changed_custom_elements) // should not happen
7555 Warn("inconsistent number of custom element properties");
7559 #if ENABLE_HISTORIC_CHUNKS
7560 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7562 struct ElementInfo *ei = &element_info[element];
7565 // ---------- custom element base property values (96 bytes) ----------------
7567 putFile16BitBE(file, element);
7569 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7570 putFile8Bit(file, ei->description[i]);
7572 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7574 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7576 putFile8Bit(file, ei->num_change_pages);
7578 putFile16BitBE(file, ei->ce_value_fixed_initial);
7579 putFile16BitBE(file, ei->ce_value_random_initial);
7580 putFile8Bit(file, ei->use_last_ce_value);
7582 putFile8Bit(file, ei->use_gfx_element);
7583 putFile16BitBE(file, ei->gfx_element_initial);
7585 putFile8Bit(file, ei->collect_score_initial);
7586 putFile8Bit(file, ei->collect_count_initial);
7588 putFile8Bit(file, ei->drop_delay_fixed);
7589 putFile8Bit(file, ei->push_delay_fixed);
7590 putFile8Bit(file, ei->drop_delay_random);
7591 putFile8Bit(file, ei->push_delay_random);
7592 putFile16BitBE(file, ei->move_delay_fixed);
7593 putFile16BitBE(file, ei->move_delay_random);
7595 // bits 0 - 15 of "move_pattern" ...
7596 putFile16BitBE(file, ei->move_pattern & 0xffff);
7597 putFile8Bit(file, ei->move_direction_initial);
7598 putFile8Bit(file, ei->move_stepsize);
7600 putFile8Bit(file, ei->slippery_type);
7602 for (y = 0; y < 3; y++)
7603 for (x = 0; x < 3; x++)
7604 putFile16BitBE(file, ei->content.e[x][y]);
7606 putFile16BitBE(file, ei->move_enter_element);
7607 putFile16BitBE(file, ei->move_leave_element);
7608 putFile8Bit(file, ei->move_leave_type);
7610 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7611 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7613 putFile8Bit(file, ei->access_direction);
7615 putFile8Bit(file, ei->explosion_delay);
7616 putFile8Bit(file, ei->ignition_delay);
7617 putFile8Bit(file, ei->explosion_type);
7619 // some free bytes for future custom property values and padding
7620 WriteUnusedBytesToFile(file, 1);
7622 // ---------- change page property values (48 bytes) ------------------------
7624 for (i = 0; i < ei->num_change_pages; i++)
7626 struct ElementChangeInfo *change = &ei->change_page[i];
7627 unsigned int event_bits;
7629 // bits 0 - 31 of "has_event[]" ...
7631 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7632 if (change->has_event[j])
7633 event_bits |= (1u << j);
7634 putFile32BitBE(file, event_bits);
7636 putFile16BitBE(file, change->target_element);
7638 putFile16BitBE(file, change->delay_fixed);
7639 putFile16BitBE(file, change->delay_random);
7640 putFile16BitBE(file, change->delay_frames);
7642 putFile16BitBE(file, change->initial_trigger_element);
7644 putFile8Bit(file, change->explode);
7645 putFile8Bit(file, change->use_target_content);
7646 putFile8Bit(file, change->only_if_complete);
7647 putFile8Bit(file, change->use_random_replace);
7649 putFile8Bit(file, change->random_percentage);
7650 putFile8Bit(file, change->replace_when);
7652 for (y = 0; y < 3; y++)
7653 for (x = 0; x < 3; x++)
7654 putFile16BitBE(file, change->target_content.e[x][y]);
7656 putFile8Bit(file, change->can_change);
7658 putFile8Bit(file, change->trigger_side);
7660 putFile8Bit(file, change->trigger_player);
7661 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7662 log_2(change->trigger_page)));
7664 putFile8Bit(file, change->has_action);
7665 putFile8Bit(file, change->action_type);
7666 putFile8Bit(file, change->action_mode);
7667 putFile16BitBE(file, change->action_arg);
7669 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7671 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7672 if (change->has_event[j])
7673 event_bits |= (1u << (j - 32));
7674 putFile8Bit(file, event_bits);
7679 #if ENABLE_HISTORIC_CHUNKS
7680 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7682 struct ElementInfo *ei = &element_info[element];
7683 struct ElementGroupInfo *group = ei->group;
7686 putFile16BitBE(file, element);
7688 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7689 putFile8Bit(file, ei->description[i]);
7691 putFile8Bit(file, group->num_elements);
7693 putFile8Bit(file, ei->use_gfx_element);
7694 putFile16BitBE(file, ei->gfx_element_initial);
7696 putFile8Bit(file, group->choice_mode);
7698 // some free bytes for future values and padding
7699 WriteUnusedBytesToFile(file, 3);
7701 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7702 putFile16BitBE(file, group->element[i]);
7706 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7707 boolean write_element)
7709 int save_type = entry->save_type;
7710 int data_type = entry->data_type;
7711 int conf_type = entry->conf_type;
7712 int byte_mask = conf_type & CONF_MASK_BYTES;
7713 int element = entry->element;
7714 int default_value = entry->default_value;
7716 boolean modified = FALSE;
7718 if (byte_mask != CONF_MASK_MULTI_BYTES)
7720 void *value_ptr = entry->value;
7721 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7724 // check if any settings have been modified before saving them
7725 if (value != default_value)
7728 // do not save if explicitly told or if unmodified default settings
7729 if ((save_type == SAVE_CONF_NEVER) ||
7730 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7734 num_bytes += putFile16BitBE(file, element);
7736 num_bytes += putFile8Bit(file, conf_type);
7737 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7738 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7739 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7742 else if (data_type == TYPE_STRING)
7744 char *default_string = entry->default_string;
7745 char *string = (char *)(entry->value);
7746 int string_length = strlen(string);
7749 // check if any settings have been modified before saving them
7750 if (!strEqual(string, default_string))
7753 // do not save if explicitly told or if unmodified default settings
7754 if ((save_type == SAVE_CONF_NEVER) ||
7755 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7759 num_bytes += putFile16BitBE(file, element);
7761 num_bytes += putFile8Bit(file, conf_type);
7762 num_bytes += putFile16BitBE(file, string_length);
7764 for (i = 0; i < string_length; i++)
7765 num_bytes += putFile8Bit(file, string[i]);
7767 else if (data_type == TYPE_ELEMENT_LIST)
7769 int *element_array = (int *)(entry->value);
7770 int num_elements = *(int *)(entry->num_entities);
7773 // check if any settings have been modified before saving them
7774 for (i = 0; i < num_elements; i++)
7775 if (element_array[i] != default_value)
7778 // do not save if explicitly told or if unmodified default settings
7779 if ((save_type == SAVE_CONF_NEVER) ||
7780 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7784 num_bytes += putFile16BitBE(file, element);
7786 num_bytes += putFile8Bit(file, conf_type);
7787 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7789 for (i = 0; i < num_elements; i++)
7790 num_bytes += putFile16BitBE(file, element_array[i]);
7792 else if (data_type == TYPE_CONTENT_LIST)
7794 struct Content *content = (struct Content *)(entry->value);
7795 int num_contents = *(int *)(entry->num_entities);
7798 // check if any settings have been modified before saving them
7799 for (i = 0; i < num_contents; i++)
7800 for (y = 0; y < 3; y++)
7801 for (x = 0; x < 3; x++)
7802 if (content[i].e[x][y] != default_value)
7805 // do not save if explicitly told or if unmodified default settings
7806 if ((save_type == SAVE_CONF_NEVER) ||
7807 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7811 num_bytes += putFile16BitBE(file, element);
7813 num_bytes += putFile8Bit(file, conf_type);
7814 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7816 for (i = 0; i < num_contents; i++)
7817 for (y = 0; y < 3; y++)
7818 for (x = 0; x < 3; x++)
7819 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7825 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7830 li = *level; // copy level data into temporary buffer
7832 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7833 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7838 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7843 li = *level; // copy level data into temporary buffer
7845 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7846 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7851 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7853 int envelope_nr = element - EL_ENVELOPE_1;
7857 chunk_size += putFile16BitBE(file, element);
7859 // copy envelope data into temporary buffer
7860 xx_envelope = level->envelope[envelope_nr];
7862 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7863 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7868 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7870 struct ElementInfo *ei = &element_info[element];
7874 chunk_size += putFile16BitBE(file, element);
7876 xx_ei = *ei; // copy element data into temporary buffer
7878 // set default description string for this specific element
7879 strcpy(xx_default_description, getDefaultElementDescription(ei));
7881 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7882 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7884 for (i = 0; i < ei->num_change_pages; i++)
7886 struct ElementChangeInfo *change = &ei->change_page[i];
7888 xx_current_change_page = i;
7890 xx_change = *change; // copy change data into temporary buffer
7893 setEventBitsFromEventFlags(change);
7895 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7896 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7903 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7905 struct ElementInfo *ei = &element_info[element];
7906 struct ElementGroupInfo *group = ei->group;
7910 chunk_size += putFile16BitBE(file, element);
7912 xx_ei = *ei; // copy element data into temporary buffer
7913 xx_group = *group; // copy group data into temporary buffer
7915 // set default description string for this specific element
7916 strcpy(xx_default_description, getDefaultElementDescription(ei));
7918 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7919 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7924 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7926 struct ElementInfo *ei = &element_info[element];
7930 chunk_size += putFile16BitBE(file, element);
7932 xx_ei = *ei; // copy element data into temporary buffer
7934 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7935 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7940 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7941 boolean save_as_template)
7947 if (!(file = fopen(filename, MODE_WRITE)))
7949 Warn("cannot save level file '%s'", filename);
7954 level->file_version = FILE_VERSION_ACTUAL;
7955 level->game_version = GAME_VERSION_ACTUAL;
7957 level->creation_date = getCurrentDate();
7959 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7960 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7962 chunk_size = SaveLevel_VERS(NULL, level);
7963 putFileChunkBE(file, "VERS", chunk_size);
7964 SaveLevel_VERS(file, level);
7966 chunk_size = SaveLevel_DATE(NULL, level);
7967 putFileChunkBE(file, "DATE", chunk_size);
7968 SaveLevel_DATE(file, level);
7970 chunk_size = SaveLevel_NAME(NULL, level);
7971 putFileChunkBE(file, "NAME", chunk_size);
7972 SaveLevel_NAME(file, level);
7974 chunk_size = SaveLevel_AUTH(NULL, level);
7975 putFileChunkBE(file, "AUTH", chunk_size);
7976 SaveLevel_AUTH(file, level);
7978 chunk_size = SaveLevel_INFO(NULL, level);
7979 putFileChunkBE(file, "INFO", chunk_size);
7980 SaveLevel_INFO(file, level);
7982 chunk_size = SaveLevel_BODY(NULL, level);
7983 putFileChunkBE(file, "BODY", chunk_size);
7984 SaveLevel_BODY(file, level);
7986 chunk_size = SaveLevel_ELEM(NULL, level);
7987 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7989 putFileChunkBE(file, "ELEM", chunk_size);
7990 SaveLevel_ELEM(file, level);
7993 for (i = 0; i < NUM_ENVELOPES; i++)
7995 int element = EL_ENVELOPE_1 + i;
7997 chunk_size = SaveLevel_NOTE(NULL, level, element);
7998 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8000 putFileChunkBE(file, "NOTE", chunk_size);
8001 SaveLevel_NOTE(file, level, element);
8005 // if not using template level, check for non-default custom/group elements
8006 if (!level->use_custom_template || save_as_template)
8008 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8010 int element = EL_CUSTOM_START + i;
8012 chunk_size = SaveLevel_CUSX(NULL, level, element);
8013 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8015 putFileChunkBE(file, "CUSX", chunk_size);
8016 SaveLevel_CUSX(file, level, element);
8020 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8022 int element = EL_GROUP_START + i;
8024 chunk_size = SaveLevel_GRPX(NULL, level, element);
8025 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8027 putFileChunkBE(file, "GRPX", chunk_size);
8028 SaveLevel_GRPX(file, level, element);
8032 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8034 int element = GET_EMPTY_ELEMENT(i);
8036 chunk_size = SaveLevel_EMPX(NULL, level, element);
8037 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8039 putFileChunkBE(file, "EMPX", chunk_size);
8040 SaveLevel_EMPX(file, level, element);
8047 SetFilePermissions(filename, PERMS_PRIVATE);
8050 void SaveLevel(int nr)
8052 char *filename = getDefaultLevelFilename(nr);
8054 SaveLevelFromFilename(&level, filename, FALSE);
8057 void SaveLevelTemplate(void)
8059 char *filename = getLocalLevelTemplateFilename();
8061 SaveLevelFromFilename(&level, filename, TRUE);
8064 boolean SaveLevelChecked(int nr)
8066 char *filename = getDefaultLevelFilename(nr);
8067 boolean new_level = !fileExists(filename);
8068 boolean level_saved = FALSE;
8070 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8075 Request("Level saved!", REQ_CONFIRM);
8083 void DumpLevel(struct LevelInfo *level)
8085 if (level->no_level_file || level->no_valid_file)
8087 Warn("cannot dump -- no valid level file found");
8093 Print("Level xxx (file version %08d, game version %08d)\n",
8094 level->file_version, level->game_version);
8097 Print("Level author: '%s'\n", level->author);
8098 Print("Level title: '%s'\n", level->name);
8100 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8102 Print("Level time: %d seconds\n", level->time);
8103 Print("Gems needed: %d\n", level->gems_needed);
8105 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8106 Print("Time for wheel: %d seconds\n", level->time_wheel);
8107 Print("Time for light: %d seconds\n", level->time_light);
8108 Print("Time for timegate: %d seconds\n", level->time_timegate);
8110 Print("Amoeba speed: %d\n", level->amoeba_speed);
8113 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8114 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8115 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8116 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8117 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8118 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8124 for (i = 0; i < NUM_ENVELOPES; i++)
8126 char *text = level->envelope[i].text;
8127 int text_len = strlen(text);
8128 boolean has_text = FALSE;
8130 for (j = 0; j < text_len; j++)
8131 if (text[j] != ' ' && text[j] != '\n')
8137 Print("Envelope %d:\n'%s'\n", i + 1, text);
8145 void DumpLevels(void)
8147 static LevelDirTree *dumplevel_leveldir = NULL;
8149 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8150 global.dumplevel_leveldir);
8152 if (dumplevel_leveldir == NULL)
8153 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8155 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8156 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8157 Fail("no such level number: %d", global.dumplevel_level_nr);
8159 leveldir_current = dumplevel_leveldir;
8161 LoadLevel(global.dumplevel_level_nr);
8168 // ============================================================================
8169 // tape file functions
8170 // ============================================================================
8172 static void setTapeInfoToDefaults(void)
8176 // always start with reliable default values (empty tape)
8179 // default values (also for pre-1.2 tapes) with only the first player
8180 tape.player_participates[0] = TRUE;
8181 for (i = 1; i < MAX_PLAYERS; i++)
8182 tape.player_participates[i] = FALSE;
8184 // at least one (default: the first) player participates in every tape
8185 tape.num_participating_players = 1;
8187 tape.property_bits = TAPE_PROPERTY_NONE;
8189 tape.level_nr = level_nr;
8191 tape.changed = FALSE;
8192 tape.solved = FALSE;
8194 tape.recording = FALSE;
8195 tape.playing = FALSE;
8196 tape.pausing = FALSE;
8198 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8199 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8201 tape.no_info_chunk = TRUE;
8202 tape.no_valid_file = FALSE;
8205 static int getTapePosSize(struct TapeInfo *tape)
8207 int tape_pos_size = 0;
8209 if (tape->use_key_actions)
8210 tape_pos_size += tape->num_participating_players;
8212 if (tape->use_mouse_actions)
8213 tape_pos_size += 3; // x and y position and mouse button mask
8215 tape_pos_size += 1; // tape action delay value
8217 return tape_pos_size;
8220 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8222 tape->use_key_actions = FALSE;
8223 tape->use_mouse_actions = FALSE;
8225 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8226 tape->use_key_actions = TRUE;
8228 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8229 tape->use_mouse_actions = TRUE;
8232 static int getTapeActionValue(struct TapeInfo *tape)
8234 return (tape->use_key_actions &&
8235 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8236 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8237 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8238 TAPE_ACTIONS_DEFAULT);
8241 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8243 tape->file_version = getFileVersion(file);
8244 tape->game_version = getFileVersion(file);
8249 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8253 tape->random_seed = getFile32BitBE(file);
8254 tape->date = getFile32BitBE(file);
8255 tape->length = getFile32BitBE(file);
8257 // read header fields that are new since version 1.2
8258 if (tape->file_version >= FILE_VERSION_1_2)
8260 byte store_participating_players = getFile8Bit(file);
8263 // since version 1.2, tapes store which players participate in the tape
8264 tape->num_participating_players = 0;
8265 for (i = 0; i < MAX_PLAYERS; i++)
8267 tape->player_participates[i] = FALSE;
8269 if (store_participating_players & (1 << i))
8271 tape->player_participates[i] = TRUE;
8272 tape->num_participating_players++;
8276 setTapeActionFlags(tape, getFile8Bit(file));
8278 tape->property_bits = getFile8Bit(file);
8279 tape->solved = getFile8Bit(file);
8281 engine_version = getFileVersion(file);
8282 if (engine_version > 0)
8283 tape->engine_version = engine_version;
8285 tape->engine_version = tape->game_version;
8291 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8293 tape->scr_fieldx = getFile8Bit(file);
8294 tape->scr_fieldy = getFile8Bit(file);
8299 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8301 char *level_identifier = NULL;
8302 int level_identifier_size;
8305 tape->no_info_chunk = FALSE;
8307 level_identifier_size = getFile16BitBE(file);
8309 level_identifier = checked_malloc(level_identifier_size);
8311 for (i = 0; i < level_identifier_size; i++)
8312 level_identifier[i] = getFile8Bit(file);
8314 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8315 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8317 checked_free(level_identifier);
8319 tape->level_nr = getFile16BitBE(file);
8321 chunk_size = 2 + level_identifier_size + 2;
8326 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8329 int tape_pos_size = getTapePosSize(tape);
8330 int chunk_size_expected = tape_pos_size * tape->length;
8332 if (chunk_size_expected != chunk_size)
8334 ReadUnusedBytesFromFile(file, chunk_size);
8335 return chunk_size_expected;
8338 for (i = 0; i < tape->length; i++)
8340 if (i >= MAX_TAPE_LEN)
8342 Warn("tape truncated -- size exceeds maximum tape size %d",
8345 // tape too large; read and ignore remaining tape data from this chunk
8346 for (;i < tape->length; i++)
8347 ReadUnusedBytesFromFile(file, tape_pos_size);
8352 if (tape->use_key_actions)
8354 for (j = 0; j < MAX_PLAYERS; j++)
8356 tape->pos[i].action[j] = MV_NONE;
8358 if (tape->player_participates[j])
8359 tape->pos[i].action[j] = getFile8Bit(file);
8363 if (tape->use_mouse_actions)
8365 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8366 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8367 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8370 tape->pos[i].delay = getFile8Bit(file);
8372 if (tape->file_version == FILE_VERSION_1_0)
8374 // eliminate possible diagonal moves in old tapes
8375 // this is only for backward compatibility
8377 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8378 byte action = tape->pos[i].action[0];
8379 int k, num_moves = 0;
8381 for (k = 0; k < 4; k++)
8383 if (action & joy_dir[k])
8385 tape->pos[i + num_moves].action[0] = joy_dir[k];
8387 tape->pos[i + num_moves].delay = 0;
8396 tape->length += num_moves;
8399 else if (tape->file_version < FILE_VERSION_2_0)
8401 // convert pre-2.0 tapes to new tape format
8403 if (tape->pos[i].delay > 1)
8406 tape->pos[i + 1] = tape->pos[i];
8407 tape->pos[i + 1].delay = 1;
8410 for (j = 0; j < MAX_PLAYERS; j++)
8411 tape->pos[i].action[j] = MV_NONE;
8412 tape->pos[i].delay--;
8419 if (checkEndOfFile(file))
8423 if (i != tape->length)
8424 chunk_size = tape_pos_size * i;
8429 static void LoadTape_SokobanSolution(char *filename)
8432 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8434 if (!(file = openFile(filename, MODE_READ)))
8436 tape.no_valid_file = TRUE;
8441 while (!checkEndOfFile(file))
8443 unsigned char c = getByteFromFile(file);
8445 if (checkEndOfFile(file))
8452 tape.pos[tape.length].action[0] = MV_UP;
8453 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8459 tape.pos[tape.length].action[0] = MV_DOWN;
8460 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8466 tape.pos[tape.length].action[0] = MV_LEFT;
8467 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8473 tape.pos[tape.length].action[0] = MV_RIGHT;
8474 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8482 // ignore white-space characters
8486 tape.no_valid_file = TRUE;
8488 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8496 if (tape.no_valid_file)
8499 tape.length_frames = GetTapeLengthFrames();
8500 tape.length_seconds = GetTapeLengthSeconds();
8503 void LoadTapeFromFilename(char *filename)
8505 char cookie[MAX_LINE_LEN];
8506 char chunk_name[CHUNK_ID_LEN + 1];
8510 // always start with reliable default values
8511 setTapeInfoToDefaults();
8513 if (strSuffix(filename, ".sln"))
8515 LoadTape_SokobanSolution(filename);
8520 if (!(file = openFile(filename, MODE_READ)))
8522 tape.no_valid_file = TRUE;
8527 getFileChunkBE(file, chunk_name, NULL);
8528 if (strEqual(chunk_name, "RND1"))
8530 getFile32BitBE(file); // not used
8532 getFileChunkBE(file, chunk_name, NULL);
8533 if (!strEqual(chunk_name, "TAPE"))
8535 tape.no_valid_file = TRUE;
8537 Warn("unknown format of tape file '%s'", filename);
8544 else // check for pre-2.0 file format with cookie string
8546 strcpy(cookie, chunk_name);
8547 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8549 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8550 cookie[strlen(cookie) - 1] = '\0';
8552 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8554 tape.no_valid_file = TRUE;
8556 Warn("unknown format of tape file '%s'", filename);
8563 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8565 tape.no_valid_file = TRUE;
8567 Warn("unsupported version of tape file '%s'", filename);
8574 // pre-2.0 tape files have no game version, so use file version here
8575 tape.game_version = tape.file_version;
8578 if (tape.file_version < FILE_VERSION_1_2)
8580 // tape files from versions before 1.2.0 without chunk structure
8581 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8582 LoadTape_BODY(file, 2 * tape.length, &tape);
8590 int (*loader)(File *, int, struct TapeInfo *);
8594 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8595 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8596 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8597 { "INFO", -1, LoadTape_INFO },
8598 { "BODY", -1, LoadTape_BODY },
8602 while (getFileChunkBE(file, chunk_name, &chunk_size))
8606 while (chunk_info[i].name != NULL &&
8607 !strEqual(chunk_name, chunk_info[i].name))
8610 if (chunk_info[i].name == NULL)
8612 Warn("unknown chunk '%s' in tape file '%s'",
8613 chunk_name, filename);
8615 ReadUnusedBytesFromFile(file, chunk_size);
8617 else if (chunk_info[i].size != -1 &&
8618 chunk_info[i].size != chunk_size)
8620 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8621 chunk_size, chunk_name, filename);
8623 ReadUnusedBytesFromFile(file, chunk_size);
8627 // call function to load this tape chunk
8628 int chunk_size_expected =
8629 (chunk_info[i].loader)(file, chunk_size, &tape);
8631 // the size of some chunks cannot be checked before reading other
8632 // chunks first (like "HEAD" and "BODY") that contain some header
8633 // information, so check them here
8634 if (chunk_size_expected != chunk_size)
8636 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8637 chunk_size, chunk_name, filename);
8645 tape.length_frames = GetTapeLengthFrames();
8646 tape.length_seconds = GetTapeLengthSeconds();
8649 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8651 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8653 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8654 tape.engine_version);
8658 void LoadTape(int nr)
8660 char *filename = getTapeFilename(nr);
8662 LoadTapeFromFilename(filename);
8665 void LoadSolutionTape(int nr)
8667 char *filename = getSolutionTapeFilename(nr);
8669 LoadTapeFromFilename(filename);
8671 if (TAPE_IS_EMPTY(tape))
8673 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8674 level.native_bd_level->replay != NULL)
8675 CopyNativeTape_BD_to_RND(&level);
8676 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8677 level.native_sp_level->demo.is_available)
8678 CopyNativeTape_SP_to_RND(&level);
8682 void LoadScoreTape(char *score_tape_basename, int nr)
8684 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8686 LoadTapeFromFilename(filename);
8689 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8691 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8693 LoadTapeFromFilename(filename);
8696 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8698 // chunk required for team mode tapes with non-default screen size
8699 return (tape->num_participating_players > 1 &&
8700 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8701 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8704 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8706 putFileVersion(file, tape->file_version);
8707 putFileVersion(file, tape->game_version);
8710 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8713 byte store_participating_players = 0;
8715 // set bits for participating players for compact storage
8716 for (i = 0; i < MAX_PLAYERS; i++)
8717 if (tape->player_participates[i])
8718 store_participating_players |= (1 << i);
8720 putFile32BitBE(file, tape->random_seed);
8721 putFile32BitBE(file, tape->date);
8722 putFile32BitBE(file, tape->length);
8724 putFile8Bit(file, store_participating_players);
8726 putFile8Bit(file, getTapeActionValue(tape));
8728 putFile8Bit(file, tape->property_bits);
8729 putFile8Bit(file, tape->solved);
8731 putFileVersion(file, tape->engine_version);
8734 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8736 putFile8Bit(file, tape->scr_fieldx);
8737 putFile8Bit(file, tape->scr_fieldy);
8740 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8742 int level_identifier_size = strlen(tape->level_identifier) + 1;
8745 putFile16BitBE(file, level_identifier_size);
8747 for (i = 0; i < level_identifier_size; i++)
8748 putFile8Bit(file, tape->level_identifier[i]);
8750 putFile16BitBE(file, tape->level_nr);
8753 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8757 for (i = 0; i < tape->length; i++)
8759 if (tape->use_key_actions)
8761 for (j = 0; j < MAX_PLAYERS; j++)
8762 if (tape->player_participates[j])
8763 putFile8Bit(file, tape->pos[i].action[j]);
8766 if (tape->use_mouse_actions)
8768 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8769 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8770 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8773 putFile8Bit(file, tape->pos[i].delay);
8777 void SaveTapeToFilename(char *filename)
8781 int info_chunk_size;
8782 int body_chunk_size;
8784 if (!(file = fopen(filename, MODE_WRITE)))
8786 Warn("cannot save level recording file '%s'", filename);
8791 tape_pos_size = getTapePosSize(&tape);
8793 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8794 body_chunk_size = tape_pos_size * tape.length;
8796 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8797 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8799 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8800 SaveTape_VERS(file, &tape);
8802 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8803 SaveTape_HEAD(file, &tape);
8805 if (checkSaveTape_SCRN(&tape))
8807 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8808 SaveTape_SCRN(file, &tape);
8811 putFileChunkBE(file, "INFO", info_chunk_size);
8812 SaveTape_INFO(file, &tape);
8814 putFileChunkBE(file, "BODY", body_chunk_size);
8815 SaveTape_BODY(file, &tape);
8819 SetFilePermissions(filename, PERMS_PRIVATE);
8822 static void SaveTapeExt(char *filename)
8826 tape.file_version = FILE_VERSION_ACTUAL;
8827 tape.game_version = GAME_VERSION_ACTUAL;
8829 tape.num_participating_players = 0;
8831 // count number of participating players
8832 for (i = 0; i < MAX_PLAYERS; i++)
8833 if (tape.player_participates[i])
8834 tape.num_participating_players++;
8836 SaveTapeToFilename(filename);
8838 tape.changed = FALSE;
8841 void SaveTape(int nr)
8843 char *filename = getTapeFilename(nr);
8845 InitTapeDirectory(leveldir_current->subdir);
8847 SaveTapeExt(filename);
8850 void SaveScoreTape(int nr)
8852 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8854 // used instead of "leveldir_current->subdir" (for network games)
8855 InitScoreTapeDirectory(levelset.identifier, nr);
8857 SaveTapeExt(filename);
8860 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8861 unsigned int req_state_added)
8863 char *filename = getTapeFilename(nr);
8864 boolean new_tape = !fileExists(filename);
8865 boolean tape_saved = FALSE;
8867 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8872 Request(msg_saved, REQ_CONFIRM | req_state_added);
8880 boolean SaveTapeChecked(int nr)
8882 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8885 boolean SaveTapeChecked_LevelSolved(int nr)
8887 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8888 "Level solved! Tape saved!", REQ_STAY_OPEN);
8891 void DumpTape(struct TapeInfo *tape)
8893 int tape_frame_counter;
8896 if (tape->no_valid_file)
8898 Warn("cannot dump -- no valid tape file found");
8905 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8906 tape->level_nr, tape->file_version, tape->game_version);
8907 Print(" (effective engine version %08d)\n",
8908 tape->engine_version);
8909 Print("Level series identifier: '%s'\n", tape->level_identifier);
8911 Print("Solution tape: %s\n",
8912 tape->solved ? "yes" :
8913 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8915 Print("Special tape properties: ");
8916 if (tape->property_bits == TAPE_PROPERTY_NONE)
8918 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8919 Print("[em_random_bug]");
8920 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8921 Print("[game_speed]");
8922 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8924 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8925 Print("[single_step]");
8926 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8927 Print("[snapshot]");
8928 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8929 Print("[replayed]");
8930 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8931 Print("[tas_keys]");
8932 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8933 Print("[small_graphics]");
8936 int year2 = tape->date / 10000;
8937 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8938 int month_index_raw = (tape->date / 100) % 100;
8939 int month_index = month_index_raw % 12; // prevent invalid index
8940 int month = month_index + 1;
8941 int day = tape->date % 100;
8943 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8947 tape_frame_counter = 0;
8949 for (i = 0; i < tape->length; i++)
8951 if (i >= MAX_TAPE_LEN)
8956 for (j = 0; j < MAX_PLAYERS; j++)
8958 if (tape->player_participates[j])
8960 int action = tape->pos[i].action[j];
8962 Print("%d:%02x ", j, action);
8963 Print("[%c%c%c%c|%c%c] - ",
8964 (action & JOY_LEFT ? '<' : ' '),
8965 (action & JOY_RIGHT ? '>' : ' '),
8966 (action & JOY_UP ? '^' : ' '),
8967 (action & JOY_DOWN ? 'v' : ' '),
8968 (action & JOY_BUTTON_1 ? '1' : ' '),
8969 (action & JOY_BUTTON_2 ? '2' : ' '));
8973 Print("(%03d) ", tape->pos[i].delay);
8974 Print("[%05d]\n", tape_frame_counter);
8976 tape_frame_counter += tape->pos[i].delay;
8982 void DumpTapes(void)
8984 static LevelDirTree *dumptape_leveldir = NULL;
8986 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8987 global.dumptape_leveldir);
8989 if (dumptape_leveldir == NULL)
8990 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8992 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8993 global.dumptape_level_nr > dumptape_leveldir->last_level)
8994 Fail("no such level number: %d", global.dumptape_level_nr);
8996 leveldir_current = dumptape_leveldir;
8998 if (options.mytapes)
8999 LoadTape(global.dumptape_level_nr);
9001 LoadSolutionTape(global.dumptape_level_nr);
9009 // ============================================================================
9010 // score file functions
9011 // ============================================================================
9013 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9017 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9019 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9020 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9021 scores->entry[i].score = 0;
9022 scores->entry[i].time = 0;
9024 scores->entry[i].id = -1;
9025 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9026 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9027 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9028 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9029 strcpy(scores->entry[i].country_code, "??");
9032 scores->num_entries = 0;
9033 scores->last_added = -1;
9034 scores->last_added_local = -1;
9036 scores->updated = FALSE;
9037 scores->uploaded = FALSE;
9038 scores->tape_downloaded = FALSE;
9039 scores->force_last_added = FALSE;
9041 // The following values are intentionally not reset here:
9045 // - continue_playing
9046 // - continue_on_return
9049 static void setScoreInfoToDefaults(void)
9051 setScoreInfoToDefaultsExt(&scores);
9054 static void setServerScoreInfoToDefaults(void)
9056 setScoreInfoToDefaultsExt(&server_scores);
9059 static void LoadScore_OLD(int nr)
9062 char *filename = getScoreFilename(nr);
9063 char cookie[MAX_LINE_LEN];
9064 char line[MAX_LINE_LEN];
9068 if (!(file = fopen(filename, MODE_READ)))
9071 // check file identifier
9072 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9074 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9075 cookie[strlen(cookie) - 1] = '\0';
9077 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9079 Warn("unknown format of score file '%s'", filename);
9086 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9088 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9089 Warn("fscanf() failed; %s", strerror(errno));
9091 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9094 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9095 line[strlen(line) - 1] = '\0';
9097 for (line_ptr = line; *line_ptr; line_ptr++)
9099 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9101 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9102 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9111 static void ConvertScore_OLD(void)
9113 // only convert score to time for levels that rate playing time over score
9114 if (!level.rate_time_over_score)
9117 // convert old score to playing time for score-less levels (like Supaplex)
9118 int time_final_max = 999;
9121 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9123 int score = scores.entry[i].score;
9125 if (score > 0 && score < time_final_max)
9126 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9130 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9132 scores->file_version = getFileVersion(file);
9133 scores->game_version = getFileVersion(file);
9138 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9140 char *level_identifier = NULL;
9141 int level_identifier_size;
9144 level_identifier_size = getFile16BitBE(file);
9146 level_identifier = checked_malloc(level_identifier_size);
9148 for (i = 0; i < level_identifier_size; i++)
9149 level_identifier[i] = getFile8Bit(file);
9151 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9152 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9154 checked_free(level_identifier);
9156 scores->level_nr = getFile16BitBE(file);
9157 scores->num_entries = getFile16BitBE(file);
9159 chunk_size = 2 + level_identifier_size + 2 + 2;
9164 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9168 for (i = 0; i < scores->num_entries; i++)
9170 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9171 scores->entry[i].name[j] = getFile8Bit(file);
9173 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9176 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9181 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9185 for (i = 0; i < scores->num_entries; i++)
9186 scores->entry[i].score = getFile16BitBE(file);
9188 chunk_size = scores->num_entries * 2;
9193 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9197 for (i = 0; i < scores->num_entries; i++)
9198 scores->entry[i].score = getFile32BitBE(file);
9200 chunk_size = scores->num_entries * 4;
9205 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9209 for (i = 0; i < scores->num_entries; i++)
9210 scores->entry[i].time = getFile32BitBE(file);
9212 chunk_size = scores->num_entries * 4;
9217 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9221 for (i = 0; i < scores->num_entries; i++)
9223 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9224 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9226 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9229 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9234 void LoadScore(int nr)
9236 char *filename = getScoreFilename(nr);
9237 char cookie[MAX_LINE_LEN];
9238 char chunk_name[CHUNK_ID_LEN + 1];
9240 boolean old_score_file_format = FALSE;
9243 // always start with reliable default values
9244 setScoreInfoToDefaults();
9246 if (!(file = openFile(filename, MODE_READ)))
9249 getFileChunkBE(file, chunk_name, NULL);
9250 if (strEqual(chunk_name, "RND1"))
9252 getFile32BitBE(file); // not used
9254 getFileChunkBE(file, chunk_name, NULL);
9255 if (!strEqual(chunk_name, "SCOR"))
9257 Warn("unknown format of score file '%s'", filename);
9264 else // check for old file format with cookie string
9266 strcpy(cookie, chunk_name);
9267 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9269 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9270 cookie[strlen(cookie) - 1] = '\0';
9272 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9274 Warn("unknown format of score file '%s'", filename);
9281 old_score_file_format = TRUE;
9284 if (old_score_file_format)
9286 // score files from versions before 4.2.4.0 without chunk structure
9289 // convert score to time, if possible (mainly for Supaplex levels)
9298 int (*loader)(File *, int, struct ScoreInfo *);
9302 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9303 { "INFO", -1, LoadScore_INFO },
9304 { "NAME", -1, LoadScore_NAME },
9305 { "SCOR", -1, LoadScore_SCOR },
9306 { "SC4R", -1, LoadScore_SC4R },
9307 { "TIME", -1, LoadScore_TIME },
9308 { "TAPE", -1, LoadScore_TAPE },
9313 while (getFileChunkBE(file, chunk_name, &chunk_size))
9317 while (chunk_info[i].name != NULL &&
9318 !strEqual(chunk_name, chunk_info[i].name))
9321 if (chunk_info[i].name == NULL)
9323 Warn("unknown chunk '%s' in score file '%s'",
9324 chunk_name, filename);
9326 ReadUnusedBytesFromFile(file, chunk_size);
9328 else if (chunk_info[i].size != -1 &&
9329 chunk_info[i].size != chunk_size)
9331 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9332 chunk_size, chunk_name, filename);
9334 ReadUnusedBytesFromFile(file, chunk_size);
9338 // call function to load this score chunk
9339 int chunk_size_expected =
9340 (chunk_info[i].loader)(file, chunk_size, &scores);
9342 // the size of some chunks cannot be checked before reading other
9343 // chunks first (like "HEAD" and "BODY") that contain some header
9344 // information, so check them here
9345 if (chunk_size_expected != chunk_size)
9347 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9348 chunk_size, chunk_name, filename);
9357 #if ENABLE_HISTORIC_CHUNKS
9358 void SaveScore_OLD(int nr)
9361 char *filename = getScoreFilename(nr);
9364 // used instead of "leveldir_current->subdir" (for network games)
9365 InitScoreDirectory(levelset.identifier);
9367 if (!(file = fopen(filename, MODE_WRITE)))
9369 Warn("cannot save score for level %d", nr);
9374 fprintf(file, "%s\n\n", SCORE_COOKIE);
9376 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9377 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9381 SetFilePermissions(filename, PERMS_PRIVATE);
9385 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9387 putFileVersion(file, scores->file_version);
9388 putFileVersion(file, scores->game_version);
9391 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9393 int level_identifier_size = strlen(scores->level_identifier) + 1;
9396 putFile16BitBE(file, level_identifier_size);
9398 for (i = 0; i < level_identifier_size; i++)
9399 putFile8Bit(file, scores->level_identifier[i]);
9401 putFile16BitBE(file, scores->level_nr);
9402 putFile16BitBE(file, scores->num_entries);
9405 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9409 for (i = 0; i < scores->num_entries; i++)
9411 int name_size = strlen(scores->entry[i].name);
9413 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9414 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9418 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9422 for (i = 0; i < scores->num_entries; i++)
9423 putFile16BitBE(file, scores->entry[i].score);
9426 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9430 for (i = 0; i < scores->num_entries; i++)
9431 putFile32BitBE(file, scores->entry[i].score);
9434 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9438 for (i = 0; i < scores->num_entries; i++)
9439 putFile32BitBE(file, scores->entry[i].time);
9442 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9446 for (i = 0; i < scores->num_entries; i++)
9448 int size = strlen(scores->entry[i].tape_basename);
9450 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9451 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9455 static void SaveScoreToFilename(char *filename)
9458 int info_chunk_size;
9459 int name_chunk_size;
9460 int scor_chunk_size;
9461 int sc4r_chunk_size;
9462 int time_chunk_size;
9463 int tape_chunk_size;
9464 boolean has_large_score_values;
9467 if (!(file = fopen(filename, MODE_WRITE)))
9469 Warn("cannot save score file '%s'", filename);
9474 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9475 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9476 scor_chunk_size = scores.num_entries * 2;
9477 sc4r_chunk_size = scores.num_entries * 4;
9478 time_chunk_size = scores.num_entries * 4;
9479 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9481 has_large_score_values = FALSE;
9482 for (i = 0; i < scores.num_entries; i++)
9483 if (scores.entry[i].score > 0xffff)
9484 has_large_score_values = TRUE;
9486 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9487 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9489 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9490 SaveScore_VERS(file, &scores);
9492 putFileChunkBE(file, "INFO", info_chunk_size);
9493 SaveScore_INFO(file, &scores);
9495 putFileChunkBE(file, "NAME", name_chunk_size);
9496 SaveScore_NAME(file, &scores);
9498 if (has_large_score_values)
9500 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9501 SaveScore_SC4R(file, &scores);
9505 putFileChunkBE(file, "SCOR", scor_chunk_size);
9506 SaveScore_SCOR(file, &scores);
9509 putFileChunkBE(file, "TIME", time_chunk_size);
9510 SaveScore_TIME(file, &scores);
9512 putFileChunkBE(file, "TAPE", tape_chunk_size);
9513 SaveScore_TAPE(file, &scores);
9517 SetFilePermissions(filename, PERMS_PRIVATE);
9520 void SaveScore(int nr)
9522 char *filename = getScoreFilename(nr);
9525 // used instead of "leveldir_current->subdir" (for network games)
9526 InitScoreDirectory(levelset.identifier);
9528 scores.file_version = FILE_VERSION_ACTUAL;
9529 scores.game_version = GAME_VERSION_ACTUAL;
9531 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9532 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9533 scores.level_nr = level_nr;
9535 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9536 if (scores.entry[i].score == 0 &&
9537 scores.entry[i].time == 0 &&
9538 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9541 scores.num_entries = i;
9543 if (scores.num_entries == 0)
9546 SaveScoreToFilename(filename);
9549 static void LoadServerScoreFromCache(int nr)
9551 struct ScoreEntry score_entry;
9560 { &score_entry.score, FALSE, 0 },
9561 { &score_entry.time, FALSE, 0 },
9562 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9563 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9564 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9565 { &score_entry.id, FALSE, 0 },
9566 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9567 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9568 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9569 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9573 char *filename = getScoreCacheFilename(nr);
9574 SetupFileHash *score_hash = loadSetupFileHash(filename);
9577 server_scores.num_entries = 0;
9579 if (score_hash == NULL)
9582 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9584 score_entry = server_scores.entry[i];
9586 for (j = 0; score_mapping[j].value != NULL; j++)
9590 sprintf(token, "%02d.%d", i, j);
9592 char *value = getHashEntry(score_hash, token);
9597 if (score_mapping[j].is_string)
9599 char *score_value = (char *)score_mapping[j].value;
9600 int value_size = score_mapping[j].string_size;
9602 strncpy(score_value, value, value_size);
9603 score_value[value_size] = '\0';
9607 int *score_value = (int *)score_mapping[j].value;
9609 *score_value = atoi(value);
9612 server_scores.num_entries = i + 1;
9615 server_scores.entry[i] = score_entry;
9618 freeSetupFileHash(score_hash);
9621 void LoadServerScore(int nr, boolean download_score)
9623 if (!setup.use_api_server)
9626 // always start with reliable default values
9627 setServerScoreInfoToDefaults();
9629 // 1st step: load server scores from cache file (which may not exist)
9630 // (this should prevent reading it while the thread is writing to it)
9631 LoadServerScoreFromCache(nr);
9633 if (download_score && runtime.use_api_server)
9635 // 2nd step: download server scores from score server to cache file
9636 // (as thread, as it might time out if the server is not reachable)
9637 ApiGetScoreAsThread(nr);
9641 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9643 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9645 // if score tape not uploaded, ask for uploading missing tapes later
9646 if (!setup.has_remaining_tapes)
9647 setup.ask_for_remaining_tapes = TRUE;
9649 setup.provide_uploading_tapes = TRUE;
9650 setup.has_remaining_tapes = TRUE;
9652 SaveSetup_ServerSetup();
9655 void SaveServerScore(int nr, boolean tape_saved)
9657 if (!runtime.use_api_server)
9659 PrepareScoreTapesForUpload(leveldir_current->subdir);
9664 ApiAddScoreAsThread(nr, tape_saved, NULL);
9667 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9668 char *score_tape_filename)
9670 if (!runtime.use_api_server)
9673 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9676 void LoadLocalAndServerScore(int nr, boolean download_score)
9678 int last_added_local = scores.last_added_local;
9679 boolean force_last_added = scores.force_last_added;
9681 // needed if only showing server scores
9682 setScoreInfoToDefaults();
9684 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9687 // restore last added local score entry (before merging server scores)
9688 scores.last_added = scores.last_added_local = last_added_local;
9690 if (setup.use_api_server &&
9691 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9693 // load server scores from cache file and trigger update from server
9694 LoadServerScore(nr, download_score);
9696 // merge local scores with scores from server
9700 if (force_last_added)
9701 scores.force_last_added = force_last_added;
9705 // ============================================================================
9706 // setup file functions
9707 // ============================================================================
9709 #define TOKEN_STR_PLAYER_PREFIX "player_"
9712 static struct TokenInfo global_setup_tokens[] =
9716 &setup.player_name, "player_name"
9720 &setup.multiple_users, "multiple_users"
9724 &setup.sound, "sound"
9728 &setup.sound_loops, "repeating_sound_loops"
9732 &setup.sound_music, "background_music"
9736 &setup.sound_simple, "simple_sound_effects"
9740 &setup.toons, "toons"
9744 &setup.global_animations, "global_animations"
9748 &setup.scroll_delay, "scroll_delay"
9752 &setup.forced_scroll_delay, "forced_scroll_delay"
9756 &setup.scroll_delay_value, "scroll_delay_value"
9760 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9764 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9768 &setup.fade_screens, "fade_screens"
9772 &setup.autorecord, "automatic_tape_recording"
9776 &setup.autorecord_after_replay, "autorecord_after_replay"
9780 &setup.auto_pause_on_start, "auto_pause_on_start"
9784 &setup.show_titlescreen, "show_titlescreen"
9788 &setup.quick_doors, "quick_doors"
9792 &setup.team_mode, "team_mode"
9796 &setup.handicap, "handicap"
9800 &setup.skip_levels, "skip_levels"
9804 &setup.increment_levels, "increment_levels"
9808 &setup.auto_play_next_level, "auto_play_next_level"
9812 &setup.count_score_after_game, "count_score_after_game"
9816 &setup.show_scores_after_game, "show_scores_after_game"
9820 &setup.time_limit, "time_limit"
9824 &setup.fullscreen, "fullscreen"
9828 &setup.window_scaling_percent, "window_scaling_percent"
9832 &setup.window_scaling_quality, "window_scaling_quality"
9836 &setup.screen_rendering_mode, "screen_rendering_mode"
9840 &setup.vsync_mode, "vsync_mode"
9844 &setup.ask_on_escape, "ask_on_escape"
9848 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9852 &setup.ask_on_game_over, "ask_on_game_over"
9856 &setup.ask_on_quit_game, "ask_on_quit_game"
9860 &setup.ask_on_quit_program, "ask_on_quit_program"
9864 &setup.quick_switch, "quick_player_switch"
9868 &setup.input_on_focus, "input_on_focus"
9872 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9876 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9880 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9884 &setup.game_speed_extended, "game_speed_extended"
9888 &setup.game_frame_delay, "game_frame_delay"
9892 &setup.bd_skip_uncovering, "bd_skip_uncovering"
9896 &setup.bd_skip_hatching, "bd_skip_hatching"
9900 &setup.bd_scroll_delay, "bd_scroll_delay"
9904 &setup.bd_smooth_movements, "bd_smooth_movements"
9908 &setup.sp_show_border_elements, "sp_show_border_elements"
9912 &setup.small_game_graphics, "small_game_graphics"
9916 &setup.show_load_save_buttons, "show_load_save_buttons"
9920 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9924 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9928 &setup.graphics_set, "graphics_set"
9932 &setup.sounds_set, "sounds_set"
9936 &setup.music_set, "music_set"
9940 &setup.override_level_graphics, "override_level_graphics"
9944 &setup.override_level_sounds, "override_level_sounds"
9948 &setup.override_level_music, "override_level_music"
9952 &setup.volume_simple, "volume_simple"
9956 &setup.volume_loops, "volume_loops"
9960 &setup.volume_music, "volume_music"
9964 &setup.network_mode, "network_mode"
9968 &setup.network_player_nr, "network_player"
9972 &setup.network_server_hostname, "network_server_hostname"
9976 &setup.touch.control_type, "touch.control_type"
9980 &setup.touch.move_distance, "touch.move_distance"
9984 &setup.touch.drop_distance, "touch.drop_distance"
9988 &setup.touch.transparency, "touch.transparency"
9992 &setup.touch.draw_outlined, "touch.draw_outlined"
9996 &setup.touch.draw_pressed, "touch.draw_pressed"
10000 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10004 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10008 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10012 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10016 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10020 static struct TokenInfo auto_setup_tokens[] =
10024 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10028 static struct TokenInfo server_setup_tokens[] =
10032 &setup.player_uuid, "player_uuid"
10036 &setup.player_version, "player_version"
10040 &setup.use_api_server, TEST_PREFIX "use_api_server"
10044 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10048 &setup.api_server_password, TEST_PREFIX "api_server_password"
10052 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10056 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10060 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10064 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10068 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10072 static struct TokenInfo editor_setup_tokens[] =
10076 &setup.editor.el_classic, "editor.el_classic"
10080 &setup.editor.el_custom, "editor.el_custom"
10084 &setup.editor.el_user_defined, "editor.el_user_defined"
10088 &setup.editor.el_dynamic, "editor.el_dynamic"
10092 &setup.editor.el_headlines, "editor.el_headlines"
10096 &setup.editor.show_element_token, "editor.show_element_token"
10100 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10104 static struct TokenInfo editor_cascade_setup_tokens[] =
10108 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10112 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10116 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10120 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10124 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10128 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10132 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10136 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10140 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10144 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10148 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10152 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10156 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10160 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10164 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10168 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10172 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10176 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10180 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10184 static struct TokenInfo shortcut_setup_tokens[] =
10188 &setup.shortcut.save_game, "shortcut.save_game"
10192 &setup.shortcut.load_game, "shortcut.load_game"
10196 &setup.shortcut.restart_game, "shortcut.restart_game"
10200 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10204 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10208 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10212 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10216 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10220 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10224 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10228 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10232 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10236 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10240 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10244 &setup.shortcut.tape_record, "shortcut.tape_record"
10248 &setup.shortcut.tape_play, "shortcut.tape_play"
10252 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10256 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10260 &setup.shortcut.sound_music, "shortcut.sound_music"
10264 &setup.shortcut.snap_left, "shortcut.snap_left"
10268 &setup.shortcut.snap_right, "shortcut.snap_right"
10272 &setup.shortcut.snap_up, "shortcut.snap_up"
10276 &setup.shortcut.snap_down, "shortcut.snap_down"
10280 static struct SetupInputInfo setup_input;
10281 static struct TokenInfo player_setup_tokens[] =
10285 &setup_input.use_joystick, ".use_joystick"
10289 &setup_input.joy.device_name, ".joy.device_name"
10293 &setup_input.joy.xleft, ".joy.xleft"
10297 &setup_input.joy.xmiddle, ".joy.xmiddle"
10301 &setup_input.joy.xright, ".joy.xright"
10305 &setup_input.joy.yupper, ".joy.yupper"
10309 &setup_input.joy.ymiddle, ".joy.ymiddle"
10313 &setup_input.joy.ylower, ".joy.ylower"
10317 &setup_input.joy.snap, ".joy.snap_field"
10321 &setup_input.joy.drop, ".joy.place_bomb"
10325 &setup_input.key.left, ".key.move_left"
10329 &setup_input.key.right, ".key.move_right"
10333 &setup_input.key.up, ".key.move_up"
10337 &setup_input.key.down, ".key.move_down"
10341 &setup_input.key.snap, ".key.snap_field"
10345 &setup_input.key.drop, ".key.place_bomb"
10349 static struct TokenInfo system_setup_tokens[] =
10353 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10357 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10361 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10365 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10369 static struct TokenInfo internal_setup_tokens[] =
10373 &setup.internal.program_title, "program_title"
10377 &setup.internal.program_version, "program_version"
10381 &setup.internal.program_author, "program_author"
10385 &setup.internal.program_email, "program_email"
10389 &setup.internal.program_website, "program_website"
10393 &setup.internal.program_copyright, "program_copyright"
10397 &setup.internal.program_company, "program_company"
10401 &setup.internal.program_icon_file, "program_icon_file"
10405 &setup.internal.default_graphics_set, "default_graphics_set"
10409 &setup.internal.default_sounds_set, "default_sounds_set"
10413 &setup.internal.default_music_set, "default_music_set"
10417 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10421 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10425 &setup.internal.fallback_music_file, "fallback_music_file"
10429 &setup.internal.default_level_series, "default_level_series"
10433 &setup.internal.default_window_width, "default_window_width"
10437 &setup.internal.default_window_height, "default_window_height"
10441 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10445 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10449 &setup.internal.create_user_levelset, "create_user_levelset"
10453 &setup.internal.info_screens_from_main, "info_screens_from_main"
10457 &setup.internal.menu_game, "menu_game"
10461 &setup.internal.menu_engines, "menu_engines"
10465 &setup.internal.menu_editor, "menu_editor"
10469 &setup.internal.menu_graphics, "menu_graphics"
10473 &setup.internal.menu_sound, "menu_sound"
10477 &setup.internal.menu_artwork, "menu_artwork"
10481 &setup.internal.menu_input, "menu_input"
10485 &setup.internal.menu_touch, "menu_touch"
10489 &setup.internal.menu_shortcuts, "menu_shortcuts"
10493 &setup.internal.menu_exit, "menu_exit"
10497 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10501 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10505 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10509 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10513 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10517 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10521 &setup.internal.info_title, "info_title"
10525 &setup.internal.info_elements, "info_elements"
10529 &setup.internal.info_music, "info_music"
10533 &setup.internal.info_credits, "info_credits"
10537 &setup.internal.info_program, "info_program"
10541 &setup.internal.info_version, "info_version"
10545 &setup.internal.info_levelset, "info_levelset"
10549 &setup.internal.info_exit, "info_exit"
10553 static struct TokenInfo debug_setup_tokens[] =
10557 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10561 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10565 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10569 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10573 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10577 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10581 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10585 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10589 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10593 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10597 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10601 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10605 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10609 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10613 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10617 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10621 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10625 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10629 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10633 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10637 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10640 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10644 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10648 &setup.debug.xsn_mode, "debug.xsn_mode"
10652 &setup.debug.xsn_percent, "debug.xsn_percent"
10656 static struct TokenInfo options_setup_tokens[] =
10660 &setup.options.verbose, "options.verbose"
10664 &setup.options.debug, "options.debug"
10668 &setup.options.debug_mode, "options.debug_mode"
10672 static void setSetupInfoToDefaults(struct SetupInfo *si)
10676 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10678 si->multiple_users = TRUE;
10681 si->sound_loops = TRUE;
10682 si->sound_music = TRUE;
10683 si->sound_simple = TRUE;
10685 si->global_animations = TRUE;
10686 si->scroll_delay = TRUE;
10687 si->forced_scroll_delay = FALSE;
10688 si->scroll_delay_value = STD_SCROLL_DELAY;
10689 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10690 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10691 si->fade_screens = TRUE;
10692 si->autorecord = TRUE;
10693 si->autorecord_after_replay = TRUE;
10694 si->auto_pause_on_start = FALSE;
10695 si->show_titlescreen = TRUE;
10696 si->quick_doors = FALSE;
10697 si->team_mode = FALSE;
10698 si->handicap = TRUE;
10699 si->skip_levels = TRUE;
10700 si->increment_levels = TRUE;
10701 si->auto_play_next_level = TRUE;
10702 si->count_score_after_game = TRUE;
10703 si->show_scores_after_game = TRUE;
10704 si->time_limit = TRUE;
10705 si->fullscreen = FALSE;
10706 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10707 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10708 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10709 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10710 si->ask_on_escape = TRUE;
10711 si->ask_on_escape_editor = TRUE;
10712 si->ask_on_game_over = TRUE;
10713 si->ask_on_quit_game = TRUE;
10714 si->ask_on_quit_program = TRUE;
10715 si->quick_switch = FALSE;
10716 si->input_on_focus = FALSE;
10717 si->prefer_aga_graphics = TRUE;
10718 si->prefer_lowpass_sounds = FALSE;
10719 si->prefer_extra_panel_items = TRUE;
10720 si->game_speed_extended = FALSE;
10721 si->game_frame_delay = GAME_FRAME_DELAY;
10722 si->bd_skip_uncovering = FALSE;
10723 si->bd_skip_hatching = FALSE;
10724 si->bd_scroll_delay = TRUE;
10725 si->bd_smooth_movements = AUTO;
10726 si->sp_show_border_elements = FALSE;
10727 si->small_game_graphics = FALSE;
10728 si->show_load_save_buttons = FALSE;
10729 si->show_undo_redo_buttons = FALSE;
10730 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10732 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10733 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10734 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10736 si->override_level_graphics = FALSE;
10737 si->override_level_sounds = FALSE;
10738 si->override_level_music = FALSE;
10740 si->volume_simple = 100; // percent
10741 si->volume_loops = 100; // percent
10742 si->volume_music = 100; // percent
10744 si->network_mode = FALSE;
10745 si->network_player_nr = 0; // first player
10746 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10748 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10749 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10750 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10751 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10752 si->touch.draw_outlined = TRUE;
10753 si->touch.draw_pressed = TRUE;
10755 for (i = 0; i < 2; i++)
10757 char *default_grid_button[6][2] =
10763 { "111222", " vv " },
10764 { "111222", " vv " }
10766 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10767 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10768 int min_xsize = MIN(6, grid_xsize);
10769 int min_ysize = MIN(6, grid_ysize);
10770 int startx = grid_xsize - min_xsize;
10771 int starty = grid_ysize - min_ysize;
10774 // virtual buttons grid can only be set to defaults if video is initialized
10775 // (this will be repeated if virtual buttons are not loaded from setup file)
10776 if (video.initialized)
10778 si->touch.grid_xsize[i] = grid_xsize;
10779 si->touch.grid_ysize[i] = grid_ysize;
10783 si->touch.grid_xsize[i] = -1;
10784 si->touch.grid_ysize[i] = -1;
10787 for (x = 0; x < MAX_GRID_XSIZE; x++)
10788 for (y = 0; y < MAX_GRID_YSIZE; y++)
10789 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10791 for (x = 0; x < min_xsize; x++)
10792 for (y = 0; y < min_ysize; y++)
10793 si->touch.grid_button[i][x][starty + y] =
10794 default_grid_button[y][0][x];
10796 for (x = 0; x < min_xsize; x++)
10797 for (y = 0; y < min_ysize; y++)
10798 si->touch.grid_button[i][startx + x][starty + y] =
10799 default_grid_button[y][1][x];
10802 si->touch.grid_initialized = video.initialized;
10804 si->touch.overlay_buttons = FALSE;
10806 si->editor.el_boulderdash = TRUE;
10807 si->editor.el_boulderdash_native = TRUE;
10808 si->editor.el_emerald_mine = TRUE;
10809 si->editor.el_emerald_mine_club = TRUE;
10810 si->editor.el_more = TRUE;
10811 si->editor.el_sokoban = TRUE;
10812 si->editor.el_supaplex = TRUE;
10813 si->editor.el_diamond_caves = TRUE;
10814 si->editor.el_dx_boulderdash = TRUE;
10816 si->editor.el_mirror_magic = TRUE;
10817 si->editor.el_deflektor = TRUE;
10819 si->editor.el_chars = TRUE;
10820 si->editor.el_steel_chars = TRUE;
10822 si->editor.el_classic = TRUE;
10823 si->editor.el_custom = TRUE;
10825 si->editor.el_user_defined = FALSE;
10826 si->editor.el_dynamic = TRUE;
10828 si->editor.el_headlines = TRUE;
10830 si->editor.show_element_token = FALSE;
10832 si->editor.show_read_only_warning = TRUE;
10834 si->editor.use_template_for_new_levels = TRUE;
10836 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10837 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10838 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10839 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10840 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10842 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10843 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10844 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10845 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10846 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10848 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10849 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10850 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10851 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10852 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10853 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10855 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10856 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10857 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10859 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10860 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10861 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10862 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10864 for (i = 0; i < MAX_PLAYERS; i++)
10866 si->input[i].use_joystick = FALSE;
10867 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10868 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10869 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10870 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10871 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10872 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10873 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10874 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10875 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10876 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10877 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10878 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10879 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10880 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10881 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10884 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10885 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10886 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10887 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10889 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10890 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10891 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10892 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10893 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10894 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10895 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10897 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10899 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10900 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10901 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10903 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10904 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10905 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10907 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10908 si->internal.choose_from_top_leveldir = FALSE;
10909 si->internal.show_scaling_in_title = TRUE;
10910 si->internal.create_user_levelset = TRUE;
10911 si->internal.info_screens_from_main = FALSE;
10913 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10914 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10916 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10917 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10918 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10919 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10920 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10921 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10922 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10923 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10924 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10925 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10927 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10928 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10929 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10930 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10931 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10932 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10933 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10934 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10935 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10936 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10938 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10939 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10941 si->debug.show_frames_per_second = FALSE;
10943 si->debug.xsn_mode = AUTO;
10944 si->debug.xsn_percent = 0;
10946 si->options.verbose = FALSE;
10947 si->options.debug = FALSE;
10948 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
10950 #if defined(PLATFORM_ANDROID)
10951 si->fullscreen = TRUE;
10952 si->touch.overlay_buttons = TRUE;
10955 setHideSetupEntry(&setup.debug.xsn_mode);
10958 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10960 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10963 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10965 si->player_uuid = NULL; // (will be set later)
10966 si->player_version = 1; // (will be set later)
10968 si->use_api_server = TRUE;
10969 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10970 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10971 si->ask_for_uploading_tapes = TRUE;
10972 si->ask_for_remaining_tapes = FALSE;
10973 si->provide_uploading_tapes = TRUE;
10974 si->ask_for_using_api_server = TRUE;
10975 si->has_remaining_tapes = FALSE;
10978 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10980 si->editor_cascade.el_bd = TRUE;
10981 si->editor_cascade.el_bd_native = TRUE;
10982 si->editor_cascade.el_em = TRUE;
10983 si->editor_cascade.el_emc = TRUE;
10984 si->editor_cascade.el_rnd = TRUE;
10985 si->editor_cascade.el_sb = TRUE;
10986 si->editor_cascade.el_sp = TRUE;
10987 si->editor_cascade.el_dc = TRUE;
10988 si->editor_cascade.el_dx = TRUE;
10990 si->editor_cascade.el_mm = TRUE;
10991 si->editor_cascade.el_df = TRUE;
10993 si->editor_cascade.el_chars = FALSE;
10994 si->editor_cascade.el_steel_chars = FALSE;
10995 si->editor_cascade.el_ce = FALSE;
10996 si->editor_cascade.el_ge = FALSE;
10997 si->editor_cascade.el_es = FALSE;
10998 si->editor_cascade.el_ref = FALSE;
10999 si->editor_cascade.el_user = FALSE;
11000 si->editor_cascade.el_dynamic = FALSE;
11003 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11005 static char *getHideSetupToken(void *setup_value)
11007 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11009 if (setup_value != NULL)
11010 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11012 return hide_setup_token;
11015 void setHideSetupEntry(void *setup_value)
11017 char *hide_setup_token = getHideSetupToken(setup_value);
11019 if (hide_setup_hash == NULL)
11020 hide_setup_hash = newSetupFileHash();
11022 if (setup_value != NULL)
11023 setHashEntry(hide_setup_hash, hide_setup_token, "");
11026 void removeHideSetupEntry(void *setup_value)
11028 char *hide_setup_token = getHideSetupToken(setup_value);
11030 if (setup_value != NULL)
11031 removeHashEntry(hide_setup_hash, hide_setup_token);
11034 boolean hideSetupEntry(void *setup_value)
11036 char *hide_setup_token = getHideSetupToken(setup_value);
11038 return (setup_value != NULL &&
11039 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11042 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11043 struct TokenInfo *token_info,
11044 int token_nr, char *token_text)
11046 char *token_hide_text = getStringCat2(token_text, ".hide");
11047 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11049 // set the value of this setup option in the setup option structure
11050 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11052 // check if this setup option should be hidden in the setup menu
11053 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11054 setHideSetupEntry(token_info[token_nr].value);
11056 free(token_hide_text);
11059 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11060 struct TokenInfo *token_info,
11063 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11064 token_info[token_nr].text);
11067 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11071 if (!setup_file_hash)
11074 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11075 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11077 setup.touch.grid_initialized = TRUE;
11078 for (i = 0; i < 2; i++)
11080 int grid_xsize = setup.touch.grid_xsize[i];
11081 int grid_ysize = setup.touch.grid_ysize[i];
11084 // if virtual buttons are not loaded from setup file, repeat initializing
11085 // virtual buttons grid with default values later when video is initialized
11086 if (grid_xsize == -1 ||
11089 setup.touch.grid_initialized = FALSE;
11094 for (y = 0; y < grid_ysize; y++)
11096 char token_string[MAX_LINE_LEN];
11098 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11100 char *value_string = getHashEntry(setup_file_hash, token_string);
11102 if (value_string == NULL)
11105 for (x = 0; x < grid_xsize; x++)
11107 char c = value_string[x];
11109 setup.touch.grid_button[i][x][y] =
11110 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11115 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11116 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11118 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11119 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11121 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11125 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11127 setup_input = setup.input[pnr];
11128 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11130 char full_token[100];
11132 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11133 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11136 setup.input[pnr] = setup_input;
11139 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11140 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11142 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11143 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11145 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11146 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11148 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11149 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11151 setHideRelatedSetupEntries();
11154 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11158 if (!setup_file_hash)
11161 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11162 setSetupInfo(auto_setup_tokens, i,
11163 getHashEntry(setup_file_hash,
11164 auto_setup_tokens[i].text));
11167 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11171 if (!setup_file_hash)
11174 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11175 setSetupInfo(server_setup_tokens, i,
11176 getHashEntry(setup_file_hash,
11177 server_setup_tokens[i].text));
11180 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11184 if (!setup_file_hash)
11187 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11188 setSetupInfo(editor_cascade_setup_tokens, i,
11189 getHashEntry(setup_file_hash,
11190 editor_cascade_setup_tokens[i].text));
11193 void LoadUserNames(void)
11195 int last_user_nr = user.nr;
11198 if (global.user_names != NULL)
11200 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11201 checked_free(global.user_names[i]);
11203 checked_free(global.user_names);
11206 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11208 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11212 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11214 if (setup_file_hash)
11216 char *player_name = getHashEntry(setup_file_hash, "player_name");
11218 global.user_names[i] = getFixedUserName(player_name);
11220 freeSetupFileHash(setup_file_hash);
11223 if (global.user_names[i] == NULL)
11224 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11227 user.nr = last_user_nr;
11230 void LoadSetupFromFilename(char *filename)
11232 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11234 if (setup_file_hash)
11236 decodeSetupFileHash_Default(setup_file_hash);
11238 freeSetupFileHash(setup_file_hash);
11242 Debug("setup", "using default setup values");
11246 static void LoadSetup_SpecialPostProcessing(void)
11248 char *player_name_new;
11250 // needed to work around problems with fixed length strings
11251 player_name_new = getFixedUserName(setup.player_name);
11252 free(setup.player_name);
11253 setup.player_name = player_name_new;
11255 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11256 if (setup.scroll_delay == FALSE)
11258 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11259 setup.scroll_delay = TRUE; // now always "on"
11262 // make sure that scroll delay value stays inside valid range
11263 setup.scroll_delay_value =
11264 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11267 void LoadSetup_Default(void)
11271 // always start with reliable default values
11272 setSetupInfoToDefaults(&setup);
11274 // try to load setup values from default setup file
11275 filename = getDefaultSetupFilename();
11277 if (fileExists(filename))
11278 LoadSetupFromFilename(filename);
11280 // try to load setup values from platform setup file
11281 filename = getPlatformSetupFilename();
11283 if (fileExists(filename))
11284 LoadSetupFromFilename(filename);
11286 // try to load setup values from user setup file
11287 filename = getSetupFilename();
11289 LoadSetupFromFilename(filename);
11291 LoadSetup_SpecialPostProcessing();
11294 void LoadSetup_AutoSetup(void)
11296 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11297 SetupFileHash *setup_file_hash = NULL;
11299 // always start with reliable default values
11300 setSetupInfoToDefaults_AutoSetup(&setup);
11302 setup_file_hash = loadSetupFileHash(filename);
11304 if (setup_file_hash)
11306 decodeSetupFileHash_AutoSetup(setup_file_hash);
11308 freeSetupFileHash(setup_file_hash);
11314 void LoadSetup_ServerSetup(void)
11316 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11317 SetupFileHash *setup_file_hash = NULL;
11319 // always start with reliable default values
11320 setSetupInfoToDefaults_ServerSetup(&setup);
11322 setup_file_hash = loadSetupFileHash(filename);
11324 if (setup_file_hash)
11326 decodeSetupFileHash_ServerSetup(setup_file_hash);
11328 freeSetupFileHash(setup_file_hash);
11333 if (setup.player_uuid == NULL)
11335 // player UUID does not yet exist in setup file
11336 setup.player_uuid = getStringCopy(getUUID());
11337 setup.player_version = 2;
11339 SaveSetup_ServerSetup();
11343 void LoadSetup_EditorCascade(void)
11345 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11346 SetupFileHash *setup_file_hash = NULL;
11348 // always start with reliable default values
11349 setSetupInfoToDefaults_EditorCascade(&setup);
11351 setup_file_hash = loadSetupFileHash(filename);
11353 if (setup_file_hash)
11355 decodeSetupFileHash_EditorCascade(setup_file_hash);
11357 freeSetupFileHash(setup_file_hash);
11363 void LoadSetup(void)
11365 LoadSetup_Default();
11366 LoadSetup_AutoSetup();
11367 LoadSetup_ServerSetup();
11368 LoadSetup_EditorCascade();
11371 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11372 char *mapping_line)
11374 char mapping_guid[MAX_LINE_LEN];
11375 char *mapping_start, *mapping_end;
11377 // get GUID from game controller mapping line: copy complete line
11378 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11379 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11381 // get GUID from game controller mapping line: cut after GUID part
11382 mapping_start = strchr(mapping_guid, ',');
11383 if (mapping_start != NULL)
11384 *mapping_start = '\0';
11386 // cut newline from game controller mapping line
11387 mapping_end = strchr(mapping_line, '\n');
11388 if (mapping_end != NULL)
11389 *mapping_end = '\0';
11391 // add mapping entry to game controller mappings hash
11392 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11395 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11400 if (!(file = fopen(filename, MODE_READ)))
11402 Warn("cannot read game controller mappings file '%s'", filename);
11407 while (!feof(file))
11409 char line[MAX_LINE_LEN];
11411 if (!fgets(line, MAX_LINE_LEN, file))
11414 addGameControllerMappingToHash(mappings_hash, line);
11420 void SaveSetup_Default(void)
11422 char *filename = getSetupFilename();
11426 InitUserDataDirectory();
11428 if (!(file = fopen(filename, MODE_WRITE)))
11430 Warn("cannot write setup file '%s'", filename);
11435 fprintFileHeader(file, SETUP_FILENAME);
11437 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11439 // just to make things nicer :)
11440 if (global_setup_tokens[i].value == &setup.multiple_users ||
11441 global_setup_tokens[i].value == &setup.sound ||
11442 global_setup_tokens[i].value == &setup.graphics_set ||
11443 global_setup_tokens[i].value == &setup.volume_simple ||
11444 global_setup_tokens[i].value == &setup.network_mode ||
11445 global_setup_tokens[i].value == &setup.touch.control_type ||
11446 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11447 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11448 fprintf(file, "\n");
11450 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11453 for (i = 0; i < 2; i++)
11455 int grid_xsize = setup.touch.grid_xsize[i];
11456 int grid_ysize = setup.touch.grid_ysize[i];
11459 fprintf(file, "\n");
11461 for (y = 0; y < grid_ysize; y++)
11463 char token_string[MAX_LINE_LEN];
11464 char value_string[MAX_LINE_LEN];
11466 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11468 for (x = 0; x < grid_xsize; x++)
11470 char c = setup.touch.grid_button[i][x][y];
11472 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11475 value_string[grid_xsize] = '\0';
11477 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11481 fprintf(file, "\n");
11482 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11483 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11485 fprintf(file, "\n");
11486 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11487 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11489 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11493 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11494 fprintf(file, "\n");
11496 setup_input = setup.input[pnr];
11497 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11498 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11501 fprintf(file, "\n");
11502 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11503 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11505 // (internal setup values not saved to user setup file)
11507 fprintf(file, "\n");
11508 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11509 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11510 setup.debug.xsn_mode != AUTO)
11511 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11513 fprintf(file, "\n");
11514 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11515 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11519 SetFilePermissions(filename, PERMS_PRIVATE);
11522 void SaveSetup_AutoSetup(void)
11524 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11528 InitUserDataDirectory();
11530 if (!(file = fopen(filename, MODE_WRITE)))
11532 Warn("cannot write auto setup file '%s'", filename);
11539 fprintFileHeader(file, AUTOSETUP_FILENAME);
11541 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11542 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11546 SetFilePermissions(filename, PERMS_PRIVATE);
11551 void SaveSetup_ServerSetup(void)
11553 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11557 InitUserDataDirectory();
11559 if (!(file = fopen(filename, MODE_WRITE)))
11561 Warn("cannot write server setup file '%s'", filename);
11568 fprintFileHeader(file, SERVERSETUP_FILENAME);
11570 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11572 // just to make things nicer :)
11573 if (server_setup_tokens[i].value == &setup.use_api_server)
11574 fprintf(file, "\n");
11576 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11581 SetFilePermissions(filename, PERMS_PRIVATE);
11586 void SaveSetup_EditorCascade(void)
11588 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11592 InitUserDataDirectory();
11594 if (!(file = fopen(filename, MODE_WRITE)))
11596 Warn("cannot write editor cascade state file '%s'", filename);
11603 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11605 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11606 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11610 SetFilePermissions(filename, PERMS_PRIVATE);
11615 void SaveSetup(void)
11617 SaveSetup_Default();
11618 SaveSetup_AutoSetup();
11619 SaveSetup_ServerSetup();
11620 SaveSetup_EditorCascade();
11623 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11628 if (!(file = fopen(filename, MODE_WRITE)))
11630 Warn("cannot write game controller mappings file '%s'", filename);
11635 BEGIN_HASH_ITERATION(mappings_hash, itr)
11637 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11639 END_HASH_ITERATION(mappings_hash, itr)
11644 void SaveSetup_AddGameControllerMapping(char *mapping)
11646 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11647 SetupFileHash *mappings_hash = newSetupFileHash();
11649 InitUserDataDirectory();
11651 // load existing personal game controller mappings
11652 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11654 // add new mapping to personal game controller mappings
11655 addGameControllerMappingToHash(mappings_hash, mapping);
11657 // save updated personal game controller mappings
11658 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11660 freeSetupFileHash(mappings_hash);
11664 void LoadCustomElementDescriptions(void)
11666 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11667 SetupFileHash *setup_file_hash;
11670 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11672 if (element_info[i].custom_description != NULL)
11674 free(element_info[i].custom_description);
11675 element_info[i].custom_description = NULL;
11679 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11682 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11684 char *token = getStringCat2(element_info[i].token_name, ".name");
11685 char *value = getHashEntry(setup_file_hash, token);
11688 element_info[i].custom_description = getStringCopy(value);
11693 freeSetupFileHash(setup_file_hash);
11696 static int getElementFromToken(char *token)
11698 char *value = getHashEntry(element_token_hash, token);
11701 return atoi(value);
11703 Warn("unknown element token '%s'", token);
11705 return EL_UNDEFINED;
11708 void FreeGlobalAnimEventInfo(void)
11710 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11712 if (gaei->event_list == NULL)
11717 for (i = 0; i < gaei->num_event_lists; i++)
11719 checked_free(gaei->event_list[i]->event_value);
11720 checked_free(gaei->event_list[i]);
11723 checked_free(gaei->event_list);
11725 gaei->event_list = NULL;
11726 gaei->num_event_lists = 0;
11729 static int AddGlobalAnimEventList(void)
11731 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11732 int list_pos = gaei->num_event_lists++;
11734 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11735 sizeof(struct GlobalAnimEventListInfo *));
11737 gaei->event_list[list_pos] =
11738 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11740 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11742 gaeli->event_value = NULL;
11743 gaeli->num_event_values = 0;
11748 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11750 // do not add empty global animation events
11751 if (event_value == ANIM_EVENT_NONE)
11754 // if list position is undefined, create new list
11755 if (list_pos == ANIM_EVENT_UNDEFINED)
11756 list_pos = AddGlobalAnimEventList();
11758 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11759 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11760 int value_pos = gaeli->num_event_values++;
11762 gaeli->event_value = checked_realloc(gaeli->event_value,
11763 gaeli->num_event_values * sizeof(int *));
11765 gaeli->event_value[value_pos] = event_value;
11770 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11772 if (list_pos == ANIM_EVENT_UNDEFINED)
11773 return ANIM_EVENT_NONE;
11775 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11776 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11778 return gaeli->event_value[value_pos];
11781 int GetGlobalAnimEventValueCount(int list_pos)
11783 if (list_pos == ANIM_EVENT_UNDEFINED)
11786 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11787 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11789 return gaeli->num_event_values;
11792 // This function checks if a string <s> of the format "string1, string2, ..."
11793 // exactly contains a string <s_contained>.
11795 static boolean string_has_parameter(char *s, char *s_contained)
11799 if (s == NULL || s_contained == NULL)
11802 if (strlen(s_contained) > strlen(s))
11805 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11807 char next_char = s[strlen(s_contained)];
11809 // check if next character is delimiter or whitespace
11810 if (next_char == ',' || next_char == '\0' ||
11811 next_char == ' ' || next_char == '\t')
11815 // check if string contains another parameter string after a comma
11816 substring = strchr(s, ',');
11817 if (substring == NULL) // string does not contain a comma
11820 // advance string pointer to next character after the comma
11823 // skip potential whitespaces after the comma
11824 while (*substring == ' ' || *substring == '\t')
11827 return string_has_parameter(substring, s_contained);
11830 static int get_anim_parameter_value_ce(char *s)
11833 char *pattern_1 = "ce_change:custom_";
11834 char *pattern_2 = ".page_";
11835 int pattern_1_len = strlen(pattern_1);
11836 char *matching_char = strstr(s_ptr, pattern_1);
11837 int result = ANIM_EVENT_NONE;
11839 if (matching_char == NULL)
11840 return ANIM_EVENT_NONE;
11842 result = ANIM_EVENT_CE_CHANGE;
11844 s_ptr = matching_char + pattern_1_len;
11846 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11847 if (*s_ptr >= '0' && *s_ptr <= '9')
11849 int gic_ce_nr = (*s_ptr++ - '0');
11851 if (*s_ptr >= '0' && *s_ptr <= '9')
11853 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11855 if (*s_ptr >= '0' && *s_ptr <= '9')
11856 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11859 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11860 return ANIM_EVENT_NONE;
11862 // custom element stored as 0 to 255
11865 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11869 // invalid custom element number specified
11871 return ANIM_EVENT_NONE;
11874 // check for change page number ("page_X" or "page_XX") (optional)
11875 if (strPrefix(s_ptr, pattern_2))
11877 s_ptr += strlen(pattern_2);
11879 if (*s_ptr >= '0' && *s_ptr <= '9')
11881 int gic_page_nr = (*s_ptr++ - '0');
11883 if (*s_ptr >= '0' && *s_ptr <= '9')
11884 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11886 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11887 return ANIM_EVENT_NONE;
11889 // change page stored as 1 to 32 (0 means "all change pages")
11891 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11895 // invalid animation part number specified
11897 return ANIM_EVENT_NONE;
11901 // discard result if next character is neither delimiter nor whitespace
11902 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11903 *s_ptr == ' ' || *s_ptr == '\t'))
11904 return ANIM_EVENT_NONE;
11909 static int get_anim_parameter_value(char *s)
11911 int event_value[] =
11919 char *pattern_1[] =
11927 char *pattern_2 = ".part_";
11928 char *matching_char = NULL;
11930 int pattern_1_len = 0;
11931 int result = ANIM_EVENT_NONE;
11934 result = get_anim_parameter_value_ce(s);
11936 if (result != ANIM_EVENT_NONE)
11939 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11941 matching_char = strstr(s_ptr, pattern_1[i]);
11942 pattern_1_len = strlen(pattern_1[i]);
11943 result = event_value[i];
11945 if (matching_char != NULL)
11949 if (matching_char == NULL)
11950 return ANIM_EVENT_NONE;
11952 s_ptr = matching_char + pattern_1_len;
11954 // check for main animation number ("anim_X" or "anim_XX")
11955 if (*s_ptr >= '0' && *s_ptr <= '9')
11957 int gic_anim_nr = (*s_ptr++ - '0');
11959 if (*s_ptr >= '0' && *s_ptr <= '9')
11960 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11962 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11963 return ANIM_EVENT_NONE;
11965 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11969 // invalid main animation number specified
11971 return ANIM_EVENT_NONE;
11974 // check for animation part number ("part_X" or "part_XX") (optional)
11975 if (strPrefix(s_ptr, pattern_2))
11977 s_ptr += strlen(pattern_2);
11979 if (*s_ptr >= '0' && *s_ptr <= '9')
11981 int gic_part_nr = (*s_ptr++ - '0');
11983 if (*s_ptr >= '0' && *s_ptr <= '9')
11984 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11986 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11987 return ANIM_EVENT_NONE;
11989 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11993 // invalid animation part number specified
11995 return ANIM_EVENT_NONE;
11999 // discard result if next character is neither delimiter nor whitespace
12000 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12001 *s_ptr == ' ' || *s_ptr == '\t'))
12002 return ANIM_EVENT_NONE;
12007 static int get_anim_parameter_values(char *s)
12009 int list_pos = ANIM_EVENT_UNDEFINED;
12010 int event_value = ANIM_EVENT_DEFAULT;
12012 if (string_has_parameter(s, "any"))
12013 event_value |= ANIM_EVENT_ANY;
12015 if (string_has_parameter(s, "click:self") ||
12016 string_has_parameter(s, "click") ||
12017 string_has_parameter(s, "self"))
12018 event_value |= ANIM_EVENT_SELF;
12020 if (string_has_parameter(s, "unclick:any"))
12021 event_value |= ANIM_EVENT_UNCLICK_ANY;
12023 // if animation event found, add it to global animation event list
12024 if (event_value != ANIM_EVENT_NONE)
12025 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12029 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12030 event_value = get_anim_parameter_value(s);
12032 // if animation event found, add it to global animation event list
12033 if (event_value != ANIM_EVENT_NONE)
12034 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12036 // continue with next part of the string, starting with next comma
12037 s = strchr(s + 1, ',');
12043 static int get_anim_action_parameter_value(char *token)
12045 // check most common default case first to massively speed things up
12046 if (strEqual(token, ARG_UNDEFINED))
12047 return ANIM_EVENT_ACTION_NONE;
12049 int result = getImageIDFromToken(token);
12053 char *gfx_token = getStringCat2("gfx.", token);
12055 result = getImageIDFromToken(gfx_token);
12057 checked_free(gfx_token);
12062 Key key = getKeyFromX11KeyName(token);
12064 if (key != KSYM_UNDEFINED)
12065 result = -(int)key;
12072 result = get_hash_from_string(token); // unsigned int => int
12073 result = ABS(result); // may be negative now
12074 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12076 setHashEntry(anim_url_hash, int2str(result, 0), token);
12081 result = ANIM_EVENT_ACTION_NONE;
12086 int get_parameter_value(char *value_raw, char *suffix, int type)
12088 char *value = getStringToLower(value_raw);
12089 int result = 0; // probably a save default value
12091 if (strEqual(suffix, ".direction"))
12093 result = (strEqual(value, "left") ? MV_LEFT :
12094 strEqual(value, "right") ? MV_RIGHT :
12095 strEqual(value, "up") ? MV_UP :
12096 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12098 else if (strEqual(suffix, ".position"))
12100 result = (strEqual(value, "left") ? POS_LEFT :
12101 strEqual(value, "right") ? POS_RIGHT :
12102 strEqual(value, "top") ? POS_TOP :
12103 strEqual(value, "upper") ? POS_UPPER :
12104 strEqual(value, "middle") ? POS_MIDDLE :
12105 strEqual(value, "lower") ? POS_LOWER :
12106 strEqual(value, "bottom") ? POS_BOTTOM :
12107 strEqual(value, "any") ? POS_ANY :
12108 strEqual(value, "ce") ? POS_CE :
12109 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12110 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12112 else if (strEqual(suffix, ".align"))
12114 result = (strEqual(value, "left") ? ALIGN_LEFT :
12115 strEqual(value, "right") ? ALIGN_RIGHT :
12116 strEqual(value, "center") ? ALIGN_CENTER :
12117 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12119 else if (strEqual(suffix, ".valign"))
12121 result = (strEqual(value, "top") ? VALIGN_TOP :
12122 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12123 strEqual(value, "middle") ? VALIGN_MIDDLE :
12124 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12126 else if (strEqual(suffix, ".anim_mode"))
12128 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12129 string_has_parameter(value, "loop") ? ANIM_LOOP :
12130 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12131 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12132 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12133 string_has_parameter(value, "random") ? ANIM_RANDOM :
12134 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12135 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12136 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12137 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12138 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12139 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12140 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12141 string_has_parameter(value, "all") ? ANIM_ALL :
12142 string_has_parameter(value, "tiled") ? ANIM_TILED :
12143 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12146 if (string_has_parameter(value, "once"))
12147 result |= ANIM_ONCE;
12149 if (string_has_parameter(value, "reverse"))
12150 result |= ANIM_REVERSE;
12152 if (string_has_parameter(value, "opaque_player"))
12153 result |= ANIM_OPAQUE_PLAYER;
12155 if (string_has_parameter(value, "static_panel"))
12156 result |= ANIM_STATIC_PANEL;
12158 else if (strEqual(suffix, ".init_event") ||
12159 strEqual(suffix, ".anim_event"))
12161 result = get_anim_parameter_values(value);
12163 else if (strEqual(suffix, ".init_delay_action") ||
12164 strEqual(suffix, ".anim_delay_action") ||
12165 strEqual(suffix, ".post_delay_action") ||
12166 strEqual(suffix, ".init_event_action") ||
12167 strEqual(suffix, ".anim_event_action"))
12169 result = get_anim_action_parameter_value(value_raw);
12171 else if (strEqual(suffix, ".class"))
12173 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12174 get_hash_from_string(value));
12176 else if (strEqual(suffix, ".style"))
12178 result = STYLE_DEFAULT;
12180 if (string_has_parameter(value, "accurate_borders"))
12181 result |= STYLE_ACCURATE_BORDERS;
12183 if (string_has_parameter(value, "inner_corners"))
12184 result |= STYLE_INNER_CORNERS;
12186 if (string_has_parameter(value, "reverse"))
12187 result |= STYLE_REVERSE;
12189 if (string_has_parameter(value, "leftmost_position"))
12190 result |= STYLE_LEFTMOST_POSITION;
12192 if (string_has_parameter(value, "block_clicks"))
12193 result |= STYLE_BLOCK;
12195 if (string_has_parameter(value, "passthrough_clicks"))
12196 result |= STYLE_PASSTHROUGH;
12198 if (string_has_parameter(value, "multiple_actions"))
12199 result |= STYLE_MULTIPLE_ACTIONS;
12201 if (string_has_parameter(value, "consume_ce_event"))
12202 result |= STYLE_CONSUME_CE_EVENT;
12204 else if (strEqual(suffix, ".fade_mode"))
12206 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12207 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12208 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12209 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12210 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12211 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12212 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12213 FADE_MODE_DEFAULT);
12215 else if (strEqual(suffix, ".auto_delay_unit"))
12217 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12218 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12219 AUTO_DELAY_UNIT_DEFAULT);
12221 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12223 result = gfx.get_font_from_token_function(value);
12225 else // generic parameter of type integer or boolean
12227 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12228 type == TYPE_INTEGER ? get_integer_from_string(value) :
12229 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12230 ARG_UNDEFINED_VALUE);
12238 static int get_token_parameter_value(char *token, char *value_raw)
12242 if (token == NULL || value_raw == NULL)
12243 return ARG_UNDEFINED_VALUE;
12245 suffix = strrchr(token, '.');
12246 if (suffix == NULL)
12249 if (strEqual(suffix, ".element"))
12250 return getElementFromToken(value_raw);
12252 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12253 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12256 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12257 boolean ignore_defaults)
12261 for (i = 0; image_config_vars[i].token != NULL; i++)
12263 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12265 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12266 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12270 *image_config_vars[i].value =
12271 get_token_parameter_value(image_config_vars[i].token, value);
12275 void InitMenuDesignSettings_Static(void)
12277 // always start with reliable default values from static default config
12278 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12281 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12285 // the following initializes hierarchical values from static configuration
12287 // special case: initialize "ARG_DEFAULT" values in static default config
12288 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12289 titlescreen_initial_first_default.fade_mode =
12290 title_initial_first_default.fade_mode;
12291 titlescreen_initial_first_default.fade_delay =
12292 title_initial_first_default.fade_delay;
12293 titlescreen_initial_first_default.post_delay =
12294 title_initial_first_default.post_delay;
12295 titlescreen_initial_first_default.auto_delay =
12296 title_initial_first_default.auto_delay;
12297 titlescreen_initial_first_default.auto_delay_unit =
12298 title_initial_first_default.auto_delay_unit;
12299 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12300 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12301 titlescreen_first_default.post_delay = title_first_default.post_delay;
12302 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12303 titlescreen_first_default.auto_delay_unit =
12304 title_first_default.auto_delay_unit;
12305 titlemessage_initial_first_default.fade_mode =
12306 title_initial_first_default.fade_mode;
12307 titlemessage_initial_first_default.fade_delay =
12308 title_initial_first_default.fade_delay;
12309 titlemessage_initial_first_default.post_delay =
12310 title_initial_first_default.post_delay;
12311 titlemessage_initial_first_default.auto_delay =
12312 title_initial_first_default.auto_delay;
12313 titlemessage_initial_first_default.auto_delay_unit =
12314 title_initial_first_default.auto_delay_unit;
12315 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12316 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12317 titlemessage_first_default.post_delay = title_first_default.post_delay;
12318 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12319 titlemessage_first_default.auto_delay_unit =
12320 title_first_default.auto_delay_unit;
12322 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12323 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12324 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12325 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12326 titlescreen_initial_default.auto_delay_unit =
12327 title_initial_default.auto_delay_unit;
12328 titlescreen_default.fade_mode = title_default.fade_mode;
12329 titlescreen_default.fade_delay = title_default.fade_delay;
12330 titlescreen_default.post_delay = title_default.post_delay;
12331 titlescreen_default.auto_delay = title_default.auto_delay;
12332 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12333 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12334 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12335 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12336 titlemessage_initial_default.auto_delay_unit =
12337 title_initial_default.auto_delay_unit;
12338 titlemessage_default.fade_mode = title_default.fade_mode;
12339 titlemessage_default.fade_delay = title_default.fade_delay;
12340 titlemessage_default.post_delay = title_default.post_delay;
12341 titlemessage_default.auto_delay = title_default.auto_delay;
12342 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12344 // special case: initialize "ARG_DEFAULT" values in static default config
12345 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12346 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12348 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12349 titlescreen_first[i] = titlescreen_first_default;
12350 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12351 titlemessage_first[i] = titlemessage_first_default;
12353 titlescreen_initial[i] = titlescreen_initial_default;
12354 titlescreen[i] = titlescreen_default;
12355 titlemessage_initial[i] = titlemessage_initial_default;
12356 titlemessage[i] = titlemessage_default;
12359 // special case: initialize "ARG_DEFAULT" values in static default config
12360 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12361 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12363 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12366 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12367 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12368 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12371 // special case: initialize "ARG_DEFAULT" values in static default config
12372 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12373 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12375 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12376 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12377 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12379 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12382 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12386 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12390 struct XY *dst, *src;
12392 game_buttons_xy[] =
12394 { &game.button.save, &game.button.stop },
12395 { &game.button.pause2, &game.button.pause },
12396 { &game.button.load, &game.button.play },
12397 { &game.button.undo, &game.button.stop },
12398 { &game.button.redo, &game.button.play },
12404 // special case: initialize later added SETUP list size from LEVELS value
12405 if (menu.list_size[GAME_MODE_SETUP] == -1)
12406 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12408 // set default position for snapshot buttons to stop/pause/play buttons
12409 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12410 if ((*game_buttons_xy[i].dst).x == -1 &&
12411 (*game_buttons_xy[i].dst).y == -1)
12412 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12414 // --------------------------------------------------------------------------
12415 // dynamic viewports (including playfield margins, borders and alignments)
12416 // --------------------------------------------------------------------------
12418 // dynamic viewports currently only supported for landscape mode
12419 int display_width = MAX(video.display_width, video.display_height);
12420 int display_height = MIN(video.display_width, video.display_height);
12422 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12424 struct RectWithBorder *vp_window = &viewport.window[i];
12425 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12426 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12427 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12428 boolean dynamic_window_width = (vp_window->min_width != -1);
12429 boolean dynamic_window_height = (vp_window->min_height != -1);
12430 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12431 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12433 // adjust window size if min/max width/height is specified
12435 if (vp_window->min_width != -1)
12437 int window_width = display_width;
12439 // when using static window height, use aspect ratio of display
12440 if (vp_window->min_height == -1)
12441 window_width = vp_window->height * display_width / display_height;
12443 vp_window->width = MAX(vp_window->min_width, window_width);
12446 if (vp_window->min_height != -1)
12448 int window_height = display_height;
12450 // when using static window width, use aspect ratio of display
12451 if (vp_window->min_width == -1)
12452 window_height = vp_window->width * display_height / display_width;
12454 vp_window->height = MAX(vp_window->min_height, window_height);
12457 if (vp_window->max_width != -1)
12458 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12460 if (vp_window->max_height != -1)
12461 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12463 int playfield_width = vp_window->width;
12464 int playfield_height = vp_window->height;
12466 // adjust playfield size and position according to specified margins
12468 playfield_width -= vp_playfield->margin_left;
12469 playfield_width -= vp_playfield->margin_right;
12471 playfield_height -= vp_playfield->margin_top;
12472 playfield_height -= vp_playfield->margin_bottom;
12474 // adjust playfield size if min/max width/height is specified
12476 if (vp_playfield->min_width != -1)
12477 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12479 if (vp_playfield->min_height != -1)
12480 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12482 if (vp_playfield->max_width != -1)
12483 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12485 if (vp_playfield->max_height != -1)
12486 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12488 // adjust playfield position according to specified alignment
12490 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12491 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12492 else if (vp_playfield->align == ALIGN_CENTER)
12493 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12494 else if (vp_playfield->align == ALIGN_RIGHT)
12495 vp_playfield->x += playfield_width - vp_playfield->width;
12497 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12498 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12499 else if (vp_playfield->valign == VALIGN_MIDDLE)
12500 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12501 else if (vp_playfield->valign == VALIGN_BOTTOM)
12502 vp_playfield->y += playfield_height - vp_playfield->height;
12504 vp_playfield->x += vp_playfield->margin_left;
12505 vp_playfield->y += vp_playfield->margin_top;
12507 // adjust individual playfield borders if only default border is specified
12509 if (vp_playfield->border_left == -1)
12510 vp_playfield->border_left = vp_playfield->border_size;
12511 if (vp_playfield->border_right == -1)
12512 vp_playfield->border_right = vp_playfield->border_size;
12513 if (vp_playfield->border_top == -1)
12514 vp_playfield->border_top = vp_playfield->border_size;
12515 if (vp_playfield->border_bottom == -1)
12516 vp_playfield->border_bottom = vp_playfield->border_size;
12518 // set dynamic playfield borders if borders are specified as undefined
12519 // (but only if window size was dynamic and playfield size was static)
12521 if (dynamic_window_width && !dynamic_playfield_width)
12523 if (vp_playfield->border_left == -1)
12525 vp_playfield->border_left = (vp_playfield->x -
12526 vp_playfield->margin_left);
12527 vp_playfield->x -= vp_playfield->border_left;
12528 vp_playfield->width += vp_playfield->border_left;
12531 if (vp_playfield->border_right == -1)
12533 vp_playfield->border_right = (vp_window->width -
12535 vp_playfield->width -
12536 vp_playfield->margin_right);
12537 vp_playfield->width += vp_playfield->border_right;
12541 if (dynamic_window_height && !dynamic_playfield_height)
12543 if (vp_playfield->border_top == -1)
12545 vp_playfield->border_top = (vp_playfield->y -
12546 vp_playfield->margin_top);
12547 vp_playfield->y -= vp_playfield->border_top;
12548 vp_playfield->height += vp_playfield->border_top;
12551 if (vp_playfield->border_bottom == -1)
12553 vp_playfield->border_bottom = (vp_window->height -
12555 vp_playfield->height -
12556 vp_playfield->margin_bottom);
12557 vp_playfield->height += vp_playfield->border_bottom;
12561 // adjust playfield size to be a multiple of a defined alignment tile size
12563 int align_size = vp_playfield->align_size;
12564 int playfield_xtiles = vp_playfield->width / align_size;
12565 int playfield_ytiles = vp_playfield->height / align_size;
12566 int playfield_width_corrected = playfield_xtiles * align_size;
12567 int playfield_height_corrected = playfield_ytiles * align_size;
12568 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12569 i == GFX_SPECIAL_ARG_EDITOR);
12571 if (is_playfield_mode &&
12572 dynamic_playfield_width &&
12573 vp_playfield->width != playfield_width_corrected)
12575 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12577 vp_playfield->width = playfield_width_corrected;
12579 if (vp_playfield->align == ALIGN_LEFT)
12581 vp_playfield->border_left += playfield_xdiff;
12583 else if (vp_playfield->align == ALIGN_RIGHT)
12585 vp_playfield->border_right += playfield_xdiff;
12587 else if (vp_playfield->align == ALIGN_CENTER)
12589 int border_left_diff = playfield_xdiff / 2;
12590 int border_right_diff = playfield_xdiff - border_left_diff;
12592 vp_playfield->border_left += border_left_diff;
12593 vp_playfield->border_right += border_right_diff;
12597 if (is_playfield_mode &&
12598 dynamic_playfield_height &&
12599 vp_playfield->height != playfield_height_corrected)
12601 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12603 vp_playfield->height = playfield_height_corrected;
12605 if (vp_playfield->valign == VALIGN_TOP)
12607 vp_playfield->border_top += playfield_ydiff;
12609 else if (vp_playfield->align == VALIGN_BOTTOM)
12611 vp_playfield->border_right += playfield_ydiff;
12613 else if (vp_playfield->align == VALIGN_MIDDLE)
12615 int border_top_diff = playfield_ydiff / 2;
12616 int border_bottom_diff = playfield_ydiff - border_top_diff;
12618 vp_playfield->border_top += border_top_diff;
12619 vp_playfield->border_bottom += border_bottom_diff;
12623 // adjust door positions according to specified alignment
12625 for (j = 0; j < 2; j++)
12627 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12629 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12630 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12631 else if (vp_door->align == ALIGN_CENTER)
12632 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12633 else if (vp_door->align == ALIGN_RIGHT)
12634 vp_door->x += vp_window->width - vp_door->width;
12636 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12637 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12638 else if (vp_door->valign == VALIGN_MIDDLE)
12639 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12640 else if (vp_door->valign == VALIGN_BOTTOM)
12641 vp_door->y += vp_window->height - vp_door->height;
12646 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12650 struct XYTileSize *dst, *src;
12653 editor_buttons_xy[] =
12656 &editor.button.element_left, &editor.palette.element_left,
12657 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12660 &editor.button.element_middle, &editor.palette.element_middle,
12661 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12664 &editor.button.element_right, &editor.palette.element_right,
12665 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12672 // set default position for element buttons to element graphics
12673 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12675 if ((*editor_buttons_xy[i].dst).x == -1 &&
12676 (*editor_buttons_xy[i].dst).y == -1)
12678 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12680 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12682 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12686 // adjust editor palette rows and columns if specified to be dynamic
12688 if (editor.palette.cols == -1)
12690 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12691 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12692 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12694 editor.palette.cols = (vp_width - sc_width) / bt_width;
12696 if (editor.palette.x == -1)
12698 int palette_width = editor.palette.cols * bt_width + sc_width;
12700 editor.palette.x = (vp_width - palette_width) / 2;
12704 if (editor.palette.rows == -1)
12706 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12707 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12708 int tx_height = getFontHeight(FONT_TEXT_2);
12710 editor.palette.rows = (vp_height - tx_height) / bt_height;
12712 if (editor.palette.y == -1)
12714 int palette_height = editor.palette.rows * bt_height + tx_height;
12716 editor.palette.y = (vp_height - palette_height) / 2;
12721 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12722 boolean initialize)
12724 // special case: check if network and preview player positions are redefined,
12725 // to compare this later against the main menu level preview being redefined
12726 struct TokenIntPtrInfo menu_config_players[] =
12728 { "main.network_players.x", &menu.main.network_players.redefined },
12729 { "main.network_players.y", &menu.main.network_players.redefined },
12730 { "main.preview_players.x", &menu.main.preview_players.redefined },
12731 { "main.preview_players.y", &menu.main.preview_players.redefined },
12732 { "preview.x", &preview.redefined },
12733 { "preview.y", &preview.redefined }
12739 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12740 *menu_config_players[i].value = FALSE;
12744 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12745 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12746 *menu_config_players[i].value = TRUE;
12750 static void InitMenuDesignSettings_PreviewPlayers(void)
12752 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12755 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12757 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12760 static void LoadMenuDesignSettingsFromFilename(char *filename)
12762 static struct TitleFadingInfo tfi;
12763 static struct TitleMessageInfo tmi;
12764 static struct TokenInfo title_tokens[] =
12766 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12767 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12768 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12769 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12770 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12774 static struct TokenInfo titlemessage_tokens[] =
12776 { TYPE_INTEGER, &tmi.x, ".x" },
12777 { TYPE_INTEGER, &tmi.y, ".y" },
12778 { TYPE_INTEGER, &tmi.width, ".width" },
12779 { TYPE_INTEGER, &tmi.height, ".height" },
12780 { TYPE_INTEGER, &tmi.chars, ".chars" },
12781 { TYPE_INTEGER, &tmi.lines, ".lines" },
12782 { TYPE_INTEGER, &tmi.align, ".align" },
12783 { TYPE_INTEGER, &tmi.valign, ".valign" },
12784 { TYPE_INTEGER, &tmi.font, ".font" },
12785 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12786 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12787 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12788 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12789 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12790 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12791 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12792 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12793 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12799 struct TitleFadingInfo *info;
12804 // initialize first titles from "enter screen" definitions, if defined
12805 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12806 { &title_first_default, "menu.enter_screen.TITLE" },
12808 // initialize title screens from "next screen" definitions, if defined
12809 { &title_initial_default, "menu.next_screen.TITLE" },
12810 { &title_default, "menu.next_screen.TITLE" },
12816 struct TitleMessageInfo *array;
12819 titlemessage_arrays[] =
12821 // initialize first titles from "enter screen" definitions, if defined
12822 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12823 { titlescreen_first, "menu.enter_screen.TITLE" },
12824 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12825 { titlemessage_first, "menu.enter_screen.TITLE" },
12827 // initialize titles from "next screen" definitions, if defined
12828 { titlescreen_initial, "menu.next_screen.TITLE" },
12829 { titlescreen, "menu.next_screen.TITLE" },
12830 { titlemessage_initial, "menu.next_screen.TITLE" },
12831 { titlemessage, "menu.next_screen.TITLE" },
12833 // overwrite titles with title definitions, if defined
12834 { titlescreen_initial_first, "[title_initial]" },
12835 { titlescreen_first, "[title]" },
12836 { titlemessage_initial_first, "[title_initial]" },
12837 { titlemessage_first, "[title]" },
12839 { titlescreen_initial, "[title_initial]" },
12840 { titlescreen, "[title]" },
12841 { titlemessage_initial, "[title_initial]" },
12842 { titlemessage, "[title]" },
12844 // overwrite titles with title screen/message definitions, if defined
12845 { titlescreen_initial_first, "[titlescreen_initial]" },
12846 { titlescreen_first, "[titlescreen]" },
12847 { titlemessage_initial_first, "[titlemessage_initial]" },
12848 { titlemessage_first, "[titlemessage]" },
12850 { titlescreen_initial, "[titlescreen_initial]" },
12851 { titlescreen, "[titlescreen]" },
12852 { titlemessage_initial, "[titlemessage_initial]" },
12853 { titlemessage, "[titlemessage]" },
12857 SetupFileHash *setup_file_hash;
12860 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12863 // the following initializes hierarchical values from dynamic configuration
12865 // special case: initialize with default values that may be overwritten
12866 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12867 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12869 struct TokenIntPtrInfo menu_config[] =
12871 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12872 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12873 { "menu.list_size", &menu.list_size[i] }
12876 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12878 char *token = menu_config[j].token;
12879 char *value = getHashEntry(setup_file_hash, token);
12882 *menu_config[j].value = get_integer_from_string(value);
12886 // special case: initialize with default values that may be overwritten
12887 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12888 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12890 struct TokenIntPtrInfo menu_config[] =
12892 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12893 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12894 { "menu.list_size.INFO", &menu.list_size_info[i] },
12895 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12896 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12899 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12901 char *token = menu_config[j].token;
12902 char *value = getHashEntry(setup_file_hash, token);
12905 *menu_config[j].value = get_integer_from_string(value);
12909 // special case: initialize with default values that may be overwritten
12910 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12911 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12913 struct TokenIntPtrInfo menu_config[] =
12915 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12916 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12919 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12921 char *token = menu_config[j].token;
12922 char *value = getHashEntry(setup_file_hash, token);
12925 *menu_config[j].value = get_integer_from_string(value);
12929 // special case: initialize with default values that may be overwritten
12930 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12931 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12933 struct TokenIntPtrInfo menu_config[] =
12935 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12936 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
12937 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12938 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12939 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12940 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12941 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12942 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12943 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12944 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12947 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12949 char *token = menu_config[j].token;
12950 char *value = getHashEntry(setup_file_hash, token);
12953 *menu_config[j].value = get_integer_from_string(value);
12957 // special case: initialize with default values that may be overwritten
12958 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12959 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12961 struct TokenIntPtrInfo menu_config[] =
12963 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12964 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12965 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12966 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12967 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12968 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12969 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12970 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12971 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12974 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12976 char *token = menu_config[j].token;
12977 char *value = getHashEntry(setup_file_hash, token);
12980 *menu_config[j].value = get_token_parameter_value(token, value);
12984 // special case: initialize with default values that may be overwritten
12985 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12986 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12990 char *token_prefix;
12991 struct RectWithBorder *struct_ptr;
12995 { "viewport.window", &viewport.window[i] },
12996 { "viewport.playfield", &viewport.playfield[i] },
12997 { "viewport.door_1", &viewport.door_1[i] },
12998 { "viewport.door_2", &viewport.door_2[i] }
13001 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13003 struct TokenIntPtrInfo vp_config[] =
13005 { ".x", &vp_struct[j].struct_ptr->x },
13006 { ".y", &vp_struct[j].struct_ptr->y },
13007 { ".width", &vp_struct[j].struct_ptr->width },
13008 { ".height", &vp_struct[j].struct_ptr->height },
13009 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13010 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13011 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13012 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13013 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13014 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13015 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13016 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13017 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13018 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13019 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13020 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13021 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13022 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13023 { ".align", &vp_struct[j].struct_ptr->align },
13024 { ".valign", &vp_struct[j].struct_ptr->valign }
13027 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13029 char *token = getStringCat2(vp_struct[j].token_prefix,
13030 vp_config[k].token);
13031 char *value = getHashEntry(setup_file_hash, token);
13034 *vp_config[k].value = get_token_parameter_value(token, value);
13041 // special case: initialize with default values that may be overwritten
13042 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13043 for (i = 0; title_info[i].info != NULL; i++)
13045 struct TitleFadingInfo *info = title_info[i].info;
13046 char *base_token = title_info[i].text;
13048 for (j = 0; title_tokens[j].type != -1; j++)
13050 char *token = getStringCat2(base_token, title_tokens[j].text);
13051 char *value = getHashEntry(setup_file_hash, token);
13055 int parameter_value = get_token_parameter_value(token, value);
13059 *(int *)title_tokens[j].value = (int)parameter_value;
13068 // special case: initialize with default values that may be overwritten
13069 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13070 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13072 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13073 char *base_token = titlemessage_arrays[i].text;
13075 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13077 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13078 char *value = getHashEntry(setup_file_hash, token);
13082 int parameter_value = get_token_parameter_value(token, value);
13084 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13088 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13089 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13091 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13101 // read (and overwrite with) values that may be specified in config file
13102 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13104 // special case: check if network and preview player positions are redefined
13105 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13107 freeSetupFileHash(setup_file_hash);
13110 void LoadMenuDesignSettings(void)
13112 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13114 InitMenuDesignSettings_Static();
13115 InitMenuDesignSettings_SpecialPreProcessing();
13116 InitMenuDesignSettings_PreviewPlayers();
13118 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13120 // first look for special settings configured in level series config
13121 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13123 if (fileExists(filename_base))
13124 LoadMenuDesignSettingsFromFilename(filename_base);
13127 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13129 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13130 LoadMenuDesignSettingsFromFilename(filename_local);
13132 InitMenuDesignSettings_SpecialPostProcessing();
13135 void LoadMenuDesignSettings_AfterGraphics(void)
13137 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13140 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13141 boolean ignore_defaults)
13145 for (i = 0; sound_config_vars[i].token != NULL; i++)
13147 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13149 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13150 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13154 *sound_config_vars[i].value =
13155 get_token_parameter_value(sound_config_vars[i].token, value);
13159 void InitSoundSettings_Static(void)
13161 // always start with reliable default values from static default config
13162 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13165 static void LoadSoundSettingsFromFilename(char *filename)
13167 SetupFileHash *setup_file_hash;
13169 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13172 // read (and overwrite with) values that may be specified in config file
13173 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13175 freeSetupFileHash(setup_file_hash);
13178 void LoadSoundSettings(void)
13180 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13182 InitSoundSettings_Static();
13184 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13186 // first look for special settings configured in level series config
13187 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13189 if (fileExists(filename_base))
13190 LoadSoundSettingsFromFilename(filename_base);
13193 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13195 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13196 LoadSoundSettingsFromFilename(filename_local);
13199 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13201 char *filename = getEditorSetupFilename();
13202 SetupFileList *setup_file_list, *list;
13203 SetupFileHash *element_hash;
13204 int num_unknown_tokens = 0;
13207 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13210 element_hash = newSetupFileHash();
13212 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13213 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13215 // determined size may be larger than needed (due to unknown elements)
13217 for (list = setup_file_list; list != NULL; list = list->next)
13220 // add space for up to 3 more elements for padding that may be needed
13221 *num_elements += 3;
13223 // free memory for old list of elements, if needed
13224 checked_free(*elements);
13226 // allocate memory for new list of elements
13227 *elements = checked_malloc(*num_elements * sizeof(int));
13230 for (list = setup_file_list; list != NULL; list = list->next)
13232 char *value = getHashEntry(element_hash, list->token);
13234 if (value == NULL) // try to find obsolete token mapping
13236 char *mapped_token = get_mapped_token(list->token);
13238 if (mapped_token != NULL)
13240 value = getHashEntry(element_hash, mapped_token);
13242 free(mapped_token);
13248 (*elements)[(*num_elements)++] = atoi(value);
13252 if (num_unknown_tokens == 0)
13255 Warn("unknown token(s) found in config file:");
13256 Warn("- config file: '%s'", filename);
13258 num_unknown_tokens++;
13261 Warn("- token: '%s'", list->token);
13265 if (num_unknown_tokens > 0)
13268 while (*num_elements % 4) // pad with empty elements, if needed
13269 (*elements)[(*num_elements)++] = EL_EMPTY;
13271 freeSetupFileList(setup_file_list);
13272 freeSetupFileHash(element_hash);
13275 for (i = 0; i < *num_elements; i++)
13276 Debug("editor", "element '%s' [%d]\n",
13277 element_info[(*elements)[i]].token_name, (*elements)[i]);
13281 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13284 SetupFileHash *setup_file_hash = NULL;
13285 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13286 char *filename_music, *filename_prefix, *filename_info;
13292 token_to_value_ptr[] =
13294 { "title_header", &tmp_music_file_info.title_header },
13295 { "artist_header", &tmp_music_file_info.artist_header },
13296 { "album_header", &tmp_music_file_info.album_header },
13297 { "year_header", &tmp_music_file_info.year_header },
13298 { "played_header", &tmp_music_file_info.played_header },
13300 { "title", &tmp_music_file_info.title },
13301 { "artist", &tmp_music_file_info.artist },
13302 { "album", &tmp_music_file_info.album },
13303 { "year", &tmp_music_file_info.year },
13304 { "played", &tmp_music_file_info.played },
13310 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13311 getCustomMusicFilename(basename));
13313 if (filename_music == NULL)
13316 // ---------- try to replace file extension ----------
13318 filename_prefix = getStringCopy(filename_music);
13319 if (strrchr(filename_prefix, '.') != NULL)
13320 *strrchr(filename_prefix, '.') = '\0';
13321 filename_info = getStringCat2(filename_prefix, ".txt");
13323 if (fileExists(filename_info))
13324 setup_file_hash = loadSetupFileHash(filename_info);
13326 free(filename_prefix);
13327 free(filename_info);
13329 if (setup_file_hash == NULL)
13331 // ---------- try to add file extension ----------
13333 filename_prefix = getStringCopy(filename_music);
13334 filename_info = getStringCat2(filename_prefix, ".txt");
13336 if (fileExists(filename_info))
13337 setup_file_hash = loadSetupFileHash(filename_info);
13339 free(filename_prefix);
13340 free(filename_info);
13343 if (setup_file_hash == NULL)
13346 // ---------- music file info found ----------
13348 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13350 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13352 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13354 *token_to_value_ptr[i].value_ptr =
13355 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13358 tmp_music_file_info.basename = getStringCopy(basename);
13359 tmp_music_file_info.music = music;
13360 tmp_music_file_info.is_sound = is_sound;
13362 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13363 *new_music_file_info = tmp_music_file_info;
13365 return new_music_file_info;
13368 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13370 return get_music_file_info_ext(basename, music, FALSE);
13373 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13375 return get_music_file_info_ext(basename, sound, TRUE);
13378 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13379 char *basename, boolean is_sound)
13381 for (; list != NULL; list = list->next)
13382 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13388 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13390 return music_info_listed_ext(list, basename, FALSE);
13393 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13395 return music_info_listed_ext(list, basename, TRUE);
13398 void LoadMusicInfo(void)
13400 int num_music_noconf = getMusicListSize_NoConf();
13401 int num_music = getMusicListSize();
13402 int num_sounds = getSoundListSize();
13403 struct FileInfo *music, *sound;
13404 struct MusicFileInfo *next, **new;
13408 while (music_file_info != NULL)
13410 next = music_file_info->next;
13412 checked_free(music_file_info->basename);
13414 checked_free(music_file_info->title_header);
13415 checked_free(music_file_info->artist_header);
13416 checked_free(music_file_info->album_header);
13417 checked_free(music_file_info->year_header);
13418 checked_free(music_file_info->played_header);
13420 checked_free(music_file_info->title);
13421 checked_free(music_file_info->artist);
13422 checked_free(music_file_info->album);
13423 checked_free(music_file_info->year);
13424 checked_free(music_file_info->played);
13426 free(music_file_info);
13428 music_file_info = next;
13431 new = &music_file_info;
13433 // get (configured or unconfigured) music file info for all levels
13434 for (i = leveldir_current->first_level;
13435 i <= leveldir_current->last_level; i++)
13439 if (levelset.music[i] != MUS_UNDEFINED)
13441 // get music file info for configured level music
13442 music_nr = levelset.music[i];
13444 else if (num_music_noconf > 0)
13446 // get music file info for unconfigured level music
13447 int level_pos = i - leveldir_current->first_level;
13449 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13456 char *basename = getMusicInfoEntryFilename(music_nr);
13458 if (basename == NULL)
13461 if (!music_info_listed(music_file_info, basename))
13463 *new = get_music_file_info(basename, music_nr);
13466 new = &(*new)->next;
13470 // get music file info for all remaining configured music files
13471 for (i = 0; i < num_music; i++)
13473 music = getMusicListEntry(i);
13475 if (music->filename == NULL)
13478 if (strEqual(music->filename, UNDEFINED_FILENAME))
13481 // a configured file may be not recognized as music
13482 if (!FileIsMusic(music->filename))
13485 if (!music_info_listed(music_file_info, music->filename))
13487 *new = get_music_file_info(music->filename, i);
13490 new = &(*new)->next;
13494 // get sound file info for all configured sound files
13495 for (i = 0; i < num_sounds; i++)
13497 sound = getSoundListEntry(i);
13499 if (sound->filename == NULL)
13502 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13505 // a configured file may be not recognized as sound
13506 if (!FileIsSound(sound->filename))
13509 if (!sound_info_listed(music_file_info, sound->filename))
13511 *new = get_sound_file_info(sound->filename, i);
13513 new = &(*new)->next;
13517 // add pointers to previous list nodes
13519 struct MusicFileInfo *node = music_file_info;
13521 while (node != NULL)
13524 node->next->prev = node;
13530 static void add_helpanim_entry(int element, int action, int direction,
13531 int delay, int *num_list_entries)
13533 struct HelpAnimInfo *new_list_entry;
13534 (*num_list_entries)++;
13537 checked_realloc(helpanim_info,
13538 *num_list_entries * sizeof(struct HelpAnimInfo));
13539 new_list_entry = &helpanim_info[*num_list_entries - 1];
13541 new_list_entry->element = element;
13542 new_list_entry->action = action;
13543 new_list_entry->direction = direction;
13544 new_list_entry->delay = delay;
13547 static void print_unknown_token(char *filename, char *token, int token_nr)
13552 Warn("unknown token(s) found in config file:");
13553 Warn("- config file: '%s'", filename);
13556 Warn("- token: '%s'", token);
13559 static void print_unknown_token_end(int token_nr)
13565 void LoadHelpAnimInfo(void)
13567 char *filename = getHelpAnimFilename();
13568 SetupFileList *setup_file_list = NULL, *list;
13569 SetupFileHash *element_hash, *action_hash, *direction_hash;
13570 int num_list_entries = 0;
13571 int num_unknown_tokens = 0;
13574 if (fileExists(filename))
13575 setup_file_list = loadSetupFileList(filename);
13577 if (setup_file_list == NULL)
13579 // use reliable default values from static configuration
13580 SetupFileList *insert_ptr;
13582 insert_ptr = setup_file_list =
13583 newSetupFileList(helpanim_config[0].token,
13584 helpanim_config[0].value);
13586 for (i = 1; helpanim_config[i].token; i++)
13587 insert_ptr = addListEntry(insert_ptr,
13588 helpanim_config[i].token,
13589 helpanim_config[i].value);
13592 element_hash = newSetupFileHash();
13593 action_hash = newSetupFileHash();
13594 direction_hash = newSetupFileHash();
13596 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13597 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13599 for (i = 0; i < NUM_ACTIONS; i++)
13600 setHashEntry(action_hash, element_action_info[i].suffix,
13601 i_to_a(element_action_info[i].value));
13603 // do not store direction index (bit) here, but direction value!
13604 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13605 setHashEntry(direction_hash, element_direction_info[i].suffix,
13606 i_to_a(1 << element_direction_info[i].value));
13608 for (list = setup_file_list; list != NULL; list = list->next)
13610 char *element_token, *action_token, *direction_token;
13611 char *element_value, *action_value, *direction_value;
13612 int delay = atoi(list->value);
13614 if (strEqual(list->token, "end"))
13616 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13621 /* first try to break element into element/action/direction parts;
13622 if this does not work, also accept combined "element[.act][.dir]"
13623 elements (like "dynamite.active"), which are unique elements */
13625 if (strchr(list->token, '.') == NULL) // token contains no '.'
13627 element_value = getHashEntry(element_hash, list->token);
13628 if (element_value != NULL) // element found
13629 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13630 &num_list_entries);
13633 // no further suffixes found -- this is not an element
13634 print_unknown_token(filename, list->token, num_unknown_tokens++);
13640 // token has format "<prefix>.<something>"
13642 action_token = strchr(list->token, '.'); // suffix may be action ...
13643 direction_token = action_token; // ... or direction
13645 element_token = getStringCopy(list->token);
13646 *strchr(element_token, '.') = '\0';
13648 element_value = getHashEntry(element_hash, element_token);
13650 if (element_value == NULL) // this is no element
13652 element_value = getHashEntry(element_hash, list->token);
13653 if (element_value != NULL) // combined element found
13654 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13655 &num_list_entries);
13657 print_unknown_token(filename, list->token, num_unknown_tokens++);
13659 free(element_token);
13664 action_value = getHashEntry(action_hash, action_token);
13666 if (action_value != NULL) // action found
13668 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13669 &num_list_entries);
13671 free(element_token);
13676 direction_value = getHashEntry(direction_hash, direction_token);
13678 if (direction_value != NULL) // direction found
13680 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13681 &num_list_entries);
13683 free(element_token);
13688 if (strchr(action_token + 1, '.') == NULL)
13690 // no further suffixes found -- this is not an action nor direction
13692 element_value = getHashEntry(element_hash, list->token);
13693 if (element_value != NULL) // combined element found
13694 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13695 &num_list_entries);
13697 print_unknown_token(filename, list->token, num_unknown_tokens++);
13699 free(element_token);
13704 // token has format "<prefix>.<suffix>.<something>"
13706 direction_token = strchr(action_token + 1, '.');
13708 action_token = getStringCopy(action_token);
13709 *strchr(action_token + 1, '.') = '\0';
13711 action_value = getHashEntry(action_hash, action_token);
13713 if (action_value == NULL) // this is no action
13715 element_value = getHashEntry(element_hash, list->token);
13716 if (element_value != NULL) // combined element found
13717 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13718 &num_list_entries);
13720 print_unknown_token(filename, list->token, num_unknown_tokens++);
13722 free(element_token);
13723 free(action_token);
13728 direction_value = getHashEntry(direction_hash, direction_token);
13730 if (direction_value != NULL) // direction found
13732 add_helpanim_entry(atoi(element_value), atoi(action_value),
13733 atoi(direction_value), delay, &num_list_entries);
13735 free(element_token);
13736 free(action_token);
13741 // this is no direction
13743 element_value = getHashEntry(element_hash, list->token);
13744 if (element_value != NULL) // combined element found
13745 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13746 &num_list_entries);
13748 print_unknown_token(filename, list->token, num_unknown_tokens++);
13750 free(element_token);
13751 free(action_token);
13754 print_unknown_token_end(num_unknown_tokens);
13756 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13757 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13759 freeSetupFileList(setup_file_list);
13760 freeSetupFileHash(element_hash);
13761 freeSetupFileHash(action_hash);
13762 freeSetupFileHash(direction_hash);
13765 for (i = 0; i < num_list_entries; i++)
13766 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13767 EL_NAME(helpanim_info[i].element),
13768 helpanim_info[i].element,
13769 helpanim_info[i].action,
13770 helpanim_info[i].direction,
13771 helpanim_info[i].delay);
13775 void LoadHelpTextInfo(void)
13777 char *filename = getHelpTextFilename();
13780 if (helptext_info != NULL)
13782 freeSetupFileHash(helptext_info);
13783 helptext_info = NULL;
13786 if (fileExists(filename))
13787 helptext_info = loadSetupFileHash(filename);
13789 if (helptext_info == NULL)
13791 // use reliable default values from static configuration
13792 helptext_info = newSetupFileHash();
13794 for (i = 0; helptext_config[i].token; i++)
13795 setHashEntry(helptext_info,
13796 helptext_config[i].token,
13797 helptext_config[i].value);
13801 BEGIN_HASH_ITERATION(helptext_info, itr)
13803 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13804 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13806 END_HASH_ITERATION(hash, itr)
13811 // ----------------------------------------------------------------------------
13813 // ----------------------------------------------------------------------------
13815 #define MAX_NUM_CONVERT_LEVELS 1000
13817 void ConvertLevels(void)
13819 static LevelDirTree *convert_leveldir = NULL;
13820 static int convert_level_nr = -1;
13821 static int num_levels_handled = 0;
13822 static int num_levels_converted = 0;
13823 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13826 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13827 global.convert_leveldir);
13829 if (convert_leveldir == NULL)
13830 Fail("no such level identifier: '%s'", global.convert_leveldir);
13832 leveldir_current = convert_leveldir;
13834 if (global.convert_level_nr != -1)
13836 convert_leveldir->first_level = global.convert_level_nr;
13837 convert_leveldir->last_level = global.convert_level_nr;
13840 convert_level_nr = convert_leveldir->first_level;
13842 PrintLine("=", 79);
13843 Print("Converting levels\n");
13844 PrintLine("-", 79);
13845 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13846 Print("Level series name: '%s'\n", convert_leveldir->name);
13847 Print("Level series author: '%s'\n", convert_leveldir->author);
13848 Print("Number of levels: %d\n", convert_leveldir->levels);
13849 PrintLine("=", 79);
13852 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13853 levels_failed[i] = FALSE;
13855 while (convert_level_nr <= convert_leveldir->last_level)
13857 char *level_filename;
13860 level_nr = convert_level_nr++;
13862 Print("Level %03d: ", level_nr);
13864 LoadLevel(level_nr);
13865 if (level.no_level_file || level.no_valid_file)
13867 Print("(no level)\n");
13871 Print("converting level ... ");
13874 // special case: conversion of some EMC levels as requested by ACME
13875 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13878 level_filename = getDefaultLevelFilename(level_nr);
13879 new_level = !fileExists(level_filename);
13883 SaveLevel(level_nr);
13885 num_levels_converted++;
13887 Print("converted.\n");
13891 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13892 levels_failed[level_nr] = TRUE;
13894 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13897 num_levels_handled++;
13901 PrintLine("=", 79);
13902 Print("Number of levels handled: %d\n", num_levels_handled);
13903 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13904 (num_levels_handled ?
13905 num_levels_converted * 100 / num_levels_handled : 0));
13906 PrintLine("-", 79);
13907 Print("Summary (for automatic parsing by scripts):\n");
13908 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13909 convert_leveldir->identifier, num_levels_converted,
13910 num_levels_handled,
13911 (num_levels_handled ?
13912 num_levels_converted * 100 / num_levels_handled : 0));
13914 if (num_levels_handled != num_levels_converted)
13916 Print(", FAILED:");
13917 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13918 if (levels_failed[i])
13923 PrintLine("=", 79);
13925 CloseAllAndExit(0);
13929 // ----------------------------------------------------------------------------
13930 // create and save images for use in level sketches (raw BMP format)
13931 // ----------------------------------------------------------------------------
13933 void CreateLevelSketchImages(void)
13939 InitElementPropertiesGfxElement();
13941 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13942 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13944 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13946 int element = getMappedElement(i);
13947 char basename1[16];
13948 char basename2[16];
13952 sprintf(basename1, "%04d.bmp", i);
13953 sprintf(basename2, "%04ds.bmp", i);
13955 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13956 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13958 DrawSizedElement(0, 0, element, TILESIZE);
13959 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13961 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13962 Fail("cannot save level sketch image file '%s'", filename1);
13964 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13965 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13967 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13968 Fail("cannot save level sketch image file '%s'", filename2);
13973 // create corresponding SQL statements (for normal and small images)
13976 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13977 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13980 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13981 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13983 // optional: create content for forum level sketch demonstration post
13985 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13988 FreeBitmap(bitmap1);
13989 FreeBitmap(bitmap2);
13992 fprintf(stderr, "\n");
13994 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13996 CloseAllAndExit(0);
14000 // ----------------------------------------------------------------------------
14001 // create and save images for element collecting animations (raw BMP format)
14002 // ----------------------------------------------------------------------------
14004 static boolean createCollectImage(int element)
14006 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14009 void CreateCollectElementImages(void)
14013 int anim_frames = num_steps - 1;
14014 int tile_size = TILESIZE;
14015 int anim_width = tile_size * anim_frames;
14016 int anim_height = tile_size;
14017 int num_collect_images = 0;
14018 int pos_collect_images = 0;
14020 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14021 if (createCollectImage(i))
14022 num_collect_images++;
14024 Info("Creating %d element collecting animation images ...",
14025 num_collect_images);
14027 int dst_width = anim_width * 2;
14028 int dst_height = anim_height * num_collect_images / 2;
14029 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14030 char *basename_bmp = "RocksCollect.bmp";
14031 char *basename_png = "RocksCollect.png";
14032 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14033 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14034 int len_filename_bmp = strlen(filename_bmp);
14035 int len_filename_png = strlen(filename_png);
14036 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14037 char cmd_convert[max_command_len];
14039 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14043 // force using RGBA surface for destination bitmap
14044 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14045 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14047 dst_bitmap->surface =
14048 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14050 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14052 if (!createCollectImage(i))
14055 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14056 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14057 int graphic = el2img(i);
14058 char *token_name = element_info[i].token_name;
14059 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14060 Bitmap *src_bitmap;
14063 Info("- creating collecting image for '%s' ...", token_name);
14065 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14067 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14068 tile_size, tile_size, 0, 0);
14070 // force using RGBA surface for temporary bitmap (using transparent black)
14071 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14072 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14074 tmp_bitmap->surface =
14075 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14077 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14079 for (j = 0; j < anim_frames; j++)
14081 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14082 int frame_size = frame_size_final * num_steps;
14083 int offset = (tile_size - frame_size_final) / 2;
14084 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14086 while (frame_size > frame_size_final)
14090 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14092 FreeBitmap(frame_bitmap);
14094 frame_bitmap = half_bitmap;
14097 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14098 frame_size_final, frame_size_final,
14099 dst_x + j * tile_size + offset, dst_y + offset);
14101 FreeBitmap(frame_bitmap);
14104 tmp_bitmap->surface_masked = NULL;
14106 FreeBitmap(tmp_bitmap);
14108 pos_collect_images++;
14111 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14112 Fail("cannot save element collecting image file '%s'", filename_bmp);
14114 FreeBitmap(dst_bitmap);
14116 Info("Converting image file from BMP to PNG ...");
14118 if (system(cmd_convert) != 0)
14119 Fail("converting image file failed");
14121 unlink(filename_bmp);
14125 CloseAllAndExit(0);
14129 // ----------------------------------------------------------------------------
14130 // create and save images for custom and group elements (raw BMP format)
14131 // ----------------------------------------------------------------------------
14133 void CreateCustomElementImages(char *directory)
14135 char *src_basename = "RocksCE-template.ilbm";
14136 char *dst_basename = "RocksCE.bmp";
14137 char *src_filename = getPath2(directory, src_basename);
14138 char *dst_filename = getPath2(directory, dst_basename);
14139 Bitmap *src_bitmap;
14141 int yoffset_ce = 0;
14142 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14145 InitVideoDefaults();
14147 ReCreateBitmap(&backbuffer, video.width, video.height);
14149 src_bitmap = LoadImage(src_filename);
14151 bitmap = CreateBitmap(TILEX * 16 * 2,
14152 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14155 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14162 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14163 TILEX * x, TILEY * y + yoffset_ce);
14165 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14167 TILEX * x + TILEX * 16,
14168 TILEY * y + yoffset_ce);
14170 for (j = 2; j >= 0; j--)
14174 BlitBitmap(src_bitmap, bitmap,
14175 TILEX + c * 7, 0, 6, 10,
14176 TILEX * x + 6 + j * 7,
14177 TILEY * y + 11 + yoffset_ce);
14179 BlitBitmap(src_bitmap, bitmap,
14180 TILEX + c * 8, TILEY, 6, 10,
14181 TILEX * 16 + TILEX * x + 6 + j * 8,
14182 TILEY * y + 10 + yoffset_ce);
14188 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14195 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14196 TILEX * x, TILEY * y + yoffset_ge);
14198 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14200 TILEX * x + TILEX * 16,
14201 TILEY * y + yoffset_ge);
14203 for (j = 1; j >= 0; j--)
14207 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14208 TILEX * x + 6 + j * 10,
14209 TILEY * y + 11 + yoffset_ge);
14211 BlitBitmap(src_bitmap, bitmap,
14212 TILEX + c * 8, TILEY + 12, 6, 10,
14213 TILEX * 16 + TILEX * x + 10 + j * 8,
14214 TILEY * y + 10 + yoffset_ge);
14220 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14221 Fail("cannot save CE graphics file '%s'", dst_filename);
14223 FreeBitmap(bitmap);
14225 CloseAllAndExit(0);