rnd-20030703-2-src
[rocksndiamonds.git] / src / files.c
index d13c28716f8a8fea115d496ca301432267ca3060..e0538955522119f6077b73d33d34e596e34750ee 100644 (file)
@@ -17,6 +17,7 @@
 #include "libgame/libgame.h"
 
 #include "files.h"
+#include "init.h"
 #include "tools.h"
 #include "tape.h"
 
 #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    14      /* unused level header bytes  */
+#define LEVEL_HEADER_UNUSED    13      /* 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_CPART_CUS3_SIZE  102     /* size of CUS3 chunk part    */
+#define LEVEL_CPART_CUS3_UNUSED        16      /* unused CUS3 bytes / part   */
 #define TAPE_HEADER_SIZE       20      /* size of tape file header   */
 #define TAPE_HEADER_UNUSED     3       /* unused tape header bytes   */
 
+#define LEVEL_CHUNK_CUS3_SIZE(x) (2 + x * LEVEL_CPART_CUS3_SIZE)
+
 /* file identifier strings */
 #define LEVEL_COOKIE_TMPL      "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
 #define TAPE_COOKIE_TMPL       "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
@@ -44,7 +49,7 @@
 
 static void setLevelInfoToDefaults()
 {
-  int i, x, y;
+  int i, j, x, y;
 
   level.file_version = FILE_VERSION_ACTUAL;
   level.game_version = GAME_VERSION_ACTUAL;
@@ -72,6 +77,8 @@ static void setLevelInfoToDefaults()
   level.gravity = FALSE;
   level.em_slippery_gems = FALSE;
 
+  level.use_custom_template = FALSE;
+
   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
     level.name[i] = '\0';
   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
@@ -83,11 +90,11 @@ static void setLevelInfoToDefaults()
   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
     level.score[i] = 10;
 
-  level.num_yam_contents = STD_ELEMENT_CONTENTS;
+  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.yam_content[i][x][y] =
+       level.yamyam_content[i][x][y] =
          (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
 
   Feld[0][0] = Ur[0][0] = EL_PLAYER_1;
@@ -96,8 +103,64 @@ static void setLevelInfoToDefaults()
 
   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
   {
-    level.custom_element_successor[i] = EL_EMPTY_SPACE;
-    Properties1[EL_CUSTOM_START + i] = EP_BITMASK_DEFAULT;
+    int element = EL_CUSTOM_START + i;
+
+    element_info[element].use_gfx_element = FALSE;
+    element_info[element].gfx_element = EL_EMPTY_SPACE;
+
+    element_info[element].score = 0;
+    element_info[element].gem_count = 0;
+
+    element_info[element].push_delay_fixed = 2;                /* special default */
+    element_info[element].push_delay_random = 8;       /* special default */
+    element_info[element].move_delay_fixed = 0;
+    element_info[element].move_delay_random = 0;
+
+    element_info[element].move_pattern = MV_ALL_DIRECTIONS;
+    element_info[element].move_direction_initial = MV_NO_MOVING;
+    element_info[element].move_stepsize = TILEX / 8;
+
+    for(x=0; x<3; x++)
+      for(y=0; y<3; y++)
+       element_info[element].content[x][y] = EL_EMPTY_SPACE;
+
+    element_info[element].change.events = CE_BITMASK_DEFAULT;
+    element_info[element].change.target_element = EL_EMPTY_SPACE;
+
+    element_info[element].change.delay_fixed = 0;
+    element_info[element].change.delay_random = 0;
+    element_info[element].change.delay_frames = -1;    /* use default */
+
+    element_info[element].change.trigger_element = EL_EMPTY_SPACE;
+
+    element_info[element].change.explode = FALSE;
+    element_info[element].change.use_content = FALSE;
+    element_info[element].change.only_complete = FALSE;
+    element_info[element].change.use_random_change = FALSE;
+    element_info[element].change.random = 0;
+    element_info[element].change.power = CP_NON_DESTRUCTIVE;
+
+    for(x=0; x<3; x++)
+      for(y=0; y<3; y++)
+       element_info[element].change.content[x][y] = EL_EMPTY_SPACE;
+
+    element_info[element].access_type = 0;
+    element_info[element].access_layer = 0;
+    element_info[element].walk_to_action = 0;
+    element_info[element].smash_targets = 0;
+    element_info[element].deadliness = 0;
+    element_info[element].consistency = 0;
+    element_info[element].change_player_action = 0;
+    element_info[element].change_collide_action = 0;
+    element_info[element].change_other_action = 0;
+
+    element_info[element].can_explode_by_fire = FALSE;
+    element_info[element].can_explode_smashed = FALSE;
+    element_info[element].can_explode_impact = FALSE;
+
+    /* start with no properties at all */
+    for (j=0; j < NUM_EP_BITFIELDS; j++)
+      Properties[element][j] = EP_BITMASK_DEFAULT;
   }
 
   BorderElement = EL_STEELWALL;
@@ -178,11 +241,11 @@ static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
     level->score[i] = fgetc(file);
 
-  level->num_yam_contents = STD_ELEMENT_CONTENTS;
+  level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
   for(i=0; i<STD_ELEMENT_CONTENTS; i++)
     for(y=0; y<3; y++)
       for(x=0; x<3; x++)
-       level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
+       level->yamyam_content[i][x][y] = checkLevelElement(fgetc(file));
 
   level->amoeba_speed          = fgetc(file);
   level->time_magic_wall       = fgetc(file);
@@ -193,6 +256,8 @@ static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
   level->encoding_16bit_field  = (fgetc(file) == 1 ? TRUE : FALSE);
   level->em_slippery_gems      = (fgetc(file) == 1 ? TRUE : FALSE);
 
+  level->use_custom_template   = (fgetc(file) == 1 ? TRUE : FALSE);
+
   ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
 
   return chunk_size;
@@ -258,19 +323,19 @@ static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
   }
 
   fgetc(file);
-  level->num_yam_contents = fgetc(file);
+  level->num_yamyam_contents = fgetc(file);
   fgetc(file);
   fgetc(file);
 
   /* correct invalid number of content fields -- should never happen */
-  if (level->num_yam_contents < 1 ||
-      level->num_yam_contents > MAX_ELEMENT_CONTENTS)
-    level->num_yam_contents = STD_ELEMENT_CONTENTS;
+  if (level->num_yamyam_contents < 1 ||
+      level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
+    level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
 
   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
     for(y=0; y<3; y++)
       for(x=0; x<3; x++)
-       level->yam_content[i][x][y] =
+       level->yamyam_content[i][x][y] =
          checkLevelElement(level->encoding_16bit_field ?
                            getFile16BitBE(file) : fgetc(file));
   return chunk_size;
@@ -300,12 +365,12 @@ static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
 
   if (element == EL_YAMYAM)
   {
-    level->num_yam_contents = num_contents;
+    level->num_yamyam_contents = num_contents;
 
     for(i=0; i<num_contents; i++)
       for(y=0; y<3; y++)
        for(x=0; x<3; x++)
-         level->yam_content[i][x][y] = content_array[i][x][y];
+         level->yamyam_content[i][x][y] = content_array[i][x][y];
   }
   else if (element == EL_BD_AMOEBA)
   {
@@ -337,7 +402,7 @@ static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
     int properties = getFile32BitBE(file);
 
     if (IS_CUSTOM_ELEMENT(element))
-      Properties1[element] = properties;
+      Properties[element][EP_BITFIELD_BASE] = properties;
     else
       Error(ERR_WARN, "invalid custom element number %d", element);
   }
@@ -360,11 +425,10 @@ static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
   for (i=0; i < num_changed_custom_elements; i++)
   {
     int element = getFile16BitBE(file);
-    int custom_element_successor = getFile16BitBE(file);
-    int i = element - EL_CUSTOM_START;
+    int custom_target_element = getFile16BitBE(file);
 
     if (IS_CUSTOM_ELEMENT(element))
-      level->custom_element_successor[i] = custom_element_successor;
+      element_info[element].change.target_element = custom_target_element;
     else
       Error(ERR_WARN, "invalid custom element number %d", element);
   }
@@ -372,6 +436,87 @@ static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
   return chunk_size;
 }
 
+static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
+{
+  int num_changed_custom_elements = getFile16BitBE(file);
+  int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
+  int i, x, y;
+
+  if (chunk_size_expected != chunk_size)
+  {
+    ReadUnusedBytesFromFile(file, chunk_size - 2);
+    return chunk_size_expected;
+  }
+
+  for (i=0; i < num_changed_custom_elements; i++)
+  {
+    int element = getFile16BitBE(file);
+
+    if (!IS_CUSTOM_ELEMENT(element))
+    {
+      Error(ERR_WARN, "invalid custom element number %d", element);
+
+      element = EL_DEFAULT;    /* dummy element used for artwork config */
+    }
+
+    Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
+
+    /* some free bytes for future properties and padding */
+    ReadUnusedBytesFromFile(file, 7);
+
+    element_info[element].use_gfx_element = getFile8Bit(file);
+    element_info[element].gfx_element =
+      checkLevelElement(getFile16BitBE(file));
+
+    element_info[element].score = getFile8Bit(file);
+    element_info[element].gem_count = 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);
+
+    element_info[element].move_pattern = getFile16BitBE(file);
+    element_info[element].move_direction_initial = getFile8Bit(file);
+    element_info[element].move_stepsize = getFile8Bit(file);
+
+    for(y=0; y<3; y++)
+      for(x=0; x<3; x++)
+       element_info[element].content[x][y] =
+         checkLevelElement(getFile16BitBE(file));
+
+    element_info[element].change.events = getFile32BitBE(file);
+
+    element_info[element].change.target_element =
+      checkLevelElement(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);
+
+    element_info[element].change.trigger_element =
+      checkLevelElement(getFile16BitBE(file));
+
+    element_info[element].change.explode = getFile8Bit(file);
+    element_info[element].change.use_content = getFile8Bit(file);
+    element_info[element].change.only_complete = getFile8Bit(file);
+    element_info[element].change.use_random_change = getFile8Bit(file);
+
+    element_info[element].change.random = getFile8Bit(file);
+    element_info[element].change.power = getFile8Bit(file);
+
+    for(y=0; y<3; y++)
+      for(x=0; x<3; x++)
+       element_info[element].change.content[x][y] =
+         checkLevelElement(getFile16BitBE(file));
+
+    /* some free bytes for future properties and padding */
+    ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
+  }
+
+  return chunk_size;
+}
+
 void LoadLevelFromFilename(char *filename)
 {
   char cookie[MAX_LINE_LEN];
@@ -452,6 +597,7 @@ void LoadLevelFromFilename(char *filename)
       { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
       { "CUS1", -1,                    LoadLevel_CUS1 },
       { "CUS2", -1,                    LoadLevel_CUS2 },
+      { "CUS3", -1,                    LoadLevel_CUS3 },
       {  NULL,  0,                     NULL }
     };
 
@@ -581,6 +727,7 @@ void LoadLevel(int level_nr)
   char *filename = getLevelFilename(level_nr);
 
   LoadLevelFromFilename(filename);
+  InitElementPropertiesEngine(level.game_version);
 }
 
 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
