added option to disable warning about read-only levels when entering editor
[rocksndiamonds.git] / src / files.c
index 2ca9976684473f08bf0f9cbf1fe4954e79b0b1c5..e9634c5e03b3c833818ac8eaf60929852c5ca843 100644 (file)
@@ -4,7 +4,7 @@
 // (c) 1995-2014 by Artsoft Entertainment
 //                         Holger Schemel
 //                 info@artsoft.org
-//                 http://www.artsoft.org/
+//                 https://www.artsoft.org/
 // ----------------------------------------------------------------------------
 // files.c
 // ============================================================================
@@ -58,7 +58,8 @@
 
 #define TAPE_CHUNK_VERS_SIZE   8       // size of file version chunk
 #define TAPE_CHUNK_HEAD_SIZE   20      // size of tape file header
-#define TAPE_CHUNK_HEAD_UNUSED 2       // unused tape header bytes
+#define TAPE_CHUNK_HEAD_UNUSED 1       // unused tape header bytes
+#define TAPE_CHUNK_SCRN_SIZE   2       // size of screen size chunk
 
 #define LEVEL_CHUNK_CNT3_SIZE(x)        (LEVEL_CHUNK_CNT3_HEADER + (x))
 #define LEVEL_CHUNK_CUS3_SIZE(x)        (2 + (x) * LEVEL_CPART_CUS3_SIZE)
@@ -257,6 +258,12 @@ static struct LevelFileConfigInfo chunk_config_INFO[] =
     &li.solved_by_one_player,          FALSE
   },
 
+  {
+    -1,                                        -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(12),
+    &li.time_score_base,               1
+  },
+
   {
     -1,                                        -1,
     -1,                                        -1,
@@ -307,6 +314,11 @@ static struct LevelFileConfigInfo chunk_config_ELEM[] =
     TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(15),
     &li.lazy_relocation,               FALSE
   },
+  {
+    EL_PLAYER_1,                       -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(16),
+    &li.finish_dig_collect,            TRUE
+  },
 
   // (these values are different for each player)
   {
@@ -2070,7 +2082,7 @@ static char *getPackedLevelBasename(int type)
 
   if ((dir = openDirectory(directory)) == NULL)
   {
-    Error(ERR_WARN, "cannot read current level directory '%s'", directory);
+    Warn("cannot read current level directory '%s'", directory);
 
     return basename;
   }
@@ -2334,7 +2346,7 @@ static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
 // functions for loading R'n'D level
 // ----------------------------------------------------------------------------
 
-static int getMappedElement(int element)
+int getMappedElement(int element)
 {
   // remap some (historic, now obsolete) elements
 
@@ -2375,7 +2387,7 @@ static int getMappedElement(int element)
     default:
       if (element >= NUM_FILE_ELEMENTS)
       {
-       Error(ERR_WARN, "invalid level element %d", element);
+       Warn("invalid level element %d", element);
 
        element = EL_UNKNOWN;
       }
@@ -2623,7 +2635,7 @@ static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
   }
   else
   {
-    Error(ERR_WARN, "cannot load content for element '%d'", element);
+    Warn("cannot load content for element '%d'", element);
   }
 
   return chunk_size;
@@ -2683,7 +2695,7 @@ static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
     if (IS_CUSTOM_ELEMENT(element))
       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
     else
-      Error(ERR_WARN, "invalid custom element number %d", element);
+      Warn("invalid custom element number %d", element);
 
     // older game versions that wrote level files with CUS1 chunks used
     // different default push delay values (not yet stored in level file)
@@ -2716,7 +2728,7 @@ static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
     if (IS_CUSTOM_ELEMENT(element))
       element_info[element].change->target_element = custom_target_element;
     else
-      Error(ERR_WARN, "invalid custom element number %d", element);
+      Warn("invalid custom element number %d", element);
   }
 
   level->file_has_custom_elements = TRUE;
@@ -2744,7 +2756,7 @@ static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
 
     if (!IS_CUSTOM_ELEMENT(element))
     {
-      Error(ERR_WARN, "invalid custom element number %d", element);
+      Warn("invalid custom element number %d", element);
 
       element = EL_INTERNAL_DUMMY;
     }
@@ -2830,9 +2842,10 @@ static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
 
   if (!IS_CUSTOM_ELEMENT(element))
   {
-    Error(ERR_WARN, "invalid custom element number %d", element);
+    Warn("invalid custom element number %d", element);
 
     ReadUnusedBytesFromFile(file, chunk_size - 2);
+
     return chunk_size;
   }
 
@@ -2978,9 +2991,10 @@ static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
 
   if (!IS_GROUP_ELEMENT(element))
   {
-    Error(ERR_WARN, "invalid group element number %d", element);
+    Warn("invalid group element number %d", element);
 
     ReadUnusedBytesFromFile(file, chunk_size - 2);
+
     return chunk_size;
   }
 
@@ -3042,9 +3056,8 @@ static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
 
        if (num_entities > max_num_entities)
        {
-         Error(ERR_WARN,
-               "truncating number of entities for element %d from %d to %d",
-               element, num_entities, max_num_entities);
+         Warn("truncating number of entities for element %d from %d to %d",
+              element, num_entities, max_num_entities);
 
          num_entities = max_num_entities;
        }
@@ -3053,8 +3066,7 @@ static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
                                  data_type == TYPE_CONTENT_LIST))
        {
          // for element and content lists, zero entities are not allowed
-         Error(ERR_WARN, "found empty list of entities for element %d",
-               element);
+         Warn("found empty list of entities for element %d", element);
 
          // do not set "num_entities" here to prevent reading behind buffer
 
@@ -3145,9 +3157,9 @@ static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
     int error_element = real_element;
 
-    Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
-         error_conf_chunk_bytes, error_conf_chunk_token,
-         error_element, EL_NAME(error_element));
+    Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
+        error_conf_chunk_bytes, error_conf_chunk_token,
+        error_element, EL_NAME(error_element));
   }
 
   return micro_chunk_size;
