Built-in ROM patcher
Implement built-in ROM patcher instead of relying upon nall to perform the patching.
This commit is contained in:
parent
b846bb0911
commit
cf4175312c
8 changed files with 362 additions and 236 deletions
|
@ -1,20 +0,0 @@
|
||||||
#ifndef _patchrom__hpp__included__
|
|
||||||
#define _patchrom__hpp__included__
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Patch a ROM. Autodetects type of patch.
|
|
||||||
*
|
|
||||||
* Parameter original: The orignal file to patch.
|
|
||||||
* Parameter patch: The patch to apply.
|
|
||||||
* Parameter offset: Offset to apply.
|
|
||||||
* Returns The patched file.
|
|
||||||
* Throws std::bad_alloc: Not enough memory.
|
|
||||||
* Throws std::runtime_error: Invalid patch file.
|
|
||||||
*/
|
|
||||||
std::vector<char> do_patch_file(const std::vector<char>& original, const std::vector<char>& patch,
|
|
||||||
int32_t offset) throw(std::bad_alloc, std::runtime_error);
|
|
||||||
|
|
||||||
#endif
|
|
47
include/library/patchrom.hpp
Normal file
47
include/library/patchrom.hpp
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#ifndef _library_patchrom__hpp__included__
|
||||||
|
#define _library_patchrom__hpp__included__
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Patch a ROM. Autodetects type of patch.
|
||||||
|
*
|
||||||
|
* Parameter original: The orignal file to patch.
|
||||||
|
* Parameter patch: The patch to apply.
|
||||||
|
* Parameter offset: Offset to apply.
|
||||||
|
* Returns The patched file.
|
||||||
|
* Throws std::bad_alloc: Not enough memory.
|
||||||
|
* Throws std::runtime_error: Invalid patch file.
|
||||||
|
*/
|
||||||
|
std::vector<char> do_patch_file(const std::vector<char>& original, const std::vector<char>& patch,
|
||||||
|
int32_t offset) throw(std::bad_alloc, std::runtime_error);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ROM patcher.
|
||||||
|
*/
|
||||||
|
struct rom_patcher
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
rom_patcher() throw(std::bad_alloc);
|
||||||
|
/**
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~rom_patcher() throw();
|
||||||
|
/**
|
||||||
|
* Identify patch.
|
||||||
|
*
|
||||||
|
* Parameter patch: The patch.
|
||||||
|
* Returns: True if my format, false if not.
|
||||||
|
*/
|
||||||
|
virtual bool identify(const std::vector<char>& patch) throw() = 0;
|
||||||
|
/**
|
||||||
|
* Do the patch.
|
||||||
|
*/
|
||||||
|
virtual void dopatch(std::vector<char>& out, const std::vector<char>& original,
|
||||||
|
const std::vector<char>& patch, int32_t offset) throw(std::bad_alloc, std::runtime_error) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -12,7 +12,7 @@ void _write_common(unsigned char* target, T1 value)
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T1, typename T2, size_t ssize, bool be>
|
template<typename T1, typename T2, size_t ssize, bool be>
|
||||||
T1 _read_common(unsigned char* source)
|
T1 _read_common(const unsigned char* source)
|
||||||
{
|
{
|
||||||
T2 value = 0;
|
T2 value = 0;
|
||||||
for(size_t i = 0; i < ssize; i++)
|
for(size_t i = 0; i < ssize; i++)
|
||||||
|
@ -39,22 +39,22 @@ T1 _read_common(unsigned char* source)
|
||||||
#define write64sle(t, v) _write_common< int64_t, uint64_t, 8, false>(reinterpret_cast<unsigned char*>(t), (v));
|
#define write64sle(t, v) _write_common< int64_t, uint64_t, 8, false>(reinterpret_cast<unsigned char*>(t), (v));
|
||||||
#define write64ube(t, v) _write_common<uint64_t, uint64_t, 8, true>(reinterpret_cast<unsigned char*>(t), (v));
|
#define write64ube(t, v) _write_common<uint64_t, uint64_t, 8, true>(reinterpret_cast<unsigned char*>(t), (v));
|
||||||
#define write64ule(t, v) _write_common<uint64_t, uint64_t, 8, false>(reinterpret_cast<unsigned char*>(t), (v));
|
#define write64ule(t, v) _write_common<uint64_t, uint64_t, 8, false>(reinterpret_cast<unsigned char*>(t), (v));
|
||||||
#define read8sbe(t) _read_common< int8_t, uint8_t, 1, true>(reinterpret_cast<unsigned char*>(t));
|
#define read8sbe(t) _read_common< int8_t, uint8_t, 1, true>(reinterpret_cast<const unsigned char*>(t));
|
||||||
#define read8sle(t) _read_common< int8_t, uint8_t, 1, false>(reinterpret_cast<unsigned char*>(t));
|
#define read8sle(t) _read_common< int8_t, uint8_t, 1, false>(reinterpret_cast<const unsigned char*>(t));
|
||||||
#define read8ube(t) _read_common< uint8_t, uint8_t, 1, true>(reinterpret_cast<unsigned char*>(t));
|
#define read8ube(t) _read_common< uint8_t, uint8_t, 1, true>(reinterpret_cast<const unsigned char*>(t));
|
||||||
#define read8ule(t) _read_common< uint8_t, uint8_t, 1, false>(reinterpret_cast<unsigned char*>(t));
|
#define read8ule(t) _read_common< uint8_t, uint8_t, 1, false>(reinterpret_cast<const unsigned char*>(t));
|
||||||
#define read16sbe(t) _read_common< int16_t, uint16_t, 2, true>(reinterpret_cast<unsigned char*>(t));
|
#define read16sbe(t) _read_common< int16_t, uint16_t, 2, true>(reinterpret_cast<const unsigned char*>(t));
|
||||||
#define read16sle(t) _read_common< int16_t, uint16_t, 2, false>(reinterpret_cast<unsigned char*>(t));
|
#define read16sle(t) _read_common< int16_t, uint16_t, 2, false>(reinterpret_cast<const unsigned char*>(t));
|
||||||
#define read16ube(t) _read_common<uint16_t, uint16_t, 2, true>(reinterpret_cast<unsigned char*>(t));
|
#define read16ube(t) _read_common<uint16_t, uint16_t, 2, true>(reinterpret_cast<const unsigned char*>(t));
|
||||||
#define read16ule(t) _read_common<uint16_t, uint16_t, 2, false>(reinterpret_cast<unsigned char*>(t));
|
#define read16ule(t) _read_common<uint16_t, uint16_t, 2, false>(reinterpret_cast<const unsigned char*>(t));
|
||||||
#define read32sbe(t) _read_common< int32_t, uint32_t, 4, true>(reinterpret_cast<unsigned char*>(t));
|
#define read32sbe(t) _read_common< int32_t, uint32_t, 4, true>(reinterpret_cast<const unsigned char*>(t));
|
||||||
#define read32sle(t) _read_common< int32_t, uint32_t, 4, false>(reinterpret_cast<unsigned char*>(t));
|
#define read32sle(t) _read_common< int32_t, uint32_t, 4, false>(reinterpret_cast<const unsigned char*>(t));
|
||||||
#define read32ube(t) _read_common<uint32_t, uint32_t, 4, true>(reinterpret_cast<unsigned char*>(t));
|
#define read32ube(t) _read_common<uint32_t, uint32_t, 4, true>(reinterpret_cast<const unsigned char*>(t));
|
||||||
#define read32ule(t) _read_common<uint32_t, uint32_t, 4, false>(reinterpret_cast<unsigned char*>(t));
|
#define read32ule(t) _read_common<uint32_t, uint32_t, 4, false>(reinterpret_cast<const unsigned char*>(t));
|
||||||
#define read64sbe(t) _read_common< int64_t, uint64_t, 8, true>(reinterpret_cast<unsigned char*>(t));
|
#define read64sbe(t) _read_common< int64_t, uint64_t, 8, true>(reinterpret_cast<const unsigned char*>(t));
|
||||||
#define read64sle(t) _read_common< int64_t, uint64_t, 8, false>(reinterpret_cast<unsigned char*>(t));
|
#define read64sle(t) _read_common< int64_t, uint64_t, 8, false>(reinterpret_cast<const unsigned char*>(t));
|
||||||
#define read64ube(t) _read_common<uint64_t, uint64_t, 8, true>(reinterpret_cast<unsigned char*>(t));
|
#define read64ube(t) _read_common<uint64_t, uint64_t, 8, true>(reinterpret_cast<const unsigned char*>(t));
|
||||||
#define read64ule(t) _read_common<uint64_t, uint64_t, 8, false>(reinterpret_cast<unsigned char*>(t));
|
#define read64ule(t) _read_common<uint64_t, uint64_t, 8, false>(reinterpret_cast<const unsigned char*>(t));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,198 +0,0 @@
|
||||||
#include "core/patchrom.hpp"
|
|
||||||
#include "core/misc.hpp"
|
|
||||||
|
|
||||||
#include <nall/ips.hpp>
|
|
||||||
#include <nall/bps/patch.hpp>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
void throw_bps_error(nall::bpspatch::result r)
|
|
||||||
{
|
|
||||||
switch(r)
|
|
||||||
{
|
|
||||||
case nall::bpspatch::unknown:
|
|
||||||
throw std::runtime_error("Unknown error status");
|
|
||||||
case nall::bpspatch::success:
|
|
||||||
break;
|
|
||||||
case nall::bpspatch::patch_too_small:
|
|
||||||
throw std::runtime_error("Patch too small to be valid");
|
|
||||||
case nall::bpspatch::patch_invalid_header:
|
|
||||||
throw std::runtime_error("Patch has invalid header");
|
|
||||||
case nall::bpspatch::source_too_small:
|
|
||||||
throw std::runtime_error("Source file is too small");
|
|
||||||
case nall::bpspatch::target_too_small:
|
|
||||||
throw std::runtime_error("INTERNAL ERROR: Target file is too small");
|
|
||||||
case nall::bpspatch::source_checksum_invalid:
|
|
||||||
throw std::runtime_error("Source file fails CRC check");
|
|
||||||
case nall::bpspatch::target_checksum_invalid:
|
|
||||||
throw std::runtime_error("Result fails CRC check");
|
|
||||||
case nall::bpspatch::patch_checksum_invalid:
|
|
||||||
throw std::runtime_error("Corrupt patch file");
|
|
||||||
default:
|
|
||||||
throw std::runtime_error("Unknown error applying patch");
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<char> do_patch_bps(const std::vector<char>& original, const std::vector<char>& patch,
|
|
||||||
int32_t offset) throw(std::bad_alloc, std::runtime_error)
|
|
||||||
{
|
|
||||||
if(offset)
|
|
||||||
throw std::runtime_error("Offsets are not supported for .bps patches");
|
|
||||||
std::vector<char> _original = original;
|
|
||||||
std::vector<char> _patch = patch;
|
|
||||||
nall::bpspatch p;
|
|
||||||
|
|
||||||
p.source(reinterpret_cast<uint8_t*>(&_original[0]), _original.size());
|
|
||||||
p.modify(reinterpret_cast<uint8_t*>(&_patch[0]), _patch.size());
|
|
||||||
|
|
||||||
//Do trial apply to get the size.
|
|
||||||
uint8_t tmp;
|
|
||||||
p.target(&tmp, 1);
|
|
||||||
nall::bpspatch::result r = p.apply();
|
|
||||||
if(r == nall::bpspatch::success) {
|
|
||||||
//Fun, the output is 0 or 1 bytes.
|
|
||||||
std::vector<char> ret;
|
|
||||||
ret.resize(p.size());
|
|
||||||
memcpy(&ret[0], &tmp, p.size());
|
|
||||||
return ret;
|
|
||||||
} else if(r != nall::bpspatch::target_too_small) {
|
|
||||||
//This is actual error in patch.
|
|
||||||
throw_bps_error(r);
|
|
||||||
}
|
|
||||||
size_t tsize = p.size();
|
|
||||||
|
|
||||||
//Okay, do it for real.
|
|
||||||
std::vector<char> ret;
|
|
||||||
ret.resize(tsize);
|
|
||||||
p.source(reinterpret_cast<uint8_t*>(&_original[0]), _original.size());
|
|
||||||
p.modify(reinterpret_cast<uint8_t*>(&_patch[0]), _patch.size());
|
|
||||||
p.target(reinterpret_cast<uint8_t*>(&ret[0]), tsize);
|
|
||||||
r = p.apply();
|
|
||||||
if(r != nall::bpspatch::success)
|
|
||||||
throw_bps_error(r);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<size_t, size_t> rewrite_ips_record(std::vector<char>& _patch, size_t woffset,
|
|
||||||
const std::vector<char>& patch, size_t roffset, int32_t offset)
|
|
||||||
{
|
|
||||||
if(patch.size() < roffset + 3)
|
|
||||||
throw std::runtime_error("Patch incomplete");
|
|
||||||
uint32_t a, b, c;
|
|
||||||
a = static_cast<uint8_t>(patch[roffset + 0]);
|
|
||||||
b = static_cast<uint8_t>(patch[roffset + 1]);
|
|
||||||
c = static_cast<uint8_t>(patch[roffset + 2]);
|
|
||||||
uint32_t rec_off = a * 65536 + b * 256 + c;
|
|
||||||
if(rec_off == 0x454F46) {
|
|
||||||
//EOF.
|
|
||||||
memcpy(&_patch[woffset], "EOF", 3);
|
|
||||||
return std::make_pair(3, 3);
|
|
||||||
}
|
|
||||||
if(patch.size() < roffset + 5)
|
|
||||||
throw std::runtime_error("Patch incomplete");
|
|
||||||
a = static_cast<uint8_t>(patch[roffset + 3]);
|
|
||||||
b = static_cast<uint8_t>(patch[roffset + 4]);
|
|
||||||
uint32_t rec_size = a * 256 + b;
|
|
||||||
uint32_t rec_rlesize = 0;
|
|
||||||
uint32_t rec_rawsize = 0;
|
|
||||||
if(!rec_size) {
|
|
||||||
if(patch.size() < roffset + 8)
|
|
||||||
throw std::runtime_error("Patch incomplete");
|
|
||||||
a = static_cast<uint8_t>(patch[roffset + 5]);
|
|
||||||
b = static_cast<uint8_t>(patch[roffset + 6]);
|
|
||||||
rec_rlesize = a * 256 + b;
|
|
||||||
rec_rawsize = 8;
|
|
||||||
} else
|
|
||||||
rec_rawsize = 5 + rec_size;
|
|
||||||
int32_t rec_noff = rec_off + offset;
|
|
||||||
if(rec_noff > 0xFFFFFF)
|
|
||||||
throw std::runtime_error("Offset exceeds IPS 16MiB limit");
|
|
||||||
if(rec_noff < 0) {
|
|
||||||
//This operation needs to clip the start as it is out of bounds.
|
|
||||||
if(rec_size) {
|
|
||||||
if(rec_size > -rec_noff) {
|
|
||||||
rec_noff = 0;
|
|
||||||
rec_size -= -rec_noff;
|
|
||||||
} else
|
|
||||||
rec_size = 0;
|
|
||||||
}
|
|
||||||
if(rec_rlesize) {
|
|
||||||
if(rec_rlesize > -rec_noff) {
|
|
||||||
rec_noff = 0;
|
|
||||||
rec_rlesize -= -rec_noff;
|
|
||||||
} else
|
|
||||||
rec_rlesize = 0;
|
|
||||||
}
|
|
||||||
if(!rec_size && !rec_rlesize)
|
|
||||||
return std::make_pair(0, rec_rawsize); //Completely out of bounds.
|
|
||||||
}
|
|
||||||
//Write the modified record.
|
|
||||||
_patch[woffset + 0] = (rec_noff >> 16);
|
|
||||||
_patch[woffset + 1] = (rec_noff >> 8);
|
|
||||||
_patch[woffset + 2] = rec_noff;
|
|
||||||
_patch[woffset + 3] = (rec_size >> 8);
|
|
||||||
_patch[woffset + 4] = rec_size;
|
|
||||||
if(rec_size == 0) {
|
|
||||||
//RLE.
|
|
||||||
_patch[woffset + 5] = (rec_rlesize >> 8);
|
|
||||||
_patch[woffset + 6] = rec_rlesize;
|
|
||||||
_patch[woffset + 7] = patch[roffset + 7];
|
|
||||||
return std::make_pair(8, 8);
|
|
||||||
} else
|
|
||||||
memcpy(&_patch[woffset + 5], &patch[roffset + rec_rawsize - rec_size], rec_size);
|
|
||||||
return std::make_pair(5 + rec_size, rec_rawsize);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<char> rewrite_ips_offset(const std::vector<char>& patch, int32_t offset)
|
|
||||||
{
|
|
||||||
size_t wsize = 5;
|
|
||||||
size_t roffset = 5;
|
|
||||||
if(patch.size() < 5)
|
|
||||||
throw std::runtime_error("IPS file doesn't even have magic");
|
|
||||||
std::vector<char> _patch;
|
|
||||||
//The result is at most the size of the original.
|
|
||||||
_patch.resize(patch.size());
|
|
||||||
memcpy(&_patch[0], "PATCH", 5);
|
|
||||||
while(true) {
|
|
||||||
std::pair<size_t, size_t> r = rewrite_ips_record(_patch, wsize, patch, roffset, offset);
|
|
||||||
wsize += r.first;
|
|
||||||
roffset += r.second;
|
|
||||||
if(r.first == 3)
|
|
||||||
break; //EOF.
|
|
||||||
}
|
|
||||||
_patch.resize(wsize);
|
|
||||||
return _patch;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<char> do_patch_ips(const std::vector<char>& original, const std::vector<char>& patch,
|
|
||||||
int32_t offset) throw(std::bad_alloc, std::runtime_error)
|
|
||||||
{
|
|
||||||
std::vector<char> _original = original;
|
|
||||||
std::vector<char> _patch = rewrite_ips_offset(patch, offset);
|
|
||||||
nall::ips p;
|
|
||||||
p.source(reinterpret_cast<uint8_t*>(&_original[0]), _original.size());
|
|
||||||
p.modify(reinterpret_cast<uint8_t*>(&_patch[0]), _patch.size());
|
|
||||||
if(!p.apply())
|
|
||||||
throw std::runtime_error("Error applying IPS patch");
|
|
||||||
std::vector<char> ret;
|
|
||||||
ret.resize(p.size);
|
|
||||||
memcpy(&ret[0], p.data, p.size);
|
|
||||||
//No, these can't be freed.
|
|
||||||
p.source(NULL, 0);
|
|
||||||
p.modify(NULL, 0);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<char> do_patch_file(const std::vector<char>& original, const std::vector<char>& patch,
|
|
||||||
int32_t offset) throw(std::bad_alloc, std::runtime_error)
|
|
||||||
{
|
|
||||||
if(patch.size() > 5 && patch[0] == 'P' && patch[1] == 'A' && patch[2] == 'T' && patch[3] == 'C' &&
|
|
||||||
patch[4] == 'H')
|
|
||||||
return do_patch_ips(original, patch, offset);
|
|
||||||
else if(patch.size() > 4 && patch[0] == 'B' && patch[1] == 'P' && patch[2] == 'S' && patch[3] == '1')
|
|
||||||
return do_patch_bps(original, patch, offset);
|
|
||||||
else
|
|
||||||
throw std::runtime_error("Unknown patch file format");
|
|
||||||
}
|
|
|
@ -5,10 +5,10 @@
|
||||||
#include "core/framerate.hpp"
|
#include "core/framerate.hpp"
|
||||||
#include "core/memorymanip.hpp"
|
#include "core/memorymanip.hpp"
|
||||||
#include "core/misc.hpp"
|
#include "core/misc.hpp"
|
||||||
#include "core/patchrom.hpp"
|
|
||||||
#include "core/rom.hpp"
|
#include "core/rom.hpp"
|
||||||
#include "core/window.hpp"
|
#include "core/window.hpp"
|
||||||
#include "interface/core.hpp"
|
#include "interface/core.hpp"
|
||||||
|
#include "library/patchrom.hpp"
|
||||||
#include "library/sha256.hpp"
|
#include "library/sha256.hpp"
|
||||||
#include "library/string.hpp"
|
#include "library/string.hpp"
|
||||||
#include "library/zip.hpp"
|
#include "library/zip.hpp"
|
||||||
|
|
173
src/library/patchrom-bps.cpp
Normal file
173
src/library/patchrom-bps.cpp
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
#include "library/minmax.hpp"
|
||||||
|
#include "library/patchrom.hpp"
|
||||||
|
#include "library/serialization.hpp"
|
||||||
|
#include "library/string.hpp"
|
||||||
|
#include <cstdint>
|
||||||
|
#include <limits>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
uint8_t readbyte(const char* buf, uint64_t& pos, uint64_t size)
|
||||||
|
{
|
||||||
|
if(pos >= size)
|
||||||
|
(stringfmt() << "Attempted to read byte past the end of patch (" << pos << " >= "
|
||||||
|
<< size << ").").throwex();
|
||||||
|
return static_cast<uint8_t>(buf[pos++]);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t safe_add(uint64_t a, uint64_t b)
|
||||||
|
{
|
||||||
|
if(a + b < a)
|
||||||
|
(stringfmt() << "Integer overflow (" << a << " + " << b << ") processing patch.").throwex();
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t safe_sub(uint64_t a, uint64_t b)
|
||||||
|
{
|
||||||
|
if(a < b)
|
||||||
|
(stringfmt() << "Integer underflow (" << a << " - " << b << ") processing patch.").throwex();
|
||||||
|
return a - b;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t decode_varint(const char* buf, uint64_t& pos, uint64_t size)
|
||||||
|
{
|
||||||
|
uint64_t v = 0;
|
||||||
|
size_t i;
|
||||||
|
uint64_t y;
|
||||||
|
for(i = 0; i < 10; i++) {
|
||||||
|
y = readbyte(buf, pos, size) ^ 0x80;
|
||||||
|
v += (y << (7 * i));
|
||||||
|
if(i == 8 && (y | ((v >> 63) ^ 1)) == 255)
|
||||||
|
(stringfmt() << "Varint decoding overlows: v=" << v << " y=" << y << ".").throwex();
|
||||||
|
if(i == 9 && y > 0)
|
||||||
|
(stringfmt() << "Varint decoding overlows: v=" << v << " y=" << y << ".").throwex();
|
||||||
|
if(y < 128)
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct bps_patcher : public rom_patcher
|
||||||
|
{
|
||||||
|
~bps_patcher() throw();
|
||||||
|
bool identify(const std::vector<char>& patch) throw();
|
||||||
|
void dopatch(std::vector<char>& out, const std::vector<char>& original,
|
||||||
|
const std::vector<char>& patch, int32_t offset) throw(std::bad_alloc, std::runtime_error);
|
||||||
|
} bpspatch;
|
||||||
|
|
||||||
|
bps_patcher::~bps_patcher() throw()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bps_patcher::identify(const std::vector<char>& patch) throw()
|
||||||
|
{
|
||||||
|
return (patch.size() > 4 && patch[0] == 'B' && patch[1] == 'P' && patch[2] == 'S' && patch[3] == '1');
|
||||||
|
}
|
||||||
|
|
||||||
|
void bps_patcher::dopatch(std::vector<char>& out, const std::vector<char>& original,
|
||||||
|
const std::vector<char>& patch, int32_t offset) throw(std::bad_alloc, std::runtime_error)
|
||||||
|
{
|
||||||
|
if(offset)
|
||||||
|
(stringfmt() << "Nonzero offsets (" << offset << ") not allowed in BPS mode.").throwex();
|
||||||
|
if(patch.size() < 19)
|
||||||
|
(stringfmt() << "Patch is too masll to be valid BPS patch (" << patch.size()
|
||||||
|
<< " < 19).").throwex();
|
||||||
|
size_t ioffset = 4;
|
||||||
|
const char* _patch = &patch[0];
|
||||||
|
size_t psize = patch.size() - 12;
|
||||||
|
uint32_t crc_init = crc32(0, NULL, 0);
|
||||||
|
uint32_t pchcrc_c = crc32(crc_init, reinterpret_cast<const uint8_t*>(&patch[0]), patch.size() - 4);
|
||||||
|
uint32_t pchcrc = read32ule(_patch + psize + 8);
|
||||||
|
if(pchcrc_c != pchcrc)
|
||||||
|
(stringfmt() << "CRC mismatch on patch: Claimed: " << pchcrc << " Actual: " << pchcrc_c
|
||||||
|
<< ".").throwex();
|
||||||
|
uint32_t srccrc = read32ule(_patch + psize + 0);
|
||||||
|
uint32_t dstcrc = read32ule(_patch + psize + 4);
|
||||||
|
uint64_t srcsize = decode_varint(_patch, ioffset, psize);
|
||||||
|
uint64_t dstsize = decode_varint(_patch, ioffset, psize);
|
||||||
|
uint64_t mdtsize = decode_varint(_patch, ioffset, psize);
|
||||||
|
ioffset += mdtsize;
|
||||||
|
if(ioffset < mdtsize || ioffset > psize)
|
||||||
|
(stringfmt() << "Metadata size invalid: " << mdtsize << "@" << ioffset << ", plimit="
|
||||||
|
<< patch.size() << ".").throwex();
|
||||||
|
|
||||||
|
if(srcsize != original.size())
|
||||||
|
(stringfmt() << "Size mismatch on original: Claimed: " << srcsize << " Actual: "
|
||||||
|
<< original.size() << ".").throwex();
|
||||||
|
uint32_t srccrc_c = crc32(crc_init, reinterpret_cast<const uint8_t*>(&original[0]), original.size());
|
||||||
|
if(srccrc_c != srccrc)
|
||||||
|
(stringfmt() << "CRC mismatch on original: Claimed: " << srccrc << " Actual: " << srccrc_c
|
||||||
|
<< ".").throwex();
|
||||||
|
|
||||||
|
out.resize(dstsize);
|
||||||
|
uint64_t target_ptr = 0;
|
||||||
|
uint64_t source_rptr = 0;
|
||||||
|
uint64_t target_rptr = 0;
|
||||||
|
while(ioffset < psize) {
|
||||||
|
uint64_t opc = decode_varint(_patch, ioffset, psize);
|
||||||
|
uint64_t len = (opc >> 2) + 1;
|
||||||
|
uint64_t off = (opc & 2) ? decode_varint(_patch, ioffset, psize) : 0;
|
||||||
|
bool negative = ((off & 1) != 0);
|
||||||
|
off >>= 1;
|
||||||
|
if(safe_add(target_ptr, len) > dstsize)
|
||||||
|
(stringfmt() << "Illegal write: " << len << "@" << target_ptr << ", wlimit="
|
||||||
|
<< dstsize << ".").throwex();
|
||||||
|
const char* src;
|
||||||
|
size_t srcoffset;
|
||||||
|
size_t srclimit;
|
||||||
|
const char* msg;
|
||||||
|
switch(opc & 3) {
|
||||||
|
case 0:
|
||||||
|
src = &original[0];
|
||||||
|
srcoffset = target_ptr;
|
||||||
|
srclimit = srcsize;
|
||||||
|
msg = "source";
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
src = &patch[0];
|
||||||
|
srcoffset = ioffset;
|
||||||
|
srclimit = psize - 12;
|
||||||
|
ioffset += len;
|
||||||
|
msg = "patch";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if(negative)
|
||||||
|
source_rptr = safe_sub(source_rptr, off);
|
||||||
|
else
|
||||||
|
source_rptr = safe_add(source_rptr, off);
|
||||||
|
src = &original[0];
|
||||||
|
srcoffset = source_rptr;
|
||||||
|
srclimit = srcsize;
|
||||||
|
source_rptr += len;
|
||||||
|
msg = "source";
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if(negative)
|
||||||
|
target_rptr = safe_sub(target_rptr, off);
|
||||||
|
else
|
||||||
|
target_rptr = safe_add(target_rptr, off);
|
||||||
|
src = &out[0];
|
||||||
|
srcoffset = target_rptr;
|
||||||
|
srclimit = min(dstsize, target_rptr + len);
|
||||||
|
target_rptr += len;
|
||||||
|
msg = "target";
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
if(safe_add(srcoffset, len) > srclimit)
|
||||||
|
(stringfmt() << "Illegal read: " << len << "@" << srcoffset << " from " << msg
|
||||||
|
<< ", limit=" << srclimit << ".").throwex();
|
||||||
|
for(uint64_t i = 0; i < len; i++)
|
||||||
|
out[target_ptr + i] = src[srcoffset + i];
|
||||||
|
target_ptr += len;
|
||||||
|
}
|
||||||
|
if(target_ptr != out.size())
|
||||||
|
(stringfmt() << "Size mismatch on result: Claimed: " << out.size() << " Actual: "
|
||||||
|
<< target_ptr << ".").throwex();
|
||||||
|
uint32_t dstcrc_c = crc32(crc_init, reinterpret_cast<const uint8_t*>(&out[0]), out.size());
|
||||||
|
if(dstcrc_c != dstcrc)
|
||||||
|
(stringfmt() << "CRC mismatch on result: Claimed: " << dstcrc << " Actual: " << dstcrc_c
|
||||||
|
<< ".").throwex();
|
||||||
|
}
|
||||||
|
}
|
87
src/library/patchrom-ips.cpp
Normal file
87
src/library/patchrom-ips.cpp
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
#include "library/minmax.hpp"
|
||||||
|
#include "library/patchrom.hpp"
|
||||||
|
#include "library/serialization.hpp"
|
||||||
|
#include "library/string.hpp"
|
||||||
|
#include <cstdint>
|
||||||
|
#include <limits>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
uint8_t readbyte(const char* buf, uint64_t& pos, uint64_t size)
|
||||||
|
{
|
||||||
|
if(pos >= size)
|
||||||
|
(stringfmt() << "Attempted to read byte past the end of patch (" << pos << " >= "
|
||||||
|
<< size << ").").throwex();
|
||||||
|
return static_cast<uint8_t>(buf[pos++]);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ips_patcher : public rom_patcher
|
||||||
|
{
|
||||||
|
~ips_patcher() throw();
|
||||||
|
bool identify(const std::vector<char>& patch) throw();
|
||||||
|
void dopatch(std::vector<char>& out, const std::vector<char>& original,
|
||||||
|
const std::vector<char>& patch, int32_t offset) throw(std::bad_alloc, std::runtime_error);
|
||||||
|
} ipspatch;
|
||||||
|
|
||||||
|
ips_patcher::~ips_patcher() throw()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ips_patcher::identify(const std::vector<char>& patch) throw()
|
||||||
|
{
|
||||||
|
return (patch.size() > 5 && patch[0] == 'P' && patch[1] == 'A' && patch[2] == 'T' && patch[3] == 'C' &&
|
||||||
|
patch[4] == 'H');
|
||||||
|
}
|
||||||
|
|
||||||
|
void ips_patcher::dopatch(std::vector<char>& out, const std::vector<char>& original,
|
||||||
|
const std::vector<char>& patch, int32_t offset) throw(std::bad_alloc, std::runtime_error)
|
||||||
|
{
|
||||||
|
//Initial guess.
|
||||||
|
out = original;
|
||||||
|
const char* _patch = &patch[0];
|
||||||
|
size_t psize = patch.size();
|
||||||
|
|
||||||
|
uint64_t ioffset = 5;
|
||||||
|
while(true) {
|
||||||
|
bool rle = false;
|
||||||
|
size_t left = patch.size() - ioffset;
|
||||||
|
uint8_t b;
|
||||||
|
uint32_t off = 0, l = 0;
|
||||||
|
off |= static_cast<uint32_t>(readbyte(_patch, ioffset, psize)) << 16;
|
||||||
|
off |= static_cast<uint32_t>(readbyte(_patch, ioffset, psize)) << 8;
|
||||||
|
off |= static_cast<uint32_t>(readbyte(_patch, ioffset, psize));
|
||||||
|
if(off == 0x454F46)
|
||||||
|
break; //EOF code.
|
||||||
|
l |= static_cast<uint32_t>(readbyte(_patch, ioffset, psize)) << 8;
|
||||||
|
l |= static_cast<uint32_t>(readbyte(_patch, ioffset, psize));
|
||||||
|
if(l == 0) {
|
||||||
|
//RLE.
|
||||||
|
l |= static_cast<uint32_t>(readbyte(_patch, ioffset, psize)) << 8;
|
||||||
|
l |= static_cast<uint32_t>(readbyte(_patch, ioffset, psize));
|
||||||
|
b = readbyte(_patch, ioffset, psize);
|
||||||
|
rle = true;
|
||||||
|
}
|
||||||
|
uint64_t extra = 0;
|
||||||
|
if(offset >= 0)
|
||||||
|
off += offset;
|
||||||
|
else {
|
||||||
|
uint32_t noffset = static_cast<uint32_t>(-offset);
|
||||||
|
uint32_t fromoff = min(noffset, off);
|
||||||
|
off -= fromoff;
|
||||||
|
extra = min(noffset - fromoff, l);
|
||||||
|
l -= extra;
|
||||||
|
}
|
||||||
|
if(off + l >= out.size())
|
||||||
|
out.resize(off + l);
|
||||||
|
if(!rle) {
|
||||||
|
ioffset += extra;
|
||||||
|
for(uint64_t i = 0; i < l; i++)
|
||||||
|
out[off + i] = readbyte(_patch, ioffset, psize);
|
||||||
|
} else
|
||||||
|
for(uint64_t i = 0; i < l; i++)
|
||||||
|
out[off + i] = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
src/library/patchrom.cpp
Normal file
37
src/library/patchrom.cpp
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#include "library/patchrom.hpp"
|
||||||
|
#include "library/string.hpp"
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::set<rom_patcher*>& patchers()
|
||||||
|
{
|
||||||
|
static std::set<rom_patcher*> t;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<char> do_patch_file(const std::vector<char>& original, const std::vector<char>& patch,
|
||||||
|
int32_t offset) throw(std::bad_alloc, std::runtime_error)
|
||||||
|
{
|
||||||
|
std::vector<char> out;
|
||||||
|
for(auto i : patchers())
|
||||||
|
if(i->identify(patch)) {
|
||||||
|
i->dopatch(out, original, patch, offset);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Unknown patch file format");
|
||||||
|
}
|
||||||
|
|
||||||
|
rom_patcher::rom_patcher() throw(std::bad_alloc)
|
||||||
|
{
|
||||||
|
patchers().insert(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
rom_patcher::~rom_patcher() throw()
|
||||||
|
{
|
||||||
|
patchers().erase(this);
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue