changed drawing preview/network players if only level preview redefined
[rocksndiamonds.git] / src / files.c
index 7af56662cd3f50734d5fd7002a579a3d294c9305..3881fbf2847cf79f8e5be33fb51b273ff3467043 100644 (file)
@@ -8171,18 +8171,19 @@ void SaveTape(int nr)
   tape.changed = FALSE;
 }
 
-static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved)
+static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
+                                 unsigned int req_state_added)
 {
   char *filename = getTapeFilename(nr);
   boolean new_tape = !fileExists(filename);
   boolean tape_saved = FALSE;
 
-  if (new_tape || Request(msg_replace, REQ_ASK))
+  if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
   {
     SaveTape(nr);
 
     if (new_tape)
-      Request(msg_saved, REQ_CONFIRM);
+      Request(msg_saved, REQ_CONFIRM | req_state_added);
 
     tape_saved = TRUE;
   }
@@ -8192,13 +8193,13 @@ static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved)
 
 boolean SaveTapeChecked(int nr)
 {
-  return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!");
+  return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
 }
 
 boolean SaveTapeChecked_LevelSolved(int nr)
 {
   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
-                               "Level solved! Tape saved!");
+                               "Level solved! Tape saved!", REQ_STAY_OPEN);
 }
 
 void DumpTape(struct TapeInfo *tape)
@@ -8350,240 +8351,6 @@ void SaveScore(int nr)
 
 #define TOKEN_STR_PLAYER_PREFIX                        "player_"
 