@@ -3264,8 +3276,8 @@ static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
 
   if (ei->num_change_pages == -1)
   {
-    Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
-         EL_NAME(element));
+    Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
+        EL_NAME(element));
 
     ei->num_change_pages = 1;
 
@@ -3352,7 +3364,7 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
     if (level_info_only)
       return;
 
-    Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
+    Warn("cannot read level '%s' -- using empty level", filename);
 
     if (!setup.editor.use_template_for_new_levels)
       return;
@@ -3379,7 +3391,7 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
     {
       level->no_valid_file = TRUE;
 
-      Error(ERR_WARN, "unknown format of level file '%s'", filename);
+      Warn("unknown format of level file '%s'", filename);
 
       closeFile(file);
 
@@ -3398,7 +3410,7 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
     {
       level->no_valid_file = TRUE;
 
-      Error(ERR_WARN, "unknown format of level file '%s'", filename);
+      Warn("unknown format of level file '%s'", filename);
 
       closeFile(file);
 
@@ -3409,7 +3421,7 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
     {
       level->no_valid_file = TRUE;
 
-      Error(ERR_WARN, "unsupported version of level file '%s'", filename);
+      Warn("unsupported version of level file '%s'", filename);
 
       closeFile(file);
 
@@ -3470,15 +3482,17 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
 
       if (chunk_info[i].name == NULL)
       {
-       Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
-             chunk_name, filename);
+       Warn("unknown chunk '%s' in level file '%s'",
+            chunk_name, filename);
+
        ReadUnusedBytesFromFile(file, chunk_size);
       }
       else if (chunk_info[i].size != -1 &&
               chunk_info[i].size != chunk_size)
       {
-       Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
-             chunk_size, chunk_name, filename);
+       Warn("wrong size (%d) of chunk '%s' in level file '%s'",
+            chunk_size, chunk_name, filename);
+
        ReadUnusedBytesFromFile(file, chunk_size);
       }
       else
@@ -3492,8 +3506,8 @@ static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
        // information, so check them here
        if (chunk_size_expected != chunk_size)
        {
-         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
-               chunk_size, chunk_name, filename);
+         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
+              chunk_size, chunk_name, filename);
        }
       }
     }
@@ -3541,6 +3555,8 @@ static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
   cav->key_score       = level->score[SC_KEY];
   cav->exit_score      = level->score[SC_TIME_BONUS];
 
+  cav->num_eater_arrays        = level->num_yamyam_contents;
+
   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
     for (y = 0; y < 3; y++)
       for (x = 0; x < 3; x++)
@@ -3649,9 +3665,9 @@ static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
   level->score[SC_KEY]         = cav->key_score;
   level->score[SC_TIME_BONUS]  = cav->exit_score;
 
-  level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
+  level->num_yamyam_contents   = cav->num_eater_arrays;
 
-  for (i = 0; i < level->num_yamyam_contents; i++)
+  for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
     for (y = 0; y < 3; y++)
       for (x = 0; x < 3; x++)
        level->yamyam_content[i].e[x][y] =
@@ -3706,6 +3722,9 @@ static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
     if (jx != -1 && jy != -1)
       level->field[jx][jy] = EL_PLAYER_1 + nr;
   }
+
+  // time score is counted for each 10 seconds left in Emerald Mine levels
+  level->time_score_base = 10;
 }
 
 
@@ -3818,7 +3837,7 @@ static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
       {
        num_invalid_elements++;
 
-       Error(ERR_DEBUG, "invalid element %d at position %d, %d",
+       Debug("level:native:SP", "invalid element %d at position %d, %d",
              element_old, x, y);
       }
 
@@ -3827,8 +3846,8 @@ static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
   }
 
   if (num_invalid_elements > 0)
-    Error(ERR_WARN, "found %d invalid elements%s", num_invalid_elements,
-         (!options.debug ? " (use '--debug' for more details)" : ""));
+    Warn("found %d invalid elements%s", num_invalid_elements,
+        (!options.debug ? " (use '--debug' for more details)" : ""));
 
   for (i = 0; i < MAX_PLAYERS; i++)
     level->initial_player_gravity[i] =
@@ -3864,8 +3883,7 @@ static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
     if (port_x < 0 || port_x >= level->fieldx ||
        port_y < 0 || port_y >= level->fieldy)
     {
-      Error(ERR_WARN, "special port position (%d, %d) out of bounds",
-           port_x, port_y);
+      Warn("special port position (%d, %d) out of bounds", port_x, port_y);
 
       continue;
     }
@@ -3875,7 +3893,7 @@ static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
        port_element > EL_SP_GRAVITY_PORT_UP)
     {
-      Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
+      Warn("no special port at position (%d, %d)", port_x, port_y);
 
       continue;
     }
@@ -3941,8 +3959,8 @@ static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
 
     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);
+      Warn("tape truncated: size exceeds maximum SP demo size %d",
+          SP_MAX_TAPE_LEN);
 
       break;
     }
@@ -3995,8 +4013,8 @@ static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
 
     if (!success)
     {
-      Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
-           MAX_TAPE_LEN);
+      Warn("SP demo truncated: size exceeds maximum tape size %d",
+          MAX_TAPE_LEN);
 
       break;
     }
@@ -4271,7 +4289,7 @@ static int getMappedElement_DC(int element)
       break;
 
     case 0x13f5:
-      element = EL_YAMYAM;
+      element = EL_YAMYAM_UP;
       break;
 
     case 0x1425:
