changed some EM engine game element names
[rocksndiamonds.git] / src / game_em / convert.c
index bea631b5baeb7b8d7f265c0755b0a1817f5cc8de..116cde4bfb2bd51708dbde2142cff266e0f9311d 100644 (file)
@@ -11,8 +11,6 @@
 #include "main_em.h"
 
 
 #include "main_em.h"
 
 
-#define ALLOW_ROLLING_SPRING
-
 static unsigned char remap_v6[256] =
 {
   /* filter crap for v6 */
 static unsigned char remap_v6[256] =
 {
   /* filter crap for v6 */
@@ -22,11 +20,7 @@ static unsigned char remap_v6[256] =
   0,16,2,18,       36,37,37,37,     40,41,42,43,     44,45,128,128,
   128,148,148,     148,45,45,45,    148,0,57,58,     59,60,61,62,63,
 
   0,16,2,18,       36,37,37,37,     40,41,42,43,     44,45,128,128,
   128,148,148,     148,45,45,45,    148,0,57,58,     59,60,61,62,63,
 
-#ifdef ALLOW_ROLLING_SPRING
   64,65,66,67,     68,69,69,71,     72,73,74,75,     118,75,75,75,
   64,65,66,67,     68,69,69,71,     72,73,74,75,     118,75,75,75,
-#else
-  64,65,66,67,     68,69,69,69,     69,73,74,75,     118,75,75,75,
-#endif
   75,75,75,75,     75,153,153,153,  153,153,153,153, 153,153,153,153,
   153,153,153,99,  100,68,68,68,    68,68,68,68,     68,118,118,118,
   118,118,114,115, 131,118,118,119, 120,121,122,118, 118,118,118,118,
   75,75,75,75,     75,153,153,153,  153,153,153,153, 153,153,153,153,
   153,153,153,99,  100,68,68,68,    68,68,68,68,     68,118,118,118,
   118,118,114,115, 131,118,118,119, 120,121,122,118, 118,118,118,118,
@@ -101,20 +95,31 @@ static unsigned char remap_v4eater[28] =
   146,147,175,65,66,64,2,18
 };
 
   146,147,175,65,66,64,2,18
 };
 