-// global setup
-enum
-{
-  SETUP_TOKEN_PLAYER_NAME = 0,
-  SETUP_TOKEN_SOUND,
-  SETUP_TOKEN_SOUND_LOOPS,
-  SETUP_TOKEN_SOUND_MUSIC,
-  SETUP_TOKEN_SOUND_SIMPLE,
-  SETUP_TOKEN_TOONS,
-  SETUP_TOKEN_SCROLL_DELAY,
-  SETUP_TOKEN_SCROLL_DELAY_VALUE,
-  SETUP_TOKEN_ENGINE_SNAPSHOT_MODE,
-  SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY,
-  SETUP_TOKEN_FADE_SCREENS,
-  SETUP_TOKEN_AUTORECORD,
-  SETUP_TOKEN_SHOW_TITLESCREEN,
-  SETUP_TOKEN_QUICK_DOORS,
-  SETUP_TOKEN_TEAM_MODE,
-  SETUP_TOKEN_HANDICAP,
-  SETUP_TOKEN_SKIP_LEVELS,
-  SETUP_TOKEN_INCREMENT_LEVELS,
-  SETUP_TOKEN_AUTO_PLAY_NEXT_LEVEL,
-  SETUP_TOKEN_SKIP_SCORES_AFTER_GAME,
-  SETUP_TOKEN_TIME_LIMIT,
-  SETUP_TOKEN_FULLSCREEN,
-  SETUP_TOKEN_WINDOW_SCALING_PERCENT,
-  SETUP_TOKEN_WINDOW_SCALING_QUALITY,
-  SETUP_TOKEN_SCREEN_RENDERING_MODE,
-  SETUP_TOKEN_VSYNC_MODE,
-  SETUP_TOKEN_ASK_ON_ESCAPE,
-  SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR,
-  SETUP_TOKEN_ASK_ON_GAME_OVER,
-  SETUP_TOKEN_QUICK_SWITCH,
-  SETUP_TOKEN_INPUT_ON_FOCUS,
-  SETUP_TOKEN_PREFER_AGA_GRAPHICS,
-  SETUP_TOKEN_GAME_SPEED_EXTENDED,
-  SETUP_TOKEN_GAME_FRAME_DELAY,
-  SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS,
-  SETUP_TOKEN_SMALL_GAME_GRAPHICS,
-  SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS,
-  SETUP_TOKEN_GRAPHICS_SET,
-  SETUP_TOKEN_SOUNDS_SET,
-  SETUP_TOKEN_MUSIC_SET,
-  SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS,
-  SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS,
-  SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC,
-  SETUP_TOKEN_VOLUME_SIMPLE,
-  SETUP_TOKEN_VOLUME_LOOPS,
-  SETUP_TOKEN_VOLUME_MUSIC,
-  SETUP_TOKEN_NETWORK_MODE,
-  SETUP_TOKEN_NETWORK_PLAYER_NR,
-  SETUP_TOKEN_NETWORK_SERVER_HOSTNAME,
-  SETUP_TOKEN_TOUCH_CONTROL_TYPE,
-  SETUP_TOKEN_TOUCH_MOVE_DISTANCE,
-  SETUP_TOKEN_TOUCH_DROP_DISTANCE,
-  SETUP_TOKEN_TOUCH_TRANSPARENCY,
-  SETUP_TOKEN_TOUCH_DRAW_OUTLINED,
-  SETUP_TOKEN_TOUCH_DRAW_PRESSED,
-  SETUP_TOKEN_TOUCH_GRID_XSIZE_0,
-  SETUP_TOKEN_TOUCH_GRID_YSIZE_0,
-  SETUP_TOKEN_TOUCH_GRID_XSIZE_1,
-  SETUP_TOKEN_TOUCH_GRID_YSIZE_1,
-
-  NUM_GLOBAL_SETUP_TOKENS
-};
-
-// auto setup
-enum
-{
-  SETUP_TOKEN_AUTO_EDITOR_ZOOM_TILESIZE = 0,
-
-  NUM_AUTO_SETUP_TOKENS
-};
-
-// editor setup
-enum
-{
-  SETUP_TOKEN_EDITOR_EL_CLASSIC = 0,
-  SETUP_TOKEN_EDITOR_EL_CUSTOM,
-  SETUP_TOKEN_EDITOR_EL_USER_DEFINED,
-  SETUP_TOKEN_EDITOR_EL_DYNAMIC,
-  SETUP_TOKEN_EDITOR_EL_HEADLINES,
-  SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN,
-
-  NUM_EDITOR_SETUP_TOKENS
-};
-
-// editor cascade setup
-enum
-{
-  SETUP_TOKEN_EDITOR_CASCADE_BD = 0,
-  SETUP_TOKEN_EDITOR_CASCADE_EM,
-  SETUP_TOKEN_EDITOR_CASCADE_EMC,
-  SETUP_TOKEN_EDITOR_CASCADE_RND,
-  SETUP_TOKEN_EDITOR_CASCADE_SB,
-  SETUP_TOKEN_EDITOR_CASCADE_SP,
-  SETUP_TOKEN_EDITOR_CASCADE_DC,
-  SETUP_TOKEN_EDITOR_CASCADE_DX,
-  SETUP_TOKEN_EDITOR_CASCADE_TEXT,
-  SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT,
-  SETUP_TOKEN_EDITOR_CASCADE_CE,
-  SETUP_TOKEN_EDITOR_CASCADE_GE,
-  SETUP_TOKEN_EDITOR_CASCADE_REF,
-  SETUP_TOKEN_EDITOR_CASCADE_USER,
-  SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC,
-
-  NUM_EDITOR_CASCADE_SETUP_TOKENS
-};
-
-// shortcut setup
-enum
-{
-  SETUP_TOKEN_SHORTCUT_SAVE_GAME = 0,
-  SETUP_TOKEN_SHORTCUT_LOAD_GAME,
-  SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE,
-  SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1,
-  SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2,
-  SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3,
-  SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4,
-  SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL,
-  SETUP_TOKEN_SHORTCUT_TAPE_EJECT,
-  SETUP_TOKEN_SHORTCUT_TAPE_EXTRA,
-  SETUP_TOKEN_SHORTCUT_TAPE_STOP,
-  SETUP_TOKEN_SHORTCUT_TAPE_PAUSE,
-  SETUP_TOKEN_SHORTCUT_TAPE_RECORD,
-  SETUP_TOKEN_SHORTCUT_TAPE_PLAY,
-  SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE,
-  SETUP_TOKEN_SHORTCUT_SOUND_LOOPS,
-  SETUP_TOKEN_SHORTCUT_SOUND_MUSIC,
-  SETUP_TOKEN_SHORTCUT_SNAP_LEFT,
-  SETUP_TOKEN_SHORTCUT_SNAP_RIGHT,
-  SETUP_TOKEN_SHORTCUT_SNAP_UP,
-  SETUP_TOKEN_SHORTCUT_SNAP_DOWN,
-
-  NUM_SHORTCUT_SETUP_TOKENS
-};
-
-// player setup
-enum
-{
-  SETUP_TOKEN_PLAYER_USE_JOYSTICK = 0,
-  SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME,
-  SETUP_TOKEN_PLAYER_JOY_XLEFT,
-  SETUP_TOKEN_PLAYER_JOY_XMIDDLE,
-  SETUP_TOKEN_PLAYER_JOY_XRIGHT,
-  SETUP_TOKEN_PLAYER_JOY_YUPPER,
-  SETUP_TOKEN_PLAYER_JOY_YMIDDLE,
-  SETUP_TOKEN_PLAYER_JOY_YLOWER,
-  SETUP_TOKEN_PLAYER_JOY_SNAP,
-  SETUP_TOKEN_PLAYER_JOY_DROP,
-  SETUP_TOKEN_PLAYER_KEY_LEFT,
-  SETUP_TOKEN_PLAYER_KEY_RIGHT,
-  SETUP_TOKEN_PLAYER_KEY_UP,
-  SETUP_TOKEN_PLAYER_KEY_DOWN,
-  SETUP_TOKEN_PLAYER_KEY_SNAP,
-  SETUP_TOKEN_PLAYER_KEY_DROP,
-
-  NUM_PLAYER_SETUP_TOKENS
-};
-
-// system setup
-enum
-{
-  SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER = 0,
-  SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER,
-  SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE,
-
-  NUM_SYSTEM_SETUP_TOKENS
-};
-
-// internal setup
-enum
-{
-  SETUP_TOKEN_INT_PROGRAM_TITLE = 0,
-  SETUP_TOKEN_INT_PROGRAM_VERSION,
-  SETUP_TOKEN_INT_PROGRAM_AUTHOR,
-  SETUP_TOKEN_INT_PROGRAM_EMAIL,
-  SETUP_TOKEN_INT_PROGRAM_WEBSITE,
-  SETUP_TOKEN_INT_PROGRAM_COPYRIGHT,
-  SETUP_TOKEN_INT_PROGRAM_COMPANY,
-  SETUP_TOKEN_INT_PROGRAM_ICON_FILE,
-  SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET,
-  SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET,
-  SETUP_TOKEN_INT_DEFAULT_MUSIC_SET,
-  SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE,
-  SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE,
-  SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE,
-  SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES,
-  SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR,
-  SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE,
-  SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH,
-  SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT,
-
-  NUM_INTERNAL_SETUP_TOKENS
-};
-
-// debug setup
-enum
-{
-  SETUP_TOKEN_DEBUG_FRAME_DELAY_0 = 0,
-  SETUP_TOKEN_DEBUG_FRAME_DELAY_1,
-  SETUP_TOKEN_DEBUG_FRAME_DELAY_2,
-  SETUP_TOKEN_DEBUG_FRAME_DELAY_3,
-  SETUP_TOKEN_DEBUG_FRAME_DELAY_4,
-  SETUP_TOKEN_DEBUG_FRAME_DELAY_5,
-  SETUP_TOKEN_DEBUG_FRAME_DELAY_6,
-  SETUP_TOKEN_DEBUG_FRAME_DELAY_7,
-  SETUP_TOKEN_DEBUG_FRAME_DELAY_8,
-  SETUP_TOKEN_DEBUG_FRAME_DELAY_9,
-  SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0,
-  SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1,
-  SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2,
-  SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3,
-  SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4,
-  SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5,
-  SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6,
-  SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7,
-  SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8,
-  SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9,
-  SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY,
-  SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY,
-  SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND,
-
-  NUM_DEBUG_SETUP_TOKENS
-};
-
-// options setup
-enum
-{
-  SETUP_TOKEN_OPTIONS_VERBOSE = 0,
-
-  NUM_OPTIONS_SETUP_TOKENS
-};
-
 
 static struct SetupInfo si;
 static struct SetupAutoSetupInfo sasi;
