Skip to content

Commit

Permalink
Merge pull request #2 from spelunky-fyi/ui-overhaul
Browse files Browse the repository at this point in the history
Ui overhaul
  • Loading branch information
gmjosack authored Dec 1, 2020
2 parents 9b80a58 + 481562c commit a0d487f
Show file tree
Hide file tree
Showing 17 changed files with 438 additions and 568 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.4.1
0.5.0
2 changes: 0 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
Flask==1.1.2
psutil==5.7.3
pyinstaller==4.0
packaging==20.4
requests==2.25.0
Expand Down
45 changes: 26 additions & 19 deletions src/modlunky2/assets/assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
from .exc import FileConflict, MissingAsset, MultipleMatchingAssets


logger = logging.getLogger("modlunky2")


@dataclass
class ExeAssetBlock:
""" Represent a block of information about an asset in the exe."""
Expand Down Expand Up @@ -108,7 +111,7 @@ def write_data(self, exe_handle, data):
exe_handle.write(data)


class ExeAsset(object):
class ExeAsset:
def __init__(self, asset_block, filepath):
self.asset_block = asset_block
self.filepath = filepath
Expand Down Expand Up @@ -150,14 +153,14 @@ def extract(

# Recompress at higher compression level to give
# better chance of assets fitting in binary
logging.info("Storing compressed asset %s...", compressed_filepath)
logger.info("Storing compressed asset %s...", compressed_filepath)
with compressed_filepath.open("wb") as compressed_file:
cctx = zstd.ZstdCompressor(level=compression_level)
compressed_data = cctx.compress(self.data)
compressed_file.write(compressed_data)

except Exception: # pylint: disable=broad-except
logging.exception("Failed compression")
logger.exception("Failed compression")
return None


Expand All @@ -171,7 +174,7 @@ def extract(
with md5sum_filepath.open("w") as md5sum_file:
md5sum_file.write(md5sum)

logging.info("Storing asset %s...", filepath)
logger.info("Storing asset %s...", filepath)
if self.filepath in PNGS:
filepath = filepath.with_suffix(".png")

Expand Down Expand Up @@ -244,15 +247,17 @@ def populate_asset_filepaths(self):
@staticmethod
def _extract_single(asset, *args, **kwargs):
try:
logging.info("Extracting %s... ", asset.filepath)
logger.info("Extracting %s... ", asset.filepath)
asset.extract(*args, **kwargs)
except Exception: # pylint: disable=broad-except
logging.exception("Failed Extraction")
logger.exception("Failed Extraction")

def extract(self, extract_dir, compressed_dir, compression_level=DEFAULT_COMPRESSION_LEVEL):
def extract(
self, extract_dir, compressed_dir,
compression_level=DEFAULT_COMPRESSION_LEVEL
):
unextracted = []
for asset in self.assets:

if asset.filepath is None:
# No known filepaths matched this asset.
unextracted.append(asset)
Expand All @@ -264,11 +269,11 @@ def extract(self, extract_dir, compressed_dir, compression_level=DEFAULT_COMPRES
futures = [
pool.submit(
self._extract_single,
asset,
extract_dir,
compressed_dir,
self.key,
compression_level,
asset,
extract_dir,
compressed_dir,
self.key,
compression_level,
)
for asset in self.assets
if asset.filepath
Expand All @@ -288,10 +293,10 @@ def pack_assets(self):
data = asset.disk_asset.get_asset_data()

if asset.asset_block.is_encrypted:
logging.info("Encrypting file %s", asset.disk_asset.asset_path)
logger.info("Encrypting file %s", asset.disk_asset.asset_path)
data = chacha(asset.filepath.encode(), data, self.key)

logging.info("Packing file %s", asset.disk_asset.asset_path)
logger.info("Packing file %s", asset.disk_asset.asset_path)
self.exe_handle.write(pack(
"<II", asset.asset_block.data_len, asset.asset_block.filepath_len
))
Expand Down Expand Up @@ -339,7 +344,9 @@ def repackage(

asset.asset_block.offset = offset
asset.asset_block.asset_len = disk_asset.get_asset_len()
asset.asset_block.asset_offset = asset.asset_block.offset + 8 + asset.asset_block.filepath_len + 1
asset.asset_block.asset_offset = (
asset.asset_block.offset + 8 + asset.asset_block.filepath_len + 1
)

# The name hash of soundbank files is padded such that the asset_offset
# is divisible by 32.
Expand Down Expand Up @@ -431,7 +438,7 @@ def from_dirs(

if resolution_policy == ResolutionPolicy.RaiseError and len(modpack_files) >= 2:
raise FileConflict(
f"{filepath} found in multiple packs: {', '.join(modpack_files)}"
f"{filepath} found in multiple packs: {', '.join(map(str, modpack_files))}"
)

idx = 0
Expand Down Expand Up @@ -532,7 +539,7 @@ def compress(self, compression_level=DEFAULT_COMPRESSION_LEVEL):
self.md5sum_path.parent.mkdir(parents=True, exist_ok=True)

if self.asset_path.suffix == ".png":
logging.info('Converting image "%s" to DDS', self.asset_path)
logger.info('Converting image "%s" to DDS', self.asset_path)
with Image.open(self.asset_path) as img:
data = png_to_dds(img)
else:
Expand All @@ -543,7 +550,7 @@ def compress(self, compression_level=DEFAULT_COMPRESSION_LEVEL):
with self.md5sum_path.open("wb") as md5sum_file:
md5sum_file.write(md5sum)

logging.info("Compressing %s...", self.asset_path)
logger.info("Compressing %s...", self.asset_path)
cctx = zstd.ZstdCompressor(level=compression_level)
data = cctx.compress(data)
with open(self.compressed_path, "wb") as compressed_file:
Expand Down
13 changes: 8 additions & 5 deletions src/modlunky2/assets/patcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
PATCH_REPLACE = b"\x48\x3B\xC1\x74\x09" + b"\x90" * 9


logger = logging.getLogger("modlunky2")


class Patcher:
def __init__(self, exe_handle):
self.exe_handle = exe_handle
Expand Down Expand Up @@ -55,23 +58,23 @@ def is_patched(self) -> bool:
def patch(self):
self.exe_handle.seek(0)

logging.info("Patching asset checksum check")
logger.info("Patching asset checksum check")
index = self.find(PATCH_START)
if index == -1:
logging.warning("Didn't find instructions to patch. Is game unmodified?")
logger.warning("Didn't find instructions to patch. Is game unmodified?")
return False

self.exe_handle.seek(index)
ops = self.exe_handle.read(14)

if ops[-1] != PATCH_END:
logging.warning(
logger.warning(
"Checksum check has unexpected form, this script has "
"to be updated for the current game version."
)
logging.warning("(Expected 0x{:02x}, found 0x{:02x})".format(PATCH_END, ops[-1]))
logger.warning("(Expected 0x{:02x}, found 0x{:02x})".format(PATCH_END, ops[-1]))
return False

logging.info("Found check at 0x{:08x}, replacing with NOPs".format(index))
logger.info("Found check at 0x{:08x}, replacing with NOPs".format(index))
self.exe_handle.seek(index)
self.exe_handle.write(PATCH_REPLACE)
63 changes: 6 additions & 57 deletions src/modlunky2/cli.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,9 @@
import argparse
import logging
import sys
from pathlib import Path

import requests
from flask import Flask
from packaging import version

from .views.assets import blueprint as assets_blueprint
from .views.index import blueprint as index_blueprint

PROCESS_NAME = "Spel2.exe"
# Setup static files to work with onefile exe
BASE_DIR = Path(__file__).resolve().parent
APP_DIR = BASE_DIR
ROOT_DIR = BASE_DIR.parent.parent
if hasattr(sys, "_MEIPASS"):
BASE_DIR = BASE_DIR / getattr(sys, "_MEIPASS")
APP_DIR = Path(sys.executable).resolve().parent
ROOT_DIR = BASE_DIR


app = Flask(
__name__,
static_folder=f"{BASE_DIR / 'static'}",
template_folder=f"{BASE_DIR / 'templates'}",
)
app.register_blueprint(index_blueprint)
app.register_blueprint(assets_blueprint, url_prefix="/assets")


def get_latest_version():
try:
return version.parse(
requests.get(
"https://api.github.com/repos/spelunky-fyi/modlunky2/releases/latest"
).json()["tag_name"]
)
except Exception: # pylint: disable=broad-except
return None


def get_current_version():
with (ROOT_DIR / "VERSION").open() as version_file:
return version.parse(version_file.read().strip())
from .constants import APP_DIR
from .ui import ModlunkyUI


def main():
Expand All @@ -53,11 +13,6 @@ def main():
)
parser.add_argument("--port", type=int, default=8040, help="Port to listen on.")
parser.add_argument("--debug", default=False, action="store_true")
parser.add_argument(
"--process-name",
default=PROCESS_NAME,
help="Name of Spelunky Process. (Default: %(default)s",
)
parser.add_argument(
"--install-dir",
default=APP_DIR,
Expand All @@ -68,13 +23,7 @@ def main():
log_format = "%(asctime)s: %(message)s"
logging.basicConfig(format=log_format, level=logging.INFO, datefmt="%H:%M:%S")

try:
app.config.SPELUNKY_INSTALL_DIR = Path(args.install_dir)
app.config.MODLUNKY_CURRENT_VERSION = get_current_version()
app.config.MODLUNKY_LATEST_VERSION = get_latest_version()
app.config.MODLUNKY_NEEDS_UPDATE = (
app.config.MODLUNKY_CURRENT_VERSION < app.config.MODLUNKY_LATEST_VERSION
)
app.run(host=args.host, port=args.port, debug=args.debug)
except Exception as err: # pylint: disable=broad-except
input(f"Failed to start ({err}). Press enter to exit... :(")
install_dir = Path(args.install_dir)

native_ui = ModlunkyUI(install_dir)
native_ui.mainloop()
12 changes: 12 additions & 0 deletions src/modlunky2/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import sys
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent
APP_DIR = BASE_DIR
ROOT_DIR = BASE_DIR.parent.parent

# Setup static files to work with onefile exe
if hasattr(sys, "_MEIPASS"):
BASE_DIR = BASE_DIR / getattr(sys, "_MEIPASS")
APP_DIR = Path(sys.executable).resolve().parent
ROOT_DIR = BASE_DIR
Loading

0 comments on commit a0d487f

Please sign in to comment.