From e534f3cf4f5640bf422bb1484e1cc45c0a4b5339 Mon Sep 17 00:00:00 2001 From: Holger Schemel Date: Fri, 11 Sep 2020 19:17:57 +0200 Subject: [PATCH] added command to automatically test tapes and try to fix if broken 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 | 16 +++--- src/main.c | 3 +- src/main.h | 2 + src/tape.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++++----- src/tape.h | 2 + 5 files changed, 157 insertions(+), 22 deletions(-) diff --git a/src/init.c b/src/init.c index 6b6e3263..be744766 100644 --- a/src/init.c +++ b/src/init.c @@ -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 ")) diff --git a/src/main.c b/src/main.c index cb0fe8a7..ca4be86d 100644 --- a/src/main.c +++ b/src/main.c @@ -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" diff --git a/src/main.h b/src/main.h index 8675f672..c412801a 100644 --- a/src/main.h +++ b/src/main.h @@ -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 diff --git a/src/tape.c b/src/tape.c index 6ff1aa5c..46fa236e 100644 --- a/src/tape.c +++ b/src/tape.c @@ -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; } diff --git a/src/tape.h b/src/tape.h index ea18e396..341dca22 100644 --- a/src/tape.h +++ b/src/tape.h @@ -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; -- 2.34.1