fixed compiler warning about uninitialized local variable
[rocksndiamonds.git] / src / files.c
index 6882aaa584737ed2404631c52d2828526d0e05ca..6c150ea0f86b3ea4be3de7cc8a7ed79b252036c2 100644 (file)
@@ -20,6 +20,7 @@
 #include "init.h"
 #include "tools.h"
 #include "tape.h"
+#include "config.h"
 
 #define ENABLE_UNUSED_CODE     0       /* currently unused functions */
 #define ENABLE_HISTORIC_CHUNKS 0       /* only for historic reference */
@@ -1765,14 +1766,19 @@ static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
 }
 
 static void setLevelInfoToDefaults(struct LevelInfo *level,
-                                  boolean level_info_only)
+                                  boolean level_info_only,
+                                  boolean reset_file_status)
 {
   setLevelInfoToDefaults_Level(level);
 
   if (!level_info_only)
     setLevelInfoToDefaults_Elements(level);
 
-  level->no_valid_file = FALSE;
+  if (reset_file_status)
+  {
+    level->no_valid_file = FALSE;
+    level->no_level_file = FALSE;
+  }
 
   level->changed = FALSE;
 }
@@ -1861,13 +1867,14 @@ static void ActivateLevelTemplate()
 
 static char *getLevelFilenameFromBasename(char *basename)
 {
-  static char *filename = NULL;
+  static char *filename[2] = { NULL, NULL };
+  int pos = (strEqual(basename, LEVELTEMPLATE_FILENAME) ? 0 : 1);
 
-  checked_free(filename);
+  checked_free(filename[pos]);
 
-  filename = getPath2(getCurrentLevelDir(), basename);
+  filename[pos] = getPath2(getCurrentLevelDir(), basename);
 
-  return filename;
+  return filename[pos];
 }
 
 static int getFileTypeFromBasename(char *basename)
@@ -1921,7 +1928,7 @@ static char *getSingleLevelBasenameExt(int nr, char *extension)
   static char basename[MAX_FILENAME_LEN];
 
   if (nr < 0)
-    sprintf(basename, "template.%s", extension);
+    sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
   else
     sprintf(basename, "%03d.%s", nr, extension);
 
@@ -2053,31 +2060,47 @@ static int getFiletypeFromID(char *filetype_id)
   return filetype;
 }
 
-static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
+char *getLocalLevelTemplateFilename()
 {
-  int nr = lfi->nr;
+  return getDefaultLevelFilename(-1);
+}
 
-  /* special case: level number is negative => check for level template file */
-  if (nr < 0)
+char *getGlobalLevelTemplateFilename()
+{
+  /* global variable "leveldir_current" must be modified in the loop below */
+  LevelDirTree *leveldir_current_last = leveldir_current;
+  char *filename = NULL;
+
+  /* check for template level in path from current to topmost tree node */
+
+  while (leveldir_current != NULL)
   {
-    /* global variable "leveldir_current" must be modified in the loop below */
-    LevelDirTree *leveldir_current_last = leveldir_current;
+    filename = getDefaultLevelFilename(-1);
 
-    /* check for template level in path from current to topmost tree node */
+    if (fileExists(filename))
+      break;
 
-    while (leveldir_current != NULL)
-    {
-      setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
-                                          "template.%s", LEVELFILE_EXTENSION);
+    leveldir_current = leveldir_current->node_parent;
+  }
 
-      if (fileExists(lfi->filename))
-       break;
+  /* restore global variable "leveldir_current" modified in above loop */
+  leveldir_current = leveldir_current_last;
 
-      leveldir_current = leveldir_current->node_parent;
-    }
+  return filename;
+}
+
+static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
+{
+  int nr = lfi->nr;
+
+  /* special case: level number is negative => check for level template file */
+  if (nr < 0)
+  {
+    setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
+                                        getSingleLevelBasename(-1));
 
-    /* restore global variable "leveldir_current" modified in above loop */
-    leveldir_current = leveldir_current_last;
+    /* replace local level template filename with global template filename */
+    lfi->filename = getGlobalLevelTemplateFilename();
 
     /* no fallback if template file not existing */
     return;
@@ -3164,11 +3187,26 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
   if (!(file = openFile(filename, MODE_READ)))
   {
     level->no_valid_file = TRUE;
+    level->no_level_file = TRUE;
 
-    if (!level_info_only)
-      Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
+    if (level_info_only)
+      return;
 
-    return;
+    Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
+
+    if (!setup.editor.use_template_for_new_levels)
+      return;
+
+    /* if level file not found, try to initialize level data from template */
+    filename = getGlobalLevelTemplateFilename();
+
+    if (!(file = openFile(filename, MODE_READ)))
+      return;
+
+    /* default: for empty levels, use level template for custom elements */
+    level->use_custom_template = TRUE;
+
+    level->no_valid_file = FALSE;
   }
 
   getFileChunkBE(file, chunk_name, NULL);
@@ -3737,10 +3775,20 @@ static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
   level_sp->header.DemoRandomSeed = tape.random_seed;
 
   demo->length = 0;
+
   for (i = 0; i < tape.length; i++)
   {
     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
     int demo_repeat = tape.pos[i].delay;
+    int demo_entries = (demo_repeat + 15) / 16;
+
+    if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
+    {
+      Error(ERR_WARN, "tape truncated: size exceeds maximum SP demo size %d",
+           SP_MAX_TAPE_LEN);
+
+      break;
+    }
 
     for (j = 0; j < demo_repeat / 16; j++)
       demo->data[demo->length++] = 0xf0 | demo_action;
@@ -3749,8 +3797,6 @@ static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
   }
 
-  demo->data[demo->length++] = 0xff;
-
   demo->is_available = TRUE;
 }
 