@@ -9167,7 +8934,7 @@ static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
 
   // global setup
   si = setup;
-  for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
+  for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
   setup = si;
 
@@ -9212,13 +8979,13 @@ static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
 
   // editor setup
   sei = setup.editor;
-  for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
+  for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
     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++)
+  for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
   setup.shortcut = ssi;
 
@@ -9230,7 +8997,7 @@ static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
 
     sii = setup.input[pnr];
-    for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
+    for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
     {
       char full_token[100];
 
@@ -9243,25 +9010,25 @@ static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
 
   // system setup
   syi = setup.system;
-  for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
+  for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
     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++)
+  for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
     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++)
+  for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
     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++)
+  for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
   setup.options = soi;
 
@@ -9277,7 +9044,7 @@ static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
 
   // auto setup
   sasi = setup.auto_setup;
-  for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
+  for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
     setSetupInfo(auto_setup_tokens, i,
                 getHashEntry(setup_file_hash,
                              auto_setup_tokens[i].text));
@@ -9293,7 +9060,7 @@ static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
 
   // editor cascade setup
   seci = setup.editor_cascade;
-  for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
+  for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
     setSetupInfo(editor_cascade_setup_tokens, i,
                 getHashEntry(setup_file_hash,
                              editor_cascade_setup_tokens[i].text));
