file_utils.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. import hashlib
  2. import os
  3. import os.path
  4. import platform
  5. import shutil
  6. import subprocess
  7. import sys
  8. def getFullToolPath(name):
  9. '''
  10. 获取工具的目录
  11. '''
  12. return get_full_path('tools', name)
  13. def getFullGameApk(name):
  14. '''
  15. 获取游戏的原始包
  16. '''
  17. return get_full_path('game', name, name + '.apk')
  18. def get_cache_game_apk(game, random, sdk):
  19. """
  20. 获取游戏的原始包
  21. """
  22. return get_full_path('game', game, sdk, random, game + '.apk')
  23. def get_full_sdk_path(sdk):
  24. """
  25. 获取sdk的目录
  26. """
  27. return get_full_path('sdk', sdk)
  28. def get_full_log_sdk_path(sdk, is_beta=False):
  29. """
  30. 获取logSdk的目录
  31. """
  32. if is_beta:
  33. return get_full_path('log_sdk_beta', sdk)
  34. else:
  35. return get_full_path('log_sdk', sdk)
  36. def get_full_log_sdk_v2_path(sdk, is_beta=False):
  37. """
  38. 获取logSdk的目录
  39. """
  40. if is_beta:
  41. return get_full_path('log_sdk_v2_beta', sdk)
  42. else:
  43. return get_full_path('log_sdk_v2', sdk)
  44. def getFullOaidSDKPath(version):
  45. """
  46. 获取oaid_sdk的目录
  47. """
  48. return get_full_path('oaid_sdk', version)
  49. def get_decompile_path(game, sdk, sub_channel, cache):
  50. """
  51. 获取解包的目录
  52. """
  53. return get_full_path('gen', game, sdk, sub_channel, cache)
  54. def getSubChannelPath(game, sdk, sub_channel):
  55. '''
  56. 获取子渠道的目录
  57. '''
  58. return get_full_path('game', game, sdk, sub_channel)
  59. def getSubChannelPath(game, sdk, tag, subChannel):
  60. """
  61. 获取子渠道的目录
  62. """
  63. return get_full_path('game', game, sdk, tag, subChannel)
  64. def getChannelPath(game, sdk):
  65. '''
  66. 获取渠道的目录
  67. '''
  68. return get_full_path('game', game, sdk)
  69. def getChannelPath(game, tag, sdk):
  70. """
  71. 获取渠道的目录
  72. """
  73. return get_full_path('game', game, sdk, tag)
  74. def get_full_game_path(game):
  75. """
  76. 获取游戏的目录
  77. """
  78. return get_full_path('game', game)
  79. def getFullInternalPath():
  80. """
  81. 获取内部目录
  82. """
  83. return os.path.join(get_current_path(), 'internal')
  84. def get_full_path(gen, *name):
  85. path = os.path.join(get_current_path(), gen)
  86. for n in name:
  87. path = os.path.join(path, str(n))
  88. return path
  89. def get_current_path():
  90. """
  91. 当前目录
  92. """
  93. return sys.path[0]
  94. def exec_format_cmd(cmd, cd=None):
  95. """
  96. 执行cmd命令
  97. 返回值:None —— 子进程尚未结束;
  98. ==0 —— 子进程正常退出;
  99. > 0—— 子进程异常退出,return code对应于出错码;
  100. < 0—— 子进程被信号杀掉了。
  101. """
  102. '''print(cmd)
  103. p = os.popen(cmd)
  104. print(p.read())'''
  105. ret = 0
  106. try:
  107. s = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, cwd=cd)
  108. std_output, err_output = s.communicate()
  109. if platform.system() == 'Windows':
  110. std_output = std_output.decode('gbk')
  111. err_output = err_output.decode('gbk')
  112. '''
  113. None —— 子进程尚未结束;
  114. ==0 —— 子进程正常退出;
  115. > 0—— 子进程异常退出,return code对应于出错码;
  116. < 0—— 子进程被信号杀掉了。
  117. '''
  118. ret = s.returncode
  119. if ret:
  120. print('*******ERROR*******')
  121. print(std_output)
  122. print(err_output)
  123. print('*******************')
  124. cmd = 'error::' + cmd + ' !!!exec Fail!!! '
  125. else:
  126. print(std_output)
  127. print(err_output)
  128. cmd = cmd + ' !!!exec success!!! '
  129. print(cmd)
  130. except Exception as e:
  131. print('Exception ' + e)
  132. return 1
  133. return ret
  134. def exec_jar_cmd(jar, params):
  135. """
  136. 执行jar
  137. """
  138. return exec_format_cmd('java -jar "%s" %s' % (jar, params))
  139. def copy_file_all_dir(from_dir, to_dir, delete=False, support=None):
  140. """
  141. 拷贝目录下所有文件文件
  142. """
  143. ret = copy_dir(from_dir, to_dir, delete, support)
  144. if ret:
  145. return ret
  146. if delete:
  147. delete_folder(from_dir)
  148. return 0
  149. def copy_dir(from_dir, to_dir, delete=False, support=None):
  150. """
  151. 拷贝目录下所有文件文件
  152. """
  153. # print('copy all file %s --> %s' % (fromDir, toDir))
  154. if not os.path.exists(from_dir):
  155. print('%s not exists!' % from_dir)
  156. return 1
  157. if not os.path.isdir(from_dir):
  158. print('%s not a dir!' % from_dir)
  159. return 1
  160. for fromFile in os.listdir(from_dir):
  161. from_file_path = os.path.join(from_dir, fromFile)
  162. to_file_path = os.path.join(to_dir, fromFile)
  163. if os.path.isdir(from_file_path):
  164. # 不支持的类型
  165. if support is not None and fromFile not in support:
  166. continue
  167. ret = copy_dir(from_file_path, to_file_path, delete, support)
  168. if ret:
  169. return ret
  170. else:
  171. ret = copy_file(from_file_path, to_file_path, delete)
  172. if ret:
  173. return ret
  174. return 0
  175. def copy_file(from_file, to_file, delete=False):
  176. """
  177. 拷贝文件
  178. """
  179. if not os.path.isfile(from_file):
  180. print('----> %s not a file!' % from_file)
  181. return 1
  182. # 分离文件名和路径
  183. file_path, file_name = os.path.split(to_file)
  184. if not os.path.exists(file_path):
  185. # 创建路径
  186. os.makedirs(file_path)
  187. if delete:
  188. # 移动文件
  189. shutil.move(from_file, to_file)
  190. else:
  191. # 复制文件
  192. shutil.copyfile(from_file, to_file)
  193. return 0
  194. def replace_content(file, old_txt, new_txt):
  195. """
  196. 全局替换
  197. """
  198. with open_file(file, 'r+') as f:
  199. t = f.read()
  200. t = t.replace(old_txt, new_txt)
  201. # 读写偏移位置移到最开始处
  202. f.seek(0, 0)
  203. # 清空内容
  204. f.truncate()
  205. f.write(t)
  206. def read_file(file):
  207. """
  208. 读取文件内容
  209. """
  210. content = ''
  211. with open_file(file, 'r') as f:
  212. content = f.read()
  213. return content
  214. def create_file(file, content):
  215. """
  216. 创建文件
  217. """
  218. file_path, file_name = os.path.split(file) # 分离文件名和路径
  219. if not os.path.exists(file_path):
  220. os.makedirs(file_path)
  221. with open_file(file, 'w') as f:
  222. f.write(content)
  223. f.close()
  224. def open_file(file, mode):
  225. return open(file, mode, encoding='UTF-8')
  226. def delete_folder(folder):
  227. """
  228. 删除目录以及目录下的文件
  229. """
  230. if not os.path.exists(folder):
  231. return
  232. shutil.rmtree(folder)
  233. def get_aapt_path():
  234. """
  235. 获取aapt
  236. """
  237. return getToolWithSystem('aapt')
  238. def get_aapt2_path():
  239. """
  240. 获取aapt2
  241. """
  242. return getToolWithSystem('aapt2')
  243. def get_android_compile_tool_path():
  244. """
  245. 获取android.jar
  246. """
  247. return getFullToolPath('android.jar')
  248. def get_dx_path():
  249. """
  250. 获取dx.jar
  251. """
  252. return getFullToolPath('dx.jar')
  253. def get_d8_path():
  254. """
  255. 获取d8.jar
  256. """
  257. return getFullToolPath('d8.jar')
  258. def get_zipalign_path():
  259. """
  260. 获取zipalign
  261. """
  262. return getToolWithSystem('zipalign')
  263. def get_multidex_path():
  264. """
  265. 获取multidex.jar
  266. """
  267. return getFullToolPath('android-support-multidex.jar')
  268. def get_apktool_path():
  269. """
  270. 获取apktool.jar
  271. """
  272. return getFullToolPath('apktool_2.6.0.jar')
  273. def get_baksmali_path():
  274. """
  275. 获取baksmali.jar
  276. """
  277. return getFullToolPath('baksmali-2.3.jar')
  278. def get_apksigner_path():
  279. '''
  280. 获取apksigner.jar
  281. '''
  282. return getFullToolPath('apksigner.jar')
  283. def getWallePath():
  284. '''
  285. 获取walle-cli-all.jar
  286. '''
  287. return getFullToolPath('walle-cli-all.jar')
  288. def getOutApkPath(game, sdk, subChannel, cache):
  289. '''
  290. 获取输出的apk的目录
  291. '''
  292. return get_full_path('target', game, sdk, cache, '%s_%s_%s.apk' % (game, sdk, subChannel))
  293. def getTargetApkPath(game, sdk, cache):
  294. '''
  295. 获取输出的apk的目录
  296. '''
  297. return get_full_path('target', game, sdk, cache)
  298. def getAlignApkPath(game, sdk, subChannel, cache):
  299. '''
  300. 获取输出的apk的目录
  301. '''
  302. return get_full_path('target', game, sdk, cache, '%s_%s_%s_align.apk' % (game, sdk, subChannel))
  303. def get_signed_apk_path(game, sdk, sub_channel, cache):
  304. """
  305. 获取输出的apk的目录
  306. """
  307. return get_full_path('target', game, sdk, cache, '%s_%s_%s_signed.apk' % (game, sdk, sub_channel))
  308. def getWalleApkPath(game, sdk, subChannel, cache):
  309. """
  310. 获取输出的apk的目录
  311. """
  312. return get_full_path('target', game, sdk, cache, '%s_%s_%s_walled.apk' % (game, sdk, subChannel))
  313. def getRenameApkPath(game, sdk, cache, name):
  314. '''
  315. 重命名输出的apk名称
  316. '''
  317. return get_full_path('target', game, sdk, cache, '%s.apk' % name)
  318. def getSignApkPath(game, sdk, subChannel, cache):
  319. '''
  320. 获取输出的apk的目录
  321. '''
  322. return get_full_path('target', game, sdk, cache, '%s_%s_%s_signed.apk' % (game, sdk, subChannel))
  323. def get_package_path(basePath, packageName):
  324. '''
  325. 包名对应的目录
  326. '''
  327. packageNameSplit = packageName.split('.')
  328. newPath = basePath
  329. for item in packageNameSplit:
  330. newPath = os.path.join(newPath, item)
  331. return newPath
  332. def getToolWithSystem(tool):
  333. '''
  334. 获取系统相关工具
  335. '''
  336. system = getSystemPath()
  337. suffix = getSystemSuffix()
  338. return os.path.join(getFullToolPath(system), tool + suffix)
  339. def getSystemPath():
  340. '''
  341. 获取系统目录
  342. '''
  343. if platform.system() == 'Windows':
  344. return 'win'
  345. elif platform.system() == 'Darwin':
  346. print('---------macos----------')
  347. return 'macos'
  348. else:
  349. return 'linux'
  350. def getSystemSuffix():
  351. """
  352. 系统工具后缀
  353. """
  354. if platform.system() == 'Windows':
  355. return '.exe'
  356. else:
  357. return ''
  358. def get_application_smali_index(file):
  359. """
  360. 获取application.smali的MultiDex信息
  361. """
  362. content = ('invoke-super', '->attachBaseContext(Landroid/content/Context;)V')
  363. index = -1
  364. with open_file(file, 'r') as f:
  365. line = f.readline()
  366. while line:
  367. if content[0] in line and content[1] in line:
  368. index = f.tell()
  369. break
  370. line = f.readline()
  371. return index
  372. def insert_application_smali(file, index):
  373. """
  374. 获取application.smali插入MultiDex的信息
  375. """
  376. if index == -1:
  377. # append
  378. content = '''# virtual methods
  379. .method protected attachBaseContext(Landroid/content/Context;)V
  380. .locals 0
  381. .param p1, "context" # Landroid/content/Context;
  382. invoke-super {p0, p1}, Landroid/app/Application;->attachBaseContext(Landroid/content/Context;)V
  383. invoke-static {p0}, Landroid/support/multidex/MultiDex;->install(Landroid/content/Context;)V
  384. return-void
  385. .end method'''
  386. with open_file(file, 'a') as f:
  387. f.write(content)
  388. else:
  389. # insert
  390. content = '\n\tinvoke-static {p0}, Landroid/support/multidex/MultiDex;->install(Landroid/content/Context;)V\n'
  391. with open_file(file, 'r+') as f:
  392. f.seek(index)
  393. last = f.read()
  394. f.seek(index)
  395. f.write(content)
  396. f.write(last)
  397. return 0
  398. def changeVersion(yml, versionCode=None, versionName=None, targetSdkVersion=None):
  399. if versionCode is None and versionName is None and targetSdkVersion is None:
  400. return 0
  401. tag = ['versionCode:', 'versionName:', 'targetSdkVersion:']
  402. with open_file(yml, 'r+') as f:
  403. content = ''
  404. line = f.readline()
  405. while line:
  406. if versionCode is not None and tag[0] in line:
  407. content += ' versionCode: \'%s\'\n' % versionCode
  408. elif versionName is not None and tag[1] in line:
  409. content += ' versionName: %s\n' % versionName
  410. elif targetSdkVersion is not None and tag[2] in line:
  411. content += ' targetSdkVersion: \'%s\'\n' % targetSdkVersion
  412. else:
  413. content += line
  414. line = f.readline()
  415. f.seek(0, 0)
  416. f.truncate()
  417. f.write(content)
  418. return 0
  419. def change_min_sdk_version(yml, min_sdk_version):
  420. with open_file(yml, 'r+') as f:
  421. content = ''
  422. line = f.readline()
  423. while line:
  424. if 'minSdkVersion' in line:
  425. start = line.index("'")
  426. version = int(line[start + 1:-2])
  427. if version < min_sdk_version:
  428. content += ' minSdkVersion: \'%s\'\n' % min_sdk_version
  429. else:
  430. return 0
  431. else:
  432. content += line
  433. line = f.readline()
  434. f.seek(0, 0)
  435. f.truncate()
  436. f.write(content)
  437. return 0
  438. def list_files(src, res_files):
  439. """
  440. 列出目录下所有的文件
  441. """
  442. if os.path.exists(src):
  443. if os.path.isfile(src):
  444. res_files.append(src)
  445. elif os.path.isdir(src):
  446. for f in os.listdir(src):
  447. list_files(os.path.join(src, f), res_files)
  448. return res_files
  449. def get_file_md5(f):
  450. """
  451. 获取文件的md5
  452. """
  453. m = hashlib.md5()
  454. while True:
  455. data = f.read(1024) # 将文件分块读取
  456. if not data:
  457. break
  458. m.update(data)
  459. return m.hexdigest()
  460. def equals_file(file1, file2):
  461. """
  462. 比较两个文件是否是同一个文件
  463. """
  464. with open(file1, 'rb') as f1, open(file2, 'rb') as f2:
  465. file1Md5 = get_file_md5(f1)
  466. file2Md5 = get_file_md5(f2)
  467. # print('file1Md5:',file1Md5)
  468. # print('file2Md5:',file2Md5)
  469. return file1Md5 == file2Md5
  470. def get_exec_permission(file):
  471. """
  472. linux下获取执行权限
  473. """
  474. if platform.system() == 'Windows':
  475. return 0
  476. return exec_format_cmd('chmod +x "%s"' % file)
  477. def copy_sdk_smali_code(game, sdk, sub_channel, config):
  478. """
  479. 拷贝代码
  480. """
  481. print('copy sdk smali code --> %s' % sdk)
  482. sdkPath = get_full_sdk_path(sdk)
  483. xmyFile = os.path.join(sdkPath, 'smali')
  484. decompliePath = get_decompile_path(game, sdk, sub_channel, config['cache'])
  485. smaliPath = os.path.join(decompliePath, 'smali')
  486. ret = copy_dir(xmyFile, smaliPath)
  487. return ret
  488. def copy_game_smali_code(game, sdk, sub_channel, config):
  489. """
  490. 拷贝代码
  491. """
  492. print('copy game smali code --> %s' % sdk)
  493. if not config['aapt2disable']:
  494. print('aapt2disable = false ~~~')
  495. return 0
  496. path = os.path.join(get_current_path(), "game_script", game, "gameSmali")
  497. print('copyDir = gameSmali~~~')
  498. decompliePath = get_decompile_path(game, sdk, sub_channel, config['cache'])
  499. smaliPath = os.path.join(decompliePath, 'smali')
  500. ret = copy_dir(path, smaliPath)
  501. return ret