More changes and fixes to use the new native world (game.dll) fixes.
This commit is contained in:
@@ -19,11 +19,7 @@ from ptdiag.assbin import (
|
||||
)
|
||||
|
||||
from .paths import AppPaths
|
||||
from .v21_post_auth_reclaim import (
|
||||
PATCHED_SHA256 as POST_AUTH_RECLAIM_GAME_DLL_SHA256,
|
||||
PostAuthReclaimPatchError,
|
||||
build_post_auth_reclaim_patch,
|
||||
)
|
||||
from .world_fix import V21_BINARY_HASHES
|
||||
|
||||
|
||||
WHISPERING_VALE_RELATIVE = Path("game/maps/WhisperingVale.rovug")
|
||||
@@ -39,10 +35,7 @@ COMPATIBILITY_BINARY_HASHES = {
|
||||
"Game.exe": "3cc0980aac0f65b379ff340ec61750498fb9f620fb6c88aa66281e8ab27023a5",
|
||||
"game.dll": "7ea85b19f452962a39c91f967cad22495f066b2d52e8ac37f4e234463d5c4f09",
|
||||
}
|
||||
CURRENT_V21_BINARY_HASHES = {
|
||||
"Game.exe": "2bc3edd26c5f6d7ae9bbdc23967c5aefd374bdf934fcdfa2ed42c6442e550837",
|
||||
"game.dll": "4b4e5c28e2bfdaa8d36c8341fad27becfd03faf63aa8ea874f5d43c5b1961868",
|
||||
}
|
||||
CURRENT_V21_BINARY_HASHES = V21_BINARY_HASHES
|
||||
PATCHED_V21_GAME_DLL_SHA256 = (
|
||||
"0be11b06ccf9be747e32b195c8d5980062f446d7d3a0a662ebfe0e602a01c538"
|
||||
)
|
||||
@@ -75,6 +68,67 @@ class CompatibilityCacheResult:
|
||||
created_at: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class RuntimeCompatibility:
|
||||
"""Read-only compatibility result for the managed game currently on disk."""
|
||||
|
||||
compatible: bool
|
||||
source_game: str
|
||||
source_scene_sha256: str | None
|
||||
scene_action: str | None
|
||||
binary_action: str | None
|
||||
reason: str | None
|
||||
|
||||
|
||||
def inspect_runtime_compatibility(
|
||||
paths: AppPaths,
|
||||
source_game: Path,
|
||||
*,
|
||||
use_world_fix: bool = True,
|
||||
) -> RuntimeCompatibility:
|
||||
"""Check whether ``prepare_runtime_view`` can recognize the installed release.
|
||||
|
||||
This preflight is intentionally read-only: it never normalizes a scene,
|
||||
builds the verified runtime DLL, prepares save data, or replaces the runtime
|
||||
view. The normal preparation path still performs every authoritative check
|
||||
immediately before launch.
|
||||
"""
|
||||
|
||||
requested_source = source_game.expanduser()
|
||||
source_scene_sha256 = None
|
||||
scene_action = None
|
||||
binary_action = None
|
||||
try:
|
||||
source = requested_source.resolve(strict=True)
|
||||
_validate_game_tree(source)
|
||||
source_scene = source / WHISPERING_VALE_RELATIVE
|
||||
inspection = inspect_wrapped_assbin(source_scene)
|
||||
source_scene_sha256 = inspection.file_sha256
|
||||
scene_action = _scene_action(inspection)
|
||||
# Retain the keyword for callers from the 0.5.8 candidate. The runtime
|
||||
# view is now always publisher-pristine; activation happens in memory.
|
||||
_ = use_world_fix
|
||||
binary_action = _inspect_runtime_binary_action(paths, source)
|
||||
except (OSError, ValueError, OverlayError) as exc:
|
||||
reason = str(exc).strip() or type(exc).__name__
|
||||
return RuntimeCompatibility(
|
||||
compatible=False,
|
||||
source_game=str(requested_source.resolve(strict=False)),
|
||||
source_scene_sha256=source_scene_sha256,
|
||||
scene_action=scene_action,
|
||||
binary_action=binary_action,
|
||||
reason=reason,
|
||||
)
|
||||
return RuntimeCompatibility(
|
||||
compatible=True,
|
||||
source_game=str(source),
|
||||
source_scene_sha256=source_scene_sha256,
|
||||
scene_action=scene_action,
|
||||
binary_action=binary_action,
|
||||
reason=None,
|
||||
)
|
||||
|
||||
|
||||
def prepare_runtime_view(
|
||||
paths: AppPaths,
|
||||
source_game: Path,
|
||||
@@ -90,10 +144,12 @@ def prepare_runtime_view(
|
||||
inspection = inspect_wrapped_assbin(source_scene)
|
||||
action = _scene_action(inspection)
|
||||
game_save = _prepare_persistent_save(paths, source)
|
||||
# ``use_world_fix`` is retained as a compatibility keyword. No code path may
|
||||
# replace the publisher DLL; the post-login fix is selected by ``run_game``.
|
||||
_ = use_world_fix
|
||||
binary_action, game_exe_source, game_dll_source = _runtime_game_binary_sources(
|
||||
paths,
|
||||
source,
|
||||
use_world_fix=use_world_fix,
|
||||
)
|
||||
|
||||
build = paths.data / f".{paths.runtime_view.name}.build-{uuid.uuid4().hex}"
|
||||
@@ -207,22 +263,14 @@ def cache_compatibility_binaries(
|
||||
def select_binary_action(
|
||||
cache_hashes: dict[str, str] | None,
|
||||
source_hashes: dict[str, str],
|
||||
runtime_patch_hash: str | None = None,
|
||||
) -> str:
|
||||
if runtime_patch_hash is not None:
|
||||
if runtime_patch_hash != POST_AUTH_RECLAIM_GAME_DLL_SHA256:
|
||||
raise OverlayError("post-authentication runtime patch has an unknown profile")
|
||||
if cache_hashes is not None:
|
||||
if cache_hashes != COMPATIBILITY_BINARY_HASHES:
|
||||
raise OverlayError("compatibility cache has an unknown binary profile")
|
||||
if source_hashes == COMPATIBILITY_BINARY_HASHES:
|
||||
return "verified-source-pair"
|
||||
if runtime_patch_hash is not None:
|
||||
if source_hashes != CURRENT_V21_BINARY_HASHES:
|
||||
raise OverlayError(
|
||||
"post-authentication runtime patch does not match the installed client profile"
|
||||
)
|
||||
return "verified-v21-post-auth-world-fix"
|
||||
if source_hashes == CURRENT_V21_BINARY_HASHES:
|
||||
return "verified-v21-publisher-pair"
|
||||
return "upstream-pair"
|
||||
|
||||
|
||||
@@ -378,38 +426,30 @@ def _reject_save_symlinks(root: Path) -> None:
|
||||
def _runtime_game_binary_sources(
|
||||
paths: AppPaths,
|
||||
source: Path,
|
||||
*,
|
||||
use_world_fix: bool = True,
|
||||
) -> tuple[str, Path, Path]:
|
||||
cache = paths.compatibility_binaries
|
||||
cache_hashes = _binary_hashes(cache) if cache.is_dir() else None
|
||||
source_hashes = _binary_hashes(source)
|
||||
patch = paths.runtime_patch_dll
|
||||
patch_hash = None
|
||||
if source_hashes == CURRENT_V21_BINARY_HASHES and use_world_fix:
|
||||
if patch.exists() or patch.is_symlink():
|
||||
if not patch.is_file() or patch.is_symlink():
|
||||
raise OverlayError(f"runtime patch cache is not a regular file: {patch}")
|
||||
patch_hash = _sha256(patch)
|
||||
else:
|
||||
try:
|
||||
result = build_post_auth_reclaim_patch(source / "game.dll", patch)
|
||||
except PostAuthReclaimPatchError as exc:
|
||||
raise OverlayError(
|
||||
f"could not build the verified post-authentication world fix: {exc}"
|
||||
) from exc
|
||||
patch_hash = result.destination_sha256
|
||||
payload = {"ok": True, **asdict(result)}
|
||||
_write_json(patch.parent / "patch.json", payload)
|
||||
_write_json(paths.runtime_patch_manifest, payload)
|
||||
action = select_binary_action(cache_hashes, source_hashes, patch_hash)
|
||||
action = select_binary_action(cache_hashes, source_hashes)
|
||||
return (
|
||||
action,
|
||||
source / "Game.exe",
|
||||
patch if action == "verified-v21-post-auth-world-fix" else source / "game.dll",
|
||||
source / "game.dll",
|
||||
)
|
||||
|
||||
|
||||
def _inspect_runtime_binary_action(
|
||||
paths: AppPaths,
|
||||
source: Path,
|
||||
) -> str:
|
||||
"""Mirror publisher binary selection without writing any runtime cache."""
|
||||
|
||||
cache = paths.compatibility_binaries
|
||||
cache_hashes = _binary_hashes(cache) if cache.is_dir() else None
|
||||
source_hashes = _binary_hashes(source)
|
||||
return select_binary_action(cache_hashes, source_hashes)
|
||||
|
||||
|
||||
def _binary_hashes(directory: Path) -> dict[str, str]:
|
||||
return {
|
||||
name: _sha256(directory / name)
|
||||
|
||||
Reference in New Issue
Block a user