import os import shutil import traceback from PySide6.QtWidgets import QMessageBox from utils.logger import setup_logger from config.config import APP_NAME from utils import msgbox_frame class PatchManager: """补丁管理器,用于处理补丁的安装和卸载""" def __init__(self, app_name, game_info, debug_manager=None, main_window=None): """初始化补丁管理器 Args: app_name: 应用程序名称,用于显示消息框标题 game_info: 游戏信息字典,包含各版本的安装路径和可执行文件名 debug_manager: 调试管理器实例,用于输出调试信息 main_window: 主窗口实例,用于访问UI和状态 """ self.app_name = app_name self.game_info = game_info self.debug_manager = debug_manager self.main_window = main_window # 添加main_window属性 self.installed_status = {} # 游戏版本的安装状态 self.logger = setup_logger("patch_manager") self.patch_detector = None # 将在main_window初始化后设置 def set_patch_detector(self, patch_detector): """设置补丁检测器实例 Args: patch_detector: 补丁检测器实例 """ self.patch_detector = patch_detector def _is_debug_mode(self): """检查是否处于调试模式 Returns: bool: 是否处于调试模式 """ if hasattr(self.debug_manager, 'ui_manager') and hasattr(self.debug_manager.ui_manager, 'debug_action'): return self.debug_manager.ui_manager.debug_action.isChecked() return False def initialize_status(self): """初始化所有游戏版本的安装状态""" self.installed_status = {f"NEKOPARA Vol.{i}": False for i in range(1, 5)} self.installed_status["NEKOPARA After"] = False def update_status(self, game_version, is_installed): """更新游戏版本的安装状态 Args: game_version: 游戏版本 is_installed: 是否已安装 """ self.installed_status[game_version] = is_installed def get_status(self, game_version=None): """获取游戏版本的安装状态 Args: game_version: 游戏版本,如果为None则返回所有状态 Returns: bool或dict: 指定版本的安装状态或所有版本的安装状态 """ if game_version: return self.installed_status.get(game_version, False) return self.installed_status def uninstall_patch(self, game_dir, game_version, silent=False): """卸载补丁 Args: game_dir: 游戏目录路径 game_version: 游戏版本 silent: 是否静默模式(不显示弹窗) Returns: bool: 卸载成功返回True,失败返回False dict: 在silent=True时,返回包含卸载结果信息的字典 """ debug_mode = self._is_debug_mode() if debug_mode: self.logger.debug(f"DEBUG: 开始卸载 {game_version} 补丁,目录: {game_dir}") self.logger.info(f"开始卸载 {game_version} 补丁,目录: {game_dir}") if game_version not in self.game_info: error_msg = f"无法识别游戏版本: {game_version}" if debug_mode: self.logger.debug(f"DEBUG: 卸载失败 - {error_msg}") self.logger.error(f"卸载失败 - {error_msg}") if not silent: QMessageBox.critical( None, f"错误 - {self.app_name}", f"\n{error_msg}\n", QMessageBox.StandardButton.Ok, ) return False if not silent else {"success": False, "message": error_msg, "files_removed": 0} try: files_removed = 0 # 获取可能的补丁文件路径 install_path_base = os.path.basename(self.game_info[game_version]["install_path"]) patch_file_path = os.path.join(game_dir, install_path_base) if debug_mode: self.logger.debug(f"DEBUG: 基础补丁文件路径: {patch_file_path}") # 尝试查找补丁文件,支持不同大小写 patch_files_to_check = [ patch_file_path, patch_file_path.lower(), patch_file_path.upper(), patch_file_path.replace("_", ""), patch_file_path.replace("_", "-"), ] if debug_mode: self.logger.debug(f"DEBUG: 查找以下可能的补丁文件路径: {patch_files_to_check}") # 查找并删除补丁文件,包括启用和禁用的 patch_file_found = False for patch_path in patch_files_to_check: # 检查常规补丁文件 if os.path.exists(patch_path): patch_file_found = True if debug_mode: self.logger.debug(f"DEBUG: 找到补丁文件: {patch_path},准备删除") self.logger.debug(f"删除补丁文件: {patch_path}") os.remove(patch_path) files_removed += 1 if debug_mode: self.logger.debug(f"DEBUG: 已删除补丁文件: {patch_path}") # 检查被禁用的补丁文件(带.fain后缀) disabled_path = f"{patch_path}.fain" if os.path.exists(disabled_path): patch_file_found = True if debug_mode: self.logger.debug(f"DEBUG: 找到被禁用的补丁文件: {disabled_path},准备删除") self.logger.debug(f"删除被禁用的补丁文件: {disabled_path}") os.remove(disabled_path) files_removed += 1 if debug_mode: self.logger.debug(f"DEBUG: 已删除被禁用的补丁文件: {disabled_path}") if not patch_file_found: if debug_mode: self.logger.debug(f"DEBUG: 未找到补丁文件,检查了以下路径: {patch_files_to_check}") self.logger.debug(f"DEBUG: 也检查了禁用的补丁文件(.fain后缀)") self.logger.warning(f"未找到 {game_version} 的补丁文件") # 检查是否有额外的签名文件 (.sig) if game_version == "NEKOPARA After": if debug_mode: self.logger.debug(f"DEBUG: {game_version} 需要检查额外的签名文件") for patch_path in patch_files_to_check: # 检查常规签名文件 sig_file_path = f"{patch_path}.sig" if os.path.exists(sig_file_path): if debug_mode: self.logger.debug(f"DEBUG: 找到签名文件: {sig_file_path},准备删除") self.logger.debug(f"删除签名文件: {sig_file_path}") os.remove(sig_file_path) files_removed += 1 if debug_mode: self.logger.debug(f"DEBUG: 已删除签名文件: {sig_file_path}") # 检查被禁用补丁的签名文件 disabled_sig_path = f"{patch_path}.fain.sig" if os.path.exists(disabled_sig_path): if debug_mode: self.logger.debug(f"DEBUG: 找到被禁用补丁的签名文件: {disabled_sig_path},准备删除") self.logger.debug(f"删除被禁用补丁的签名文件: {disabled_sig_path}") os.remove(disabled_sig_path) files_removed += 1 if debug_mode: self.logger.debug(f"DEBUG: 已删除被禁用补丁的签名文件: {disabled_sig_path}") # 删除patch文件夹 if debug_mode: self.logger.debug(f"DEBUG: 检查并删除patch文件夹") patch_folders_to_check = [ os.path.join(game_dir, "patch"), os.path.join(game_dir, "Patch"), os.path.join(game_dir, "PATCH"), ] for patch_folder in patch_folders_to_check: if os.path.exists(patch_folder): if debug_mode: self.logger.debug(f"DEBUG: 找到补丁文件夹: {patch_folder},准备删除") self.logger.debug(f"删除补丁文件夹: {patch_folder}") import shutil shutil.rmtree(patch_folder) files_removed += 1 if debug_mode: self.logger.debug(f"DEBUG: 已删除补丁文件夹: {patch_folder}") # 删除game/patch文件夹 if debug_mode: self.logger.debug(f"DEBUG: 检查并删除game/patch文件夹") game_folders = ["game", "Game", "GAME"] patch_folders = ["patch", "Patch", "PATCH"] for game_folder in game_folders: for patch_folder in patch_folders: game_patch_folder = os.path.join(game_dir, game_folder, patch_folder) if os.path.exists(game_patch_folder): if debug_mode: self.logger.debug(f"DEBUG: 找到game/patch文件夹: {game_patch_folder},准备删除") self.logger.debug(f"删除game/patch文件夹: {game_patch_folder}") import shutil shutil.rmtree(game_patch_folder) files_removed += 1 if debug_mode: self.logger.debug(f"DEBUG: 已删除game/patch文件夹: {game_patch_folder}") # 删除配置文件 if debug_mode: self.logger.debug(f"DEBUG: 检查并删除配置文件和脚本文件") config_files = ["config.json", "Config.json", "CONFIG.JSON"] script_files = ["scripts.json", "Scripts.json", "SCRIPTS.JSON"] for game_folder in game_folders: game_path = os.path.join(game_dir, game_folder) if os.path.exists(game_path): # 删除配置文件 for config_file in config_files: config_path = os.path.join(game_path, config_file) if os.path.exists(config_path): if debug_mode: self.logger.debug(f"DEBUG: 找到配置文件: {config_path},准备删除") self.logger.debug(f"删除配置文件: {config_path}") os.remove(config_path) files_removed += 1 if debug_mode: self.logger.debug(f"DEBUG: 已删除配置文件: {config_path}") # 删除脚本文件 for script_file in script_files: script_path = os.path.join(game_path, script_file) if os.path.exists(script_path): if debug_mode: self.logger.debug(f"DEBUG: 找到脚本文件: {script_path},准备删除") self.logger.debug(f"删除脚本文件: {script_path}") os.remove(script_path) files_removed += 1 if debug_mode: self.logger.debug(f"DEBUG: 已删除脚本文件: {script_path}") # 更新安装状态 self.installed_status[game_version] = False if debug_mode: self.logger.debug(f"DEBUG: 已更新 {game_version} 的安装状态为未安装") # 在非静默模式且非批量卸载模式下显示卸载成功消息 if not silent and game_version != "all": # 显示卸载成功消息 if files_removed > 0: success_msg = f"\n{game_version} 补丁卸载成功!\n共删除 {files_removed} 个文件/文件夹。\n" if debug_mode: self.logger.debug(f"DEBUG: 显示卸载成功消息: {success_msg}") QMessageBox.information( None, f"卸载完成 - {self.app_name}", success_msg, QMessageBox.StandardButton.Ok, ) else: warning_msg = f"\n未找到 {game_version} 的补丁文件,可能未安装补丁或已被移除。\n" if debug_mode: self.logger.debug(f"DEBUG: 显示警告消息: {warning_msg}") QMessageBox.warning( None, f"警告 - {self.app_name}", warning_msg, QMessageBox.StandardButton.Ok, ) # 卸载成功 if debug_mode: self.logger.debug(f"DEBUG: {game_version} 卸载完成,共删除 {files_removed} 个文件/文件夹") self.logger.info(f"{game_version} 卸载完成,共删除 {files_removed} 个文件/文件夹") if silent: return {"success": True, "message": f"{game_version} 补丁卸载成功", "files_removed": files_removed} return True except Exception as e: error_message = f"卸载 {game_version} 补丁时出错:{str(e)}" if debug_mode: self.logger.debug(f"DEBUG: {error_message}") import traceback self.logger.debug(f"DEBUG: 错误详情:\n{traceback.format_exc()}") self.logger.error(error_message) # 在非静默模式且非批量卸载模式下显示卸载失败消息 if not silent and game_version != "all": # 显示卸载失败消息 error_message = f"\n卸载 {game_version} 补丁时出错:\n\n{str(e)}\n" if debug_mode: self.logger.debug(f"DEBUG: 显示卸载失败消息") QMessageBox.critical( None, f"卸载失败 - {self.app_name}", error_message, QMessageBox.StandardButton.Ok, ) # 卸载失败 if silent: return {"success": False, "message": f"卸载 {game_version} 补丁时出错: {str(e)}", "files_removed": 0} return False def batch_uninstall_patches(self, game_dirs): """批量卸载多个游戏的补丁 Args: game_dirs: 游戏版本到游戏目录的映射字典 Returns: tuple: (成功数量, 失败数量, 详细结果列表) """ success_count = 0 fail_count = 0 debug_mode = self._is_debug_mode() results = [] if debug_mode: self.logger.debug(f"DEBUG: 开始批量卸载补丁,游戏数量: {len(game_dirs)}") self.logger.debug(f"DEBUG: 要卸载的游戏: {list(game_dirs.keys())}") self.logger.info(f"开始批量卸载补丁,游戏数量: {len(game_dirs)}") self.logger.debug(f"要卸载的游戏: {list(game_dirs.keys())}") for version, path in game_dirs.items(): if debug_mode: self.logger.debug(f"DEBUG: 处理游戏 {version},路径: {path}") self.logger.info(f"开始卸载 {version} 的补丁") try: # 在批量模式下使用静默卸载 if debug_mode: self.logger.debug(f"DEBUG: 使用静默模式卸载 {version}") result = self.uninstall_patch(path, version, silent=True) if isinstance(result, dict): # 使用了静默模式 if result["success"]: success_count += 1 if debug_mode: self.logger.debug(f"DEBUG: {version} 卸载成功,删除了 {result['files_removed']} 个文件/文件夹") self.logger.info(f"{version} 卸载成功,删除了 {result['files_removed']} 个文件/文件夹") else: fail_count += 1 if debug_mode: self.logger.debug(f"DEBUG: {version} 卸载失败,原因: {result['message']}") self.logger.warning(f"{version} 卸载失败,原因: {result['message']}") results.append({ "version": version, "success": result["success"], "message": result["message"], "files_removed": result["files_removed"] }) else: # 兼容旧代码,不应该执行到这里 if result: success_count += 1 if debug_mode: self.logger.debug(f"DEBUG: {version} 卸载成功(旧格式)") self.logger.info(f"{version} 卸载成功(旧格式)") else: fail_count += 1 if debug_mode: self.logger.debug(f"DEBUG: {version} 卸载失败(旧格式)") self.logger.warning(f"{version} 卸载失败(旧格式)") results.append({ "version": version, "success": result, "message": f"{version} 卸载{'成功' if result else '失败'}", "files_removed": 0 }) except Exception as e: if debug_mode: self.logger.debug(f"DEBUG: 卸载 {version} 时出错: {str(e)}") import traceback self.logger.debug(f"DEBUG: 错误详情:\n{traceback.format_exc()}") self.logger.error(f"卸载 {version} 时出错: {str(e)}") fail_count += 1 results.append({ "version": version, "success": False, "message": f"卸载出错: {str(e)}", "files_removed": 0 }) if debug_mode: self.logger.debug(f"DEBUG: 批量卸载完成,成功: {success_count},失败: {fail_count}") self.logger.info(f"批量卸载完成,成功: {success_count},失败: {fail_count}") return success_count, fail_count, results def show_uninstall_result(self, success_count, fail_count, results=None): """显示批量卸载结果 Args: success_count: 成功卸载的数量 fail_count: 卸载失败的数量 results: 详细结果列表,如果提供,会显示更详细的信息 """ debug_mode = self._is_debug_mode() if debug_mode: self.logger.debug(f"DEBUG: 显示卸载结果,成功: {success_count},失败: {fail_count}") result_text = f"\n批量卸载完成!\n成功: {success_count} 个\n失败: {fail_count} 个\n" # 如果有详细结果,添加到消息中 if results: success_list = [r["version"] for r in results if r["success"]] fail_list = [r["version"] for r in results if not r["success"]] if debug_mode: self.logger.debug(f"DEBUG: 成功卸载的游戏: {success_list}") self.logger.debug(f"DEBUG: 卸载失败的游戏: {fail_list}") if success_list: result_text += f"\n【成功卸载】:\n{chr(10).join(success_list)}\n" if fail_list: result_text += f"\n【卸载失败】:\n{chr(10).join(fail_list)}\n" # 记录更详细的失败原因 if debug_mode: for r in results: if not r["success"]: self.logger.debug(f"DEBUG: {r['version']} 卸载失败原因: {r['message']}") if debug_mode: self.logger.debug(f"DEBUG: 显示卸载结果对话框") QMessageBox.information( None, f"批量卸载完成 - {self.app_name}", result_text, QMessageBox.StandardButton.Ok, ) def check_patch_installed(self, game_dir, game_version): """检查游戏是否已安装补丁(调用patch_detector) Args: game_dir: 游戏目录路径 game_version: 游戏版本 Returns: bool: 如果已安装补丁或有被禁用的补丁文件返回True,否则返回False """ if self.patch_detector: return self.patch_detector.check_patch_installed(game_dir, game_version) # 如果patch_detector未设置,使用原始逻辑(应该不会执行到这里) debug_mode = self._is_debug_mode() if game_version not in self.game_info: return False # 获取可能的补丁文件路径 install_path_base = os.path.basename(self.game_info[game_version]["install_path"]) patch_file_path = os.path.join(game_dir, install_path_base) # 尝试查找补丁文件,支持不同大小写 patch_files_to_check = [ patch_file_path, patch_file_path.lower(), patch_file_path.upper(), patch_file_path.replace("_", ""), patch_file_path.replace("_", "-"), ] # 查找补丁文件 for patch_path in patch_files_to_check: if os.path.exists(patch_path): if debug_mode: self.logger.debug(f"找到补丁文件: {patch_path}") return True # 检查是否存在被禁用的补丁文件(带.fain后缀) disabled_path = f"{patch_path}.fain" if os.path.exists(disabled_path): if debug_mode: self.logger.debug(f"找到被禁用的补丁文件: {disabled_path}") return True # 检查是否有补丁文件夹 patch_folders_to_check = [ os.path.join(game_dir, "patch"), os.path.join(game_dir, "Patch"), os.path.join(game_dir, "PATCH"), ] for patch_folder in patch_folders_to_check: if os.path.exists(patch_folder): if debug_mode: self.logger.debug(f"找到补丁文件夹: {patch_folder}") return True # 检查game/patch文件夹 game_folders = ["game", "Game", "GAME"] patch_folders = ["patch", "Patch", "PATCH"] for game_folder in game_folders: for patch_folder in patch_folders: game_patch_folder = os.path.join(game_dir, game_folder, patch_folder) if os.path.exists(game_patch_folder): if debug_mode: self.logger.debug(f"找到game/patch文件夹: {game_patch_folder}") return True # 检查配置文件 config_files = ["config.json", "Config.json", "CONFIG.JSON"] script_files = ["scripts.json", "Scripts.json", "SCRIPTS.JSON"] for game_folder in game_folders: game_path = os.path.join(game_dir, game_folder) if os.path.exists(game_path): # 检查配置文件 for config_file in config_files: config_path = os.path.join(game_path, config_file) if os.path.exists(config_path): if debug_mode: self.logger.debug(f"找到配置文件: {config_path}") return True # 检查脚本文件 for script_file in script_files: script_path = os.path.join(game_path, script_file) if os.path.exists(script_path): if debug_mode: self.logger.debug(f"找到脚本文件: {script_path}") return True # 没有找到补丁文件或文件夹 if debug_mode: self.logger.debug(f"{game_version} 在 {game_dir} 中没有安装补丁") return False def check_patch_disabled(self, game_dir, game_version): """检查游戏的补丁是否已被禁用(调用patch_detector) Args: game_dir: 游戏目录路径 game_version: 游戏版本 Returns: bool: 如果补丁被禁用返回True,否则返回False str: 禁用的补丁文件路径,如果没有禁用返回None """ if self.patch_detector: return self.patch_detector.check_patch_disabled(game_dir, game_version) # 如果patch_detector未设置,使用原始逻辑(应该不会执行到这里) debug_mode = self._is_debug_mode() if game_version not in self.game_info: return False, None # 获取可能的补丁文件路径 install_path_base = os.path.basename(self.game_info[game_version]["install_path"]) patch_file_path = os.path.join(game_dir, install_path_base) # 检查是否存在禁用的补丁文件(.fain后缀) disabled_patch_files = [ f"{patch_file_path}.fain", f"{patch_file_path.lower()}.fain", f"{patch_file_path.upper()}.fain", f"{patch_file_path.replace('_', '')}.fain", f"{patch_file_path.replace('_', '-')}.fain", ] # 检查是否有禁用的补丁文件 for disabled_path in disabled_patch_files: if os.path.exists(disabled_path): if debug_mode: self.logger.debug(f"找到禁用的补丁文件: {disabled_path}") return True, disabled_path if debug_mode: self.logger.debug(f"{game_version} 在 {game_dir} 的补丁未被禁用") return False, None def toggle_patch(self, game_dir, game_version, operation=None, silent=False): """切换补丁的禁用/启用状态 Args: game_dir: 游戏目录路径 game_version: 游戏版本 operation: 指定操作,可以是"enable"、"disable"或None(None则自动切换当前状态) silent: 是否静默模式(不显示弹窗) Returns: dict: 包含操作结果信息的字典 """ debug_mode = self._is_debug_mode() if debug_mode: self.logger.debug(f"开始切换补丁状态 - 游戏版本: {game_version}, 游戏目录: {game_dir}, 操作: {operation}") if game_version not in self.game_info: if debug_mode: self.logger.debug(f"无法识别游戏版本: {game_version}") if not silent: QMessageBox.critical( None, f"错误 - {self.app_name}", f"\n无法识别游戏版本: {game_version}\n", QMessageBox.StandardButton.Ok, ) return {"success": False, "message": f"无法识别游戏版本: {game_version}", "action": "none"} # 检查补丁是否已安装 is_patch_installed = self.check_patch_installed(game_dir, game_version) if debug_mode: self.logger.debug(f"补丁安装状态检查结果: {is_patch_installed}") if not is_patch_installed: if debug_mode: self.logger.debug(f"{game_version} 未安装补丁,无法进行禁用/启用操作") if not silent: QMessageBox.warning( None, f"提示 - {self.app_name}", f"\n{game_version} 未安装补丁,无法进行禁用/启用操作。\n", QMessageBox.StandardButton.Ok, ) return {"success": False, "message": f"{game_version} 未安装补丁", "action": "none"} try: # 检查当前状态 is_disabled, disabled_path = self.check_patch_disabled(game_dir, game_version) if debug_mode: self.logger.debug(f"补丁禁用状态检查结果 - 是否禁用: {is_disabled}, 禁用路径: {disabled_path}") # 获取可能的补丁文件路径 install_path_base = os.path.basename(self.game_info[game_version]["install_path"]) patch_file_path = os.path.join(game_dir, install_path_base) # 尝试查找原始补丁文件,支持不同大小写 patch_files_to_check = [ patch_file_path, patch_file_path.lower(), patch_file_path.upper(), patch_file_path.replace("_", ""), patch_file_path.replace("_", "-"), ] if debug_mode: self.logger.debug(f"将检查以下可能的补丁文件: {patch_files_to_check}") # 确定操作类型 if operation: if operation == "enable": action_needed = is_disabled # 只有当前是禁用状态时才需要启用 elif operation == "disable": action_needed = not is_disabled # 只有当前是启用状态时才需要禁用 else: action_needed = True # 无效操作类型,强制进行操作 else: action_needed = True # 未指定操作类型,始终执行切换 if debug_mode: self.logger.debug(f"操作决策 - 操作类型: {operation}, 是否需要执行操作: {action_needed}") if not action_needed: # 补丁已经是目标状态,无需操作 if operation == "enable": message = f"{game_version} 补丁已经是启用状态" else: message = f"{game_version} 补丁已经是禁用状态" if debug_mode: self.logger.debug(f"{message}, 无需操作") if not silent: QMessageBox.information( None, f"提示 - {self.app_name}", f"\n{message}\n", QMessageBox.StandardButton.Ok, ) return {"success": True, "message": message, "action": "none"} if is_disabled: # 当前是禁用状态,需要启用 if disabled_path and os.path.exists(disabled_path): # 从禁用文件名去掉.fain后缀 enabled_path = disabled_path[:-5] # 去掉.fain if debug_mode: self.logger.debug(f"正在启用补丁 - 从 {disabled_path} 重命名为 {enabled_path}") os.rename(disabled_path, enabled_path) if debug_mode: self.logger.debug(f"已启用 {game_version} 的补丁,重命名文件成功") action = "enable" message = f"{game_version} 补丁已启用" else: # 未找到禁用的补丁文件,但状态是禁用 message = f"未找到禁用的补丁文件: {disabled_path}" if debug_mode: self.logger.debug(f"{message}") return {"success": False, "message": message, "action": "none"} else: # 当前是启用状态,需要禁用 # 查找正在使用的补丁文件 active_patch_file = None for patch_path in patch_files_to_check: if os.path.exists(patch_path): active_patch_file = patch_path if debug_mode: self.logger.debug(f"找到活跃的补丁文件: {active_patch_file}") break if active_patch_file: # 给补丁文件添加.fain后缀禁用它 disabled_path = f"{active_patch_file}.fain" if debug_mode: self.logger.debug(f"正在禁用补丁 - 从 {active_patch_file} 重命名为 {disabled_path}") os.rename(active_patch_file, disabled_path) if debug_mode: self.logger.debug(f"已禁用 {game_version} 的补丁,重命名文件成功") action = "disable" message = f"{game_version} 补丁已禁用" else: # 未找到活跃的补丁文件,但状态是启用 message = f"未找到启用的补丁文件,请检查游戏目录: {game_dir}" if debug_mode: self.logger.debug(f"{message}") return {"success": False, "message": message, "action": "none"} # 非静默模式下显示操作结果 if not silent: QMessageBox.information( None, f"操作成功 - {self.app_name}", f"\n{message}\n", QMessageBox.StandardButton.Ok, ) if debug_mode: self.logger.debug(f"切换补丁状态操作完成 - 结果: 成功, 操作: {action}, 消息: {message}") return {"success": True, "message": message, "action": action} except Exception as e: error_message = f"切换 {game_version} 补丁状态时出错: {str(e)}" if debug_mode: self.logger.debug(f"{error_message}") import traceback self.logger.debug(f"错误详情:\n{traceback.format_exc()}") if not silent: QMessageBox.critical( None, f"操作失败 - {self.app_name}", f"\n{error_message}\n", QMessageBox.StandardButton.Ok, ) return {"success": False, "message": error_message, "action": "none"} def batch_toggle_patches(self, game_dirs, operation=None): """批量切换多个游戏补丁的禁用/启用状态 Args: game_dirs: 游戏版本到游戏目录的映射字典 operation: 指定操作,可以是"enable"、"disable"或None(None则自动切换当前状态) Returns: tuple: (成功数量, 失败数量, 详细结果列表) """ success_count = 0 fail_count = 0 debug_mode = self._is_debug_mode() results = [] if debug_mode: self.logger.debug(f"开始批量切换补丁状态 - 操作: {operation}, 游戏数量: {len(game_dirs)}") self.logger.debug(f"游戏列表: {list(game_dirs.keys())}") for version, path in game_dirs.items(): try: if debug_mode: self.logger.debug(f"处理游戏 {version}, 目录: {path}") # 在批量模式下使用静默操作 result = self.toggle_patch(path, version, operation=operation, silent=True) if debug_mode: self.logger.debug(f"游戏 {version} 操作结果: {result}") if result["success"]: success_count += 1 if debug_mode: self.logger.debug(f"游戏 {version} 操作成功,操作类型: {result['action']}") else: fail_count += 1 if debug_mode: self.logger.debug(f"游戏 {version} 操作失败,原因: {result['message']}") results.append({ "version": version, "success": result["success"], "message": result["message"], "action": result["action"] }) except Exception as e: if debug_mode: self.logger.debug(f"切换 {version} 补丁状态时出错: {str(e)}") import traceback self.logger.debug(f"错误详情:\n{traceback.format_exc()}") fail_count += 1 results.append({ "version": version, "success": False, "message": f"操作出错: {str(e)}", "action": "none" }) if debug_mode: self.logger.debug(f"批量切换补丁状态完成 - 成功: {success_count}, 失败: {fail_count}") return success_count, fail_count, results def show_toggle_result(self, success_count, fail_count, results=None): """显示批量切换补丁状态的结果 Args: success_count: 成功操作的数量 fail_count: 操作失败的数量 results: 详细结果列表,如果提供,会显示更详细的信息 """ result_text = f"\n批量操作完成!\n成功: {success_count} 个\n失败: {fail_count} 个\n" # 如果有详细结果,添加到消息中 if results: enabled_list = [r["version"] for r in results if r["success"] and r["action"] == "enable"] disabled_list = [r["version"] for r in results if r["success"] and r["action"] == "disable"] skipped_list = [r["version"] for r in results if r["success"] and r["action"] == "none"] fail_list = [r["version"] for r in results if not r["success"]] if enabled_list: result_text += f"\n【已启用补丁】:\n{chr(10).join(enabled_list)}\n" if disabled_list: result_text += f"\n【已禁用补丁】:\n{chr(10).join(disabled_list)}\n" if skipped_list: result_text += f"\n【无需操作】:\n{chr(10).join(skipped_list)}\n" if fail_list: result_text += f"\n【操作失败】:\n{chr(10).join(fail_list)}\n" QMessageBox.information( None, f"批量操作完成 - {self.app_name}", result_text, QMessageBox.StandardButton.Ok, ) def show_result(self): """显示安装结果,区分不同情况""" # 获取当前安装状态 installed_versions = [] # 成功安装的版本 skipped_versions = [] # 已有补丁跳过的版本 failed_versions = [] # 安装失败的版本 not_found_versions = [] # 未找到的版本 # 获取所有游戏版本路径 install_paths = self.main_window.download_manager.get_install_paths() if hasattr(self.main_window.download_manager, "get_install_paths") else {} # 检查是否处于离线模式 is_offline_mode = False if hasattr(self.main_window, 'offline_mode_manager'): is_offline_mode = self.main_window.offline_mode_manager.is_in_offline_mode() # 获取本次实际安装的游戏列表 installed_games = [] # 在线模式下使用download_queue_history if hasattr(self.main_window, 'download_queue_history') and self.main_window.download_queue_history: installed_games = self.main_window.download_queue_history # 离线模式下使用offline_mode_manager.installed_games if is_offline_mode and hasattr(self.main_window.offline_mode_manager, 'installed_games'): installed_games = self.main_window.offline_mode_manager.installed_games debug_mode = self._is_debug_mode() if debug_mode: self.logger.debug(f"DEBUG: 显示安装结果,离线模式: {is_offline_mode}") self.logger.debug(f"DEBUG: 本次安装的游戏: {installed_games}") for game_version, is_installed in self.main_window.installed_status.items(): # 只处理install_paths中存在的游戏版本 if game_version in install_paths: path = install_paths[game_version] # 检查游戏是否存在但未通过本次安装补丁 if is_installed: # 游戏已安装补丁 if game_version in installed_games: # 本次成功安装 installed_versions.append(game_version) else: # 已有补丁,被跳过下载 skipped_versions.append(game_version) else: # 游戏未安装补丁 if os.path.exists(path): # 游戏文件夹存在,但安装失败 failed_versions.append(game_version) else: # 游戏文件夹不存在 not_found_versions.append(game_version) # 构建结果信息 result_text = f"\n安装结果:\n" # 总数统计 - 只显示本次实际安装的数量 total_installed = len(installed_versions) total_failed = len(failed_versions) result_text += f"安装成功:{total_installed} 个 安装失败:{total_failed} 个\n\n" # 详细列表 if installed_versions: result_text += f"【成功安装】:\n{chr(10).join(installed_versions)}\n\n" if failed_versions: result_text += f"【安装失败】:\n{chr(10).join(failed_versions)}\n\n" if not_found_versions: # 只有在真正检测到了游戏但未安装补丁时才显示 result_text += f"【尚未安装补丁的游戏】:\n{chr(10).join(not_found_versions)}\n" QMessageBox.information( self.main_window, f"安装完成 - {APP_NAME}", result_text )