Add bps soft-patching support

This commit is contained in:
OV2 2011-09-10 16:20:00 +02:00
parent b442002071
commit b4f07bc9be

View File

@ -3750,9 +3750,10 @@ void CMemory::ApplyROMFixes (void)
}
}
// UPS % IPS
// BPS % UPS % IPS
static uint32 ReadUPSPointer (const uint8 *data, unsigned &addr, unsigned size)
// number decoding used for both BPS and UPS
static uint32 XPSdecode (const uint8 *data, unsigned &addr, unsigned size)
{
uint32 offset = 0, shift = 1;
while(addr < size) {
@ -3807,8 +3808,8 @@ static bool8 ReadUPSPatch (Reader *r, long, int32 &rom_size)
if(patch_crc32 != pp_crc32) { delete[] data; return false; } //patch is corrupted
if((rom_crc32 != px_crc32) && (rom_crc32 != py_crc32)) { delete[] data; return false; } //patch is for a different ROM
uint32 px_size = ReadUPSPointer(data, addr, size);
uint32 py_size = ReadUPSPointer(data, addr, size);
uint32 px_size = XPSdecode(data, addr, size);
uint32 py_size = XPSdecode(data, addr, size);
uint32 out_size = ((uint32) rom_size == px_size) ? py_size : px_size;
if(out_size > CMemory::MAX_ROM_SIZE) { delete[] data; return false; } //applying this patch will overflow Memory.ROM buffer
@ -3820,7 +3821,7 @@ static bool8 ReadUPSPatch (Reader *r, long, int32 &rom_size)
uint32 relative = 0;
while(addr < size - 12) {
relative += ReadUPSPointer(data, addr, size);
relative += XPSdecode(data, addr, size);
while(addr < size - 12) {
uint8 x = data[addr++];
Memory.ROM[relative++] ^= x;
@ -3852,6 +3853,101 @@ static bool8 ReadUPSPatch (Reader *r, long, int32 &rom_size)
}
}
// header notes for UPS patches also apply to BPS
//
// logic taken from http://byuu.org/programming/bps and the accompanying source
//
static bool8 ReadBPSPatch (Reader *r, long, int32 &rom_size)
{
uint8 *data = new uint8[8 * 1024 * 1024]; //allocate a lot of memory, better safe than sorry ...
uint32 size = 0;
while(true) {
int value = r->get_char();
if(value == EOF) break;
data[size++] = value;
if(size >= 8 * 1024 * 1024) {
//prevent buffer overflow: SNES-made BPS patches should never be this big anyway ...
delete[] data;
return false;
}
}
/* 4-byte header + 1-byte input size + 1-byte output size + 1-byte metadata size
+ 4-byte unpatched CRC32 + 4-byte patched CRC32 + 4-byte patch CRC32 */
if(size < 19) { delete[] data; return false; } //patch is too small
uint32 addr = 0;
if(data[addr++] != 'B') { delete[] data; return false; } //patch has an invalid header
if(data[addr++] != 'P') { delete[] data; return false; } //...
if(data[addr++] != 'S') { delete[] data; return false; } //...
if(data[addr++] != '1') { delete[] data; return false; } //...
uint32 patch_crc32 = caCRC32(data, size - 4); //don't include patch CRC32 itself in CRC32 calculation
uint32 rom_crc32 = caCRC32(Memory.ROM, rom_size);
uint32 source_crc32 = (data[size - 12] << 0) + (data[size - 11] << 8) + (data[size - 10] << 16) + (data[size - 9] << 24);
uint32 target_crc32 = (data[size - 8] << 0) + (data[size - 7] << 8) + (data[size - 6] << 16) + (data[size - 5] << 24);
uint32 pp_crc32 = (data[size - 4] << 0) + (data[size - 3] << 8) + (data[size - 2] << 16) + (data[size - 1] << 24);
if(patch_crc32 != pp_crc32) { delete[] data; return false; } //patch is corrupted
if(rom_crc32 != source_crc32) { delete[] data; return false; } //patch is for a different ROM
uint32 source_size = XPSdecode(data, addr, size);
uint32 target_size = XPSdecode(data, addr, size);
uint32 metadata_size = XPSdecode(data, addr, size);
addr += metadata_size;
if(target_size > CMemory::MAX_ROM_SIZE) { delete[] data; return false; } //applying this patch will overflow Memory.ROM buffer
enum : uint32 { SourceRead, TargetRead, SourceCopy, TargetCopy };
uint32 outputOffset = 0, sourceRelativeOffset = 0, targetRelativeOffset = 0;
uint8 *patched_rom = new uint8[target_size];
memset(patched_rom,0,target_size);
while(addr < size - 12) {
uint32 length = XPSdecode(data, addr, size);
uint32 mode = length & 3;
length = (length >> 2) + 1;
switch(mode) {
case SourceRead:
while(length--) patched_rom[outputOffset++] = Memory.ROM[outputOffset];
break;
case TargetRead:
while(length--) patched_rom[outputOffset++] = data[addr++];
break;
case SourceCopy:
case TargetCopy:
int32 offset = XPSdecode(data, addr, size);
bool negative = offset & 1;
offset >>= 1;
if(negative) offset = -offset;
if(mode == SourceCopy) {
sourceRelativeOffset += offset;
while(length--) patched_rom[outputOffset++] = Memory.ROM[sourceRelativeOffset++];
} else {
targetRelativeOffset += offset;
while(length--) patched_rom[outputOffset++] = patched_rom[targetRelativeOffset++];
}
break;
}
}
delete[] data;
uint32 out_crc32 = caCRC32(patched_rom, target_size);
if(out_crc32 == target_crc32) {
memcpy(Memory.ROM, patched_rom, target_size);
rom_size = target_size;
delete[] patched_rom;
return true;
} else {
delete[] patched_rom;
fprintf(stderr, "WARNING: BPS patching failed.\nROM has not been altered.\n");
return false;
}
}
static long ReadInt (Reader *r, unsigned nbytes)
{
long v = 0;
@ -3969,7 +4065,7 @@ static int unzFindExtension (unzFile &file, const char *ext, bool restart, bool
if (len >= l + 1 && name[len - l - 1] == '.' && strcasecmp(name + len - l, ext) == 0 && unzOpenCurrentFile(file) == UNZ_OK)
{
if (print)
printf("Using IPS or UPS patch %s", name);
printf("Using patch %s", name);
return (port);
}
@ -3994,9 +4090,70 @@ void CMemory::CheckForAnyPatch (const char *rom_filename, bool8 header, int32 &r
char dir[_MAX_DIR + 1], drive[_MAX_DRIVE + 1], name[_MAX_FNAME + 1], ext[_MAX_EXT + 1], ips[_MAX_EXT + 3], fname[PATH_MAX + 1];
const char *n;
_splitpath(rom_filename, drive, dir, name, ext);
// BPS
_makepath(fname, drive, dir, name, "bps");
if ((patch_file = OPEN_STREAM(fname, "rb")) != NULL)
{
printf("Using BPS patch %s", fname);
ret = ReadBPSPatch(new fReader(patch_file), 0, rom_size);
CLOSE_STREAM(patch_file);
if (ret)
{
printf("!\n");
return;
}
else
printf(" failed!\n");
}
#ifdef UNZIP_SUPPORT
if (!strcasecmp(ext, "zip") || !strcasecmp(ext, ".zip"))
{
unzFile file = unzOpen(rom_filename);
if (file)
{
int port = unzFindExtension(file, "bps");
if (port == UNZ_OK)
{
printf(" in %s", rom_filename);
ret = ReadBPSPatch(new unzReader(file), offset, rom_size);
unzCloseCurrentFile(file);
if (ret)
printf("!\n");
else
printf(" failed!\n");
}
}
}
#endif
n = S9xGetFilename(".bps", IPS_DIR);
if ((patch_file = OPEN_STREAM(n, "rb")) != NULL)
{
printf("Using BPS patch %s", n);
ret = ReadBPSPatch(new fReader(patch_file), 0, rom_size);
CLOSE_STREAM(patch_file);
if (ret)
{
printf("!\n");
return;
}
else
printf(" failed!\n");
}
// UPS
_splitpath(rom_filename, drive, dir, name, ext);
_makepath(fname, drive, dir, name, "ups");
if ((patch_file = OPEN_STREAM(fname, "rb")) != NULL)
@ -4058,7 +4215,6 @@ void CMemory::CheckForAnyPatch (const char *rom_filename, bool8 header, int32 &r
// IPS
_splitpath(rom_filename, drive, dir, name, ext);
_makepath(fname, drive, dir, name, "ips");
if ((patch_file = OPEN_STREAM(fname, "rb")) != NULL)