@@ -9465,16 +9232,16 @@ void SaveSetup(void)
 
   // global setup
   si = setup;
-  for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
+  for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
   {
     // just to make things nicer :)
-    if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
-       i == SETUP_TOKEN_GRAPHICS_SET ||
-       i == SETUP_TOKEN_VOLUME_SIMPLE ||
-       i == SETUP_TOKEN_NETWORK_MODE ||
-       i == SETUP_TOKEN_TOUCH_CONTROL_TYPE ||
-       i == SETUP_TOKEN_TOUCH_GRID_XSIZE_0 ||
-       i == SETUP_TOKEN_TOUCH_GRID_XSIZE_1)
+    if (global_setup_tokens[i].value == &si.sound              ||
+       global_setup_tokens[i].value == &si.graphics_set        ||
+       global_setup_tokens[i].value == &si.volume_simple       ||
+       global_setup_tokens[i].value == &si.network_mode        ||
+       global_setup_tokens[i].value == &si.touch.control_type  ||
+       global_setup_tokens[i].value == &si.touch.grid_xsize[0] ||
+       global_setup_tokens[i].value == &si.touch.grid_xsize[1])
       fprintf(file, "\n");
 
     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
@@ -9512,13 +9279,13 @@ void SaveSetup(void)
   // editor setup
   sei = setup.editor;
   fprintf(file, "\n");
-  for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
+  for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
 
   // shortcut setup
   ssi = setup.shortcut;
   fprintf(file, "\n");
-  for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
+  for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
 
   // player setup
@@ -9530,14 +9297,14 @@ void SaveSetup(void)
     fprintf(file, "\n");
 
     sii = setup.input[pnr];
-    for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
+    for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
   }
 
   // system setup
   syi = setup.system;
   fprintf(file, "\n");
-  for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
+  for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
 
   // internal setup
@@ -9546,13 +9313,13 @@ void SaveSetup(void)
   // debug setup
   sdi = setup.debug;
   fprintf(file, "\n");
-  for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
+  for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
     fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
 
   // options setup
   soi = setup.options;
   fprintf(file, "\n");
-  for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
+  for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
 
   fclose(file);
@@ -9578,7 +9345,7 @@ void SaveSetup_AutoSetup(void)
   fprintFileHeader(file, AUTOSETUP_FILENAME);
 
   sasi = setup.auto_setup;
-  for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
+  for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
 
   fclose(file);
@@ -9606,7 +9373,7 @@ void SaveSetup_EditorCascade(void)
   fprintFileHeader(file, EDITORCASCADE_FILENAME);
 
   seci = setup.editor_cascade;
-  for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
+  for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
 
   fclose(file);
@@ -9842,7 +9609,7 @@ static void InitMenuDesignSettings_SpecialPostProcessing(void)
 
     { NULL,                    NULL                    }
   };
