openMSX
IPSPatch.cc
Go to the documentation of this file.
1 #include "IPSPatch.hh"
2 #include "File.hh"
3 #include "Filename.hh"
4 #include "MSXException.hh"
5 #include "ranges.hh"
6 #include "stl.hh"
7 #include <cstring>
8 #include <cassert>
9 
10 using std::vector;
11 
12 namespace openmsx {
13 
14 static size_t getStop(const IPSPatch::PatchMap::const_iterator& it)
15 {
16  return it->first + it->second.size();
17 }
18 
20  std::unique_ptr<const PatchInterface> parent_)
21  : filename(std::move(filename_))
22  , parent(std::move(parent_))
23 {
24  File ipsFile(filename);
25 
26  byte buf[5];
27  ipsFile.read(buf, 5);
28  if (memcmp(buf, "PATCH", 5) != 0) {
29  throw MSXException("Invalid IPS file: ", filename.getOriginal());
30  }
31  ipsFile.read(buf, 3);
32  while (memcmp(buf, "EOF", 3) != 0) {
33  size_t offset = 0x10000 * buf[0] + 0x100 * buf[1] + buf[2];
34  ipsFile.read(buf, 2);
35  size_t length = 0x100 * buf[0] + buf[1];
36  vector<byte> v;
37  if (length == 0) {
38  // RLE encoded
39  ipsFile.read(buf, 3);
40  length = 0x100 * buf[0] + buf[1];
41  v.resize(length, buf[2]);
42  } else {
43  // patch bytes
44  v.resize(length);
45  ipsFile.read(&v.front(), length);
46  }
47  // find overlapping or adjacent patch regions
48  auto b = ranges::lower_bound(patchMap, offset, LessTupleElement<0>());
49  if (b != begin(patchMap)) {
50  --b;
51  if (getStop(b) < offset) ++b;
52  }
53  auto e = ranges::upper_bound(patchMap, offset + v.size(), LessTupleElement<0>());
54  if (b != e) {
55  // remove operlapping regions, merge adjacent regions
56  --e;
57  auto start = std::min(b->first, offset);
58  auto stop = std::max(offset + length, getStop(e));
59  auto length2 = stop - start;
60  ++e;
61  vector<byte> tmp(length2);
62  for (auto it = b; it != e; ++it) {
63  memcpy(&tmp[it->first - start], &it->second[0],
64  it->second.size());
65  }
66  memcpy(&tmp[offset - start], v.data(), v.size());
67  *b = std::make_pair(start, std::move(tmp));
68  patchMap.erase(b + 1, e);
69  } else {
70  // add new region
71  patchMap.emplace(b, offset, std::move(v));
72  }
73 
74  ipsFile.read(buf, 3);
75  }
76  if (patchMap.empty()) {
77  size = parent->getSize();
78  } else {
79  auto it = --end(patchMap);
80  size = std::max(parent->getSize(), getStop(it));
81  }
82 }
83 
84 void IPSPatch::copyBlock(size_t src, byte* dst, size_t num) const
85 {
86  parent->copyBlock(src, dst, num);
87 
88  auto b = ranges::lower_bound(patchMap, src, LessTupleElement<0>());
89  if (b != begin(patchMap)) --b;
90  auto e = ranges::upper_bound(patchMap, src + num - 1, LessTupleElement<0>());
91  for (auto it = b; it != e; ++it) {
92  auto chunkStart = it->first;
93  int chunkSize = int(it->second.size());
94  // calc chunkOffset, chunkStart
95  int chunkOffset = int(src - chunkStart);
96  if (chunkOffset < 0) {
97  // start after src
98  assert(-chunkOffset < int(num)); // dont start past end
99  chunkOffset = 0;
100  } else if (chunkOffset >= chunkSize) {
101  // chunk completely before src, skip
102  continue;
103  } else {
104  // start before src
105  chunkSize -= chunkOffset;
106  chunkStart += chunkOffset;
107  }
108  // calc chuncksize
109  assert(src <= chunkStart);
110  int overflow = int(chunkStart - src + chunkSize - num);
111  if (overflow > 0) {
112  assert(chunkSize > overflow);
113  chunkSize -= overflow;
114  }
115  // copy
116  assert(chunkOffset < int(it->second.size()));
117  assert((chunkOffset + chunkSize) <= int(it->second.size()));
118  assert(src <= chunkStart);
119  assert((chunkStart + chunkSize) <= (src + num));
120  memcpy(dst + chunkStart - src, &it->second[chunkOffset],
121  chunkSize);
122  }
123 }
124 
125 size_t IPSPatch::getSize() const
126 {
127  return size;
128 }
129 
130 std::vector<Filename> IPSPatch::getFilenames() const
131 {
132  auto result = parent->getFilenames();
133  result.push_back(filename);
134  return result;
135 }
136 
137 } // namespace openmsx
T length(const vecN< N, T > &x)
Definition: gl_vec.hh:343
T length2(const vecN< N, T > &x)
Definition: gl_vec.hh:336
vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:269
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
STL namespace.
vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:287
auto upper_bound(ForwardRange &&range, const T &value)
Definition: ranges.hh:83
auto begin(const string_view &x)
Definition: string_view.hh:151
const std::string & getOriginal() const
Definition: Filename.hh:26
This class represents a filename.
Definition: Filename.hh:17
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
void copyBlock(size_t src, byte *dst, size_t num) const override
Definition: IPSPatch.cc:84
IPSPatch(Filename filename, std::unique_ptr< const PatchInterface > parent)
Definition: IPSPatch.cc:19
auto lower_bound(ForwardRange &&range, const T &value)
Definition: ranges.hh:71
size_t getSize() const override
Definition: IPSPatch.cc:125
void read(void *buffer, size_t num)
Read from file.
Definition: File.cc:83
auto end(const string_view &x)
Definition: string_view.hh:152
std::vector< Filename > getFilenames() const override
Definition: IPSPatch.cc:130