@@ -5297,7 +5315,7 @@ static int getMappedElement_DC(int element)
       break;
 
     case 0x1682:       // secret gate (red)
-      element = EL_GATE_1_GRAY;
+      element = EL_EM_GATE_1_GRAY;
       break;
 
     case 0x1683:       // gate (yellow)
@@ -5305,7 +5323,7 @@ static int getMappedElement_DC(int element)
       break;
 
     case 0x1684:       // secret gate (yellow)
-      element = EL_GATE_2_GRAY;
+      element = EL_EM_GATE_2_GRAY;
       break;
 
     case 0x1685:       // gate (blue)
@@ -5313,7 +5331,7 @@ static int getMappedElement_DC(int element)
       break;
 
     case 0x1686:       // secret gate (blue)
-      element = EL_GATE_4_GRAY;
+      element = EL_EM_GATE_4_GRAY;
       break;
 
     case 0x1687:       // gate (green)
@@ -5321,7 +5339,7 @@ static int getMappedElement_DC(int element)
       break;
 
     case 0x1688:       // secret gate (green)
-      element = EL_GATE_3_GRAY;
+      element = EL_EM_GATE_3_GRAY;
       break;
 
     case 0x1689:       // gate (white)
@@ -5552,7 +5570,8 @@ static int getMappedElement_DC(int element)
        element = EL_INVISIBLE_SAND;
       else
       {
-       Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
+       Warn("unknown Diamond Caves element 0x%04x", element);
+
        element = EL_UNKNOWN;
       }
       break;
@@ -5600,7 +5619,7 @@ static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
   {
     level->no_valid_file = TRUE;
 
-    Error(ERR_WARN, "cannot decode level from stream -- using empty level");
+    Warn("cannot decode level from stream -- using empty level");
 
     return;
   }
@@ -5713,9 +5732,19 @@ static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
   level->extra_time            = header[56] | (header[57] << 8);
   level->shield_normal_time    = header[58] | (header[59] << 8);
 
+  // shield and extra time elements do not have a score
+  level->score[SC_SHIELD]      = 0;
+  level->extra_time_score      = 0;
+
+  // set time for normal and deadly shields to the same value
+  level->shield_deadly_time    = level->shield_normal_time;
+
   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
   // can slip down from flat walls, like normal walls and steel walls
   level->em_slippery_gems = TRUE;
+
+  // time score is counted for each 10 seconds left in Diamond Caves levels
+  level->time_score_base = 10;
 }
 
 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
@@ -5733,7 +5762,7 @@ static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
     level->no_valid_file = TRUE;
 
     if (!level_info_only)
-      Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
+      Warn("cannot read level '%s' -- using empty level", filename);
 
     return;
   }
@@ -5751,8 +5780,7 @@ static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
     {
       level->no_valid_file = TRUE;
 
-      Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
-           filename);
+      Warn("unknown DC level file '%s' -- using empty level", filename);
 
       return;
     }
@@ -5778,8 +5806,7 @@ static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
        {
          level->no_valid_file = TRUE;
 
-         Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
-               filename);
+         Warn("cannot fseek in file '%s' -- using empty level", filename);
 
          return;
        }
@@ -5797,8 +5824,7 @@ static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
     {
       level->no_valid_file = TRUE;
 
-      Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
-           filename);
+      Warn("unknown DC2 level file '%s' -- using empty level", filename);
 
       return;
     }
@@ -5873,7 +5899,7 @@ static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
     level->no_valid_file = TRUE;
 
     if (!level_info_only)
-      Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
+      Warn("cannot read level '%s' -- using empty level", filename);
 
     return;
   }
@@ -6047,7 +6073,7 @@ static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
   {
     level->no_valid_file = TRUE;
 
-    Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
+    Warn("cannot read level '%s' -- using empty level", filename);
 
     return;
   }
@@ -6242,7 +6268,7 @@ static void LoadLevel_InitVersion(struct LevelInfo *level)
   if (level->game_version < VERSION_IDENT(3,2,0,5))
   {
     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
-    level->score[SC_TIME_BONUS] /= 10;
+    level->time_score_base = 10;
   }
 
   if (leveldir_current->latest_engine)
@@ -6414,6 +6440,10 @@ static void LoadLevel_InitVersion(struct LevelInfo *level)
   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
   if (level->game_version < VERSION_IDENT(4,1,1,1))
     level->sb_objects_needed = FALSE;
+
+  // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
+  if (level->game_version <= VERSION_IDENT(4,2,2,0))
+    level->finish_dig_collect = FALSE;
 }
 
 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
@@ -6594,7 +6624,7 @@ static void LoadLevel_InitPlayfield(struct LevelInfo *level)
   // copy elements to runtime playfield array
   for (x = 0; x < MAX_LEV_FIELDX; x++)
     for (y = 0; y < MAX_LEV_FIELDY; y++)
-      Feld[x][y] = level->field[x][y];
+      Tile[x][y] = level->field[x][y];
 
   // initialize level size variables for faster access
   lev_fieldx = level->fieldx;
@@ -6629,7 +6659,7 @@ void LoadLevelTemplate(int nr)
 {
   if (!fileExists(getGlobalLevelTemplateFilename()))
   {
-    Error(ERR_WARN, "no level template found for this level");
+    Warn("no level template found for this level");
 
     return;
   }
@@ -6792,8 +6822,8 @@ static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
   int chunk_size = 0;
   int x, y;
 
-  for (y = 0; y < level->fieldy; y++) 
-    for (x = 0; x < level->fieldx; x++) 
+  for (y = 0; y < level->fieldy; y++)
+    for (x = 0; x < level->fieldx; x++)
       if (level->encoding_16bit_field)
        chunk_size += putFile16BitBE(file, level->field[x][y]);
       else
@@ -6870,7 +6900,8 @@ static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
     // chunk header already written -- write empty chunk data
     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
 
-    Error(ERR_WARN, "cannot save content for element '%d'", element);
+    Warn("cannot save content for element '%d'", element);
+
     return;
   }
 
