Python之Epic游戏安装信息获取

0 阅读2分钟

更多 Python 文章见《有容乃大(Python集萃)》专栏

如何在 Windows 上自动发现 Epic Games 已安装游戏及其运行信息:

  • 通过注册表定位 Launcher 安装路径
  • 定位、解析 JSON manifest 文件
  • 从元数据中获取所需信息

整体架构

架构图

epic-arch.png


输入层

  • 注册表:提供 Launcher 安装入口

发现层

  • get_install_path():定位 Launcher
  • _get_manifests_path():定位 manifest

处理层

  • 文件过滤(hex 文件名)
  • JSON 解析
  • 数据合法性校验

输出层

  • games dict(核心 API)
  • 单游戏详情(扩展 API)

核心流程

流程图

epic-flow.png

核心处理流程-循环执行:

  • 扫描 .item 文件
  • 过滤非 hex 文件
  • 解析 JSON
  • 校验关键字段
  • 构建 game_info

关键决策点

决策点作用
注册表是否存在是否 fallback
文件名是否 hex是否为合法 manifest
InstallLocation 是否存在防止脏数据
exe 是否存在是否可直接运行

核心实现拆解

配置 / 输入

数据结构

{
  "appid": str,
  "name": str,
  "namespace": str,
  "install_location": str,
  "version": str,
  "executable": Optional[str]
}

设计点:

设计点原因
使用 dict灵活扩展字段
appid 作为 key唯一标识
executable 可选并非所有游戏都有

核心处理模块

核心机制

文件系统扫描 → 过滤 → JSON解析 → 数据验证 → 输出

设计点

设计目的
hex 校验避免误解析非游戏文件
JSON 标准解析简化实现
路径存在校验防止卸载残留
分阶段处理提高可维护性

核心代码解析

路径发现核心

get_install_path()核心代码

def get_install_path():
    try:
        with winreg.OpenKey(HKEY_LOCAL_MACHINE, REGISTRY_PATH) as key:
            exe_path, _ = winreg.QueryValueEx(key, REGISTRY_VALUE)

        exe_path = os.path.normpath(exe_path)

        parts = exe_path.split(os.sep)

        for i, part in enumerate(parts):
            if part.lower() == "launcher":
                install_path = os.sep.join(parts[:i+1])
                if os.path.exists(install_path):
                    return install_path

    except Exception:
        pass

    # fallback
    for path in COMMON_INSTALL_PATHS:
        if os.path.exists(path):
            return path

    return ""

从注册表获取到路径后,必须 os.path.exists() 再返回

if os.path.exists(install_path):

防御点:

  • 注册表可能残留(卸载未清理)
  • 路径可能失效

数据源定位

_get_manifests_path()核心代码

def _get_manifests_path():
    data_path = os.path.expandvars(DATA_PATH_TEMPLATE)
    manifests_path = os.path.join(data_path, MANIFESTS_SUBDIR)

    if os.path.exists(manifests_path):
        return manifests_path

    return ""

过滤器设计

_is_hex_filename()核心代码

def _is_hex_filename(filename):
    name, _ = os.path.splitext(filename)

    if not name:
        return False

    try:
        int(name, 16)
        return True
    except ValueError:
        return False
方案优点缺点
int(...,16)快 + 语义强不直观
regex可读性高性能略低

主处理管线

get_installed_games()核心代码骨架

games = {}

for item_file in Path(manifests_path).glob("*.item"):

    if not _is_hex_filename(item_file.name):
        continue

    data = json.load(...)

    appid = data.get("CatalogItemId")
    if not appid:
        continue

    install_location = data.get("InstallLocation")
    if not os.path.exists(install_location):
        continue

    game_info = {...}

    launch_exe = data.get("LaunchExecutable")
    if launch_exe:
        exe_path = os.path.join(install_location, launch_exe)
        if os.path.exists(exe_path):
            game_info["executable"] = exe_path

    games[appid] = game_info

核心机制:Pipeline(流水线)

逐字段校验,逐字段 fail-fast,避免:

  • manifest 不完整
  • 卸载残留
  • 手动篡改

install_location 必须存在,否则会出现:

  • UI 显示“已安装”
  • 实际已删除

总结

整个核心代码可以抽象为:

Environment Discovery
    ↓
Data Source Location
    ↓
File Filtering
    ↓
Structured Parsing
    ↓
Validation Pipeline
    ↓
Normalized Output