Lua: Don't lua_error() out of context with pending dtors
[lsnes.git] / src / library / fileimage-patch-ips.cpp
blob244c1dc82c29dc8618485f3553d8d5fd69d3dbf5
1 #include "minmax.hpp"
2 #include "fileimage-patch.hpp"
3 #include "serialization.hpp"
4 #include "string.hpp"
5 #include <cstdint>
6 #include <limits>
7 #include <cstring>
8 #include <iostream>
10 namespace fileimage
12 namespace
14 uint8_t readbyte(const char* buf, uint64_t& pos, uint64_t size)
16 if(pos >= size)
17 (stringfmt() << "Attempted to read byte past the end of patch (" << pos << " >= "
18 << size << ").").throwex();
19 return static_cast<uint8_t>(buf[pos++]);
22 struct ips_patcher : public patcher
24 ~ips_patcher() throw();
25 bool identify(const std::vector<char>& patch) throw();
26 void dopatch(std::vector<char>& out, const std::vector<char>& original,
27 const std::vector<char>& patch, int32_t offset) throw(std::bad_alloc, std::runtime_error);
28 } ipspatch;
30 ips_patcher::~ips_patcher() throw()
34 bool ips_patcher::identify(const std::vector<char>& patch) throw()
36 return (patch.size() > 5 && patch[0] == 'P' && patch[1] == 'A' && patch[2] == 'T' &&
37 patch[3] == 'C' && patch[4] == 'H');
40 void ips_patcher::dopatch(std::vector<char>& out, const std::vector<char>& original,
41 const std::vector<char>& patch, int32_t offset) throw(std::bad_alloc, std::runtime_error)
43 //Initial guess.
44 out = original;
45 const char* _patch = &patch[0];
46 size_t psize = patch.size();
48 uint64_t ioffset = 5;
49 while(true) {
50 bool rle = false;
51 uint8_t b;
52 uint32_t off = 0, l = 0;
53 off |= static_cast<uint32_t>(readbyte(_patch, ioffset, psize)) << 16;
54 off |= static_cast<uint32_t>(readbyte(_patch, ioffset, psize)) << 8;
55 off |= static_cast<uint32_t>(readbyte(_patch, ioffset, psize));
56 if(off == 0x454F46)
57 break; //EOF code.
58 l |= static_cast<uint32_t>(readbyte(_patch, ioffset, psize)) << 8;
59 l |= static_cast<uint32_t>(readbyte(_patch, ioffset, psize));
60 if(l == 0) {
61 //RLE.
62 l |= static_cast<uint32_t>(readbyte(_patch, ioffset, psize)) << 8;
63 l |= static_cast<uint32_t>(readbyte(_patch, ioffset, psize));
64 b = readbyte(_patch, ioffset, psize);
65 rle = true;
67 uint64_t extra = 0;
68 if(offset >= 0)
69 off += offset;
70 else {
71 uint32_t noffset = static_cast<uint32_t>(-offset);
72 uint32_t fromoff = min(noffset, off);
73 off -= fromoff;
74 extra = min(noffset - fromoff, l);
75 l -= extra;
77 if(off + l >= out.size())
78 out.resize(off + l);
79 if(!rle) {
80 ioffset += extra;
81 for(uint64_t i = 0; i < l; i++)
82 out[off + i] = readbyte(_patch, ioffset, psize);
83 } else
84 for(uint64_t i = 0; i < l; i++)
85 out[off + i] = b;