@@ -6938,7 +6969,7 @@ 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");
+    Warn("inconsistent number of custom element properties");
 }
 #endif
 
@@ -6967,7 +6998,7 @@ 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 target elements");
+    Warn("inconsistent number of custom target elements");
 }
 #endif
 
@@ -7050,7 +7081,7 @@ static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
   }
 
   if (check != num_changed_custom_elements)    // should not happen
-    Error(ERR_WARN, "inconsistent number of custom element properties");
+    Warn("inconsistent number of custom element properties");
 }
 #endif
 
@@ -7428,7 +7459,8 @@ static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
 
   if (!(file = fopen(filename, MODE_WRITE)))
   {
-    Error(ERR_WARN, "cannot save level file '%s'", filename);
+    Warn("cannot save level file '%s'", filename);
+
     return;
   }
 
@@ -7553,7 +7585,7 @@ void DumpLevel(struct LevelInfo *level)
 {
   if (level->no_level_file || level->no_valid_file)
   {
-    Error(ERR_WARN, "cannot dump -- no valid level file found");
+    Warn("cannot dump -- no valid level file found");
 
     return;
   }
@@ -7608,6 +7640,8 @@ static void setTapeInfoToDefaults(void)
   // at least one (default: the first) player participates in every tape
   tape.num_participating_players = 1;
 
+  tape.property_bits = TAPE_PROPERTY_NONE;
+
   tape.level_nr = level_nr;
   tape.counter = 0;
   tape.changed = FALSE;
@@ -7616,6 +7650,9 @@ static void setTapeInfoToDefaults(void)
   tape.playing = FALSE;
   tape.pausing = FALSE;
 
+  tape.scr_fieldx = SCR_FIELDX_DEFAULT;
+  tape.scr_fieldy = SCR_FIELDY_DEFAULT;
+
   tape.no_valid_file = FALSE;
 }
 
@@ -7623,9 +7660,10 @@ static int getTapePosSize(struct TapeInfo *tape)
 {
   int tape_pos_size = 0;
 
-  if (!tape->use_mouse)
+  if (tape->use_key_actions)
     tape_pos_size += tape->num_participating_players;
-  else
+
+  if (tape->use_mouse_actions)
     tape_pos_size += 3;                // x and y position and mouse button mask
 
   tape_pos_size += 1;          // tape action delay value
@@ -7633,6 +7671,27 @@ static int getTapePosSize(struct TapeInfo *tape)
   return tape_pos_size;
 }
 
+static void setTapeActionFlags(struct TapeInfo *tape, int value)
+{
+  tape->use_key_actions = FALSE;
+  tape->use_mouse_actions = FALSE;
+
+  if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
+    tape->use_key_actions = TRUE;
+
+  if (value != TAPE_USE_KEY_ACTIONS_ONLY)
+    tape->use_mouse_actions = TRUE;
+}
+
+static int getTapeActionValue(struct TapeInfo *tape)
+{
+  return (tape->use_key_actions &&
+         tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
+         tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
+         tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
+         TAPE_ACTIONS_DEFAULT);
+}
+
 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
 {
   tape->file_version = getFileVersion(file);
@@ -7668,7 +7727,9 @@ static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
       }
     }
 
-    tape->use_mouse = (getFile8Bit(file) == 1 ? TRUE : FALSE);
+    setTapeActionFlags(tape, getFile8Bit(file));
+
+    tape->property_bits = getFile8Bit(file);
 
     ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
 
@@ -7682,6 +7743,14 @@ static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
   return chunk_size;
 }
 
