Created
March 25, 2020 15:41
-
-
Save tjanas/112b7d667027481c6e68e75dbdded321 to your computer and use it in GitHub Desktop.
BPS Decoder: prints instructions from BPS patches
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cstdio> | |
#include <cstdlib> | |
#include <cstdint> | |
#include <cstring> | |
// BPS Decoder. Created by tjanas. | |
// Prints instructions from BPS patches. | |
// Usage: <bps_decode> <patch_file.bps> | |
// Compile: g++ -o bps_decode bps_decode.cpp -std=c++11 | |
// BPS file format created by byuu. https://byuu.org/ | |
// Public domain | |
uint64_t decode_number(FILE* fp) | |
{ | |
uint64_t data = 0, shift = 1; | |
while(true) | |
{ | |
uint8_t x; | |
if (fread(&x, 1, 1, fp) != 1) | |
{ | |
fprintf(stderr, "decode error\n"); | |
exit(EXIT_FAILURE); | |
} | |
data += (x & 0x7f) * shift; | |
if (x & 0x80) | |
break; | |
shift <<= 7; | |
data += shift; | |
} | |
return data; | |
} | |
int main(int argc, char *argv[]) | |
{ | |
uint64_t outputOffset = 0; | |
uint64_t sourceRelativeOffset = 0; | |
uint64_t targetRelativeOffset = 0; | |
if (argc < 2) | |
{ | |
fprintf(stderr, "usage: %s <patch_file.bps>\n", argv[0]); | |
return 0; | |
} | |
FILE* bpsFile = fopen(argv[1], "rb"); | |
if (bpsFile == NULL) | |
{ | |
fprintf(stderr, "could not open BPS file\n"); | |
return 1; | |
} | |
// Get BPS file size | |
if (fseek(bpsFile,0,SEEK_END)) | |
{ | |
fprintf(stderr, "fseek error\n"); | |
return 2; | |
} | |
const long int bpsSize = ftell(bpsFile); | |
if (bpsSize == -1) | |
{ | |
fprintf(stderr, "ftell error\n"); | |
return 3; | |
} | |
if (fseek(bpsFile,0,SEEK_SET)) | |
{ | |
fprintf(stderr, "fseek error\n"); | |
return 2; | |
} | |
// 4 byte header | |
// minimum 3 bytes for source, target, metadata sizes | |
// 12 bytes for trailer checksums | |
if (bpsSize < 19) | |
{ | |
fprintf(stderr, "bps file size should be at least 19 bytes\n"); | |
return 4; | |
} | |
printf("bps file size: %ld\n", bpsSize); | |
char fileFormatMarker[4]; | |
fread(fileFormatMarker, 1, 4, bpsFile); | |
if (memcmp(fileFormatMarker, "BPS1", 4) != 0) | |
{ | |
fprintf(stderr, "beat header invalid\n"); | |
return 5; | |
} | |
uint64_t sourceSize = decode_number(bpsFile); | |
printf("source-size: %llu\n", sourceSize); | |
uint64_t targetSize = decode_number(bpsFile); | |
printf("target-size: %llu\n", targetSize); | |
uint64_t metaDataSize = decode_number(bpsFile); | |
printf("metadata-size: %llu\n", metaDataSize); | |
if (metaDataSize > 0) | |
{ // skip meta data | |
if (fseek(bpsFile, metaDataSize, SEEK_CUR)) | |
{ | |
fprintf(stderr, "fseek error\n"); | |
return 2; | |
} | |
} | |
// Commands repeat until the 12 byte footer. | |
while ( ftell(bpsFile) < (bpsSize - 12) ) | |
{ | |
uint64_t data = decode_number(bpsFile); | |
uint64_t command = data & 3; | |
uint64_t length = (data >> 2) + 1; | |
switch (command) | |
{ | |
case 0: // SourceRead | |
{ | |
printf("copy %llu bytes from source to target at outputOffset=0x%llX\n", length, outputOffset); | |
outputOffset += length; | |
break; | |
} | |
case 1: // TargetRead | |
{ | |
long int bpsOffset = ftell(bpsFile); | |
if (bpsOffset == -1) | |
{ | |
fprintf(stderr, "ftell error\n"); | |
return 6; | |
} | |
printf("copy %llu bytes from BPS offset 0x%lX to target at outputOffset=0x%llX\n", length, bpsOffset, outputOffset); | |
outputOffset += length; | |
if (fseek(bpsFile, length, SEEK_CUR)) | |
{ | |
fprintf(stderr, "fseek error\n"); | |
return 7; | |
} | |
break; | |
} | |
case 2: // SourceCopy | |
{ | |
data = decode_number(bpsFile); | |
if (data & 1) | |
sourceRelativeOffset -= (data >> 1); | |
else | |
sourceRelativeOffset += (data >> 1); | |
printf("copy %llu bytes from source offset 0x%llX to target at outputOffset=0x%llX\n", length, sourceRelativeOffset, outputOffset); | |
sourceRelativeOffset += length; | |
outputOffset += length; | |
break; | |
} | |
case 3: // TargetCopy | |
{ | |
data = decode_number(bpsFile); | |
if (data & 1) | |
targetRelativeOffset -= (data >> 1); | |
else | |
targetRelativeOffset += (data >> 1); | |
printf("copy %llu bytes from target offset 0x%llX to target at outputOffset=0x%llX\n", length, targetRelativeOffset, outputOffset); | |
targetRelativeOffset += length; | |
outputOffset += length; | |
break; | |
} | |
default: // error | |
{ | |
fprintf(stderr, "unhandled command type\n"); | |
return 2; | |
} | |
} | |
} | |
uint32_t sourceChecksum; | |
if (fread(&sourceChecksum, sizeof(sourceChecksum), 1, bpsFile) != 1) | |
{ | |
fprintf(stderr, "could not decode source-checksum\n"); | |
return 8; | |
} | |
printf("source-checksum: %08X\n", sourceChecksum); | |
uint32_t targetChecksum; | |
if (fread(&targetChecksum, sizeof(targetChecksum), 1, bpsFile) != 1) | |
{ | |
fprintf(stderr, "could not decode target-checksum\n"); | |
return 9; | |
} | |
printf("target-checksum: %08X\n", targetChecksum); | |
uint32_t patchChecksum; | |
if (fread(&patchChecksum, sizeof(patchChecksum), 1, bpsFile) != 1) | |
{ | |
fprintf(stderr, "could not decode patch-checksum\n"); | |
return 10; | |
} | |
printf("patch-checksum: %08X\n", patchChecksum); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment