From c75911fe226e56b818152ecb23cb981626bf0b1a Mon Sep 17 00:00:00 2001 From: DualFroz Date: Fri, 4 Sep 2026 15:26:44 +0200 Subject: [PATCH] feat(vf): add IntegrityIssue and IntegrityReport --- core/include/vf/SaveIntegrity.h | 146 ++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 core/include/vf/SaveIntegrity.h diff --git a/core/include/vf/SaveIntegrity.h b/core/include/vf/SaveIntegrity.h new file mode 100644 index 0000000..77c0191 --- /dev/null +++ b/core/include/vf/SaveIntegrity.h @@ -0,0 +1,146 @@ +#pragma once + +#include +#include +#include +#include + +#include "Chunk.h" +#include "Codec.h" +#include "Math.h" +#include "SaveMigration.h" + +namespace vf { + +// Consistency control for a region save container (format in SaveMigration.h). +// +// The scanner never trusts a field before it has been bounded by the buffer it +// came from, so a save damaged in any way still produces a report rather than a +// crash. Every finding carries a severity, a location and a sentence a person +// can act on; the repair planner turns the recoverable ones into explicit +// actions, and applying them rewrites the container while leaving every healthy +// chunk byte for byte where it was. + +enum class IssueSeverity : uint8_t { + Info = 0, // worth mentioning, costs nothing + Warning = 1, // stale or sloppy, still readable + Recoverable = 2, // damage the repair planner can act on + Fatal = 3, // the container cannot be interpreted at all +}; + +enum class IntegrityIssueKind : uint8_t { + BadMagic, + UnknownVersion, + MalformedHeader, + TruncatedBuffer, + ChunkCountOverrun, + OrphanEntry, + OverlappingEntries, + ChunkOutsideRegion, + ChunkHeightOutOfRange, + MalformedRecord, + PaletteIndexOutOfRange, + ChecksumMismatch, + LightingInconsistent, + TrailingBytes, +}; + +const char* issueSeverityName(IssueSeverity severity); +const char* integrityIssueName(IntegrityIssueKind kind); + +struct IntegrityIssue { + IntegrityIssueKind kind = IntegrityIssueKind::BadMagic; + IssueSeverity severity = IssueSeverity::Fatal; + int regionX = 0; + int regionZ = 0; + int entryIndex = -1; + bool hasChunkCoord = false; + Vec3i chunkCoord; + std::size_t byteOffset = 0; + std::string detail; +}; + +struct IntegrityReport { + bool headerReadable = false; + SaveVersion version = currentSaveVersion; + int regionX = 0; + int regionZ = 0; + std::size_t bufferBytes = 0; + std::size_t declaredChunks = 0; + std::size_t tableEntries = 0; + std::size_t readableChunks = 0; + std::vector issues; + + bool clean() const { return issues.empty(); } + bool fatal() const; + bool repairable() const; + std::size_t countOf(IntegrityIssueKind kind) const; + std::size_t countAtLeast(IssueSeverity severity) const; + const IntegrityIssue* firstOf(IntegrityIssueKind kind) const; +}; + +enum class RepairActionKind : uint8_t { + DropChunk, + TruncateBuffer, + RecomputeChecksum, + RegenerateChunk, + RemoveOrphanEntry, +}; + +const char* repairActionName(RepairActionKind kind); + +struct RepairAction { + RepairActionKind kind = RepairActionKind::DropChunk; + int entryIndex = -1; + bool hasChunkCoord = false; + Vec3i chunkCoord; + std::size_t truncateTo = 0; + std::string reason; +}; + +struct RepairPlan { + bool possible = false; + std::string refusal; + uint64_t worldSeed = 0; + std::vector actions; + + const RepairAction* forEntry(int entryIndex) const; + std::size_t countOf(RepairActionKind kind) const; +}; + +struct RepairResult { + bool ok = false; + ByteBuffer output; + std::vector applied; + std::size_t chunksPreserved = 0; + std::size_t chunksDropped = 0; + std::size_t chunksRegenerated = 0; + std::size_t checksumsRewritten = 0; + std::size_t orphanEntriesRemoved = 0; + std::size_t bytesLost = 0; + std::string failure; +}; + +// Deterministic replacement terrain for a chunk that cannot be salvaged: an +// integer value-noise surface over a 16 block lattice, seeded by the world seed +// and the chunk coordinate only. The same seed always rebuilds the same blocks. +Chunk regenerateChunkFromSeed(uint64_t seed, const Vec3i& coord); +int regeneratedSurfaceHeight(uint64_t seed, int worldX, int worldZ); + +class SaveScanner { +public: + uint64_t worldSeed = 0; + bool allowRegeneration = true; + bool checkPaletteIndices = true; + bool checkLighting = true; + + IntegrityReport scan(const uint8_t* data, std::size_t size) const; + IntegrityReport scan(const ByteBuffer& buffer) const; + + RepairPlan planRepair(const uint8_t* data, std::size_t size, + const IntegrityReport& report) const; + RepairResult repair(const uint8_t* data, std::size_t size, const RepairPlan& plan) const; + RepairResult scanAndRepair(const ByteBuffer& buffer) const; +}; + +} // namespace vf