rnd-20060509-1-src
[rocksndiamonds.git] / src / files.c
index ccea3f949f2f645f55ad17ff4d95bef93bb48982..b23388e9435e212d8e54b9990eaaac52226bc952 100644 (file)
@@ -29,7 +29,7 @@
 #define CHUNK_SIZE_NONE                -1      /* do not write chunk size    */
 #define FILE_VERS_CHUNK_SIZE   8       /* size of file version chunk */
 #define LEVEL_HEADER_SIZE      80      /* size of level file header  */
-#define LEVEL_HEADER_UNUSED    1       /* unused level header bytes  */
+#define LEVEL_HEADER_UNUSED    0       /* unused level header bytes  */
 #define LEVEL_CHUNK_CNT2_SIZE  160     /* size of level CNT2 chunk   */
 #define LEVEL_CHUNK_CNT2_UNUSED        11      /* unused CNT2 chunk bytes    */
 #define LEVEL_CHUNK_CNT3_HEADER        16      /* size of level CNT3 header  */
 #define TAPE_HEADER_SIZE       20      /* size of tape file header   */
 #define TAPE_HEADER_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) (48 + 48 + (x) * 48)
+#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"
+#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 "CONF" chunk */
+#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))
+
+/* a sequence of configuration values can be terminated by this value */
+#define CONF_LAST_ENTRY                        CONF_VALUE_1_BYTE(0)
+
+/* 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_INTEGER_1           CONF_VALUE_8_BIT(1)
+#define CONF_VALUE_INTEGER_2           CONF_VALUE_8_BIT(2)
+#define CONF_VALUE_INTEGER_3           CONF_VALUE_8_BIT(3)
+#define CONF_VALUE_INTEGER_4           CONF_VALUE_8_BIT(4)
+#define CONF_VALUE_INTEGER_5           CONF_VALUE_8_BIT(5)
+#define CONF_VALUE_INTEGER_6           CONF_VALUE_8_BIT(6)
+#define CONF_VALUE_INTEGER_7           CONF_VALUE_8_BIT(7)
+#define CONF_VALUE_INTEGER_8           CONF_VALUE_8_BIT(8)
+#define CONF_VALUE_BOOLEAN_1           CONF_VALUE_8_BIT(9)
+#define CONF_VALUE_BOOLEAN_2           CONF_VALUE_8_BIT(10)
+#define CONF_VALUE_BOOLEAN_3           CONF_VALUE_8_BIT(11)
+#define CONF_VALUE_BOOLEAN_4           CONF_VALUE_8_BIT(12)
+#define CONF_VALUE_BOOLEAN_5           CONF_VALUE_8_BIT(13)
+#define CONF_VALUE_BOOLEAN_6           CONF_VALUE_8_BIT(14)
+#define CONF_VALUE_BOOLEAN_7           CONF_VALUE_8_BIT(15)
+#define CONF_VALUE_BOOLEAN_8           CONF_VALUE_8_BIT(16)
+
+#define CONF_VALUE_ELEMENT_1           CONF_VALUE_16_BIT(1)
+#define CONF_VALUE_ELEMENT_2           CONF_VALUE_16_BIT(2)
+#define CONF_VALUE_ELEMENT_3           CONF_VALUE_16_BIT(3)
+#define CONF_VALUE_ELEMENT_4           CONF_VALUE_16_BIT(4)
+#define CONF_VALUE_ELEMENT_5           CONF_VALUE_16_BIT(5)
+#define CONF_VALUE_ELEMENT_6           CONF_VALUE_16_BIT(6)
+#define CONF_VALUE_ELEMENT_7           CONF_VALUE_16_BIT(7)
+#define CONF_VALUE_ELEMENT_8           CONF_VALUE_16_BIT(8)
 
-/* values for level file type identifier */
-#define LEVEL_FILE_TYPE_UNKNOWN                0
-#define LEVEL_FILE_TYPE_RND            1
-#define LEVEL_FILE_TYPE_BD             2
-#define LEVEL_FILE_TYPE_EM             3
-#define LEVEL_FILE_TYPE_SP             4
-#define LEVEL_FILE_TYPE_DX             5
-#define LEVEL_FILE_TYPE_SB             6
-#define LEVEL_FILE_TYPE_DC             7
+#if 0
+#define CONF_VALUE_ELEMENTS            CONF_VALUE_BYTES(1)
+#define CONF_VALUE_CONTENTS            CONF_VALUE_BYTES(2)
+#endif
+
+#if 0
+#define CONF_VALUE_INTEGER(x)          ((x) >= CONF_VALUE_INTEGER_1 && \
+                                        (x) <= CONF_VALUE_INTEGER_8)
+
+#define CONF_VALUE_BOOLEAN(x)          ((x) >= CONF_VALUE_BOOLEAN_1 && \
+                                        (x) <= CONF_VALUE_BOOLEAN_8)
+#endif
+
+#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)
+
+#if 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) == CONF_VALUE_ELEMENTS ?   \
+                                        CONF_ELEMENT_NUM_BYTES :       \
+                                        (t) == CONF_VALUE_CONTENTS ?   \
+                                        CONF_CONTENT_NUM_BYTES : 1)
+#endif
+
+#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]))
+
+#if 0
+static void LoadLevel_InitPlayfield(struct LevelInfo *, char *);
+#endif
+
+/* temporary variables used to store pointers to structure members */
+static struct LevelInfo li;
+static struct ElementInfo xx_ei;
+static struct ElementChangeInfo xx_change;
+static struct ElementGroupInfo xx_group;
+static unsigned int xx_event_bits_0_31, xx_event_bits_32_63;
+static int xx_num_description_bytes;
+static int xx_num_contents;
+static int xx_current_change_page;
+
+struct ElementFileConfig
+{
+  int element;                 /* element for which data is to be stored */
+  int data_type;               /* internal type of data */
+  int conf_type;               /* special type 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 *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 */
+};
+
+static struct ElementFileConfig element_conf[] =
+{
+  /* ---------- 1-byte values ---------------------------------------------- */
+
+  {
+    EL_EMC_ANDROID,
+    TYPE_INTEGER,                      CONF_VALUE_INTEGER_1,
+    &li.android_move_time,             10
+  },
+  {
+    EL_EMC_ANDROID,
+    TYPE_INTEGER,                      CONF_VALUE_INTEGER_2,
+    &li.android_clone_time,            10
+  },
+  {
+    EL_EMC_LENSES,
+    TYPE_INTEGER,                      CONF_VALUE_INTEGER_1,
+    &li.lenses_score,                  10
+  },
+  {
+    EL_EMC_LENSES,
+    TYPE_INTEGER,                      CONF_VALUE_INTEGER_2,
+    &li.lenses_time,                   10
+  },
+  {
+    EL_EMC_MAGNIFIER,
+    TYPE_INTEGER,                      CONF_VALUE_INTEGER_1,
+    &li.magnify_score,                 10
+  },
+  {
+    EL_EMC_MAGNIFIER,
+    TYPE_INTEGER,                      CONF_VALUE_INTEGER_2,
+    &li.magnify_time,                  10
+  },
+  {
+    EL_ROBOT,
+    TYPE_INTEGER,                      CONF_VALUE_INTEGER_1,
+    &li.slurp_score,                   10
+  },
+  {
+    EL_GAME_OF_LIFE,
+    TYPE_INTEGER,                      CONF_VALUE_INTEGER_1,
+    &li.game_of_life[0],               2
+  },
+  {
+    EL_GAME_OF_LIFE,
+    TYPE_INTEGER,                      CONF_VALUE_INTEGER_2,
+    &li.game_of_life[1],               3
+  },
+  {
+    EL_GAME_OF_LIFE,
+    TYPE_INTEGER,                      CONF_VALUE_INTEGER_3,
+    &li.game_of_life[2],               3
+  },
+  {
+    EL_GAME_OF_LIFE,
+    TYPE_INTEGER,                      CONF_VALUE_INTEGER_4,
+    &li.game_of_life[3],               3
+  },
+  {
+    EL_BIOMAZE,
+    TYPE_INTEGER,                      CONF_VALUE_INTEGER_1,
+    &li.biomaze[0],                    2
+  },
+  {
+    EL_BIOMAZE,
+    TYPE_INTEGER,                      CONF_VALUE_INTEGER_2,
+    &li.biomaze[1],                    3
+  },
+  {
+    EL_BIOMAZE,
+    TYPE_INTEGER,                      CONF_VALUE_INTEGER_3,
+    &li.biomaze[2],                    3
+  },
+  {
+    EL_BIOMAZE,
+    TYPE_INTEGER,                      CONF_VALUE_INTEGER_4,
+    &li.biomaze[3],                    3
+  },
+  {
+    EL_BALLOON,
+    TYPE_BITFIELD,                     CONF_VALUE_INTEGER_1,
+    &li.wind_direction_initial,                MV_NONE
+  },
+  {
+    EL_TIMEGATE_SWITCH,
+    TYPE_INTEGER,                      CONF_VALUE_INTEGER_1,
+    &li.time_timegate,                 10
+  },
+  {
+    EL_LIGHT_SWITCH_ACTIVE,
+    TYPE_INTEGER,                      CONF_VALUE_INTEGER_1,
+    &li.time_light,                    10
+  },
+  {
+    EL_SHIELD_NORMAL,
+    TYPE_INTEGER,                      CONF_VALUE_INTEGER_1,
+    &li.shield_normal_time,            10
+  },
+  {
+    EL_SHIELD_DEADLY,
+    TYPE_INTEGER,                      CONF_VALUE_INTEGER_1,
+    &li.shield_deadly_time,            10
+  },
+  {
+    EL_EXTRA_TIME,
+    TYPE_INTEGER,                      CONF_VALUE_INTEGER_1,
+    &li.extra_time,                    10
+  },
+  {
+    EL_EXTRA_TIME,
+    TYPE_INTEGER,                      CONF_VALUE_INTEGER_2,
+    &li.extra_time_score,              10
+  },
+  {
+    EL_TIME_ORB_FULL,
+    TYPE_INTEGER,                      CONF_VALUE_INTEGER_1,
+    &li.time_orb_time,                 10
+  },
+  {
+    EL_TIME_ORB_FULL,
+    TYPE_BOOLEAN,                      CONF_VALUE_BOOLEAN_1,
+    &li.use_time_orb_bug,              FALSE
+  },
+  {
+    EL_PLAYER_1,
+    TYPE_BOOLEAN,                      CONF_VALUE_BOOLEAN_1,
+    &li.block_snap_field,              TRUE
+  },
+  {
+    EL_PLAYER_1,
+    TYPE_BOOLEAN,                      CONF_VALUE_BOOLEAN_2,
+    &li.use_start_element[0],          FALSE
+  },
+  {
+    EL_PLAYER_2,
+    TYPE_BOOLEAN,                      CONF_VALUE_BOOLEAN_2,
+    &li.use_start_element[1],          FALSE
+  },
+  {
+    EL_PLAYER_3,
+    TYPE_BOOLEAN,                      CONF_VALUE_BOOLEAN_2,
+    &li.use_start_element[2],          FALSE
+  },
+  {
+    EL_PLAYER_4,
+    TYPE_BOOLEAN,                      CONF_VALUE_BOOLEAN_2,
+    &li.use_start_element[3],          FALSE
+  },
+  {
+    EL_PLAYER_1,
+    TYPE_BOOLEAN,                      CONF_VALUE_BOOLEAN_3,
+    &li.use_artwork_element[0],                FALSE
+  },
+  {
+    EL_PLAYER_2,
+    TYPE_BOOLEAN,                      CONF_VALUE_BOOLEAN_3,
+    &li.use_artwork_element[1],                FALSE
+  },
+  {
+    EL_PLAYER_3,
+    TYPE_BOOLEAN,                      CONF_VALUE_BOOLEAN_3,
+    &li.use_artwork_element[2],                FALSE
+  },
+  {
+    EL_PLAYER_4,
+    TYPE_BOOLEAN,                      CONF_VALUE_BOOLEAN_3,
+    &li.use_artwork_element[3],                FALSE
+  },
+  {
+    EL_PLAYER_1,
+    TYPE_BOOLEAN,                      CONF_VALUE_BOOLEAN_4,
+    &li.use_explosion_element[0],      FALSE
+  },
+  {
+    EL_PLAYER_2,
+    TYPE_BOOLEAN,                      CONF_VALUE_BOOLEAN_4,
+    &li.use_explosion_element[1],      FALSE
+  },
+  {
+    EL_PLAYER_3,
+    TYPE_BOOLEAN,                      CONF_VALUE_BOOLEAN_4,
+    &li.use_explosion_element[2],      FALSE
+  },
+  {
+    EL_PLAYER_4,
+    TYPE_BOOLEAN,                      CONF_VALUE_BOOLEAN_4,
+    &li.use_explosion_element[3],      FALSE
+  },
+  {
+    EL_PLAYER_1,
+    TYPE_BOOLEAN,                      CONF_VALUE_BOOLEAN_5,
+    &li.continuous_snapping,           TRUE
+  },
+  {
+    EL_PLAYER_1,
+    TYPE_INTEGER,                      CONF_VALUE_INTEGER_1,
+    &li.initial_player_stepsize,       STEPSIZE_NORMAL
+  },
+  {
+    EL_EMC_MAGIC_BALL,
+    TYPE_INTEGER,                      CONF_VALUE_INTEGER_1,
+    &li.ball_time,                     10
+  },
+  {
+    EL_EMC_MAGIC_BALL,
+    TYPE_BOOLEAN,                      CONF_VALUE_BOOLEAN_1,
+    &li.ball_random,                   FALSE
+  },
+  {
+    EL_EMC_MAGIC_BALL,
+    TYPE_BOOLEAN,                      CONF_VALUE_BOOLEAN_2,
+    &li.ball_state_initial,            FALSE
+  },
+
+  /* ---------- 2-byte values ---------------------------------------------- */
+
+  {
+    EL_PLAYER_1,
+    TYPE_ELEMENT,                      CONF_VALUE_ELEMENT_1,
+    &li.start_element[0],              EL_PLAYER_1
+  },
+  {
+    EL_PLAYER_2,
+    TYPE_ELEMENT,                      CONF_VALUE_ELEMENT_1,
+    &li.start_element[1],              EL_PLAYER_2
+  },
+  {
+    EL_PLAYER_3,
+    TYPE_ELEMENT,                      CONF_VALUE_ELEMENT_1,
+    &li.start_element[2],              EL_PLAYER_3
+  },
+  {
+    EL_PLAYER_4,
+    TYPE_ELEMENT,                      CONF_VALUE_ELEMENT_1,
+    &li.start_element[3],              EL_PLAYER_4
+  },
+  {
+    EL_PLAYER_1,
+    TYPE_ELEMENT,                      CONF_VALUE_ELEMENT_2,
+    &li.artwork_element[0],            EL_PLAYER_1
+  },
+  {
+    EL_PLAYER_2,
+    TYPE_ELEMENT,                      CONF_VALUE_ELEMENT_2,
+    &li.artwork_element[1],            EL_PLAYER_2
+  },
+  {
+    EL_PLAYER_3,
+    TYPE_ELEMENT,                      CONF_VALUE_ELEMENT_2,
+    &li.artwork_element[2],            EL_PLAYER_3
+  },
+  {
+    EL_PLAYER_4,
+    TYPE_ELEMENT,                      CONF_VALUE_ELEMENT_2,
+    &li.artwork_element[3],            EL_PLAYER_4
+  },
+  {
+    EL_PLAYER_1,
+    TYPE_ELEMENT,                      CONF_VALUE_ELEMENT_3,
+    &li.explosion_element[0],          EL_PLAYER_1
+  },
+  {
+    EL_PLAYER_2,
+    TYPE_ELEMENT,                      CONF_VALUE_ELEMENT_3,
+    &li.explosion_element[1],          EL_PLAYER_2
+  },
+  {
+    EL_PLAYER_3,
+    TYPE_ELEMENT,                      CONF_VALUE_ELEMENT_3,
+    &li.explosion_element[2],          EL_PLAYER_3
+  },
+  {
+    EL_PLAYER_4,
+    TYPE_ELEMENT,                      CONF_VALUE_ELEMENT_3,
+    &li.explosion_element[3],          EL_PLAYER_4
+  },
+
+  /* ---------- multi-byte values ------------------------------------------ */
+
+  {
+    EL_EMC_ANDROID,
+    TYPE_ELEMENT_LIST,                 CONF_VALUE_BYTES(1),
+    &li.android_clone_element[0],      EL_EMPTY,
+    &li.num_android_clone_elements,    1, MAX_ANDROID_ELEMENTS
+  },
+  {
+    EL_EMC_MAGIC_BALL,
+    TYPE_CONTENT_LIST,                 CONF_VALUE_BYTES(2),
+    &li.ball_content,                  EL_EMPTY,
+    &li.num_ball_contents,             4, MAX_ELEMENT_CONTENTS
+  },
+
+  {
+    -1,
+    -1,                                        -1,
+    NULL,                              -1,
+  },
+};
+
+static struct ElementFileConfig custom_element_conf[] =
+{
+  {
+    -1,
+    TYPE_STRING,                       CONF_VALUE_BYTES(1),
+    &xx_ei.description[0],             0,
+    &xx_num_description_bytes, MAX_ELEMENT_NAME_LEN, MAX_ELEMENT_NAME_LEN,
+  },
+
+  {
+    -1,
+    TYPE_BITFIELD,                     CONF_VALUE_32_BIT(1),
+    &xx_ei.properties[EP_BITFIELD_BASE],
+#if 0
+    EP_BITMASK_DEFAULT
+#else
+    (1 << EP_CAN_MOVE_INTO_ACID)
+#endif
+  },
+#if 0
+  /* (reserved) */
+  {
+    -1,
+    TYPE_BITFIELD,                     CONF_VALUE_32_BIT(2),
+    &xx_ei.properties[EP_BITFIELD_BASE + 1], EP_BITMASK_DEFAULT
+  },
+#endif
+
+  {
+    -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(1),
+    &xx_ei.use_gfx_element,            FALSE
+  },
+  {
+    -1,
+    TYPE_ELEMENT,                      CONF_VALUE_16_BIT(1),
+    &xx_ei.gfx_element,                        EL_EMPTY_SPACE
+  },
+
+  {
+    -1,
+    TYPE_BITFIELD,                     CONF_VALUE_8_BIT(2),
+    &xx_ei.access_direction,           MV_ALL_DIRECTIONS
+  },
+
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(2),
+    &xx_ei.collect_score_initial,      10
+  },
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(3),
+    &xx_ei.collect_count_initial,      1
+  },
+
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(4),
+    &xx_ei.ce_value_fixed_initial,     0
+  },
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(5),
+    &xx_ei.ce_value_random_initial,    0
+  },
+  {
+    -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(3),
+    &xx_ei.use_last_ce_value,          FALSE
+  },
+
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(6),
+    &xx_ei.push_delay_fixed,           8,
+  },
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(7),
+    &xx_ei.push_delay_random,          8,
+  },
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(8),
+    &xx_ei.drop_delay_fixed,           0
+  },
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(9),
+    &xx_ei.drop_delay_random,          0
+  },
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(10),
+    &xx_ei.move_delay_fixed,           0
+  },
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(11),
+    &xx_ei.move_delay_random,          0
+  },
+
+  {
+    -1,
+    TYPE_BITFIELD,                     CONF_VALUE_32_BIT(3),
+    &xx_ei.move_pattern,               MV_ALL_DIRECTIONS
+  },
+  {
+    -1,
+    TYPE_BITFIELD,                     CONF_VALUE_8_BIT(4),
+    &xx_ei.move_direction_initial,     MV_START_AUTOMATIC
+  },
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(5),
+    &xx_ei.move_stepsize,              TILEX / 8
+  },
+
+  {
+    -1,
+    TYPE_ELEMENT,                      CONF_VALUE_16_BIT(12),
+    &xx_ei.move_enter_element,         EL_EMPTY_SPACE
+  },
+  {
+    -1,
+    TYPE_ELEMENT,                      CONF_VALUE_16_BIT(13),
+    &xx_ei.move_leave_element,         EL_EMPTY_SPACE
+  },
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(6),
+    &xx_ei.move_leave_type,            LEAVE_TYPE_UNLIMITED
+  },
+
+  {
+    -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(7),
+    &xx_ei.slippery_type,              SLIPPERY_ANY_RANDOM
+  },
+
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(8),
+    &xx_ei.explosion_type,             EXPLODES_3X3
+  },
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(14),
+    &xx_ei.explosion_delay,            16
+  },
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(15),
+    &xx_ei.ignition_delay,             8
+  },
+
+  {
+    -1,
+    TYPE_CONTENT_LIST,                 CONF_VALUE_BYTES(2),
+    &xx_ei.content,                    EL_EMPTY_SPACE,
+    &xx_num_contents,                  1, 1
+  },
+
+  /* ---------- "num_change_pages" must be the last entry ------------------- */
+
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(9),
+    &xx_ei.num_change_pages,           -1      /* always save this value */
+  },
+
+  {
+    -1,
+    -1,                                        -1,
+    NULL,                              -1,
+  },
+};
 
-#define LEVEL_FILE_TYPE_RND_PACKED     (10 + LEVEL_FILE_TYPE_RND)
-#define LEVEL_FILE_TYPE_EM_PACKED      (10 + LEVEL_FILE_TYPE_EM)
+static struct ElementFileConfig custom_element_change_conf[] =
+{
+  /* ---------- "current_change_page" must be the first entry --------------- */
+
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(1),
+    &xx_current_change_page,           -1
+  },
+
+  {
+    -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(2),
+    &xx_change.can_change,             FALSE
+  },
+
+  {
+    -1,
+    TYPE_BITFIELD,                     CONF_VALUE_32_BIT(1),
+    &xx_event_bits_0_31,               0
+  },
+  {
+    -1,
+    TYPE_BITFIELD,                     CONF_VALUE_32_BIT(2),
+    &xx_event_bits_32_63,              0
+  },
+
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(3),
+    &xx_change.trigger_player,         CH_PLAYER_ANY
+  },
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(4),
+    &xx_change.trigger_side,           CH_SIDE_ANY
+  },
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(5),
+    &xx_change.trigger_page,           CH_PAGE_ANY
+  },
 
-#define IS_SINGLE_LEVEL_FILE(x)                (x < 10)
-#define IS_PACKED_LEVEL_FILE(x)                (x > 10)
+  {
+    -1,
+    TYPE_ELEMENT,                      CONF_VALUE_16_BIT(1),
+    &xx_change.target_element,         EL_EMPTY_SPACE
+  },
+
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(2),
+    &xx_change.delay_fixed,            0
+  },
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(3),
+    &xx_change.delay_random,           0
+  },
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(4),
+    &xx_change.delay_frames,           FRAMES_PER_SECOND
+  },
+
+  {
+    -1,
+    TYPE_ELEMENT,                      CONF_VALUE_16_BIT(5),
+    &xx_change.trigger_element,                EL_EMPTY_SPACE
+  },
+
+  {
+    -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(6),
+    &xx_change.explode,                        FALSE
+  },
+  {
+    -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(7),
+    &xx_change.use_target_content,     FALSE
+  },
+  {
+    -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(8),
+    &xx_change.only_if_complete,       FALSE
+  },
+  {
+    -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(9),
+    &xx_change.use_random_replace,     FALSE
+  },
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(10),
+    &xx_change.random_percentage,      100
+  },
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(11),
+    &xx_change.replace_when,           CP_WHEN_EMPTY
+  },
+
+  {
+    -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(12),
+    &xx_change.has_action,             FALSE
+  },
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(13),
+    &xx_change.action_type,            CA_NO_ACTION
+  },
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(14),
+    &xx_change.action_mode,            CA_MODE_UNDEFINED
+  },
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(6),
+    &xx_change.action_arg,             CA_ARG_UNDEFINED
+  },
+
+  {
+    -1,
+    TYPE_CONTENT_LIST,                 CONF_VALUE_BYTES(1),
+    &xx_change.target_content,         EL_EMPTY_SPACE,
+    &xx_num_contents,                  1, 1
+  },
+
+  {
+    -1,
+    -1,                                        -1,
+    NULL,                              -1,
+  },
+};
+
+static struct ElementFileConfig group_element_conf[] =
+{
+  {
+    -1,
+    TYPE_STRING,                       CONF_VALUE_BYTES(1),
+    &xx_ei.description[0],             0,
+    &xx_num_description_bytes, MAX_ELEMENT_NAME_LEN, MAX_ELEMENT_NAME_LEN,
+  },
+
+  {
+    -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(1),
+    &xx_ei.use_gfx_element,            FALSE
+  },
+  {
+    -1,
+    TYPE_ELEMENT,                      CONF_VALUE_16_BIT(1),
+    &xx_ei.gfx_element,                        EL_EMPTY_SPACE
+  },
+
+  {
+    -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(2),
+    &xx_group.choice_mode,             ANIM_RANDOM
+  },
+
+  {
+    -1,
+    TYPE_ELEMENT_LIST,                 CONF_VALUE_BYTES(2),
+    &xx_group.element[0],              EL_EMPTY_SPACE,
+    &xx_group.num_elements,            1, MAX_ELEMENTS_IN_GROUP
+  },
+
+  {
+    -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 void setLevelInfoToDefaultsFromConfigList(struct LevelInfo *level)
+{
+  int i;
+
+  li = *level;         /* copy level data into temporary buffer */
+
+  for (i = 0; element_conf[i].data_type != -1; i++)
+  {
+    int default_value = element_conf[i].default_value;
+    int data_type = element_conf[i].data_type;
+    int conf_type = element_conf[i].conf_type;
+    int byte_mask = conf_type & CONF_MASK_BYTES;
+
+    if (byte_mask == CONF_MASK_MULTI_BYTES)
+    {
+      int default_num_entities = element_conf[i].default_num_entities;
+      int max_num_entities = element_conf[i].max_num_entities;
+
+      *(int *)(element_conf[i].num_entities) = default_num_entities;
+
+      if (data_type == TYPE_ELEMENT_LIST)
+      {
+       int *element_array = (int *)(element_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 *)(element_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 *)(element_conf[i].value) = default_value;
+      else
+       *(int *)    (element_conf[i].value) = default_value;
+    }
+  }
+
+  *level = li;         /* copy temporary buffer back to level data */
+}
+
 void setElementChangePages(struct ElementInfo *ei, int change_pages)
 {
   int change_page_size = sizeof(struct ElementChangeInfo);
@@ -87,11 +924,12 @@ void setElementChangePages(struct ElementInfo *ei, int change_pages)
 
 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
 {
-  int x, y;
+  int i, x, y;
 
   change->can_change = FALSE;
 
-  change->events = CE_BITMASK_DEFAULT;
+  for (i = 0; i < NUM_CHANGE_EVENTS; i++)
+    change->has_event[i] = FALSE;
 
   change->trigger_player = CH_PLAYER_ANY;
   change->trigger_side = CH_SIDE_ANY;
@@ -101,7 +939,7 @@ void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
 
   change->delay_fixed = 0;
   change->delay_random = 0;
-  change->delay_frames = 1;
+  change->delay_frames = FRAMES_PER_SECOND;
 
   change->trigger_element = EL_EMPTY_SPACE;
 
@@ -112,9 +950,14 @@ void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
   change->random_percentage = 100;
   change->replace_when = CP_WHEN_EMPTY;
 
+  change->has_action = FALSE;
+  change->action_type = CA_NO_ACTION;
+  change->action_mode = CA_MODE_UNDEFINED;
+  change->action_arg = CA_ARG_UNDEFINED;
+
   for (x = 0; x < 3; x++)
     for (y = 0; y < 3; y++)
-      change->target_content[x][y] = EL_EMPTY_SPACE;
+      change->target_content.e[x][y] = EL_EMPTY_SPACE;
 
   change->direct_action = 0;
   change->other_action = 0;
@@ -127,9 +970,19 @@ void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
 static void setLevelInfoToDefaults(struct LevelInfo *level)
 {
   static boolean clipboard_elements_initialized = FALSE;
-
   int i, j, x, y;
 
+#if 1
+  InitElementPropertiesStatic();
+#endif
+
+  setLevelInfoToDefaultsFromConfigList(level);
+  setLevelInfoToDefaults_EM();
+
+  level->native_em_level = &native_em_level;
+
+  level->game_engine_type = GAME_ENGINE_TYPE_RND;
+
   level->file_version = FILE_VERSION_ACTUAL;
   level->game_version = GAME_VERSION_ACTUAL;
 
@@ -151,26 +1004,70 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
 
   level->time_magic_wall = 10;
   level->time_wheel = 10;
+#if 0
   level->time_light = 10;
   level->time_timegate = 10;
+#endif
 
   level->amoeba_content = EL_DIAMOND;
 
+  level->game_of_life[0] = 2;
+  level->game_of_life[1] = 3;
+  level->game_of_life[2] = 3;
+  level->game_of_life[3] = 3;
+
+  level->biomaze[0] = 2;
+  level->biomaze[1] = 3;
+  level->biomaze[2] = 3;
+  level->biomaze[3] = 3;
+
+#if 0
   level->double_speed = FALSE;
+#endif
   level->initial_gravity = FALSE;
   level->em_slippery_gems = FALSE;
-  level->block_last_field = FALSE;
-  level->sp_block_last_field = TRUE;
   level->instant_relocation = FALSE;
   level->can_pass_to_walkable = FALSE;
   level->grow_into_diggable = TRUE;
 
+#if 0
+  level->block_snap_field = TRUE;
+#endif
+
+  level->block_last_field = FALSE;     /* EM does not block by default */
+  level->sp_block_last_field = TRUE;   /* SP blocks the last field */
+
   level->can_move_into_acid_bits = ~0; /* everything can move into acid */
-  level->dont_collide_with_bits = ~0;  /* always deadly when colliding  */
+  level->dont_collide_with_bits = ~0;  /* always deadly when colliding */
 
   level->use_spring_bug = FALSE;
+  level->use_time_orb_bug = FALSE;
+
   level->use_step_counter = FALSE;
 
+  /* values for the new EMC elements */
+#if 0
+  level->android_move_time = 10;
+  level->android_clone_time = 10;
+  level->ball_time = 10;
+  level->lenses_score = 10;
+  level->lenses_time = 10;
+  level->magnify_score = 10;
+  level->magnify_time = 10;
+  level->slurp_score = 10;
+  level->wind_direction_initial = MV_NONE;
+  level->ball_random = FALSE;
+  level->ball_state_initial = FALSE;
+  for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+    for (x = 0; x < 3; x++)
+      for (y = 0; y < 3; y++)
+       level->ball_content[i].e[x][y] = EL_EMPTY;
+#endif
+#if 0
+  for (i = 0; i < 16; i++)
+    level->android_array[i] = FALSE;
+#endif
+
   level->use_custom_template = FALSE;
 
   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
@@ -189,13 +1086,13 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
   }
 
   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
-    level->score[i] = 10;
+    level->score[i] = (i == SC_TIME_BONUS ? 1 : 10);
 
   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
     for (x = 0; x < 3; x++)
       for (y = 0; y < 3; y++)
-       level->yamyam_content[i][x][y] =
+       level->yamyam_content[i].e[x][y] =
          (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
 
   level->field[0][0] = EL_PLAYER_1;
@@ -204,83 +1101,95 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
   {
     int element = i;
+    struct ElementInfo *ei = &element_info[element];
 
     /* never initialize clipboard elements after the very first time */
     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
       continue;
 
-    setElementChangePages(&element_info[element], 1);
-    setElementChangeInfoToDefaults(element_info[element].change);
+    setElementChangePages(ei, 1);
+    setElementChangeInfoToDefaults(ei->change);
 
     if (IS_CUSTOM_ELEMENT(element) ||
        IS_GROUP_ELEMENT(element) ||
        IS_INTERNAL_ELEMENT(element))
     {
       for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
-       element_info[element].description[j] = '\0';
+       ei->description[j] = '\0';
 
-      if (element_info[element].custom_description != NULL)
-       strncpy(element_info[element].description,
-               element_info[element].custom_description,MAX_ELEMENT_NAME_LEN);
+      if (ei->custom_description != NULL)
+       strncpy(ei->description, ei->custom_description,MAX_ELEMENT_NAME_LEN);
       else
-       strcpy(element_info[element].description,
-              element_info[element].editor_description);
+       strcpy(ei->description, ei->editor_description);
 
-      element_info[element].use_gfx_element = FALSE;
-      element_info[element].gfx_element = EL_EMPTY_SPACE;
+      ei->use_gfx_element = FALSE;
+      ei->gfx_element = EL_EMPTY_SPACE;
 
-      element_info[element].modified_settings = FALSE;
+      ei->modified_settings = FALSE;
     }
 
     if (IS_CUSTOM_ELEMENT(element) ||
        IS_INTERNAL_ELEMENT(element))
     {
-      element_info[element].access_direction = MV_ALL_DIRECTIONS;
+      ei->access_direction = MV_ALL_DIRECTIONS;
+
+      ei->collect_score_initial = 10;  /* special default */
+      ei->collect_count_initial = 1;   /* special default */
 
-      element_info[element].collect_score = 10;                /* special default */
-      element_info[element].collect_count = 1;         /* special default */
+      ei->ce_value_fixed_initial = 0;
+      ei->ce_value_random_initial = 0;
+      ei->use_last_ce_value = FALSE;
 
-      element_info[element].push_delay_fixed = -1;     /* initialize later */
-      element_info[element].push_delay_random = -1;    /* initialize later */
-      element_info[element].drop_delay_fixed = 0;
-      element_info[element].drop_delay_random = 0;
-      element_info[element].move_delay_fixed = 0;
-      element_info[element].move_delay_random = 0;
+      ei->push_delay_fixed = -1;       /* initialize later */
+      ei->push_delay_random = -1;      /* initialize later */
+      ei->drop_delay_fixed = 0;
+      ei->drop_delay_random = 0;
+      ei->move_delay_fixed = 0;
+      ei->move_delay_random = 0;
 
-      element_info[element].move_pattern = MV_ALL_DIRECTIONS;
-      element_info[element].move_direction_initial = MV_START_AUTOMATIC;
-      element_info[element].move_stepsize = TILEX / 8;
+      ei->move_pattern = MV_ALL_DIRECTIONS;
+      ei->move_direction_initial = MV_START_AUTOMATIC;
+      ei->move_stepsize = TILEX / 8;
 
-      element_info[element].move_enter_element = EL_EMPTY_SPACE;
-      element_info[element].move_leave_element = EL_EMPTY_SPACE;
-      element_info[element].move_leave_type = LEAVE_TYPE_UNLIMITED;
+      ei->move_enter_element = EL_EMPTY_SPACE;
+      ei->move_leave_element = EL_EMPTY_SPACE;
+      ei->move_leave_type = LEAVE_TYPE_UNLIMITED;
 
-      element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
+      ei->slippery_type = SLIPPERY_ANY_RANDOM;
 
-      element_info[element].explosion_type = EXPLODES_3X3;
-      element_info[element].explosion_delay = 16;
-      element_info[element].ignition_delay = 8;
+      ei->explosion_type = EXPLODES_3X3;
+      ei->explosion_delay = 16;
+      ei->ignition_delay = 8;
 
       for (x = 0; x < 3; x++)
        for (y = 0; y < 3; y++)
-         element_info[element].content[x][y] = EL_EMPTY_SPACE;
+         ei->content.e[x][y] = EL_EMPTY_SPACE;
 
-      element_info[element].access_type = 0;
-      element_info[element].access_layer = 0;
-      element_info[element].access_protected = 0;
-      element_info[element].walk_to_action = 0;
-      element_info[element].smash_targets = 0;
-      element_info[element].deadliness = 0;
+      ei->access_type = 0;
+      ei->access_layer = 0;
+      ei->access_protected = 0;
+      ei->walk_to_action = 0;
+      ei->smash_targets = 0;
+      ei->deadliness = 0;
 
-      element_info[element].can_explode_by_fire = FALSE;
-      element_info[element].can_explode_smashed = FALSE;
-      element_info[element].can_explode_impact = FALSE;
+      ei->can_explode_by_fire = FALSE;
+      ei->can_explode_smashed = FALSE;
+      ei->can_explode_impact = FALSE;
 
-      element_info[element].current_change_page = 0;
+      ei->current_change_page = 0;
 
+#if 0
+      /* !!! now done in InitElementPropertiesStatic() (see above) !!! */
+      /* !!! (else properties set there will be overwritten here)  !!! */
       /* start with no properties at all */
+#if 1
+      for (j = 0; j < NUM_EP_BITFIELDS; j++)
+       ei->properties[j] = EP_BITMASK_DEFAULT;
+#else
       for (j = 0; j < NUM_EP_BITFIELDS; j++)
        Properties[element][j] = EP_BITMASK_DEFAULT;
+#endif
+#endif
 
       /* now set default properties */
       SET_PROPERTY(element, EP_CAN_MOVE_INTO_ACID, TRUE);
@@ -289,18 +1198,21 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
     if (IS_GROUP_ELEMENT(element) ||
        IS_INTERNAL_ELEMENT(element))
     {
+      struct ElementGroupInfo *group;
+
       /* initialize memory for list of elements in group */
-      if (element_info[element].group == NULL)
-       element_info[element].group =
-         checked_malloc(sizeof(struct ElementGroupInfo));
+      if (ei->group == NULL)
+       ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
+
+      group = ei->group;
 
       for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
-       element_info[element].group->element[j] = EL_EMPTY_SPACE;
+       group->element[j] = EL_EMPTY_SPACE;
 
       /* default: only one element in group */
-      element_info[element].group->num_elements = 1;
+      group->num_elements = 1;
 
-      element_info[element].group->choice_mode = ANIM_RANDOM;
+      group->choice_mode = ANIM_RANDOM;
     }
   }
 
@@ -310,11 +1222,13 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
 
   level->no_valid_file = FALSE;
 
+  level->changed = FALSE;
+
   if (leveldir_current == NULL)                /* only when dumping level */
     return;
 
   /* try to determine better author name than 'anonymous' */
-  if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
+  if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
   {
     strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
     level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
@@ -355,9 +1269,15 @@ static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
 
 static void ActivateLevelTemplate()
 {
+#if 1
+  /* Currently there is no special action needed to activate the template
+     data, because 'element_info' property settings overwrite the original
+     level data, while all other variables do not change. */
+#else
   /* Currently there is no special action needed to activate the template
      data, because 'element_info' and 'Properties' overwrite the original
      level data, while all other variables do not change. */
+#endif
 }
 
 static char *getLevelFilenameFromBasename(char *basename)
@@ -391,41 +1311,21 @@ static int getFileTypeFromBasename(char *basename)
   if (stat(filename, &file_status) == 0)
   {
     /* check for typical filesize of a Supaplex level package file */
-    if (file_status.st_size == 170496)
-      return LEVEL_FILE_TYPE_SP;
-  }
-
-  return LEVEL_FILE_TYPE_UNKNOWN;
-}
-
-static char *getSingleLevelBasename(int nr, int type)
-{
-  static char basename[MAX_FILENAME_LEN];
-  char *level_filename = getStringCopy(leveldir_current->level_filename);
-
-  if (level_filename == NULL)
-    level_filename = getStringCat2("%03d.", LEVELFILE_EXTENSION);
-
-  switch (type)
-  {
-    case LEVEL_FILE_TYPE_RND:
-      if (nr < 0)
-       sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
-      else
-       sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
-      break;
+    if (file_status.st_size == 170496)
+      return LEVEL_FILE_TYPE_SP;
+  }
 
-    case LEVEL_FILE_TYPE_EM:
-      sprintf(basename, "%d", nr);
-      break;
+  return LEVEL_FILE_TYPE_UNKNOWN;
+}
 
-    case LEVEL_FILE_TYPE_UNKNOWN:
-    default:
-      sprintf(basename, level_filename, nr);
-      break;
-  }
+static char *getSingleLevelBasename(int nr)
+{
+  static char basename[MAX_FILENAME_LEN];
 
-  free(level_filename);
+  if (nr < 0)
+    sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
+  else
+    sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
 
   return basename;
 }
@@ -468,9 +1368,9 @@ static char *getPackedLevelBasename(int type)
   return basename;
 }
 
-static char *getSingleLevelFilename(int nr, int type)
+static char *getSingleLevelFilename(int nr)
 {
-  return getLevelFilenameFromBasename(getSingleLevelBasename(nr, type));
+  return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
 }
 
 #if 0
@@ -482,9 +1382,10 @@ static char *getPackedLevelFilename(int type)
 
 char *getDefaultLevelFilename(int nr)
 {
-  return getSingleLevelFilename(nr, LEVEL_FILE_TYPE_RND);
+  return getSingleLevelFilename(nr);
 }
 
+#if 0
 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
                                                 int type)
 {
@@ -493,6 +1394,23 @@ static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
   lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
   lfi->filename = getLevelFilenameFromBasename(lfi->basename);
 }
+#endif
+
+static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
+                                                int type, char *format, ...)
+{
+  static char basename[MAX_FILENAME_LEN];
+  va_list ap;
+
+  va_start(ap, format);
+  vsprintf(basename, format, ap);
+  va_end(ap);
+
+  lfi->type = type;
+  lfi->packed = FALSE;
+  lfi->basename = basename;
+  lfi->filename = getLevelFilenameFromBasename(lfi->basename);
+}
 
 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
                                                 int type)
@@ -503,31 +1421,94 @@ static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
   lfi->filename = getLevelFilenameFromBasename(lfi->basename);
 }
 
+static int getFiletypeFromID(char *filetype_id)
+{
+  char *filetype_id_lower;
+  int filetype = LEVEL_FILE_TYPE_UNKNOWN;
+  int i;
+
+  if (filetype_id == NULL)
+    return LEVEL_FILE_TYPE_UNKNOWN;
+
+  filetype_id_lower = getStringToLower(filetype_id);
+
+  for (i = 0; filetype_id_list[i].id != NULL; i++)
+  {
+    char *id_lower = getStringToLower(filetype_id_list[i].id);
+    
+    if (strEqual(filetype_id_lower, id_lower))
+      filetype = filetype_id_list[i].filetype;
+
+    free(id_lower);
+
+    if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
+      break;
+  }
+
+  free(filetype_id_lower);
+
+  return filetype;
+}
+
 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
 {
+  int nr = lfi->nr;
+
   /* special case: level number is negative => check for level template file */
-  if (lfi->nr < 0)
+  if (nr < 0)
   {
-    setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND);
+    setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
+                                        "template.%s", LEVELFILE_EXTENSION);
 
+    /* no fallback if template file not existing */
     return;
   }
 