-  int i;
+  int i, j;
 
   // special case: initialize later added SETUP list size from LEVELS value
   if (menu.list_size[GAME_MODE_SETUP] == -1)
@@ -9853,6 +9620,237 @@ static void InitMenuDesignSettings_SpecialPostProcessing(void)
     if ((*game_buttons_xy[i].dst).x == -1 &&
        (*game_buttons_xy[i].dst).y == -1)
       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
+
+  // --------------------------------------------------------------------------
+  // dynamic viewports (including playfield margins, borders and alignments)
+  // --------------------------------------------------------------------------
+
+  // dynamic viewports currently only supported for landscape mode
+  int display_width  = MAX(video.display_width, video.display_height);
+  int display_height = MIN(video.display_width, video.display_height);
+
+  for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
+  {
+    struct RectWithBorder *vp_window    = &viewport.window[i];
+    struct RectWithBorder *vp_playfield = &viewport.playfield[i];
+    struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
+    struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
+    boolean dynamic_window_width     = (vp_window->min_width     != -1);
+    boolean dynamic_window_height    = (vp_window->min_height    != -1);
+    boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
+    boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
+
+    // adjust window size if min/max width/height is specified
+
+    if (vp_window->min_width != -1)
+    {
+      int window_width = display_width;
+
+      // when using static window height, use aspect ratio of display
+      if (vp_window->min_height == -1)
+       window_width = vp_window->height * display_width / display_height;
+
+      vp_window->width = MAX(vp_window->min_width, window_width);
+    }
+
+    if (vp_window->min_height != -1)
+    {
+      int window_height = display_height;
+
+      // when using static window width, use aspect ratio of display
+      if (vp_window->min_width == -1)
+       window_height = vp_window->width * display_height / display_width;
+
+      vp_window->height = MAX(vp_window->min_height, window_height);
+    }
+
+    if (vp_window->max_width != -1)
+      vp_window->width = MIN(vp_window->width, vp_window->max_width);
+
+    if (vp_window->max_height != -1)
+      vp_window->height = MIN(vp_window->height, vp_window->max_height);
+
+    int playfield_width  = vp_window->width;
+    int playfield_height = vp_window->height;
+
+    // adjust playfield size and position according to specified margins
+
+    playfield_width  -= vp_playfield->margin_left;
+    playfield_width  -= vp_playfield->margin_right;
+
+    playfield_height -= vp_playfield->margin_top;
+    playfield_height -= vp_playfield->margin_bottom;
+
+    // adjust playfield size if min/max width/height is specified
+
+    if (vp_playfield->min_width != -1)
+      vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
+
+    if (vp_playfield->min_height != -1)
+      vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
+
+    if (vp_playfield->max_width != -1)
+      vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
+
+    if (vp_playfield->max_height != -1)
+      vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
+
+    // adjust playfield position according to specified alignment
+
+    if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
+      vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
+    else if (vp_playfield->align == ALIGN_CENTER)
+      vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
+    else if (vp_playfield->align == ALIGN_RIGHT)
+      vp_playfield->x += playfield_width - vp_playfield->width;
+
+    if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
+      vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
+    else if (vp_playfield->valign == VALIGN_MIDDLE)
+      vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
+    else if (vp_playfield->valign == VALIGN_BOTTOM)
+      vp_playfield->y += playfield_height - vp_playfield->height;
+
+    vp_playfield->x += vp_playfield->margin_left;
+    vp_playfield->y += vp_playfield->margin_top;
+
+    // adjust individual playfield borders if only default border is specified
+
+    if (vp_playfield->border_left == -1)
+      vp_playfield->border_left = vp_playfield->border_size;
+    if (vp_playfield->border_right == -1)
+      vp_playfield->border_right = vp_playfield->border_size;
+    if (vp_playfield->border_top == -1)
+      vp_playfield->border_top = vp_playfield->border_size;
+    if (vp_playfield->border_bottom == -1)
+      vp_playfield->border_bottom = vp_playfield->border_size;
+
+    // set dynamic playfield borders if borders are specified as undefined
+    // (but only if window size was dynamic and playfield size was static)
+
+    if (dynamic_window_width && !dynamic_playfield_width)
+    {
+      if (vp_playfield->border_left == -1)
+      {
+       vp_playfield->border_left = (vp_playfield->x -
+                                    vp_playfield->margin_left);
+       vp_playfield->x     -= vp_playfield->border_left;
+       vp_playfield->width += vp_playfield->border_left;
+      }
+
+      if (vp_playfield->border_right == -1)
+      {
+       vp_playfield->border_right = (vp_window->width -
+                                     vp_playfield->x -
+                                     vp_playfield->width -
+                                     vp_playfield->margin_right);
+       vp_playfield->width += vp_playfield->border_right;
+      }
+    }
+
+    if (dynamic_window_height && !dynamic_playfield_height)
+    {
+      if (vp_playfield->border_top == -1)
+      {
+       vp_playfield->border_top = (vp_playfield->y -
+                                   vp_playfield->margin_top);
+       vp_playfield->y      -= vp_playfield->border_top;
+       vp_playfield->height += vp_playfield->border_top;
+      }
+
+      if (vp_playfield->border_bottom == -1)
+      {
+       vp_playfield->border_bottom = (vp_window->height -
+                                      vp_playfield->y -
+                                      vp_playfield->height -
+                                      vp_playfield->margin_bottom);
+       vp_playfield->height += vp_playfield->border_bottom;
+      }
+    }
+
+    // adjust playfield size to be a multiple of a defined alignment tile size
+
+    int align_size = vp_playfield->align_size;
+    int playfield_xtiles = vp_playfield->width  / align_size;
+    int playfield_ytiles = vp_playfield->height / align_size;
+    int playfield_width_corrected  = playfield_xtiles * align_size;
+    int playfield_height_corrected = playfield_ytiles * align_size;
+    boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
+                                i == GFX_SPECIAL_ARG_EDITOR);
+
+    if (is_playfield_mode &&
+       dynamic_playfield_width &&
+       vp_playfield->width != playfield_width_corrected)
+    {
+      int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
+
+      vp_playfield->width = playfield_width_corrected;
+
+      if (vp_playfield->align == ALIGN_LEFT)
+      {
+       vp_playfield->border_left += playfield_xdiff;
+      }
+      else if (vp_playfield->align == ALIGN_RIGHT)
+      {
+       vp_playfield->border_right += playfield_xdiff;
+      }
+      else if (vp_playfield->align == ALIGN_CENTER)
+      {
+       int border_left_diff  = playfield_xdiff / 2;
+       int border_right_diff = playfield_xdiff - border_left_diff;
+
+       vp_playfield->border_left  += border_left_diff;
+       vp_playfield->border_right += border_right_diff;
+      }
+    }
+
+    if (is_playfield_mode &&
+       dynamic_playfield_height &&
+       vp_playfield->height != playfield_height_corrected)
+    {
+      int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
+
+      vp_playfield->height = playfield_height_corrected;
+
+      if (vp_playfield->valign == VALIGN_TOP)
+      {
+       vp_playfield->border_top += playfield_ydiff;
+      }
+      else if (vp_playfield->align == VALIGN_BOTTOM)
+      {
+       vp_playfield->border_right += playfield_ydiff;
+      }
+      else if (vp_playfield->align == VALIGN_MIDDLE)
+      {
+       int border_top_diff    = playfield_ydiff / 2;
+       int border_bottom_diff = playfield_ydiff - border_top_diff;
+
+       vp_playfield->border_top    += border_top_diff;
+       vp_playfield->border_bottom += border_bottom_diff;
+      }
+    }
+
+    // adjust door positions according to specified alignment
+
+    for (j = 0; j < 2; j++)
+    {
+      struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
+
+      if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
+       vp_door->x = ALIGNED_VP_XPOS(vp_door);
+      else if (vp_door->align == ALIGN_CENTER)
+       vp_door->x = vp_window->width / 2 - vp_door->width / 2;
+      else if (vp_door->align == ALIGN_RIGHT)
+       vp_door->x += vp_window->width - vp_door->width;
+
+      if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
+       vp_door->y = ALIGNED_VP_YPOS(vp_door);
+      else if (vp_door->valign == VALIGN_MIDDLE)
+       vp_door->y = vp_window->height / 2 - vp_door->height / 2;
+      else if (vp_door->valign == VALIGN_BOTTOM)
+       vp_door->y += vp_window->height - vp_door->height;
+    }
+  }
 }
 
 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
