1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
27 #define ENABLE_UNUSED_CODE 0 // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
29 #define ENABLE_RESERVED_CODE 0 // reserved for later use
31 #define CHUNK_ID_LEN 4 // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE -1 // do not write chunk size
35 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
38 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
61 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
65 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
67 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER 0
78 #define SAVE_CONF_ALWAYS 1
79 #define SAVE_CONF_WHEN_CHANGED -1
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE 0x00
83 #define CONF_MASK_2_BYTE 0x40
84 #define CONF_MASK_4_BYTE 0x80
85 #define CONF_MASK_MULTI_BYTES 0xc0
87 #define CONF_MASK_BYTES 0xc0
88 #define CONF_MASK_TOKEN 0x3f
90 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
91 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
92 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
93 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
101 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
102 (x) == CONF_MASK_2_BYTE ? 2 : \
103 (x) == CONF_MASK_4_BYTE ? 4 : 0)
105 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES (2)
109 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
110 (t) == TYPE_ELEMENT_LIST ? \
111 CONF_ELEMENT_NUM_BYTES : \
112 (t) == TYPE_CONTENT || \
113 (t) == TYPE_CONTENT_LIST ? \
114 CONF_CONTENT_NUM_BYTES : 1)
116 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
122 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
123 CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
140 struct LevelFileConfigInfo
142 int element; // element for which data is to be stored
143 int save_type; // save data always, never or when changed
144 int data_type; // data type (used internally, not stored)
145 int conf_type; // micro chunk identifier (stored in file)
148 void *value; // variable that holds the data to be stored
149 int default_value; // initial default value for this variable
152 void *value_copy; // variable that holds the data to be copied
153 void *num_entities; // number of entities for multi-byte data
154 int default_num_entities; // default number of entities for this data
155 int max_num_entities; // maximal number of entities for this data
156 char *default_string; // optional default string for string data
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 // ---------- values not related to single elements -------------------------
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
166 &li.game_engine_type, GAME_ENGINE_TYPE_RND
170 -1, SAVE_CONF_ALWAYS,
171 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
172 &li.fieldx, STD_LEV_FIELDX
175 -1, SAVE_CONF_ALWAYS,
176 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
177 &li.fieldy, STD_LEV_FIELDY
181 -1, SAVE_CONF_ALWAYS,
182 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
187 -1, SAVE_CONF_ALWAYS,
188 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
194 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
200 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
201 &li.use_step_counter, FALSE
206 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
207 &li.wind_direction_initial, MV_NONE
212 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
213 &li.em_slippery_gems, FALSE
218 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
219 &li.use_custom_template, FALSE
224 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
225 &li.can_move_into_acid_bits, ~0 // default: everything can
230 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
231 &li.dont_collide_with_bits, ~0 // default: always deadly
236 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
237 &li.em_explodes_by_fire, FALSE
242 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
243 &li.score[SC_TIME_BONUS], 1
248 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
249 &li.auto_exit_sokoban, FALSE
254 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
255 &li.auto_count_gems, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
261 &li.solved_by_one_player, FALSE
266 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
267 &li.time_score_base, 1
272 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
273 &li.rate_time_over_score, FALSE
278 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
279 &li.bd_intermission, FALSE
284 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
285 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
290 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
291 &li.bd_pal_timing, FALSE
296 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
297 &li.bd_cycle_delay_ms, 200
302 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
303 &li.bd_cycle_delay_c64, 0
308 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
309 &li.bd_hatching_delay_cycles, 21
314 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
315 &li.bd_hatching_delay_seconds, 2
320 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
321 &li.bd_line_shifting_borders, FALSE
326 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
327 &li.bd_wraparound_objects, FALSE
337 static struct LevelFileConfigInfo chunk_config_ELEM[] =
339 // (these values are the same for each player)
342 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
343 &li.block_last_field, FALSE // default case for EM levels
347 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
348 &li.sp_block_last_field, TRUE // default case for SP levels
352 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
353 &li.instant_relocation, FALSE
357 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
358 &li.can_pass_to_walkable, FALSE
362 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
363 &li.block_snap_field, TRUE
367 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
368 &li.continuous_snapping, TRUE
372 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
373 &li.shifted_relocation, FALSE
377 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
378 &li.lazy_relocation, FALSE
382 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
383 &li.finish_dig_collect, TRUE
387 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
388 &li.keep_walkable_ce, FALSE
391 // (these values are different for each player)
394 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
395 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
399 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
400 &li.initial_player_gravity[0], FALSE
404 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
405 &li.use_start_element[0], FALSE
409 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
410 &li.start_element[0], EL_PLAYER_1
414 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
415 &li.use_artwork_element[0], FALSE
419 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
420 &li.artwork_element[0], EL_PLAYER_1
424 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
425 &li.use_explosion_element[0], FALSE
429 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
430 &li.explosion_element[0], EL_PLAYER_1
434 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
435 &li.use_initial_inventory[0], FALSE
439 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
440 &li.initial_inventory_size[0], 1
444 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
445 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
446 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
451 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
452 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
456 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
457 &li.initial_player_gravity[1], FALSE
461 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
462 &li.use_start_element[1], FALSE
466 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
467 &li.start_element[1], EL_PLAYER_2
471 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
472 &li.use_artwork_element[1], FALSE
476 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
477 &li.artwork_element[1], EL_PLAYER_2
481 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
482 &li.use_explosion_element[1], FALSE
486 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
487 &li.explosion_element[1], EL_PLAYER_2
491 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
492 &li.use_initial_inventory[1], FALSE
496 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
497 &li.initial_inventory_size[1], 1
501 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
502 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
503 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
508 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
509 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
513 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
514 &li.initial_player_gravity[2], FALSE
518 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
519 &li.use_start_element[2], FALSE
523 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
524 &li.start_element[2], EL_PLAYER_3
528 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
529 &li.use_artwork_element[2], FALSE
533 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
534 &li.artwork_element[2], EL_PLAYER_3
538 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
539 &li.use_explosion_element[2], FALSE
543 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
544 &li.explosion_element[2], EL_PLAYER_3
548 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
549 &li.use_initial_inventory[2], FALSE
553 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
554 &li.initial_inventory_size[2], 1
558 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
559 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
560 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
565 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
566 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
570 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
571 &li.initial_player_gravity[3], FALSE
575 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
576 &li.use_start_element[3], FALSE
580 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
581 &li.start_element[3], EL_PLAYER_4
585 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
586 &li.use_artwork_element[3], FALSE
590 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
591 &li.artwork_element[3], EL_PLAYER_4
595 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
596 &li.use_explosion_element[3], FALSE
600 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
601 &li.explosion_element[3], EL_PLAYER_4
605 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
606 &li.use_initial_inventory[3], FALSE
610 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
611 &li.initial_inventory_size[3], 1
615 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
616 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
617 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
620 // (these values are only valid for BD style levels)
623 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
624 &li.bd_diagonal_movements, FALSE
629 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
630 &li.score[SC_DIAMOND_EXTRA], 20
633 // (the following values are related to various game elements)
637 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
638 &li.score[SC_EMERALD], 10
643 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
644 &li.score[SC_DIAMOND], 10
649 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
650 &li.score[SC_BUG], 10
655 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
656 &li.score[SC_SPACESHIP], 10
661 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
662 &li.score[SC_PACMAN], 10
667 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
668 &li.score[SC_NUT], 10
673 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
674 &li.score[SC_DYNAMITE], 10
679 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
680 &li.score[SC_KEY], 10
685 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
686 &li.score[SC_PEARL], 10
691 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
692 &li.score[SC_CRYSTAL], 10
697 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
698 &li.amoeba_content, EL_DIAMOND
702 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
707 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
708 &li.grow_into_diggable, TRUE
713 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
714 &li.yamyam_content, EL_ROCK, NULL,
715 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
719 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
720 &li.score[SC_YAMYAM], 10
725 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
726 &li.score[SC_ROBOT], 10
730 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
736 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
742 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
743 &li.time_magic_wall, 10
748 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
749 &li.game_of_life[0], 2
753 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
754 &li.game_of_life[1], 3
758 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
759 &li.game_of_life[2], 3
763 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
764 &li.game_of_life[3], 3
768 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
769 &li.use_life_bugs, FALSE
774 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
779 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
784 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
789 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
794 EL_TIMEGATE_SWITCH, -1,
795 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
796 &li.time_timegate, 10
800 EL_LIGHT_SWITCH_ACTIVE, -1,
801 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
806 EL_SHIELD_NORMAL, -1,
807 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
808 &li.shield_normal_time, 10
811 EL_SHIELD_NORMAL, -1,
812 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
813 &li.score[SC_SHIELD], 10
817 EL_SHIELD_DEADLY, -1,
818 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
819 &li.shield_deadly_time, 10
822 EL_SHIELD_DEADLY, -1,
823 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
824 &li.score[SC_SHIELD], 10
829 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
834 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
835 &li.extra_time_score, 10
839 EL_TIME_ORB_FULL, -1,
840 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
841 &li.time_orb_time, 10
844 EL_TIME_ORB_FULL, -1,
845 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
846 &li.use_time_orb_bug, FALSE
851 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
852 &li.use_spring_bug, FALSE
857 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
858 &li.android_move_time, 10
862 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
863 &li.android_clone_time, 10
866 EL_EMC_ANDROID, SAVE_CONF_NEVER,
867 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
868 &li.android_clone_element[0], EL_EMPTY, NULL,
869 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
873 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
874 &li.android_clone_element[0], EL_EMPTY, NULL,
875 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
880 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
885 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
890 EL_EMC_MAGNIFIER, -1,
891 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
892 &li.magnify_score, 10
895 EL_EMC_MAGNIFIER, -1,
896 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
901 EL_EMC_MAGIC_BALL, -1,
902 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
906 EL_EMC_MAGIC_BALL, -1,
907 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
908 &li.ball_random, FALSE
911 EL_EMC_MAGIC_BALL, -1,
912 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
913 &li.ball_active_initial, FALSE
916 EL_EMC_MAGIC_BALL, -1,
917 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
918 &li.ball_content, EL_EMPTY, NULL,
919 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
923 EL_SOKOBAN_FIELD_EMPTY, -1,
924 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
925 &li.sb_fields_needed, TRUE
929 EL_SOKOBAN_OBJECT, -1,
930 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
931 &li.sb_objects_needed, TRUE
936 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
937 &li.mm_laser_red, FALSE
941 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
942 &li.mm_laser_green, FALSE
946 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
947 &li.mm_laser_blue, TRUE
952 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
953 &li.df_laser_red, TRUE
957 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
958 &li.df_laser_green, TRUE
962 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
963 &li.df_laser_blue, FALSE
967 EL_MM_FUSE_ACTIVE, -1,
968 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
973 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
979 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
984 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
985 &li.mm_ball_choice_mode, ANIM_RANDOM
989 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
990 &li.mm_ball_content, EL_EMPTY, NULL,
991 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
995 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
996 &li.rotate_mm_ball_content, TRUE
1000 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1001 &li.explode_mm_ball, FALSE
1005 EL_MM_STEEL_BLOCK, -1,
1006 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1007 &li.mm_time_block, 75
1010 EL_MM_LIGHTBALL, -1,
1011 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1012 &li.score[SC_ELEM_BONUS], 10
1022 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1026 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1027 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1031 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1032 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1037 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1038 &xx_envelope.autowrap, FALSE
1042 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1043 &xx_envelope.centered, FALSE
1048 TYPE_STRING, CONF_VALUE_BYTES(1),
1049 &xx_envelope.text, -1, NULL,
1050 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1051 &xx_default_string_empty[0]
1061 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1065 TYPE_STRING, CONF_VALUE_BYTES(1),
1066 &xx_ei.description[0], -1,
1067 &yy_ei.description[0],
1068 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1069 &xx_default_description[0]
1074 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1075 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1076 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1078 #if ENABLE_RESERVED_CODE
1079 // (reserved for later use)
1082 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1083 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1084 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1090 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1091 &xx_ei.use_gfx_element, FALSE,
1092 &yy_ei.use_gfx_element
1096 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1097 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1098 &yy_ei.gfx_element_initial
1103 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1104 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1105 &yy_ei.access_direction
1110 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1111 &xx_ei.collect_score_initial, 10,
1112 &yy_ei.collect_score_initial
1116 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1117 &xx_ei.collect_count_initial, 1,
1118 &yy_ei.collect_count_initial
1123 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1124 &xx_ei.ce_value_fixed_initial, 0,
1125 &yy_ei.ce_value_fixed_initial
1129 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1130 &xx_ei.ce_value_random_initial, 0,
1131 &yy_ei.ce_value_random_initial
1135 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1136 &xx_ei.use_last_ce_value, FALSE,
1137 &yy_ei.use_last_ce_value
1142 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1143 &xx_ei.push_delay_fixed, 8,
1144 &yy_ei.push_delay_fixed
1148 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1149 &xx_ei.push_delay_random, 8,
1150 &yy_ei.push_delay_random
1154 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1155 &xx_ei.drop_delay_fixed, 0,
1156 &yy_ei.drop_delay_fixed
1160 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1161 &xx_ei.drop_delay_random, 0,
1162 &yy_ei.drop_delay_random
1166 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1167 &xx_ei.move_delay_fixed, 0,
1168 &yy_ei.move_delay_fixed
1172 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1173 &xx_ei.move_delay_random, 0,
1174 &yy_ei.move_delay_random
1178 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1179 &xx_ei.step_delay_fixed, 0,
1180 &yy_ei.step_delay_fixed
1184 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1185 &xx_ei.step_delay_random, 0,
1186 &yy_ei.step_delay_random
1191 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1192 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1197 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1198 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1199 &yy_ei.move_direction_initial
1203 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1204 &xx_ei.move_stepsize, TILEX / 8,
1205 &yy_ei.move_stepsize
1210 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1211 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1212 &yy_ei.move_enter_element
1216 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1217 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1218 &yy_ei.move_leave_element
1222 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1223 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1224 &yy_ei.move_leave_type
1229 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1230 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1231 &yy_ei.slippery_type
1236 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1237 &xx_ei.explosion_type, EXPLODES_3X3,
1238 &yy_ei.explosion_type
1242 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1243 &xx_ei.explosion_delay, 16,
1244 &yy_ei.explosion_delay
1248 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1249 &xx_ei.ignition_delay, 8,
1250 &yy_ei.ignition_delay
1255 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1256 &xx_ei.content, EL_EMPTY_SPACE,
1258 &xx_num_contents, 1, 1
1261 // ---------- "num_change_pages" must be the last entry ---------------------
1264 -1, SAVE_CONF_ALWAYS,
1265 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1266 &xx_ei.num_change_pages, 1,
1267 &yy_ei.num_change_pages
1278 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1280 // ---------- "current_change_page" must be the first entry -----------------
1283 -1, SAVE_CONF_ALWAYS,
1284 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1285 &xx_current_change_page, -1
1288 // ---------- (the remaining entries can be in any order) -------------------
1292 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1293 &xx_change.can_change, FALSE
1298 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1299 &xx_event_bits[0], 0
1303 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1304 &xx_event_bits[1], 0
1309 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1310 &xx_change.trigger_player, CH_PLAYER_ANY
1314 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1315 &xx_change.trigger_side, CH_SIDE_ANY
1319 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1320 &xx_change.trigger_page, CH_PAGE_ANY
1325 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1326 &xx_change.target_element, EL_EMPTY_SPACE
1331 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1332 &xx_change.delay_fixed, 0
1336 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1337 &xx_change.delay_random, 0
1341 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1342 &xx_change.delay_frames, FRAMES_PER_SECOND
1347 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1348 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1353 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1354 &xx_change.explode, FALSE
1358 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1359 &xx_change.use_target_content, FALSE
1363 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1364 &xx_change.only_if_complete, FALSE
1368 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1369 &xx_change.use_random_replace, FALSE
1373 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1374 &xx_change.random_percentage, 100
1378 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1379 &xx_change.replace_when, CP_WHEN_EMPTY
1384 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1385 &xx_change.has_action, FALSE
1389 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1390 &xx_change.action_type, CA_NO_ACTION
1394 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1395 &xx_change.action_mode, CA_MODE_UNDEFINED
1399 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1400 &xx_change.action_arg, CA_ARG_UNDEFINED
1405 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1406 &xx_change.action_element, EL_EMPTY_SPACE
1411 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1412 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1413 &xx_num_contents, 1, 1
1423 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1427 TYPE_STRING, CONF_VALUE_BYTES(1),
1428 &xx_ei.description[0], -1, NULL,
1429 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1430 &xx_default_description[0]
1435 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1436 &xx_ei.use_gfx_element, FALSE
1440 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1441 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1446 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1447 &xx_group.choice_mode, ANIM_RANDOM
1452 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1453 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1454 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1464 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1468 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1469 &xx_ei.use_gfx_element, FALSE
1473 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1474 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1484 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1488 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1489 &li.block_snap_field, TRUE
1493 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1494 &li.continuous_snapping, TRUE
1498 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1499 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1503 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1504 &li.use_start_element[0], FALSE
1508 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1509 &li.start_element[0], EL_PLAYER_1
1513 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1514 &li.use_artwork_element[0], FALSE
1518 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1519 &li.artwork_element[0], EL_PLAYER_1
1523 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1524 &li.use_explosion_element[0], FALSE
1528 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1529 &li.explosion_element[0], EL_PLAYER_1
1544 filetype_id_list[] =
1546 { LEVEL_FILE_TYPE_RND, "RND" },
1547 { LEVEL_FILE_TYPE_BD, "BD" },
1548 { LEVEL_FILE_TYPE_EM, "EM" },
1549 { LEVEL_FILE_TYPE_SP, "SP" },
1550 { LEVEL_FILE_TYPE_DX, "DX" },
1551 { LEVEL_FILE_TYPE_SB, "SB" },
1552 { LEVEL_FILE_TYPE_DC, "DC" },
1553 { LEVEL_FILE_TYPE_MM, "MM" },
1554 { LEVEL_FILE_TYPE_MM, "DF" },
1559 // ============================================================================
1560 // level file functions
1561 // ============================================================================
1563 static boolean check_special_flags(char *flag)
1565 if (strEqual(options.special_flags, flag) ||
1566 strEqual(leveldir_current->special_flags, flag))
1572 static struct DateInfo getCurrentDate(void)
1574 time_t epoch_seconds = time(NULL);
1575 struct tm *now = localtime(&epoch_seconds);
1576 struct DateInfo date;
1578 date.year = now->tm_year + 1900;
1579 date.month = now->tm_mon + 1;
1580 date.day = now->tm_mday;
1582 date.src = DATE_SRC_CLOCK;
1587 static void resetEventFlags(struct ElementChangeInfo *change)
1591 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1592 change->has_event[i] = FALSE;
1595 static void resetEventBits(void)
1599 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1600 xx_event_bits[i] = 0;
1603 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1607 /* important: only change event flag if corresponding event bit is set
1608 (this is because all xx_event_bits[] values are loaded separately,
1609 and all xx_event_bits[] values are set back to zero before loading
1610 another value xx_event_bits[x] (each value representing 32 flags)) */
1612 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1613 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1614 change->has_event[i] = TRUE;
1617 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1621 /* in contrast to the above function setEventFlagsFromEventBits(), it
1622 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1623 depending on the corresponding change->has_event[i] values here, as
1624 all xx_event_bits[] values are reset in resetEventBits() before */
1626 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1627 if (change->has_event[i])
1628 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1631 static char *getDefaultElementDescription(struct ElementInfo *ei)
1633 static char description[MAX_ELEMENT_NAME_LEN + 1];
1634 char *default_description = (ei->custom_description != NULL ?
1635 ei->custom_description :
1636 ei->editor_description);
1639 // always start with reliable default values
1640 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1641 description[i] = '\0';
1643 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1644 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1646 return &description[0];
1649 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1651 char *default_description = getDefaultElementDescription(ei);
1654 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1655 ei->description[i] = default_description[i];
1658 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1662 for (i = 0; conf[i].data_type != -1; i++)
1664 int default_value = conf[i].default_value;
1665 int data_type = conf[i].data_type;
1666 int conf_type = conf[i].conf_type;
1667 int byte_mask = conf_type & CONF_MASK_BYTES;
1669 if (byte_mask == CONF_MASK_MULTI_BYTES)
1671 int default_num_entities = conf[i].default_num_entities;
1672 int max_num_entities = conf[i].max_num_entities;
1674 *(int *)(conf[i].num_entities) = default_num_entities;
1676 if (data_type == TYPE_STRING)
1678 char *default_string = conf[i].default_string;
1679 char *string = (char *)(conf[i].value);
1681 strncpy(string, default_string, max_num_entities);
1683 else if (data_type == TYPE_ELEMENT_LIST)
1685 int *element_array = (int *)(conf[i].value);
1688 for (j = 0; j < max_num_entities; j++)
1689 element_array[j] = default_value;
1691 else if (data_type == TYPE_CONTENT_LIST)
1693 struct Content *content = (struct Content *)(conf[i].value);
1696 for (c = 0; c < max_num_entities; c++)
1697 for (y = 0; y < 3; y++)
1698 for (x = 0; x < 3; x++)
1699 content[c].e[x][y] = default_value;
1702 else // constant size configuration data (1, 2 or 4 bytes)
1704 if (data_type == TYPE_BOOLEAN)
1705 *(boolean *)(conf[i].value) = default_value;
1707 *(int *) (conf[i].value) = default_value;
1712 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1716 for (i = 0; conf[i].data_type != -1; i++)
1718 int data_type = conf[i].data_type;
1719 int conf_type = conf[i].conf_type;
1720 int byte_mask = conf_type & CONF_MASK_BYTES;
1722 if (byte_mask == CONF_MASK_MULTI_BYTES)
1724 int max_num_entities = conf[i].max_num_entities;
1726 if (data_type == TYPE_STRING)
1728 char *string = (char *)(conf[i].value);
1729 char *string_copy = (char *)(conf[i].value_copy);
1731 strncpy(string_copy, string, max_num_entities);
1733 else if (data_type == TYPE_ELEMENT_LIST)
1735 int *element_array = (int *)(conf[i].value);
1736 int *element_array_copy = (int *)(conf[i].value_copy);
1739 for (j = 0; j < max_num_entities; j++)
1740 element_array_copy[j] = element_array[j];
1742 else if (data_type == TYPE_CONTENT_LIST)
1744 struct Content *content = (struct Content *)(conf[i].value);
1745 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1748 for (c = 0; c < max_num_entities; c++)
1749 for (y = 0; y < 3; y++)
1750 for (x = 0; x < 3; x++)
1751 content_copy[c].e[x][y] = content[c].e[x][y];
1754 else // constant size configuration data (1, 2 or 4 bytes)
1756 if (data_type == TYPE_BOOLEAN)
1757 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1759 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1764 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1768 xx_ei = *ei_from; // copy element data into temporary buffer
1769 yy_ei = *ei_to; // copy element data into temporary buffer
1771 copyConfigFromConfigList(chunk_config_CUSX_base);
1776 // ---------- reinitialize and copy change pages ----------
1778 ei_to->num_change_pages = ei_from->num_change_pages;
1779 ei_to->current_change_page = ei_from->current_change_page;
1781 setElementChangePages(ei_to, ei_to->num_change_pages);
1783 for (i = 0; i < ei_to->num_change_pages; i++)
1784 ei_to->change_page[i] = ei_from->change_page[i];
1786 // ---------- copy group element info ----------
1787 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1788 *ei_to->group = *ei_from->group;
1790 // mark this custom element as modified
1791 ei_to->modified_settings = TRUE;
1794 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1796 int change_page_size = sizeof(struct ElementChangeInfo);
1798 ei->num_change_pages = MAX(1, change_pages);
1801 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1803 if (ei->current_change_page >= ei->num_change_pages)
1804 ei->current_change_page = ei->num_change_pages - 1;
1806 ei->change = &ei->change_page[ei->current_change_page];
1809 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1811 xx_change = *change; // copy change data into temporary buffer
1813 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1815 *change = xx_change;
1817 resetEventFlags(change);
1819 change->direct_action = 0;
1820 change->other_action = 0;
1822 change->pre_change_function = NULL;
1823 change->change_function = NULL;
1824 change->post_change_function = NULL;
1827 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1831 li = *level; // copy level data into temporary buffer
1832 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1833 *level = li; // copy temporary buffer back to level data
1835 setLevelInfoToDefaults_BD();
1836 setLevelInfoToDefaults_EM();
1837 setLevelInfoToDefaults_SP();
1838 setLevelInfoToDefaults_MM();
1840 level->native_bd_level = &native_bd_level;
1841 level->native_em_level = &native_em_level;
1842 level->native_sp_level = &native_sp_level;
1843 level->native_mm_level = &native_mm_level;
1845 level->file_version = FILE_VERSION_ACTUAL;
1846 level->game_version = GAME_VERSION_ACTUAL;
1848 level->creation_date = getCurrentDate();
1850 level->encoding_16bit_field = TRUE;
1851 level->encoding_16bit_yamyam = TRUE;
1852 level->encoding_16bit_amoeba = TRUE;
1854 // clear level name and level author string buffers
1855 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1856 level->name[i] = '\0';
1857 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1858 level->author[i] = '\0';
1860 // set level name and level author to default values
1861 strcpy(level->name, NAMELESS_LEVEL_NAME);
1862 strcpy(level->author, ANONYMOUS_NAME);
1864 // set level playfield to playable default level with player and exit
1865 for (x = 0; x < MAX_LEV_FIELDX; x++)
1866 for (y = 0; y < MAX_LEV_FIELDY; y++)
1867 level->field[x][y] = EL_SAND;
1869 level->field[0][0] = EL_PLAYER_1;
1870 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1872 BorderElement = EL_STEELWALL;
1874 // detect custom elements when loading them
1875 level->file_has_custom_elements = FALSE;
1877 // set all bug compatibility flags to "false" => do not emulate this bug
1878 level->use_action_after_change_bug = FALSE;
1880 if (leveldir_current)
1882 // try to determine better author name than 'anonymous'
1883 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1885 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1886 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1890 switch (LEVELCLASS(leveldir_current))
1892 case LEVELCLASS_TUTORIAL:
1893 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1896 case LEVELCLASS_CONTRIB:
1897 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1898 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1901 case LEVELCLASS_PRIVATE:
1902 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1903 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1907 // keep default value
1914 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1916 static boolean clipboard_elements_initialized = FALSE;
1919 InitElementPropertiesStatic();
1921 li = *level; // copy level data into temporary buffer
1922 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1923 *level = li; // copy temporary buffer back to level data
1925 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1928 struct ElementInfo *ei = &element_info[element];
1930 if (element == EL_MM_GRAY_BALL)
1932 struct LevelInfo_MM *level_mm = level->native_mm_level;
1935 for (j = 0; j < level->num_mm_ball_contents; j++)
1936 level->mm_ball_content[j] =
1937 map_element_MM_to_RND(level_mm->ball_content[j]);
1940 // never initialize clipboard elements after the very first time
1941 // (to be able to use clipboard elements between several levels)
1942 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1945 if (IS_ENVELOPE(element))
1947 int envelope_nr = element - EL_ENVELOPE_1;
1949 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1951 level->envelope[envelope_nr] = xx_envelope;
1954 if (IS_CUSTOM_ELEMENT(element) ||
1955 IS_GROUP_ELEMENT(element) ||
1956 IS_INTERNAL_ELEMENT(element))
1958 xx_ei = *ei; // copy element data into temporary buffer
1960 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1965 setElementChangePages(ei, 1);
1966 setElementChangeInfoToDefaults(ei->change);
1968 if (IS_CUSTOM_ELEMENT(element) ||
1969 IS_GROUP_ELEMENT(element))
1971 setElementDescriptionToDefault(ei);
1973 ei->modified_settings = FALSE;
1976 if (IS_CUSTOM_ELEMENT(element) ||
1977 IS_INTERNAL_ELEMENT(element))
1979 // internal values used in level editor
1981 ei->access_type = 0;
1982 ei->access_layer = 0;
1983 ei->access_protected = 0;
1984 ei->walk_to_action = 0;
1985 ei->smash_targets = 0;
1988 ei->can_explode_by_fire = FALSE;
1989 ei->can_explode_smashed = FALSE;
1990 ei->can_explode_impact = FALSE;
1992 ei->current_change_page = 0;
1995 if (IS_GROUP_ELEMENT(element) ||
1996 IS_INTERNAL_ELEMENT(element))
1998 struct ElementGroupInfo *group;
2000 // initialize memory for list of elements in group
2001 if (ei->group == NULL)
2002 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2006 xx_group = *group; // copy group data into temporary buffer
2008 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2013 if (IS_EMPTY_ELEMENT(element) ||
2014 IS_INTERNAL_ELEMENT(element))
2016 xx_ei = *ei; // copy element data into temporary buffer
2018 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2024 clipboard_elements_initialized = TRUE;
2027 static void setLevelInfoToDefaults(struct LevelInfo *level,
2028 boolean level_info_only,
2029 boolean reset_file_status)
2031 setLevelInfoToDefaults_Level(level);
2033 if (!level_info_only)
2034 setLevelInfoToDefaults_Elements(level);
2036 if (reset_file_status)
2038 level->no_valid_file = FALSE;
2039 level->no_level_file = FALSE;
2042 level->changed = FALSE;
2045 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2047 level_file_info->nr = 0;
2048 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2049 level_file_info->packed = FALSE;
2051 setString(&level_file_info->basename, NULL);
2052 setString(&level_file_info->filename, NULL);
2055 int getMappedElement_SB(int, boolean);
2057 static void ActivateLevelTemplate(void)
2061 if (check_special_flags("load_xsb_to_ces"))
2063 // fill smaller playfields with padding "beyond border wall" elements
2064 if (level.fieldx < level_template.fieldx ||
2065 level.fieldy < level_template.fieldy)
2067 short field[level.fieldx][level.fieldy];
2068 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2069 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2070 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2071 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2073 // copy old playfield (which is smaller than the visible area)
2074 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2075 field[x][y] = level.field[x][y];
2077 // fill new, larger playfield with "beyond border wall" elements
2078 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2079 level.field[x][y] = getMappedElement_SB('_', TRUE);
2081 // copy the old playfield to the middle of the new playfield
2082 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2083 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2085 level.fieldx = new_fieldx;
2086 level.fieldy = new_fieldy;
2090 // Currently there is no special action needed to activate the template
2091 // data, because 'element_info' property settings overwrite the original
2092 // level data, while all other variables do not change.
2094 // Exception: 'from_level_template' elements in the original level playfield
2095 // are overwritten with the corresponding elements at the same position in
2096 // playfield from the level template.
2098 for (x = 0; x < level.fieldx; x++)
2099 for (y = 0; y < level.fieldy; y++)
2100 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2101 level.field[x][y] = level_template.field[x][y];
2103 if (check_special_flags("load_xsb_to_ces"))
2105 struct LevelInfo level_backup = level;
2107 // overwrite all individual level settings from template level settings
2108 level = level_template;
2110 // restore level file info
2111 level.file_info = level_backup.file_info;
2113 // restore playfield size
2114 level.fieldx = level_backup.fieldx;
2115 level.fieldy = level_backup.fieldy;
2117 // restore playfield content
2118 for (x = 0; x < level.fieldx; x++)
2119 for (y = 0; y < level.fieldy; y++)
2120 level.field[x][y] = level_backup.field[x][y];
2122 // restore name and author from individual level
2123 strcpy(level.name, level_backup.name);
2124 strcpy(level.author, level_backup.author);
2126 // restore flag "use_custom_template"
2127 level.use_custom_template = level_backup.use_custom_template;
2131 static boolean checkForPackageFromBasename_BD(char *basename)
2133 // check for native BD level file extensions
2134 if (!strSuffixLower(basename, ".bd") &&
2135 !strSuffixLower(basename, ".bdr") &&
2136 !strSuffixLower(basename, ".brc") &&
2137 !strSuffixLower(basename, ".gds"))
2140 // check for standard single-level BD files (like "001.bd")
2141 if (strSuffixLower(basename, ".bd") &&
2142 strlen(basename) == 6 &&
2143 basename[0] >= '0' && basename[0] <= '9' &&
2144 basename[1] >= '0' && basename[1] <= '9' &&
2145 basename[2] >= '0' && basename[2] <= '9')
2148 // this is a level package in native BD file format
2152 static char *getLevelFilenameFromBasename(char *basename)
2154 static char *filename = NULL;
2156 checked_free(filename);
2158 filename = getPath2(getCurrentLevelDir(), basename);
2163 static int getFileTypeFromBasename(char *basename)
2165 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2167 static char *filename = NULL;
2168 struct stat file_status;
2170 // ---------- try to determine file type from filename ----------
2172 // check for typical filename of a Supaplex level package file
2173 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2174 return LEVEL_FILE_TYPE_SP;
2176 // check for typical filename of a Diamond Caves II level package file
2177 if (strSuffixLower(basename, ".dc") ||
2178 strSuffixLower(basename, ".dc2"))
2179 return LEVEL_FILE_TYPE_DC;
2181 // check for typical filename of a Sokoban level package file
2182 if (strSuffixLower(basename, ".xsb") &&
2183 strchr(basename, '%') == NULL)
2184 return LEVEL_FILE_TYPE_SB;
2186 // check for typical filename of a Boulder Dash (GDash) level package file
2187 if (checkForPackageFromBasename_BD(basename))
2188 return LEVEL_FILE_TYPE_BD;
2190 // ---------- try to determine file type from filesize ----------
2192 checked_free(filename);
2193 filename = getPath2(getCurrentLevelDir(), basename);
2195 if (stat(filename, &file_status) == 0)
2197 // check for typical filesize of a Supaplex level package file
2198 if (file_status.st_size == 170496)
2199 return LEVEL_FILE_TYPE_SP;
2202 return LEVEL_FILE_TYPE_UNKNOWN;
2205 static int getFileTypeFromMagicBytes(char *filename, int type)
2209 if ((file = openFile(filename, MODE_READ)))
2211 char chunk_name[CHUNK_ID_LEN + 1];
2213 getFileChunkBE(file, chunk_name, NULL);
2215 if (strEqual(chunk_name, "MMII") ||
2216 strEqual(chunk_name, "MIRR"))
2217 type = LEVEL_FILE_TYPE_MM;
2225 static boolean checkForPackageFromBasename(char *basename)
2227 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2228 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2230 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2233 static char *getSingleLevelBasenameExt(int nr, char *extension)
2235 static char basename[MAX_FILENAME_LEN];
2238 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2240 sprintf(basename, "%03d.%s", nr, extension);
2245 static char *getSingleLevelBasename(int nr)
2247 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2250 static char *getPackedLevelBasename(int type)
2252 static char basename[MAX_FILENAME_LEN];
2253 char *directory = getCurrentLevelDir();
2255 DirectoryEntry *dir_entry;
2257 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2259 if ((dir = openDirectory(directory)) == NULL)
2261 Warn("cannot read current level directory '%s'", directory);
2266 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2268 char *entry_basename = dir_entry->basename;
2269 int entry_type = getFileTypeFromBasename(entry_basename);
2271 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2273 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2276 strcpy(basename, entry_basename);
2283 closeDirectory(dir);
2288 static char *getSingleLevelFilename(int nr)
2290 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2293 #if ENABLE_UNUSED_CODE
2294 static char *getPackedLevelFilename(int type)
2296 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2300 char *getDefaultLevelFilename(int nr)
2302 return getSingleLevelFilename(nr);
2305 #if ENABLE_UNUSED_CODE
2306 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2310 lfi->packed = FALSE;
2312 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2313 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2317 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2318 int type, char *format, ...)
2320 static char basename[MAX_FILENAME_LEN];
2323 va_start(ap, format);
2324 vsprintf(basename, format, ap);
2328 lfi->packed = FALSE;
2330 setString(&lfi->basename, basename);
2331 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2334 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2340 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2341 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2344 static int getFiletypeFromID(char *filetype_id)
2346 char *filetype_id_lower;
2347 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2350 if (filetype_id == NULL)
2351 return LEVEL_FILE_TYPE_UNKNOWN;
2353 filetype_id_lower = getStringToLower(filetype_id);
2355 for (i = 0; filetype_id_list[i].id != NULL; i++)
2357 char *id_lower = getStringToLower(filetype_id_list[i].id);
2359 if (strEqual(filetype_id_lower, id_lower))
2360 filetype = filetype_id_list[i].filetype;
2364 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2368 free(filetype_id_lower);
2373 char *getLocalLevelTemplateFilename(void)
2375 return getDefaultLevelFilename(-1);
2378 char *getGlobalLevelTemplateFilename(void)
2380 // global variable "leveldir_current" must be modified in the loop below
2381 LevelDirTree *leveldir_current_last = leveldir_current;
2382 char *filename = NULL;
2384 // check for template level in path from current to topmost tree node
2386 while (leveldir_current != NULL)
2388 filename = getDefaultLevelFilename(-1);
2390 if (fileExists(filename))
2393 leveldir_current = leveldir_current->node_parent;
2396 // restore global variable "leveldir_current" modified in above loop
2397 leveldir_current = leveldir_current_last;
2402 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2406 // special case: level number is negative => check for level template file
2409 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2410 getSingleLevelBasename(-1));
2412 // replace local level template filename with global template filename
2413 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2415 // no fallback if template file not existing
2419 // special case: check for file name/pattern specified in "levelinfo.conf"
2420 if (leveldir_current->level_filename != NULL)
2422 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2424 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2425 leveldir_current->level_filename, nr);
2427 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2429 if (fileExists(lfi->filename))
2432 else if (leveldir_current->level_filetype != NULL)
2434 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2436 // check for specified native level file with standard file name
2437 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2438 "%03d.%s", nr, LEVELFILE_EXTENSION);
2439 if (fileExists(lfi->filename))
2443 // check for native Rocks'n'Diamonds level file
2444 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2445 "%03d.%s", nr, LEVELFILE_EXTENSION);
2446 if (fileExists(lfi->filename))
2449 // check for native Boulder Dash level file
2450 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2451 if (fileExists(lfi->filename))
2454 // check for Emerald Mine level file (V1)
2455 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2456 'a' + (nr / 10) % 26, '0' + nr % 10);
2457 if (fileExists(lfi->filename))
2459 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2460 'A' + (nr / 10) % 26, '0' + nr % 10);
2461 if (fileExists(lfi->filename))
2464 // check for Emerald Mine level file (V2 to V5)
2465 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2466 if (fileExists(lfi->filename))
2469 // check for Emerald Mine level file (V6 / single mode)
2470 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2471 if (fileExists(lfi->filename))
2473 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2474 if (fileExists(lfi->filename))
2477 // check for Emerald Mine level file (V6 / teamwork mode)
2478 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2479 if (fileExists(lfi->filename))
2481 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2482 if (fileExists(lfi->filename))
2485 // check for various packed level file formats
2486 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2487 if (fileExists(lfi->filename))
2490 // no known level file found -- use default values (and fail later)
2491 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2492 "%03d.%s", nr, LEVELFILE_EXTENSION);
2495 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2497 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2498 lfi->type = getFileTypeFromBasename(lfi->basename);
2500 if (lfi->type == LEVEL_FILE_TYPE_RND)
2501 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2504 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2506 // always start with reliable default values
2507 setFileInfoToDefaults(level_file_info);
2509 level_file_info->nr = nr; // set requested level number
2511 determineLevelFileInfo_Filename(level_file_info);
2512 determineLevelFileInfo_Filetype(level_file_info);
2515 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2516 struct LevelFileInfo *lfi_to)
2518 lfi_to->nr = lfi_from->nr;
2519 lfi_to->type = lfi_from->type;
2520 lfi_to->packed = lfi_from->packed;
2522 setString(&lfi_to->basename, lfi_from->basename);
2523 setString(&lfi_to->filename, lfi_from->filename);
2526 // ----------------------------------------------------------------------------
2527 // functions for loading R'n'D level
2528 // ----------------------------------------------------------------------------
2530 int getMappedElement(int element)
2532 // remap some (historic, now obsolete) elements
2536 case EL_PLAYER_OBSOLETE:
2537 element = EL_PLAYER_1;
2540 case EL_KEY_OBSOLETE:
2544 case EL_EM_KEY_1_FILE_OBSOLETE:
2545 element = EL_EM_KEY_1;
2548 case EL_EM_KEY_2_FILE_OBSOLETE:
2549 element = EL_EM_KEY_2;
2552 case EL_EM_KEY_3_FILE_OBSOLETE:
2553 element = EL_EM_KEY_3;
2556 case EL_EM_KEY_4_FILE_OBSOLETE:
2557 element = EL_EM_KEY_4;
2560 case EL_ENVELOPE_OBSOLETE:
2561 element = EL_ENVELOPE_1;
2569 if (element >= NUM_FILE_ELEMENTS)
2571 Warn("invalid level element %d", element);
2573 element = EL_UNKNOWN;
2581 static int getMappedElementByVersion(int element, int game_version)
2583 // remap some elements due to certain game version
2585 if (game_version <= VERSION_IDENT(2,2,0,0))
2587 // map game font elements
2588 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2589 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2590 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2591 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2594 if (game_version < VERSION_IDENT(3,0,0,0))
2596 // map Supaplex gravity tube elements
2597 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2598 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2599 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2600 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2607 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2609 level->file_version = getFileVersion(file);
2610 level->game_version = getFileVersion(file);
2615 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2617 level->creation_date.year = getFile16BitBE(file);
2618 level->creation_date.month = getFile8Bit(file);
2619 level->creation_date.day = getFile8Bit(file);
2621 level->creation_date.src = DATE_SRC_LEVELFILE;
2626 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2628 int initial_player_stepsize;
2629 int initial_player_gravity;
2632 level->fieldx = getFile8Bit(file);
2633 level->fieldy = getFile8Bit(file);
2635 level->time = getFile16BitBE(file);
2636 level->gems_needed = getFile16BitBE(file);
2638 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2639 level->name[i] = getFile8Bit(file);
2640 level->name[MAX_LEVEL_NAME_LEN] = 0;
2642 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2643 level->score[i] = getFile8Bit(file);
2645 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2646 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2647 for (y = 0; y < 3; y++)
2648 for (x = 0; x < 3; x++)
2649 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2651 level->amoeba_speed = getFile8Bit(file);
2652 level->time_magic_wall = getFile8Bit(file);
2653 level->time_wheel = getFile8Bit(file);
2654 level->amoeba_content = getMappedElement(getFile8Bit(file));
2656 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2659 for (i = 0; i < MAX_PLAYERS; i++)
2660 level->initial_player_stepsize[i] = initial_player_stepsize;
2662 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2664 for (i = 0; i < MAX_PLAYERS; i++)
2665 level->initial_player_gravity[i] = initial_player_gravity;
2667 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2668 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2670 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2672 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2673 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2674 level->can_move_into_acid_bits = getFile32BitBE(file);
2675 level->dont_collide_with_bits = getFile8Bit(file);
2677 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2678 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2680 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2681 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2682 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2684 level->game_engine_type = getFile8Bit(file);
2686 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2691 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2695 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2696 level->name[i] = getFile8Bit(file);
2697 level->name[MAX_LEVEL_NAME_LEN] = 0;
2702 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2706 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2707 level->author[i] = getFile8Bit(file);
2708 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2713 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2716 int chunk_size_expected = level->fieldx * level->fieldy;
2718 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2719 stored with 16-bit encoding (and should be twice as big then).
2720 Even worse, playfield data was stored 16-bit when only yamyam content
2721 contained 16-bit elements and vice versa. */
2723 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2724 chunk_size_expected *= 2;
2726 if (chunk_size_expected != chunk_size)
2728 ReadUnusedBytesFromFile(file, chunk_size);
2729 return chunk_size_expected;
2732 for (y = 0; y < level->fieldy; y++)
2733 for (x = 0; x < level->fieldx; x++)
2734 level->field[x][y] =
2735 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2740 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2743 int header_size = 4;
2744 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2745 int chunk_size_expected = header_size + content_size;
2747 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2748 stored with 16-bit encoding (and should be twice as big then).
2749 Even worse, playfield data was stored 16-bit when only yamyam content
2750 contained 16-bit elements and vice versa. */
2752 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2753 chunk_size_expected += content_size;
2755 if (chunk_size_expected != chunk_size)
2757 ReadUnusedBytesFromFile(file, chunk_size);
2758 return chunk_size_expected;
2762 level->num_yamyam_contents = getFile8Bit(file);
2766 // correct invalid number of content fields -- should never happen
2767 if (level->num_yamyam_contents < 1 ||
2768 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2769 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2771 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2772 for (y = 0; y < 3; y++)
2773 for (x = 0; x < 3; x++)
2774 level->yamyam_content[i].e[x][y] =
2775 getMappedElement(level->encoding_16bit_field ?
2776 getFile16BitBE(file) : getFile8Bit(file));
2780 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2785 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2787 element = getMappedElement(getFile16BitBE(file));
2788 num_contents = getFile8Bit(file);
2790 getFile8Bit(file); // content x size (unused)
2791 getFile8Bit(file); // content y size (unused)
2793 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2795 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2796 for (y = 0; y < 3; y++)
2797 for (x = 0; x < 3; x++)
2798 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2800 // correct invalid number of content fields -- should never happen
2801 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2802 num_contents = STD_ELEMENT_CONTENTS;
2804 if (element == EL_YAMYAM)
2806 level->num_yamyam_contents = num_contents;
2808 for (i = 0; i < num_contents; i++)
2809 for (y = 0; y < 3; y++)
2810 for (x = 0; x < 3; x++)
2811 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2813 else if (element == EL_BD_AMOEBA)
2815 level->amoeba_content = content_array[0][0][0];
2819 Warn("cannot load content for element '%d'", element);
2825 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2831 int chunk_size_expected;
2833 element = getMappedElement(getFile16BitBE(file));
2834 if (!IS_ENVELOPE(element))
2835 element = EL_ENVELOPE_1;
2837 envelope_nr = element - EL_ENVELOPE_1;
2839 envelope_len = getFile16BitBE(file);
2841 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2842 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2844 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2846 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2847 if (chunk_size_expected != chunk_size)
2849 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2850 return chunk_size_expected;
2853 for (i = 0; i < envelope_len; i++)
2854 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2859 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2861 int num_changed_custom_elements = getFile16BitBE(file);
2862 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2865 if (chunk_size_expected != chunk_size)
2867 ReadUnusedBytesFromFile(file, chunk_size - 2);
2868 return chunk_size_expected;
2871 for (i = 0; i < num_changed_custom_elements; i++)
2873 int element = getMappedElement(getFile16BitBE(file));
2874 int properties = getFile32BitBE(file);
2876 if (IS_CUSTOM_ELEMENT(element))
2877 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2879 Warn("invalid custom element number %d", element);
2881 // older game versions that wrote level files with CUS1 chunks used
2882 // different default push delay values (not yet stored in level file)
2883 element_info[element].push_delay_fixed = 2;
2884 element_info[element].push_delay_random = 8;
2887 level->file_has_custom_elements = TRUE;
2892 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2894 int num_changed_custom_elements = getFile16BitBE(file);
2895 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2898 if (chunk_size_expected != chunk_size)
2900 ReadUnusedBytesFromFile(file, chunk_size - 2);
2901 return chunk_size_expected;
2904 for (i = 0; i < num_changed_custom_elements; i++)
2906 int element = getMappedElement(getFile16BitBE(file));
2907 int custom_target_element = getMappedElement(getFile16BitBE(file));
2909 if (IS_CUSTOM_ELEMENT(element))
2910 element_info[element].change->target_element = custom_target_element;
2912 Warn("invalid custom element number %d", element);
2915 level->file_has_custom_elements = TRUE;
2920 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2922 int num_changed_custom_elements = getFile16BitBE(file);
2923 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2926 if (chunk_size_expected != chunk_size)
2928 ReadUnusedBytesFromFile(file, chunk_size - 2);
2929 return chunk_size_expected;
2932 for (i = 0; i < num_changed_custom_elements; i++)
2934 int element = getMappedElement(getFile16BitBE(file));
2935 struct ElementInfo *ei = &element_info[element];
2936 unsigned int event_bits;
2938 if (!IS_CUSTOM_ELEMENT(element))
2940 Warn("invalid custom element number %d", element);
2942 element = EL_INTERNAL_DUMMY;
2945 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2946 ei->description[j] = getFile8Bit(file);
2947 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2949 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2951 // some free bytes for future properties and padding
2952 ReadUnusedBytesFromFile(file, 7);
2954 ei->use_gfx_element = getFile8Bit(file);
2955 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2957 ei->collect_score_initial = getFile8Bit(file);
2958 ei->collect_count_initial = getFile8Bit(file);
2960 ei->push_delay_fixed = getFile16BitBE(file);
2961 ei->push_delay_random = getFile16BitBE(file);
2962 ei->move_delay_fixed = getFile16BitBE(file);
2963 ei->move_delay_random = getFile16BitBE(file);
2965 ei->move_pattern = getFile16BitBE(file);
2966 ei->move_direction_initial = getFile8Bit(file);
2967 ei->move_stepsize = getFile8Bit(file);
2969 for (y = 0; y < 3; y++)
2970 for (x = 0; x < 3; x++)
2971 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2973 // bits 0 - 31 of "has_event[]"
2974 event_bits = getFile32BitBE(file);
2975 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2976 if (event_bits & (1u << j))
2977 ei->change->has_event[j] = TRUE;
2979 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2981 ei->change->delay_fixed = getFile16BitBE(file);
2982 ei->change->delay_random = getFile16BitBE(file);
2983 ei->change->delay_frames = getFile16BitBE(file);
2985 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2987 ei->change->explode = getFile8Bit(file);
2988 ei->change->use_target_content = getFile8Bit(file);
2989 ei->change->only_if_complete = getFile8Bit(file);
2990 ei->change->use_random_replace = getFile8Bit(file);
2992 ei->change->random_percentage = getFile8Bit(file);
2993 ei->change->replace_when = getFile8Bit(file);
2995 for (y = 0; y < 3; y++)
2996 for (x = 0; x < 3; x++)
2997 ei->change->target_content.e[x][y] =
2998 getMappedElement(getFile16BitBE(file));
3000 ei->slippery_type = getFile8Bit(file);
3002 // some free bytes for future properties and padding
3003 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3005 // mark that this custom element has been modified
3006 ei->modified_settings = TRUE;
3009 level->file_has_custom_elements = TRUE;
3014 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3016 struct ElementInfo *ei;
3017 int chunk_size_expected;
3021 // ---------- custom element base property values (96 bytes) ----------------
3023 element = getMappedElement(getFile16BitBE(file));
3025 if (!IS_CUSTOM_ELEMENT(element))
3027 Warn("invalid custom element number %d", element);
3029 ReadUnusedBytesFromFile(file, chunk_size - 2);
3034 ei = &element_info[element];
3036 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3037 ei->description[i] = getFile8Bit(file);
3038 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3040 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3042 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3044 ei->num_change_pages = getFile8Bit(file);
3046 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3047 if (chunk_size_expected != chunk_size)
3049 ReadUnusedBytesFromFile(file, chunk_size - 43);
3050 return chunk_size_expected;
3053 ei->ce_value_fixed_initial = getFile16BitBE(file);
3054 ei->ce_value_random_initial = getFile16BitBE(file);
3055 ei->use_last_ce_value = getFile8Bit(file);
3057 ei->use_gfx_element = getFile8Bit(file);
3058 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3060 ei->collect_score_initial = getFile8Bit(file);
3061 ei->collect_count_initial = getFile8Bit(file);
3063 ei->drop_delay_fixed = getFile8Bit(file);
3064 ei->push_delay_fixed = getFile8Bit(file);
3065 ei->drop_delay_random = getFile8Bit(file);
3066 ei->push_delay_random = getFile8Bit(file);
3067 ei->move_delay_fixed = getFile16BitBE(file);
3068 ei->move_delay_random = getFile16BitBE(file);
3070 // bits 0 - 15 of "move_pattern" ...
3071 ei->move_pattern = getFile16BitBE(file);
3072 ei->move_direction_initial = getFile8Bit(file);
3073 ei->move_stepsize = getFile8Bit(file);
3075 ei->slippery_type = getFile8Bit(file);
3077 for (y = 0; y < 3; y++)
3078 for (x = 0; x < 3; x++)
3079 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3081 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3082 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3083 ei->move_leave_type = getFile8Bit(file);
3085 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3086 ei->move_pattern |= (getFile16BitBE(file) << 16);
3088 ei->access_direction = getFile8Bit(file);
3090 ei->explosion_delay = getFile8Bit(file);
3091 ei->ignition_delay = getFile8Bit(file);
3092 ei->explosion_type = getFile8Bit(file);
3094 // some free bytes for future custom property values and padding
3095 ReadUnusedBytesFromFile(file, 1);
3097 // ---------- change page property values (48 bytes) ------------------------
3099 setElementChangePages(ei, ei->num_change_pages);
3101 for (i = 0; i < ei->num_change_pages; i++)
3103 struct ElementChangeInfo *change = &ei->change_page[i];
3104 unsigned int event_bits;
3106 // always start with reliable default values
3107 setElementChangeInfoToDefaults(change);
3109 // bits 0 - 31 of "has_event[]" ...
3110 event_bits = getFile32BitBE(file);
3111 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3112 if (event_bits & (1u << j))
3113 change->has_event[j] = TRUE;
3115 change->target_element = getMappedElement(getFile16BitBE(file));
3117 change->delay_fixed = getFile16BitBE(file);
3118 change->delay_random = getFile16BitBE(file);
3119 change->delay_frames = getFile16BitBE(file);
3121 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3123 change->explode = getFile8Bit(file);
3124 change->use_target_content = getFile8Bit(file);
3125 change->only_if_complete = getFile8Bit(file);
3126 change->use_random_replace = getFile8Bit(file);
3128 change->random_percentage = getFile8Bit(file);
3129 change->replace_when = getFile8Bit(file);
3131 for (y = 0; y < 3; y++)
3132 for (x = 0; x < 3; x++)
3133 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3135 change->can_change = getFile8Bit(file);
3137 change->trigger_side = getFile8Bit(file);
3139 change->trigger_player = getFile8Bit(file);
3140 change->trigger_page = getFile8Bit(file);
3142 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3143 CH_PAGE_ANY : (1 << change->trigger_page));
3145 change->has_action = getFile8Bit(file);
3146 change->action_type = getFile8Bit(file);
3147 change->action_mode = getFile8Bit(file);
3148 change->action_arg = getFile16BitBE(file);
3150 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3151 event_bits = getFile8Bit(file);
3152 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3153 if (event_bits & (1u << (j - 32)))
3154 change->has_event[j] = TRUE;
3157 // mark this custom element as modified
3158 ei->modified_settings = TRUE;
3160 level->file_has_custom_elements = TRUE;
3165 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3167 struct ElementInfo *ei;
3168 struct ElementGroupInfo *group;
3172 element = getMappedElement(getFile16BitBE(file));
3174 if (!IS_GROUP_ELEMENT(element))
3176 Warn("invalid group element number %d", element);
3178 ReadUnusedBytesFromFile(file, chunk_size - 2);
3183 ei = &element_info[element];
3185 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3186 ei->description[i] = getFile8Bit(file);
3187 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3189 group = element_info[element].group;
3191 group->num_elements = getFile8Bit(file);
3193 ei->use_gfx_element = getFile8Bit(file);
3194 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3196 group->choice_mode = getFile8Bit(file);
3198 // some free bytes for future values and padding
3199 ReadUnusedBytesFromFile(file, 3);
3201 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3202 group->element[i] = getMappedElement(getFile16BitBE(file));
3204 // mark this group element as modified
3205 element_info[element].modified_settings = TRUE;
3207 level->file_has_custom_elements = TRUE;
3212 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3213 int element, int real_element)
3215 int micro_chunk_size = 0;
3216 int conf_type = getFile8Bit(file);
3217 int byte_mask = conf_type & CONF_MASK_BYTES;
3218 boolean element_found = FALSE;
3221 micro_chunk_size += 1;
3223 if (byte_mask == CONF_MASK_MULTI_BYTES)
3225 int num_bytes = getFile16BitBE(file);
3226 byte *buffer = checked_malloc(num_bytes);
3228 ReadBytesFromFile(file, buffer, num_bytes);
3230 for (i = 0; conf[i].data_type != -1; i++)
3232 if (conf[i].element == element &&
3233 conf[i].conf_type == conf_type)
3235 int data_type = conf[i].data_type;
3236 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3237 int max_num_entities = conf[i].max_num_entities;
3239 if (num_entities > max_num_entities)
3241 Warn("truncating number of entities for element %d from %d to %d",
3242 element, num_entities, max_num_entities);
3244 num_entities = max_num_entities;
3247 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3248 data_type == TYPE_CONTENT_LIST))
3250 // for element and content lists, zero entities are not allowed
3251 Warn("found empty list of entities for element %d", element);
3253 // do not set "num_entities" here to prevent reading behind buffer
3255 *(int *)(conf[i].num_entities) = 1; // at least one is required
3259 *(int *)(conf[i].num_entities) = num_entities;
3262 element_found = TRUE;
3264 if (data_type == TYPE_STRING)
3266 char *string = (char *)(conf[i].value);
3269 for (j = 0; j < max_num_entities; j++)
3270 string[j] = (j < num_entities ? buffer[j] : '\0');
3272 else if (data_type == TYPE_ELEMENT_LIST)
3274 int *element_array = (int *)(conf[i].value);
3277 for (j = 0; j < num_entities; j++)
3279 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3281 else if (data_type == TYPE_CONTENT_LIST)
3283 struct Content *content= (struct Content *)(conf[i].value);
3286 for (c = 0; c < num_entities; c++)
3287 for (y = 0; y < 3; y++)
3288 for (x = 0; x < 3; x++)
3289 content[c].e[x][y] =
3290 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3293 element_found = FALSE;
3299 checked_free(buffer);
3301 micro_chunk_size += 2 + num_bytes;
3303 else // constant size configuration data (1, 2 or 4 bytes)
3305 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3306 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3307 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3309 for (i = 0; conf[i].data_type != -1; i++)
3311 if (conf[i].element == element &&
3312 conf[i].conf_type == conf_type)
3314 int data_type = conf[i].data_type;
3316 if (data_type == TYPE_ELEMENT)
3317 value = getMappedElement(value);
3319 if (data_type == TYPE_BOOLEAN)
3320 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3322 *(int *) (conf[i].value) = value;
3324 element_found = TRUE;
3330 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3335 char *error_conf_chunk_bytes =
3336 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3337 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3338 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3339 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3340 int error_element = real_element;
3342 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3343 error_conf_chunk_bytes, error_conf_chunk_token,
3344 error_element, EL_NAME(error_element));
3347 return micro_chunk_size;
3350 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3352 int real_chunk_size = 0;
3354 li = *level; // copy level data into temporary buffer
3356 while (!checkEndOfFile(file))
3358 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3360 if (real_chunk_size >= chunk_size)
3364 *level = li; // copy temporary buffer back to level data
3366 return real_chunk_size;
3369 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3371 int real_chunk_size = 0;
3373 li = *level; // copy level data into temporary buffer
3375 while (!checkEndOfFile(file))
3377 int element = getMappedElement(getFile16BitBE(file));
3379 real_chunk_size += 2;
3380 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3382 if (real_chunk_size >= chunk_size)
3386 *level = li; // copy temporary buffer back to level data
3388 return real_chunk_size;
3391 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3393 int real_chunk_size = 0;
3395 li = *level; // copy level data into temporary buffer
3397 while (!checkEndOfFile(file))
3399 int element = getMappedElement(getFile16BitBE(file));
3401 real_chunk_size += 2;
3402 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3404 if (real_chunk_size >= chunk_size)
3408 *level = li; // copy temporary buffer back to level data
3410 return real_chunk_size;
3413 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3415 int element = getMappedElement(getFile16BitBE(file));
3416 int envelope_nr = element - EL_ENVELOPE_1;
3417 int real_chunk_size = 2;
3419 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3421 while (!checkEndOfFile(file))
3423 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3426 if (real_chunk_size >= chunk_size)
3430 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3432 return real_chunk_size;
3435 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3437 int element = getMappedElement(getFile16BitBE(file));
3438 int real_chunk_size = 2;
3439 struct ElementInfo *ei = &element_info[element];
3442 xx_ei = *ei; // copy element data into temporary buffer
3444 xx_ei.num_change_pages = -1;
3446 while (!checkEndOfFile(file))
3448 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3450 if (xx_ei.num_change_pages != -1)
3453 if (real_chunk_size >= chunk_size)
3459 if (ei->num_change_pages == -1)
3461 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3464 ei->num_change_pages = 1;
3466 setElementChangePages(ei, 1);
3467 setElementChangeInfoToDefaults(ei->change);
3469 return real_chunk_size;
3472 // initialize number of change pages stored for this custom element
3473 setElementChangePages(ei, ei->num_change_pages);
3474 for (i = 0; i < ei->num_change_pages; i++)
3475 setElementChangeInfoToDefaults(&ei->change_page[i]);
3477 // start with reading properties for the first change page
3478 xx_current_change_page = 0;
3480 while (!checkEndOfFile(file))
3482 // level file might contain invalid change page number
3483 if (xx_current_change_page >= ei->num_change_pages)
3486 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3488 xx_change = *change; // copy change data into temporary buffer
3490 resetEventBits(); // reset bits; change page might have changed
3492 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3495 *change = xx_change;
3497 setEventFlagsFromEventBits(change);
3499 if (real_chunk_size >= chunk_size)
3503 level->file_has_custom_elements = TRUE;
3505 return real_chunk_size;
3508 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3510 int element = getMappedElement(getFile16BitBE(file));
3511 int real_chunk_size = 2;
3512 struct ElementInfo *ei = &element_info[element];
3513 struct ElementGroupInfo *group = ei->group;
3518 xx_ei = *ei; // copy element data into temporary buffer
3519 xx_group = *group; // copy group data into temporary buffer
3521 while (!checkEndOfFile(file))
3523 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3526 if (real_chunk_size >= chunk_size)
3533 level->file_has_custom_elements = TRUE;
3535 return real_chunk_size;
3538 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3540 int element = getMappedElement(getFile16BitBE(file));
3541 int real_chunk_size = 2;
3542 struct ElementInfo *ei = &element_info[element];
3544 xx_ei = *ei; // copy element data into temporary buffer
3546 while (!checkEndOfFile(file))
3548 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3551 if (real_chunk_size >= chunk_size)
3557 level->file_has_custom_elements = TRUE;
3559 return real_chunk_size;
3562 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3563 struct LevelFileInfo *level_file_info,
3564 boolean level_info_only)
3566 char *filename = level_file_info->filename;
3567 char cookie[MAX_LINE_LEN];
3568 char chunk_name[CHUNK_ID_LEN + 1];
3572 if (!(file = openFile(filename, MODE_READ)))
3574 level->no_valid_file = TRUE;
3575 level->no_level_file = TRUE;
3577 if (level_info_only)
3580 Warn("cannot read level '%s' -- using empty level", filename);
3582 if (!setup.editor.use_template_for_new_levels)
3585 // if level file not found, try to initialize level data from template
3586 filename = getGlobalLevelTemplateFilename();
3588 if (!(file = openFile(filename, MODE_READ)))
3591 // default: for empty levels, use level template for custom elements
3592 level->use_custom_template = TRUE;
3594 level->no_valid_file = FALSE;
3597 getFileChunkBE(file, chunk_name, NULL);
3598 if (strEqual(chunk_name, "RND1"))
3600 getFile32BitBE(file); // not used
3602 getFileChunkBE(file, chunk_name, NULL);
3603 if (!strEqual(chunk_name, "CAVE"))
3605 level->no_valid_file = TRUE;
3607 Warn("unknown format of level file '%s'", filename);
3614 else // check for pre-2.0 file format with cookie string
3616 strcpy(cookie, chunk_name);
3617 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3619 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3620 cookie[strlen(cookie) - 1] = '\0';
3622 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3624 level->no_valid_file = TRUE;
3626 Warn("unknown format of level file '%s'", filename);
3633 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3635 level->no_valid_file = TRUE;
3637 Warn("unsupported version of level file '%s'", filename);
3644 // pre-2.0 level files have no game version, so use file version here
3645 level->game_version = level->file_version;
3648 if (level->file_version < FILE_VERSION_1_2)
3650 // level files from versions before 1.2.0 without chunk structure
3651 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3652 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3660 int (*loader)(File *, int, struct LevelInfo *);
3664 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3665 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3666 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3667 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3668 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3669 { "INFO", -1, LoadLevel_INFO },
3670 { "BODY", -1, LoadLevel_BODY },
3671 { "CONT", -1, LoadLevel_CONT },
3672 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3673 { "CNT3", -1, LoadLevel_CNT3 },
3674 { "CUS1", -1, LoadLevel_CUS1 },
3675 { "CUS2", -1, LoadLevel_CUS2 },
3676 { "CUS3", -1, LoadLevel_CUS3 },
3677 { "CUS4", -1, LoadLevel_CUS4 },
3678 { "GRP1", -1, LoadLevel_GRP1 },
3679 { "CONF", -1, LoadLevel_CONF },
3680 { "ELEM", -1, LoadLevel_ELEM },
3681 { "NOTE", -1, LoadLevel_NOTE },
3682 { "CUSX", -1, LoadLevel_CUSX },
3683 { "GRPX", -1, LoadLevel_GRPX },
3684 { "EMPX", -1, LoadLevel_EMPX },
3689 while (getFileChunkBE(file, chunk_name, &chunk_size))
3693 while (chunk_info[i].name != NULL &&
3694 !strEqual(chunk_name, chunk_info[i].name))
3697 if (chunk_info[i].name == NULL)
3699 Warn("unknown chunk '%s' in level file '%s'",
3700 chunk_name, filename);
3702 ReadUnusedBytesFromFile(file, chunk_size);
3704 else if (chunk_info[i].size != -1 &&
3705 chunk_info[i].size != chunk_size)
3707 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3708 chunk_size, chunk_name, filename);
3710 ReadUnusedBytesFromFile(file, chunk_size);
3714 // call function to load this level chunk
3715 int chunk_size_expected =
3716 (chunk_info[i].loader)(file, chunk_size, level);
3718 if (chunk_size_expected < 0)
3720 Warn("error reading chunk '%s' in level file '%s'",
3721 chunk_name, filename);
3726 // the size of some chunks cannot be checked before reading other
3727 // chunks first (like "HEAD" and "BODY") that contain some header
3728 // information, so check them here
3729 if (chunk_size_expected != chunk_size)
3731 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3732 chunk_size, chunk_name, filename);
3744 // ----------------------------------------------------------------------------
3745 // functions for loading BD level
3746 // ----------------------------------------------------------------------------
3748 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3750 struct LevelInfo_BD *level_bd = level->native_bd_level;
3751 GdCave *cave = NULL; // will be changed below
3752 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3753 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3756 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3758 // cave and map newly allocated when set to defaults above
3759 cave = level_bd->cave;
3761 for (i = 0; i < 5; i++)
3763 cave->level_time[i] = level->time;
3764 cave->level_diamonds[i] = level->gems_needed;
3765 cave->level_magic_wall_time[i] = level->time_magic_wall;
3767 cave->level_speed[i] = level->bd_cycle_delay_ms;
3768 cave->level_ckdelay[i] = level->bd_cycle_delay_c64;
3769 cave->level_hatching_delay_frame[i] = level->bd_hatching_delay_cycles;
3770 cave->level_hatching_delay_time[i] = level->bd_hatching_delay_seconds;
3772 cave->level_timevalue[i] = level->score[SC_TIME_BONUS];
3775 cave->diamond_value = level->score[SC_EMERALD];
3776 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3778 cave->scheduling = level->bd_scheduling_type;
3779 cave->pal_timing = level->bd_pal_timing;
3780 cave->intermission = level->bd_intermission;
3781 cave->diagonal_movements = level->bd_diagonal_movements;
3783 cave->lineshift = level->bd_line_shifting_borders;
3784 cave->wraparound_objects = level->bd_wraparound_objects;
3786 strncpy(cave->name, level->name, sizeof(GdString));
3787 cave->name[sizeof(GdString) - 1] = '\0';
3789 for (x = 0; x < cave->w; x++)
3790 for (y = 0; y < cave->h; y++)
3791 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3794 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3796 struct LevelInfo_BD *level_bd = level->native_bd_level;
3797 GdCave *cave = level_bd->cave;
3798 int bd_level_nr = level_bd->level_nr;
3801 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3802 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3804 level->time = cave->level_time[bd_level_nr];
3805 level->gems_needed = cave->level_diamonds[bd_level_nr];
3806 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3808 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
3809 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
3810 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
3811 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
3813 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3814 level->score[SC_EMERALD] = cave->diamond_value;
3815 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
3817 level->bd_scheduling_type = cave->scheduling;
3818 level->bd_pal_timing = cave->pal_timing;
3819 level->bd_intermission = cave->intermission;
3820 level->bd_diagonal_movements = cave->diagonal_movements;
3822 level->bd_line_shifting_borders = cave->lineshift;
3823 level->bd_wraparound_objects = cave->wraparound_objects;
3825 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
3827 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
3828 level->name[MAX_LEVEL_NAME_LEN] = '\0';
3830 for (x = 0; x < level->fieldx; x++)
3831 for (y = 0; y < level->fieldy; y++)
3832 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3834 checked_free(cave_name);
3837 static void setTapeInfoToDefaults(void);
3839 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
3841 struct LevelInfo_BD *level_bd = level->native_bd_level;
3842 GdCave *cave = level_bd->cave;
3843 GdReplay *replay = level_bd->replay;
3849 // always start with reliable default values
3850 setTapeInfoToDefaults();
3852 tape.level_nr = level_nr; // (currently not used)
3853 tape.random_seed = replay->seed;
3855 TapeSetDateFromIsoDateString(replay->date);
3858 tape.pos[tape.counter].delay = 0;
3860 tape.bd_replay = TRUE;
3862 // all time calculations only used to display approximate tape time
3863 int cave_speed = cave->speed;
3864 int milliseconds_game = 0;
3865 int milliseconds_elapsed = 20;
3867 for (i = 0; i < replay->movements->len; i++)
3869 int replay_action = replay->movements->data[i];
3870 int tape_action = map_action_BD_to_RND(replay_action);
3871 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3872 boolean success = 0;
3876 success = TapeAddAction(action);
3878 milliseconds_game += milliseconds_elapsed;
3880 if (milliseconds_game >= cave_speed)
3882 milliseconds_game -= cave_speed;
3889 tape.pos[tape.counter].delay = 0;
3890 tape.pos[tape.counter].action[0] = 0;
3894 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
3900 TapeHaltRecording();
3904 // ----------------------------------------------------------------------------
3905 // functions for loading EM level
3906 // ----------------------------------------------------------------------------
3908 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3910 static int ball_xy[8][2] =
3921 struct LevelInfo_EM *level_em = level->native_em_level;
3922 struct CAVE *cav = level_em->cav;
3925 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3926 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3928 cav->time_seconds = level->time;
3929 cav->gems_needed = level->gems_needed;
3931 cav->emerald_score = level->score[SC_EMERALD];
3932 cav->diamond_score = level->score[SC_DIAMOND];
3933 cav->alien_score = level->score[SC_ROBOT];
3934 cav->tank_score = level->score[SC_SPACESHIP];
3935 cav->bug_score = level->score[SC_BUG];
3936 cav->eater_score = level->score[SC_YAMYAM];
3937 cav->nut_score = level->score[SC_NUT];
3938 cav->dynamite_score = level->score[SC_DYNAMITE];
3939 cav->key_score = level->score[SC_KEY];
3940 cav->exit_score = level->score[SC_TIME_BONUS];
3942 cav->num_eater_arrays = level->num_yamyam_contents;
3944 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3945 for (y = 0; y < 3; y++)
3946 for (x = 0; x < 3; x++)
3947 cav->eater_array[i][y * 3 + x] =
3948 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3950 cav->amoeba_time = level->amoeba_speed;
3951 cav->wonderwall_time = level->time_magic_wall;
3952 cav->wheel_time = level->time_wheel;
3954 cav->android_move_time = level->android_move_time;
3955 cav->android_clone_time = level->android_clone_time;
3956 cav->ball_random = level->ball_random;
3957 cav->ball_active = level->ball_active_initial;
3958 cav->ball_time = level->ball_time;
3959 cav->num_ball_arrays = level->num_ball_contents;
3961 cav->lenses_score = level->lenses_score;
3962 cav->magnify_score = level->magnify_score;
3963 cav->slurp_score = level->slurp_score;
3965 cav->lenses_time = level->lenses_time;
3966 cav->magnify_time = level->magnify_time;
3968 cav->wind_time = 9999;
3969 cav->wind_direction =
3970 map_direction_RND_to_EM(level->wind_direction_initial);
3972 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3973 for (j = 0; j < 8; j++)
3974 cav->ball_array[i][j] =
3975 map_element_RND_to_EM_cave(level->ball_content[i].
3976 e[ball_xy[j][0]][ball_xy[j][1]]);
3978 map_android_clone_elements_RND_to_EM(level);
3980 // first fill the complete playfield with the empty space element
3981 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3982 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3983 cav->cave[x][y] = Cblank;
3985 // then copy the real level contents from level file into the playfield
3986 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3988 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3990 if (level->field[x][y] == EL_AMOEBA_DEAD)
3991 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3993 cav->cave[x][y] = new_element;
3996 for (i = 0; i < MAX_PLAYERS; i++)
3998 cav->player_x[i] = -1;
3999 cav->player_y[i] = -1;
4002 // initialize player positions and delete players from the playfield
4003 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4005 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4007 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4009 cav->player_x[player_nr] = x;
4010 cav->player_y[player_nr] = y;
4012 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4017 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4019 static int ball_xy[8][2] =
4030 struct LevelInfo_EM *level_em = level->native_em_level;
4031 struct CAVE *cav = level_em->cav;
4034 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4035 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4037 level->time = cav->time_seconds;
4038 level->gems_needed = cav->gems_needed;
4040 sprintf(level->name, "Level %d", level->file_info.nr);
4042 level->score[SC_EMERALD] = cav->emerald_score;
4043 level->score[SC_DIAMOND] = cav->diamond_score;
4044 level->score[SC_ROBOT] = cav->alien_score;
4045 level->score[SC_SPACESHIP] = cav->tank_score;
4046 level->score[SC_BUG] = cav->bug_score;
4047 level->score[SC_YAMYAM] = cav->eater_score;
4048 level->score[SC_NUT] = cav->nut_score;
4049 level->score[SC_DYNAMITE] = cav->dynamite_score;
4050 level->score[SC_KEY] = cav->key_score;
4051 level->score[SC_TIME_BONUS] = cav->exit_score;
4053 level->num_yamyam_contents = cav->num_eater_arrays;
4055 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4056 for (y = 0; y < 3; y++)
4057 for (x = 0; x < 3; x++)
4058 level->yamyam_content[i].e[x][y] =
4059 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4061 level->amoeba_speed = cav->amoeba_time;
4062 level->time_magic_wall = cav->wonderwall_time;
4063 level->time_wheel = cav->wheel_time;
4065 level->android_move_time = cav->android_move_time;
4066 level->android_clone_time = cav->android_clone_time;
4067 level->ball_random = cav->ball_random;
4068 level->ball_active_initial = cav->ball_active;
4069 level->ball_time = cav->ball_time;
4070 level->num_ball_contents = cav->num_ball_arrays;
4072 level->lenses_score = cav->lenses_score;
4073 level->magnify_score = cav->magnify_score;
4074 level->slurp_score = cav->slurp_score;
4076 level->lenses_time = cav->lenses_time;
4077 level->magnify_time = cav->magnify_time;
4079 level->wind_direction_initial =
4080 map_direction_EM_to_RND(cav->wind_direction);
4082 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4083 for (j = 0; j < 8; j++)
4084 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4085 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4087 map_android_clone_elements_EM_to_RND(level);
4089 // convert the playfield (some elements need special treatment)
4090 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4092 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4094 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4095 new_element = EL_AMOEBA_DEAD;
4097 level->field[x][y] = new_element;
4100 for (i = 0; i < MAX_PLAYERS; i++)
4102 // in case of all players set to the same field, use the first player
4103 int nr = MAX_PLAYERS - i - 1;
4104 int jx = cav->player_x[nr];
4105 int jy = cav->player_y[nr];
4107 if (jx != -1 && jy != -1)
4108 level->field[jx][jy] = EL_PLAYER_1 + nr;
4111 // time score is counted for each 10 seconds left in Emerald Mine levels
4112 level->time_score_base = 10;
4116 // ----------------------------------------------------------------------------
4117 // functions for loading SP level
4118 // ----------------------------------------------------------------------------
4120 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4122 struct LevelInfo_SP *level_sp = level->native_sp_level;
4123 LevelInfoType *header = &level_sp->header;
4126 level_sp->width = level->fieldx;
4127 level_sp->height = level->fieldy;
4129 for (x = 0; x < level->fieldx; x++)
4130 for (y = 0; y < level->fieldy; y++)
4131 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4133 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4135 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4136 header->LevelTitle[i] = level->name[i];
4137 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4139 header->InfotronsNeeded = level->gems_needed;
4141 header->SpecialPortCount = 0;
4143 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4145 boolean gravity_port_found = FALSE;
4146 boolean gravity_port_valid = FALSE;
4147 int gravity_port_flag;
4148 int gravity_port_base_element;
4149 int element = level->field[x][y];
4151 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4152 element <= EL_SP_GRAVITY_ON_PORT_UP)
4154 gravity_port_found = TRUE;
4155 gravity_port_valid = TRUE;
4156 gravity_port_flag = 1;
4157 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4159 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4160 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4162 gravity_port_found = TRUE;
4163 gravity_port_valid = TRUE;
4164 gravity_port_flag = 0;
4165 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4167 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4168 element <= EL_SP_GRAVITY_PORT_UP)
4170 // change R'n'D style gravity inverting special port to normal port
4171 // (there are no gravity inverting ports in native Supaplex engine)
4173 gravity_port_found = TRUE;
4174 gravity_port_valid = FALSE;
4175 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4178 if (gravity_port_found)
4180 if (gravity_port_valid &&
4181 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4183 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4185 port->PortLocation = (y * level->fieldx + x) * 2;
4186 port->Gravity = gravity_port_flag;
4188 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4190 header->SpecialPortCount++;
4194 // change special gravity port to normal port
4196 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4199 level_sp->playfield[x][y] = element - EL_SP_START;
4204 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4206 struct LevelInfo_SP *level_sp = level->native_sp_level;
4207 LevelInfoType *header = &level_sp->header;
4208 boolean num_invalid_elements = 0;
4211 level->fieldx = level_sp->width;
4212 level->fieldy = level_sp->height;
4214 for (x = 0; x < level->fieldx; x++)
4216 for (y = 0; y < level->fieldy; y++)
4218 int element_old = level_sp->playfield[x][y];
4219 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4221 if (element_new == EL_UNKNOWN)
4223 num_invalid_elements++;
4225 Debug("level:native:SP", "invalid element %d at position %d, %d",
4229 level->field[x][y] = element_new;
4233 if (num_invalid_elements > 0)
4234 Warn("found %d invalid elements%s", num_invalid_elements,
4235 (!options.debug ? " (use '--debug' for more details)" : ""));
4237 for (i = 0; i < MAX_PLAYERS; i++)
4238 level->initial_player_gravity[i] =
4239 (header->InitialGravity == 1 ? TRUE : FALSE);
4241 // skip leading spaces
4242 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4243 if (header->LevelTitle[i] != ' ')
4247 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4248 level->name[j] = header->LevelTitle[i];
4249 level->name[j] = '\0';
4251 // cut trailing spaces
4253 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4254 level->name[j - 1] = '\0';
4256 level->gems_needed = header->InfotronsNeeded;
4258 for (i = 0; i < header->SpecialPortCount; i++)
4260 SpecialPortType *port = &header->SpecialPort[i];
4261 int port_location = port->PortLocation;
4262 int gravity = port->Gravity;
4263 int port_x, port_y, port_element;
4265 port_x = (port_location / 2) % level->fieldx;
4266 port_y = (port_location / 2) / level->fieldx;
4268 if (port_x < 0 || port_x >= level->fieldx ||
4269 port_y < 0 || port_y >= level->fieldy)
4271 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4276 port_element = level->field[port_x][port_y];
4278 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4279 port_element > EL_SP_GRAVITY_PORT_UP)
4281 Warn("no special port at position (%d, %d)", port_x, port_y);
4286 // change previous (wrong) gravity inverting special port to either
4287 // gravity enabling special port or gravity disabling special port
4288 level->field[port_x][port_y] +=
4289 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4290 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4293 // change special gravity ports without database entries to normal ports
4294 for (x = 0; x < level->fieldx; x++)
4295 for (y = 0; y < level->fieldy; y++)
4296 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4297 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4298 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4300 level->time = 0; // no time limit
4301 level->amoeba_speed = 0;
4302 level->time_magic_wall = 0;
4303 level->time_wheel = 0;
4304 level->amoeba_content = EL_EMPTY;
4306 // original Supaplex does not use score values -- rate by playing time
4307 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4308 level->score[i] = 0;
4310 level->rate_time_over_score = TRUE;
4312 // there are no yamyams in supaplex levels
4313 for (i = 0; i < level->num_yamyam_contents; i++)
4314 for (x = 0; x < 3; x++)
4315 for (y = 0; y < 3; y++)
4316 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4319 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4321 struct LevelInfo_SP *level_sp = level->native_sp_level;
4322 struct DemoInfo_SP *demo = &level_sp->demo;
4325 // always start with reliable default values
4326 demo->is_available = FALSE;
4329 if (TAPE_IS_EMPTY(tape))
4332 demo->level_nr = tape.level_nr; // (currently not used)
4334 level_sp->header.DemoRandomSeed = tape.random_seed;
4338 for (i = 0; i < tape.length; i++)
4340 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4341 int demo_repeat = tape.pos[i].delay;
4342 int demo_entries = (demo_repeat + 15) / 16;
4344 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4346 Warn("tape truncated: size exceeds maximum SP demo size %d",
4352 for (j = 0; j < demo_repeat / 16; j++)
4353 demo->data[demo->length++] = 0xf0 | demo_action;
4355 if (demo_repeat % 16)
4356 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4359 demo->is_available = TRUE;
4362 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4364 struct LevelInfo_SP *level_sp = level->native_sp_level;
4365 struct DemoInfo_SP *demo = &level_sp->demo;
4366 char *filename = level->file_info.filename;
4369 // always start with reliable default values
4370 setTapeInfoToDefaults();
4372 if (!demo->is_available)
4375 tape.level_nr = demo->level_nr; // (currently not used)
4376 tape.random_seed = level_sp->header.DemoRandomSeed;
4378 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4381 tape.pos[tape.counter].delay = 0;
4383 for (i = 0; i < demo->length; i++)
4385 int demo_action = demo->data[i] & 0x0f;
4386 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4387 int tape_action = map_key_SP_to_RND(demo_action);
4388 int tape_repeat = demo_repeat + 1;
4389 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4390 boolean success = 0;
4393 for (j = 0; j < tape_repeat; j++)
4394 success = TapeAddAction(action);
4398 Warn("SP demo truncated: size exceeds maximum tape size %d",
4405 TapeHaltRecording();
4409 // ----------------------------------------------------------------------------
4410 // functions for loading MM level
4411 // ----------------------------------------------------------------------------
4413 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4415 struct LevelInfo_MM *level_mm = level->native_mm_level;
4418 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4419 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4421 level_mm->time = level->time;
4422 level_mm->kettles_needed = level->gems_needed;
4423 level_mm->auto_count_kettles = level->auto_count_gems;
4425 level_mm->mm_laser_red = level->mm_laser_red;
4426 level_mm->mm_laser_green = level->mm_laser_green;
4427 level_mm->mm_laser_blue = level->mm_laser_blue;
4429 level_mm->df_laser_red = level->df_laser_red;
4430 level_mm->df_laser_green = level->df_laser_green;
4431 level_mm->df_laser_blue = level->df_laser_blue;
4433 strcpy(level_mm->name, level->name);
4434 strcpy(level_mm->author, level->author);
4436 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4437 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4438 level_mm->score[SC_KEY] = level->score[SC_KEY];
4439 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4440 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4442 level_mm->amoeba_speed = level->amoeba_speed;
4443 level_mm->time_fuse = level->mm_time_fuse;
4444 level_mm->time_bomb = level->mm_time_bomb;
4445 level_mm->time_ball = level->mm_time_ball;
4446 level_mm->time_block = level->mm_time_block;
4448 level_mm->num_ball_contents = level->num_mm_ball_contents;
4449 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4450 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4451 level_mm->explode_ball = level->explode_mm_ball;
4453 for (i = 0; i < level->num_mm_ball_contents; i++)
4454 level_mm->ball_content[i] =
4455 map_element_RND_to_MM(level->mm_ball_content[i]);
4457 for (x = 0; x < level->fieldx; x++)
4458 for (y = 0; y < level->fieldy; y++)
4460 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4463 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4465 struct LevelInfo_MM *level_mm = level->native_mm_level;
4468 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4469 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4471 level->time = level_mm->time;
4472 level->gems_needed = level_mm->kettles_needed;
4473 level->auto_count_gems = level_mm->auto_count_kettles;
4475 level->mm_laser_red = level_mm->mm_laser_red;
4476 level->mm_laser_green = level_mm->mm_laser_green;
4477 level->mm_laser_blue = level_mm->mm_laser_blue;
4479 level->df_laser_red = level_mm->df_laser_red;
4480 level->df_laser_green = level_mm->df_laser_green;
4481 level->df_laser_blue = level_mm->df_laser_blue;
4483 strcpy(level->name, level_mm->name);
4485 // only overwrite author from 'levelinfo.conf' if author defined in level
4486 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4487 strcpy(level->author, level_mm->author);
4489 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4490 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4491 level->score[SC_KEY] = level_mm->score[SC_KEY];
4492 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4493 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4495 level->amoeba_speed = level_mm->amoeba_speed;
4496 level->mm_time_fuse = level_mm->time_fuse;
4497 level->mm_time_bomb = level_mm->time_bomb;
4498 level->mm_time_ball = level_mm->time_ball;
4499 level->mm_time_block = level_mm->time_block;
4501 level->num_mm_ball_contents = level_mm->num_ball_contents;
4502 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4503 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4504 level->explode_mm_ball = level_mm->explode_ball;
4506 for (i = 0; i < level->num_mm_ball_contents; i++)
4507 level->mm_ball_content[i] =
4508 map_element_MM_to_RND(level_mm->ball_content[i]);
4510 for (x = 0; x < level->fieldx; x++)
4511 for (y = 0; y < level->fieldy; y++)
4512 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4516 // ----------------------------------------------------------------------------
4517 // functions for loading DC level
4518 // ----------------------------------------------------------------------------
4520 #define DC_LEVEL_HEADER_SIZE 344
4522 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4525 static int last_data_encoded;
4529 int diff_hi, diff_lo;
4530 int data_hi, data_lo;
4531 unsigned short data_decoded;
4535 last_data_encoded = 0;
4542 diff = data_encoded - last_data_encoded;
4543 diff_hi = diff & ~0xff;
4544 diff_lo = diff & 0xff;
4548 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4549 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4550 data_hi = data_hi & 0xff00;
4552 data_decoded = data_hi | data_lo;
4554 last_data_encoded = data_encoded;
4556 offset1 = (offset1 + 1) % 31;
4557 offset2 = offset2 & 0xff;
4559 return data_decoded;
4562 static int getMappedElement_DC(int element)
4570 // 0x0117 - 0x036e: (?)
4573 // 0x042d - 0x0684: (?)
4589 element = EL_CRYSTAL;
4592 case 0x0e77: // quicksand (boulder)
4593 element = EL_QUICKSAND_FAST_FULL;
4596 case 0x0e99: // slow quicksand (boulder)
4597 element = EL_QUICKSAND_FULL;
4601 element = EL_EM_EXIT_OPEN;
4605 element = EL_EM_EXIT_CLOSED;
4609 element = EL_EM_STEEL_EXIT_OPEN;
4613 element = EL_EM_STEEL_EXIT_CLOSED;
4616 case 0x0f4f: // dynamite (lit 1)
4617 element = EL_EM_DYNAMITE_ACTIVE;
4620 case 0x0f57: // dynamite (lit 2)
4621 element = EL_EM_DYNAMITE_ACTIVE;
4624 case 0x0f5f: // dynamite (lit 3)
4625 element = EL_EM_DYNAMITE_ACTIVE;
4628 case 0x0f67: // dynamite (lit 4)
4629 element = EL_EM_DYNAMITE_ACTIVE;
4636 element = EL_AMOEBA_WET;
4640 element = EL_AMOEBA_DROP;
4644 element = EL_DC_MAGIC_WALL;
4648 element = EL_SPACESHIP_UP;
4652 element = EL_SPACESHIP_DOWN;
4656 element = EL_SPACESHIP_LEFT;
4660 element = EL_SPACESHIP_RIGHT;
4664 element = EL_BUG_UP;
4668 element = EL_BUG_DOWN;
4672 element = EL_BUG_LEFT;
4676 element = EL_BUG_RIGHT;
4680 element = EL_MOLE_UP;
4684 element = EL_MOLE_DOWN;
4688 element = EL_MOLE_LEFT;
4692 element = EL_MOLE_RIGHT;
4700 element = EL_YAMYAM_UP;
4704 element = EL_SWITCHGATE_OPEN;
4708 element = EL_SWITCHGATE_CLOSED;
4712 element = EL_DC_SWITCHGATE_SWITCH_UP;
4716 element = EL_TIMEGATE_CLOSED;
4719 case 0x144c: // conveyor belt switch (green)
4720 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4723 case 0x144f: // conveyor belt switch (red)
4724 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4727 case 0x1452: // conveyor belt switch (blue)
4728 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4732 element = EL_CONVEYOR_BELT_3_MIDDLE;
4736 element = EL_CONVEYOR_BELT_3_LEFT;
4740 element = EL_CONVEYOR_BELT_3_RIGHT;
4744 element = EL_CONVEYOR_BELT_1_MIDDLE;
4748 element = EL_CONVEYOR_BELT_1_LEFT;
4752 element = EL_CONVEYOR_BELT_1_RIGHT;
4756 element = EL_CONVEYOR_BELT_4_MIDDLE;
4760 element = EL_CONVEYOR_BELT_4_LEFT;
4764 element = EL_CONVEYOR_BELT_4_RIGHT;
4768 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4772 element = EL_EXPANDABLE_WALL_VERTICAL;
4776 element = EL_EXPANDABLE_WALL_ANY;
4779 case 0x14ce: // growing steel wall (left/right)
4780 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4783 case 0x14df: // growing steel wall (up/down)
4784 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4787 case 0x14e8: // growing steel wall (up/down/left/right)
4788 element = EL_EXPANDABLE_STEELWALL_ANY;
4792 element = EL_SHIELD_DEADLY;
4796 element = EL_EXTRA_TIME;
4804 element = EL_EMPTY_SPACE;
4807 case 0x1578: // quicksand (empty)
4808 element = EL_QUICKSAND_FAST_EMPTY;
4811 case 0x1579: // slow quicksand (empty)
4812 element = EL_QUICKSAND_EMPTY;
4822 element = EL_EM_DYNAMITE;
4825 case 0x15a1: // key (red)
4826 element = EL_EM_KEY_1;
4829 case 0x15a2: // key (yellow)
4830 element = EL_EM_KEY_2;
4833 case 0x15a3: // key (blue)
4834 element = EL_EM_KEY_4;
4837 case 0x15a4: // key (green)
4838 element = EL_EM_KEY_3;
4841 case 0x15a5: // key (white)
4842 element = EL_DC_KEY_WHITE;
4846 element = EL_WALL_SLIPPERY;
4853 case 0x15a8: // wall (not round)
4857 case 0x15a9: // (blue)
4858 element = EL_CHAR_A;
4861 case 0x15aa: // (blue)
4862 element = EL_CHAR_B;
4865 case 0x15ab: // (blue)
4866 element = EL_CHAR_C;
4869 case 0x15ac: // (blue)
4870 element = EL_CHAR_D;
4873 case 0x15ad: // (blue)
4874 element = EL_CHAR_E;
4877 case 0x15ae: // (blue)
4878 element = EL_CHAR_F;
4881 case 0x15af: // (blue)
4882 element = EL_CHAR_G;
4885 case 0x15b0: // (blue)
4886 element = EL_CHAR_H;
4889 case 0x15b1: // (blue)
4890 element = EL_CHAR_I;
4893 case 0x15b2: // (blue)
4894 element = EL_CHAR_J;
4897 case 0x15b3: // (blue)
4898 element = EL_CHAR_K;
4901 case 0x15b4: // (blue)
4902 element = EL_CHAR_L;
4905 case 0x15b5: // (blue)
4906 element = EL_CHAR_M;
4909 case 0x15b6: // (blue)
4910 element = EL_CHAR_N;
4913 case 0x15b7: // (blue)
4914 element = EL_CHAR_O;
4917 case 0x15b8: // (blue)
4918 element = EL_CHAR_P;
4921 case 0x15b9: // (blue)
4922 element = EL_CHAR_Q;
4925 case 0x15ba: // (blue)
4926 element = EL_CHAR_R;
4929 case 0x15bb: // (blue)
4930 element = EL_CHAR_S;
4933 case 0x15bc: // (blue)
4934 element = EL_CHAR_T;
4937 case 0x15bd: // (blue)
4938 element = EL_CHAR_U;
4941 case 0x15be: // (blue)
4942 element = EL_CHAR_V;
4945 case 0x15bf: // (blue)
4946 element = EL_CHAR_W;
4949 case 0x15c0: // (blue)
4950 element = EL_CHAR_X;
4953 case 0x15c1: // (blue)
4954 element = EL_CHAR_Y;
4957 case 0x15c2: // (blue)
4958 element = EL_CHAR_Z;
4961 case 0x15c3: // (blue)
4962 element = EL_CHAR_AUMLAUT;
4965 case 0x15c4: // (blue)
4966 element = EL_CHAR_OUMLAUT;
4969 case 0x15c5: // (blue)
4970 element = EL_CHAR_UUMLAUT;
4973 case 0x15c6: // (blue)
4974 element = EL_CHAR_0;
4977 case 0x15c7: // (blue)
4978 element = EL_CHAR_1;
4981 case 0x15c8: // (blue)
4982 element = EL_CHAR_2;
4985 case 0x15c9: // (blue)
4986 element = EL_CHAR_3;
4989 case 0x15ca: // (blue)
4990 element = EL_CHAR_4;
4993 case 0x15cb: // (blue)
4994 element = EL_CHAR_5;
4997 case 0x15cc: // (blue)
4998 element = EL_CHAR_6;
5001 case 0x15cd: // (blue)
5002 element = EL_CHAR_7;
5005 case 0x15ce: // (blue)
5006 element = EL_CHAR_8;
5009 case 0x15cf: // (blue)
5010 element = EL_CHAR_9;
5013 case 0x15d0: // (blue)
5014 element = EL_CHAR_PERIOD;
5017 case 0x15d1: // (blue)
5018 element = EL_CHAR_EXCLAM;
5021 case 0x15d2: // (blue)
5022 element = EL_CHAR_COLON;
5025 case 0x15d3: // (blue)
5026 element = EL_CHAR_LESS;
5029 case 0x15d4: // (blue)
5030 element = EL_CHAR_GREATER;
5033 case 0x15d5: // (blue)
5034 element = EL_CHAR_QUESTION;
5037 case 0x15d6: // (blue)
5038 element = EL_CHAR_COPYRIGHT;
5041 case 0x15d7: // (blue)
5042 element = EL_CHAR_UP;
5045 case 0x15d8: // (blue)
5046 element = EL_CHAR_DOWN;
5049 case 0x15d9: // (blue)
5050 element = EL_CHAR_BUTTON;
5053 case 0x15da: // (blue)
5054 element = EL_CHAR_PLUS;
5057 case 0x15db: // (blue)
5058 element = EL_CHAR_MINUS;
5061 case 0x15dc: // (blue)
5062 element = EL_CHAR_APOSTROPHE;
5065 case 0x15dd: // (blue)
5066 element = EL_CHAR_PARENLEFT;
5069 case 0x15de: // (blue)
5070 element = EL_CHAR_PARENRIGHT;
5073 case 0x15df: // (green)
5074 element = EL_CHAR_A;
5077 case 0x15e0: // (green)
5078 element = EL_CHAR_B;
5081 case 0x15e1: // (green)
5082 element = EL_CHAR_C;
5085 case 0x15e2: // (green)
5086 element = EL_CHAR_D;
5089 case 0x15e3: // (green)
5090 element = EL_CHAR_E;
5093 case 0x15e4: // (green)
5094 element = EL_CHAR_F;
5097 case 0x15e5: // (green)
5098 element = EL_CHAR_G;
5101 case 0x15e6: // (green)
5102 element = EL_CHAR_H;
5105 case 0x15e7: // (green)
5106 element = EL_CHAR_I;
5109 case 0x15e8: // (green)
5110 element = EL_CHAR_J;
5113 case 0x15e9: // (green)
5114 element = EL_CHAR_K;
5117 case 0x15ea: // (green)
5118 element = EL_CHAR_L;
5121 case 0x15eb: // (green)
5122 element = EL_CHAR_M;
5125 case 0x15ec: // (green)
5126 element = EL_CHAR_N;
5129 case 0x15ed: // (green)
5130 element = EL_CHAR_O;
5133 case 0x15ee: // (green)
5134 element = EL_CHAR_P;
5137 case 0x15ef: // (green)
5138 element = EL_CHAR_Q;
5141 case 0x15f0: // (green)
5142 element = EL_CHAR_R;
5145 case 0x15f1: // (green)
5146 element = EL_CHAR_S;
5149 case 0x15f2: // (green)
5150 element = EL_CHAR_T;
5153 case 0x15f3: // (green)
5154 element = EL_CHAR_U;
5157 case 0x15f4: // (green)
5158 element = EL_CHAR_V;
5161 case 0x15f5: // (green)
5162 element = EL_CHAR_W;
5165 case 0x15f6: // (green)
5166 element = EL_CHAR_X;
5169 case 0x15f7: // (green)
5170 element = EL_CHAR_Y;
5173 case 0x15f8: // (green)
5174 element = EL_CHAR_Z;
5177 case 0x15f9: // (green)
5178 element = EL_CHAR_AUMLAUT;
5181 case 0x15fa: // (green)
5182 element = EL_CHAR_OUMLAUT;
5185 case 0x15fb: // (green)
5186 element = EL_CHAR_UUMLAUT;
5189 case 0x15fc: // (green)
5190 element = EL_CHAR_0;
5193 case 0x15fd: // (green)
5194 element = EL_CHAR_1;
5197 case 0x15fe: // (green)
5198 element = EL_CHAR_2;
5201 case 0x15ff: // (green)
5202 element = EL_CHAR_3;
5205 case 0x1600: // (green)
5206 element = EL_CHAR_4;
5209 case 0x1601: // (green)
5210 element = EL_CHAR_5;
5213 case 0x1602: // (green)
5214 element = EL_CHAR_6;
5217 case 0x1603: // (green)
5218 element = EL_CHAR_7;
5221 case 0x1604: // (green)
5222 element = EL_CHAR_8;
5225 case 0x1605: // (green)
5226 element = EL_CHAR_9;
5229 case 0x1606: // (green)
5230 element = EL_CHAR_PERIOD;
5233 case 0x1607: // (green)
5234 element = EL_CHAR_EXCLAM;
5237 case 0x1608: // (green)
5238 element = EL_CHAR_COLON;
5241 case 0x1609: // (green)
5242 element = EL_CHAR_LESS;
5245 case 0x160a: // (green)
5246 element = EL_CHAR_GREATER;
5249 case 0x160b: // (green)
5250 element = EL_CHAR_QUESTION;
5253 case 0x160c: // (green)
5254 element = EL_CHAR_COPYRIGHT;
5257 case 0x160d: // (green)
5258 element = EL_CHAR_UP;
5261 case 0x160e: // (green)
5262 element = EL_CHAR_DOWN;
5265 case 0x160f: // (green)
5266 element = EL_CHAR_BUTTON;
5269 case 0x1610: // (green)
5270 element = EL_CHAR_PLUS;
5273 case 0x1611: // (green)
5274 element = EL_CHAR_MINUS;
5277 case 0x1612: // (green)
5278 element = EL_CHAR_APOSTROPHE;
5281 case 0x1613: // (green)
5282 element = EL_CHAR_PARENLEFT;
5285 case 0x1614: // (green)
5286 element = EL_CHAR_PARENRIGHT;
5289 case 0x1615: // (blue steel)
5290 element = EL_STEEL_CHAR_A;
5293 case 0x1616: // (blue steel)
5294 element = EL_STEEL_CHAR_B;
5297 case 0x1617: // (blue steel)
5298 element = EL_STEEL_CHAR_C;
5301 case 0x1618: // (blue steel)
5302 element = EL_STEEL_CHAR_D;
5305 case 0x1619: // (blue steel)
5306 element = EL_STEEL_CHAR_E;
5309 case 0x161a: // (blue steel)
5310 element = EL_STEEL_CHAR_F;
5313 case 0x161b: // (blue steel)
5314 element = EL_STEEL_CHAR_G;
5317 case 0x161c: // (blue steel)
5318 element = EL_STEEL_CHAR_H;
5321 case 0x161d: // (blue steel)
5322 element = EL_STEEL_CHAR_I;
5325 case 0x161e: // (blue steel)
5326 element = EL_STEEL_CHAR_J;
5329 case 0x161f: // (blue steel)
5330 element = EL_STEEL_CHAR_K;
5333 case 0x1620: // (blue steel)
5334 element = EL_STEEL_CHAR_L;
5337 case 0x1621: // (blue steel)
5338 element = EL_STEEL_CHAR_M;
5341 case 0x1622: // (blue steel)
5342 element = EL_STEEL_CHAR_N;
5345 case 0x1623: // (blue steel)
5346 element = EL_STEEL_CHAR_O;
5349 case 0x1624: // (blue steel)
5350 element = EL_STEEL_CHAR_P;
5353 case 0x1625: // (blue steel)
5354 element = EL_STEEL_CHAR_Q;
5357 case 0x1626: // (blue steel)
5358 element = EL_STEEL_CHAR_R;
5361 case 0x1627: // (blue steel)
5362 element = EL_STEEL_CHAR_S;
5365 case 0x1628: // (blue steel)
5366 element = EL_STEEL_CHAR_T;
5369 case 0x1629: // (blue steel)
5370 element = EL_STEEL_CHAR_U;
5373 case 0x162a: // (blue steel)
5374 element = EL_STEEL_CHAR_V;
5377 case 0x162b: // (blue steel)
5378 element = EL_STEEL_CHAR_W;
5381 case 0x162c: // (blue steel)
5382 element = EL_STEEL_CHAR_X;
5385 case 0x162d: // (blue steel)
5386 element = EL_STEEL_CHAR_Y;
5389 case 0x162e: // (blue steel)
5390 element = EL_STEEL_CHAR_Z;
5393 case 0x162f: // (blue steel)
5394 element = EL_STEEL_CHAR_AUMLAUT;
5397 case 0x1630: // (blue steel)
5398 element = EL_STEEL_CHAR_OUMLAUT;
5401 case 0x1631: // (blue steel)
5402 element = EL_STEEL_CHAR_UUMLAUT;
5405 case 0x1632: // (blue steel)
5406 element = EL_STEEL_CHAR_0;
5409 case 0x1633: // (blue steel)
5410 element = EL_STEEL_CHAR_1;
5413 case 0x1634: // (blue steel)
5414 element = EL_STEEL_CHAR_2;
5417 case 0x1635: // (blue steel)
5418 element = EL_STEEL_CHAR_3;
5421 case 0x1636: // (blue steel)
5422 element = EL_STEEL_CHAR_4;
5425 case 0x1637: // (blue steel)
5426 element = EL_STEEL_CHAR_5;
5429 case 0x1638: // (blue steel)
5430 element = EL_STEEL_CHAR_6;
5433 case 0x1639: // (blue steel)
5434 element = EL_STEEL_CHAR_7;
5437 case 0x163a: // (blue steel)
5438 element = EL_STEEL_CHAR_8;
5441 case 0x163b: // (blue steel)
5442 element = EL_STEEL_CHAR_9;
5445 case 0x163c: // (blue steel)
5446 element = EL_STEEL_CHAR_PERIOD;
5449 case 0x163d: // (blue steel)
5450 element = EL_STEEL_CHAR_EXCLAM;
5453 case 0x163e: // (blue steel)
5454 element = EL_STEEL_CHAR_COLON;
5457 case 0x163f: // (blue steel)
5458 element = EL_STEEL_CHAR_LESS;
5461 case 0x1640: // (blue steel)
5462 element = EL_STEEL_CHAR_GREATER;
5465 case 0x1641: // (blue steel)
5466 element = EL_STEEL_CHAR_QUESTION;
5469 case 0x1642: // (blue steel)
5470 element = EL_STEEL_CHAR_COPYRIGHT;
5473 case 0x1643: // (blue steel)
5474 element = EL_STEEL_CHAR_UP;
5477 case 0x1644: // (blue steel)
5478 element = EL_STEEL_CHAR_DOWN;
5481 case 0x1645: // (blue steel)
5482 element = EL_STEEL_CHAR_BUTTON;
5485 case 0x1646: // (blue steel)
5486 element = EL_STEEL_CHAR_PLUS;
5489 case 0x1647: // (blue steel)
5490 element = EL_STEEL_CHAR_MINUS;
5493 case 0x1648: // (blue steel)
5494 element = EL_STEEL_CHAR_APOSTROPHE;
5497 case 0x1649: // (blue steel)
5498 element = EL_STEEL_CHAR_PARENLEFT;
5501 case 0x164a: // (blue steel)
5502 element = EL_STEEL_CHAR_PARENRIGHT;
5505 case 0x164b: // (green steel)
5506 element = EL_STEEL_CHAR_A;
5509 case 0x164c: // (green steel)
5510 element = EL_STEEL_CHAR_B;
5513 case 0x164d: // (green steel)
5514 element = EL_STEEL_CHAR_C;
5517 case 0x164e: // (green steel)
5518 element = EL_STEEL_CHAR_D;
5521 case 0x164f: // (green steel)
5522 element = EL_STEEL_CHAR_E;
5525 case 0x1650: // (green steel)
5526 element = EL_STEEL_CHAR_F;
5529 case 0x1651: // (green steel)
5530 element = EL_STEEL_CHAR_G;
5533 case 0x1652: // (green steel)
5534 element = EL_STEEL_CHAR_H;
5537 case 0x1653: // (green steel)
5538 element = EL_STEEL_CHAR_I;
5541 case 0x1654: // (green steel)
5542 element = EL_STEEL_CHAR_J;
5545 case 0x1655: // (green steel)
5546 element = EL_STEEL_CHAR_K;
5549 case 0x1656: // (green steel)
5550 element = EL_STEEL_CHAR_L;
5553 case 0x1657: // (green steel)
5554 element = EL_STEEL_CHAR_M;
5557 case 0x1658: // (green steel)
5558 element = EL_STEEL_CHAR_N;
5561 case 0x1659: // (green steel)
5562 element = EL_STEEL_CHAR_O;
5565 case 0x165a: // (green steel)
5566 element = EL_STEEL_CHAR_P;
5569 case 0x165b: // (green steel)
5570 element = EL_STEEL_CHAR_Q;
5573 case 0x165c: // (green steel)
5574 element = EL_STEEL_CHAR_R;
5577 case 0x165d: // (green steel)
5578 element = EL_STEEL_CHAR_S;
5581 case 0x165e: // (green steel)
5582 element = EL_STEEL_CHAR_T;
5585 case 0x165f: // (green steel)
5586 element = EL_STEEL_CHAR_U;
5589 case 0x1660: // (green steel)
5590 element = EL_STEEL_CHAR_V;
5593 case 0x1661: // (green steel)
5594 element = EL_STEEL_CHAR_W;
5597 case 0x1662: // (green steel)
5598 element = EL_STEEL_CHAR_X;
5601 case 0x1663: // (green steel)
5602 element = EL_STEEL_CHAR_Y;
5605 case 0x1664: // (green steel)
5606 element = EL_STEEL_CHAR_Z;
5609 case 0x1665: // (green steel)
5610 element = EL_STEEL_CHAR_AUMLAUT;
5613 case 0x1666: // (green steel)
5614 element = EL_STEEL_CHAR_OUMLAUT;
5617 case 0x1667: // (green steel)
5618 element = EL_STEEL_CHAR_UUMLAUT;
5621 case 0x1668: // (green steel)
5622 element = EL_STEEL_CHAR_0;
5625 case 0x1669: // (green steel)
5626 element = EL_STEEL_CHAR_1;
5629 case 0x166a: // (green steel)
5630 element = EL_STEEL_CHAR_2;
5633 case 0x166b: // (green steel)
5634 element = EL_STEEL_CHAR_3;
5637 case 0x166c: // (green steel)
5638 element = EL_STEEL_CHAR_4;
5641 case 0x166d: // (green steel)
5642 element = EL_STEEL_CHAR_5;
5645 case 0x166e: // (green steel)
5646 element = EL_STEEL_CHAR_6;
5649 case 0x166f: // (green steel)
5650 element = EL_STEEL_CHAR_7;
5653 case 0x1670: // (green steel)
5654 element = EL_STEEL_CHAR_8;
5657 case 0x1671: // (green steel)
5658 element = EL_STEEL_CHAR_9;
5661 case 0x1672: // (green steel)
5662 element = EL_STEEL_CHAR_PERIOD;
5665 case 0x1673: // (green steel)
5666 element = EL_STEEL_CHAR_EXCLAM;
5669 case 0x1674: // (green steel)
5670 element = EL_STEEL_CHAR_COLON;
5673 case 0x1675: // (green steel)
5674 element = EL_STEEL_CHAR_LESS;
5677 case 0x1676: // (green steel)
5678 element = EL_STEEL_CHAR_GREATER;
5681 case 0x1677: // (green steel)
5682 element = EL_STEEL_CHAR_QUESTION;
5685 case 0x1678: // (green steel)
5686 element = EL_STEEL_CHAR_COPYRIGHT;
5689 case 0x1679: // (green steel)
5690 element = EL_STEEL_CHAR_UP;
5693 case 0x167a: // (green steel)
5694 element = EL_STEEL_CHAR_DOWN;
5697 case 0x167b: // (green steel)
5698 element = EL_STEEL_CHAR_BUTTON;
5701 case 0x167c: // (green steel)
5702 element = EL_STEEL_CHAR_PLUS;
5705 case 0x167d: // (green steel)
5706 element = EL_STEEL_CHAR_MINUS;
5709 case 0x167e: // (green steel)
5710 element = EL_STEEL_CHAR_APOSTROPHE;
5713 case 0x167f: // (green steel)
5714 element = EL_STEEL_CHAR_PARENLEFT;
5717 case 0x1680: // (green steel)
5718 element = EL_STEEL_CHAR_PARENRIGHT;
5721 case 0x1681: // gate (red)
5722 element = EL_EM_GATE_1;
5725 case 0x1682: // secret gate (red)
5726 element = EL_EM_GATE_1_GRAY;
5729 case 0x1683: // gate (yellow)
5730 element = EL_EM_GATE_2;
5733 case 0x1684: // secret gate (yellow)
5734 element = EL_EM_GATE_2_GRAY;
5737 case 0x1685: // gate (blue)
5738 element = EL_EM_GATE_4;
5741 case 0x1686: // secret gate (blue)
5742 element = EL_EM_GATE_4_GRAY;
5745 case 0x1687: // gate (green)
5746 element = EL_EM_GATE_3;
5749 case 0x1688: // secret gate (green)
5750 element = EL_EM_GATE_3_GRAY;
5753 case 0x1689: // gate (white)
5754 element = EL_DC_GATE_WHITE;
5757 case 0x168a: // secret gate (white)
5758 element = EL_DC_GATE_WHITE_GRAY;
5761 case 0x168b: // secret gate (no key)
5762 element = EL_DC_GATE_FAKE_GRAY;
5766 element = EL_ROBOT_WHEEL;
5770 element = EL_DC_TIMEGATE_SWITCH;
5774 element = EL_ACID_POOL_BOTTOM;
5778 element = EL_ACID_POOL_TOPLEFT;
5782 element = EL_ACID_POOL_TOPRIGHT;
5786 element = EL_ACID_POOL_BOTTOMLEFT;
5790 element = EL_ACID_POOL_BOTTOMRIGHT;
5794 element = EL_STEELWALL;
5798 element = EL_STEELWALL_SLIPPERY;
5801 case 0x1695: // steel wall (not round)
5802 element = EL_STEELWALL;
5805 case 0x1696: // steel wall (left)
5806 element = EL_DC_STEELWALL_1_LEFT;
5809 case 0x1697: // steel wall (bottom)
5810 element = EL_DC_STEELWALL_1_BOTTOM;
5813 case 0x1698: // steel wall (right)
5814 element = EL_DC_STEELWALL_1_RIGHT;
5817 case 0x1699: // steel wall (top)
5818 element = EL_DC_STEELWALL_1_TOP;
5821 case 0x169a: // steel wall (left/bottom)
5822 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5825 case 0x169b: // steel wall (right/bottom)
5826 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5829 case 0x169c: // steel wall (right/top)
5830 element = EL_DC_STEELWALL_1_TOPRIGHT;
5833 case 0x169d: // steel wall (left/top)
5834 element = EL_DC_STEELWALL_1_TOPLEFT;
5837 case 0x169e: // steel wall (right/bottom small)
5838 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5841 case 0x169f: // steel wall (left/bottom small)
5842 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5845 case 0x16a0: // steel wall (right/top small)
5846 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5849 case 0x16a1: // steel wall (left/top small)
5850 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5853 case 0x16a2: // steel wall (left/right)
5854 element = EL_DC_STEELWALL_1_VERTICAL;
5857 case 0x16a3: // steel wall (top/bottom)
5858 element = EL_DC_STEELWALL_1_HORIZONTAL;
5861 case 0x16a4: // steel wall 2 (left end)
5862 element = EL_DC_STEELWALL_2_LEFT;
5865 case 0x16a5: // steel wall 2 (right end)
5866 element = EL_DC_STEELWALL_2_RIGHT;
5869 case 0x16a6: // steel wall 2 (top end)
5870 element = EL_DC_STEELWALL_2_TOP;
5873 case 0x16a7: // steel wall 2 (bottom end)
5874 element = EL_DC_STEELWALL_2_BOTTOM;
5877 case 0x16a8: // steel wall 2 (left/right)
5878 element = EL_DC_STEELWALL_2_HORIZONTAL;
5881 case 0x16a9: // steel wall 2 (up/down)
5882 element = EL_DC_STEELWALL_2_VERTICAL;
5885 case 0x16aa: // steel wall 2 (mid)
5886 element = EL_DC_STEELWALL_2_MIDDLE;
5890 element = EL_SIGN_EXCLAMATION;
5894 element = EL_SIGN_RADIOACTIVITY;
5898 element = EL_SIGN_STOP;
5902 element = EL_SIGN_WHEELCHAIR;
5906 element = EL_SIGN_PARKING;
5910 element = EL_SIGN_NO_ENTRY;
5914 element = EL_SIGN_HEART;
5918 element = EL_SIGN_GIVE_WAY;
5922 element = EL_SIGN_ENTRY_FORBIDDEN;
5926 element = EL_SIGN_EMERGENCY_EXIT;
5930 element = EL_SIGN_YIN_YANG;
5934 element = EL_WALL_EMERALD;
5938 element = EL_WALL_DIAMOND;
5942 element = EL_WALL_PEARL;
5946 element = EL_WALL_CRYSTAL;
5950 element = EL_INVISIBLE_WALL;
5954 element = EL_INVISIBLE_STEELWALL;
5958 // EL_INVISIBLE_SAND
5961 element = EL_LIGHT_SWITCH;
5965 element = EL_ENVELOPE_1;
5969 if (element >= 0x0117 && element <= 0x036e) // (?)
5970 element = EL_DIAMOND;
5971 else if (element >= 0x042d && element <= 0x0684) // (?)
5972 element = EL_EMERALD;
5973 else if (element >= 0x157c && element <= 0x158b)
5975 else if (element >= 0x1590 && element <= 0x159f)
5976 element = EL_DC_LANDMINE;
5977 else if (element >= 0x16bc && element <= 0x16cb)
5978 element = EL_INVISIBLE_SAND;
5981 Warn("unknown Diamond Caves element 0x%04x", element);
5983 element = EL_UNKNOWN;
5988 return getMappedElement(element);
5991 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5993 byte header[DC_LEVEL_HEADER_SIZE];
5995 int envelope_header_pos = 62;
5996 int envelope_content_pos = 94;
5997 int level_name_pos = 251;
5998 int level_author_pos = 292;
5999 int envelope_header_len;
6000 int envelope_content_len;
6002 int level_author_len;
6004 int num_yamyam_contents;
6007 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6009 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6011 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6013 header[i * 2 + 0] = header_word >> 8;
6014 header[i * 2 + 1] = header_word & 0xff;
6017 // read some values from level header to check level decoding integrity
6018 fieldx = header[6] | (header[7] << 8);
6019 fieldy = header[8] | (header[9] << 8);
6020 num_yamyam_contents = header[60] | (header[61] << 8);
6022 // do some simple sanity checks to ensure that level was correctly decoded
6023 if (fieldx < 1 || fieldx > 256 ||
6024 fieldy < 1 || fieldy > 256 ||
6025 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6027 level->no_valid_file = TRUE;
6029 Warn("cannot decode level from stream -- using empty level");
6034 // maximum envelope header size is 31 bytes
6035 envelope_header_len = header[envelope_header_pos];
6036 // maximum envelope content size is 110 (156?) bytes
6037 envelope_content_len = header[envelope_content_pos];
6039 // maximum level title size is 40 bytes
6040 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6041 // maximum level author size is 30 (51?) bytes
6042 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6046 for (i = 0; i < envelope_header_len; i++)
6047 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6048 level->envelope[0].text[envelope_size++] =
6049 header[envelope_header_pos + 1 + i];
6051 if (envelope_header_len > 0 && envelope_content_len > 0)
6053 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6054 level->envelope[0].text[envelope_size++] = '\n';
6055 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6056 level->envelope[0].text[envelope_size++] = '\n';
6059 for (i = 0; i < envelope_content_len; i++)
6060 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6061 level->envelope[0].text[envelope_size++] =
6062 header[envelope_content_pos + 1 + i];
6064 level->envelope[0].text[envelope_size] = '\0';
6066 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6067 level->envelope[0].ysize = 10;
6068 level->envelope[0].autowrap = TRUE;
6069 level->envelope[0].centered = TRUE;
6071 for (i = 0; i < level_name_len; i++)
6072 level->name[i] = header[level_name_pos + 1 + i];
6073 level->name[level_name_len] = '\0';
6075 for (i = 0; i < level_author_len; i++)
6076 level->author[i] = header[level_author_pos + 1 + i];
6077 level->author[level_author_len] = '\0';
6079 num_yamyam_contents = header[60] | (header[61] << 8);
6080 level->num_yamyam_contents =
6081 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6083 for (i = 0; i < num_yamyam_contents; i++)
6085 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6087 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6088 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6090 if (i < MAX_ELEMENT_CONTENTS)
6091 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6095 fieldx = header[6] | (header[7] << 8);
6096 fieldy = header[8] | (header[9] << 8);
6097 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6098 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6100 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6102 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6103 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6105 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6106 level->field[x][y] = getMappedElement_DC(element_dc);
6109 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6110 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6111 level->field[x][y] = EL_PLAYER_1;
6113 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6114 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6115 level->field[x][y] = EL_PLAYER_2;
6117 level->gems_needed = header[18] | (header[19] << 8);
6119 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6120 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6121 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6122 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6123 level->score[SC_NUT] = header[28] | (header[29] << 8);
6124 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6125 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6126 level->score[SC_BUG] = header[34] | (header[35] << 8);
6127 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6128 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6129 level->score[SC_KEY] = header[40] | (header[41] << 8);
6130 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6132 level->time = header[44] | (header[45] << 8);
6134 level->amoeba_speed = header[46] | (header[47] << 8);
6135 level->time_light = header[48] | (header[49] << 8);
6136 level->time_timegate = header[50] | (header[51] << 8);
6137 level->time_wheel = header[52] | (header[53] << 8);
6138 level->time_magic_wall = header[54] | (header[55] << 8);
6139 level->extra_time = header[56] | (header[57] << 8);
6140 level->shield_normal_time = header[58] | (header[59] << 8);
6142 // shield and extra time elements do not have a score
6143 level->score[SC_SHIELD] = 0;
6144 level->extra_time_score = 0;
6146 // set time for normal and deadly shields to the same value
6147 level->shield_deadly_time = level->shield_normal_time;
6149 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6150 // can slip down from flat walls, like normal walls and steel walls
6151 level->em_slippery_gems = TRUE;
6153 // time score is counted for each 10 seconds left in Diamond Caves levels
6154 level->time_score_base = 10;
6157 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6158 struct LevelFileInfo *level_file_info,
6159 boolean level_info_only)
6161 char *filename = level_file_info->filename;
6163 int num_magic_bytes = 8;
6164 char magic_bytes[num_magic_bytes + 1];
6165 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6167 if (!(file = openFile(filename, MODE_READ)))
6169 level->no_valid_file = TRUE;
6171 if (!level_info_only)
6172 Warn("cannot read level '%s' -- using empty level", filename);
6177 // fseek(file, 0x0000, SEEK_SET);
6179 if (level_file_info->packed)
6181 // read "magic bytes" from start of file
6182 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6183 magic_bytes[0] = '\0';
6185 // check "magic bytes" for correct file format
6186 if (!strPrefix(magic_bytes, "DC2"))
6188 level->no_valid_file = TRUE;
6190 Warn("unknown DC level file '%s' -- using empty level", filename);
6195 if (strPrefix(magic_bytes, "DC2Win95") ||
6196 strPrefix(magic_bytes, "DC2Win98"))
6198 int position_first_level = 0x00fa;
6199 int extra_bytes = 4;
6202 // advance file stream to first level inside the level package
6203 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6205 // each block of level data is followed by block of non-level data
6206 num_levels_to_skip *= 2;
6208 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6209 while (num_levels_to_skip >= 0)
6211 // advance file stream to next level inside the level package
6212 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6214 level->no_valid_file = TRUE;
6216 Warn("cannot fseek in file '%s' -- using empty level", filename);
6221 // skip apparently unused extra bytes following each level
6222 ReadUnusedBytesFromFile(file, extra_bytes);
6224 // read size of next level in level package
6225 skip_bytes = getFile32BitLE(file);
6227 num_levels_to_skip--;
6232 level->no_valid_file = TRUE;
6234 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6240 LoadLevelFromFileStream_DC(file, level);
6246 // ----------------------------------------------------------------------------
6247 // functions for loading SB level
6248 // ----------------------------------------------------------------------------
6250 int getMappedElement_SB(int element_ascii, boolean use_ces)
6258 sb_element_mapping[] =
6260 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6261 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6262 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6263 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6264 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6265 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6266 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6267 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6274 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6275 if (element_ascii == sb_element_mapping[i].ascii)
6276 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6278 return EL_UNDEFINED;
6281 static void SetLevelSettings_SB(struct LevelInfo *level)
6285 level->use_step_counter = TRUE;
6288 level->score[SC_TIME_BONUS] = 0;
6289 level->time_score_base = 1;
6290 level->rate_time_over_score = TRUE;
6293 level->auto_exit_sokoban = TRUE;
6296 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6297 struct LevelFileInfo *level_file_info,
6298 boolean level_info_only)
6300 char *filename = level_file_info->filename;
6301 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6302 char last_comment[MAX_LINE_LEN];
6303 char level_name[MAX_LINE_LEN];
6306 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6307 boolean read_continued_line = FALSE;
6308 boolean reading_playfield = FALSE;
6309 boolean got_valid_playfield_line = FALSE;
6310 boolean invalid_playfield_char = FALSE;
6311 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6312 int file_level_nr = 0;
6313 int x = 0, y = 0; // initialized to make compilers happy
6315 last_comment[0] = '\0';
6316 level_name[0] = '\0';
6318 if (!(file = openFile(filename, MODE_READ)))
6320 level->no_valid_file = TRUE;
6322 if (!level_info_only)
6323 Warn("cannot read level '%s' -- using empty level", filename);
6328 while (!checkEndOfFile(file))
6330 // level successfully read, but next level may follow here
6331 if (!got_valid_playfield_line && reading_playfield)
6333 // read playfield from single level file -- skip remaining file
6334 if (!level_file_info->packed)
6337 if (file_level_nr >= num_levels_to_skip)
6342 last_comment[0] = '\0';
6343 level_name[0] = '\0';
6345 reading_playfield = FALSE;
6348 got_valid_playfield_line = FALSE;
6350 // read next line of input file
6351 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6354 // cut trailing line break (this can be newline and/or carriage return)
6355 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6356 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6359 // copy raw input line for later use (mainly debugging output)
6360 strcpy(line_raw, line);
6362 if (read_continued_line)
6364 // append new line to existing line, if there is enough space
6365 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6366 strcat(previous_line, line_ptr);
6368 strcpy(line, previous_line); // copy storage buffer to line
6370 read_continued_line = FALSE;
6373 // if the last character is '\', continue at next line
6374 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6376 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6377 strcpy(previous_line, line); // copy line to storage buffer
6379 read_continued_line = TRUE;
6385 if (line[0] == '\0')
6388 // extract comment text from comment line
6391 for (line_ptr = line; *line_ptr; line_ptr++)
6392 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6395 strcpy(last_comment, line_ptr);
6400 // extract level title text from line containing level title
6401 if (line[0] == '\'')
6403 strcpy(level_name, &line[1]);
6405 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6406 level_name[strlen(level_name) - 1] = '\0';
6411 // skip lines containing only spaces (or empty lines)
6412 for (line_ptr = line; *line_ptr; line_ptr++)
6413 if (*line_ptr != ' ')
6415 if (*line_ptr == '\0')
6418 // at this point, we have found a line containing part of a playfield
6420 got_valid_playfield_line = TRUE;
6422 if (!reading_playfield)
6424 reading_playfield = TRUE;
6425 invalid_playfield_char = FALSE;
6427 for (x = 0; x < MAX_LEV_FIELDX; x++)
6428 for (y = 0; y < MAX_LEV_FIELDY; y++)
6429 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6434 // start with topmost tile row
6438 // skip playfield line if larger row than allowed
6439 if (y >= MAX_LEV_FIELDY)
6442 // start with leftmost tile column
6445 // read playfield elements from line
6446 for (line_ptr = line; *line_ptr; line_ptr++)
6448 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6450 // stop parsing playfield line if larger column than allowed
6451 if (x >= MAX_LEV_FIELDX)
6454 if (mapped_sb_element == EL_UNDEFINED)
6456 invalid_playfield_char = TRUE;
6461 level->field[x][y] = mapped_sb_element;
6463 // continue with next tile column
6466 level->fieldx = MAX(x, level->fieldx);
6469 if (invalid_playfield_char)
6471 // if first playfield line, treat invalid lines as comment lines
6473 reading_playfield = FALSE;
6478 // continue with next tile row
6486 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6487 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6489 if (!reading_playfield)
6491 level->no_valid_file = TRUE;
6493 Warn("cannot read level '%s' -- using empty level", filename);
6498 if (*level_name != '\0')
6500 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6501 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6503 else if (*last_comment != '\0')
6505 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6506 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6510 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6513 // set all empty fields beyond the border walls to invisible steel wall
6514 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6516 if ((x == 0 || x == level->fieldx - 1 ||
6517 y == 0 || y == level->fieldy - 1) &&
6518 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6519 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6520 level->field, level->fieldx, level->fieldy);
6523 // set special level settings for Sokoban levels
6524 SetLevelSettings_SB(level);
6526 if (load_xsb_to_ces)
6528 // special global settings can now be set in level template
6529 level->use_custom_template = TRUE;
6534 // -------------------------------------------------------------------------
6535 // functions for handling native levels
6536 // -------------------------------------------------------------------------
6538 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6539 struct LevelFileInfo *level_file_info,
6540 boolean level_info_only)
6544 // determine position of requested level inside level package
6545 if (level_file_info->packed)
6546 pos = level_file_info->nr - leveldir_current->first_level;
6548 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6549 level->no_valid_file = TRUE;
6552 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6553 struct LevelFileInfo *level_file_info,
6554 boolean level_info_only)
6556 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6557 level->no_valid_file = TRUE;
6560 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6561 struct LevelFileInfo *level_file_info,
6562 boolean level_info_only)
6566 // determine position of requested level inside level package
6567 if (level_file_info->packed)
6568 pos = level_file_info->nr - leveldir_current->first_level;
6570 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6571 level->no_valid_file = TRUE;
6574 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6575 struct LevelFileInfo *level_file_info,
6576 boolean level_info_only)
6578 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6579 level->no_valid_file = TRUE;
6582 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6584 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6585 CopyNativeLevel_RND_to_BD(level);
6586 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6587 CopyNativeLevel_RND_to_EM(level);
6588 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6589 CopyNativeLevel_RND_to_SP(level);
6590 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6591 CopyNativeLevel_RND_to_MM(level);
6594 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6596 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6597 CopyNativeLevel_BD_to_RND(level);
6598 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6599 CopyNativeLevel_EM_to_RND(level);
6600 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6601 CopyNativeLevel_SP_to_RND(level);
6602 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6603 CopyNativeLevel_MM_to_RND(level);
6606 void SaveNativeLevel(struct LevelInfo *level)
6608 // saving native level files only supported for some game engines
6609 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6610 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6613 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6614 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6615 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6616 char *filename = getLevelFilenameFromBasename(basename);
6618 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6621 boolean success = FALSE;
6623 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6625 CopyNativeLevel_RND_to_BD(level);
6626 // CopyNativeTape_RND_to_BD(level);
6628 success = SaveNativeLevel_BD(filename);
6630 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6632 CopyNativeLevel_RND_to_SP(level);
6633 CopyNativeTape_RND_to_SP(level);
6635 success = SaveNativeLevel_SP(filename);
6639 Request("Native level file saved!", REQ_CONFIRM);
6641 Request("Failed to save native level file!", REQ_CONFIRM);
6645 // ----------------------------------------------------------------------------
6646 // functions for loading generic level
6647 // ----------------------------------------------------------------------------
6649 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6650 struct LevelFileInfo *level_file_info,
6651 boolean level_info_only)
6653 // always start with reliable default values
6654 setLevelInfoToDefaults(level, level_info_only, TRUE);
6656 switch (level_file_info->type)
6658 case LEVEL_FILE_TYPE_RND:
6659 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6662 case LEVEL_FILE_TYPE_BD:
6663 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6664 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6667 case LEVEL_FILE_TYPE_EM:
6668 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6669 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6672 case LEVEL_FILE_TYPE_SP:
6673 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6674 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6677 case LEVEL_FILE_TYPE_MM:
6678 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6679 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6682 case LEVEL_FILE_TYPE_DC:
6683 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6686 case LEVEL_FILE_TYPE_SB:
6687 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6691 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6695 // if level file is invalid, restore level structure to default values
6696 if (level->no_valid_file)
6697 setLevelInfoToDefaults(level, level_info_only, FALSE);
6699 if (check_special_flags("use_native_bd_game_engine"))
6700 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6702 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6703 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6705 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6706 CopyNativeLevel_Native_to_RND(level);
6709 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6711 static struct LevelFileInfo level_file_info;
6713 // always start with reliable default values
6714 setFileInfoToDefaults(&level_file_info);
6716 level_file_info.nr = 0; // unknown level number
6717 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6719 setString(&level_file_info.filename, filename);
6721 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6724 static void LoadLevel_InitVersion(struct LevelInfo *level)
6728 if (leveldir_current == NULL) // only when dumping level
6731 // all engine modifications also valid for levels which use latest engine
6732 if (level->game_version < VERSION_IDENT(3,2,0,5))
6734 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6735 level->time_score_base = 10;
6738 if (leveldir_current->latest_engine)
6740 // ---------- use latest game engine --------------------------------------
6742 /* For all levels which are forced to use the latest game engine version
6743 (normally all but user contributed, private and undefined levels), set
6744 the game engine version to the actual version; this allows for actual
6745 corrections in the game engine to take effect for existing, converted
6746 levels (from "classic" or other existing games) to make the emulation
6747 of the corresponding game more accurate, while (hopefully) not breaking
6748 existing levels created from other players. */
6750 level->game_version = GAME_VERSION_ACTUAL;
6752 /* Set special EM style gems behaviour: EM style gems slip down from
6753 normal, steel and growing wall. As this is a more fundamental change,
6754 it seems better to set the default behaviour to "off" (as it is more
6755 natural) and make it configurable in the level editor (as a property
6756 of gem style elements). Already existing converted levels (neither
6757 private nor contributed levels) are changed to the new behaviour. */
6759 if (level->file_version < FILE_VERSION_2_0)
6760 level->em_slippery_gems = TRUE;
6765 // ---------- use game engine the level was created with --------------------
6767 /* For all levels which are not forced to use the latest game engine
6768 version (normally user contributed, private and undefined levels),
6769 use the version of the game engine the levels were created for.
6771 Since 2.0.1, the game engine version is now directly stored
6772 in the level file (chunk "VERS"), so there is no need anymore
6773 to set the game version from the file version (except for old,
6774 pre-2.0 levels, where the game version is still taken from the
6775 file format version used to store the level -- see above). */
6777 // player was faster than enemies in 1.0.0 and before
6778 if (level->file_version == FILE_VERSION_1_0)
6779 for (i = 0; i < MAX_PLAYERS; i++)
6780 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6782 // default behaviour for EM style gems was "slippery" only in 2.0.1
6783 if (level->game_version == VERSION_IDENT(2,0,1,0))
6784 level->em_slippery_gems = TRUE;
6786 // springs could be pushed over pits before (pre-release version) 2.2.0
6787 if (level->game_version < VERSION_IDENT(2,2,0,0))
6788 level->use_spring_bug = TRUE;
6790 if (level->game_version < VERSION_IDENT(3,2,0,5))
6792 // time orb caused limited time in endless time levels before 3.2.0-5
6793 level->use_time_orb_bug = TRUE;
6795 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6796 level->block_snap_field = FALSE;
6798 // extra time score was same value as time left score before 3.2.0-5
6799 level->extra_time_score = level->score[SC_TIME_BONUS];
6802 if (level->game_version < VERSION_IDENT(3,2,0,7))
6804 // default behaviour for snapping was "not continuous" before 3.2.0-7
6805 level->continuous_snapping = FALSE;
6808 // only few elements were able to actively move into acid before 3.1.0
6809 // trigger settings did not exist before 3.1.0; set to default "any"
6810 if (level->game_version < VERSION_IDENT(3,1,0,0))
6812 // correct "can move into acid" settings (all zero in old levels)
6814 level->can_move_into_acid_bits = 0; // nothing can move into acid
6815 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6817 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6818 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6819 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6820 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6822 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6823 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6825 // correct trigger settings (stored as zero == "none" in old levels)
6827 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6829 int element = EL_CUSTOM_START + i;
6830 struct ElementInfo *ei = &element_info[element];
6832 for (j = 0; j < ei->num_change_pages; j++)
6834 struct ElementChangeInfo *change = &ei->change_page[j];
6836 change->trigger_player = CH_PLAYER_ANY;
6837 change->trigger_page = CH_PAGE_ANY;
6842 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6844 int element = EL_CUSTOM_256;
6845 struct ElementInfo *ei = &element_info[element];
6846 struct ElementChangeInfo *change = &ei->change_page[0];
6848 /* This is needed to fix a problem that was caused by a bugfix in function
6849 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6850 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6851 not replace walkable elements, but instead just placed the player on it,
6852 without placing the Sokoban field under the player). Unfortunately, this
6853 breaks "Snake Bite" style levels when the snake is halfway through a door
6854 that just closes (the snake head is still alive and can be moved in this
6855 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6856 player (without Sokoban element) which then gets killed as designed). */
6858 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6859 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6860 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6861 change->target_element = EL_PLAYER_1;
6864 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6865 if (level->game_version < VERSION_IDENT(3,2,5,0))
6867 /* This is needed to fix a problem that was caused by a bugfix in function
6868 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6869 corrects the behaviour when a custom element changes to another custom
6870 element with a higher element number that has change actions defined.
6871 Normally, only one change per frame is allowed for custom elements.
6872 Therefore, it is checked if a custom element already changed in the
6873 current frame; if it did, subsequent changes are suppressed.
6874 Unfortunately, this is only checked for element changes, but not for
6875 change actions, which are still executed. As the function above loops
6876 through all custom elements from lower to higher, an element change
6877 resulting in a lower CE number won't be checked again, while a target
6878 element with a higher number will also be checked, and potential change
6879 actions will get executed for this CE, too (which is wrong), while
6880 further changes are ignored (which is correct). As this bugfix breaks
6881 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6882 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6883 behaviour for existing levels and tapes that make use of this bug */
6885 level->use_action_after_change_bug = TRUE;
6888 // not centering level after relocating player was default only in 3.2.3
6889 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6890 level->shifted_relocation = TRUE;
6892 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6893 if (level->game_version < VERSION_IDENT(3,2,6,0))
6894 level->em_explodes_by_fire = TRUE;
6896 // levels were solved by the first player entering an exit up to 4.1.0.0
6897 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6898 level->solved_by_one_player = TRUE;
6900 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6901 if (level->game_version < VERSION_IDENT(4,1,1,1))
6902 level->use_life_bugs = TRUE;
6904 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6905 if (level->game_version < VERSION_IDENT(4,1,1,1))
6906 level->sb_objects_needed = FALSE;
6908 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6909 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6910 level->finish_dig_collect = FALSE;
6912 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6913 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6914 level->keep_walkable_ce = TRUE;
6917 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6919 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6922 // check if this level is (not) a Sokoban level
6923 for (y = 0; y < level->fieldy; y++)
6924 for (x = 0; x < level->fieldx; x++)
6925 if (!IS_SB_ELEMENT(Tile[x][y]))
6926 is_sokoban_level = FALSE;
6928 if (is_sokoban_level)
6930 // set special level settings for Sokoban levels
6931 SetLevelSettings_SB(level);
6935 static void LoadLevel_InitSettings(struct LevelInfo *level)
6937 // adjust level settings for (non-native) Sokoban-style levels
6938 LoadLevel_InitSettings_SB(level);
6940 // rename levels with title "nameless level" or if renaming is forced
6941 if (leveldir_current->empty_level_name != NULL &&
6942 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6943 leveldir_current->force_level_name))
6944 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6945 leveldir_current->empty_level_name, level_nr);
6948 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6952 // map elements that have changed in newer versions
6953 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6954 level->game_version);
6955 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6956 for (x = 0; x < 3; x++)
6957 for (y = 0; y < 3; y++)
6958 level->yamyam_content[i].e[x][y] =
6959 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6960 level->game_version);
6964 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6968 // map custom element change events that have changed in newer versions
6969 // (these following values were accidentally changed in version 3.0.1)
6970 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6971 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6973 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6975 int element = EL_CUSTOM_START + i;
6977 // order of checking and copying events to be mapped is important
6978 // (do not change the start and end value -- they are constant)
6979 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6981 if (HAS_CHANGE_EVENT(element, j - 2))
6983 SET_CHANGE_EVENT(element, j - 2, FALSE);
6984 SET_CHANGE_EVENT(element, j, TRUE);
6988 // order of checking and copying events to be mapped is important
6989 // (do not change the start and end value -- they are constant)
6990 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6992 if (HAS_CHANGE_EVENT(element, j - 1))
6994 SET_CHANGE_EVENT(element, j - 1, FALSE);
6995 SET_CHANGE_EVENT(element, j, TRUE);
7001 // initialize "can_change" field for old levels with only one change page
7002 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7004 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7006 int element = EL_CUSTOM_START + i;
7008 if (CAN_CHANGE(element))
7009 element_info[element].change->can_change = TRUE;
7013 // correct custom element values (for old levels without these options)
7014 if (level->game_version < VERSION_IDENT(3,1,1,0))
7016 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7018 int element = EL_CUSTOM_START + i;
7019 struct ElementInfo *ei = &element_info[element];
7021 if (ei->access_direction == MV_NO_DIRECTION)
7022 ei->access_direction = MV_ALL_DIRECTIONS;
7026 // correct custom element values (fix invalid values for all versions)
7029 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7031 int element = EL_CUSTOM_START + i;
7032 struct ElementInfo *ei = &element_info[element];
7034 for (j = 0; j < ei->num_change_pages; j++)
7036 struct ElementChangeInfo *change = &ei->change_page[j];
7038 if (change->trigger_player == CH_PLAYER_NONE)
7039 change->trigger_player = CH_PLAYER_ANY;
7041 if (change->trigger_side == CH_SIDE_NONE)
7042 change->trigger_side = CH_SIDE_ANY;
7047 // initialize "can_explode" field for old levels which did not store this
7048 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7049 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7051 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7053 int element = EL_CUSTOM_START + i;
7055 if (EXPLODES_1X1_OLD(element))
7056 element_info[element].explosion_type = EXPLODES_1X1;
7058 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7059 EXPLODES_SMASHED(element) ||
7060 EXPLODES_IMPACT(element)));
7064 // correct previously hard-coded move delay values for maze runner style
7065 if (level->game_version < VERSION_IDENT(3,1,1,0))
7067 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7069 int element = EL_CUSTOM_START + i;
7071 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7073 // previously hard-coded and therefore ignored
7074 element_info[element].move_delay_fixed = 9;
7075 element_info[element].move_delay_random = 0;
7080 // set some other uninitialized values of custom elements in older levels
7081 if (level->game_version < VERSION_IDENT(3,1,0,0))
7083 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7085 int element = EL_CUSTOM_START + i;
7087 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7089 element_info[element].explosion_delay = 17;
7090 element_info[element].ignition_delay = 8;
7094 // set mouse click change events to work for left/middle/right mouse button
7095 if (level->game_version < VERSION_IDENT(4,2,3,0))
7097 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7099 int element = EL_CUSTOM_START + i;
7100 struct ElementInfo *ei = &element_info[element];
7102 for (j = 0; j < ei->num_change_pages; j++)
7104 struct ElementChangeInfo *change = &ei->change_page[j];
7106 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7107 change->has_event[CE_PRESSED_BY_MOUSE] ||
7108 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7109 change->has_event[CE_MOUSE_PRESSED_ON_X])
7110 change->trigger_side = CH_SIDE_ANY;
7116 static void LoadLevel_InitElements(struct LevelInfo *level)
7118 LoadLevel_InitStandardElements(level);
7120 if (level->file_has_custom_elements)
7121 LoadLevel_InitCustomElements(level);
7123 // initialize element properties for level editor etc.
7124 InitElementPropertiesEngine(level->game_version);
7125 InitElementPropertiesGfxElement();
7128 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7132 // map elements that have changed in newer versions
7133 for (y = 0; y < level->fieldy; y++)
7134 for (x = 0; x < level->fieldx; x++)
7135 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7136 level->game_version);
7138 // clear unused playfield data (nicer if level gets resized in editor)
7139 for (x = 0; x < MAX_LEV_FIELDX; x++)
7140 for (y = 0; y < MAX_LEV_FIELDY; y++)
7141 if (x >= level->fieldx || y >= level->fieldy)
7142 level->field[x][y] = EL_EMPTY;
7144 // copy elements to runtime playfield array
7145 for (x = 0; x < MAX_LEV_FIELDX; x++)
7146 for (y = 0; y < MAX_LEV_FIELDY; y++)
7147 Tile[x][y] = level->field[x][y];
7149 // initialize level size variables for faster access
7150 lev_fieldx = level->fieldx;
7151 lev_fieldy = level->fieldy;
7153 // determine border element for this level
7154 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7155 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7160 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7162 struct LevelFileInfo *level_file_info = &level->file_info;
7164 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7165 CopyNativeLevel_RND_to_Native(level);
7168 static void LoadLevelTemplate_LoadAndInit(void)
7170 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7172 LoadLevel_InitVersion(&level_template);
7173 LoadLevel_InitElements(&level_template);
7174 LoadLevel_InitSettings(&level_template);
7176 ActivateLevelTemplate();
7179 void LoadLevelTemplate(int nr)
7181 if (!fileExists(getGlobalLevelTemplateFilename()))
7183 Warn("no level template found for this level");
7188 setLevelFileInfo(&level_template.file_info, nr);
7190 LoadLevelTemplate_LoadAndInit();
7193 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7195 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7197 LoadLevelTemplate_LoadAndInit();
7200 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7202 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7204 if (level.use_custom_template)
7206 if (network_level != NULL)
7207 LoadNetworkLevelTemplate(network_level);
7209 LoadLevelTemplate(-1);
7212 LoadLevel_InitVersion(&level);
7213 LoadLevel_InitElements(&level);
7214 LoadLevel_InitPlayfield(&level);
7215 LoadLevel_InitSettings(&level);
7217 LoadLevel_InitNativeEngines(&level);
7220 void LoadLevel(int nr)
7222 SetLevelSetInfo(leveldir_current->identifier, nr);
7224 setLevelFileInfo(&level.file_info, nr);
7226 LoadLevel_LoadAndInit(NULL);
7229 void LoadLevelInfoOnly(int nr)
7231 setLevelFileInfo(&level.file_info, nr);
7233 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7236 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7238 SetLevelSetInfo(network_level->leveldir_identifier,
7239 network_level->file_info.nr);
7241 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7243 LoadLevel_LoadAndInit(network_level);
7246 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7250 chunk_size += putFileVersion(file, level->file_version);
7251 chunk_size += putFileVersion(file, level->game_version);
7256 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7260 chunk_size += putFile16BitBE(file, level->creation_date.year);
7261 chunk_size += putFile8Bit(file, level->creation_date.month);
7262 chunk_size += putFile8Bit(file, level->creation_date.day);
7267 #if ENABLE_HISTORIC_CHUNKS
7268 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7272 putFile8Bit(file, level->fieldx);
7273 putFile8Bit(file, level->fieldy);
7275 putFile16BitBE(file, level->time);
7276 putFile16BitBE(file, level->gems_needed);
7278 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7279 putFile8Bit(file, level->name[i]);
7281 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7282 putFile8Bit(file, level->score[i]);
7284 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7285 for (y = 0; y < 3; y++)
7286 for (x = 0; x < 3; x++)
7287 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7288 level->yamyam_content[i].e[x][y]));
7289 putFile8Bit(file, level->amoeba_speed);
7290 putFile8Bit(file, level->time_magic_wall);
7291 putFile8Bit(file, level->time_wheel);
7292 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7293 level->amoeba_content));
7294 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7295 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7296 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7297 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7299 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7301 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7302 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7303 putFile32BitBE(file, level->can_move_into_acid_bits);
7304 putFile8Bit(file, level->dont_collide_with_bits);
7306 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7307 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7309 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7310 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7311 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7313 putFile8Bit(file, level->game_engine_type);
7315 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7319 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7324 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7325 chunk_size += putFile8Bit(file, level->name[i]);
7330 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7335 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7336 chunk_size += putFile8Bit(file, level->author[i]);
7341 #if ENABLE_HISTORIC_CHUNKS
7342 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7347 for (y = 0; y < level->fieldy; y++)
7348 for (x = 0; x < level->fieldx; x++)
7349 if (level->encoding_16bit_field)
7350 chunk_size += putFile16BitBE(file, level->field[x][y]);
7352 chunk_size += putFile8Bit(file, level->field[x][y]);
7358 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7363 for (y = 0; y < level->fieldy; y++)
7364 for (x = 0; x < level->fieldx; x++)
7365 chunk_size += putFile16BitBE(file, level->field[x][y]);
7370 #if ENABLE_HISTORIC_CHUNKS
7371 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7375 putFile8Bit(file, EL_YAMYAM);
7376 putFile8Bit(file, level->num_yamyam_contents);
7377 putFile8Bit(file, 0);
7378 putFile8Bit(file, 0);
7380 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7381 for (y = 0; y < 3; y++)
7382 for (x = 0; x < 3; x++)
7383 if (level->encoding_16bit_field)
7384 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7386 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7390 #if ENABLE_HISTORIC_CHUNKS
7391 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7394 int num_contents, content_xsize, content_ysize;
7395 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7397 if (element == EL_YAMYAM)
7399 num_contents = level->num_yamyam_contents;
7403 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7404 for (y = 0; y < 3; y++)
7405 for (x = 0; x < 3; x++)
7406 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7408 else if (element == EL_BD_AMOEBA)
7414 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7415 for (y = 0; y < 3; y++)
7416 for (x = 0; x < 3; x++)
7417 content_array[i][x][y] = EL_EMPTY;
7418 content_array[0][0][0] = level->amoeba_content;
7422 // chunk header already written -- write empty chunk data
7423 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7425 Warn("cannot save content for element '%d'", element);
7430 putFile16BitBE(file, element);
7431 putFile8Bit(file, num_contents);
7432 putFile8Bit(file, content_xsize);
7433 putFile8Bit(file, content_ysize);
7435 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7437 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7438 for (y = 0; y < 3; y++)
7439 for (x = 0; x < 3; x++)
7440 putFile16BitBE(file, content_array[i][x][y]);
7444 #if ENABLE_HISTORIC_CHUNKS
7445 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7447 int envelope_nr = element - EL_ENVELOPE_1;
7448 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7452 chunk_size += putFile16BitBE(file, element);
7453 chunk_size += putFile16BitBE(file, envelope_len);
7454 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7455 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7457 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7458 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7460 for (i = 0; i < envelope_len; i++)
7461 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7467 #if ENABLE_HISTORIC_CHUNKS
7468 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7469 int num_changed_custom_elements)
7473 putFile16BitBE(file, num_changed_custom_elements);
7475 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7477 int element = EL_CUSTOM_START + i;
7479 struct ElementInfo *ei = &element_info[element];
7481 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7483 if (check < num_changed_custom_elements)
7485 putFile16BitBE(file, element);
7486 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7493 if (check != num_changed_custom_elements) // should not happen
7494 Warn("inconsistent number of custom element properties");
7498 #if ENABLE_HISTORIC_CHUNKS
7499 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7500 int num_changed_custom_elements)
7504 putFile16BitBE(file, num_changed_custom_elements);
7506 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7508 int element = EL_CUSTOM_START + i;
7510 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7512 if (check < num_changed_custom_elements)
7514 putFile16BitBE(file, element);
7515 putFile16BitBE(file, element_info[element].change->target_element);
7522 if (check != num_changed_custom_elements) // should not happen
7523 Warn("inconsistent number of custom target elements");
7527 #if ENABLE_HISTORIC_CHUNKS
7528 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7529 int num_changed_custom_elements)
7531 int i, j, x, y, check = 0;
7533 putFile16BitBE(file, num_changed_custom_elements);
7535 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7537 int element = EL_CUSTOM_START + i;
7538 struct ElementInfo *ei = &element_info[element];
7540 if (ei->modified_settings)
7542 if (check < num_changed_custom_elements)
7544 putFile16BitBE(file, element);
7546 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7547 putFile8Bit(file, ei->description[j]);
7549 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7551 // some free bytes for future properties and padding
7552 WriteUnusedBytesToFile(file, 7);
7554 putFile8Bit(file, ei->use_gfx_element);
7555 putFile16BitBE(file, ei->gfx_element_initial);
7557 putFile8Bit(file, ei->collect_score_initial);
7558 putFile8Bit(file, ei->collect_count_initial);
7560 putFile16BitBE(file, ei->push_delay_fixed);
7561 putFile16BitBE(file, ei->push_delay_random);
7562 putFile16BitBE(file, ei->move_delay_fixed);
7563 putFile16BitBE(file, ei->move_delay_random);
7565 putFile16BitBE(file, ei->move_pattern);
7566 putFile8Bit(file, ei->move_direction_initial);
7567 putFile8Bit(file, ei->move_stepsize);
7569 for (y = 0; y < 3; y++)
7570 for (x = 0; x < 3; x++)
7571 putFile16BitBE(file, ei->content.e[x][y]);
7573 putFile32BitBE(file, ei->change->events);
7575 putFile16BitBE(file, ei->change->target_element);
7577 putFile16BitBE(file, ei->change->delay_fixed);
7578 putFile16BitBE(file, ei->change->delay_random);
7579 putFile16BitBE(file, ei->change->delay_frames);
7581 putFile16BitBE(file, ei->change->initial_trigger_element);
7583 putFile8Bit(file, ei->change->explode);
7584 putFile8Bit(file, ei->change->use_target_content);
7585 putFile8Bit(file, ei->change->only_if_complete);
7586 putFile8Bit(file, ei->change->use_random_replace);
7588 putFile8Bit(file, ei->change->random_percentage);
7589 putFile8Bit(file, ei->change->replace_when);
7591 for (y = 0; y < 3; y++)
7592 for (x = 0; x < 3; x++)
7593 putFile16BitBE(file, ei->change->content.e[x][y]);
7595 putFile8Bit(file, ei->slippery_type);
7597 // some free bytes for future properties and padding
7598 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7605 if (check != num_changed_custom_elements) // should not happen
7606 Warn("inconsistent number of custom element properties");
7610 #if ENABLE_HISTORIC_CHUNKS
7611 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7613 struct ElementInfo *ei = &element_info[element];
7616 // ---------- custom element base property values (96 bytes) ----------------
7618 putFile16BitBE(file, element);
7620 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7621 putFile8Bit(file, ei->description[i]);
7623 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7625 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7627 putFile8Bit(file, ei->num_change_pages);
7629 putFile16BitBE(file, ei->ce_value_fixed_initial);
7630 putFile16BitBE(file, ei->ce_value_random_initial);
7631 putFile8Bit(file, ei->use_last_ce_value);
7633 putFile8Bit(file, ei->use_gfx_element);
7634 putFile16BitBE(file, ei->gfx_element_initial);
7636 putFile8Bit(file, ei->collect_score_initial);
7637 putFile8Bit(file, ei->collect_count_initial);
7639 putFile8Bit(file, ei->drop_delay_fixed);
7640 putFile8Bit(file, ei->push_delay_fixed);
7641 putFile8Bit(file, ei->drop_delay_random);
7642 putFile8Bit(file, ei->push_delay_random);
7643 putFile16BitBE(file, ei->move_delay_fixed);
7644 putFile16BitBE(file, ei->move_delay_random);
7646 // bits 0 - 15 of "move_pattern" ...
7647 putFile16BitBE(file, ei->move_pattern & 0xffff);
7648 putFile8Bit(file, ei->move_direction_initial);
7649 putFile8Bit(file, ei->move_stepsize);
7651 putFile8Bit(file, ei->slippery_type);
7653 for (y = 0; y < 3; y++)
7654 for (x = 0; x < 3; x++)
7655 putFile16BitBE(file, ei->content.e[x][y]);
7657 putFile16BitBE(file, ei->move_enter_element);
7658 putFile16BitBE(file, ei->move_leave_element);
7659 putFile8Bit(file, ei->move_leave_type);
7661 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7662 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7664 putFile8Bit(file, ei->access_direction);
7666 putFile8Bit(file, ei->explosion_delay);
7667 putFile8Bit(file, ei->ignition_delay);
7668 putFile8Bit(file, ei->explosion_type);
7670 // some free bytes for future custom property values and padding
7671 WriteUnusedBytesToFile(file, 1);
7673 // ---------- change page property values (48 bytes) ------------------------
7675 for (i = 0; i < ei->num_change_pages; i++)
7677 struct ElementChangeInfo *change = &ei->change_page[i];
7678 unsigned int event_bits;
7680 // bits 0 - 31 of "has_event[]" ...
7682 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7683 if (change->has_event[j])
7684 event_bits |= (1u << j);
7685 putFile32BitBE(file, event_bits);
7687 putFile16BitBE(file, change->target_element);
7689 putFile16BitBE(file, change->delay_fixed);
7690 putFile16BitBE(file, change->delay_random);
7691 putFile16BitBE(file, change->delay_frames);
7693 putFile16BitBE(file, change->initial_trigger_element);
7695 putFile8Bit(file, change->explode);
7696 putFile8Bit(file, change->use_target_content);
7697 putFile8Bit(file, change->only_if_complete);
7698 putFile8Bit(file, change->use_random_replace);
7700 putFile8Bit(file, change->random_percentage);
7701 putFile8Bit(file, change->replace_when);
7703 for (y = 0; y < 3; y++)
7704 for (x = 0; x < 3; x++)
7705 putFile16BitBE(file, change->target_content.e[x][y]);
7707 putFile8Bit(file, change->can_change);
7709 putFile8Bit(file, change->trigger_side);
7711 putFile8Bit(file, change->trigger_player);
7712 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7713 log_2(change->trigger_page)));
7715 putFile8Bit(file, change->has_action);
7716 putFile8Bit(file, change->action_type);
7717 putFile8Bit(file, change->action_mode);
7718 putFile16BitBE(file, change->action_arg);
7720 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7722 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7723 if (change->has_event[j])
7724 event_bits |= (1u << (j - 32));
7725 putFile8Bit(file, event_bits);
7730 #if ENABLE_HISTORIC_CHUNKS
7731 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7733 struct ElementInfo *ei = &element_info[element];
7734 struct ElementGroupInfo *group = ei->group;
7737 putFile16BitBE(file, element);
7739 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7740 putFile8Bit(file, ei->description[i]);
7742 putFile8Bit(file, group->num_elements);
7744 putFile8Bit(file, ei->use_gfx_element);
7745 putFile16BitBE(file, ei->gfx_element_initial);
7747 putFile8Bit(file, group->choice_mode);
7749 // some free bytes for future values and padding
7750 WriteUnusedBytesToFile(file, 3);
7752 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7753 putFile16BitBE(file, group->element[i]);
7757 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7758 boolean write_element)
7760 int save_type = entry->save_type;
7761 int data_type = entry->data_type;
7762 int conf_type = entry->conf_type;
7763 int byte_mask = conf_type & CONF_MASK_BYTES;
7764 int element = entry->element;
7765 int default_value = entry->default_value;
7767 boolean modified = FALSE;
7769 if (byte_mask != CONF_MASK_MULTI_BYTES)
7771 void *value_ptr = entry->value;
7772 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7775 // check if any settings have been modified before saving them
7776 if (value != default_value)
7779 // do not save if explicitly told or if unmodified default settings
7780 if ((save_type == SAVE_CONF_NEVER) ||
7781 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7785 num_bytes += putFile16BitBE(file, element);
7787 num_bytes += putFile8Bit(file, conf_type);
7788 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7789 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7790 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7793 else if (data_type == TYPE_STRING)
7795 char *default_string = entry->default_string;
7796 char *string = (char *)(entry->value);
7797 int string_length = strlen(string);
7800 // check if any settings have been modified before saving them
7801 if (!strEqual(string, default_string))
7804 // do not save if explicitly told or if unmodified default settings
7805 if ((save_type == SAVE_CONF_NEVER) ||
7806 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7810 num_bytes += putFile16BitBE(file, element);
7812 num_bytes += putFile8Bit(file, conf_type);
7813 num_bytes += putFile16BitBE(file, string_length);
7815 for (i = 0; i < string_length; i++)
7816 num_bytes += putFile8Bit(file, string[i]);
7818 else if (data_type == TYPE_ELEMENT_LIST)
7820 int *element_array = (int *)(entry->value);
7821 int num_elements = *(int *)(entry->num_entities);
7824 // check if any settings have been modified before saving them
7825 for (i = 0; i < num_elements; i++)
7826 if (element_array[i] != default_value)
7829 // do not save if explicitly told or if unmodified default settings
7830 if ((save_type == SAVE_CONF_NEVER) ||
7831 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7835 num_bytes += putFile16BitBE(file, element);
7837 num_bytes += putFile8Bit(file, conf_type);
7838 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7840 for (i = 0; i < num_elements; i++)
7841 num_bytes += putFile16BitBE(file, element_array[i]);
7843 else if (data_type == TYPE_CONTENT_LIST)
7845 struct Content *content = (struct Content *)(entry->value);
7846 int num_contents = *(int *)(entry->num_entities);
7849 // check if any settings have been modified before saving them
7850 for (i = 0; i < num_contents; i++)
7851 for (y = 0; y < 3; y++)
7852 for (x = 0; x < 3; x++)
7853 if (content[i].e[x][y] != default_value)
7856 // do not save if explicitly told or if unmodified default settings
7857 if ((save_type == SAVE_CONF_NEVER) ||
7858 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7862 num_bytes += putFile16BitBE(file, element);
7864 num_bytes += putFile8Bit(file, conf_type);
7865 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7867 for (i = 0; i < num_contents; i++)
7868 for (y = 0; y < 3; y++)
7869 for (x = 0; x < 3; x++)
7870 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7876 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7881 li = *level; // copy level data into temporary buffer
7883 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7884 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7889 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7894 li = *level; // copy level data into temporary buffer
7896 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7897 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7902 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7904 int envelope_nr = element - EL_ENVELOPE_1;
7908 chunk_size += putFile16BitBE(file, element);
7910 // copy envelope data into temporary buffer
7911 xx_envelope = level->envelope[envelope_nr];
7913 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7914 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7919 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7921 struct ElementInfo *ei = &element_info[element];
7925 chunk_size += putFile16BitBE(file, element);
7927 xx_ei = *ei; // copy element data into temporary buffer
7929 // set default description string for this specific element
7930 strcpy(xx_default_description, getDefaultElementDescription(ei));
7932 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7933 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7935 for (i = 0; i < ei->num_change_pages; i++)
7937 struct ElementChangeInfo *change = &ei->change_page[i];
7939 xx_current_change_page = i;
7941 xx_change = *change; // copy change data into temporary buffer
7944 setEventBitsFromEventFlags(change);
7946 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7947 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7954 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7956 struct ElementInfo *ei = &element_info[element];
7957 struct ElementGroupInfo *group = ei->group;
7961 chunk_size += putFile16BitBE(file, element);
7963 xx_ei = *ei; // copy element data into temporary buffer
7964 xx_group = *group; // copy group data into temporary buffer
7966 // set default description string for this specific element
7967 strcpy(xx_default_description, getDefaultElementDescription(ei));
7969 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7970 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7975 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7977 struct ElementInfo *ei = &element_info[element];
7981 chunk_size += putFile16BitBE(file, element);
7983 xx_ei = *ei; // copy element data into temporary buffer
7985 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7986 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7991 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7992 boolean save_as_template)
7998 if (!(file = fopen(filename, MODE_WRITE)))
8000 Warn("cannot save level file '%s'", filename);
8005 level->file_version = FILE_VERSION_ACTUAL;
8006 level->game_version = GAME_VERSION_ACTUAL;
8008 level->creation_date = getCurrentDate();
8010 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8011 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8013 chunk_size = SaveLevel_VERS(NULL, level);
8014 putFileChunkBE(file, "VERS", chunk_size);
8015 SaveLevel_VERS(file, level);
8017 chunk_size = SaveLevel_DATE(NULL, level);
8018 putFileChunkBE(file, "DATE", chunk_size);
8019 SaveLevel_DATE(file, level);
8021 chunk_size = SaveLevel_NAME(NULL, level);
8022 putFileChunkBE(file, "NAME", chunk_size);
8023 SaveLevel_NAME(file, level);
8025 chunk_size = SaveLevel_AUTH(NULL, level);
8026 putFileChunkBE(file, "AUTH", chunk_size);
8027 SaveLevel_AUTH(file, level);
8029 chunk_size = SaveLevel_INFO(NULL, level);
8030 putFileChunkBE(file, "INFO", chunk_size);
8031 SaveLevel_INFO(file, level);
8033 chunk_size = SaveLevel_BODY(NULL, level);
8034 putFileChunkBE(file, "BODY", chunk_size);
8035 SaveLevel_BODY(file, level);
8037 chunk_size = SaveLevel_ELEM(NULL, level);
8038 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8040 putFileChunkBE(file, "ELEM", chunk_size);
8041 SaveLevel_ELEM(file, level);
8044 for (i = 0; i < NUM_ENVELOPES; i++)
8046 int element = EL_ENVELOPE_1 + i;
8048 chunk_size = SaveLevel_NOTE(NULL, level, element);
8049 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8051 putFileChunkBE(file, "NOTE", chunk_size);
8052 SaveLevel_NOTE(file, level, element);
8056 // if not using template level, check for non-default custom/group elements
8057 if (!level->use_custom_template || save_as_template)
8059 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8061 int element = EL_CUSTOM_START + i;
8063 chunk_size = SaveLevel_CUSX(NULL, level, element);
8064 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8066 putFileChunkBE(file, "CUSX", chunk_size);
8067 SaveLevel_CUSX(file, level, element);
8071 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8073 int element = EL_GROUP_START + i;
8075 chunk_size = SaveLevel_GRPX(NULL, level, element);
8076 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8078 putFileChunkBE(file, "GRPX", chunk_size);
8079 SaveLevel_GRPX(file, level, element);
8083 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8085 int element = GET_EMPTY_ELEMENT(i);
8087 chunk_size = SaveLevel_EMPX(NULL, level, element);
8088 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8090 putFileChunkBE(file, "EMPX", chunk_size);
8091 SaveLevel_EMPX(file, level, element);
8098 SetFilePermissions(filename, PERMS_PRIVATE);
8101 void SaveLevel(int nr)
8103 char *filename = getDefaultLevelFilename(nr);
8105 SaveLevelFromFilename(&level, filename, FALSE);
8108 void SaveLevelTemplate(void)
8110 char *filename = getLocalLevelTemplateFilename();
8112 SaveLevelFromFilename(&level, filename, TRUE);
8115 boolean SaveLevelChecked(int nr)
8117 char *filename = getDefaultLevelFilename(nr);
8118 boolean new_level = !fileExists(filename);
8119 boolean level_saved = FALSE;
8121 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8126 Request("Level saved!", REQ_CONFIRM);
8134 void DumpLevel(struct LevelInfo *level)
8136 if (level->no_level_file || level->no_valid_file)
8138 Warn("cannot dump -- no valid level file found");
8144 Print("Level xxx (file version %08d, game version %08d)\n",
8145 level->file_version, level->game_version);
8148 Print("Level author: '%s'\n", level->author);
8149 Print("Level title: '%s'\n", level->name);
8151 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8153 Print("Level time: %d seconds\n", level->time);
8154 Print("Gems needed: %d\n", level->gems_needed);
8156 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8157 Print("Time for wheel: %d seconds\n", level->time_wheel);
8158 Print("Time for light: %d seconds\n", level->time_light);
8159 Print("Time for timegate: %d seconds\n", level->time_timegate);
8161 Print("Amoeba speed: %d\n", level->amoeba_speed);
8164 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8165 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8166 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8167 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8168 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8169 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8175 for (i = 0; i < NUM_ENVELOPES; i++)
8177 char *text = level->envelope[i].text;
8178 int text_len = strlen(text);
8179 boolean has_text = FALSE;
8181 for (j = 0; j < text_len; j++)
8182 if (text[j] != ' ' && text[j] != '\n')
8188 Print("Envelope %d:\n'%s'\n", i + 1, text);
8196 void DumpLevels(void)
8198 static LevelDirTree *dumplevel_leveldir = NULL;
8200 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8201 global.dumplevel_leveldir);
8203 if (dumplevel_leveldir == NULL)
8204 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8206 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8207 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8208 Fail("no such level number: %d", global.dumplevel_level_nr);
8210 leveldir_current = dumplevel_leveldir;
8212 LoadLevel(global.dumplevel_level_nr);
8219 // ============================================================================
8220 // tape file functions
8221 // ============================================================================
8223 static void setTapeInfoToDefaults(void)
8227 // always start with reliable default values (empty tape)
8230 // default values (also for pre-1.2 tapes) with only the first player
8231 tape.player_participates[0] = TRUE;
8232 for (i = 1; i < MAX_PLAYERS; i++)
8233 tape.player_participates[i] = FALSE;
8235 // at least one (default: the first) player participates in every tape
8236 tape.num_participating_players = 1;
8238 tape.property_bits = TAPE_PROPERTY_NONE;
8240 tape.level_nr = level_nr;
8242 tape.changed = FALSE;
8243 tape.solved = FALSE;
8245 tape.recording = FALSE;
8246 tape.playing = FALSE;
8247 tape.pausing = FALSE;
8249 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8250 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8252 tape.no_info_chunk = TRUE;
8253 tape.no_valid_file = FALSE;
8256 static int getTapePosSize(struct TapeInfo *tape)
8258 int tape_pos_size = 0;
8260 if (tape->use_key_actions)
8261 tape_pos_size += tape->num_participating_players;
8263 if (tape->use_mouse_actions)
8264 tape_pos_size += 3; // x and y position and mouse button mask
8266 tape_pos_size += 1; // tape action delay value
8268 return tape_pos_size;
8271 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8273 tape->use_key_actions = FALSE;
8274 tape->use_mouse_actions = FALSE;
8276 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8277 tape->use_key_actions = TRUE;
8279 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8280 tape->use_mouse_actions = TRUE;
8283 static int getTapeActionValue(struct TapeInfo *tape)
8285 return (tape->use_key_actions &&
8286 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8287 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8288 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8289 TAPE_ACTIONS_DEFAULT);
8292 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8294 tape->file_version = getFileVersion(file);
8295 tape->game_version = getFileVersion(file);
8300 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8304 tape->random_seed = getFile32BitBE(file);
8305 tape->date = getFile32BitBE(file);
8306 tape->length = getFile32BitBE(file);
8308 // read header fields that are new since version 1.2
8309 if (tape->file_version >= FILE_VERSION_1_2)
8311 byte store_participating_players = getFile8Bit(file);
8314 // since version 1.2, tapes store which players participate in the tape
8315 tape->num_participating_players = 0;
8316 for (i = 0; i < MAX_PLAYERS; i++)
8318 tape->player_participates[i] = FALSE;
8320 if (store_participating_players & (1 << i))
8322 tape->player_participates[i] = TRUE;
8323 tape->num_participating_players++;
8327 setTapeActionFlags(tape, getFile8Bit(file));
8329 tape->property_bits = getFile8Bit(file);
8330 tape->solved = getFile8Bit(file);
8332 engine_version = getFileVersion(file);
8333 if (engine_version > 0)
8334 tape->engine_version = engine_version;
8336 tape->engine_version = tape->game_version;
8342 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8344 tape->scr_fieldx = getFile8Bit(file);
8345 tape->scr_fieldy = getFile8Bit(file);
8350 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8352 char *level_identifier = NULL;
8353 int level_identifier_size;
8356 tape->no_info_chunk = FALSE;
8358 level_identifier_size = getFile16BitBE(file);
8360 level_identifier = checked_malloc(level_identifier_size);
8362 for (i = 0; i < level_identifier_size; i++)
8363 level_identifier[i] = getFile8Bit(file);
8365 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8366 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8368 checked_free(level_identifier);
8370 tape->level_nr = getFile16BitBE(file);
8372 chunk_size = 2 + level_identifier_size + 2;
8377 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8380 int tape_pos_size = getTapePosSize(tape);
8381 int chunk_size_expected = tape_pos_size * tape->length;
8383 if (chunk_size_expected != chunk_size)
8385 ReadUnusedBytesFromFile(file, chunk_size);
8386 return chunk_size_expected;
8389 for (i = 0; i < tape->length; i++)
8391 if (i >= MAX_TAPE_LEN)
8393 Warn("tape truncated -- size exceeds maximum tape size %d",
8396 // tape too large; read and ignore remaining tape data from this chunk
8397 for (;i < tape->length; i++)
8398 ReadUnusedBytesFromFile(file, tape_pos_size);
8403 if (tape->use_key_actions)
8405 for (j = 0; j < MAX_PLAYERS; j++)
8407 tape->pos[i].action[j] = MV_NONE;
8409 if (tape->player_participates[j])
8410 tape->pos[i].action[j] = getFile8Bit(file);
8414 if (tape->use_mouse_actions)
8416 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8417 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8418 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8421 tape->pos[i].delay = getFile8Bit(file);
8423 if (tape->file_version == FILE_VERSION_1_0)
8425 // eliminate possible diagonal moves in old tapes
8426 // this is only for backward compatibility
8428 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8429 byte action = tape->pos[i].action[0];
8430 int k, num_moves = 0;
8432 for (k = 0; k < 4; k++)
8434 if (action & joy_dir[k])
8436 tape->pos[i + num_moves].action[0] = joy_dir[k];
8438 tape->pos[i + num_moves].delay = 0;
8447 tape->length += num_moves;
8450 else if (tape->file_version < FILE_VERSION_2_0)
8452 // convert pre-2.0 tapes to new tape format
8454 if (tape->pos[i].delay > 1)
8457 tape->pos[i + 1] = tape->pos[i];
8458 tape->pos[i + 1].delay = 1;
8461 for (j = 0; j < MAX_PLAYERS; j++)
8462 tape->pos[i].action[j] = MV_NONE;
8463 tape->pos[i].delay--;
8470 if (checkEndOfFile(file))
8474 if (i != tape->length)
8475 chunk_size = tape_pos_size * i;
8480 static void LoadTape_SokobanSolution(char *filename)
8483 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8485 if (!(file = openFile(filename, MODE_READ)))
8487 tape.no_valid_file = TRUE;
8492 while (!checkEndOfFile(file))
8494 unsigned char c = getByteFromFile(file);
8496 if (checkEndOfFile(file))
8503 tape.pos[tape.length].action[0] = MV_UP;
8504 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8510 tape.pos[tape.length].action[0] = MV_DOWN;
8511 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8517 tape.pos[tape.length].action[0] = MV_LEFT;
8518 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8524 tape.pos[tape.length].action[0] = MV_RIGHT;
8525 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8533 // ignore white-space characters
8537 tape.no_valid_file = TRUE;
8539 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8547 if (tape.no_valid_file)
8550 tape.length_frames = GetTapeLengthFrames();
8551 tape.length_seconds = GetTapeLengthSeconds();
8554 void LoadTapeFromFilename(char *filename)
8556 char cookie[MAX_LINE_LEN];
8557 char chunk_name[CHUNK_ID_LEN + 1];
8561 // always start with reliable default values
8562 setTapeInfoToDefaults();
8564 if (strSuffix(filename, ".sln"))
8566 LoadTape_SokobanSolution(filename);
8571 if (!(file = openFile(filename, MODE_READ)))
8573 tape.no_valid_file = TRUE;
8578 getFileChunkBE(file, chunk_name, NULL);
8579 if (strEqual(chunk_name, "RND1"))
8581 getFile32BitBE(file); // not used
8583 getFileChunkBE(file, chunk_name, NULL);
8584 if (!strEqual(chunk_name, "TAPE"))
8586 tape.no_valid_file = TRUE;
8588 Warn("unknown format of tape file '%s'", filename);
8595 else // check for pre-2.0 file format with cookie string
8597 strcpy(cookie, chunk_name);
8598 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8600 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8601 cookie[strlen(cookie) - 1] = '\0';
8603 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8605 tape.no_valid_file = TRUE;
8607 Warn("unknown format of tape file '%s'", filename);
8614 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8616 tape.no_valid_file = TRUE;
8618 Warn("unsupported version of tape file '%s'", filename);
8625 // pre-2.0 tape files have no game version, so use file version here
8626 tape.game_version = tape.file_version;
8629 if (tape.file_version < FILE_VERSION_1_2)
8631 // tape files from versions before 1.2.0 without chunk structure
8632 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8633 LoadTape_BODY(file, 2 * tape.length, &tape);
8641 int (*loader)(File *, int, struct TapeInfo *);
8645 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8646 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8647 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8648 { "INFO", -1, LoadTape_INFO },
8649 { "BODY", -1, LoadTape_BODY },
8653 while (getFileChunkBE(file, chunk_name, &chunk_size))
8657 while (chunk_info[i].name != NULL &&
8658 !strEqual(chunk_name, chunk_info[i].name))
8661 if (chunk_info[i].name == NULL)
8663 Warn("unknown chunk '%s' in tape file '%s'",
8664 chunk_name, filename);
8666 ReadUnusedBytesFromFile(file, chunk_size);
8668 else if (chunk_info[i].size != -1 &&
8669 chunk_info[i].size != chunk_size)
8671 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8672 chunk_size, chunk_name, filename);
8674 ReadUnusedBytesFromFile(file, chunk_size);
8678 // call function to load this tape chunk
8679 int chunk_size_expected =
8680 (chunk_info[i].loader)(file, chunk_size, &tape);
8682 // the size of some chunks cannot be checked before reading other
8683 // chunks first (like "HEAD" and "BODY") that contain some header
8684 // information, so check them here
8685 if (chunk_size_expected != chunk_size)
8687 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8688 chunk_size, chunk_name, filename);
8696 tape.length_frames = GetTapeLengthFrames();
8697 tape.length_seconds = GetTapeLengthSeconds();
8700 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8702 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8704 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8705 tape.engine_version);
8709 void LoadTape(int nr)
8711 char *filename = getTapeFilename(nr);
8713 LoadTapeFromFilename(filename);
8716 void LoadSolutionTape(int nr)
8718 char *filename = getSolutionTapeFilename(nr);
8720 LoadTapeFromFilename(filename);
8722 if (TAPE_IS_EMPTY(tape))
8724 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8725 level.native_bd_level->replay != NULL)
8726 CopyNativeTape_BD_to_RND(&level);
8727 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8728 level.native_sp_level->demo.is_available)
8729 CopyNativeTape_SP_to_RND(&level);
8733 void LoadScoreTape(char *score_tape_basename, int nr)
8735 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8737 LoadTapeFromFilename(filename);
8740 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8742 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8744 LoadTapeFromFilename(filename);
8747 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8749 // chunk required for team mode tapes with non-default screen size
8750 return (tape->num_participating_players > 1 &&
8751 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8752 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8755 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8757 putFileVersion(file, tape->file_version);
8758 putFileVersion(file, tape->game_version);
8761 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8764 byte store_participating_players = 0;
8766 // set bits for participating players for compact storage
8767 for (i = 0; i < MAX_PLAYERS; i++)
8768 if (tape->player_participates[i])
8769 store_participating_players |= (1 << i);
8771 putFile32BitBE(file, tape->random_seed);
8772 putFile32BitBE(file, tape->date);
8773 putFile32BitBE(file, tape->length);
8775 putFile8Bit(file, store_participating_players);
8777 putFile8Bit(file, getTapeActionValue(tape));
8779 putFile8Bit(file, tape->property_bits);
8780 putFile8Bit(file, tape->solved);
8782 putFileVersion(file, tape->engine_version);
8785 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8787 putFile8Bit(file, tape->scr_fieldx);
8788 putFile8Bit(file, tape->scr_fieldy);
8791 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8793 int level_identifier_size = strlen(tape->level_identifier) + 1;
8796 putFile16BitBE(file, level_identifier_size);
8798 for (i = 0; i < level_identifier_size; i++)
8799 putFile8Bit(file, tape->level_identifier[i]);
8801 putFile16BitBE(file, tape->level_nr);
8804 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8808 for (i = 0; i < tape->length; i++)
8810 if (tape->use_key_actions)
8812 for (j = 0; j < MAX_PLAYERS; j++)
8813 if (tape->player_participates[j])
8814 putFile8Bit(file, tape->pos[i].action[j]);
8817 if (tape->use_mouse_actions)
8819 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8820 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8821 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8824 putFile8Bit(file, tape->pos[i].delay);
8828 void SaveTapeToFilename(char *filename)
8832 int info_chunk_size;
8833 int body_chunk_size;
8835 if (!(file = fopen(filename, MODE_WRITE)))
8837 Warn("cannot save level recording file '%s'", filename);
8842 tape_pos_size = getTapePosSize(&tape);
8844 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8845 body_chunk_size = tape_pos_size * tape.length;
8847 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8848 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8850 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8851 SaveTape_VERS(file, &tape);
8853 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8854 SaveTape_HEAD(file, &tape);
8856 if (checkSaveTape_SCRN(&tape))
8858 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8859 SaveTape_SCRN(file, &tape);
8862 putFileChunkBE(file, "INFO", info_chunk_size);
8863 SaveTape_INFO(file, &tape);
8865 putFileChunkBE(file, "BODY", body_chunk_size);
8866 SaveTape_BODY(file, &tape);
8870 SetFilePermissions(filename, PERMS_PRIVATE);
8873 static void SaveTapeExt(char *filename)
8877 tape.file_version = FILE_VERSION_ACTUAL;
8878 tape.game_version = GAME_VERSION_ACTUAL;
8880 tape.num_participating_players = 0;
8882 // count number of participating players
8883 for (i = 0; i < MAX_PLAYERS; i++)
8884 if (tape.player_participates[i])
8885 tape.num_participating_players++;
8887 SaveTapeToFilename(filename);
8889 tape.changed = FALSE;
8892 void SaveTape(int nr)
8894 char *filename = getTapeFilename(nr);
8896 InitTapeDirectory(leveldir_current->subdir);
8898 SaveTapeExt(filename);
8901 void SaveScoreTape(int nr)
8903 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8905 // used instead of "leveldir_current->subdir" (for network games)
8906 InitScoreTapeDirectory(levelset.identifier, nr);
8908 SaveTapeExt(filename);
8911 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8912 unsigned int req_state_added)
8914 char *filename = getTapeFilename(nr);
8915 boolean new_tape = !fileExists(filename);
8916 boolean tape_saved = FALSE;
8918 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8923 Request(msg_saved, REQ_CONFIRM | req_state_added);
8931 boolean SaveTapeChecked(int nr)
8933 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8936 boolean SaveTapeChecked_LevelSolved(int nr)
8938 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8939 "Level solved! Tape saved!", REQ_STAY_OPEN);
8942 void DumpTape(struct TapeInfo *tape)
8944 int tape_frame_counter;
8947 if (tape->no_valid_file)
8949 Warn("cannot dump -- no valid tape file found");
8956 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8957 tape->level_nr, tape->file_version, tape->game_version);
8958 Print(" (effective engine version %08d)\n",
8959 tape->engine_version);
8960 Print("Level series identifier: '%s'\n", tape->level_identifier);
8962 Print("Solution tape: %s\n",
8963 tape->solved ? "yes" :
8964 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8966 Print("Special tape properties: ");
8967 if (tape->property_bits == TAPE_PROPERTY_NONE)
8969 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8970 Print("[em_random_bug]");
8971 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8972 Print("[game_speed]");
8973 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8975 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8976 Print("[single_step]");
8977 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8978 Print("[snapshot]");
8979 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8980 Print("[replayed]");
8981 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8982 Print("[tas_keys]");
8983 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8984 Print("[small_graphics]");
8987 int year2 = tape->date / 10000;
8988 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8989 int month_index_raw = (tape->date / 100) % 100;
8990 int month_index = month_index_raw % 12; // prevent invalid index
8991 int month = month_index + 1;
8992 int day = tape->date % 100;
8994 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8998 tape_frame_counter = 0;
9000 for (i = 0; i < tape->length; i++)
9002 if (i >= MAX_TAPE_LEN)
9007 for (j = 0; j < MAX_PLAYERS; j++)
9009 if (tape->player_participates[j])
9011 int action = tape->pos[i].action[j];
9013 Print("%d:%02x ", j, action);
9014 Print("[%c%c%c%c|%c%c] - ",
9015 (action & JOY_LEFT ? '<' : ' '),
9016 (action & JOY_RIGHT ? '>' : ' '),
9017 (action & JOY_UP ? '^' : ' '),
9018 (action & JOY_DOWN ? 'v' : ' '),
9019 (action & JOY_BUTTON_1 ? '1' : ' '),
9020 (action & JOY_BUTTON_2 ? '2' : ' '));
9024 Print("(%03d) ", tape->pos[i].delay);
9025 Print("[%05d]\n", tape_frame_counter);
9027 tape_frame_counter += tape->pos[i].delay;
9033 void DumpTapes(void)
9035 static LevelDirTree *dumptape_leveldir = NULL;
9037 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9038 global.dumptape_leveldir);
9040 if (dumptape_leveldir == NULL)
9041 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9043 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9044 global.dumptape_level_nr > dumptape_leveldir->last_level)
9045 Fail("no such level number: %d", global.dumptape_level_nr);
9047 leveldir_current = dumptape_leveldir;
9049 if (options.mytapes)
9050 LoadTape(global.dumptape_level_nr);
9052 LoadSolutionTape(global.dumptape_level_nr);
9060 // ============================================================================
9061 // score file functions
9062 // ============================================================================
9064 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9068 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9070 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9071 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9072 scores->entry[i].score = 0;
9073 scores->entry[i].time = 0;
9075 scores->entry[i].id = -1;
9076 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9077 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9078 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9079 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9080 strcpy(scores->entry[i].country_code, "??");
9083 scores->num_entries = 0;
9084 scores->last_added = -1;
9085 scores->last_added_local = -1;
9087 scores->updated = FALSE;
9088 scores->uploaded = FALSE;
9089 scores->tape_downloaded = FALSE;
9090 scores->force_last_added = FALSE;
9092 // The following values are intentionally not reset here:
9096 // - continue_playing
9097 // - continue_on_return
9100 static void setScoreInfoToDefaults(void)
9102 setScoreInfoToDefaultsExt(&scores);
9105 static void setServerScoreInfoToDefaults(void)
9107 setScoreInfoToDefaultsExt(&server_scores);
9110 static void LoadScore_OLD(int nr)
9113 char *filename = getScoreFilename(nr);
9114 char cookie[MAX_LINE_LEN];
9115 char line[MAX_LINE_LEN];
9119 if (!(file = fopen(filename, MODE_READ)))
9122 // check file identifier
9123 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9125 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9126 cookie[strlen(cookie) - 1] = '\0';
9128 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9130 Warn("unknown format of score file '%s'", filename);
9137 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9139 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9140 Warn("fscanf() failed; %s", strerror(errno));
9142 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9145 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9146 line[strlen(line) - 1] = '\0';
9148 for (line_ptr = line; *line_ptr; line_ptr++)
9150 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9152 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9153 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9162 static void ConvertScore_OLD(void)
9164 // only convert score to time for levels that rate playing time over score
9165 if (!level.rate_time_over_score)
9168 // convert old score to playing time for score-less levels (like Supaplex)
9169 int time_final_max = 999;
9172 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9174 int score = scores.entry[i].score;
9176 if (score > 0 && score < time_final_max)
9177 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9181 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9183 scores->file_version = getFileVersion(file);
9184 scores->game_version = getFileVersion(file);
9189 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9191 char *level_identifier = NULL;
9192 int level_identifier_size;
9195 level_identifier_size = getFile16BitBE(file);
9197 level_identifier = checked_malloc(level_identifier_size);
9199 for (i = 0; i < level_identifier_size; i++)
9200 level_identifier[i] = getFile8Bit(file);
9202 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9203 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9205 checked_free(level_identifier);
9207 scores->level_nr = getFile16BitBE(file);
9208 scores->num_entries = getFile16BitBE(file);
9210 chunk_size = 2 + level_identifier_size + 2 + 2;
9215 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9219 for (i = 0; i < scores->num_entries; i++)
9221 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9222 scores->entry[i].name[j] = getFile8Bit(file);
9224 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9227 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9232 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9236 for (i = 0; i < scores->num_entries; i++)
9237 scores->entry[i].score = getFile16BitBE(file);
9239 chunk_size = scores->num_entries * 2;
9244 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9248 for (i = 0; i < scores->num_entries; i++)
9249 scores->entry[i].score = getFile32BitBE(file);
9251 chunk_size = scores->num_entries * 4;
9256 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9260 for (i = 0; i < scores->num_entries; i++)
9261 scores->entry[i].time = getFile32BitBE(file);
9263 chunk_size = scores->num_entries * 4;
9268 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9272 for (i = 0; i < scores->num_entries; i++)
9274 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9275 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9277 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9280 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9285 void LoadScore(int nr)
9287 char *filename = getScoreFilename(nr);
9288 char cookie[MAX_LINE_LEN];
9289 char chunk_name[CHUNK_ID_LEN + 1];
9291 boolean old_score_file_format = FALSE;
9294 // always start with reliable default values
9295 setScoreInfoToDefaults();
9297 if (!(file = openFile(filename, MODE_READ)))
9300 getFileChunkBE(file, chunk_name, NULL);
9301 if (strEqual(chunk_name, "RND1"))
9303 getFile32BitBE(file); // not used
9305 getFileChunkBE(file, chunk_name, NULL);
9306 if (!strEqual(chunk_name, "SCOR"))
9308 Warn("unknown format of score file '%s'", filename);
9315 else // check for old file format with cookie string
9317 strcpy(cookie, chunk_name);
9318 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9320 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9321 cookie[strlen(cookie) - 1] = '\0';
9323 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9325 Warn("unknown format of score file '%s'", filename);
9332 old_score_file_format = TRUE;
9335 if (old_score_file_format)
9337 // score files from versions before 4.2.4.0 without chunk structure
9340 // convert score to time, if possible (mainly for Supaplex levels)
9349 int (*loader)(File *, int, struct ScoreInfo *);
9353 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9354 { "INFO", -1, LoadScore_INFO },
9355 { "NAME", -1, LoadScore_NAME },
9356 { "SCOR", -1, LoadScore_SCOR },
9357 { "SC4R", -1, LoadScore_SC4R },
9358 { "TIME", -1, LoadScore_TIME },
9359 { "TAPE", -1, LoadScore_TAPE },
9364 while (getFileChunkBE(file, chunk_name, &chunk_size))
9368 while (chunk_info[i].name != NULL &&
9369 !strEqual(chunk_name, chunk_info[i].name))
9372 if (chunk_info[i].name == NULL)
9374 Warn("unknown chunk '%s' in score file '%s'",
9375 chunk_name, filename);
9377 ReadUnusedBytesFromFile(file, chunk_size);
9379 else if (chunk_info[i].size != -1 &&
9380 chunk_info[i].size != chunk_size)
9382 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9383 chunk_size, chunk_name, filename);
9385 ReadUnusedBytesFromFile(file, chunk_size);
9389 // call function to load this score chunk
9390 int chunk_size_expected =
9391 (chunk_info[i].loader)(file, chunk_size, &scores);
9393 // the size of some chunks cannot be checked before reading other
9394 // chunks first (like "HEAD" and "BODY") that contain some header
9395 // information, so check them here
9396 if (chunk_size_expected != chunk_size)
9398 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9399 chunk_size, chunk_name, filename);
9408 #if ENABLE_HISTORIC_CHUNKS
9409 void SaveScore_OLD(int nr)
9412 char *filename = getScoreFilename(nr);
9415 // used instead of "leveldir_current->subdir" (for network games)
9416 InitScoreDirectory(levelset.identifier);
9418 if (!(file = fopen(filename, MODE_WRITE)))
9420 Warn("cannot save score for level %d", nr);
9425 fprintf(file, "%s\n\n", SCORE_COOKIE);
9427 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9428 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9432 SetFilePermissions(filename, PERMS_PRIVATE);
9436 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9438 putFileVersion(file, scores->file_version);
9439 putFileVersion(file, scores->game_version);
9442 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9444 int level_identifier_size = strlen(scores->level_identifier) + 1;
9447 putFile16BitBE(file, level_identifier_size);
9449 for (i = 0; i < level_identifier_size; i++)
9450 putFile8Bit(file, scores->level_identifier[i]);
9452 putFile16BitBE(file, scores->level_nr);
9453 putFile16BitBE(file, scores->num_entries);
9456 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9460 for (i = 0; i < scores->num_entries; i++)
9462 int name_size = strlen(scores->entry[i].name);
9464 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9465 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9469 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9473 for (i = 0; i < scores->num_entries; i++)
9474 putFile16BitBE(file, scores->entry[i].score);
9477 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9481 for (i = 0; i < scores->num_entries; i++)
9482 putFile32BitBE(file, scores->entry[i].score);
9485 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9489 for (i = 0; i < scores->num_entries; i++)
9490 putFile32BitBE(file, scores->entry[i].time);
9493 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9497 for (i = 0; i < scores->num_entries; i++)
9499 int size = strlen(scores->entry[i].tape_basename);
9501 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9502 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9506 static void SaveScoreToFilename(char *filename)
9509 int info_chunk_size;
9510 int name_chunk_size;
9511 int scor_chunk_size;
9512 int sc4r_chunk_size;
9513 int time_chunk_size;
9514 int tape_chunk_size;
9515 boolean has_large_score_values;
9518 if (!(file = fopen(filename, MODE_WRITE)))
9520 Warn("cannot save score file '%s'", filename);
9525 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9526 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9527 scor_chunk_size = scores.num_entries * 2;
9528 sc4r_chunk_size = scores.num_entries * 4;
9529 time_chunk_size = scores.num_entries * 4;
9530 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9532 has_large_score_values = FALSE;
9533 for (i = 0; i < scores.num_entries; i++)
9534 if (scores.entry[i].score > 0xffff)
9535 has_large_score_values = TRUE;
9537 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9538 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9540 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9541 SaveScore_VERS(file, &scores);
9543 putFileChunkBE(file, "INFO", info_chunk_size);
9544 SaveScore_INFO(file, &scores);
9546 putFileChunkBE(file, "NAME", name_chunk_size);
9547 SaveScore_NAME(file, &scores);
9549 if (has_large_score_values)
9551 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9552 SaveScore_SC4R(file, &scores);
9556 putFileChunkBE(file, "SCOR", scor_chunk_size);
9557 SaveScore_SCOR(file, &scores);
9560 putFileChunkBE(file, "TIME", time_chunk_size);
9561 SaveScore_TIME(file, &scores);
9563 putFileChunkBE(file, "TAPE", tape_chunk_size);
9564 SaveScore_TAPE(file, &scores);
9568 SetFilePermissions(filename, PERMS_PRIVATE);
9571 void SaveScore(int nr)
9573 char *filename = getScoreFilename(nr);
9576 // used instead of "leveldir_current->subdir" (for network games)
9577 InitScoreDirectory(levelset.identifier);
9579 scores.file_version = FILE_VERSION_ACTUAL;
9580 scores.game_version = GAME_VERSION_ACTUAL;
9582 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9583 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9584 scores.level_nr = level_nr;
9586 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9587 if (scores.entry[i].score == 0 &&
9588 scores.entry[i].time == 0 &&
9589 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9592 scores.num_entries = i;
9594 if (scores.num_entries == 0)
9597 SaveScoreToFilename(filename);
9600 static void LoadServerScoreFromCache(int nr)
9602 struct ScoreEntry score_entry;
9611 { &score_entry.score, FALSE, 0 },
9612 { &score_entry.time, FALSE, 0 },
9613 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9614 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9615 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9616 { &score_entry.id, FALSE, 0 },
9617 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9618 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9619 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9620 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9624 char *filename = getScoreCacheFilename(nr);
9625 SetupFileHash *score_hash = loadSetupFileHash(filename);
9628 server_scores.num_entries = 0;
9630 if (score_hash == NULL)
9633 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9635 score_entry = server_scores.entry[i];
9637 for (j = 0; score_mapping[j].value != NULL; j++)
9641 sprintf(token, "%02d.%d", i, j);
9643 char *value = getHashEntry(score_hash, token);
9648 if (score_mapping[j].is_string)
9650 char *score_value = (char *)score_mapping[j].value;
9651 int value_size = score_mapping[j].string_size;
9653 strncpy(score_value, value, value_size);
9654 score_value[value_size] = '\0';
9658 int *score_value = (int *)score_mapping[j].value;
9660 *score_value = atoi(value);
9663 server_scores.num_entries = i + 1;
9666 server_scores.entry[i] = score_entry;
9669 freeSetupFileHash(score_hash);
9672 void LoadServerScore(int nr, boolean download_score)
9674 if (!setup.use_api_server)
9677 // always start with reliable default values
9678 setServerScoreInfoToDefaults();
9680 // 1st step: load server scores from cache file (which may not exist)
9681 // (this should prevent reading it while the thread is writing to it)
9682 LoadServerScoreFromCache(nr);
9684 if (download_score && runtime.use_api_server)
9686 // 2nd step: download server scores from score server to cache file
9687 // (as thread, as it might time out if the server is not reachable)
9688 ApiGetScoreAsThread(nr);
9692 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9694 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9696 // if score tape not uploaded, ask for uploading missing tapes later
9697 if (!setup.has_remaining_tapes)
9698 setup.ask_for_remaining_tapes = TRUE;
9700 setup.provide_uploading_tapes = TRUE;
9701 setup.has_remaining_tapes = TRUE;
9703 SaveSetup_ServerSetup();
9706 void SaveServerScore(int nr, boolean tape_saved)
9708 if (!runtime.use_api_server)
9710 PrepareScoreTapesForUpload(leveldir_current->subdir);
9715 ApiAddScoreAsThread(nr, tape_saved, NULL);
9718 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9719 char *score_tape_filename)
9721 if (!runtime.use_api_server)
9724 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9727 void LoadLocalAndServerScore(int nr, boolean download_score)
9729 int last_added_local = scores.last_added_local;
9730 boolean force_last_added = scores.force_last_added;
9732 // needed if only showing server scores
9733 setScoreInfoToDefaults();
9735 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9738 // restore last added local score entry (before merging server scores)
9739 scores.last_added = scores.last_added_local = last_added_local;
9741 if (setup.use_api_server &&
9742 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9744 // load server scores from cache file and trigger update from server
9745 LoadServerScore(nr, download_score);
9747 // merge local scores with scores from server
9751 if (force_last_added)
9752 scores.force_last_added = force_last_added;
9756 // ============================================================================
9757 // setup file functions
9758 // ============================================================================
9760 #define TOKEN_STR_PLAYER_PREFIX "player_"
9763 static struct TokenInfo global_setup_tokens[] =
9767 &setup.player_name, "player_name"
9771 &setup.multiple_users, "multiple_users"
9775 &setup.sound, "sound"
9779 &setup.sound_loops, "repeating_sound_loops"
9783 &setup.sound_music, "background_music"
9787 &setup.sound_simple, "simple_sound_effects"
9791 &setup.toons, "toons"
9795 &setup.global_animations, "global_animations"
9799 &setup.scroll_delay, "scroll_delay"
9803 &setup.forced_scroll_delay, "forced_scroll_delay"
9807 &setup.scroll_delay_value, "scroll_delay_value"
9811 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9815 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9819 &setup.fade_screens, "fade_screens"
9823 &setup.autorecord, "automatic_tape_recording"
9827 &setup.autorecord_after_replay, "autorecord_after_replay"
9831 &setup.auto_pause_on_start, "auto_pause_on_start"
9835 &setup.show_titlescreen, "show_titlescreen"
9839 &setup.quick_doors, "quick_doors"
9843 &setup.team_mode, "team_mode"
9847 &setup.handicap, "handicap"
9851 &setup.skip_levels, "skip_levels"
9855 &setup.increment_levels, "increment_levels"
9859 &setup.auto_play_next_level, "auto_play_next_level"
9863 &setup.count_score_after_game, "count_score_after_game"
9867 &setup.show_scores_after_game, "show_scores_after_game"
9871 &setup.time_limit, "time_limit"
9875 &setup.fullscreen, "fullscreen"
9879 &setup.window_scaling_percent, "window_scaling_percent"
9883 &setup.window_scaling_quality, "window_scaling_quality"
9887 &setup.screen_rendering_mode, "screen_rendering_mode"
9891 &setup.vsync_mode, "vsync_mode"
9895 &setup.ask_on_escape, "ask_on_escape"
9899 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9903 &setup.ask_on_game_over, "ask_on_game_over"
9907 &setup.ask_on_quit_game, "ask_on_quit_game"
9911 &setup.ask_on_quit_program, "ask_on_quit_program"
9915 &setup.quick_switch, "quick_player_switch"
9919 &setup.input_on_focus, "input_on_focus"
9923 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9927 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9931 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9935 &setup.game_speed_extended, "game_speed_extended"
9939 &setup.game_frame_delay, "game_frame_delay"
9943 &setup.bd_skip_uncovering, "bd_skip_uncovering"
9947 &setup.bd_skip_hatching, "bd_skip_hatching"
9951 &setup.bd_scroll_delay, "bd_scroll_delay"
9955 &setup.bd_smooth_movements, "bd_smooth_movements"
9959 &setup.sp_show_border_elements, "sp_show_border_elements"
9963 &setup.small_game_graphics, "small_game_graphics"
9967 &setup.show_load_save_buttons, "show_load_save_buttons"
9971 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9975 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9979 &setup.graphics_set, "graphics_set"
9983 &setup.sounds_set, "sounds_set"
9987 &setup.music_set, "music_set"
9991 &setup.override_level_graphics, "override_level_graphics"
9995 &setup.override_level_sounds, "override_level_sounds"
9999 &setup.override_level_music, "override_level_music"
10003 &setup.volume_simple, "volume_simple"
10007 &setup.volume_loops, "volume_loops"
10011 &setup.volume_music, "volume_music"
10015 &setup.network_mode, "network_mode"
10019 &setup.network_player_nr, "network_player"
10023 &setup.network_server_hostname, "network_server_hostname"
10027 &setup.touch.control_type, "touch.control_type"
10031 &setup.touch.move_distance, "touch.move_distance"
10035 &setup.touch.drop_distance, "touch.drop_distance"
10039 &setup.touch.transparency, "touch.transparency"
10043 &setup.touch.draw_outlined, "touch.draw_outlined"
10047 &setup.touch.draw_pressed, "touch.draw_pressed"
10051 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10055 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10059 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10063 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10067 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10071 static struct TokenInfo auto_setup_tokens[] =
10075 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10079 static struct TokenInfo server_setup_tokens[] =
10083 &setup.player_uuid, "player_uuid"
10087 &setup.player_version, "player_version"
10091 &setup.use_api_server, TEST_PREFIX "use_api_server"
10095 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10099 &setup.api_server_password, TEST_PREFIX "api_server_password"
10103 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10107 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10111 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10115 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10119 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10123 static struct TokenInfo editor_setup_tokens[] =
10127 &setup.editor.el_classic, "editor.el_classic"
10131 &setup.editor.el_custom, "editor.el_custom"
10135 &setup.editor.el_user_defined, "editor.el_user_defined"
10139 &setup.editor.el_dynamic, "editor.el_dynamic"
10143 &setup.editor.el_headlines, "editor.el_headlines"
10147 &setup.editor.show_element_token, "editor.show_element_token"
10151 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10155 static struct TokenInfo editor_cascade_setup_tokens[] =
10159 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10163 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10167 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10171 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10175 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10179 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10183 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10187 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10191 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10195 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10199 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10203 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10207 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10211 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10215 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10219 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10223 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10227 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10231 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10235 static struct TokenInfo shortcut_setup_tokens[] =
10239 &setup.shortcut.save_game, "shortcut.save_game"
10243 &setup.shortcut.load_game, "shortcut.load_game"
10247 &setup.shortcut.restart_game, "shortcut.restart_game"
10251 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10255 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10259 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10263 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10267 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10271 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10275 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10279 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10283 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10287 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10291 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10295 &setup.shortcut.tape_record, "shortcut.tape_record"
10299 &setup.shortcut.tape_play, "shortcut.tape_play"
10303 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10307 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10311 &setup.shortcut.sound_music, "shortcut.sound_music"
10315 &setup.shortcut.snap_left, "shortcut.snap_left"
10319 &setup.shortcut.snap_right, "shortcut.snap_right"
10323 &setup.shortcut.snap_up, "shortcut.snap_up"
10327 &setup.shortcut.snap_down, "shortcut.snap_down"
10331 static struct SetupInputInfo setup_input;
10332 static struct TokenInfo player_setup_tokens[] =
10336 &setup_input.use_joystick, ".use_joystick"
10340 &setup_input.joy.device_name, ".joy.device_name"
10344 &setup_input.joy.xleft, ".joy.xleft"
10348 &setup_input.joy.xmiddle, ".joy.xmiddle"
10352 &setup_input.joy.xright, ".joy.xright"
10356 &setup_input.joy.yupper, ".joy.yupper"
10360 &setup_input.joy.ymiddle, ".joy.ymiddle"
10364 &setup_input.joy.ylower, ".joy.ylower"
10368 &setup_input.joy.snap, ".joy.snap_field"
10372 &setup_input.joy.drop, ".joy.place_bomb"
10376 &setup_input.key.left, ".key.move_left"
10380 &setup_input.key.right, ".key.move_right"
10384 &setup_input.key.up, ".key.move_up"
10388 &setup_input.key.down, ".key.move_down"
10392 &setup_input.key.snap, ".key.snap_field"
10396 &setup_input.key.drop, ".key.place_bomb"
10400 static struct TokenInfo system_setup_tokens[] =
10404 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10408 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10412 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10416 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10420 static struct TokenInfo internal_setup_tokens[] =
10424 &setup.internal.program_title, "program_title"
10428 &setup.internal.program_version, "program_version"
10432 &setup.internal.program_author, "program_author"
10436 &setup.internal.program_email, "program_email"
10440 &setup.internal.program_website, "program_website"
10444 &setup.internal.program_copyright, "program_copyright"
10448 &setup.internal.program_company, "program_company"
10452 &setup.internal.program_icon_file, "program_icon_file"
10456 &setup.internal.default_graphics_set, "default_graphics_set"
10460 &setup.internal.default_sounds_set, "default_sounds_set"
10464 &setup.internal.default_music_set, "default_music_set"
10468 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10472 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10476 &setup.internal.fallback_music_file, "fallback_music_file"
10480 &setup.internal.default_level_series, "default_level_series"
10484 &setup.internal.default_window_width, "default_window_width"
10488 &setup.internal.default_window_height, "default_window_height"
10492 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10496 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10500 &setup.internal.create_user_levelset, "create_user_levelset"
10504 &setup.internal.info_screens_from_main, "info_screens_from_main"
10508 &setup.internal.menu_game, "menu_game"
10512 &setup.internal.menu_engines, "menu_engines"
10516 &setup.internal.menu_editor, "menu_editor"
10520 &setup.internal.menu_graphics, "menu_graphics"
10524 &setup.internal.menu_sound, "menu_sound"
10528 &setup.internal.menu_artwork, "menu_artwork"
10532 &setup.internal.menu_input, "menu_input"
10536 &setup.internal.menu_touch, "menu_touch"
10540 &setup.internal.menu_shortcuts, "menu_shortcuts"
10544 &setup.internal.menu_exit, "menu_exit"
10548 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10552 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10556 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10560 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10564 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10568 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10572 &setup.internal.info_title, "info_title"
10576 &setup.internal.info_elements, "info_elements"
10580 &setup.internal.info_music, "info_music"
10584 &setup.internal.info_credits, "info_credits"
10588 &setup.internal.info_program, "info_program"
10592 &setup.internal.info_version, "info_version"
10596 &setup.internal.info_levelset, "info_levelset"
10600 &setup.internal.info_exit, "info_exit"
10604 static struct TokenInfo debug_setup_tokens[] =
10608 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10612 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10616 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10620 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10624 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10628 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10632 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10636 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10640 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10644 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10648 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10652 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10656 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10660 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10664 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10668 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10672 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10676 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10680 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10684 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10688 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10691 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10695 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10699 &setup.debug.xsn_mode, "debug.xsn_mode"
10703 &setup.debug.xsn_percent, "debug.xsn_percent"
10707 static struct TokenInfo options_setup_tokens[] =
10711 &setup.options.verbose, "options.verbose"
10715 &setup.options.debug, "options.debug"
10719 &setup.options.debug_mode, "options.debug_mode"
10723 static void setSetupInfoToDefaults(struct SetupInfo *si)
10727 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10729 si->multiple_users = TRUE;
10732 si->sound_loops = TRUE;
10733 si->sound_music = TRUE;
10734 si->sound_simple = TRUE;
10736 si->global_animations = TRUE;
10737 si->scroll_delay = TRUE;
10738 si->forced_scroll_delay = FALSE;
10739 si->scroll_delay_value = STD_SCROLL_DELAY;
10740 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10741 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10742 si->fade_screens = TRUE;
10743 si->autorecord = TRUE;
10744 si->autorecord_after_replay = TRUE;
10745 si->auto_pause_on_start = FALSE;
10746 si->show_titlescreen = TRUE;
10747 si->quick_doors = FALSE;
10748 si->team_mode = FALSE;
10749 si->handicap = TRUE;
10750 si->skip_levels = TRUE;
10751 si->increment_levels = TRUE;
10752 si->auto_play_next_level = TRUE;
10753 si->count_score_after_game = TRUE;
10754 si->show_scores_after_game = TRUE;
10755 si->time_limit = TRUE;
10756 si->fullscreen = FALSE;
10757 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10758 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10759 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10760 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10761 si->ask_on_escape = TRUE;
10762 si->ask_on_escape_editor = TRUE;
10763 si->ask_on_game_over = TRUE;
10764 si->ask_on_quit_game = TRUE;
10765 si->ask_on_quit_program = TRUE;
10766 si->quick_switch = FALSE;
10767 si->input_on_focus = FALSE;
10768 si->prefer_aga_graphics = TRUE;
10769 si->prefer_lowpass_sounds = FALSE;
10770 si->prefer_extra_panel_items = TRUE;
10771 si->game_speed_extended = FALSE;
10772 si->game_frame_delay = GAME_FRAME_DELAY;
10773 si->bd_skip_uncovering = FALSE;
10774 si->bd_skip_hatching = FALSE;
10775 si->bd_scroll_delay = TRUE;
10776 si->bd_smooth_movements = AUTO;
10777 si->sp_show_border_elements = FALSE;
10778 si->small_game_graphics = FALSE;
10779 si->show_load_save_buttons = FALSE;
10780 si->show_undo_redo_buttons = FALSE;
10781 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10783 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10784 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10785 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10787 si->override_level_graphics = FALSE;
10788 si->override_level_sounds = FALSE;
10789 si->override_level_music = FALSE;
10791 si->volume_simple = 100; // percent
10792 si->volume_loops = 100; // percent
10793 si->volume_music = 100; // percent
10795 si->network_mode = FALSE;
10796 si->network_player_nr = 0; // first player
10797 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10799 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10800 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10801 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10802 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10803 si->touch.draw_outlined = TRUE;
10804 si->touch.draw_pressed = TRUE;
10806 for (i = 0; i < 2; i++)
10808 char *default_grid_button[6][2] =
10814 { "111222", " vv " },
10815 { "111222", " vv " }
10817 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10818 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10819 int min_xsize = MIN(6, grid_xsize);
10820 int min_ysize = MIN(6, grid_ysize);
10821 int startx = grid_xsize - min_xsize;
10822 int starty = grid_ysize - min_ysize;
10825 // virtual buttons grid can only be set to defaults if video is initialized
10826 // (this will be repeated if virtual buttons are not loaded from setup file)
10827 if (video.initialized)
10829 si->touch.grid_xsize[i] = grid_xsize;
10830 si->touch.grid_ysize[i] = grid_ysize;
10834 si->touch.grid_xsize[i] = -1;
10835 si->touch.grid_ysize[i] = -1;
10838 for (x = 0; x < MAX_GRID_XSIZE; x++)
10839 for (y = 0; y < MAX_GRID_YSIZE; y++)
10840 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10842 for (x = 0; x < min_xsize; x++)
10843 for (y = 0; y < min_ysize; y++)
10844 si->touch.grid_button[i][x][starty + y] =
10845 default_grid_button[y][0][x];
10847 for (x = 0; x < min_xsize; x++)
10848 for (y = 0; y < min_ysize; y++)
10849 si->touch.grid_button[i][startx + x][starty + y] =
10850 default_grid_button[y][1][x];
10853 si->touch.grid_initialized = video.initialized;
10855 si->touch.overlay_buttons = FALSE;
10857 si->editor.el_boulderdash = TRUE;
10858 si->editor.el_boulderdash_native = TRUE;
10859 si->editor.el_emerald_mine = TRUE;
10860 si->editor.el_emerald_mine_club = TRUE;
10861 si->editor.el_more = TRUE;
10862 si->editor.el_sokoban = TRUE;
10863 si->editor.el_supaplex = TRUE;
10864 si->editor.el_diamond_caves = TRUE;
10865 si->editor.el_dx_boulderdash = TRUE;
10867 si->editor.el_mirror_magic = TRUE;
10868 si->editor.el_deflektor = TRUE;
10870 si->editor.el_chars = TRUE;
10871 si->editor.el_steel_chars = TRUE;
10873 si->editor.el_classic = TRUE;
10874 si->editor.el_custom = TRUE;
10876 si->editor.el_user_defined = FALSE;
10877 si->editor.el_dynamic = TRUE;
10879 si->editor.el_headlines = TRUE;
10881 si->editor.show_element_token = FALSE;
10883 si->editor.show_read_only_warning = TRUE;
10885 si->editor.use_template_for_new_levels = TRUE;
10887 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10888 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10889 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10890 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10891 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10893 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10894 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10895 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10896 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10897 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10899 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10900 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10901 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10902 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10903 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10904 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10906 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10907 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10908 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10910 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10911 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10912 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10913 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10915 for (i = 0; i < MAX_PLAYERS; i++)
10917 si->input[i].use_joystick = FALSE;
10918 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10919 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10920 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10921 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10922 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10923 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10924 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10925 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10926 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10927 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10928 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10929 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10930 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10931 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10932 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10935 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10936 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10937 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10938 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10940 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10941 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10942 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10943 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10944 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10945 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10946 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10948 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10950 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10951 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10952 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10954 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10955 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10956 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10958 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10959 si->internal.choose_from_top_leveldir = FALSE;
10960 si->internal.show_scaling_in_title = TRUE;
10961 si->internal.create_user_levelset = TRUE;
10962 si->internal.info_screens_from_main = FALSE;
10964 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10965 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10967 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10968 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10969 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10970 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10971 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10972 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10973 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10974 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10975 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10976 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10978 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10979 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10980 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10981 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10982 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10983 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10984 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10985 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10986 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10987 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10989 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10990 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10992 si->debug.show_frames_per_second = FALSE;
10994 si->debug.xsn_mode = AUTO;
10995 si->debug.xsn_percent = 0;
10997 si->options.verbose = FALSE;
10998 si->options.debug = FALSE;
10999 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11001 #if defined(PLATFORM_ANDROID)
11002 si->fullscreen = TRUE;
11003 si->touch.overlay_buttons = TRUE;
11006 setHideSetupEntry(&setup.debug.xsn_mode);
11009 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11011 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11014 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11016 si->player_uuid = NULL; // (will be set later)
11017 si->player_version = 1; // (will be set later)
11019 si->use_api_server = TRUE;
11020 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11021 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11022 si->ask_for_uploading_tapes = TRUE;
11023 si->ask_for_remaining_tapes = FALSE;
11024 si->provide_uploading_tapes = TRUE;
11025 si->ask_for_using_api_server = TRUE;
11026 si->has_remaining_tapes = FALSE;
11029 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11031 si->editor_cascade.el_bd = TRUE;
11032 si->editor_cascade.el_bd_native = TRUE;
11033 si->editor_cascade.el_em = TRUE;
11034 si->editor_cascade.el_emc = TRUE;
11035 si->editor_cascade.el_rnd = TRUE;
11036 si->editor_cascade.el_sb = TRUE;
11037 si->editor_cascade.el_sp = TRUE;
11038 si->editor_cascade.el_dc = TRUE;
11039 si->editor_cascade.el_dx = TRUE;
11041 si->editor_cascade.el_mm = TRUE;
11042 si->editor_cascade.el_df = TRUE;
11044 si->editor_cascade.el_chars = FALSE;
11045 si->editor_cascade.el_steel_chars = FALSE;
11046 si->editor_cascade.el_ce = FALSE;
11047 si->editor_cascade.el_ge = FALSE;
11048 si->editor_cascade.el_es = FALSE;
11049 si->editor_cascade.el_ref = FALSE;
11050 si->editor_cascade.el_user = FALSE;
11051 si->editor_cascade.el_dynamic = FALSE;
11054 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11056 static char *getHideSetupToken(void *setup_value)
11058 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11060 if (setup_value != NULL)
11061 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11063 return hide_setup_token;
11066 void setHideSetupEntry(void *setup_value)
11068 char *hide_setup_token = getHideSetupToken(setup_value);
11070 if (hide_setup_hash == NULL)
11071 hide_setup_hash = newSetupFileHash();
11073 if (setup_value != NULL)
11074 setHashEntry(hide_setup_hash, hide_setup_token, "");
11077 void removeHideSetupEntry(void *setup_value)
11079 char *hide_setup_token = getHideSetupToken(setup_value);
11081 if (setup_value != NULL)
11082 removeHashEntry(hide_setup_hash, hide_setup_token);
11085 boolean hideSetupEntry(void *setup_value)
11087 char *hide_setup_token = getHideSetupToken(setup_value);
11089 return (setup_value != NULL &&
11090 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11093 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11094 struct TokenInfo *token_info,
11095 int token_nr, char *token_text)
11097 char *token_hide_text = getStringCat2(token_text, ".hide");
11098 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11100 // set the value of this setup option in the setup option structure
11101 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11103 // check if this setup option should be hidden in the setup menu
11104 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11105 setHideSetupEntry(token_info[token_nr].value);
11107 free(token_hide_text);
11110 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11111 struct TokenInfo *token_info,
11114 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11115 token_info[token_nr].text);
11118 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11122 if (!setup_file_hash)
11125 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11126 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11128 setup.touch.grid_initialized = TRUE;
11129 for (i = 0; i < 2; i++)
11131 int grid_xsize = setup.touch.grid_xsize[i];
11132 int grid_ysize = setup.touch.grid_ysize[i];
11135 // if virtual buttons are not loaded from setup file, repeat initializing
11136 // virtual buttons grid with default values later when video is initialized
11137 if (grid_xsize == -1 ||
11140 setup.touch.grid_initialized = FALSE;
11145 for (y = 0; y < grid_ysize; y++)
11147 char token_string[MAX_LINE_LEN];
11149 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11151 char *value_string = getHashEntry(setup_file_hash, token_string);
11153 if (value_string == NULL)
11156 for (x = 0; x < grid_xsize; x++)
11158 char c = value_string[x];
11160 setup.touch.grid_button[i][x][y] =
11161 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11166 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11167 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11169 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11170 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11172 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11176 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11178 setup_input = setup.input[pnr];
11179 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11181 char full_token[100];
11183 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11184 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11187 setup.input[pnr] = setup_input;
11190 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11191 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11193 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11194 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11196 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11197 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11199 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11200 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11202 setHideRelatedSetupEntries();
11205 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11209 if (!setup_file_hash)
11212 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11213 setSetupInfo(auto_setup_tokens, i,
11214 getHashEntry(setup_file_hash,
11215 auto_setup_tokens[i].text));
11218 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11222 if (!setup_file_hash)
11225 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11226 setSetupInfo(server_setup_tokens, i,
11227 getHashEntry(setup_file_hash,
11228 server_setup_tokens[i].text));
11231 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11235 if (!setup_file_hash)
11238 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11239 setSetupInfo(editor_cascade_setup_tokens, i,
11240 getHashEntry(setup_file_hash,
11241 editor_cascade_setup_tokens[i].text));
11244 void LoadUserNames(void)
11246 int last_user_nr = user.nr;
11249 if (global.user_names != NULL)
11251 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11252 checked_free(global.user_names[i]);
11254 checked_free(global.user_names);
11257 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11259 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11263 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11265 if (setup_file_hash)
11267 char *player_name = getHashEntry(setup_file_hash, "player_name");
11269 global.user_names[i] = getFixedUserName(player_name);
11271 freeSetupFileHash(setup_file_hash);
11274 if (global.user_names[i] == NULL)
11275 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11278 user.nr = last_user_nr;
11281 void LoadSetupFromFilename(char *filename)
11283 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11285 if (setup_file_hash)
11287 decodeSetupFileHash_Default(setup_file_hash);
11289 freeSetupFileHash(setup_file_hash);
11293 Debug("setup", "using default setup values");
11297 static void LoadSetup_SpecialPostProcessing(void)
11299 char *player_name_new;
11301 // needed to work around problems with fixed length strings
11302 player_name_new = getFixedUserName(setup.player_name);
11303 free(setup.player_name);
11304 setup.player_name = player_name_new;
11306 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11307 if (setup.scroll_delay == FALSE)
11309 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11310 setup.scroll_delay = TRUE; // now always "on"
11313 // make sure that scroll delay value stays inside valid range
11314 setup.scroll_delay_value =
11315 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11318 void LoadSetup_Default(void)
11322 // always start with reliable default values
11323 setSetupInfoToDefaults(&setup);
11325 // try to load setup values from default setup file
11326 filename = getDefaultSetupFilename();
11328 if (fileExists(filename))
11329 LoadSetupFromFilename(filename);
11331 // try to load setup values from platform setup file
11332 filename = getPlatformSetupFilename();
11334 if (fileExists(filename))
11335 LoadSetupFromFilename(filename);
11337 // try to load setup values from user setup file
11338 filename = getSetupFilename();
11340 LoadSetupFromFilename(filename);
11342 LoadSetup_SpecialPostProcessing();
11345 void LoadSetup_AutoSetup(void)
11347 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11348 SetupFileHash *setup_file_hash = NULL;
11350 // always start with reliable default values
11351 setSetupInfoToDefaults_AutoSetup(&setup);
11353 setup_file_hash = loadSetupFileHash(filename);
11355 if (setup_file_hash)
11357 decodeSetupFileHash_AutoSetup(setup_file_hash);
11359 freeSetupFileHash(setup_file_hash);
11365 void LoadSetup_ServerSetup(void)
11367 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11368 SetupFileHash *setup_file_hash = NULL;
11370 // always start with reliable default values
11371 setSetupInfoToDefaults_ServerSetup(&setup);
11373 setup_file_hash = loadSetupFileHash(filename);
11375 if (setup_file_hash)
11377 decodeSetupFileHash_ServerSetup(setup_file_hash);
11379 freeSetupFileHash(setup_file_hash);
11384 if (setup.player_uuid == NULL)
11386 // player UUID does not yet exist in setup file
11387 setup.player_uuid = getStringCopy(getUUID());
11388 setup.player_version = 2;
11390 SaveSetup_ServerSetup();
11394 void LoadSetup_EditorCascade(void)
11396 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11397 SetupFileHash *setup_file_hash = NULL;
11399 // always start with reliable default values
11400 setSetupInfoToDefaults_EditorCascade(&setup);
11402 setup_file_hash = loadSetupFileHash(filename);
11404 if (setup_file_hash)
11406 decodeSetupFileHash_EditorCascade(setup_file_hash);
11408 freeSetupFileHash(setup_file_hash);
11414 void LoadSetup(void)
11416 LoadSetup_Default();
11417 LoadSetup_AutoSetup();
11418 LoadSetup_ServerSetup();
11419 LoadSetup_EditorCascade();
11422 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11423 char *mapping_line)
11425 char mapping_guid[MAX_LINE_LEN];
11426 char *mapping_start, *mapping_end;
11428 // get GUID from game controller mapping line: copy complete line
11429 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11430 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11432 // get GUID from game controller mapping line: cut after GUID part
11433 mapping_start = strchr(mapping_guid, ',');
11434 if (mapping_start != NULL)
11435 *mapping_start = '\0';
11437 // cut newline from game controller mapping line
11438 mapping_end = strchr(mapping_line, '\n');
11439 if (mapping_end != NULL)
11440 *mapping_end = '\0';
11442 // add mapping entry to game controller mappings hash
11443 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11446 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11451 if (!(file = fopen(filename, MODE_READ)))
11453 Warn("cannot read game controller mappings file '%s'", filename);
11458 while (!feof(file))
11460 char line[MAX_LINE_LEN];
11462 if (!fgets(line, MAX_LINE_LEN, file))
11465 addGameControllerMappingToHash(mappings_hash, line);
11471 void SaveSetup_Default(void)
11473 char *filename = getSetupFilename();
11477 InitUserDataDirectory();
11479 if (!(file = fopen(filename, MODE_WRITE)))
11481 Warn("cannot write setup file '%s'", filename);
11486 fprintFileHeader(file, SETUP_FILENAME);
11488 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11490 // just to make things nicer :)
11491 if (global_setup_tokens[i].value == &setup.multiple_users ||
11492 global_setup_tokens[i].value == &setup.sound ||
11493 global_setup_tokens[i].value == &setup.graphics_set ||
11494 global_setup_tokens[i].value == &setup.volume_simple ||
11495 global_setup_tokens[i].value == &setup.network_mode ||
11496 global_setup_tokens[i].value == &setup.touch.control_type ||
11497 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11498 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11499 fprintf(file, "\n");
11501 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11504 for (i = 0; i < 2; i++)
11506 int grid_xsize = setup.touch.grid_xsize[i];
11507 int grid_ysize = setup.touch.grid_ysize[i];
11510 fprintf(file, "\n");
11512 for (y = 0; y < grid_ysize; y++)
11514 char token_string[MAX_LINE_LEN];
11515 char value_string[MAX_LINE_LEN];
11517 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11519 for (x = 0; x < grid_xsize; x++)
11521 char c = setup.touch.grid_button[i][x][y];
11523 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11526 value_string[grid_xsize] = '\0';
11528 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11532 fprintf(file, "\n");
11533 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11534 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11536 fprintf(file, "\n");
11537 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11538 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11540 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11544 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11545 fprintf(file, "\n");
11547 setup_input = setup.input[pnr];
11548 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11549 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11552 fprintf(file, "\n");
11553 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11554 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11556 // (internal setup values not saved to user setup file)
11558 fprintf(file, "\n");
11559 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11560 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11561 setup.debug.xsn_mode != AUTO)
11562 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11564 fprintf(file, "\n");
11565 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11566 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11570 SetFilePermissions(filename, PERMS_PRIVATE);
11573 void SaveSetup_AutoSetup(void)
11575 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11579 InitUserDataDirectory();
11581 if (!(file = fopen(filename, MODE_WRITE)))
11583 Warn("cannot write auto setup file '%s'", filename);
11590 fprintFileHeader(file, AUTOSETUP_FILENAME);
11592 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11593 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11597 SetFilePermissions(filename, PERMS_PRIVATE);
11602 void SaveSetup_ServerSetup(void)
11604 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11608 InitUserDataDirectory();
11610 if (!(file = fopen(filename, MODE_WRITE)))
11612 Warn("cannot write server setup file '%s'", filename);
11619 fprintFileHeader(file, SERVERSETUP_FILENAME);
11621 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11623 // just to make things nicer :)
11624 if (server_setup_tokens[i].value == &setup.use_api_server)
11625 fprintf(file, "\n");
11627 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11632 SetFilePermissions(filename, PERMS_PRIVATE);
11637 void SaveSetup_EditorCascade(void)
11639 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11643 InitUserDataDirectory();
11645 if (!(file = fopen(filename, MODE_WRITE)))
11647 Warn("cannot write editor cascade state file '%s'", filename);
11654 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11656 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11657 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11661 SetFilePermissions(filename, PERMS_PRIVATE);
11666 void SaveSetup(void)
11668 SaveSetup_Default();
11669 SaveSetup_AutoSetup();
11670 SaveSetup_ServerSetup();
11671 SaveSetup_EditorCascade();
11674 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11679 if (!(file = fopen(filename, MODE_WRITE)))
11681 Warn("cannot write game controller mappings file '%s'", filename);
11686 BEGIN_HASH_ITERATION(mappings_hash, itr)
11688 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11690 END_HASH_ITERATION(mappings_hash, itr)
11695 void SaveSetup_AddGameControllerMapping(char *mapping)
11697 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11698 SetupFileHash *mappings_hash = newSetupFileHash();
11700 InitUserDataDirectory();
11702 // load existing personal game controller mappings
11703 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11705 // add new mapping to personal game controller mappings
11706 addGameControllerMappingToHash(mappings_hash, mapping);
11708 // save updated personal game controller mappings
11709 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11711 freeSetupFileHash(mappings_hash);
11715 void LoadCustomElementDescriptions(void)
11717 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11718 SetupFileHash *setup_file_hash;
11721 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11723 if (element_info[i].custom_description != NULL)
11725 free(element_info[i].custom_description);
11726 element_info[i].custom_description = NULL;
11730 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11733 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11735 char *token = getStringCat2(element_info[i].token_name, ".name");
11736 char *value = getHashEntry(setup_file_hash, token);
11739 element_info[i].custom_description = getStringCopy(value);
11744 freeSetupFileHash(setup_file_hash);
11747 static int getElementFromToken(char *token)
11749 char *value = getHashEntry(element_token_hash, token);
11752 return atoi(value);
11754 Warn("unknown element token '%s'", token);
11756 return EL_UNDEFINED;
11759 void FreeGlobalAnimEventInfo(void)
11761 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11763 if (gaei->event_list == NULL)
11768 for (i = 0; i < gaei->num_event_lists; i++)
11770 checked_free(gaei->event_list[i]->event_value);
11771 checked_free(gaei->event_list[i]);
11774 checked_free(gaei->event_list);
11776 gaei->event_list = NULL;
11777 gaei->num_event_lists = 0;
11780 static int AddGlobalAnimEventList(void)
11782 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11783 int list_pos = gaei->num_event_lists++;
11785 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11786 sizeof(struct GlobalAnimEventListInfo *));
11788 gaei->event_list[list_pos] =
11789 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11791 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11793 gaeli->event_value = NULL;
11794 gaeli->num_event_values = 0;
11799 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11801 // do not add empty global animation events
11802 if (event_value == ANIM_EVENT_NONE)
11805 // if list position is undefined, create new list
11806 if (list_pos == ANIM_EVENT_UNDEFINED)
11807 list_pos = AddGlobalAnimEventList();
11809 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11810 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11811 int value_pos = gaeli->num_event_values++;
11813 gaeli->event_value = checked_realloc(gaeli->event_value,
11814 gaeli->num_event_values * sizeof(int *));
11816 gaeli->event_value[value_pos] = event_value;
11821 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11823 if (list_pos == ANIM_EVENT_UNDEFINED)
11824 return ANIM_EVENT_NONE;
11826 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11827 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11829 return gaeli->event_value[value_pos];
11832 int GetGlobalAnimEventValueCount(int list_pos)
11834 if (list_pos == ANIM_EVENT_UNDEFINED)
11837 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11838 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11840 return gaeli->num_event_values;
11843 // This function checks if a string <s> of the format "string1, string2, ..."
11844 // exactly contains a string <s_contained>.
11846 static boolean string_has_parameter(char *s, char *s_contained)
11850 if (s == NULL || s_contained == NULL)
11853 if (strlen(s_contained) > strlen(s))
11856 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11858 char next_char = s[strlen(s_contained)];
11860 // check if next character is delimiter or whitespace
11861 if (next_char == ',' || next_char == '\0' ||
11862 next_char == ' ' || next_char == '\t')
11866 // check if string contains another parameter string after a comma
11867 substring = strchr(s, ',');
11868 if (substring == NULL) // string does not contain a comma
11871 // advance string pointer to next character after the comma
11874 // skip potential whitespaces after the comma
11875 while (*substring == ' ' || *substring == '\t')
11878 return string_has_parameter(substring, s_contained);
11881 static int get_anim_parameter_value_ce(char *s)
11884 char *pattern_1 = "ce_change:custom_";
11885 char *pattern_2 = ".page_";
11886 int pattern_1_len = strlen(pattern_1);
11887 char *matching_char = strstr(s_ptr, pattern_1);
11888 int result = ANIM_EVENT_NONE;
11890 if (matching_char == NULL)
11891 return ANIM_EVENT_NONE;
11893 result = ANIM_EVENT_CE_CHANGE;
11895 s_ptr = matching_char + pattern_1_len;
11897 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11898 if (*s_ptr >= '0' && *s_ptr <= '9')
11900 int gic_ce_nr = (*s_ptr++ - '0');
11902 if (*s_ptr >= '0' && *s_ptr <= '9')
11904 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11906 if (*s_ptr >= '0' && *s_ptr <= '9')
11907 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11910 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11911 return ANIM_EVENT_NONE;
11913 // custom element stored as 0 to 255
11916 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11920 // invalid custom element number specified
11922 return ANIM_EVENT_NONE;
11925 // check for change page number ("page_X" or "page_XX") (optional)
11926 if (strPrefix(s_ptr, pattern_2))
11928 s_ptr += strlen(pattern_2);
11930 if (*s_ptr >= '0' && *s_ptr <= '9')
11932 int gic_page_nr = (*s_ptr++ - '0');
11934 if (*s_ptr >= '0' && *s_ptr <= '9')
11935 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11937 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11938 return ANIM_EVENT_NONE;
11940 // change page stored as 1 to 32 (0 means "all change pages")
11942 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11946 // invalid animation part number specified
11948 return ANIM_EVENT_NONE;
11952 // discard result if next character is neither delimiter nor whitespace
11953 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11954 *s_ptr == ' ' || *s_ptr == '\t'))
11955 return ANIM_EVENT_NONE;
11960 static int get_anim_parameter_value(char *s)
11962 int event_value[] =
11970 char *pattern_1[] =
11978 char *pattern_2 = ".part_";
11979 char *matching_char = NULL;
11981 int pattern_1_len = 0;
11982 int result = ANIM_EVENT_NONE;
11985 result = get_anim_parameter_value_ce(s);
11987 if (result != ANIM_EVENT_NONE)
11990 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11992 matching_char = strstr(s_ptr, pattern_1[i]);
11993 pattern_1_len = strlen(pattern_1[i]);
11994 result = event_value[i];
11996 if (matching_char != NULL)
12000 if (matching_char == NULL)
12001 return ANIM_EVENT_NONE;
12003 s_ptr = matching_char + pattern_1_len;
12005 // check for main animation number ("anim_X" or "anim_XX")
12006 if (*s_ptr >= '0' && *s_ptr <= '9')
12008 int gic_anim_nr = (*s_ptr++ - '0');
12010 if (*s_ptr >= '0' && *s_ptr <= '9')
12011 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12013 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12014 return ANIM_EVENT_NONE;
12016 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12020 // invalid main animation number specified
12022 return ANIM_EVENT_NONE;
12025 // check for animation part number ("part_X" or "part_XX") (optional)
12026 if (strPrefix(s_ptr, pattern_2))
12028 s_ptr += strlen(pattern_2);
12030 if (*s_ptr >= '0' && *s_ptr <= '9')
12032 int gic_part_nr = (*s_ptr++ - '0');
12034 if (*s_ptr >= '0' && *s_ptr <= '9')
12035 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12037 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12038 return ANIM_EVENT_NONE;
12040 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12044 // invalid animation part number specified
12046 return ANIM_EVENT_NONE;
12050 // discard result if next character is neither delimiter nor whitespace
12051 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12052 *s_ptr == ' ' || *s_ptr == '\t'))
12053 return ANIM_EVENT_NONE;
12058 static int get_anim_parameter_values(char *s)
12060 int list_pos = ANIM_EVENT_UNDEFINED;
12061 int event_value = ANIM_EVENT_DEFAULT;
12063 if (string_has_parameter(s, "any"))
12064 event_value |= ANIM_EVENT_ANY;
12066 if (string_has_parameter(s, "click:self") ||
12067 string_has_parameter(s, "click") ||
12068 string_has_parameter(s, "self"))
12069 event_value |= ANIM_EVENT_SELF;
12071 if (string_has_parameter(s, "unclick:any"))
12072 event_value |= ANIM_EVENT_UNCLICK_ANY;
12074 // if animation event found, add it to global animation event list
12075 if (event_value != ANIM_EVENT_NONE)
12076 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12080 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12081 event_value = get_anim_parameter_value(s);
12083 // if animation event found, add it to global animation event list
12084 if (event_value != ANIM_EVENT_NONE)
12085 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12087 // continue with next part of the string, starting with next comma
12088 s = strchr(s + 1, ',');
12094 static int get_anim_action_parameter_value(char *token)
12096 // check most common default case first to massively speed things up
12097 if (strEqual(token, ARG_UNDEFINED))
12098 return ANIM_EVENT_ACTION_NONE;
12100 int result = getImageIDFromToken(token);
12104 char *gfx_token = getStringCat2("gfx.", token);
12106 result = getImageIDFromToken(gfx_token);
12108 checked_free(gfx_token);
12113 Key key = getKeyFromX11KeyName(token);
12115 if (key != KSYM_UNDEFINED)
12116 result = -(int)key;
12123 result = get_hash_from_string(token); // unsigned int => int
12124 result = ABS(result); // may be negative now
12125 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12127 setHashEntry(anim_url_hash, int2str(result, 0), token);
12132 result = ANIM_EVENT_ACTION_NONE;
12137 int get_parameter_value(char *value_raw, char *suffix, int type)
12139 char *value = getStringToLower(value_raw);
12140 int result = 0; // probably a save default value
12142 if (strEqual(suffix, ".direction"))
12144 result = (strEqual(value, "left") ? MV_LEFT :
12145 strEqual(value, "right") ? MV_RIGHT :
12146 strEqual(value, "up") ? MV_UP :
12147 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12149 else if (strEqual(suffix, ".position"))
12151 result = (strEqual(value, "left") ? POS_LEFT :
12152 strEqual(value, "right") ? POS_RIGHT :
12153 strEqual(value, "top") ? POS_TOP :
12154 strEqual(value, "upper") ? POS_UPPER :
12155 strEqual(value, "middle") ? POS_MIDDLE :
12156 strEqual(value, "lower") ? POS_LOWER :
12157 strEqual(value, "bottom") ? POS_BOTTOM :
12158 strEqual(value, "any") ? POS_ANY :
12159 strEqual(value, "ce") ? POS_CE :
12160 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12161 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12163 else if (strEqual(suffix, ".align"))
12165 result = (strEqual(value, "left") ? ALIGN_LEFT :
12166 strEqual(value, "right") ? ALIGN_RIGHT :
12167 strEqual(value, "center") ? ALIGN_CENTER :
12168 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12170 else if (strEqual(suffix, ".valign"))
12172 result = (strEqual(value, "top") ? VALIGN_TOP :
12173 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12174 strEqual(value, "middle") ? VALIGN_MIDDLE :
12175 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12177 else if (strEqual(suffix, ".anim_mode"))
12179 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12180 string_has_parameter(value, "loop") ? ANIM_LOOP :
12181 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12182 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12183 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12184 string_has_parameter(value, "random") ? ANIM_RANDOM :
12185 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12186 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12187 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12188 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12189 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12190 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12191 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12192 string_has_parameter(value, "all") ? ANIM_ALL :
12193 string_has_parameter(value, "tiled") ? ANIM_TILED :
12194 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12197 if (string_has_parameter(value, "once"))
12198 result |= ANIM_ONCE;
12200 if (string_has_parameter(value, "reverse"))
12201 result |= ANIM_REVERSE;
12203 if (string_has_parameter(value, "opaque_player"))
12204 result |= ANIM_OPAQUE_PLAYER;
12206 if (string_has_parameter(value, "static_panel"))
12207 result |= ANIM_STATIC_PANEL;
12209 else if (strEqual(suffix, ".init_event") ||
12210 strEqual(suffix, ".anim_event"))
12212 result = get_anim_parameter_values(value);
12214 else if (strEqual(suffix, ".init_delay_action") ||
12215 strEqual(suffix, ".anim_delay_action") ||
12216 strEqual(suffix, ".post_delay_action") ||
12217 strEqual(suffix, ".init_event_action") ||
12218 strEqual(suffix, ".anim_event_action"))
12220 result = get_anim_action_parameter_value(value_raw);
12222 else if (strEqual(suffix, ".class"))
12224 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12225 get_hash_from_string(value));
12227 else if (strEqual(suffix, ".style"))
12229 result = STYLE_DEFAULT;
12231 if (string_has_parameter(value, "accurate_borders"))
12232 result |= STYLE_ACCURATE_BORDERS;
12234 if (string_has_parameter(value, "inner_corners"))
12235 result |= STYLE_INNER_CORNERS;
12237 if (string_has_parameter(value, "reverse"))
12238 result |= STYLE_REVERSE;
12240 if (string_has_parameter(value, "leftmost_position"))
12241 result |= STYLE_LEFTMOST_POSITION;
12243 if (string_has_parameter(value, "block_clicks"))
12244 result |= STYLE_BLOCK;
12246 if (string_has_parameter(value, "passthrough_clicks"))
12247 result |= STYLE_PASSTHROUGH;
12249 if (string_has_parameter(value, "multiple_actions"))
12250 result |= STYLE_MULTIPLE_ACTIONS;
12252 if (string_has_parameter(value, "consume_ce_event"))
12253 result |= STYLE_CONSUME_CE_EVENT;
12255 else if (strEqual(suffix, ".fade_mode"))
12257 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12258 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12259 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12260 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12261 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12262 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12263 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12264 FADE_MODE_DEFAULT);
12266 else if (strEqual(suffix, ".auto_delay_unit"))
12268 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12269 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12270 AUTO_DELAY_UNIT_DEFAULT);
12272 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12274 result = gfx.get_font_from_token_function(value);
12276 else // generic parameter of type integer or boolean
12278 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12279 type == TYPE_INTEGER ? get_integer_from_string(value) :
12280 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12281 ARG_UNDEFINED_VALUE);
12289 static int get_token_parameter_value(char *token, char *value_raw)
12293 if (token == NULL || value_raw == NULL)
12294 return ARG_UNDEFINED_VALUE;
12296 suffix = strrchr(token, '.');
12297 if (suffix == NULL)
12300 if (strEqual(suffix, ".element"))
12301 return getElementFromToken(value_raw);
12303 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12304 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12307 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12308 boolean ignore_defaults)
12312 for (i = 0; image_config_vars[i].token != NULL; i++)
12314 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12316 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12317 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12321 *image_config_vars[i].value =
12322 get_token_parameter_value(image_config_vars[i].token, value);
12326 void InitMenuDesignSettings_Static(void)
12328 // always start with reliable default values from static default config
12329 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12332 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12336 // the following initializes hierarchical values from static configuration
12338 // special case: initialize "ARG_DEFAULT" values in static default config
12339 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12340 titlescreen_initial_first_default.fade_mode =
12341 title_initial_first_default.fade_mode;
12342 titlescreen_initial_first_default.fade_delay =
12343 title_initial_first_default.fade_delay;
12344 titlescreen_initial_first_default.post_delay =
12345 title_initial_first_default.post_delay;
12346 titlescreen_initial_first_default.auto_delay =
12347 title_initial_first_default.auto_delay;
12348 titlescreen_initial_first_default.auto_delay_unit =
12349 title_initial_first_default.auto_delay_unit;
12350 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12351 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12352 titlescreen_first_default.post_delay = title_first_default.post_delay;
12353 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12354 titlescreen_first_default.auto_delay_unit =
12355 title_first_default.auto_delay_unit;
12356 titlemessage_initial_first_default.fade_mode =
12357 title_initial_first_default.fade_mode;
12358 titlemessage_initial_first_default.fade_delay =
12359 title_initial_first_default.fade_delay;
12360 titlemessage_initial_first_default.post_delay =
12361 title_initial_first_default.post_delay;
12362 titlemessage_initial_first_default.auto_delay =
12363 title_initial_first_default.auto_delay;
12364 titlemessage_initial_first_default.auto_delay_unit =
12365 title_initial_first_default.auto_delay_unit;
12366 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12367 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12368 titlemessage_first_default.post_delay = title_first_default.post_delay;
12369 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12370 titlemessage_first_default.auto_delay_unit =
12371 title_first_default.auto_delay_unit;
12373 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12374 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12375 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12376 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12377 titlescreen_initial_default.auto_delay_unit =
12378 title_initial_default.auto_delay_unit;
12379 titlescreen_default.fade_mode = title_default.fade_mode;
12380 titlescreen_default.fade_delay = title_default.fade_delay;
12381 titlescreen_default.post_delay = title_default.post_delay;
12382 titlescreen_default.auto_delay = title_default.auto_delay;
12383 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12384 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12385 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12386 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12387 titlemessage_initial_default.auto_delay_unit =
12388 title_initial_default.auto_delay_unit;
12389 titlemessage_default.fade_mode = title_default.fade_mode;
12390 titlemessage_default.fade_delay = title_default.fade_delay;
12391 titlemessage_default.post_delay = title_default.post_delay;
12392 titlemessage_default.auto_delay = title_default.auto_delay;
12393 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12395 // special case: initialize "ARG_DEFAULT" values in static default config
12396 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12397 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12399 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12400 titlescreen_first[i] = titlescreen_first_default;
12401 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12402 titlemessage_first[i] = titlemessage_first_default;
12404 titlescreen_initial[i] = titlescreen_initial_default;
12405 titlescreen[i] = titlescreen_default;
12406 titlemessage_initial[i] = titlemessage_initial_default;
12407 titlemessage[i] = titlemessage_default;
12410 // special case: initialize "ARG_DEFAULT" values in static default config
12411 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12412 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12414 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12417 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12418 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12419 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12422 // special case: initialize "ARG_DEFAULT" values in static default config
12423 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12424 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12426 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12427 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12428 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12430 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12433 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12437 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12441 struct XY *dst, *src;
12443 game_buttons_xy[] =
12445 { &game.button.save, &game.button.stop },
12446 { &game.button.pause2, &game.button.pause },
12447 { &game.button.load, &game.button.play },
12448 { &game.button.undo, &game.button.stop },
12449 { &game.button.redo, &game.button.play },
12455 // special case: initialize later added SETUP list size from LEVELS value
12456 if (menu.list_size[GAME_MODE_SETUP] == -1)
12457 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12459 // set default position for snapshot buttons to stop/pause/play buttons
12460 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12461 if ((*game_buttons_xy[i].dst).x == -1 &&
12462 (*game_buttons_xy[i].dst).y == -1)
12463 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12465 // --------------------------------------------------------------------------
12466 // dynamic viewports (including playfield margins, borders and alignments)
12467 // --------------------------------------------------------------------------
12469 // dynamic viewports currently only supported for landscape mode
12470 int display_width = MAX(video.display_width, video.display_height);
12471 int display_height = MIN(video.display_width, video.display_height);
12473 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12475 struct RectWithBorder *vp_window = &viewport.window[i];
12476 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12477 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12478 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12479 boolean dynamic_window_width = (vp_window->min_width != -1);
12480 boolean dynamic_window_height = (vp_window->min_height != -1);
12481 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12482 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12484 // adjust window size if min/max width/height is specified
12486 if (vp_window->min_width != -1)
12488 int window_width = display_width;
12490 // when using static window height, use aspect ratio of display
12491 if (vp_window->min_height == -1)
12492 window_width = vp_window->height * display_width / display_height;
12494 vp_window->width = MAX(vp_window->min_width, window_width);
12497 if (vp_window->min_height != -1)
12499 int window_height = display_height;
12501 // when using static window width, use aspect ratio of display
12502 if (vp_window->min_width == -1)
12503 window_height = vp_window->width * display_height / display_width;
12505 vp_window->height = MAX(vp_window->min_height, window_height);
12508 if (vp_window->max_width != -1)
12509 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12511 if (vp_window->max_height != -1)
12512 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12514 int playfield_width = vp_window->width;
12515 int playfield_height = vp_window->height;
12517 // adjust playfield size and position according to specified margins
12519 playfield_width -= vp_playfield->margin_left;
12520 playfield_width -= vp_playfield->margin_right;
12522 playfield_height -= vp_playfield->margin_top;
12523 playfield_height -= vp_playfield->margin_bottom;
12525 // adjust playfield size if min/max width/height is specified
12527 if (vp_playfield->min_width != -1)
12528 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12530 if (vp_playfield->min_height != -1)
12531 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12533 if (vp_playfield->max_width != -1)
12534 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12536 if (vp_playfield->max_height != -1)
12537 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12539 // adjust playfield position according to specified alignment
12541 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12542 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12543 else if (vp_playfield->align == ALIGN_CENTER)
12544 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12545 else if (vp_playfield->align == ALIGN_RIGHT)
12546 vp_playfield->x += playfield_width - vp_playfield->width;
12548 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12549 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12550 else if (vp_playfield->valign == VALIGN_MIDDLE)
12551 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12552 else if (vp_playfield->valign == VALIGN_BOTTOM)
12553 vp_playfield->y += playfield_height - vp_playfield->height;
12555 vp_playfield->x += vp_playfield->margin_left;
12556 vp_playfield->y += vp_playfield->margin_top;
12558 // adjust individual playfield borders if only default border is specified
12560 if (vp_playfield->border_left == -1)
12561 vp_playfield->border_left = vp_playfield->border_size;
12562 if (vp_playfield->border_right == -1)
12563 vp_playfield->border_right = vp_playfield->border_size;
12564 if (vp_playfield->border_top == -1)
12565 vp_playfield->border_top = vp_playfield->border_size;
12566 if (vp_playfield->border_bottom == -1)
12567 vp_playfield->border_bottom = vp_playfield->border_size;
12569 // set dynamic playfield borders if borders are specified as undefined
12570 // (but only if window size was dynamic and playfield size was static)
12572 if (dynamic_window_width && !dynamic_playfield_width)
12574 if (vp_playfield->border_left == -1)
12576 vp_playfield->border_left = (vp_playfield->x -
12577 vp_playfield->margin_left);
12578 vp_playfield->x -= vp_playfield->border_left;
12579 vp_playfield->width += vp_playfield->border_left;
12582 if (vp_playfield->border_right == -1)
12584 vp_playfield->border_right = (vp_window->width -
12586 vp_playfield->width -
12587 vp_playfield->margin_right);
12588 vp_playfield->width += vp_playfield->border_right;
12592 if (dynamic_window_height && !dynamic_playfield_height)
12594 if (vp_playfield->border_top == -1)
12596 vp_playfield->border_top = (vp_playfield->y -
12597 vp_playfield->margin_top);
12598 vp_playfield->y -= vp_playfield->border_top;
12599 vp_playfield->height += vp_playfield->border_top;
12602 if (vp_playfield->border_bottom == -1)
12604 vp_playfield->border_bottom = (vp_window->height -
12606 vp_playfield->height -
12607 vp_playfield->margin_bottom);
12608 vp_playfield->height += vp_playfield->border_bottom;
12612 // adjust playfield size to be a multiple of a defined alignment tile size
12614 int align_size = vp_playfield->align_size;
12615 int playfield_xtiles = vp_playfield->width / align_size;
12616 int playfield_ytiles = vp_playfield->height / align_size;
12617 int playfield_width_corrected = playfield_xtiles * align_size;
12618 int playfield_height_corrected = playfield_ytiles * align_size;
12619 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12620 i == GFX_SPECIAL_ARG_EDITOR);
12622 if (is_playfield_mode &&
12623 dynamic_playfield_width &&
12624 vp_playfield->width != playfield_width_corrected)
12626 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12628 vp_playfield->width = playfield_width_corrected;
12630 if (vp_playfield->align == ALIGN_LEFT)
12632 vp_playfield->border_left += playfield_xdiff;
12634 else if (vp_playfield->align == ALIGN_RIGHT)
12636 vp_playfield->border_right += playfield_xdiff;
12638 else if (vp_playfield->align == ALIGN_CENTER)
12640 int border_left_diff = playfield_xdiff / 2;
12641 int border_right_diff = playfield_xdiff - border_left_diff;
12643 vp_playfield->border_left += border_left_diff;
12644 vp_playfield->border_right += border_right_diff;
12648 if (is_playfield_mode &&
12649 dynamic_playfield_height &&
12650 vp_playfield->height != playfield_height_corrected)
12652 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12654 vp_playfield->height = playfield_height_corrected;
12656 if (vp_playfield->valign == VALIGN_TOP)
12658 vp_playfield->border_top += playfield_ydiff;
12660 else if (vp_playfield->align == VALIGN_BOTTOM)
12662 vp_playfield->border_right += playfield_ydiff;
12664 else if (vp_playfield->align == VALIGN_MIDDLE)
12666 int border_top_diff = playfield_ydiff / 2;
12667 int border_bottom_diff = playfield_ydiff - border_top_diff;
12669 vp_playfield->border_top += border_top_diff;
12670 vp_playfield->border_bottom += border_bottom_diff;
12674 // adjust door positions according to specified alignment
12676 for (j = 0; j < 2; j++)
12678 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12680 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12681 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12682 else if (vp_door->align == ALIGN_CENTER)
12683 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12684 else if (vp_door->align == ALIGN_RIGHT)
12685 vp_door->x += vp_window->width - vp_door->width;
12687 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12688 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12689 else if (vp_door->valign == VALIGN_MIDDLE)
12690 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12691 else if (vp_door->valign == VALIGN_BOTTOM)
12692 vp_door->y += vp_window->height - vp_door->height;
12697 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12701 struct XYTileSize *dst, *src;
12704 editor_buttons_xy[] =
12707 &editor.button.element_left, &editor.palette.element_left,
12708 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12711 &editor.button.element_middle, &editor.palette.element_middle,
12712 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12715 &editor.button.element_right, &editor.palette.element_right,
12716 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12723 // set default position for element buttons to element graphics
12724 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12726 if ((*editor_buttons_xy[i].dst).x == -1 &&
12727 (*editor_buttons_xy[i].dst).y == -1)
12729 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12731 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12733 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12737 // adjust editor palette rows and columns if specified to be dynamic
12739 if (editor.palette.cols == -1)
12741 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12742 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12743 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12745 editor.palette.cols = (vp_width - sc_width) / bt_width;
12747 if (editor.palette.x == -1)
12749 int palette_width = editor.palette.cols * bt_width + sc_width;
12751 editor.palette.x = (vp_width - palette_width) / 2;
12755 if (editor.palette.rows == -1)
12757 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12758 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12759 int tx_height = getFontHeight(FONT_TEXT_2);
12761 editor.palette.rows = (vp_height - tx_height) / bt_height;
12763 if (editor.palette.y == -1)
12765 int palette_height = editor.palette.rows * bt_height + tx_height;
12767 editor.palette.y = (vp_height - palette_height) / 2;
12772 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12773 boolean initialize)
12775 // special case: check if network and preview player positions are redefined,
12776 // to compare this later against the main menu level preview being redefined
12777 struct TokenIntPtrInfo menu_config_players[] =
12779 { "main.network_players.x", &menu.main.network_players.redefined },
12780 { "main.network_players.y", &menu.main.network_players.redefined },
12781 { "main.preview_players.x", &menu.main.preview_players.redefined },
12782 { "main.preview_players.y", &menu.main.preview_players.redefined },
12783 { "preview.x", &preview.redefined },
12784 { "preview.y", &preview.redefined }
12790 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12791 *menu_config_players[i].value = FALSE;
12795 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12796 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12797 *menu_config_players[i].value = TRUE;
12801 static void InitMenuDesignSettings_PreviewPlayers(void)
12803 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12806 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12808 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12811 static void LoadMenuDesignSettingsFromFilename(char *filename)
12813 static struct TitleFadingInfo tfi;
12814 static struct TitleMessageInfo tmi;
12815 static struct TokenInfo title_tokens[] =
12817 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12818 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12819 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12820 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12821 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12825 static struct TokenInfo titlemessage_tokens[] =
12827 { TYPE_INTEGER, &tmi.x, ".x" },
12828 { TYPE_INTEGER, &tmi.y, ".y" },
12829 { TYPE_INTEGER, &tmi.width, ".width" },
12830 { TYPE_INTEGER, &tmi.height, ".height" },
12831 { TYPE_INTEGER, &tmi.chars, ".chars" },
12832 { TYPE_INTEGER, &tmi.lines, ".lines" },
12833 { TYPE_INTEGER, &tmi.align, ".align" },
12834 { TYPE_INTEGER, &tmi.valign, ".valign" },
12835 { TYPE_INTEGER, &tmi.font, ".font" },
12836 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12837 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12838 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12839 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12840 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12841 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12842 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12843 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12844 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12850 struct TitleFadingInfo *info;
12855 // initialize first titles from "enter screen" definitions, if defined
12856 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12857 { &title_first_default, "menu.enter_screen.TITLE" },
12859 // initialize title screens from "next screen" definitions, if defined
12860 { &title_initial_default, "menu.next_screen.TITLE" },
12861 { &title_default, "menu.next_screen.TITLE" },
12867 struct TitleMessageInfo *array;
12870 titlemessage_arrays[] =
12872 // initialize first titles from "enter screen" definitions, if defined
12873 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12874 { titlescreen_first, "menu.enter_screen.TITLE" },
12875 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12876 { titlemessage_first, "menu.enter_screen.TITLE" },
12878 // initialize titles from "next screen" definitions, if defined
12879 { titlescreen_initial, "menu.next_screen.TITLE" },
12880 { titlescreen, "menu.next_screen.TITLE" },
12881 { titlemessage_initial, "menu.next_screen.TITLE" },
12882 { titlemessage, "menu.next_screen.TITLE" },
12884 // overwrite titles with title definitions, if defined
12885 { titlescreen_initial_first, "[title_initial]" },
12886 { titlescreen_first, "[title]" },
12887 { titlemessage_initial_first, "[title_initial]" },
12888 { titlemessage_first, "[title]" },
12890 { titlescreen_initial, "[title_initial]" },
12891 { titlescreen, "[title]" },
12892 { titlemessage_initial, "[title_initial]" },
12893 { titlemessage, "[title]" },
12895 // overwrite titles with title screen/message definitions, if defined
12896 { titlescreen_initial_first, "[titlescreen_initial]" },
12897 { titlescreen_first, "[titlescreen]" },
12898 { titlemessage_initial_first, "[titlemessage_initial]" },
12899 { titlemessage_first, "[titlemessage]" },
12901 { titlescreen_initial, "[titlescreen_initial]" },
12902 { titlescreen, "[titlescreen]" },
12903 { titlemessage_initial, "[titlemessage_initial]" },
12904 { titlemessage, "[titlemessage]" },
12908 SetupFileHash *setup_file_hash;
12911 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12914 // the following initializes hierarchical values from dynamic configuration
12916 // special case: initialize with default values that may be overwritten
12917 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12918 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12920 struct TokenIntPtrInfo menu_config[] =
12922 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12923 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12924 { "menu.list_size", &menu.list_size[i] }
12927 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12929 char *token = menu_config[j].token;
12930 char *value = getHashEntry(setup_file_hash, token);
12933 *menu_config[j].value = get_integer_from_string(value);
12937 // special case: initialize with default values that may be overwritten
12938 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12939 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12941 struct TokenIntPtrInfo menu_config[] =
12943 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12944 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12945 { "menu.list_size.INFO", &menu.list_size_info[i] },
12946 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12947 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12950 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12952 char *token = menu_config[j].token;
12953 char *value = getHashEntry(setup_file_hash, token);
12956 *menu_config[j].value = get_integer_from_string(value);
12960 // special case: initialize with default values that may be overwritten
12961 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12962 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12964 struct TokenIntPtrInfo menu_config[] =
12966 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12967 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
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_integer_from_string(value);
12980 // special case: initialize with default values that may be overwritten
12981 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12982 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12984 struct TokenIntPtrInfo menu_config[] =
12986 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12987 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
12988 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12989 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12990 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12991 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12992 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12993 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12994 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12995 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12998 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13000 char *token = menu_config[j].token;
13001 char *value = getHashEntry(setup_file_hash, token);
13004 *menu_config[j].value = get_integer_from_string(value);
13008 // special case: initialize with default values that may be overwritten
13009 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13010 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13012 struct TokenIntPtrInfo menu_config[] =
13014 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13015 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13016 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13017 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13018 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13019 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13020 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13021 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13022 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13025 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13027 char *token = menu_config[j].token;
13028 char *value = getHashEntry(setup_file_hash, token);
13031 *menu_config[j].value = get_token_parameter_value(token, value);
13035 // special case: initialize with default values that may be overwritten
13036 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13037 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13041 char *token_prefix;
13042 struct RectWithBorder *struct_ptr;
13046 { "viewport.window", &viewport.window[i] },
13047 { "viewport.playfield", &viewport.playfield[i] },
13048 { "viewport.door_1", &viewport.door_1[i] },
13049 { "viewport.door_2", &viewport.door_2[i] }
13052 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13054 struct TokenIntPtrInfo vp_config[] =
13056 { ".x", &vp_struct[j].struct_ptr->x },
13057 { ".y", &vp_struct[j].struct_ptr->y },
13058 { ".width", &vp_struct[j].struct_ptr->width },
13059 { ".height", &vp_struct[j].struct_ptr->height },
13060 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13061 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13062 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13063 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13064 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13065 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13066 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13067 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13068 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13069 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13070 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13071 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13072 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13073 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13074 { ".align", &vp_struct[j].struct_ptr->align },
13075 { ".valign", &vp_struct[j].struct_ptr->valign }
13078 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13080 char *token = getStringCat2(vp_struct[j].token_prefix,
13081 vp_config[k].token);
13082 char *value = getHashEntry(setup_file_hash, token);
13085 *vp_config[k].value = get_token_parameter_value(token, value);
13092 // special case: initialize with default values that may be overwritten
13093 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13094 for (i = 0; title_info[i].info != NULL; i++)
13096 struct TitleFadingInfo *info = title_info[i].info;
13097 char *base_token = title_info[i].text;
13099 for (j = 0; title_tokens[j].type != -1; j++)
13101 char *token = getStringCat2(base_token, title_tokens[j].text);
13102 char *value = getHashEntry(setup_file_hash, token);
13106 int parameter_value = get_token_parameter_value(token, value);
13110 *(int *)title_tokens[j].value = (int)parameter_value;
13119 // special case: initialize with default values that may be overwritten
13120 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13121 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13123 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13124 char *base_token = titlemessage_arrays[i].text;
13126 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13128 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13129 char *value = getHashEntry(setup_file_hash, token);
13133 int parameter_value = get_token_parameter_value(token, value);
13135 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13139 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13140 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13142 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13152 // read (and overwrite with) values that may be specified in config file
13153 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13155 // special case: check if network and preview player positions are redefined
13156 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13158 freeSetupFileHash(setup_file_hash);
13161 void LoadMenuDesignSettings(void)
13163 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13165 InitMenuDesignSettings_Static();
13166 InitMenuDesignSettings_SpecialPreProcessing();
13167 InitMenuDesignSettings_PreviewPlayers();
13169 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13171 // first look for special settings configured in level series config
13172 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13174 if (fileExists(filename_base))
13175 LoadMenuDesignSettingsFromFilename(filename_base);
13178 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13180 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13181 LoadMenuDesignSettingsFromFilename(filename_local);
13183 InitMenuDesignSettings_SpecialPostProcessing();
13186 void LoadMenuDesignSettings_AfterGraphics(void)
13188 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13191 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13192 boolean ignore_defaults)
13196 for (i = 0; sound_config_vars[i].token != NULL; i++)
13198 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13200 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13201 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13205 *sound_config_vars[i].value =
13206 get_token_parameter_value(sound_config_vars[i].token, value);
13210 void InitSoundSettings_Static(void)
13212 // always start with reliable default values from static default config
13213 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13216 static void LoadSoundSettingsFromFilename(char *filename)
13218 SetupFileHash *setup_file_hash;
13220 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13223 // read (and overwrite with) values that may be specified in config file
13224 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13226 freeSetupFileHash(setup_file_hash);
13229 void LoadSoundSettings(void)
13231 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13233 InitSoundSettings_Static();
13235 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13237 // first look for special settings configured in level series config
13238 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13240 if (fileExists(filename_base))
13241 LoadSoundSettingsFromFilename(filename_base);
13244 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13246 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13247 LoadSoundSettingsFromFilename(filename_local);
13250 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13252 char *filename = getEditorSetupFilename();
13253 SetupFileList *setup_file_list, *list;
13254 SetupFileHash *element_hash;
13255 int num_unknown_tokens = 0;
13258 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13261 element_hash = newSetupFileHash();
13263 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13264 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13266 // determined size may be larger than needed (due to unknown elements)
13268 for (list = setup_file_list; list != NULL; list = list->next)
13271 // add space for up to 3 more elements for padding that may be needed
13272 *num_elements += 3;
13274 // free memory for old list of elements, if needed
13275 checked_free(*elements);
13277 // allocate memory for new list of elements
13278 *elements = checked_malloc(*num_elements * sizeof(int));
13281 for (list = setup_file_list; list != NULL; list = list->next)
13283 char *value = getHashEntry(element_hash, list->token);
13285 if (value == NULL) // try to find obsolete token mapping
13287 char *mapped_token = get_mapped_token(list->token);
13289 if (mapped_token != NULL)
13291 value = getHashEntry(element_hash, mapped_token);
13293 free(mapped_token);
13299 (*elements)[(*num_elements)++] = atoi(value);
13303 if (num_unknown_tokens == 0)
13306 Warn("unknown token(s) found in config file:");
13307 Warn("- config file: '%s'", filename);
13309 num_unknown_tokens++;
13312 Warn("- token: '%s'", list->token);
13316 if (num_unknown_tokens > 0)
13319 while (*num_elements % 4) // pad with empty elements, if needed
13320 (*elements)[(*num_elements)++] = EL_EMPTY;
13322 freeSetupFileList(setup_file_list);
13323 freeSetupFileHash(element_hash);
13326 for (i = 0; i < *num_elements; i++)
13327 Debug("editor", "element '%s' [%d]\n",
13328 element_info[(*elements)[i]].token_name, (*elements)[i]);
13332 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13335 SetupFileHash *setup_file_hash = NULL;
13336 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13337 char *filename_music, *filename_prefix, *filename_info;
13343 token_to_value_ptr[] =
13345 { "title_header", &tmp_music_file_info.title_header },
13346 { "artist_header", &tmp_music_file_info.artist_header },
13347 { "album_header", &tmp_music_file_info.album_header },
13348 { "year_header", &tmp_music_file_info.year_header },
13349 { "played_header", &tmp_music_file_info.played_header },
13351 { "title", &tmp_music_file_info.title },
13352 { "artist", &tmp_music_file_info.artist },
13353 { "album", &tmp_music_file_info.album },
13354 { "year", &tmp_music_file_info.year },
13355 { "played", &tmp_music_file_info.played },
13361 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13362 getCustomMusicFilename(basename));
13364 if (filename_music == NULL)
13367 // ---------- try to replace file extension ----------
13369 filename_prefix = getStringCopy(filename_music);
13370 if (strrchr(filename_prefix, '.') != NULL)
13371 *strrchr(filename_prefix, '.') = '\0';
13372 filename_info = getStringCat2(filename_prefix, ".txt");
13374 if (fileExists(filename_info))
13375 setup_file_hash = loadSetupFileHash(filename_info);
13377 free(filename_prefix);
13378 free(filename_info);
13380 if (setup_file_hash == NULL)
13382 // ---------- try to add file extension ----------
13384 filename_prefix = getStringCopy(filename_music);
13385 filename_info = getStringCat2(filename_prefix, ".txt");
13387 if (fileExists(filename_info))
13388 setup_file_hash = loadSetupFileHash(filename_info);
13390 free(filename_prefix);
13391 free(filename_info);
13394 if (setup_file_hash == NULL)
13397 // ---------- music file info found ----------
13399 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13401 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13403 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13405 *token_to_value_ptr[i].value_ptr =
13406 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13409 tmp_music_file_info.basename = getStringCopy(basename);
13410 tmp_music_file_info.music = music;
13411 tmp_music_file_info.is_sound = is_sound;
13413 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13414 *new_music_file_info = tmp_music_file_info;
13416 return new_music_file_info;
13419 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13421 return get_music_file_info_ext(basename, music, FALSE);
13424 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13426 return get_music_file_info_ext(basename, sound, TRUE);
13429 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13430 char *basename, boolean is_sound)
13432 for (; list != NULL; list = list->next)
13433 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13439 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13441 return music_info_listed_ext(list, basename, FALSE);
13444 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13446 return music_info_listed_ext(list, basename, TRUE);
13449 void LoadMusicInfo(void)
13451 int num_music_noconf = getMusicListSize_NoConf();
13452 int num_music = getMusicListSize();
13453 int num_sounds = getSoundListSize();
13454 struct FileInfo *music, *sound;
13455 struct MusicFileInfo *next, **new;
13459 while (music_file_info != NULL)
13461 next = music_file_info->next;
13463 checked_free(music_file_info->basename);
13465 checked_free(music_file_info->title_header);
13466 checked_free(music_file_info->artist_header);
13467 checked_free(music_file_info->album_header);
13468 checked_free(music_file_info->year_header);
13469 checked_free(music_file_info->played_header);
13471 checked_free(music_file_info->title);
13472 checked_free(music_file_info->artist);
13473 checked_free(music_file_info->album);
13474 checked_free(music_file_info->year);
13475 checked_free(music_file_info->played);
13477 free(music_file_info);
13479 music_file_info = next;
13482 new = &music_file_info;
13484 // get (configured or unconfigured) music file info for all levels
13485 for (i = leveldir_current->first_level;
13486 i <= leveldir_current->last_level; i++)
13490 if (levelset.music[i] != MUS_UNDEFINED)
13492 // get music file info for configured level music
13493 music_nr = levelset.music[i];
13495 else if (num_music_noconf > 0)
13497 // get music file info for unconfigured level music
13498 int level_pos = i - leveldir_current->first_level;
13500 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13507 char *basename = getMusicInfoEntryFilename(music_nr);
13509 if (basename == NULL)
13512 if (!music_info_listed(music_file_info, basename))
13514 *new = get_music_file_info(basename, music_nr);
13517 new = &(*new)->next;
13521 // get music file info for all remaining configured music files
13522 for (i = 0; i < num_music; i++)
13524 music = getMusicListEntry(i);
13526 if (music->filename == NULL)
13529 if (strEqual(music->filename, UNDEFINED_FILENAME))
13532 // a configured file may be not recognized as music
13533 if (!FileIsMusic(music->filename))
13536 if (!music_info_listed(music_file_info, music->filename))
13538 *new = get_music_file_info(music->filename, i);
13541 new = &(*new)->next;
13545 // get sound file info for all configured sound files
13546 for (i = 0; i < num_sounds; i++)
13548 sound = getSoundListEntry(i);
13550 if (sound->filename == NULL)
13553 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13556 // a configured file may be not recognized as sound
13557 if (!FileIsSound(sound->filename))
13560 if (!sound_info_listed(music_file_info, sound->filename))
13562 *new = get_sound_file_info(sound->filename, i);
13564 new = &(*new)->next;
13568 // add pointers to previous list nodes
13570 struct MusicFileInfo *node = music_file_info;
13572 while (node != NULL)
13575 node->next->prev = node;
13581 static void add_helpanim_entry(int element, int action, int direction,
13582 int delay, int *num_list_entries)
13584 struct HelpAnimInfo *new_list_entry;
13585 (*num_list_entries)++;
13588 checked_realloc(helpanim_info,
13589 *num_list_entries * sizeof(struct HelpAnimInfo));
13590 new_list_entry = &helpanim_info[*num_list_entries - 1];
13592 new_list_entry->element = element;
13593 new_list_entry->action = action;
13594 new_list_entry->direction = direction;
13595 new_list_entry->delay = delay;
13598 static void print_unknown_token(char *filename, char *token, int token_nr)
13603 Warn("unknown token(s) found in config file:");
13604 Warn("- config file: '%s'", filename);
13607 Warn("- token: '%s'", token);
13610 static void print_unknown_token_end(int token_nr)
13616 void LoadHelpAnimInfo(void)
13618 char *filename = getHelpAnimFilename();
13619 SetupFileList *setup_file_list = NULL, *list;
13620 SetupFileHash *element_hash, *action_hash, *direction_hash;
13621 int num_list_entries = 0;
13622 int num_unknown_tokens = 0;
13625 if (fileExists(filename))
13626 setup_file_list = loadSetupFileList(filename);
13628 if (setup_file_list == NULL)
13630 // use reliable default values from static configuration
13631 SetupFileList *insert_ptr;
13633 insert_ptr = setup_file_list =
13634 newSetupFileList(helpanim_config[0].token,
13635 helpanim_config[0].value);
13637 for (i = 1; helpanim_config[i].token; i++)
13638 insert_ptr = addListEntry(insert_ptr,
13639 helpanim_config[i].token,
13640 helpanim_config[i].value);
13643 element_hash = newSetupFileHash();
13644 action_hash = newSetupFileHash();
13645 direction_hash = newSetupFileHash();
13647 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13648 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13650 for (i = 0; i < NUM_ACTIONS; i++)
13651 setHashEntry(action_hash, element_action_info[i].suffix,
13652 i_to_a(element_action_info[i].value));
13654 // do not store direction index (bit) here, but direction value!
13655 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13656 setHashEntry(direction_hash, element_direction_info[i].suffix,
13657 i_to_a(1 << element_direction_info[i].value));
13659 for (list = setup_file_list; list != NULL; list = list->next)
13661 char *element_token, *action_token, *direction_token;
13662 char *element_value, *action_value, *direction_value;
13663 int delay = atoi(list->value);
13665 if (strEqual(list->token, "end"))
13667 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13672 /* first try to break element into element/action/direction parts;
13673 if this does not work, also accept combined "element[.act][.dir]"
13674 elements (like "dynamite.active"), which are unique elements */
13676 if (strchr(list->token, '.') == NULL) // token contains no '.'
13678 element_value = getHashEntry(element_hash, list->token);
13679 if (element_value != NULL) // element found
13680 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13681 &num_list_entries);
13684 // no further suffixes found -- this is not an element
13685 print_unknown_token(filename, list->token, num_unknown_tokens++);
13691 // token has format "<prefix>.<something>"
13693 action_token = strchr(list->token, '.'); // suffix may be action ...
13694 direction_token = action_token; // ... or direction
13696 element_token = getStringCopy(list->token);
13697 *strchr(element_token, '.') = '\0';
13699 element_value = getHashEntry(element_hash, element_token);
13701 if (element_value == NULL) // this is no element
13703 element_value = getHashEntry(element_hash, list->token);
13704 if (element_value != NULL) // combined element found
13705 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13706 &num_list_entries);
13708 print_unknown_token(filename, list->token, num_unknown_tokens++);
13710 free(element_token);
13715 action_value = getHashEntry(action_hash, action_token);
13717 if (action_value != NULL) // action found
13719 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13720 &num_list_entries);
13722 free(element_token);
13727 direction_value = getHashEntry(direction_hash, direction_token);
13729 if (direction_value != NULL) // direction found
13731 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13732 &num_list_entries);
13734 free(element_token);
13739 if (strchr(action_token + 1, '.') == NULL)
13741 // no further suffixes found -- this is not an action nor direction
13743 element_value = getHashEntry(element_hash, list->token);
13744 if (element_value != NULL) // combined element found
13745 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13746 &num_list_entries);
13748 print_unknown_token(filename, list->token, num_unknown_tokens++);
13750 free(element_token);
13755 // token has format "<prefix>.<suffix>.<something>"
13757 direction_token = strchr(action_token + 1, '.');
13759 action_token = getStringCopy(action_token);
13760 *strchr(action_token + 1, '.') = '\0';
13762 action_value = getHashEntry(action_hash, action_token);
13764 if (action_value == NULL) // this is no action
13766 element_value = getHashEntry(element_hash, list->token);
13767 if (element_value != NULL) // combined element found
13768 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13769 &num_list_entries);
13771 print_unknown_token(filename, list->token, num_unknown_tokens++);
13773 free(element_token);
13774 free(action_token);
13779 direction_value = getHashEntry(direction_hash, direction_token);
13781 if (direction_value != NULL) // direction found
13783 add_helpanim_entry(atoi(element_value), atoi(action_value),
13784 atoi(direction_value), delay, &num_list_entries);
13786 free(element_token);
13787 free(action_token);
13792 // this is no direction
13794 element_value = getHashEntry(element_hash, list->token);
13795 if (element_value != NULL) // combined element found
13796 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13797 &num_list_entries);
13799 print_unknown_token(filename, list->token, num_unknown_tokens++);
13801 free(element_token);
13802 free(action_token);
13805 print_unknown_token_end(num_unknown_tokens);
13807 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13808 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13810 freeSetupFileList(setup_file_list);
13811 freeSetupFileHash(element_hash);
13812 freeSetupFileHash(action_hash);
13813 freeSetupFileHash(direction_hash);
13816 for (i = 0; i < num_list_entries; i++)
13817 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13818 EL_NAME(helpanim_info[i].element),
13819 helpanim_info[i].element,
13820 helpanim_info[i].action,
13821 helpanim_info[i].direction,
13822 helpanim_info[i].delay);
13826 void LoadHelpTextInfo(void)
13828 char *filename = getHelpTextFilename();
13831 if (helptext_info != NULL)
13833 freeSetupFileHash(helptext_info);
13834 helptext_info = NULL;
13837 if (fileExists(filename))
13838 helptext_info = loadSetupFileHash(filename);
13840 if (helptext_info == NULL)
13842 // use reliable default values from static configuration
13843 helptext_info = newSetupFileHash();
13845 for (i = 0; helptext_config[i].token; i++)
13846 setHashEntry(helptext_info,
13847 helptext_config[i].token,
13848 helptext_config[i].value);
13852 BEGIN_HASH_ITERATION(helptext_info, itr)
13854 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13855 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13857 END_HASH_ITERATION(hash, itr)
13862 // ----------------------------------------------------------------------------
13864 // ----------------------------------------------------------------------------
13866 #define MAX_NUM_CONVERT_LEVELS 1000
13868 void ConvertLevels(void)
13870 static LevelDirTree *convert_leveldir = NULL;
13871 static int convert_level_nr = -1;
13872 static int num_levels_handled = 0;
13873 static int num_levels_converted = 0;
13874 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13877 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13878 global.convert_leveldir);
13880 if (convert_leveldir == NULL)
13881 Fail("no such level identifier: '%s'", global.convert_leveldir);
13883 leveldir_current = convert_leveldir;
13885 if (global.convert_level_nr != -1)
13887 convert_leveldir->first_level = global.convert_level_nr;
13888 convert_leveldir->last_level = global.convert_level_nr;
13891 convert_level_nr = convert_leveldir->first_level;
13893 PrintLine("=", 79);
13894 Print("Converting levels\n");
13895 PrintLine("-", 79);
13896 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13897 Print("Level series name: '%s'\n", convert_leveldir->name);
13898 Print("Level series author: '%s'\n", convert_leveldir->author);
13899 Print("Number of levels: %d\n", convert_leveldir->levels);
13900 PrintLine("=", 79);
13903 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13904 levels_failed[i] = FALSE;
13906 while (convert_level_nr <= convert_leveldir->last_level)
13908 char *level_filename;
13911 level_nr = convert_level_nr++;
13913 Print("Level %03d: ", level_nr);
13915 LoadLevel(level_nr);
13916 if (level.no_level_file || level.no_valid_file)
13918 Print("(no level)\n");
13922 Print("converting level ... ");
13925 // special case: conversion of some EMC levels as requested by ACME
13926 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13929 level_filename = getDefaultLevelFilename(level_nr);
13930 new_level = !fileExists(level_filename);
13934 SaveLevel(level_nr);
13936 num_levels_converted++;
13938 Print("converted.\n");
13942 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13943 levels_failed[level_nr] = TRUE;
13945 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13948 num_levels_handled++;
13952 PrintLine("=", 79);
13953 Print("Number of levels handled: %d\n", num_levels_handled);
13954 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13955 (num_levels_handled ?
13956 num_levels_converted * 100 / num_levels_handled : 0));
13957 PrintLine("-", 79);
13958 Print("Summary (for automatic parsing by scripts):\n");
13959 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13960 convert_leveldir->identifier, num_levels_converted,
13961 num_levels_handled,
13962 (num_levels_handled ?
13963 num_levels_converted * 100 / num_levels_handled : 0));
13965 if (num_levels_handled != num_levels_converted)
13967 Print(", FAILED:");
13968 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13969 if (levels_failed[i])
13974 PrintLine("=", 79);
13976 CloseAllAndExit(0);
13980 // ----------------------------------------------------------------------------
13981 // create and save images for use in level sketches (raw BMP format)
13982 // ----------------------------------------------------------------------------
13984 void CreateLevelSketchImages(void)
13990 InitElementPropertiesGfxElement();
13992 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13993 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13995 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13997 int element = getMappedElement(i);
13998 char basename1[16];
13999 char basename2[16];
14003 sprintf(basename1, "%04d.bmp", i);
14004 sprintf(basename2, "%04ds.bmp", i);
14006 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14007 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14009 DrawSizedElement(0, 0, element, TILESIZE);
14010 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14012 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14013 Fail("cannot save level sketch image file '%s'", filename1);
14015 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14016 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14018 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14019 Fail("cannot save level sketch image file '%s'", filename2);
14024 // create corresponding SQL statements (for normal and small images)
14027 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14028 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14031 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14032 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14034 // optional: create content for forum level sketch demonstration post
14036 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14039 FreeBitmap(bitmap1);
14040 FreeBitmap(bitmap2);
14043 fprintf(stderr, "\n");
14045 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14047 CloseAllAndExit(0);
14051 // ----------------------------------------------------------------------------
14052 // create and save images for element collecting animations (raw BMP format)
14053 // ----------------------------------------------------------------------------
14055 static boolean createCollectImage(int element)
14057 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14060 void CreateCollectElementImages(void)
14064 int anim_frames = num_steps - 1;
14065 int tile_size = TILESIZE;
14066 int anim_width = tile_size * anim_frames;
14067 int anim_height = tile_size;
14068 int num_collect_images = 0;
14069 int pos_collect_images = 0;
14071 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14072 if (createCollectImage(i))
14073 num_collect_images++;
14075 Info("Creating %d element collecting animation images ...",
14076 num_collect_images);
14078 int dst_width = anim_width * 2;
14079 int dst_height = anim_height * num_collect_images / 2;
14080 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14081 char *basename_bmp = "RocksCollect.bmp";
14082 char *basename_png = "RocksCollect.png";
14083 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14084 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14085 int len_filename_bmp = strlen(filename_bmp);
14086 int len_filename_png = strlen(filename_png);
14087 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14088 char cmd_convert[max_command_len];
14090 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14094 // force using RGBA surface for destination bitmap
14095 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14096 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14098 dst_bitmap->surface =
14099 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14101 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14103 if (!createCollectImage(i))
14106 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14107 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14108 int graphic = el2img(i);
14109 char *token_name = element_info[i].token_name;
14110 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14111 Bitmap *src_bitmap;
14114 Info("- creating collecting image for '%s' ...", token_name);
14116 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14118 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14119 tile_size, tile_size, 0, 0);
14121 // force using RGBA surface for temporary bitmap (using transparent black)
14122 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14123 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14125 tmp_bitmap->surface =
14126 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14128 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14130 for (j = 0; j < anim_frames; j++)
14132 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14133 int frame_size = frame_size_final * num_steps;
14134 int offset = (tile_size - frame_size_final) / 2;
14135 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14137 while (frame_size > frame_size_final)
14141 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14143 FreeBitmap(frame_bitmap);
14145 frame_bitmap = half_bitmap;
14148 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14149 frame_size_final, frame_size_final,
14150 dst_x + j * tile_size + offset, dst_y + offset);
14152 FreeBitmap(frame_bitmap);
14155 tmp_bitmap->surface_masked = NULL;
14157 FreeBitmap(tmp_bitmap);
14159 pos_collect_images++;
14162 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14163 Fail("cannot save element collecting image file '%s'", filename_bmp);
14165 FreeBitmap(dst_bitmap);
14167 Info("Converting image file from BMP to PNG ...");
14169 if (system(cmd_convert) != 0)
14170 Fail("converting image file failed");
14172 unlink(filename_bmp);
14176 CloseAllAndExit(0);
14180 // ----------------------------------------------------------------------------
14181 // create and save images for custom and group elements (raw BMP format)
14182 // ----------------------------------------------------------------------------
14184 void CreateCustomElementImages(char *directory)
14186 char *src_basename = "RocksCE-template.ilbm";
14187 char *dst_basename = "RocksCE.bmp";
14188 char *src_filename = getPath2(directory, src_basename);
14189 char *dst_filename = getPath2(directory, dst_basename);
14190 Bitmap *src_bitmap;
14192 int yoffset_ce = 0;
14193 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14196 InitVideoDefaults();
14198 ReCreateBitmap(&backbuffer, video.width, video.height);
14200 src_bitmap = LoadImage(src_filename);
14202 bitmap = CreateBitmap(TILEX * 16 * 2,
14203 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14206 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14213 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14214 TILEX * x, TILEY * y + yoffset_ce);
14216 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14218 TILEX * x + TILEX * 16,
14219 TILEY * y + yoffset_ce);
14221 for (j = 2; j >= 0; j--)
14225 BlitBitmap(src_bitmap, bitmap,
14226 TILEX + c * 7, 0, 6, 10,
14227 TILEX * x + 6 + j * 7,
14228 TILEY * y + 11 + yoffset_ce);
14230 BlitBitmap(src_bitmap, bitmap,
14231 TILEX + c * 8, TILEY, 6, 10,
14232 TILEX * 16 + TILEX * x + 6 + j * 8,
14233 TILEY * y + 10 + yoffset_ce);
14239 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14246 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14247 TILEX * x, TILEY * y + yoffset_ge);
14249 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14251 TILEX * x + TILEX * 16,
14252 TILEY * y + yoffset_ge);
14254 for (j = 1; j >= 0; j--)
14258 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14259 TILEX * x + 6 + j * 10,
14260 TILEY * y + 11 + yoffset_ge);
14262 BlitBitmap(src_bitmap, bitmap,
14263 TILEX + c * 8, TILEY + 12, 6, 10,
14264 TILEX * 16 + TILEX * x + 10 + j * 8,
14265 TILEY * y + 10 + yoffset_ge);
14271 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14272 Fail("cannot save CE graphics file '%s'", dst_filename);
14274 FreeBitmap(bitmap);
14276 CloseAllAndExit(0);