@@ -609,7 +756,7 @@ static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
     for(y=0; y<3; y++)
       for(x=0; x<3; x++)
        fputc((level->encoding_16bit_yamyam ? EL_EMPTY :
-              level->yam_content[i][x][y]),
+              level->yamyam_content[i][x][y]),
              file);
   fputc(level->amoeba_speed, file);
   fputc(level->time_magic_wall, file);
@@ -621,6 +768,8 @@ static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
   fputc((level->encoding_16bit_field ? 1 : 0), file);
   fputc((level->em_slippery_gems ? 1 : 0), file);
 
+  fputc((level->use_custom_template ? 1 : 0), file);
+
   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
 }
 
@@ -650,7 +799,7 @@ static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
   int i, x, y;
 
   fputc(EL_YAMYAM, file);
-  fputc(level->num_yam_contents, file);
+  fputc(level->num_yamyam_contents, file);
   fputc(0, file);
   fputc(0, file);
 
@@ -658,9 +807,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->yam_content[i][x][y]);
+         putFile16BitBE(file, level->yamyam_content[i][x][y]);
        else
-         fputc(level->yam_content[i][x][y], file);
+         fputc(level->yamyam_content[i][x][y], file);
 }
 #endif
 
@@ -672,14 +821,14 @@ static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
 
   if (element == EL_YAMYAM)
   {
-    num_contents = level->num_yam_contents;
+    num_contents = level->num_yamyam_contents;
     content_xsize = 3;
     content_ysize = 3;
 
     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->yam_content[i][x][y];
+         content_array[i][x][y] = level->yamyam_content[i][x][y];
   }
   else if (element == EL_BD_AMOEBA)
   {
@@ -715,6 +864,7 @@ static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
        putFile16BitBE(file, content_array[i][x][y]);
 }
 