@@ -9894,6 +9892,40 @@ static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
     }
   }
+
+  // adjust editor palette rows and columns if specified to be dynamic
+
+  if (editor.palette.cols == -1)
+  {
+    int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
+    int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
+    int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
+
+    editor.palette.cols = (vp_width - sc_width) / bt_width;
+
+    if (editor.palette.x == -1)
+    {
+      int palette_width = editor.palette.cols * bt_width + sc_width;
+
+      editor.palette.x = (vp_width - palette_width) / 2;
+    }
+  }
+
+  if (editor.palette.rows == -1)
+  {
+    int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
+    int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
+    int tx_height = getFontHeight(FONT_TEXT_2);
+
+    editor.palette.rows = (vp_height - tx_height) / bt_height;
+
+    if (editor.palette.y == -1)
+    {
+      int palette_height = editor.palette.rows * bt_height + tx_height;
+
+      editor.palette.y = (vp_height - palette_height) / 2;
+    }
+  }
 }
 
 static void LoadMenuDesignSettingsFromFilename(char *filename)
@@ -10140,7 +10172,22 @@ static void LoadMenuDesignSettingsFromFilename(char *filename)
         { ".y",                        &vp_struct[j].struct_ptr->y             },
         { ".width",            &vp_struct[j].struct_ptr->width         },
         { ".height",           &vp_struct[j].struct_ptr->height        },
