added new level option to only redraw off-screen player relocation
[rocksndiamonds.git] / src / files.c
index ff600579fd8150761456211a422276cb30120137..ac73db93f8faf2337d1cba0f3e8e268b998478cc 100644 (file)
@@ -288,6 +288,11 @@ static struct LevelFileConfigInfo chunk_config_ELEM[] =
     TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(12),
     &li.shifted_relocation,            FALSE
   },
+  {
+    EL_PLAYER_1,                       -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(15),
+    &li.lazy_relocation,               FALSE
+  },
 
   /* (these values are different for each player) */
   {
@@ -3007,6 +3012,8 @@ static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
   int envelope_nr = element - EL_ENVELOPE_1;
   int real_chunk_size = 2;
 
+  xx_envelope = level->envelope[envelope_nr];  /* copy into temporary buffer */
+
   while (!checkEndOfFile(file))
   {
     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
@@ -3016,7 +3023,7 @@ static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
       break;
   }
 
-  level->envelope[envelope_nr] = xx_envelope;
+  level->envelope[envelope_nr] = xx_envelope;  /* copy from temporary buffer */
 
   return real_chunk_size;
 }
@@ -7158,34 +7165,34 @@ void DumpLevel(struct LevelInfo *level)
     return;
   }
 
-  printf_line("-", 79);
-  printf("Level xxx (file version %08d, game version %08d)\n",
-        level->file_version, level->game_version);
-  printf_line("-", 79);
+  PrintLine("-", 79);
+  Print("Level xxx (file version %08d, game version %08d)\n",
+       level->file_version, level->game_version);
+  PrintLine("-", 79);
 
-  printf("Level author: '%s'\n", level->author);
-  printf("Level title:  '%s'\n", level->name);
-  printf("\n");
-  printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
-  printf("\n");
-  printf("Level time:  %d seconds\n", level->time);
-  printf("Gems needed: %d\n", level->gems_needed);
-  printf("\n");
-  printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
-  printf("Time for wheel:      %d seconds\n", level->time_wheel);
-  printf("Time for light:      %d seconds\n", level->time_light);
-  printf("Time for timegate:   %d seconds\n", level->time_timegate);
-  printf("\n");
-  printf("Amoeba speed: %d\n", level->amoeba_speed);
-  printf("\n");
+  Print("Level author: '%s'\n", level->author);
+  Print("Level title:  '%s'\n", level->name);
+  Print("\n");
+  Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
+  Print("\n");
+  Print("Level time:  %d seconds\n", level->time);
+  Print("Gems needed: %d\n", level->gems_needed);
+  Print("\n");
+  Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
+  Print("Time for wheel:      %d seconds\n", level->time_wheel);
+  Print("Time for light:      %d seconds\n", level->time_light);
+  Print("Time for timegate:   %d seconds\n", level->time_timegate);
+  Print("\n");
+  Print("Amoeba speed: %d\n", level->amoeba_speed);
+  Print("\n");
 
-  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"));
-  printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
-  printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
+  Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
+  Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
+  Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
+  Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
+  Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
 
-  printf_line("-", 79);
+  PrintLine("-", 79);
 }
 
 
@@ -7748,13 +7755,13 @@ void DumpTape(struct TapeInfo *tape)
     return;
   }
 
-  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);
+  PrintLine("-", 79);
+  Print("Tape of Level %03d (file version %08d, game version %08d)\n",
+       tape->level_nr, tape->file_version, tape->game_version);
+  Print("                  (effective engine version %08d)\n",
+       tape->engine_version);
+  Print("Level series identifier: '%s'\n", tape->level_identifier);
+  PrintLine("-", 79);
 
   tape_frame_counter = 0;
 
@@ -7763,7 +7770,7 @@ void DumpTape(struct TapeInfo *tape)
     if (i >= MAX_TAPE_LEN)
       break;
 
