feat(vf): add IntegrityIssue and IntegrityReport
This commit is contained in:
1 parent
22324d7b48
commit
c75911fe22
1 file changed
+146
@@ -0,0 +1,146 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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<IntegrityIssue> 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<RepairAction> actions;
|
||||||
|
|
||||||
|
const RepairAction* forEntry(int entryIndex) const;
|
||||||
|
std::size_t countOf(RepairActionKind kind) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RepairResult {
|
||||||
|
bool ok = false;
|
||||||
|
ByteBuffer output;
|
||||||
|
std::vector<RepairAction> 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
|
||||||
Reference in new issue
Block a user