+static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
+{
+  tape->scr_fieldx = getFile8Bit(file);
+  tape->scr_fieldy = getFile8Bit(file);
+
+  return chunk_size;
+}
+
 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
 {
   int level_identifier_size;
@@ -7718,7 +7787,7 @@ static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
   {
     if (i >= MAX_TAPE_LEN)
     {
-      Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
+      Warn("tape truncated -- size exceeds maximum tape size %d",
            MAX_TAPE_LEN);
 
       // tape too large; read and ignore remaining tape data from this chunk
@@ -7728,15 +7797,7 @@ static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
       break;
     }
 
-    if (tape->use_mouse)
-    {
-      tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
-      tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
-      tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
-
-      tape->pos[i].action[TAPE_ACTION_UNUSED] = 0;
-    }
-    else
+    if (tape->use_key_actions)
     {
       for (j = 0; j < MAX_PLAYERS; j++)
       {
@@ -7747,6 +7808,13 @@ static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
       }
     }
 
+    if (tape->use_mouse_actions)
+    {
+      tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
+      tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
+      tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
+    }
+
     tape->pos[i].delay = getFile8Bit(file);
 
     if (tape->file_version == FILE_VERSION_1_0)
@@ -7865,7 +7933,7 @@ static void LoadTape_SokobanSolution(char *filename)
       default:
        tape.no_valid_file = TRUE;
 
-       Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
+       Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
 
        break;
     }
@@ -7914,7 +7982,7 @@ void LoadTapeFromFilename(char *filename)
     {
       tape.no_valid_file = TRUE;
 
-      Error(ERR_WARN, "unknown format of tape file '%s'", filename);
+      Warn("unknown format of tape file '%s'", filename);
 
       closeFile(file);
 
@@ -7933,7 +8001,7 @@ void LoadTapeFromFilename(char *filename)
     {
       tape.no_valid_file = TRUE;
 
-      Error(ERR_WARN, "unknown format of tape file '%s'", filename);
+      Warn("unknown format of tape file '%s'", filename);
 
       closeFile(file);
 
@@ -7944,7 +8012,7 @@ void LoadTapeFromFilename(char *filename)
     {
       tape.no_valid_file = TRUE;
 
-      Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
+      Warn("unsupported version of tape file '%s'", filename);
 
       closeFile(file);
 
@@ -7973,6 +8041,7 @@ void LoadTapeFromFilename(char *filename)
     {
       { "VERS", TAPE_CHUNK_VERS_SIZE,  LoadTape_VERS },
       { "HEAD", TAPE_CHUNK_HEAD_SIZE,  LoadTape_HEAD },
+      { "SCRN", TAPE_CHUNK_SCRN_SIZE,  LoadTape_SCRN },
       { "INFO", -1,                    LoadTape_INFO },
       { "BODY", -1,                    LoadTape_BODY },
       {  NULL,  0,                     NULL }
@@ -7988,15 +8057,17 @@ void LoadTapeFromFilename(char *filename)
 
       if (chunk_info[i].name == NULL)
       {
-       Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
+       Warn("unknown chunk '%s' in tape file '%s'",
              chunk_name, filename);
+
        ReadUnusedBytesFromFile(file, chunk_size);
       }
       else if (chunk_info[i].size != -1 &&
               chunk_info[i].size != chunk_size)
       {
-       Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
+       Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
              chunk_size, chunk_name, filename);
+
        ReadUnusedBytesFromFile(file, chunk_size);
       }
       else
@@ -8010,7 +8081,7 @@ void LoadTapeFromFilename(char *filename)
        // information, so check them here
        if (chunk_size_expected != chunk_size)
        {
-         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
+         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
                chunk_size, chunk_name, filename);
        }
       }
@@ -8023,9 +8094,12 @@ void LoadTapeFromFilename(char *filename)
   tape.length_seconds = GetTapeLengthSeconds();
 
 #if 0
-  printf("::: tape file version: %d\n",   tape.file_version);
-  printf("::: tape game version: %d\n",   tape.game_version);
-  printf("::: tape engine version: %d\n", tape.engine_version);
+  Debug("files:LoadTapeFromFilename", "tape file version: %d",
+       tape.file_version);
+  Debug("files:LoadTapeFromFilename", "tape game version: %d",
+       tape.game_version);
+  Debug("files:LoadTapeFromFilename", "tape engine version: %d",
+       tape.engine_version);
 #endif
 }
 
@@ -8048,6 +8122,14 @@ void LoadSolutionTape(int nr)
     CopyNativeTape_SP_to_RND(&level);
 }
 
+static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
+{
+  // chunk required for team mode tapes with non-default screen size
+  return (tape->num_participating_players > 1 &&
+         (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
+          tape->scr_fieldy != SCR_FIELDY_DEFAULT));
+}
+
 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
 {
   putFileVersion(file, tape->file_version);
@@ -8070,7 +8152,9 @@ static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
 
   putFile8Bit(file, store_participating_players);
 
-  putFile8Bit(file, (tape->use_mouse ? 1 : 0));
+  putFile8Bit(file, getTapeActionValue(tape));
+
+  putFile8Bit(file, tape->property_bits);
 
   // unused bytes not at the end here for 4-byte alignment of engine_version
   WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
@@ -8078,6 +8162,12 @@ static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
   putFileVersion(file, tape->engine_version);
 }
 
+static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
+{
+  putFile8Bit(file, tape->scr_fieldx);
+  putFile8Bit(file, tape->scr_fieldy);
+}
+
 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
 {
   int level_identifier_size = strlen(tape->level_identifier) + 1;
@@ -8097,50 +8187,38 @@ static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
 
   for (i = 0; i < tape->length; i++)
   {
-    if (tape->use_mouse)
-    {
-      putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
-      putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
-      putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
-    }
-    else
+    if (tape->use_key_actions)
     {
       for (j = 0; j < MAX_PLAYERS; j++)
        if (tape->player_participates[j])
          putFile8Bit(file, tape->pos[i].action[j]);
     }
 
+    if (tape->use_mouse_actions)
+    {
+      putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
+      putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
+      putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
+    }
+
     putFile8Bit(file, tape->pos[i].delay);
   }
 }
 
-void SaveTape(int nr)
+void SaveTapeToFilename(char *filename)
 {
-  char *filename = getTapeFilename(nr);
   FILE *file;
   int tape_pos_size;
   int info_chunk_size;
   int body_chunk_size;
-  int i;
-
-  InitTapeDirectory(leveldir_current->subdir);
 
   if (!(file = fopen(filename, MODE_WRITE)))
   {
-    Error(ERR_WARN, "cannot save level recording file '%s'", filename);
+    Warn("cannot save level recording file '%s'", filename);
+
     return;
   }
 
-  tape.file_version = FILE_VERSION_ACTUAL;
-  tape.game_version = GAME_VERSION_ACTUAL;
-
-  tape.num_participating_players = 0;
-
-  // count number of participating players
-  for (i = 0; i < MAX_PLAYERS; i++)
-    if (tape.player_participates[i])
-      tape.num_participating_players++;
-
   tape_pos_size = getTapePosSize(&tape);
 
   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
@@ -8155,6 +8233,12 @@ void SaveTape(int nr)
   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
   SaveTape_HEAD(file, &tape);
 
+  if (checkSaveTape_SCRN(&tape))
+  {
+    putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
+    SaveTape_SCRN(file, &tape);
+  }
+
   putFileChunkBE(file, "INFO", info_chunk_size);
   SaveTape_INFO(file, &tape);
 
@@ -8164,6 +8248,26 @@ void SaveTape(int nr)
   fclose(file);
 
   SetFilePermissions(filename, PERMS_PRIVATE);
+}
+
+void SaveTape(int nr)
+{
+  char *filename = getTapeFilename(nr);
+  int i;
+
+  InitTapeDirectory(leveldir_current->subdir);
+
+  tape.file_version = FILE_VERSION_ACTUAL;
+  tape.game_version = GAME_VERSION_ACTUAL;
+
+  tape.num_participating_players = 0;
+
+  // count number of participating players
+  for (i = 0; i < MAX_PLAYERS; i++)
+    if (tape.player_participates[i])
+      tape.num_participating_players++;
+
+  SaveTapeToFilename(filename);
 
   tape.changed = FALSE;
 }
