Files
pristontale-eu-linux/ptlinux/world_fix.py

67 lines
1.9 KiB
Python

"""Exact-profile selection for the post-login V21 world-transition helper."""
from __future__ import annotations
import hashlib
from pathlib import Path
V21_BINARY_HASHES = {
"Game.exe": "2bc3edd26c5f6d7ae9bbdc23967c5aefd374bdf934fcdfa2ed42c6442e550837",
"game.dll": "4b4e5c28e2bfdaa8d36c8341fad27becfd03faf63aa8ea874f5d43c5b1961868",
}
WORLD_FIX_HELPER_FILENAME = "pt-world-fix.exe"
# Filled from the reproducible stripped PE after the native helper is built.
WORLD_FIX_HELPER_SHA256 = (
"d5bdf655f62dae429eff627cf8960f670ec59c12a9759dcd14342786f53e4019"
)
class WorldFixError(RuntimeError):
"""Raised when an exact client needs a missing or untrusted helper."""
def packaged_world_fix_helper() -> Path:
return Path(__file__).resolve().parent / "native" / WORLD_FIX_HELPER_FILENAME
def resolve_world_fix_helper(
game_directory: Path,
*,
helper: Path | None = None,
) -> Path | None:
"""Return the verified helper only for the exact publisher V21 pair."""
game = game_directory.expanduser().resolve(strict=False)
hashes = {
name: _sha256(game / name)
for name in V21_BINARY_HASHES
if (game / name).is_file()
}
if hashes != V21_BINARY_HASHES:
return None
requested = packaged_world_fix_helper() if helper is None else helper.expanduser()
if requested.is_symlink():
raise WorldFixError(
"the post-login world-fix helper must not be a symbolic link"
)
candidate = requested.resolve()
if not candidate.is_file():
raise WorldFixError(
"the verified post-login world-fix helper is missing; reinstall the launcher"
)
digest = _sha256(candidate)
if digest != WORLD_FIX_HELPER_SHA256:
raise WorldFixError(
"the post-login world-fix helper failed its launcher integrity check"
)
return candidate
def _sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()