Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
LegacyChunk.cpp
Go to the documentation of this file.
1#include "LegacyChunk.hpp"
2
3#include <algorithm>
4
5namespace monopoly::data
6{
7 namespace
8 {
9 [[nodiscard]] std::uint32_t readChunkSize(
10 const std::byte* bytes) noexcept
11 {
12 // Representation Win32 prouvee du bitfield source : les 24 bits
13 // bas portent la taille totale, le dernier octet porte l'ID.
14 return
15 static_cast<std::uint32_t>(
16 std::to_integer<std::uint8_t>(bytes[0])) |
17 (static_cast<std::uint32_t>(
18 std::to_integer<std::uint8_t>(bytes[1])) << 8U) |
19 (static_cast<std::uint32_t>(
20 std::to_integer<std::uint8_t>(bytes[2])) << 16U);
21 }
22 }
23
24
25 std::string_view chunkErrorCodeName(ChunkErrorCode code) noexcept
26 {
27 switch (code)
28 {
29 case ChunkErrorCode::None: return "None";
30 case ChunkErrorCode::EndOfParent: return "EndOfParent";
31 case ChunkErrorCode::ChunkNotFound: return "ChunkNotFound";
32 case ChunkErrorCode::HeaderTruncated: return "HeaderTruncated";
33 case ChunkErrorCode::InvalidSize: return "InvalidSize";
34 case ChunkErrorCode::ChunkPastParent: return "ChunkPastParent";
35 case ChunkErrorCode::InvalidId: return "InvalidId";
36 case ChunkErrorCode::MaximumDepth: return "MaximumDepth";
37 case ChunkErrorCode::AlreadyAtRoot: return "AlreadyAtRoot";
38 case ChunkErrorCode::InvalidLevel: return "InvalidLevel";
40 return "PositionOutOfRange";
41 }
42
43 return "InvalidChunkErrorCode";
44 }
45
46
48 std::span<const std::byte> bytes) noexcept
49 : bytes_(bytes)
50 {
51 levels_[0] = { 0, bytes_.size(), NullStandardId };
52 }
53
54
56 : owner_(std::move(bytes)),
57 bytes_(owner_ ? std::span<const std::byte>(*owner_) :
58 std::span<const std::byte>{})
59 {
60 levels_[0] = { 0, bytes_.size(), NullStandardId };
61 }
62
63
64 ChunkError LegacyChunkReader::error(
65 ChunkErrorCode code,
66 std::string detail,
67 std::size_t offset) const
68 {
69 return { code, offset, std::move(detail) };
70 }
71
72
73 std::expected<ChunkInfo, ChunkError> LegacyChunkReader::descend(
74 std::uint8_t findId)
75 {
76 if (level_ + 1 >= MaximumLevels)
77 {
78 return std::unexpected(error(
80 "legacy chunk nesting cannot exceed seven child levels",
81 currentOffset_));
82 }
83
84 const std::size_t parentEnd = levels_[level_].end;
85 std::size_t candidateOffset = currentOffset_;
86
88 {
89 const std::size_t bytesRemaining = parentEnd - candidateOffset;
90
92 {
93 return std::unexpected(error(
95 "fewer than four bytes remain for a chunk header",
97 }
98
99 const auto* header = bytes_.data() + candidateOffset;
100 const std::uint32_t totalSize = readChunkSize(header);
101 const std::uint8_t id =
102 std::to_integer<std::uint8_t>(header[3]);
103
105 {
106 return std::unexpected(error(
108 "chunk size includes its header and cannot be below four",
110 }
111
112 if (static_cast<std::size_t>(totalSize) > bytesRemaining)
113 {
114 return std::unexpected(error(
116 "chunk extends beyond its parent boundary",
118 }
119
120 const std::size_t chunkEnd = candidateOffset + totalSize;
121
122 if (findId == NullStandardId || findId == id)
123 {
124 // LE_CHUNK_Descend ne testait le sentinel nul qu'apres avoir
125 // trouve le chunk demande. Une recherche par ID peut donc
126 // franchir un sibling 0/128 bien forme; descend() (next) ou
127 // une recherche explicite de 128 doivent en revanche echouer.
128 if ((id & 0x7FU) == 0)
129 {
130 return std::unexpected(error(
132 "selected chunk ID 0 or 128 is a null sentinel",
134 }
135
136 ++level_;
137 const std::size_t dataStart =
139 levels_[level_] = { dataStart, chunkEnd, id };
140 currentOffset_ = dataStart;
141
142 return ChunkInfo
143 {
144 id,
146 dataStart,
147 static_cast<std::size_t>(totalSize) -
150 };
151 }
152
154 currentOffset_ = candidateOffset;
155 }
156
157 return std::unexpected(error(
162 "no next child chunk remains" :
163 "requested chunk ID was not found among remaining siblings",
164 currentOffset_));
165 }
166
167
168 std::expected<void, ChunkError> LegacyChunkReader::ascend()
169 {
170 if (level_ == 0)
171 {
172 return std::unexpected(error(
174 "cannot ascend above the root byte container",
175 currentOffset_));
176 }
177
178 currentOffset_ = levels_[level_].end;
179 --level_;
180 return {};
181 }
182
183
184 std::expected<std::span<const std::byte>, ChunkError>
186 {
187 const std::size_t amount = std::min(requestedBytes, remaining());
188 const auto mapped = bytes_.subspan(currentOffset_, amount);
189 currentOffset_ += amount;
190 return mapped;
191 }
192
193
194 std::expected<std::size_t, ChunkError> LegacyChunkReader::read(
195 std::span<std::byte> destination)
196 {
197 auto mapped = map(destination.size());
198
199 if (!mapped)
200 {
201 return std::unexpected(mapped.error());
202 }
203
204 std::copy(mapped->begin(), mapped->end(), destination.begin());
205 return mapped->size();
206 }
207
208
209 std::expected<void, ChunkError> LegacyChunkReader::seek(
210 std::size_t absoluteOffset)
211 {
212 const auto& currentLevel = levels_[level_];
213
214 if (absoluteOffset < currentLevel.dataStart ||
216 {
217 return std::unexpected(error(
219 "position must remain inside current chunk data",
221 }
222
223 currentOffset_ = absoluteOffset;
224 return {};
225 }
226
227
229 {
230 return currentOffset_;
231 }
232
233
235 {
236 return level_;
237 }
238
239
241 std::size_t level) const noexcept
242 {
243 return level <= level_ ? levels_[level].id : NullStandardId;
244 }
245
246
248 std::size_t level) const noexcept
249 {
250 return level <= level_ ?
251 levels_[level].end - levels_[level].dataStart : 0;
252 }
253
254
256 std::size_t level) const noexcept
257 {
258 return level <= level_ ? levels_[level].dataStart : 0;
259 }
260
261
263 {
264 return levels_[level_].end - currentOffset_;
265 }
266
267
268 std::expected<LegacyChunkReader, DataError> openLegacyChunkReader(
269 const DataSource& source,
270 DataId id)
271 {
272 auto metadata = source.metadata(id);
273
274 if (!metadata)
275 {
276 return std::unexpected(metadata.error());
277 }
278
279 if (metadata->type != LegacyDataType::Chunky)
280 {
281 return std::unexpected(DataError
282 {
284 {},
285 dataTag(id),
286 "LE_CHUNK_ReadFromDataID requires a DataChunky item"
287 });
288 }
289
290 auto bytes = source.load(id);
291
292 if (!bytes)
293 {
294 return std::unexpected(bytes.error());
295 }
296
297 return LegacyChunkReader(std::move(*bytes));
298 }
299}
std::size_t offset
const char * header
LegacyBitmapMetadata metadata
std::vector< std::byte > bytes
data::DataId id
std::shared_ptr< const data::HmdTextureImage > source
unsigned index
std::string error
std::expected< std::size_t, ChunkError > read(std::span< std::byte > destination)
std::uint8_t idForLevel(std::size_t level) const noexcept
std::size_t level() const noexcept
static constexpr std::uint8_t NullStandardId
std::expected< std::span< const std::byte >, ChunkError > map(std::size_t requestedBytes)
static constexpr std::size_t PhysicalHeaderSize
std::size_t dataSizeForLevel(std::size_t level) const noexcept
LegacyChunkReader(std::span< const std::byte > bytes) noexcept
std::size_t dataStartForLevel(std::size_t level) const noexcept
std::expected< void, ChunkError > ascend()
std::expected< ChunkInfo, ChunkError > descend(std::uint8_t findId=NullStandardId)
std::expected< void, ChunkError > seek(std::size_t absoluteOffset)
std::size_t remaining() const noexcept
static constexpr std::size_t MaximumLevels
std::size_t currentOffset() const noexcept
std::uint32_t DataId
Definition DataBanks.hpp:10
constexpr DataTag dataTag(DataId id) noexcept
Definition DataBanks.hpp:64
std::string_view chunkErrorCodeName(ChunkErrorCode code) noexcept
std::shared_ptr< const DataBytes > SharedDataBytes
std::expected< LegacyChunkReader, DataError > openLegacyChunkReader(const DataSource &source, DataId id)