added command to automatically test tapes and try to fix if broken
authorHolger Schemel <info@artsoft.org>
Fri, 11 Sep 2020 17:17:57 +0000 (19:17 +0200)
committerHolger Schemel <info@artsoft.org>
Sat, 12 Sep 2020 11:53:32 +0000 (13:53 +0200)
This adds a new command "autofix" that can be used to test and try to
fix existing tapes (by setting a property bit in the tape header).

The only patch currently available is "em_random_bug" that sets the
property bit for using the 64-bit random value workaround in the EM
game engine (see commit 1006b2db for details).

This command is an improved variant for the command "patch tapes"
introduced with commit 7c1d6252.

Thanks to Bela Lubkin for convincing me that this command is in fact a
good idea! :-)

src/init.c
src/main.c
src/main.h
src/tape.c
src/tape.h

index 6b6e3263412bdca4868c69f52ab9352c7c0620ce..be7447666d29dc90c4e29ca326d51a365bb2c84d 100644 (file)
@@ -5032,18 +5032,20 @@ static void Execute_Command(char *command)
 
     exit(0);
   }
-  else if (strPrefix(command, "autotest ") ||
-          strPrefix(command, "autoplay ") ||
+  else if (strPrefix(command, "autoplay ") ||
           strPrefix(command, "autoffwd ") ||
-          strPrefix(command, "autowarp "))
+          strPrefix(command, "autowarp ") ||
+          strPrefix(command, "autotest ") ||
+          strPrefix(command, "autofix "))
   {
-    char *str_ptr = getStringCopy(&command[9]);        // read command parameters
+    char *str_ptr = getStringCopy(&command[8]);        // read command parameters
 
     global.autoplay_mode =
-      (strPrefix(command, "autotest") ? AUTOPLAY_MODE_TEST :
-       strPrefix(command, "autoplay") ? AUTOPLAY_MODE_PLAY :
+      (strPrefix(command, "autoplay") ? AUTOPLAY_MODE_PLAY :
        strPrefix(command, "autoffwd") ? AUTOPLAY_MODE_FFWD :
        strPrefix(command, "autowarp") ? AUTOPLAY_MODE_WARP :
+       strPrefix(command, "autotest") ? AUTOPLAY_MODE_TEST :
+       strPrefix(command, "autofix")  ? AUTOPLAY_MODE_FIX :
        AUTOPLAY_MODE_NONE);
 
     while (*str_ptr != '\0')                   // continue parsing string
@@ -5078,7 +5080,7 @@ static void Execute_Command(char *command)
        str_ptr++;
     }
 
-    if (global.autoplay_mode == AUTOPLAY_MODE_TEST)
+    if (global.autoplay_mode & AUTOPLAY_MODE_WARP_NO_DISPLAY)
       program.headless = TRUE;
   }
   else if (strPrefix(command, "patch tapes "))
index cb0fe8a7ae60ea5f1aeb189b749387f14ea24487..ca4be86d05615216959671464a53987732226652 100644 (file)
@@ -7633,10 +7633,11 @@ static void print_usage(void)
        "  \"print helptext.conf\"            print default helptext config\n"
        "  \"dump level FILE\"                dump level data from FILE\n"
        "  \"dump tape FILE\"                 dump tape data from FILE\n"
-       "  \"autotest LEVELDIR [NR ...]\"     test level tapes for LEVELDIR\n"
        "  \"autoplay LEVELDIR [NR ...]\"     play level tapes for LEVELDIR\n"
        "  \"autoffwd LEVELDIR [NR ...]\"     ffwd level tapes for LEVELDIR\n"
        "  \"autowarp LEVELDIR [NR ...]\"     warp level tapes for LEVELDIR\n"
