From a317902c6f4f3fcac34c70c6210306ebff829327 Mon Sep 17 00:00:00 2001 From: Sir Walrus Date: Wed, 15 Mar 2023 10:29:02 +0100 Subject: [PATCH] Add (or rather unhide) BPS disassembly function --- flips.cpp | 15 +++++++++++++-- libbps.cpp | 41 +++++++++++++++++++++-------------------- libbps.h | 3 +++ 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/flips.cpp b/flips.cpp index 236850d..294edfa 100644 --- a/flips.cpp +++ b/flips.cpp @@ -1088,7 +1088,7 @@ struct errorinfo CreatePatch(LPCWSTR inromname, LPCWSTR outromname, enum patchty return errinf; } -errorlevel patchinfo(LPCWSTR patchname, struct manifestinfo * manifestinfo) +errorlevel patchinfo(LPCWSTR patchname, struct manifestinfo * manifestinfo, int verbosity) { GUIClaimConsole(); @@ -1161,6 +1161,14 @@ errorlevel patchinfo(LPCWSTR patchname, struct manifestinfo * manifestinfo) } } + if (verbosity >= 1) + { + puts("Disassembly:"); + struct mem patchmem = patch->read(); + bps_disassemble(patchmem, stdout); + free(patchmem.ptr); + } + free(meta.ptr); return el_ok; } @@ -1192,6 +1200,7 @@ void usage() " if output filename is not given, Flips defaults to patch.smc beside the patch\n" "-c --create: create IPS or BPS patch (default if given three arguments)\n" "-I --info: BPS files contain information about input and output roms, print it\n" + " with --verbose, disassemble the entire patch\n" //" also estimates how much of the source file is retained\n" //" anything under 400 is fine, anything over 600 should be treated with suspicion\n" //(TODO: --info --verbose) @@ -1231,6 +1240,7 @@ int flipsmain(int argc, WCHAR * argv[]) int numargs=0; LPCWSTR arg[3]={NULL,NULL,NULL}; bool hasFlags=false; + int verbosity = 0; bool ignoreChecksum=false; @@ -1317,6 +1327,7 @@ int flipsmain(int argc, WCHAR * argv[]) return 0; } else if (!wcscmp(argv[i], TEXT("--help")) || !wcscmp(argv[i], TEXT("-h")) || !wcscmp(argv[i], TEXT("-?"))) usage(); + else if (!wcscmp(argv[i], TEXT("--verbose"))) verbosity++; else usage(); } #ifdef _WIN32 @@ -1426,7 +1437,7 @@ int flipsmain(int argc, WCHAR * argv[]) case a_info: { if (numargs!=1) usage(); - return error_to_exit(patchinfo(arg[0], &manifestinfo)); + return error_to_exit(patchinfo(arg[0], &manifestinfo, verbosity)); } } return 99;//doesn't happen diff --git a/libbps.cpp b/libbps.cpp index 1985c2f..470af34 100644 --- a/libbps.cpp +++ b/libbps.cpp @@ -488,28 +488,20 @@ struct bpsinfo bps_get_info(file* patch, bool changefrac) return ret; } - - -#ifdef BPS_DEBUG -#warning Disable this in release versions. - #include -//Congratulations, you found the undocumented features! They disassemble a patch, telling what it -// does; compare two equivalent BPS patches and tells where each one is more compact. -//They crash or give bogus answers on invalid patches, latter also misbehaves on non-equivalent ones. -//Have fun. -void bps_dump(struct mem patch) +void bps_disassemble(struct mem patch, FILE* out) { #define read8() (*(patchat++)) #define decodeto(var) decodenum(patchat, var) const uint8_t * patchat=patch.ptr; const uint8_t * patchend=patch.ptr+patch.len-12; - read8(); - read8(); - read8(); - read8(); + if (read8() != 'B' || read8() != 'P' || read8() != 'S' || read8() != '1') + { + fprintf(out, "Not a BPS patch\n"); + return; + } size_t inreadat = 0; size_t inlen; @@ -536,13 +528,13 @@ void bps_dump(struct mem patch) { case SourceRead: { - printf("SourceRead %zu from %zu to %zu (0)\n", length, outat, outat); + fprintf(out, "SourceRead %zu from %zu to %zu\n", length, outat, outat); outat += length; } break; case TargetRead: { - printf("TargetRead %zu to %zu\n", length, outat); + fprintf(out, "TargetRead %zu to %zu\n", length, outat); patchat += length; outat += length; } @@ -555,7 +547,7 @@ void bps_dump(struct mem patch) if ((encodeddistance&1)==0) inreadat+=distance; else inreadat-=distance; - printf("SourceCopy %zu from %zu to %zu (%+zi)\n", length, inreadat, outat, inreadat-outat); + fprintf(out, "SourceCopy %zu from %zu to %zu (rel %+zi)\n", length, inreadat, outat, inreadat-outat); inreadat += length; outat += length; } @@ -568,7 +560,7 @@ void bps_dump(struct mem patch) if ((encodeddistance&1)==0) outreadat+=distance; else outreadat-=distance; - printf("TargetCopy %zu from %zu to %zu (%+zi)\n", length, outreadat, outat, outreadat-outat); + fprintf(out, "TargetCopy %zu from %zu to %zu (rel %+zi)\n", length, outreadat, outat, outreadat-outat); outreadat += length; outat += length; } @@ -576,12 +568,21 @@ void bps_dump(struct mem patch) } } - printf("sanity check: %zu=%zu (%+zi), ", patchat-patch.ptr, patchend-patch.ptr, patchat-patchend); - printf("%zu=%zu (%+zi)", outat, outlen, outat-outlen); + if (patchat != patchend) + fprintf(out, "WARNING: patch pointer at %zu != %zu, corrupt patch?\n", patchat-patch.ptr, patchend-patch.ptr); + if (outat != outlen) + fprintf(out, "WARNING: output pointer at %zu != %zu, corrupt patch?\n", outat, outlen); #undef read8 #undef decodeto } + +#ifdef BPS_DEBUG +#warning Disable this in release versions. + +//Congratulations, you found the undocumented feature! It compares two equivalent BPS patches and tells where each one is more compact. +//It will crash or otherwise misbehave on invalid or non-equivalent patches. +//Have fun. void bps_compare(struct mem patch1mem, struct mem patch2mem) { const uint8_t * patch[2]={patch1mem.ptr, patch2mem.ptr}; diff --git a/libbps.h b/libbps.h index 67a9605..db386b3 100644 --- a/libbps.h +++ b/libbps.h @@ -100,6 +100,9 @@ struct bpsinfo { struct bpsinfo bps_get_info(file* patch, bool changefrac); #endif +#include +void bps_disassemble(struct mem patch, FILE* out); + #ifdef __cplusplus } #endif