Waydroid Blue Archive Fix
TLDR
如果你是ARM平台的linux,不用修,直接能用。
x86_64平台:
- waydroid_script安装
libndk
或者libhoudini
实现arm64 - libndk patch或者libhoudini patch
01 转译层
已经安装的可以跳过。
需要先安装lzip
,sudo apt install lzip
或者nix-shell -p lzip
,按你的发行版来
使用waydroid_script
安装转译层:
git clone https://github.com/casualsnek/waydroid_script
cd waydroid_script
python3 -m venv venv
venv/bin/pip install -r requirements.txt
AMD用户安装libndk
,intel用libhoudini
。
sudo venv/bin/python3 main.py install libndk # AMD
sudo venv/bin/python3 main.py install libhoudini # INTEL
02 修复转译层
修复NDK
scripton_ndk.txt,重命名成sh后缀然后执行
原始issue
修复Houdini
scripton.txt,重命名成sh后缀然后执行
原始issue
Troubleshooting
我用修复NDK的没成功,用以下脚本成功了
import sys
from argparse import ArgumentParser
from dataclasses import dataclass
from pathlib import Path
from typing import BinaryIO, Sequence
class MismatchError(Exception):
pass
@dataclass
class Patch:
offset: int
expected: bytes
replacement: bytes
def is_applied(self, io: BinaryIO) -> bool:
io.seek(self.offset)
actual = io.read(len(self.expected))
if actual == self.expected:
return False
elif actual == self.replacement:
return True
else:
raise MismatchError
def apply(self, io: BinaryIO) -> bool:
if self.is_applied(io):
return False
io.seek(self.offset)
io.write(self.replacement)
return True
def reset(self, io: BinaryIO) -> bool:
if not self.is_applied(io):
return False
io.seek(self.offset)
io.write(self.expected)
return True
class PatchManager:
def __init__(self, filename: Path, patches: Sequence[Patch]) -> None:
self.filename = filename
self.patches = patches
def check(self):
with open(self.filename, "rb") as f:
for patch in self.patches:
if patch.is_applied(f):
print(f"Offset 0x{patch.offset:x}: patched")
else:
print(f"Offset 0x{patch.offset:x}: not patched")
def reset(self):
with open(self.filename, "r+b") as f:
for patch in self.patches:
patch.is_applied(f)
for patch in self.patches:
if patch.reset(f):
print(
f"Offset 0x{patch.offset:x}: 0x{patch.replacement.hex()} -> 0x{patch.expected.hex()}"
)
def apply(self):
with open(self.filename, "r+b") as f:
for patch in self.patches:
patch.is_applied(f)
for patch in self.patches:
if patch.apply(f):
print(
f"Offset 0x{patch.offset:x}: 0x{patch.expected.hex()} -> 0x{patch.replacement.hex()}"
)
# Patches from https://github.com/waydroid/waydroid/issues/788
HOUDINI_PATCHES = [
Patch(
offset=0x3062A5,
expected=b"\x48\xb8\xfb\xff\xff\xff",
replacement=b"\x48\xb8\xff\xff\xff\xff",
),
Patch(
offset=0x3099D6,
expected=b"\x83\xe0\xfb",
replacement=b"\x83\xe0\xff",
),
Patch(
offset=0x309B42,
expected=b"\xe8\x89\x2f\xee\xff",
replacement=b"\x90\x90\x90\x90\x90",
),
]
NDK_PATCHES = [
Patch(
offset=0x206DD1,
expected=b"\x83\xe2\xfa",
replacement=b"\x83\xe2\xff",
),
Patch(
offset=0x206CD6,
expected=b"\x83\xe2\xfa",
replacement=b"\x83\xe2\xff",
),
]
HOUDINI_PATH = Path("/var/lib/waydroid/overlay/system/lib64/libhoudini.so")
NDK_PATH = Path("/var/lib/waydroid/overlay/system/lib64/libndk_translation.so")
HOUDINI_FILENAME = HOUDINI_PATH.name
NDK_FILENAME = NDK_PATH.name
if __name__ == "__main__":
parser = ArgumentParser(
prog="patch-waydroid-bluearchive",
description="Patch libhoudini or libndk to play Blue Archive",
)
parser.add_argument("-t", "--type", choices=["houdini", "ndk"])
parser.add_argument("-f", "--filename", type=Path)
parser.add_argument("command", choices=["check", "patch", "reset"])
args = parser.parse_args()
if (not args.type) and args.filename:
if args.filename.name == HOUDINI_FILENAME:
type = "houdini"
elif args.filename.name == NDK_FILENAME:
type = "ndk"
else:
parser.error("Could not detect type from filename, please specify --type.")
print(f"Detected type: {type}")
filename = args.filename
elif args.type and (not args.filename):
type = args.type
if args.type == "houdini":
filename = HOUDINI_PATH
elif args.type == "ndk":
filename = NDK_PATH
else:
parser.error(f"Invalid type: {type}")
print(f"Detected filename: {filename}")
elif args.type and args.filename:
type = args.type
filename = args.filename
else:
parser.error("Please specify one of --filename or --type.")
if type == "houdini":
patches = HOUDINI_PATCHES
elif type == "ndk":
patches = NDK_PATCHES
else:
parser.error(f"Invalid type: {type}")
manager = PatchManager(filename, patches)
try:
if args.command == "check":
manager.check()
elif args.command == "patch":
manager.apply()
elif args.command == "reset":
manager.reset()
except MismatchError:
print(
f"{filename} doesn't seem to be a valid {type} shared object, refusing to patch",
file=sys.stderr,
)
sys.exit(1)
except PermissionError:
print(f"{filename} is not writable, please run with sudo", file=sys.stderr)
sys.exit(1)
except FileNotFoundError:
print(f"{filename} not found", file=sys.stderr)
sys.exit(1)