import hashlib import os import os.path import platform import shutil import subprocess import sys def getFullToolPath(name): ''' 获取工具的目录 ''' return get_full_path('tools', name) def getFullGameApk(name): ''' 获取游戏的原始包 ''' return get_full_path('game', name, name + '.apk') def get_cache_game_apk(game, random, sdk): """ 获取游戏的原始包 """ return get_full_path('game', game, sdk, random, game + '.apk') def get_full_sdk_path(sdk): """ 获取sdk的目录 """ return get_full_path('sdk', sdk) def get_full_log_sdk_path(sdk, is_beta=False): """ 获取logSdk的目录 """ if is_beta: return get_full_path('log_sdk_beta', sdk) else: return get_full_path('log_sdk', sdk) def get_full_log_sdk_v2_path(sdk, is_beta=False): """ 获取logSdk的目录 """ if is_beta: return get_full_path('log_sdk_v2_beta', sdk) else: return get_full_path('log_sdk_v2', sdk) def getFullOaidSDKPath(version): """ 获取oaid_sdk的目录 """ return get_full_path('oaid_sdk', version) def get_decompile_path(game, sdk, sub_channel, cache): """ 获取解包的目录 """ return get_full_path('gen', game, sdk, sub_channel, cache) def getSubChannelPath(game, sdk, sub_channel): ''' 获取子渠道的目录 ''' return get_full_path('game', game, sdk, sub_channel) def getSubChannelPath(game, sdk, tag, subChannel): """ 获取子渠道的目录 """ return get_full_path('game', game, sdk, tag, subChannel) def getChannelPath(game, sdk): ''' 获取渠道的目录 ''' return get_full_path('game', game, sdk) def getChannelPath(game, tag, sdk): """ 获取渠道的目录 """ return get_full_path('game', game, sdk, tag) def get_full_game_path(game): """ 获取游戏的目录 """ return get_full_path('game', game) def getFullInternalPath(): """ 获取内部目录 """ return os.path.join(get_current_path(), 'internal') def get_full_path(gen, *name): path = os.path.join(get_current_path(), gen) for n in name: path = os.path.join(path, str(n)) return path def get_current_path(): """ 当前目录 """ return sys.path[0] def exec_format_cmd(cmd, cd=None): """ 执行cmd命令 返回值:None —— 子进程尚未结束; ==0 —— 子进程正常退出; > 0—— 子进程异常退出,return code对应于出错码; < 0—— 子进程被信号杀掉了。 """ '''print(cmd) p = os.popen(cmd) print(p.read())''' ret = 0 try: s = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, cwd=cd) std_output, err_output = s.communicate() if platform.system() == 'Windows': std_output = std_output.decode('gbk') err_output = err_output.decode('gbk') ''' None —— 子进程尚未结束; ==0 —— 子进程正常退出; > 0—— 子进程异常退出,return code对应于出错码; < 0—— 子进程被信号杀掉了。 ''' ret = s.returncode if ret: print('*******ERROR*******') print(std_output) print(err_output) print('*******************') cmd = 'error::' + cmd + ' !!!exec Fail!!! ' else: print(std_output) print(err_output) cmd = cmd + ' !!!exec success!!! ' print(cmd) except Exception as e: print('Exception ' + e) return 1 return ret def exec_jar_cmd(jar, params): """ 执行jar """ return exec_format_cmd('java -jar "%s" %s' % (jar, params)) def copy_file_all_dir(from_dir, to_dir, delete=False, support=None): """ 拷贝目录下所有文件文件 """ ret = copy_dir(from_dir, to_dir, delete, support) if ret: return ret if delete: delete_folder(from_dir) return 0 def copy_dir(from_dir, to_dir, delete=False, support=None): """ 拷贝目录下所有文件文件 """ # print('copy all file %s --> %s' % (fromDir, toDir)) if not os.path.exists(from_dir): print('%s not exists!' % from_dir) return 1 if not os.path.isdir(from_dir): print('%s not a dir!' % from_dir) return 1 for fromFile in os.listdir(from_dir): from_file_path = os.path.join(from_dir, fromFile) to_file_path = os.path.join(to_dir, fromFile) if os.path.isdir(from_file_path): # 不支持的类型 if support is not None and fromFile not in support: continue ret = copy_dir(from_file_path, to_file_path, delete, support) if ret: return ret else: ret = copy_file(from_file_path, to_file_path, delete) if ret: return ret return 0 def copy_file(from_file, to_file, delete=False): """ 拷贝文件 """ if not os.path.isfile(from_file): print('----> %s not a file!' % from_file) return 1 # 分离文件名和路径 file_path, file_name = os.path.split(to_file) if not os.path.exists(file_path): # 创建路径 os.makedirs(file_path) if delete: # 移动文件 shutil.move(from_file, to_file) else: # 复制文件 shutil.copyfile(from_file, to_file) return 0 def replace_content(file, old_txt, new_txt): """ 全局替换 """ with open_file(file, 'r+') as f: t = f.read() t = t.replace(old_txt, new_txt) # 读写偏移位置移到最开始处 f.seek(0, 0) # 清空内容 f.truncate() f.write(t) def read_file(file): """ 读取文件内容 """ content = '' with open_file(file, 'r') as f: content = f.read() return content def create_file(file, content): """ 创建文件 """ file_path, file_name = os.path.split(file) # 分离文件名和路径 if not os.path.exists(file_path): os.makedirs(file_path) with open_file(file, 'w') as f: f.write(content) f.close() def open_file(file, mode): return open(file, mode, encoding='UTF-8') def delete_folder(folder): """ 删除目录以及目录下的文件 """ if not os.path.exists(folder): return shutil.rmtree(folder) def get_aapt_path(): """ 获取aapt """ return getToolWithSystem('aapt') def get_aapt2_path(): """ 获取aapt2 """ return getToolWithSystem('aapt2') def get_android_compile_tool_path(): """ 获取android.jar """ return getFullToolPath('android.jar') def get_dx_path(): """ 获取dx.jar """ return getFullToolPath('dx.jar') def get_d8_path(): """ 获取d8.jar """ return getFullToolPath('d8.jar') def get_zipalign_path(): """ 获取zipalign """ return getToolWithSystem('zipalign') def get_multidex_path(): """ 获取multidex.jar """ return getFullToolPath('android-support-multidex.jar') def get_apktool_path(): """ 获取apktool.jar """ return getFullToolPath('apktool_2.6.0.jar') def get_baksmali_path(): """ 获取baksmali.jar """ return getFullToolPath('baksmali-2.3.jar') def get_apksigner_path(): ''' 获取apksigner.jar ''' return getFullToolPath('apksigner.jar') def getWallePath(): ''' 获取walle-cli-all.jar ''' return getFullToolPath('walle-cli-all.jar') def getOutApkPath(game, sdk, subChannel, cache): ''' 获取输出的apk的目录 ''' return get_full_path('target', game, sdk, cache, '%s_%s_%s.apk' % (game, sdk, subChannel)) def getTargetApkPath(game, sdk, cache): ''' 获取输出的apk的目录 ''' return get_full_path('target', game, sdk, cache) def getAlignApkPath(game, sdk, subChannel, cache): ''' 获取输出的apk的目录 ''' return get_full_path('target', game, sdk, cache, '%s_%s_%s_align.apk' % (game, sdk, subChannel)) def get_signed_apk_path(game, sdk, sub_channel, cache): """ 获取输出的apk的目录 """ return get_full_path('target', game, sdk, cache, '%s_%s_%s_signed.apk' % (game, sdk, sub_channel)) def getWalleApkPath(game, sdk, subChannel, cache): """ 获取输出的apk的目录 """ return get_full_path('target', game, sdk, cache, '%s_%s_%s_walled.apk' % (game, sdk, subChannel)) def getRenameApkPath(game, sdk, cache, name): ''' 重命名输出的apk名称 ''' return get_full_path('target', game, sdk, cache, '%s.apk' % name) def getSignApkPath(game, sdk, subChannel, cache): ''' 获取输出的apk的目录 ''' return get_full_path('target', game, sdk, cache, '%s_%s_%s_signed.apk' % (game, sdk, subChannel)) def get_package_path(basePath, packageName): ''' 包名对应的目录 ''' packageNameSplit = packageName.split('.') newPath = basePath for item in packageNameSplit: newPath = os.path.join(newPath, item) return newPath def getToolWithSystem(tool): ''' 获取系统相关工具 ''' system = getSystemPath() suffix = getSystemSuffix() return os.path.join(getFullToolPath(system), tool + suffix) def getSystemPath(): ''' 获取系统目录 ''' if platform.system() == 'Windows': return 'win' elif platform.system() == 'Darwin': print('---------macos----------') return 'macos' else: return 'linux' def getSystemSuffix(): """ 系统工具后缀 """ if platform.system() == 'Windows': return '.exe' else: return '' def get_application_smali_index(file): """ 获取application.smali的MultiDex信息 """ content = ('invoke-super', '->attachBaseContext(Landroid/content/Context;)V') index = -1 with open_file(file, 'r') as f: line = f.readline() while line: if content[0] in line and content[1] in line: index = f.tell() break line = f.readline() return index def insert_application_smali(file, index): """ 获取application.smali插入MultiDex的信息 """ if index == -1: # append content = '''# virtual methods .method protected attachBaseContext(Landroid/content/Context;)V .locals 0 .param p1, "context" # Landroid/content/Context; invoke-super {p0, p1}, Landroid/app/Application;->attachBaseContext(Landroid/content/Context;)V invoke-static {p0}, Landroid/support/multidex/MultiDex;->install(Landroid/content/Context;)V return-void .end method''' with open_file(file, 'a') as f: f.write(content) else: # insert content = '\n\tinvoke-static {p0}, Landroid/support/multidex/MultiDex;->install(Landroid/content/Context;)V\n' with open_file(file, 'r+') as f: f.seek(index) last = f.read() f.seek(index) f.write(content) f.write(last) return 0 def changeVersion(yml, versionCode=None, versionName=None, targetSdkVersion=None): if versionCode is None and versionName is None and targetSdkVersion is None: return 0 tag = ['versionCode:', 'versionName:', 'targetSdkVersion:'] with open_file(yml, 'r+') as f: content = '' line = f.readline() while line: if versionCode is not None and tag[0] in line: content += ' versionCode: \'%s\'\n' % versionCode elif versionName is not None and tag[1] in line: content += ' versionName: %s\n' % versionName elif targetSdkVersion is not None and tag[2] in line: content += ' targetSdkVersion: \'%s\'\n' % targetSdkVersion else: content += line line = f.readline() f.seek(0, 0) f.truncate() f.write(content) return 0 def change_min_sdk_version(yml, min_sdk_version): with open_file(yml, 'r+') as f: content = '' line = f.readline() while line: if 'minSdkVersion' in line: start = line.index("'") version = int(line[start + 1:-2]) if version < min_sdk_version: content += ' minSdkVersion: \'%s\'\n' % min_sdk_version else: return 0 else: content += line line = f.readline() f.seek(0, 0) f.truncate() f.write(content) return 0 def list_files(src, res_files): """ 列出目录下所有的文件 """ if os.path.exists(src): if os.path.isfile(src): res_files.append(src) elif os.path.isdir(src): for f in os.listdir(src): list_files(os.path.join(src, f), res_files) return res_files def get_file_md5(f): """ 获取文件的md5 """ m = hashlib.md5() while True: data = f.read(1024) # 将文件分块读取 if not data: break m.update(data) return m.hexdigest() def equals_file(file1, file2): """ 比较两个文件是否是同一个文件 """ with open(file1, 'rb') as f1, open(file2, 'rb') as f2: file1Md5 = get_file_md5(f1) file2Md5 = get_file_md5(f2) # print('file1Md5:',file1Md5) # print('file2Md5:',file2Md5) return file1Md5 == file2Md5 def get_exec_permission(file): """ linux下获取执行权限 """ if platform.system() == 'Windows': return 0 return exec_format_cmd('chmod +x "%s"' % file) def copy_sdk_smali_code(game, sdk, sub_channel, config): """ 拷贝代码 """ print('copy sdk smali code --> %s' % sdk) sdkPath = get_full_sdk_path(sdk) xmyFile = os.path.join(sdkPath, 'smali') decompliePath = get_decompile_path(game, sdk, sub_channel, config['cache']) smaliPath = os.path.join(decompliePath, 'smali') ret = copy_dir(xmyFile, smaliPath) return ret def copy_game_smali_code(game, sdk, sub_channel, config): """ 拷贝代码 """ print('copy game smali code --> %s' % sdk) if not config['aapt2disable']: print('aapt2disable = false ~~~') return 0 path = os.path.join(get_current_path(), "game_script", game, "gameSmali") print('copyDir = gameSmali~~~') decompliePath = get_decompile_path(game, sdk, sub_channel, config['cache']) smaliPath = os.path.join(decompliePath, 'smali') ret = copy_dir(path, smaliPath) return ret