+       "  \"autotest LEVELDIR [NR ...]\"     test level tapes for LEVELDIR\n"
+       "  \"autofix LEVELDIR [NR ...]\"      test and fix tapes for LEVELDIR\n"
        "  \"patch tapes MODE LEVELDIR [NR]\" patch level tapes for LEVELDIR\n"
        "  \"convert LEVELDIR [NR]\"          convert levels in LEVELDIR\n"
        "  \"create images DIRECTORY\"        write BMP images to DIRECTORY\n"
index 8675f6720a3906ca14c633c5f4d4495413c37dad..c412801a2607b2e667710e9a6f7fc06ac2ecc9de 100644 (file)
@@ -2641,6 +2641,7 @@ enum
 #define AUTOPLAY_FFWD                  (1 << 1)
 #define AUTOPLAY_WARP                  (1 << 2)
 #define AUTOPLAY_TEST                  (1 << 3)
+#define AUTOPLAY_FIX                   (1 << 4)
 #define AUTOPLAY_WARP_NO_DISPLAY       AUTOPLAY_TEST
 
 #define AUTOPLAY_MODE_NONE             0
@@ -2648,6 +2649,7 @@ enum
 #define AUTOPLAY_MODE_FFWD             (AUTOPLAY_MODE_PLAY | AUTOPLAY_FFWD)
 #define AUTOPLAY_MODE_WARP             (AUTOPLAY_MODE_FFWD | AUTOPLAY_WARP)
 #define AUTOPLAY_MODE_TEST             (AUTOPLAY_MODE_WARP | AUTOPLAY_TEST)
+#define AUTOPLAY_MODE_FIX              (AUTOPLAY_MODE_TEST | AUTOPLAY_FIX)
 #define AUTOPLAY_MODE_WARP_NO_DISPLAY  AUTOPLAY_MODE_TEST
 
 
index 6ff1aa5c929dd5ca911ac2aa3f207cf30526fe63..46fa236eaf460c098ebc9bb5a81e4e305cdf0595 100644 (file)
@@ -465,6 +465,8 @@ void TapeDeactivateDisplayOff(boolean redraw_display)
 // tape logging functions
 // ============================================================================
 