-    printf("%04d: ", i);
+    Print("%04d: ", i);
 
     for (j = 0; j < MAX_PLAYERS; j++)
     {
@@ -7771,24 +7778,24 @@ void DumpTape(struct TapeInfo *tape)
       {
        int action = tape->pos[i].action[j];
 
-       printf("%d:%02x ", j, action);
-       printf("[%c%c%c%c|%c%c] - ",
-              (action & JOY_LEFT ? '<' : ' '),
-              (action & JOY_RIGHT ? '>' : ' '),
-              (action & JOY_UP ? '^' : ' '),
-              (action & JOY_DOWN ? 'v' : ' '),
-              (action & JOY_BUTTON_1 ? '1' : ' '),
-              (action & JOY_BUTTON_2 ? '2' : ' '));
+       Print("%d:%02x ", j, action);
+       Print("[%c%c%c%c|%c%c] - ",
+             (action & JOY_LEFT ? '<' : ' '),
+             (action & JOY_RIGHT ? '>' : ' '),
+             (action & JOY_UP ? '^' : ' '),
+             (action & JOY_DOWN ? 'v' : ' '),
+             (action & JOY_BUTTON_1 ? '1' : ' '),
+             (action & JOY_BUTTON_2 ? '2' : ' '));
       }
     }
 
-    printf("(%03d) ", tape->pos[i].delay);
-    printf("[%05d]\n", tape_frame_counter);
+    Print("(%03d) ", tape->pos[i].delay);
+    Print("[%05d]\n", tape_frame_counter);
 
     tape_frame_counter += tape->pos[i].delay;
   }
 
-  printf_line("-", 79);
+  PrintLine("-", 79);
 }
 
 
@@ -7892,7 +7899,7 @@ void SaveScore(int nr)
 #define SETUP_TOKEN_TOONS                      5
 #define SETUP_TOKEN_SCROLL_DELAY               6
 #define SETUP_TOKEN_SCROLL_DELAY_VALUE         7
-#define SETUP_TOKEN_SOFT_SCROLLING             8
+#define SETUP_TOKEN_ENGINE_SNAPSHOT_MODE       8
 #define SETUP_TOKEN_FADE_SCREENS               9
 #define SETUP_TOKEN_AUTORECORD                 10
 #define SETUP_TOKEN_SHOW_TITLESCREEN           11
@@ -7913,20 +7920,21 @@ void SaveScore(int nr)
 #define SETUP_TOKEN_GAME_FRAME_DELAY           26
 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS    27
 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS                28
-#define SETUP_TOKEN_GRAPHICS_SET               29
-#define SETUP_TOKEN_SOUNDS_SET                 30
-#define SETUP_TOKEN_MUSIC_SET                  31
-#define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS    32
-#define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS      33
-#define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC       34
-#define SETUP_TOKEN_VOLUME_SIMPLE              35
-#define SETUP_TOKEN_VOLUME_LOOPS               36
-#define SETUP_TOKEN_VOLUME_MUSIC               37
-#define SETUP_TOKEN_TOUCH_CONTROL_TYPE         38
-#define SETUP_TOKEN_TOUCH_MOVE_DISTANCE                39
-#define SETUP_TOKEN_TOUCH_DROP_DISTANCE                40
-
-#define NUM_GLOBAL_SETUP_TOKENS                        41
+#define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS      29
+#define SETUP_TOKEN_GRAPHICS_SET               30
+#define SETUP_TOKEN_SOUNDS_SET                 31
+#define SETUP_TOKEN_MUSIC_SET                  32
+#define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS    33
+#define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS      34
+#define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC       35
+#define SETUP_TOKEN_VOLUME_SIMPLE              36
+#define SETUP_TOKEN_VOLUME_LOOPS               37
+#define SETUP_TOKEN_VOLUME_MUSIC               38
+#define SETUP_TOKEN_TOUCH_CONTROL_TYPE         39
+#define SETUP_TOKEN_TOUCH_MOVE_DISTANCE                40
+#define SETUP_TOKEN_TOUCH_DROP_DISTANCE                41
+
+#define NUM_GLOBAL_SETUP_TOKENS                        42
 
 /* editor setup */
 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH      0
