def copy_libs(decompile_dir, sdk_dir): cpus = [ 'arm64-v8a', 'armeabi', 'armeabi-v7a', 'mips', 'mips64', 'x86', 'x86_64' ] dest_dir = os.path.join(decompile_dir, 'lib') src_dir = os.path.join(sdk_dir, 'libs') if not os.path.exists(src_dir): return if os.path.exists(dest_dir): for f in os.listdir(dest_dir): if f.endswith('.jar'): continue source_file = os.path.join(src_dir, f) target_file = os.path.join(dest_dir, f) if os.path.exists(source_file): Utils.copy_file(source_file, target_file) else: os.mkdir(dest_dir) for fi in os.listdir(src_dir): if fi.endswith('.jar'): continue source_file = os.path.join(src_dir, fi) target_file = os.path.join(dest_dir, fi) Utils.copy_file(source_file, target_file)
def save_data(self): if self.game_name_value.text().strip() == "": QMessageBox.warning(self, "警告", "游戏名称不能为空!") return False if self.keystore_path.text().strip() == "": QMessageBox.warning(self, "警告", "必须上传keystore签名文件!") return False if self.keystore_pwd_value.text().strip() == "": QMessageBox.warning(self, "警告", "keystore密码不能为空!") return False if self.keystore_alias_value.text().strip() == "": QMessageBox.warning(self, "警告", "alias不能为空!") return False if self.keystore_aliaspwd_value.text().strip() == "": QMessageBox.warning(self, "警告", "alias密码不能为空!") return False self.game['name'] = self.game_name_value.text().strip() self.game['desc'] = self.game_desc_value.toPlainText().strip() if self.keystore_exchanged: keystore = os.path.basename(self.keystore_path.text().strip()) self.game['keystore'] = keystore self.game['keypwd'] = self.keystore_pwd_value.text().strip() self.game['alias'] = self.keystore_alias_value.text().strip() self.game['aliaspwd'] = self.keystore_aliaspwd_value.text().strip() keystore_path = Utils.get_full_path('games/' + self.game['id'] + '/keystore/' + keystore) if not os.path.exists(keystore_path): Utils.copy_file(self.keystore_path.text().strip(), keystore_path) self.main_win.games[self.main_win.game_index] = self.game self.game_list_model.update_item(self.main_win.game_index, self.game) return Utils.update_games(Utils.get_full_path('games/games.xml'), self.game, self.main_win.game_index)
def create(self): if self.game_name_value.text().strip() == "": QMessageBox.warning(self, "警告", "游戏名称不能为空!") return if self.game_appid_value.text().strip() == "": QMessageBox.warning(self, "警告", "游戏Id不能为空!") return if self.game_appkey_value.text().strip() == "": QMessageBox.warning(self, "警告", "客户端Key不能为空!") return if self.keystore_path.text().strip() == "": QMessageBox.warning(self, "警告", "必须上传keystore签名文件!") return if self.keystore_pwd_value.text().strip() == "": QMessageBox.warning(self, "警告", "keystore密码不能为空!") return if self.keystore_alias_value.text().strip() == "": QMessageBox.warning(self, "警告", "alias不能为空!") return if self.keystore_aliaspwd_value.text().strip() == "": QMessageBox.warning(self, "警告", "alias密码不能为空!") return self.game['name'] = self.game_name_value.text().strip() self.game['desc'] = self.game_desc_value.toPlainText().strip() self.game['id'] = self.game_appid_value.text().strip() self.game['key'] = self.game_appkey_value.text().strip() if os.path.exists(Utils.get_full_path('games/' + self.game['id'])): QMessageBox.warning(self, "警告", "游戏已存在!") return if not os.path.exists(Utils.get_full_path('games')): os.makedirs(Utils.get_full_path('games')) os.mkdir(Utils.get_full_path('games/' + self.game['id'])) keystore = os.path.basename(self.keystore_path.text().strip()) self.game['keystore'] = keystore self.game['keypwd'] = self.keystore_pwd_value.text().strip() self.game['alias'] = self.keystore_alias_value.text().strip() self.game['aliaspwd'] = self.keystore_aliaspwd_value.text().strip() os.mkdir(Utils.get_full_path('games/' + self.game['id'] + '/keystore')) keystore_path = Utils.get_full_path('games/' + self.game['id'] + '/keystore/' + keystore) Utils.copy_file(self.keystore_path.text().strip(), keystore_path) os.mkdir(Utils.get_full_path('games/' + self.game['id'] + '/icon')) if self.icon_path is not None: icon_file = Utils.get_full_path('games/' + self.game['id'] + '/icon/icon.png') Utils.copy_file(self.icon_path, icon_file) self.main_win.games.append(self.game) Utils.add_game(Utils.get_full_path('games/games.xml'), self.game) self.main_win.game_index = len(self.main_win.games) - 1 self.main_win.set_game_list_widget(self.main_win.games)
def generate_r_file(package_name, decompile_dir): # check_value_resources(decompile_dir) temp_path = os.path.dirname(decompile_dir) + '/temp' LogUtils.debug('generate R:the temp path is %s', temp_path) if os.path.exists(temp_path): Utils.del_file(temp_path) os.makedirs(temp_path) res_path = decompile_dir + '/res' temp_res_path = temp_path + '/res' Utils.copy_file(res_path, temp_res_path) gen_path = temp_path + '/gen' os.makedirs(gen_path) aapt_path = Utils.get_full_path('tools/aapt2.exe') android_path = Utils.get_full_path('tools/android.jar') java = Utils.get_full_path('tools/jdk/bin/java') javac = Utils.get_full_path('tools/jdk/bin/javac') manifest_path = decompile_dir + '/AndroidManifest.xml' res_flat_path = temp_path + "/res_flat.zip" cmd = '%s compile -o %s --dir %s' % (aapt_path, res_flat_path, temp_res_path) ret = Utils.exec_cmd(cmd) if ret: return 1 cmd = '%s link -o %s --manifest %s -I %s --java %s %s' % ( aapt_path, temp_path + '/res.apk', manifest_path, android_path, gen_path, res_flat_path) ret = Utils.exec_cmd(cmd) if ret: return 1 LogUtils.info('package_name:%s', package_name) r_path = package_name.replace('.', '/') r_path = gen_path + '/' + r_path + '/R.java' cmd = '%s -source 1.8 -target 1.8 -encoding UTF-8 %s' % (javac, r_path) ret = Utils.exec_cmd(cmd) if ret: return 1 dex_path = temp_path + '/classes.dex' dex_tool_path = Utils.get_full_path('tools/dx.jar') cmd = '%s -jar %s --dex --output %s %s' % (java, dex_tool_path, dex_path, gen_path) ret = Utils.exec_cmd(cmd) if ret: return 1 smali_path = decompile_dir + '/smali' ret = dex2smali(dex_path, smali_path, '') if ret: return 1 return 0
def exchange_icon(self): fname = QFileDialog.getOpenFileName( self, '选择icon', Utils.get_full_path('games/' + self.game['id'] + '/icon/'), ("Images (*.png)")) if fname[0]: pix = QPixmap(fname[0]) if pix.width() != 512 or pix.height() != 512: QMessageBox.warning(self, "警告", "必须上传512*512.png图片") return self.icon_img.setPixmap(pix) current_icon = Utils.get_full_path('games/' + self.game['id'] + '/icon/icon.png') if os.path.exists(current_icon): if os.path.samefile(os.path.dirname(fname[0]), os.path.dirname(current_icon)): if not os.path.samefile( fname[0], current_icon ): # 如果选中的,在game的icon目录下,但不是当前icon,则进行重命名 count = 0 temp = 'icon0.png' while os.path.exists( Utils.get_full_path('games/' + self.game['id'] + '/icon/' + temp)): count += 1 temp = 'icon' + str(count) + '.png' os.renames( current_icon, Utils.get_full_path('games/' + self.game['id'] + '/icon/' + temp)) os.renames(fname[0], current_icon) else: # 如果所选的是当前icon,不做处理 return else: # 如果选中的不在game的icon目录下,则重命名当前icon,并将选中的icon复制到目录下作为当前icon count = 0 temp = 'icon0.png' while os.path.exists( Utils.get_full_path('games/' + self.game['id'] + '/icon/' + temp)): count += 1 temp = 'icon' + str(count) + '.png' os.renames( current_icon, Utils.get_full_path('games/' + self.game['id'] + '/icon/' + temp)) Utils.copy_file(fname[0], current_icon) else: Utils.copy_file(fname[0], current_icon)
def run(self): # 开启任务,发送打包信号 self.signal.signal.emit(self.channel['channelId'], 0, "正在打包......", 0) # 清空已有的workspace work_dir = Utils.get_full_path('workspace/' + self.game['id'] + '/' + self.channel['channelId']) Utils.del_file(work_dir) os.makedirs(work_dir) # 生成当前打包任务的logger,添加到字典 LogUtils.add_logger(work_dir) LogUtils.info('Current Selected Game ID is : %s, with SDK is : %s', self.game['id'], self.channel['sdk']) LogUtils.info('game:\n%s', self.game) LogUtils.info('channel:\n%s', self.channel) src_apk_path = work_dir + '/songshu.apk' # 复制上传母包到workspace result = Utils.copy_file(self.apk, src_apk_path) if self.flag(result, "打包失败:复制母包文件失败,详情查看log.log", 5): return # 反编译母包 decompile_dir = work_dir + '/decompile' frame_work_dir = work_dir + '/framework' result = ApkUtils.decompile_apk(src_apk_path, decompile_dir, frame_work_dir) if self.flag(result, "打包失败:反编译母包异常,详情查看log.log", 15): return # 复制sdk资源到工作目录 sdk_source_dir = Utils.get_full_path('channelsdk/' + self.channel['sdk']) sdk_dest_dir = work_dir + '/sdk' result = Utils.copy_file(sdk_source_dir, sdk_dest_dir) if self.flag(result, "打包失败:复制SDK文件夹失败,详情查看log.log", 18): return # 将插件里的jar资源转dex result = ApkUtils.jar2dex(sdk_source_dir, sdk_dest_dir + '/classes.dex') if self.flag(result, "打包失败:渠道jar转dex异常,详情查看log.log", 25): return # 将插件里的dex资源转smali,合并到母包反编译目录中 result = ApkUtils.dex2smali(sdk_dest_dir + '/classes.dex', decompile_dir + '/smali', '') if self.flag(result, "打包失败:渠道dex转smali异常,详情查看log.log", 28): return # 合并manifest文件 result = ApkUtils.merge_manifest(decompile_dir, sdk_dest_dir) if self.flag(result, "打包失败:合并manifest文件失败,详情查看log.log", 30): return # 复制插件libs里的so库 ApkUtils.copy_libs(decompile_dir, sdk_dest_dir) if self.flag(0, "", 33): return # 复制插件assets文件夹 result = Utils.copy_file(sdk_dest_dir + '/assets', decompile_dir + '/assets') if self.flag(result, "打包失败:复制assets文件夹失败,详情查看log.log", 35): return # 复制插件res文件夹 result = Utils.copy_file(sdk_dest_dir + '/res', decompile_dir + '/res') if self.flag(result, "打包失败:复制res文件夹失败,详情查看log.log", 38): return # 复制渠道特殊配置资源,比如,针对个别渠道设置的loading页或logo ApkUtils.copy_ext_res(self.game, decompile_dir) if self.flag(0, "", 40): return # 将游戏原来的包名替换成渠道里面的包名,四大组件也会按照相关规则替换包名 package_name = ApkUtils.rename_package_name(decompile_dir, self.channel['package']) if self.flag(0, "", 45): return # 给对应的icon添加角标 ApkUtils.append_channel_mark(self.game, sdk_dest_dir, decompile_dir) if self.flag(0, "", 50): return # 配置参数写入 result = ApkUtils.write_develop_info(self.game, self.channel, decompile_dir) if self.flag(result, "打包失败:写入配置参数失败,详情查看log.log", 52): return # 如果主sdk有特殊的逻辑。执行特殊的逻辑脚本。 result = ApkUtils.do_sdk_script(self.channel, decompile_dir, package_name, sdk_dest_dir) if self.flag(result, "打包失败:执行渠道脚本异常,详情查看log.log", 55): return # 修改游戏名称,并将meta-data写入manifest文件 ApkUtils.modify_manifest(self.channel, decompile_dir, package_name) if self.flag(0, "", 60): return # 重新生成R文件,并导入到包名下 result = ApkUtils.generate_r_file(package_name, decompile_dir) if self.flag(result, "打包失败:重新生成R文件异常,详情查看log.log", 75): return # 防止方法数超65535,判断是否分dex result = ApkUtils.classes_split(decompile_dir, sdk_dest_dir) if self.flag(result, "打包失败:分dex出现异常,详情查看log.log", 78): return # 修改apktool.yml里的压缩配置,防止包体变大 ApkUtils.edit_yml(self.channel, decompile_dir) if self.flag(0, "", 80): return # 回编译生成apk target_apk = work_dir + '/output.apk' result = ApkUtils.recompile_apk(decompile_dir, target_apk, frame_work_dir) if self.flag(result, "打包失败:回编译APK异常,详情查看log.log", 90): return # 复制添加资源到apk result = ApkUtils.copy_root_ext_files(target_apk, decompile_dir) if self.flag(result, "打包失败:母包其余资源导入异常,详情查看log.log", 92): return # apk签名(v1签名) result = ApkUtils.sign_apk(self.game, target_apk) if self.flag(result, "打包失败:渠道包签名异常,详情查看log.log", 98): return # apk对齐 time_str = QDateTime.currentDateTime().toString("yyyyMMddhhmm") dest_apk_name = self.game['name'] + '-' + self.channel[ 'name'] + '-' + time_str + '.apk' dest_apk_dir = Utils.get_full_path('output/' + self.game['id'] + '/' + self.channel['channelId']) if not os.path.exists(dest_apk_dir): os.makedirs(dest_apk_dir) dest_apk = dest_apk_dir + '/' + dest_apk_name result = ApkUtils.align_apk(target_apk, dest_apk) if self.is_close: pass else: if result == 0: self.signal.signal.emit(self.channel['channelId'], 1, "打包成功:双击打开出包目录", 100) else: self.signal.signal.emit(self.channel['channelId'], result, "打包失败:apk包体4k对齐异常,详情查看log.log", 100) LogUtils.close()
def copy_ext_res(game, decompile_dir): ext_res_path = 'games/' + game['id'] + '/ext' ext_res_path = Utils.get_full_path(ext_res_path) if os.path.exists(ext_res_path): LogUtils.warning('the game ext res path: %s', ext_res_path) Utils.copy_file(ext_res_path, decompile_dir)