Get rid of nall

If not using bsnes core, nall is not available, so don't use it.
This commit is contained in:
Ilari Liusvaara 2012-07-11 15:50:05 +03:00
parent 008536d21c
commit 3a7be1738a
9 changed files with 340 additions and 235 deletions

1
.gitignore vendored
View file

@ -10,5 +10,6 @@ lsnes
*.util
/core
/bsnes
/gambatte
src/fonts/font.cpp
src/core/version.cpp

View file

@ -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
View 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

View file

@ -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
{

View file

@ -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");
}

View file

@ -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
View 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
View 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
View 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);
}