mirror of
https://github.com/ACreTeam/ac-decomp
synced 2026-07-08 20:24:49 -04:00
Initial status of repo, builds.
This commit is contained in:
+12
@@ -0,0 +1,12 @@
|
||||
__pycache__/
|
||||
.vscode/
|
||||
build/
|
||||
out/
|
||||
dump/*
|
||||
!dump/foresta.rel.sha1
|
||||
!dump/main.dol.sha1
|
||||
*.dol
|
||||
*.rel
|
||||
*.exe
|
||||
*.dll
|
||||
build.ninja
|
||||
@@ -0,0 +1,6 @@
|
||||
[submodule "tools/libyaz0"]
|
||||
path = tools/libyaz0
|
||||
url = https://github.com/aboood40091/libyaz0
|
||||
[submodule "tools/ppcdis"]
|
||||
path = tools/ppcdis
|
||||
url = https://github.com/SeekyCt/ppcdis
|
||||
@@ -0,0 +1,29 @@
|
||||
# Animal Crossing Decompilation
|
||||
|
||||
Decompilation in progress of Animal Crossing (GAFE01)
|
||||
|
||||
## Cloning
|
||||
|
||||
Use `--recursive` when cloning to have ppcdis and libyaz0 in the repository.
|
||||
|
||||
## Build
|
||||
|
||||
**Currently, it has only been tested with [wibo](https://github.com/decompals/wibo) under WSL2, but should build under other platforms and with Wine.**
|
||||
|
||||
### Building
|
||||
|
||||
- Dump a copy of the game and extract **main.dol** and **foresta.rel.szs**.
|
||||
- Decompress **foresta.rel.szs** with libyaz0 found in *tools/libyaz0*.
|
||||
- Place **main.dol** in *dump/sys/* and **foresta.rel** in *dump/root/*.
|
||||
- Place CodeWarrior 1.3.2 in *tools/1.3.2/*.
|
||||
- Install DevkitPPC, Ninja and Python:
|
||||
- **Only tested with DevkitPPC r41 and Python 3.8.10**, however other versions should work fine.
|
||||
- Install Python modules from requirements.txt (`pip install -r requirements.txt`)
|
||||
- Run configure.py
|
||||
- Run ninja
|
||||
|
||||
## Credits
|
||||
|
||||
- jamchamb, Cuyler36, NWPlayer123 and fraser125 for past documentation of Animal Crossing.
|
||||
- SeekyCt for [ppcdis](https://github.com/SeekyCt/ppcdis/) and helping setting up the project.
|
||||
- msg for helping with *tools/map.py*.
|
||||
@@ -0,0 +1,360 @@
|
||||
"""
|
||||
Common functions & definitions
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from hashlib import sha1
|
||||
import json
|
||||
import os
|
||||
from subprocess import PIPE, run
|
||||
from sys import executable as PYTHON, platform
|
||||
from typing import List, Tuple, Union
|
||||
|
||||
import yaml
|
||||
try:
|
||||
from yaml import CLoader as Loader
|
||||
except ImportError:
|
||||
from yaml import Loader
|
||||
|
||||
#############
|
||||
# Functions #
|
||||
#############
|
||||
|
||||
def get_file_sha1(path: str) -> bytes:
|
||||
"""Gets the SHA1 hash of a file"""
|
||||
|
||||
with open(path, 'rb') as f:
|
||||
return sha1(f.read()).digest()
|
||||
|
||||
def get_cmd_stdout(cmd: str, text=True) -> str:
|
||||
"""Run a command and get the stdout output as a string"""
|
||||
|
||||
ret = run(cmd.split(), stdout=PIPE, text=text)
|
||||
assert ret.returncode == 0, f"Command '{cmd}' returned {ret.returncode}"
|
||||
return ret.stdout
|
||||
|
||||
class Binary(Enum):
|
||||
DOL = 1
|
||||
REL = 2
|
||||
|
||||
# ppcdis source output
|
||||
SourceDesc = Union[str, Tuple[str, int, int]]
|
||||
|
||||
def get_containing_slice(addr: int) -> Tuple[Binary, SourceDesc]:
|
||||
"""Finds the binary containing an address and its source file
|
||||
Source file is empty string if not decompiled"""
|
||||
|
||||
dol_raw = get_cmd_stdout(f"{SLICES} {DOL_YML} {DOL_SLICES} -p {DOL_SRCDIR}/ --containing {addr:x}")
|
||||
containing = json.loads(dol_raw)
|
||||
if containing is None:
|
||||
rel_raw = get_cmd_stdout(
|
||||
f"{SLICES} {REL_YML} {REL_SLICES} -p {REL_SRCDIR}/ --containing {addr:x}"
|
||||
)
|
||||
containing = json.loads(rel_raw)
|
||||
assert containing is not None, f"Unknown address {addr:x}"
|
||||
return (Binary.REL, containing)
|
||||
else:
|
||||
return (Binary.DOL, containing)
|
||||
|
||||
def lookup_sym(sym: str, dol: bool = False, rel: bool = False, source_name: str = None) -> int:
|
||||
"""Takes a symbol as a name or address and returns the address"""
|
||||
|
||||
# Get binary
|
||||
if dol:
|
||||
binary_name = DOL_YML
|
||||
elif rel:
|
||||
binary_name = REL_YML
|
||||
else:
|
||||
binary_name = None
|
||||
|
||||
# Determine type
|
||||
try:
|
||||
return int(sym, 16)
|
||||
except ValueError:
|
||||
return get_address(sym, binary_name, source_name)
|
||||
|
||||
def lookup_sym_full(sym: str, dol: bool = False, rel: bool = False, source_name: str = None
|
||||
) -> int:
|
||||
"""Takes a symbol as a name or address and returns both the name and address"""
|
||||
|
||||
# Get binary
|
||||
if dol:
|
||||
binary_name = DOL_YML
|
||||
elif rel:
|
||||
binary_name = REL_YML
|
||||
else:
|
||||
binary_name = None
|
||||
|
||||
# Determine type
|
||||
try:
|
||||
return int(sym, 16), get_name(sym)
|
||||
except ValueError:
|
||||
return get_address(sym, binary_name, source_name), sym
|
||||
|
||||
def get_address(name: str, binary: bool = None, source_name: bool = None) -> int:
|
||||
"""Finds the address of a symbol"""
|
||||
|
||||
args = [name]
|
||||
if binary is not None:
|
||||
args.append(f"-b {binary}")
|
||||
if source_name is not None:
|
||||
args.append(f"-n {source_name}")
|
||||
|
||||
raw = get_cmd_stdout(f"{SYMBOLS} {GAME_SYMBOLS} --get-addr {' '.join(args)}")
|
||||
return json.loads(raw)
|
||||
|
||||
def get_name(addr: int, binary: bool = None, source_name: bool = None) -> int:
|
||||
"""Finds the name of a symbol"""
|
||||
|
||||
args = [addr]
|
||||
if binary is not None:
|
||||
args.append(f"-b {binary}")
|
||||
if source_name is not None:
|
||||
args.append(f"-n {source_name}")
|
||||
|
||||
raw = get_cmd_stdout(f"{SYMBOLS} {GAME_SYMBOLS} --get-name {' '.join(args)}")
|
||||
return json.loads(raw)
|
||||
|
||||
def find_headers(dirname: str, base=None) -> List[str]:
|
||||
"""Returns a list of all headers in a folder recursively"""
|
||||
|
||||
if base is None:
|
||||
base = dirname
|
||||
|
||||
ret = []
|
||||
for name in os.listdir(dirname):
|
||||
path = dirname + '/' + name
|
||||
if os.path.isdir(path):
|
||||
ret.extend(find_headers(path, base))
|
||||
elif name.endswith('.h'):
|
||||
ret.append(path[len(base)+1:])
|
||||
|
||||
return ret
|
||||
|
||||
def load_from_yaml(path: str, default=None):
|
||||
"""Loads an object from a yaml file"""
|
||||
|
||||
if default is None:
|
||||
default = {}
|
||||
with open(path) as f:
|
||||
ret = yaml.load(f.read(), Loader)
|
||||
if ret is None:
|
||||
ret = default
|
||||
return ret
|
||||
|
||||
################
|
||||
# Project dirs #
|
||||
################
|
||||
|
||||
# Directory for decompiled dol code
|
||||
DOL_SRCDIR = "src"
|
||||
|
||||
# Directory for decompiled rel code
|
||||
REL_SRCDIR = "rel"
|
||||
|
||||
# Include directory
|
||||
INCDIR = "include"
|
||||
|
||||
# Build artifacts directory
|
||||
BUILDDIR = "build"
|
||||
|
||||
# Build include directory
|
||||
BUILD_INCDIR = f"{BUILDDIR}/include"
|
||||
|
||||
# Output binaries directory
|
||||
OUTDIR = "out"
|
||||
|
||||
# Original binaries directory
|
||||
ORIG = "dump"
|
||||
|
||||
# Tools directory
|
||||
TOOLS = "tools"
|
||||
|
||||
# Config directory
|
||||
CONFIG = "config"
|
||||
|
||||
# Extracted assets directory
|
||||
ASSETS = "assets"
|
||||
|
||||
#########
|
||||
# Tools #
|
||||
#########
|
||||
|
||||
# ppcdis
|
||||
PPCDIS = "tools/ppcdis"
|
||||
PPCDIS_INCDIR = f"{PPCDIS}/include"
|
||||
RELEXTERN = f"{PYTHON} {PPCDIS}/relextern.py"
|
||||
ANALYSER = f"{PYTHON} {PPCDIS}/analyser.py"
|
||||
DISASSEMBLER = f"{PYTHON} {PPCDIS}/disassembler.py"
|
||||
ORDERSTRINGS = f"{PYTHON} {PPCDIS}/orderstrings.py"
|
||||
ORDERFLOATS = f"{PYTHON} {PPCDIS}/orderfloats.py"
|
||||
ASSETRIP = f"{PYTHON} {PPCDIS}/assetrip.py"
|
||||
ASSETINC = f"{PYTHON} {PPCDIS}/assetinc.py"
|
||||
FORCEACTIVEGEN = f"{PYTHON} {PPCDIS}/forceactivegen.py"
|
||||
ELF2DOL = f"{PYTHON} {PPCDIS}/elf2dol.py"
|
||||
ELF2REL = f"{PYTHON} {PPCDIS}/elf2rel.py"
|
||||
SLICES = f"{PYTHON} {PPCDIS}/slices.py"
|
||||
PROGRESS = f"{PYTHON} {PPCDIS}/progress.py"
|
||||
SYMBOLS = f"{PYTHON} {PPCDIS}/symbols.py"
|
||||
|
||||
# Codewarrior
|
||||
TOOLS = "tools"
|
||||
CODEWARRIOR = os.path.join(TOOLS, "1.3.2")
|
||||
CC = os.path.join(CODEWARRIOR, "mwcceppc.exe")
|
||||
LD = os.path.join(CODEWARRIOR, "mwldeppc.exe")
|
||||
if platform != "win32":
|
||||
CC = f"wibo {CC}"
|
||||
LD = f"wibo {LD}"
|
||||
|
||||
# DevkitPPC
|
||||
DEVKITPPC = os.environ.get("DEVKITPPC")
|
||||
AS = os.path.join(DEVKITPPC, "bin", "powerpc-eabi-as")
|
||||
OBJDUMP = os.path.join(DEVKITPPC, "bin", "powerpc-eabi-objdump")
|
||||
CPP = os.path.join(DEVKITPPC, "bin", "powerpc-eabi-cpp")
|
||||
|
||||
ICONV = f"{PYTHON} tools/sjis.py" # TODO: get actual iconv working(?)
|
||||
|
||||
#########
|
||||
# Files #
|
||||
#########
|
||||
|
||||
# Slices
|
||||
DOL_SLICES = f"{CONFIG}/dol_slices.yml"
|
||||
REL_SLICES = f"{CONFIG}/rel_slices.yml"
|
||||
|
||||
# Overrides
|
||||
ANALYSIS_OVERRIDES = f"{CONFIG}/analysis_overrides.yml"
|
||||
DOL_DISASM_OVERRIDES = f"{CONFIG}/disasm_overrides.yml"
|
||||
REL_DISASM_OVERRIDES = f"{CONFIG}/rel_disasm_overrides.yml"
|
||||
|
||||
# Binaries
|
||||
DOL = f"{ORIG}/main.dol" # read in python code
|
||||
REL = f"{ORIG}/foresta.rel" # read in python code
|
||||
DOL_YML = f"{CONFIG}/dol.yml"
|
||||
REL_YML = f"{CONFIG}/rel.yml"
|
||||
DOL_SHA = f"{ORIG}/main.dol.sha1"
|
||||
REL_SHA = f"{ORIG}/foresta.rel.sha1"
|
||||
DOL_OK = f"{BUILDDIR}/main.dol.ok"
|
||||
REL_OK = f"{BUILDDIR}/foresta.rel.ok"
|
||||
DOL_ASM_LIST = f"{BUILDDIR}/main.dol.asml"
|
||||
REL_ASM_LIST = f"{BUILDDIR}/foresta.rel.asml"
|
||||
|
||||
# Symbols
|
||||
GAME_SYMBOLS = f"{CONFIG}/symbols.yml"
|
||||
|
||||
# Assets
|
||||
ASSETS_YML = f"{CONFIG}/assets.yml"
|
||||
|
||||
# Analysis outputs
|
||||
EXTERNS = f"{BUILDDIR}/externs.pickle"
|
||||
DOL_LABELS = f"{BUILDDIR}/labels.pickle"
|
||||
DOL_RELOCS = f"{BUILDDIR}/relocs.pickle"
|
||||
REL_LABELS = f"{BUILDDIR}/rel_labels.pickle"
|
||||
REL_RELOCS = f"{BUILDDIR}/rel_relocs.pickle"
|
||||
|
||||
# Linker
|
||||
DOL_LCF_TEMPLATE = f"{CONFIG}/dol.lcf"
|
||||
DOL_LCF = f"{BUILDDIR}/dol.lcf"
|
||||
REL_LCF = f"{CONFIG}/rel.lcf"
|
||||
|
||||
# Outputs
|
||||
DOL_ELF = f"{BUILDDIR}/main.elf"
|
||||
REL_PLF = f"{BUILDDIR}/foresta.plf"
|
||||
DOL_OUT = f"{OUTDIR}/main.dol"
|
||||
REL_OUT = f"{OUTDIR}/foresta.rel"
|
||||
DOL_MAP = f"{OUTDIR}/main.map"
|
||||
REL_MAP = f"{OUTDIR}/foresta.map"
|
||||
|
||||
# Optional full disassembly
|
||||
DOL_FULL = f"{OUTDIR}/dol.s"
|
||||
REL_FULL = f"{OUTDIR}/rel.s"
|
||||
|
||||
##############
|
||||
# Tool Flags #
|
||||
##############
|
||||
|
||||
ASFLAGS = ' '.join([
|
||||
"-m gekko",
|
||||
f"-I {INCDIR}",
|
||||
f"-I {PPCDIS_INCDIR}",
|
||||
f"-I orig"
|
||||
])
|
||||
|
||||
CPPFLAGS = ' '.join([
|
||||
"-nostdinc",
|
||||
f"-I {INCDIR}",
|
||||
f"-I {PPCDIS_INCDIR}",
|
||||
f"-I {BUILD_INCDIR}"
|
||||
])
|
||||
|
||||
DOL_SDATA2_SIZE = 4
|
||||
REL_SDATA2_SIZE = 0
|
||||
|
||||
CFLAGS = [
|
||||
"-O4",
|
||||
]
|
||||
BASE_DOL_CFLAGS = CFLAGS + [
|
||||
"-inline all",
|
||||
"-sdata 4",
|
||||
f"-sdata2 {DOL_SDATA2_SIZE}"
|
||||
]
|
||||
BASE_REL_CFLAGS = CFLAGS + [
|
||||
"-sdata 0",
|
||||
f"-sdata2 {REL_SDATA2_SIZE}",
|
||||
"-pool off",
|
||||
"-ordered-fp-compares"
|
||||
]
|
||||
|
||||
LOCAL_CFLAGS = [
|
||||
"-nostdinc",
|
||||
"-proc gekko",
|
||||
"-maxerrors 1",
|
||||
"-I-",
|
||||
f"-i {INCDIR}",
|
||||
f"-i {PPCDIS_INCDIR}",
|
||||
f"-i {BUILD_INCDIR}"
|
||||
]
|
||||
DOL_CFLAGS = ' '.join(BASE_DOL_CFLAGS + LOCAL_CFLAGS)
|
||||
REL_CFLAGS = ' '.join(BASE_REL_CFLAGS + LOCAL_CFLAGS)
|
||||
EXTERNAL_DOL_CFLAGS = ' '.join(BASE_DOL_CFLAGS)
|
||||
EXTERNAL_REL_CFLAGS = ' '.join(BASE_REL_CFLAGS)
|
||||
|
||||
LDFLAGS = ' '.join([
|
||||
"-fp hard",
|
||||
"-maxerrors 1",
|
||||
"-mapunused"
|
||||
])
|
||||
|
||||
PPCDIS_ANALYSIS_FLAGS = ' '.join([
|
||||
f"-o {ANALYSIS_OVERRIDES}",
|
||||
f"-l {EXTERNS}"
|
||||
])
|
||||
|
||||
############
|
||||
# Contexts #
|
||||
############
|
||||
|
||||
@dataclass
|
||||
class SourceContext:
|
||||
srcdir: str
|
||||
cflags: str
|
||||
binary: str
|
||||
labels: str
|
||||
relocs: str
|
||||
slices: str
|
||||
sdata2_threshold: int
|
||||
disasm_overrides: int
|
||||
|
||||
DOL_CTX = SourceContext(DOL_SRCDIR, DOL_CFLAGS, DOL_YML, DOL_LABELS, DOL_RELOCS, DOL_SLICES,
|
||||
DOL_SDATA2_SIZE, DOL_DISASM_OVERRIDES)
|
||||
REL_CTX = SourceContext(REL_SRCDIR, REL_CFLAGS, REL_YML, REL_LABELS, REL_RELOCS, REL_SLICES,
|
||||
REL_SDATA2_SIZE, REL_DISASM_OVERRIDES)
|
||||
|
||||
####################
|
||||
# diff.py Expected #
|
||||
####################
|
||||
|
||||
EXPECTED = "expected"
|
||||
DOL_EXPECTED = f"{EXPECTED}/build/main.elf"
|
||||
REL_EXPECTED = f"{EXPECTED}/build/foresta.plf"
|
||||
@@ -0,0 +1,403 @@
|
||||
blocked_pointers:
|
||||
- 0x800A8440
|
||||
- 0x800A8514
|
||||
forced_types:
|
||||
0x80003534: ENTRY
|
||||
0x80005468: ENTRY
|
||||
0x8003ab3c: ENTRY
|
||||
0x8003ab70: ENTRY
|
||||
0x8003ab70: ENTRY
|
||||
0x8003ab70: ENTRY
|
||||
0x8003ab70: ENTRY
|
||||
0x8003ab78: ENTRY
|
||||
0x8003ab88: ENTRY
|
||||
0x8003abe0: ENTRY
|
||||
0x8003abf0: ENTRY
|
||||
0x8003abfc: ENTRY
|
||||
0x8003ac0c: ENTRY
|
||||
0x8003ac48: ENTRY
|
||||
0x8003ac48: ENTRY
|
||||
0x8003ac48: ENTRY
|
||||
0x8003ac58: ENTRY
|
||||
0x8003ac70: ENTRY
|
||||
0x8003ac90: ENTRY
|
||||
0x8003aca4: ENTRY
|
||||
0x8003acb4: ENTRY
|
||||
0x8003acd0: ENTRY
|
||||
0x8003ace0: ENTRY
|
||||
0x8003ada4: ENTRY
|
||||
0x8003ada4: ENTRY
|
||||
0x8003ada4: ENTRY
|
||||
0x8003ada4: ENTRY
|
||||
0x8003ada4: ENTRY
|
||||
0x8003adac: ENTRY
|
||||
0x8003b5d4: ENTRY
|
||||
0x8003b5f4: ENTRY
|
||||
0x8003b61c: ENTRY
|
||||
0x8003b648: ENTRY
|
||||
0x8003b674: ENTRY
|
||||
0x8003b684: ENTRY
|
||||
0x8003b698: ENTRY
|
||||
0x8003b6c4: ENTRY
|
||||
0x8003b6f0: ENTRY
|
||||
0x8003b6f0: ENTRY
|
||||
0x8003b6f0: ENTRY
|
||||
0x8003b6f0: ENTRY
|
||||
0x8003b6f0: ENTRY
|
||||
0x8003b6f0: ENTRY
|
||||
0x8003b6f0: ENTRY
|
||||
0x8003b6f0: ENTRY
|
||||
0x8003b700: ENTRY
|
||||
0x8003b70c: ENTRY
|
||||
0x8003b70c: ENTRY
|
||||
0x8003b70c: ENTRY
|
||||
0x8003b70c: ENTRY
|
||||
0x8003b71c: ENTRY
|
||||
0x8003b71c: ENTRY
|
||||
0x8003b71c: ENTRY
|
||||
0x8003b71c: ENTRY
|
||||
0x8003b71c: ENTRY
|
||||
0x8003b72c: ENTRY
|
||||
0x8003b734: ENTRY
|
||||
0x8003b73c: ENTRY
|
||||
0x8003b744: ENTRY
|
||||
0x8003b754: ENTRY
|
||||
0x8003b764: ENTRY
|
||||
0x8003b774: ENTRY
|
||||
0x8003b784: ENTRY
|
||||
0x8003b794: ENTRY
|
||||
0x8003b7a4: ENTRY
|
||||
0x8003b7bc: ENTRY
|
||||
0x8003b7d4: ENTRY
|
||||
0x8003b7fc: ENTRY
|
||||
0x8003b82c: ENTRY
|
||||
0x8003b834: ENTRY
|
||||
0x8003b83c: ENTRY
|
||||
0x8003b84c: ENTRY
|
||||
0x8003b85c: ENTRY
|
||||
0x8003b86c: ENTRY
|
||||
0x8003b87c: ENTRY
|
||||
0x8003b88c: ENTRY
|
||||
0x8003b89c: ENTRY
|
||||
0x8003b8ac: ENTRY
|
||||
0x8003b8bc: ENTRY
|
||||
0x8003b8bc: ENTRY
|
||||
0x8003b8bc: ENTRY
|
||||
0x8003b8bc: ENTRY
|
||||
0x8003b8bc: ENTRY
|
||||
0x8003b8bc: ENTRY
|
||||
0x8003b8bc: ENTRY
|
||||
0x8003b8bc: ENTRY
|
||||
0x8003b8d0: ENTRY
|
||||
0x8003b8d0: ENTRY
|
||||
0x8003b8d0: ENTRY
|
||||
0x8003b8e4: ENTRY
|
||||
0x8003b8e4: ENTRY
|
||||
0x8003b8e4: ENTRY
|
||||
0x8003b8f8: ENTRY
|
||||
0x8003b8f8: ENTRY
|
||||
0x8003b8f8: ENTRY
|
||||
0x8003b8f8: ENTRY
|
||||
0x8003b8f8: ENTRY
|
||||
0x8003b8f8: ENTRY
|
||||
0x8003b8f8: ENTRY
|
||||
0x8003b8f8: ENTRY
|
||||
0x8003b928: ENTRY
|
||||
0x8003b928: ENTRY
|
||||
0x8003b928: ENTRY
|
||||
0x8003b928: ENTRY
|
||||
0x8003b928: ENTRY
|
||||
0x8003b928: ENTRY
|
||||
0x8003b928: ENTRY
|
||||
0x8003b928: ENTRY
|
||||
0x8003b960: ENTRY
|
||||
0x8003b960: ENTRY
|
||||
0x8003b960: ENTRY
|
||||
0x8003b960: ENTRY
|
||||
0x8003b960: ENTRY
|
||||
0x8003b960: ENTRY
|
||||
0x8003b960: ENTRY
|
||||
0x8003b960: ENTRY
|
||||
0x8003b970: ENTRY
|
||||
0x8003b970: ENTRY
|
||||
0x8003b970: ENTRY
|
||||
0x8003b970: ENTRY
|
||||
0x8003b970: ENTRY
|
||||
0x8003b970: ENTRY
|
||||
0x8003b970: ENTRY
|
||||
0x8003b970: ENTRY
|
||||
0x8003b980: ENTRY
|
||||
0x8003b980: ENTRY
|
||||
0x8003b990: ENTRY
|
||||
0x8003b990: ENTRY
|
||||
0x8003b990: ENTRY
|
||||
0x8003b990: ENTRY
|
||||
0x8003b990: ENTRY
|
||||
0x8003b990: ENTRY
|
||||
0x8003b990: ENTRY
|
||||
0x8003b990: ENTRY
|
||||
0x8003b9a0: ENTRY
|
||||
0x8003b9a0: ENTRY
|
||||
0x8003b9a0: ENTRY
|
||||
0x8003b9a0: ENTRY
|
||||
0x8003b9b8: ENTRY
|
||||
0x8003b9b8: ENTRY
|
||||
0x8003b9b8: ENTRY
|
||||
0x8003b9b8: ENTRY
|
||||
0x8003b9d0: ENTRY
|
||||
0x8003b9e4: ENTRY
|
||||
0x8003b9e4: ENTRY
|
||||
0x8003b9e4: ENTRY
|
||||
0x8003b9e4: ENTRY
|
||||
0x8003ba00: ENTRY
|
||||
0x8003ba14: ENTRY
|
||||
0x8003ba14: ENTRY
|
||||
0x8003ba14: ENTRY
|
||||
0x8003ba14: ENTRY
|
||||
0x8003ba30: ENTRY
|
||||
0x8003ba48: ENTRY
|
||||
0x8003ba48: ENTRY
|
||||
0x8003ba48: ENTRY
|
||||
0x8003ba48: ENTRY
|
||||
0x8003ba68: ENTRY
|
||||
0x8003ba80: ENTRY
|
||||
0x8003ba80: ENTRY
|
||||
0x8003ba80: ENTRY
|
||||
0x8003ba80: ENTRY
|
||||
0x8003baa0: ENTRY
|
||||
0x8003babc: ENTRY
|
||||
0x8003bad8: ENTRY
|
||||
0x8003baf4: ENTRY
|
||||
0x8003bb10: ENTRY
|
||||
0x8003bb2c: ENTRY
|
||||
0x8003bb48: ENTRY
|
||||
0x8003bb64: ENTRY
|
||||
0x8003bb80: ENTRY
|
||||
0x8003bb88: ENTRY
|
||||
0x8003bbac: ENTRY
|
||||
0x8003bbc0: ENTRY
|
||||
0x8003bbdc: ENTRY
|
||||
0x8003bbe0: ENTRY
|
||||
0x8003bc10: ENTRY
|
||||
0x8003bc18: ENTRY
|
||||
0x8003bc44: ENTRY
|
||||
0x8003bc54: ENTRY
|
||||
0x8003bc5c: ENTRY
|
||||
0x8003bcc8: ENTRY
|
||||
0x8003bcd0: ENTRY
|
||||
0x8003bd20: ENTRY
|
||||
0x8003bd30: ENTRY
|
||||
0x8003bd3c: ENTRY
|
||||
0x8003bd60: ENTRY
|
||||
0x8003bd70: ENTRY
|
||||
0x8003bd78: ENTRY
|
||||
0x8003bd88: ENTRY
|
||||
0x8003bd98: ENTRY
|
||||
0x8003bda8: ENTRY
|
||||
0x8003bdb0: ENTRY
|
||||
0x8003bdb4: ENTRY
|
||||
0x8003bdbc: ENTRY
|
||||
0x8003bdc8: ENTRY
|
||||
0x8003bdc8: ENTRY
|
||||
0x8003bdd4: ENTRY
|
||||
0x8003be64: ENTRY
|
||||
0x8003bf28: ENTRY
|
||||
0x8003bf38: ENTRY
|
||||
0x8003bf9c: ENTRY
|
||||
0x8003bfd0: ENTRY
|
||||
0x8003bfdc: ENTRY
|
||||
0x8003bfe8: ENTRY
|
||||
0x8003bfec: ENTRY
|
||||
0x8003c000: ENTRY
|
||||
0x8003c020: ENTRY
|
||||
0x8003c030: ENTRY
|
||||
0x8003c034: ENTRY
|
||||
0x8003c094: ENTRY
|
||||
0x8003c0e0: ENTRY
|
||||
0x8003c1f4: ENTRY
|
||||
0x8003c210: ENTRY
|
||||
0x8003c214: ENTRY
|
||||
0x8003c24c: ENTRY
|
||||
0x8003c260: ENTRY
|
||||
0x8003c274: ENTRY
|
||||
0x8003c274: ENTRY
|
||||
0x8003c274: ENTRY
|
||||
0x8003c274: ENTRY
|
||||
0x8003c28c: ENTRY
|
||||
0x8003c28c: ENTRY
|
||||
0x8003c28c: ENTRY
|
||||
0x8003c28c: ENTRY
|
||||
0x8003c28c: ENTRY
|
||||
0x8003c28c: ENTRY
|
||||
0x8003c28c: ENTRY
|
||||
0x8003c28c: ENTRY
|
||||
0x8003c28c: ENTRY
|
||||
0x8003c28c: ENTRY
|
||||
0x8003c28c: ENTRY
|
||||
0x8003c28c: ENTRY
|
||||
0x8003c28c: ENTRY
|
||||
0x8003c28c: ENTRY
|
||||
0x8003c2ac: ENTRY
|
||||
0x8003c2e0: ENTRY
|
||||
0x8003c32c: ENTRY
|
||||
0x8003c540: ENTRY
|
||||
0x8003c5cc: ENTRY
|
||||
0x8003c5cc: ENTRY
|
||||
0x8003c5d8: ENTRY
|
||||
0x8003c60c: ENTRY
|
||||
0x8003c614: ENTRY
|
||||
0x8003c640: ENTRY
|
||||
0x8003c6e4: ENTRY
|
||||
0x8003c6ec: ENTRY
|
||||
0x8003c894: ENTRY
|
||||
0x8003c8bc: ENTRY
|
||||
0x8003c8e8: ENTRY
|
||||
0x8003c930: ENTRY
|
||||
0x8003caf0: ENTRY
|
||||
0x8003cb20: ENTRY
|
||||
0x8003cb6c: ENTRY
|
||||
0x8003cb78: ENTRY
|
||||
0x8003cb8c: ENTRY
|
||||
0x8003cba0: ENTRY
|
||||
0x8003ccf4: ENTRY
|
||||
0x8003cd0c: ENTRY
|
||||
0x8003ce58: ENTRY
|
||||
0x8003ce58: ENTRY
|
||||
0x8003ce58: ENTRY
|
||||
0x8003ce58: ENTRY
|
||||
0x8003ce58: ENTRY
|
||||
0x8003ce64: ENTRY
|
||||
0x8003cf80: ENTRY
|
||||
0x8003cf80: ENTRY
|
||||
0x8003cf80: ENTRY
|
||||
0x8003cf80: ENTRY
|
||||
0x8003cf80: ENTRY
|
||||
0x8003cf80: ENTRY
|
||||
0x8003cf80: ENTRY
|
||||
0x8003cf80: ENTRY
|
||||
0x8003cf98: ENTRY
|
||||
0x8003cf98: ENTRY
|
||||
0x8003cf98: ENTRY
|
||||
0x8003cf98: ENTRY
|
||||
0x8003cfb0: ENTRY
|
||||
0x8003cfb0: ENTRY
|
||||
0x8003cfbc: ENTRY
|
||||
0x8003cfc8: ENTRY
|
||||
0x8003d010: ENTRY
|
||||
0x8003d010: ENTRY
|
||||
0x8003d01c: ENTRY
|
||||
0x8003d0ac: ENTRY
|
||||
0x8003d0f8: ENTRY
|
||||
0x8003d0fc: ENTRY
|
||||
0x8003d13c: ENTRY
|
||||
0x8003d16c: ENTRY
|
||||
0x8003d1b0: ENTRY
|
||||
0x8003d1b4: ENTRY
|
||||
0x8003d1e4: ENTRY
|
||||
0x8003d1e4: ENTRY
|
||||
0x8003d1e4: ENTRY
|
||||
0x8003d1e4: ENTRY
|
||||
0x8003d1f8: ENTRY
|
||||
0x8003d264: ENTRY
|
||||
0x8003d2b0: ENTRY
|
||||
0x8003d368: ENTRY
|
||||
0x8003d3dc: ENTRY
|
||||
0x8003d444: ENTRY
|
||||
0x8003d45c: ENTRY
|
||||
0x8003d4a4: ENTRY
|
||||
0x8003d4ec: ENTRY
|
||||
0x8003d514: ENTRY
|
||||
0x8003d53c: ENTRY
|
||||
0x8003d564: ENTRY
|
||||
0x8003d584: ENTRY
|
||||
0x8003d590: ENTRY
|
||||
0x8003d5b4: ENTRY
|
||||
0x8003d5d8: ENTRY
|
||||
0x8003d614: ENTRY
|
||||
0x8003d678: ENTRY
|
||||
0x8003d6ac: ENTRY
|
||||
0x8003d700: ENTRY
|
||||
0x8003d754: ENTRY
|
||||
0x8003d76c: ENTRY
|
||||
0x8003d7e8: ENTRY
|
||||
0x8003d7f4: ENTRY
|
||||
0x8003d82c: ENTRY
|
||||
0x8003d8d4: ENTRY
|
||||
0x8003d928: ENTRY
|
||||
0x8003d940: ENTRY
|
||||
0x8003d96c: ENTRY
|
||||
0x8003d990: ENTRY
|
||||
0x8003da50: ENTRY
|
||||
0x8003da88: ENTRY
|
||||
0x8003da90: ENTRY
|
||||
0x8003db68: ENTRY
|
||||
0x8003dbd8: ENTRY
|
||||
0x8003dbf4: ENTRY
|
||||
0x8003dc20: ENTRY
|
||||
0x8003dc28: ENTRY
|
||||
0x8003dc28: ENTRY
|
||||
0x8003dcd4: ENTRY
|
||||
0x8003dd3c: ENTRY
|
||||
0x8003ddb8: ENTRY
|
||||
0x8003dddc: ENTRY
|
||||
0x8003de50: ENTRY
|
||||
0x8003de78: ENTRY
|
||||
0x80078edc: ENTRY
|
||||
0x80078f00: ENTRY
|
||||
0x80078f00: ENTRY
|
||||
0x80078f04: ENTRY
|
||||
0x80078f34: ENTRY
|
||||
0x80078f8c: ENTRY
|
||||
0x80078f9c: ENTRY
|
||||
0x80078fcc: ENTRY
|
||||
0x8007ac24: ENTRY
|
||||
0x8007ac34: ENTRY
|
||||
0x8007db20: ENTRY
|
||||
0x8007db3c: ENTRY
|
||||
0x8009ae2c: ENTRY
|
||||
0x8009ae34: ENTRY
|
||||
0x8009ae38: ENTRY
|
||||
0x8009ae3c: ENTRY
|
||||
0x8009ae78: ENTRY
|
||||
0x8009ae80: ENTRY
|
||||
0x8009ae84: ENTRY
|
||||
0x8009ae88: ENTRY
|
||||
0x8009ae98: ENTRY
|
||||
0x8009ae9c: ENTRY
|
||||
0x8009aea0: ENTRY
|
||||
0x8009aea4: ENTRY
|
||||
0x8009aea8: ENTRY
|
||||
0x8009aeac: ENTRY
|
||||
0x8009aeb0: ENTRY
|
||||
0x8009aeb4: ENTRY
|
||||
0x8009aeb8: ENTRY
|
||||
0x8009aebc: ENTRY
|
||||
0x8009aec0: ENTRY
|
||||
0x8009aec4: ENTRY
|
||||
0x8009aec8: ENTRY
|
||||
0x8009aecc: ENTRY
|
||||
0x8009aed0: ENTRY
|
||||
0x8009aed4: ENTRY
|
||||
0x8009aee4: ENTRY
|
||||
0x8009aee8: ENTRY
|
||||
0x8009aeec: ENTRY
|
||||
0x8009aef0: ENTRY
|
||||
0x8009aef4: ENTRY
|
||||
0x8009aef8: ENTRY
|
||||
0x8009aefc: ENTRY
|
||||
0x8009af00: ENTRY
|
||||
0x8009af04: ENTRY
|
||||
0x8009af08: ENTRY
|
||||
0x8009af0c: ENTRY
|
||||
0x8009af10: ENTRY
|
||||
0x8009af14: ENTRY
|
||||
0x8009af18: ENTRY
|
||||
0x8009af1c: ENTRY
|
||||
0x8009af20: ENTRY
|
||||
0x800A6A74: FUNCTION # TRKExceptionHandler
|
||||
0x800A6BD4: FUNCTION # TRKInterruptHandlerEnableInterrupts
|
||||
0x800a8130: ENTRY
|
||||
0x800a8138: ENTRY
|
||||
0x800a8140: ENTRY
|
||||
0x800a8148: ENTRY
|
||||
0x800ab260: ENTRY
|
||||
@@ -0,0 +1,41 @@
|
||||
MEMORY {
|
||||
text : origin = 0x80003100
|
||||
forcestrip : origin = 0
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
GROUP:{
|
||||
.init ALIGN(0x20):{}
|
||||
extab_ ALIGN(0x20):{}
|
||||
extabindex_ ALIGN(0x20):{}
|
||||
.text ALIGN(0x20):{}
|
||||
.ctors ALIGN(0x20):{}
|
||||
.dtors ALIGN(0x20):{}
|
||||
.rodata ALIGN(0x20):{}
|
||||
.data ALIGN(0x20):{}
|
||||
.bss ALIGN(0x20):{}
|
||||
.sdata ALIGN(0x20):{}
|
||||
.sbss ALIGN(0x20):{}
|
||||
.sdata2 ALIGN(0x20):{}
|
||||
.sbss2 ALIGN(0x20):{}
|
||||
.stack ALIGN(0x100):{}
|
||||
} > text
|
||||
GROUP:{
|
||||
forcestrip ALIGN(0x20):{}
|
||||
} > forcestrip
|
||||
_stack_addr = (_f_sbss2 + SIZEOF(.sbss2) + 65536 + 0x7) & ~0x7;
|
||||
_stack_end = _f_sbss2 + SIZEOF(.sbss2);
|
||||
_db_stack_addr = (_stack_addr + 0x2000);
|
||||
_db_stack_end = _stack_addr;
|
||||
__ArenaLo = (_db_stack_addr + 0x1f) & ~0x1f;
|
||||
__ArenaHi = 0x81700000;
|
||||
_eti_init_info = 0x80005684;
|
||||
}
|
||||
FORCEACTIVE {
|
||||
PPCDIS_FORCEACTIVE
|
||||
}
|
||||
__dummy_str = 0;
|
||||
__dummy_float = 0;
|
||||
__dummy_double = 0;
|
||||
__dummy_pointer = 0;
|
||||
@@ -0,0 +1,23 @@
|
||||
path: dump/main.dol
|
||||
r13: 0x8021fb80
|
||||
r2: 0x80220be0
|
||||
section_defs:
|
||||
text:
|
||||
- name: .init
|
||||
- name: .text
|
||||
data:
|
||||
- name: extab_
|
||||
attr: a
|
||||
- name: extabindex_
|
||||
attr: a
|
||||
- name: .ctors
|
||||
balign: 0
|
||||
- name: .dtors
|
||||
balign: 0
|
||||
- name: .rodata
|
||||
- name: .data
|
||||
- name: .sdata
|
||||
- name: .sdata2
|
||||
bss:
|
||||
- name: .bss
|
||||
- name: .sbss
|
||||
@@ -0,0 +1,21 @@
|
||||
SECTIONS {
|
||||
GROUP:{
|
||||
.init:{}
|
||||
.text:{}
|
||||
.ctors:{}
|
||||
.dtors:{}
|
||||
.rodata:{}
|
||||
.data:{}
|
||||
.bss:{}
|
||||
|
||||
// Removed by elf2rel
|
||||
forcestrip:{}
|
||||
relsymdef:{}
|
||||
}
|
||||
}
|
||||
FORCEFILES {
|
||||
}
|
||||
|
||||
__dummy_str = 0;
|
||||
__dummy_float = 0;
|
||||
__dummy_double = 0;
|
||||
@@ -0,0 +1,4 @@
|
||||
path: dump/foresta.rel
|
||||
address: 0x803701C0
|
||||
bss_address: 0x8125A7C0
|
||||
dol: config/dol.yml
|
||||
@@ -0,0 +1,5 @@
|
||||
trim_ctors: true
|
||||
trim_dtors: true
|
||||
symbol_aligns:
|
||||
0x8064d500 : 0x20
|
||||
0x8125a7c0 : 0x20
|
||||
+58130
File diff suppressed because it is too large
Load Diff
+786
@@ -0,0 +1,786 @@
|
||||
"""
|
||||
Creates a build script for ninja
|
||||
"""
|
||||
from abc import ABC, abstractmethod
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
|
||||
import json
|
||||
import os
|
||||
import pickle
|
||||
import re
|
||||
from io import StringIO
|
||||
|
||||
from typing import List, Tuple
|
||||
from ninja_syntax import Writer
|
||||
|
||||
import common as c
|
||||
|
||||
####################
|
||||
# Setup Validation #
|
||||
####################
|
||||
|
||||
# Check CodeWarrior was added
|
||||
assert os.path.exists("tools/1.3.2/mwcceppc.exe") and \
|
||||
os.path.exists("tools/1.3.2/mwldeppc.exe"), \
|
||||
"Error: Codewarrior not found in tools/1.3.2"
|
||||
|
||||
# Check binaries were added
|
||||
assert os.path.exists(c.DOL) and os.path.exists(c.REL), \
|
||||
"Error: Base binaries not found!"
|
||||
|
||||
#Check if binaries are correct
|
||||
dol_hash = c.get_file_sha1(c.DOL)
|
||||
rel_hash = c.get_file_sha1(c.REL)
|
||||
|
||||
assert dol_hash == bytes.fromhex("2ae8f56e7791d37e165bd5900921f2269f9515bf") and rel_hash == bytes.fromhex("c59d278ad8542bb05d6cbb632f60a0db05bef203"), \
|
||||
"Error: Base binaries hashes aren't correct!"
|
||||
|
||||
#Check submodules added
|
||||
assert os.path.exists(c.PPCDIS), \
|
||||
"Error: Git submodules not initialised!"
|
||||
|
||||
###############
|
||||
# Ninja Setup #
|
||||
###############
|
||||
|
||||
outbuf = StringIO()
|
||||
n = Writer(outbuf)
|
||||
n.variable("ninja_required_version", "1.3")
|
||||
n.newline()
|
||||
|
||||
################
|
||||
# Project Dirs #
|
||||
################
|
||||
|
||||
n.variable("builddir", c.BUILDDIR)
|
||||
n.variable("outdir", c.OUTDIR)
|
||||
n.variable("dump", c.ORIG)
|
||||
n.variable("tools", c.TOOLS)
|
||||
n.variable("config", c.CONFIG)
|
||||
n.newline()
|
||||
|
||||
os.makedirs(c.BUILDDIR, exist_ok=True)
|
||||
|
||||
#########
|
||||
# Tools #
|
||||
#########
|
||||
|
||||
n.variable("python", c.PYTHON)
|
||||
n.variable("ppcdis", c.PPCDIS)
|
||||
n.variable("relextern", c.RELEXTERN)
|
||||
n.variable("analyser", c.ANALYSER)
|
||||
n.variable("disassembler", c.DISASSEMBLER)
|
||||
n.variable("orderstrings", c.ORDERSTRINGS)
|
||||
n.variable("orderfloats", c.ORDERFLOATS)
|
||||
n.variable("assetrip", c.ASSETRIP)
|
||||
n.variable("assetinc", c.ASSETINC)
|
||||
n.variable("forceactivegen", c.FORCEACTIVEGEN)
|
||||
n.variable("elf2dol", c.ELF2DOL)
|
||||
n.variable("elf2rel", c.ELF2REL)
|
||||
n.variable("codewarrior", c.CODEWARRIOR)
|
||||
n.variable("cc", c.CC)
|
||||
n.variable("ld", c.LD)
|
||||
n.variable("devkitppc", c.DEVKITPPC)
|
||||
n.variable("as", c.AS)
|
||||
n.variable("cpp", c.CPP)
|
||||
n.variable("iconv", c.ICONV)
|
||||
n.newline()
|
||||
|
||||
##############
|
||||
# Tool Flags #
|
||||
##############
|
||||
|
||||
n.variable("asflags", c.ASFLAGS)
|
||||
n.variable("ldflags", c.LDFLAGS)
|
||||
n.variable("cppflags", c.CPPFLAGS)
|
||||
n.variable("ppcdis_analysis_flags", c.PPCDIS_ANALYSIS_FLAGS)
|
||||
n.newline()
|
||||
|
||||
#########
|
||||
# Rules #
|
||||
#########
|
||||
|
||||
# Windows can't use && without this statement
|
||||
ALLOW_CHAIN = "cmd /c " if os.name == "nt" else ""
|
||||
|
||||
n.rule(
|
||||
"relextern",
|
||||
command = "$relextern $out $in",
|
||||
description = "ppcdis rel extern $in"
|
||||
)
|
||||
|
||||
n.rule(
|
||||
"analyse",
|
||||
command = "$analyser $in $out $analysisflags",
|
||||
description = "ppcdis analysis $in",
|
||||
pool="console"
|
||||
)
|
||||
|
||||
n.rule(
|
||||
"disasm",
|
||||
command = "$disassembler $in $out -q $disasmflags",
|
||||
description = "ppcdis full disassembly $out"
|
||||
)
|
||||
|
||||
n.rule(
|
||||
"disasm_slice",
|
||||
command = "$disassembler $in $out -q $disasmflags -s $slice",
|
||||
description = "ppcdis disassembly $out"
|
||||
)
|
||||
|
||||
n.rule(
|
||||
"disasm_single",
|
||||
command = "$disassembler $in $out -j $addr -q $disasmflags",
|
||||
description = "ppcdis function disassembly $addr"
|
||||
)
|
||||
|
||||
n.rule(
|
||||
"jumptable",
|
||||
command = "$disassembler $in $out -j $addr -q $disassembler",
|
||||
description = "Jumptable $addr"
|
||||
)
|
||||
|
||||
n.rule(
|
||||
"orderstrings",
|
||||
command = "$orderstrings $in $addrs $out $flags --enc shift-jis --pool",
|
||||
description = "Order strings $in $addrs"
|
||||
)
|
||||
|
||||
n.rule(
|
||||
"orderfloats",
|
||||
command = "$orderfloats $in $addrs $out $flags",
|
||||
description = "Order floats $in $addrs"
|
||||
)
|
||||
|
||||
n.rule(
|
||||
"assetrip",
|
||||
command = "$assetrip $in $addrs $out",
|
||||
description = "Asset rip $out"
|
||||
)
|
||||
|
||||
n.rule(
|
||||
"assetinc",
|
||||
command = "$assetinc $in $out",
|
||||
description = "Asset include generation $out"
|
||||
)
|
||||
|
||||
n.rule(
|
||||
"forceactivegen",
|
||||
command = "$forceactivegen $in $out",
|
||||
description = "LCF FORCEACTIVEGEN generation $in"
|
||||
)
|
||||
|
||||
n.rule(
|
||||
"elf2dol",
|
||||
command = "$elf2dol $in -o $out",
|
||||
description = "elf2dol $in"
|
||||
)
|
||||
|
||||
n.rule(
|
||||
"elf2rel",
|
||||
command = "$elf2rel $in -o $out $flags",
|
||||
description = "elf2rel $in"
|
||||
)
|
||||
|
||||
n.rule(
|
||||
"sha1sum",
|
||||
command = ALLOW_CHAIN + "sha1sum -c $in && touch $out",
|
||||
description ="Verify $in",
|
||||
pool ="console"
|
||||
)
|
||||
|
||||
n.rule(
|
||||
"as",
|
||||
command = f"$as $asflags -c $in -o $out",
|
||||
description = "AS $in"
|
||||
)
|
||||
|
||||
n.rule(
|
||||
"cc",
|
||||
command = ALLOW_CHAIN + f"$cpp -M $in -MF $out.d $cppflags && $cc $cflags -c $in -o $out",
|
||||
description = "CC $in",
|
||||
deps = "gcc",
|
||||
depfile = "$out.d"
|
||||
)
|
||||
|
||||
n.rule(
|
||||
"css",
|
||||
command = ALLOW_CHAIN + f"$cpp -M $in -MF $out.d $cppflags && $cc $cflags -S $in -o $out",
|
||||
description = "CC -S $in",
|
||||
deps = "gcc",
|
||||
depfile = "$out.d"
|
||||
)
|
||||
|
||||
n.rule(
|
||||
"ld",
|
||||
command = "$ld $ldflags -map $map -lcf $lcf $in -o $out",
|
||||
description = "LD $out",
|
||||
)
|
||||
|
||||
n.rule(
|
||||
"iconv",
|
||||
command = "$iconv $in $out",
|
||||
description = "iconv $in",
|
||||
)
|
||||
|
||||
##########
|
||||
# Assets #
|
||||
##########
|
||||
|
||||
@dataclass
|
||||
class Asset:
|
||||
binary: str
|
||||
path: str
|
||||
start: int
|
||||
end: int
|
||||
|
||||
def load(yml_path: str):
|
||||
return {
|
||||
asset : Asset(binary, asset, *adat["addrs"])
|
||||
for binary, bdat in c.load_from_yaml(yml_path).items()
|
||||
for asset, adat in bdat.items()
|
||||
}
|
||||
assets = Asset.load(c.ASSETS_YML)
|
||||
|
||||
|
||||
###########
|
||||
# Sources #
|
||||
###########
|
||||
|
||||
class GeneratedInclude(ABC):
|
||||
def __init__(self, ctx: c.SourceContext, source_name: str, path: str):
|
||||
self.ctx = ctx
|
||||
self.source_name = source_name
|
||||
self.path = path
|
||||
|
||||
@abstractmethod
|
||||
def build(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def find(ctx: c.SourceContext, source_name: str, txt: str) -> List["GeneratedInclude"]:
|
||||
return [
|
||||
cl(ctx, source_name, match)
|
||||
for cl in (
|
||||
AsmInclude,
|
||||
JumptableInclude,
|
||||
StringInclude,
|
||||
FloatInclude,
|
||||
DoubleInclude,
|
||||
AssetInclude
|
||||
)
|
||||
for match in re.findall(cl.REGEX, txt)
|
||||
]
|
||||
|
||||
class AsmInclude(GeneratedInclude):
|
||||
REGEX = r'#include "asm\/([0-9a-f]{8})\.s"'
|
||||
|
||||
def __init__(self, ctx: c.SourceContext, source_name: str, match: str):
|
||||
self.addr = match
|
||||
super().__init__(ctx, source_name, f"{c.BUILD_INCDIR}/asm/{self.addr}.s")
|
||||
|
||||
def build(includes: List["AsmInclude"]):
|
||||
# Skip empty list
|
||||
if len(includes) == 0:
|
||||
return
|
||||
|
||||
# Get ctx from first include (all should be equal)
|
||||
ctx = includes[0].ctx
|
||||
|
||||
# Sort by source name
|
||||
batches = defaultdict(list)
|
||||
for inc in includes:
|
||||
batches[inc.source_name].append(inc)
|
||||
|
||||
# Compile by source name
|
||||
for source_name, incs in batches.items():
|
||||
n.build(
|
||||
[inc.path for inc in incs],
|
||||
rule="disasm_single",
|
||||
inputs=[ctx.binary, ctx.labels, ctx.relocs],
|
||||
implicit=[c.GAME_SYMBOLS, ctx.disasm_overrides],
|
||||
variables={
|
||||
"disasmflags" : f"-m {c.GAME_SYMBOLS} -o {ctx.disasm_overrides} -n {source_name}",
|
||||
"addr" : ' '.join(inc.addr for inc in incs) }
|
||||
)
|
||||
|
||||
def _repr__(self):
|
||||
return f"AsmInclude({self.addr})"
|
||||
|
||||
class JumptableInclude(GeneratedInclude):
|
||||
REGEX = r'#include "jumptable\/([0-9a-f]{8})\.inc"'
|
||||
|
||||
def __init__(self, ctx: c.SourceContext, source_name: str, match: str):
|
||||
self.addr = match
|
||||
super().__init__(ctx, source_name, f"{c.BUILD_INCDIR}/jumptable/{self.addr}.inc")
|
||||
|
||||
def build(includes: List["JumptableInclude"]):
|
||||
# Skip empty list
|
||||
if len(includes) == 0:
|
||||
return
|
||||
|
||||
# Get context from first include (all should be equal)
|
||||
ctx = includes[0].ctx
|
||||
|
||||
# Sort by source name
|
||||
batches = defaultdict(list)
|
||||
for inc in includes:
|
||||
batches[inc.source_name].append(inc)
|
||||
|
||||
# Compile by source name
|
||||
# TODO: subdivide large batches
|
||||
for source_name, incs in batches.items():
|
||||
n.build(
|
||||
[inc.path for inc in incs],
|
||||
rule="jumptable",
|
||||
inputs=[ctx.binary, ctx.labels, ctx.relocs],
|
||||
implicit=[c.GAME_SYMBOLS, ctx.disasm_overrides],
|
||||
variables={
|
||||
"disasmflags" : f"-m {c.GAME_SYMBOLS} -o {ctx.disasm_overrides} -n {source_name}",
|
||||
"addr" : ' '.join(inc.addr for inc in incs)
|
||||
}
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"JumptableInclude({self.addr})"
|
||||
|
||||
|
||||
class StringInclude(GeneratedInclude):
|
||||
REGEX = r'#include "orderstrings\/([0-9a-f]{8})_([0-9a-f]{8})\.inc"'
|
||||
|
||||
def __init__(self, ctx: c.SourceContext, source_name: str, match: Tuple[str]):
|
||||
self.start, self.end = match
|
||||
super().__init__(ctx, source_name,
|
||||
f"{c.BUILD_INCDIR}/orderstrings/{self.start}_{self.end}.inc")
|
||||
|
||||
def build(includes: List["StringInclude"]):
|
||||
# Skip empty list
|
||||
if len(includes) == 0:
|
||||
return
|
||||
|
||||
# Get context from first include (all should be equal)
|
||||
ctx = includes[0].ctx
|
||||
|
||||
# Build
|
||||
for inc in includes:
|
||||
n.build(
|
||||
inc.path,
|
||||
rule="orderstrings",
|
||||
inputs=ctx.binary,
|
||||
variables={
|
||||
"addrs" : f"{inc.start} {inc.end}"
|
||||
}
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"StringInclude({self.start}, {self.end})"
|
||||
|
||||
|
||||
class FloatInclude(GeneratedInclude):
|
||||
REGEX = r'#include "(orderfloats(m?))\/([0-9a-f]{8})_([0-9a-f]{8})\.inc"'
|
||||
|
||||
def __init__(self, ctx: c.SourceContext, source_name: str, match: Tuple[str]):
|
||||
folder, manual, self.start, self.end = match
|
||||
self.manual = manual != ''
|
||||
super().__init__(ctx, source_name,
|
||||
f"{c.BUILD_INCDIR}/{folder}/{self.start}_{self.end}.inc")
|
||||
|
||||
def build(includes: List["FloatInclude"]):
|
||||
# Skip empty list
|
||||
if len(includes) == 0:
|
||||
return
|
||||
|
||||
# Get context from first include (all should be equal)
|
||||
ctx = includes[0].ctx
|
||||
|
||||
# Build
|
||||
for inc in includes:
|
||||
sda = "--sda " if ctx.sdata2_threshold >= 4 else ""
|
||||
asm = "" if inc.manual else "--asm"
|
||||
n.build(
|
||||
inc.path,
|
||||
rule="orderfloats",
|
||||
inputs=inc.ctx.binary,
|
||||
variables={
|
||||
"addrs" : f"{inc.start} {inc.end}",
|
||||
"flags" : f"{sda} {asm}"
|
||||
}
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"FloatInclude({self.start}, {self.end})"
|
||||
|
||||
class DoubleInclude(GeneratedInclude):
|
||||
REGEX = r'#include "orderdoubles\/([0-9a-f]{8})_([0-9a-f]{8})\.inc"'
|
||||
|
||||
def __init__(self, ctx: c.SourceContext, source_name: str, match: Tuple[str]):
|
||||
self.start, self.end = match
|
||||
super().__init__(ctx, source_name,
|
||||
f"{c.BUILD_INCDIR}/orderdoubles/{self.start}_{self.end}.inc")
|
||||
|
||||
def build(includes: List["DoubleInclude"]):
|
||||
# Skip empty list
|
||||
if len(includes) == 0:
|
||||
return
|
||||
|
||||
# Get context from first include (all should be equal)
|
||||
ctx = includes[0].ctx
|
||||
|
||||
# Build
|
||||
for inc in includes:
|
||||
n.build(
|
||||
inc.path,
|
||||
rule="orderfloats",
|
||||
inputs=ctx.binary,
|
||||
variables={
|
||||
"addrs" : f"{inc.start} {inc.end}",
|
||||
"flags" : f"--double"
|
||||
}
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"DoubleInclude({self.start}, {self.end})"
|
||||
|
||||
class AssetInclude(GeneratedInclude):
|
||||
REGEX = r'#include "assets\/(.+)\.inc"'
|
||||
|
||||
def __init__(self, ctx: c.SourceContext, source_name: str, match: Tuple[str]):
|
||||
self.asset = assets[match]
|
||||
self.asset_path = f"{c.ASSETS}/{self.asset.path}"
|
||||
assert ctx.binary == self.asset.binary, f"Tried to include from other binary"
|
||||
super().__init__(ctx, source_name, f"{c.BUILD_INCDIR}/assets/{self.asset.path}.inc")
|
||||
|
||||
def build(includes: List["AssetInclude"]):
|
||||
# Skip empty list
|
||||
if len(includes) == 0:
|
||||
return
|
||||
|
||||
# Build
|
||||
for inc in includes:
|
||||
n.build(
|
||||
inc.asset_path,
|
||||
rule="assetrip",
|
||||
inputs=inc.asset.binary,
|
||||
variables={
|
||||
"addrs" : f"{inc.asset.start:x} {inc.asset.end:x}"
|
||||
}
|
||||
)
|
||||
n.build(
|
||||
inc.path,
|
||||
rule="assetinc",
|
||||
inputs=inc.asset_path
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"AssetInclude({self.asset})"
|
||||
|
||||
class Source(ABC):
|
||||
def __init__(self, decompiled: bool, src_path: str, o_path: str,
|
||||
gen_includes: List[GeneratedInclude] = []):
|
||||
self.decompiled = decompiled
|
||||
self.src_path = src_path
|
||||
self.o_path = o_path
|
||||
self.o_stem = o_path[:-2]
|
||||
self.gen_includes = gen_includes
|
||||
|
||||
def build(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def make(ctx: c.SourceContext, source: c.SourceDesc):
|
||||
if isinstance(source, str):
|
||||
print(source)
|
||||
ext = source.split('.')[-1].lower()
|
||||
if ext in ("c", "cpp", "cxx", "cc"):
|
||||
return CSource(ctx, source)
|
||||
elif ext == "s":
|
||||
return AsmSource(ctx, source)
|
||||
else:
|
||||
assert 0, f"Unknown source type .{ext}"
|
||||
else:
|
||||
return GenAsmSource(ctx, *source)
|
||||
|
||||
class GenAsmSource(Source):
|
||||
def __init__(self, ctx: c.SourceContext, section: str, start: int, end: int):
|
||||
self.start = start
|
||||
self.end = end
|
||||
self.ctx = ctx
|
||||
name = f"{section}_{start:x}_{end:x}.s"
|
||||
src_path = f"$builddir/asm/{name}"
|
||||
super().__init__(False, src_path, src_path + ".o")
|
||||
|
||||
|
||||
def build(self):
|
||||
n.build(
|
||||
self.src_path,
|
||||
rule = "disasm_slice",
|
||||
inputs = [self.ctx.binary, self.ctx.labels, self.ctx.relocs],
|
||||
implicit = [c.GAME_SYMBOLS, self.ctx.disasm_overrides],
|
||||
variables = {
|
||||
"slice" : f"{self.start:x} {self.end:x}",
|
||||
"disasmflags" : f"-m {c.GAME_SYMBOLS} -o {self.ctx.disasm_overrides}"
|
||||
}
|
||||
)
|
||||
n.build(
|
||||
self.o_path,
|
||||
rule="as",
|
||||
inputs=self.src_path
|
||||
)
|
||||
def batch_build(sources: List["GenAsmSource"], batch_size=20):
|
||||
# TODO: configure batch size based on cpu core count
|
||||
|
||||
# Skip empty list
|
||||
if len(sources) == 0:
|
||||
return
|
||||
|
||||
# Get context from first include (all should be equal)
|
||||
ctx = sources[0].ctx
|
||||
|
||||
for src in sources:
|
||||
n.build(
|
||||
src.o_path,
|
||||
rule="as",
|
||||
inputs=src.src_path
|
||||
)
|
||||
|
||||
while len(sources) > 0:
|
||||
batch, sources = sources[:batch_size], sources[batch_size:]
|
||||
n.build(
|
||||
[src.src_path for src in batch],
|
||||
rule = "disasm_slice",
|
||||
inputs = [ctx.binary, ctx.labels, ctx.relocs],
|
||||
implicit = [c.GAME_SYMBOLS, ctx.disasm_overrides],
|
||||
variables = {
|
||||
"slice" : ' '.join(
|
||||
f"{src.start:x} {src.end:x}"
|
||||
for src in batch
|
||||
),
|
||||
"disasmflags" : f"-m {c.GAME_SYMBOLS} -o {ctx.disasm_overrides}"
|
||||
}
|
||||
)
|
||||
|
||||
class AsmSource(Source):
|
||||
def __init__(self, ctx: c.SourceContext, path: str):
|
||||
super().__init__(True, path, f"$builddir/{path}.o")
|
||||
|
||||
def build(self):
|
||||
n.build(
|
||||
self.o_path,
|
||||
rule = "as",
|
||||
inputs = self.src_path
|
||||
)
|
||||
|
||||
class CSource(Source):
|
||||
def __init__(self, ctx: c.SourceContext, path: str):
|
||||
self.cflags = ctx.cflags
|
||||
self.iconv_path = f"$builddir/iconv/{path}"
|
||||
|
||||
# Find generated includes
|
||||
with open(path, encoding="utf-8") as f:
|
||||
gen_includes = GeneratedInclude.find(ctx, path, f.read())
|
||||
|
||||
self.s_path = f"$builddir/{path}.s"
|
||||
super().__init__(True, path, f"$builddir/{path}.o", gen_includes)
|
||||
|
||||
def build(self):
|
||||
n.build(
|
||||
self.iconv_path,
|
||||
rule="iconv",
|
||||
inputs=self.src_path
|
||||
)
|
||||
n.build(
|
||||
self.o_path,
|
||||
rule = "cc",
|
||||
inputs = self.iconv_path,
|
||||
implicit = [inc.path for inc in self.gen_includes],
|
||||
variables = {
|
||||
"cflags" : self.cflags
|
||||
}
|
||||
)
|
||||
# Optional manual debug target
|
||||
n.build(
|
||||
self.s_path,
|
||||
rule = "ccs",
|
||||
inputs = self.iconv_path,
|
||||
implicit = [inc.path for inc in self.gen_includes],
|
||||
variables = {
|
||||
"cflags" : self.cflags
|
||||
}
|
||||
)
|
||||
|
||||
def load_sources(ctx: c.SourceContext):
|
||||
raw = c.get_cmd_stdout(
|
||||
f"{c.SLICES} {ctx.binary} {ctx.slices} -o -p {ctx.srcdir}/"
|
||||
)
|
||||
return [Source.make(ctx, s) for s in json.loads(raw)]
|
||||
|
||||
def find_gen_includes(sources: List[Source]):
|
||||
ret = defaultdict(list)
|
||||
for source in sources:
|
||||
if not isinstance(source, CSource):
|
||||
continue
|
||||
|
||||
for inc in source.gen_includes:
|
||||
ret[type(inc)].append(inc)
|
||||
|
||||
return ret
|
||||
|
||||
def make_asm_list(path: str, asm_includes: List[AsmInclude]):
|
||||
with open(path, 'wb') as f:
|
||||
pickle.dump(
|
||||
[
|
||||
int(inc.addr, 16)
|
||||
for inc in asm_includes
|
||||
],
|
||||
f
|
||||
)
|
||||
|
||||
dol_sources = load_sources(c.DOL_CTX)
|
||||
dol_gen_includes = find_gen_includes(dol_sources)
|
||||
make_asm_list(c.DOL_ASM_LIST, dol_gen_includes[AsmInclude])
|
||||
|
||||
rel_sources = load_sources(c.REL_CTX)
|
||||
rel_gen_includes = find_gen_includes(rel_sources)
|
||||
make_asm_list(c.REL_ASM_LIST, rel_gen_includes[AsmInclude])
|
||||
|
||||
##########
|
||||
# Builds #
|
||||
##########
|
||||
|
||||
n.build(
|
||||
c.EXTERNS,
|
||||
rule = "relextern",
|
||||
inputs = c.REL_YML
|
||||
)
|
||||
|
||||
n.build(
|
||||
[c.REL_LABELS, c.REL_RELOCS],
|
||||
rule = "analyse",
|
||||
inputs = c.REL_YML,
|
||||
implicit = [c.ANALYSIS_OVERRIDES, c.EXTERNS],
|
||||
variables = {
|
||||
"analysisflags" : "$ppcdis_analysis_flags"
|
||||
}
|
||||
)
|
||||
|
||||
n.build(
|
||||
[c.DOL_LABELS, c.DOL_RELOCS],
|
||||
rule = "analyse",
|
||||
inputs = c.DOL_YML,
|
||||
implicit = [c.ANALYSIS_OVERRIDES, c.EXTERNS],
|
||||
variables = {
|
||||
"analysisflags" : f"$ppcdis_analysis_flags"
|
||||
}
|
||||
)
|
||||
|
||||
for cl, includes in dol_gen_includes.items():
|
||||
cl.build(includes)
|
||||
|
||||
for cl, includes in rel_gen_includes.items():
|
||||
cl.build(includes)
|
||||
|
||||
dol_gen_asm = []
|
||||
for source in dol_sources:
|
||||
if isinstance(source, GenAsmSource):
|
||||
dol_gen_asm.append(source)
|
||||
else:
|
||||
source.build()
|
||||
GenAsmSource.batch_build(dol_gen_asm)
|
||||
|
||||
rel_gen_asm = []
|
||||
for source in rel_sources:
|
||||
if isinstance(source, GenAsmSource):
|
||||
rel_gen_asm.append(source)
|
||||
else:
|
||||
source.build()
|
||||
GenAsmSource.batch_build(rel_gen_asm)
|
||||
|
||||
n.build(
|
||||
c.DOL_LCF,
|
||||
rule="forceactivegen",
|
||||
inputs=[c.DOL_LCF_TEMPLATE, c.DOL_YML, c.DOL_LABELS, c.GAME_SYMBOLS, c.EXTERNS]
|
||||
)
|
||||
|
||||
n.build(
|
||||
c.DOL_ELF,
|
||||
rule="ld",
|
||||
inputs=[s.o_path for s in dol_sources],
|
||||
implicit=c.DOL_LCF,
|
||||
implicit_outputs=c.DOL_MAP,
|
||||
variables={
|
||||
"map" : c.DOL_MAP,
|
||||
"lcf" : c.DOL_LCF
|
||||
}
|
||||
)
|
||||
|
||||
n.build(
|
||||
c.DOL_OUT,
|
||||
rule="elf2dol",
|
||||
inputs=c.DOL_ELF,
|
||||
)
|
||||
|
||||
n.build(
|
||||
c.DOL_OK,
|
||||
rule = "sha1sum",
|
||||
inputs = c.DOL_SHA,
|
||||
implicit = [c.DOL_OUT]
|
||||
)
|
||||
n.default(c.DOL_OK)
|
||||
|
||||
n.build(
|
||||
c.REL_PLF,
|
||||
rule="ld",
|
||||
inputs=[s.o_path for s in rel_sources],
|
||||
implicit=c.REL_LCF,
|
||||
implicit_outputs=c.REL_MAP,
|
||||
variables={
|
||||
"map" : c.REL_MAP,
|
||||
"lcf" : c.REL_LCF,
|
||||
"ldflags" : c.LDFLAGS + " -r1 -m _prolog"
|
||||
}
|
||||
)
|
||||
|
||||
n.build(
|
||||
c.REL_OUT,
|
||||
rule="elf2rel",
|
||||
inputs=[c.REL_PLF, c.DOL_ELF],
|
||||
variables={
|
||||
"flags" : f"-n 20 --name-size 0x38 -v 2 -r {c.REL_YML}"
|
||||
}
|
||||
)
|
||||
|
||||
n.build(
|
||||
c.REL_OK,
|
||||
rule = "sha1sum",
|
||||
inputs = c.REL_SHA,
|
||||
implicit = [c.REL_OUT]
|
||||
)
|
||||
n.default(c.REL_OK)
|
||||
|
||||
# Optional full binary disassembly
|
||||
n.build(
|
||||
c.DOL_FULL,
|
||||
rule = "disasm",
|
||||
inputs=[c.DOL_YML, c.DOL_LABELS, c.DOL_RELOCS],
|
||||
implicit=[c.GAME_SYMBOLS, c.DOL_DISASM_OVERRIDES],
|
||||
variables={
|
||||
"disasmflags" : f"-m {c.GAME_SYMBOLS} -o {c.REL_DISASM_OVERRIDES}"
|
||||
}
|
||||
)
|
||||
|
||||
n.build(
|
||||
c.REL_FULL,
|
||||
rule = "disasm",
|
||||
inputs=[c.REL_YML, c.REL_LABELS, c.REL_RELOCS],
|
||||
implicit=[c.GAME_SYMBOLS, c.REL_DISASM_OVERRIDES],
|
||||
variables={
|
||||
"disasmflags" : f"-m {c.GAME_SYMBOLS} -o {c.REL_DISASM_OVERRIDES}"
|
||||
}
|
||||
)
|
||||
|
||||
##########
|
||||
# Ouptut #
|
||||
##########
|
||||
|
||||
with open("build.ninja", 'w') as f:
|
||||
f.write(outbuf.getvalue())
|
||||
n.close()
|
||||
@@ -0,0 +1 @@
|
||||
c59d278ad8542bb05d6cbb632f60a0db05bef203 *out/foresta.rel
|
||||
@@ -0,0 +1 @@
|
||||
2ae8f56e7791d37e165bd5900921f2269f9515bf *out/main.dol
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
"""
|
||||
Progress message generation
|
||||
"""
|
||||
|
||||
from argparse import ArgumentParser
|
||||
import os.path
|
||||
import pickle
|
||||
import json
|
||||
from typing import Dict, List, Tuple
|
||||
|
||||
from prettytable import PrettyTable
|
||||
|
||||
import common as c
|
||||
|
||||
def load_progress_info(ctx: c.SourceContext, asm_list: str
|
||||
) -> Tuple[Dict[str, int], Dict[str, int]]:
|
||||
assert os.path.exists(ctx.labels), "Error: analysis has not ran!"
|
||||
|
||||
# Get data
|
||||
raw = c.get_cmd_stdout(f"{c.PROGRESS} {ctx.binary} {ctx.labels} {ctx.slices}")
|
||||
dat = json.loads(raw)
|
||||
assert dat.get("version") == 2, "Outdated progress json version, try a clean & rebuild"
|
||||
decomp_sizes = dat["decomp_slices_sizes"]
|
||||
total_sizes = dat["total_sizes"]
|
||||
symbol_sizes = dat["symbol_sizes"]
|
||||
|
||||
# Subtract undecompiled functions in decompiled slices
|
||||
# TODO: this assumes none of .init is decompiled
|
||||
with open(asm_list, 'rb') as f:
|
||||
funcs = pickle.load(f)
|
||||
for func in funcs:
|
||||
decomp_sizes[".text"] -= symbol_sizes[str(func)]
|
||||
|
||||
return decomp_sizes, total_sizes
|
||||
|
||||
def print_binary_progress(sections: List[str], decomp_sizes: Dict[str, int],
|
||||
total_sizes: Dict[str, int], binary: str, show_total: bool):
|
||||
table = PrettyTable(["Section", "Decompiled Bytes", "Total Bytes", "Percentage"])
|
||||
bin_decomp_size = 0
|
||||
bin_total_size = 0
|
||||
pct = lambda d, t: f"{(d/t)*100:.4f}"
|
||||
for sec in sections:
|
||||
decomp_size = decomp_sizes[sec]
|
||||
total_size = total_sizes[sec]
|
||||
bin_decomp_size += decomp_size
|
||||
bin_total_size += total_size
|
||||
table.add_row([
|
||||
sec,
|
||||
hex(decomp_size),
|
||||
hex(total_size),
|
||||
pct(decomp_size, total_size)
|
||||
])
|
||||
if show_total:
|
||||
table.add_row([
|
||||
"Total",
|
||||
hex(bin_decomp_size),
|
||||
hex(bin_total_size),
|
||||
pct(bin_decomp_size, bin_total_size)
|
||||
])
|
||||
print(f"{binary} progress:")
|
||||
print(table)
|
||||
|
||||
if __name__=="__main__":
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument("-f", "--full", action="store_true", help="Print progress of all sections")
|
||||
args = parser.parse_args()
|
||||
decomp_sizes, total_sizes = load_progress_info(c.DOL_CTX, c.DOL_ASM_LIST)
|
||||
rel_decomp_sizes, rel_total_sizes = load_progress_info(c.REL_CTX, c.REL_ASM_LIST)
|
||||
if args.full:
|
||||
print("Warning: progress for sections other than .text isn't fully accurate")
|
||||
dol_secs = [
|
||||
".init",
|
||||
"extab_",
|
||||
"extabindex_",
|
||||
".text",
|
||||
".ctors",
|
||||
".dtors",
|
||||
".rodata",
|
||||
".data",
|
||||
".bss",
|
||||
".sdata",
|
||||
".sbss",
|
||||
".sdata2",
|
||||
]
|
||||
rel_secs = [
|
||||
".text",
|
||||
".rodata",
|
||||
".data",
|
||||
".bss",
|
||||
]
|
||||
else:
|
||||
dol_secs = rel_secs = [".text"]
|
||||
print_binary_progress(dol_secs, decomp_sizes, total_sizes, "main.dol", args.full)
|
||||
print_binary_progress(rel_secs, rel_decomp_sizes, rel_total_sizes, "foresta.rel", args.full)
|
||||
@@ -0,0 +1,4 @@
|
||||
-r ./tools/ppcdis/requirements.txt
|
||||
|
||||
ninja_syntax
|
||||
prettytable
|
||||
Submodule
+1
Submodule tools/libyaz0 added at 5b5986401d
+128
@@ -0,0 +1,128 @@
|
||||
import re
|
||||
from argparse import ArgumentParser
|
||||
|
||||
parser = ArgumentParser(description="Fetch symbols from map file")
|
||||
parser.add_argument('mapf', type=str, help="Map file path")
|
||||
parser.add_argument('relf', type=str, help="Rel file path")
|
||||
parser.add_argument('yml_p', type=str, help="YML output path")
|
||||
args = parser.parse_args()
|
||||
|
||||
class entry:
|
||||
def __init__(self, address, size, virt, num, symbol, file, source):
|
||||
self.address = address
|
||||
self.size = size
|
||||
self.virt = virt
|
||||
self.num = num
|
||||
self.symbol = symbol
|
||||
self.file = file
|
||||
self.source = source
|
||||
def yaml(self):
|
||||
return f"0x{self.virt}: {self.symbol}"
|
||||
def shiftvirt(self, shift):
|
||||
cast = int(self.virt, base=16)
|
||||
self.virt = hex(cast+shift)[2:].upper()
|
||||
def sourcefile(self):
|
||||
if self.source != None:
|
||||
return self.source
|
||||
else:
|
||||
if self.file.endswith(".o"):
|
||||
return self.file.replace(".o", ".c")
|
||||
else:
|
||||
return self.file
|
||||
allentries = []
|
||||
for isrel, filename in enumerate((args.mapf, args.relf)):
|
||||
with open(filename, 'r') as f:
|
||||
data = f.readlines()
|
||||
line = 0
|
||||
while True:
|
||||
while line < len(data) and not data[line].endswith("section layout\n"):
|
||||
line += 1
|
||||
if line >= len(data): break #end when the sections are exhausted
|
||||
|
||||
sect = data[line].replace(" section layout\n", '')
|
||||
line += 4 #skip section header
|
||||
|
||||
entries = []
|
||||
while data[line] != "\n":
|
||||
entryof = data[line].find("(entry of") != -1
|
||||
if entryof:
|
||||
d = re.sub(r"\(entry of .*?\)", "", data[line])
|
||||
else:
|
||||
d = data[line]
|
||||
d = d.strip().replace("\t", "").split(" ")
|
||||
d = [x for x in d if x != ""]
|
||||
#if sect == "extab":
|
||||
# print(data[line])
|
||||
# print(d)
|
||||
if entryof:
|
||||
source = d[5] if len(d) > 5 else None
|
||||
entries.append(entry(d[0], d[1], d[2], None, d[3], d[4], source))
|
||||
else:
|
||||
source = d[6] if len(d) > 6 else None
|
||||
entries.append(entry(d[0], d[1], d[2], d[3], d[4], d[5], source))
|
||||
if isrel:
|
||||
relshifts = {".bss": 0x8125A7C0, ".text": 0x803702A9, ".rodata": 0x80641260, ".data": 0x8064D500}
|
||||
entries[-1].file = "rel/" + entries[-1].file
|
||||
if entries[-1].source is not None:
|
||||
entries[-1].source = "rel/" + entries[-1].source
|
||||
if sect in relshifts:
|
||||
entries[-1].shiftvirt(relshifts[sect])
|
||||
else:
|
||||
entries[-1].file = "src/" + entries[-1].file
|
||||
if entries[-1].source is not None:
|
||||
entries[-1].source = "src/" + entries[-1].source
|
||||
|
||||
line += 1
|
||||
|
||||
allentries.append(entries)
|
||||
#print(sect, len(entries))
|
||||
#if sect == "extab":
|
||||
# for i in entries:
|
||||
# print(i.yaml())
|
||||
# print(i.address, i.size, i.virt, i.num, i.symbol, i.file)
|
||||
line += 1
|
||||
|
||||
filereg = {"global": {}, }
|
||||
duplicates = []
|
||||
|
||||
for sect in allentries:
|
||||
if len(sect) == 0: continue
|
||||
for e in sect:
|
||||
if (e.num != "1"
|
||||
and e.num is not None
|
||||
and "$" not in e.symbol
|
||||
and "@" not in e.symbol
|
||||
and "..." not in e.symbol):
|
||||
newfile = e.sourcefile()
|
||||
if e.symbol not in filereg["global"] and e.symbol not in duplicates:
|
||||
filereg["global"][e.symbol] = [e]
|
||||
elif e.symbol in duplicates: # previously removed from global
|
||||
if newfile not in filereg:
|
||||
filereg[newfile] = {}
|
||||
if e.symbol in filereg[newfile]:
|
||||
print("same file collision:", newfile, e.symbol)
|
||||
filereg[newfile][e.symbol].append(e)
|
||||
else:
|
||||
filereg[newfile][e.symbol] = [e]
|
||||
else: # encountered a wild duplicate in global
|
||||
duplicates.append(e.symbol)
|
||||
oldfile = filereg["global"][e.symbol][0].sourcefile()
|
||||
if oldfile not in filereg:
|
||||
filereg[oldfile] = {}
|
||||
if newfile not in filereg:
|
||||
filereg[newfile] = {}
|
||||
filereg[oldfile][e.symbol] = filereg["global"][e.symbol]
|
||||
if oldfile == newfile:
|
||||
print("same file collision:", newfile, e.symbol)
|
||||
filereg[newfile][e.symbol].append(e)
|
||||
else:
|
||||
filereg[newfile][e.symbol] = [e]
|
||||
filereg["global"].pop(e.symbol)
|
||||
|
||||
|
||||
with open(args.yml_p, 'w') as d:
|
||||
for file, entries in filereg.items():
|
||||
print(f"{file}:", file=d)
|
||||
flat_list = [item for sublist in entries.values() for item in sublist]
|
||||
for e in sorted(flat_list, key=lambda x: x.virt):
|
||||
print(" " + e.yaml(), file=d)
|
||||
Submodule
+1
Submodule tools/ppcdis added at 0ab8975ba8
@@ -0,0 +1,12 @@
|
||||
from argparse import ArgumentParser
|
||||
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument("input")
|
||||
parser.add_argument("output")
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.input, encoding="utf-8") as f:
|
||||
txt = f.read()
|
||||
|
||||
with open(args.output, 'w', encoding="shift-jis") as f:
|
||||
f.write(txt)
|
||||
@@ -0,0 +1,66 @@
|
||||
import re
|
||||
|
||||
from argparse import ArgumentParser
|
||||
|
||||
parser = ArgumentParser(description="Fetch symbols from map file")
|
||||
parser.add_argument('mapf', type=str, help="Map file path")
|
||||
parser.add_argument('yml_p', type=str, help="YML output path")
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.mapf, 'r') as f:
|
||||
data = f.readlines()
|
||||
class entry:
|
||||
def __init__(self, address, size, virt, num, symbol, file):
|
||||
self.address = address
|
||||
self.size = size
|
||||
self.virt = virt
|
||||
self.num = num
|
||||
self.symbol = symbol
|
||||
self.file = file
|
||||
def yaml(self):
|
||||
return f"0x{self.virt}: ENTRY"
|
||||
|
||||
allentries = []
|
||||
|
||||
line = 0
|
||||
while True:
|
||||
while line < len(data) and not data[line].endswith("section layout\n"):
|
||||
line += 1
|
||||
if line >= len(data): break #end when the sections are exhausted
|
||||
|
||||
sect = data[line].replace(" section layout\n", '')
|
||||
line += 4 #skip section header
|
||||
|
||||
entries = []
|
||||
while data[line] != "\n":
|
||||
entryof = data[line].find("(entry of") != -1
|
||||
if entryof:
|
||||
d = re.sub(r"\(entry of .*?\)", "", data[line])
|
||||
else:
|
||||
d = data[line]
|
||||
d = d.strip().replace("\t", "").split(" ")
|
||||
d = [x for x in d if x != ""]
|
||||
#if sect == "extab":
|
||||
# print(data[line])
|
||||
# print(d)
|
||||
if entryof:
|
||||
entries.append(entry(d[0], d[1], d[2], None, d[3], d[4]))
|
||||
else:
|
||||
entries.append(entry(d[0], d[1], d[2], d[3], d[4], d[5]))
|
||||
line += 1
|
||||
|
||||
allentries.append(entries)
|
||||
#print(sect, len(entries))
|
||||
#if sect == "extab":
|
||||
# for i in entries:
|
||||
# print(i.yaml())
|
||||
# print(i.address, i.size, i.virt, i.num, i.symbol, i.file)
|
||||
line += 1
|
||||
with open(args.yml_p, 'w') as d:
|
||||
print("global:", file=d)
|
||||
for sect in allentries:
|
||||
if len(sect) == 0: continue
|
||||
# print(" #" + sect[0].yaml())
|
||||
for e in sect:
|
||||
if e.num is None and "..." not in e.symbol:
|
||||
print(" " + e.yaml(), file=d)
|
||||
@@ -0,0 +1,69 @@
|
||||
import re
|
||||
|
||||
from argparse import ArgumentParser
|
||||
|
||||
parser = ArgumentParser(description="Fetch symbols from map file")
|
||||
parser.add_argument('mapf', type=str, help="Map file path")
|
||||
parser.add_argument('yml_p', type=str, help="YML output path")
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.mapf, 'r') as f:
|
||||
data = f.readlines()
|
||||
class entry:
|
||||
def __init__(self, address, size, virt, num, symbol, file):
|
||||
self.address = address
|
||||
self.size = size
|
||||
self.virt = virt
|
||||
self.num = num
|
||||
self.symbol = symbol
|
||||
self.file = file
|
||||
def yaml(self):
|
||||
cast = int(self.virt, base=16)
|
||||
Sum = cast+0x803701C0
|
||||
Sum = hex(Sum)
|
||||
return f"0x{Sum}: ENTRY"
|
||||
|
||||
allentries = []
|
||||
|
||||
line = 0
|
||||
while True:
|
||||
while line < len(data) and not data[line].endswith("section layout\n"):
|
||||
line += 1
|
||||
if line >= len(data): break #end when the sections are exhausted
|
||||
|
||||
sect = data[line].replace(" section layout\n", '')
|
||||
line += 4 #skip section header
|
||||
|
||||
entries = []
|
||||
while data[line] != "\n":
|
||||
entryof = data[line].find("(entry of") != -1
|
||||
if entryof:
|
||||
d = re.sub(r"\(entry of .*?\)", "", data[line])
|
||||
else:
|
||||
d = data[line]
|
||||
d = d.strip().replace("\t", "").split(" ")
|
||||
d = [x for x in d if x != ""]
|
||||
#if sect == "extab":
|
||||
# print(data[line])
|
||||
# print(d)
|
||||
if entryof:
|
||||
entries.append(entry(d[0], d[1], d[2], None, d[3], d[4]))
|
||||
else:
|
||||
entries.append(entry(d[0], d[1], d[2], d[3], d[4], d[5]))
|
||||
line += 1
|
||||
|
||||
allentries.append(entries)
|
||||
#print(sect, len(entries))
|
||||
#if sect == "extab":
|
||||
# for i in entries:
|
||||
# print(i.yaml())
|
||||
# print(i.address, i.size, i.virt, i.num, i.symbol, i.file)
|
||||
line += 1
|
||||
with open(args.yml_p, 'w') as d:
|
||||
print("global:", file=d)
|
||||
for sect in allentries:
|
||||
if len(sect) == 0: continue
|
||||
# print(" #" + sect[0].yaml())
|
||||
for e in sect:
|
||||
if e.num is None and "..." not in e.symbol:
|
||||
print(" - " + e.yaml(), file=d)
|
||||
Reference in New Issue
Block a user