-int cleanup_em_level(unsigned char *src, int length)
+static boolean filename_has_v1_format(char *filename)
+{
+  char *basename = getBaseNamePtr(filename);
+
+  return (strlen(basename) == 3 &&
+         basename[0] == 'a' &&
+         basename[1] >= 'a' && basename[1] <= 'k' &&
+         basename[2] >= '0' && basename[2] <= '9');
+}
+
+int cleanup_em_level(unsigned char *src, int length, char *filename)
 {
 {
-  int file_version = 0;
+  int file_version = FILE_VERSION_EM_UNKNOWN;
   int i;
 
   if (length >= 2172 &&
   int i;
 
   if (length >= 2172 &&
-      src[2106] == 255 &&
-      src[2107] == 54 &&
-      src[2108] == 48 &&
-      src[2109] == 48)
+      src[2106] == 255 &&              /* version id: */
+      src[2107] == 54 &&               /* '6' */
+      src[2108] == 48 &&               /* '0' */
+      src[2109] == 48)                 /* '0' */
   {
     /* ---------- this cave has V6 file format ---------- */
     file_version = FILE_VERSION_EM_V6;
 
   {
     /* ---------- this cave has V6 file format ---------- */
     file_version = FILE_VERSION_EM_V6;
 
+    /* remap elements to internal EMC level format */
     for (i = 0; i < 2048; i++)
       src[i] = remap_v6[src[i]];
     for (i = 2048; i < 2084; i++)
     for (i = 0; i < 2048; i++)
       src[i] = remap_v6[src[i]];
     for (i = 2048; i < 2084; i++)
@@ -123,14 +128,15 @@ int cleanup_em_level(unsigned char *src, int length)
       src[i] = remap_v6[src[i]];
   }
   else if (length >= 2110 &&
       src[i] = remap_v6[src[i]];
   }
   else if (length >= 2110 &&
-          src[2106] == 255 &&
-          src[2107] == 53 &&
-          src[2108] == 48 &&
-          src[2109] == 48)
+          src[2106] == 255 &&          /* version id: */
+          src[2107] == 53 &&           /* '5' */
+          src[2108] == 48 &&           /* '0' */
+          src[2109] == 48)             /* '0' */
   {
     /* ---------- this cave has V5 file format ---------- */
     file_version = FILE_VERSION_EM_V5;
 
   {
     /* ---------- this cave has V5 file format ---------- */
     file_version = FILE_VERSION_EM_V5;
 
+    /* remap elements to internal EMC level format */
     for (i = 0; i < 2048; i++)
       src[i] = remap_v5[src[i]];
     for (i = 2048; i < 2084; i++)
     for (i = 0; i < 2048; i++)
       src[i] = remap_v5[src[i]];
     for (i = 2048; i < 2084; i++)
@@ -138,54 +144,64 @@ int cleanup_em_level(unsigned char *src, int length)
     for (i = 2112; i < 2148; i++)
       src[i] = src[i - 64];
   }
     for (i = 2112; i < 2148; i++)
       src[i] = src[i - 64];
   }
-#if 0
-  else if (length >= 2106)     /* !!! TEST ONLY: SHOW BROKEN LEVELS !!! */
-#else
   else if (length >= 2106 &&
   else if (length >= 2106 &&
-          src[1983] == 116)
-#endif
+          (src[1983] == 27 ||          /* encrypted (only EM I/II/III) */
+           src[1983] == 116 ||         /* unencrypted (usual case) */
+           src[1983] == 131))          /* unencrypted (rare case) */
   {
   {
-    /* ---------- this cave has V4 file format ---------- */
-    file_version = FILE_VERSION_EM_V4;
+    /* ---------- this cave has V1, V2 or V3 file format ---------- */
 
 
-    for (i = 0; i < 2048; i++)
-      src[i] = remap_v4[src[i]];
-    for (i = 2048; i < 2084; i++)
-      src[i] = remap_v4eater[src[i] >= 28 ? 0 : src[i]];
-    for (i = 2112; i < 2148; i++) src[i] = src[i - 64];
-  }
-  else if (length >= 2106 &&
-          src[0] == 241 &&     /* <-- Emerald Mine I levels */
-          src[1983] == 27)
-  {
-    unsigned char j = 94;
+    boolean fix_copyright = FALSE;
 
 
-    /* ---------- this cave has V3 file format ---------- */
-    file_version = FILE_VERSION_EM_V3;
+    /*
+      byte at position 1983 (0x07bf) is used as "magic byte":
+      - 27  (0x1b)     => encrypted level (V3 only / Kingsoft original games)
+      - 116 (0x74)     => unencrypted level (byte is corrected to 131 (0x83))
+      - 131 (0x83)     => unencrypted level (happens only in very rare cases)
+    */
 
 
-    for (i = 0; i < 2106; i++)
-      src[i] = (src[i] ^ (j += 7)) - 0x11;
-    src[1] = 131;
-    for (i = 0; i < 2048; i++)
-      src[i] = remap_v4[src[i]];
-    for (i = 2048; i < 2084; i++)
-      src[i] = remap_v4eater[src[i] >= 28 ? 0 : src[i]];
-    for (i = 2112; i < 2148; i++)
-      src[i] = src[i - 64];
-  }
-#if 1
-  else if (length >= 2106 &&
-          src[0] == 245 &&     /* <-- Emerald Mine II levels */
-          src[1983] == 27)
-  {
-    unsigned char j = 94;
+    if (src[1983] == 27)       /* (0x1b) -- after decryption: 116 (0x74) */
+    {
+      /* this is original (encrypted) Emerald Mine I, II or III level file */
+
+      int first_byte = src[0];
+      unsigned char code0 = 0x65;
+      unsigned char code1 = 0x11;
+
+      /* decode encrypted level data */
+      for (i = 0; i < 2106; i++)
+      {
+       src[i] ^= code0;
+       src[i] -= code1;
 
 
-    /* ---------- this cave has V3 file format ---------- */
-    file_version = FILE_VERSION_EM_V3;
+       code0 = (code0 + 7) & 0xff;
+      }
+
+      src[1] = 131;            /* needed for all Emerald Mine levels */
+
+      /* first byte is either 0xf1 (EM I and III) or 0xf5 (EM II) */
+      if (first_byte == 0xf5)
+      {
+       src[0] = 131;           /* only needed for Emerald Mine II levels */
+
+       fix_copyright = TRUE;
+      }
+
+      /* ---------- this cave has V3 file format ---------- */
+      file_version = FILE_VERSION_EM_V3;
+    }
+    else if (filename_has_v1_format(filename))
+    {
+      /* ---------- this cave has V1 file format ---------- */
+      file_version = FILE_VERSION_EM_V1;
+    }
+    else
+    {
+      /* ---------- this cave has V2 file format ---------- */
+      file_version = FILE_VERSION_EM_V2;
+    }
 
 
-    for (i = 0; i < 2106; i++)
-      src[i] = (src[i] ^ (j += 7)) - 0x11;
-    src[1] = 131;
+    /* remap elements to internal EMC level format */
     for (i = 0; i < 2048; i++)
       src[i] = remap_v4[src[i]];
     for (i = 2048; i < 2084; i++)
     for (i = 0; i < 2048; i++)
       src[i] = remap_v4[src[i]];
     for (i = 2048; i < 2084; i++)
@@ -193,33 +209,40 @@ int cleanup_em_level(unsigned char *src, int length)
     for (i = 2112; i < 2148; i++)
       src[i] = src[i - 64];
 
     for (i = 2112; i < 2148; i++)
       src[i] = src[i - 64];
 
-    /* fix copyright sign in Emerald Mine II levels */
-    for (i = 0; i < 2048; i++)
-      if (src[i] == 241)
-       src[i] = 254;           /* replace 'Xdecor_1' with 'Xalpha_copyr' */
+    if (fix_copyright)         /* fix "(c)" sign in Emerald Mine II levels */
+    {
+      for (i = 0; i < 2048; i++)
+       if (src[i] == 241)
+         src[i] = 254;         /* replace 'Xdecor_1' with 'Xalpha_copyr' */
+    }
   }
   }
-#endif
   else
   {
     /* ---------- this cave has unknown file format ---------- */
 
   else
   {
     /* ---------- this cave has unknown file format ---------- */
 
-#if 0
-    printf("::: %d, %d\n", src[0], src[1983]);
-#endif
+    /* if file has length of old-style level file, print (wrong) magic byte */
+    if (length < 2110)
+      Error(ERR_WARN, "unknown magic byte 0x%02x at position 0x%04x",
+           src[1983], 1983);
 
 
-    return 0;
+    return FILE_VERSION_EM_UNKNOWN;
   }
 
   if (file_version < FILE_VERSION_EM_V6)
   {
     /* id */
   }
 
   if (file_version < FILE_VERSION_EM_V6)
   {
     /* id */
-    src[2106] = 255;
-    src[2107] = 54;
-    src[2108] = 48;
-    src[2109] = 48;
+    src[2106] = 255;           /* version id: */
+    src[2107] = 54;            /* '6' */
+    src[2108] = 48;            /* '0' */
+    src[2109] = 48;            /* '0' */
 
     /* time */
     i = src[2094] * 10;
 
     /* time */
     i = src[2094] * 10;
+    /* stored level time of levels for the V2 player was changed to 50% of the
+       time for the V1 player (original V3 levels already considered this) */
+    if (file_version != FILE_VERSION_EM_V1 &&
+       file_version != FILE_VERSION_EM_V3)
+      i /= 2;
     src[2110] = i >> 8;
     src[2111] = i;
 
     src[2110] = i >> 8;
     src[2111] = i;
 
@@ -241,11 +264,34 @@ int cleanup_em_level(unsigned char *src, int length)
       src[i] = 147;
 
 #if 0
       src[i] = 147;
 
 #if 0
+
   /* fix acid */
   for (i = 64; i < 2048; i++)
     if (src[i] == 63)          /* replace element above 'Xacid_s' ... */
       src[i - 64] = 101;       /* ... with 'Xacid_1' */
 
   /* fix acid */
   for (i = 64; i < 2048; i++)
     if (src[i] == 63)          /* replace element above 'Xacid_s' ... */
       src[i - 64] = 101;       /* ... with 'Xacid_1' */
 
+#else
+
+#if 1
+  /* fix acid */
+  for (i = 64; i < 2048; i++)
+    if (src[i] == 63)          /* replace element above 'Xacid_s' ... */
+      src[i - 64] = 101;       /* ... with 'Xacid_1' */
+
+  /* fix acid with no base beneath it (see below for details (*)) */
+  for (i = 64; i < 2048 - 1; i++)
+  {
+    if (file_version <= FILE_VERSION_EM_V2 &&
+       src[i - 64] == 101 && src[i] != 63)     /* acid without base */
+    {
+      if (src[i - 1] == 101 ||                 /* remove acid over acid row */
+         src[i + 1] == 101)
+       src[i - 64] = 6;        /* replace element above with 'Xblank' */
+      else
+       src[i - 64] = 255;      /* replace element above with 'Xfake_acid_1' */
+    }
+  }
+
 #else
 
   /* fix acid */
 #else
 
   /* fix acid */
@@ -253,13 +299,25 @@ int cleanup_em_level(unsigned char *src, int length)
   {
     if (src[i] == 63)          /* 'Xacid_s' (acid pool, bottom middle) */
     {
   {
     if (src[i] == 63)          /* 'Xacid_s' (acid pool, bottom middle) */
     {
-      if (file_version == FILE_VERSION_EM_V4 &&
+      if (file_version <= FILE_VERSION_EM_V2 &&
          i < 2048 - 64 && src[i + 64] == 63)
          i < 2048 - 64 && src[i + 64] == 63)
-       src[i - 64] = 255;      /* replace element above with 'Xfake_acid_1' */
+      {
+       int obj_left  = remap_emerald[src[i - 1]];
+       int obj_right = remap_emerald[src[i + 1]];
+
+       if (obj_left == Xblank || obj_right == Xblank ||
+           obj_left == Xplant || obj_right == Xplant)
+         src[i - 64] = 6;      /* replace element above with 'Xblank' */
+       else
+         src[i - 64] = 255;    /* replace element above with 'Xfake_acid_1' */
+      }
       else
       else
+      {
        src[i - 64] = 101;      /* replace element above with 'Xacid_1' */
        src[i - 64] = 101;      /* replace element above with 'Xacid_1' */
+      }
     }
   }
     }
   }
+#endif
 #endif
 
   /* fix acid in eater 1 */
 #endif
 
   /* fix acid in eater 1 */
@@ -390,9 +448,9 @@ int cleanup_em_level(unsigned char *src, int length)
   /* size of v6 cave */
   length = 2172;
 
   /* size of v6 cave */
   length = 2172;
 
-#if 1
+#if 0
   if (options.debug)
   if (options.debug)
-    printf("::: EM level file version: %d\n", file_version);
+    Error(ERR_DEBUG, "EM level file version: %d", file_version);
 #endif
 
   return file_version;
 #endif
 
   return file_version;
@@ -417,6 +475,9 @@ int cleanup_em_level(unsigned char *src, int length)
  *   in eater.
  * - acid is always deadly even with no base beneath it (this breaks cave 0 in
  *   downunder mine 16)
  *   in eater.
  * - acid is always deadly even with no base beneath it (this breaks cave 0 in
  *   downunder mine 16)
+ *   (*) fixed (see above):
+ *       - downunder mine 16, level 0, works again
+ *       - downunder mine 11, level 71, corrected (only cosmetically)
  *
  * so far all below have not broken any caves:
  *
  *
  * so far all below have not broken any caves:
  *
@@ -450,19 +511,23 @@ int cleanup_em_level(unsigned char *src, int length)
  * - introduced ALLOW_ROLLING_SPRING; if defined, do NOT turn rolling spring
  *   into regular spring, because this breaks at least E.M.C. Mine 3, level 79
  *   (see comment directly above)
  * - introduced ALLOW_ROLLING_SPRING; if defined, do NOT turn rolling spring
  *   into regular spring, because this breaks at least E.M.C. Mine 3, level 79
  *   (see comment directly above)
+ *
+ * 2020-01-19
+ * - always use BAD_ROLL and ALLOW_ROLLING_SPRING code when converting caves
+ *   (that is, always allow initial rolling objects in caves now)
  */
 
 static unsigned short remap_emerald[256] =
 {
   Xstone,              Xstone,         Xdiamond,       Xdiamond,
   Xalien,              Xalien,         Xblank,         Xblank,
  */
 
 static unsigned short remap_emerald[256] =
 {
   Xstone,              Xstone,         Xdiamond,       Xdiamond,
   Xalien,              Xalien,         Xblank,         Xblank,
-  Xtank_n,             Xtank_e,        Xtank_s,        Xtank_w,
-  Xtank_gon,           Xtank_goe,      Xtank_gos,      Xtank_gow,
+  Xtank_1_n,           Xtank_1_e,      Xtank_1_s,      Xtank_1_w,
+  Xtank_2_n,           Xtank_2_e,      Xtank_2_s,      Xtank_2_w,
 
   Xbomb,               Xbomb,          Xemerald,       Xemerald,
 
   Xbomb,               Xbomb,          Xemerald,       Xemerald,
-  Xbug_n,              Xbug_e,         Xbug_s,         Xbug_w,
-  Xbug_gon,            Xbug_goe,       Xbug_gos,       Xbug_gow,
-  Xdrip_eat,           Xdrip_eat,      Xdrip_eat,      Xdrip_eat,
+  Xbug_1_n,            Xbug_1_e,       Xbug_1_s,       Xbug_1_w,
+  Xbug_2_n,            Xbug_2_e,       Xbug_2_s,       Xbug_2_w,
+  Xdrip,               Xdrip,          Xdrip,          Xdrip,
 
   Xstone,              Xbomb,          Xdiamond,       Xemerald,
   Xwonderwall,         Xnut,           Xnut,           Xnut,
 
   Xstone,              Xbomb,          Xdiamond,       Xemerald,
   Xwonderwall,         Xnut,           Xnut,           Xnut,
@@ -471,56 +536,33 @@ static unsigned short remap_emerald[256] =
 
   Xblank,              Xsand,          Xsand,          Xsand,
   Xsand_stone,         Xsand_stone,    Xsand_stone,    Xsand,
 
   Xblank,              Xsand,          Xsand,          Xsand,
   Xsand_stone,         Xsand_stone,    Xsand_stone,    Xsand,
-  Xstone,              Xgrow_ew,       Xgrow_ns,       Xdynamite_1,
+  Xstone,              Xslide_ew,      Xslide_ns,      Xdynamite_1,
   Xdynamite_2,         Xdynamite_3,    Xdynamite_4,    Xacid_s,
 
   Xdynamite_2,         Xdynamite_3,    Xdynamite_4,    Xacid_s,
 
-#ifdef ALLOW_ROLLING_SPRING
   Xexit_1,             Xexit_2,        Xexit_3,        Xballoon,
   Xplant,              Xspring,        Xspring_fall,   Xspring_w,
   Xspring_e,           Xball_1,        Xball_2,        Xandroid,
   Xblank,              Xandroid,       Xandroid,       Xandroid,
   Xexit_1,             Xexit_2,        Xexit_3,        Xballoon,
   Xplant,              Xspring,        Xspring_fall,   Xspring_w,
   Xspring_e,           Xball_1,        Xball_2,        Xandroid,
   Xblank,              Xandroid,       Xandroid,       Xandroid,
-#else
-  Xexit_1,             Xexit_2,        Xexit_3,        Xballoon,
-  Xplant,              Xspring,        Xspring,        Xspring,
-  Xspring,             Xball_1,        Xball_2,        Xandroid,
-  Xblank,              Xandroid,       Xandroid,       Xandroid,
-#endif
 
   Xandroid,            Xandroid,       Xandroid,       Xandroid,
   Xandroid,            Xblank,         Xblank,         Xblank,
   Xblank,              Xblank,         Xblank,         Xblank,
   Xblank,              Xblank,         Xblank,         Xblank,
 
 
   Xandroid,            Xandroid,       Xandroid,       Xandroid,
   Xandroid,            Xblank,         Xblank,         Xblank,
   Xblank,              Xblank,         Xblank,         Xblank,
   Xblank,              Xblank,         Xblank,         Xblank,
 
-#ifdef BAD_ROLL
-
-  Xblank,              Xblank,         Xblank,         Xspring_force_w,
-  Xspring_force_e,     Xacid_1,        Xacid_2,        Xacid_3,
+  Xblank,              Xblank,         Xblank,         Xpush_spring_w,
+  Xpush_spring_e,      Xacid_1,        Xacid_2,        Xacid_3,
   Xacid_4,             Xacid_5,        Xacid_6,        Xacid_7,
   Xacid_8,             Xblank,         Xblank,         Xblank,
 
   Xacid_4,             Xacid_5,        Xacid_6,        Xacid_7,
   Xacid_8,             Xblank,         Xblank,         Xblank,
 
-  Xblank,              Xblank,         Xnut_force_w,   Xnut_force_e,
-  Xsteel_1,            Xblank,         Xblank,         Xbomb_force_w,
-  Xbomb_force_e,       Xstone_force_w, Xstone_force_e, Xblank,
+  Xblank,              Xblank,         Xpush_nut_w,    Xpush_nut_e,
+  Xsteel_1,            Xblank,         Xblank,         Xpush_bomb_w,
+  Xpush_bomb_e,                Xpush_stone_w,  Xpush_stone_e,  Xblank,
   Xblank,              Xblank,         Xblank,         Xblank,
 
   Xblank,              Xblank,         Xblank,         Xblank,
 
-#else
-
-  Xblank,              Xblank,         Xblank,         Xspring,
-  Xspring,             Xacid_1,        Xacid_2,        Xacid_3,
-  Xacid_4,             Xacid_5,        Xacid_6,        Xacid_7,
-  Xacid_8,             Xblank,         Xblank,         Xblank,
-
-  Xblank,              Xblank,         Xnut,           Xnut,
-  Xsteel_1,            Xblank,         Xblank,         Xbomb,
-  Xbomb,               Xstone,         Xstone,         Xblank,
-  Xblank,              Xblank,         Xblank,         Xblank,
-
-#endif
-
-  Xblank,              Xround_wall_1,  Xgrass,         Xsteel_1,
+  Xblank,              Xroundwall_1,   Xgrass,         Xsteel_1,
   Xwall_1,             Xkey_1,         Xkey_2,         Xkey_3,
   Xkey_4,              Xdoor_1,        Xdoor_2,        Xdoor_3,
   Xwall_1,             Xkey_1,         Xkey_2,         Xkey_3,
   Xkey_4,              Xdoor_1,        Xdoor_2,        Xdoor_3,
-  Xdoor_4,             Xdripper,       Xfake_door_1,   Xfake_door_2,
+  Xdoor_4,             Xfake_amoeba,   Xfake_door_1,   Xfake_door_2,
 
   Xfake_door_3,                Xfake_door_4,   Xwonderwall,    Xwheel,
   Xsand,               Xacid_nw,       Xacid_ne,       Xacid_sw,
 
   Xfake_door_3,                Xfake_door_4,   Xwonderwall,    Xwheel,
   Xsand,               Xacid_nw,       Xacid_ne,       Xacid_sw,
@@ -533,8 +575,8 @@ static unsigned short remap_emerald[256] =
   Xalpha_comma,                Xalpha_quote,   Xalpha_minus,   Xdynamite,
 
   Xsteel_3,            Xdecor_6,       Xdecor_7,       Xsteel_2,
   Xalpha_comma,                Xalpha_quote,   Xalpha_minus,   Xdynamite,
 
   Xsteel_3,            Xdecor_6,       Xdecor_7,       Xsteel_2,
-  Xround_wall_2,       Xdecor_2,       Xdecor_4,       Xdecor_3,
-  Xwind_nesw,          Xwind_e,        Xwind_s,        Xwind_w,
+  Xroundwall_2,                Xdecor_2,       Xdecor_4,       Xdecor_3,
+  Xwind_any,           Xwind_e,        Xwind_s,        Xwind_w,
   Xwind_n,             Xdirt,          Xplant,         Xkey_5,
 
   Xkey_6,              Xkey_7,         Xkey_8,         Xdoor_5,
   Xwind_n,             Xdirt,          Xplant,         Xkey_5,
 
   Xkey_6,              Xkey_7,         Xkey_8,         Xdoor_5,
@@ -567,9 +609,9 @@ static int get_em_element(unsigned short em_element_raw, int file_version)
 {
   int em_element = remap_emerald[em_element_raw];
 
 {
   int em_element = remap_emerald[em_element_raw];
 
-  if (file_version <= FILE_VERSION_EM_V4)
+  if (file_version < FILE_VERSION_EM_V5)
   {
   {
-    /* versions up to V4 had no grass, but only sand/dirt */
+    /* versions below V5 had no grass, but only sand/dirt */
     if (em_element == Xgrass)
       em_element = Xdirt;
   }
     if (em_element == Xgrass)
       em_element = Xdirt;
   }
@@ -601,8 +643,8 @@ void convert_em_level(unsigned char *src, int file_version)
   for (i = 0; i < 2; i++)
   {
     temp = src[0x830 + i * 2] << 8 | src[0x831 + i * 2];
   for (i = 0; i < 2; i++)
   {
     temp = src[0x830 + i * 2] << 8 | src[0x831 + i * 2];
-    ply[i].x_initial = (temp & 63) + 1;
-    ply[i].y_initial = (temp >> 6 & 31) + 1;
+    ply[i].x_initial = (temp & 63);
+    ply[i].y_initial = (temp >> 6 & 31);
   }
 
   temp = (src[0x834] << 8 | src[0x835]) * 28;
   }
 
   temp = (src[0x834] << 8 | src[0x835]) * 28;
@@ -724,29 +766,29 @@ void convert_em_level(unsigned char *src, int file_version)
 
   if (temp & 32)
   {
 
   if (temp & 32)
   {
-    lev.android_array[Xtank_n]         = Xtank_n;
-    lev.android_array[Xtank_gon]       = Xtank_n;
-    lev.android_array[Ytank_nB]                = Xtank_n;
-    lev.android_array[Ytank_n_e]       = Xtank_n;
-    lev.android_array[Ytank_n_w]       = Xtank_n;
-
-    lev.android_array[Xtank_e]         = Xtank_e;
-    lev.android_array[Xtank_goe]       = Xtank_e;
-    lev.android_array[Ytank_eB]                = Xtank_e;
-    lev.android_array[Ytank_e_s]       = Xtank_e;
-    lev.android_array[Ytank_e_n]       = Xtank_e;
-
-    lev.android_array[Xtank_s]         = Xtank_s;
-    lev.android_array[Xtank_gos]       = Xtank_s;
-    lev.android_array[Ytank_sB]                = Xtank_s;
-    lev.android_array[Ytank_s_w]       = Xtank_s;
-    lev.android_array[Ytank_s_e]       = Xtank_s;
-
-    lev.android_array[Xtank_w]         = Xtank_w;
-    lev.android_array[Xtank_gow]       = Xtank_w;
-    lev.android_array[Ytank_wB]                = Xtank_w;
-    lev.android_array[Ytank_w_n]       = Xtank_w;
-    lev.android_array[Ytank_w_s]       = Xtank_w;
+    lev.android_array[Xtank_1_n]       = Xtank_1_n;
+    lev.android_array[Xtank_2_n]       = Xtank_1_n;
+    lev.android_array[Ytank_nB]                = Xtank_1_n;
+    lev.android_array[Ytank_n_e]       = Xtank_1_n;
+    lev.android_array[Ytank_n_w]       = Xtank_1_n;
+
+    lev.android_array[Xtank_1_e]       = Xtank_1_e;
+    lev.android_array[Xtank_2_e]       = Xtank_1_e;
+    lev.android_array[Ytank_eB]                = Xtank_1_e;
+    lev.android_array[Ytank_e_s]       = Xtank_1_e;
+    lev.android_array[Ytank_e_n]       = Xtank_1_e;
+
+    lev.android_array[Xtank_1_s]       = Xtank_1_s;
+    lev.android_array[Xtank_2_s]       = Xtank_1_s;
+    lev.android_array[Ytank_sB]                = Xtank_1_s;
+    lev.android_array[Ytank_s_w]       = Xtank_1_s;
+    lev.android_array[Ytank_s_e]       = Xtank_1_s;
+
+    lev.android_array[Xtank_1_w]       = Xtank_1_w;
+    lev.android_array[Xtank_2_w]       = Xtank_1_w;
+    lev.android_array[Ytank_wB]                = Xtank_1_w;
+    lev.android_array[Ytank_w_n]       = Xtank_1_w;
+    lev.android_array[Ytank_w_s]       = Xtank_1_w;
   }
 
   if (temp & 64)
   }
 
   if (temp & 64)
@@ -766,29 +808,29 @@ void convert_em_level(unsigned char *src, int file_version)
 
   if (temp & 128)
   {
 
   if (temp & 128)
   {
-    lev.android_array[Xbug_n]          = Xbug_gon;
-    lev.android_array[Xbug_gon]                = Xbug_gon;
-    lev.android_array[Ybug_nB]         = Xbug_gon;
-    lev.android_array[Ybug_n_e]                = Xbug_gon;
-    lev.android_array[Ybug_n_w]                = Xbug_gon;
-
-    lev.android_array[Xbug_e]          = Xbug_goe;
-    lev.android_array[Xbug_goe]                = Xbug_goe;
-    lev.android_array[Ybug_eB]         = Xbug_goe;
-    lev.android_array[Ybug_e_s]                = Xbug_goe;
-    lev.android_array[Ybug_e_n]                = Xbug_goe;
-
-    lev.android_array[Xbug_s]          = Xbug_gos;
-    lev.android_array[Xbug_gos]                = Xbug_gos;
-    lev.android_array[Ybug_sB]         = Xbug_gos;
-    lev.android_array[Ybug_s_w]                = Xbug_gos;
-    lev.android_array[Ybug_s_e]                = Xbug_gos;
-
-    lev.android_array[Xbug_w]          = Xbug_gow;
-    lev.android_array[Xbug_gow]                = Xbug_gow;
-    lev.android_array[Ybug_wB]         = Xbug_gow;
-    lev.android_array[Ybug_w_n]                = Xbug_gow;
-    lev.android_array[Ybug_w_s]                = Xbug_gow;
+    lev.android_array[Xbug_1_n]                = Xbug_2_n;
+    lev.android_array[Xbug_2_n]                = Xbug_2_n;
+    lev.android_array[Ybug_nB]         = Xbug_2_n;
+    lev.android_array[Ybug_n_e]                = Xbug_2_n;
+    lev.android_array[Ybug_n_w]                = Xbug_2_n;
+
+    lev.android_array[Xbug_1_e]                = Xbug_2_e;
+    lev.android_array[Xbug_2_e]                = Xbug_2_e;
+    lev.android_array[Ybug_eB]         = Xbug_2_e;
+    lev.android_array[Ybug_e_s]                = Xbug_2_e;
+    lev.android_array[Ybug_e_n]                = Xbug_2_e;
+
+    lev.android_array[Xbug_1_s]                = Xbug_2_s;
+    lev.android_array[Xbug_2_s]                = Xbug_2_s;
+    lev.android_array[Ybug_sB]         = Xbug_2_s;
+    lev.android_array[Ybug_s_w]                = Xbug_2_s;
+    lev.android_array[Ybug_s_e]                = Xbug_2_s;
+
+    lev.android_array[Xbug_1_w]                = Xbug_2_w;
+    lev.android_array[Xbug_2_w]                = Xbug_2_w;
+    lev.android_array[Ybug_wB]         = Xbug_2_w;
+    lev.android_array[Ybug_w_n]                = Xbug_2_w;
+    lev.android_array[Ybug_w_s]                = Xbug_2_w;
   }
 
   if (temp & 256)
   }
 
   if (temp & 256)
@@ -807,10 +849,10 @@ void convert_em_level(unsigned char *src, int file_version)
     lev.android_array[Xspring_pause]   = Xspring;
     lev.android_array[Xspring_e]       = Xspring;
     lev.android_array[Yspring_eB]      = Xspring;
     lev.android_array[Xspring_pause]   = Xspring;
     lev.android_array[Xspring_e]       = Xspring;
     lev.android_array[Yspring_eB]      = Xspring;
-    lev.android_array[Yspring_kill_eB] = Xspring;
+    lev.android_array[Yspring_alien_eB]        = Xspring;
     lev.android_array[Xspring_w]       = Xspring;
     lev.android_array[Yspring_wB]      = Xspring;
     lev.android_array[Xspring_w]       = Xspring;
     lev.android_array[Yspring_wB]      = Xspring;
-    lev.android_array[Yspring_kill_wB] = Xspring;
+    lev.android_array[Yspring_alien_wB]        = Xspring;
     lev.android_array[Xspring_fall]    = Xspring;
     lev.android_array[Yspring_sB]      = Xspring;
   }
     lev.android_array[Xspring_fall]    = Xspring;
     lev.android_array[Yspring_sB]      = Xspring;
   }
@@ -826,16 +868,16 @@ void convert_em_level(unsigned char *src, int file_version)
 
   if (temp & 2048)
   {
 
   if (temp & 2048)
   {
-    lev.android_array[Xdripper]                = Xdrip_eat;
-    lev.android_array[XdripperB]       = Xdrip_eat;
-    lev.android_array[Xamoeba_1]       = Xdrip_eat;
-    lev.android_array[Xamoeba_2]       = Xdrip_eat;
-    lev.android_array[Xamoeba_3]       = Xdrip_eat;
-    lev.android_array[Xamoeba_4]       = Xdrip_eat;
-    lev.android_array[Xamoeba_5]       = Xdrip_eat;
-    lev.android_array[Xamoeba_6]       = Xdrip_eat;
-    lev.android_array[Xamoeba_7]       = Xdrip_eat;
-    lev.android_array[Xamoeba_8]       = Xdrip_eat;
+    lev.android_array[Xfake_amoeba]    = Xdrip;
+    lev.android_array[Yfake_amoeba]    = Xdrip;
+    lev.android_array[Xamoeba_1]       = Xdrip;
+    lev.android_array[Xamoeba_2]       = Xdrip;
+    lev.android_array[Xamoeba_3]       = Xdrip;
+    lev.android_array[Xamoeba_4]       = Xdrip;
+    lev.android_array[Xamoeba_5]       = Xdrip;
+    lev.android_array[Xamoeba_6]       = Xdrip;
+    lev.android_array[Xamoeba_7]       = Xdrip;
+    lev.android_array[Xamoeba_8]       = Xdrip;
   }
 
   if (temp & 4096)
   }
 
   if (temp & 4096)
@@ -858,48 +900,6 @@ void convert_em_level(unsigned char *src, int file_version)
        lev.wheel_cnt_initial = lev.wheel_time;
        break;
 
        lev.wheel_cnt_initial = lev.wheel_time;
        break;
 
-#ifndef BAD_ROLL
-      case 0x63:                               /* spring roll left */
-       src[temp - 1] = 0x45;
-       src[temp] = 0x80;
-       break;
-
-      case 0x64:                               /* spring roll right */
-       src[temp + 1] = 0x45;
-       src[temp] = 0x80;
-       break;
-
-      case 0x72:                               /* nut roll left */
-       src[temp - 1] = 0x25;
-       src[temp] = 0x80;
-       break;
-
-      case 0x73:                               /* nut roll right */
-       src[temp + 1] = 0x25;
-       src[temp] = 0x80;
-       break;
-
-      case 0x77:                               /* bomb roll left */
-       src[temp - 1] = 0x10;
-       src[temp] = 0x80;
-       break;
-
-      case 0x78:                               /* bomb roll right */
-       src[temp + 1] = 0x10;
-       src[temp] = 0x80;
-       break;
-
-      case 0x79:                               /* stone roll left */
-       src[temp - 1] = 0x00;
-       src[temp] = 0x80;
-       break;
-
-      case 0x7A:                               /* stone roll right */
-       src[temp + 1] = 0x00;
-       src[temp] = 0x80;
-       break;
-#endif
-
       case 0xA3:                               /* fake blank */
        lev.lenses_cnt_initial = 9999;
        break;
       case 0xA3:                               /* fake blank */
        lev.lenses_cnt_initial = 9999;
        break;
@@ -911,27 +911,21 @@ void convert_em_level(unsigned char *src, int file_version)
   }
 
   /* first fill the complete playfield with the default border element */
   }
 
   /* first fill the complete playfield with the default border element */
-  for (y = 0; y < HEIGHT; y++)
-    for (x = 0; x < WIDTH; x++)
-      native_em_level.cave[x][y] = ZBORDER;
+  for (y = 0; y < CAVE_HEIGHT; y++)
+    for (x = 0; x < CAVE_WIDTH; x++)
+      native_em_level.cave[x][y] = Zborder;
 
   /* then copy the real level contents from level file into the playfield */
   temp = 0;
   for (y = 0; y < lev.height; y++)
     for (x = 0; x < lev.width; x++)
 
   /* then copy the real level contents from level file into the playfield */
   temp = 0;
   for (y = 0; y < lev.height; y++)
     for (x = 0; x < lev.width; x++)
-      native_em_level.cave[x + 1][y + 1] =
+      native_em_level.cave[x][y] =
        get_em_element(src[temp++], file_version);
 
   /* at last, set the two players at their positions in the playfield */
   /* (native EM[C] levels always have exactly two players in a level) */
        get_em_element(src[temp++], file_version);
 
   /* at last, set the two players at their positions in the playfield */
   /* (native EM[C] levels always have exactly two players in a level) */
-#if 1
   for (i = 0; i < 2; i++)
     native_em_level.cave[ply[i].x_initial][ply[i].y_initial] = Zplayer;
   for (i = 0; i < 2; i++)
     native_em_level.cave[ply[i].x_initial][ply[i].y_initial] = Zplayer;
-#else
-  for (i = 0; i < 2; i++)
-    if (ply[i].alive_initial)
-      native_em_level.cave[ply[i].x_initial][ply[i].y_initial] = Zplayer;
-#endif
 
   native_em_level.file_version = file_version;
 }
 
   native_em_level.file_version = file_version;
 }
@@ -940,27 +934,40 @@ void prepare_em_level(void)
 {
   int i, x, y;
   int players_left;
 {
   int i, x, y;
   int players_left;
-  int num_tape_players;
+  boolean team_mode;
 
   /* reset all runtime variables to their initial values */
 
 
   /* reset all runtime variables to their initial values */
 
-  for (y = 0; y < HEIGHT; y++)
-    for (x = 0; x < WIDTH; x++)
-      Cave[y][x] = native_em_level.cave[x][y];
+  game_init_cave_buffers();
 
 
-  for (y = 0; y < HEIGHT; y++)
-    for (x = 0; x < WIDTH; x++)
-      Next[y][x] = Cave[y][x];
+  lev.left = CAVE_BUFFER_XOFFSET;
+  lev.top  = CAVE_BUFFER_YOFFSET;
+  lev.right = lev.left + lev.width;
+  lev.bottom = lev.top + lev.height;
 
 
-  for (y = 0; y < HEIGHT; y++)
-    for (x = 0; x < WIDTH; x++)
-      Draw[y][x] = Cave[y][x];
+  /* add linked cave buffer columns for wrap-around movement */
+  for (x = 0; x < lev.left; x++)
+  {
+    lev.cavecol[x] = lev.cavecol[lev.width + x];
+    lev.nextcol[x] = lev.nextcol[lev.width + x];
+    lev.drawcol[x] = lev.drawcol[lev.width + x];
+    lev.boomcol[x] = lev.boomcol[lev.width + x];
+
+    lev.cavecol[lev.right + x] = lev.cavecol[lev.left + x];
+    lev.nextcol[lev.right + x] = lev.nextcol[lev.left + x];
+    lev.drawcol[lev.right + x] = lev.drawcol[lev.left + x];
+    lev.boomcol[lev.right + x] = lev.boomcol[lev.left + x];
+  }
+
+  for (x = 0; x < lev.width; x++)
+    for (y = 0; y < lev.height; y++)
+      lev.cave[lev.left + x][lev.top + y] = native_em_level.cave[x][y];
+
+  for (x = lev.left; x < lev.right; x++)
+    for (y = lev.top; y < lev.bottom; y++)
+      lev.next[x][y] = lev.draw[x][y] = lev.cave[x][y];
 
 
-#if 1
   lev.time_initial = lev.time_seconds;
   lev.time_initial = lev.time_seconds;
-#else
-  lev.time_initial = (lev.time_seconds * 50 + 7) / 8;
-#endif
   lev.time = lev.time_initial;
 
   lev.required = lev.required_initial;
   lev.time = lev.time_initial;
 
   lev.required = lev.required_initial;
@@ -999,7 +1006,7 @@ void prepare_em_level(void)
     ply[i].exists = 0;
     ply[i].alive_initial = FALSE;
 
     ply[i].exists = 0;
     ply[i].alive_initial = FALSE;
 
-    if (ply[i].x_initial > 0 && ply[i].y_initial > 0)
+    if (ply[i].x_initial != -1 && ply[i].y_initial != -1)
     {
       ply[i].exists = 1;
 
     {
       ply[i].exists = 1;
 
@@ -1007,12 +1014,10 @@ void prepare_em_level(void)
     }
   }
 
     }
   }
 
-  num_tape_players = getNumActivePlayers_EM();
+  team_mode = getTeamMode_EM();
 
 
-  if (num_tape_players != -1)
-    lev.home_initial = MIN(lev.home_initial, num_tape_players);
-  else if (!setup.team_mode)
-    lev.home_initial = MIN(lev.home_initial, 1);
+  if (!team_mode)
+    lev.home_initial = 1;
 
   lev.home = lev.home_initial;
   players_left = lev.home_initial;
 
   lev.home = lev.home_initial;
   players_left = lev.home_initial;
@@ -1033,7 +1038,9 @@ void prepare_em_level(void)
 
        native_em_level.cave[x][y] = Xblank;
 
 
        native_em_level.cave[x][y] = Xblank;
 
-       Cave[y][x] = Next[y][x] = Draw[y][x] = Xblank;
+       lev.cave[lev.left + x][lev.top + y] = Xblank;
+       lev.next[lev.left + x][lev.top + y] = Xblank;
+       lev.draw[lev.left + x][lev.top + y] = Xblank;
       }
     }
   }
       }
     }
   }
