mirror of
https://github.com/isledecomp/isle.git
synced 2026-01-20 23:01:16 +00:00
99 lines
3.6 KiB
Python
Executable File
99 lines
3.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import argparse
|
|
import pathlib
|
|
import re
|
|
import requests
|
|
import subprocess
|
|
|
|
ISLE_PATH = pathlib.Path(__file__).parents[0]
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("-i", "--inspect", metavar='<offset>',
|
|
help="Get an assembly diff for the function at a given offset after building. "
|
|
"When decomping you will typically run this command over and over again as "
|
|
"you refine your code towards a 100% match.")
|
|
parser.add_argument("-s", "--status", action="store_true",
|
|
help="Get a general status report on the progress decompiling all functions.")
|
|
parser.add_argument("--inspect-shim", nargs=2, metavar=('<file>', '<line>'),
|
|
help="Inspect the assembly diff of the function spanning <line> in <file>. "
|
|
"Intended to be invoked by IDE commands.")
|
|
parser.add_argument("-B", dest="builddir", default=ISLE_PATH / "build", type=pathlib.Path, help="build directory")
|
|
args = parser.parse_args()
|
|
|
|
# Figure out what offset to pass to the reccmp tool if any
|
|
def get_inspect_offset():
|
|
if inspect_offset := args.inspect:
|
|
if inspect_offset.startswith("0x"):
|
|
return inspect_offset
|
|
else:
|
|
return "0x" + inspect_offset
|
|
elif args.inspect_shim:
|
|
print("Inspect: ", args.inspect_shim[0], ":", args.inspect_shim[1])
|
|
with open(args.inspect_shim[0], 'r') as f:
|
|
# get the lines in an array
|
|
lines = f.readlines()
|
|
OFFSET_PATTERN = re.compile(r'OFFSET:\s*LEGO1\s*(0x[0-9a-f]+)')
|
|
for i in range(int(args.inspect_shim[1]), 0, -1):
|
|
if i > 0 and i < len(lines):
|
|
if match := OFFSET_PATTERN.search(lines[i]):
|
|
inspect_offset = match.group(1)
|
|
break
|
|
if inspect_offset:
|
|
return inspect_offset
|
|
else:
|
|
print("Could not find any function to inspect near cursor.")
|
|
exit(1)
|
|
return None
|
|
|
|
# If the build directory doesn't exist yet, run configure.py
|
|
if not args.builddir.exists():
|
|
print("Not configured yet, please run configure.py first.")
|
|
exit(1)
|
|
|
|
# Run cmake, with no parallel build because that does not play nice the
|
|
# MSVC420 compiler the original game was built with thanks to the different
|
|
# threads contending over the pdb file.
|
|
result = subprocess.run(["cmake", "--build", ".", "-j", "1"], cwd=str(args.builddir))
|
|
|
|
def require_original():
|
|
if not (ISLE_PATH / "original/LEGO1.DLL").exists():
|
|
(ISLE_PATH / "original").mkdir(parents=True, exist_ok=True)
|
|
print("Couldn't find original/LEGO1.DLL. Downloading a copy...")
|
|
url1 = "https://legoisland.org/download/ISLE.EXE"
|
|
r1 = requests.get(url1, stream=True)
|
|
if r1.status_code == 200:
|
|
with open("original/ISLE.EXE", 'wb') as f:
|
|
for chunk in r1.iter_content(chunk_size=8192):
|
|
f.write(chunk)
|
|
url2 = "https://legoisland.org/download/LEGO1.DLL"
|
|
r2 = requests.get(url2, stream=True)
|
|
if r2.status_code == 200:
|
|
with open("original/LEGO1.DLL", 'wb') as f:
|
|
for chunk in r2.iter_content(chunk_size=8192):
|
|
f.write(chunk)
|
|
else:
|
|
print("Failed to find or download LEGO1.DLL.")
|
|
print("Please obtain a copy from the original game and place it in `original/`")
|
|
exit(1)
|
|
print("Downloaded LEGO1.DLL to `original/`.")
|
|
|
|
# If the build succeeded, run the decompiler
|
|
if result.returncode == 0:
|
|
if inspect_offset := get_inspect_offset():
|
|
require_original()
|
|
subprocess.run(["python", "tools/reccmp/reccmp.py",
|
|
"original/LEGO1.DLL", "build/LEGO1.DLL",
|
|
"build/LEGO1.PDB", ".",
|
|
"-v", inspect_offset])
|
|
|
|
elif args.status:
|
|
require_original()
|
|
subprocess.run(["python", "tools/reccmp/reccmp.py",
|
|
"original/LEGO1.DLL", "build/LEGO1.DLL",
|
|
"build/LEGO1.PDB", "."])
|
|
|
|
else:
|
|
print("Build failed.")
|
|
exit(1)
|