@@ -8206,7 +8310,7 @@ void DumpTape(struct TapeInfo *tape)
 
   if (tape->no_valid_file)
   {
-    Error(ERR_WARN, "cannot dump -- no valid tape file found");
+    Warn("cannot dump -- no valid tape file found");
 
     return;
   }
@@ -8286,15 +8390,18 @@ void LoadScore(int nr)
 
   if (!checkCookieString(cookie, SCORE_COOKIE))
   {
-    Error(ERR_WARN, "unknown format of score file '%s'", filename);
+    Warn("unknown format of score file '%s'", filename);
+
     fclose(file);
+
     return;
   }
 
   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
   {
     if (fscanf(file, "%d", &highscore[i].Score) == EOF)
-      Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
+      Warn("fscanf() failed; %s", strerror(errno));
+
     if (fgets(line, MAX_LINE_LEN, file) == NULL)
       line[0] = '\0';
 
@@ -8327,7 +8434,8 @@ void SaveScore(int nr)
 
   if (!(file = fopen(filename, MODE_WRITE)))
   {
-    Error(ERR_WARN, "cannot save score for level %d", nr);
+    Warn("cannot save score for level %d", nr);
+
     return;
   }
 
@@ -8355,6 +8463,10 @@ static struct TokenInfo global_setup_tokens[] =
     TYPE_STRING,
     &setup.player_name,                                "player_name"
   },
+  {
+    TYPE_SWITCH,
+    &setup.multiple_users,                     "multiple_users"
+  },
   {
     TYPE_SWITCH,
     &setup.sound,                              "sound"
@@ -8483,6 +8595,14 @@ static struct TokenInfo global_setup_tokens[] =
     TYPE_SWITCH,
     &setup.prefer_aga_graphics,                        "prefer_aga_graphics"
   },
+  {
+    TYPE_SWITCH,
+    &setup.prefer_lowpass_sounds,              "prefer_lowpass_sounds"
+  },
+  {
+    TYPE_SWITCH,
+    &setup.prefer_extra_panel_items,           "prefer_extra_panel_items"
+  },
   {
     TYPE_SWITCH,
     &setup.game_speed_extended,                        "game_speed_extended"
@@ -8627,6 +8747,10 @@ static struct TokenInfo editor_setup_tokens[] =
     TYPE_SWITCH,
     &setup.editor.show_element_token,          "editor.show_element_token"
   },
+  {
+    TYPE_SWITCH,
+    &setup.editor.show_read_only_warning,      "editor.show_read_only_warning"
+  },
 };
 
 static struct TokenInfo editor_cascade_setup_tokens[] =
@@ -8860,6 +8984,10 @@ static struct TokenInfo player_setup_tokens[] =
 
 static struct TokenInfo system_setup_tokens[] =
 {
+  {
+    TYPE_STRING,
+    &setup.system.sdl_renderdriver,            "system.sdl_renderdriver"
+  },
   {
     TYPE_STRING,
     &setup.system.sdl_videodriver,             "system.sdl_videodriver"
@@ -9091,6 +9219,14 @@ static struct TokenInfo debug_setup_tokens[] =
     TYPE_BOOLEAN,
     &setup.debug.show_frames_per_second,       "debug.show_frames_per_second"
   },
+  {
+    TYPE_SWITCH3,
+    &setup.debug.xsn_mode,                     "debug.xsn_mode"
+  },
+  {
+    TYPE_INTEGER,
+    &setup.debug.xsn_percent,                  "debug.xsn_percent"
+  },
 };
 
 static struct TokenInfo options_setup_tokens[] =
@@ -9101,26 +9237,13 @@ static struct TokenInfo options_setup_tokens[] =
   },
 };
 
-static char *get_corrected_login_name(char *login_name)
-{
-  // needed because player name must be a fixed length string
-  char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
-
-  strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
-  login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
-
-  if (strlen(login_name) > MAX_PLAYER_NAME_LEN)                // name has been cut
-    if (strchr(login_name_new, ' '))
-      *strchr(login_name_new, ' ') = '\0';
-
-  return login_name_new;
-}
-
 static void setSetupInfoToDefaults(struct SetupInfo *si)
 {
   int i;
 
-  si->player_name = get_corrected_login_name(getLoginName());
+  si->player_name = getStringCopy(getDefaultUserName(user.nr));
+
+  si->multiple_users = TRUE;
 
   si->sound = TRUE;
   si->sound_loops = TRUE;
@@ -9154,6 +9277,8 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->quick_switch = FALSE;
   si->input_on_focus = FALSE;
   si->prefer_aga_graphics = TRUE;
+  si->prefer_lowpass_sounds = FALSE;
+  si->prefer_extra_panel_items = TRUE;
   si->game_speed_extended = FALSE;
   si->game_frame_delay = GAME_FRAME_DELAY;
   si->sp_show_border_elements = FALSE;
@@ -9257,6 +9382,8 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
 
   si->editor.show_element_token                = FALSE;
 
+  si->editor.show_read_only_warning    = TRUE;
+
   si->editor.use_template_for_new_levels = TRUE;
 
   si->shortcut.save_game       = DEFAULT_KEY_SAVE_GAME;
@@ -9305,6 +9432,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
   }
 
