aboutsummaryrefslogtreecommitdiff
path: root/libs/basic_games/epic_utils.py
blob: 94ae6a95e2dec9968cbad0de6a4ce082075c79a5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# -*- encoding: utf-8 -*-
from __future__ import annotations

import itertools
import json
import os
import sys
from collections.abc import Iterable
from pathlib import Path

try:
    import winreg
except ImportError:
    winreg = None

ErrorList = list[tuple[str, Exception]]


def find_epic_games(
    errors: ErrorList | None = None,
) -> Iterable[tuple[str, Path]]:
    if winreg is None:
        return

    try:
        with winreg.OpenKey(
            winreg.HKEY_LOCAL_MACHINE,
            r"Software\Wow6432Node\Epic Games\EpicGamesLauncher",
        ) as key:
            epic_data_path, _ = winreg.QueryValueEx(key, "AppDataPath")
    except FileNotFoundError:
        epic_data_path = r"%ProgramData%\Epic\EpicGamesLauncher\Data"

    manifests_path = Path(os.path.expandvars(epic_data_path)).joinpath("Manifests")
    if manifests_path.exists():
        for manifest_file_path in manifests_path.glob("*.item"):
            try:
                with open(manifest_file_path, encoding="utf-8") as manifest_file:
                    manifest_file_data = json.load(manifest_file)
                yield (
                    manifest_file_data["AppName"],
                    Path(manifest_file_data["InstallLocation"]),
                )
            except (json.JSONDecodeError, KeyError) as e:
                error_message = (
                    f'Unable to parse Epic Games manifest file: "{manifest_file_path}"\n'
                    " Try to run the launcher recreate it."
                )
                print(
                    error_message,
                    e,
                    file=sys.stderr,
                )
                if errors is not None:
                    errors.append((error_message, e))


def find_legendary_games(
    config_path: str | None = None, errors: ErrorList | None = None
) -> Iterable[tuple[str, Path]]:
    # Based on legendary source:
    # https://github.com/derrod/legendary/blob/master/legendary/lfs/lgndry.py
    if config_path := config_path or os.environ.get("XDG_CONFIG_HOME"):
        legendary_config_path = Path(config_path, "legendary")
    else:
        legendary_config_path = Path("~/.config/legendary").expanduser()

    installed_path = legendary_config_path / "installed.json"
    if installed_path.exists():
        try:
            with open(installed_path, encoding="utf-8") as installed_file:
                installed_games = json.load(installed_file)
            for game in installed_games.values():
                yield game["app_name"], Path(game["install_path"])
        except (json.JSONDecodeError, AttributeError, KeyError) as e:
            error_message = (
                f'Unable to parse installed games from Legendary/Heroic launcher: "{installed_path}"\n'
                " Try to run the launcher to recrated the file."
            )
            print(
                error_message,
                e,
                file=sys.stderr,
            )
            if errors is not None:
                errors.append((error_message, e))


def find_heroic_games(errors: ErrorList | None = None):
    return find_legendary_games(
        os.path.expandvars(r"%AppData%\heroic\legendaryConfig"), errors
    )


def find_games(errors: ErrorList | None = None) -> dict[str, Path]:
    return dict(
        itertools.chain(
            find_epic_games(errors=errors),
            find_legendary_games(errors=errors),
            find_heroic_games(errors=errors),
        )
    )


if __name__ == "__main__":
    games = find_games()
    for k, v in games.items():
        print("Found game with id {} at {}.".format(k, v))