1
0
mirror of https://github.com/0O0o0oOoO00/Alas.git synced 2026-05-14 15:29:25 +08:00
Files
Alas/module/luahook/crack.py
2026-01-14 14:39:07 +08:00

659 lines
26 KiB
Python

import hashlib
import json
import os
from pathlib import Path
from typing import Union, List, Type
import requests
from pydantic import BaseModel
from module.config.config import AzurLaneConfig
from module.config.deep import deep_get
from module.device.device import Device
from module.luahook.exception import CrackerError
from module.logger import logger
from module.luahook.api import CrackApi
from module.luahook.op import CrackOp
ALL_ENABLE_OPS = [
CrackOp.EnableHookedLuaFunctionTrace,
CrackOp.EnableGlobalShipProperties,
CrackOp.EnableChapterFastMove,
CrackOp.EnableRemoveHardModeLimit,
CrackOp.EnableFakePlayer,
CrackOp.EnableNoBBAnimation,
CrackOp.EnableNoEmotionWarning,
CrackOp.EnableOpsiFastMove,
CrackOp.EnableRemoveHardModeShipTypeLimit,
CrackOp.EnableRemoveHardModeShipPropertiesLimit,
CrackOp.EnableGGFactor,
CrackOp.EnableGlobalSpeedup,
CrackOp.EnableBetterGlobalSpeedup,
CrackOp.EnableExerciseGodMod,
CrackOp.EnableExerciseMorePower,
CrackOp.EnableFastWave,
CrackOp.EnableMonsterKillSelf,
CrackOp.EnableSkipBattleCelebrate,
CrackOp.EnableNoDamage,
CrackOp.EnableOpsiForceAuto,
CrackOp.EnableOpsiNoMapFog,
CrackOp.EnableSkipShipGainShow,
CrackOp.EnableChapterForceEnableAutoFight,
CrackOp.EnableChapterSkipPrecombat,
CrackOp.EnableChapterAutoNextBattle,
CrackOp.EnableChapterAutoAmbush,
CrackOp.EnableChapterAutoClear,
CrackOp.EnableSkipStory,
CrackOp.EnableInfiniteBattle,
CrackOp.EnableSkipAirStrikeAnimation,
CrackOp.EnableAutoOnceAgain,
]
REMOTE_PORT = 23897
class Cracker(CrackApi):
def __init__(self, config: AzurLaneConfig, device: Device):
self.config = config
self.device = device
super().__init__(f"http://127.0.0.1:{device.adb.forward_port(REMOTE_PORT)}")
def do_crack_op_on_func(
before_call: Union[Type[CrackOp.Op], List[Type[CrackOp.Op]]] = None,
after_call: Union[Type[CrackOp.Op], List[Type[CrackOp.Op]]] = None
):
def wrapper(func):
def inner(*args, **kwargs):
obj = args[0]
if before_call is not None:
do_crack_op(obj.config, obj.device, before_call)
try:
ret = func(*args, **kwargs)
except Exception as e:
if after_call is not None:
do_crack_op(obj.config, obj.device, after_call)
raise e
if after_call is not None:
do_crack_op(obj.config, obj.device, after_call)
return ret
return inner
return wrapper
g_cracker_has_inited = False
def do_crack_op(config: AzurLaneConfig, device: Device, ops: Union[Type[CrackOp.Op], List[Type[CrackOp.Op]]]):
if not config.full_config.Hook_HookGeneral_Enable:
return
if not device.app_is_running():
logger.info("Game not running, do not crack")
return
if isinstance(ops, list):
l = ops
else:
if ops == CrackOp.EnableAll:
l = ALL_ENABLE_OPS
else:
l = [ops]
full_config = config.full_config
base_url = f"http://127.0.0.1:{device.adb.forward_port(REMOTE_PORT)}"
timeout = full_config.Hook_HookGeneral_RequestTimeLimit
api = CrackApi(base_url, timeout=timeout)
without_pause = full_config.Hook_HookGeneral_OperateWithoutPause
global g_cracker_has_inited
if not g_cracker_has_inited:
if without_pause:
api.init_without_pause()
else:
api.init()
g_cracker_has_inited = True
for op in l:
if op == CrackOp.DisableAll:
if without_pause:
api.disable_all_without_pause()
else:
api.disable_all()
elif op == CrackOp.EnableHookedLuaFunctionTrace:
if full_config.Hook_HookGeneral_HookedLuaFunctionTrace:
api.enable_hooked_lua_function_trace()
elif op == CrackOp.DisableHookedLuaFunctionTrace:
api.disable_hooked_lua_function_trace()
elif op == CrackOp.EnableGlobalShipProperties:
if config.full_config.Hook_ShipProperty_Method != "final_properties":
continue
api.update_global_ship_properties(
CrackApi.ShipProperties(
armor=float(full_config.Hook_ShipProperty_Armor),
speed=float(full_config.Hook_ShipProperty_Speed),
antiaircraft=float(full_config.Hook_ShipProperty_AntiAircraft),
oxy_recovery_bench=float(full_config.Hook_ShipProperty_OxyRecoveryBench),
torpedo=float(full_config.Hook_ShipProperty_Torpedo),
hit=float(full_config.Hook_ShipProperty_Hit),
sonarRange=float(full_config.Hook_ShipProperty_SonarRange),
attack_duration=float(full_config.Hook_ShipProperty_AttackDuration),
raid_distance=float(full_config.Hook_ShipProperty_RaidDistance),
oxy_recovery_surface=float(full_config.Hook_ShipProperty_OxyRecoverySurface),
oxy_recovery=float(full_config.Hook_ShipProperty_OxyRecovery),
dodge=float(full_config.Hook_ShipProperty_Dodge),
luck=float(full_config.Hook_ShipProperty_Luck),
reload=float(full_config.Hook_ShipProperty_Reload),
oxy_cost=float(full_config.Hook_ShipProperty_OxyCost),
durability=float(full_config.Hook_ShipProperty_Durability),
air=float(full_config.Hook_ShipProperty_Air),
oxy_max=float(full_config.Hook_ShipProperty_OxyMax),
cannon=float(full_config.Hook_ShipProperty_Cannon),
antisub=float(full_config.Hook_ShipProperty_AntiSub),
)
)
api.enable_global_ship_properties_crack()
elif op == CrackOp.DisableGlobalShipProperties:
api.disable_global_ship_properties_crack()
elif op == CrackOp.EnableChapterFastMove:
if not full_config.Hook_Misc_ChapterMove:
continue
if without_pause:
api.enable_fast_stage_move_without_pause()
else:
api.enable_fast_stage_move()
elif op == CrackOp.DisableChapterFastMove:
if without_pause:
api.disable_fast_stage_move_without_pause()
else:
api.disable_fast_stage_move()
elif op == CrackOp.EnableRemoveHardModeShipPropertiesLimit:
if full_config.Hook_Misc_RemoveHardMapLimit != "remove_ship_properties_limit":
continue
api.enable_remove_hard_mode_ship_properties_limit()
elif op == CrackOp.DisableRemoveHardModeShipPropertiesLimit:
api.disable_remove_hard_mode_ship_properties_limit()
elif op == CrackOp.EnableRemoveHardModeShipTypeLimit:
if full_config.Hook_Misc_RemoveHardMapLimit != "remove_ship_type_limit":
continue
api.enable_remove_hard_mode_ship_type_limit()
elif op == CrackOp.DisableRemoveHardModeShipTypeLimit:
api.disable_remove_hard_mode_ship_type_limit()
elif op == CrackOp.EnableRemoveHardModeLimit:
if full_config.Hook_Misc_RemoveHardMapLimit != "remove_both":
continue
api.enable_remove_hard_mode_limit()
elif op == CrackOp.DisableRemoveHardModeLimit:
api.disable_remove_hard_mode_limit()
elif op == CrackOp.EnableFakePlayer:
if not full_config.Hook_FakePlayer_Enable:
continue
api.update_fake_player_info(
CrackApi.FakePlayerInfo(
name=str(full_config.Hook_FakePlayer_Name),
level=str(full_config.Hook_FakePlayer_Level),
id=str(full_config.Hook_FakePlayer_Id),
)
)
api.enable_fake_player()
elif op == CrackOp.DisableFakePlayer:
api.disable_fake_player()
elif op == CrackOp.EnableNoBBAnimation:
if not full_config.Hook_Misc_NoBBAnimation:
continue
api.enable_no_bb_animation()
elif op == CrackOp.DisableNoBBAnimation:
api.disable_no_bb_animation()
elif op == CrackOp.EnableNoEmotionWarning:
if not full_config.Hook_Misc_NoEmotionWarning:
continue
api.enable_no_emotion_warning()
elif op == CrackOp.DisableNoEmotionWarning:
api.disable_no_emotion_warning()
elif op == CrackOp.IsAlive:
api.is_alive()
elif op == CrackOp.EnableOpsiFastMove:
api.enable_opsi_fast_move()
elif op == CrackOp.DisableOpsiFastMove:
api.disable_opsi_fast_move()
elif op == CrackOp.EnableGGFactor:
if full_config.Hook_ShipProperty_Method != "gg_factor":
continue
api.update_gg_factor(float(full_config.Hook_ShipProperty_Factor))
api.enable_gg_factor()
elif op == CrackOp.DisableGGFactor:
api.disable_gg_factor()
elif op == CrackOp.EnableGlobalSpeedup:
rate = float(full_config.Hook_Misc_GlobalSpeedup)
if rate == 1.0:
continue
api.update_global_speedup_rate(CrackApi.GlobalSpeedupRate(rate=rate))
api.enable_global_speedup()
elif op == CrackOp.DisableGlobalSpeedup:
api.disable_global_speedup()
elif op == CrackOp.EnableBetterGlobalSpeedup:
rate = float(full_config.Hook_Misc_BetterGlobalSpeedup)
if rate == 1.0:
continue
if without_pause:
api.update_better_global_speedup_rate_without_pause(CrackApi.BetterGlobalSpeedupRate(rate=rate))
api.enable_better_global_speedup_without_pause()
else:
api.update_better_global_speedup_rate(CrackApi.BetterGlobalSpeedupRate(rate=rate))
api.enable_better_global_speedup()
elif op == CrackOp.DisableBetterGlobalSpeedup:
if without_pause:
api.disable_better_global_speedup_without_pause()
else:
api.disable_better_global_speedup()
elif op == CrackOp.EnableExerciseGodMod:
if full_config.Hook_Misc_ExerciseGodMod:
api.enable_exercise_god_mode()
elif op == CrackOp.DisableExerciseGodMod:
api.disable_exercise_god_mode()
elif op == CrackOp.EnableExerciseMorePower:
rate = float(full_config.Hook_Misc_ExerciseMorePower)
if rate == -1.0:
continue
api.update_exercise_more_power_rate(CrackApi.ExerciseMorePowerRate(rate=rate))
api.enable_exercise_more_power()
elif op == CrackOp.DisableExerciseMorePower:
api.disable_exercise_more_power()
elif op == CrackOp.EnableFastWave:
if full_config.Hook_Misc_FastWave:
api.enable_fast_wave()
elif op == CrackOp.DisableFastWave:
api.disable_fast_wave()
elif op == CrackOp.EnableMonsterKillSelf:
if full_config.Hook_Misc_MonsterKillSelf:
api.enable_monster_kill_self()
elif op == CrackOp.DisableMonsterKillSelf:
api.disable_monster_kill_self()
elif op == CrackOp.EnableSkipBattleCelebrate:
if full_config.Hook_Misc_SkipBattleCelebrate:
if without_pause:
api.enable_skip_battle_celebrate_without_pause()
else:
api.enable_skip_battle_celebrate()
elif op == CrackOp.DisableSkipBattleCelebrate:
if without_pause:
api.disable_skip_battle_celebrate_without_pause()
else:
api.disable_skip_battle_celebrate()
elif op == CrackOp.EnableNoDamage:
if full_config.Hook_Misc_NoDamage:
api.enable_no_damage()
elif op == CrackOp.DisableNoDamage:
api.disable_no_damage()
elif op == CrackOp.EnableOpsiForceAuto:
if full_config.Hook_Misc_OpsiForceAuto:
api.enable_opsi_force_auto()
elif op == CrackOp.DisableOpsiForceAuto:
api.disable_opsi_force_auto()
elif op == CrackOp.EnableOpsiNoMapFog:
if full_config.Hook_Misc_OpsiNoMapFog:
api.enable_opsi_no_map_fog()
elif op == CrackOp.DisableOpsiNoMapFog:
api.disable_opsi_no_map_fog()
elif op == CrackOp.EnableSkipShipGainShow:
if full_config.Hook_Misc_SkipShipGainShow:
api.enable_skip_ship_gain_show()
elif op == CrackOp.DisableSkipShipGainShow:
api.disable_skip_ship_gain_show()
elif op == CrackOp.EnableChapterForceEnableAutoFight:
if full_config.Hook_Misc_ChapterForceEnableAutoFight:
api.enable_chapter_force_enable_auto_fight()
elif op == CrackOp.DisableChapterForceEnableAutoFight:
api.disable_chapter_force_enable_auto_fight()
elif op == CrackOp.EnableChapterSkipPrecombat:
if full_config.Hook_Misc_ChapterSkipPrecombat:
api.enable_chapter_skip_precombat()
elif op == CrackOp.DisableChapterSkipPrecombat:
api.disable_chapter_skip_precombat()
elif op == CrackOp.EnableChapterAutoNextBattle:
if full_config.Hook_Misc_ChapterAutoNextBattle:
api.enable_chapter_auto_next_battle()
elif op == CrackOp.DisableChapterAutoNextBattle:
api.disable_chapter_auto_next_battle()
elif op == CrackOp.EnableChapterAutoAmbush:
if full_config.Hook_Misc_ChapterAutoAmbush:
api.enable_chapter_auto_ambush()
elif op == CrackOp.DisableChapterAutoAmbush:
api.disable_chapter_auto_ambush()
elif op == CrackOp.EnableChapterAutoClear:
if full_config.Hook_Misc_ChapterAutoClear:
api.set_chapter_auto_clear_step_interval(CrackApi.ChapterAutoClearStepInterval(interval=float(full_config.Hook_Misc_ChapterAutoClearStepInterval)))
api.enable_chapter_auto_clear()
elif op == CrackOp.DisableChapterAutoClear:
api.disable_chapter_auto_clear()
elif op == CrackOp.EnableSkipStory:
if full_config.Hook_Misc_SkipStory:
api.enable_skip_story()
elif op == CrackOp.DisableSkipStory:
api.disable_skip_story()
elif op == CrackOp.EnableInfiniteBattle:
if full_config.Hook_Misc_InfiniteBattle:
api.enable_infinite_battle()
elif op == CrackOp.DisableInfiniteBattle:
api.disable_infinite_battle()
elif op == CrackOp.EnableSkipAirStrikeAnimation:
if full_config.Hook_Misc_SkipAirStrikeAnimation:
api.enable_skip_air_strike_animation()
elif op == CrackOp.DisableSkipAirStrikeAnimation:
api.disable_skip_air_strike_animation()
elif op == CrackOp.EnableAutoOnceAgain:
if full_config.Hook_Misc_AutoOnceAgain:
api.enable_auto_once_again()
elif op == CrackOp.DisableAutoOnceAgain:
api.disable_auto_once_again()
else:
logger.error(f"Unsupported op: {op}")
crack_op = do_crack_op_on_func
def disable_all_crack(f):
def wrapper(*args, **kwargs):
obj = args[0]
logger.info("Disabe all luahook cracks")
do_crack_op(obj.config, obj.device, CrackOp.DisableAll)
return f(*args, **kwargs)
return wrapper
def enable_all_crack(f):
def wrapper(*args, **kwargs):
obj = args[0]
do_crack_op(obj.config, obj.device, CrackOp.EnableAll)
return f(*args, **kwargs)
return wrapper
def luahook_crack_all(config: AzurLaneConfig, device: Device):
logger.info("Crack all with luahook")
do_crack_op(config, device, CrackOp.EnableAll)
def luahook_disable_all(config: AzurLaneConfig, device: Device):
logger.info("Disable all luahook")
do_crack_op(config, device, CrackOp.DisableAll)
CHAPTER_CRACK_OPS = [
CrackOp.EnableHookedLuaFunctionTrace,
CrackOp.EnableChapterFastMove,
CrackOp.EnableNoBBAnimation,
CrackOp.EnableNoEmotionWarning,
CrackOp.EnableGlobalShipProperties,
CrackOp.EnableRemoveHardModeLimit,
CrackOp.EnableFakePlayer,
CrackOp.EnableGGFactor,
CrackOp.EnableGlobalSpeedup,
CrackOp.EnableBetterGlobalSpeedup,
CrackOp.EnableFastWave,
CrackOp.EnableMonsterKillSelf,
CrackOp.EnableSkipBattleCelebrate,
CrackOp.EnableNoDamage,
CrackOp.EnableSkipShipGainShow,
CrackOp.EnableChapterForceEnableAutoFight,
CrackOp.EnableChapterSkipPrecombat,
CrackOp.EnableChapterAutoNextBattle,
CrackOp.EnableChapterAutoAmbush,
CrackOp.EnableChapterAutoClear,
CrackOp.EnableSkipStory,
CrackOp.EnableInfiniteBattle,
CrackOp.EnableSkipAirStrikeAnimation,
CrackOp.EnableAutoOnceAgain,
]
def chapter_task_crack(f):
def wrapper(*args, **kwargs):
obj = args[0]
do_crack_op(obj.config, obj.device, CHAPTER_CRACK_OPS)
return f(*args, **kwargs)
return wrapper
OPSI_CRACK_OPS = [
CrackOp.EnableHookedLuaFunctionTrace,
CrackOp.EnableNoBBAnimation,
CrackOp.EnableOpsiFastMove,
CrackOp.EnableGlobalShipProperties,
CrackOp.EnableFakePlayer,
CrackOp.EnableGGFactor,
CrackOp.EnableGlobalSpeedup,
CrackOp.EnableBetterGlobalSpeedup,
CrackOp.EnableFastWave,
CrackOp.EnableMonsterKillSelf,
CrackOp.EnableSkipBattleCelebrate,
CrackOp.EnableNoDamage,
CrackOp.EnableOpsiForceAuto,
CrackOp.EnableOpsiNoMapFog,
CrackOp.EnableSkipShipGainShow,
CrackOp.EnableSkipStory,
]
def opsi_task_crack(f):
def wrapper(*args, **kwargs):
obj = args[0]
do_crack_op(obj.config, obj.device, OPSI_CRACK_OPS)
return f(*args, **kwargs)
return wrapper
class UpdateServerApi:
class ResourceFile(BaseModel):
arch: str
file: str
def __init__(self, api_url: str = ""):
self.api_url = api_url
def post(self, path: str, data=None):
logger.info(f"UpdateServerApi post: {path}")
url = f'{self.api_url}/{path}'
try:
response = requests.post(url, data=data) # TODO: add timeout
except requests.exceptions.Timeout:
raise CrackerError('UpdateServerApi request timeout')
except Exception as e:
raise CrackerError(f'UpdateServerApi request error: {e}')
if response.status_code != 200:
raise CrackerError(f'UpdateServerApi response error: {response.status_code}')
return response
def download_file(self, arch: str, file_name: str) -> bytes:
res = self.post("files", UpdateServerApi.ResourceFile(arch=arch, file=file_name).json())
return res.content
def get_hash(self, arch: str, file_name: str) -> str:
res = self.post("get_hash", UpdateServerApi.ResourceFile(arch=arch, file=file_name).json())
return res.text
class CrackResource:
ARCH_MAP = {
"x86": "x86",
"x86_64": "x86",
"armv7": "armeabi-v7a",
"arm64": "arm64-v8a",
"aarch64": "arm64-v8a",
"aarch": "armeabi-v7a",
}
def __init__(self, config: AzurLaneConfig, device: Device):
self.config = config
self.device = device
arch_conf = config.full_config.Hook_HookGeneral_Architecture
if arch_conf == "auto":
arch_ret = device.adb_shell("uname -m").lower()
self.arch = self.ARCH_MAP.get(arch_ret, "")
if not self.arch:
raise CrackerError(f"Unsupported arch: {arch_ret}")
logger.info(f"Arch: {arch_ret} -> {self.arch}")
else:
self.arch = arch_conf
logger.info(f"Use arch: {self.arch}")
self.resource_root = f"./bin/hook"
self.resource_dir = f"{self.resource_root}/{self.arch}"
self.version_file = f"{self.resource_dir}/version.json"
self.first_init = False
self.has_new_version = False
self.update_server = self.__get_update_server()
self.upload_dir = f"/data/hook/{self.arch}"
self.game_lib_dir = Path(self.__format_str(config.full_config.Hook_HookGeneral_GameLibDir)).as_posix()
def __file_hash(self, file_name):
with open(file_name, "rb") as f:
return hashlib.md5(f.read()).hexdigest()
def __gen_version_file_from_local(self):
d = {}
for file_name in os.listdir(self.resource_dir):
f = f"{self.resource_dir}/{file_name}"
if not os.path.isfile(f):
continue
if file_name.endswith(".json"):
continue
hash_value = self.__file_hash(f).upper()
d[file_name] = hash_value
with open(self.version_file, "w") as f:
json.dump(d, f, indent=4)
def __ensure_local_resource(self):
if self.update_server is None:
logger.info("No update server, skip ensure local resource")
return
if not os.path.exists(f"{self.resource_dir}/libcracker.so"):
self.__download_resource("libcracker.so")
self.first_init = True
if not os.path.exists(f"{self.resource_dir}/patchelf"):
self.__download_resource("patchelf")
self.first_init = True
__update_local_version = __gen_version_file_from_local
def __ensure_crack_resource(self, ):
if not os.path.exists(self.resource_root):
os.makedirs(self.resource_root)
if not os.path.exists(self.resource_dir):
os.makedirs(self.resource_dir)
self.__ensure_local_resource()
if not os.path.exists(self.version_file):
self.__gen_version_file_from_local()
def __download_resource(self, file_name: str):
if self.update_server is None:
logger.info("No update server, skip download resource")
return
logger.info(f"Download resource: {self.arch}/{file_name}")
res = self.update_server.download_file(self.arch, file_name)
with open(f"{self.resource_dir}/{file_name}", "wb") as f:
f.write(res)
def __is_same_version(self, version_json: dict, file_name: str):
h = self.update_server.get_hash(self.arch, file_name)
return h == version_json.get(file_name, "")
def __check_update(self):
if self.update_server is None:
logger.info("No update server, skip update check, use local resource")
return
if not self.config.full_config.Hook_HookGeneral_UpdateEveryTime:
logger.info("Update skip, use local resource")
return
with open(self.version_file, "r") as f:
local_version = json.load(f)
if not self.__is_same_version(local_version, "libcracker.so"):
self.__download_resource("libcracker.so")
self.has_new_version = True
if not self.__is_same_version(local_version, "patchelf"):
self.__download_resource("patchelf")
self.has_new_version = True
if self.has_new_version:
self.__update_local_version()
def __format_str(self, s: str) -> str:
return ("".join([i for i in s if i.isprintable()])).replace(" ", "").replace("\n", "")
def __get_update_server(self) -> Union[UpdateServerApi, None]:
update_server_url: str = self.config.full_config.Hook_HookGeneral_UpdateServer
update_server_url = self.__format_str(update_server_url)
if not update_server_url:
return None
return UpdateServerApi(update_server_url)
def __push(self, file_name: str):
self.device.adb_shell(f"mkdir -p {self.upload_dir}")
self.device.adb_shell(f"rm {self.upload_dir}/{file_name}")
self.device.adb_push(f"{self.resource_dir}/{file_name}", f"{self.upload_dir}/{file_name}")
self.device.adb_shell(f"chmod 777 {self.upload_dir}/{file_name}")
def __push_resource(self):
logger.info("Push resource to device")
self.__push("libcracker.so")
self.__push("patchelf")
def __do_inject(self):
if not self.game_lib_dir:
raise CrackerError("GameLibDir not set")
logger.info(f"GameLibDir: {self.game_lib_dir}, do inject")
cmd = f"cd {self.upload_dir} && ./patchelf --local-patch --game-lib-dir '{self.game_lib_dir}'"
self.device.adb_shell(cmd)
def __is_exist(self, path):
cmd = f"if [ -f {path} ]; then echo 'exist'; else if [ -d {path} ]; then echo 'exist'; else echo 'not exist'; fi; fi"
return self.device.adb_shell(cmd) == "exist"
def __is_remote_file_exist(self) -> bool:
return self.__is_exist(self.upload_dir) and self.__is_exist(f"{self.upload_dir}/libcracker.so") and self.__is_exist(f"{self.upload_dir}/patchelf")
def __adb_su_do(self, cmd: str):
return self.device.adb_shell(f"su -c '{cmd}'")
def __adb_root(self):
self.device.adb_command(["root"])
def __check_game_lib_dir(self):
return self.__is_exist(f"{self.game_lib_dir}/libil2cpp.so") and self.__is_exist(f"{self.game_lib_dir}/libtolua.so")
def ensure(self):
if not self.config.full_config.Hook_HookGeneral_Enable:
return
if not self.__check_game_lib_dir():
raise CrackerError(f"GameLibDir '{self.game_lib_dir}' is invalid")
self.__adb_root()
self.__ensure_crack_resource()
if not self.first_init:
self.__check_update()
if self.has_new_version or not self.__is_remote_file_exist() or self.config.full_config.Hook_HookGeneral_PushEveryTime:
self.__push_resource()
self.device.app_stop()
self.config.task_call("Restart")
self.__do_inject()