diff --git a/cli/tools/vrts.py b/cli/tools/vrts.py new file mode 100644 index 00000000..cbe1c7a1 --- /dev/null +++ b/cli/tools/vrts.py @@ -0,0 +1,509 @@ +# VRTS - VGMSTREAM REGRESSION TESTING SCRIPT +# +# Searches for files in a directory (or optionally subdirs) and compares +# the output of two CLI versions, both wav and stdout, for regression +# testing. This creates and deletes temp files, trying to process all +# extensions found unless specified (except a few). + +# TODO reject some .wav but not all (detect created by v) +# TODO capture stdout and enable fuzzy depending on codec +# TODO fix -l option, add decode reset option +import os, argparse, time, datetime, glob, subprocess, struct + + +# don't try to decode common stuff +IGNORED_EXTENSIONS = ['.exe', '.dll', '.zip', '.7z', '.rar', '.bat', '.sh', '.txt', '.lnk', '.wav', '.py', '.md', '.idb'] +#TODO others +FUZZY_CODECS = ['ffmpeg', 'vorbis', 'mpeg', 'speex', 'celt'] + +DEFAULT_CLI_NEW = 'vgmstream-cli' +DEFAULT_CLI_OLD = 'vgmstream-cli_old' + +# result codes, where >= 0: ok (acceptable), <0: ko (not good) +RESULT_SAME = 0 # no diffs +RESULT_FUZZY = 1 # no duffs allowing +-N +RESULT_NONE = 2 # neither exists +RESULT_DIFFS = -3 # different +RESULT_SIZES = -4 # different sizes +RESULT_MISSING_NEW = -5 # new does not exist +RESULT_MISSING_OLD = 6 # old does not exist + +############################################################################### + +def parse_args(): + description = ( + "Compares new vs old vgmstream CLI output, for regression testing" + ) + epilog = ( + "examples:\n" + "%(prog)s *.ogg -r -n\n" + "- checks for differences in ogg of this and subfolders\n" + "%(prog)s *.adx -nd\n" + "- checks for differences in adx and doesn't delete wav output\n" + "%(prog)s -p\n" + "- compares performance performance of all files\n" + ) + + ap = argparse.ArgumentParser(description=description, epilog=epilog, formatter_class=argparse.RawTextHelpFormatter) + ap.add_argument("files", help="files to match", nargs='*', default=["*.*"]) + ap.add_argument("-r","--recursive", help="search files in subfolders", action='store_true') + ap.add_argument("-z","--fuzzy", help="fuzzy threshold of +-N PCM16LE", type=int, default=1) + ap.add_argument("-nd","--no-delete", help="don't delete output", action='store_true') + ap.add_argument("-rd","--result-diffs", help="only report full diffs", action='store_true') + ap.add_argument("-rz","--result-fuzzy", help="only report full and fuzzy diffs", action='store_true') + ap.add_argument("-p","--performance-both", help="compare decode performance", action='store_true') + ap.add_argument("-pn","--performance-new", help="test performance of new CLI", action='store_true') + ap.add_argument("-po","--performance-old", help="test performance of old CLI", action='store_true') + ap.add_argument("-pw","--performance-write", help="compare decode+write performance", action='store_true') + ap.add_argument("-pr","--performance-repeat", help="repeat decoding files N times\n(more files makes easier to see performance changes)", type=int, default=0) + ap.add_argument("-l","--looping", help="compare looping files (slower)", action='store_true') + ap.add_argument("-cn","--cli-new", help="sets name of new CLI (can be a path)") + ap.add_argument("-co","--cli-old", help="sets name of old CLI (can be a path)") + + args = ap.parse_args() + + # derived defaults to simplify + args.performance = args.performance_both or args.performance_new or args.performance_old or args.performance_write + args.compare = not args.performance + if args.performance_both: + args.performance_new = True + args.performance_old = True + + return args + +############################################################################### + +S16_UNPACK = struct.Struct('= pcm2 - max and pcm1 <= pcm2 + max): + print("%i vs %i +- %i at %x" % (pcm1, pcm2, max, self._offset + pos)) + self.fuzzy_diff = pcm1 - pcm2 + self.fuzzy_offset = self._offset + pos + return RESULT_DIFFS + + pos += 2 + + self.fuzzy_count = 1 + return 0 + + def _compare_bytes(self, b1, b2): + # even though python is much slower than C this test is reasonably fast + if b1 == b2: + return RESULT_SAME + + # different: fuzzy check if same + if self._fuzzy_max: + return self._compare_fuzzy(b1, b2) + + return RESULT_DIFFS + + def _compare_files(self, f1, f2): + + # header not part of fuzzyness (no need to get exact with sizes) + if self._fuzzy_max: + b1 = f1.read(self.CHUNK_HEADER) + b2 = f2.read(self.CHUNK_HEADER) + cmp = self._compare_bytes(b1, b2) + if cmp < 0: + return cmp + self._offset += self.CHUNK_HEADER + + while True: + b1 = f1.read(self.CHUNK_SIZE) + b2 = f2.read(self.CHUNK_SIZE) + if not b1 or not b2: + break + + cmp = self._compare_bytes(b1, b2) + if cmp < 0: + return cmp + self._offset += self.CHUNK_SIZE + + return 0 + + + def compare(self): + try: + f1_len = os.path.getsize(self._path1) + except FileNotFoundError: + f1_len = -1 + try: + f2_len = os.path.getsize(self._path2) + except FileNotFoundError: + f2_len = -1 + + if f1_len < 0 and f2_len < 0: + return RESULT_NONE + + if f1_len < 0: + return RESULT_MISSING_NEW + if f2_len < 0: + return RESULT_MISSING_OLD + + if f1_len != f2_len: + return RESULT_SIZES + + with open(self._path1, 'rb') as f1, open(self._path2, 'rb') as f2: + cmp = self._compare_files(f1, f2) + if cmp < 0: + return cmp + + if self.fuzzy_count > 0: + return RESULT_FUZZY + return RESULT_SAME + + +############################################################################### + +# prints colored text in CLI +# https://pkg.go.dev/github.com/whitedevops/colors +# https://stackoverflow.com/questions/287871/ +class VrtsPrinter: + RESET = '\033[0m' + BOLD = '\033[1m' + + LIGHT_RED = '\033[91m' + LIGHT_GREEN = '\033[92m' + LIGHT_YELLOW = '\033[93m' + LIGHT_BLUE = '\033[94m' + LIGHT_CYAN = '\033[96m' + WHITE = '\033[97m' + LIGHT_GRAY = "\033[37m" + DARK_GRAY = "\033[90m" + + COLOR_RESULT = { + RESULT_SAME: WHITE, + RESULT_FUZZY: LIGHT_GREEN, + RESULT_NONE: LIGHT_YELLOW, + RESULT_DIFFS: LIGHT_RED, + RESULT_SIZES: LIGHT_RED, + RESULT_MISSING_NEW: LIGHT_RED, + RESULT_MISSING_OLD: LIGHT_YELLOW, + } + TEXT_RESULT = { + RESULT_SAME: 'same', + RESULT_FUZZY: 'fuzzy same', + RESULT_NONE: 'neither works', + RESULT_DIFFS: 'diffs', + RESULT_SIZES: 'wrong sizes', + RESULT_MISSING_NEW: 'missing new', + RESULT_MISSING_OLD: 'missing old', + } + + def __init__(self): + try: + os.system('color') #win only? + except: + pass + + def _print(self, msg, color=None): + if color: + print("%s%s%s" % (color, msg, self.RESET)) + else: + print(msg) + + + def result(self, msg, code, fuzzy_diff, fuzzy_offset): + text = self.TEXT_RESULT.get(code) + color = self.COLOR_RESULT.get(code) + if not text: + text = code + msg = "%s: %s" % (msg, text) + if fuzzy_diff != 0: + msg += " (%s @0x%x)" % (fuzzy_diff, fuzzy_offset) + self._print(msg, color) + + + def info(self, msg): + msg = "%s (%s)" % (msg, self._get_date()) + self._print(msg, self.DARK_GRAY) + pass + + def _get_date(self): + return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + +############################################################################### + +class VrtsFiles: + def __init__(self, args): + self._args = args + self.filenames = [] + + def prepare(self): + for fpattern in self._args.files: + + recursive = self._args.recursive + if recursive: + fpattern = '**/' + fpattern + + files = glob.glob(fpattern, recursive=recursive) + for file in files: + if not os.path.isfile(file): + continue + + # ignores non useful files + _, ext = os.path.splitext(file) + if ext.lower() in IGNORED_EXTENSIONS: + continue + + self.filenames.append(file) + + # same file N times + if self._args.performance and self._args.performance_repeat: + for i in range(self._args.performance_repeat): + self.filenames.append(file) + + +# calling subprocess with python: +# - os.system(command) +# - not recommended by docs (less flexible and spawns a new process?) +# - subprocess.call +# - wait till complete and returns code +# - subprocess.check_call +# - wait till complete and raise CalledProcessError on nonzero return code +# - subprocess.check_output +# - call without wait, raise CalledProcessError on nonzero return code +# - subprocess.run +# - recommended but python 3.5+ +# (check=True: raise exceptions like check_*, capture_output: return STDOUT/STDERR) +class VrtsProcess: + + def call(self, args, stdout=False): + try: + #with open(os.devnull, 'wb') as DEVNULL: #python2 + # res = subprocess.check_call(args, stdout=DEVNULL, stderr=DEVNULL) + + res = subprocess.run(args, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) #capture_output=stdout, + #print("result:", res.returncode) + #print("result:", res.strout, res.strerr) + + if stdout: + return res.stdout + + return True #exists and returns ok + + except subprocess.CalledProcessError as e: + #print("call error: ", e) #, e.stderr: disable DEVNULL + return False #non-zero, exists but returns strerr (ex. ran with no args) + + except FileNotFoundError as e: + #print("file error: ", e) + return None #doesn't exists/etc + + +class VrtsApp: + + def __init__(self, args): + self._args = args + self._files = VrtsFiles(args) + self._prc = VrtsProcess() + self._p = VrtsPrinter() + self._cli_new = None + self._cli_old = None + + def _find_cli(self, arg_cli, default_cli): + + if arg_cli and os.path.isdir(arg_cli): + cli = os.path.join(arg_cli, default_cli) + + elif arg_cli: #is file + cli = arg_cli + + else: + cli = default_cli + + args = [cli] #plain call to see if program is in PATH + res = self._prc.call(args) + if res is not None: + return cli + + return None + + # detects CLI location: + # - defaults to (cli) [new] + (cli)_old [old] assumed to be in PATH + # - can be passed a dir or file for old/new + # - old is optional in performance mode + def _detect_cli(self): + cli = self._find_cli(self._args.cli_new, DEFAULT_CLI_NEW) + if cli: + self._cli_new = cli + + cli = self._find_cli(self._args.cli_old, DEFAULT_CLI_OLD) + if cli: + self._cli_old = cli + + if not self._cli_new and (self._args.compare or self._args.performance_new): + raise ValueError("new CLI not found") + if not self._cli_old and (self._args.compare or self._args.performance_old): + raise ValueError("old CLI not found") + + def _get_performance_args(self, cli): + args = [cli, '-O'] #flag to not write files + if self._args.looping: + args.append('-i') + args.extend(self._files.filenames) + return args + + def _performance(self): + flag_looping = '' + if self._args.looping: + flag_looping = '-i' + + # pases all files at once, as it's faster than 1 by 1 (that has to init program every time) + if self._performance_new: + self._p.info("testing new performance") + ts_st = time.time() + + args = self._get_performance_args(self._cli_new) + res = self._prc.call(args) + + ts_ed = time.time() + self._p.info("done: elapsed %ss" % (ts_ed - ts_st)) + + if self._performance_old: + self._p.info("testing old performance") + ts_st = time.time() + + args = self._get_performance_args(self._cli_old) + res = self._prc.call(args) + + ts_ed = time.time() + self._p.info("done: elapsed %ss (%s)" % (ts_ed - ts_st)) + + #if self._performance_both: + # ... + + + # returns max fuzzy count, except for non-fuzzable files (that use int math) + def _get_fuzzy_count(self, stdout): + fuzzy = self._args.fuzzy + if self._args.fuzzy <= 0: + return 0 + + if not stdout: + return fuzzy + + try: + pos = stdout.index(b'encoding:') + codec_line = stdout[0:].split('\n', 1)[0] + for fuzzy_codec in FUZZY_CODECS: + if fuzzy_codec in codec_line: + return fuzzy + except Exception as e: + pass + + return 0 #non-fuzable + + def _get_compare_args(self, cli, outwav, filename): + args = [cli, '-o', outwav] #flag to not write files + if self._args.looping: + args.append('-i') + args.append(filename) + return args + + def _compare(self): + ts_st = time.time() + self._p.info("comparing files") + + flag_looping = '' + if self._args.looping: + flag_looping = '-i' + + total_ok = 0 + total_ko = 0 + for filename in self._files.filenames: + filename_newwav = filename + ".new.wav" + filename_oldwav = filename + ".old.wav" + + # main decode (ignores errors, comparator already checks them) + args = self._get_compare_args(self._cli_new, filename_newwav, filename) + stdout = self._prc.call(args, stdout=True) + + args = self._get_compare_args(self._cli_old, filename_oldwav, filename) + self._prc.call(args, stdout=False) + + # test results + fuzzy = self._get_fuzzy_count(stdout) + cmp = VrtsComparator(filename_newwav, filename_oldwav, fuzzy) + code = cmp.compare() + + self._p.result(filename, code, cmp.fuzzy_diff, cmp.fuzzy_offset) + + if code < 0: + total_ko += 1 + else: + total_ok += 1 + + # post cleanup + if not self._args.no_delete: + try: + os.remove(filename_newwav) + except: + pass + try: + os.remove(filename_oldwav) + except: + pass + + ts_ed = time.time() + self._p.info("done: ok=%s, ko=%s, elapsed %ss" % (total_ok, total_ko, ts_ed - ts_st)) + + + def start(self): + self._detect_cli() + self._files.prepare() + if self._args.performance: + self._performance() + else: + self._compare() + + +def main(): + args = parse_args() + if not args: + return + + try: + VrtsApp(args).start() + except ValueError as e: + print(e) + +if __name__ == "__main__": + main() diff --git a/cli/vrts.bat b/cli/vrts.bat deleted file mode 100644 index 73b10e96..00000000 --- a/cli/vrts.bat +++ /dev/null @@ -1,285 +0,0 @@ -@echo off -chcp 65001 -REM #------------------------------------------------------------------------- -REM # VGMSTREAM REGRESSION TESTING SCRIPT -REM # -REM # Searches for files in a directory (or optionally subdirs) and compares -REM # the output of two test.exe versions, both wav and stdout, for regression -REM # testing. This creates and deletes temp files, trying to process all -REM # extensions found unless specified (except a few). -REM # -REM # Options: see below. -REM #------------------------------------------------------------------------- - - -REM #------------------------------------------------------------------------- -REM #options -REM #------------------------------------------------------------------------- -REM # -vo -vn : path to old/new exe -set OP_CMD_OLD=test_old.exe -set OP_CMD_NEW=test.exe -REM # -f : search wildcard (ex. -f "*.adx") -set OP_SEARCH="*.*" -REM # -r: recursive subfolders -set OP_RECURSIVE= -REM # -nd: don't delete compared files -set OP_NODELETE= -REM # -nc: don't report correct files -set OP_NOCORRECT= -REM # -p: performance test new (decode with new exe and no comparison done) -REM # -P: performance test new (same but also don't write file) -REM # -po: performance test old (decode with new old and no comparison done) -REM # -Po: performance test old (same but also don't write file) -REM # -Pm: performance test new (only parse meta) -REM # -Pmo: performance test old (only parse meta) -set OP_PERFORMANCE= -REM # -fc : file comparer -REM # Windows's FC is slow, try -fc ffc.exe (https://github.com/bnnm/vgm-tools/tree/master/misc/ffc) -set OP_CMD_FC=fc /a /b - - -REM # parse options -:set_options -if "%~1"=="" goto end_options -if "%~1"=="-vo" set OP_CMD_OLD=%2 -if "%~1"=="-vn" set OP_CMD_NEW=%2 -if "%~1"=="-f" set OP_SEARCH=%2 -if "%~1"=="-r" set OP_RECURSIVE=/s -if "%~1"=="-nd" set OP_NODELETE=true -if "%~1"=="-nc" set OP_NOCORRECT=true -if "%~1"=="-p" set OP_PERFORMANCE=1 -if "%~1"=="-P" set OP_PERFORMANCE=2 -if "%~1"=="-po" set OP_PERFORMANCE=3 -if "%~1"=="-Po" set OP_PERFORMANCE=4 -if "%~1"=="-Pm" set OP_PERFORMANCE=5 -if "%~1"=="-Pmo" set OP_PERFORMANCE=6 -if "%~1"=="-fc" set OP_CMD_FC=%2 -shift -goto set_options -:end_options - -REM # output color defs -set COLOR_WN=0e -set COLOR_ER=0c -set COLOR_OK=0f - -REM # remove command options and possibly " -for /f "tokens=1-1 delims= " %%A in ("%OP_CMD_OLD%") do set CHECK_OLD=%%A -for /f "tokens=1-1 delims= " %%A in ("%OP_CMD_NEW%") do set CHECK_NEW=%%A -set CHECK_OLD=%CHECK_OLD:"=% -set CHECK_NEW=%CHECK_NEW:"=% - -REM # check exe -set CMD_CHECK=where "%CHECK_OLD%" "%CHECK_NEW%" -%CMD_CHECK% > nul -if %ERRORLEVEL% NEQ 0 ( - echo Old/new exe not found - goto error -) - -if %OP_SEARCH%=="" ( - echo Search wildcard not specified - goto error -) - -REM # process start -set TIME_START=%time% -set FILES_OK=0 -set FILES_KO=0 -echo VRTS: start @%TIME_START% - -REM # search for files -set CMD_DIR=dir /a:-d /b %OP_RECURSIVE% %OP_SEARCH% -set CMD_FIND=findstr /i /v "\.exe$ \.dll$ \.zip$ \.7z$ \.rar$ \.bat$ \.sh$ \.txt$ \.lnk$ \.wav$" - -REM # process files -for /f "delims=" %%x in ('%CMD_DIR% ^| %CMD_FIND%') do ( - set CMD_FILE=%%x - - REM # must enable/disable after setting CMD as ! & in dir names would become expanded/removed otherwise - setlocal EnableDelayedExpansion - - if "%OP_PERFORMANCE%" == "" ( - call :process_file "!CMD_FILE!" - ) else ( - call :performance_file "!CMD_FILE!" - ) - - REM this doesn't seem to properly disable it and will error out after N files - REM but using endlocal will remove vars and counters won't work - REM setlocal DisableDelayedExpansion - endlocal -) - -REM # find time elapsed -set TIME_END=%time% -for /F "tokens=1-4 delims=:.," %%a in ("%TIME_START%") do ( - set /A "TIME_START_S=(((%%a*60)+1%%b %% 100)*60+1%%c %% 100)*100+1%%d %% 100" -) -for /F "tokens=1-4 delims=:.," %%a in ("%TIME_END%") do ( - set /A "TIME_END_S=(((%%a*60)+1%%b %% 100)*60+1%%c %% 100)*100+1%%d %% 100" -) -set /A TIME_ELAPSED_S=(TIME_END_S-TIME_START_S)/100 -set /A TIME_ELAPSED_C=(TIME_END_S-TIME_START_S)%%100 - - -REM # process end (ok) -echo VRTS: done @%TIME_END% (%TIME_ELAPSED_S%,%TIME_ELAPSED_C%s) -echo VRTS: ok=%FILES_OK%, ko=%FILES_KO% - -goto exit - - -REM # ######################################################################## -REM # test a single file -REM # ######################################################################## -:process_file outer - REM # ignore files starting with dot (no filename) - set CMD_SHORTNAME=%~n1 - if "%CMD_SHORTNAME%" == "" goto process_file_continue - - - REM # get file (not from arg %1) - set CMD_FILE=!CMD_FILE! - set CMD_FILE=!CMD_FILE:"=! - REM echo VTRS: file !CMD_FILE! - - REM # old/new temp output - set WAV_OLD=!CMD_FILE!.old.wav - set TXT_OLD=!CMD_FILE!.old.txt - set CMD_VGM_OLD="%OP_CMD_OLD%" -o "!WAV_OLD!" "!CMD_FILE!" - !CMD_VGM_OLD! 1> "!TXT_OLD!" 2>&1 & REM || goto error - - set WAV_NEW=!CMD_FILE!.new.wav - set TXT_NEW=!CMD_FILE!.new.txt - set CMD_VGM_NEW="%OP_CMD_NEW%" -o "!WAV_NEW!" "!CMD_FILE!" - !CMD_VGM_NEW! 1> "!TXT_NEW!" 2>&1 & REM || goto error - - REM # ignore if no files are created (unsupported formats) - if not exist "!WAV_NEW!" ( - if not exist "!WAV_OLD!" ( - REM echo VRTS: nothing created for file !CMD_FILE! - if exist "!TXT_NEW!" del /a:a "!TXT_NEW!" - if exist "!TXT_OLD!" del /a:a "!TXT_OLD!" - goto process_file_continue - ) - ) - - REM # compare files (without /b may to be faster for small files?) - set CMP_WAV=%OP_CMD_FC% "!WAV_OLD!" "!WAV_NEW!" - set CMP_TXT=%OP_CMD_FC% "!TXT_OLD!" "!TXT_NEW!" - - !CMP_WAV! 1> nul 2>&1 - set CMP_WAV_ERROR=0 - if %ERRORLEVEL% NEQ 0 set CMP_WAV_ERROR=1 - - !CMP_TXT! 1> nul 2>&1 - set CMP_TXT_ERROR=0 - if %ERRORLEVEL% NEQ 0 set CMP_TXT_ERROR=1 - - REM # print output - if %CMP_WAV_ERROR% EQU 1 ( - if %CMP_TXT_ERROR% EQU 1 ( - call :echo_color %COLOR_ER% "!CMD_FILE!" "wav and txt diffs" - ) else ( - call :echo_color %COLOR_ER% "!CMD_FILE!" "wav diffs" - ) - set /a FILES_KO=FILES_KO + 1 - ) else ( - if %CMP_TXT_ERROR% EQU 1 ( - call :echo_color %COLOR_WN% "!CMD_FILE!" "txt diffs" - ) else ( - if "%OP_NOCORRECT%" == "" ( - call :echo_color %COLOR_OK% "!CMD_FILE!" "no diffs" - ) - ) - - set /a FILES_OK=FILES_OK + 1 - ) - - REM # delete temp files - if "%OP_NODELETE%" == "" ( - if exist "!WAV_OLD!" del /a:a "!WAV_OLD!" - if exist "!TXT_OLD!" del /a:a "!TXT_OLD!" - if exist "!WAV_NEW!" del /a:a "!WAV_NEW!" - if exist "!TXT_NEW!" del /a:a "!TXT_NEW!" - ) - -:process_file_continue -exit /B -REM :process_file end, continue from last call - - -REM # ######################################################################## -REM # decode only (no comparisons done), for performance testing -REM # ######################################################################## -:performance_file outer - REM # ignore files starting with dot (no filename) - set CMD_SHORTNAME=%~n1 - if "%CMD_SHORTNAME%" == "" goto performance_file_continue - - REM # get file (not from arg %1) - set CMD_FILE=!CMD_FILE! - set CMD_FILE=!CMD_FILE:"=! - REM echo VTRS: file !CMD_FILE! - - set WAV_NEW=!CMD_FILE!.test.wav - if "%OP_PERFORMANCE%" == "1" ( - set CMD_VGM="%OP_CMD_NEW%" -o "!WAV_NEW!" "!CMD_FILE!" - ) - if "%OP_PERFORMANCE%" == "2" ( - set CMD_VGM="%OP_CMD_NEW%" -O "!CMD_FILE!" - ) - if "%OP_PERFORMANCE%" == "3" ( - set CMD_VGM="%OP_CMD_OLD%" -o "!WAV_OLD!" "!CMD_FILE!" - ) - if "%OP_PERFORMANCE%" == "4" ( - set CMD_VGM="%OP_CMD_OLD%" -O "!CMD_FILE!" - ) - if "%OP_PERFORMANCE%" == "5" ( - set CMD_VGM="%OP_CMD_NEW%" -m "!CMD_FILE!" - ) - if "%OP_PERFORMANCE%" == "6" ( - set CMD_VGM="%OP_CMD_OLD%" -m "!CMD_FILE!" - ) - - !CMD_VGM! 1> nul 2>&1 & REM || goto error - set CMP_WAV_ERROR=0 - if %ERRORLEVEL% NEQ 0 set CMP_WAV_ERROR=1 - - call :echo_color %COLOR_OK% "!CMD_FILE!" "done" - - REM # ignore output - if exist "!WAV_NEW!" del /a:a "!WAV_NEW!" - -:performance_file_continue -exit /B -REM :performance_file end, continue from last call - - -REM # ######################################################################## -REM # hack to get colored output in Windows CMD using findstr + temp file -REM # ######################################################################## -:echo_color -REM # don't use %2 but !CMD_FILE! -set TEMP_FILE=!CMD_FILE!-result -set TEMP_FILE=!TEMP_FILE:"=! -set TEMP_TEXT=%3 -set TEMP_TEXT=!TEMP_TEXT:"=! -echo !TEMP_TEXT! > "!TEMP_FILE!" - -REM # show colored filename + any text in temp file -findstr /a:%1 /r ".*" "!TEMP_FILE!" nul -del "!TEMP_FILE!" - -exit /B -REM :echo_color end, continue from last call - - -REM # ######################################################################## - -:error -echo VRTS: error -goto exit - -:exit