+  /* special case: check for file name/pattern specified in "levelinfo.conf" */
   if (leveldir_current->level_filename != NULL)
   {
-    /* check for file name/pattern specified in "levelinfo.conf" */
-    setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
+    int filetype = getFiletypeFromID(leveldir_current->level_filetype);
+
+    setLevelFileInfo_FormatLevelFilename(lfi, filetype,
+                                        leveldir_current->level_filename, nr);
     if (fileExists(lfi->filename))
       return;
   }
 
   /* check for native Rocks'n'Diamonds level file */
-  setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND);
+  setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
+                                      "%03d.%s", nr, LEVELFILE_EXTENSION);
+  if (fileExists(lfi->filename))
+    return;
+
+  /* check for Emerald Mine level file (V1) */
+  setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
+                                      'a' + (nr / 10) % 26, '0' + nr % 10);
+  if (fileExists(lfi->filename))
+    return;
+  setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
+                                      'A' + (nr / 10) % 26, '0' + nr % 10);
+  if (fileExists(lfi->filename))
+    return;
+
+  /* check for Emerald Mine level file (V2 to V5) */
+  setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
   if (fileExists(lfi->filename))
     return;
 
-  /* check for classic Emerald Mine level file */
-  setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_EM);
+  /* check for Emerald Mine level file (V6 / single mode) */
+  setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
+  if (fileExists(lfi->filename))
+    return;
+  setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
+  if (fileExists(lfi->filename))
+    return;
+
+  /* check for Emerald Mine level file (V6 / teamwork mode) */
+  setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
+  if (fileExists(lfi->filename))
+    return;
+  setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
   if (fileExists(lfi->filename))
     return;
 
@@ -536,8 +1517,9 @@ static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
   if (fileExists(lfi->filename))
     return;
 
-  /* no known level file found -- try to use default values */
-  setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
+  /* no known level file found -- use default values (and fail later) */
+  setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
+                                      "%03d.%s", nr, LEVELFILE_EXTENSION);
 }
 
 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
@@ -546,22 +1528,17 @@ static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
     lfi->type = getFileTypeFromBasename(lfi->basename);
 }
 
-static struct LevelFileInfo *getLevelFileInfo(int nr)
+static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
 {
-  static struct LevelFileInfo level_file_info;
-
   /* always start with reliable default values */
-  setFileInfoToDefaults(&level_file_info);
+  setFileInfoToDefaults(level_file_info);
 
-  level_file_info.nr = nr;     /* set requested level number */
+  level_file_info->nr = nr;    /* set requested level number */
 
-  determineLevelFileInfo_Filename(&level_file_info);
-  determineLevelFileInfo_Filetype(&level_file_info);
-
-  return &level_file_info;
+  determineLevelFileInfo_Filename(level_file_info);
+  determineLevelFileInfo_Filetype(level_file_info);
 }
 
-
 /* ------------------------------------------------------------------------- */
 /* functions for loading R'n'D level                                         */
 /* ------------------------------------------------------------------------- */
@@ -570,7 +1547,6 @@ int getMappedElement(int element)
 {
   /* remap some (historic, now obsolete) elements */
 
-#if 1
   switch (element)
   {
     case EL_PLAYER_OBSOLETE:
@@ -613,18 +1589,6 @@ int getMappedElement(int element)
       }
       break;
   }
-#else
-  if (element >= NUM_FILE_ELEMENTS)
-  {
-    Error(ERR_WARN, "invalid level element %d", element);
-
-    element = EL_UNKNOWN;
-  }
-  else if (element == EL_PLAYER_OBSOLETE)
-    element = EL_PLAYER_1;
-  else if (element == EL_KEY_OBSOLETE)
-    element = EL_KEY_1;
-#endif
 
   return element;
 }
@@ -684,13 +1648,16 @@ static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
     for (y = 0; y < 3; y++)
       for (x = 0; x < 3; x++)
-       level->yamyam_content[i][x][y] = getMappedElement(getFile8Bit(file));
+       level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
 
   level->amoeba_speed          = getFile8Bit(file);
   level->time_magic_wall       = getFile8Bit(file);
   level->time_wheel            = getFile8Bit(file);
   level->amoeba_content                = getMappedElement(getFile8Bit(file));
-  level->double_speed          = (getFile8Bit(file) == 1 ? TRUE : FALSE);
+
+  level->initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
+                                   STEPSIZE_NORMAL);
+
   level->initial_gravity       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
   level->encoding_16bit_field  = (getFile8Bit(file) == 1 ? TRUE : FALSE);
   level->em_slippery_gems      = (getFile8Bit(file) == 1 ? TRUE : FALSE);
@@ -709,6 +1676,8 @@ static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
   level->can_pass_to_walkable  = (getFile8Bit(file) == 1 ? TRUE : FALSE);
   level->grow_into_diggable    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
 
+  level->game_engine_type      = getFile8Bit(file);
+
   ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
 
   return chunk_size;
@@ -786,7 +1755,7 @@ static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
     for (y = 0; y < 3; y++)
       for (x = 0; x < 3; x++)
-       level->yamyam_content[i][x][y] =
+       level->yamyam_content[i].e[x][y] =
          getMappedElement(level->encoding_16bit_field ?
                           getFile16BitBE(file) : getFile8Bit(file));
   return chunk_size;
@@ -822,7 +1791,7 @@ static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
     for (i = 0; i < num_contents; i++)
       for (y = 0; y < 3; y++)
        for (x = 0; x < 3; x++)
-         level->yamyam_content[i][x][y] = content_array[i][x][y];
+         level->yamyam_content[i].e[x][y] = content_array[i][x][y];
   }
   else if (element == EL_BD_AMOEBA)
   {
@@ -887,10 +1856,17 @@ static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
     int element = getFile16BitBE(file);
     int properties = getFile32BitBE(file);
 
+#if 1
+    if (IS_CUSTOM_ELEMENT(element))
+      element_info[element].properties[EP_BITFIELD_BASE] = properties;
+    else
+      Error(ERR_WARN, "invalid custom element number %d", element);
+#else
     if (IS_CUSTOM_ELEMENT(element))
       Properties[element][EP_BITFIELD_BASE] = properties;
     else
       Error(ERR_WARN, "invalid custom element number %d", element);
+#endif
   }
 
   return chunk_size;
@@ -937,6 +1913,8 @@ static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
   for (i = 0; i < num_changed_custom_elements; i++)
   {
     int element = getFile16BitBE(file);
+    struct ElementInfo *ei = &element_info[element];
+    unsigned int event_bits;
 
     if (!IS_CUSTOM_ELEMENT(element))
     {
@@ -946,67 +1924,70 @@ static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
     }
 
     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
-      element_info[element].description[j] = getFile8Bit(file);
-    element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
+      ei->description[j] = getFile8Bit(file);
+    ei->description[MAX_ELEMENT_NAME_LEN] = 0;
 
+#if 1
+    ei->properties[EP_BITFIELD_BASE] = getFile32BitBE(file);
+#else
     Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
+#endif
 
     /* some free bytes for future properties and padding */
     ReadUnusedBytesFromFile(file, 7);
 
-    element_info[element].use_gfx_element = getFile8Bit(file);
-    element_info[element].gfx_element =
-      getMappedElement(getFile16BitBE(file));
+    ei->use_gfx_element = getFile8Bit(file);
+    ei->gfx_element = getMappedElement(getFile16BitBE(file));
 
-    element_info[element].collect_score = getFile8Bit(file);
-    element_info[element].collect_count = getFile8Bit(file);
+    ei->collect_score_initial = getFile8Bit(file);
+    ei->collect_count_initial = getFile8Bit(file);
 
-    element_info[element].push_delay_fixed = getFile16BitBE(file);
-    element_info[element].push_delay_random = getFile16BitBE(file);
-    element_info[element].move_delay_fixed = getFile16BitBE(file);
-    element_info[element].move_delay_random = getFile16BitBE(file);
+    ei->push_delay_fixed = getFile16BitBE(file);
+    ei->push_delay_random = getFile16BitBE(file);
+    ei->move_delay_fixed = getFile16BitBE(file);
+    ei->move_delay_random = getFile16BitBE(file);
 
-    element_info[element].move_pattern = getFile16BitBE(file);
-    element_info[element].move_direction_initial = getFile8Bit(file);
-    element_info[element].move_stepsize = getFile8Bit(file);
+    ei->move_pattern = getFile16BitBE(file);
+    ei->move_direction_initial = getFile8Bit(file);
+    ei->move_stepsize = getFile8Bit(file);
 
     for (y = 0; y < 3; y++)
       for (x = 0; x < 3; x++)
-       element_info[element].content[x][y] =
-         getMappedElement(getFile16BitBE(file));
+       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
 
-    element_info[element].change->events = getFile32BitBE(file);
+    event_bits = getFile32BitBE(file);
+    for (j = 0; j < NUM_CHANGE_EVENTS; j++)
+      if (event_bits & (1 << j))
+       ei->change->has_event[j] = TRUE;
 
-    element_info[element].change->target_element =
-      getMappedElement(getFile16BitBE(file));
+    ei->change->target_element = getMappedElement(getFile16BitBE(file));
 
-    element_info[element].change->delay_fixed = getFile16BitBE(file);
-    element_info[element].change->delay_random = getFile16BitBE(file);
-    element_info[element].change->delay_frames = getFile16BitBE(file);
+    ei->change->delay_fixed = getFile16BitBE(file);
+    ei->change->delay_random = getFile16BitBE(file);
+    ei->change->delay_frames = getFile16BitBE(file);
 
-    element_info[element].change->trigger_element =
-      getMappedElement(getFile16BitBE(file));
+    ei->change->trigger_element = getMappedElement(getFile16BitBE(file));
 
-    element_info[element].change->explode = getFile8Bit(file);
-    element_info[element].change->use_target_content = getFile8Bit(file);
-    element_info[element].change->only_if_complete = getFile8Bit(file);
-    element_info[element].change->use_random_replace = getFile8Bit(file);
+    ei->change->explode = getFile8Bit(file);
+    ei->change->use_target_content = getFile8Bit(file);
+    ei->change->only_if_complete = getFile8Bit(file);
+    ei->change->use_random_replace = getFile8Bit(file);
 
-    element_info[element].change->random_percentage = getFile8Bit(file);
-    element_info[element].change->replace_when = getFile8Bit(file);
+    ei->change->random_percentage = getFile8Bit(file);
+    ei->change->replace_when = getFile8Bit(file);
 
     for (y = 0; y < 3; y++)
       for (x = 0; x < 3; x++)
-       element_info[element].change->target_content[x][y] =
+       ei->change->target_content.e[x][y] =
          getMappedElement(getFile16BitBE(file));
 
-    element_info[element].slippery_type = getFile8Bit(file);
+    ei->slippery_type = getFile8Bit(file);
 
     /* some free bytes for future properties and padding */
     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
 
     /* mark that this custom element has been modified */
-    element_info[element].modified_settings = TRUE;
+    ei->modified_settings = TRUE;
   }
 
   return chunk_size;
@@ -1017,7 +1998,9 @@ static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
   struct ElementInfo *ei;
   int chunk_size_expected;
   int element;
-  int i, x, y;
+  int i, j, x, y;
+
+  /* ---------- custom element base property values (96 bytes) ------------- */
 
   element = getFile16BitBE(file);
 
@@ -1035,28 +2018,31 @@ static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
     ei->description[i] = getFile8Bit(file);
   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
 
+#if 1
+  ei->properties[EP_BITFIELD_BASE] = getFile32BitBE(file);
+#else
   Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
+#endif
   ReadUnusedBytesFromFile(file, 4);    /* reserved for more base properties */
 
   ei->num_change_pages = getFile8Bit(file);
 
-  /* some free bytes for future base property values and padding */
-  ReadUnusedBytesFromFile(file, 5);
-
   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
   if (chunk_size_expected != chunk_size)
   {
-    ReadUnusedBytesFromFile(file, chunk_size - 48);
+    ReadUnusedBytesFromFile(file, chunk_size - 43);
     return chunk_size_expected;
   }
 
-  /* read custom property values */
+  ei->ce_value_fixed_initial = getFile16BitBE(file);
+  ei->ce_value_random_initial = getFile16BitBE(file);
+  ei->use_last_ce_value = getFile8Bit(file);
 
   ei->use_gfx_element = getFile8Bit(file);
   ei->gfx_element = getMappedElement(getFile16BitBE(file));
 
-  ei->collect_score = getFile8Bit(file);
-  ei->collect_count = getFile8Bit(file);
+  ei->collect_score_initial = getFile8Bit(file);
+  ei->collect_count_initial = getFile8Bit(file);
 
   ei->drop_delay_fixed = getFile8Bit(file);
   ei->push_delay_fixed = getFile8Bit(file);
@@ -1074,7 +2060,7 @@ static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
 
   for (y = 0; y < 3; y++)
     for (x = 0; x < 3; x++)
-      ei->content[x][y] = getMappedElement(getFile16BitBE(file));
+      ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
 
   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
@@ -1085,115 +2071,469 @@ static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
 
   ei->access_direction = getFile8Bit(file);
 
-  ei->explosion_delay = getFile8Bit(file);
-  ei->ignition_delay = getFile8Bit(file);
-  ei->explosion_type = getFile8Bit(file);
+  ei->explosion_delay = getFile8Bit(file);
+  ei->ignition_delay = getFile8Bit(file);
+  ei->explosion_type = getFile8Bit(file);
+
+  /* some free bytes for future custom property values and padding */
+  ReadUnusedBytesFromFile(file, 1);
+
+  /* ---------- change page property values (48 bytes) --------------------- */
+
+  setElementChangePages(ei, ei->num_change_pages);
+
+  for (i = 0; i < ei->num_change_pages; i++)
+  {
+    struct ElementChangeInfo *change = &ei->change_page[i];
+    unsigned int event_bits;
+
+    /* always start with reliable default values */
+    setElementChangeInfoToDefaults(change);
+
+    /* bits 0 - 31 of "has_event[]" ... */
+    event_bits = getFile32BitBE(file);
+    for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
+      if (event_bits & (1 << j))
+       change->has_event[j] = TRUE;
+
+    change->target_element = getMappedElement(getFile16BitBE(file));
+
+    change->delay_fixed = getFile16BitBE(file);
+    change->delay_random = getFile16BitBE(file);
+    change->delay_frames = getFile16BitBE(file);
+
+    change->trigger_element = getMappedElement(getFile16BitBE(file));
+
+    change->explode = getFile8Bit(file);
+    change->use_target_content = getFile8Bit(file);
+    change->only_if_complete = getFile8Bit(file);
+    change->use_random_replace = getFile8Bit(file);
+
+    change->random_percentage = getFile8Bit(file);
+    change->replace_when = getFile8Bit(file);
+
+    for (y = 0; y < 3; y++)
+      for (x = 0; x < 3; x++)
+       change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
+
+    change->can_change = getFile8Bit(file);
+
+    change->trigger_side = getFile8Bit(file);
+
+    change->trigger_player = getFile8Bit(file);
+    change->trigger_page = getFile8Bit(file);
+
+    change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
+                           CH_PAGE_ANY : (1 << change->trigger_page));
+
+    change->has_action = getFile8Bit(file);
+    change->action_type = getFile8Bit(file);
+    change->action_mode = getFile8Bit(file);
+    change->action_arg = getFile16BitBE(file);
+
+    /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
+    event_bits = getFile8Bit(file);
+    for (j = 32; j < NUM_CHANGE_EVENTS; j++)
+      if (event_bits & (1 << (j - 32)))
+       change->has_event[j] = TRUE;
+  }
+
+  /* mark this custom element as modified */
+  ei->modified_settings = TRUE;
+
+  return chunk_size;
+}
+
+static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
+{
+  struct ElementInfo *ei;
+  struct ElementGroupInfo *group;
+  int element;
+  int i;
+
+  element = getFile16BitBE(file);
+
+  if (!IS_GROUP_ELEMENT(element))
+  {
+    Error(ERR_WARN, "invalid group element number %d", element);
+
+    ReadUnusedBytesFromFile(file, chunk_size - 2);
+    return chunk_size;
+  }
+
+  ei = &element_info[element];
+
+  for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
+    ei->description[i] = getFile8Bit(file);
+  ei->description[MAX_ELEMENT_NAME_LEN] = 0;
+
+  group = element_info[element].group;
+
+  group->num_elements = getFile8Bit(file);
+
+  ei->use_gfx_element = getFile8Bit(file);
+  ei->gfx_element = getMappedElement(getFile16BitBE(file));
+
+  group->choice_mode = getFile8Bit(file);
+
+  /* some free bytes for future values and padding */
+  ReadUnusedBytesFromFile(file, 3);
+
+  for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
+    group->element[i] = getMappedElement(getFile16BitBE(file));
+
+  /* mark this group element as modified */
+  element_info[element].modified_settings = TRUE;
+
+  return chunk_size;
+}
+
+static int LoadLevel_MicroChunk(FILE *file, struct ElementFileConfig *config,
+                               int element)
+{
+  int micro_chunk_size = 0;
+  int conf_type = getFile8Bit(file);
+  int byte_mask = conf_type & CONF_MASK_BYTES;
+  boolean element_found = FALSE;
+  int i;
+
+  micro_chunk_size += 1;
+
+  if (byte_mask == CONF_MASK_MULTI_BYTES)
+  {
+    int num_bytes = getFile16BitBE(file);
+    byte *buffer = checked_malloc(num_bytes);
+
+#if 1
+    printf("::: - found multi bytes\n");
+#endif
+
+    ReadBytesFromFile(file, buffer, num_bytes);
+
+    for (i = 0; config[i].data_type != -1; i++)
+    {
+      if (config[i].element == element &&
+         config[i].conf_type == conf_type)
+      {
+       int data_type = config[i].data_type;
+       int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
+       int max_num_entities = config[i].max_num_entities;
+
+       if (num_entities > max_num_entities)
+       {
+         Error(ERR_WARN,
+               "truncating number of entities for element %d from %d to %d",
+               element, num_entities, max_num_entities);
+
+         num_entities = max_num_entities;
+       }
+
+       *(int *)(config[i].num_entities) = num_entities;
+
+       element_found = TRUE;
+
+       if (data_type == TYPE_ELEMENT_LIST)
+       {
+         int *element_array = (int *)(config[i].value);
+         int j;
+
+         for (j = 0; j < num_entities; j++)
+           element_array[j] =
+             getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
+       }
+       else if (data_type == TYPE_CONTENT_LIST)
+       {
+         struct Content *content= (struct Content *)(config[i].value);
+         int c, x, y;
+
+         for (c = 0; c < num_entities; c++)
+           for (y = 0; y < 3; y++)
+             for (x = 0; x < 3; x++)
+               content[c].e[x][y] =
+                 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
+       }
+       else
+         element_found = FALSE;
+
+       break;
+      }
+    }
+
+    checked_free(buffer);
+
+    micro_chunk_size += 2 + num_bytes;
+  }
+  else         /* constant size configuration data (1, 2 or 4 bytes) */
+  {
+    int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
+                byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
+                byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
+
+#if 1
+    printf("::: - found single bytes\n");
+#endif
+
+    for (i = 0; config[i].data_type != -1; i++)
+    {
+      if (config[i].element == element &&
+         config[i].conf_type == conf_type)
+      {
+       int data_type = config[i].data_type;
+
+       if (data_type == TYPE_BOOLEAN)
+         *(boolean *)(config[i].value) = value;
+       else
+         *(int *)    (config[i].value) = value;
+
+       element_found = TRUE;
+
+       break;
+      }
+    }
+
+    micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
+  }
+
+  if (!element_found)
+    Error(ERR_WARN, "cannot load micro chunk value for element %d", element);
+
+  return micro_chunk_size;
+}
+
+static int LoadLevel_CONF(FILE *file, int chunk_size, struct LevelInfo *level)
+{
+  int real_chunk_size = 0;
+
+#if 1
+  li = *level;         /* copy level data into temporary buffer */
+#endif
+
+  while (!feof(file))
+  {
+    int element = getFile16BitBE(file);
+#if 1
+    real_chunk_size += 2;
+    real_chunk_size += LoadLevel_MicroChunk(file, element_conf, element);
+#else
+    int conf_type = getFile8Bit(file);
+    int byte_mask = conf_type & CONF_MASK_BYTES;
+    boolean element_found = FALSE;
+    int i;
+
+    real_chunk_size += 3;
+
+#if 0
+    li = *level;       /* copy level data into temporary buffer */
+#endif
+
+    if (byte_mask == CONF_MASK_MULTI_BYTES)
+    {
+      int num_bytes = getFile16BitBE(file);
+      byte *buffer = checked_malloc(num_bytes);
+
+      ReadBytesFromFile(file, buffer, num_bytes);
 
-  /* some free bytes for future custom property values and padding */
-  ReadUnusedBytesFromFile(file, 1);
+      for (i = 0; element_conf[i].data_type != -1; i++)
+      {
+       if (element_conf[i].element == element &&
+           element_conf[i].conf_type == conf_type)
+       {
+         int data_type = element_conf[i].data_type;
+         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
+         int max_num_entities = element_conf[i].max_num_entities;
+
+         if (num_entities > max_num_entities)
+         {
+           Error(ERR_WARN,
+                 "truncating number of entities for element %d from %d to %d",
+                 element, num_entities, max_num_entities);
+
+           num_entities = max_num_entities;
+         }
+
+         *(int *)(element_conf[i].num_entities) = num_entities;
+
+         element_found = TRUE;
+
+         if (data_type == TYPE_ELEMENT_LIST)
+         {
+           int *element_array = (int *)(element_conf[i].value);
+           int j;
+
+           for (j = 0; j < num_entities; j++)
+             element_array[j] =
+               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
+         }
+         else if (data_type == TYPE_CONTENT_LIST)
+         {
+           struct Content *content= (struct Content *)(element_conf[i].value);
+           int c, x, y;
+
+           for (c = 0; c < num_entities; c++)
+             for (y = 0; y < 3; y++)
+               for (x = 0; x < 3; x++)
+                 content[c].e[x][y] =
+                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
+         }
+         else
+           element_found = FALSE;
+
+         break;
+       }
+      }
 
-  /* read change property values */
+      checked_free(buffer);
 
-  setElementChangePages(ei, ei->num_change_pages);
+      real_chunk_size += 2 + num_bytes;
+    }
+    else       /* constant size configuration data (1, 2 or 4 bytes) */
+    {
+      int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
+                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
+                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
 
-  for (i = 0; i < ei->num_change_pages; i++)
-  {
-    struct ElementChangeInfo *change = &ei->change_page[i];
+      for (i = 0; element_conf[i].data_type != -1; i++)
+      {
+       if (element_conf[i].element == element &&
+           element_conf[i].conf_type == conf_type)
+       {
+         int data_type = element_conf[i].data_type;
 
-    /* always start with reliable default values */
-    setElementChangeInfoToDefaults(change);
+         if (data_type == TYPE_BOOLEAN)
+           *(boolean *)(element_conf[i].value) = value;
+         else
+           *(int *)    (element_conf[i].value) = value;
 
-    change->events = getFile32BitBE(file);
+         element_found = TRUE;
 
-    change->target_element = getMappedElement(getFile16BitBE(file));
+         break;
+       }
+      }
 
-    change->delay_fixed = getFile16BitBE(file);
-    change->delay_random = getFile16BitBE(file);
-    change->delay_frames = getFile16BitBE(file);
+      real_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
+    }
 
-    change->trigger_element = getMappedElement(getFile16BitBE(file));
+    if (!element_found)
+      Error(ERR_WARN, "cannot load CONF value for element %d", element);
+#endif
 
-    change->explode = getFile8Bit(file);
-    change->use_target_content = getFile8Bit(file);
-    change->only_if_complete = getFile8Bit(file);
-    change->use_random_replace = getFile8Bit(file);
+#if 0
+    *level = li;       /* copy temporary buffer back to level data */
+#endif
 
-    change->random_percentage = getFile8Bit(file);
-    change->replace_when = getFile8Bit(file);
+#if 1
+    if (real_chunk_size >= chunk_size)
+      break;
+#else
+    if (conf_type == CONF_LAST_ENTRY || real_chunk_size >= chunk_size)
+      break;
+#endif
+  }
 
-    for (y = 0; y < 3; y++)
-      for (x = 0; x < 3; x++)
-       change->target_content[x][y] = getMappedElement(getFile16BitBE(file));
+#if 1
+  *level = li;         /* copy temporary buffer back to level data */
+#endif
 
-    change->can_change = getFile8Bit(file);
+  return real_chunk_size;
+}
 
-    change->trigger_side = getFile8Bit(file);
+static int LoadLevel_CUSX(FILE *file, int chunk_size, struct LevelInfo *level)
+{
+  int element = getFile16BitBE(file);
+  int real_chunk_size = 2;
+  struct ElementInfo *ei = &element_info[element];
 
 #if 1
-    change->trigger_player = getFile8Bit(file);
-    change->trigger_page = getFile8Bit(file);
+  printf("::: CUSX: loading element '%s' ...\n", EL_NAME(element));
+#endif
 
-    change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
-                           CH_PAGE_ANY : (1 << change->trigger_page));
+  xx_ei = *ei;         /* copy element data into temporary buffer */
 
-    /* some free bytes for future change property values and padding */
-    ReadUnusedBytesFromFile(file, 6);
+  xx_ei.num_change_pages = -1;
 
-#else
+  while (!feof(file))
+  {
+    real_chunk_size += LoadLevel_MicroChunk(file, custom_element_conf, -1);
 
-    /* some free bytes for future change property values and padding */
-    ReadUnusedBytesFromFile(file, 8);
+#if 1
+    printf("::: - real_chunk_size now %d\n", real_chunk_size);
 #endif
+
+    if (xx_ei.num_change_pages != -1)
+      break;
+
+    if (real_chunk_size >= chunk_size)
+      break;
   }
 
-  /* mark this custom element as modified */
-  ei->modified_settings = TRUE;
+  *ei = xx_ei;
 
-  return chunk_size;
-}
+  if (ei->num_change_pages == -1)
+  {
+    Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
+         EL_NAME(element));
 
