import hashlib import os import struct import tempfile import unittest import zipfile from pathlib import Path from mhwmm.core.paks import path_hash, pak_name from mhwmm.core.paths import Paths def snapshot(root: Path) -> dict: """Byte-level picture of a tree: files by hash, symlinks by target, and directories.""" snap = {} for dirpath, dirnames, filenames in os.walk(root): for n in dirnames + filenames: p = Path(dirpath) / n rel = str(p.relative_to(root)) if p.is_symlink(): snap[rel] = ("l", os.readlink(p)) elif p.is_dir(): snap[rel] = ("d",) else: snap[rel] = ("f", hashlib.sha256(p.read_bytes()).hexdigest()) return snap def make_game(root: Path, ceiling: int = 15) -> Path: game = root / "game" game.mkdir() for chain in ("main", "sub"): for n in range(1, ceiling + 1): (game / pak_name(chain, n)).write_bytes(f"vanilla-{chain}-{n}".encode()) (game / "re_chunk_000.pak").write_bytes(b"base") (game / "MonsterHunterWilds.exe").write_bytes(b"exe") return game def write_tree(root: Path, files: dict[str, bytes | str]) -> Path: for rel, data in files.items(): p = root / rel p.parent.mkdir(parents=True, exist_ok=True) p.write_bytes(data if isinstance(data, bytes) else data.encode()) return root def make_kpka(paths: list[str]) -> bytes: """Minimal KPKA with entries keyed by the path hashes of `paths`.""" out = b"KPKA" + struct.pack(" Path: with zipfile.ZipFile(path, "w") as zf: for name, data in files.items(): zf.writestr(name, data) return path class TmpCase(unittest.TestCase): def setUp(self): self._td = tempfile.TemporaryDirectory() self.addCleanup(self._td.cleanup) self.tmp = Path(self._td.name) self.paths = Paths(self.tmp / "data", self.tmp / "config") self.paths.ensure()