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 strncpy(level->name, cave->name, MAX_LEVEL_NAME_LEN);
3775 level->name[MAX_LEVEL_NAME_LEN] = '\0';
3777 for (x = 0; x < level->fieldx; x++)
3778 for (y = 0; y < level->fieldy; y++)
3779 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3782 static void setTapeInfoToDefaults(void);
3784 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
3786 struct LevelInfo_BD *level_bd = level->native_bd_level;
3787 GdCave *cave = level_bd->cave;
3788 GdReplay *replay = level_bd->replay;
3794 // always start with reliable default values
3795 setTapeInfoToDefaults();
3797 tape.level_nr = level_nr; // (currently not used)
3798 tape.random_seed = replay->seed;
3800 TapeSetDateFromIsoDateString(replay->date);
3803 tape.pos[tape.counter].delay = 0;
3805 tape.bd_replay = TRUE;
3807 // all time calculations only used to display approximate tape time
3808 int cave_speed = cave->speed;
3809 int milliseconds_game = 0;
3810 int milliseconds_elapsed = 20;
3812 for (i = 0; i < replay->movements->len; i++)
3814 int replay_action = replay->movements->data[i];
3815 int tape_action = map_action_BD_to_RND(replay_action);
3816 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3817 boolean success = 0;
3821 success = TapeAddAction(action);
3823 milliseconds_game += milliseconds_elapsed;
3825 if (milliseconds_game >= cave_speed)
3827 milliseconds_game -= cave_speed;
3834 tape.pos[tape.counter].delay = 0;
3835 tape.pos[tape.counter].action[0] = 0;
3839 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
3845 TapeHaltRecording();
3849 // ----------------------------------------------------------------------------
3850 // functions for loading EM level
3851 // ----------------------------------------------------------------------------
3853 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3855 static int ball_xy[8][2] =
3866 struct LevelInfo_EM *level_em = level->native_em_level;
3867 struct CAVE *cav = level_em->cav;
3870 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3871 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3873 cav->time_seconds = level->time;
3874 cav->gems_needed = level->gems_needed;
3876 cav->emerald_score = level->score[SC_EMERALD];
3877 cav->diamond_score = level->score[SC_DIAMOND];
3878 cav->alien_score = level->score[SC_ROBOT];
3879 cav->tank_score = level->score[SC_SPACESHIP];
3880 cav->bug_score = level->score[SC_BUG];
3881 cav->eater_score = level->score[SC_YAMYAM];
3882 cav->nut_score = level->score[SC_NUT];
3883 cav->dynamite_score = level->score[SC_DYNAMITE];
3884 cav->key_score = level->score[SC_KEY];
3885 cav->exit_score = level->score[SC_TIME_BONUS];
3887 cav->num_eater_arrays = level->num_yamyam_contents;
3889 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3890 for (y = 0; y < 3; y++)
3891 for (x = 0; x < 3; x++)
3892 cav->eater_array[i][y * 3 + x] =
3893 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3895 cav->amoeba_time = level->amoeba_speed;
3896 cav->wonderwall_time = level->time_magic_wall;
3897 cav->wheel_time = level->time_wheel;
3899 cav->android_move_time = level->android_move_time;
3900 cav->android_clone_time = level->android_clone_time;
3901 cav->ball_random = level->ball_random;
3902 cav->ball_active = level->ball_active_initial;
3903 cav->ball_time = level->ball_time;
3904 cav->num_ball_arrays = level->num_ball_contents;
3906 cav->lenses_score = level->lenses_score;
3907 cav->magnify_score = level->magnify_score;
3908 cav->slurp_score = level->slurp_score;
3910 cav->lenses_time = level->lenses_time;
3911 cav->magnify_time = level->magnify_time;
3913 cav->wind_time = 9999;
3914 cav->wind_direction =
3915 map_direction_RND_to_EM(level->wind_direction_initial);
3917 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3918 for (j = 0; j < 8; j++)
3919 cav->ball_array[i][j] =
3920 map_element_RND_to_EM_cave(level->ball_content[i].
3921 e[ball_xy[j][0]][ball_xy[j][1]]);
3923 map_android_clone_elements_RND_to_EM(level);
3925 // first fill the complete playfield with the empty space element
3926 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3927 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3928 cav->cave[x][y] = Cblank;
3930 // then copy the real level contents from level file into the playfield
3931 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3933 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3935 if (level->field[x][y] == EL_AMOEBA_DEAD)
3936 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3938 cav->cave[x][y] = new_element;
3941 for (i = 0; i < MAX_PLAYERS; i++)
3943 cav->player_x[i] = -1;
3944 cav->player_y[i] = -1;
3947 // initialize player positions and delete players from the playfield
3948 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3950 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3952 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3954 cav->player_x[player_nr] = x;
3955 cav->player_y[player_nr] = y;
3957 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3962 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3964 static int ball_xy[8][2] =
3975 struct LevelInfo_EM *level_em = level->native_em_level;
3976 struct CAVE *cav = level_em->cav;
3979 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3980 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3982 level->time = cav->time_seconds;
3983 level->gems_needed = cav->gems_needed;
3985 sprintf(level->name, "Level %d", level->file_info.nr);
3987 level->score[SC_EMERALD] = cav->emerald_score;
3988 level->score[SC_DIAMOND] = cav->diamond_score;
3989 level->score[SC_ROBOT] = cav->alien_score;
3990 level->score[SC_SPACESHIP] = cav->tank_score;
3991 level->score[SC_BUG] = cav->bug_score;
3992 level->score[SC_YAMYAM] = cav->eater_score;
3993 level->score[SC_NUT] = cav->nut_score;
3994 level->score[SC_DYNAMITE] = cav->dynamite_score;
3995 level->score[SC_KEY] = cav->key_score;
3996 level->score[SC_TIME_BONUS] = cav->exit_score;
3998 level->num_yamyam_contents = cav->num_eater_arrays;
4000 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4001 for (y = 0; y < 3; y++)
4002 for (x = 0; x < 3; x++)
4003 level->yamyam_content[i].e[x][y] =
4004 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4006 level->amoeba_speed = cav->amoeba_time;
4007 level->time_magic_wall = cav->wonderwall_time;
4008 level->time_wheel = cav->wheel_time;
4010 level->android_move_time = cav->android_move_time;
4011 level->android_clone_time = cav->android_clone_time;
4012 level->ball_random = cav->ball_random;
4013 level->ball_active_initial = cav->ball_active;
4014 level->ball_time = cav->ball_time;
4015 level->num_ball_contents = cav->num_ball_arrays;
4017 level->lenses_score = cav->lenses_score;
4018 level->magnify_score = cav->magnify_score;
4019 level->slurp_score = cav->slurp_score;
4021 level->lenses_time = cav->lenses_time;
4022 level->magnify_time = cav->magnify_time;
4024 level->wind_direction_initial =
4025 map_direction_EM_to_RND(cav->wind_direction);
4027 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4028 for (j = 0; j < 8; j++)
4029 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4030 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4032 map_android_clone_elements_EM_to_RND(level);
4034 // convert the playfield (some elements need special treatment)
4035 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4037 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4039 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4040 new_element = EL_AMOEBA_DEAD;
4042 level->field[x][y] = new_element;
4045 for (i = 0; i < MAX_PLAYERS; i++)
4047 // in case of all players set to the same field, use the first player
4048 int nr = MAX_PLAYERS - i - 1;
4049 int jx = cav->player_x[nr];
4050 int jy = cav->player_y[nr];
4052 if (jx != -1 && jy != -1)
4053 level->field[jx][jy] = EL_PLAYER_1 + nr;
4056 // time score is counted for each 10 seconds left in Emerald Mine levels
4057 level->time_score_base = 10;
4061 // ----------------------------------------------------------------------------
4062 // functions for loading SP level
4063 // ----------------------------------------------------------------------------
4065 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4067 struct LevelInfo_SP *level_sp = level->native_sp_level;
4068 LevelInfoType *header = &level_sp->header;
4071 level_sp->width = level->fieldx;
4072 level_sp->height = level->fieldy;
4074 for (x = 0; x < level->fieldx; x++)
4075 for (y = 0; y < level->fieldy; y++)
4076 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4078 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4080 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4081 header->LevelTitle[i] = level->name[i];
4082 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4084 header->InfotronsNeeded = level->gems_needed;
4086 header->SpecialPortCount = 0;
4088 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4090 boolean gravity_port_found = FALSE;
4091 boolean gravity_port_valid = FALSE;
4092 int gravity_port_flag;
4093 int gravity_port_base_element;
4094 int element = level->field[x][y];
4096 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4097 element <= EL_SP_GRAVITY_ON_PORT_UP)
4099 gravity_port_found = TRUE;
4100 gravity_port_valid = TRUE;
4101 gravity_port_flag = 1;
4102 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4104 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4105 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4107 gravity_port_found = TRUE;
4108 gravity_port_valid = TRUE;
4109 gravity_port_flag = 0;
4110 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4112 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4113 element <= EL_SP_GRAVITY_PORT_UP)
4115 // change R'n'D style gravity inverting special port to normal port
4116 // (there are no gravity inverting ports in native Supaplex engine)
4118 gravity_port_found = TRUE;
4119 gravity_port_valid = FALSE;
4120 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4123 if (gravity_port_found)
4125 if (gravity_port_valid &&
4126 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4128 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4130 port->PortLocation = (y * level->fieldx + x) * 2;
4131 port->Gravity = gravity_port_flag;
4133 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4135 header->SpecialPortCount++;
4139 // change special gravity port to normal port
4141 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4144 level_sp->playfield[x][y] = element - EL_SP_START;
4149 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4151 struct LevelInfo_SP *level_sp = level->native_sp_level;
4152 LevelInfoType *header = &level_sp->header;
4153 boolean num_invalid_elements = 0;
4156 level->fieldx = level_sp->width;
4157 level->fieldy = level_sp->height;
4159 for (x = 0; x < level->fieldx; x++)
4161 for (y = 0; y < level->fieldy; y++)
4163 int element_old = level_sp->playfield[x][y];
4164 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4166 if (element_new == EL_UNKNOWN)
4168 num_invalid_elements++;
4170 Debug("level:native:SP", "invalid element %d at position %d, %d",
4174 level->field[x][y] = element_new;
4178 if (num_invalid_elements > 0)
4179 Warn("found %d invalid elements%s", num_invalid_elements,
4180 (!options.debug ? " (use '--debug' for more details)" : ""));
4182 for (i = 0; i < MAX_PLAYERS; i++)
4183 level->initial_player_gravity[i] =
4184 (header->InitialGravity == 1 ? TRUE : FALSE);
4186 // skip leading spaces
4187 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4188 if (header->LevelTitle[i] != ' ')
4192 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4193 level->name[j] = header->LevelTitle[i];
4194 level->name[j] = '\0';
4196 // cut trailing spaces
4198 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4199 level->name[j - 1] = '\0';
4201 level->gems_needed = header->InfotronsNeeded;
4203 for (i = 0; i < header->SpecialPortCount; i++)
4205 SpecialPortType *port = &header->SpecialPort[i];
4206 int port_location = port->PortLocation;
4207 int gravity = port->Gravity;
4208 int port_x, port_y, port_element;
4210 port_x = (port_location / 2) % level->fieldx;
4211 port_y = (port_location / 2) / level->fieldx;
4213 if (port_x < 0 || port_x >= level->fieldx ||
4214 port_y < 0 || port_y >= level->fieldy)
4216 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4221 port_element = level->field[port_x][port_y];
4223 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4224 port_element > EL_SP_GRAVITY_PORT_UP)
4226 Warn("no special port at position (%d, %d)", port_x, port_y);
4231 // change previous (wrong) gravity inverting special port to either
4232 // gravity enabling special port or gravity disabling special port
4233 level->field[port_x][port_y] +=
4234 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4235 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4238 // change special gravity ports without database entries to normal ports
4239 for (x = 0; x < level->fieldx; x++)
4240 for (y = 0; y < level->fieldy; y++)
4241 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4242 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4243 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4245 level->time = 0; // no time limit
4246 level->amoeba_speed = 0;
4247 level->time_magic_wall = 0;
4248 level->time_wheel = 0;
4249 level->amoeba_content = EL_EMPTY;
4251 // original Supaplex does not use score values -- rate by playing time
4252 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4253 level->score[i] = 0;
4255 level->rate_time_over_score = TRUE;
4257 // there are no yamyams in supaplex levels
4258 for (i = 0; i < level->num_yamyam_contents; i++)
4259 for (x = 0; x < 3; x++)
4260 for (y = 0; y < 3; y++)
4261 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4264 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4266 struct LevelInfo_SP *level_sp = level->native_sp_level;
4267 struct DemoInfo_SP *demo = &level_sp->demo;
4270 // always start with reliable default values
4271 demo->is_available = FALSE;
4274 if (TAPE_IS_EMPTY(tape))
4277 demo->level_nr = tape.level_nr; // (currently not used)
4279 level_sp->header.DemoRandomSeed = tape.random_seed;
4283 for (i = 0; i < tape.length; i++)
4285 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4286 int demo_repeat = tape.pos[i].delay;
4287 int demo_entries = (demo_repeat + 15) / 16;
4289 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4291 Warn("tape truncated: size exceeds maximum SP demo size %d",
4297 for (j = 0; j < demo_repeat / 16; j++)
4298 demo->data[demo->length++] = 0xf0 | demo_action;
4300 if (demo_repeat % 16)
4301 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4304 demo->is_available = TRUE;
4307 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4309 struct LevelInfo_SP *level_sp = level->native_sp_level;
4310 struct DemoInfo_SP *demo = &level_sp->demo;
4311 char *filename = level->file_info.filename;
4314 // always start with reliable default values
4315 setTapeInfoToDefaults();
4317 if (!demo->is_available)
4320 tape.level_nr = demo->level_nr; // (currently not used)
4321 tape.random_seed = level_sp->header.DemoRandomSeed;
4323 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4326 tape.pos[tape.counter].delay = 0;
4328 for (i = 0; i < demo->length; i++)
4330 int demo_action = demo->data[i] & 0x0f;
4331 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4332 int tape_action = map_key_SP_to_RND(demo_action);
4333 int tape_repeat = demo_repeat + 1;
4334 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4335 boolean success = 0;
4338 for (j = 0; j < tape_repeat; j++)
4339 success = TapeAddAction(action);
4343 Warn("SP demo truncated: size exceeds maximum tape size %d",
4350 TapeHaltRecording();
4354 // ----------------------------------------------------------------------------
4355 // functions for loading MM level
4356 // ----------------------------------------------------------------------------
4358 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4360 struct LevelInfo_MM *level_mm = level->native_mm_level;
4363 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4364 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4366 level_mm->time = level->time;
4367 level_mm->kettles_needed = level->gems_needed;
4368 level_mm->auto_count_kettles = level->auto_count_gems;
4370 level_mm->mm_laser_red = level->mm_laser_red;
4371 level_mm->mm_laser_green = level->mm_laser_green;
4372 level_mm->mm_laser_blue = level->mm_laser_blue;
4374 level_mm->df_laser_red = level->df_laser_red;
4375 level_mm->df_laser_green = level->df_laser_green;
4376 level_mm->df_laser_blue = level->df_laser_blue;
4378 strcpy(level_mm->name, level->name);
4379 strcpy(level_mm->author, level->author);
4381 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4382 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4383 level_mm->score[SC_KEY] = level->score[SC_KEY];
4384 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4385 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4387 level_mm->amoeba_speed = level->amoeba_speed;
4388 level_mm->time_fuse = level->mm_time_fuse;
4389 level_mm->time_bomb = level->mm_time_bomb;
4390 level_mm->time_ball = level->mm_time_ball;
4391 level_mm->time_block = level->mm_time_block;
4393 level_mm->num_ball_contents = level->num_mm_ball_contents;
4394 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4395 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4396 level_mm->explode_ball = level->explode_mm_ball;
4398 for (i = 0; i < level->num_mm_ball_contents; i++)
4399 level_mm->ball_content[i] =
4400 map_element_RND_to_MM(level->mm_ball_content[i]);
4402 for (x = 0; x < level->fieldx; x++)
4403 for (y = 0; y < level->fieldy; y++)
4405 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4408 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4410 struct LevelInfo_MM *level_mm = level->native_mm_level;
4413 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4414 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4416 level->time = level_mm->time;
4417 level->gems_needed = level_mm->kettles_needed;
4418 level->auto_count_gems = level_mm->auto_count_kettles;
4420 level->mm_laser_red = level_mm->mm_laser_red;
4421 level->mm_laser_green = level_mm->mm_laser_green;
4422 level->mm_laser_blue = level_mm->mm_laser_blue;
4424 level->df_laser_red = level_mm->df_laser_red;
4425 level->df_laser_green = level_mm->df_laser_green;
4426 level->df_laser_blue = level_mm->df_laser_blue;
4428 strcpy(level->name, level_mm->name);
4430 // only overwrite author from 'levelinfo.conf' if author defined in level
4431 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4432 strcpy(level->author, level_mm->author);
4434 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4435 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4436 level->score[SC_KEY] = level_mm->score[SC_KEY];
4437 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4438 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4440 level->amoeba_speed = level_mm->amoeba_speed;
4441 level->mm_time_fuse = level_mm->time_fuse;
4442 level->mm_time_bomb = level_mm->time_bomb;
4443 level->mm_time_ball = level_mm->time_ball;
4444 level->mm_time_block = level_mm->time_block;
4446 level->num_mm_ball_contents = level_mm->num_ball_contents;
4447 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4448 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4449 level->explode_mm_ball = level_mm->explode_ball;
4451 for (i = 0; i < level->num_mm_ball_contents; i++)
4452 level->mm_ball_content[i] =
4453 map_element_MM_to_RND(level_mm->ball_content[i]);
4455 for (x = 0; x < level->fieldx; x++)
4456 for (y = 0; y < level->fieldy; y++)
4457 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4461 // ----------------------------------------------------------------------------
4462 // functions for loading DC level
4463 // ----------------------------------------------------------------------------
4465 #define DC_LEVEL_HEADER_SIZE 344
4467 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4470 static int last_data_encoded;
4474 int diff_hi, diff_lo;
4475 int data_hi, data_lo;
4476 unsigned short data_decoded;
4480 last_data_encoded = 0;
4487 diff = data_encoded - last_data_encoded;
4488 diff_hi = diff & ~0xff;
4489 diff_lo = diff & 0xff;
4493 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4494 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4495 data_hi = data_hi & 0xff00;
4497 data_decoded = data_hi | data_lo;
4499 last_data_encoded = data_encoded;
4501 offset1 = (offset1 + 1) % 31;
4502 offset2 = offset2 & 0xff;
4504 return data_decoded;
4507 static int getMappedElement_DC(int element)
4515 // 0x0117 - 0x036e: (?)
4518 // 0x042d - 0x0684: (?)
4534 element = EL_CRYSTAL;
4537 case 0x0e77: // quicksand (boulder)
4538 element = EL_QUICKSAND_FAST_FULL;
4541 case 0x0e99: // slow quicksand (boulder)
4542 element = EL_QUICKSAND_FULL;
4546 element = EL_EM_EXIT_OPEN;
4550 element = EL_EM_EXIT_CLOSED;
4554 element = EL_EM_STEEL_EXIT_OPEN;
4558 element = EL_EM_STEEL_EXIT_CLOSED;
4561 case 0x0f4f: // dynamite (lit 1)
4562 element = EL_EM_DYNAMITE_ACTIVE;
4565 case 0x0f57: // dynamite (lit 2)
4566 element = EL_EM_DYNAMITE_ACTIVE;
4569 case 0x0f5f: // dynamite (lit 3)
4570 element = EL_EM_DYNAMITE_ACTIVE;
4573 case 0x0f67: // dynamite (lit 4)
4574 element = EL_EM_DYNAMITE_ACTIVE;
4581 element = EL_AMOEBA_WET;
4585 element = EL_AMOEBA_DROP;
4589 element = EL_DC_MAGIC_WALL;
4593 element = EL_SPACESHIP_UP;
4597 element = EL_SPACESHIP_DOWN;
4601 element = EL_SPACESHIP_LEFT;
4605 element = EL_SPACESHIP_RIGHT;
4609 element = EL_BUG_UP;
4613 element = EL_BUG_DOWN;
4617 element = EL_BUG_LEFT;
4621 element = EL_BUG_RIGHT;
4625 element = EL_MOLE_UP;
4629 element = EL_MOLE_DOWN;
4633 element = EL_MOLE_LEFT;
4637 element = EL_MOLE_RIGHT;
4645 element = EL_YAMYAM_UP;
4649 element = EL_SWITCHGATE_OPEN;
4653 element = EL_SWITCHGATE_CLOSED;
4657 element = EL_DC_SWITCHGATE_SWITCH_UP;
4661 element = EL_TIMEGATE_CLOSED;
4664 case 0x144c: // conveyor belt switch (green)
4665 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4668 case 0x144f: // conveyor belt switch (red)
4669 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4672 case 0x1452: // conveyor belt switch (blue)
4673 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4677 element = EL_CONVEYOR_BELT_3_MIDDLE;
4681 element = EL_CONVEYOR_BELT_3_LEFT;
4685 element = EL_CONVEYOR_BELT_3_RIGHT;
4689 element = EL_CONVEYOR_BELT_1_MIDDLE;
4693 element = EL_CONVEYOR_BELT_1_LEFT;
4697 element = EL_CONVEYOR_BELT_1_RIGHT;
4701 element = EL_CONVEYOR_BELT_4_MIDDLE;
4705 element = EL_CONVEYOR_BELT_4_LEFT;
4709 element = EL_CONVEYOR_BELT_4_RIGHT;
4713 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4717 element = EL_EXPANDABLE_WALL_VERTICAL;
4721 element = EL_EXPANDABLE_WALL_ANY;
4724 case 0x14ce: // growing steel wall (left/right)
4725 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4728 case 0x14df: // growing steel wall (up/down)
4729 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4732 case 0x14e8: // growing steel wall (up/down/left/right)
4733 element = EL_EXPANDABLE_STEELWALL_ANY;
4737 element = EL_SHIELD_DEADLY;
4741 element = EL_EXTRA_TIME;
4749 element = EL_EMPTY_SPACE;
4752 case 0x1578: // quicksand (empty)
4753 element = EL_QUICKSAND_FAST_EMPTY;
4756 case 0x1579: // slow quicksand (empty)
4757 element = EL_QUICKSAND_EMPTY;
4767 element = EL_EM_DYNAMITE;
4770 case 0x15a1: // key (red)
4771 element = EL_EM_KEY_1;
4774 case 0x15a2: // key (yellow)
4775 element = EL_EM_KEY_2;
4778 case 0x15a3: // key (blue)
4779 element = EL_EM_KEY_4;
4782 case 0x15a4: // key (green)
4783 element = EL_EM_KEY_3;
4786 case 0x15a5: // key (white)
4787 element = EL_DC_KEY_WHITE;
4791 element = EL_WALL_SLIPPERY;
4798 case 0x15a8: // wall (not round)
4802 case 0x15a9: // (blue)
4803 element = EL_CHAR_A;
4806 case 0x15aa: // (blue)
4807 element = EL_CHAR_B;
4810 case 0x15ab: // (blue)
4811 element = EL_CHAR_C;
4814 case 0x15ac: // (blue)
4815 element = EL_CHAR_D;
4818 case 0x15ad: // (blue)
4819 element = EL_CHAR_E;
4822 case 0x15ae: // (blue)
4823 element = EL_CHAR_F;
4826 case 0x15af: // (blue)
4827 element = EL_CHAR_G;
4830 case 0x15b0: // (blue)
4831 element = EL_CHAR_H;
4834 case 0x15b1: // (blue)
4835 element = EL_CHAR_I;
4838 case 0x15b2: // (blue)
4839 element = EL_CHAR_J;
4842 case 0x15b3: // (blue)
4843 element = EL_CHAR_K;
4846 case 0x15b4: // (blue)
4847 element = EL_CHAR_L;
4850 case 0x15b5: // (blue)
4851 element = EL_CHAR_M;
4854 case 0x15b6: // (blue)
4855 element = EL_CHAR_N;
4858 case 0x15b7: // (blue)
4859 element = EL_CHAR_O;
4862 case 0x15b8: // (blue)
4863 element = EL_CHAR_P;
4866 case 0x15b9: // (blue)
4867 element = EL_CHAR_Q;
4870 case 0x15ba: // (blue)
4871 element = EL_CHAR_R;
4874 case 0x15bb: // (blue)
4875 element = EL_CHAR_S;
4878 case 0x15bc: // (blue)
4879 element = EL_CHAR_T;
4882 case 0x15bd: // (blue)
4883 element = EL_CHAR_U;
4886 case 0x15be: // (blue)
4887 element = EL_CHAR_V;
4890 case 0x15bf: // (blue)
4891 element = EL_CHAR_W;
4894 case 0x15c0: // (blue)
4895 element = EL_CHAR_X;
4898 case 0x15c1: // (blue)
4899 element = EL_CHAR_Y;
4902 case 0x15c2: // (blue)
4903 element = EL_CHAR_Z;
4906 case 0x15c3: // (blue)
4907 element = EL_CHAR_AUMLAUT;
4910 case 0x15c4: // (blue)
4911 element = EL_CHAR_OUMLAUT;
4914 case 0x15c5: // (blue)
4915 element = EL_CHAR_UUMLAUT;
4918 case 0x15c6: // (blue)
4919 element = EL_CHAR_0;
4922 case 0x15c7: // (blue)
4923 element = EL_CHAR_1;
4926 case 0x15c8: // (blue)
4927 element = EL_CHAR_2;
4930 case 0x15c9: // (blue)
4931 element = EL_CHAR_3;
4934 case 0x15ca: // (blue)
4935 element = EL_CHAR_4;
4938 case 0x15cb: // (blue)
4939 element = EL_CHAR_5;
4942 case 0x15cc: // (blue)
4943 element = EL_CHAR_6;
4946 case 0x15cd: // (blue)
4947 element = EL_CHAR_7;
4950 case 0x15ce: // (blue)
4951 element = EL_CHAR_8;
4954 case 0x15cf: // (blue)
4955 element = EL_CHAR_9;
4958 case 0x15d0: // (blue)
4959 element = EL_CHAR_PERIOD;
4962 case 0x15d1: // (blue)
4963 element = EL_CHAR_EXCLAM;
4966 case 0x15d2: // (blue)
4967 element = EL_CHAR_COLON;
4970 case 0x15d3: // (blue)
4971 element = EL_CHAR_LESS;
4974 case 0x15d4: // (blue)
4975 element = EL_CHAR_GREATER;
4978 case 0x15d5: // (blue)
4979 element = EL_CHAR_QUESTION;
4982 case 0x15d6: // (blue)
4983 element = EL_CHAR_COPYRIGHT;
4986 case 0x15d7: // (blue)
4987 element = EL_CHAR_UP;
4990 case 0x15d8: // (blue)
4991 element = EL_CHAR_DOWN;
4994 case 0x15d9: // (blue)
4995 element = EL_CHAR_BUTTON;
4998 case 0x15da: // (blue)
4999 element = EL_CHAR_PLUS;
5002 case 0x15db: // (blue)
5003 element = EL_CHAR_MINUS;
5006 case 0x15dc: // (blue)
5007 element = EL_CHAR_APOSTROPHE;
5010 case 0x15dd: // (blue)
5011 element = EL_CHAR_PARENLEFT;
5014 case 0x15de: // (blue)
5015 element = EL_CHAR_PARENRIGHT;
5018 case 0x15df: // (green)
5019 element = EL_CHAR_A;
5022 case 0x15e0: // (green)
5023 element = EL_CHAR_B;
5026 case 0x15e1: // (green)
5027 element = EL_CHAR_C;
5030 case 0x15e2: // (green)
5031 element = EL_CHAR_D;
5034 case 0x15e3: // (green)
5035 element = EL_CHAR_E;
5038 case 0x15e4: // (green)
5039 element = EL_CHAR_F;
5042 case 0x15e5: // (green)
5043 element = EL_CHAR_G;
5046 case 0x15e6: // (green)
5047 element = EL_CHAR_H;
5050 case 0x15e7: // (green)
5051 element = EL_CHAR_I;
5054 case 0x15e8: // (green)
5055 element = EL_CHAR_J;
5058 case 0x15e9: // (green)
5059 element = EL_CHAR_K;
5062 case 0x15ea: // (green)
5063 element = EL_CHAR_L;
5066 case 0x15eb: // (green)
5067 element = EL_CHAR_M;
5070 case 0x15ec: // (green)
5071 element = EL_CHAR_N;
5074 case 0x15ed: // (green)
5075 element = EL_CHAR_O;
5078 case 0x15ee: // (green)
5079 element = EL_CHAR_P;
5082 case 0x15ef: // (green)
5083 element = EL_CHAR_Q;
5086 case 0x15f0: // (green)
5087 element = EL_CHAR_R;
5090 case 0x15f1: // (green)
5091 element = EL_CHAR_S;
5094 case 0x15f2: // (green)
5095 element = EL_CHAR_T;
5098 case 0x15f3: // (green)
5099 element = EL_CHAR_U;
5102 case 0x15f4: // (green)
5103 element = EL_CHAR_V;
5106 case 0x15f5: // (green)
5107 element = EL_CHAR_W;
5110 case 0x15f6: // (green)
5111 element = EL_CHAR_X;
5114 case 0x15f7: // (green)
5115 element = EL_CHAR_Y;
5118 case 0x15f8: // (green)
5119 element = EL_CHAR_Z;
5122 case 0x15f9: // (green)
5123 element = EL_CHAR_AUMLAUT;
5126 case 0x15fa: // (green)
5127 element = EL_CHAR_OUMLAUT;
5130 case 0x15fb: // (green)
5131 element = EL_CHAR_UUMLAUT;
5134 case 0x15fc: // (green)
5135 element = EL_CHAR_0;
5138 case 0x15fd: // (green)
5139 element = EL_CHAR_1;
5142 case 0x15fe: // (green)
5143 element = EL_CHAR_2;
5146 case 0x15ff: // (green)
5147 element = EL_CHAR_3;
5150 case 0x1600: // (green)
5151 element = EL_CHAR_4;
5154 case 0x1601: // (green)
5155 element = EL_CHAR_5;
5158 case 0x1602: // (green)
5159 element = EL_CHAR_6;
5162 case 0x1603: // (green)
5163 element = EL_CHAR_7;
5166 case 0x1604: // (green)
5167 element = EL_CHAR_8;
5170 case 0x1605: // (green)
5171 element = EL_CHAR_9;
5174 case 0x1606: // (green)
5175 element = EL_CHAR_PERIOD;
5178 case 0x1607: // (green)
5179 element = EL_CHAR_EXCLAM;
5182 case 0x1608: // (green)
5183 element = EL_CHAR_COLON;
5186 case 0x1609: // (green)
5187 element = EL_CHAR_LESS;
5190 case 0x160a: // (green)
5191 element = EL_CHAR_GREATER;
5194 case 0x160b: // (green)
5195 element = EL_CHAR_QUESTION;
5198 case 0x160c: // (green)
5199 element = EL_CHAR_COPYRIGHT;
5202 case 0x160d: // (green)
5203 element = EL_CHAR_UP;
5206 case 0x160e: // (green)
5207 element = EL_CHAR_DOWN;
5210 case 0x160f: // (green)
5211 element = EL_CHAR_BUTTON;
5214 case 0x1610: // (green)
5215 element = EL_CHAR_PLUS;
5218 case 0x1611: // (green)
5219 element = EL_CHAR_MINUS;
5222 case 0x1612: // (green)
5223 element = EL_CHAR_APOSTROPHE;
5226 case 0x1613: // (green)
5227 element = EL_CHAR_PARENLEFT;
5230 case 0x1614: // (green)
5231 element = EL_CHAR_PARENRIGHT;
5234 case 0x1615: // (blue steel)
5235 element = EL_STEEL_CHAR_A;
5238 case 0x1616: // (blue steel)
5239 element = EL_STEEL_CHAR_B;
5242 case 0x1617: // (blue steel)
5243 element = EL_STEEL_CHAR_C;
5246 case 0x1618: // (blue steel)
5247 element = EL_STEEL_CHAR_D;
5250 case 0x1619: // (blue steel)
5251 element = EL_STEEL_CHAR_E;
5254 case 0x161a: // (blue steel)
5255 element = EL_STEEL_CHAR_F;
5258 case 0x161b: // (blue steel)
5259 element = EL_STEEL_CHAR_G;
5262 case 0x161c: // (blue steel)
5263 element = EL_STEEL_CHAR_H;
5266 case 0x161d: // (blue steel)
5267 element = EL_STEEL_CHAR_I;
5270 case 0x161e: // (blue steel)
5271 element = EL_STEEL_CHAR_J;
5274 case 0x161f: // (blue steel)
5275 element = EL_STEEL_CHAR_K;
5278 case 0x1620: // (blue steel)
5279 element = EL_STEEL_CHAR_L;
5282 case 0x1621: // (blue steel)
5283 element = EL_STEEL_CHAR_M;
5286 case 0x1622: // (blue steel)
5287 element = EL_STEEL_CHAR_N;
5290 case 0x1623: // (blue steel)
5291 element = EL_STEEL_CHAR_O;
5294 case 0x1624: // (blue steel)
5295 element = EL_STEEL_CHAR_P;
5298 case 0x1625: // (blue steel)
5299 element = EL_STEEL_CHAR_Q;
5302 case 0x1626: // (blue steel)
5303 element = EL_STEEL_CHAR_R;
5306 case 0x1627: // (blue steel)
5307 element = EL_STEEL_CHAR_S;
5310 case 0x1628: // (blue steel)
5311 element = EL_STEEL_CHAR_T;
5314 case 0x1629: // (blue steel)
5315 element = EL_STEEL_CHAR_U;
5318 case 0x162a: // (blue steel)
5319 element = EL_STEEL_CHAR_V;
5322 case 0x162b: // (blue steel)
5323 element = EL_STEEL_CHAR_W;
5326 case 0x162c: // (blue steel)
5327 element = EL_STEEL_CHAR_X;
5330 case 0x162d: // (blue steel)
5331 element = EL_STEEL_CHAR_Y;
5334 case 0x162e: // (blue steel)
5335 element = EL_STEEL_CHAR_Z;
5338 case 0x162f: // (blue steel)
5339 element = EL_STEEL_CHAR_AUMLAUT;
5342 case 0x1630: // (blue steel)
5343 element = EL_STEEL_CHAR_OUMLAUT;
5346 case 0x1631: // (blue steel)
5347 element = EL_STEEL_CHAR_UUMLAUT;
5350 case 0x1632: // (blue steel)
5351 element = EL_STEEL_CHAR_0;
5354 case 0x1633: // (blue steel)
5355 element = EL_STEEL_CHAR_1;
5358 case 0x1634: // (blue steel)
5359 element = EL_STEEL_CHAR_2;
5362 case 0x1635: // (blue steel)
5363 element = EL_STEEL_CHAR_3;
5366 case 0x1636: // (blue steel)
5367 element = EL_STEEL_CHAR_4;
5370 case 0x1637: // (blue steel)
5371 element = EL_STEEL_CHAR_5;
5374 case 0x1638: // (blue steel)
5375 element = EL_STEEL_CHAR_6;
5378 case 0x1639: // (blue steel)
5379 element = EL_STEEL_CHAR_7;
5382 case 0x163a: // (blue steel)
5383 element = EL_STEEL_CHAR_8;
5386 case 0x163b: // (blue steel)
5387 element = EL_STEEL_CHAR_9;
5390 case 0x163c: // (blue steel)
5391 element = EL_STEEL_CHAR_PERIOD;
5394 case 0x163d: // (blue steel)
5395 element = EL_STEEL_CHAR_EXCLAM;
5398 case 0x163e: // (blue steel)
5399 element = EL_STEEL_CHAR_COLON;
5402 case 0x163f: // (blue steel)
5403 element = EL_STEEL_CHAR_LESS;
5406 case 0x1640: // (blue steel)
5407 element = EL_STEEL_CHAR_GREATER;
5410 case 0x1641: // (blue steel)
5411 element = EL_STEEL_CHAR_QUESTION;
5414 case 0x1642: // (blue steel)
5415 element = EL_STEEL_CHAR_COPYRIGHT;
5418 case 0x1643: // (blue steel)
5419 element = EL_STEEL_CHAR_UP;
5422 case 0x1644: // (blue steel)
5423 element = EL_STEEL_CHAR_DOWN;
5426 case 0x1645: // (blue steel)
5427 element = EL_STEEL_CHAR_BUTTON;
5430 case 0x1646: // (blue steel)
5431 element = EL_STEEL_CHAR_PLUS;
5434 case 0x1647: // (blue steel)
5435 element = EL_STEEL_CHAR_MINUS;
5438 case 0x1648: // (blue steel)
5439 element = EL_STEEL_CHAR_APOSTROPHE;
5442 case 0x1649: // (blue steel)
5443 element = EL_STEEL_CHAR_PARENLEFT;
5446 case 0x164a: // (blue steel)
5447 element = EL_STEEL_CHAR_PARENRIGHT;
5450 case 0x164b: // (green steel)
5451 element = EL_STEEL_CHAR_A;
5454 case 0x164c: // (green steel)
5455 element = EL_STEEL_CHAR_B;
5458 case 0x164d: // (green steel)
5459 element = EL_STEEL_CHAR_C;
5462 case 0x164e: // (green steel)
5463 element = EL_STEEL_CHAR_D;
5466 case 0x164f: // (green steel)
5467 element = EL_STEEL_CHAR_E;
5470 case 0x1650: // (green steel)
5471 element = EL_STEEL_CHAR_F;
5474 case 0x1651: // (green steel)
5475 element = EL_STEEL_CHAR_G;
5478 case 0x1652: // (green steel)
5479 element = EL_STEEL_CHAR_H;
5482 case 0x1653: // (green steel)
5483 element = EL_STEEL_CHAR_I;
5486 case 0x1654: // (green steel)
5487 element = EL_STEEL_CHAR_J;
5490 case 0x1655: // (green steel)
5491 element = EL_STEEL_CHAR_K;
5494 case 0x1656: // (green steel)
5495 element = EL_STEEL_CHAR_L;
5498 case 0x1657: // (green steel)
5499 element = EL_STEEL_CHAR_M;
5502 case 0x1658: // (green steel)
5503 element = EL_STEEL_CHAR_N;
5506 case 0x1659: // (green steel)
5507 element = EL_STEEL_CHAR_O;
5510 case 0x165a: // (green steel)
5511 element = EL_STEEL_CHAR_P;
5514 case 0x165b: // (green steel)
5515 element = EL_STEEL_CHAR_Q;
5518 case 0x165c: // (green steel)
5519 element = EL_STEEL_CHAR_R;
5522 case 0x165d: // (green steel)
5523 element = EL_STEEL_CHAR_S;
5526 case 0x165e: // (green steel)
5527 element = EL_STEEL_CHAR_T;
5530 case 0x165f: // (green steel)
5531 element = EL_STEEL_CHAR_U;
5534 case 0x1660: // (green steel)
5535 element = EL_STEEL_CHAR_V;
5538 case 0x1661: // (green steel)
5539 element = EL_STEEL_CHAR_W;
5542 case 0x1662: // (green steel)
5543 element = EL_STEEL_CHAR_X;
5546 case 0x1663: // (green steel)
5547 element = EL_STEEL_CHAR_Y;
5550 case 0x1664: // (green steel)
5551 element = EL_STEEL_CHAR_Z;
5554 case 0x1665: // (green steel)
5555 element = EL_STEEL_CHAR_AUMLAUT;
5558 case 0x1666: // (green steel)
5559 element = EL_STEEL_CHAR_OUMLAUT;
5562 case 0x1667: // (green steel)
5563 element = EL_STEEL_CHAR_UUMLAUT;
5566 case 0x1668: // (green steel)
5567 element = EL_STEEL_CHAR_0;
5570 case 0x1669: // (green steel)
5571 element = EL_STEEL_CHAR_1;
5574 case 0x166a: // (green steel)
5575 element = EL_STEEL_CHAR_2;
5578 case 0x166b: // (green steel)
5579 element = EL_STEEL_CHAR_3;
5582 case 0x166c: // (green steel)
5583 element = EL_STEEL_CHAR_4;
5586 case 0x166d: // (green steel)
5587 element = EL_STEEL_CHAR_5;
5590 case 0x166e: // (green steel)
5591 element = EL_STEEL_CHAR_6;
5594 case 0x166f: // (green steel)
5595 element = EL_STEEL_CHAR_7;
5598 case 0x1670: // (green steel)
5599 element = EL_STEEL_CHAR_8;
5602 case 0x1671: // (green steel)
5603 element = EL_STEEL_CHAR_9;
5606 case 0x1672: // (green steel)
5607 element = EL_STEEL_CHAR_PERIOD;
5610 case 0x1673: // (green steel)
5611 element = EL_STEEL_CHAR_EXCLAM;
5614 case 0x1674: // (green steel)
5615 element = EL_STEEL_CHAR_COLON;
5618 case 0x1675: // (green steel)
5619 element = EL_STEEL_CHAR_LESS;
5622 case 0x1676: // (green steel)
5623 element = EL_STEEL_CHAR_GREATER;
5626 case 0x1677: // (green steel)
5627 element = EL_STEEL_CHAR_QUESTION;
5630 case 0x1678: // (green steel)
5631 element = EL_STEEL_CHAR_COPYRIGHT;
5634 case 0x1679: // (green steel)
5635 element = EL_STEEL_CHAR_UP;
5638 case 0x167a: // (green steel)
5639 element = EL_STEEL_CHAR_DOWN;
5642 case 0x167b: // (green steel)
5643 element = EL_STEEL_CHAR_BUTTON;
5646 case 0x167c: // (green steel)
5647 element = EL_STEEL_CHAR_PLUS;
5650 case 0x167d: // (green steel)
5651 element = EL_STEEL_CHAR_MINUS;
5654 case 0x167e: // (green steel)
5655 element = EL_STEEL_CHAR_APOSTROPHE;
5658 case 0x167f: // (green steel)
5659 element = EL_STEEL_CHAR_PARENLEFT;
5662 case 0x1680: // (green steel)
5663 element = EL_STEEL_CHAR_PARENRIGHT;
5666 case 0x1681: // gate (red)
5667 element = EL_EM_GATE_1;
5670 case 0x1682: // secret gate (red)
5671 element = EL_EM_GATE_1_GRAY;
5674 case 0x1683: // gate (yellow)
5675 element = EL_EM_GATE_2;
5678 case 0x1684: // secret gate (yellow)
5679 element = EL_EM_GATE_2_GRAY;
5682 case 0x1685: // gate (blue)
5683 element = EL_EM_GATE_4;
5686 case 0x1686: // secret gate (blue)
5687 element = EL_EM_GATE_4_GRAY;
5690 case 0x1687: // gate (green)
5691 element = EL_EM_GATE_3;
5694 case 0x1688: // secret gate (green)
5695 element = EL_EM_GATE_3_GRAY;
5698 case 0x1689: // gate (white)
5699 element = EL_DC_GATE_WHITE;
5702 case 0x168a: // secret gate (white)
5703 element = EL_DC_GATE_WHITE_GRAY;
5706 case 0x168b: // secret gate (no key)
5707 element = EL_DC_GATE_FAKE_GRAY;
5711 element = EL_ROBOT_WHEEL;
5715 element = EL_DC_TIMEGATE_SWITCH;
5719 element = EL_ACID_POOL_BOTTOM;
5723 element = EL_ACID_POOL_TOPLEFT;
5727 element = EL_ACID_POOL_TOPRIGHT;
5731 element = EL_ACID_POOL_BOTTOMLEFT;
5735 element = EL_ACID_POOL_BOTTOMRIGHT;
5739 element = EL_STEELWALL;
5743 element = EL_STEELWALL_SLIPPERY;
5746 case 0x1695: // steel wall (not round)
5747 element = EL_STEELWALL;
5750 case 0x1696: // steel wall (left)
5751 element = EL_DC_STEELWALL_1_LEFT;
5754 case 0x1697: // steel wall (bottom)
5755 element = EL_DC_STEELWALL_1_BOTTOM;
5758 case 0x1698: // steel wall (right)
5759 element = EL_DC_STEELWALL_1_RIGHT;
5762 case 0x1699: // steel wall (top)
5763 element = EL_DC_STEELWALL_1_TOP;
5766 case 0x169a: // steel wall (left/bottom)
5767 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5770 case 0x169b: // steel wall (right/bottom)
5771 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5774 case 0x169c: // steel wall (right/top)
5775 element = EL_DC_STEELWALL_1_TOPRIGHT;
5778 case 0x169d: // steel wall (left/top)
5779 element = EL_DC_STEELWALL_1_TOPLEFT;
5782 case 0x169e: // steel wall (right/bottom small)
5783 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5786 case 0x169f: // steel wall (left/bottom small)
5787 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5790 case 0x16a0: // steel wall (right/top small)
5791 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5794 case 0x16a1: // steel wall (left/top small)
5795 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5798 case 0x16a2: // steel wall (left/right)
5799 element = EL_DC_STEELWALL_1_VERTICAL;
5802 case 0x16a3: // steel wall (top/bottom)
5803 element = EL_DC_STEELWALL_1_HORIZONTAL;
5806 case 0x16a4: // steel wall 2 (left end)
5807 element = EL_DC_STEELWALL_2_LEFT;
5810 case 0x16a5: // steel wall 2 (right end)
5811 element = EL_DC_STEELWALL_2_RIGHT;
5814 case 0x16a6: // steel wall 2 (top end)
5815 element = EL_DC_STEELWALL_2_TOP;
5818 case 0x16a7: // steel wall 2 (bottom end)
5819 element = EL_DC_STEELWALL_2_BOTTOM;
5822 case 0x16a8: // steel wall 2 (left/right)
5823 element = EL_DC_STEELWALL_2_HORIZONTAL;
5826 case 0x16a9: // steel wall 2 (up/down)
5827 element = EL_DC_STEELWALL_2_VERTICAL;
5830 case 0x16aa: // steel wall 2 (mid)
5831 element = EL_DC_STEELWALL_2_MIDDLE;
5835 element = EL_SIGN_EXCLAMATION;
5839 element = EL_SIGN_RADIOACTIVITY;
5843 element = EL_SIGN_STOP;
5847 element = EL_SIGN_WHEELCHAIR;
5851 element = EL_SIGN_PARKING;
5855 element = EL_SIGN_NO_ENTRY;
5859 element = EL_SIGN_HEART;
5863 element = EL_SIGN_GIVE_WAY;
5867 element = EL_SIGN_ENTRY_FORBIDDEN;
5871 element = EL_SIGN_EMERGENCY_EXIT;
5875 element = EL_SIGN_YIN_YANG;
5879 element = EL_WALL_EMERALD;
5883 element = EL_WALL_DIAMOND;
5887 element = EL_WALL_PEARL;
5891 element = EL_WALL_CRYSTAL;
5895 element = EL_INVISIBLE_WALL;
5899 element = EL_INVISIBLE_STEELWALL;
5903 // EL_INVISIBLE_SAND
5906 element = EL_LIGHT_SWITCH;
5910 element = EL_ENVELOPE_1;
5914 if (element >= 0x0117 && element <= 0x036e) // (?)
5915 element = EL_DIAMOND;
5916 else if (element >= 0x042d && element <= 0x0684) // (?)
5917 element = EL_EMERALD;
5918 else if (element >= 0x157c && element <= 0x158b)
5920 else if (element >= 0x1590 && element <= 0x159f)
5921 element = EL_DC_LANDMINE;
5922 else if (element >= 0x16bc && element <= 0x16cb)
5923 element = EL_INVISIBLE_SAND;
5926 Warn("unknown Diamond Caves element 0x%04x", element);
5928 element = EL_UNKNOWN;
5933 return getMappedElement(element);
5936 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5938 byte header[DC_LEVEL_HEADER_SIZE];
5940 int envelope_header_pos = 62;
5941 int envelope_content_pos = 94;
5942 int level_name_pos = 251;
5943 int level_author_pos = 292;
5944 int envelope_header_len;
5945 int envelope_content_len;
5947 int level_author_len;
5949 int num_yamyam_contents;
5952 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5954 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5956 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5958 header[i * 2 + 0] = header_word >> 8;
5959 header[i * 2 + 1] = header_word & 0xff;
5962 // read some values from level header to check level decoding integrity
5963 fieldx = header[6] | (header[7] << 8);
5964 fieldy = header[8] | (header[9] << 8);
5965 num_yamyam_contents = header[60] | (header[61] << 8);
5967 // do some simple sanity checks to ensure that level was correctly decoded
5968 if (fieldx < 1 || fieldx > 256 ||
5969 fieldy < 1 || fieldy > 256 ||
5970 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5972 level->no_valid_file = TRUE;
5974 Warn("cannot decode level from stream -- using empty level");
5979 // maximum envelope header size is 31 bytes
5980 envelope_header_len = header[envelope_header_pos];
5981 // maximum envelope content size is 110 (156?) bytes
5982 envelope_content_len = header[envelope_content_pos];
5984 // maximum level title size is 40 bytes
5985 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5986 // maximum level author size is 30 (51?) bytes
5987 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5991 for (i = 0; i < envelope_header_len; i++)
5992 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5993 level->envelope[0].text[envelope_size++] =
5994 header[envelope_header_pos + 1 + i];
5996 if (envelope_header_len > 0 && envelope_content_len > 0)
5998 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5999 level->envelope[0].text[envelope_size++] = '\n';
6000 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6001 level->envelope[0].text[envelope_size++] = '\n';
6004 for (i = 0; i < envelope_content_len; i++)
6005 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6006 level->envelope[0].text[envelope_size++] =
6007 header[envelope_content_pos + 1 + i];
6009 level->envelope[0].text[envelope_size] = '\0';
6011 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6012 level->envelope[0].ysize = 10;
6013 level->envelope[0].autowrap = TRUE;
6014 level->envelope[0].centered = TRUE;
6016 for (i = 0; i < level_name_len; i++)
6017 level->name[i] = header[level_name_pos + 1 + i];
6018 level->name[level_name_len] = '\0';
6020 for (i = 0; i < level_author_len; i++)
6021 level->author[i] = header[level_author_pos + 1 + i];
6022 level->author[level_author_len] = '\0';
6024 num_yamyam_contents = header[60] | (header[61] << 8);
6025 level->num_yamyam_contents =
6026 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6028 for (i = 0; i < num_yamyam_contents; i++)
6030 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6032 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6033 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6035 if (i < MAX_ELEMENT_CONTENTS)
6036 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6040 fieldx = header[6] | (header[7] << 8);
6041 fieldy = header[8] | (header[9] << 8);
6042 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6043 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6045 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6047 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6048 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6050 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6051 level->field[x][y] = getMappedElement_DC(element_dc);
6054 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6055 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6056 level->field[x][y] = EL_PLAYER_1;
6058 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6059 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6060 level->field[x][y] = EL_PLAYER_2;
6062 level->gems_needed = header[18] | (header[19] << 8);
6064 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6065 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6066 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6067 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6068 level->score[SC_NUT] = header[28] | (header[29] << 8);
6069 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6070 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6071 level->score[SC_BUG] = header[34] | (header[35] << 8);
6072 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6073 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6074 level->score[SC_KEY] = header[40] | (header[41] << 8);
6075 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6077 level->time = header[44] | (header[45] << 8);
6079 level->amoeba_speed = header[46] | (header[47] << 8);
6080 level->time_light = header[48] | (header[49] << 8);
6081 level->time_timegate = header[50] | (header[51] << 8);
6082 level->time_wheel = header[52] | (header[53] << 8);
6083 level->time_magic_wall = header[54] | (header[55] << 8);
6084 level->extra_time = header[56] | (header[57] << 8);
6085 level->shield_normal_time = header[58] | (header[59] << 8);
6087 // shield and extra time elements do not have a score
6088 level->score[SC_SHIELD] = 0;
6089 level->extra_time_score = 0;
6091 // set time for normal and deadly shields to the same value
6092 level->shield_deadly_time = level->shield_normal_time;
6094 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6095 // can slip down from flat walls, like normal walls and steel walls
6096 level->em_slippery_gems = TRUE;
6098 // time score is counted for each 10 seconds left in Diamond Caves levels
6099 level->time_score_base = 10;
6102 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6103 struct LevelFileInfo *level_file_info,
6104 boolean level_info_only)
6106 char *filename = level_file_info->filename;
6108 int num_magic_bytes = 8;
6109 char magic_bytes[num_magic_bytes + 1];
6110 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6112 if (!(file = openFile(filename, MODE_READ)))
6114 level->no_valid_file = TRUE;
6116 if (!level_info_only)
6117 Warn("cannot read level '%s' -- using empty level", filename);
6122 // fseek(file, 0x0000, SEEK_SET);
6124 if (level_file_info->packed)
6126 // read "magic bytes" from start of file
6127 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6128 magic_bytes[0] = '\0';
6130 // check "magic bytes" for correct file format
6131 if (!strPrefix(magic_bytes, "DC2"))
6133 level->no_valid_file = TRUE;
6135 Warn("unknown DC level file '%s' -- using empty level", filename);
6140 if (strPrefix(magic_bytes, "DC2Win95") ||
6141 strPrefix(magic_bytes, "DC2Win98"))
6143 int position_first_level = 0x00fa;
6144 int extra_bytes = 4;
6147 // advance file stream to first level inside the level package
6148 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6150 // each block of level data is followed by block of non-level data
6151 num_levels_to_skip *= 2;
6153 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6154 while (num_levels_to_skip >= 0)
6156 // advance file stream to next level inside the level package
6157 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6159 level->no_valid_file = TRUE;
6161 Warn("cannot fseek in file '%s' -- using empty level", filename);
6166 // skip apparently unused extra bytes following each level
6167 ReadUnusedBytesFromFile(file, extra_bytes);
6169 // read size of next level in level package
6170 skip_bytes = getFile32BitLE(file);
6172 num_levels_to_skip--;
6177 level->no_valid_file = TRUE;
6179 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6185 LoadLevelFromFileStream_DC(file, level);
6191 // ----------------------------------------------------------------------------
6192 // functions for loading SB level
6193 // ----------------------------------------------------------------------------
6195 int getMappedElement_SB(int element_ascii, boolean use_ces)
6203 sb_element_mapping[] =
6205 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6206 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6207 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6208 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6209 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6210 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6211 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6212 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6219 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6220 if (element_ascii == sb_element_mapping[i].ascii)
6221 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6223 return EL_UNDEFINED;
6226 static void SetLevelSettings_SB(struct LevelInfo *level)
6230 level->use_step_counter = TRUE;
6233 level->score[SC_TIME_BONUS] = 0;
6234 level->time_score_base = 1;
6235 level->rate_time_over_score = TRUE;
6238 level->auto_exit_sokoban = TRUE;
6241 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6242 struct LevelFileInfo *level_file_info,
6243 boolean level_info_only)
6245 char *filename = level_file_info->filename;
6246 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6247 char last_comment[MAX_LINE_LEN];
6248 char level_name[MAX_LINE_LEN];
6251 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6252 boolean read_continued_line = FALSE;
6253 boolean reading_playfield = FALSE;
6254 boolean got_valid_playfield_line = FALSE;
6255 boolean invalid_playfield_char = FALSE;
6256 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6257 int file_level_nr = 0;
6258 int x = 0, y = 0; // initialized to make compilers happy
6260 last_comment[0] = '\0';
6261 level_name[0] = '\0';
6263 if (!(file = openFile(filename, MODE_READ)))
6265 level->no_valid_file = TRUE;
6267 if (!level_info_only)
6268 Warn("cannot read level '%s' -- using empty level", filename);
6273 while (!checkEndOfFile(file))
6275 // level successfully read, but next level may follow here
6276 if (!got_valid_playfield_line && reading_playfield)
6278 // read playfield from single level file -- skip remaining file
6279 if (!level_file_info->packed)
6282 if (file_level_nr >= num_levels_to_skip)
6287 last_comment[0] = '\0';
6288 level_name[0] = '\0';
6290 reading_playfield = FALSE;
6293 got_valid_playfield_line = FALSE;
6295 // read next line of input file
6296 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6299 // cut trailing line break (this can be newline and/or carriage return)
6300 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6301 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6304 // copy raw input line for later use (mainly debugging output)
6305 strcpy(line_raw, line);
6307 if (read_continued_line)
6309 // append new line to existing line, if there is enough space
6310 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6311 strcat(previous_line, line_ptr);
6313 strcpy(line, previous_line); // copy storage buffer to line
6315 read_continued_line = FALSE;
6318 // if the last character is '\', continue at next line
6319 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6321 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6322 strcpy(previous_line, line); // copy line to storage buffer
6324 read_continued_line = TRUE;
6330 if (line[0] == '\0')
6333 // extract comment text from comment line
6336 for (line_ptr = line; *line_ptr; line_ptr++)
6337 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6340 strcpy(last_comment, line_ptr);
6345 // extract level title text from line containing level title
6346 if (line[0] == '\'')
6348 strcpy(level_name, &line[1]);
6350 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6351 level_name[strlen(level_name) - 1] = '\0';
6356 // skip lines containing only spaces (or empty lines)
6357 for (line_ptr = line; *line_ptr; line_ptr++)
6358 if (*line_ptr != ' ')
6360 if (*line_ptr == '\0')
6363 // at this point, we have found a line containing part of a playfield
6365 got_valid_playfield_line = TRUE;
6367 if (!reading_playfield)
6369 reading_playfield = TRUE;
6370 invalid_playfield_char = FALSE;
6372 for (x = 0; x < MAX_LEV_FIELDX; x++)
6373 for (y = 0; y < MAX_LEV_FIELDY; y++)
6374 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6379 // start with topmost tile row
6383 // skip playfield line if larger row than allowed
6384 if (y >= MAX_LEV_FIELDY)
6387 // start with leftmost tile column
6390 // read playfield elements from line
6391 for (line_ptr = line; *line_ptr; line_ptr++)
6393 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6395 // stop parsing playfield line if larger column than allowed
6396 if (x >= MAX_LEV_FIELDX)
6399 if (mapped_sb_element == EL_UNDEFINED)
6401 invalid_playfield_char = TRUE;
6406 level->field[x][y] = mapped_sb_element;
6408 // continue with next tile column
6411 level->fieldx = MAX(x, level->fieldx);
6414 if (invalid_playfield_char)
6416 // if first playfield line, treat invalid lines as comment lines
6418 reading_playfield = FALSE;
6423 // continue with next tile row
6431 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6432 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6434 if (!reading_playfield)
6436 level->no_valid_file = TRUE;
6438 Warn("cannot read level '%s' -- using empty level", filename);
6443 if (*level_name != '\0')
6445 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6446 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6448 else if (*last_comment != '\0')
6450 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6451 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6455 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6458 // set all empty fields beyond the border walls to invisible steel wall
6459 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6461 if ((x == 0 || x == level->fieldx - 1 ||
6462 y == 0 || y == level->fieldy - 1) &&
6463 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6464 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6465 level->field, level->fieldx, level->fieldy);
6468 // set special level settings for Sokoban levels
6469 SetLevelSettings_SB(level);
6471 if (load_xsb_to_ces)
6473 // special global settings can now be set in level template
6474 level->use_custom_template = TRUE;
6479 // -------------------------------------------------------------------------
6480 // functions for handling native levels
6481 // -------------------------------------------------------------------------
6483 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6484 struct LevelFileInfo *level_file_info,
6485 boolean level_info_only)
6489 // determine position of requested level inside level package
6490 if (level_file_info->packed)
6491 pos = level_file_info->nr - leveldir_current->first_level;
6493 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6494 level->no_valid_file = TRUE;
6497 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6498 struct LevelFileInfo *level_file_info,
6499 boolean level_info_only)
6501 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6502 level->no_valid_file = TRUE;
6505 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6506 struct LevelFileInfo *level_file_info,
6507 boolean level_info_only)
6511 // determine position of requested level inside level package
6512 if (level_file_info->packed)
6513 pos = level_file_info->nr - leveldir_current->first_level;
6515 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6516 level->no_valid_file = TRUE;
6519 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6520 struct LevelFileInfo *level_file_info,
6521 boolean level_info_only)
6523 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6524 level->no_valid_file = TRUE;
6527 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6529 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6530 CopyNativeLevel_RND_to_BD(level);
6531 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6532 CopyNativeLevel_RND_to_EM(level);
6533 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6534 CopyNativeLevel_RND_to_SP(level);
6535 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6536 CopyNativeLevel_RND_to_MM(level);
6539 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6541 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6542 CopyNativeLevel_BD_to_RND(level);
6543 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6544 CopyNativeLevel_EM_to_RND(level);
6545 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6546 CopyNativeLevel_SP_to_RND(level);
6547 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6548 CopyNativeLevel_MM_to_RND(level);
6551 void SaveNativeLevel(struct LevelInfo *level)
6553 // saving native level files only supported for some game engines
6554 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6555 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6558 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6559 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6560 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6561 char *filename = getLevelFilenameFromBasename(basename);
6563 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6566 boolean success = FALSE;
6568 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6570 CopyNativeLevel_RND_to_BD(level);
6571 // CopyNativeTape_RND_to_BD(level);
6573 success = SaveNativeLevel_BD(filename);
6575 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6577 CopyNativeLevel_RND_to_SP(level);
6578 CopyNativeTape_RND_to_SP(level);
6580 success = SaveNativeLevel_SP(filename);
6584 Request("Native level file saved!", REQ_CONFIRM);
6586 Request("Failed to save native level file!", REQ_CONFIRM);
6590 // ----------------------------------------------------------------------------
6591 // functions for loading generic level
6592 // ----------------------------------------------------------------------------
6594 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6595 struct LevelFileInfo *level_file_info,
6596 boolean level_info_only)
6598 // always start with reliable default values
6599 setLevelInfoToDefaults(level, level_info_only, TRUE);
6601 switch (level_file_info->type)
6603 case LEVEL_FILE_TYPE_RND:
6604 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6607 case LEVEL_FILE_TYPE_BD:
6608 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6609 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6612 case LEVEL_FILE_TYPE_EM:
6613 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6614 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6617 case LEVEL_FILE_TYPE_SP:
6618 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6619 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6622 case LEVEL_FILE_TYPE_MM:
6623 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6624 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6627 case LEVEL_FILE_TYPE_DC:
6628 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6631 case LEVEL_FILE_TYPE_SB:
6632 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6636 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6640 // if level file is invalid, restore level structure to default values
6641 if (level->no_valid_file)
6642 setLevelInfoToDefaults(level, level_info_only, FALSE);
6644 if (check_special_flags("use_native_bd_game_engine"))
6645 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6647 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6648 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6650 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6651 CopyNativeLevel_Native_to_RND(level);
6654 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6656 static struct LevelFileInfo level_file_info;
6658 // always start with reliable default values
6659 setFileInfoToDefaults(&level_file_info);
6661 level_file_info.nr = 0; // unknown level number
6662 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6664 setString(&level_file_info.filename, filename);
6666 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6669 static void LoadLevel_InitVersion(struct LevelInfo *level)
6673 if (leveldir_current == NULL) // only when dumping level
6676 // all engine modifications also valid for levels which use latest engine
6677 if (level->game_version < VERSION_IDENT(3,2,0,5))
6679 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6680 level->time_score_base = 10;
6683 if (leveldir_current->latest_engine)
6685 // ---------- use latest game engine --------------------------------------
6687 /* For all levels which are forced to use the latest game engine version
6688 (normally all but user contributed, private and undefined levels), set
6689 the game engine version to the actual version; this allows for actual
6690 corrections in the game engine to take effect for existing, converted
6691 levels (from "classic" or other existing games) to make the emulation
6692 of the corresponding game more accurate, while (hopefully) not breaking
6693 existing levels created from other players. */
6695 level->game_version = GAME_VERSION_ACTUAL;
6697 /* Set special EM style gems behaviour: EM style gems slip down from
6698 normal, steel and growing wall. As this is a more fundamental change,
6699 it seems better to set the default behaviour to "off" (as it is more
6700 natural) and make it configurable in the level editor (as a property
6701 of gem style elements). Already existing converted levels (neither
6702 private nor contributed levels) are changed to the new behaviour. */
6704 if (level->file_version < FILE_VERSION_2_0)
6705 level->em_slippery_gems = TRUE;
6710 // ---------- use game engine the level was created with --------------------
6712 /* For all levels which are not forced to use the latest game engine
6713 version (normally user contributed, private and undefined levels),
6714 use the version of the game engine the levels were created for.
6716 Since 2.0.1, the game engine version is now directly stored
6717 in the level file (chunk "VERS"), so there is no need anymore
6718 to set the game version from the file version (except for old,
6719 pre-2.0 levels, where the game version is still taken from the
6720 file format version used to store the level -- see above). */
6722 // player was faster than enemies in 1.0.0 and before
6723 if (level->file_version == FILE_VERSION_1_0)
6724 for (i = 0; i < MAX_PLAYERS; i++)
6725 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6727 // default behaviour for EM style gems was "slippery" only in 2.0.1
6728 if (level->game_version == VERSION_IDENT(2,0,1,0))
6729 level->em_slippery_gems = TRUE;
6731 // springs could be pushed over pits before (pre-release version) 2.2.0
6732 if (level->game_version < VERSION_IDENT(2,2,0,0))
6733 level->use_spring_bug = TRUE;
6735 if (level->game_version < VERSION_IDENT(3,2,0,5))
6737 // time orb caused limited time in endless time levels before 3.2.0-5
6738 level->use_time_orb_bug = TRUE;
6740 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6741 level->block_snap_field = FALSE;
6743 // extra time score was same value as time left score before 3.2.0-5
6744 level->extra_time_score = level->score[SC_TIME_BONUS];
6747 if (level->game_version < VERSION_IDENT(3,2,0,7))
6749 // default behaviour for snapping was "not continuous" before 3.2.0-7
6750 level->continuous_snapping = FALSE;
6753 // only few elements were able to actively move into acid before 3.1.0
6754 // trigger settings did not exist before 3.1.0; set to default "any"
6755 if (level->game_version < VERSION_IDENT(3,1,0,0))
6757 // correct "can move into acid" settings (all zero in old levels)
6759 level->can_move_into_acid_bits = 0; // nothing can move into acid
6760 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6762 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6763 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6764 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6765 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6767 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6768 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6770 // correct trigger settings (stored as zero == "none" in old levels)
6772 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6774 int element = EL_CUSTOM_START + i;
6775 struct ElementInfo *ei = &element_info[element];
6777 for (j = 0; j < ei->num_change_pages; j++)
6779 struct ElementChangeInfo *change = &ei->change_page[j];
6781 change->trigger_player = CH_PLAYER_ANY;
6782 change->trigger_page = CH_PAGE_ANY;
6787 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6789 int element = EL_CUSTOM_256;
6790 struct ElementInfo *ei = &element_info[element];
6791 struct ElementChangeInfo *change = &ei->change_page[0];
6793 /* This is needed to fix a problem that was caused by a bugfix in function
6794 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6795 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6796 not replace walkable elements, but instead just placed the player on it,
6797 without placing the Sokoban field under the player). Unfortunately, this
6798 breaks "Snake Bite" style levels when the snake is halfway through a door
6799 that just closes (the snake head is still alive and can be moved in this
6800 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6801 player (without Sokoban element) which then gets killed as designed). */
6803 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6804 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6805 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6806 change->target_element = EL_PLAYER_1;
6809 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6810 if (level->game_version < VERSION_IDENT(3,2,5,0))
6812 /* This is needed to fix a problem that was caused by a bugfix in function
6813 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6814 corrects the behaviour when a custom element changes to another custom
6815 element with a higher element number that has change actions defined.
6816 Normally, only one change per frame is allowed for custom elements.
6817 Therefore, it is checked if a custom element already changed in the
6818 current frame; if it did, subsequent changes are suppressed.
6819 Unfortunately, this is only checked for element changes, but not for
6820 change actions, which are still executed. As the function above loops
6821 through all custom elements from lower to higher, an element change
6822 resulting in a lower CE number won't be checked again, while a target
6823 element with a higher number will also be checked, and potential change
6824 actions will get executed for this CE, too (which is wrong), while
6825 further changes are ignored (which is correct). As this bugfix breaks
6826 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6827 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6828 behaviour for existing levels and tapes that make use of this bug */
6830 level->use_action_after_change_bug = TRUE;
6833 // not centering level after relocating player was default only in 3.2.3
6834 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6835 level->shifted_relocation = TRUE;
6837 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6838 if (level->game_version < VERSION_IDENT(3,2,6,0))
6839 level->em_explodes_by_fire = TRUE;
6841 // levels were solved by the first player entering an exit up to 4.1.0.0
6842 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6843 level->solved_by_one_player = TRUE;
6845 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6846 if (level->game_version < VERSION_IDENT(4,1,1,1))
6847 level->use_life_bugs = TRUE;
6849 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6850 if (level->game_version < VERSION_IDENT(4,1,1,1))
6851 level->sb_objects_needed = FALSE;
6853 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6854 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6855 level->finish_dig_collect = FALSE;
6857 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6858 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6859 level->keep_walkable_ce = TRUE;
6862 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6864 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6867 // check if this level is (not) a Sokoban level
6868 for (y = 0; y < level->fieldy; y++)
6869 for (x = 0; x < level->fieldx; x++)
6870 if (!IS_SB_ELEMENT(Tile[x][y]))
6871 is_sokoban_level = FALSE;
6873 if (is_sokoban_level)
6875 // set special level settings for Sokoban levels
6876 SetLevelSettings_SB(level);
6880 static void LoadLevel_InitSettings(struct LevelInfo *level)
6882 // adjust level settings for (non-native) Sokoban-style levels
6883 LoadLevel_InitSettings_SB(level);
6885 // rename levels with title "nameless level" or if renaming is forced
6886 if (leveldir_current->empty_level_name != NULL &&
6887 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6888 leveldir_current->force_level_name))
6889 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6890 leveldir_current->empty_level_name, level_nr);
6893 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6897 // map elements that have changed in newer versions
6898 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6899 level->game_version);
6900 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6901 for (x = 0; x < 3; x++)
6902 for (y = 0; y < 3; y++)
6903 level->yamyam_content[i].e[x][y] =
6904 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6905 level->game_version);
6909 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6913 // map custom element change events that have changed in newer versions
6914 // (these following values were accidentally changed in version 3.0.1)
6915 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6916 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6918 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6920 int element = EL_CUSTOM_START + i;
6922 // order of checking and copying events to be mapped is important
6923 // (do not change the start and end value -- they are constant)
6924 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6926 if (HAS_CHANGE_EVENT(element, j - 2))
6928 SET_CHANGE_EVENT(element, j - 2, FALSE);
6929 SET_CHANGE_EVENT(element, j, TRUE);
6933 // order of checking and copying events to be mapped is important
6934 // (do not change the start and end value -- they are constant)
6935 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6937 if (HAS_CHANGE_EVENT(element, j - 1))
6939 SET_CHANGE_EVENT(element, j - 1, FALSE);
6940 SET_CHANGE_EVENT(element, j, TRUE);
6946 // initialize "can_change" field for old levels with only one change page
6947 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6949 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6951 int element = EL_CUSTOM_START + i;
6953 if (CAN_CHANGE(element))
6954 element_info[element].change->can_change = TRUE;
6958 // correct custom element values (for old levels without these options)
6959 if (level->game_version < VERSION_IDENT(3,1,1,0))
6961 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6963 int element = EL_CUSTOM_START + i;
6964 struct ElementInfo *ei = &element_info[element];
6966 if (ei->access_direction == MV_NO_DIRECTION)
6967 ei->access_direction = MV_ALL_DIRECTIONS;
6971 // correct custom element values (fix invalid values for all versions)
6974 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6976 int element = EL_CUSTOM_START + i;
6977 struct ElementInfo *ei = &element_info[element];
6979 for (j = 0; j < ei->num_change_pages; j++)
6981 struct ElementChangeInfo *change = &ei->change_page[j];
6983 if (change->trigger_player == CH_PLAYER_NONE)
6984 change->trigger_player = CH_PLAYER_ANY;
6986 if (change->trigger_side == CH_SIDE_NONE)
6987 change->trigger_side = CH_SIDE_ANY;
6992 // initialize "can_explode" field for old levels which did not store this
6993 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6994 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6996 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6998 int element = EL_CUSTOM_START + i;
7000 if (EXPLODES_1X1_OLD(element))
7001 element_info[element].explosion_type = EXPLODES_1X1;
7003 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7004 EXPLODES_SMASHED(element) ||
7005 EXPLODES_IMPACT(element)));
7009 // correct previously hard-coded move delay values for maze runner style
7010 if (level->game_version < VERSION_IDENT(3,1,1,0))
7012 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7014 int element = EL_CUSTOM_START + i;
7016 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7018 // previously hard-coded and therefore ignored
7019 element_info[element].move_delay_fixed = 9;
7020 element_info[element].move_delay_random = 0;
7025 // set some other uninitialized values of custom elements in older levels
7026 if (level->game_version < VERSION_IDENT(3,1,0,0))
7028 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7030 int element = EL_CUSTOM_START + i;
7032 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7034 element_info[element].explosion_delay = 17;
7035 element_info[element].ignition_delay = 8;
7039 // set mouse click change events to work for left/middle/right mouse button
7040 if (level->game_version < VERSION_IDENT(4,2,3,0))
7042 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7044 int element = EL_CUSTOM_START + i;
7045 struct ElementInfo *ei = &element_info[element];
7047 for (j = 0; j < ei->num_change_pages; j++)
7049 struct ElementChangeInfo *change = &ei->change_page[j];
7051 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7052 change->has_event[CE_PRESSED_BY_MOUSE] ||
7053 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7054 change->has_event[CE_MOUSE_PRESSED_ON_X])
7055 change->trigger_side = CH_SIDE_ANY;
7061 static void LoadLevel_InitElements(struct LevelInfo *level)
7063 LoadLevel_InitStandardElements(level);
7065 if (level->file_has_custom_elements)
7066 LoadLevel_InitCustomElements(level);
7068 // initialize element properties for level editor etc.
7069 InitElementPropertiesEngine(level->game_version);
7070 InitElementPropertiesGfxElement();
7073 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7077 // map elements that have changed in newer versions
7078 for (y = 0; y < level->fieldy; y++)
7079 for (x = 0; x < level->fieldx; x++)
7080 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7081 level->game_version);
7083 // clear unused playfield data (nicer if level gets resized in editor)
7084 for (x = 0; x < MAX_LEV_FIELDX; x++)
7085 for (y = 0; y < MAX_LEV_FIELDY; y++)
7086 if (x >= level->fieldx || y >= level->fieldy)
7087 level->field[x][y] = EL_EMPTY;
7089 // copy elements to runtime playfield array
7090 for (x = 0; x < MAX_LEV_FIELDX; x++)
7091 for (y = 0; y < MAX_LEV_FIELDY; y++)
7092 Tile[x][y] = level->field[x][y];
7094 // initialize level size variables for faster access
7095 lev_fieldx = level->fieldx;
7096 lev_fieldy = level->fieldy;
7098 // determine border element for this level
7099 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7100 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7105 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7107 struct LevelFileInfo *level_file_info = &level->file_info;
7109 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7110 CopyNativeLevel_RND_to_Native(level);
7113 static void LoadLevelTemplate_LoadAndInit(void)
7115 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7117 LoadLevel_InitVersion(&level_template);
7118 LoadLevel_InitElements(&level_template);
7119 LoadLevel_InitSettings(&level_template);
7121 ActivateLevelTemplate();
7124 void LoadLevelTemplate(int nr)
7126 if (!fileExists(getGlobalLevelTemplateFilename()))
7128 Warn("no level template found for this level");
7133 setLevelFileInfo(&level_template.file_info, nr);
7135 LoadLevelTemplate_LoadAndInit();
7138 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7140 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7142 LoadLevelTemplate_LoadAndInit();
7145 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7147 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7149 if (level.use_custom_template)
7151 if (network_level != NULL)
7152 LoadNetworkLevelTemplate(network_level);
7154 LoadLevelTemplate(-1);
7157 LoadLevel_InitVersion(&level);
7158 LoadLevel_InitElements(&level);
7159 LoadLevel_InitPlayfield(&level);
7160 LoadLevel_InitSettings(&level);
7162 LoadLevel_InitNativeEngines(&level);
7165 void LoadLevel(int nr)
7167 SetLevelSetInfo(leveldir_current->identifier, nr);
7169 setLevelFileInfo(&level.file_info, nr);
7171 LoadLevel_LoadAndInit(NULL);
7174 void LoadLevelInfoOnly(int nr)
7176 setLevelFileInfo(&level.file_info, nr);
7178 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7181 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7183 SetLevelSetInfo(network_level->leveldir_identifier,
7184 network_level->file_info.nr);
7186 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7188 LoadLevel_LoadAndInit(network_level);
7191 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7195 chunk_size += putFileVersion(file, level->file_version);
7196 chunk_size += putFileVersion(file, level->game_version);
7201 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7205 chunk_size += putFile16BitBE(file, level->creation_date.year);
7206 chunk_size += putFile8Bit(file, level->creation_date.month);
7207 chunk_size += putFile8Bit(file, level->creation_date.day);
7212 #if ENABLE_HISTORIC_CHUNKS
7213 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7217 putFile8Bit(file, level->fieldx);
7218 putFile8Bit(file, level->fieldy);
7220 putFile16BitBE(file, level->time);
7221 putFile16BitBE(file, level->gems_needed);
7223 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7224 putFile8Bit(file, level->name[i]);
7226 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7227 putFile8Bit(file, level->score[i]);
7229 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7230 for (y = 0; y < 3; y++)
7231 for (x = 0; x < 3; x++)
7232 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7233 level->yamyam_content[i].e[x][y]));
7234 putFile8Bit(file, level->amoeba_speed);
7235 putFile8Bit(file, level->time_magic_wall);
7236 putFile8Bit(file, level->time_wheel);
7237 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7238 level->amoeba_content));
7239 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7240 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7241 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7242 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7244 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7246 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7247 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7248 putFile32BitBE(file, level->can_move_into_acid_bits);
7249 putFile8Bit(file, level->dont_collide_with_bits);
7251 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7252 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7254 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7255 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7256 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7258 putFile8Bit(file, level->game_engine_type);
7260 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7264 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7269 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7270 chunk_size += putFile8Bit(file, level->name[i]);
7275 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7280 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7281 chunk_size += putFile8Bit(file, level->author[i]);
7286 #if ENABLE_HISTORIC_CHUNKS
7287 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7292 for (y = 0; y < level->fieldy; y++)
7293 for (x = 0; x < level->fieldx; x++)
7294 if (level->encoding_16bit_field)
7295 chunk_size += putFile16BitBE(file, level->field[x][y]);
7297 chunk_size += putFile8Bit(file, level->field[x][y]);
7303 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7308 for (y = 0; y < level->fieldy; y++)
7309 for (x = 0; x < level->fieldx; x++)
7310 chunk_size += putFile16BitBE(file, level->field[x][y]);
7315 #if ENABLE_HISTORIC_CHUNKS
7316 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7320 putFile8Bit(file, EL_YAMYAM);
7321 putFile8Bit(file, level->num_yamyam_contents);
7322 putFile8Bit(file, 0);
7323 putFile8Bit(file, 0);
7325 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7326 for (y = 0; y < 3; y++)
7327 for (x = 0; x < 3; x++)
7328 if (level->encoding_16bit_field)
7329 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7331 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7335 #if ENABLE_HISTORIC_CHUNKS
7336 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7339 int num_contents, content_xsize, content_ysize;
7340 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7342 if (element == EL_YAMYAM)
7344 num_contents = level->num_yamyam_contents;
7348 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7349 for (y = 0; y < 3; y++)
7350 for (x = 0; x < 3; x++)
7351 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7353 else if (element == EL_BD_AMOEBA)
7359 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7360 for (y = 0; y < 3; y++)
7361 for (x = 0; x < 3; x++)
7362 content_array[i][x][y] = EL_EMPTY;
7363 content_array[0][0][0] = level->amoeba_content;
7367 // chunk header already written -- write empty chunk data
7368 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7370 Warn("cannot save content for element '%d'", element);
7375 putFile16BitBE(file, element);
7376 putFile8Bit(file, num_contents);
7377 putFile8Bit(file, content_xsize);
7378 putFile8Bit(file, content_ysize);
7380 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7382 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7383 for (y = 0; y < 3; y++)
7384 for (x = 0; x < 3; x++)
7385 putFile16BitBE(file, content_array[i][x][y]);
7389 #if ENABLE_HISTORIC_CHUNKS
7390 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7392 int envelope_nr = element - EL_ENVELOPE_1;
7393 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7397 chunk_size += putFile16BitBE(file, element);
7398 chunk_size += putFile16BitBE(file, envelope_len);
7399 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7400 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7402 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7403 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7405 for (i = 0; i < envelope_len; i++)
7406 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7412 #if ENABLE_HISTORIC_CHUNKS
7413 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7414 int num_changed_custom_elements)
7418 putFile16BitBE(file, num_changed_custom_elements);
7420 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7422 int element = EL_CUSTOM_START + i;
7424 struct ElementInfo *ei = &element_info[element];
7426 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7428 if (check < num_changed_custom_elements)
7430 putFile16BitBE(file, element);
7431 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7438 if (check != num_changed_custom_elements) // should not happen
7439 Warn("inconsistent number of custom element properties");
7443 #if ENABLE_HISTORIC_CHUNKS
7444 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7445 int num_changed_custom_elements)
7449 putFile16BitBE(file, num_changed_custom_elements);
7451 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7453 int element = EL_CUSTOM_START + i;
7455 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7457 if (check < num_changed_custom_elements)
7459 putFile16BitBE(file, element);
7460 putFile16BitBE(file, element_info[element].change->target_element);
7467 if (check != num_changed_custom_elements) // should not happen
7468 Warn("inconsistent number of custom target elements");
7472 #if ENABLE_HISTORIC_CHUNKS
7473 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7474 int num_changed_custom_elements)
7476 int i, j, x, y, check = 0;
7478 putFile16BitBE(file, num_changed_custom_elements);
7480 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7482 int element = EL_CUSTOM_START + i;
7483 struct ElementInfo *ei = &element_info[element];
7485 if (ei->modified_settings)
7487 if (check < num_changed_custom_elements)
7489 putFile16BitBE(file, element);
7491 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7492 putFile8Bit(file, ei->description[j]);
7494 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7496 // some free bytes for future properties and padding
7497 WriteUnusedBytesToFile(file, 7);
7499 putFile8Bit(file, ei->use_gfx_element);
7500 putFile16BitBE(file, ei->gfx_element_initial);
7502 putFile8Bit(file, ei->collect_score_initial);
7503 putFile8Bit(file, ei->collect_count_initial);
7505 putFile16BitBE(file, ei->push_delay_fixed);
7506 putFile16BitBE(file, ei->push_delay_random);
7507 putFile16BitBE(file, ei->move_delay_fixed);
7508 putFile16BitBE(file, ei->move_delay_random);
7510 putFile16BitBE(file, ei->move_pattern);
7511 putFile8Bit(file, ei->move_direction_initial);
7512 putFile8Bit(file, ei->move_stepsize);
7514 for (y = 0; y < 3; y++)
7515 for (x = 0; x < 3; x++)
7516 putFile16BitBE(file, ei->content.e[x][y]);
7518 putFile32BitBE(file, ei->change->events);
7520 putFile16BitBE(file, ei->change->target_element);
7522 putFile16BitBE(file, ei->change->delay_fixed);
7523 putFile16BitBE(file, ei->change->delay_random);
7524 putFile16BitBE(file, ei->change->delay_frames);
7526 putFile16BitBE(file, ei->change->initial_trigger_element);
7528 putFile8Bit(file, ei->change->explode);
7529 putFile8Bit(file, ei->change->use_target_content);
7530 putFile8Bit(file, ei->change->only_if_complete);
7531 putFile8Bit(file, ei->change->use_random_replace);
7533 putFile8Bit(file, ei->change->random_percentage);
7534 putFile8Bit(file, ei->change->replace_when);
7536 for (y = 0; y < 3; y++)
7537 for (x = 0; x < 3; x++)
7538 putFile16BitBE(file, ei->change->content.e[x][y]);
7540 putFile8Bit(file, ei->slippery_type);
7542 // some free bytes for future properties and padding
7543 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7550 if (check != num_changed_custom_elements) // should not happen
7551 Warn("inconsistent number of custom element properties");
7555 #if ENABLE_HISTORIC_CHUNKS
7556 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7558 struct ElementInfo *ei = &element_info[element];
7561 // ---------- custom element base property values (96 bytes) ----------------
7563 putFile16BitBE(file, element);
7565 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7566 putFile8Bit(file, ei->description[i]);
7568 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7570 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7572 putFile8Bit(file, ei->num_change_pages);
7574 putFile16BitBE(file, ei->ce_value_fixed_initial);
7575 putFile16BitBE(file, ei->ce_value_random_initial);
7576 putFile8Bit(file, ei->use_last_ce_value);
7578 putFile8Bit(file, ei->use_gfx_element);
7579 putFile16BitBE(file, ei->gfx_element_initial);
7581 putFile8Bit(file, ei->collect_score_initial);
7582 putFile8Bit(file, ei->collect_count_initial);
7584 putFile8Bit(file, ei->drop_delay_fixed);
7585 putFile8Bit(file, ei->push_delay_fixed);
7586 putFile8Bit(file, ei->drop_delay_random);
7587 putFile8Bit(file, ei->push_delay_random);
7588 putFile16BitBE(file, ei->move_delay_fixed);
7589 putFile16BitBE(file, ei->move_delay_random);
7591 // bits 0 - 15 of "move_pattern" ...
7592 putFile16BitBE(file, ei->move_pattern & 0xffff);
7593 putFile8Bit(file, ei->move_direction_initial);
7594 putFile8Bit(file, ei->move_stepsize);
7596 putFile8Bit(file, ei->slippery_type);
7598 for (y = 0; y < 3; y++)
7599 for (x = 0; x < 3; x++)
7600 putFile16BitBE(file, ei->content.e[x][y]);
7602 putFile16BitBE(file, ei->move_enter_element);
7603 putFile16BitBE(file, ei->move_leave_element);
7604 putFile8Bit(file, ei->move_leave_type);
7606 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7607 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7609 putFile8Bit(file, ei->access_direction);
7611 putFile8Bit(file, ei->explosion_delay);
7612 putFile8Bit(file, ei->ignition_delay);
7613 putFile8Bit(file, ei->explosion_type);
7615 // some free bytes for future custom property values and padding
7616 WriteUnusedBytesToFile(file, 1);
7618 // ---------- change page property values (48 bytes) ------------------------
7620 for (i = 0; i < ei->num_change_pages; i++)
7622 struct ElementChangeInfo *change = &ei->change_page[i];
7623 unsigned int event_bits;
7625 // bits 0 - 31 of "has_event[]" ...
7627 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7628 if (change->has_event[j])
7629 event_bits |= (1u << j);
7630 putFile32BitBE(file, event_bits);
7632 putFile16BitBE(file, change->target_element);
7634 putFile16BitBE(file, change->delay_fixed);
7635 putFile16BitBE(file, change->delay_random);
7636 putFile16BitBE(file, change->delay_frames);
7638 putFile16BitBE(file, change->initial_trigger_element);
7640 putFile8Bit(file, change->explode);
7641 putFile8Bit(file, change->use_target_content);
7642 putFile8Bit(file, change->only_if_complete);
7643 putFile8Bit(file, change->use_random_replace);
7645 putFile8Bit(file, change->random_percentage);
7646 putFile8Bit(file, change->replace_when);
7648 for (y = 0; y < 3; y++)
7649 for (x = 0; x < 3; x++)
7650 putFile16BitBE(file, change->target_content.e[x][y]);
7652 putFile8Bit(file, change->can_change);
7654 putFile8Bit(file, change->trigger_side);
7656 putFile8Bit(file, change->trigger_player);
7657 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7658 log_2(change->trigger_page)));
7660 putFile8Bit(file, change->has_action);
7661 putFile8Bit(file, change->action_type);
7662 putFile8Bit(file, change->action_mode);
7663 putFile16BitBE(file, change->action_arg);
7665 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7667 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7668 if (change->has_event[j])
7669 event_bits |= (1u << (j - 32));
7670 putFile8Bit(file, event_bits);
7675 #if ENABLE_HISTORIC_CHUNKS
7676 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7678 struct ElementInfo *ei = &element_info[element];
7679 struct ElementGroupInfo *group = ei->group;
7682 putFile16BitBE(file, element);
7684 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7685 putFile8Bit(file, ei->description[i]);
7687 putFile8Bit(file, group->num_elements);
7689 putFile8Bit(file, ei->use_gfx_element);
7690 putFile16BitBE(file, ei->gfx_element_initial);
7692 putFile8Bit(file, group->choice_mode);
7694 // some free bytes for future values and padding
7695 WriteUnusedBytesToFile(file, 3);
7697 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7698 putFile16BitBE(file, group->element[i]);
7702 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7703 boolean write_element)
7705 int save_type = entry->save_type;
7706 int data_type = entry->data_type;
7707 int conf_type = entry->conf_type;
7708 int byte_mask = conf_type & CONF_MASK_BYTES;
7709 int element = entry->element;
7710 int default_value = entry->default_value;
7712 boolean modified = FALSE;
7714 if (byte_mask != CONF_MASK_MULTI_BYTES)
7716 void *value_ptr = entry->value;
7717 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7720 // check if any settings have been modified before saving them
7721 if (value != default_value)
7724 // do not save if explicitly told or if unmodified default settings
7725 if ((save_type == SAVE_CONF_NEVER) ||
7726 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7730 num_bytes += putFile16BitBE(file, element);
7732 num_bytes += putFile8Bit(file, conf_type);
7733 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7734 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7735 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7738 else if (data_type == TYPE_STRING)
7740 char *default_string = entry->default_string;
7741 char *string = (char *)(entry->value);
7742 int string_length = strlen(string);
7745 // check if any settings have been modified before saving them
7746 if (!strEqual(string, default_string))
7749 // do not save if explicitly told or if unmodified default settings
7750 if ((save_type == SAVE_CONF_NEVER) ||
7751 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7755 num_bytes += putFile16BitBE(file, element);
7757 num_bytes += putFile8Bit(file, conf_type);
7758 num_bytes += putFile16BitBE(file, string_length);
7760 for (i = 0; i < string_length; i++)
7761 num_bytes += putFile8Bit(file, string[i]);
7763 else if (data_type == TYPE_ELEMENT_LIST)
7765 int *element_array = (int *)(entry->value);
7766 int num_elements = *(int *)(entry->num_entities);
7769 // check if any settings have been modified before saving them
7770 for (i = 0; i < num_elements; i++)
7771 if (element_array[i] != default_value)
7774 // do not save if explicitly told or if unmodified default settings
7775 if ((save_type == SAVE_CONF_NEVER) ||
7776 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7780 num_bytes += putFile16BitBE(file, element);
7782 num_bytes += putFile8Bit(file, conf_type);
7783 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7785 for (i = 0; i < num_elements; i++)
7786 num_bytes += putFile16BitBE(file, element_array[i]);
7788 else if (data_type == TYPE_CONTENT_LIST)
7790 struct Content *content = (struct Content *)(entry->value);
7791 int num_contents = *(int *)(entry->num_entities);
7794 // check if any settings have been modified before saving them
7795 for (i = 0; i < num_contents; i++)
7796 for (y = 0; y < 3; y++)
7797 for (x = 0; x < 3; x++)
7798 if (content[i].e[x][y] != default_value)
7801 // do not save if explicitly told or if unmodified default settings
7802 if ((save_type == SAVE_CONF_NEVER) ||
7803 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7807 num_bytes += putFile16BitBE(file, element);
7809 num_bytes += putFile8Bit(file, conf_type);
7810 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7812 for (i = 0; i < num_contents; i++)
7813 for (y = 0; y < 3; y++)
7814 for (x = 0; x < 3; x++)
7815 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7821 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7826 li = *level; // copy level data into temporary buffer
7828 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7829 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7834 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7839 li = *level; // copy level data into temporary buffer
7841 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7842 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7847 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7849 int envelope_nr = element - EL_ENVELOPE_1;
7853 chunk_size += putFile16BitBE(file, element);
7855 // copy envelope data into temporary buffer
7856 xx_envelope = level->envelope[envelope_nr];
7858 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7859 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7864 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7866 struct ElementInfo *ei = &element_info[element];
7870 chunk_size += putFile16BitBE(file, element);
7872 xx_ei = *ei; // copy element data into temporary buffer
7874 // set default description string for this specific element
7875 strcpy(xx_default_description, getDefaultElementDescription(ei));
7877 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7878 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7880 for (i = 0; i < ei->num_change_pages; i++)
7882 struct ElementChangeInfo *change = &ei->change_page[i];
7884 xx_current_change_page = i;
7886 xx_change = *change; // copy change data into temporary buffer
7889 setEventBitsFromEventFlags(change);
7891 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7892 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7899 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7901 struct ElementInfo *ei = &element_info[element];
7902 struct ElementGroupInfo *group = ei->group;
7906 chunk_size += putFile16BitBE(file, element);
7908 xx_ei = *ei; // copy element data into temporary buffer
7909 xx_group = *group; // copy group data into temporary buffer
7911 // set default description string for this specific element
7912 strcpy(xx_default_description, getDefaultElementDescription(ei));
7914 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7915 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7920 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7922 struct ElementInfo *ei = &element_info[element];
7926 chunk_size += putFile16BitBE(file, element);
7928 xx_ei = *ei; // copy element data into temporary buffer
7930 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7931 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7936 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7937 boolean save_as_template)
7943 if (!(file = fopen(filename, MODE_WRITE)))
7945 Warn("cannot save level file '%s'", filename);
7950 level->file_version = FILE_VERSION_ACTUAL;
7951 level->game_version = GAME_VERSION_ACTUAL;
7953 level->creation_date = getCurrentDate();
7955 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7956 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7958 chunk_size = SaveLevel_VERS(NULL, level);
7959 putFileChunkBE(file, "VERS", chunk_size);
7960 SaveLevel_VERS(file, level);
7962 chunk_size = SaveLevel_DATE(NULL, level);
7963 putFileChunkBE(file, "DATE", chunk_size);
7964 SaveLevel_DATE(file, level);
7966 chunk_size = SaveLevel_NAME(NULL, level);
7967 putFileChunkBE(file, "NAME", chunk_size);
7968 SaveLevel_NAME(file, level);
7970 chunk_size = SaveLevel_AUTH(NULL, level);
7971 putFileChunkBE(file, "AUTH", chunk_size);
7972 SaveLevel_AUTH(file, level);
7974 chunk_size = SaveLevel_INFO(NULL, level);
7975 putFileChunkBE(file, "INFO", chunk_size);
7976 SaveLevel_INFO(file, level);
7978 chunk_size = SaveLevel_BODY(NULL, level);
7979 putFileChunkBE(file, "BODY", chunk_size);
7980 SaveLevel_BODY(file, level);
7982 chunk_size = SaveLevel_ELEM(NULL, level);
7983 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7985 putFileChunkBE(file, "ELEM", chunk_size);
7986 SaveLevel_ELEM(file, level);
7989 for (i = 0; i < NUM_ENVELOPES; i++)
7991 int element = EL_ENVELOPE_1 + i;
7993 chunk_size = SaveLevel_NOTE(NULL, level, element);
7994 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7996 putFileChunkBE(file, "NOTE", chunk_size);
7997 SaveLevel_NOTE(file, level, element);
8001 // if not using template level, check for non-default custom/group elements
8002 if (!level->use_custom_template || save_as_template)
8004 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8006 int element = EL_CUSTOM_START + i;
8008 chunk_size = SaveLevel_CUSX(NULL, level, element);
8009 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8011 putFileChunkBE(file, "CUSX", chunk_size);
8012 SaveLevel_CUSX(file, level, element);
8016 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8018 int element = EL_GROUP_START + i;
8020 chunk_size = SaveLevel_GRPX(NULL, level, element);
8021 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8023 putFileChunkBE(file, "GRPX", chunk_size);
8024 SaveLevel_GRPX(file, level, element);
8028 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8030 int element = GET_EMPTY_ELEMENT(i);
8032 chunk_size = SaveLevel_EMPX(NULL, level, element);
8033 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8035 putFileChunkBE(file, "EMPX", chunk_size);
8036 SaveLevel_EMPX(file, level, element);
8043 SetFilePermissions(filename, PERMS_PRIVATE);
8046 void SaveLevel(int nr)
8048 char *filename = getDefaultLevelFilename(nr);
8050 SaveLevelFromFilename(&level, filename, FALSE);
8053 void SaveLevelTemplate(void)
8055 char *filename = getLocalLevelTemplateFilename();
8057 SaveLevelFromFilename(&level, filename, TRUE);
8060 boolean SaveLevelChecked(int nr)
8062 char *filename = getDefaultLevelFilename(nr);
8063 boolean new_level = !fileExists(filename);
8064 boolean level_saved = FALSE;
8066 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8071 Request("Level saved!", REQ_CONFIRM);
8079 void DumpLevel(struct LevelInfo *level)
8081 if (level->no_level_file || level->no_valid_file)
8083 Warn("cannot dump -- no valid level file found");
8089 Print("Level xxx (file version %08d, game version %08d)\n",
8090 level->file_version, level->game_version);
8093 Print("Level author: '%s'\n", level->author);
8094 Print("Level title: '%s'\n", level->name);
8096 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8098 Print("Level time: %d seconds\n", level->time);
8099 Print("Gems needed: %d\n", level->gems_needed);
8101 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8102 Print("Time for wheel: %d seconds\n", level->time_wheel);
8103 Print("Time for light: %d seconds\n", level->time_light);
8104 Print("Time for timegate: %d seconds\n", level->time_timegate);
8106 Print("Amoeba speed: %d\n", level->amoeba_speed);
8109 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8110 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8111 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8112 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8113 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8114 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8120 for (i = 0; i < NUM_ENVELOPES; i++)
8122 char *text = level->envelope[i].text;
8123 int text_len = strlen(text);
8124 boolean has_text = FALSE;
8126 for (j = 0; j < text_len; j++)
8127 if (text[j] != ' ' && text[j] != '\n')
8133 Print("Envelope %d:\n'%s'\n", i + 1, text);
8141 void DumpLevels(void)
8143 static LevelDirTree *dumplevel_leveldir = NULL;
8145 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8146 global.dumplevel_leveldir);
8148 if (dumplevel_leveldir == NULL)
8149 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8151 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8152 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8153 Fail("no such level number: %d", global.dumplevel_level_nr);
8155 leveldir_current = dumplevel_leveldir;
8157 LoadLevel(global.dumplevel_level_nr);
8164 // ============================================================================
8165 // tape file functions
8166 // ============================================================================
8168 static void setTapeInfoToDefaults(void)
8172 // always start with reliable default values (empty tape)
8175 // default values (also for pre-1.2 tapes) with only the first player
8176 tape.player_participates[0] = TRUE;
8177 for (i = 1; i < MAX_PLAYERS; i++)
8178 tape.player_participates[i] = FALSE;
8180 // at least one (default: the first) player participates in every tape
8181 tape.num_participating_players = 1;
8183 tape.property_bits = TAPE_PROPERTY_NONE;
8185 tape.level_nr = level_nr;
8187 tape.changed = FALSE;
8188 tape.solved = FALSE;
8190 tape.recording = FALSE;
8191 tape.playing = FALSE;
8192 tape.pausing = FALSE;
8194 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8195 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8197 tape.no_info_chunk = TRUE;
8198 tape.no_valid_file = FALSE;
8201 static int getTapePosSize(struct TapeInfo *tape)
8203 int tape_pos_size = 0;
8205 if (tape->use_key_actions)
8206 tape_pos_size += tape->num_participating_players;
8208 if (tape->use_mouse_actions)
8209 tape_pos_size += 3; // x and y position and mouse button mask
8211 tape_pos_size += 1; // tape action delay value
8213 return tape_pos_size;
8216 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8218 tape->use_key_actions = FALSE;
8219 tape->use_mouse_actions = FALSE;
8221 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8222 tape->use_key_actions = TRUE;
8224 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8225 tape->use_mouse_actions = TRUE;
8228 static int getTapeActionValue(struct TapeInfo *tape)
8230 return (tape->use_key_actions &&
8231 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8232 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8233 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8234 TAPE_ACTIONS_DEFAULT);
8237 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8239 tape->file_version = getFileVersion(file);
8240 tape->game_version = getFileVersion(file);
8245 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8249 tape->random_seed = getFile32BitBE(file);
8250 tape->date = getFile32BitBE(file);
8251 tape->length = getFile32BitBE(file);
8253 // read header fields that are new since version 1.2
8254 if (tape->file_version >= FILE_VERSION_1_2)
8256 byte store_participating_players = getFile8Bit(file);
8259 // since version 1.2, tapes store which players participate in the tape
8260 tape->num_participating_players = 0;
8261 for (i = 0; i < MAX_PLAYERS; i++)
8263 tape->player_participates[i] = FALSE;
8265 if (store_participating_players & (1 << i))
8267 tape->player_participates[i] = TRUE;
8268 tape->num_participating_players++;
8272 setTapeActionFlags(tape, getFile8Bit(file));
8274 tape->property_bits = getFile8Bit(file);
8275 tape->solved = getFile8Bit(file);
8277 engine_version = getFileVersion(file);
8278 if (engine_version > 0)
8279 tape->engine_version = engine_version;
8281 tape->engine_version = tape->game_version;
8287 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8289 tape->scr_fieldx = getFile8Bit(file);
8290 tape->scr_fieldy = getFile8Bit(file);
8295 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8297 char *level_identifier = NULL;
8298 int level_identifier_size;
8301 tape->no_info_chunk = FALSE;
8303 level_identifier_size = getFile16BitBE(file);
8305 level_identifier = checked_malloc(level_identifier_size);
8307 for (i = 0; i < level_identifier_size; i++)
8308 level_identifier[i] = getFile8Bit(file);
8310 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8311 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8313 checked_free(level_identifier);
8315 tape->level_nr = getFile16BitBE(file);
8317 chunk_size = 2 + level_identifier_size + 2;
8322 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8325 int tape_pos_size = getTapePosSize(tape);
8326 int chunk_size_expected = tape_pos_size * tape->length;
8328 if (chunk_size_expected != chunk_size)
8330 ReadUnusedBytesFromFile(file, chunk_size);
8331 return chunk_size_expected;
8334 for (i = 0; i < tape->length; i++)
8336 if (i >= MAX_TAPE_LEN)
8338 Warn("tape truncated -- size exceeds maximum tape size %d",
8341 // tape too large; read and ignore remaining tape data from this chunk
8342 for (;i < tape->length; i++)
8343 ReadUnusedBytesFromFile(file, tape_pos_size);
8348 if (tape->use_key_actions)
8350 for (j = 0; j < MAX_PLAYERS; j++)
8352 tape->pos[i].action[j] = MV_NONE;
8354 if (tape->player_participates[j])
8355 tape->pos[i].action[j] = getFile8Bit(file);
8359 if (tape->use_mouse_actions)
8361 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8362 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8363 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8366 tape->pos[i].delay = getFile8Bit(file);
8368 if (tape->file_version == FILE_VERSION_1_0)
8370 // eliminate possible diagonal moves in old tapes
8371 // this is only for backward compatibility
8373 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8374 byte action = tape->pos[i].action[0];
8375 int k, num_moves = 0;
8377 for (k = 0; k < 4; k++)
8379 if (action & joy_dir[k])
8381 tape->pos[i + num_moves].action[0] = joy_dir[k];
8383 tape->pos[i + num_moves].delay = 0;
8392 tape->length += num_moves;
8395 else if (tape->file_version < FILE_VERSION_2_0)
8397 // convert pre-2.0 tapes to new tape format
8399 if (tape->pos[i].delay > 1)
8402 tape->pos[i + 1] = tape->pos[i];
8403 tape->pos[i + 1].delay = 1;
8406 for (j = 0; j < MAX_PLAYERS; j++)
8407 tape->pos[i].action[j] = MV_NONE;
8408 tape->pos[i].delay--;
8415 if (checkEndOfFile(file))
8419 if (i != tape->length)
8420 chunk_size = tape_pos_size * i;
8425 static void LoadTape_SokobanSolution(char *filename)
8428 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8430 if (!(file = openFile(filename, MODE_READ)))
8432 tape.no_valid_file = TRUE;
8437 while (!checkEndOfFile(file))
8439 unsigned char c = getByteFromFile(file);
8441 if (checkEndOfFile(file))
8448 tape.pos[tape.length].action[0] = MV_UP;
8449 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8455 tape.pos[tape.length].action[0] = MV_DOWN;
8456 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8462 tape.pos[tape.length].action[0] = MV_LEFT;
8463 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8469 tape.pos[tape.length].action[0] = MV_RIGHT;
8470 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8478 // ignore white-space characters
8482 tape.no_valid_file = TRUE;
8484 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8492 if (tape.no_valid_file)
8495 tape.length_frames = GetTapeLengthFrames();
8496 tape.length_seconds = GetTapeLengthSeconds();
8499 void LoadTapeFromFilename(char *filename)
8501 char cookie[MAX_LINE_LEN];
8502 char chunk_name[CHUNK_ID_LEN + 1];
8506 // always start with reliable default values
8507 setTapeInfoToDefaults();
8509 if (strSuffix(filename, ".sln"))
8511 LoadTape_SokobanSolution(filename);
8516 if (!(file = openFile(filename, MODE_READ)))
8518 tape.no_valid_file = TRUE;
8523 getFileChunkBE(file, chunk_name, NULL);
8524 if (strEqual(chunk_name, "RND1"))
8526 getFile32BitBE(file); // not used
8528 getFileChunkBE(file, chunk_name, NULL);
8529 if (!strEqual(chunk_name, "TAPE"))
8531 tape.no_valid_file = TRUE;
8533 Warn("unknown format of tape file '%s'", filename);
8540 else // check for pre-2.0 file format with cookie string
8542 strcpy(cookie, chunk_name);
8543 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8545 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8546 cookie[strlen(cookie) - 1] = '\0';
8548 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8550 tape.no_valid_file = TRUE;
8552 Warn("unknown format of tape file '%s'", filename);
8559 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8561 tape.no_valid_file = TRUE;
8563 Warn("unsupported version of tape file '%s'", filename);
8570 // pre-2.0 tape files have no game version, so use file version here
8571 tape.game_version = tape.file_version;
8574 if (tape.file_version < FILE_VERSION_1_2)
8576 // tape files from versions before 1.2.0 without chunk structure
8577 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8578 LoadTape_BODY(file, 2 * tape.length, &tape);
8586 int (*loader)(File *, int, struct TapeInfo *);
8590 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8591 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8592 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8593 { "INFO", -1, LoadTape_INFO },
8594 { "BODY", -1, LoadTape_BODY },
8598 while (getFileChunkBE(file, chunk_name, &chunk_size))
8602 while (chunk_info[i].name != NULL &&
8603 !strEqual(chunk_name, chunk_info[i].name))
8606 if (chunk_info[i].name == NULL)
8608 Warn("unknown chunk '%s' in tape file '%s'",
8609 chunk_name, filename);
8611 ReadUnusedBytesFromFile(file, chunk_size);
8613 else if (chunk_info[i].size != -1 &&
8614 chunk_info[i].size != chunk_size)
8616 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8617 chunk_size, chunk_name, filename);
8619 ReadUnusedBytesFromFile(file, chunk_size);
8623 // call function to load this tape chunk
8624 int chunk_size_expected =
8625 (chunk_info[i].loader)(file, chunk_size, &tape);
8627 // the size of some chunks cannot be checked before reading other
8628 // chunks first (like "HEAD" and "BODY") that contain some header
8629 // information, so check them here
8630 if (chunk_size_expected != chunk_size)
8632 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8633 chunk_size, chunk_name, filename);
8641 tape.length_frames = GetTapeLengthFrames();
8642 tape.length_seconds = GetTapeLengthSeconds();
8645 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8647 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8649 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8650 tape.engine_version);
8654 void LoadTape(int nr)
8656 char *filename = getTapeFilename(nr);
8658 LoadTapeFromFilename(filename);
8661 void LoadSolutionTape(int nr)
8663 char *filename = getSolutionTapeFilename(nr);
8665 LoadTapeFromFilename(filename);
8667 if (TAPE_IS_EMPTY(tape))
8669 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8670 level.native_bd_level->replay != NULL)
8671 CopyNativeTape_BD_to_RND(&level);
8672 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8673 level.native_sp_level->demo.is_available)
8674 CopyNativeTape_SP_to_RND(&level);
8678 void LoadScoreTape(char *score_tape_basename, int nr)
8680 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8682 LoadTapeFromFilename(filename);
8685 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8687 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8689 LoadTapeFromFilename(filename);
8692 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8694 // chunk required for team mode tapes with non-default screen size
8695 return (tape->num_participating_players > 1 &&
8696 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8697 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8700 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8702 putFileVersion(file, tape->file_version);
8703 putFileVersion(file, tape->game_version);
8706 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8709 byte store_participating_players = 0;
8711 // set bits for participating players for compact storage
8712 for (i = 0; i < MAX_PLAYERS; i++)
8713 if (tape->player_participates[i])
8714 store_participating_players |= (1 << i);
8716 putFile32BitBE(file, tape->random_seed);
8717 putFile32BitBE(file, tape->date);
8718 putFile32BitBE(file, tape->length);
8720 putFile8Bit(file, store_participating_players);
8722 putFile8Bit(file, getTapeActionValue(tape));
8724 putFile8Bit(file, tape->property_bits);
8725 putFile8Bit(file, tape->solved);
8727 putFileVersion(file, tape->engine_version);
8730 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8732 putFile8Bit(file, tape->scr_fieldx);
8733 putFile8Bit(file, tape->scr_fieldy);
8736 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8738 int level_identifier_size = strlen(tape->level_identifier) + 1;
8741 putFile16BitBE(file, level_identifier_size);
8743 for (i = 0; i < level_identifier_size; i++)
8744 putFile8Bit(file, tape->level_identifier[i]);
8746 putFile16BitBE(file, tape->level_nr);
8749 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8753 for (i = 0; i < tape->length; i++)
8755 if (tape->use_key_actions)
8757 for (j = 0; j < MAX_PLAYERS; j++)
8758 if (tape->player_participates[j])
8759 putFile8Bit(file, tape->pos[i].action[j]);
8762 if (tape->use_mouse_actions)
8764 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8765 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8766 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8769 putFile8Bit(file, tape->pos[i].delay);
8773 void SaveTapeToFilename(char *filename)
8777 int info_chunk_size;
8778 int body_chunk_size;
8780 if (!(file = fopen(filename, MODE_WRITE)))
8782 Warn("cannot save level recording file '%s'", filename);
8787 tape_pos_size = getTapePosSize(&tape);
8789 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8790 body_chunk_size = tape_pos_size * tape.length;
8792 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8793 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8795 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8796 SaveTape_VERS(file, &tape);
8798 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8799 SaveTape_HEAD(file, &tape);
8801 if (checkSaveTape_SCRN(&tape))
8803 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8804 SaveTape_SCRN(file, &tape);
8807 putFileChunkBE(file, "INFO", info_chunk_size);
8808 SaveTape_INFO(file, &tape);
8810 putFileChunkBE(file, "BODY", body_chunk_size);
8811 SaveTape_BODY(file, &tape);
8815 SetFilePermissions(filename, PERMS_PRIVATE);
8818 static void SaveTapeExt(char *filename)
8822 tape.file_version = FILE_VERSION_ACTUAL;
8823 tape.game_version = GAME_VERSION_ACTUAL;
8825 tape.num_participating_players = 0;
8827 // count number of participating players
8828 for (i = 0; i < MAX_PLAYERS; i++)
8829 if (tape.player_participates[i])
8830 tape.num_participating_players++;
8832 SaveTapeToFilename(filename);
8834 tape.changed = FALSE;
8837 void SaveTape(int nr)
8839 char *filename = getTapeFilename(nr);
8841 InitTapeDirectory(leveldir_current->subdir);
8843 SaveTapeExt(filename);
8846 void SaveScoreTape(int nr)
8848 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8850 // used instead of "leveldir_current->subdir" (for network games)
8851 InitScoreTapeDirectory(levelset.identifier, nr);
8853 SaveTapeExt(filename);
8856 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8857 unsigned int req_state_added)
8859 char *filename = getTapeFilename(nr);
8860 boolean new_tape = !fileExists(filename);
8861 boolean tape_saved = FALSE;
8863 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8868 Request(msg_saved, REQ_CONFIRM | req_state_added);
8876 boolean SaveTapeChecked(int nr)
8878 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8881 boolean SaveTapeChecked_LevelSolved(int nr)
8883 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8884 "Level solved! Tape saved!", REQ_STAY_OPEN);
8887 void DumpTape(struct TapeInfo *tape)
8889 int tape_frame_counter;
8892 if (tape->no_valid_file)
8894 Warn("cannot dump -- no valid tape file found");
8901 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8902 tape->level_nr, tape->file_version, tape->game_version);
8903 Print(" (effective engine version %08d)\n",
8904 tape->engine_version);
8905 Print("Level series identifier: '%s'\n", tape->level_identifier);
8907 Print("Solution tape: %s\n",
8908 tape->solved ? "yes" :
8909 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8911 Print("Special tape properties: ");
8912 if (tape->property_bits == TAPE_PROPERTY_NONE)
8914 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8915 Print("[em_random_bug]");
8916 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8917 Print("[game_speed]");
8918 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8920 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8921 Print("[single_step]");
8922 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8923 Print("[snapshot]");
8924 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8925 Print("[replayed]");
8926 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8927 Print("[tas_keys]");
8928 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8929 Print("[small_graphics]");
8932 int year2 = tape->date / 10000;
8933 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8934 int month_index_raw = (tape->date / 100) % 100;
8935 int month_index = month_index_raw % 12; // prevent invalid index
8936 int month = month_index + 1;
8937 int day = tape->date % 100;
8939 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8943 tape_frame_counter = 0;
8945 for (i = 0; i < tape->length; i++)
8947 if (i >= MAX_TAPE_LEN)
8952 for (j = 0; j < MAX_PLAYERS; j++)
8954 if (tape->player_participates[j])
8956 int action = tape->pos[i].action[j];
8958 Print("%d:%02x ", j, action);
8959 Print("[%c%c%c%c|%c%c] - ",
8960 (action & JOY_LEFT ? '<' : ' '),
8961 (action & JOY_RIGHT ? '>' : ' '),
8962 (action & JOY_UP ? '^' : ' '),
8963 (action & JOY_DOWN ? 'v' : ' '),
8964 (action & JOY_BUTTON_1 ? '1' : ' '),
8965 (action & JOY_BUTTON_2 ? '2' : ' '));
8969 Print("(%03d) ", tape->pos[i].delay);
8970 Print("[%05d]\n", tape_frame_counter);
8972 tape_frame_counter += tape->pos[i].delay;
8978 void DumpTapes(void)
8980 static LevelDirTree *dumptape_leveldir = NULL;
8982 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8983 global.dumptape_leveldir);
8985 if (dumptape_leveldir == NULL)
8986 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8988 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8989 global.dumptape_level_nr > dumptape_leveldir->last_level)
8990 Fail("no such level number: %d", global.dumptape_level_nr);
8992 leveldir_current = dumptape_leveldir;
8994 if (options.mytapes)
8995 LoadTape(global.dumptape_level_nr);
8997 LoadSolutionTape(global.dumptape_level_nr);
9005 // ============================================================================
9006 // score file functions
9007 // ============================================================================
9009 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9013 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9015 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9016 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9017 scores->entry[i].score = 0;
9018 scores->entry[i].time = 0;
9020 scores->entry[i].id = -1;
9021 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9022 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9023 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9024 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9025 strcpy(scores->entry[i].country_code, "??");
9028 scores->num_entries = 0;
9029 scores->last_added = -1;
9030 scores->last_added_local = -1;
9032 scores->updated = FALSE;
9033 scores->uploaded = FALSE;
9034 scores->tape_downloaded = FALSE;
9035 scores->force_last_added = FALSE;
9037 // The following values are intentionally not reset here:
9041 // - continue_playing
9042 // - continue_on_return
9045 static void setScoreInfoToDefaults(void)
9047 setScoreInfoToDefaultsExt(&scores);
9050 static void setServerScoreInfoToDefaults(void)
9052 setScoreInfoToDefaultsExt(&server_scores);
9055 static void LoadScore_OLD(int nr)
9058 char *filename = getScoreFilename(nr);
9059 char cookie[MAX_LINE_LEN];
9060 char line[MAX_LINE_LEN];
9064 if (!(file = fopen(filename, MODE_READ)))
9067 // check file identifier
9068 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9070 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9071 cookie[strlen(cookie) - 1] = '\0';
9073 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9075 Warn("unknown format of score file '%s'", filename);
9082 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9084 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9085 Warn("fscanf() failed; %s", strerror(errno));
9087 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9090 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9091 line[strlen(line) - 1] = '\0';
9093 for (line_ptr = line; *line_ptr; line_ptr++)
9095 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9097 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9098 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9107 static void ConvertScore_OLD(void)
9109 // only convert score to time for levels that rate playing time over score
9110 if (!level.rate_time_over_score)
9113 // convert old score to playing time for score-less levels (like Supaplex)
9114 int time_final_max = 999;
9117 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9119 int score = scores.entry[i].score;
9121 if (score > 0 && score < time_final_max)
9122 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9126 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9128 scores->file_version = getFileVersion(file);
9129 scores->game_version = getFileVersion(file);
9134 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9136 char *level_identifier = NULL;
9137 int level_identifier_size;
9140 level_identifier_size = getFile16BitBE(file);
9142 level_identifier = checked_malloc(level_identifier_size);
9144 for (i = 0; i < level_identifier_size; i++)
9145 level_identifier[i] = getFile8Bit(file);
9147 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9148 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9150 checked_free(level_identifier);
9152 scores->level_nr = getFile16BitBE(file);
9153 scores->num_entries = getFile16BitBE(file);
9155 chunk_size = 2 + level_identifier_size + 2 + 2;
9160 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9164 for (i = 0; i < scores->num_entries; i++)
9166 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9167 scores->entry[i].name[j] = getFile8Bit(file);
9169 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9172 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9177 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9181 for (i = 0; i < scores->num_entries; i++)
9182 scores->entry[i].score = getFile16BitBE(file);
9184 chunk_size = scores->num_entries * 2;
9189 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9193 for (i = 0; i < scores->num_entries; i++)
9194 scores->entry[i].score = getFile32BitBE(file);
9196 chunk_size = scores->num_entries * 4;
9201 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9205 for (i = 0; i < scores->num_entries; i++)
9206 scores->entry[i].time = getFile32BitBE(file);
9208 chunk_size = scores->num_entries * 4;
9213 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9217 for (i = 0; i < scores->num_entries; i++)
9219 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9220 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9222 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9225 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9230 void LoadScore(int nr)
9232 char *filename = getScoreFilename(nr);
9233 char cookie[MAX_LINE_LEN];
9234 char chunk_name[CHUNK_ID_LEN + 1];
9236 boolean old_score_file_format = FALSE;
9239 // always start with reliable default values
9240 setScoreInfoToDefaults();
9242 if (!(file = openFile(filename, MODE_READ)))
9245 getFileChunkBE(file, chunk_name, NULL);
9246 if (strEqual(chunk_name, "RND1"))
9248 getFile32BitBE(file); // not used
9250 getFileChunkBE(file, chunk_name, NULL);
9251 if (!strEqual(chunk_name, "SCOR"))
9253 Warn("unknown format of score file '%s'", filename);
9260 else // check for old file format with cookie string
9262 strcpy(cookie, chunk_name);
9263 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9265 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9266 cookie[strlen(cookie) - 1] = '\0';
9268 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9270 Warn("unknown format of score file '%s'", filename);
9277 old_score_file_format = TRUE;
9280 if (old_score_file_format)
9282 // score files from versions before 4.2.4.0 without chunk structure
9285 // convert score to time, if possible (mainly for Supaplex levels)
9294 int (*loader)(File *, int, struct ScoreInfo *);
9298 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9299 { "INFO", -1, LoadScore_INFO },
9300 { "NAME", -1, LoadScore_NAME },
9301 { "SCOR", -1, LoadScore_SCOR },
9302 { "SC4R", -1, LoadScore_SC4R },
9303 { "TIME", -1, LoadScore_TIME },
9304 { "TAPE", -1, LoadScore_TAPE },
9309 while (getFileChunkBE(file, chunk_name, &chunk_size))
9313 while (chunk_info[i].name != NULL &&
9314 !strEqual(chunk_name, chunk_info[i].name))
9317 if (chunk_info[i].name == NULL)
9319 Warn("unknown chunk '%s' in score file '%s'",
9320 chunk_name, filename);
9322 ReadUnusedBytesFromFile(file, chunk_size);
9324 else if (chunk_info[i].size != -1 &&
9325 chunk_info[i].size != chunk_size)
9327 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9328 chunk_size, chunk_name, filename);
9330 ReadUnusedBytesFromFile(file, chunk_size);
9334 // call function to load this score chunk
9335 int chunk_size_expected =
9336 (chunk_info[i].loader)(file, chunk_size, &scores);
9338 // the size of some chunks cannot be checked before reading other
9339 // chunks first (like "HEAD" and "BODY") that contain some header
9340 // information, so check them here
9341 if (chunk_size_expected != chunk_size)
9343 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9344 chunk_size, chunk_name, filename);
9353 #if ENABLE_HISTORIC_CHUNKS
9354 void SaveScore_OLD(int nr)
9357 char *filename = getScoreFilename(nr);
9360 // used instead of "leveldir_current->subdir" (for network games)
9361 InitScoreDirectory(levelset.identifier);
9363 if (!(file = fopen(filename, MODE_WRITE)))
9365 Warn("cannot save score for level %d", nr);
9370 fprintf(file, "%s\n\n", SCORE_COOKIE);
9372 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9373 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9377 SetFilePermissions(filename, PERMS_PRIVATE);
9381 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9383 putFileVersion(file, scores->file_version);
9384 putFileVersion(file, scores->game_version);
9387 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9389 int level_identifier_size = strlen(scores->level_identifier) + 1;
9392 putFile16BitBE(file, level_identifier_size);
9394 for (i = 0; i < level_identifier_size; i++)
9395 putFile8Bit(file, scores->level_identifier[i]);
9397 putFile16BitBE(file, scores->level_nr);
9398 putFile16BitBE(file, scores->num_entries);
9401 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9405 for (i = 0; i < scores->num_entries; i++)
9407 int name_size = strlen(scores->entry[i].name);
9409 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9410 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9414 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9418 for (i = 0; i < scores->num_entries; i++)
9419 putFile16BitBE(file, scores->entry[i].score);
9422 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9426 for (i = 0; i < scores->num_entries; i++)
9427 putFile32BitBE(file, scores->entry[i].score);
9430 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9434 for (i = 0; i < scores->num_entries; i++)
9435 putFile32BitBE(file, scores->entry[i].time);
9438 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9442 for (i = 0; i < scores->num_entries; i++)
9444 int size = strlen(scores->entry[i].tape_basename);
9446 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9447 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9451 static void SaveScoreToFilename(char *filename)
9454 int info_chunk_size;
9455 int name_chunk_size;
9456 int scor_chunk_size;
9457 int sc4r_chunk_size;
9458 int time_chunk_size;
9459 int tape_chunk_size;
9460 boolean has_large_score_values;
9463 if (!(file = fopen(filename, MODE_WRITE)))
9465 Warn("cannot save score file '%s'", filename);
9470 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9471 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9472 scor_chunk_size = scores.num_entries * 2;
9473 sc4r_chunk_size = scores.num_entries * 4;
9474 time_chunk_size = scores.num_entries * 4;
9475 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9477 has_large_score_values = FALSE;
9478 for (i = 0; i < scores.num_entries; i++)
9479 if (scores.entry[i].score > 0xffff)
9480 has_large_score_values = TRUE;
9482 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9483 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9485 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9486 SaveScore_VERS(file, &scores);
9488 putFileChunkBE(file, "INFO", info_chunk_size);
9489 SaveScore_INFO(file, &scores);
9491 putFileChunkBE(file, "NAME", name_chunk_size);
9492 SaveScore_NAME(file, &scores);
9494 if (has_large_score_values)
9496 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9497 SaveScore_SC4R(file, &scores);
9501 putFileChunkBE(file, "SCOR", scor_chunk_size);
9502 SaveScore_SCOR(file, &scores);
9505 putFileChunkBE(file, "TIME", time_chunk_size);
9506 SaveScore_TIME(file, &scores);
9508 putFileChunkBE(file, "TAPE", tape_chunk_size);
9509 SaveScore_TAPE(file, &scores);
9513 SetFilePermissions(filename, PERMS_PRIVATE);
9516 void SaveScore(int nr)
9518 char *filename = getScoreFilename(nr);
9521 // used instead of "leveldir_current->subdir" (for network games)
9522 InitScoreDirectory(levelset.identifier);
9524 scores.file_version = FILE_VERSION_ACTUAL;
9525 scores.game_version = GAME_VERSION_ACTUAL;
9527 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9528 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9529 scores.level_nr = level_nr;
9531 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9532 if (scores.entry[i].score == 0 &&
9533 scores.entry[i].time == 0 &&
9534 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9537 scores.num_entries = i;
9539 if (scores.num_entries == 0)
9542 SaveScoreToFilename(filename);
9545 static void LoadServerScoreFromCache(int nr)
9547 struct ScoreEntry score_entry;
9556 { &score_entry.score, FALSE, 0 },
9557 { &score_entry.time, FALSE, 0 },
9558 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9559 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9560 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9561 { &score_entry.id, FALSE, 0 },
9562 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9563 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9564 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9565 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9569 char *filename = getScoreCacheFilename(nr);
9570 SetupFileHash *score_hash = loadSetupFileHash(filename);
9573 server_scores.num_entries = 0;
9575 if (score_hash == NULL)
9578 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9580 score_entry = server_scores.entry[i];
9582 for (j = 0; score_mapping[j].value != NULL; j++)
9586 sprintf(token, "%02d.%d", i, j);
9588 char *value = getHashEntry(score_hash, token);
9593 if (score_mapping[j].is_string)
9595 char *score_value = (char *)score_mapping[j].value;
9596 int value_size = score_mapping[j].string_size;
9598 strncpy(score_value, value, value_size);
9599 score_value[value_size] = '\0';
9603 int *score_value = (int *)score_mapping[j].value;
9605 *score_value = atoi(value);
9608 server_scores.num_entries = i + 1;
9611 server_scores.entry[i] = score_entry;
9614 freeSetupFileHash(score_hash);
9617 void LoadServerScore(int nr, boolean download_score)
9619 if (!setup.use_api_server)
9622 // always start with reliable default values
9623 setServerScoreInfoToDefaults();
9625 // 1st step: load server scores from cache file (which may not exist)
9626 // (this should prevent reading it while the thread is writing to it)
9627 LoadServerScoreFromCache(nr);
9629 if (download_score && runtime.use_api_server)
9631 // 2nd step: download server scores from score server to cache file
9632 // (as thread, as it might time out if the server is not reachable)
9633 ApiGetScoreAsThread(nr);
9637 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9639 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9641 // if score tape not uploaded, ask for uploading missing tapes later
9642 if (!setup.has_remaining_tapes)
9643 setup.ask_for_remaining_tapes = TRUE;
9645 setup.provide_uploading_tapes = TRUE;
9646 setup.has_remaining_tapes = TRUE;
9648 SaveSetup_ServerSetup();
9651 void SaveServerScore(int nr, boolean tape_saved)
9653 if (!runtime.use_api_server)
9655 PrepareScoreTapesForUpload(leveldir_current->subdir);
9660 ApiAddScoreAsThread(nr, tape_saved, NULL);
9663 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9664 char *score_tape_filename)
9666 if (!runtime.use_api_server)
9669 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9672 void LoadLocalAndServerScore(int nr, boolean download_score)
9674 int last_added_local = scores.last_added_local;
9675 boolean force_last_added = scores.force_last_added;
9677 // needed if only showing server scores
9678 setScoreInfoToDefaults();
9680 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9683 // restore last added local score entry (before merging server scores)
9684 scores.last_added = scores.last_added_local = last_added_local;
9686 if (setup.use_api_server &&
9687 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9689 // load server scores from cache file and trigger update from server
9690 LoadServerScore(nr, download_score);
9692 // merge local scores with scores from server
9696 if (force_last_added)
9697 scores.force_last_added = force_last_added;
9701 // ============================================================================
9702 // setup file functions
9703 // ============================================================================
9705 #define TOKEN_STR_PLAYER_PREFIX "player_"
9708 static struct TokenInfo global_setup_tokens[] =
9712 &setup.player_name, "player_name"
9716 &setup.multiple_users, "multiple_users"
9720 &setup.sound, "sound"
9724 &setup.sound_loops, "repeating_sound_loops"
9728 &setup.sound_music, "background_music"
9732 &setup.sound_simple, "simple_sound_effects"
9736 &setup.toons, "toons"
9740 &setup.global_animations, "global_animations"
9744 &setup.scroll_delay, "scroll_delay"
9748 &setup.forced_scroll_delay, "forced_scroll_delay"
9752 &setup.scroll_delay_value, "scroll_delay_value"
9756 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9760 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9764 &setup.fade_screens, "fade_screens"
9768 &setup.autorecord, "automatic_tape_recording"
9772 &setup.autorecord_after_replay, "autorecord_after_replay"
9776 &setup.auto_pause_on_start, "auto_pause_on_start"
9780 &setup.show_titlescreen, "show_titlescreen"
9784 &setup.quick_doors, "quick_doors"
9788 &setup.team_mode, "team_mode"
9792 &setup.handicap, "handicap"
9796 &setup.skip_levels, "skip_levels"
9800 &setup.increment_levels, "increment_levels"
9804 &setup.auto_play_next_level, "auto_play_next_level"
9808 &setup.count_score_after_game, "count_score_after_game"
9812 &setup.show_scores_after_game, "show_scores_after_game"
9816 &setup.time_limit, "time_limit"
9820 &setup.fullscreen, "fullscreen"
9824 &setup.window_scaling_percent, "window_scaling_percent"
9828 &setup.window_scaling_quality, "window_scaling_quality"
9832 &setup.screen_rendering_mode, "screen_rendering_mode"
9836 &setup.vsync_mode, "vsync_mode"
9840 &setup.ask_on_escape, "ask_on_escape"
9844 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9848 &setup.ask_on_game_over, "ask_on_game_over"
9852 &setup.ask_on_quit_game, "ask_on_quit_game"
9856 &setup.ask_on_quit_program, "ask_on_quit_program"
9860 &setup.quick_switch, "quick_player_switch"
9864 &setup.input_on_focus, "input_on_focus"
9868 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9872 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9876 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9880 &setup.game_speed_extended, "game_speed_extended"
9884 &setup.game_frame_delay, "game_frame_delay"
9888 &setup.bd_skip_uncovering, "bd_skip_uncovering"
9892 &setup.bd_skip_hatching, "bd_skip_hatching"
9896 &setup.bd_scroll_delay, "bd_scroll_delay"
9900 &setup.bd_smooth_movements, "bd_smooth_movements"
9904 &setup.sp_show_border_elements, "sp_show_border_elements"
9908 &setup.small_game_graphics, "small_game_graphics"
9912 &setup.show_load_save_buttons, "show_load_save_buttons"
9916 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9920 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9924 &setup.graphics_set, "graphics_set"
9928 &setup.sounds_set, "sounds_set"
9932 &setup.music_set, "music_set"
9936 &setup.override_level_graphics, "override_level_graphics"
9940 &setup.override_level_sounds, "override_level_sounds"
9944 &setup.override_level_music, "override_level_music"
9948 &setup.volume_simple, "volume_simple"
9952 &setup.volume_loops, "volume_loops"
9956 &setup.volume_music, "volume_music"
9960 &setup.network_mode, "network_mode"
9964 &setup.network_player_nr, "network_player"
9968 &setup.network_server_hostname, "network_server_hostname"
9972 &setup.touch.control_type, "touch.control_type"
9976 &setup.touch.move_distance, "touch.move_distance"
9980 &setup.touch.drop_distance, "touch.drop_distance"
9984 &setup.touch.transparency, "touch.transparency"
9988 &setup.touch.draw_outlined, "touch.draw_outlined"
9992 &setup.touch.draw_pressed, "touch.draw_pressed"
9996 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10000 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10004 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10008 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10012 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10016 static struct TokenInfo auto_setup_tokens[] =
10020 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10024 static struct TokenInfo server_setup_tokens[] =
10028 &setup.player_uuid, "player_uuid"
10032 &setup.player_version, "player_version"
10036 &setup.use_api_server, TEST_PREFIX "use_api_server"
10040 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10044 &setup.api_server_password, TEST_PREFIX "api_server_password"
10048 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10052 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10056 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10060 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10064 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10068 static struct TokenInfo editor_setup_tokens[] =
10072 &setup.editor.el_classic, "editor.el_classic"
10076 &setup.editor.el_custom, "editor.el_custom"
10080 &setup.editor.el_user_defined, "editor.el_user_defined"
10084 &setup.editor.el_dynamic, "editor.el_dynamic"
10088 &setup.editor.el_headlines, "editor.el_headlines"
10092 &setup.editor.show_element_token, "editor.show_element_token"
10096 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10100 static struct TokenInfo editor_cascade_setup_tokens[] =
10104 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10108 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10112 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10116 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10120 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10124 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10128 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10132 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10136 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10140 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10144 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10148 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10152 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10156 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10160 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10164 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10168 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10172 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10176 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10180 static struct TokenInfo shortcut_setup_tokens[] =
10184 &setup.shortcut.save_game, "shortcut.save_game"
10188 &setup.shortcut.load_game, "shortcut.load_game"
10192 &setup.shortcut.restart_game, "shortcut.restart_game"
10196 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10200 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10204 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10208 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10212 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10216 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10220 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10224 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10228 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10232 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10236 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10240 &setup.shortcut.tape_record, "shortcut.tape_record"
10244 &setup.shortcut.tape_play, "shortcut.tape_play"
10248 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10252 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10256 &setup.shortcut.sound_music, "shortcut.sound_music"
10260 &setup.shortcut.snap_left, "shortcut.snap_left"
10264 &setup.shortcut.snap_right, "shortcut.snap_right"
10268 &setup.shortcut.snap_up, "shortcut.snap_up"
10272 &setup.shortcut.snap_down, "shortcut.snap_down"
10276 static struct SetupInputInfo setup_input;
10277 static struct TokenInfo player_setup_tokens[] =
10281 &setup_input.use_joystick, ".use_joystick"
10285 &setup_input.joy.device_name, ".joy.device_name"
10289 &setup_input.joy.xleft, ".joy.xleft"
10293 &setup_input.joy.xmiddle, ".joy.xmiddle"
10297 &setup_input.joy.xright, ".joy.xright"
10301 &setup_input.joy.yupper, ".joy.yupper"
10305 &setup_input.joy.ymiddle, ".joy.ymiddle"
10309 &setup_input.joy.ylower, ".joy.ylower"
10313 &setup_input.joy.snap, ".joy.snap_field"
10317 &setup_input.joy.drop, ".joy.place_bomb"
10321 &setup_input.key.left, ".key.move_left"
10325 &setup_input.key.right, ".key.move_right"
10329 &setup_input.key.up, ".key.move_up"
10333 &setup_input.key.down, ".key.move_down"
10337 &setup_input.key.snap, ".key.snap_field"
10341 &setup_input.key.drop, ".key.place_bomb"
10345 static struct TokenInfo system_setup_tokens[] =
10349 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10353 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10357 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10361 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10365 static struct TokenInfo internal_setup_tokens[] =
10369 &setup.internal.program_title, "program_title"
10373 &setup.internal.program_version, "program_version"
10377 &setup.internal.program_author, "program_author"
10381 &setup.internal.program_email, "program_email"
10385 &setup.internal.program_website, "program_website"
10389 &setup.internal.program_copyright, "program_copyright"
10393 &setup.internal.program_company, "program_company"
10397 &setup.internal.program_icon_file, "program_icon_file"
10401 &setup.internal.default_graphics_set, "default_graphics_set"
10405 &setup.internal.default_sounds_set, "default_sounds_set"
10409 &setup.internal.default_music_set, "default_music_set"
10413 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10417 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10421 &setup.internal.fallback_music_file, "fallback_music_file"
10425 &setup.internal.default_level_series, "default_level_series"
10429 &setup.internal.default_window_width, "default_window_width"
10433 &setup.internal.default_window_height, "default_window_height"
10437 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10441 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10445 &setup.internal.create_user_levelset, "create_user_levelset"
10449 &setup.internal.info_screens_from_main, "info_screens_from_main"
10453 &setup.internal.menu_game, "menu_game"
10457 &setup.internal.menu_engines, "menu_engines"
10461 &setup.internal.menu_editor, "menu_editor"
10465 &setup.internal.menu_graphics, "menu_graphics"
10469 &setup.internal.menu_sound, "menu_sound"
10473 &setup.internal.menu_artwork, "menu_artwork"
10477 &setup.internal.menu_input, "menu_input"
10481 &setup.internal.menu_touch, "menu_touch"
10485 &setup.internal.menu_shortcuts, "menu_shortcuts"
10489 &setup.internal.menu_exit, "menu_exit"
10493 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10497 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10501 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10505 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10509 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10513 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10517 &setup.internal.info_title, "info_title"
10521 &setup.internal.info_elements, "info_elements"
10525 &setup.internal.info_music, "info_music"
10529 &setup.internal.info_credits, "info_credits"
10533 &setup.internal.info_program, "info_program"
10537 &setup.internal.info_version, "info_version"
10541 &setup.internal.info_levelset, "info_levelset"
10545 &setup.internal.info_exit, "info_exit"
10549 static struct TokenInfo debug_setup_tokens[] =
10553 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10557 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10561 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10565 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10569 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10573 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10577 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10581 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10585 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10589 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10593 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10597 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10601 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10605 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10609 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10613 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10617 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10621 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10625 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10629 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10633 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10636 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10640 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10644 &setup.debug.xsn_mode, "debug.xsn_mode"
10648 &setup.debug.xsn_percent, "debug.xsn_percent"
10652 static struct TokenInfo options_setup_tokens[] =
10656 &setup.options.verbose, "options.verbose"
10660 &setup.options.debug, "options.debug"
10664 &setup.options.debug_mode, "options.debug_mode"
10668 static void setSetupInfoToDefaults(struct SetupInfo *si)
10672 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10674 si->multiple_users = TRUE;
10677 si->sound_loops = TRUE;
10678 si->sound_music = TRUE;
10679 si->sound_simple = TRUE;
10681 si->global_animations = TRUE;
10682 si->scroll_delay = TRUE;
10683 si->forced_scroll_delay = FALSE;
10684 si->scroll_delay_value = STD_SCROLL_DELAY;
10685 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10686 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10687 si->fade_screens = TRUE;
10688 si->autorecord = TRUE;
10689 si->autorecord_after_replay = TRUE;
10690 si->auto_pause_on_start = FALSE;
10691 si->show_titlescreen = TRUE;
10692 si->quick_doors = FALSE;
10693 si->team_mode = FALSE;
10694 si->handicap = TRUE;
10695 si->skip_levels = TRUE;
10696 si->increment_levels = TRUE;
10697 si->auto_play_next_level = TRUE;
10698 si->count_score_after_game = TRUE;
10699 si->show_scores_after_game = TRUE;
10700 si->time_limit = TRUE;
10701 si->fullscreen = FALSE;
10702 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10703 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10704 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10705 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10706 si->ask_on_escape = TRUE;
10707 si->ask_on_escape_editor = TRUE;
10708 si->ask_on_game_over = TRUE;
10709 si->ask_on_quit_game = TRUE;
10710 si->ask_on_quit_program = TRUE;
10711 si->quick_switch = FALSE;
10712 si->input_on_focus = FALSE;
10713 si->prefer_aga_graphics = TRUE;
10714 si->prefer_lowpass_sounds = FALSE;
10715 si->prefer_extra_panel_items = TRUE;
10716 si->game_speed_extended = FALSE;
10717 si->game_frame_delay = GAME_FRAME_DELAY;
10718 si->bd_skip_uncovering = FALSE;
10719 si->bd_skip_hatching = FALSE;
10720 si->bd_scroll_delay = TRUE;
10721 si->bd_smooth_movements = AUTO;
10722 si->sp_show_border_elements = FALSE;
10723 si->small_game_graphics = FALSE;
10724 si->show_load_save_buttons = FALSE;
10725 si->show_undo_redo_buttons = FALSE;
10726 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10728 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10729 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10730 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10732 si->override_level_graphics = FALSE;
10733 si->override_level_sounds = FALSE;
10734 si->override_level_music = FALSE;
10736 si->volume_simple = 100; // percent
10737 si->volume_loops = 100; // percent
10738 si->volume_music = 100; // percent
10740 si->network_mode = FALSE;
10741 si->network_player_nr = 0; // first player
10742 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10744 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10745 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10746 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10747 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10748 si->touch.draw_outlined = TRUE;
10749 si->touch.draw_pressed = TRUE;
10751 for (i = 0; i < 2; i++)
10753 char *default_grid_button[6][2] =
10759 { "111222", " vv " },
10760 { "111222", " vv " }
10762 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10763 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10764 int min_xsize = MIN(6, grid_xsize);
10765 int min_ysize = MIN(6, grid_ysize);
10766 int startx = grid_xsize - min_xsize;
10767 int starty = grid_ysize - min_ysize;
10770 // virtual buttons grid can only be set to defaults if video is initialized
10771 // (this will be repeated if virtual buttons are not loaded from setup file)
10772 if (video.initialized)
10774 si->touch.grid_xsize[i] = grid_xsize;
10775 si->touch.grid_ysize[i] = grid_ysize;
10779 si->touch.grid_xsize[i] = -1;
10780 si->touch.grid_ysize[i] = -1;
10783 for (x = 0; x < MAX_GRID_XSIZE; x++)
10784 for (y = 0; y < MAX_GRID_YSIZE; y++)
10785 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10787 for (x = 0; x < min_xsize; x++)
10788 for (y = 0; y < min_ysize; y++)
10789 si->touch.grid_button[i][x][starty + y] =
10790 default_grid_button[y][0][x];
10792 for (x = 0; x < min_xsize; x++)
10793 for (y = 0; y < min_ysize; y++)
10794 si->touch.grid_button[i][startx + x][starty + y] =
10795 default_grid_button[y][1][x];
10798 si->touch.grid_initialized = video.initialized;
10800 si->touch.overlay_buttons = FALSE;
10802 si->editor.el_boulderdash = TRUE;
10803 si->editor.el_boulderdash_native = TRUE;
10804 si->editor.el_emerald_mine = TRUE;
10805 si->editor.el_emerald_mine_club = TRUE;
10806 si->editor.el_more = TRUE;
10807 si->editor.el_sokoban = TRUE;
10808 si->editor.el_supaplex = TRUE;
10809 si->editor.el_diamond_caves = TRUE;
10810 si->editor.el_dx_boulderdash = TRUE;
10812 si->editor.el_mirror_magic = TRUE;
10813 si->editor.el_deflektor = TRUE;
10815 si->editor.el_chars = TRUE;
10816 si->editor.el_steel_chars = TRUE;
10818 si->editor.el_classic = TRUE;
10819 si->editor.el_custom = TRUE;
10821 si->editor.el_user_defined = FALSE;
10822 si->editor.el_dynamic = TRUE;
10824 si->editor.el_headlines = TRUE;
10826 si->editor.show_element_token = FALSE;
10828 si->editor.show_read_only_warning = TRUE;
10830 si->editor.use_template_for_new_levels = TRUE;
10832 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10833 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10834 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10835 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10836 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10838 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10839 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10840 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10841 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10842 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10844 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10845 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10846 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10847 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10848 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10849 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10851 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10852 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10853 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10855 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10856 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10857 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10858 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10860 for (i = 0; i < MAX_PLAYERS; i++)
10862 si->input[i].use_joystick = FALSE;
10863 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10864 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10865 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10866 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10867 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10868 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10869 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10870 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10871 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10872 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10873 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10874 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10875 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10876 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10877 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10880 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10881 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10882 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10883 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10885 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10886 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10887 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10888 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10889 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10890 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10891 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10893 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10895 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10896 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10897 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10899 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10900 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10901 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10903 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10904 si->internal.choose_from_top_leveldir = FALSE;
10905 si->internal.show_scaling_in_title = TRUE;
10906 si->internal.create_user_levelset = TRUE;
10907 si->internal.info_screens_from_main = FALSE;
10909 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10910 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10912 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10913 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10914 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10915 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10916 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10917 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10918 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10919 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10920 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10921 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10923 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10924 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10925 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10926 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10927 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10928 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10929 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10930 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10931 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10932 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10934 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10935 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10937 si->debug.show_frames_per_second = FALSE;
10939 si->debug.xsn_mode = AUTO;
10940 si->debug.xsn_percent = 0;
10942 si->options.verbose = FALSE;
10943 si->options.debug = FALSE;
10944 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
10946 #if defined(PLATFORM_ANDROID)
10947 si->fullscreen = TRUE;
10948 si->touch.overlay_buttons = TRUE;
10951 setHideSetupEntry(&setup.debug.xsn_mode);
10954 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10956 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10959 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10961 si->player_uuid = NULL; // (will be set later)
10962 si->player_version = 1; // (will be set later)
10964 si->use_api_server = TRUE;
10965 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10966 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10967 si->ask_for_uploading_tapes = TRUE;
10968 si->ask_for_remaining_tapes = FALSE;
10969 si->provide_uploading_tapes = TRUE;
10970 si->ask_for_using_api_server = TRUE;
10971 si->has_remaining_tapes = FALSE;
10974 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10976 si->editor_cascade.el_bd = TRUE;
10977 si->editor_cascade.el_bd_native = TRUE;
10978 si->editor_cascade.el_em = TRUE;
10979 si->editor_cascade.el_emc = TRUE;
10980 si->editor_cascade.el_rnd = TRUE;
10981 si->editor_cascade.el_sb = TRUE;
10982 si->editor_cascade.el_sp = TRUE;
10983 si->editor_cascade.el_dc = TRUE;
10984 si->editor_cascade.el_dx = TRUE;
10986 si->editor_cascade.el_mm = TRUE;
10987 si->editor_cascade.el_df = TRUE;
10989 si->editor_cascade.el_chars = FALSE;
10990 si->editor_cascade.el_steel_chars = FALSE;
10991 si->editor_cascade.el_ce = FALSE;
10992 si->editor_cascade.el_ge = FALSE;
10993 si->editor_cascade.el_es = FALSE;
10994 si->editor_cascade.el_ref = FALSE;
10995 si->editor_cascade.el_user = FALSE;
10996 si->editor_cascade.el_dynamic = FALSE;
10999 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11001 static char *getHideSetupToken(void *setup_value)
11003 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11005 if (setup_value != NULL)
11006 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11008 return hide_setup_token;
11011 void setHideSetupEntry(void *setup_value)
11013 char *hide_setup_token = getHideSetupToken(setup_value);
11015 if (hide_setup_hash == NULL)
11016 hide_setup_hash = newSetupFileHash();
11018 if (setup_value != NULL)
11019 setHashEntry(hide_setup_hash, hide_setup_token, "");
11022 void removeHideSetupEntry(void *setup_value)
11024 char *hide_setup_token = getHideSetupToken(setup_value);
11026 if (setup_value != NULL)
11027 removeHashEntry(hide_setup_hash, hide_setup_token);
11030 boolean hideSetupEntry(void *setup_value)
11032 char *hide_setup_token = getHideSetupToken(setup_value);
11034 return (setup_value != NULL &&
11035 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11038 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11039 struct TokenInfo *token_info,
11040 int token_nr, char *token_text)
11042 char *token_hide_text = getStringCat2(token_text, ".hide");
11043 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11045 // set the value of this setup option in the setup option structure
11046 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11048 // check if this setup option should be hidden in the setup menu
11049 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11050 setHideSetupEntry(token_info[token_nr].value);
11052 free(token_hide_text);
11055 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11056 struct TokenInfo *token_info,
11059 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11060 token_info[token_nr].text);
11063 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11067 if (!setup_file_hash)
11070 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11071 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11073 setup.touch.grid_initialized = TRUE;
11074 for (i = 0; i < 2; i++)
11076 int grid_xsize = setup.touch.grid_xsize[i];
11077 int grid_ysize = setup.touch.grid_ysize[i];
11080 // if virtual buttons are not loaded from setup file, repeat initializing
11081 // virtual buttons grid with default values later when video is initialized
11082 if (grid_xsize == -1 ||
11085 setup.touch.grid_initialized = FALSE;
11090 for (y = 0; y < grid_ysize; y++)
11092 char token_string[MAX_LINE_LEN];
11094 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11096 char *value_string = getHashEntry(setup_file_hash, token_string);
11098 if (value_string == NULL)
11101 for (x = 0; x < grid_xsize; x++)
11103 char c = value_string[x];
11105 setup.touch.grid_button[i][x][y] =
11106 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11111 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11112 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11114 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11115 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11117 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11121 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11123 setup_input = setup.input[pnr];
11124 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11126 char full_token[100];
11128 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11129 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11132 setup.input[pnr] = setup_input;
11135 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11136 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11138 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11139 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11141 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11142 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11144 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11145 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11147 setHideRelatedSetupEntries();
11150 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11154 if (!setup_file_hash)
11157 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11158 setSetupInfo(auto_setup_tokens, i,
11159 getHashEntry(setup_file_hash,
11160 auto_setup_tokens[i].text));
11163 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11167 if (!setup_file_hash)
11170 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11171 setSetupInfo(server_setup_tokens, i,
11172 getHashEntry(setup_file_hash,
11173 server_setup_tokens[i].text));
11176 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11180 if (!setup_file_hash)
11183 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11184 setSetupInfo(editor_cascade_setup_tokens, i,
11185 getHashEntry(setup_file_hash,
11186 editor_cascade_setup_tokens[i].text));
11189 void LoadUserNames(void)
11191 int last_user_nr = user.nr;
11194 if (global.user_names != NULL)
11196 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11197 checked_free(global.user_names[i]);
11199 checked_free(global.user_names);
11202 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11204 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11208 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11210 if (setup_file_hash)
11212 char *player_name = getHashEntry(setup_file_hash, "player_name");
11214 global.user_names[i] = getFixedUserName(player_name);
11216 freeSetupFileHash(setup_file_hash);
11219 if (global.user_names[i] == NULL)
11220 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11223 user.nr = last_user_nr;
11226 void LoadSetupFromFilename(char *filename)
11228 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11230 if (setup_file_hash)
11232 decodeSetupFileHash_Default(setup_file_hash);
11234 freeSetupFileHash(setup_file_hash);
11238 Debug("setup", "using default setup values");
11242 static void LoadSetup_SpecialPostProcessing(void)
11244 char *player_name_new;
11246 // needed to work around problems with fixed length strings
11247 player_name_new = getFixedUserName(setup.player_name);
11248 free(setup.player_name);
11249 setup.player_name = player_name_new;
11251 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11252 if (setup.scroll_delay == FALSE)
11254 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11255 setup.scroll_delay = TRUE; // now always "on"
11258 // make sure that scroll delay value stays inside valid range
11259 setup.scroll_delay_value =
11260 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11263 void LoadSetup_Default(void)
11267 // always start with reliable default values
11268 setSetupInfoToDefaults(&setup);
11270 // try to load setup values from default setup file
11271 filename = getDefaultSetupFilename();
11273 if (fileExists(filename))
11274 LoadSetupFromFilename(filename);
11276 // try to load setup values from platform setup file
11277 filename = getPlatformSetupFilename();
11279 if (fileExists(filename))
11280 LoadSetupFromFilename(filename);
11282 // try to load setup values from user setup file
11283 filename = getSetupFilename();
11285 LoadSetupFromFilename(filename);
11287 LoadSetup_SpecialPostProcessing();
11290 void LoadSetup_AutoSetup(void)
11292 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11293 SetupFileHash *setup_file_hash = NULL;
11295 // always start with reliable default values
11296 setSetupInfoToDefaults_AutoSetup(&setup);
11298 setup_file_hash = loadSetupFileHash(filename);
11300 if (setup_file_hash)
11302 decodeSetupFileHash_AutoSetup(setup_file_hash);
11304 freeSetupFileHash(setup_file_hash);
11310 void LoadSetup_ServerSetup(void)
11312 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11313 SetupFileHash *setup_file_hash = NULL;
11315 // always start with reliable default values
11316 setSetupInfoToDefaults_ServerSetup(&setup);
11318 setup_file_hash = loadSetupFileHash(filename);
11320 if (setup_file_hash)
11322 decodeSetupFileHash_ServerSetup(setup_file_hash);
11324 freeSetupFileHash(setup_file_hash);
11329 if (setup.player_uuid == NULL)
11331 // player UUID does not yet exist in setup file
11332 setup.player_uuid = getStringCopy(getUUID());
11333 setup.player_version = 2;
11335 SaveSetup_ServerSetup();
11339 void LoadSetup_EditorCascade(void)
11341 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11342 SetupFileHash *setup_file_hash = NULL;
11344 // always start with reliable default values
11345 setSetupInfoToDefaults_EditorCascade(&setup);
11347 setup_file_hash = loadSetupFileHash(filename);
11349 if (setup_file_hash)
11351 decodeSetupFileHash_EditorCascade(setup_file_hash);
11353 freeSetupFileHash(setup_file_hash);
11359 void LoadSetup(void)
11361 LoadSetup_Default();
11362 LoadSetup_AutoSetup();
11363 LoadSetup_ServerSetup();
11364 LoadSetup_EditorCascade();
11367 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11368 char *mapping_line)
11370 char mapping_guid[MAX_LINE_LEN];
11371 char *mapping_start, *mapping_end;
11373 // get GUID from game controller mapping line: copy complete line
11374 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11375 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11377 // get GUID from game controller mapping line: cut after GUID part
11378 mapping_start = strchr(mapping_guid, ',');
11379 if (mapping_start != NULL)
11380 *mapping_start = '\0';
11382 // cut newline from game controller mapping line
11383 mapping_end = strchr(mapping_line, '\n');
11384 if (mapping_end != NULL)
11385 *mapping_end = '\0';
11387 // add mapping entry to game controller mappings hash
11388 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11391 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11396 if (!(file = fopen(filename, MODE_READ)))
11398 Warn("cannot read game controller mappings file '%s'", filename);
11403 while (!feof(file))
11405 char line[MAX_LINE_LEN];
11407 if (!fgets(line, MAX_LINE_LEN, file))
11410 addGameControllerMappingToHash(mappings_hash, line);
11416 void SaveSetup_Default(void)
11418 char *filename = getSetupFilename();
11422 InitUserDataDirectory();
11424 if (!(file = fopen(filename, MODE_WRITE)))
11426 Warn("cannot write setup file '%s'", filename);
11431 fprintFileHeader(file, SETUP_FILENAME);
11433 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11435 // just to make things nicer :)
11436 if (global_setup_tokens[i].value == &setup.multiple_users ||
11437 global_setup_tokens[i].value == &setup.sound ||
11438 global_setup_tokens[i].value == &setup.graphics_set ||
11439 global_setup_tokens[i].value == &setup.volume_simple ||
11440 global_setup_tokens[i].value == &setup.network_mode ||
11441 global_setup_tokens[i].value == &setup.touch.control_type ||
11442 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11443 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11444 fprintf(file, "\n");
11446 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11449 for (i = 0; i < 2; i++)
11451 int grid_xsize = setup.touch.grid_xsize[i];
11452 int grid_ysize = setup.touch.grid_ysize[i];
11455 fprintf(file, "\n");
11457 for (y = 0; y < grid_ysize; y++)
11459 char token_string[MAX_LINE_LEN];
11460 char value_string[MAX_LINE_LEN];
11462 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11464 for (x = 0; x < grid_xsize; x++)
11466 char c = setup.touch.grid_button[i][x][y];
11468 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11471 value_string[grid_xsize] = '\0';
11473 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11477 fprintf(file, "\n");
11478 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11479 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11481 fprintf(file, "\n");
11482 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11483 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11485 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11489 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11490 fprintf(file, "\n");
11492 setup_input = setup.input[pnr];
11493 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11494 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11497 fprintf(file, "\n");
11498 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11499 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11501 // (internal setup values not saved to user setup file)
11503 fprintf(file, "\n");
11504 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11505 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11506 setup.debug.xsn_mode != AUTO)
11507 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11509 fprintf(file, "\n");
11510 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11511 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11515 SetFilePermissions(filename, PERMS_PRIVATE);
11518 void SaveSetup_AutoSetup(void)
11520 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11524 InitUserDataDirectory();
11526 if (!(file = fopen(filename, MODE_WRITE)))
11528 Warn("cannot write auto setup file '%s'", filename);
11535 fprintFileHeader(file, AUTOSETUP_FILENAME);
11537 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11538 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11542 SetFilePermissions(filename, PERMS_PRIVATE);
11547 void SaveSetup_ServerSetup(void)
11549 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11553 InitUserDataDirectory();
11555 if (!(file = fopen(filename, MODE_WRITE)))
11557 Warn("cannot write server setup file '%s'", filename);
11564 fprintFileHeader(file, SERVERSETUP_FILENAME);
11566 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11568 // just to make things nicer :)
11569 if (server_setup_tokens[i].value == &setup.use_api_server)
11570 fprintf(file, "\n");
11572 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11577 SetFilePermissions(filename, PERMS_PRIVATE);
11582 void SaveSetup_EditorCascade(void)
11584 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11588 InitUserDataDirectory();
11590 if (!(file = fopen(filename, MODE_WRITE)))
11592 Warn("cannot write editor cascade state file '%s'", filename);
11599 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11601 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11602 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11606 SetFilePermissions(filename, PERMS_PRIVATE);
11611 void SaveSetup(void)
11613 SaveSetup_Default();
11614 SaveSetup_AutoSetup();
11615 SaveSetup_ServerSetup();
11616 SaveSetup_EditorCascade();
11619 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11624 if (!(file = fopen(filename, MODE_WRITE)))
11626 Warn("cannot write game controller mappings file '%s'", filename);
11631 BEGIN_HASH_ITERATION(mappings_hash, itr)
11633 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11635 END_HASH_ITERATION(mappings_hash, itr)
11640 void SaveSetup_AddGameControllerMapping(char *mapping)
11642 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11643 SetupFileHash *mappings_hash = newSetupFileHash();
11645 InitUserDataDirectory();
11647 // load existing personal game controller mappings
11648 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11650 // add new mapping to personal game controller mappings
11651 addGameControllerMappingToHash(mappings_hash, mapping);
11653 // save updated personal game controller mappings
11654 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11656 freeSetupFileHash(mappings_hash);
11660 void LoadCustomElementDescriptions(void)
11662 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11663 SetupFileHash *setup_file_hash;
11666 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11668 if (element_info[i].custom_description != NULL)
11670 free(element_info[i].custom_description);
11671 element_info[i].custom_description = NULL;
11675 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11678 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11680 char *token = getStringCat2(element_info[i].token_name, ".name");
11681 char *value = getHashEntry(setup_file_hash, token);
11684 element_info[i].custom_description = getStringCopy(value);
11689 freeSetupFileHash(setup_file_hash);
11692 static int getElementFromToken(char *token)
11694 char *value = getHashEntry(element_token_hash, token);
11697 return atoi(value);
11699 Warn("unknown element token '%s'", token);
11701 return EL_UNDEFINED;
11704 void FreeGlobalAnimEventInfo(void)
11706 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11708 if (gaei->event_list == NULL)
11713 for (i = 0; i < gaei->num_event_lists; i++)
11715 checked_free(gaei->event_list[i]->event_value);
11716 checked_free(gaei->event_list[i]);
11719 checked_free(gaei->event_list);
11721 gaei->event_list = NULL;
11722 gaei->num_event_lists = 0;
11725 static int AddGlobalAnimEventList(void)
11727 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11728 int list_pos = gaei->num_event_lists++;
11730 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11731 sizeof(struct GlobalAnimEventListInfo *));
11733 gaei->event_list[list_pos] =
11734 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11736 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11738 gaeli->event_value = NULL;
11739 gaeli->num_event_values = 0;
11744 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11746 // do not add empty global animation events
11747 if (event_value == ANIM_EVENT_NONE)
11750 // if list position is undefined, create new list
11751 if (list_pos == ANIM_EVENT_UNDEFINED)
11752 list_pos = AddGlobalAnimEventList();
11754 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11755 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11756 int value_pos = gaeli->num_event_values++;
11758 gaeli->event_value = checked_realloc(gaeli->event_value,
11759 gaeli->num_event_values * sizeof(int *));
11761 gaeli->event_value[value_pos] = event_value;
11766 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11768 if (list_pos == ANIM_EVENT_UNDEFINED)
11769 return ANIM_EVENT_NONE;
11771 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11772 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11774 return gaeli->event_value[value_pos];
11777 int GetGlobalAnimEventValueCount(int list_pos)
11779 if (list_pos == ANIM_EVENT_UNDEFINED)
11782 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11783 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11785 return gaeli->num_event_values;
11788 // This function checks if a string <s> of the format "string1, string2, ..."
11789 // exactly contains a string <s_contained>.
11791 static boolean string_has_parameter(char *s, char *s_contained)
11795 if (s == NULL || s_contained == NULL)
11798 if (strlen(s_contained) > strlen(s))
11801 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11803 char next_char = s[strlen(s_contained)];
11805 // check if next character is delimiter or whitespace
11806 if (next_char == ',' || next_char == '\0' ||
11807 next_char == ' ' || next_char == '\t')
11811 // check if string contains another parameter string after a comma
11812 substring = strchr(s, ',');
11813 if (substring == NULL) // string does not contain a comma
11816 // advance string pointer to next character after the comma
11819 // skip potential whitespaces after the comma
11820 while (*substring == ' ' || *substring == '\t')
11823 return string_has_parameter(substring, s_contained);
11826 static int get_anim_parameter_value_ce(char *s)
11829 char *pattern_1 = "ce_change:custom_";
11830 char *pattern_2 = ".page_";
11831 int pattern_1_len = strlen(pattern_1);
11832 char *matching_char = strstr(s_ptr, pattern_1);
11833 int result = ANIM_EVENT_NONE;
11835 if (matching_char == NULL)
11836 return ANIM_EVENT_NONE;
11838 result = ANIM_EVENT_CE_CHANGE;
11840 s_ptr = matching_char + pattern_1_len;
11842 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11843 if (*s_ptr >= '0' && *s_ptr <= '9')
11845 int gic_ce_nr = (*s_ptr++ - '0');
11847 if (*s_ptr >= '0' && *s_ptr <= '9')
11849 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11851 if (*s_ptr >= '0' && *s_ptr <= '9')
11852 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11855 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11856 return ANIM_EVENT_NONE;
11858 // custom element stored as 0 to 255
11861 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11865 // invalid custom element number specified
11867 return ANIM_EVENT_NONE;
11870 // check for change page number ("page_X" or "page_XX") (optional)
11871 if (strPrefix(s_ptr, pattern_2))
11873 s_ptr += strlen(pattern_2);
11875 if (*s_ptr >= '0' && *s_ptr <= '9')
11877 int gic_page_nr = (*s_ptr++ - '0');
11879 if (*s_ptr >= '0' && *s_ptr <= '9')
11880 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11882 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11883 return ANIM_EVENT_NONE;
11885 // change page stored as 1 to 32 (0 means "all change pages")
11887 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11891 // invalid animation part number specified
11893 return ANIM_EVENT_NONE;
11897 // discard result if next character is neither delimiter nor whitespace
11898 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11899 *s_ptr == ' ' || *s_ptr == '\t'))
11900 return ANIM_EVENT_NONE;
11905 static int get_anim_parameter_value(char *s)
11907 int event_value[] =
11915 char *pattern_1[] =
11923 char *pattern_2 = ".part_";
11924 char *matching_char = NULL;
11926 int pattern_1_len = 0;
11927 int result = ANIM_EVENT_NONE;
11930 result = get_anim_parameter_value_ce(s);
11932 if (result != ANIM_EVENT_NONE)
11935 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11937 matching_char = strstr(s_ptr, pattern_1[i]);
11938 pattern_1_len = strlen(pattern_1[i]);
11939 result = event_value[i];
11941 if (matching_char != NULL)
11945 if (matching_char == NULL)
11946 return ANIM_EVENT_NONE;
11948 s_ptr = matching_char + pattern_1_len;
11950 // check for main animation number ("anim_X" or "anim_XX")
11951 if (*s_ptr >= '0' && *s_ptr <= '9')
11953 int gic_anim_nr = (*s_ptr++ - '0');
11955 if (*s_ptr >= '0' && *s_ptr <= '9')
11956 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11958 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11959 return ANIM_EVENT_NONE;
11961 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11965 // invalid main animation number specified
11967 return ANIM_EVENT_NONE;
11970 // check for animation part number ("part_X" or "part_XX") (optional)
11971 if (strPrefix(s_ptr, pattern_2))
11973 s_ptr += strlen(pattern_2);
11975 if (*s_ptr >= '0' && *s_ptr <= '9')
11977 int gic_part_nr = (*s_ptr++ - '0');
11979 if (*s_ptr >= '0' && *s_ptr <= '9')
11980 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11982 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11983 return ANIM_EVENT_NONE;
11985 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11989 // invalid animation part number specified
11991 return ANIM_EVENT_NONE;
11995 // discard result if next character is neither delimiter nor whitespace
11996 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11997 *s_ptr == ' ' || *s_ptr == '\t'))
11998 return ANIM_EVENT_NONE;
12003 static int get_anim_parameter_values(char *s)
12005 int list_pos = ANIM_EVENT_UNDEFINED;
12006 int event_value = ANIM_EVENT_DEFAULT;
12008 if (string_has_parameter(s, "any"))
12009 event_value |= ANIM_EVENT_ANY;
12011 if (string_has_parameter(s, "click:self") ||
12012 string_has_parameter(s, "click") ||
12013 string_has_parameter(s, "self"))
12014 event_value |= ANIM_EVENT_SELF;
12016 if (string_has_parameter(s, "unclick:any"))
12017 event_value |= ANIM_EVENT_UNCLICK_ANY;
12019 // if animation event found, add it to global animation event list
12020 if (event_value != ANIM_EVENT_NONE)
12021 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12025 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12026 event_value = get_anim_parameter_value(s);
12028 // if animation event found, add it to global animation event list
12029 if (event_value != ANIM_EVENT_NONE)
12030 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12032 // continue with next part of the string, starting with next comma
12033 s = strchr(s + 1, ',');
12039 static int get_anim_action_parameter_value(char *token)
12041 // check most common default case first to massively speed things up
12042 if (strEqual(token, ARG_UNDEFINED))
12043 return ANIM_EVENT_ACTION_NONE;
12045 int result = getImageIDFromToken(token);
12049 char *gfx_token = getStringCat2("gfx.", token);
12051 result = getImageIDFromToken(gfx_token);
12053 checked_free(gfx_token);
12058 Key key = getKeyFromX11KeyName(token);
12060 if (key != KSYM_UNDEFINED)
12061 result = -(int)key;
12068 result = get_hash_from_string(token); // unsigned int => int
12069 result = ABS(result); // may be negative now
12070 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12072 setHashEntry(anim_url_hash, int2str(result, 0), token);
12077 result = ANIM_EVENT_ACTION_NONE;
12082 int get_parameter_value(char *value_raw, char *suffix, int type)
12084 char *value = getStringToLower(value_raw);
12085 int result = 0; // probably a save default value
12087 if (strEqual(suffix, ".direction"))
12089 result = (strEqual(value, "left") ? MV_LEFT :
12090 strEqual(value, "right") ? MV_RIGHT :
12091 strEqual(value, "up") ? MV_UP :
12092 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12094 else if (strEqual(suffix, ".position"))
12096 result = (strEqual(value, "left") ? POS_LEFT :
12097 strEqual(value, "right") ? POS_RIGHT :
12098 strEqual(value, "top") ? POS_TOP :
12099 strEqual(value, "upper") ? POS_UPPER :
12100 strEqual(value, "middle") ? POS_MIDDLE :
12101 strEqual(value, "lower") ? POS_LOWER :
12102 strEqual(value, "bottom") ? POS_BOTTOM :
12103 strEqual(value, "any") ? POS_ANY :
12104 strEqual(value, "ce") ? POS_CE :
12105 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12106 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12108 else if (strEqual(suffix, ".align"))
12110 result = (strEqual(value, "left") ? ALIGN_LEFT :
12111 strEqual(value, "right") ? ALIGN_RIGHT :
12112 strEqual(value, "center") ? ALIGN_CENTER :
12113 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12115 else if (strEqual(suffix, ".valign"))
12117 result = (strEqual(value, "top") ? VALIGN_TOP :
12118 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12119 strEqual(value, "middle") ? VALIGN_MIDDLE :
12120 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12122 else if (strEqual(suffix, ".anim_mode"))
12124 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12125 string_has_parameter(value, "loop") ? ANIM_LOOP :
12126 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12127 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12128 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12129 string_has_parameter(value, "random") ? ANIM_RANDOM :
12130 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12131 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12132 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12133 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12134 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12135 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12136 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12137 string_has_parameter(value, "all") ? ANIM_ALL :
12138 string_has_parameter(value, "tiled") ? ANIM_TILED :
12139 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12142 if (string_has_parameter(value, "once"))
12143 result |= ANIM_ONCE;
12145 if (string_has_parameter(value, "reverse"))
12146 result |= ANIM_REVERSE;
12148 if (string_has_parameter(value, "opaque_player"))
12149 result |= ANIM_OPAQUE_PLAYER;
12151 if (string_has_parameter(value, "static_panel"))
12152 result |= ANIM_STATIC_PANEL;
12154 else if (strEqual(suffix, ".init_event") ||
12155 strEqual(suffix, ".anim_event"))
12157 result = get_anim_parameter_values(value);
12159 else if (strEqual(suffix, ".init_delay_action") ||
12160 strEqual(suffix, ".anim_delay_action") ||
12161 strEqual(suffix, ".post_delay_action") ||
12162 strEqual(suffix, ".init_event_action") ||
12163 strEqual(suffix, ".anim_event_action"))
12165 result = get_anim_action_parameter_value(value_raw);
12167 else if (strEqual(suffix, ".class"))
12169 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12170 get_hash_from_string(value));
12172 else if (strEqual(suffix, ".style"))
12174 result = STYLE_DEFAULT;
12176 if (string_has_parameter(value, "accurate_borders"))
12177 result |= STYLE_ACCURATE_BORDERS;
12179 if (string_has_parameter(value, "inner_corners"))
12180 result |= STYLE_INNER_CORNERS;
12182 if (string_has_parameter(value, "reverse"))
12183 result |= STYLE_REVERSE;
12185 if (string_has_parameter(value, "leftmost_position"))
12186 result |= STYLE_LEFTMOST_POSITION;
12188 if (string_has_parameter(value, "block_clicks"))
12189 result |= STYLE_BLOCK;
12191 if (string_has_parameter(value, "passthrough_clicks"))
12192 result |= STYLE_PASSTHROUGH;
12194 if (string_has_parameter(value, "multiple_actions"))
12195 result |= STYLE_MULTIPLE_ACTIONS;
12197 if (string_has_parameter(value, "consume_ce_event"))
12198 result |= STYLE_CONSUME_CE_EVENT;
12200 else if (strEqual(suffix, ".fade_mode"))
12202 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12203 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12204 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12205 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12206 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12207 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12208 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12209 FADE_MODE_DEFAULT);
12211 else if (strEqual(suffix, ".auto_delay_unit"))
12213 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12214 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12215 AUTO_DELAY_UNIT_DEFAULT);
12217 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12219 result = gfx.get_font_from_token_function(value);
12221 else // generic parameter of type integer or boolean
12223 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12224 type == TYPE_INTEGER ? get_integer_from_string(value) :
12225 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12226 ARG_UNDEFINED_VALUE);
12234 static int get_token_parameter_value(char *token, char *value_raw)
12238 if (token == NULL || value_raw == NULL)
12239 return ARG_UNDEFINED_VALUE;
12241 suffix = strrchr(token, '.');
12242 if (suffix == NULL)
12245 if (strEqual(suffix, ".element"))
12246 return getElementFromToken(value_raw);
12248 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12249 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12252 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12253 boolean ignore_defaults)
12257 for (i = 0; image_config_vars[i].token != NULL; i++)
12259 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12261 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12262 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12266 *image_config_vars[i].value =
12267 get_token_parameter_value(image_config_vars[i].token, value);
12271 void InitMenuDesignSettings_Static(void)
12273 // always start with reliable default values from static default config
12274 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12277 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12281 // the following initializes hierarchical values from static configuration
12283 // special case: initialize "ARG_DEFAULT" values in static default config
12284 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12285 titlescreen_initial_first_default.fade_mode =
12286 title_initial_first_default.fade_mode;
12287 titlescreen_initial_first_default.fade_delay =
12288 title_initial_first_default.fade_delay;
12289 titlescreen_initial_first_default.post_delay =
12290 title_initial_first_default.post_delay;
12291 titlescreen_initial_first_default.auto_delay =
12292 title_initial_first_default.auto_delay;
12293 titlescreen_initial_first_default.auto_delay_unit =
12294 title_initial_first_default.auto_delay_unit;
12295 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12296 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12297 titlescreen_first_default.post_delay = title_first_default.post_delay;
12298 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12299 titlescreen_first_default.auto_delay_unit =
12300 title_first_default.auto_delay_unit;
12301 titlemessage_initial_first_default.fade_mode =
12302 title_initial_first_default.fade_mode;
12303 titlemessage_initial_first_default.fade_delay =
12304 title_initial_first_default.fade_delay;
12305 titlemessage_initial_first_default.post_delay =
12306 title_initial_first_default.post_delay;
12307 titlemessage_initial_first_default.auto_delay =
12308 title_initial_first_default.auto_delay;
12309 titlemessage_initial_first_default.auto_delay_unit =
12310 title_initial_first_default.auto_delay_unit;
12311 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12312 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12313 titlemessage_first_default.post_delay = title_first_default.post_delay;
12314 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12315 titlemessage_first_default.auto_delay_unit =
12316 title_first_default.auto_delay_unit;
12318 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12319 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12320 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12321 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12322 titlescreen_initial_default.auto_delay_unit =
12323 title_initial_default.auto_delay_unit;
12324 titlescreen_default.fade_mode = title_default.fade_mode;
12325 titlescreen_default.fade_delay = title_default.fade_delay;
12326 titlescreen_default.post_delay = title_default.post_delay;
12327 titlescreen_default.auto_delay = title_default.auto_delay;
12328 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12329 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12330 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12331 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12332 titlemessage_initial_default.auto_delay_unit =
12333 title_initial_default.auto_delay_unit;
12334 titlemessage_default.fade_mode = title_default.fade_mode;
12335 titlemessage_default.fade_delay = title_default.fade_delay;
12336 titlemessage_default.post_delay = title_default.post_delay;
12337 titlemessage_default.auto_delay = title_default.auto_delay;
12338 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12340 // special case: initialize "ARG_DEFAULT" values in static default config
12341 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12342 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12344 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12345 titlescreen_first[i] = titlescreen_first_default;
12346 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12347 titlemessage_first[i] = titlemessage_first_default;
12349 titlescreen_initial[i] = titlescreen_initial_default;
12350 titlescreen[i] = titlescreen_default;
12351 titlemessage_initial[i] = titlemessage_initial_default;
12352 titlemessage[i] = titlemessage_default;
12355 // special case: initialize "ARG_DEFAULT" values in static default config
12356 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12357 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12359 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12362 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12363 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12364 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12367 // special case: initialize "ARG_DEFAULT" values in static default config
12368 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12369 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12371 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12372 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12373 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12375 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12378 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12382 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12386 struct XY *dst, *src;
12388 game_buttons_xy[] =
12390 { &game.button.save, &game.button.stop },
12391 { &game.button.pause2, &game.button.pause },
12392 { &game.button.load, &game.button.play },
12393 { &game.button.undo, &game.button.stop },
12394 { &game.button.redo, &game.button.play },
12400 // special case: initialize later added SETUP list size from LEVELS value
12401 if (menu.list_size[GAME_MODE_SETUP] == -1)
12402 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12404 // set default position for snapshot buttons to stop/pause/play buttons
12405 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12406 if ((*game_buttons_xy[i].dst).x == -1 &&
12407 (*game_buttons_xy[i].dst).y == -1)
12408 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12410 // --------------------------------------------------------------------------
12411 // dynamic viewports (including playfield margins, borders and alignments)
12412 // --------------------------------------------------------------------------
12414 // dynamic viewports currently only supported for landscape mode
12415 int display_width = MAX(video.display_width, video.display_height);
12416 int display_height = MIN(video.display_width, video.display_height);
12418 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12420 struct RectWithBorder *vp_window = &viewport.window[i];
12421 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12422 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12423 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12424 boolean dynamic_window_width = (vp_window->min_width != -1);
12425 boolean dynamic_window_height = (vp_window->min_height != -1);
12426 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12427 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12429 // adjust window size if min/max width/height is specified
12431 if (vp_window->min_width != -1)
12433 int window_width = display_width;
12435 // when using static window height, use aspect ratio of display
12436 if (vp_window->min_height == -1)
12437 window_width = vp_window->height * display_width / display_height;
12439 vp_window->width = MAX(vp_window->min_width, window_width);
12442 if (vp_window->min_height != -1)
12444 int window_height = display_height;
12446 // when using static window width, use aspect ratio of display
12447 if (vp_window->min_width == -1)
12448 window_height = vp_window->width * display_height / display_width;
12450 vp_window->height = MAX(vp_window->min_height, window_height);
12453 if (vp_window->max_width != -1)
12454 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12456 if (vp_window->max_height != -1)
12457 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12459 int playfield_width = vp_window->width;
12460 int playfield_height = vp_window->height;
12462 // adjust playfield size and position according to specified margins
12464 playfield_width -= vp_playfield->margin_left;
12465 playfield_width -= vp_playfield->margin_right;
12467 playfield_height -= vp_playfield->margin_top;
12468 playfield_height -= vp_playfield->margin_bottom;
12470 // adjust playfield size if min/max width/height is specified
12472 if (vp_playfield->min_width != -1)
12473 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12475 if (vp_playfield->min_height != -1)
12476 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12478 if (vp_playfield->max_width != -1)
12479 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12481 if (vp_playfield->max_height != -1)
12482 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12484 // adjust playfield position according to specified alignment
12486 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12487 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12488 else if (vp_playfield->align == ALIGN_CENTER)
12489 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12490 else if (vp_playfield->align == ALIGN_RIGHT)
12491 vp_playfield->x += playfield_width - vp_playfield->width;
12493 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12494 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12495 else if (vp_playfield->valign == VALIGN_MIDDLE)
12496 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12497 else if (vp_playfield->valign == VALIGN_BOTTOM)
12498 vp_playfield->y += playfield_height - vp_playfield->height;
12500 vp_playfield->x += vp_playfield->margin_left;
12501 vp_playfield->y += vp_playfield->margin_top;
12503 // adjust individual playfield borders if only default border is specified
12505 if (vp_playfield->border_left == -1)
12506 vp_playfield->border_left = vp_playfield->border_size;
12507 if (vp_playfield->border_right == -1)
12508 vp_playfield->border_right = vp_playfield->border_size;
12509 if (vp_playfield->border_top == -1)
12510 vp_playfield->border_top = vp_playfield->border_size;
12511 if (vp_playfield->border_bottom == -1)
12512 vp_playfield->border_bottom = vp_playfield->border_size;
12514 // set dynamic playfield borders if borders are specified as undefined
12515 // (but only if window size was dynamic and playfield size was static)
12517 if (dynamic_window_width && !dynamic_playfield_width)
12519 if (vp_playfield->border_left == -1)
12521 vp_playfield->border_left = (vp_playfield->x -
12522 vp_playfield->margin_left);
12523 vp_playfield->x -= vp_playfield->border_left;
12524 vp_playfield->width += vp_playfield->border_left;
12527 if (vp_playfield->border_right == -1)
12529 vp_playfield->border_right = (vp_window->width -
12531 vp_playfield->width -
12532 vp_playfield->margin_right);
12533 vp_playfield->width += vp_playfield->border_right;
12537 if (dynamic_window_height && !dynamic_playfield_height)
12539 if (vp_playfield->border_top == -1)
12541 vp_playfield->border_top = (vp_playfield->y -
12542 vp_playfield->margin_top);
12543 vp_playfield->y -= vp_playfield->border_top;
12544 vp_playfield->height += vp_playfield->border_top;
12547 if (vp_playfield->border_bottom == -1)
12549 vp_playfield->border_bottom = (vp_window->height -
12551 vp_playfield->height -
12552 vp_playfield->margin_bottom);
12553 vp_playfield->height += vp_playfield->border_bottom;
12557 // adjust playfield size to be a multiple of a defined alignment tile size
12559 int align_size = vp_playfield->align_size;
12560 int playfield_xtiles = vp_playfield->width / align_size;
12561 int playfield_ytiles = vp_playfield->height / align_size;
12562 int playfield_width_corrected = playfield_xtiles * align_size;
12563 int playfield_height_corrected = playfield_ytiles * align_size;
12564 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12565 i == GFX_SPECIAL_ARG_EDITOR);
12567 if (is_playfield_mode &&
12568 dynamic_playfield_width &&
12569 vp_playfield->width != playfield_width_corrected)
12571 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12573 vp_playfield->width = playfield_width_corrected;
12575 if (vp_playfield->align == ALIGN_LEFT)
12577 vp_playfield->border_left += playfield_xdiff;
12579 else if (vp_playfield->align == ALIGN_RIGHT)
12581 vp_playfield->border_right += playfield_xdiff;
12583 else if (vp_playfield->align == ALIGN_CENTER)
12585 int border_left_diff = playfield_xdiff / 2;
12586 int border_right_diff = playfield_xdiff - border_left_diff;
12588 vp_playfield->border_left += border_left_diff;
12589 vp_playfield->border_right += border_right_diff;
12593 if (is_playfield_mode &&
12594 dynamic_playfield_height &&
12595 vp_playfield->height != playfield_height_corrected)
12597 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12599 vp_playfield->height = playfield_height_corrected;
12601 if (vp_playfield->valign == VALIGN_TOP)
12603 vp_playfield->border_top += playfield_ydiff;
12605 else if (vp_playfield->align == VALIGN_BOTTOM)
12607 vp_playfield->border_right += playfield_ydiff;
12609 else if (vp_playfield->align == VALIGN_MIDDLE)
12611 int border_top_diff = playfield_ydiff / 2;
12612 int border_bottom_diff = playfield_ydiff - border_top_diff;
12614 vp_playfield->border_top += border_top_diff;
12615 vp_playfield->border_bottom += border_bottom_diff;
12619 // adjust door positions according to specified alignment
12621 for (j = 0; j < 2; j++)
12623 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12625 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12626 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12627 else if (vp_door->align == ALIGN_CENTER)
12628 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12629 else if (vp_door->align == ALIGN_RIGHT)
12630 vp_door->x += vp_window->width - vp_door->width;
12632 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12633 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12634 else if (vp_door->valign == VALIGN_MIDDLE)
12635 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12636 else if (vp_door->valign == VALIGN_BOTTOM)
12637 vp_door->y += vp_window->height - vp_door->height;
12642 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12646 struct XYTileSize *dst, *src;
12649 editor_buttons_xy[] =
12652 &editor.button.element_left, &editor.palette.element_left,
12653 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12656 &editor.button.element_middle, &editor.palette.element_middle,
12657 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12660 &editor.button.element_right, &editor.palette.element_right,
12661 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12668 // set default position for element buttons to element graphics
12669 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12671 if ((*editor_buttons_xy[i].dst).x == -1 &&
12672 (*editor_buttons_xy[i].dst).y == -1)
12674 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12676 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12678 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12682 // adjust editor palette rows and columns if specified to be dynamic
12684 if (editor.palette.cols == -1)
12686 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12687 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12688 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12690 editor.palette.cols = (vp_width - sc_width) / bt_width;
12692 if (editor.palette.x == -1)
12694 int palette_width = editor.palette.cols * bt_width + sc_width;
12696 editor.palette.x = (vp_width - palette_width) / 2;
12700 if (editor.palette.rows == -1)
12702 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12703 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12704 int tx_height = getFontHeight(FONT_TEXT_2);
12706 editor.palette.rows = (vp_height - tx_height) / bt_height;
12708 if (editor.palette.y == -1)
12710 int palette_height = editor.palette.rows * bt_height + tx_height;
12712 editor.palette.y = (vp_height - palette_height) / 2;
12717 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12718 boolean initialize)
12720 // special case: check if network and preview player positions are redefined,
12721 // to compare this later against the main menu level preview being redefined
12722 struct TokenIntPtrInfo menu_config_players[] =
12724 { "main.network_players.x", &menu.main.network_players.redefined },
12725 { "main.network_players.y", &menu.main.network_players.redefined },
12726 { "main.preview_players.x", &menu.main.preview_players.redefined },
12727 { "main.preview_players.y", &menu.main.preview_players.redefined },
12728 { "preview.x", &preview.redefined },
12729 { "preview.y", &preview.redefined }
12735 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12736 *menu_config_players[i].value = FALSE;
12740 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12741 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12742 *menu_config_players[i].value = TRUE;
12746 static void InitMenuDesignSettings_PreviewPlayers(void)
12748 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12751 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12753 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12756 static void LoadMenuDesignSettingsFromFilename(char *filename)
12758 static struct TitleFadingInfo tfi;
12759 static struct TitleMessageInfo tmi;
12760 static struct TokenInfo title_tokens[] =
12762 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12763 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12764 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12765 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12766 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12770 static struct TokenInfo titlemessage_tokens[] =
12772 { TYPE_INTEGER, &tmi.x, ".x" },
12773 { TYPE_INTEGER, &tmi.y, ".y" },
12774 { TYPE_INTEGER, &tmi.width, ".width" },
12775 { TYPE_INTEGER, &tmi.height, ".height" },
12776 { TYPE_INTEGER, &tmi.chars, ".chars" },
12777 { TYPE_INTEGER, &tmi.lines, ".lines" },
12778 { TYPE_INTEGER, &tmi.align, ".align" },
12779 { TYPE_INTEGER, &tmi.valign, ".valign" },
12780 { TYPE_INTEGER, &tmi.font, ".font" },
12781 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12782 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12783 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12784 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12785 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12786 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12787 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12788 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12789 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12795 struct TitleFadingInfo *info;
12800 // initialize first titles from "enter screen" definitions, if defined
12801 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12802 { &title_first_default, "menu.enter_screen.TITLE" },
12804 // initialize title screens from "next screen" definitions, if defined
12805 { &title_initial_default, "menu.next_screen.TITLE" },
12806 { &title_default, "menu.next_screen.TITLE" },
12812 struct TitleMessageInfo *array;
12815 titlemessage_arrays[] =
12817 // initialize first titles from "enter screen" definitions, if defined
12818 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12819 { titlescreen_first, "menu.enter_screen.TITLE" },
12820 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12821 { titlemessage_first, "menu.enter_screen.TITLE" },
12823 // initialize titles from "next screen" definitions, if defined
12824 { titlescreen_initial, "menu.next_screen.TITLE" },
12825 { titlescreen, "menu.next_screen.TITLE" },
12826 { titlemessage_initial, "menu.next_screen.TITLE" },
12827 { titlemessage, "menu.next_screen.TITLE" },
12829 // overwrite titles with title definitions, if defined
12830 { titlescreen_initial_first, "[title_initial]" },
12831 { titlescreen_first, "[title]" },
12832 { titlemessage_initial_first, "[title_initial]" },
12833 { titlemessage_first, "[title]" },
12835 { titlescreen_initial, "[title_initial]" },
12836 { titlescreen, "[title]" },
12837 { titlemessage_initial, "[title_initial]" },
12838 { titlemessage, "[title]" },
12840 // overwrite titles with title screen/message definitions, if defined
12841 { titlescreen_initial_first, "[titlescreen_initial]" },
12842 { titlescreen_first, "[titlescreen]" },
12843 { titlemessage_initial_first, "[titlemessage_initial]" },
12844 { titlemessage_first, "[titlemessage]" },
12846 { titlescreen_initial, "[titlescreen_initial]" },
12847 { titlescreen, "[titlescreen]" },
12848 { titlemessage_initial, "[titlemessage_initial]" },
12849 { titlemessage, "[titlemessage]" },
12853 SetupFileHash *setup_file_hash;
12856 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12859 // the following initializes hierarchical values from dynamic configuration
12861 // special case: initialize with default values that may be overwritten
12862 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12863 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12865 struct TokenIntPtrInfo menu_config[] =
12867 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12868 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12869 { "menu.list_size", &menu.list_size[i] }
12872 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12874 char *token = menu_config[j].token;
12875 char *value = getHashEntry(setup_file_hash, token);
12878 *menu_config[j].value = get_integer_from_string(value);
12882 // special case: initialize with default values that may be overwritten
12883 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12884 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12886 struct TokenIntPtrInfo menu_config[] =
12888 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12889 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12890 { "menu.list_size.INFO", &menu.list_size_info[i] },
12891 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12892 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12895 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12897 char *token = menu_config[j].token;
12898 char *value = getHashEntry(setup_file_hash, token);
12901 *menu_config[j].value = get_integer_from_string(value);
12905 // special case: initialize with default values that may be overwritten
12906 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12907 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12909 struct TokenIntPtrInfo menu_config[] =
12911 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12912 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12915 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12917 char *token = menu_config[j].token;
12918 char *value = getHashEntry(setup_file_hash, token);
12921 *menu_config[j].value = get_integer_from_string(value);
12925 // special case: initialize with default values that may be overwritten
12926 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12927 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12929 struct TokenIntPtrInfo menu_config[] =
12931 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12932 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
12933 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12934 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12935 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12936 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12937 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12938 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12939 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12940 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12943 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12945 char *token = menu_config[j].token;
12946 char *value = getHashEntry(setup_file_hash, token);
12949 *menu_config[j].value = get_integer_from_string(value);
12953 // special case: initialize with default values that may be overwritten
12954 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12955 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12957 struct TokenIntPtrInfo menu_config[] =
12959 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12960 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12961 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12962 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12963 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12964 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12965 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12966 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12967 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12970 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12972 char *token = menu_config[j].token;
12973 char *value = getHashEntry(setup_file_hash, token);
12976 *menu_config[j].value = get_token_parameter_value(token, value);
12980 // special case: initialize with default values that may be overwritten
12981 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12982 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12986 char *token_prefix;
12987 struct RectWithBorder *struct_ptr;
12991 { "viewport.window", &viewport.window[i] },
12992 { "viewport.playfield", &viewport.playfield[i] },
12993 { "viewport.door_1", &viewport.door_1[i] },
12994 { "viewport.door_2", &viewport.door_2[i] }
12997 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12999 struct TokenIntPtrInfo vp_config[] =
13001 { ".x", &vp_struct[j].struct_ptr->x },
13002 { ".y", &vp_struct[j].struct_ptr->y },
13003 { ".width", &vp_struct[j].struct_ptr->width },
13004 { ".height", &vp_struct[j].struct_ptr->height },
13005 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13006 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13007 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13008 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13009 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13010 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13011 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13012 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13013 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13014 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13015 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13016 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13017 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13018 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13019 { ".align", &vp_struct[j].struct_ptr->align },
13020 { ".valign", &vp_struct[j].struct_ptr->valign }
13023 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13025 char *token = getStringCat2(vp_struct[j].token_prefix,
13026 vp_config[k].token);
13027 char *value = getHashEntry(setup_file_hash, token);
13030 *vp_config[k].value = get_token_parameter_value(token, value);
13037 // special case: initialize with default values that may be overwritten
13038 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13039 for (i = 0; title_info[i].info != NULL; i++)
13041 struct TitleFadingInfo *info = title_info[i].info;
13042 char *base_token = title_info[i].text;
13044 for (j = 0; title_tokens[j].type != -1; j++)
13046 char *token = getStringCat2(base_token, title_tokens[j].text);
13047 char *value = getHashEntry(setup_file_hash, token);
13051 int parameter_value = get_token_parameter_value(token, value);
13055 *(int *)title_tokens[j].value = (int)parameter_value;
13064 // special case: initialize with default values that may be overwritten
13065 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13066 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13068 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13069 char *base_token = titlemessage_arrays[i].text;
13071 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13073 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13074 char *value = getHashEntry(setup_file_hash, token);
13078 int parameter_value = get_token_parameter_value(token, value);
13080 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13084 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13085 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13087 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13097 // read (and overwrite with) values that may be specified in config file
13098 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13100 // special case: check if network and preview player positions are redefined
13101 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13103 freeSetupFileHash(setup_file_hash);
13106 void LoadMenuDesignSettings(void)
13108 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13110 InitMenuDesignSettings_Static();
13111 InitMenuDesignSettings_SpecialPreProcessing();
13112 InitMenuDesignSettings_PreviewPlayers();
13114 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13116 // first look for special settings configured in level series config
13117 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13119 if (fileExists(filename_base))
13120 LoadMenuDesignSettingsFromFilename(filename_base);
13123 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13125 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13126 LoadMenuDesignSettingsFromFilename(filename_local);
13128 InitMenuDesignSettings_SpecialPostProcessing();
13131 void LoadMenuDesignSettings_AfterGraphics(void)
13133 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13136 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13137 boolean ignore_defaults)
13141 for (i = 0; sound_config_vars[i].token != NULL; i++)
13143 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13145 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13146 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13150 *sound_config_vars[i].value =
13151 get_token_parameter_value(sound_config_vars[i].token, value);
13155 void InitSoundSettings_Static(void)
13157 // always start with reliable default values from static default config
13158 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13161 static void LoadSoundSettingsFromFilename(char *filename)
13163 SetupFileHash *setup_file_hash;
13165 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13168 // read (and overwrite with) values that may be specified in config file
13169 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13171 freeSetupFileHash(setup_file_hash);
13174 void LoadSoundSettings(void)
13176 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13178 InitSoundSettings_Static();
13180 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13182 // first look for special settings configured in level series config
13183 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13185 if (fileExists(filename_base))
13186 LoadSoundSettingsFromFilename(filename_base);
13189 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13191 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13192 LoadSoundSettingsFromFilename(filename_local);
13195 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13197 char *filename = getEditorSetupFilename();
13198 SetupFileList *setup_file_list, *list;
13199 SetupFileHash *element_hash;
13200 int num_unknown_tokens = 0;
13203 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13206 element_hash = newSetupFileHash();
13208 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13209 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13211 // determined size may be larger than needed (due to unknown elements)
13213 for (list = setup_file_list; list != NULL; list = list->next)
13216 // add space for up to 3 more elements for padding that may be needed
13217 *num_elements += 3;
13219 // free memory for old list of elements, if needed
13220 checked_free(*elements);
13222 // allocate memory for new list of elements
13223 *elements = checked_malloc(*num_elements * sizeof(int));
13226 for (list = setup_file_list; list != NULL; list = list->next)
13228 char *value = getHashEntry(element_hash, list->token);
13230 if (value == NULL) // try to find obsolete token mapping
13232 char *mapped_token = get_mapped_token(list->token);
13234 if (mapped_token != NULL)
13236 value = getHashEntry(element_hash, mapped_token);
13238 free(mapped_token);
13244 (*elements)[(*num_elements)++] = atoi(value);
13248 if (num_unknown_tokens == 0)
13251 Warn("unknown token(s) found in config file:");
13252 Warn("- config file: '%s'", filename);
13254 num_unknown_tokens++;
13257 Warn("- token: '%s'", list->token);
13261 if (num_unknown_tokens > 0)
13264 while (*num_elements % 4) // pad with empty elements, if needed
13265 (*elements)[(*num_elements)++] = EL_EMPTY;
13267 freeSetupFileList(setup_file_list);
13268 freeSetupFileHash(element_hash);
13271 for (i = 0; i < *num_elements; i++)
13272 Debug("editor", "element '%s' [%d]\n",
13273 element_info[(*elements)[i]].token_name, (*elements)[i]);
13277 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13280 SetupFileHash *setup_file_hash = NULL;
13281 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13282 char *filename_music, *filename_prefix, *filename_info;
13288 token_to_value_ptr[] =
13290 { "title_header", &tmp_music_file_info.title_header },
13291 { "artist_header", &tmp_music_file_info.artist_header },
13292 { "album_header", &tmp_music_file_info.album_header },
13293 { "year_header", &tmp_music_file_info.year_header },
13294 { "played_header", &tmp_music_file_info.played_header },
13296 { "title", &tmp_music_file_info.title },
13297 { "artist", &tmp_music_file_info.artist },
13298 { "album", &tmp_music_file_info.album },
13299 { "year", &tmp_music_file_info.year },
13300 { "played", &tmp_music_file_info.played },
13306 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13307 getCustomMusicFilename(basename));
13309 if (filename_music == NULL)
13312 // ---------- try to replace file extension ----------
13314 filename_prefix = getStringCopy(filename_music);
13315 if (strrchr(filename_prefix, '.') != NULL)
13316 *strrchr(filename_prefix, '.') = '\0';
13317 filename_info = getStringCat2(filename_prefix, ".txt");
13319 if (fileExists(filename_info))
13320 setup_file_hash = loadSetupFileHash(filename_info);
13322 free(filename_prefix);
13323 free(filename_info);
13325 if (setup_file_hash == NULL)
13327 // ---------- try to add file extension ----------
13329 filename_prefix = getStringCopy(filename_music);
13330 filename_info = getStringCat2(filename_prefix, ".txt");
13332 if (fileExists(filename_info))
13333 setup_file_hash = loadSetupFileHash(filename_info);
13335 free(filename_prefix);
13336 free(filename_info);
13339 if (setup_file_hash == NULL)
13342 // ---------- music file info found ----------
13344 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13346 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13348 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13350 *token_to_value_ptr[i].value_ptr =
13351 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13354 tmp_music_file_info.basename = getStringCopy(basename);
13355 tmp_music_file_info.music = music;
13356 tmp_music_file_info.is_sound = is_sound;
13358 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13359 *new_music_file_info = tmp_music_file_info;
13361 return new_music_file_info;
13364 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13366 return get_music_file_info_ext(basename, music, FALSE);
13369 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13371 return get_music_file_info_ext(basename, sound, TRUE);
13374 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13375 char *basename, boolean is_sound)
13377 for (; list != NULL; list = list->next)
13378 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13384 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13386 return music_info_listed_ext(list, basename, FALSE);
13389 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13391 return music_info_listed_ext(list, basename, TRUE);
13394 void LoadMusicInfo(void)
13396 int num_music_noconf = getMusicListSize_NoConf();
13397 int num_music = getMusicListSize();
13398 int num_sounds = getSoundListSize();
13399 struct FileInfo *music, *sound;
13400 struct MusicFileInfo *next, **new;
13404 while (music_file_info != NULL)
13406 next = music_file_info->next;
13408 checked_free(music_file_info->basename);
13410 checked_free(music_file_info->title_header);
13411 checked_free(music_file_info->artist_header);
13412 checked_free(music_file_info->album_header);
13413 checked_free(music_file_info->year_header);
13414 checked_free(music_file_info->played_header);
13416 checked_free(music_file_info->title);
13417 checked_free(music_file_info->artist);
13418 checked_free(music_file_info->album);
13419 checked_free(music_file_info->year);
13420 checked_free(music_file_info->played);
13422 free(music_file_info);
13424 music_file_info = next;
13427 new = &music_file_info;
13429 // get (configured or unconfigured) music file info for all levels
13430 for (i = leveldir_current->first_level;
13431 i <= leveldir_current->last_level; i++)
13435 if (levelset.music[i] != MUS_UNDEFINED)
13437 // get music file info for configured level music
13438 music_nr = levelset.music[i];
13440 else if (num_music_noconf > 0)
13442 // get music file info for unconfigured level music
13443 int level_pos = i - leveldir_current->first_level;
13445 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13452 char *basename = getMusicInfoEntryFilename(music_nr);
13454 if (basename == NULL)
13457 if (!music_info_listed(music_file_info, basename))
13459 *new = get_music_file_info(basename, music_nr);
13462 new = &(*new)->next;
13466 // get music file info for all remaining configured music files
13467 for (i = 0; i < num_music; i++)
13469 music = getMusicListEntry(i);
13471 if (music->filename == NULL)
13474 if (strEqual(music->filename, UNDEFINED_FILENAME))
13477 // a configured file may be not recognized as music
13478 if (!FileIsMusic(music->filename))
13481 if (!music_info_listed(music_file_info, music->filename))
13483 *new = get_music_file_info(music->filename, i);
13486 new = &(*new)->next;
13490 // get sound file info for all configured sound files
13491 for (i = 0; i < num_sounds; i++)
13493 sound = getSoundListEntry(i);
13495 if (sound->filename == NULL)
13498 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13501 // a configured file may be not recognized as sound
13502 if (!FileIsSound(sound->filename))
13505 if (!sound_info_listed(music_file_info, sound->filename))
13507 *new = get_sound_file_info(sound->filename, i);
13509 new = &(*new)->next;
13513 // add pointers to previous list nodes
13515 struct MusicFileInfo *node = music_file_info;
13517 while (node != NULL)
13520 node->next->prev = node;
13526 static void add_helpanim_entry(int element, int action, int direction,
13527 int delay, int *num_list_entries)
13529 struct HelpAnimInfo *new_list_entry;
13530 (*num_list_entries)++;
13533 checked_realloc(helpanim_info,
13534 *num_list_entries * sizeof(struct HelpAnimInfo));
13535 new_list_entry = &helpanim_info[*num_list_entries - 1];
13537 new_list_entry->element = element;
13538 new_list_entry->action = action;
13539 new_list_entry->direction = direction;
13540 new_list_entry->delay = delay;
13543 static void print_unknown_token(char *filename, char *token, int token_nr)
13548 Warn("unknown token(s) found in config file:");
13549 Warn("- config file: '%s'", filename);
13552 Warn("- token: '%s'", token);
13555 static void print_unknown_token_end(int token_nr)
13561 void LoadHelpAnimInfo(void)
13563 char *filename = getHelpAnimFilename();
13564 SetupFileList *setup_file_list = NULL, *list;
13565 SetupFileHash *element_hash, *action_hash, *direction_hash;
13566 int num_list_entries = 0;
13567 int num_unknown_tokens = 0;
13570 if (fileExists(filename))
13571 setup_file_list = loadSetupFileList(filename);
13573 if (setup_file_list == NULL)
13575 // use reliable default values from static configuration
13576 SetupFileList *insert_ptr;
13578 insert_ptr = setup_file_list =
13579 newSetupFileList(helpanim_config[0].token,
13580 helpanim_config[0].value);
13582 for (i = 1; helpanim_config[i].token; i++)
13583 insert_ptr = addListEntry(insert_ptr,
13584 helpanim_config[i].token,
13585 helpanim_config[i].value);
13588 element_hash = newSetupFileHash();
13589 action_hash = newSetupFileHash();
13590 direction_hash = newSetupFileHash();
13592 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13593 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13595 for (i = 0; i < NUM_ACTIONS; i++)
13596 setHashEntry(action_hash, element_action_info[i].suffix,
13597 i_to_a(element_action_info[i].value));
13599 // do not store direction index (bit) here, but direction value!
13600 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13601 setHashEntry(direction_hash, element_direction_info[i].suffix,
13602 i_to_a(1 << element_direction_info[i].value));
13604 for (list = setup_file_list; list != NULL; list = list->next)
13606 char *element_token, *action_token, *direction_token;
13607 char *element_value, *action_value, *direction_value;
13608 int delay = atoi(list->value);
13610 if (strEqual(list->token, "end"))
13612 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13617 /* first try to break element into element/action/direction parts;
13618 if this does not work, also accept combined "element[.act][.dir]"
13619 elements (like "dynamite.active"), which are unique elements */
13621 if (strchr(list->token, '.') == NULL) // token contains no '.'
13623 element_value = getHashEntry(element_hash, list->token);
13624 if (element_value != NULL) // element found
13625 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13626 &num_list_entries);
13629 // no further suffixes found -- this is not an element
13630 print_unknown_token(filename, list->token, num_unknown_tokens++);
13636 // token has format "<prefix>.<something>"
13638 action_token = strchr(list->token, '.'); // suffix may be action ...
13639 direction_token = action_token; // ... or direction
13641 element_token = getStringCopy(list->token);
13642 *strchr(element_token, '.') = '\0';
13644 element_value = getHashEntry(element_hash, element_token);
13646 if (element_value == NULL) // this is no element
13648 element_value = getHashEntry(element_hash, list->token);
13649 if (element_value != NULL) // combined element found
13650 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13651 &num_list_entries);
13653 print_unknown_token(filename, list->token, num_unknown_tokens++);
13655 free(element_token);
13660 action_value = getHashEntry(action_hash, action_token);
13662 if (action_value != NULL) // action found
13664 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13665 &num_list_entries);
13667 free(element_token);
13672 direction_value = getHashEntry(direction_hash, direction_token);
13674 if (direction_value != NULL) // direction found
13676 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13677 &num_list_entries);
13679 free(element_token);
13684 if (strchr(action_token + 1, '.') == NULL)
13686 // no further suffixes found -- this is not an action nor direction
13688 element_value = getHashEntry(element_hash, list->token);
13689 if (element_value != NULL) // combined element found
13690 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13691 &num_list_entries);
13693 print_unknown_token(filename, list->token, num_unknown_tokens++);
13695 free(element_token);
13700 // token has format "<prefix>.<suffix>.<something>"
13702 direction_token = strchr(action_token + 1, '.');
13704 action_token = getStringCopy(action_token);
13705 *strchr(action_token + 1, '.') = '\0';
13707 action_value = getHashEntry(action_hash, action_token);
13709 if (action_value == NULL) // this is no action
13711 element_value = getHashEntry(element_hash, list->token);
13712 if (element_value != NULL) // combined element found
13713 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13714 &num_list_entries);
13716 print_unknown_token(filename, list->token, num_unknown_tokens++);
13718 free(element_token);
13719 free(action_token);
13724 direction_value = getHashEntry(direction_hash, direction_token);
13726 if (direction_value != NULL) // direction found
13728 add_helpanim_entry(atoi(element_value), atoi(action_value),
13729 atoi(direction_value), delay, &num_list_entries);
13731 free(element_token);
13732 free(action_token);
13737 // this is no direction
13739 element_value = getHashEntry(element_hash, list->token);
13740 if (element_value != NULL) // combined element found
13741 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13742 &num_list_entries);
13744 print_unknown_token(filename, list->token, num_unknown_tokens++);
13746 free(element_token);
13747 free(action_token);
13750 print_unknown_token_end(num_unknown_tokens);
13752 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13753 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13755 freeSetupFileList(setup_file_list);
13756 freeSetupFileHash(element_hash);
13757 freeSetupFileHash(action_hash);
13758 freeSetupFileHash(direction_hash);
13761 for (i = 0; i < num_list_entries; i++)
13762 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13763 EL_NAME(helpanim_info[i].element),
13764 helpanim_info[i].element,
13765 helpanim_info[i].action,
13766 helpanim_info[i].direction,
13767 helpanim_info[i].delay);
13771 void LoadHelpTextInfo(void)
13773 char *filename = getHelpTextFilename();
13776 if (helptext_info != NULL)
13778 freeSetupFileHash(helptext_info);
13779 helptext_info = NULL;
13782 if (fileExists(filename))
13783 helptext_info = loadSetupFileHash(filename);
13785 if (helptext_info == NULL)
13787 // use reliable default values from static configuration
13788 helptext_info = newSetupFileHash();
13790 for (i = 0; helptext_config[i].token; i++)
13791 setHashEntry(helptext_info,
13792 helptext_config[i].token,
13793 helptext_config[i].value);
13797 BEGIN_HASH_ITERATION(helptext_info, itr)
13799 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13800 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13802 END_HASH_ITERATION(hash, itr)
13807 // ----------------------------------------------------------------------------
13809 // ----------------------------------------------------------------------------
13811 #define MAX_NUM_CONVERT_LEVELS 1000
13813 void ConvertLevels(void)
13815 static LevelDirTree *convert_leveldir = NULL;
13816 static int convert_level_nr = -1;
13817 static int num_levels_handled = 0;
13818 static int num_levels_converted = 0;
13819 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13822 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13823 global.convert_leveldir);
13825 if (convert_leveldir == NULL)
13826 Fail("no such level identifier: '%s'", global.convert_leveldir);
13828 leveldir_current = convert_leveldir;
13830 if (global.convert_level_nr != -1)
13832 convert_leveldir->first_level = global.convert_level_nr;
13833 convert_leveldir->last_level = global.convert_level_nr;
13836 convert_level_nr = convert_leveldir->first_level;
13838 PrintLine("=", 79);
13839 Print("Converting levels\n");
13840 PrintLine("-", 79);
13841 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13842 Print("Level series name: '%s'\n", convert_leveldir->name);
13843 Print("Level series author: '%s'\n", convert_leveldir->author);
13844 Print("Number of levels: %d\n", convert_leveldir->levels);
13845 PrintLine("=", 79);
13848 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13849 levels_failed[i] = FALSE;
13851 while (convert_level_nr <= convert_leveldir->last_level)
13853 char *level_filename;
13856 level_nr = convert_level_nr++;
13858 Print("Level %03d: ", level_nr);
13860 LoadLevel(level_nr);
13861 if (level.no_level_file || level.no_valid_file)
13863 Print("(no level)\n");
13867 Print("converting level ... ");
13870 // special case: conversion of some EMC levels as requested by ACME
13871 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13874 level_filename = getDefaultLevelFilename(level_nr);
13875 new_level = !fileExists(level_filename);
13879 SaveLevel(level_nr);
13881 num_levels_converted++;
13883 Print("converted.\n");
13887 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13888 levels_failed[level_nr] = TRUE;
13890 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13893 num_levels_handled++;
13897 PrintLine("=", 79);
13898 Print("Number of levels handled: %d\n", num_levels_handled);
13899 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13900 (num_levels_handled ?
13901 num_levels_converted * 100 / num_levels_handled : 0));
13902 PrintLine("-", 79);
13903 Print("Summary (for automatic parsing by scripts):\n");
13904 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13905 convert_leveldir->identifier, num_levels_converted,
13906 num_levels_handled,
13907 (num_levels_handled ?
13908 num_levels_converted * 100 / num_levels_handled : 0));
13910 if (num_levels_handled != num_levels_converted)
13912 Print(", FAILED:");
13913 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13914 if (levels_failed[i])
13919 PrintLine("=", 79);
13921 CloseAllAndExit(0);
13925 // ----------------------------------------------------------------------------
13926 // create and save images for use in level sketches (raw BMP format)
13927 // ----------------------------------------------------------------------------
13929 void CreateLevelSketchImages(void)
13935 InitElementPropertiesGfxElement();
13937 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13938 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13940 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13942 int element = getMappedElement(i);
13943 char basename1[16];
13944 char basename2[16];
13948 sprintf(basename1, "%04d.bmp", i);
13949 sprintf(basename2, "%04ds.bmp", i);
13951 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13952 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13954 DrawSizedElement(0, 0, element, TILESIZE);
13955 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13957 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13958 Fail("cannot save level sketch image file '%s'", filename1);
13960 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13961 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13963 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13964 Fail("cannot save level sketch image file '%s'", filename2);
13969 // create corresponding SQL statements (for normal and small images)
13972 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13973 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13976 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13977 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13979 // optional: create content for forum level sketch demonstration post
13981 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13984 FreeBitmap(bitmap1);
13985 FreeBitmap(bitmap2);
13988 fprintf(stderr, "\n");
13990 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13992 CloseAllAndExit(0);
13996 // ----------------------------------------------------------------------------
13997 // create and save images for element collecting animations (raw BMP format)
13998 // ----------------------------------------------------------------------------
14000 static boolean createCollectImage(int element)
14002 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14005 void CreateCollectElementImages(void)
14009 int anim_frames = num_steps - 1;
14010 int tile_size = TILESIZE;
14011 int anim_width = tile_size * anim_frames;
14012 int anim_height = tile_size;
14013 int num_collect_images = 0;
14014 int pos_collect_images = 0;
14016 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14017 if (createCollectImage(i))
14018 num_collect_images++;
14020 Info("Creating %d element collecting animation images ...",
14021 num_collect_images);
14023 int dst_width = anim_width * 2;
14024 int dst_height = anim_height * num_collect_images / 2;
14025 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14026 char *basename_bmp = "RocksCollect.bmp";
14027 char *basename_png = "RocksCollect.png";
14028 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14029 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14030 int len_filename_bmp = strlen(filename_bmp);
14031 int len_filename_png = strlen(filename_png);
14032 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14033 char cmd_convert[max_command_len];
14035 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14039 // force using RGBA surface for destination bitmap
14040 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14041 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14043 dst_bitmap->surface =
14044 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14046 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14048 if (!createCollectImage(i))
14051 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14052 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14053 int graphic = el2img(i);
14054 char *token_name = element_info[i].token_name;
14055 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14056 Bitmap *src_bitmap;
14059 Info("- creating collecting image for '%s' ...", token_name);
14061 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14063 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14064 tile_size, tile_size, 0, 0);
14066 // force using RGBA surface for temporary bitmap (using transparent black)
14067 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14068 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14070 tmp_bitmap->surface =
14071 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14073 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14075 for (j = 0; j < anim_frames; j++)
14077 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14078 int frame_size = frame_size_final * num_steps;
14079 int offset = (tile_size - frame_size_final) / 2;
14080 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14082 while (frame_size > frame_size_final)
14086 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14088 FreeBitmap(frame_bitmap);
14090 frame_bitmap = half_bitmap;
14093 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14094 frame_size_final, frame_size_final,
14095 dst_x + j * tile_size + offset, dst_y + offset);
14097 FreeBitmap(frame_bitmap);
14100 tmp_bitmap->surface_masked = NULL;
14102 FreeBitmap(tmp_bitmap);
14104 pos_collect_images++;
14107 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14108 Fail("cannot save element collecting image file '%s'", filename_bmp);
14110 FreeBitmap(dst_bitmap);
14112 Info("Converting image file from BMP to PNG ...");
14114 if (system(cmd_convert) != 0)
14115 Fail("converting image file failed");
14117 unlink(filename_bmp);
14121 CloseAllAndExit(0);
14125 // ----------------------------------------------------------------------------
14126 // create and save images for custom and group elements (raw BMP format)
14127 // ----------------------------------------------------------------------------
14129 void CreateCustomElementImages(char *directory)
14131 char *src_basename = "RocksCE-template.ilbm";
14132 char *dst_basename = "RocksCE.bmp";
14133 char *src_filename = getPath2(directory, src_basename);
14134 char *dst_filename = getPath2(directory, dst_basename);
14135 Bitmap *src_bitmap;
14137 int yoffset_ce = 0;
14138 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14141 InitVideoDefaults();
14143 ReCreateBitmap(&backbuffer, video.width, video.height);
14145 src_bitmap = LoadImage(src_filename);
14147 bitmap = CreateBitmap(TILEX * 16 * 2,
14148 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14151 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14158 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14159 TILEX * x, TILEY * y + yoffset_ce);
14161 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14163 TILEX * x + TILEX * 16,
14164 TILEY * y + yoffset_ce);
14166 for (j = 2; j >= 0; j--)
14170 BlitBitmap(src_bitmap, bitmap,
14171 TILEX + c * 7, 0, 6, 10,
14172 TILEX * x + 6 + j * 7,
14173 TILEY * y + 11 + yoffset_ce);
14175 BlitBitmap(src_bitmap, bitmap,
14176 TILEX + c * 8, TILEY, 6, 10,
14177 TILEX * 16 + TILEX * x + 6 + j * 8,
14178 TILEY * y + 10 + yoffset_ce);
14184 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14191 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14192 TILEX * x, TILEY * y + yoffset_ge);
14194 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14196 TILEX * x + TILEX * 16,
14197 TILEY * y + yoffset_ge);
14199 for (j = 1; j >= 0; j--)
14203 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14204 TILEX * x + 6 + j * 10,
14205 TILEY * y + 11 + yoffset_ge);
14207 BlitBitmap(src_bitmap, bitmap,
14208 TILEX + c * 8, TILEY + 12, 6, 10,
14209 TILEX * 16 + TILEX * x + 10 + j * 8,
14210 TILEY * y + 10 + yoffset_ge);
14216 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14217 Fail("cannot save CE graphics file '%s'", dst_filename);
14219 FreeBitmap(bitmap);
14221 CloseAllAndExit(0);