-static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
-{
-  struct ElementInfo *ei;
-  struct ElementGroupInfo *group;
-  int element;
-  int i;
+    ei->num_change_pages = 1;
+    setElementChangePages(ei, 1);
 
-  element = getFile16BitBE(file);
+    return real_chunk_size;
+  }
 
-  if (!IS_GROUP_ELEMENT(element))
+  setElementChangePages(ei, ei->num_change_pages);
+
+  xx_current_change_page = 0;
+
+  xx_event_bits_0_31 = 0;
+  xx_event_bits_32_63 = 0;
+
+  while (!feof(file))
   {
-    Error(ERR_WARN, "invalid group element number %d", element);
+    struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
+    int i;
 
-    ReadUnusedBytesFromFile(file, chunk_size - 2);
-    return chunk_size;
-  }
+    xx_change = *change;       /* copy change data into temporary buffer */
 
-  ei = &element_info[element];
+    real_chunk_size += LoadLevel_MicroChunk(file,custom_element_change_conf,-1);
 
-  for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
-    ei->description[i] = getFile8Bit(file);
-  ei->description[MAX_ELEMENT_NAME_LEN] = 0;
+    *change = xx_change;
 
-  group = element_info[element].group;
+    for (i = 0; i < NUM_CHANGE_EVENTS; i++)
+      if ((i <  32 && xx_event_bits_0_31  & (1 << i)) ||
+         (i >= 32 && xx_event_bits_32_63 & (1 << (i - 32))))
+       change->has_event[i] = TRUE;
 
-  group->num_elements = getFile8Bit(file);
+    xx_event_bits_0_31 = 0;
+    xx_event_bits_32_63 = 0;
 
-  ei->use_gfx_element = getFile8Bit(file);
-  ei->gfx_element = getMappedElement(getFile16BitBE(file));
+    if (real_chunk_size >= chunk_size)
+      break;
+  }
 
-  group->choice_mode = getFile8Bit(file);
+  return real_chunk_size;
+}
 
-  /* some free bytes for future values and padding */
-  ReadUnusedBytesFromFile(file, 3);
+static int LoadLevel_GRPX(FILE *file, int chunk_size, struct LevelInfo *level)
+{
+  int element = getFile16BitBE(file);
+  int real_chunk_size = 2;
+  struct ElementInfo *ei = &element_info[element];
+  struct ElementGroupInfo *group = ei->group;
 
-  for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
-    group->element[i] = getMappedElement(getFile16BitBE(file));
+  xx_ei = *ei;         /* copy element data into temporary buffer */
+  xx_group = *group;   /* copy group data into temporary buffer */
 
-  /* mark this group element as modified */
-  element_info[element].modified_settings = TRUE;
+  while (!feof(file))
+  {
+    real_chunk_size += LoadLevel_MicroChunk(file, group_element_conf, -1);
 
-  return chunk_size;
+    if (real_chunk_size >= chunk_size)
+      break;
+  }
+
+  *ei = xx_ei;
+  *group = xx_group;
+
+  return real_chunk_size;
 }
 
 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
@@ -1216,12 +2556,12 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
   }
 
   getFileChunkBE(file, chunk_name, NULL);
-  if (strcmp(chunk_name, "RND1") == 0)
+  if (strEqual(chunk_name, "RND1"))
   {
     getFile32BitBE(file);              /* not used */
 
     getFileChunkBE(file, chunk_name, NULL);
-    if (strcmp(chunk_name, "CAVE") != 0)
+    if (!strEqual(chunk_name, "CAVE"))
     {
       level->no_valid_file = TRUE;
 
@@ -1287,6 +2627,12 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
       { "CUS3", -1,                    LoadLevel_CUS3 },
       { "CUS4", -1,                    LoadLevel_CUS4 },
       { "GRP1", -1,                    LoadLevel_GRP1 },
+      { "CONF", -1,                    LoadLevel_CONF },
+#if 1
+      { "CUSX", -1,                    LoadLevel_CUSX },
+      { "GRPX", -1,                    LoadLevel_GRPX },
+#endif
+
       {  NULL,  0,                     NULL }
     };
 
@@ -1295,7 +2641,7 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
       int i = 0;
 
       while (chunk_info[i].name != NULL &&
-            strcmp(chunk_name, chunk_info[i].name) != 0)
+            !strEqual(chunk_name, chunk_info[i].name))
        i++;
 
       if (chunk_info[i].name == NULL)
@@ -1336,6 +2682,8 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
 /* functions for loading EM level                                            */
 /* ------------------------------------------------------------------------- */
 
