123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508 |
- # -*- coding:utf-8 -*-
- import channel_special_replace
- from print_log import printlog
- import os, xml.etree.ElementTree as ET, shutil, re
- androidNS = 'http://schemas.android.com/apk/res/android'
- namespace = '{' + androidNS + '}'
- 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 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', androidNS)
- printlog("[apkManifestPath] : %s" % apkManifestPath)
- printlog("[sdkConfig] : %s" % sdkConfigPath)
- if not os.path.exists(apkManifestPath) or not os.path.exists(sdkConfigPath):
- printlog("targetManifest or sdkConfigPath is not exists!")
- return 1
- sdkRoot = ET.parse(sdkConfigPath).getroot()
- if sdkRoot is 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): # 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 not os.path.exists(fromFile):
- printlog("file %s is not exists" % fromFile)
- return
- filePattern = re.compile(r".*\..*")
- toDir = toFile
- if filePattern.match(toFile):
- toDir = os.path.dirname(toFile)
- if not os.path.exists(toDir):
- 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):
- # 获取特殊替换
- printlog(" 开始进行渠道特殊处理 :%s" % sdk_name)
- 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(apk_decompile_tmp_dir, value):
- if not value:
- return
- # 命名空间,用于获取对应的值
- amTree = ET.parse(os.path.join(apk_decompile_tmp_dir, 'AndroidManifest.xml'))
- if amTree is None:
- return
- amRoot = amTree.getroot()
- applicationNode = amRoot.find("application")
- nameLabelAttrib = applicationNode.attrib.get("{%s}label" % androidNS)[1:]
- nameLabelArr = nameLabelAttrib.split("/")
- nameLabelFile = nameLabelArr[0]
- nameLabel = nameLabelArr[1]
- modifyFileTree = ET.parse("%s/res/values/%ss.xml" % (apk_decompile_tmp_dir, nameLabelFile))
- if modifyFileTree is None:
- return False
- modifyFileRoot = modifyFileTree.getroot()
- for child in list(modifyFileRoot):
- if child.attrib["name"] == nameLabel:
- child.text = value
- modifyFileTree.write("%s/res/values/%ss.xml" % (apk_decompile_tmp_dir, 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()
- 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_classes2', 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_classes2', new_package_name_path, 'wxapi')
- printlog('[new_wx_api_code_path]:%s' % new_wx_api_code_path)
- if old_wx_api_code_path == new_wx_api_code_path:
- printlog('已经在包名相同的微信回调代码,无需修改')
- return 0
- if not os.path.exists(old_wx_api_code_path):
- old_wx_api_code_path = os.path.join(apk_decompile_tmp_dir, 'smali', old_package_name_path,
- 'wxapi')
- 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')
|