Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions app/task/MAA/tools/avd.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
from __future__ import annotations

import hashlib
import json
import shutil
from pathlib import Path
from typing import Any
Expand Down Expand Up @@ -82,13 +83,36 @@ def _sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()


#: ``modavd-manifest.json`` 里外壳的路径。ModAVD 0.40.1 起整包自带外壳,就在这里。
PACKAGE_SHIM_PATH = f"{MUMU_SHIM_DIR}/shell/sdk/external_renderer_ipc.dll"


def _package_ships_shim(root: str | Path) -> bool:
"""根目录的 ``modavd-manifest.json`` 列了外壳:这份文件归 ModAVD 的包管。"""
try:
manifest = json.loads(
(Path(root) / "modavd-manifest.json").read_text(encoding="utf-8-sig")
)
except (OSError, ValueError):
return False
files = manifest.get("files") if isinstance(manifest, dict) else None
return isinstance(files, dict) and PACKAGE_SHIM_PATH in files


def ensure_mumu_shim(root: str | Path) -> Path:
"""确保 ``<根>\\mumu-shim\\shell\\sdk\\external_renderer_ipc.dll`` 是随本体发的那一份。
"""确保 ``<根>\\mumu-shim\\shell\\sdk\\external_renderer_ipc.dll`` 在,MAA 能当 MuMu 载入。

ModAVD 0.40.1 起整包自带这份外壳(列在 ``modavd-manifest.json`` 里),这时用包里的,
不覆盖:换成本体的那份会和包的清单对不上,ModAVD 用 Mirror 酱增量更新时就核对失败。
包里的被删了才补本体这份,下次 ModAVD 更新会换回包里的。

随本体更新 DLL 也会变,所以不只看在不在,内容不同就覆盖。正在被 MAA 加载时覆盖会失败,
那就留着旧的并记警告——旧的也能用,下次运行前再换。
更早的包不带外壳,照旧放本体发的那一份:随本体更新 DLL 也会变,所以不只看在不在,
内容不同就覆盖。正在被 MAA 加载时覆盖会失败,那就留着旧的并记警告——旧的也能用,
下次运行前再换。
"""
target = shim_dll(root)
if target.is_file() and _package_ships_shim(root):
return target
if not SHIM_SOURCE.is_file():
raise FileNotFoundError(f"缺少截图组件 {SHIM_SOURCE}")
if target.is_file() and _sha256(target) == _sha256(SHIM_SOURCE):
Expand Down
2 changes: 2 additions & 0 deletions changelog.d/fix-avd-shim-package-owned-20261113.fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
project: maa
ModAVD 自带 MAA 截图外壳时不再覆盖,免得它的增量更新核对失败
76 changes: 76 additions & 0 deletions tests/task/test_maa_avd_shim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"""MAA 截图外壳的放置:ModAVD 包自带外壳时不覆盖。"""

from __future__ import annotations

import json
from pathlib import Path

import pytest

from app.task.MAA.tools import avd


@pytest.fixture
def own_shim(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> bytes:
src = tmp_path / "res" / "external_renderer_ipc.dll"
src.parent.mkdir(parents=True)
src.write_bytes(b"auto-mas shim")
monkeypatch.setattr(avd, "SHIM_SOURCE", src)
return b"auto-mas shim"


def _root(tmp_path: Path, *, shim: bytes | None, listed: bool | None) -> Path:
root = tmp_path / "root"
root.mkdir()
if shim is not None:
avd.shim_dll(root).parent.mkdir(parents=True)
avd.shim_dll(root).write_bytes(shim)
if listed is not None:
files = {"tools/modavd.exe": {"size": 1, "sha256": "0" * 64}}
if listed:
files[avd.PACKAGE_SHIM_PATH] = {"size": 12, "sha256": "1" * 64}
(root / "modavd-manifest.json").write_text(
json.dumps({"schema": 1, "version": "v0.40.1", "files": files}),
encoding="utf-8",
)
return root


def test_package_shim_is_left_alone(tmp_path: Path, own_shim: bytes) -> None:
root = _root(tmp_path, shim=b"package shim", listed=True)
assert avd.ensure_mumu_shim(root) == avd.shim_dll(root)
assert avd.shim_dll(root).read_bytes() == b"package shim"


def test_package_shim_missing_gets_ours(tmp_path: Path, own_shim: bytes) -> None:
root = _root(tmp_path, shim=None, listed=True)
avd.ensure_mumu_shim(root)
assert avd.shim_dll(root).read_bytes() == own_shim


@pytest.mark.parametrize("listed", [False, None])
def test_older_package_gets_ours(
tmp_path: Path, own_shim: bytes, listed: bool | None
) -> None:
"""0.40.1 以前的包不带外壳(清单里没有,或者没有清单):照旧换成本体的那份。"""
root = _root(tmp_path, shim=b"old copy", listed=listed)
avd.ensure_mumu_shim(root)
assert avd.shim_dll(root).read_bytes() == own_shim


@pytest.mark.parametrize("text", ["{", "[]", '{"files": []}', "{}"])
def test_unreadable_manifest_counts_as_older(
tmp_path: Path, own_shim: bytes, text: str
) -> None:
root = _root(tmp_path, shim=b"old copy", listed=None)
(root / "modavd-manifest.json").write_text(text, encoding="utf-8")
avd.ensure_mumu_shim(root)
assert avd.shim_dll(root).read_bytes() == own_shim


def test_bom_manifest_is_read(tmp_path: Path, own_shim: bytes) -> None:
root = _root(tmp_path, shim=b"package shim", listed=True)
path = root / "modavd-manifest.json"
path.write_bytes(b"\xef\xbb\xbf" + path.read_bytes())
avd.ensure_mumu_shim(root)
assert avd.shim_dll(root).read_bytes() == b"package shim"
Loading