ys_operate.py 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. import os
  2. import threading
  3. import winreg
  4. from tools.emulator.emulator import Emulator
  5. from model.custom_struct import EmulatorInfo
  6. from tools.ini_operate import ConfigIni
  7. from tools.log import logger
  8. from tools.utils import Utils
  9. # from tools.window_operate import WindowOperate
  10. class YS(Emulator):
  11. def __init__(self,system_type=9):
  12. self.install_path = self.get_ys_install_dir()
  13. super().__init__(self.install_path)
  14. self.emulator_type = 'ys'
  15. self.system_type = system_type
  16. if not os.path.exists(self.install_path + r'\NoxConsole.exe'):
  17. raise ValueError("模拟器安装路径错误")
  18. self.config_path = os.path.join(os.getenv('LOCALAPPDATA'), 'Nox')
  19. self._lock = threading.Lock()
  20. def set_install_path(self, path: str):
  21. self.install_path = path
  22. def get_install_path(self) -> str:
  23. return self.install_path
  24. def noc(self, cmd: str) -> str:
  25. return self._execute_cmd(r'\NoxConsole.exe" ' + cmd)
  26. def adb(self, cmd: str,_timeout=60) -> str:
  27. return self._execute_cmd(r'\nox_adb.exe" ' + cmd)
  28. def shell(self, index: int, cmd: str) -> str:
  29. return self.adb(f'-s {self.get_device_ip_by_index(index)} shell {cmd}')
  30. def start(self, index: int):
  31. super().start(index)
  32. self.noc(f'launch -index:{index}')
  33. def is_started(self, index: int) -> tuple[bool, EmulatorInfo | None]:
  34. emulator_info = None
  35. result = self.is_enter_android(index)
  36. if result:
  37. emulator_info = self.get_info(index)
  38. return result, emulator_info
  39. def is_exists(self, index: int) -> bool:
  40. return os.path.exists(os.path.join(self.get_install_path(), 'BignoxVMS', f'Nox_{index}'))
  41. def close(self, index: int):
  42. super().close(index)
  43. self.noc(f'quit -index:{index}')
  44. def close_all(self):
  45. self.noc(f'quitall')
  46. def add(self, name: str):
  47. self.noc(f'add -name:{name} -systemtype:{self.system_type}')
  48. def remove(self, index: int):
  49. self.noc(f'remove -index:{index}')
  50. def rename(self, index: int, name: str):
  51. self.noc(f'rename -index:{index} -title:{name}')
  52. def restore(self, index: int, file_path: str):
  53. self.noc(f'restore -index:{index} -file:{file_path}')
  54. def is_enter_android(self, index: int) -> bool:
  55. return self.shell(index, 'getprop sys.boot_completed').strip() == '1'
  56. def read_emulator_file(self, index: int, file_path: str) -> str:
  57. """
  58. 读取模拟器文本文件
  59. :param index: 模拟器索引
  60. :param file_path:模拟器文件路径
  61. :return:
  62. """
  63. return super().read_emulator_file(index, file_path)
  64. def write_emulator_file(self, index: int, file_path: str, content: str) -> bool:
  65. """
  66. 写入模拟器文本文件
  67. :param index: 模拟器索引
  68. :param file_path:模拟器文件路径
  69. :param content: 写入内容
  70. :return:
  71. """
  72. return super().write_emulator_file(index, file_path, content)
  73. def get_list(self) -> list[EmulatorInfo]:
  74. """
  75. 获取模拟器列表
  76. :return:
  77. """
  78. info_list = []
  79. info_str = self.noc('list').strip()
  80. for line in info_str.split("\n"):
  81. tmp_list = line.split(',')
  82. if len(tmp_list) >= 7:
  83. info = EmulatorInfo(index=int(tmp_list[0]), name=tmp_list[2], top_handle=int(tmp_list[3], 16),
  84. bind_handle=int(tmp_list[4], 16), is_enter_android=None, pid=int(tmp_list[5]),
  85. vbox_pid=int(tmp_list[6]), width=None, height=None, dpi=None)
  86. info_list.append(info)
  87. return info_list
  88. def get_info(self, index: int) -> EmulatorInfo | None:
  89. info_str = self.noc('list').strip()
  90. for line in info_str.split("\n"):
  91. if line:
  92. tmp_list = line.split(',')
  93. # if int(tmp_list[0]) == index:
  94. # tmp_handle = WindowOperate.find_subwindow_by_title_and_class(int(tmp_list[3], 16),'sub','subWin')
  95. # bind_handle = WindowOperate.get_parent_window(tmp_handle)
  96. # return EmulatorInfo(index=int(tmp_list[0]), name=tmp_list[2], top_handle=int(tmp_list[3], 16),
  97. # bind_handle=bind_handle, is_enter_android=None, pid=int(tmp_list[5]),
  98. # vbox_pid=int(tmp_list[6]), width=None, height=None, dpi=None)
  99. return None
  100. def modify(self,index,cpu=None, memory=None, resolution=None, manufacturer=None, model=None, pnumber=None,
  101. imei=None, imsi=None, simserial=None, androidid=None, mac=None, autorotate=None, lockwindow=None, root=None):
  102. """
  103. 修改模拟器机型
  104. :param index: 模拟器索引
  105. :param args:
  106. [--resolution <w,h,dpi>]
  107. [--cpu <1 | 2 | 3 | 4>]
  108. [--memory <256 | 512 | 768 | 1024 | 1536 | 2048 | 4096 | 8192>]
  109. [--manufacturer asus]
  110. [--model ASUS_Z00DUO]
  111. [--pnumber 13800000000]
  112. [--imei <auto | 865166023949731>]
  113. [--imsi <auto | 460000000000000>]
  114. [--simserial <auto | 89860000000000000000>]
  115. [--androidid <auto | 0123456789abcdef>]
  116. [--mac <auto | 000000000000>]
  117. [--autorotate <1 | 0> 自动旋转
  118. [--lockwindow <1 | 0> 锁定窗口大小
  119. [--root <1 | 0>
  120. """
  121. formatted_args = " ".join([f"-{key}:{value}" for key, value in locals().items() if value and key not in ['index', 'self']])
  122. self.noc(f'modify -index:{index} {formatted_args}')
  123. self.set_config(index, 'vm_androidid', androidid)
  124. def get_config(self,index:int, key:str):
  125. try:
  126. config_file = os.path.join(self.config_path, f'clone_Nox_{index}_conf.ini')
  127. if os.path.exists(config_file):
  128. config = ConfigIni(config_file)
  129. return config.get('setting', key)
  130. return None
  131. except Exception as e:
  132. logger.exception("get_config error: {e}")
  133. return None
  134. def set_config(self,index:int, key:str,value:str):
  135. try:
  136. config_file = os.path.join(self.config_path, f'clone_Nox_{index}_conf.ini')
  137. if os.path.exists(config_file):
  138. config = ConfigIni(config_file)
  139. config.set('setting', key, value)
  140. except Exception as e:
  141. logger.exception("get_config error: {e}")
  142. def get_manufacturer(self, index: int):
  143. return self.shell(index, 'getprop persist.nox.manufacturer').strip()
  144. def get_model(self, index: int):
  145. return self.shell(index, 'getprop persist.nox.model').strip()
  146. def get_android_version(self, index: int):
  147. return self.shell(index, 'getprop ro.build.version.release').strip()
  148. def get_android_id(self, index: int):
  149. return self.shell(index, 'getprop persist.nox.androidid').strip()
  150. def get_IMEI(self, index: int):
  151. return self.shell(index, 'getprop persist.nox.modem.imei').strip()
  152. def get_IMSI(self, index: int):
  153. return self.shell(index, 'getprop persist.nox.modem.imsi').strip()
  154. def get_mac(self, index: int):
  155. return self.shell(index, 'cat /sys/class/net/wlan0/address').strip()
  156. def get_resolution(self, index: int):
  157. return self.shell(index, 'wm size')
  158. def get_sim_serial(self, index: int):
  159. return self.shell(index, 'persist.nox.modem.serial').strip()
  160. def get_phone_number(self, index: int):
  161. return self.shell(index, 'persist.nox.modem.phonumber').strip()
  162. def get_ip(self, index: int):
  163. tmp_str = self.shell(index, 'ifconfig eth0')
  164. return Utils.extract_between_strings(tmp_str, 'addr:', ' ').strip()
  165. def set_share_dir(self, index: int, path: str, dir_type=None) -> bool:
  166. logger.info(f"Setting shared directory for emulator {index} to {path}")
  167. try:
  168. config_file = os.path.join(self.config_path, f'clone_Nox_{index}_conf.ini')
  169. if os.path.exists(config_file):
  170. config = ConfigIni(config_file)
  171. config.set('setting', 'share_path', path)
  172. return True
  173. return False
  174. except FileNotFoundError:
  175. logger.exception("The file was not found.")
  176. except Exception as e:
  177. logger.exception(f"An unexpected error occurred: {e}")
  178. return False
  179. def get_share_dir(self, index: int):
  180. try:
  181. config_file = os.path.join(self.config_path, f'clone_Nox_{index}_conf.ini')
  182. if os.path.exists(config_file):
  183. config = ConfigIni(config_file)
  184. share_path = config.get('setting', 'share_path')
  185. if share_path:
  186. return True, share_path
  187. return False, None
  188. return False, None
  189. except FileNotFoundError:
  190. logger.exception("The file was not found.")
  191. except Exception as e:
  192. logger.exception(f"An unexpected error occurred: {e}")
  193. return False
  194. def start_app(self, index, packagename):
  195. return self.noc(f"runapp -index:{index} -packagename:{packagename}")
  196. @staticmethod
  197. def get_ys_install_dir() -> str:
  198. temp_path = Utils.get_registry_value(r"SOFTWARE\WOW6432Node\DuoDianOnline\SetupInfo", "InstallPath",winreg.HKEY_LOCAL_MACHINE)
  199. if temp_path:
  200. install_path = os.path.join(temp_path, "bin")
  201. else:
  202. install_path = Utils.get_install_dir_from_shortcut(r'\夜神模拟器.lnk').replace(r'\Nox.exe', "")
  203. return install_path
  204. @staticmethod
  205. def get_device_ip_by_index(index: int):
  206. return '127.0.0.1:' + str(62024 + index)
  207. @staticmethod
  208. def get_index_by_device_ip(device_ip: str):
  209. return int(device_ip.replace('127.0.0.1:', '')) - 62024