#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