@@ -8020,6 +8028,25 @@ void SaveScore(int nr)
 
 #define NUM_SYSTEM_SETUP_TOKENS                        3
 
+/* 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 NUM_INTERNAL_SETUP_TOKENS              15
+
 /* options setup */
 #define SETUP_TOKEN_OPTIONS_VERBOSE            0
 
@@ -8032,6 +8059,7 @@ static struct SetupEditorCascadeInfo seci;
 static struct SetupShortcutInfo ssi;
 static struct SetupInputInfo sii;
 static struct SetupSystemInfo syi;
+static struct SetupInternalInfo sxi;
 static struct OptionInfo soi;
 
 static struct TokenInfo global_setup_tokens[] =
@@ -8044,7 +8072,7 @@ static struct TokenInfo global_setup_tokens[] =
   { TYPE_SWITCH, &si.toons,                   "toons"                  },
   { TYPE_SWITCH, &si.scroll_delay,            "scroll_delay"           },
   { TYPE_INTEGER,&si.scroll_delay_value,      "scroll_delay_value"     },
-  { TYPE_SWITCH, &si.soft_scrolling,          "soft_scrolling"         },
+  { TYPE_STRING, &si.engine_snapshot_mode,    "engine_snapshot_mode"   },
   { TYPE_SWITCH, &si.fade_screens,            "fade_screens"           },
   { TYPE_SWITCH, &si.autorecord,              "automatic_tape_recording"},
   { TYPE_SWITCH, &si.show_titlescreen,        "show_titlescreen"       },
@@ -8065,6 +8093,7 @@ static struct TokenInfo global_setup_tokens[] =
   { TYPE_INTEGER,&si.game_frame_delay,        "game_frame_delay"       },
   { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements"        },
   { TYPE_SWITCH, &si.small_game_graphics,     "small_game_graphics"    },
+  { TYPE_SWITCH, &si.show_snapshot_buttons,   "show_snapshot_buttons"  },
   { TYPE_STRING, &si.graphics_set,            "graphics_set"           },
   { TYPE_STRING, &si.sounds_set,              "sounds_set"             },
   { TYPE_STRING, &si.music_set,               "music_set"              },
@@ -8166,11 +8195,30 @@ static struct TokenInfo player_setup_tokens[] =
 
 static struct TokenInfo system_setup_tokens[] =
 {
-  { TYPE_STRING,  &syi.sdl_videodriver,        "system.sdl_videodriver"        },
-  { TYPE_STRING,  &syi.sdl_audiodriver,        "system.sdl_audiodriver"        },
+  { TYPE_STRING,  &syi.sdl_videodriver,    "system.sdl_videodriver"    },
+  { TYPE_STRING,  &syi.sdl_audiodriver,           "system.sdl_audiodriver"     },
   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size"        },
 };
 
+static struct TokenInfo internal_setup_tokens[] =
+{
+  { TYPE_STRING, &sxi.program_title,           "program_title"         },
+  { TYPE_STRING, &sxi.program_author,          "program_author"        },
+  { TYPE_STRING, &sxi.program_email,           "program_email"         },
+  { TYPE_STRING, &sxi.program_website,         "program_website"       },
+  { TYPE_STRING, &sxi.program_copyright,       "program_copyright"     },
+  { TYPE_STRING, &sxi.program_company,         "program_company"       },
+  { TYPE_STRING, &sxi.program_icon_file,       "program_icon_file"     },
+  { TYPE_STRING, &sxi.default_graphics_set,    "default_graphics_set"  },
+  { TYPE_STRING, &sxi.default_sounds_set,      "default_sounds_set"    },
+  { TYPE_STRING, &sxi.default_music_set,       "default_music_set"     },
+  { TYPE_STRING, &sxi.fallback_graphics_file,  "fallback_graphics_file"},
+  { TYPE_STRING, &sxi.fallback_sounds_file,    "fallback_sounds_file"  },
+  { TYPE_STRING, &sxi.fallback_music_file,     "fallback_music_file"   },
+  { TYPE_STRING, &sxi.default_level_series,    "default_level_series"  },
+  { TYPE_STRING, &sxi.choose_from_top_leveldir,        "choose_from_top_leveldir" },
+};
+
 static struct TokenInfo options_setup_tokens[] =
 {
   { TYPE_BOOLEAN, &soi.verbose,                "options.verbose"               },
@@ -8204,7 +8252,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->toons = TRUE;
   si->scroll_delay = TRUE;
   si->scroll_delay_value = STD_SCROLL_DELAY;
-  si->soft_scrolling = TRUE;
+  si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
   si->fade_screens = TRUE;
   si->autorecord = TRUE;
   si->show_titlescreen = TRUE;
@@ -8225,10 +8273,12 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->game_frame_delay = GAME_FRAME_DELAY;
   si->sp_show_border_elements = FALSE;
   si->small_game_graphics = FALSE;
+  si->show_snapshot_buttons = FALSE;
+
+  si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
+  si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
+  si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
 
-  si->graphics_set = getStringCopy(GFX_DEFAULT_SUBDIR);
-  si->sounds_set = getStringCopy(SND_DEFAULT_SUBDIR);
-  si->music_set = getStringCopy(MUS_DEFAULT_SUBDIR);
   si->override_level_graphics = FALSE;
   si->override_level_sounds = FALSE;
   si->override_level_music = FALSE;
@@ -8309,16 +8359,27 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
 
-  si->options.verbose = FALSE;
+  si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
+  si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
+  si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
+  si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
+  si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
+  si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
 
-#if defined(CREATE_SPECIAL_EDITION_RND_JUE)
-  si->toons = FALSE;
-  si->handicap = FALSE;
-  si->fullscreen = TRUE;
-  si->override_level_graphics = AUTO;
-  si->override_level_sounds = AUTO;
-  si->override_level_music = AUTO;
-#endif
+  si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
+
+  si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
+  si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
+  si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
+
+  si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
+  si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
+  si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
+
+  si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
+  si->internal.choose_from_top_leveldir = FALSE;
+
+  si->options.verbose = FALSE;
 
 #if defined(PLATFORM_ANDROID)
   si->fullscreen = TRUE;
@@ -8399,6 +8460,13 @@ static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
                 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
   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));