@@ -3770,22 +3816,36 @@ static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
     return;
 
   tape.level_nr = demo->level_nr;      /* (currently not used) */
-  tape.length = demo->length - 1;      /* without "end of demo" byte */
   tape.random_seed = level_sp->header.DemoRandomSeed;
 
   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
 
-  for (i = 0; i < demo->length - 1; i++)
+  tape.counter = 0;
+  tape.pos[tape.counter].delay = 0;
+
+  for (i = 0; i < demo->length; i++)
   {
     int demo_action = demo->data[i] & 0x0f;
     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
+    int tape_action = map_key_SP_to_RND(demo_action);
+    int tape_repeat = demo_repeat + 1;
+    byte action[MAX_PLAYERS] = { tape_action, 0, 0, 0 };
+    boolean success = 0;
+    int j;
 
-    tape.pos[i].action[0] = map_key_SP_to_RND(demo_action);
-    tape.pos[i].delay = demo_repeat + 1;
+    for (j = 0; j < tape_repeat; j++)
+      success = TapeAddAction(action);
+
+    if (!success)
+    {
+      Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
+           MAX_TAPE_LEN);
+
+      break;
+    }
   }
 
-  tape.length_frames  = GetTapeLengthFrames();
-  tape.length_seconds = GetTapeLengthSeconds();
+  TapeHaltRecording();
 }
 
 
