File size: 2,813 Bytes
de15a9f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
"""
neural-cd-preserve CLI -- scan a disc into a self-healing .pt archive.

  archive <source> <out.pt> [--label L --k K --m M --sector S]
        source = optical drive (e.g. \\\\.\\D:) or an .iso/image file
  info    <archive.pt>              show metadata
  verify  <archive.pt>              check health (corruption / recoverability)
  heal    <archive.pt> [--out O]    repair detected corruption (RS)
  restore <archive.pt> <out.iso>    reconstruct the disc image (RS-corrected)
"""
import argparse
from cdpreserve import scan, archive


def main():
    ap = argparse.ArgumentParser(prog="neural-cd-preserve")
    sub = ap.add_subparsers(dest="cmd", required=True)
    a = sub.add_parser("archive"); a.add_argument("source"); a.add_argument("out")
    a.add_argument("--label", default="disc"); a.add_argument("--k", type=int, default=4)
    a.add_argument("--m", type=int, default=2); a.add_argument("--sector", type=int, default=scan.SECTOR)
    for name in ("info", "verify"):
        q = sub.add_parser(name); q.add_argument("archive")
    h = sub.add_parser("heal"); h.add_argument("archive"); h.add_argument("--out")
    r = sub.add_parser("restore"); r.add_argument("archive"); r.add_argument("out")
    args = ap.parse_args()

    if args.cmd == "archive":
        data, bad = scan.scan_image(args.source, args.sector)
        arc = archive.build(data, label=args.label, k=args.k, m=args.m, bad_sectors=bad)
        archive.save(arc, args.out)
        red = args.k + args.m
        print(f"archived '{args.label}': {len(data)//1024} KB, {len(arc['chunks'])} unique chunks, "
              f"RS {args.k}+{args.m} (~{red/args.k:.2f}x). bad sectors at scan: {len(bad)}")
        if bad:
            print(f"  WARNING: {len(bad)} sectors were unreadable at scan time (zero-filled). "
                  f"Re-scan and merge to recover them.")
    elif args.cmd == "info":
        meta = archive.load(args.archive)["meta"]
        for k, v in meta.items():
            print(f"  {k}: {v if k != 'bad_sectors' else f'{len(v)} sectors'}")
    elif args.cmd == "verify":
        h = archive.verify(archive.load(args.archive))
        print(f"health: {'HEALTHY' if h['healthy'] else 'DAMAGED'}  "
              f"chunks={h['chunks']} corrupted_shards={h['corrupted_shards']} "
              f"repairable={h['repairable_chunks']} lost={h['lost_chunks']}")
    elif args.cmd == "heal":
        arc = archive.load(args.archive)
        n = archive.heal(arc)
        archive.save(arc, args.out or args.archive)
        print(f"repaired {n} shard(s) -> {args.out or args.archive}")
    elif args.cmd == "restore":
        data = archive.restore(archive.load(args.archive))
        open(args.out, "wb").write(data)
        print(f"restored {len(data)//1024} KB -> {args.out}")


if __name__ == "__main__":
    main()