+  setup.internal = sxi;
+
   /* options setup */
   soi = setup.options;
   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
@@ -8423,43 +8491,62 @@ static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
   setup.editor_cascade = seci;
 }
 
-void LoadSetup()
+void LoadSetupFromFilename(char *filename)
 {
-  char *filename = getSetupFilename();
-  SetupFileHash *setup_file_hash = NULL;
-
-  /* always start with reliable default values */
-  setSetupInfoToDefaults(&setup);
-
-  setup_file_hash = loadSetupFileHash(filename);
+  SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
 
   if (setup_file_hash)
   {
-    char *player_name_new;
-
-    checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
     decodeSetupFileHash(setup_file_hash);
 
     freeSetupFileHash(setup_file_hash);
+  }
+  else
+  {
+    Error(ERR_WARN, "using default setup values");
+  }
+}
 
-    /* needed to work around problems with fixed length strings */
-    player_name_new = get_corrected_login_name(setup.player_name);
-    free(setup.player_name);
-    setup.player_name = player_name_new;
+static void LoadSetup_SpecialPostProcessing()
+{
+  char *player_name_new;
 
-    /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
-    if (setup.scroll_delay == FALSE)
-    {
-      setup.scroll_delay_value = MIN_SCROLL_DELAY;
-      setup.scroll_delay = TRUE;                       /* now always "on" */
-    }
+  /* needed to work around problems with fixed length strings */
+  player_name_new = get_corrected_login_name(setup.player_name);
+  free(setup.player_name);
+  setup.player_name = player_name_new;
 
-    /* make sure that scroll delay value stays inside valid range */
-    setup.scroll_delay_value =
-      MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
+  /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
+  if (setup.scroll_delay == FALSE)
+  {
+    setup.scroll_delay_value = MIN_SCROLL_DELAY;
+    setup.scroll_delay = TRUE;                 /* now always "on" */
   }
-  else
-    Error(ERR_WARN, "using default setup values");
+
+  /* make sure that scroll delay value stays inside valid range */
+  setup.scroll_delay_value =
+    MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
+}
+
+void LoadSetup()
+{
+  char *filename;
+
+  /* always start with reliable default values */
+  setSetupInfoToDefaults(&setup);
+
+  /* try to load setup values from default setup file */
+  filename = getDefaultSetupFilename();
+
+  if (fileExists(filename))
+    LoadSetupFromFilename(filename);
+
+  /* try to load setup values from user setup file */
+  filename = getSetupFilename();
+
+  LoadSetupFromFilename(filename);
+
+  LoadSetup_SpecialPostProcessing();
 }
 
 void LoadSetup_EditorCascade()
