Add bps soft-patching support
This commit is contained in:
parent
b442002071
commit
b4f07bc9be
172
memmap.cpp
172
memmap.cpp
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user