@@ -1046,22 +1053,30 @@ void prepare_em_level(void)
     ply[i].dynamite_cnt = 0;
     ply[i].keys = 0;
     ply[i].anim = 0;
     ply[i].dynamite_cnt = 0;
     ply[i].keys = 0;
     ply[i].anim = 0;
-    ply[i].oldx = ply[i].x = ply[i].x_initial;
-    ply[i].oldy = ply[i].y = ply[i].y_initial;
+    ply[i].oldx = ply[i].x = ply[i].x_initial + lev.left;
+    ply[i].oldy = ply[i].y = ply[i].y_initial + lev.top;
     ply[i].last_move_dir = MV_NONE;
     ply[i].joy_n = ply[i].joy_e = ply[i].joy_s = ply[i].joy_w = 0;
     ply[i].joy_snap  = ply[i].joy_drop = 0;
     ply[i].joy_stick = ply[i].joy_spin = 0;
     ply[i].last_move_dir = MV_NONE;
     ply[i].joy_n = ply[i].joy_e = ply[i].joy_s = ply[i].joy_w = 0;
     ply[i].joy_snap  = ply[i].joy_drop = 0;
     ply[i].joy_stick = ply[i].joy_spin = 0;
-
-#if 0
-    printf("player %d: x/y == %d/%d, alive == %d\n",
-          i, ply[i].x_initial, ply[i].y_initial, ply[i].alive);
-#endif
   }
 
   }
 
+  // the following engine variables are initialized to version-specific values
+  // in function InitGameEngine() (src/game.c):
+  //
+  // - game_em.use_single_button (default: TRUE)
+  // - game_em.use_snap_key_bug (default: FALSE)
+
+  game_em.level_solved = FALSE;
+  game_em.game_over = FALSE;
+
   game_em.any_player_moving = FALSE;
   game_em.any_player_moving = FALSE;
+  game_em.any_player_snapping = FALSE;
+
   game_em.last_moving_player = 0;      /* default: first player */
 
   for (i = 0; i < MAX_PLAYERS; i++)
     game_em.last_player_direction[i] = MV_NONE;
   game_em.last_moving_player = 0;      /* default: first player */
 
   for (i = 0; i < MAX_PLAYERS; i++)
     game_em.last_player_direction[i] = MV_NONE;
+
+  lev.exit_x = lev.exit_y = -1;        /* kludge for playing player exit sound */
 }
 }