+#if 0
+
 static int map_em_element_yam(int element)
 {
   switch (element)
@@ -1648,115 +2996,533 @@ static int map_em_element_field(int element)
 #define EM_LEVEL_XSIZE                 64
 #define EM_LEVEL_YSIZE                 32
 
-static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
-                                    struct LevelFileInfo *level_file_info)
+static void OLD_LoadLevelFromFileInfo_EM(struct LevelInfo *level,
+                                        struct LevelFileInfo *level_file_info)
 {
   char *filename = level_file_info->filename;
   FILE *file;
   unsigned char leveldata[EM_LEVEL_SIZE];
   unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
-  unsigned char code0 = 0x65;
-  unsigned char code1 = 0x11;
-  boolean level_is_crypted = FALSE;
   int nr = level_file_info->nr;
   int i, x, y;
 
-  if (!(file = fopen(filename, MODE_READ)))
+  if (!(file = fopen(filename, MODE_READ)))
+  {
+    level->no_valid_file = TRUE;
+
+    Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
+
+    return;
+  }
+
+  for (i = 0; i < EM_LEVEL_SIZE; i++)
+    leveldata[i] = fgetc(file);
+
+  fclose(file);
+
+  /* check if level data is crypted by testing against known starting bytes
+     of the few existing crypted level files (from Emerald Mine 1 + 2) */
+
+  if ((leveldata[0] == 0xf1 ||
+       leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
+  {
+    unsigned char code0 = 0x65;
+    unsigned char code1 = 0x11;
+
+    if (leveldata[0] == 0xf5)  /* error in crypted Emerald Mine 2 levels */
+      leveldata[0] = 0xf1;
+
+    /* decode crypted level data */
+
+    for (i = 0; i < EM_LEVEL_SIZE; i++)
+    {
+      leveldata[i] ^= code0;
+      leveldata[i] -= code1;
+
+      code0 = (code0 + 7) & 0xff;
+    }
+  }
+
+  level->fieldx        = EM_LEVEL_XSIZE;
+  level->fieldy        = EM_LEVEL_YSIZE;
+
+  level->time          = header[46] * 10;
+  level->gems_needed   = header[47];
+
+  /* The original Emerald Mine levels have their level number stored
+     at the second byte of the level file...
+     Do not trust this information at other level files, e.g. EMC,
+     but correct it anyway (normally the first row is completely
+     steel wall, so the correction does not hurt anyway). */
+
+  if (leveldata[1] == nr)
+    leveldata[1] = leveldata[2];       /* correct level number field */
+
+  sprintf(level->name, "Level %d", nr);                /* set level name */
+
+  level->score[SC_EMERALD]     = header[36];
+  level->score[SC_DIAMOND]     = header[37];
+  level->score[SC_ROBOT]       = header[38];
+  level->score[SC_SPACESHIP]   = header[39];
+  level->score[SC_BUG]         = header[40];
+  level->score[SC_YAMYAM]      = header[41];
+  level->score[SC_NUT]         = header[42];
+  level->score[SC_DYNAMITE]    = header[43];
+  level->score[SC_TIME_BONUS]  = header[44];
+
+  level->num_yamyam_contents = 4;
+
+  for (i = 0; i < level->num_yamyam_contents; i++)
+    for (y = 0; y < 3; y++)
+      for (x = 0; x < 3; x++)
+       level->yamyam_content[i].e[x][y] =
+         map_em_element_yam(header[i * 9 + y * 3 + x]);
+
+  level->amoeba_speed          = (header[52] * 256 + header[53]) % 256;
+  level->time_magic_wall       = (header[54] * 256 + header[55]) * 16 / 100;
+  level->time_wheel            = (header[56] * 256 + header[57]) * 16 / 100;
+  level->amoeba_content                = EL_DIAMOND;
+
+  for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
+  {
+    int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
+
+    if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
+      new_element = EL_AMOEBA_WET;
+
+    level->field[x][y] = new_element;
+  }
+
+  x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
+  y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
+  level->field[x][y] = EL_PLAYER_1;
+
+  x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
+  y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
+  level->field[x][y] = EL_PLAYER_2;
+}
+
+#endif
+
+void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
+{
+  static int ball_xy[8][2] =
+  {
+    { 0, 0 },
+    { 1, 0 },
+    { 2, 0 },
+    { 0, 1 },
+    { 2, 1 },
+    { 0, 2 },
+    { 1, 2 },
+    { 2, 2 },
+  };
+  struct LevelInfo_EM *level_em = level->native_em_level;
+  struct LEVEL *lev = level_em->lev;
+  struct PLAYER **ply = level_em->ply;
+  int i, j, x, y;
+
+#if 0
+  printf("::: A\n");
+  for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+    for (j = 0; j < 8; j++)
+      printf("::: ball %d, %d: %d\n", i, j,
+            level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
+#endif
+
+  lev->width  = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
+  lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
+
+  lev->time_seconds     = level->time;
+  lev->required_initial = level->gems_needed;
+
+  lev->emerald_score   = level->score[SC_EMERALD];
+  lev->diamond_score   = level->score[SC_DIAMOND];
+  lev->alien_score     = level->score[SC_ROBOT];
+  lev->tank_score      = level->score[SC_SPACESHIP];
+  lev->bug_score       = level->score[SC_BUG];
+  lev->eater_score     = level->score[SC_YAMYAM];
+  lev->nut_score       = level->score[SC_NUT];
+  lev->dynamite_score  = level->score[SC_DYNAMITE];
+  lev->key_score       = level->score[SC_KEY];
+  lev->exit_score      = level->score[SC_TIME_BONUS];
+
+  for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+    for (y = 0; y < 3; y++)
+      for (x = 0; x < 3; x++)
+       lev->eater_array[i][y * 3 + x] =
+         map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
+
+  lev->amoeba_time             = level->amoeba_speed;
+  lev->wonderwall_time_initial = level->time_magic_wall;
+  lev->wheel_time              = level->time_wheel;
+
+  lev->android_move_time       = level->android_move_time;
+  lev->android_clone_time      = level->android_clone_time;
+  lev->ball_random             = level->ball_random;
+  lev->ball_state_initial      = level->ball_state_initial;
+  lev->ball_time               = level->ball_time;
+  lev->num_ball_arrays         = level->num_ball_contents;
+
+  lev->lenses_score            = level->lenses_score;
+  lev->magnify_score           = level->magnify_score;
+  lev->slurp_score             = level->slurp_score;
+
+  lev->lenses_time             = level->lenses_time;
+  lev->magnify_time            = level->magnify_time;
+
+  lev->wind_direction_initial =
+    map_direction_RND_to_EM(level->wind_direction_initial);
+  lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
+                          lev->wind_time : 0);
+
+  for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+    for (j = 0; j < 8; j++)
+      lev->ball_array[i][j] =
+       map_element_RND_to_EM(level->
+                             ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
+
+#if 0
+  for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+    for (j = 0; j < 8; j++)
+      printf("::: ball %d, %d: %d\n", i, j,
+            level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
+#endif
+
+  map_android_clone_elements_RND_to_EM(level);
+
+#if 0
+  for (i = 0; i < 16; i++)
+    lev->android_array[i] = FALSE;     /* !!! YET TO COME !!! */
+#endif
+
+  /* first fill the complete playfield with the default border element */
+  for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
+    for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
+      level_em->cave[x][y] = ZBORDER;
+
+#if 1
+
+#if 0
+#if 1
+  LoadLevel_InitPlayfield();
+#else
+  lev_fieldx = lev->width;     /* !!! also in LoadLevel_InitPlayfield() !!! */
+  lev_fieldy = lev->height;    /* !!! also in LoadLevel_InitPlayfield() !!! */
+  SetBorderElement();          /* !!! also in LoadLevel_InitPlayfield() !!! */
+#endif
+#endif
+
+#if 0
+  printf("::: BorderElement == %d\n", BorderElement);
+#endif
+
+  if (BorderElement == EL_STEELWALL)
+  {
+    for (y = 0; y < lev->height + 2; y++)
+      for (x = 0; x < lev->width + 2; x++)
+       level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
+  }
+
+  /* then copy the real level contents from level file into the playfield */
+  for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
   {
-    level->no_valid_file = TRUE;
+    int new_element = map_element_RND_to_EM(level->field[x][y]);
+    int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
+    int xx = x + 1 + offset;
+    int yy = y + 1 + offset;
 
-    Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
+    if (level->field[x][y] == EL_AMOEBA_DEAD)
+      new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
 
-    return;
+    level_em->cave[xx][yy] = new_element;
   }
 
-  for(i = 0; i < EM_LEVEL_SIZE; i++)
-    leveldata[i] = fgetc(file);
+#else
 
-  fclose(file);
+  /* then copy the real level contents from level file into the playfield */
+  for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
+  {
+    int new_element = map_element_RND_to_EM(level->field[x][y]);
 
-  /* check if level data is crypted by testing against known starting bytes
-     of the few existing crypted level files (from Emerald Mine 1 + 2) */
+    if (level->field[x][y] == EL_AMOEBA_DEAD)
+      new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
 
-  if ((leveldata[0] == 0xf1 ||
-       leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
-  {
-    level_is_crypted = TRUE;
+    level_em->cave[x + 1][y + 1] = new_element;
+  }
 
-    if (leveldata[0] == 0xf5)  /* error in crypted Emerald Mine 2 levels */
-      leveldata[0] = 0xf1;
+#endif
+
+#if 1
+
+  for (i = 0; i < MAX_PLAYERS; i++)
+  {
+    ply[i]->x_initial = 0;
+    ply[i]->y_initial = 0;
   }
 
-  if (level_is_crypted)                /* decode crypted level data */
+#else
+
+  ply1->x_initial = 0;
+  ply1->y_initial = 0;
+
+  ply2->x_initial = 0;
+  ply2->y_initial = 0;
+
+#endif
+
+  /* initialize player positions and delete players from the playfield */
+  for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
   {
-    for(i = 0; i < EM_LEVEL_SIZE; i++)
+
+#if 1
+    if (ELEM_IS_PLAYER(level->field[x][y]))
     {
-      leveldata[i] ^= code0;
-      leveldata[i] -= code1;
+      int player_nr = GET_PLAYER_NR(level->field[x][y]);
+      int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
+      int xx = x + 1 + offset;
+      int yy = y + 1 + offset;
+
+      ply[player_nr]->x_initial = xx;
+      ply[player_nr]->y_initial = yy;
+
+      level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
+    }
 
-      code0  = (code0 + 7) & 0xff;
+#else
+
+#if 1
+    /* !!! CURRENTLY ONLY SUPPORT FOR ONE PLAYER !!! */
+    if (ELEM_IS_PLAYER(level->field[x][y]))
+    {
+      ply1->x_initial = x + 1;
+      ply1->y_initial = y + 1;
+      level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_EMPTY);
     }
+#else
+    /* !!! ADD SUPPORT FOR MORE THAN ONE PLAYER !!! */
+    if (level->field[x][y] == EL_PLAYER_1)
+    {
+      ply1->x_initial = x + 1;
+      ply1->y_initial = y + 1;
+      level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_EMPTY);
+    }
+    else if (level->field[x][y] == EL_PLAYER_2)
+    {
+      ply2->x_initial = x + 1;
+      ply2->y_initial = y + 1;
+      level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_EMPTY);
+    }
+#endif
+
+#endif
+
   }
 
-  level->fieldx        = EM_LEVEL_XSIZE;
-  level->fieldy        = EM_LEVEL_YSIZE;
+  if (BorderElement == EL_STEELWALL)
+  {
+#if 1
+    lev->width  += 2;
+    lev->height += 2;
+#endif
+  }
+}
 
-  level->time          = header[46] * 10;
-  level->gems_needed   = header[47];
+void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
+{
+  static int ball_xy[8][2] =
+  {
+    { 0, 0 },
+    { 1, 0 },
+    { 2, 0 },
+    { 0, 1 },
+    { 2, 1 },
+    { 0, 2 },
+    { 1, 2 },
+    { 2, 2 },
+  };
+  struct LevelInfo_EM *level_em = level->native_em_level;
+  struct LEVEL *lev = level_em->lev;
+  struct PLAYER **ply = level_em->ply;
+  int i, j, x, y;
 
-  /* The original Emerald Mine levels have their level number stored
-     at the second byte of the level file...
-     Do not trust this information at other level files, e.g. EMC,
-     but correct it anyway (normally the first row is completely
-     steel wall, so the correction does not hurt anyway). */
+  level->fieldx = MIN(lev->width,  MAX_LEV_FIELDX);
+  level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
 
-  if (leveldata[1] == nr)
-    leveldata[1] = leveldata[2];       /* correct level number field */
+  level->time        = lev->time_seconds;
+  level->gems_needed = lev->required_initial;
 
-  sprintf(level->name, "Level %d", nr);                /* set level name */
+  sprintf(level->name, "Level %d", level->file_info.nr);
 
-  level->score[SC_EMERALD]     = header[36];
-  level->score[SC_DIAMOND]     = header[37];
-  level->score[SC_ROBOT]       = header[38];
-  level->score[SC_SPACESHIP]   = header[39];
-  level->score[SC_BUG]         = header[40];
-  level->score[SC_YAMYAM]      = header[41];
-  level->score[SC_NUT]         = header[42];
-  level->score[SC_DYNAMITE]    = header[43];
-  level->score[SC_TIME_BONUS]  = header[44];
+  level->score[SC_EMERALD]     = lev->emerald_score;
+  level->score[SC_DIAMOND]     = lev->diamond_score;
+  level->score[SC_ROBOT]       = lev->alien_score;
+  level->score[SC_SPACESHIP]   = lev->tank_score;
+  level->score[SC_BUG]         = lev->bug_score;
+  level->score[SC_YAMYAM]      = lev->eater_score;
+  level->score[SC_NUT]         = lev->nut_score;
+  level->score[SC_DYNAMITE]    = lev->dynamite_score;
+  level->score[SC_KEY]         = lev->key_score;
+  level->score[SC_TIME_BONUS]  = lev->exit_score;
 
-  level->num_yamyam_contents = 4;
+  level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
 
-  for(i = 0; i < level->num_yamyam_contents; i++)
-    for(y = 0; y < 3; y++)
-      for(x = 0; x < 3; x++)
-       level->yamyam_content[i][x][y] =
-         map_em_element_yam(header[i * 9 + y * 3 + x]);
+  for (i = 0; i < level->num_yamyam_contents; i++)
+    for (y = 0; y < 3; y++)
+      for (x = 0; x < 3; x++)
+       level->yamyam_content[i].e[x][y] =
+         map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
 
-  level->amoeba_speed          = (header[52] * 256 + header[53]) % 256;
-  level->time_magic_wall       = (header[54] * 256 + header[55]) * 16 / 100;
-  level->time_wheel            = (header[56] * 256 + header[57]) * 16 / 100;
-  level->amoeba_content                = EL_DIAMOND;
+  level->amoeba_speed          = lev->amoeba_time;
+  level->time_magic_wall       = lev->wonderwall_time_initial;
+  level->time_wheel            = lev->wheel_time;
+
+  level->android_move_time     = lev->android_move_time;
+  level->android_clone_time    = lev->android_clone_time;
+  level->ball_random           = lev->ball_random;
+  level->ball_state_initial    = lev->ball_state_initial;
+  level->ball_time             = lev->ball_time;
+  level->num_ball_contents     = lev->num_ball_arrays;
+
+  level->lenses_score          = lev->lenses_score;
+  level->magnify_score         = lev->magnify_score;
+  level->slurp_score           = lev->slurp_score;
+
+  level->lenses_time           = lev->lenses_time;
+  level->magnify_time          = lev->magnify_time;
+
+  level->wind_direction_initial =
+    map_direction_EM_to_RND(lev->wind_direction_initial);
+
+#if 0
+  printf("::: foo\n");
+  for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+    for (j = 0; j < 8; j++)
+      printf("::: ball %d, %d: %d\n", i, j,
+            level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
+#endif
 
+  for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+    for (j = 0; j < 8; j++)
+      level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
+       map_element_EM_to_RND(lev->ball_array[i][j]);
+
+#if 0
+  printf("::: bar\n");
+  for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+    for (j = 0; j < 8; j++)
+      printf("::: ball %d, %d: %d\n", i, j,
+            level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
+#endif
+
+  map_android_clone_elements_EM_to_RND(level);
+
+#if 0
+  for (i = 0; i < 16; i++)
+    level->android_array[i] = FALSE;   /* !!! YET TO COME !!! */
+#endif
+
+  /* convert the playfield (some elements need special treatment) */
   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
   {
-    int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
+    int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
 
-    if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
-      new_element = EL_AMOEBA_WET;
+    if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
+      new_element = EL_AMOEBA_DEAD;
 
     level->field[x][y] = new_element;
   }
 
-  x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
-  y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
-  level->field[x][y] = EL_PLAYER_1;
+#if 0
+  printf("::: bar 0\n");
+  for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+    for (j = 0; j < 8; j++)
+      printf("::: ball %d, %d: %d\n", i, j,
+            level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
+#endif
 
-  x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
-  y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
-  level->field[x][y] = EL_PLAYER_2;
+#if 1
+
+  for (i = 0; i < MAX_PLAYERS; i++)
+  {
+    /* in case of all players set to the same field, use the first player */
+    int nr = MAX_PLAYERS - i - 1;
+    int jx = ply[nr]->x_initial - 1;
+    int jy = ply[nr]->y_initial - 1;
+
+#if 0
+    printf("::: player %d: %d, %d\n", nr, jx, jy);
+#endif
+
+    if (jx != -1 && jy != -1)
+      level->field[jx][jy] = EL_PLAYER_1 + nr;
+  }
+
+#else
+
+  /* in case of both players set to the same field, use the first player */
+  level->field[ply2->x_initial - 1][ply2->y_initial - 1] = EL_PLAYER_2;
+  level->field[ply1->x_initial - 1][ply1->y_initial - 1] = EL_PLAYER_1;
+
+#endif
+
+#if 0
+  printf("::: native Emerald Mine file version: %d\n", level_em->file_version);
+#endif
+
+#if 0
+  printf("::: bar 2\n");
+  for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+    for (j = 0; j < 8; j++)
+      printf("::: ball %d, %d: %d\n", i, j,
+            level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
+#endif
+}
+
+static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
+                                    struct LevelFileInfo *level_file_info)
+{
+  if (!LoadNativeLevel_EM(level_file_info->filename))
+    level->no_valid_file = TRUE;
+}
+
+void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
+{
+  if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
+    CopyNativeLevel_RND_to_EM(level);
+}
+
+void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
+{
+
+#if 0
+  {
+    static int ball_xy[8][2] =
+      {
+       { 0, 0 },
+       { 1, 0 },
+       { 2, 0 },
+       { 0, 1 },
+       { 2, 1 },
+       { 0, 2 },
+       { 1, 2 },
+       { 2, 2 },
+      };
+    int i, j;
+
+    printf("::: A6\n");
+    for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+      for (j = 0; j < 8; j++)
+       printf("::: ball %d, %d: %d\n", i, j,
+              level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
+  }
+#endif
+
+  if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
+    CopyNativeLevel_EM_to_RND(level);
 }
 
+
 /* ------------------------------------------------------------------------- */
 /* functions for loading SP level                                            */
 /* ------------------------------------------------------------------------- */
@@ -1770,8 +3536,12 @@ static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
                                       int nr)
 {
+  int num_special_ports;
   int i, x, y;
 
+  /* for details of the Supaplex level format, see Herman Perk's Supaplex
+     documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */
+
   /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
   for (y = 0; y < SP_LEVEL_YSIZE; y++)
   {
@@ -1796,28 +3566,104 @@ static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
     }
   }
 
-  ReadUnusedBytesFromFile(file, 4);
+  ReadUnusedBytesFromFile(file, 4);    /* (not used by Supaplex engine) */
 
-  /* Initial gravitation: 1 == "on", anything else (0) == "off" */
+  /* initial gravity: 1 == "on", anything else (0) == "off" */
   level->initial_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
 
-  ReadUnusedBytesFromFile(file, 1);
+  ReadUnusedBytesFromFile(file, 1);    /* (not used by Supaplex engine) */
 
   /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
     level->name[i] = fgetc(file);
   level->name[SP_LEVEL_NAME_LEN] = '\0';
 
-  /* initial "freeze zonks": 2 == "on", anything else (0) == "off" */
-  ReadUnusedBytesFromFile(file, 1);    /* !!! NOT SUPPORTED YET !!! */
+  /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */
+  ReadUnusedBytesFromFile(file, 1);    /* (not used by R'n'D engine) */
 
   /* number of infotrons needed; 0 means that Supaplex will count the total
-     amount of infotrons in the level and use the low byte of that number.
+     amount of infotrons in the level and use the low byte of that number
      (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
   level->gems_needed = fgetc(file);
 
-  /* information about special gravity port entries */
-  ReadUnusedBytesFromFile(file, 65);   /* !!! NOT SUPPORTED YET !!! */
+  /* number of special ("gravity") port entries below (maximum 10 allowed) */
+  num_special_ports = fgetc(file);
+
+  /* database of properties of up to 10 special ports (6 bytes per port) */
+  for (i = 0; i < 10; i++)
+  {
+    int port_location, port_x, port_y, port_element;
+    int gravity;
+
+    /* high and low byte of the location of a special port; if (x, y) are the
+       coordinates of a port in the field and (0, 0) is the top-left corner,
+       the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice
+       of what may be expected: Supaplex works with a game field in memory
+       which is 2 bytes per tile) */
+    port_location = getFile16BitBE(file);
+
+    /* change gravity: 1 == "turn on", anything else (0) == "turn off" */
+    gravity = fgetc(file);
+
+    /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */
+    ReadUnusedBytesFromFile(file, 1);  /* (not used by R'n'D engine) */
+
+    /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */
+    ReadUnusedBytesFromFile(file, 1);  /* (not used by R'n'D engine) */
+
+    ReadUnusedBytesFromFile(file, 1);  /* (not used by Supaplex engine) */
+
+    if (i >= num_special_ports)
+      continue;
+
+    port_x = (port_location / 2) % SP_LEVEL_XSIZE;
+    port_y = (port_location / 2) / SP_LEVEL_XSIZE;
+
+    if (port_x < 0 || port_x >= SP_LEVEL_XSIZE ||
+       port_y < 0 || port_y >= SP_LEVEL_YSIZE)
+    {
+      Error(ERR_WARN, "special port position (%d, %d) out of bounds",
+           port_x, port_y);
+
+      continue;
+    }
+
+    port_element = level->field[port_x][port_y];
+
+    if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
+       port_element > EL_SP_GRAVITY_PORT_UP)
+    {
+      Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
+
+      continue;
+    }
+
+    /* change previous (wrong) gravity inverting special port to either
+       gravity enabling special port or gravity disabling special port */
+    level->field[port_x][port_y] +=
+      (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
+       EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
+  }
+
+  ReadUnusedBytesFromFile(file, 4);    /* (not used by Supaplex engine) */
+
+  /* change special gravity ports without database entries to normal ports */
+  for (y = 0; y < SP_LEVEL_YSIZE; y++)
+    for (x = 0; x < SP_LEVEL_XSIZE; x++)
+      if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
+         level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
+       level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
+
+  /* auto-determine number of infotrons if it was stored as "0" -- see above */
+  if (level->gems_needed == 0)
+  {
+    for (y = 0; y < SP_LEVEL_YSIZE; y++)
+      for (x = 0; x < SP_LEVEL_XSIZE; x++)
+       if (level->field[x][y] == EL_SP_INFOTRON)
+         level->gems_needed++;
+
+    level->gems_needed &= 0xff;                /* only use low byte -- see above */
+  }
 
   level->fieldx = SP_LEVEL_XSIZE;
   level->fieldy = SP_LEVEL_YSIZE;
@@ -1828,14 +3674,18 @@ static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
   level->time_wheel = 0;
   level->amoeba_content = EL_EMPTY;
 
-  for(i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
+#if 1
+  /* original Supaplex does not use score values -- use default values */
+#else
+  for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
     level->score[i] = 0;               /* !!! CORRECT THIS !!! */
+#endif
 
   /* there are no yamyams in supaplex levels */
-  for(i = 0; i < level->num_yamyam_contents; i++)
-    for(y = 0; y < 3; y++)
-      for(x = 0; x < 3; x++)
-       level->yamyam_content[i][x][y] = EL_EMPTY;
+  for (i = 0; i < level->num_yamyam_contents; i++)
+    for (y = 0; y < 3; y++)
+      for (x = 0; x < 3; x++)
+       level->yamyam_content[i].e[x][y] = EL_EMPTY;
 }
 
 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
@@ -1932,7 +3782,7 @@ static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
 
     if (reading_multipart_level &&
        (!is_multipart_level ||
-        strcmp(level->name, multipart_level.name) != 0))
+        !strEqual(level->name, multipart_level.name)))
     {
       /* we are already reading parts of a multi-part level, but this level is
         either not a multi-part level, or a part of a different multi-part
@@ -2035,6 +3885,7 @@ void LoadLevelFromFileInfo(struct LevelInfo *level,
 
     case LEVEL_FILE_TYPE_EM:
       LoadLevelFromFileInfo_EM(level, level_file_info);
+      level->game_engine_type = GAME_ENGINE_TYPE_EM;
       break;
 
     case LEVEL_FILE_TYPE_SP:
@@ -2045,6 +3896,23 @@ void LoadLevelFromFileInfo(struct LevelInfo *level,
       LoadLevelFromFileInfo_RND(level, level_file_info);
       break;
   }
+
+  /* if level file is invalid, restore level structure to default values */
+  if (level->no_valid_file)
+    setLevelInfoToDefaults(level);
+
+  if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
+    level->game_engine_type = GAME_ENGINE_TYPE_RND;
+
+#if 1
+  if (level_file_info->type != LEVEL_FILE_TYPE_RND)
+    CopyNativeLevel_Native_to_RND(level);
+#else
+  if (level_file_info->type == LEVEL_FILE_TYPE_RND)
+    CopyNativeLevel_RND_to_Native(level);
+  else
+    CopyNativeLevel_Native_to_RND(level);
+#endif
 }
 
 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
@@ -2061,106 +3929,28 @@ void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
   LoadLevelFromFileInfo(level, &level_file_info);
 }
 
-static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
-{
-  if (leveldir_current == NULL)                /* only when dumping level */
-    return;
-
-#if 0
-  printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
-#endif
-
-  /* determine correct game engine version of current level */
-#if 1
-  if (!leveldir_current->latest_engine)
-#else
-  if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
-      IS_LEVELCLASS_PRIVATE(leveldir_current) ||
-      IS_LEVELCLASS_UNDEFINED(leveldir_current))
-#endif
-  {
-#if 0
-    printf("\n::: This level is private or contributed: '%s'\n", filename);
-#endif
-
-#if 0
-    printf("\n::: Use the stored game engine version for this level\n");
-#endif
-
-    /* For all levels which are not forced to use the latest game engine
-       version (normally user contributed, private and undefined levels),
-       use the version of the game engine the levels were created for.
-
-       Since 2.0.1, the game engine version is now directly stored
-       in the level file (chunk "VERS"), so there is no need anymore
-       to set the game version from the file version (except for old,
-       pre-2.0 levels, where the game version is still taken from the
-       file format version used to store the level -- see above). */
-
-#if 1
-    /* player was faster than enemies in 1.0.0 and before */
-    if (level->file_version == FILE_VERSION_1_0)
-      level->double_speed = TRUE;
-#else
-    /* do some special adjustments to support older level versions */
-    if (level->file_version == FILE_VERSION_1_0)
-    {
-      Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
-      Error(ERR_WARN, "using high speed movement for player");
-
-      /* player was faster than monsters in (pre-)1.0 levels */
-      level->double_speed = TRUE;
-    }
-#endif
-
-    /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
-    if (level->game_version == VERSION_IDENT(2,0,1,0))
-      level->em_slippery_gems = TRUE;
-
-    if (level->game_version < VERSION_IDENT(2,2,0,0))
-      level->use_spring_bug = TRUE;
-
-    if (level->game_version < VERSION_IDENT(3,1,0,0))
-    {
-      int i, j;
-
-      level->can_move_into_acid_bits = 0; /* nothing can move into acid */
-      level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
-
-      setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
-      setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
-      setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
-      setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
-
-      for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
-       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
-
-      for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
-      {
-       int element = EL_CUSTOM_START + i;
-       struct ElementInfo *ei = &element_info[element];
-
-       for (j = 0; j < ei->num_change_pages; j++)
-       {
-         struct ElementChangeInfo *change = &ei->change_page[j];
-
-         change->trigger_player = CH_PLAYER_ANY;
-         change->trigger_page = CH_PAGE_ANY;
-       }
-      }
-    }
-  }
-  else
+static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
+{
+  if (leveldir_current == NULL)                /* only when dumping level */
+    return;
+
+  /* all engine modifications also valid for levels which use latest engine */
+#if 1
+  if (level->game_version < VERSION_IDENT(3,2,0,5))
   {
-#if 0
-    printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
-          leveldir_current->sort_priority, filename);
+    /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
+    level->score[SC_TIME_BONUS] /= 10;
+  }
 #endif
 
 #if 0
-    printf("\n::: Use latest game engine version for this level.\n");
+  leveldir_current->latest_engine = TRUE;      /* !!! TEST ONLY !!! */
 #endif
 
+  if (leveldir_current->latest_engine)
+  {
+    /* ---------- use latest game engine ----------------------------------- */
+
     /* For all levels which are forced to use the latest game engine version
        (normally all but user contributed, private and undefined levels), set
        the game engine version to the actual version; this allows for actual
@@ -2169,11 +3959,6 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
        of the corresponding game more accurate, while (hopefully) not breaking
        existing levels created from other players. */
 
-#if 0
-    printf("::: changing engine from %d to %d\n",
-          level->game_version, GAME_VERSION_ACTUAL);
-#endif
-
     level->game_version = GAME_VERSION_ACTUAL;
 
     /* Set special EM style gems behaviour: EM style gems slip down from
@@ -2185,11 +3970,92 @@ static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
 
     if (level->file_version < FILE_VERSION_2_0)
       level->em_slippery_gems = TRUE;
+
+    return;
   }
 
+  /* ---------- use game engine the level was created with ----------------- */
+
+  /* For all levels which are not forced to use the latest game engine
+     version (normally user contributed, private and undefined levels),
+     use the version of the game engine the levels were created for.
+
+     Since 2.0.1, the game engine version is now directly stored
+     in the level file (chunk "VERS"), so there is no need anymore
+     to set the game version from the file version (except for old,
+     pre-2.0 levels, where the game version is still taken from the
+     file format version used to store the level -- see above). */
+
+  /* player was faster than enemies in 1.0.0 and before */
+  if (level->file_version == FILE_VERSION_1_0)
+    level->initial_player_stepsize = STEPSIZE_FAST;
+
+  /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
+  if (level->game_version == VERSION_IDENT(2,0,1,0))
+    level->em_slippery_gems = TRUE;
+
+  /* springs could be pushed over pits before (pre-release version) 2.2.0 */
+  if (level->game_version < VERSION_IDENT(2,2,0,0))
+    level->use_spring_bug = TRUE;
+
+  if (level->game_version < VERSION_IDENT(3,2,0,5))
+  {
+    /* time orb caused limited time in endless time levels before 3.2.0-5 */
+    level->use_time_orb_bug = TRUE;
+
+    /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
+    level->block_snap_field = FALSE;
+
+    /* extra time score was same value as time left score before 3.2.0-5 */
+    level->extra_time_score = level->score[SC_TIME_BONUS];
+
 #if 0
-  printf("::: => %d\n", level->game_version);
+    /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
+    level->score[SC_TIME_BONUS] /= 10;
 #endif
+  }
+
+  if (level->game_version < VERSION_IDENT(3,2,0,7))
+  {
+    /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
+    level->continuous_snapping = FALSE;
+  }
+
+  /* only few elements were able to actively move into acid before 3.1.0 */
+  /* trigger settings did not exist before 3.1.0; set to default "any" */
+  if (level->game_version < VERSION_IDENT(3,1,0,0))
+  {
+    int i, j;
+
+    /* correct "can move into acid" settings (all zero in old levels) */
+
+    level->can_move_into_acid_bits = 0; /* nothing can move into acid */
+    level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
+
+    setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
+    setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
+    setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
+    setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
+
+    for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+      SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
+
+    /* correct trigger settings (stored as zero == "none" in old levels) */
+
+    for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+    {
+      int element = EL_CUSTOM_START + i;
+      struct ElementInfo *ei = &element_info[element];
+
+      for (j = 0; j < ei->num_change_pages; j++)
+      {
+       struct ElementChangeInfo *change = &ei->change_page[j];
+
+       change->trigger_player = CH_PLAYER_ANY;
+       change->trigger_page = CH_PAGE_ANY;
+      }
+    }
+  }
 }
 
 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
@@ -2197,7 +4063,8 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
   int i, j, x, y;
 
   /* map custom element change events that have changed in newer versions
-     (these following values were accidentally changed in version 3.0.1) */
+     (these following values were accidentally changed in version 3.0.1)
+     (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
   if (level->game_version <= VERSION_IDENT(3,0,0,0))
   {
     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
@@ -2205,7 +4072,8 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
       int element = EL_CUSTOM_START + i;
 
       /* order of checking and copying events to be mapped is important */
-      for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
+      /* (do not change the start and end value -- they are constant) */
+      for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
       {
        if (HAS_CHANGE_EVENT(element, j - 2))
        {
@@ -2215,7 +4083,8 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
       }
 
       /* order of checking and copying events to be mapped is important */
-      for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--)
+      /* (do not change the start and end value -- they are constant) */
+      for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
       {
        if (HAS_CHANGE_EVENT(element, j - 1))
        {
@@ -2226,21 +4095,6 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
     }
   }
 
-  /* some custom element change events get mapped since version 3.0.3 */
-  for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
-  {
-    int element = EL_CUSTOM_START + i;
-
-    if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
-       HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
-    {
-      SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
-      SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
-
-      SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
-    }
-  }
-
   /* initialize "can_change" field for old levels with only one change page */
   if (level->game_version <= VERSION_IDENT(3,0,2,0))
   {
@@ -2254,24 +4108,53 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
   }
 
   /* correct custom element values (for old levels without these options) */
-  for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+  if (level->game_version < VERSION_IDENT(3,1,1,0))
   {
-    int element = EL_CUSTOM_START + i;
-    struct ElementInfo *ei = &element_info[element];
+    for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+    {
+      int element = EL_CUSTOM_START + i;
+      struct ElementInfo *ei = &element_info[element];
 
-    if (ei->access_direction == MV_NO_MOVING)
-      ei->access_direction = MV_ALL_DIRECTIONS;
+      if (ei->access_direction == MV_NO_DIRECTION)
+       ei->access_direction = MV_ALL_DIRECTIONS;
+
+#if 0
+      for (j = 0; j < ei->num_change_pages; j++)
+      {
+       struct ElementChangeInfo *change = &ei->change_page[j];
+
+       if (change->trigger_side == CH_SIDE_NONE)
+         change->trigger_side = CH_SIDE_ANY;
+      }
+#endif
+    }
+  }
 
-    for (j = 0; j < ei->num_change_pages; j++)
+#if 1
+  /* correct custom element values (fix invalid values for all versions) */
+  if (1)
+  {
+    for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
     {
-      struct ElementChangeInfo *change = &ei->change_page[j];
+      int element = EL_CUSTOM_START + i;
+      struct ElementInfo *ei = &element_info[element];
+
+      for (j = 0; j < ei->num_change_pages; j++)
+      {
+       struct ElementChangeInfo *change = &ei->change_page[j];
+
+       if (change->trigger_player == CH_PLAYER_NONE)
+         change->trigger_player = CH_PLAYER_ANY;
 
-      if (change->trigger_side == CH_SIDE_NONE)
-       change->trigger_side = CH_SIDE_ANY;
+       if (change->trigger_side == CH_SIDE_NONE)
+         change->trigger_side = CH_SIDE_ANY;
+      }
     }
   }
+#endif
 
   /* initialize "can_explode" field for old levels which did not store this */
+  /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
   if (level->game_version <= VERSION_IDENT(3,1,0,0))
   {
     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
@@ -2287,30 +4170,21 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
     }
   }
 
-#if 0
-  /* set default push delay values (corrected since version 3.0.7-1) */
-  if (level->game_version < VERSION_IDENT(3,0,7,1))
-  {
-    game.default_push_delay_fixed = 2;
-    game.default_push_delay_random = 8;
-  }
-  else
-  {
-    game.default_push_delay_fixed = 8;
-    game.default_push_delay_random = 8;
-  }
-
-  /* set uninitialized push delay values of custom elements in older levels */
-  for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+  /* correct previously hard-coded move delay values for maze runner style */
+  if (level->game_version < VERSION_IDENT(3,1,1,0))
   {
-    int element = EL_CUSTOM_START + i;
+    for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+    {
+      int element = EL_CUSTOM_START + i;
 
-    if (element_info[element].push_delay_fixed == -1)
-      element_info[element].push_delay_fixed = game.default_push_delay_fixed;
-    if (element_info[element].push_delay_random == -1)
-      element_info[element].push_delay_random = game.default_push_delay_random;
+      if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
+      {
+       /* previously hard-coded and therefore ignored */
+       element_info[element].move_delay_fixed = 9;
+       element_info[element].move_delay_random = 0;
+      }
+    }
   }
-#endif
 
   /* map elements that have changed in newer versions */
   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
@@ -2318,8 +4192,8 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
     for (x = 0; x < 3; x++)
       for (y = 0; y < 3; y++)
-       level->yamyam_content[i][x][y] =
-         getMappedElementByVersion(level->yamyam_content[i][x][y],
+       level->yamyam_content[i].e[x][y] =
+         getMappedElementByVersion(level->yamyam_content[i].e[x][y],
                                    level->game_version);
 
   /* initialize element properties for level editor etc. */
@@ -2332,37 +4206,9 @@ static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
 
   /* map elements that have changed in newer versions */
   for (y = 0; y < level->fieldy; y++)
-  {
     for (x = 0; x < level->fieldx; x++)
-    {
-      int element = level->field[x][y];
-
-#if 1
-      element = getMappedElementByVersion(element, level->game_version);
-#else
-      if (level->game_version <= VERSION_IDENT(2,2,0,0))
-      {
-       /* map game font elements */
-       element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
-                  element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
-                  element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
-                  element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
-      }
-
-      if (level->game_version < VERSION_IDENT(3,0,0,0))
-      {
-       /* map Supaplex gravity tube elements */
-       element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
-                  element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
-                  element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
-                  element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
-                  element);
-      }
-#endif
-
-      level->field[x][y] = element;
-    }
-  }
+      level->field[x][y] = getMappedElementByVersion(level->field[x][y],
+                                                    level->game_version);
 
   /* copy elements to runtime playfield array */
   for (x = 0; x < MAX_LEV_FIELDX; x++)
@@ -2377,48 +4223,53 @@ static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
   SetBorderElement();
 }
 
-void LoadLevelTemplate(int nr)
+static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
 {
-#if 1
-  struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
-  char *filename = level_file_info->filename;
+  struct LevelFileInfo *level_file_info = &level->file_info;
 
-  LoadLevelFromFileInfo(&level_template, level_file_info);
+#if 1
+  if (level_file_info->type == LEVEL_FILE_TYPE_RND)
+    CopyNativeLevel_RND_to_Native(level);
 #else
-  char *filename = getDefaultLevelFilename(nr);
-
-  LoadLevelFromFilename_RND(&level_template, filename);
+  if (level_file_info->type == LEVEL_FILE_TYPE_RND)
+    CopyNativeLevel_RND_to_Native(level);
+  else
+    CopyNativeLevel_Native_to_RND(level);
 #endif
+}
 
-  LoadLevel_InitVersion(&level, filename);
-  LoadLevel_InitElements(&level, filename);
+void LoadLevelTemplate(int nr)
+{
+  char *filename;
+
+  setLevelFileInfo(&level_template.file_info, nr);
+  filename = level_template.file_info.filename;
+
+  LoadLevelFromFileInfo(&level_template, &level_template.file_info);
+
+  LoadLevel_InitVersion(&level_template, filename);
+  LoadLevel_InitElements(&level_template, filename);
 
   ActivateLevelTemplate();
 }
 
 void LoadLevel(int nr)
 {
-#if 1
-  struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
-  char *filename = level_file_info->filename;
+  char *filename;
 
-  LoadLevelFromFileInfo(&level, level_file_info);
-#else
-  char *filename = getLevelFilename(nr);
+  setLevelFileInfo(&level.file_info, nr);
+  filename = level.file_info.filename;
 
-  LoadLevelFromFilename_RND(&level, filename);
-#endif
+  LoadLevelFromFileInfo(&level, &level.file_info);
 
   if (level.use_custom_template)
     LoadLevelTemplate(-1);
 
-#if 1
   LoadLevel_InitVersion(&level, filename);
   LoadLevel_InitElements(&level, filename);
   LoadLevel_InitPlayfield(&level, filename);
-#else
-  LoadLevel_InitLevel(&level, filename);
-#endif
+
+  LoadLevel_InitNativeEngines(&level, filename);
 }
 
 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
@@ -2447,13 +4298,13 @@ static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
     for (y = 0; y < 3; y++)
       for (x = 0; x < 3; x++)
        putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
-                          level->yamyam_content[i][x][y]));
+                          level->yamyam_content[i].e[x][y]));
   putFile8Bit(file, level->amoeba_speed);
   putFile8Bit(file, level->time_magic_wall);
   putFile8Bit(file, level->time_wheel);
   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
                     level->amoeba_content));
