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.empty = false;
|
||||||
out.meta = SaveChunkMeta{};
|
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