Files
ShadowMountPlus/tools/generate_shellcore_offsets_from_files.py
T

108 lines
3.5 KiB
Python

#!/usr/bin/env python3
"""Generate SceShellCore lifecycle offsets from a flat ELF directory.
Input files must be named ``SceShellCoreXXX.elf``, ``SceShellCoreXXXX.elf`` or
use an equivalent build suffix such as ``SceShellCore701_00.elf``.
The last two digits are the minor firmware version; the preceding digits are
the major version. For example, ``SceShellCore1140.elf`` is firmware 11.40.
"""
from __future__ import annotations
import argparse
import re
from pathlib import Path
from generate_shellcore_offsets import (
ShellCore,
TARGET_NAMES,
firmware_key,
)
SHELLCORE_NAME = re.compile(r"^SceShellCore(\d{3,4})(?:_\d{2})?\.elf$")
def firmware_files(root: Path) -> list[tuple[int, int, Path]]:
by_version: dict[tuple[int, int], Path] = {}
for path in root.iterdir():
if not path.is_file():
continue
match = SHELLCORE_NAME.fullmatch(path.name)
if match is None:
continue
version = match.group(1)
key = (int(version[:-2]), int(version[-2:]))
previous = by_version.get(key)
if previous is not None:
if previous.read_bytes() != path.read_bytes():
raise ValueError(
f"different SceShellCore builds for {key[0]}.{key[1]:02d}: "
f"{previous.name}, {path.name}"
)
if "_" in previous.stem and "_" not in path.stem:
by_version[key] = path
continue
by_version[key] = path
return [
(major, minor, by_version[(major, minor)])
for major, minor in sorted(by_version)
]
def generate(root: Path) -> str:
records: list[str] = []
files = firmware_files(root)
if not files:
raise ValueError(f"{root}: no SceShellCore firmware files found")
for major, minor, path in files:
firmware_name = f"{major}.{minor:02d}"
firmware = firmware_key(firmware_name)
shellcore = ShellCore(path)
targets = shellcore.locate_targets()
cave_offset, cave_size = shellcore.bridge_cave()
records.append(
" {\n"
f" .firmware = 0x{firmware:04x}u,\n"
f" .name = \"{firmware_name}\",\n"
f" .bridge_cave_offset = 0x{cave_offset:x}u,\n"
f" .bridge_cave_size = 0x{cave_size:x}u,\n"
f" .sandbox_call_target_offset = "
f"0x{targets['sandbox_ready_target']:x}u,\n"
" .targets = {"
+ ", ".join(
"{.offset = "
f"0x{targets[name]:x}u, .patch_size = "
f"{5 if name == 'sandbox_ready' else shellcore.patch_size(targets[name])}u}}"
for name in TARGET_NAMES
)
+ "},\n }"
)
if not records:
raise ValueError(f"{root}: no SceShellCore firmware files found")
return (
"/* Generated by tools/generate_shellcore_offsets_from_files.py. */\n"
+ ",\n".join(records)
+ "\n"
)
def main() -> int:
parser = argparse.ArgumentParser(
description="Generate offsets from SceShellCoreXXXX.elf files"
)
parser.add_argument("root", type=Path, help="directory containing the ELF files")
parser.add_argument("--output", type=Path)
args = parser.parse_args()
output = generate(args.root)
if args.output:
with args.output.open("w", encoding="utf-8", newline="\n") as file:
file.write(output)
else:
print(output, end="")
return 0
if __name__ == "__main__":
raise SystemExit(main())