+static char tape_patch_info[MAX_OUTPUT_LINESIZE];
+
 static void PrintTapeReplayProgress(boolean replay_finished)
 {
   static unsigned int counter_last = -1;
@@ -478,8 +480,9 @@ static void PrintTapeReplayProgress(boolean replay_finished)
     if (counter > counter_last + counter_delay)
     {
       PrintNoLog("\r");
-      PrintNoLog("Tape %03d [%02d:%02d]: [%02d:%02d] - playing tape ... ",
-                level_nr, tape.length_seconds / 60, tape.length_seconds % 60,
+      PrintNoLog("Tape %03d %s[%02d:%02d]: [%02d:%02d] - playing tape ... ",
+                level_nr,  tape_patch_info,
+                tape.length_seconds / 60, tape.length_seconds % 60,
                 TapeTime / 60, TapeTime % 60);
 
       counter_last = counter;
@@ -490,11 +493,15 @@ static void PrintTapeReplayProgress(boolean replay_finished)
     float tape_length_seconds = GetTapeLengthSecondsFloat();
 
     PrintNoLog("\r");
-    Print("Tape %03d [%02d:%02d]: (%02d:%02d.%03d / %.2f %%) - %s.\n",
-         level_nr, tape.length_seconds / 60, tape.length_seconds % 60,
+    Print("Tape %03d %s[%02d:%02d]: (%02d:%02d.%03d / %.2f %%) - %s.\n",
+         level_nr, tape_patch_info,
+         tape.length_seconds / 60, tape.length_seconds % 60,
          counter_seconds / 60, counter_seconds % 60, counter % 1000,
          (float)counter / tape_length_seconds / 10,
-         tape.auto_play_level_solved ? "solved" : "NOT SOLVED");
+         tape.auto_play_level_fixed ? "solved and fixed" :
+         tape.auto_play_level_solved ? "solved" :
+         tape.auto_play_level_not_fixable ? "NOT SOLVED, NOT FIXED" :
+         "NOT SOLVED");
 
     counter_last = -1;
   }
@@ -564,6 +571,8 @@ static void TapeRewind(void)
   tape.deactivate_display = FALSE;
   tape.auto_play = (global.autoplay_leveldir != NULL);
   tape.auto_play_level_solved = FALSE;
+  tape.auto_play_level_fixed = FALSE;
+  tape.auto_play_level_not_fixable = FALSE;
   tape.quick_resume = FALSE;
   tape.single_step = FALSE;
 
@@ -1148,21 +1157,89 @@ void AutoPlayTapes(void)
   static int autoplay_level_nr = -1;
   static int num_levels_played = 0;
   static int num_levels_solved = 0;
+  static int num_tapes_patched = 0;
   static int num_tape_missing = 0;
   static boolean level_failed[MAX_TAPES_PER_SET];
+  static int patch_nr = 0;
+  static char *patch_name[] =
+  {
+    "original tape",
+    "em_random_bug",
+
+    NULL
+  };
+  static int patch_version_first[] =
+  {
+    VERSION_IDENT(0,0,0,0),
+    VERSION_IDENT(3,3,1,0),
+
+    -1
+  };
+  static int patch_version_last[] =
+  {
+    VERSION_IDENT(9,9,9,9),
+    VERSION_IDENT(4,0,1,1),
+
+    -1
+  };
+  static byte patch_property_bit[] =
+  {
+    TAPE_PROPERTY_NONE,
+    TAPE_PROPERTY_EM_RANDOM_BUG,
+
+    -1
+  };
   int i;
 
   if (autoplay_initialized)
   {
+    if (global.autoplay_mode == AUTOPLAY_MODE_FIX)
+    {
+      if (tape.auto_play_level_solved)
+      {
+       if (patch_nr > 0)
+       {
+         // level solved by patched tape -- save fixed tape
+         char *filename = getTapeFilename(level_nr);
+         char *filename_orig = getStringCat2(filename, ".orig");
+
+         // create backup from old tape, if not yet existing
+         if (!fileExists(filename_orig))
+           rename(filename, filename_orig);
+
+         SaveTapeToFilename(filename);
+
+         tape.auto_play_level_fixed = TRUE;
+         num_tapes_patched++;
+       }
+
+       // continue with next tape
+       patch_nr = 0;
+      }
+      else if (patch_name[patch_nr + 1] != NULL)
+      {
+       // level not solved by patched tape -- continue with next patch
+       patch_nr++;
+      }
+      else
+      {
+       // level not solved by any patched tape -- continue with next tape
+       tape.auto_play_level_not_fixable = TRUE;
+       patch_nr = 0;
+      }
+    }
+
     // just finished auto-playing tape
     PrintTapeReplayProgress(TRUE);
 
-    num_levels_played++;
+    if (patch_nr == 0)
+      num_levels_played++;
 
     if (tape.auto_play_level_solved)
       num_levels_solved++;
-    else if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
-      level_failed[level_nr] = TRUE;
+
+    if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
+      level_failed[level_nr] = !tape.auto_play_level_solved;
   }
   else
   {
@@ -1188,7 +1265,10 @@ void AutoPlayTapes(void)
     autoplay_level_nr = autoplay_leveldir->first_level;
 
     PrintLine("=", 79);
-    Print("Automatically playing level tapes\n");
+    if (global.autoplay_mode == AUTOPLAY_MODE_FIX)
+      Print("Automatically fixing level tapes\n");
+    else
+      Print("Automatically playing level tapes\n");
     PrintLine("-", 79);
     Print("Level series identifier: '%s'\n", autoplay_leveldir->identifier);
     Print("Level series name:       '%s'\n", autoplay_leveldir->name);
@@ -1200,12 +1280,26 @@ void AutoPlayTapes(void)
     for (i = 0; i < MAX_TAPES_PER_SET; i++)
       level_failed[i] = FALSE;
 
+    // only private tapes may be modified
+    if (global.autoplay_mode == AUTOPLAY_MODE_FIX)
+      options.mytapes = TRUE;
+
     autoplay_initialized = TRUE;
   }
 
-  while (autoplay_level_nr <= autoplay_leveldir->last_level)
+  while (1)
   {
-    level_nr = autoplay_level_nr++;
+    if (global.autoplay_mode != AUTOPLAY_MODE_FIX || patch_nr == 0)
+      level_nr = autoplay_level_nr++;
+
+    if (level_nr > autoplay_leveldir->last_level)
+      break;
+
+    // set patch info (required for progress output)
+    strcpy(tape_patch_info, "");
+    if (global.autoplay_mode == AUTOPLAY_MODE_FIX)
+      snprintf(tape_patch_info, MAX_OUTPUT_LINESIZE, "[%-13s] ",
+              patch_name[patch_nr]);
 
     if (!global.autoplay_all && !global.autoplay_level[level_nr])
       continue;
@@ -1218,7 +1312,7 @@ void AutoPlayTapes(void)
 
     if (level.no_level_file || level.no_valid_file)
     {
-      Print("(no level)\n");
+      Print("(no level found)\n");
 
       continue;
     }
@@ -1238,11 +1332,43 @@ void AutoPlayTapes(void)
     {
       num_tape_missing++;
 
-      Print("(not found)\n");
+      Print("(no tape found)\n");
 
       continue;
     }
 
+    if (global.autoplay_mode == AUTOPLAY_MODE_FIX)
+    {
+      if (tape.engine_version < patch_version_first[patch_nr] ||
+         tape.engine_version > patch_version_last[patch_nr])
+      {
+       PrintNoLog("\r");
+       PrintNoLog("Tape %03d %s[%02d:%02d]: (%s %d.%d.%d.%d) - skipped.\n",
+                  level_nr,  tape_patch_info,
+                  tape.length_seconds / 60, tape.length_seconds % 60,
+                  "not suitable for version",
+                  (tape.engine_version / 1000000) % 100,
+                  (tape.engine_version / 10000  ) % 100,
+                  (tape.engine_version / 100    ) % 100,
+                  (tape.engine_version          ) % 100);
+
+       if (patch_name[patch_nr + 1] != NULL)
+       {
+         // continue with next patch
+         patch_nr++;
+       }
+       else
+       {
+         // continue with next tape
+         patch_nr = 0;
+       }
+
+       continue;
+      }
+
+      tape.property_bits |= patch_property_bit[patch_nr];
+    }
+
     InitCounter();
 
     TapeStartGamePlaying();
@@ -1256,6 +1382,8 @@ void AutoPlayTapes(void)
   Print("Number of levels played: %d\n", num_levels_played);
   Print("Number of levels solved: %d (%d%%)\n", num_levels_solved,
        (num_levels_played ? num_levels_solved * 100 / num_levels_played : 0));
+  if (global.autoplay_mode == AUTOPLAY_MODE_FIX)
+    Print("Number of tapes fixed: %d\n", num_tapes_patched);
   PrintLine("-", 79);
   Print("Summary (for automatic parsing by scripts):\n");
   Print("LEVELDIR [%s] '%s', SOLVED %d/%d (%d%%)",
@@ -1405,7 +1533,7 @@ void PatchTapes(void)
 
     if (tape.no_valid_file)
     {
-      Print("(not found)\n");
+      Print("(no tape found)\n");
 
       continue;
     }
index ea18e396bff86a0e82e7da2d8a1fb817a7a21ddf..341dca22e2c9aa3d937d4ed526af675c7c44b284 100644 (file)
@@ -194,6 +194,8 @@ struct TapeInfo
   boolean deactivate_display;
   boolean auto_play;
   boolean auto_play_level_solved;
+  boolean auto_play_level_fixed;
+  boolean auto_play_level_not_fixable;
   boolean quick_resume;
   boolean single_step;
   boolean changed;