+/* (element number, number of change pages, change page number) */
+#define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
+
+/* (element number only) */
+#define LEVEL_CHUNK_GRPX_UNCHANGED 2
+#define LEVEL_CHUNK_NOTE_UNCHANGED 2
+
+/* (nothing at all if unchanged) */
+#define LEVEL_CHUNK_ELEM_UNCHANGED 0
+
+#define TAPE_CHUNK_VERS_SIZE 8 /* size of file version chunk */
+#define TAPE_CHUNK_HEAD_SIZE 20 /* size of tape file header */
+#define TAPE_CHUNK_HEAD_UNUSED 3 /* unused tape header bytes */
+
+#define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
+#define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
+#define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
+
+/* file identifier strings */
+#define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
+#define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
+#define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
+
+/* values for deciding when (not) to save configuration data */
+#define SAVE_CONF_NEVER 0
+#define SAVE_CONF_ALWAYS 1
+#define SAVE_CONF_WHEN_CHANGED -1
+
+/* values for chunks using micro chunks */
+#define CONF_MASK_1_BYTE 0x00
+#define CONF_MASK_2_BYTE 0x40
+#define CONF_MASK_4_BYTE 0x80
+#define CONF_MASK_MULTI_BYTES 0xc0
+
+#define CONF_MASK_BYTES 0xc0
+#define CONF_MASK_TOKEN 0x3f
+
+#define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
+#define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
+#define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
+#define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
+
+/* these definitions are just for convenience of use and readability */
+#define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
+#define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
+#define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
+#define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
+
+#define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
+ (x) == CONF_MASK_2_BYTE ? 2 : \
+ (x) == CONF_MASK_4_BYTE ? 4 : 0)
+
+#define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
+#define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
+#define CONF_ELEMENT_NUM_BYTES (2)
+
+#define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
+ (t) == TYPE_ELEMENT_LIST ? \
+ CONF_ELEMENT_NUM_BYTES : \
+ (t) == TYPE_CONTENT || \
+ (t) == TYPE_CONTENT_LIST ? \
+ CONF_CONTENT_NUM_BYTES : 1)
+
+#define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
+#define CONF_ELEMENTS_ELEMENT(b,i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
+ (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
+
+#define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
+ (y) * 3 + (x))
+#define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
+ CONF_ELEMENT_NUM_BYTES)
+#define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
+ (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
+
+/* temporary variables used to store pointers to structure members */
+static struct LevelInfo li;
+static struct ElementInfo xx_ei, yy_ei;
+static struct ElementChangeInfo xx_change;
+static struct ElementGroupInfo xx_group;
+static struct EnvelopeInfo xx_envelope;
+static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
+static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
+static int xx_num_contents;
+static int xx_current_change_page;
+static char xx_default_string_empty[1] = "";
+static int xx_string_length_unused;
+
+struct LevelFileConfigInfo
+{
+ int element; /* element for which data is to be stored */
+ int save_type; /* save data always, never or when changed */
+ int data_type; /* data type (used internally, not stored) */
+ int conf_type; /* micro chunk identifier (stored in file) */
+
+ /* (mandatory) */
+ void *value; /* variable that holds the data to be stored */
+ int default_value; /* initial default value for this variable */
+
+ /* (optional) */
+ void *value_copy; /* variable that holds the data to be copied */
+ void *num_entities; /* number of entities for multi-byte data */
+ int default_num_entities; /* default number of entities for this data */
+ int max_num_entities; /* maximal number of entities for this data */
+ char *default_string; /* optional default string for string data */
+};
+
+static struct LevelFileConfigInfo chunk_config_INFO[] =
+{
+ /* ---------- values not related to single elements ----------------------- */
+
+ {
+ -1, SAVE_CONF_ALWAYS,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(1),
+ &li.game_engine_type, GAME_ENGINE_TYPE_RND
+ },
+
+ {
+ -1, SAVE_CONF_ALWAYS,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.fieldx, STD_LEV_FIELDX
+ },
+ {
+ -1, SAVE_CONF_ALWAYS,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(2),
+ &li.fieldy, STD_LEV_FIELDY
+ },
+
+ {
+ -1, SAVE_CONF_ALWAYS,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(3),
+ &li.time, 100
+ },
+
+ {
+ -1, SAVE_CONF_ALWAYS,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(4),
+ &li.gems_needed, 0
+ },
+
+ {
+ -1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
+ &li.use_step_counter, FALSE
+ },
+
+ {
+ -1, -1,
+ TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
+ &li.wind_direction_initial, MV_NONE
+ },
+
+ {
+ -1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
+ &li.em_slippery_gems, FALSE
+ },
+
+ {
+ -1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
+ &li.use_custom_template, FALSE
+ },
+
+ {
+ -1, -1,
+ TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
+ &li.can_move_into_acid_bits, ~0 /* default: everything can */
+ },
+
+ {
+ -1, -1,
+ TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
+ &li.dont_collide_with_bits, ~0 /* default: always deadly */
+ },
+
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(5),
+ &li.score[SC_TIME_BONUS], 1
+ },
+
+ {
+ -1, -1,
+ -1, -1,
+ NULL, -1
+ }
+};
+
+static struct LevelFileConfigInfo chunk_config_ELEM[] =
+{
+ /* (these values are the same for each player) */
+ {
+ EL_PLAYER_1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
+ &li.block_last_field, FALSE /* default case for EM levels */
+ },
+ {
+ EL_PLAYER_1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
+ &li.sp_block_last_field, TRUE /* default case for SP levels */
+ },
+ {
+ EL_PLAYER_1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
+ &li.instant_relocation, FALSE
+ },
+ {
+ EL_PLAYER_1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
+ &li.can_pass_to_walkable, FALSE
+ },
+ {
+ EL_PLAYER_1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
+ &li.block_snap_field, TRUE
+ },
+ {
+ EL_PLAYER_1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
+ &li.continuous_snapping, TRUE
+ },
+
+ /* (these values are different for each player) */
+ {
+ EL_PLAYER_1, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(7),
+ &li.initial_player_stepsize[0], STEPSIZE_NORMAL
+ },
+ {
+ EL_PLAYER_1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
+ &li.initial_player_gravity[0], FALSE
+ },
+ {
+ EL_PLAYER_1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
+ &li.use_start_element[0], FALSE
+ },
+ {
+ EL_PLAYER_1, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
+ &li.start_element[0], EL_PLAYER_1
+ },
+ {
+ EL_PLAYER_1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
+ &li.use_artwork_element[0], FALSE
+ },
+ {
+ EL_PLAYER_1, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
+ &li.artwork_element[0], EL_PLAYER_1
+ },
+ {
+ EL_PLAYER_1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
+ &li.use_explosion_element[0], FALSE
+ },
+ {
+ EL_PLAYER_1, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
+ &li.explosion_element[0], EL_PLAYER_1
+ },
+
+ {
+ EL_PLAYER_2, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(7),
+ &li.initial_player_stepsize[1], STEPSIZE_NORMAL
+ },
+ {
+ EL_PLAYER_2, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
+ &li.initial_player_gravity[1], FALSE
+ },
+ {
+ EL_PLAYER_2, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
+ &li.use_start_element[1], FALSE
+ },
+ {
+ EL_PLAYER_2, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
+ &li.start_element[1], EL_PLAYER_2
+ },
+ {
+ EL_PLAYER_2, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
+ &li.use_artwork_element[1], FALSE
+ },
+ {
+ EL_PLAYER_2, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
+ &li.artwork_element[1], EL_PLAYER_2
+ },
+ {
+ EL_PLAYER_2, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
+ &li.use_explosion_element[1], FALSE
+ },
+ {
+ EL_PLAYER_2, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
+ &li.explosion_element[1], EL_PLAYER_2
+ },
+
+ {
+ EL_PLAYER_3, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(7),
+ &li.initial_player_stepsize[2], STEPSIZE_NORMAL
+ },
+ {
+ EL_PLAYER_3, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
+ &li.initial_player_gravity[2], FALSE
+ },
+ {
+ EL_PLAYER_3, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
+ &li.use_start_element[2], FALSE
+ },
+ {
+ EL_PLAYER_3, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
+ &li.start_element[2], EL_PLAYER_3
+ },
+ {
+ EL_PLAYER_3, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
+ &li.use_artwork_element[2], FALSE
+ },
+ {
+ EL_PLAYER_3, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
+ &li.artwork_element[2], EL_PLAYER_3
+ },
+ {
+ EL_PLAYER_3, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
+ &li.use_explosion_element[2], FALSE
+ },
+ {
+ EL_PLAYER_3, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
+ &li.explosion_element[2], EL_PLAYER_3
+ },
+
+ {
+ EL_PLAYER_4, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(7),
+ &li.initial_player_stepsize[3], STEPSIZE_NORMAL
+ },
+ {
+ EL_PLAYER_4, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
+ &li.initial_player_gravity[3], FALSE
+ },
+ {
+ EL_PLAYER_4, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
+ &li.use_start_element[3], FALSE
+ },
+ {
+ EL_PLAYER_4, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
+ &li.start_element[3], EL_PLAYER_4
+ },
+ {
+ EL_PLAYER_4, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
+ &li.use_artwork_element[3], FALSE
+ },
+ {
+ EL_PLAYER_4, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
+ &li.artwork_element[3], EL_PLAYER_4
+ },
+ {
+ EL_PLAYER_4, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
+ &li.use_explosion_element[3], FALSE
+ },
+ {
+ EL_PLAYER_4, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
+ &li.explosion_element[3], EL_PLAYER_4
+ },
+
+ {
+ EL_EMERALD, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.score[SC_EMERALD], 10
+ },
+
+ {
+ EL_DIAMOND, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.score[SC_DIAMOND], 10
+ },
+
+ {
+ EL_BUG, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.score[SC_BUG], 10
+ },
+
+ {
+ EL_SPACESHIP, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.score[SC_SPACESHIP], 10
+ },
+
+ {
+ EL_PACMAN, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.score[SC_PACMAN], 10
+ },
+
+ {
+ EL_NUT, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.score[SC_NUT], 10
+ },
+
+ {
+ EL_DYNAMITE, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.score[SC_DYNAMITE], 10
+ },
+
+ {
+ EL_KEY_1, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.score[SC_KEY], 10
+ },
+
+ {
+ EL_PEARL, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.score[SC_PEARL], 10
+ },
+
+ {
+ EL_CRYSTAL, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.score[SC_CRYSTAL], 10
+ },
+
+ {
+ EL_BD_AMOEBA, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
+ &li.amoeba_content, EL_DIAMOND
+ },
+ {
+ EL_BD_AMOEBA, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(2),
+ &li.amoeba_speed, 10
+ },
+ {
+ EL_BD_AMOEBA, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
+ &li.grow_into_diggable, TRUE
+ },
+
+ {
+ EL_YAMYAM, -1,
+ TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
+ &li.yamyam_content, EL_ROCK, NULL,
+ &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
+ },
+ {
+ EL_YAMYAM, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.score[SC_YAMYAM], 10
+ },
+
+ {
+ EL_ROBOT, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.score[SC_ROBOT], 10
+ },
+ {
+ EL_ROBOT, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(2),
+ &li.slurp_score, 10
+ },
+
+ {
+ EL_ROBOT_WHEEL, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.time_wheel, 10
+ },
+
+ {
+ EL_MAGIC_WALL, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.time_magic_wall, 10
+ },
+
+ {
+ EL_GAME_OF_LIFE, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(1),
+ &li.game_of_life[0], 2
+ },
+ {
+ EL_GAME_OF_LIFE, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(2),
+ &li.game_of_life[1], 3
+ },
+ {
+ EL_GAME_OF_LIFE, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(3),
+ &li.game_of_life[2], 3
+ },
+ {
+ EL_GAME_OF_LIFE, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(4),
+ &li.game_of_life[3], 3
+ },
+
+ {
+ EL_BIOMAZE, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(1),
+ &li.biomaze[0], 2
+ },
+ {
+ EL_BIOMAZE, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(2),
+ &li.biomaze[1], 3
+ },
+ {
+ EL_BIOMAZE, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(3),
+ &li.biomaze[2], 3
+ },
+ {
+ EL_BIOMAZE, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(4),
+ &li.biomaze[3], 3
+ },
+
+ {
+ EL_TIMEGATE_SWITCH, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.time_timegate, 10
+ },
+
+ {
+ EL_LIGHT_SWITCH_ACTIVE, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.time_light, 10
+ },
+
+ {
+ EL_SHIELD_NORMAL, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.shield_normal_time, 10
+ },
+ {
+ EL_SHIELD_NORMAL, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(2),
+ &li.score[SC_SHIELD], 10
+ },
+
+ {
+ EL_SHIELD_DEADLY, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.shield_deadly_time, 10
+ },
+ {
+ EL_SHIELD_DEADLY, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(2),
+ &li.score[SC_SHIELD], 10
+ },
+
+ {
+ EL_EXTRA_TIME, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.extra_time, 10
+ },
+ {
+ EL_EXTRA_TIME, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(2),
+ &li.extra_time_score, 10
+ },
+
+ {
+ EL_TIME_ORB_FULL, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.time_orb_time, 10
+ },
+ {
+ EL_TIME_ORB_FULL, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
+ &li.use_time_orb_bug, FALSE
+ },
+
+ {
+ EL_SPRING, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
+ &li.use_spring_bug, FALSE
+ },
+
+ {
+ EL_EMC_ANDROID, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.android_move_time, 10
+ },
+ {
+ EL_EMC_ANDROID, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(2),
+ &li.android_clone_time, 10
+ },
+ {
+ EL_EMC_ANDROID, -1,
+ TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
+ &li.android_clone_element[0], EL_EMPTY, NULL,
+ &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
+ },
+
+ {
+ EL_EMC_LENSES, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.lenses_score, 10
+ },
+ {
+ EL_EMC_LENSES, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(2),
+ &li.lenses_time, 10
+ },
+
+ {
+ EL_EMC_MAGNIFIER, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.magnify_score, 10
+ },
+ {
+ EL_EMC_MAGNIFIER, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(2),
+ &li.magnify_time, 10
+ },
+
+ {
+ EL_EMC_MAGIC_BALL, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.ball_time, 10
+ },
+ {
+ EL_EMC_MAGIC_BALL, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
+ &li.ball_random, FALSE
+ },
+ {
+ EL_EMC_MAGIC_BALL, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
+ &li.ball_state_initial, FALSE
+ },
+ {
+ EL_EMC_MAGIC_BALL, -1,
+ TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
+ &li.ball_content, EL_EMPTY, NULL,
+ &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
+ },
+
+ /* ---------- unused values ----------------------------------------------- */
+
+ {
+ EL_UNKNOWN, SAVE_CONF_NEVER,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.score[SC_UNKNOWN_14], 10
+ },
+ {
+ EL_UNKNOWN, SAVE_CONF_NEVER,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(2),
+ &li.score[SC_UNKNOWN_15], 10
+ },
+
+ {
+ -1, -1,
+ -1, -1,
+ NULL, -1
+ }
+};
+
+static struct LevelFileConfigInfo chunk_config_NOTE[] =
+{
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(1),
+ &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
+ },
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(2),
+ &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
+ },
+
+ {
+ -1, -1,
+ TYPE_STRING, CONF_VALUE_BYTES(1),
+ &xx_envelope.text, -1, NULL,
+ &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
+ &xx_default_string_empty[0]
+ },
+
+ {
+ -1, -1,
+ -1, -1,
+ NULL, -1
+ }
+};
+
+static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
+{
+ {
+ -1, -1,
+ TYPE_STRING, CONF_VALUE_BYTES(1),
+ &xx_ei.description[0], -1,
+ &yy_ei.description[0],
+ &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
+ &xx_default_description[0]
+ },
+
+ {
+ -1, -1,
+ TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
+ &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
+ &yy_ei.properties[EP_BITFIELD_BASE_NR]
+ },
+#if 0
+ /* (reserved) */
+ {
+ -1, -1,
+ TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
+ &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
+ &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
+ },
+#endif
+
+ {
+ -1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
+ &xx_ei.use_gfx_element, FALSE,
+ &yy_ei.use_gfx_element
+ },
+ {
+ -1, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
+ &xx_ei.gfx_element, EL_EMPTY_SPACE,
+ &yy_ei.gfx_element
+ },
+
+ {
+ -1, -1,
+ TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
+ &xx_ei.access_direction, MV_ALL_DIRECTIONS,
+ &yy_ei.access_direction
+ },
+
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(2),
+ &xx_ei.collect_score_initial, 10,
+ &yy_ei.collect_score_initial
+ },
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(3),
+ &xx_ei.collect_count_initial, 1,
+ &yy_ei.collect_count_initial
+ },
+
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(4),
+ &xx_ei.ce_value_fixed_initial, 0,
+ &yy_ei.ce_value_fixed_initial
+ },
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(5),
+ &xx_ei.ce_value_random_initial, 0,
+ &yy_ei.ce_value_random_initial
+ },
+ {
+ -1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
+ &xx_ei.use_last_ce_value, FALSE,
+ &yy_ei.use_last_ce_value
+ },
+
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(6),
+ &xx_ei.push_delay_fixed, 8,
+ &yy_ei.push_delay_fixed
+ },
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(7),
+ &xx_ei.push_delay_random, 8,
+ &yy_ei.push_delay_random
+ },
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(8),
+ &xx_ei.drop_delay_fixed, 0,
+ &yy_ei.drop_delay_fixed
+ },
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(9),
+ &xx_ei.drop_delay_random, 0,
+ &yy_ei.drop_delay_random
+ },
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(10),
+ &xx_ei.move_delay_fixed, 0,
+ &yy_ei.move_delay_fixed
+ },
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(11),
+ &xx_ei.move_delay_random, 0,
+ &yy_ei.move_delay_random
+ },
+
+ {
+ -1, -1,
+ TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
+ &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
+ &yy_ei.move_pattern
+ },
+ {
+ -1, -1,
+ TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
+ &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
+ &yy_ei.move_direction_initial
+ },
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(5),
+ &xx_ei.move_stepsize, TILEX / 8,
+ &yy_ei.move_stepsize
+ },
+
+ {
+ -1, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
+ &xx_ei.move_enter_element, EL_EMPTY_SPACE,
+ &yy_ei.move_enter_element
+ },
+ {
+ -1, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
+ &xx_ei.move_leave_element, EL_EMPTY_SPACE,
+ &yy_ei.move_leave_element
+ },
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(6),
+ &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
+ &yy_ei.move_leave_type
+ },
+
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(7),
+ &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
+ &yy_ei.slippery_type
+ },
+
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(8),
+ &xx_ei.explosion_type, EXPLODES_3X3,
+ &yy_ei.explosion_type
+ },
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(14),
+ &xx_ei.explosion_delay, 16,
+ &yy_ei.explosion_delay
+ },
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(15),
+ &xx_ei.ignition_delay, 8,
+ &yy_ei.ignition_delay
+ },
+
+ {
+ -1, -1,
+ TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
+ &xx_ei.content, EL_EMPTY_SPACE,
+ &yy_ei.content,
+ &xx_num_contents, 1, 1
+ },
+
+ /* ---------- "num_change_pages" must be the last entry ------------------- */
+
+ {
+ -1, SAVE_CONF_ALWAYS,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(9),
+ &xx_ei.num_change_pages, 1,
+ &yy_ei.num_change_pages
+ },
+
+ {
+ -1, -1,
+ -1, -1,
+ NULL, -1,
+ NULL
+ }
+};
+
+static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
+{
+ /* ---------- "current_change_page" must be the first entry --------------- */
+
+ {
+ -1, SAVE_CONF_ALWAYS,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(1),
+ &xx_current_change_page, -1
+ },
+
+ /* ---------- (the remaining entries can be in any order) ----------------- */
+
+ {
+ -1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
+ &xx_change.can_change, FALSE
+ },
+
+ {
+ -1, -1,
+ TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
+ &xx_event_bits[0], 0
+ },
+ {
+ -1, -1,
+ TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
+ &xx_event_bits[1], 0
+ },
+
+ {
+ -1, -1,
+ TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
+ &xx_change.trigger_player, CH_PLAYER_ANY
+ },
+ {
+ -1, -1,
+ TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
+ &xx_change.trigger_side, CH_SIDE_ANY
+ },
+ {
+ -1, -1,
+ TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
+ &xx_change.trigger_page, CH_PAGE_ANY
+ },
+
+ {
+ -1, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
+ &xx_change.target_element, EL_EMPTY_SPACE
+ },
+
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(2),
+ &xx_change.delay_fixed, 0
+ },
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(3),
+ &xx_change.delay_random, 0
+ },
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(4),
+ &xx_change.delay_frames, FRAMES_PER_SECOND
+ },
+
+ {
+ -1, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
+ &xx_change.trigger_element, EL_EMPTY_SPACE
+ },
+
+ {
+ -1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
+ &xx_change.explode, FALSE
+ },
+ {
+ -1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
+ &xx_change.use_target_content, FALSE
+ },
+ {
+ -1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
+ &xx_change.only_if_complete, FALSE
+ },
+ {
+ -1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
+ &xx_change.use_random_replace, FALSE
+ },
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(10),
+ &xx_change.random_percentage, 100
+ },
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(11),
+ &xx_change.replace_when, CP_WHEN_EMPTY
+ },
+
+ {
+ -1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
+ &xx_change.has_action, FALSE
+ },
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(13),
+ &xx_change.action_type, CA_NO_ACTION
+ },
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(14),
+ &xx_change.action_mode, CA_MODE_UNDEFINED
+ },
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(6),
+ &xx_change.action_arg, CA_ARG_UNDEFINED
+ },
+
+ {
+ -1, -1,
+ TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
+ &xx_change.target_content, EL_EMPTY_SPACE, NULL,
+ &xx_num_contents, 1, 1
+ },
+
+ {
+ -1, -1,
+ -1, -1,
+ NULL, -1
+ }
+};
+
+static struct LevelFileConfigInfo chunk_config_GRPX[] =
+{
+ {
+ -1, -1,
+ TYPE_STRING, CONF_VALUE_BYTES(1),
+ &xx_ei.description[0], -1, NULL,
+ &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
+ &xx_default_description[0]
+ },
+
+ {
+ -1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
+ &xx_ei.use_gfx_element, FALSE
+ },
+ {
+ -1, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
+ &xx_ei.gfx_element, EL_EMPTY_SPACE
+ },
+
+ {
+ -1, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(2),
+ &xx_group.choice_mode, ANIM_RANDOM
+ },
+
+ {
+ -1, -1,
+ TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
+ &xx_group.element[0], EL_EMPTY_SPACE, NULL,
+ &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
+ },
+
+ {
+ -1, -1,
+ -1, -1,
+ NULL, -1
+ }
+};
+
+static struct LevelFileConfigInfo chunk_config_CONF[] = /* (OBSOLETE) */
+{
+ {
+ EL_PLAYER_1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
+ &li.block_snap_field, TRUE
+ },
+ {
+ EL_PLAYER_1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
+ &li.continuous_snapping, TRUE
+ },
+ {
+ EL_PLAYER_1, -1,
+ TYPE_INTEGER, CONF_VALUE_8_BIT(1),
+ &li.initial_player_stepsize[0], STEPSIZE_NORMAL
+ },
+ {
+ EL_PLAYER_1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
+ &li.use_start_element[0], FALSE
+ },
+ {
+ EL_PLAYER_1, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
+ &li.start_element[0], EL_PLAYER_1
+ },
+ {
+ EL_PLAYER_1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
+ &li.use_artwork_element[0], FALSE
+ },
+ {
+ EL_PLAYER_1, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
+ &li.artwork_element[0], EL_PLAYER_1
+ },
+ {
+ EL_PLAYER_1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
+ &li.use_explosion_element[0], FALSE
+ },
+ {
+ EL_PLAYER_1, -1,
+ TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
+ &li.explosion_element[0], EL_PLAYER_1
+ },
+
+ {
+ -1, -1,
+ -1, -1,
+ NULL, -1
+ }
+};
+
+static struct
+{
+ int filetype;
+ char *id;
+}
+filetype_id_list[] =
+{
+ { LEVEL_FILE_TYPE_RND, "RND" },
+ { LEVEL_FILE_TYPE_BD, "BD" },
+ { LEVEL_FILE_TYPE_EM, "EM" },
+ { LEVEL_FILE_TYPE_SP, "SP" },
+ { LEVEL_FILE_TYPE_DX, "DX" },
+ { LEVEL_FILE_TYPE_SB, "SB" },
+ { LEVEL_FILE_TYPE_DC, "DC" },
+ { -1, NULL },
+};
+
+
+/* ========================================================================= */
+/* level file functions */
+/* ========================================================================= */
+
+static struct DateInfo getCurrentDate()
+{
+ time_t epoch_seconds = time(NULL);
+ struct tm *now = localtime(&epoch_seconds);
+ struct DateInfo date;
+
+ date.year = now->tm_year + 1900;
+ date.month = now->tm_mon + 1;
+ date.day = now->tm_mday;
+
+ return date;
+}
+
+static void resetEventFlags(struct ElementChangeInfo *change)
+{
+ int i;
+
+ for (i = 0; i < NUM_CHANGE_EVENTS; i++)
+ change->has_event[i] = FALSE;
+}
+
+static void resetEventBits()
+{
+ int i;
+
+ for (i = 0; i < NUM_CE_BITFIELDS; i++)
+ xx_event_bits[i] = 0;
+}
+
+static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
+{
+ int i;
+
+ /* important: only change event flag if corresponding event bit is set */
+ for (i = 0; i < NUM_CHANGE_EVENTS; i++)
+ if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
+ change->has_event[i] = TRUE;
+}
+
+static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
+{
+ int i;
+
+ /* important: only change event bit if corresponding event flag is set */
+ for (i = 0; i < NUM_CHANGE_EVENTS; i++)
+ if (change->has_event[i])
+ xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
+}
+
+static char *getDefaultElementDescription(struct ElementInfo *ei)
+{
+ static char description[MAX_ELEMENT_NAME_LEN + 1];
+ char *default_description = (ei->custom_description != NULL ?
+ ei->custom_description :
+ ei->editor_description);
+ int i;
+
+ /* always start with reliable default values */
+ for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
+ description[i] = '\0';
+
+ /* truncate element description to MAX_ELEMENT_NAME_LEN bytes */
+ strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
+
+ return &description[0];
+}
+
+static void setElementDescriptionToDefault(struct ElementInfo *ei)
+{
+ char *default_description = getDefaultElementDescription(ei);
+ int i;
+
+ for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
+ ei->description[i] = default_description[i];
+}
+
+static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
+{
+ int i;
+
+ for (i = 0; conf[i].data_type != -1; i++)
+ {
+ int default_value = conf[i].default_value;
+ int data_type = conf[i].data_type;
+ int conf_type = conf[i].conf_type;
+ int byte_mask = conf_type & CONF_MASK_BYTES;
+
+ if (byte_mask == CONF_MASK_MULTI_BYTES)
+ {
+ int default_num_entities = conf[i].default_num_entities;
+ int max_num_entities = conf[i].max_num_entities;
+
+ *(int *)(conf[i].num_entities) = default_num_entities;
+
+ if (data_type == TYPE_STRING)
+ {
+ char *default_string = conf[i].default_string;
+ char *string = (char *)(conf[i].value);
+
+ strncpy(string, default_string, max_num_entities);
+ }
+ else if (data_type == TYPE_ELEMENT_LIST)
+ {
+ int *element_array = (int *)(conf[i].value);
+ int j;
+
+ for (j = 0; j < max_num_entities; j++)
+ element_array[j] = default_value;
+ }
+ else if (data_type == TYPE_CONTENT_LIST)
+ {
+ struct Content *content = (struct Content *)(conf[i].value);
+ int c, x, y;
+
+ for (c = 0; c < max_num_entities; c++)
+ for (y = 0; y < 3; y++)
+ for (x = 0; x < 3; x++)
+ content[c].e[x][y] = default_value;
+ }
+ }
+ else /* constant size configuration data (1, 2 or 4 bytes) */
+ {
+ if (data_type == TYPE_BOOLEAN)
+ *(boolean *)(conf[i].value) = default_value;
+ else
+ *(int *) (conf[i].value) = default_value;
+ }
+ }
+}
+
+static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
+{
+ int i;
+
+ for (i = 0; conf[i].data_type != -1; i++)
+ {
+ int data_type = conf[i].data_type;
+ int conf_type = conf[i].conf_type;
+ int byte_mask = conf_type & CONF_MASK_BYTES;
+
+ if (byte_mask == CONF_MASK_MULTI_BYTES)
+ {
+ int max_num_entities = conf[i].max_num_entities;
+
+ if (data_type == TYPE_STRING)
+ {
+ char *string = (char *)(conf[i].value);
+ char *string_copy = (char *)(conf[i].value_copy);
+
+ strncpy(string_copy, string, max_num_entities);
+ }
+ else if (data_type == TYPE_ELEMENT_LIST)
+ {
+ int *element_array = (int *)(conf[i].value);
+ int *element_array_copy = (int *)(conf[i].value_copy);
+ int j;
+
+ for (j = 0; j < max_num_entities; j++)
+ element_array_copy[j] = element_array[j];
+ }
+ else if (data_type == TYPE_CONTENT_LIST)
+ {
+ struct Content *content = (struct Content *)(conf[i].value);
+ struct Content *content_copy = (struct Content *)(conf[i].value_copy);
+ int c, x, y;
+
+ for (c = 0; c < max_num_entities; c++)
+ for (y = 0; y < 3; y++)
+ for (x = 0; x < 3; x++)
+ content_copy[c].e[x][y] = content[c].e[x][y];
+ }
+ }
+ else /* constant size configuration data (1, 2 or 4 bytes) */
+ {
+ if (data_type == TYPE_BOOLEAN)
+ *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
+ else
+ *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
+ }
+ }
+}
+
+#if 1
+void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
+{
+#if 1
+ int i;
+#else
+ int i, x, y;
+#endif