-  putFile8Bit(file, (level->double_speed ? 1 : 0));
+  putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
@@ -2472,6 +4323,8 @@ static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
 
+  putFile8Bit(file, level->game_engine_type);
+
   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
 }
 
@@ -2509,9 +4362,9 @@ static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
     for (y = 0; y < 3; y++)
       for (x = 0; x < 3; x++)
        if (level->encoding_16bit_field)
-         putFile16BitBE(file, level->yamyam_content[i][x][y]);
+         putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
        else
-         putFile8Bit(file, level->yamyam_content[i][x][y]);
+         putFile8Bit(file, level->yamyam_content[i].e[x][y]);
 }
 #endif
 
@@ -2530,7 +4383,7 @@ static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
       for (y = 0; y < 3; y++)
        for (x = 0; x < 3; x++)
-         content_array[i][x][y] = level->yamyam_content[i][x][y];
+         content_array[i][x][y] = level->yamyam_content[i].e[x][y];
   }
   else if (element == EL_BD_AMOEBA)
   {
@@ -2595,6 +4448,20 @@ static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
   {
     int element = EL_CUSTOM_START + i;
 
+#if 1
+    struct ElementInfo *ei = &element_info[element];
+
+    if (ei->properties[EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
+    {
+      if (check < num_changed_custom_elements)
+      {
+       putFile16BitBE(file, element);
+       putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE]);
+      }
+
+      check++;
+    }
+#else
     if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
     {
       if (check < num_changed_custom_elements)
@@ -2605,6 +4472,7 @@ static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
 
       check++;
     }
+#endif
   }
 
   if (check != num_changed_custom_elements)    /* should not happen */
@@ -2652,63 +4520,68 @@ static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
   {
     int element = EL_CUSTOM_START + i;
+    struct ElementInfo *ei = &element_info[element];
 
-    if (element_info[element].modified_settings)
+    if (ei->modified_settings)
     {
       if (check < num_changed_custom_elements)
       {
        putFile16BitBE(file, element);
 
        for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
-         putFile8Bit(file, element_info[element].description[j]);
+         putFile8Bit(file, ei->description[j]);
 
+#if 1
+       putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE]);
+#else
        putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
+#endif
 
        /* some free bytes for future properties and padding */
        WriteUnusedBytesToFile(file, 7);
 
-       putFile8Bit(file, element_info[element].use_gfx_element);
-       putFile16BitBE(file, element_info[element].gfx_element);
+       putFile8Bit(file, ei->use_gfx_element);
+       putFile16BitBE(file, ei->gfx_element);
 
-       putFile8Bit(file, element_info[element].collect_score);
-       putFile8Bit(file, element_info[element].collect_count);
+       putFile8Bit(file, ei->collect_score_initial);
+       putFile8Bit(file, ei->collect_count_initial);
 
-       putFile16BitBE(file, element_info[element].push_delay_fixed);
-       putFile16BitBE(file, element_info[element].push_delay_random);
-       putFile16BitBE(file, element_info[element].move_delay_fixed);
-       putFile16BitBE(file, element_info[element].move_delay_random);
+       putFile16BitBE(file, ei->push_delay_fixed);
+       putFile16BitBE(file, ei->push_delay_random);
+       putFile16BitBE(file, ei->move_delay_fixed);
+       putFile16BitBE(file, ei->move_delay_random);
 
-       putFile16BitBE(file, element_info[element].move_pattern);
-       putFile8Bit(file, element_info[element].move_direction_initial);
-       putFile8Bit(file, element_info[element].move_stepsize);
+       putFile16BitBE(file, ei->move_pattern);
+       putFile8Bit(file, ei->move_direction_initial);
+       putFile8Bit(file, ei->move_stepsize);
 
        for (y = 0; y < 3; y++)
          for (x = 0; x < 3; x++)
-           putFile16BitBE(file, element_info[element].content[x][y]);
+           putFile16BitBE(file, ei->content.e[x][y]);
 
-       putFile32BitBE(file, element_info[element].change->events);
+       putFile32BitBE(file, ei->change->events);
 
-       putFile16BitBE(file, element_info[element].change->target_element);
+       putFile16BitBE(file, ei->change->target_element);
 
-       putFile16BitBE(file, element_info[element].change->delay_fixed);
-       putFile16BitBE(file, element_info[element].change->delay_random);
-       putFile16BitBE(file, element_info[element].change->delay_frames);
+       putFile16BitBE(file, ei->change->delay_fixed);
+       putFile16BitBE(file, ei->change->delay_random);
+       putFile16BitBE(file, ei->change->delay_frames);
 
-       putFile16BitBE(file, element_info[element].change->trigger_element);
+       putFile16BitBE(file, ei->change->trigger_element);
 
-       putFile8Bit(file, element_info[element].change->explode);
-       putFile8Bit(file, element_info[element].change->use_target_content);
-       putFile8Bit(file, element_info[element].change->only_if_complete);
-       putFile8Bit(file, element_info[element].change->use_random_replace);
+       putFile8Bit(file, ei->change->explode);
+       putFile8Bit(file, ei->change->use_target_content);
+       putFile8Bit(file, ei->change->only_if_complete);
+       putFile8Bit(file, ei->change->use_random_replace);
 
-       putFile8Bit(file, element_info[element].change->random_percentage);
-       putFile8Bit(file, element_info[element].change->replace_when);
+       putFile8Bit(file, ei->change->random_percentage);
+       putFile8Bit(file, ei->change->replace_when);
 
        for (y = 0; y < 3; y++)
          for (x = 0; x < 3; x++)
-           putFile16BitBE(file, element_info[element].change->content[x][y]);
+           putFile16BitBE(file, ei->change->content.e[x][y]);
 
-       putFile8Bit(file, element_info[element].slippery_type);
+       putFile8Bit(file, ei->slippery_type);
 
        /* some free bytes for future properties and padding */
        WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
@@ -2726,28 +4599,33 @@ static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
 {
   struct ElementInfo *ei = &element_info[element];
-  int i, x, y;
+  int i, j, x, y;
+
+  /* ---------- custom element base property values (96 bytes) ------------- */
 
   putFile16BitBE(file, element);
 
   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
     putFile8Bit(file, ei->description[i]);
 
+#if 1
+  putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE]);
+#else
   putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
+#endif
   WriteUnusedBytesToFile(file, 4);     /* reserved for more base properties */
 
   putFile8Bit(file, ei->num_change_pages);
 
-  /* some free bytes for future base property values and padding */
-  WriteUnusedBytesToFile(file, 5);
-
-  /* write custom property values */
+  putFile16BitBE(file, ei->ce_value_fixed_initial);
+  putFile16BitBE(file, ei->ce_value_random_initial);
+  putFile8Bit(file, ei->use_last_ce_value);
 
   putFile8Bit(file, ei->use_gfx_element);
   putFile16BitBE(file, ei->gfx_element);
 
-  putFile8Bit(file, ei->collect_score);
-  putFile8Bit(file, ei->collect_count);
+  putFile8Bit(file, ei->collect_score_initial);
+  putFile8Bit(file, ei->collect_count_initial);
 
   putFile8Bit(file, ei->drop_delay_fixed);
   putFile8Bit(file, ei->push_delay_fixed);
@@ -2765,7 +4643,7 @@ static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
 
   for (y = 0; y < 3; y++)
     for (x = 0; x < 3; x++)
-      putFile16BitBE(file, ei->content[x][y]);
+      putFile16BitBE(file, ei->content.e[x][y]);
 
   putFile16BitBE(file, ei->move_enter_element);
   putFile16BitBE(file, ei->move_leave_element);
@@ -2783,13 +4661,19 @@ static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
   /* some free bytes for future custom property values and padding */
   WriteUnusedBytesToFile(file, 1);
 
-  /* write change property values */
+  /* ---------- change page property values (48 bytes) --------------------- */
 
   for (i = 0; i < ei->num_change_pages; i++)
   {
     struct ElementChangeInfo *change = &ei->change_page[i];
+    unsigned int event_bits;
 
-    putFile32BitBE(file, change->events);
+    /* bits 0 - 31 of "has_event[]" ... */
+    event_bits = 0;
+    for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
+      if (change->has_event[j])
+       event_bits |= (1 << j);
+    putFile32BitBE(file, event_bits);
 
     putFile16BitBE(file, change->target_element);
 
@@ -2809,56 +4693,306 @@ static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
 
     for (y = 0; y < 3; y++)
       for (x = 0; x < 3; x++)
-       putFile16BitBE(file, change->target_content[x][y]);
+       putFile16BitBE(file, change->target_content.e[x][y]);
+
+    putFile8Bit(file, change->can_change);
+
+    putFile8Bit(file, change->trigger_side);
+
+    putFile8Bit(file, change->trigger_player);
+    putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
+                      log_2(change->trigger_page)));
+
+    putFile8Bit(file, change->has_action);
+    putFile8Bit(file, change->action_type);
+    putFile8Bit(file, change->action_mode);
+    putFile16BitBE(file, change->action_arg);
+
+    /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
+    event_bits = 0;
+    for (j = 32; j < NUM_CHANGE_EVENTS; j++)
+      if (change->has_event[j])
+       event_bits |= (1 << (j - 32));
+    putFile8Bit(file, event_bits);
+  }
+}
+
+static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
+{
+  struct ElementInfo *ei = &element_info[element];
+  struct ElementGroupInfo *group = ei->group;
+  int i;
+
+  putFile16BitBE(file, element);
+
+  for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
+    putFile8Bit(file, ei->description[i]);
+
+  putFile8Bit(file, group->num_elements);
+
+  putFile8Bit(file, ei->use_gfx_element);
+  putFile16BitBE(file, ei->gfx_element);
+
+  putFile8Bit(file, group->choice_mode);
+
+  /* some free bytes for future values and padding */
+  WriteUnusedBytesToFile(file, 3);
+
+  for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
+    putFile16BitBE(file, group->element[i]);
+}
+
+static int SaveLevel_MicroChunk_SingleValue(FILE *file,
+                                           struct ElementFileConfig *entry)
+{
+  int default_value = entry->default_value;
+  int element = entry->element;
+  int data_type = entry->data_type;
+  int conf_type = entry->conf_type;
+  int byte_mask = conf_type & CONF_MASK_BYTES;
+  void *value_ptr = entry->value;
+  int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
+              *(int *)value_ptr);
+  int num_bytes = 0;
+  boolean modified = FALSE;
+
+  /* check if any settings have been modified before saving them */
+  if (value != default_value)
+    modified = TRUE;
+
+  if (!modified)               /* do not save unmodified default settings */
+    return 0;
+
+#if 0
+  printf("::: %02x, %d: %d != %d\n",
+        byte_mask, conf_type & CONF_MASK_TOKEN,
+        value, default_value);
+#endif
+
+  if (element != -1)
+    num_bytes += putFile16BitBE(file, element);
+
+  num_bytes += putFile8Bit(file, conf_type);
+  num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
+               byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
+               byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :0);
+
+  return num_bytes;
+}
+
+static int SaveLevel_MicroChunk_ElementList(FILE *file,
+                                           struct ElementFileConfig *entry)
+{
+  int *element_array = (int *)(entry->value);
+  int num_elements = *(int *)(entry->num_entities);
+  int default_value = entry->default_value;
+  int element = entry->element;
+  int conf_type = entry->conf_type;
+  int num_bytes = 0;
+  boolean modified = FALSE;
+  int i;
+
+  /* check if any settings have been modified before saving them */
+  for (i = 0; i < num_elements; i++)
+    if (element_array[i] != default_value)
+      modified = TRUE;
+
+  if (!modified)               /* do not save unmodified default settings */
+    return 0;
+
+  if (element != -1)
+    num_bytes += putFile16BitBE(file, element);
+
+  num_bytes += putFile8Bit(file, conf_type);
+  num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
+
+  for (i = 0; i < num_elements; i++)
+    num_bytes += putFile16BitBE(file, element_array[i]);
+
+  return num_bytes;
+}
+
+static int SaveLevel_MicroChunk_ContentList(FILE *file,
+                                           struct ElementFileConfig *entry)
+{
+  struct Content *content = (struct Content *)(entry->value);
+  int num_contents = *(int *)(entry->num_entities);
+  int default_value = entry->default_value;
+  int element = entry->element;
+  int conf_type = entry->conf_type;
+  int num_bytes = 0;
+  boolean modified = FALSE;
+  int i, x, y;
+
+  /* check if any settings have been modified before saving them */
+  for (i = 0; i < num_contents; i++)
+    for (y = 0; y < 3; y++)
+      for (x = 0; x < 3; x++)
+       if (content[i].e[x][y] != default_value)
+         modified = TRUE;
+
+  if (!modified)               /* do not save unmodified default settings */
+    return 0;
+
+  if (element != -1)
+    num_bytes += putFile16BitBE(file, element);
+
+  num_bytes += putFile8Bit(file, conf_type);
+  num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
+
+  for (i = 0; i < num_contents; i++)
+    for (y = 0; y < 3; y++)
+      for (x = 0; x < 3; x++)
+       num_bytes += putFile16BitBE(file, content[i].e[x][y]);
+
+  return num_bytes;
+}
+
+static int SaveLevel_CONF(FILE *file, struct LevelInfo *level)
+{
+  int chunk_size = 0;
+  int i;
+
+  li = *level;         /* copy level data into temporary buffer */
+
+  for (i = 0; element_conf[i].data_type != -1; i++)
+  {
+    struct ElementFileConfig *config = &element_conf[i];
+    int data_type = config->data_type;
+    int conf_type = config->conf_type;
+    int byte_mask = conf_type & CONF_MASK_BYTES;
+
+    if (byte_mask != CONF_MASK_MULTI_BYTES)
+      chunk_size += SaveLevel_MicroChunk_SingleValue(file, config);
+    else if (data_type == TYPE_ELEMENT_LIST)
+      chunk_size += SaveLevel_MicroChunk_ElementList(file, config);
+    else if (data_type == TYPE_CONTENT_LIST)
+      chunk_size += SaveLevel_MicroChunk_ContentList(file, config);
+  }
+
+  return chunk_size;
+}
+
+static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
+{
+  struct ElementInfo *ei = &element_info[element];
+  int chunk_size = 0;
+  int i;
+
+  chunk_size += putFile16BitBE(file, element);
+
+  xx_ei = *ei;         /* copy element data into temporary buffer */
+
+  xx_num_description_bytes = MAX_ELEMENT_NAME_LEN;
+  xx_num_contents = 1;
+
+#if 0
+  printf("::: - element config\n");
+#endif
+
+  for (i = 0; custom_element_conf[i].data_type != -1; i++)
+  {
+    struct ElementFileConfig *config = &custom_element_conf[i];
+    int data_type = config->data_type;
+    int conf_type = config->conf_type;
+    int byte_mask = conf_type & CONF_MASK_BYTES;
+
+    if (byte_mask != CONF_MASK_MULTI_BYTES)
+      chunk_size += SaveLevel_MicroChunk_SingleValue(file, config);
+    else if (data_type == TYPE_ELEMENT_LIST)
+      chunk_size += SaveLevel_MicroChunk_ElementList(file, config);
+    else if (data_type == TYPE_CONTENT_LIST)
+      chunk_size += SaveLevel_MicroChunk_ContentList(file, config);
+  }
+
+#if 0
+  printf("::: - change pages\n");
+#endif
+
+  for (i = 0; i < ei->num_change_pages; i++)
+  {
+    struct ElementChangeInfo *change = &ei->change_page[i];
 
-    putFile8Bit(file, change->can_change);
+    xx_current_change_page = i;
 
-    putFile8Bit(file, change->trigger_side);
+    xx_change = *change;       /* copy change data into temporary buffer */
 
-#if 1
-    putFile8Bit(file, change->trigger_player);
-    putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
-                      log_2(change->trigger_page)));
+#if 0
+    printf(":::   %d: xx_change.action_mode == %d\n",
+          i, xx_change.action_mode);
+    printf(":::   %d: xx_change.action_arg == %d\n",
+          i, xx_change.action_arg);
+#endif
 
