// Package regionmedic detects and surgically repairs corrupted 7 Days to Die // region (".7rg") files using the panel's own backups. // // Background // // 7DTD stores the world as a grid of 512×512-block "region" files, each // holding a 32×32 grid of chunks. When the server process dies mid-save (a // bare `docker stop` SIGKILL with no `saveworld`, an OOM kill, a crash) a // region file can be left with a torn chunk: the chunk's bytes no longer begin // with the expected header. On the next load 7DTD logs "Wrong chunk header!", // writes an `error_backup__` salvage file, and drops the chunk — so a // base straddling the damaged region loses exactly the half that lived in it. // // This package reproduces, as deterministic code, the manual recovery that // works: localize the damaged region(s) from the error_backup files, find the // newest pre-corruption backup whose copy of that region validates clean, // snapshot the corrupt live file, swap the clean copy in (in place, preserving // permissions), and quarantine the error_backup salvage files. // // The .7rg on-disk format (reverse-engineered + verified byte-for-byte against // real region files, see RE notes in the repo history): // // Sector size = 4096 bytes; file size is always a multiple of 4096. // Region = 32×32 = 1024 chunks. // Sector 0 = header: magic "7rg"+0x01 at 0x000; bytes 0x004..0xFFF zero. // Sector 1 = location table at 0x1000: 1024 entries × 4 bytes, each // [3-byte little-endian sector offset][1-byte sector count]; // an absent chunk is 0x00000000. Table index = localX + localZ*32 // (X varies fastest). Chunk blob lives at sectorOffset*4096. // Chunk blob = [u32 LE blockLength][12 bytes zero][magic "ttc\0"] // [u32 LE const 47][raw DEFLATE stream]. // blockLength = 8 + len(deflate); the on-disk blob occupies // 16 + blockLength bytes, rounded up to whole sectors (the // stored sector count may be one larger — allocator slack). // The decompressed payload begins int32 chunkX, int32 0, // int32 chunkZ. // // The "ttc\0" magic at blob offset 0x10 is the exact byte sequence 7DTD checks // when it logs "Wrong chunk header!", so it is both what Validate flags and the // minimal faithful corruption used by the test harness. // // Frozen API surface (do not break callers — the agent dispatch handler, the // region-medic CLI, and the unit tests all depend on these signatures): // // Validation: ValidateRegionBytes, ValidateRegionFile -> RegionReport // Localize: RegionCoord, RegionForChunk, ParseErrorBackup, ParseRegionFileName // Scan: ScanRegionDir -> RegionDirScan / AffectedRegion // Backups: BackupRef, ListPanelBackups, ExtractRegionFromBackup, // FindLastGoodRegion -> Candidate // Heal: PlanHeal -> HealPlan; ApplyHeal -> HealResult // // Everything here is pure (filesystem + archive/tar + compress/flate, stdlib // only). It performs no Docker or network actions; orchestration (graceful // save, stop, start, log-watch) is the caller's job, which keeps the decision // logic fully unit-testable. package regionmedic