import os import threading import winreg from tools.emulator.emulator import Emulator from model.custom_struct import EmulatorInfo from tools.ini_operate import ConfigIni from tools.log import logger from tools.utils import Utils # from tools.window_operate import WindowOperate class YS(Emulator): def __init__(self,system_type=9): self.install_path = self.get_ys_install_dir() super().__init__(self.install_path) self.emulator_type = 'ys' self.system_type = system_type if not os.path.exists(self.install_path + r'\NoxConsole.exe'): raise ValueError("模拟器安装路径错误") self.config_path = os.path.join(os.getenv('LOCALAPPDATA'), 'Nox') self._lock = threading.Lock() def set_install_path(self, path: str): self.install_path = path def get_install_path(self) -> str: return self.install_path def noc(self, cmd: str) -> str: return self._execute_cmd(r'\NoxConsole.exe" ' + cmd) def adb(self, cmd: str,_timeout=60) -> str: return self._execute_cmd(r'\nox_adb.exe" ' + cmd) def shell(self, index: int, cmd: str) -> str: return self.adb(f'-s {self.get_device_ip_by_index(index)} shell {cmd}') def start(self, index: int): super().start(index) self.noc(f'launch -index:{index}') def is_started(self, index: int) -> tuple[bool, EmulatorInfo | None]: emulator_info = None result = self.is_enter_android(index) if result: emulator_info = self.get_info(index) return result, emulator_info def is_exists(self, index: int) -> bool: return os.path.exists(os.path.join(self.get_install_path(), 'BignoxVMS', f'Nox_{index}')) def close(self, index: int): super().close(index) self.noc(f'quit -index:{index}') def close_all(self): self.noc(f'quitall') def add(self, name: str): self.noc(f'add -name:{name} -systemtype:{self.system_type}') def remove(self, index: int): self.noc(f'remove -index:{index}') def rename(self, index: int, name: str): self.noc(f'rename -index:{index} -title:{name}') def restore(self, index: int, file_path: str): self.noc(f'restore -index:{index} -file:{file_path}') def is_enter_android(self, index: int) -> bool: return self.shell(index, 'getprop sys.boot_completed').strip() == '1' def read_emulator_file(self, index: int, file_path: str) -> str: """ 读取模拟器文本文件 :param index: 模拟器索引 :param file_path:模拟器文件路径 :return: """ return super().read_emulator_file(index, file_path) def write_emulator_file(self, index: int, file_path: str, content: str) -> bool: """ 写入模拟器文本文件 :param index: 模拟器索引 :param file_path:模拟器文件路径 :param content: 写入内容 :return: """ return super().write_emulator_file(index, file_path, content) def get_list(self) -> list[EmulatorInfo]: """ 获取模拟器列表 :return: """ info_list = [] info_str = self.noc('list').strip() for line in info_str.split("\n"): tmp_list = line.split(',') if len(tmp_list) >= 7: info = EmulatorInfo(index=int(tmp_list[0]), name=tmp_list[2], top_handle=int(tmp_list[3], 16), bind_handle=int(tmp_list[4], 16), is_enter_android=None, pid=int(tmp_list[5]), vbox_pid=int(tmp_list[6]), width=None, height=None, dpi=None) info_list.append(info) return info_list def get_info(self, index: int) -> EmulatorInfo | None: info_str = self.noc('list').strip() for line in info_str.split("\n"): if line: tmp_list = line.split(',') # if int(tmp_list[0]) == index: # tmp_handle = WindowOperate.find_subwindow_by_title_and_class(int(tmp_list[3], 16),'sub','subWin') # bind_handle = WindowOperate.get_parent_window(tmp_handle) # return EmulatorInfo(index=int(tmp_list[0]), name=tmp_list[2], top_handle=int(tmp_list[3], 16), # bind_handle=bind_handle, is_enter_android=None, pid=int(tmp_list[5]), # vbox_pid=int(tmp_list[6]), width=None, height=None, dpi=None) return None def modify(self,index,cpu=None, memory=None, resolution=None, manufacturer=None, model=None, pnumber=None, imei=None, imsi=None, simserial=None, androidid=None, mac=None, autorotate=None, lockwindow=None, root=None): """ 修改模拟器机型 :param index: 模拟器索引 :param args: [--resolution ] [--cpu <1 | 2 | 3 | 4>] [--memory <256 | 512 | 768 | 1024 | 1536 | 2048 | 4096 | 8192>] [--manufacturer asus] [--model ASUS_Z00DUO] [--pnumber 13800000000] [--imei ] [--imsi ] [--simserial ] [--androidid ] [--mac ] [--autorotate <1 | 0> 自动旋转 [--lockwindow <1 | 0> 锁定窗口大小 [--root <1 | 0> """ formatted_args = " ".join([f"-{key}:{value}" for key, value in locals().items() if value and key not in ['index', 'self']]) self.noc(f'modify -index:{index} {formatted_args}') self.set_config(index, 'vm_androidid', androidid) def get_config(self,index:int, key:str): try: config_file = os.path.join(self.config_path, f'clone_Nox_{index}_conf.ini') if os.path.exists(config_file): config = ConfigIni(config_file) return config.get('setting', key) return None except Exception as e: logger.exception("get_config error: {e}") return None def set_config(self,index:int, key:str,value:str): try: config_file = os.path.join(self.config_path, f'clone_Nox_{index}_conf.ini') if os.path.exists(config_file): config = ConfigIni(config_file) config.set('setting', key, value) except Exception as e: logger.exception("get_config error: {e}") def get_manufacturer(self, index: int): return self.shell(index, 'getprop persist.nox.manufacturer').strip() def get_model(self, index: int): return self.shell(index, 'getprop persist.nox.model').strip() def get_android_version(self, index: int): return self.shell(index, 'getprop ro.build.version.release').strip() def get_android_id(self, index: int): return self.shell(index, 'getprop persist.nox.androidid').strip() def get_IMEI(self, index: int): return self.shell(index, 'getprop persist.nox.modem.imei').strip() def get_IMSI(self, index: int): return self.shell(index, 'getprop persist.nox.modem.imsi').strip() def get_mac(self, index: int): return self.shell(index, 'cat /sys/class/net/wlan0/address').strip() def get_resolution(self, index: int): return self.shell(index, 'wm size') def get_sim_serial(self, index: int): return self.shell(index, 'persist.nox.modem.serial').strip() def get_phone_number(self, index: int): return self.shell(index, 'persist.nox.modem.phonumber').strip() def get_ip(self, index: int): tmp_str = self.shell(index, 'ifconfig eth0') return Utils.extract_between_strings(tmp_str, 'addr:', ' ').strip() def set_share_dir(self, index: int, path: str, dir_type=None) -> bool: logger.info(f"Setting shared directory for emulator {index} to {path}") try: config_file = os.path.join(self.config_path, f'clone_Nox_{index}_conf.ini') if os.path.exists(config_file): config = ConfigIni(config_file) config.set('setting', 'share_path', path) return True return False except FileNotFoundError: logger.exception("The file was not found.") except Exception as e: logger.exception(f"An unexpected error occurred: {e}") return False def get_share_dir(self, index: int): try: config_file = os.path.join(self.config_path, f'clone_Nox_{index}_conf.ini') if os.path.exists(config_file): config = ConfigIni(config_file) share_path = config.get('setting', 'share_path') if share_path: return True, share_path return False, None return False, None except FileNotFoundError: logger.exception("The file was not found.") except Exception as e: logger.exception(f"An unexpected error occurred: {e}") return False def start_app(self, index, packagename): return self.noc(f"runapp -index:{index} -packagename:{packagename}") @staticmethod def get_ys_install_dir() -> str: temp_path = Utils.get_registry_value(r"SOFTWARE\WOW6432Node\DuoDianOnline\SetupInfo", "InstallPath",winreg.HKEY_LOCAL_MACHINE) if temp_path: install_path = os.path.join(temp_path, "bin") else: install_path = Utils.get_install_dir_from_shortcut(r'\夜神模拟器.lnk').replace(r'\Nox.exe', "") return install_path @staticmethod def get_device_ip_by_index(index: int): return '127.0.0.1:' + str(62024 + index) @staticmethod def get_index_by_device_ip(device_ip: str): return int(device_ip.replace('127.0.0.1:', '')) - 62024