+  si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
@@ -9362,11 +9490,16 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
 
   si->debug.show_frames_per_second = FALSE;
 
+  si->debug.xsn_mode = AUTO;
+  si->debug.xsn_percent = 0;
+
   si->options.verbose = FALSE;
 
 #if defined(PLATFORM_ANDROID)
   si->fullscreen = TRUE;
 #endif
+
+  setHideSetupEntry(&setup.debug.xsn_mode);
 }
 
 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
@@ -9413,10 +9546,21 @@ void setHideSetupEntry(void *setup_value)
 {
   char *hide_setup_token = getHideSetupToken(setup_value);
 
+  if (hide_setup_hash == NULL)
+    hide_setup_hash = newSetupFileHash();
+
   if (setup_value != NULL)
     setHashEntry(hide_setup_hash, hide_setup_token, "");
 }
 
+void removeHideSetupEntry(void *setup_value)
+{
+  char *hide_setup_token = getHideSetupToken(setup_value);
+
+  if (setup_value != NULL)
+    removeHashEntry(hide_setup_hash, hide_setup_token);
+}
+
 boolean hideSetupEntry(void *setup_value)
 {
   char *hide_setup_token = getHideSetupToken(setup_value);
@@ -9438,6 +9582,8 @@ static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
   // 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);
+
+  free(token_hide_text);
 }
 
 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
@@ -9455,9 +9601,6 @@ static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
   if (!setup_file_hash)
     return;
 
-  if (hide_setup_hash == NULL)
-    hide_setup_hash = newSetupFileHash();
-
   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
 
@@ -9564,6 +9707,43 @@ static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
                              editor_cascade_setup_tokens[i].text));
 }
 
+void LoadUserNames(void)
+{
+  int last_user_nr = user.nr;
+  int i;
+
+  if (global.user_names != NULL)
+  {
+    for (i = 0; i < MAX_PLAYER_NAMES; i++)
+      checked_free(global.user_names[i]);
+
+    checked_free(global.user_names);
+  }
+
+  global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
+
+  for (i = 0; i < MAX_PLAYER_NAMES; i++)
+  {
+    user.nr = i;
+
+    SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
+
+    if (setup_file_hash)
+    {
+      char *player_name = getHashEntry(setup_file_hash, "player_name");
+
+      global.user_names[i] = getFixedUserName(player_name);
+
+      freeSetupFileHash(setup_file_hash);
+    }
+
+    if (global.user_names[i] == NULL)
+      global.user_names[i] = getStringCopy(getDefaultUserName(i));
+  }
+
+  user.nr = last_user_nr;
+}
+
 void LoadSetupFromFilename(char *filename)
 {
   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
@@ -9576,7 +9756,7 @@ void LoadSetupFromFilename(char *filename)
   }
   else
   {
-    Error(ERR_DEBUG, "using default setup values");
+    Debug("setup", "using default setup values");
   }
 }
 
@@ -9585,7 +9765,7 @@ static void LoadSetup_SpecialPostProcessing(void)
   char *player_name_new;
 
   // needed to work around problems with fixed length strings
-  player_name_new = get_corrected_login_name(setup.player_name);
+  player_name_new = getFixedUserName(setup.player_name);
   free(setup.player_name);
   setup.player_name = player_name_new;
 
@@ -9693,7 +9873,7 @@ static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
 
   if (!(file = fopen(filename, MODE_READ)))
   {
-    Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
+    Warn("cannot read game controller mappings file '%s'", filename);
 
     return;
   }
@@ -9721,7 +9901,8 @@ void SaveSetup(void)
 
   if (!(file = fopen(filename, MODE_WRITE)))
   {
-    Error(ERR_WARN, "cannot write setup file '%s'", filename);
+    Warn("cannot write setup file '%s'", filename);
+
     return;
   }
 