+#if 0
 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
                           int num_changed_custom_elements)
 {
@@ -726,12 +876,12 @@ static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
   {
     int element = EL_CUSTOM_START + i;
 
-    if (Properties1[element] != EP_BITMASK_DEFAULT)
+    if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
     {
       if (check < num_changed_custom_elements)
       {
        putFile16BitBE(file, element);
-       putFile32BitBE(file, Properties1[element]);
+       putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
       }
 
       check++;
@@ -741,7 +891,9 @@ static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
   if (check != num_changed_custom_elements)    /* should not happen */
     Error(ERR_WARN, "inconsistent number of custom element properties");
 }
+#endif
 
+#if 0
 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
                           int num_changed_custom_elements)
 {
@@ -753,12 +905,87 @@ static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
   {
     int element = EL_CUSTOM_START + i;
 
-    if (level->custom_element_successor[i] != EL_EMPTY_SPACE)
+    if (element_info[element].change.target_element != EL_EMPTY_SPACE)
+    {
+      if (check < num_changed_custom_elements)
+      {
+       putFile16BitBE(file, element);
+       putFile16BitBE(file, element_info[element].change.target_element);
+      }
+
+      check++;
+    }
+  }
+
+  if (check != num_changed_custom_elements)    /* should not happen */
+    Error(ERR_WARN, "inconsistent number of custom target elements");
+}
+#endif
+
+static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
+                          int num_changed_custom_elements)
+{
+  int i, x, y, check = 0;
+
+  putFile16BitBE(file, num_changed_custom_elements);
+
+  for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+  {
+    int element = EL_CUSTOM_START + i;
+
+    if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
     {
       if (check < num_changed_custom_elements)
       {
        putFile16BitBE(file, element);
-       putFile16BitBE(file, level->custom_element_successor[i]);
+       putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
+
+       /* 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, element_info[element].score);
+       putFile8Bit(file, element_info[element].gem_count);
+
+       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, element_info[element].move_pattern);
+       putFile8Bit(file, element_info[element].move_direction_initial);
+       putFile8Bit(file, element_info[element].move_stepsize);
+
+       for(y=0; y<3; y++)
+         for(x=0; x<3; x++)
+           putFile16BitBE(file, element_info[element].content[x][y]);
+
+       putFile32BitBE(file, element_info[element].change.events);
+
+       putFile16BitBE(file, element_info[element].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, element_info[element].change.trigger_element);
+
+       putFile8Bit(file, element_info[element].change.explode);
+       putFile8Bit(file, element_info[element].change.use_content);
+       putFile8Bit(file, element_info[element].change.only_complete);
+       putFile8Bit(file, element_info[element].change.use_random_change);
+
+       putFile8Bit(file, element_info[element].change.random);
+       putFile8Bit(file, element_info[element].change.power);
+
+       for(y=0; y<3; y++)
+         for(x=0; x<3; x++)
+           putFile16BitBE(file, element_info[element].change.content[x][y]);
+
+       /* some free bytes for future properties and padding */
+       WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
       }
 
       check++;
@@ -766,15 +993,15 @@ static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
   }
 
   if (check != num_changed_custom_elements)    /* should not happen */
-    Error(ERR_WARN, "inconsistent number of custom element successors");
+    Error(ERR_WARN, "inconsistent number of custom element properties");
 }
 
 void SaveLevel(int level_nr)
 {
   char *filename = getLevelFilename(level_nr);
   int body_chunk_size;
-  int num_changed_custom_elements1 = 0;
-  int num_changed_custom_elements2 = 0;
+  int num_changed_custom_elements = 0;
+  int level_chunk_CUS3_size;
   int i, x, y;
   FILE *file;
 
@@ -796,10 +1023,10 @@ void SaveLevel(int level_nr)
 
   /* check yamyam content for 16-bit elements */
   level.encoding_16bit_yamyam = FALSE;
-  for(i=0; i<level.num_yam_contents; i++)
+  for(i=0; i<level.num_yamyam_contents; i++)
     for(y=0; y<3; y++)
       for(x=0; x<3; x++)
-       if (level.yam_content[i][x][y] > 255)
+       if (level.yamyam_content[i][x][y] > 255)
          level.encoding_16bit_yamyam = TRUE;
 
   /* check amoeba content for 16-bit elements */
@@ -811,15 +1038,11 @@ void SaveLevel(int level_nr)
   body_chunk_size =
     level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
 
-  /* check for non-standard custom elements and calculate "CUS1" chunk size */
+  /* check for non-standard custom elements and calculate "CUS3" chunk size */
   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
-    if (Properties1[EL_CUSTOM_START + i] != EP_BITMASK_DEFAULT)
-      num_changed_custom_elements1++;
-
-  /* check for non-standard custom elements and calculate "CUS2" chunk size */
-  for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
-    if (level.custom_element_successor[i] != EL_EMPTY_SPACE)
-      num_changed_custom_elements2++;
+    if (Properties[EL_CUSTOM_START +i][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
+      num_changed_custom_elements++;
+  level_chunk_CUS3_size = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
 
   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
@@ -837,7 +1060,7 @@ void SaveLevel(int level_nr)
   SaveLevel_BODY(file, &level);
 
   if (level.encoding_16bit_yamyam ||
-      level.num_yam_contents != STD_ELEMENT_CONTENTS)
+      level.num_yamyam_contents != STD_ELEMENT_CONTENTS)
   {
     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
     SaveLevel_CNT2(file, &level, EL_YAMYAM);
@@ -849,16 +1072,10 @@ void SaveLevel(int level_nr)
     SaveLevel_CNT2(file, &level, EL_BD_AMOEBA);
   }
 
-  if (num_changed_custom_elements1 > 0)
-  {
-    putFileChunkBE(file, "CUS1", 2 + num_changed_custom_elements1 * 6);
-    SaveLevel_CUS1(file, &level, num_changed_custom_elements1);
-  }
-
-  if (num_changed_custom_elements2 > 0)
+  if (num_changed_custom_elements > 0)
   {
-    putFileChunkBE(file, "CUS2", 2 + num_changed_custom_elements2 * 4);
-    SaveLevel_CUS2(file, &level, num_changed_custom_elements2);
+    putFileChunkBE(file, "CUS3", level_chunk_CUS3_size);
+    SaveLevel_CUS3(file, &level, num_changed_custom_elements);
   }
 
   fclose(file);
@@ -869,7 +1086,7 @@ void SaveLevel(int level_nr)
 void DumpLevel(struct LevelInfo *level)
 {
   printf_line("-", 79);
-  printf("Level xxx (file version %06d, game version %06d)\n",
+  printf("Level xxx (file version %08d, game version %08d)\n",
         level->file_version, level->game_version);
   printf_line("-", 79);
 
@@ -1189,6 +1406,10 @@ void LoadTapeFromFilename(char *filename)
   fclose(file);
 
   tape.length_seconds = GetTapeLength();
+
+#if 0
+  printf("tape version: %d\n", tape.game_version);
+#endif
 }
 
 void LoadTape(int level_nr)
@@ -1326,7 +1547,7 @@ void DumpTape(struct TapeInfo *tape)
   }
 
   printf_line("-", 79);
-  printf("Tape of Level %03d (file version %06d, game version %06d)\n",
+  printf("Tape of Level %03d (file version %08d, game version %08d)\n",
         tape->level_nr, tape->file_version, tape->game_version);
   printf("Level series identifier: '%s'\n", tape->level_identifier);
   printf_line("-", 79);
@@ -1698,32 +1919,32 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->options.verbose = FALSE;
 }
 
-static void decodeSetupFileList(struct SetupFileList *setup_file_list)
+static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
 {
   int i, pnr;
 
-  if (!setup_file_list)
+  if (!setup_file_hash)
     return;
 
   /* global setup */
   si = setup;
   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
     setSetupInfo(global_setup_tokens, i,
-                getTokenValue(setup_file_list, global_setup_tokens[i].text));
+                getHashEntry(setup_file_hash, global_setup_tokens[i].text));
   setup = si;
 
   /* editor setup */
   sei = setup.editor;
   for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
     setSetupInfo(editor_setup_tokens, i,
-                getTokenValue(setup_file_list,editor_setup_tokens[i].text));
+                getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
   setup.editor = sei;
 
   /* shortcut setup */
   ssi = setup.shortcut;
   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
     setSetupInfo(shortcut_setup_tokens, i,
-                getTokenValue(setup_file_list,shortcut_setup_tokens[i].text));
+                getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
   setup.shortcut = ssi;
 
   /* player setup */
@@ -1740,7 +1961,7 @@ static void decodeSetupFileList(struct SetupFileList *setup_file_list)
 
       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
       setSetupInfo(player_setup_tokens, i,
-                  getTokenValue(setup_file_list, full_token));
+                  getHashEntry(setup_file_hash, full_token));
     }
     setup.input[pnr] = sii;
   }
