feat: add verifyChunkPayloadChecksum and saveCoordLess to SaveMigration
This commit is contained in:
1 parent
6ffb347552
commit
12f0549bbb
1 file changed
+155
@@ -313,3 +313,158 @@ bool decodeChunkPayload(SaveVersion version, const uint8_t* data, std::size_t si
|
||||
out.empty = false;
|
||||
out.meta = SaveChunkMeta{};
|
||||
|
||||
if (version == SaveVersion::V1) {
|
||||
PayloadCursor cursor{data, size};
|
||||
uint32_t blockBytes = cursor.u32();
|
||||
if (cursor.bad || blockBytes != static_cast<uint32_t>(chunkVolume)) return false;
|
||||
std::vector<uint8_t> raw;
|
||||
if (!cursor.take(blockBytes, raw)) return false;
|
||||
std::array<Block, chunkVolume>& blocks = out.blocks.raw();
|
||||
for (std::size_t i = 0; i < blocks.size(); ++i) blocks[i] = static_cast<Block>(raw[i]);
|
||||
out.empty = chunkIsAllAir(out.blocks);
|
||||
if (info) info->rawBodyBytes = size;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (version == SaveVersion::V2 || version == SaveVersion::V3) {
|
||||
PayloadCursor cursor{data, size};
|
||||
uint32_t paletteCount = cursor.u32();
|
||||
if (cursor.bad || paletteCount == 0 || paletteCount > 4096u) return false;
|
||||
std::vector<uint8_t> paletteBytes;
|
||||
if (!cursor.take(paletteCount, paletteBytes)) return false;
|
||||
uint32_t indexBytes = cursor.u32();
|
||||
if (cursor.bad || indexBytes != static_cast<uint32_t>(chunkVolume)) return false;
|
||||
std::vector<uint8_t> indices;
|
||||
if (!cursor.take(indexBytes, indices)) return false;
|
||||
|
||||
uint32_t maxIndex = 0;
|
||||
std::array<Block, chunkVolume>& blocks = out.blocks.raw();
|
||||
for (std::size_t i = 0; i < blocks.size(); ++i) {
|
||||
uint32_t index = indices[i];
|
||||
if (index > maxIndex) maxIndex = index;
|
||||
blocks[i] = index < paletteCount ? static_cast<Block>(paletteBytes[index]) : Block::Air;
|
||||
}
|
||||
uint32_t lightBytes = 0;
|
||||
if (version == SaveVersion::V3) {
|
||||
lightBytes = cursor.u32();
|
||||
if (cursor.bad || lightBytes > static_cast<uint32_t>(chunkVolume)) return false;
|
||||
if (!cursor.take(lightBytes, out.light)) return false;
|
||||
if (!cursor.skip(4)) return false;
|
||||
}
|
||||
out.empty = chunkIsAllAir(out.blocks);
|
||||
if (info) {
|
||||
info->paletteCount = paletteCount;
|
||||
info->maxIndex = maxIndex;
|
||||
info->bitWidth = 8;
|
||||
info->lightBytes = lightBytes;
|
||||
info->rawBodyBytes = size;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
PayloadCursor cursor{data, size};
|
||||
uint32_t flags = cursor.u32();
|
||||
uint32_t storedBytes = cursor.u32();
|
||||
if (cursor.bad || storedBytes > size) return false;
|
||||
std::vector<uint8_t> stored;
|
||||
if (!cursor.take(storedBytes, stored)) return false;
|
||||
if (!cursor.skip(4)) return false;
|
||||
|
||||
ByteBuffer body;
|
||||
if ((flags & saveRecordCompressed) != 0) {
|
||||
if (!decompress(stored.data(), stored.size(), body)) return false;
|
||||
} else {
|
||||
body = std::move(stored);
|
||||
}
|
||||
if (!decodeBodyV4(body.data(), body.size(), out, info)) return false;
|
||||
out.empty = (flags & saveRecordEmpty) != 0;
|
||||
if (info) {
|
||||
info->compressed = (flags & saveRecordCompressed) != 0;
|
||||
info->empty = out.empty;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool verifyChunkPayloadChecksum(SaveVersion version, const uint8_t* data, std::size_t size,
|
||||
uint32_t& stored, uint32_t& computed) {
|
||||
if (!versionHasChecksum(version)) return false;
|
||||
if (version == SaveVersion::V3) {
|
||||
if (size < 4) return false;
|
||||
stored = readU32Be(data + size - 4);
|
||||
computed = fnv1a32(data, size - 4);
|
||||
return true;
|
||||
}
|
||||
if (size < 12) return false;
|
||||
uint32_t flags = readU32Be(data + 0);
|
||||
uint32_t storedBytes = readU32Be(data + 4);
|
||||
if (static_cast<std::size_t>(storedBytes) + 12 > size) return false;
|
||||
stored = readU32Be(data + 8 + storedBytes);
|
||||
if ((flags & saveRecordCompressed) != 0) {
|
||||
ByteBuffer body;
|
||||
if (!decompress(data + 8, storedBytes, body)) return false;
|
||||
computed = fnv1a32(body.data(), body.size());
|
||||
} else {
|
||||
computed = fnv1a32(data + 8, storedBytes);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool saveCoordLess(const Vec3i& a, const Vec3i& b) {
|
||||
if (a.x != b.x) return a.x < b.x;
|
||||
if (a.y != b.y) return a.y < b.y;
|
||||
return a.z < b.z;
|
||||
}
|
||||
|
||||
ByteBuffer packSaveContainer(SaveVersion version, int regionX, int regionZ, uint32_t sectorBytes,
|
||||
const std::vector<Vec3i>& coords,
|
||||
const std::vector<ByteBuffer>& payloads,
|
||||
std::vector<uint32_t>* sectorOffsets) {
|
||||
uint32_t sectors = sectorBytes == 0 ? saveSectorBytes : sectorBytes;
|
||||
|
||||
SaveHeader header;
|
||||
header.magic = saveMagic;
|
||||
header.version = static_cast<uint32_t>(version);
|
||||
header.regionX = regionX;
|
||||
header.regionZ = regionZ;
|
||||
header.chunkCount = static_cast<uint32_t>(coords.size());
|
||||
header.sectorBytes = sectors;
|
||||
|
||||
std::size_t sectorCursor = saveHeaderSectors(header);
|
||||
if (sectorOffsets) sectorOffsets->clear();
|
||||
|
||||
ByteBuffer table;
|
||||
table.reserve(coords.size() * saveEntryBytes);
|
||||
std::vector<std::size_t> starts;
|
||||
starts.reserve(coords.size());
|
||||
for (std::size_t i = 0; i < coords.size(); ++i) {
|
||||
uint32_t sectorCount = static_cast<uint32_t>(divideUp(payloads[i].size(), sectors));
|
||||
putU32Be(table, static_cast<uint32_t>(coords[i].x));
|
||||
putU32Be(table, static_cast<uint32_t>(coords[i].y));
|
||||
putU32Be(table, static_cast<uint32_t>(coords[i].z));
|
||||
putU32Be(table, static_cast<uint32_t>(sectorCursor));
|
||||
putU32Be(table, sectorCount);
|
||||
putU32Be(table, static_cast<uint32_t>(payloads[i].size()));
|
||||
if (sectorOffsets) sectorOffsets->push_back(static_cast<uint32_t>(sectorCursor));
|
||||
starts.push_back(sectorCursor * static_cast<std::size_t>(sectors));
|
||||
sectorCursor += sectorCount;
|
||||
}
|
||||
|
||||
header.tableChecksum =
|
||||
versionHasChecksum(version) ? fnv1a32(table.data(), table.size()) : 0u;
|
||||
|
||||
ByteBuffer out(sectorCursor * static_cast<std::size_t>(sectors), 0);
|
||||
saveWriteU32At(out.data(), out.size(), 0, header.magic);
|
||||
saveWriteU32At(out.data(), out.size(), 4, header.version);
|
||||
saveWriteU32At(out.data(), out.size(), 8, static_cast<uint32_t>(header.regionX));
|
||||
saveWriteU32At(out.data(), out.size(), 12, static_cast<uint32_t>(header.regionZ));
|
||||
saveWriteU32At(out.data(), out.size(), 16, header.chunkCount);
|
||||
saveWriteU32At(out.data(), out.size(), 20, header.sectorBytes);
|
||||
saveWriteU32At(out.data(), out.size(), 24, header.tableChecksum);
|
||||
std::copy(table.begin(), table.end(), out.begin() + static_cast<std::ptrdiff_t>(saveHeaderBytes));
|
||||
|
||||
for (std::size_t i = 0; i < payloads.size(); ++i)
|
||||
std::copy(payloads[i].begin(), payloads[i].end(),
|
||||
out.begin() + static_cast<std::ptrdiff_t>(starts[i]));
|
||||
return out;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user