@@ -9730,7 +9911,8 @@ void SaveSetup(void)
   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
   {
     // just to make things nicer :)
-    if (global_setup_tokens[i].value == &setup.sound                   ||
+    if (global_setup_tokens[i].value == &setup.multiple_users          ||
+       global_setup_tokens[i].value == &setup.sound                    ||
        global_setup_tokens[i].value == &setup.graphics_set             ||
        global_setup_tokens[i].value == &setup.volume_simple            ||
        global_setup_tokens[i].value == &setup.network_mode             ||
@@ -9798,7 +9980,9 @@ void SaveSetup(void)
 
   fprintf(file, "\n");
   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
-    fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
+    if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
+       setup.debug.xsn_mode != AUTO)
+      fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
 
   fprintf(file, "\n");
   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
@@ -9819,8 +10003,10 @@ void SaveSetup_AutoSetup(void)
 
   if (!(file = fopen(filename, MODE_WRITE)))
   {
-    Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
+    Warn("cannot write auto setup file '%s'", filename);
+
     free(filename);
+
     return;
   }
 
@@ -9846,8 +10032,10 @@ void SaveSetup_EditorCascade(void)
 
   if (!(file = fopen(filename, MODE_WRITE)))
   {
-    Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
+    Warn("cannot write editor cascade state file '%s'", filename);
+
     free(filename);
+
     return;
   }
 
@@ -9870,7 +10058,7 @@ static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
 
   if (!(file = fopen(filename, MODE_WRITE)))
   {
-    Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
+    Warn("cannot write game controller mappings file '%s'", filename);
 
     return;
   }
@@ -9943,7 +10131,7 @@ static int getElementFromToken(char *token)
   if (value != NULL)
     return atoi(value);
 
-  Error(ERR_WARN, "unknown element token '%s'", token);
+  Warn("unknown element token '%s'", token);
 
   return EL_UNDEFINED;
 }
@@ -10200,6 +10388,10 @@ static int get_anim_parameter_values(char *s)
 
 static int get_anim_action_parameter_value(char *token)
 {
+  // check most common default case first to massively speed things up
+  if (strEqual(token, ARG_UNDEFINED))
+    return ANIM_EVENT_ACTION_NONE;
+
   int result = getImageIDFromToken(token);
 
   if (result == -1)
@@ -10323,6 +10515,9 @@ int get_parameter_value(char *value_raw, char *suffix, int type)
     if (string_has_parameter(value, "reverse"))
       result |= STYLE_REVERSE;
 
+    if (string_has_parameter(value, "leftmost_position"))
+      result |= STYLE_LEFTMOST_POSITION;
+
     if (string_has_parameter(value, "block_clicks"))
       result |= STYLE_BLOCK;
 
@@ -11292,19 +11487,19 @@ void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
     {
       if (num_unknown_tokens == 0)
       {
-       Error(ERR_INFO_LINE, "-");
-       Error(ERR_INFO, "warning: unknown token(s) found in config file:");
-       Error(ERR_INFO, "- config file: '%s'", filename);
+       Warn("---");
+       Warn("unknown token(s) found in config file:");
+       Warn("- config file: '%s'", filename);
 
        num_unknown_tokens++;
       }
 
-      Error(ERR_INFO, "- token: '%s'", list->token);
+      Warn("- token: '%s'", list->token);
     }
   }
 
   if (num_unknown_tokens > 0)
-    Error(ERR_INFO_LINE, "-");
+    Warn("---");
 
   while (*num_elements % 4)    // pad with empty elements, if needed
     (*elements)[(*num_elements)++] = EL_EMPTY;
@@ -11314,8 +11509,8 @@ void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
 
 #if 0
   for (i = 0; i < *num_elements; i++)
-    printf("editor: element '%s' [%d]\n",
-          element_info[(*elements)[i]].token_name, (*elements)[i]);
+    Debug("editor", "element '%s' [%d]\n",
+         element_info[(*elements)[i]].token_name, (*elements)[i]);
 #endif
 }
 
@@ -11494,7 +11689,8 @@ void LoadMusicInfo(void)
 
   if ((dir = openDirectory(music_directory)) == NULL)
   {
-    Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
+    Warn("cannot read music directory '%s'", music_directory);
+
     return;
   }
 
@@ -11582,18 +11778,18 @@ static void print_unknown_token(char *filename, char *token, int token_nr)
 {
   if (token_nr == 0)
   {
-    Error(ERR_INFO_LINE, "-");
-    Error(ERR_INFO, "warning: unknown token(s) found in config file:");
-    Error(ERR_INFO, "- config file: '%s'", filename);
+    Warn("---");
+    Warn("unknown token(s) found in config file:");
+    Warn("- config file: '%s'", filename);
   }
 
-  Error(ERR_INFO, "- token: '%s'", token);
+  Warn("- token: '%s'", token);
 }
 
 static void print_unknown_token_end(int token_nr)
 {
   if (token_nr > 0)
-    Error(ERR_INFO_LINE, "-");
+    Warn("---");
 }
 
 void LoadHelpAnimInfo(void)
@@ -11797,12 +11993,12 @@ void LoadHelpAnimInfo(void)
 
 #if 0
   for (i = 0; i < num_list_entries; i++)
-    printf("::: '%s': %d, %d, %d => %d\n",
-          EL_NAME(helpanim_info[i].element),
-          helpanim_info[i].element,
-          helpanim_info[i].action,
-          helpanim_info[i].direction,
-          helpanim_info[i].delay);
+    Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
+         EL_NAME(helpanim_info[i].element),
+         helpanim_info[i].element,
+         helpanim_info[i].action,
+         helpanim_info[i].direction,
+         helpanim_info[i].delay);
 #endif
 }
 
@@ -11834,8 +12030,8 @@ void LoadHelpTextInfo(void)
 #if 0
   BEGIN_HASH_ITERATION(helptext_info, itr)
   {
-    printf("::: '%s' => '%s'\n",
-          HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
+    Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
+         HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
   }
   END_HASH_ITERATION(hash, itr)
 #endif
@@ -11861,8 +12057,7 @@ void ConvertLevels(void)
                                               global.convert_leveldir);
 
   if (convert_leveldir == NULL)
-    Error(ERR_EXIT, "no such level identifier: '%s'",
-         global.convert_leveldir);
+    Fail("no such level identifier: '%s'", global.convert_leveldir);
 
   leveldir_current = convert_leveldir;
 
@@ -11989,13 +12184,13 @@ void CreateLevelSketchImages(void)
     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
 
     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
-      Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
+      Fail("cannot save level sketch image file '%s'", filename1);
 
     DrawSizedElement(0, 0, element, MINI_TILESIZE);
     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
 
     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
-      Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
+      Fail("cannot save level sketch image file '%s'", filename2);
 
     free(filename1);
     free(filename2);
@@ -12021,7 +12216,7 @@ void CreateLevelSketchImages(void)
   if (options.debug)
     fprintf(stderr, "\n");
 
-  Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
+  Info("%d normal and small images created", NUM_FILE_ELEMENTS);
 
   CloseAllAndExit(0);
 }
@@ -12119,7 +12314,7 @@ void CreateCustomElementImages(char *directory)
   }
 
   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
-    Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
+    Fail("cannot save CE graphics file '%s'", dst_filename);
 
   FreeBitmap(bitmap);