@@ -1749,37 +1970,37 @@ static void decodeSetupFileList(struct SetupFileList *setup_file_list)
   syi = setup.system;
   for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
     setSetupInfo(system_setup_tokens, i,
-                getTokenValue(setup_file_list, system_setup_tokens[i].text));
+                getHashEntry(setup_file_hash, system_setup_tokens[i].text));
   setup.system = syi;
 
   /* options setup */
   soi = setup.options;
   for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
     setSetupInfo(options_setup_tokens, i,
-                getTokenValue(setup_file_list, options_setup_tokens[i].text));
+                getHashEntry(setup_file_hash, options_setup_tokens[i].text));
   setup.options = soi;
 }
 
 void LoadSetup()
 {
   char *filename = getSetupFilename();
-  struct SetupFileList *setup_file_list = NULL;
+  SetupFileHash *setup_file_hash = NULL;
 
   /* always start with reliable default values */
   setSetupInfoToDefaults(&setup);
 
-  setup_file_list = loadSetupFileList(filename);
+  setup_file_hash = loadSetupFileHash(filename);
 
-  if (setup_file_list)
+  if (setup_file_hash)
   {
     char *player_name_new;
 
-    checkSetupFileListIdentifier(setup_file_list, getCookie("SETUP"));
-    decodeSetupFileList(setup_file_list);
+    checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
+    decodeSetupFileHash(setup_file_hash);
 
     setup.direct_draw = !setup.double_buffering;
 
-    freeSetupFileList(setup_file_list);
+    freeSetupFileHash(setup_file_hash);
 
     /* needed to work around problems with fixed length strings */
     player_name_new = get_corrected_login_name(setup.player_name);
@@ -1865,7 +2086,7 @@ void SaveSetup()
 void LoadCustomElementDescriptions()
 {
   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
-  struct SetupFileList *setup_file_list;
+  SetupFileHash *setup_file_hash;
   int i;
 
   for (i=0; i<NUM_FILE_ELEMENTS; i++)
@@ -1877,13 +2098,13 @@ void LoadCustomElementDescriptions()
     }
   }
 
-  if ((setup_file_list = loadSetupFileList(filename)) == NULL)
+  if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
     return;
 
   for (i=0; i<NUM_FILE_ELEMENTS; i++)
   {
     char *token = getStringCat2(element_info[i].token_name, ".name");
-    char *value = getTokenValue(setup_file_list, token);
+    char *value = getHashEntry(setup_file_hash, token);
 
     if (value != NULL)
       element_info[i].custom_description = getStringCopy(value);
@@ -1891,54 +2112,48 @@ void LoadCustomElementDescriptions()
     free(token);
   }
 
-  freeSetupFileList(setup_file_list);
+  freeSetupFileHash(setup_file_hash);
 }
 
 void LoadSpecialMenuDesignSettings()
 {
   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
-  struct SetupFileList *setup_file_list;
-  char *value;
-
-  /* !!! CHANGE THIS !!! (redundant initialization) !!! */
-  global.num_toons = 20;
-  global.menu_draw_xoffset = 0;
-  global.menu_draw_yoffset = 0;
-  global.menu_draw_xoffset_MAIN = 0;
-  global.menu_draw_yoffset_MAIN = 0;
-  global.door_step_offset = 2;
-  global.door_step_delay = 10;
-
-  if ((setup_file_list = loadSetupFileList(filename)) == NULL)
-    return;
-
-  value = getTokenValue(setup_file_list, "global.num_toons");
-  if (value != NULL)
-    global.num_toons = get_integer_from_string(value);
-
-  value = getTokenValue(setup_file_list, "menu.draw_xoffset");
-  if (value != NULL)
-    global.menu_draw_xoffset = get_integer_from_string(value);
+  SetupFileHash *setup_file_hash;
+  int i, j;
 
-  value = getTokenValue(setup_file_list, "menu.draw_yoffset");
-  if (value != NULL)
-    global.menu_draw_yoffset = get_integer_from_string(value);
+  /* 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_integer_from_string(image_config[j].value);
 
-  value = getTokenValue(setup_file_list, "menu.draw_xoffset.MAIN");
-  if (value != NULL)
-    global.menu_draw_xoffset_MAIN = get_integer_from_string(value);
+  if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
+    return;
 
-  value = getTokenValue(setup_file_list, "menu.draw_yoffset.MAIN");
-  if (value != NULL)
-    global.menu_draw_yoffset_MAIN = get_integer_from_string(value);
+  /* special case: initialize with default values that may be overwritten */
+  for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
+  {
+    char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
+    char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
+    char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
+
+    if (value_x != NULL)
+      menu.draw_xoffset[i] = get_integer_from_string(value_x);
+    if (value_y != NULL)
+      menu.draw_yoffset[i] = get_integer_from_string(value_y);
+    if (list_size != NULL)
+      menu.list_size[i] = get_integer_from_string(list_size);
+  }
 
-  value = getTokenValue(setup_file_list, "door.step_offset");
-  if (value != NULL)
-    global.door_step_offset = get_integer_from_string(value);
+  /* read (and overwrite with) values that may be specified in config file */
+  for (i=0; image_config_vars[i].token != NULL; i++)
+  {
+    char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
 
-  value = getTokenValue(setup_file_list, "door.step_delay");
-  if (value != NULL)
-    global.door_step_delay = get_integer_from_string(value);
+    if (value != NULL)
+      *image_config_vars[i].value = get_integer_from_string(value);
+  }
 
-  freeSetupFileList(setup_file_list);
+  freeSetupFileHash(setup_file_hash);
 }