commit d5523ba3cb1b210f2bef9a376d15925885428bb5 Author: Joseph Montanaro Date: Sat Apr 17 16:44:55 2021 -0700 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..990f932 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +aphremove.egg-info/** +**__pycache__** \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e2740fb --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +This script removes the Aphrodite textures from Supergiant Games' *Hades*. + + +## Installation + + git clone https://git.jfmonty2.com/jfmonty2/aphremove.git + pip install -e aphremove + aphremove + +If Hades is installed to a Steam library folder somewhere other than the standard `C:\Program Files (x86)\Steam`, then + + aphremove path\to\steam\dir + + +## Notes + +Heavy lifting is done by [quaerus/deppth](https://github.com/quaerus/deppth). +All thanks are due to that project, as there is no way I would have been able to reverse-engineer the Hades package format myself. diff --git a/aphremove/__init__.py b/aphremove/__init__.py new file mode 100644 index 0000000..504f46c --- /dev/null +++ b/aphremove/__init__.py @@ -0,0 +1,84 @@ +import json +import os, os.path +import pathlib +import sys +import tempfile + +from deppth import deppth # not sure why the nested path +from PIL import Image, ImageDraw + + +def get_list(filename): + d = pathlib.Path(__file__).parent + lines = (d / filename).read_text().splitlines() + return set(lines) + + +def remove_subtexture(package_dir, manifest, sub_atlas): + filename = manifest['name'].split(os.path.sep)[-1] + '.png' + file = package_dir / 'textures/atlases' / filename + img = Image.open(file) + + draw = ImageDraw.Draw(img) + r = sub_atlas['rect'] + top_left = (r['x'], r['y']) + bottom_right = (r['x'] + r['width'], r['y'] + r['height']) + draw.rectangle(xy=(top_left, bottom_right), fill=(0, 0, 0, 0)) + + img.save(file) + + +def process_pkg(src_path, include, exclude): + print('Processing package', src_path,) + with tempfile.TemporaryDirectory() as tmp: + tmp = pathlib.Path(tmp) + pkg_dir = str(tmp / 'package') + print('Extracting package - this may take some time.') + deppth.extract(src_path, pkg_dir) + + manifests = [] + for m in (tmp / 'package/manifest').iterdir(): + with open(m) as f: + manifests.append(json.load(f)) + + print('Removing subtextures') + modified = [] + for manifest in manifests: + for sub in manifest['subAtlases']: + if 'aphrodite' in sub['name'].lower(): + if sub['name'] in include: + remove_subtexture(tmp / 'package', manifest, sub) + manifest_name = manifest['name'].split(os.path.sep)[-1] + modified.append(manifest_name) + elif sub['name'] not in exclude: + print(f'WARNING: Unknown texture "{sub["name"]}". You may wish to add this texture to include.txt or exclude.txt.') + + print('Constructing patches') + patch_path = str(tmp / 'patch.pkg') + deppth.pack(pkg_dir, patch_path, *modified) + print('Repacking package') + deppth.patch(src_path, patch_path) + print('Finished processing package.') + + +def run(): + if len(sys.argv) > 1: + steam_dir = sys.argv[1] + else: + steam_dir = 'C:/Program Files (x86)/Steam' + base_dir = pathlib.Path(steam_dir) / 'steamapps/common/Hades/Content/Win/Packages' + if not base_dir.exists(): + print(f'Could not find Hades at {base_dir}. Is {steam_dir} the correct location for your Steam library?') + return 1 + + include = get_list('include.txt') + exclude = get_list('exclude.txt') + for pkg in 'BC3/GUI.pkg', 'GUI.pkg', '720p/GUI.pkg': + path = str(base_dir / pkg) + process_pkg(path, include, exclude) + print() + print('Finished.') + + +if __name__ == '__main__': + run() diff --git a/aphremove/exclude.txt b/aphremove/exclude.txt new file mode 100644 index 0000000..e134b22 --- /dev/null +++ b/aphremove/exclude.txt @@ -0,0 +1,78 @@ +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0001 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0002 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0003 +GUI\Screens\BoonIcons\Aphrodite_05_Large +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0004 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0005 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0006 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0007 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0008 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0009 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0045 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0047 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0048 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0049 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0010 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0050 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0011 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0040 +GUI\HUD\BoonIconAphrodite +GUI\Screens\BoonIcons\Aphrodite_13_Large +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0041 +GUI\Screens\BoonIcons\Aphrodite_09_Large +GUI\Screens\BoonIcons\Aphrodite_11_Large +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0012 +GUI\Icons\RoomReward\Aphrodite +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0042 +GUI\Screens\BoonIcons\Aphrodite_secondary_attack +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0043 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0044 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0014 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0046 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0015 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0016 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0017 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0018 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0019 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0021 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0022 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0023 +GUI\Screens\BoonIcons\Aphrodite_07_Large +GUI\Screens\BoonIcons\Aphrodite_12_Large +GUI\Screens\BoonIcons\Aphrodite_Athena_01 +GUI\Screens\BoonIcons\Aphrodite_01_Large +GUI\Screens\AwardMenu\KeepsakeMaxGift\Aphrodite_02 +GUI\Screens\AwardMenu\KeepsakeMaxGift\Aphrodite +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0013 +GUI\Screens\BoonSelectSymbols\Aphrodite +GUI\Epilogue\Olympus_Aphrodite +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0024 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0025 +GUI\Screens\BoonSelectSymbols\Aphrodite2 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0020 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0026 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0032 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0034 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0027 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0035 +GUI\Screens\BoonIcons\Ares_Aphrodite_01 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0028 +GUI\Screens\BoonIcons\Zeus_Aphrodite_01 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0029 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0030 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0031 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0033 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0037 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0036 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0038 +GUI\Screens\BoonSelectSymbols\Spin\AphroditeIconSpin\AphroditeIconSpin0039 +GUI\Screens\BoonIcons\Aphrodite_03_Large +GUI\Screens\BoonIcons\Aphrodite_04_Large +GUI\Screens\BoonIcons\Artemis_Aphrodite_01 +GUI\Screens\BoonIcons\Aphrodite_06_Large +GUI\Screens\BoonIcons\Aphrodite_10_Large +GUI\Screens\BoonIcons\Aphrodite_Demeter_01 +GUI\Screens\BoonIcons\Aphrodite_Poseidon_01 +GUI\Screens\BoonIcons\Dionysus_Aphrodite_01 +GUI\Screens\BoonIcons\Aphrodite_02_Large +GUI\Screens\BoonIcons\Aphrodite_08_Large diff --git a/aphremove/include.txt b/aphremove/include.txt new file mode 100644 index 0000000..b1686c8 --- /dev/null +++ b/aphremove/include.txt @@ -0,0 +1,2 @@ +Portraits\Portraits_Aphrodite_01 +Portraits\Codex\CodexPortrait_Aphrodite \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..975fc29 --- /dev/null +++ b/setup.py @@ -0,0 +1,15 @@ +from setuptools import setup, find_packages + +setup( + name='aphremove', + description='Remove Aphrodite textures from Hades game', + author='Joseph Montanaro', + install_requires=[ + 'deppth@https://github.com/quaerus/deppth/releases/download/v0.1.0.0/deppth-0.1.0.0-py3-none-any.whl', + 'pillow' + ], + packages=find_packages(), + entry_points = { + 'console_scripts': ['aphremove = aphremove:run'] + }, +) \ No newline at end of file