106 if (
auto order = CheckOrder(replay); !order)
107 return std::unexpected(order.error());
109 const Header header{.world =
static_cast<uint32_t
>(replay.
world),
114 .commandCount = replay.
commands.size(),
117 const auto total = ExpectedBytes(header);
119 return std::unexpected(total.error());
121 std::vector<std::byte> bytes;
122 bytes.reserve(*total);
123 Append(bytes, std::as_bytes(std::span(&header, 1)));
125 bytes.resize(HEADER_BYTES + AlignUp(replay.
initialDelta.size()), std::byte{0});
126 Append(bytes, std::as_bytes(std::span(replay.
commands)));
129 const std::array<uint64_t, 2> words = {entry.
tick, entry.
hash};
130 Append(bytes, std::as_bytes(std::span(words)));
151 std::expected<ReplayFile, std::string>
ParseReplay(std::span<const std::byte> bytes) {
153 if (bytes.size() < HEADER_BYTES)
154 return std::unexpected(
"再生ファイルが見出しより短い");
156 std::memcpy(&header, bytes.data(), HEADER_BYTES);
157 if (header.magic != MAGIC)
158 return std::unexpected(
"再生ファイルではない(先頭が BCRP でない)");
161 return std::unexpected(std::format(
"再生ファイルの形式の版 {} は読めない(読めるのは {})",
166 return std::unexpected(std::format(
"知らない世界の種類 {}", header.world));
168 const auto total = ExpectedBytes(header);
170 return std::unexpected(total.error());
172 if (*total != bytes.size())
173 return std::unexpected(
174 std::format(
"再生ファイルの大きさが合わない: 見出しから {} 実際 {}", *total, bytes.size()));
177 .tableVersion = header.tableVersion,
179 .startTick = header.startTick};
182 replay.
commands.resize(header.commandCount);
183 std::vector<std::array<uint64_t, 2>> hashes(header.hashCount);
184 uint64_t offset = HEADER_BYTES;
186 offset += AlignUp(header.deltaBytes);
187 CopyOut(bytes, offset, std::span(replay.
commands));
189 CopyOut(bytes, offset, std::span(hashes));
191 for (
const auto& [tick, hash] : hashes)
192 replay.
tickHashes.push_back({.tick = tick, .hash = hash});
194 if (
auto order = CheckOrder(replay); !order)
195 return std::unexpected(order.error());
201 std::ifstream file(path, std::ios::binary | std::ios::ate);
203 return std::unexpected(std::format(
"再生ファイルを開けない: {}", PathText(path)));
205 const std::streamoff size = file.tellg();
206 if (size < 0 ||
static_cast<uint64_t
>(size) > MAX_FILE_BYTES)
207 return std::unexpected(std::format(
"再生ファイルの大きさが扱えない: {}", PathText(path)));
209 std::vector<std::byte> bytes(
static_cast<size_t>(size));
211 file.read(
reinterpret_cast<char*
>(bytes.data()), size);
213 return std::unexpected(std::format(
"再生ファイルを読めない: {}", PathText(path)));