from importlib import reload import channel_special_replace,contants,path_utils from PrintLog import PrintLog import os, xml.etree.ElementTree as ET, shutil, re def read_file(file): """ 读取文件内容 """ content = '' with open_file(file, 'r') as f: content = f.read() return content # 文件修改时间比较 # file1 > file2 return 1 # file1 == file2 return 0 # file1 < file2 return -1 # 任意一个文件不存在则返回None def compareFileModifyTime(file1, file2): result = 0 if not os.path.exists(file1): err = "file %s is not exists" % file1 PrintLog(err) return None if not os.path.exists(file2): err = "file %s is not exists" % file2 PrintLog(err) return None file1MTime = os.stat(file1).st_mtime file2MTime = os.stat(file2).st_mtime PrintLog("[file1MTime]: %s" % file1MTime) PrintLog("[file2MTime]: %s" % file2MTime) if file1MTime > file2MTime: return 1 elif file1MTime < file2MTime: return -1 else: return 0 pass def open_file(file, mode): return open(file, mode, encoding='UTF-8') # 修改包名 def modifyPkgName(apkPath, pkgName): manifestRoot = ET.parse("%s/AndroidManifest.xml" % apkPath).getroot() if manifestRoot is None: PrintLog("AndroidManifest.xml 文件解析失败") return 1 if pkgName == "": PrintLog("包名未配置,保持母包包名") else: oldPkgName = manifestRoot.attrib["package"] content = open("%s/AndroidManifest.xml" % apkPath, "r").read() content = content.replace(oldPkgName, pkgName) open("%s/AndroidManifest.xml" % apkPath, "w").write(content) PrintLog("[old pkg name] : %s ----------------------------" % oldPkgName) PrintLog("[new pkg name] : %s ----------------------------" % pkgName) # sdkConfig.xml + AndroidManifest.xml # sdkConfigPath: 渠道配置路径 # apkPath:解包后AndroidManifest.xml路径 def mergeManifest(sdkConfigPath, apkManifestPath): PrintLog("------------------------- start to merge AndroidManifest.xml -------------------------") ret = 0 # 注册命名空间 ET.register_namespace('android', "http://schemas.android.com/apk/res/android") PrintLog("[apkManifestPath] : %s" % apkManifestPath) PrintLog("[sdkConfig] : %s" % sdkConfigPath) if os.path.exists(apkManifestPath) == False or os.path.exists(sdkConfigPath) == False: PrintLog("targetManifest or sdkConfigPath is not exists!") return 1 sdkRoot = ET.parse(sdkConfigPath).getroot() if sdkRoot == None: PrintLog("cant parse sdkConfig!") return 1 targetTree = ET.parse(apkManifestPath) targetRoot = targetTree.getroot() if targetRoot is None: PrintLog("cant parse targetTree") return 1 targetApplicationRoot = targetRoot.find("application") # 合并activity和service sdkActivities = sdkRoot.find("sdk_activity").iter("activity") sdkActivities_alicas = sdkRoot.find("sdk_activity").iter("activity-alias") sdkServices = sdkRoot.find("sdk_activity").iter("service") sdkReceiver = sdkRoot.find("sdk_activity").iter("receiver") sdkProvider = sdkRoot.find("sdk_activity").iter("provider") for activity in sdkActivities: targetApplicationRoot.append(activity) for service in sdkServices: targetApplicationRoot.append(service) for receiver in sdkReceiver: targetApplicationRoot.append(receiver) for provider in sdkProvider: targetApplicationRoot.append(provider) for alicas in sdkActivities_alicas: targetApplicationRoot.append(alicas) PrintLog("merging activity and service completed!") # 合并meta-data sdkMetas = sdkRoot.find("sdk_meta").iter("meta-data") for meta in sdkMetas: targetApplicationRoot.append(meta) PrintLog("merging meta-data completed!") # 合并permission sdkPermissions = [] for per in sdkRoot.find("sdk_permission").findall("uses-permission"): sdkPermissions.append(list(per.attrib.values())[0]) targetPermissions = [] for per in targetRoot.findall("uses-permission"): targetPermissions.append(list(per.attrib.values())[0]) targetRoot.remove(per) # 利用set排重 mergePermissions = list(set(sdkPermissions) | set(targetPermissions)) for per in mergePermissions: element = ET.Element("uses-permission") element.attrib = {"android:name": per} element.tail = "\n\t" targetRoot.append(element) PrintLog("merging permission completed!") targetTree.write("%s" % apkManifestPath, "utf-8", xml_declaration=True) # 根据配置修改游戏名,包名等 PrintLog("------------------------- finished merging AndroidManifest.xml -------------------------") return ret # 文件安全删除 def safeFileDelete(filePath): if os.path.exists(filePath): if os.path.isdir(filePath): shutil.rmtree(filePath) PrintLog("[delete isdir filePath] : %s" % filePath) elif os.path.isfile(filePath): PrintLog("[delete isfile filePath] : %s" % filePath) os.remove(filePath) return # 删除文件但不删除文件夹 def del_file(path_data): for i in os.listdir(path_data): # os.listdir(path_data)#返回一个列表,里面是当前目录下面的所有东西的相对路径 file_data = path_data + "/" + i # 当前文件夹的下面的所有东西的绝对路径 if os.path.isfile(file_data) == True: # os.path.isfile判断是否为文件,如果是文件,就删除.如果是文件夹.递归给del_file. os.remove(file_data) else: del_file(file_data) # 复制并覆盖文件 def copyAllFile(fromFile, toFile): for parent, dirName, fileNames in os.walk(fromFile): oriParent = parent.replace(fromFile, toFile) for fileName in fileNames: fromDir = "%s/%s" % (parent, fileName) # PrintLog("fromDir :%s"%fromDir) toDir = "%s/%s" % (oriParent, fileName) # PrintLog("toDir: %s"%toDir) copyFileForCompareFile(fromDir, toDir) pass 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: # 复制文件 if not from_file.endswith('.DS_Store'): shutil.copyfile(from_file, to_file) return 0 def copyFileForCompareFile(fromFile, toFile): if os.path.exists(fromFile) == False: PrintLog("file %s is not exists" % fromFile) return filePattern = re.compile(r".*\..*") toDir = toFile if filePattern.match(toFile): toDir = os.path.dirname(toFile) if os.path.exists(toDir) == False: os.makedirs(toDir) shutil.copy(fromFile, toFile) pass 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 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 iterate_dir_path(dir): path_list = [] for path in os.listdir(dir): abs_path = os.path.join(dir, path) path_list.append(abs_path) return path_list 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 replace_manifest_meta_data(am_path, key, value): lines = open(am_path).readlines() fp = open(am_path, 'w') for s in lines: if s.find('android:name=\"' + key + '\"') != -1: PrintLog("Before replaceAM: %s" % s) s = re.sub('android:name=\"' + key + '\" android:value=\".*\"', 'android:name=\"' + key + '\" android:value=\"' + value + '\"', s) PrintLog("After replaceAM: %s" % s) fp.write(s) fp.close() pass def replace_assets_param(assets_param_path, key, value): lines = open(assets_param_path).readlines() fp = open(assets_param_path, 'w') for s in lines: s = re.sub(key + '=.*', key + '=' + value, s) fp.write(s) fp.close() pass def special_replace(sdk_name, prj_path, key, value): reload(channel_special_replace) # 获取特殊替换 replaceFun = channel_special_replace.get_special_replace(sdk_name) if replaceFun: replaceFun(prj_path, key, value) else: PrintLog(" 渠道无需特殊处理 :%s" % sdk_name) pass def replace_string_app_name(prj_path, value): if value == None or value == "": return # 命名空间,用于获取对应的值 namespace = "http://schemas.android.com/apk/res/android" amTree = ET.parse("%s/AndroidManifest.xml" % prj_path) if amTree == None: return amRoot = amTree.getroot() applicationNode = amRoot.find("application") nameLabelAttrib = applicationNode.attrib.get("{%s}label" % namespace)[1:] nameLabelArr = nameLabelAttrib.split("/") nameLabelFile = nameLabelArr[0] nameLabel = nameLabelArr[1] modifyFileTree = ET.parse("%s/res/values/%ss.xml" % (prj_path, nameLabelFile)) if modifyFileTree is None: return False modifyFileRoot = modifyFileTree.getroot() for child in modifyFileRoot.findall('application'): if child.attrib["name"] == nameLabel: child.text = value modifyFileTree.write("%s/res/values/%ss.xml" % (prj_path, nameLabelFile), "utf-8", xml_declaration=True) return True pass def replace_version_name(yml_path, value): if not os.path.exists(yml_path): return False replaceVersionNameShell = "/bin/sed -i -E \"s/(versionName:).*/\\1 %s/\" %s" % (value, yml_path) PrintLog("[replaceVersionNameShell] : %s" % replaceVersionNameShell) ret = os.system(replaceVersionNameShell) return ret pass def replace_version_code(yml_path, value): if not os.path.exists(yml_path): return False replaceVersionCodeShell = "/bin/sed -i -E \"s/(versionCode:).*/\\1 %s/\" %s" % (value, yml_path) PrintLog("[replaceVersionCodeShell] : %s" % replaceVersionCodeShell) ret = os.system(replaceVersionCodeShell) return ret pass def replace_yml_target_sdk_version(apkYmlPath, code): lines = open(apkYmlPath).readlines() fp = open(apkYmlPath, 'w') for line in lines: if 'targetSdkVersion' in line: newTargetVersion = " targetSdkVersion: '%s'" % code + '\n' PrintLog("oldTargetVersion:%s --------newTargetVersion:%s" % (line, newTargetVersion)) line = re.sub(line, newTargetVersion, line) fp.write(line) fp.close() pass def changeVersion(yml, versionCode=None, versionName=None, targetSdkVersion=None): tag = ['versionCode:', 'versionName:', 'targetSdkVersion:'] PrintLog('versionCode:%s versionName:%s targetSdkVersion:%s'%(versionCode,versionName,targetSdkVersion)) with open_file(yml, 'r+') as f: content = '' line = f.readline() while line: if versionCode and tag[0] in line: content += ' versionCode: \'%s\'\n' % versionCode elif versionName and tag[1] in line: content += ' versionName: \'%s\'\n' % versionName elif targetSdkVersion 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) def replace_file_keyword(file, keyword, value): if not os.path.exists(file): return False replaceVersionCodeShell = "/bin/sed -i -E \"s/(%s).*/\\1 %s/\" %s" % (keyword, value, file) PrintLog("[replaceVersionCodeShell] : %s" % replaceVersionCodeShell) ret = os.system(replaceVersionCodeShell) return ret pass # 修改微信回调页面 def merge_wx_page(package_name, apk_decompile_tmp_dir): manifest_path = os.path.join(apk_decompile_tmp_dir, 'AndroidManifest.xml') manifest_root = ET.parse(manifest_path).getroot() namespace = '{http://schemas.android.com/apk/res/android}' for child in manifest_root.findall('application/activity'): if 'wxapi' in child.attrib[namespace + "name"]: old_package_name_path = child.attrib[namespace + "name"].split('.wxapi.')[0].replace(".", "/") old_wx_api_code_path = os.path.join(apk_decompile_tmp_dir, 'smali', old_package_name_path,'wxapi') PrintLog('[old_wx_api_code_path]:%s' % old_wx_api_code_path) new_package_name_path = package_name.replace(".", "/") new_wx_api_code_path = os.path.join(apk_decompile_tmp_dir, 'smali', new_package_name_path,'wxapi') PrintLog('[new_wx_api_code_path]:%s' % new_wx_api_code_path) if not os.path.exists(old_wx_api_code_path): PrintLog('微信回调代码不存在,不执行合并操作') return 0 copy_file_all_dir(old_wx_api_code_path, new_wx_api_code_path, False) for parent, dirnames, filenames in os.walk(new_wx_api_code_path): for filename in filenames: abs_filename = os.path.join(new_wx_api_code_path, filename) PrintLog("[start to replace package_name]:%s" % abs_filename) replace_content(abs_filename, old_package_name_path, new_package_name_path) replace_content(manifest_path, old_package_name_path.replace('/', '.'), package_name) return if __name__ == "__main__": merge_wx_page('com.uxmfe.fgkg', '/Users/kaiweicai/Documents/Project/PackKit/YYXXPackKit/game/104082/dcm_tmphuawei')