Get rid of nall
If not using bsnes core, nall is not available, so don't use it.
This commit is contained in:
parent
008536d21c
commit
3a7be1738a
9 changed files with 340 additions and 235 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -10,5 +10,6 @@ lsnes
|
|||
*.util
|
||||
/core
|
||||
/bsnes
|
||||
/gambatte
|
||||
src/fonts/font.cpp
|
||||
src/core/version.cpp
|
||||
|
|
|
@ -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
|
40
include/library/patch.hpp
Normal file
40
include/library/patch.hpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
#ifndef _slot__hpp__included__
|
||||
#define _slot__hpp__included__
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <stdexcept>
|
||||
|
||||
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
|
|
@ -11,22 +11,6 @@
|
|||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <cstdint>
|
||||
/*
|
||||
typedef uint8_t uint8;
|
||||
typedef uint16_t uint16;
|
||||
typedef uint32_t uint32;
|
||||
typedef int8_t int8;
|
||||
typedef int16_t int16;
|
||||
typedef int32_t int32;
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/endian.hpp>
|
||||
#include <nall/varint.hpp>
|
||||
#include <nall/bit.hpp>
|
||||
#include <nall/serializer.hpp>
|
||||
#include <nall/property.hpp>
|
||||
using namespace nall;
|
||||
using namespace SNES;
|
||||
*/
|
||||
|
||||
namespace
|
||||
{
|
||||
|
|
|
@ -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");
|
||||
}
|
|
@ -7,9 +7,9 @@
|
|||
#include "core/mainloop.hpp"
|
||||
#include "core/memorymanip.hpp"
|
||||
#include "core/misc.hpp"
|
||||
#include "core/patchrom.hpp"
|
||||
#include "core/rom.hpp"
|
||||
#include "core/window.hpp"
|
||||
#include "library/patch.hpp"
|
||||
#include "library/sha256.hpp"
|
||||
#include "library/string.hpp"
|
||||
#include "library/zip.hpp"
|
||||
|
|
173
src/library/patch-bps.cpp
Normal file
173
src/library/patch-bps.cpp
Normal file
|
@ -0,0 +1,173 @@
|
|||
#include "library/minmax.hpp"
|
||||
#include "library/patch.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();
|
||||
uint64_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/patch-ips.cpp
Normal file
87
src/library/patch-ips.cpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
#include "library/minmax.hpp"
|
||||
#include "library/patch.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;
|
||||
}
|
||||
}
|
||||
}
|
38
src/library/patch.cpp
Normal file
38
src/library/patch.cpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
#include "library/patch.hpp"
|
||||
#include "library/sha256.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