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