#include "vf/SaveIntegrity.h" #include #include #include "vf/Block.h" #include "vf/Region.h" namespace vf { namespace { std::string decimal(long long value) { return std::to_string(value); } std::string coordText(const Vec3i& coord) { return "(" + decimal(coord.x) + ", " + decimal(coord.y) + ", " + decimal(coord.z) + ")"; } uint64_t mixBits(uint64_t value) { value += 0x9E3779B97F4A7C15ull; value = (value ^ (value >> 30)) * 0xBF58476D1CE4E5B9ull; value = (value ^ (value >> 27)) * 0x94D049BB133111EBull; return value ^ (value >> 31); } uint64_t latticeHash(uint64_t seed, int gx, int gz) { uint64_t h = seed; h = mixBits(h ^ (static_cast(static_cast(gx)) * 0x2545F4914F6CDD1Dull)); h = mixBits(h ^ (static_cast(static_cast(gz)) * 0x9E3779B97F4A7C15ull)); return h; } int latticeHeight(uint64_t seed, int gx, int gz) { return 32 + static_cast(latticeHash(seed, gx, gz) % 33ull); } struct SectorSpan { uint32_t first = 0; uint32_t last = 0; int entryIndex = -1; }; IntegrityIssue makeIssue(IntegrityIssueKind kind, IssueSeverity severity, std::size_t byteOffset, std::string detail) { IntegrityIssue issue; issue.kind = kind; issue.severity = severity; issue.byteOffset = byteOffset; issue.detail = std::move(detail); return issue; } bool actionOutranks(RepairActionKind candidate, RepairActionKind existing) { auto rank = [](RepairActionKind kind) { switch (kind) { case RepairActionKind::RecomputeChecksum: return 0; case RepairActionKind::RegenerateChunk: return 1; case RepairActionKind::RemoveOrphanEntry: return 2; case RepairActionKind::DropChunk: return 3; case RepairActionKind::TruncateBuffer: return 4; } return 0; }; return rank(candidate) > rank(existing); } } // namespace const char* issueSeverityName(IssueSeverity severity) { switch (severity) { case IssueSeverity::Info: return "info"; case IssueSeverity::Warning: return "warning"; case IssueSeverity::Recoverable: return "recoverable"; case IssueSeverity::Fatal: return "fatal"; } return "unknown"; } const char* integrityIssueName(IntegrityIssueKind kind) { switch (kind) { case IntegrityIssueKind::BadMagic: return "badMagic"; case IntegrityIssueKind::UnknownVersion: return "unknownVersion"; case IntegrityIssueKind::MalformedHeader: return "malformedHeader"; case IntegrityIssueKind::TruncatedBuffer: return "truncatedBuffer"; case IntegrityIssueKind::ChunkCountOverrun: return "chunkCountOverrun"; case IntegrityIssueKind::OrphanEntry: return "orphanEntry"; case IntegrityIssueKind::OverlappingEntries: return "overlappingEntries"; case IntegrityIssueKind::ChunkOutsideRegion: return "chunkOutsideRegion"; case IntegrityIssueKind::ChunkHeightOutOfRange: return "chunkHeightOutOfRange"; case IntegrityIssueKind::MalformedRecord: return "malformedRecord"; case IntegrityIssueKind::PaletteIndexOutOfRange: return "paletteIndexOutOfRange"; case IntegrityIssueKind::ChecksumMismatch: return "checksumMismatch"; case IntegrityIssueKind::LightingInconsistent: return "lightingInconsistent"; case IntegrityIssueKind::TrailingBytes: return "trailingBytes"; } return "unknown"; } const char* repairActionName(RepairActionKind kind) { switch (kind) { case RepairActionKind::DropChunk: return "dropChunk"; case RepairActionKind::TruncateBuffer: return "truncateBuffer"; case RepairActionKind::RecomputeChecksum: return "recomputeChecksum"; case RepairActionKind::RegenerateChunk: return "regenerateChunk"; case RepairActionKind::RemoveOrphanEntry: return "removeOrphanEntry"; } return "unknown"; } bool IntegrityReport::fatal() const { for (const IntegrityIssue& issue : issues) if (issue.severity == IssueSeverity::Fatal) return true; return false; } bool IntegrityReport::repairable() const { if (fatal()) return false; for (const IntegrityIssue& issue : issues) if (issue.severity == IssueSeverity::Recoverable) return true; return false; } std::size_t IntegrityReport::countOf(IntegrityIssueKind kind) const { std::size_t total = 0; for (const IntegrityIssue& issue : issues) if (issue.kind == kind) ++total; return total; } std::size_t IntegrityReport::countAtLeast(IssueSeverity severity) const { std::size_t total = 0; for (const IntegrityIssue& issue : issues) if (issue.severity >= severity) ++total; return total; } const IntegrityIssue* IntegrityReport::firstOf(IntegrityIssueKind kind) const { for (const IntegrityIssue& issue : issues) if (issue.kind == kind) return &issue; return nullptr; } const RepairAction* RepairPlan::forEntry(int entryIndex) const { for (const RepairAction& action : actions) if (action.entryIndex == entryIndex) return &action; return nullptr; } std::size_t RepairPlan::countOf(RepairActionKind kind) const { std::size_t total = 0; for (const RepairAction& action : actions) if (action.kind == kind) ++total; return total; } int regeneratedSurfaceHeight(uint64_t seed, int worldX, int worldZ) { int gx = floorDiv(worldX, chunkSize); int gz = floorDiv(worldZ, chunkSize); int fx = floorMod(worldX, chunkSize); int fz = floorMod(worldZ, chunkSize);