Skip to content

Instantly share code, notes, and snippets.

@tjanas
Created March 25, 2020 15:41
Show Gist options
  • Save tjanas/112b7d667027481c6e68e75dbdded321 to your computer and use it in GitHub Desktop.
Save tjanas/112b7d667027481c6e68e75dbdded321 to your computer and use it in GitHub Desktop.
BPS Decoder: prints instructions from BPS patches
#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