-    /* some free bytes for future change property values and padding */
-    WriteUnusedBytesToFile(file, 6);
+    xx_event_bits_0_31 = 0;
+    xx_event_bits_32_63 = 0;
 
-#else
+    for (i = 0; i < NUM_CHANGE_EVENTS; i++)
+    {
+      if (change->has_event[i])
+      {
+       if (i < 32)
+         xx_event_bits_0_31 |= (1 << i);
+       else
+         xx_event_bits_32_63 |= (1 << (i - 32));
+      }
+    }
 
-    /* some free bytes for future change property values and padding */
-    WriteUnusedBytesToFile(file, 8);
-#endif
+    for (i = 0; custom_element_change_conf[i].data_type != -1; i++)
+    {
+      struct ElementFileConfig *config = &custom_element_change_conf[i];
+      int data_type = config->data_type;
+      int conf_type = config->conf_type;
+      int byte_mask = conf_type & CONF_MASK_BYTES;
+
+      if (byte_mask != CONF_MASK_MULTI_BYTES)
+       chunk_size += SaveLevel_MicroChunk_SingleValue(file, config);
+      else if (data_type == TYPE_ELEMENT_LIST)
+       chunk_size += SaveLevel_MicroChunk_ElementList(file, config);
+      else if (data_type == TYPE_CONTENT_LIST)
+       chunk_size += SaveLevel_MicroChunk_ContentList(file, config);
+    }
   }
+
+  return chunk_size;
 }
 
-static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
+static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
 {
   struct ElementInfo *ei = &element_info[element];
   struct ElementGroupInfo *group = ei->group;
+  int chunk_size = 0;
   int i;
 
-  putFile16BitBE(file, element);
-
-  for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
-    putFile8Bit(file, ei->description[i]);
-
-  putFile8Bit(file, group->num_elements);
+  chunk_size += putFile16BitBE(file, element);
 
-  putFile8Bit(file, ei->use_gfx_element);
-  putFile16BitBE(file, ei->gfx_element);
+  xx_ei = *ei;         /* copy element data into temporary buffer */
+  xx_group = *group;   /* copy group data into temporary buffer */
 
-  putFile8Bit(file, group->choice_mode);
+  xx_num_description_bytes = MAX_ELEMENT_NAME_LEN;
+  xx_num_contents = 1;
 
-  /* some free bytes for future values and padding */
-  WriteUnusedBytesToFile(file, 3);
+  for (i = 0; group_element_conf[i].data_type != -1; i++)
+  {
+    struct ElementFileConfig *config = &group_element_conf[i];
+    int data_type = config->data_type;
+    int conf_type = config->conf_type;
+    int byte_mask = conf_type & CONF_MASK_BYTES;
+
+    if (byte_mask != CONF_MASK_MULTI_BYTES)
+      chunk_size += SaveLevel_MicroChunk_SingleValue(file, config);
+    else if (data_type == TYPE_ELEMENT_LIST)
+      chunk_size += SaveLevel_MicroChunk_ElementList(file, config);
+    else if (data_type == TYPE_CONTENT_LIST)
+      chunk_size += SaveLevel_MicroChunk_ContentList(file, config);
+  }
 
-  for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
-    putFile16BitBE(file, group->element[i]);
+  return chunk_size;
 }
 
 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
 {
-  int body_chunk_size;
+  int body_chunk_size, conf_chunk_size;
   int i, x, y;
   FILE *file;
 
@@ -2883,7 +5017,7 @@ static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
   for (i = 0; i < level->num_yamyam_contents; i++)
     for (y = 0; y < 3; y++)
       for (x = 0; x < 3; x++)
-       if (level->yamyam_content[i][x][y] > 255)
+       if (level->yamyam_content[i].e[x][y] > 255)
          level->encoding_16bit_yamyam = TRUE;
 
   /* check amoeba content for 16-bit elements */
@@ -2935,9 +5069,10 @@ static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
     }
   }
 
-  /* check for non-default custom elements (unless using template level) */
+  /* if not using template level, check for non-default custom/group elements */
   if (!level->use_custom_template)
   {
+#if 1
     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
     {
       int element = EL_CUSTOM_START + i;
@@ -2950,11 +5085,9 @@ static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
        SaveLevel_CUS4(file, level, element);
       }
     }
-  }
+#endif
 
-  /* check for non-default group elements (unless using template level) */
-  if (!level->use_custom_template)
-  {
+#if 1
     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
     {
       int element = EL_GROUP_START + i;
@@ -2965,6 +5098,73 @@ static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
        SaveLevel_GRP1(file, level, element);
       }
     }
+#endif
+  }
+
+  conf_chunk_size = SaveLevel_CONF(NULL, level);
+
+  /* check if non-default element settings need to be saved */
+  if (conf_chunk_size > 0)
+  {
+    putFileChunkBE(file, "CONF", conf_chunk_size);
+    SaveLevel_CONF(file, level);
+  }
+
+  /* if not using template level, check for non-default custom/group elements */
+  if (!level->use_custom_template)
+  {
+    /* (element number, number of change pages, change page number) */
+    int cusx_chunk_size_no_changes = (2) + (1 + 1) + (1 + 1);
+    /* (element number only) */
+    int grpx_chunk_size_no_changes = (2);
+
+#if 1
+    for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+    {
+      int element = EL_CUSTOM_START + i;
+      int cusx_chunk_size = SaveLevel_CUSX(NULL, level, element);
+
+      /* check if non-default element settings need to be saved */
+      if (cusx_chunk_size > cusx_chunk_size_no_changes)
+      {
+#if 1
+       printf("::: SAVING CE %d\n", i + 1);
+#endif
+
+       putFileChunkBE(file, "CUSX", cusx_chunk_size);
+       SaveLevel_CUSX(file, level, element);
+      }
+
+#if 0
+      if (i == 1)
+       break;
+#endif
+    }
+#endif
+
+#if 1
+    for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
+    {
+      int element = EL_GROUP_START + i;
+      int grpx_chunk_size = SaveLevel_GRPX(NULL, level, element);
+
+      /* check if non-default element settings need to be saved */
+      if (grpx_chunk_size > grpx_chunk_size_no_changes)
+      {
+#if 1
+       printf("::: SAVING GE %d\n", i + 1);
+#endif
+
+       putFileChunkBE(file, "GRPX", grpx_chunk_size);
+       SaveLevel_GRPX(file, level, element);
+      }
+
+#if 0
+      if (i == 1)
+       break;
+#endif
+    }
+#endif
   }
 
   fclose(file);
@@ -3016,7 +5216,7 @@ void DumpLevel(struct LevelInfo *level)
   printf("Amoeba speed: %d\n", level->amoeba_speed);
   printf("\n");
   printf("Initial gravity:             %s\n", (level->initial_gravity ? "yes" : "no"));
-  printf("Double speed movement:       %s\n", (level->double_speed ? "yes" : "no"));
+  printf("Initial player stepsize:     %d\n", level->initial_player_stepsize);
   printf("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
   printf("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
   printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
@@ -3138,12 +5338,12 @@ static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
 
   for (i = 0; i < tape->length; i++)
   {
-    if (i >= MAX_TAPELEN)
+    if (i >= MAX_TAPE_LEN)
       break;
 
     for (j = 0; j < MAX_PLAYERS; j++)
     {
-      tape->pos[i].action[j] = MV_NO_MOVING;
+      tape->pos[i].action[j] = MV_NONE;
 
       if (tape->player_participates[j])
        tape->pos[i].action[j] = getFile8Bit(file);
@@ -3190,7 +5390,7 @@ static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
 
        /* delay part */
        for (j = 0; j < MAX_PLAYERS; j++)
-         tape->pos[i].action[j] = MV_NO_MOVING;
+         tape->pos[i].action[j] = MV_NONE;
        tape->pos[i].delay--;
 
        i++;
@@ -3222,20 +5422,16 @@ void LoadTapeFromFilename(char *filename)
   {
     tape.no_valid_file = TRUE;
 
-#if 0
-    Error(ERR_WARN, "cannot read tape '%s' -- using empty tape", filename);
-#endif
-
     return;
   }
 
   getFileChunkBE(file, chunk_name, NULL);
-  if (strcmp(chunk_name, "RND1") == 0)
+  if (strEqual(chunk_name, "RND1"))
   {
     getFile32BitBE(file);              /* not used */
 
     getFileChunkBE(file, chunk_name, NULL);
-    if (strcmp(chunk_name, "TAPE") != 0)
+    if (!strEqual(chunk_name, "TAPE"))
     {
       tape.no_valid_file = TRUE;
 
@@ -3301,7 +5497,7 @@ void LoadTapeFromFilename(char *filename)
       int i = 0;
 
       while (chunk_info[i].name != NULL &&
-            strcmp(chunk_name, chunk_info[i].name) != 0)
+            !strEqual(chunk_name, chunk_info[i].name))
        i++;
 
       if (chunk_info[i].name == NULL)
@@ -3427,7 +5623,7 @@ void SaveTape(int nr)
   InitTapeDirectory(leveldir_current->subdir);
 
   /* if a tape still exists, ask to overwrite it */
-  if (access(filename, F_OK) == 0)
+  if (fileExists(filename))
   {
     new_tape = FALSE;
     if (!Request("Replace old tape ?", REQ_ASK))
@@ -3473,41 +5669,37 @@ void SaveTape(int nr)
   tape.changed = FALSE;
 
   if (new_tape)
-    Request("tape saved !", REQ_CONFIRM);
+    Request("Tape saved !", REQ_CONFIRM);
 }
 
 void DumpTape(struct TapeInfo *tape)
 {
+  int tape_frame_counter;
   int i, j;
 
-#if 1
   if (tape->no_valid_file)
   {
     Error(ERR_WARN, "cannot dump -- no valid tape file found");
 
     return;
   }
-#else
-  if (TAPE_IS_EMPTY(*tape))
-  {
-    Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
-
-    return;
-  }
-#endif
 
   printf_line("-", 79);
   printf("Tape of Level %03d (file version %08d, game version %08d)\n",
         tape->level_nr, tape->file_version, tape->game_version);
+  printf("                  (effective engine version %08d)\n",
+        tape->engine_version);
   printf("Level series identifier: '%s'\n", tape->level_identifier);
   printf_line("-", 79);
 
+  tape_frame_counter = 0;
+
   for (i = 0; i < tape->length; i++)
   {
-    if (i >= MAX_TAPELEN)
+    if (i >= MAX_TAPE_LEN)
       break;
 
-    printf("%03d: ", i);
+    printf("%04d: ", i);
 
     for (j = 0; j < MAX_PLAYERS; j++)
     {
@@ -3526,7 +5718,10 @@ void DumpTape(struct TapeInfo *tape)
       }
     }
 
-    printf("(%03d)\n", tape->pos[i].delay);
+    printf("(%03d) ", tape->pos[i].delay);
+    printf("[%05d]\n", tape_frame_counter);
+
+    tape_frame_counter += tape->pos[i].delay;
   }
 
   printf_line("-", 79);
@@ -3632,43 +5827,74 @@ void SaveScore(int nr)
 #define SETUP_TOKEN_SOFT_SCROLLING             7
 #define SETUP_TOKEN_FADING                     8
 #define SETUP_TOKEN_AUTORECORD                 9
-#define SETUP_TOKEN_QUICK_DOORS                        10
-#define SETUP_TOKEN_TEAM_MODE                  11
-#define SETUP_TOKEN_HANDICAP                   12
-#define SETUP_TOKEN_TIME_LIMIT                 13
-#define SETUP_TOKEN_FULLSCREEN                 14
-#define SETUP_TOKEN_ASK_ON_ESCAPE              15
-#define SETUP_TOKEN_GRAPHICS_SET               16
-#define SETUP_TOKEN_SOUNDS_SET                 17
-#define SETUP_TOKEN_MUSIC_SET                  18
-#define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS    19
-#define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS      20
-#define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC       21
-
-#define NUM_GLOBAL_SETUP_TOKENS                        22
+#define SETUP_TOKEN_SHOW_TITLESCREEN           10
+#define SETUP_TOKEN_QUICK_DOORS                        11
+#define SETUP_TOKEN_TEAM_MODE                  12
+#define SETUP_TOKEN_HANDICAP                   13
+#define SETUP_TOKEN_SKIP_LEVELS                        14
+#define SETUP_TOKEN_TIME_LIMIT                 15
+#define SETUP_TOKEN_FULLSCREEN                 16
+#define SETUP_TOKEN_ASK_ON_ESCAPE              17
+#define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR       18
+#define SETUP_TOKEN_QUICK_SWITCH               19
+#define SETUP_TOKEN_INPUT_ON_FOCUS             20
+#define SETUP_TOKEN_PREFER_AGA_GRAPHICS                21
+#define SETUP_TOKEN_GRAPHICS_SET               22
+#define SETUP_TOKEN_SOUNDS_SET                 23
+#define SETUP_TOKEN_MUSIC_SET                  24
+#define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS    25
+#define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS      26
+#define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC       27
+
+#define NUM_GLOBAL_SETUP_TOKENS                        28
 
 /* editor setup */
 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH      0
 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE     1
-#define SETUP_TOKEN_EDITOR_EL_MORE             2
-#define SETUP_TOKEN_EDITOR_EL_SOKOBAN          3
-#define SETUP_TOKEN_EDITOR_EL_SUPAPLEX         4
-#define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES    5
-#define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH   6
-#define SETUP_TOKEN_EDITOR_EL_CHARS            7
-#define SETUP_TOKEN_EDITOR_EL_CUSTOM           8
-#define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE      9
+#define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE_CLUB        2
+#define SETUP_TOKEN_EDITOR_EL_MORE             3
+#define SETUP_TOKEN_EDITOR_EL_SOKOBAN          4
+#define SETUP_TOKEN_EDITOR_EL_SUPAPLEX         5
+#define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES    6
+#define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH   7
+#define SETUP_TOKEN_EDITOR_EL_CHARS            8
+#define SETUP_TOKEN_EDITOR_EL_CUSTOM           9
 #define SETUP_TOKEN_EDITOR_EL_HEADLINES                10
 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED     11
-
-#define NUM_EDITOR_SETUP_TOKENS                        12
+#define SETUP_TOKEN_EDITOR_EL_DYNAMIC          12
+#define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN  13
+
+#define NUM_EDITOR_SETUP_TOKENS                        14
+
+/* editor cascade setup */
+#define SETUP_TOKEN_EDITOR_CASCADE_BD          0
+#define SETUP_TOKEN_EDITOR_CASCADE_EM          1
+#define SETUP_TOKEN_EDITOR_CASCADE_EMC         2
+#define SETUP_TOKEN_EDITOR_CASCADE_RND         3
+#define SETUP_TOKEN_EDITOR_CASCADE_SB          4
+#define SETUP_TOKEN_EDITOR_CASCADE_SP          5
+#define SETUP_TOKEN_EDITOR_CASCADE_DC          6
+#define SETUP_TOKEN_EDITOR_CASCADE_DX          7
+#define SETUP_TOKEN_EDITOR_CASCADE_TEXT                8
+#define SETUP_TOKEN_EDITOR_CASCADE_CE          9
+#define SETUP_TOKEN_EDITOR_CASCADE_GE          10
+#define SETUP_TOKEN_EDITOR_CASCADE_USER                11
+#define SETUP_TOKEN_EDITOR_CASCADE_GENERIC     12
+#define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC     13
+
+#define NUM_EDITOR_CASCADE_SETUP_TOKENS                14
 
 /* shortcut setup */
 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME         0
 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME         1
 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE      2
+#define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1    3
+#define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2    4
+#define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3    5
+#define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4    6
+#define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL  7
 
-#define NUM_SHORTCUT_SETUP_TOKENS              3
+#define NUM_SHORTCUT_SETUP_TOKENS              8
 
 /* player setup */
 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK                0
@@ -3704,6 +5930,7 @@ void SaveScore(int nr)
 
 static struct SetupInfo si;
 static struct SetupEditorInfo sei;
+static struct SetupEditorCascadeInfo seci;
 static struct SetupShortcutInfo ssi;
 static struct SetupInputInfo sii;
 static struct SetupSystemInfo syi;
@@ -3721,12 +5948,18 @@ static struct TokenInfo global_setup_tokens[] =
   { TYPE_SWITCH, &si.soft_scrolling,   "soft_scrolling"                },
   { TYPE_SWITCH, &si.fading,           "screen_fading"                 },
   { TYPE_SWITCH, &si.autorecord,       "automatic_tape_recording"      },
+  { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen"              },
   { TYPE_SWITCH, &si.quick_doors,      "quick_doors"                   },
   { TYPE_SWITCH, &si.team_mode,                "team_mode"                     },
   { TYPE_SWITCH, &si.handicap,         "handicap"                      },
+  { TYPE_SWITCH, &si.skip_levels,      "skip_levels"                   },
   { TYPE_SWITCH, &si.time_limit,       "time_limit"                    },
   { TYPE_SWITCH, &si.fullscreen,       "fullscreen"                    },
   { TYPE_SWITCH, &si.ask_on_escape,    "ask_on_escape"                 },
+  { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor"      },
+  { TYPE_SWITCH, &si.quick_switch,     "quick_player_switch"           },
+  { TYPE_SWITCH, &si.input_on_focus,   "input_on_focus"                },
+  { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics"                },
   { TYPE_STRING, &si.graphics_set,     "graphics_set"                  },
   { TYPE_STRING, &si.sounds_set,       "sounds_set"                    },
   { TYPE_STRING, &si.music_set,                "music_set"                     },
@@ -3739,6 +5972,7 @@ static struct TokenInfo editor_setup_tokens[] =
 {
   { TYPE_SWITCH, &sei.el_boulderdash,  "editor.el_boulderdash"         },
   { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine"        },
+  { TYPE_SWITCH, &sei.el_emerald_mine_club,"editor.el_emerald_mine_club"},
   { TYPE_SWITCH, &sei.el_more,         "editor.el_more"                },
   { TYPE_SWITCH, &sei.el_sokoban,      "editor.el_sokoban"             },
   { TYPE_SWITCH, &sei.el_supaplex,     "editor.el_supaplex"            },
@@ -3746,16 +5980,39 @@ static struct TokenInfo editor_setup_tokens[] =
   { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"     },
   { TYPE_SWITCH, &sei.el_chars,                "editor.el_chars"               },
   { TYPE_SWITCH, &sei.el_custom,       "editor.el_custom"              },
-  { TYPE_SWITCH, &sei.el_custom_more,  "editor.el_custom_more"         },
   { TYPE_SWITCH, &sei.el_headlines,    "editor.el_headlines"           },
   { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined"        },
+  { TYPE_SWITCH, &sei.el_dynamic,      "editor.el_dynamic"             },
+  { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token"   },
+};
+
+static struct TokenInfo editor_cascade_setup_tokens[] =
+{
+  { TYPE_SWITCH, &seci.el_bd,          "editor.cascade.el_bd"          },
+  { TYPE_SWITCH, &seci.el_em,          "editor.cascade.el_em"          },
+  { TYPE_SWITCH, &seci.el_emc,         "editor.cascade.el_emc"         },
+  { TYPE_SWITCH, &seci.el_rnd,         "editor.cascade.el_rnd"         },
+  { TYPE_SWITCH, &seci.el_sb,          "editor.cascade.el_sb"          },
+  { TYPE_SWITCH, &seci.el_sp,          "editor.cascade.el_sp"          },
+  { TYPE_SWITCH, &seci.el_dc,          "editor.cascade.el_dc"          },
+  { TYPE_SWITCH, &seci.el_dx,          "editor.cascade.el_dx"          },
+  { TYPE_SWITCH, &seci.el_chars,       "editor.cascade.el_chars"       },
+  { TYPE_SWITCH, &seci.el_ce,          "editor.cascade.el_ce"          },
+  { TYPE_SWITCH, &seci.el_ge,          "editor.cascade.el_ge"          },
+  { TYPE_SWITCH, &seci.el_user,                "editor.cascade.el_user"        },
+  { TYPE_SWITCH, &seci.el_dynamic,     "editor.cascade.el_dynamic"     },
 };
 
 static struct TokenInfo shortcut_setup_tokens[] =
 {
   { TYPE_KEY_X11, &ssi.save_game,      "shortcut.save_game"            },
   { TYPE_KEY_X11, &ssi.load_game,      "shortcut.load_game"            },
-  { TYPE_KEY_X11, &ssi.toggle_pause,   "shortcut.toggle_pause"         }
+  { TYPE_KEY_X11, &ssi.toggle_pause,   "shortcut.toggle_pause"         },
+  { TYPE_KEY_X11, &ssi.focus_player[0],        "shortcut.focus_player_1"       },
+  { TYPE_KEY_X11, &ssi.focus_player[1],        "shortcut.focus_player_2"       },
+  { TYPE_KEY_X11, &ssi.focus_player[2],        "shortcut.focus_player_3"       },
+  { TYPE_KEY_X11, &ssi.focus_player[3],        "shortcut.focus_player_4"       },
+  { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all"    },
 };
 
 static struct TokenInfo player_setup_tokens[] =
@@ -3775,18 +6032,18 @@ static struct TokenInfo player_setup_tokens[] =
   { TYPE_KEY_X11, &sii.key.up,         ".key.move_up"                  },
   { TYPE_KEY_X11, &sii.key.down,       ".key.move_down"                },
   { TYPE_KEY_X11, &sii.key.snap,       ".key.snap_field"               },
-  { TYPE_KEY_X11, &sii.key.drop,       ".key.place_bomb"               }
+  { TYPE_KEY_X11, &sii.key.drop,       ".key.place_bomb"               },
 };
 
 static struct TokenInfo system_setup_tokens[] =
 {
   { TYPE_STRING,  &syi.sdl_audiodriver,        "system.sdl_audiodriver"        },
-  { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size"        }
+  { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size"        },
 };
 
 static struct TokenInfo options_setup_tokens[] =
 {
-  { TYPE_BOOLEAN, &soi.verbose,                "options.verbose"               }
+  { TYPE_BOOLEAN, &soi.verbose,                "options.verbose"               },
 };
 
 static char *get_corrected_login_name(char *login_name)
@@ -3821,12 +6078,18 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->soft_scrolling = TRUE;
   si->fading = FALSE;
   si->autorecord = TRUE;
+  si->show_titlescreen = TRUE;
   si->quick_doors = FALSE;
   si->team_mode = FALSE;
   si->handicap = TRUE;
+  si->skip_levels = TRUE;
   si->time_limit = TRUE;
   si->fullscreen = FALSE;
   si->ask_on_escape = TRUE;
+  si->ask_on_escape_editor = TRUE;
+  si->quick_switch = FALSE;
+  si->input_on_focus = FALSE;
+  si->prefer_aga_graphics = TRUE;
 
   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
   si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
@@ -3835,24 +6098,33 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->override_level_sounds = FALSE;
   si->override_level_music = FALSE;
 
-  si->editor.el_boulderdash = TRUE;
-  si->editor.el_emerald_mine = TRUE;
-  si->editor.el_more = TRUE;
-  si->editor.el_sokoban = TRUE;
-  si->editor.el_supaplex = TRUE;
-  si->editor.el_diamond_caves = TRUE;
-  si->editor.el_dx_boulderdash = TRUE;
-  si->editor.el_chars = TRUE;
-  si->editor.el_custom = TRUE;
-  si->editor.el_custom_more = FALSE;
+  si->editor.el_boulderdash       = TRUE;
+  si->editor.el_emerald_mine      = TRUE;
+  si->editor.el_emerald_mine_club = TRUE;
+  si->editor.el_more              = TRUE;
+  si->editor.el_sokoban           = TRUE;
+  si->editor.el_supaplex          = TRUE;
+  si->editor.el_diamond_caves     = TRUE;
+  si->editor.el_dx_boulderdash    = TRUE;
+  si->editor.el_chars             = TRUE;
+  si->editor.el_custom            = TRUE;
 
   si->editor.el_headlines = TRUE;
   si->editor.el_user_defined = FALSE;
+  si->editor.el_dynamic = TRUE;
+
+  si->editor.show_element_token = FALSE;
 
   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
 
+  si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
+  si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
+  si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
+  si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
+  si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
+
   for (i = 0; i < MAX_PLAYERS; i++)
   {
     si->input[i].use_joystick = FALSE;
@@ -3879,6 +6151,24 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->options.verbose = FALSE;
 }
 
+static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
+{
+  si->editor_cascade.el_bd     = TRUE;
+  si->editor_cascade.el_em     = TRUE;
+  si->editor_cascade.el_emc    = TRUE;
+  si->editor_cascade.el_rnd    = TRUE;
+  si->editor_cascade.el_sb     = TRUE;
+  si->editor_cascade.el_sp     = TRUE;
+  si->editor_cascade.el_dc     = TRUE;
+  si->editor_cascade.el_dx     = TRUE;
+
+  si->editor_cascade.el_chars  = FALSE;
+  si->editor_cascade.el_ce     = FALSE;
+  si->editor_cascade.el_ge     = FALSE;
+  si->editor_cascade.el_user   = FALSE;
+  si->editor_cascade.el_dynamic        = FALSE;
+}
+
 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
 {
   int i, pnr;
@@ -3941,6 +6231,22 @@ static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
   setup.options = soi;
 }
 
+static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
+{
+  int i;
+
+  if (!setup_file_hash)
+    return;
+
+  /* editor cascade setup */
+  seci = setup.editor_cascade;
+  for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
+    setSetupInfo(editor_cascade_setup_tokens, i,
+                getHashEntry(setup_file_hash,
+                             editor_cascade_setup_tokens[i].text));
+  setup.editor_cascade = seci;
+}
+
 void LoadSetup()
 {
   char *filename = getSetupFilename();
@@ -3955,7 +6261,7 @@ void LoadSetup()
   {
     char *player_name_new;
 
-    checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
+    checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
     decodeSetupFileHash(setup_file_hash);
 
     setup.direct_draw = !setup.double_buffering;
@@ -3971,6 +6277,27 @@ void LoadSetup()
     Error(ERR_WARN, "using default setup values");
 }
 
+void LoadSetup_EditorCascade()
+{
+  char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
+  SetupFileHash *setup_file_hash = NULL;
+
+  /* always start with reliable default values */
+  setSetupInfoToDefaults_EditorCascade(&setup);
+
+  setup_file_hash = loadSetupFileHash(filename);
+
+  if (setup_file_hash)
+  {
+    checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
+    decodeSetupFileHash_EditorCascade(setup_file_hash);
+
+    freeSetupFileHash(setup_file_hash);
+  }
+
+  free(filename);
+}
+
 void SaveSetup()
 {
   char *filename = getSetupFilename();
@@ -4043,6 +6370,37 @@ void SaveSetup()
   SetFilePermissions(filename, PERMS_PRIVATE);
 }
 
+void SaveSetup_EditorCascade()
+{
+  char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
+  FILE *file;
+  int i;
+
+  InitUserDataDirectory();
+
+  if (!(file = fopen(filename, MODE_WRITE)))
+  {
+    Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
+    free(filename);
+    return;
+  }
+
+  fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
+                                              getCookie("SETUP")));
+  fprintf(file, "\n");
+
+  seci = setup.editor_cascade;
+  fprintf(file, "\n");
+  for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
+    fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
+
+  fclose(file);
+
+  SetFilePermissions(filename, PERMS_PRIVATE);
+
+  free(filename);
+}
+
 void LoadCustomElementDescriptions()
 {
   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
@@ -4075,19 +6433,14 @@ void LoadCustomElementDescriptions()
   freeSetupFileHash(setup_file_hash);
 }
 
-void LoadSpecialMenuDesignSettings()
+static void LoadSpecialMenuDesignSettingsFromFilename(char *filename)
 {
-  char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
   SetupFileHash *setup_file_hash;
-  int i, j;
+  int i;
 
-  /* always start with reliable default values from default config */
-  for (i = 0; image_config_vars[i].token != NULL; i++)
-    for (j = 0; image_config[j].token != NULL; j++)
-      if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
-       *image_config_vars[i].value =
-         get_auto_parameter_value(image_config_vars[i].token,
-                                  image_config[j].value);
+#if 0
+  printf("LoadSpecialMenuDesignSettings from file '%s' ...\n", filename);
+#endif
 
   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
     return;
@@ -4120,6 +6473,42 @@ void LoadSpecialMenuDesignSettings()
   freeSetupFileHash(setup_file_hash);
 }
 
+void LoadSpecialMenuDesignSettings()
+{
+  char *filename_base = UNDEFINED_FILENAME, *filename_local;
+  int i, j;
+
+  /* always start with reliable default values from default config */
+  for (i = 0; image_config_vars[i].token != NULL; i++)
+    for (j = 0; image_config[j].token != NULL; j++)
+      if (strEqual(image_config_vars[i].token, image_config[j].token))
+       *image_config_vars[i].value =
+         get_auto_parameter_value(image_config_vars[i].token,
+                                  image_config[j].value);
+
+#if 1
+  if (!SETUP_OVERRIDE_ARTWORK(setup, ARTWORK_TYPE_GRAPHICS))
+  {
+    /* first look for special settings configured in level series config */
+    filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
+
+    if (fileExists(filename_base))
+      LoadSpecialMenuDesignSettingsFromFilename(filename_base);
+  }
+
+  filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
+
+  if (filename_local != NULL && !strEqual(filename_base, filename_local))
+    LoadSpecialMenuDesignSettingsFromFilename(filename_local);
+
+#else
+
+  filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
+
+  LoadSpecialMenuDesignSettingsFromFilename(filename_local);
+#endif
+}
+
 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
 {
   char *filename = getEditorSetupFilename();
@@ -4144,6 +6533,10 @@ void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
   /* add space for up to 3 more elements for padding that may be needed */
   *num_elements += 3;
 
+  /* free memory for old list of elements, if needed */
+  checked_free(*elements);
+
+  /* allocate memory for new list of elements */
   *elements = checked_malloc(*num_elements * sizeof(int));
 
   *num_elements = 0;
@@ -4151,7 +6544,19 @@ void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
   {
     char *value = getHashEntry(element_hash, list->token);
 
-    if (value)
+    if (value == NULL)         /* try to find obsolete token mapping */
+    {
+      char *mapped_token = get_mapped_token(list->token);
+
+      if (mapped_token != NULL)
+      {
+       value = getHashEntry(element_hash, mapped_token);
+
+       free(mapped_token);
+      }
+    }
+
+    if (value != NULL)
     {
       (*elements)[(*num_elements)++] = atoi(value);
     }
@@ -4180,7 +6585,6 @@ void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
   freeSetupFileHash(element_hash);
 
 #if 0
-  /* TEST-ONLY */
   for (i = 0; i < *num_elements; i++)
     printf("editor: element '%s' [%d]\n",
           element_info[(*elements)[i]].token_name, (*elements)[i]);
@@ -4294,7 +6698,7 @@ static boolean music_info_listed_ext(struct MusicFileInfo *list,
                                     char *basename, boolean is_sound)
 {
   for (; list != NULL; list = list->next)
-    if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
+    if (list->is_sound == is_sound && strEqual(list->basename, basename))
       return TRUE;
 
   return FALSE;
@@ -4345,22 +6749,14 @@ void LoadMusicInfo()
 
   new = &music_file_info;
 
-#if 0
-  printf("::: num_music == %d\n", num_music);
-#endif
-
   for (i = 0; i < num_music; i++)
   {
     music = getMusicListEntry(i);
 
-#if 0
-    printf("::: %d [%08x]\n", i, music->filename);
-#endif
-
     if (music->filename == NULL)
       continue;
 
-    if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
+    if (strEqual(music->filename, UNDEFINED_FILENAME))
       continue;
 
     /* a configured file may be not recognized as music */
@@ -4399,7 +6795,7 @@ void LoadMusicInfo()
       if (music->filename == NULL)
        continue;
 
-      if (strcmp(basename, music->filename) == 0)
+      if (strEqual(basename, music->filename))
       {
        music_already_used = TRUE;
        break;
@@ -4435,7 +6831,7 @@ void LoadMusicInfo()
     if (sound->filename == NULL)
       continue;
 
-    if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
+    if (strEqual(sound->filename, UNDEFINED_FILENAME))
       continue;
 
     /* a configured file may be not recognized as sound */
@@ -4455,7 +6851,6 @@ void LoadMusicInfo()
   }
 
 #if 0
-  /* TEST-ONLY */
   for (next = music_file_info; next != NULL; next = next->next)
     printf("::: title == '%s'\n", next->title);
 #endif
@@ -4535,7 +6930,7 @@ void LoadHelpAnimInfo()
                 i_to_a(element_action_info[i].value));
 
   /* do not store direction index (bit) here, but direction value! */
-  for (i = 0; i < NUM_DIRECTIONS; i++)
+  for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
     setHashEntry(direction_hash, element_direction_info[i].suffix,
                 i_to_a(1 << element_direction_info[i].value));
 
@@ -4545,7 +6940,7 @@ void LoadHelpAnimInfo()
     char *element_value, *action_value, *direction_value;
     int delay = atoi(list->value);
 
-    if (strcmp(list->token, "end") == 0)
+    if (strEqual(list->token, "end"))
     {
       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
 
@@ -4696,9 +7091,9 @@ void LoadHelpAnimInfo()
   freeSetupFileHash(direction_hash);
 
 #if 0
-  /* TEST ONLY */
   for (i = 0; i < num_list_entries; i++)
-    printf("::: %d, %d, %d => %d\n",
+    printf("::: '%s': %d, %d, %d => %d\n",
+          EL_NAME(helpanim_info[i].element),
           helpanim_info[i].element,
           helpanim_info[i].action,
           helpanim_info[i].direction,
@@ -4732,7 +7127,6 @@ void LoadHelpTextInfo()
   }
 
 #if 0
-  /* TEST ONLY */
   BEGIN_HASH_ITERATION(helptext_info, itr)
   {
     printf("::: '%s' => '%s'\n",
@@ -4741,3 +7135,117 @@ void LoadHelpTextInfo()
   END_HASH_ITERATION(hash, itr)
 #endif
 }
+
+
+/* ------------------------------------------------------------------------- *
+ * convert levels
+ * ------------------------------------------------------------------------- */
+
+#define MAX_NUM_CONVERT_LEVELS         1000
+
+void ConvertLevels()
+{
+  static LevelDirTree *convert_leveldir = NULL;
+  static int convert_level_nr = -1;
+  static int num_levels_handled = 0;
+  static int num_levels_converted = 0;
+  static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
+  int i;
+
+  convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
+                                              global.convert_leveldir);
+
+  if (convert_leveldir == NULL)
+    Error(ERR_EXIT, "no such level identifier: '%s'",
+         global.convert_leveldir);
+
+  leveldir_current = convert_leveldir;
+
+  if (global.convert_level_nr != -1)
+  {
+    convert_leveldir->first_level = global.convert_level_nr;
+    convert_leveldir->last_level  = global.convert_level_nr;
+  }
+
+  convert_level_nr = convert_leveldir->first_level;
+
+  printf_line("=", 79);
+  printf("Converting levels\n");
+  printf_line("-", 79);
+  printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
+  printf("Level series name:       '%s'\n", convert_leveldir->name);
+  printf("Level series author:     '%s'\n", convert_leveldir->author);
+  printf("Number of levels:        %d\n",   convert_leveldir->levels);
+  printf_line("=", 79);
+  printf("\n");
+
+  for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
+    levels_failed[i] = FALSE;
+
+  while (convert_level_nr <= convert_leveldir->last_level)
+  {
+    char *level_filename;
+    boolean new_level;
+
+    level_nr = convert_level_nr++;
+
+    printf("Level %03d: ", level_nr);
+
+    LoadLevel(level_nr);
+    if (level.no_valid_file)
+    {
+      printf("(no level)\n");
+      continue;
+    }
+
+    printf("converting level ... ");
+
+    level_filename = getDefaultLevelFilename(level_nr);
+    new_level = !fileExists(level_filename);
+
+    if (new_level)
+    {
+      SaveLevel(level_nr);
+
+      num_levels_converted++;
+
+      printf("converted.\n");
+    }
+    else
+    {
+      if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
+       levels_failed[level_nr] = TRUE;
+
+      printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
+    }
+
+    num_levels_handled++;
+  }
+
+  printf("\n");
+  printf_line("=", 79);
+  printf("Number of levels handled: %d\n", num_levels_handled);
+  printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
+        (num_levels_handled ?
+         num_levels_converted * 100 / num_levels_handled : 0));
+  printf_line("-", 79);
+  printf("Summary (for automatic parsing by scripts):\n");
+  printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
+        convert_leveldir->identifier, num_levels_converted,
+        num_levels_handled,
+        (num_levels_handled ?
+         num_levels_converted * 100 / num_levels_handled : 0));
+
+  if (num_levels_handled != num_levels_converted)
+  {
+    printf(", FAILED:");
+    for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
+      if (levels_failed[i])
+       printf(" %03d", i);
+  }
+
+  printf("\n");
+  printf_line("=", 79);
+
+  CloseAllAndExit(0);
+}