-        { ".border_size",      &vp_struct[j].struct_ptr->border_size   }
+        { ".min_width",                &vp_struct[j].struct_ptr->min_width     },
+        { ".min_height",       &vp_struct[j].struct_ptr->min_height    },
+        { ".max_width",                &vp_struct[j].struct_ptr->max_width     },
+        { ".max_height",       &vp_struct[j].struct_ptr->max_height    },
+        { ".margin_left",      &vp_struct[j].struct_ptr->margin_left   },
+        { ".margin_right",     &vp_struct[j].struct_ptr->margin_right  },
+        { ".margin_top",       &vp_struct[j].struct_ptr->margin_top    },
+        { ".margin_bottom",    &vp_struct[j].struct_ptr->margin_bottom },
+        { ".border_left",      &vp_struct[j].struct_ptr->border_left   },
+        { ".border_right",     &vp_struct[j].struct_ptr->border_right  },
+        { ".border_top",       &vp_struct[j].struct_ptr->border_top    },
+        { ".border_bottom",    &vp_struct[j].struct_ptr->border_bottom },
+        { ".border_size",      &vp_struct[j].struct_ptr->border_size   },
+        { ".align_size",       &vp_struct[j].struct_ptr->align_size    },
+        { ".align",            &vp_struct[j].struct_ptr->align         },
+        { ".valign",           &vp_struct[j].struct_ptr->valign        }
       };
 
       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
@@ -10217,6 +10264,25 @@ static void LoadMenuDesignSettingsFromFilename(char *filename)
     }
   }
 
+  // special case: check if network and preview player positions are redefined,
+  // to compare this later against the main menu level preview being redefined
+  struct TokenIntPtrInfo menu_config_players[] =
+  {
+    { "main.network_players.x",        &menu.main.network_players.redefined    },
+    { "main.network_players.y",        &menu.main.network_players.redefined    },
+    { "main.preview_players.x",        &menu.main.preview_players.redefined    },
+    { "main.preview_players.y",        &menu.main.preview_players.redefined    },
+    { "preview.x",             &preview.redefined                      },
+    { "preview.y",             &preview.redefined                      }
+  };
+
+  for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
+    *menu_config_players[i].value = FALSE;
+
+  for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
+    if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
+      *menu_config_players[i].value = TRUE;
+
   // read (and overwrite with) values that may be specified in config file
   for (i = 0; image_config_vars[i].token != NULL; i++)
   {