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