@@ -8474,7 +8561,6 @@ void LoadSetup_EditorCascade()
 
   if (setup_file_hash)
   {
-    checkSetupFileHashIdentifier(setup_file_hash, filename,getCookie("SETUP"));
     decodeSetupFileHash_EditorCascade(setup_file_hash);
 
     freeSetupFileHash(setup_file_hash);
@@ -8497,9 +8583,7 @@ void SaveSetup()
     return;
   }
 
-  fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
-                                              getCookie("SETUP")));
-  fprintf(file, "\n");
+  fprintFileHeader(file, SETUP_FILENAME);
 
   /* global setup */
   si = setup;
@@ -8546,6 +8630,9 @@ void SaveSetup()
   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
 
+  /* internal setup */
+  /* (internal setup values not saved to user setup file) */
+
   /* options setup */
   soi = setup.options;
   fprintf(file, "\n");
@@ -8572,12 +8659,9 @@ void SaveSetup_EditorCascade()
     return;
   }
 
-  fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
-                                              getCookie("SETUP")));
-  fprintf(file, "\n");
+  fprintFileHeader(file, EDITORCASCADE_FILENAME);
 
   seci = setup.editor_cascade;
-  fprintf(file, "\n");
   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
 
@@ -8711,9 +8795,36 @@ static void InitMenuDesignSettings_SpecialPreProcessing()
 
 static void InitMenuDesignSettings_SpecialPostProcessing()
 {
+  static struct
+  {
+    struct XY *dst, *src;
+  }
+  game_buttons_xy[] =
+  {
+    { &game.button.save,       &game.button.stop       },
+    { &game.button.pause2,     &game.button.pause      },
+    { &game.button.load,       &game.button.play       },
+    { &game.button.undo,       &game.button.stop       },
+    { &game.button.redo,       &game.button.play       },
+
+    { NULL,                    NULL                    }
+  };
+  int i;
+
   /* special case: initialize later added SETUP list size from LEVELS value */
   if (menu.list_size[GAME_MODE_SETUP] == -1)
     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
+
+  /* set default position for snapshot buttons to stop/pause/play buttons */
+  for (i = 0; game_buttons_xy[i].dst != NULL; i++)
+  {
+    if ((*game_buttons_xy[i].dst).x == -1 &&
+       (*game_buttons_xy[i].dst).y == -1)
+    {
+      (*game_buttons_xy[i].dst).x = (*game_buttons_xy[i].src).x;
+      (*game_buttons_xy[i].dst).y = (*game_buttons_xy[i].src).y;
+    }
+  }
 }
 
 static void LoadMenuDesignSettingsFromFilename(char *filename)
@@ -8788,6 +8899,14 @@ static void LoadMenuDesignSettingsFromFilename(char *filename)
       menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
     if (value_2 != NULL)
       menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
+
+    if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
+    {
+      char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
+
+      if (value_1 != NULL)
+       menu.list_size_info[i] = get_integer_from_string(value_1);
+    }
   }
 
   /* special case: initialize with default values that may be overwritten */