@@ -5858,7 +5918,7 @@ static void LoadLevelFromFileInfo(struct LevelInfo *level,
                                  boolean level_info_only)
 {
   /* always start with reliable default values */
-  setLevelInfoToDefaults(level, level_info_only);
+  setLevelInfoToDefaults(level, level_info_only, TRUE);
 
   switch (level_file_info->type)
   {
@@ -5891,11 +5951,7 @@ static void LoadLevelFromFileInfo(struct LevelInfo *level,
 
   /* if level file is invalid, restore level structure to default values */
   if (level->no_valid_file)
-  {
-    setLevelInfoToDefaults(level, level_info_only);
-
-    level->no_valid_file = TRUE;       /* but keep "no valid file" flag */
-  }
+    setLevelInfoToDefaults(level, level_info_only, FALSE);
 
   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
     level->game_engine_type = GAME_ENGINE_TYPE_RND;
@@ -7032,7 +7088,8 @@ static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
   return chunk_size;
 }
 
-static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
+static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
+                                 boolean save_as_template)
 {
   int chunk_size;
   int i;
@@ -7096,7 +7153,7 @@ static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
   }
 
   /* if not using template level, check for non-default custom/group elements */
-  if (!level->use_custom_template)
+  if (!level->use_custom_template || save_as_template)
   {
     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
     {
@@ -7132,14 +7189,14 @@ void SaveLevel(int nr)
 {
   char *filename = getDefaultLevelFilename(nr);
 
-  SaveLevelFromFilename(&level, filename);
+  SaveLevelFromFilename(&level, filename, FALSE);
 }
 
 void SaveLevelTemplate()
 {
-  char *filename = getDefaultLevelFilename(-1);
+  char *filename = getLocalLevelTemplateFilename();
 
-  SaveLevelFromFilename(&level, filename);
+  SaveLevelFromFilename(&level, filename, TRUE);
 }
 
 boolean SaveLevelChecked(int nr)
@@ -7163,7 +7220,7 @@ boolean SaveLevelChecked(int nr)
 
 void DumpLevel(struct LevelInfo *level)
 {
-  if (level->no_valid_file)
+  if (level->no_level_file || level->no_valid_file)
   {
     Error(ERR_WARN, "cannot dump -- no valid level file found");
 
@@ -7313,7 +7370,16 @@ static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
   for (i = 0; i < tape->length; i++)
   {
     if (i >= MAX_TAPE_LEN)
+    {
+      Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
+           MAX_TAPE_LEN);
+
+      // tape too large; read and ignore remaining tape data from this chunk
+      for (;i < tape->length; i++)
+       ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
+
       break;
+    }
 
     for (j = 0; j < MAX_PLAYERS; j++)
     {
@@ -7867,6 +7933,7 @@ void LoadScore(int nr)
 void SaveScore(int nr)
 {
   int i;
+  int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
   char *filename = getScoreFilename(nr);
   FILE *file;
 
@@ -7885,7 +7952,7 @@ void SaveScore(int nr)
 
   fclose(file);
 
-  SetFilePermissions(filename, PERMS_PUBLIC);
+  SetFilePermissions(filename, permissions);
 }
 
 
@@ -7913,55 +7980,45 @@ void SaveScore(int nr)
 #define SETUP_TOKEN_TEAM_MODE                  14
 #define SETUP_TOKEN_HANDICAP                   15
 #define SETUP_TOKEN_SKIP_LEVELS                        16
-#define SETUP_TOKEN_TIME_LIMIT                 17
-#define SETUP_TOKEN_FULLSCREEN                 18
-#define SETUP_TOKEN_WINDOW_SCALING_PERCENT     19
-#define SETUP_TOKEN_WINDOW_SCALING_QUALITY     20
-#define SETUP_TOKEN_SCREEN_RENDERING_MODE      21
-#define SETUP_TOKEN_ASK_ON_ESCAPE              22
-#define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR       23
-#define SETUP_TOKEN_QUICK_SWITCH               24
-#define SETUP_TOKEN_INPUT_ON_FOCUS             25
-#define SETUP_TOKEN_PREFER_AGA_GRAPHICS                26
-#define SETUP_TOKEN_GAME_FRAME_DELAY           27
-#define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS    28
-#define SETUP_TOKEN_SMALL_GAME_GRAPHICS                29
-#define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS      30
-#define SETUP_TOKEN_GRAPHICS_SET               31
-#define SETUP_TOKEN_SOUNDS_SET                 32
-#define SETUP_TOKEN_MUSIC_SET                  33
-#define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS    34
-#define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS      35
-#define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC       36
-#define SETUP_TOKEN_VOLUME_SIMPLE              37
-#define SETUP_TOKEN_VOLUME_LOOPS               38
-#define SETUP_TOKEN_VOLUME_MUSIC               39
-#define SETUP_TOKEN_TOUCH_CONTROL_TYPE         40
-#define SETUP_TOKEN_TOUCH_MOVE_DISTANCE                41
-#define SETUP_TOKEN_TOUCH_DROP_DISTANCE                42
-
-#define NUM_GLOBAL_SETUP_TOKENS                        43
+#define SETUP_TOKEN_INCREMENT_LEVELS           17
+#define SETUP_TOKEN_TIME_LIMIT                 18
+#define SETUP_TOKEN_FULLSCREEN                 19
+#define SETUP_TOKEN_WINDOW_SCALING_PERCENT     20
+#define SETUP_TOKEN_WINDOW_SCALING_QUALITY     21
+#define SETUP_TOKEN_SCREEN_RENDERING_MODE      22
+#define SETUP_TOKEN_ASK_ON_ESCAPE              23
+#define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR       24
+#define SETUP_TOKEN_QUICK_SWITCH               25
+#define SETUP_TOKEN_INPUT_ON_FOCUS             26
+#define SETUP_TOKEN_PREFER_AGA_GRAPHICS                27
+#define SETUP_TOKEN_GAME_FRAME_DELAY           28
+#define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS    29
+#define SETUP_TOKEN_SMALL_GAME_GRAPHICS                30
+#define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS      31
+#define SETUP_TOKEN_GRAPHICS_SET               32
+#define SETUP_TOKEN_SOUNDS_SET                 33
+#define SETUP_TOKEN_MUSIC_SET                  34
+#define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS    35
+#define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS      36
+#define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC       37
+#define SETUP_TOKEN_VOLUME_SIMPLE              38
+#define SETUP_TOKEN_VOLUME_LOOPS               39
+#define SETUP_TOKEN_VOLUME_MUSIC               40
+#define SETUP_TOKEN_TOUCH_CONTROL_TYPE         41
+#define SETUP_TOKEN_TOUCH_MOVE_DISTANCE                42
+#define SETUP_TOKEN_TOUCH_DROP_DISTANCE                43
+
+#define NUM_GLOBAL_SETUP_TOKENS                        44
 
 /* editor setup */
-#define SETUP_TOKEN_EDITOR_EL_BOULDERDASH      0
-#define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE     1
-#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_STEEL_CHARS      9
-#define SETUP_TOKEN_EDITOR_EL_CUSTOM           10
-#define SETUP_TOKEN_EDITOR_EL_HEADLINES                11
-#define SETUP_TOKEN_EDITOR_EL_USER_DEFINED     12
-#define SETUP_TOKEN_EDITOR_EL_DYNAMIC          13
-#define SETUP_TOKEN_EDITOR_EL_BY_GAME          14
-#define SETUP_TOKEN_EDITOR_EL_BY_TYPE          15
-#define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN  16
-
-#define NUM_EDITOR_SETUP_TOKENS                        17
+#define SETUP_TOKEN_EDITOR_EL_CLASSIC          0
+#define SETUP_TOKEN_EDITOR_EL_CUSTOM           1
+#define SETUP_TOKEN_EDITOR_EL_USER_DEFINED     2
+#define SETUP_TOKEN_EDITOR_EL_DYNAMIC          3
+#define SETUP_TOKEN_EDITOR_EL_HEADLINES                4
+#define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN  5
+
+#define NUM_EDITOR_SETUP_TOKENS                        6
 
 /* editor cascade setup */
 #define SETUP_TOKEN_EDITOR_CASCADE_BD          0
@@ -8036,24 +8093,26 @@ void SaveScore(int nr)
 
 /* internal setup */
 #define SETUP_TOKEN_INT_PROGRAM_TITLE          0
-#define SETUP_TOKEN_INT_PROGRAM_AUTHOR         1
-#define SETUP_TOKEN_INT_PROGRAM_EMAIL          2
-#define SETUP_TOKEN_INT_PROGRAM_WEBSITE                3
-#define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT      4
-#define SETUP_TOKEN_INT_PROGRAM_COMPANY                5
-#define SETUP_TOKEN_INT_PROGRAM_ICON_FILE      6
-#define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET   7
-#define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET     8
-#define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET      9
-#define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE 10
-#define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE   11
-#define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE    12
-#define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES   13
-#define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 14
-#define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH   15
-#define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT  16
-
-#define NUM_INTERNAL_SETUP_TOKENS              17
+#define SETUP_TOKEN_INT_PROGRAM_VERSION                1
+#define SETUP_TOKEN_INT_PROGRAM_AUTHOR         2
+#define SETUP_TOKEN_INT_PROGRAM_EMAIL          3
+#define SETUP_TOKEN_INT_PROGRAM_WEBSITE                4
+#define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT      5
+#define SETUP_TOKEN_INT_PROGRAM_COMPANY                6
+#define SETUP_TOKEN_INT_PROGRAM_ICON_FILE      7
+#define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET   8
+#define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET     9
+#define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET      10
+#define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE 11
+#define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE   12
+#define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE    13
+#define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES   14
+#define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 15
+#define SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE  16
+#define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH   17
+#define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT  18
+
+#define NUM_INTERNAL_SETUP_TOKENS              19
 
 /* debug setup */
 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_0                0
@@ -8078,8 +8137,9 @@ void SaveScore(int nr)
 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9    19
 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20
 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY        21
+#define SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND 22
 
-#define NUM_DEBUG_SETUP_TOKENS                 22
+#define NUM_DEBUG_SETUP_TOKENS                 23
 
 /* options setup */
 #define SETUP_TOKEN_OPTIONS_VERBOSE            0
@@ -8116,6 +8176,7 @@ static struct TokenInfo global_setup_tokens[] =
   { TYPE_SWITCH, &si.team_mode,               "team_mode"              },
   { TYPE_SWITCH, &si.handicap,                "handicap"               },
   { TYPE_SWITCH, &si.skip_levels,             "skip_levels"            },
+  { TYPE_SWITCH, &si.increment_levels,        "increment_levels"       },
   { TYPE_SWITCH, &si.time_limit,              "time_limit"             },
   { TYPE_SWITCH, &si.fullscreen,              "fullscreen"             },
   { TYPE_INTEGER,&si.window_scaling_percent,  "window_scaling_percent" },
@@ -8146,22 +8207,11 @@ static struct TokenInfo global_setup_tokens[] =
 
 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"            },
-  { TYPE_SWITCH, &sei.el_diamond_caves,        "editor.el_diamond_caves"       },
-  { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"     },
-  { TYPE_SWITCH, &sei.el_chars,                "editor.el_chars"               },
-  { TYPE_SWITCH, &sei.el_steel_chars,  "editor.el_steel_chars"         },
+  { TYPE_SWITCH, &sei.el_classic,      "editor.el_classic"             },
   { TYPE_SWITCH, &sei.el_custom,       "editor.el_custom"              },
-  { 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.el_by_game,      "editor.el_by_game"             },
-  { TYPE_SWITCH, &sei.el_by_type,      "editor.el_by_type"             },
+  { TYPE_SWITCH, &sei.el_headlines,    "editor.el_headlines"           },
   { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token"   },
 };
 
@@ -8239,6 +8289,7 @@ static struct TokenInfo system_setup_tokens[] =
 static struct TokenInfo internal_setup_tokens[] =
 {
   { TYPE_STRING, &sxi.program_title,           "program_title"         },
+  { TYPE_STRING, &sxi.program_version,         "program_version"       },
   { TYPE_STRING, &sxi.program_author,          "program_author"        },
   { TYPE_STRING, &sxi.program_email,           "program_email"         },
   { TYPE_STRING, &sxi.program_website,         "program_website"       },
@@ -8253,6 +8304,7 @@ static struct TokenInfo internal_setup_tokens[] =
   { TYPE_STRING, &sxi.fallback_music_file,     "fallback_music_file"   },
   { TYPE_STRING, &sxi.default_level_series,    "default_level_series"  },
   { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir,        "choose_from_top_leveldir" },
+  { TYPE_BOOLEAN,&sxi.show_scaling_in_title,   "show_scaling_in_title" },
   { TYPE_INTEGER,&sxi.default_window_width,    "default_window_width"  },
   { TYPE_INTEGER,&sxi.default_window_height,   "default_window_height" },
 };
@@ -8281,6 +8333,7 @@ static struct TokenInfo debug_setup_tokens[] =
   { TYPE_KEY_X11, &sdi.frame_delay_key[9],     "debug.key.frame_delay_9" },
   { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
   { TYPE_BOOLEAN, &sdi.frame_delay_game_only,  "debug.frame_delay.game_only" },
+  { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
 };
 
 static struct TokenInfo options_setup_tokens[] =
@@ -8325,6 +8378,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->team_mode = FALSE;
   si->handicap = TRUE;
   si->skip_levels = TRUE;
+  si->increment_levels = TRUE;
   si->time_limit = TRUE;
   si->fullscreen = FALSE;
   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
@@ -8366,13 +8420,18 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->editor.el_dx_boulderdash         = TRUE;
   si->editor.el_chars                  = TRUE;
   si->editor.el_steel_chars            = TRUE;
+
+  si->editor.el_classic                        = TRUE;
   si->editor.el_custom                 = TRUE;
 
-  si->editor.el_headlines = TRUE;
-  si->editor.el_user_defined = FALSE;
-  si->editor.el_dynamic = TRUE;
+  si->editor.el_user_defined           = FALSE;
+  si->editor.el_dynamic                        = TRUE;
+
+  si->editor.el_headlines              = TRUE;
 
-  si->editor.show_element_token = FALSE;
+  si->editor.show_element_token                = FALSE;
+
+  si->editor.use_template_for_new_levels = TRUE;
 
   si->shortcut.save_game       = DEFAULT_KEY_SAVE_GAME;
   si->shortcut.load_game       = DEFAULT_KEY_LOAD_GAME;
@@ -8425,6 +8484,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
 
   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
+  si->internal.program_version   = getStringCopy(getProgramRealVersionString());
   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
@@ -8443,6 +8503,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
 
   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
   si->internal.choose_from_top_leveldir = FALSE;
+  si->internal.show_scaling_in_title = TRUE;
 
   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
@@ -8472,6 +8533,8 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
 
+  si->debug.show_frames_per_second = FALSE;
+
   si->options.verbose = FALSE;
 
 #if defined(PLATFORM_ANDROID)
@@ -8499,6 +8562,60 @@ static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
   si->editor_cascade.el_dynamic                = FALSE;
 }
 
+#define MAX_HIDE_SETUP_TOKEN_SIZE              20
+
+static char *getHideSetupToken(void *setup_value)
+{
+  static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
+
+  if (setup_value != NULL)
+    snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
+
+  return hide_setup_token;
+}
+
+static void setHideSetupEntry(void *setup_value_raw)
+{
+  /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
+  void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
+
+  char *hide_setup_token = getHideSetupToken(setup_value);
+
+  if (setup_value != NULL)
+    setHashEntry(hide_setup_hash, hide_setup_token, "");
+}
+
+boolean hideSetupEntry(void *setup_value)
+{
+  char *hide_setup_token = getHideSetupToken(setup_value);
+
+  return (setup_value != NULL &&
+         getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
+}
+
+static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
+                                     struct TokenInfo *token_info,
+                                     int token_nr, char *token_text)
+{
+  char *token_hide_text = getStringCat2(token_text, ".hide");
+  char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
+
+  /* set the value of this setup option in the setup option structure */
+  setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
+
+  /* check if this setup option should be hidden in the setup menu */
+  if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
+    setHideSetupEntry(token_info[token_nr].value);
+}
+
+static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
+                                     struct TokenInfo *token_info,
+                                     int token_nr)
+{
+  setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
+                           token_info[token_nr].text);
+}
+
 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
 {
   int i, pnr;
@@ -8506,25 +8623,25 @@ static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
   if (!setup_file_hash)
     return;
 
+  if (hide_setup_hash == NULL)
+    hide_setup_hash = newSetupFileHash();
+
   /* global setup */
   si = setup;
   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
-    setSetupInfo(global_setup_tokens, i,
-                getHashEntry(setup_file_hash, global_setup_tokens[i].text));
+    setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
   setup = si;
 
   /* editor setup */
   sei = setup.editor;
   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
-    setSetupInfo(editor_setup_tokens, i,
-                getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
+    setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
   setup.editor = sei;
 
   /* shortcut setup */
   ssi = setup.shortcut;
   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
-    setSetupInfo(shortcut_setup_tokens, i,
-                getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
+    setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
   setup.shortcut = ssi;
 
   /* player setup */
@@ -8540,8 +8657,8 @@ static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
       char full_token[100];
 
       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
-      setSetupInfo(player_setup_tokens, i,
-                  getHashEntry(setup_file_hash, full_token));
+      setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
+                               full_token);
     }
     setup.input[pnr] = sii;
   }
@@ -8549,29 +8666,25 @@ static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
   /* system setup */
   syi = setup.system;
   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
-    setSetupInfo(system_setup_tokens, i,
-                getHashEntry(setup_file_hash, system_setup_tokens[i].text));
+    setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
   setup.system = syi;
 
   /* internal setup */
   sxi = setup.internal;
   for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
-    setSetupInfo(internal_setup_tokens, i,
-                getHashEntry(setup_file_hash, internal_setup_tokens[i].text));
+    setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
   setup.internal = sxi;
 
   /* debug setup */
   sdi = setup.debug;
   for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
-    setSetupInfo(debug_setup_tokens, i,
-                getHashEntry(setup_file_hash, debug_setup_tokens[i].text));
+    setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
   setup.debug = sdi;
 
   /* options setup */
   soi = setup.options;
   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
-    setSetupInfo(options_setup_tokens, i,
-                getHashEntry(setup_file_hash, options_setup_tokens[i].text));
+    setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
   setup.options = soi;
 }
 
@@ -8603,7 +8716,7 @@ void LoadSetupFromFilename(char *filename)
   }
   else
   {
-    Error(ERR_WARN, "using default setup values");
+    Error(ERR_DEBUG, "using default setup values");
   }
 }
 
@@ -8669,6 +8782,55 @@ void LoadSetup_EditorCascade()
   free(filename);
 }
 
+static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
+                                          char *mapping_line)
+{
+  char mapping_guid[MAX_LINE_LEN];
+  char *mapping_start, *mapping_end;
+
+  // get GUID from game controller mapping line: copy complete line
+  strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
+  mapping_guid[MAX_LINE_LEN - 1] = '\0';
+
+  // get GUID from game controller mapping line: cut after GUID part
+  mapping_start = strchr(mapping_guid, ',');
+  if (mapping_start != NULL)
+    *mapping_start = '\0';
+
+  // cut newline from game controller mapping line
+  mapping_end = strchr(mapping_line, '\n');
+  if (mapping_end != NULL)
+    *mapping_end = '\0';
+
+  // add mapping entry to game controller mappings hash
+  setHashEntry(mappings_hash, mapping_guid, mapping_line);
+}
+
+static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
+                                                char *filename)
+{
+  FILE *file;
+
+  if (!(file = fopen(filename, MODE_READ)))
+  {
+    Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
+
+    return;
+  }
+
+  while (!feof(file))
+  {
+    char line[MAX_LINE_LEN];
+
+    if (!fgets(line, MAX_LINE_LEN, file))
+      break;
+
+    addGameControllerMappingToHash(mappings_hash, line);
+  }
+
+  fclose(file);
+}
+
 void SaveSetup()
 {
   char *filename = getSetupFilename();
@@ -8778,6 +8940,47 @@ void SaveSetup_EditorCascade()
   free(filename);
 }
 
+static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
+                                                 char *filename)
+{
+  FILE *file;
+
+  if (!(file = fopen(filename, MODE_WRITE)))
+  {
+    Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
+
+    return;
+  }
+
+  BEGIN_HASH_ITERATION(mappings_hash, itr)
+  {
+    fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
+  }
+  END_HASH_ITERATION(mappings_hash, itr)
+
+  fclose(file);
+}
+
+void SaveSetup_AddGameControllerMapping(char *mapping)
+{
+  char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
+  SetupFileHash *mappings_hash = newSetupFileHash();
+
+  InitUserDataDirectory();
+
+  // load existing personal game controller mappings
+  LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
+
+  // add new mapping to personal game controller mappings
+  addGameControllerMappingToHash(mappings_hash, mapping);
+
+  // save updated personal game controller mappings
+  SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
+
+  freeSetupFileHash(mappings_hash);
+  free(filename);
+}
+
 void LoadCustomElementDescriptions()
 {
   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
@@ -10065,7 +10268,7 @@ void ConvertLevels()
     Print("Level %03d: ", level_nr);
 
     LoadLevel(level_nr);
-    if (level.no_valid_file)
+    if (level.no_level_file || level.no_valid_file)
     {
       Print("(no level)\n");
       continue;