mirror of
https://github.com/pmret/papermario.git
synced 2024-11-08 12:02:30 +01:00
git subrepo pull tools/splat
subrepo: subdir: "tools/splat" merged: "3a66565988" upstream: origin: "https://github.com/ethteck/splat.git" branch: "master" commit: "3a66565988" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo" commit: "2f68596"
This commit is contained in:
parent
e578b7d90a
commit
39ea4c7a86
@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/ethteck/splat.git
|
||||
branch = master
|
||||
commit = 7e40c55d56f779521350fa881aa4dbdf30032a8c
|
||||
parent = 68e927503b880c53ebb03a74030479ca519eb9bc
|
||||
commit = 3a66565988438d58d8c53449254a49f31ff2817c
|
||||
parent = e578b7d90ac01d1e3f235fa6ebec09a0ddb5c415
|
||||
method = merge
|
||||
cmdver = 0.4.3
|
||||
|
@ -37,3 +37,10 @@ There's also a new option, `create_new_c_files`, which disables the creation of
|
||||
I am also working on adding bss support as well. It should almost be all set, aside from the changes needed in the linker script.
|
||||
|
||||
**Breaking change**: The `files` field in `code` segments should now be renamed to `subsections`.
|
||||
|
||||
### 0.6.3: More refactoring
|
||||
**Breaking Change**: The command line args to split.py have changed. Currently, only the config path is now a required argument to splat. The old `rom` and `outdir` parameters are now optional (`--rom`, `--outdir`). Now, you can add rom and out directory paths in the yaml.
|
||||
|
||||
The `out_dir` option specifies a directory relative to the config file. If your config file is in a subdirectory of the main repo, you can set `out_dir: ../`, for example.
|
||||
|
||||
The `target_path` option spcifies a path to the binary file to split, relative to the `out_dir`. If your `baserom.z64` is in the top-level of the repo, you can set `target_path: baserom.z64`, for example.
|
||||
|
@ -1,11 +1,12 @@
|
||||
import os
|
||||
from segtypes.n64.segment import N64Segment
|
||||
from pathlib import Path
|
||||
from segtypes.segment import Segment
|
||||
|
||||
|
||||
class N64SegBin(N64Segment):
|
||||
def split(self, rom_bytes, base_path):
|
||||
out_dir = self.create_split_dir(base_path, self.options.get("assets_dir", "bin"))
|
||||
out_dir = Segment.create_split_dir(base_path, self.options.get("assets_dir", "bin"))
|
||||
|
||||
bin_path = os.path.join(out_dir, self.name + ".bin")
|
||||
Path(bin_path).parent.mkdir(parents=True, exist_ok=True)
|
||||
|
@ -1,9 +1,12 @@
|
||||
from typing import get_args
|
||||
from capstone import *
|
||||
from capstone.mips import *
|
||||
|
||||
from collections import OrderedDict
|
||||
from segtypes.n64.segment import N64Segment
|
||||
from segtypes.segment import Segment
|
||||
from segtypes.n64.palette import N64SegPalette
|
||||
from segtypes.n64.ci4 import N64SegCi4
|
||||
import png
|
||||
import os
|
||||
from pathlib import Path, PurePath
|
||||
import re
|
||||
@ -11,41 +14,13 @@ import sys
|
||||
from util import floats
|
||||
from util.symbol import Symbol
|
||||
|
||||
|
||||
STRIP_C_COMMENTS_RE = re.compile(
|
||||
r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
|
||||
re.DOTALL | re.MULTILINE
|
||||
)
|
||||
|
||||
C_FUNC_RE = re.compile(
|
||||
r"^(static\s+)?[^\s]+\s+([^\s(]+)\(([^;)]*)\)[^;]+?{",
|
||||
re.MULTILINE
|
||||
)
|
||||
|
||||
double_mnemonics = ["ldc1", "sdc1"]
|
||||
word_mnemonics = ["addiu", "sw", "lw", "jtbl"]
|
||||
float_mnemonics = ["lwc1", "swc1"]
|
||||
short_mnemonics = ["addiu", "lh", "sh", "lhu"]
|
||||
byte_mnemonics = ["lb", "sb", "lbu"]
|
||||
|
||||
def strip_c_comments(text):
|
||||
def replacer(match):
|
||||
s = match.group(0)
|
||||
if s.startswith("/"):
|
||||
return " "
|
||||
else:
|
||||
return s
|
||||
return re.sub(STRIP_C_COMMENTS_RE, replacer, text)
|
||||
|
||||
|
||||
def get_funcs_defined_in_c(c_file):
|
||||
with open(c_file, "r") as f:
|
||||
text = strip_c_comments(f.read())
|
||||
|
||||
return set(m.group(2) for m in C_FUNC_RE.finditer(text))
|
||||
|
||||
class Subsegment():
|
||||
|
||||
def __init__(self, start, end, name, type, vram, args):
|
||||
self.rom_start = start
|
||||
self.rom_end = end
|
||||
@ -104,8 +79,163 @@ class Subsegment():
|
||||
def should_run(self, options):
|
||||
return self.type in options["modes"] or "all" in options["modes"]
|
||||
|
||||
def get_generic_out_path(self, base_path, options):
|
||||
return os.path.join(
|
||||
base_path,
|
||||
self.get_out_subdir(options),
|
||||
self.name + "." + self.get_ext()
|
||||
)
|
||||
|
||||
def split_inner(self, segment, rom_bytes, base_path, generic_out_path):
|
||||
pass
|
||||
|
||||
def split(self, segment, rom_bytes, base_path):
|
||||
if self.should_run(segment.options) and not self.name.startswith("."):
|
||||
self.split_inner(segment, rom_bytes, base_path, self.get_generic_out_path(base_path, segment.options))
|
||||
|
||||
@staticmethod
|
||||
def get_subclass(typ):
|
||||
if typ in ["data", ".data", "rodata", ".rodata"]:
|
||||
return DataSubsegment
|
||||
elif typ in ["bss", ".bss"]:
|
||||
return BssSubsegment
|
||||
elif typ == "bin":
|
||||
return BinSubsegment
|
||||
elif typ in ["c", "asm", "hasm"]:
|
||||
return CodeSubsegment
|
||||
elif typ == "palette":
|
||||
return PaletteSubsegment
|
||||
else:
|
||||
return Subsegment
|
||||
|
||||
class CodeSubsegment(Subsegment):
|
||||
md = Cs(CS_ARCH_MIPS, CS_MODE_MIPS64 + CS_MODE_BIG_ENDIAN)
|
||||
md.detail = True
|
||||
md.skipdata = True
|
||||
|
||||
STRIP_C_COMMENTS_RE = re.compile(
|
||||
r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
|
||||
re.DOTALL | re.MULTILINE
|
||||
)
|
||||
|
||||
C_FUNC_RE = re.compile(
|
||||
r"^(static\s+)?[^\s]+\s+([^\s(]+)\(([^;)]*)\)[^;]+?{",
|
||||
re.MULTILINE
|
||||
)
|
||||
|
||||
def strip_c_comments(text):
|
||||
def replacer(match):
|
||||
s = match.group(0)
|
||||
if s.startswith("/"):
|
||||
return " "
|
||||
else:
|
||||
return s
|
||||
return re.sub(CodeSubsegment.STRIP_C_COMMENTS_RE, replacer, text)
|
||||
|
||||
@staticmethod
|
||||
def get_funcs_defined_in_c(c_file):
|
||||
with open(c_file, "r") as f:
|
||||
text = CodeSubsegment.strip_c_comments(f.read())
|
||||
|
||||
return set(m.group(2) for m in CodeSubsegment.C_FUNC_RE.finditer(text))
|
||||
|
||||
@staticmethod
|
||||
def get_asm_header():
|
||||
ret = []
|
||||
|
||||
ret.append(".include \"macro.inc\"")
|
||||
ret.append("")
|
||||
ret.append("# assembler directives")
|
||||
ret.append(".set noat # allow manual use of $at")
|
||||
ret.append(".set noreorder # don't insert nops after branches")
|
||||
ret.append(".set gp=64 # allow use of 64-bit general purpose registers")
|
||||
ret.append("")
|
||||
ret.append(".section .text, \"ax\"")
|
||||
ret.append("")
|
||||
|
||||
return ret
|
||||
|
||||
def split_inner(self, segment, rom_bytes, base_path, generic_out_path):
|
||||
if not self.rom_start == self.rom_end:
|
||||
asm_out_dir = Segment.create_split_dir(base_path, "asm")
|
||||
|
||||
rom_addr = self.rom_start
|
||||
|
||||
insns = [insn for insn in CodeSubsegment.md.disasm(rom_bytes[self.rom_start : self.rom_end], self.vram_start)]
|
||||
|
||||
funcs = segment.process_insns(insns, rom_addr)
|
||||
|
||||
# TODO: someday make func a subclass of symbol and store this disasm info there too
|
||||
for func in funcs:
|
||||
segment.get_symbol(func, type="func", create=True, define=True, local_only=True)
|
||||
|
||||
funcs = segment.determine_symbols(funcs)
|
||||
segment.gather_jumptable_labels(rom_bytes)
|
||||
funcs_text = segment.add_labels(funcs)
|
||||
|
||||
if self.type == "c":
|
||||
if os.path.exists(generic_out_path):
|
||||
defined_funcs = CodeSubsegment.get_funcs_defined_in_c(generic_out_path)
|
||||
segment.mark_c_funcs_as_defined(defined_funcs)
|
||||
else:
|
||||
defined_funcs = set()
|
||||
|
||||
asm_out_dir = Segment.create_split_dir(base_path, os.path.join("asm", "nonmatchings"))
|
||||
|
||||
for func in funcs_text:
|
||||
func_name = segment.get_symbol(func, type="func", local_only=True).name
|
||||
|
||||
if func_name not in defined_funcs:
|
||||
segment.create_c_asm_file(funcs_text, func, asm_out_dir, self, func_name)
|
||||
|
||||
if not os.path.exists(generic_out_path) and self.options.get("create_new_c_files", True):
|
||||
self.create_c_file(funcs_text, self, asm_out_dir, base_path, generic_out_path)
|
||||
else:
|
||||
out_lines = self.get_asm_header()
|
||||
for func in funcs_text:
|
||||
out_lines.extend(funcs_text[func][0])
|
||||
out_lines.append("")
|
||||
|
||||
outpath = Path(os.path.join(asm_out_dir, self.name + ".s"))
|
||||
outpath.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with open(outpath, "w", newline="\n") as f:
|
||||
f.write("\n".join(out_lines))
|
||||
|
||||
class DataSubsegment(Subsegment):
|
||||
def split_inner(self, segment, rom_bytes, base_path, generic_out_path):
|
||||
asm_out_dir = Segment.create_split_dir(base_path, os.path.join("asm", "data"))
|
||||
|
||||
outpath = Path(os.path.join(asm_out_dir, self.name + f".{self.type}.s"))
|
||||
outpath.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
file_text = segment.disassemble_data(self, rom_bytes)
|
||||
if file_text:
|
||||
with open(outpath, "w", newline="\n") as f:
|
||||
f.write(file_text)
|
||||
|
||||
class BssSubsegment(DataSubsegment):
|
||||
def __init__(self, start, end, name, type, vram, args):
|
||||
super().__init__(start, end, name, type, vram, args)
|
||||
self.size = self.args[0]
|
||||
self.vram_end = self.vram_start + self.size
|
||||
|
||||
class BinSubsegment(Subsegment):
|
||||
def split_inner(self, segment, rom_bytes, base_path, generic_out_path):
|
||||
Path(generic_out_path).parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(generic_out_path, "wb") as f:
|
||||
f.write(rom_bytes[self.rom_start : self.rom_end])
|
||||
|
||||
class PaletteSubsegment(Subsegment):
|
||||
def split_inner(self, segment, rom_bytes, base_path, generic_out_path):
|
||||
img_bytes = rom_bytes[self.rom_start : self.rom_end]
|
||||
|
||||
palette = N64SegPalette.parse_palette(img_bytes)
|
||||
segment.palettes[self.name] = palette
|
||||
|
||||
class N64SegCode(N64Segment):
|
||||
palettes = {}
|
||||
|
||||
def parse_subsegments(self, segment_yaml):
|
||||
prefix = self.name if self.name.endswith("/") else f"{self.name}_"
|
||||
|
||||
@ -141,7 +271,9 @@ class N64SegCode(N64Segment):
|
||||
|
||||
vram = self.rom_to_ram(start)
|
||||
|
||||
ret.append(Subsegment(start, end, name, typ, vram, args))
|
||||
subsegment_class = Subsegment.get_subclass(typ)
|
||||
|
||||
ret.append(subsegment_class(start, end, name, typ, vram, args))
|
||||
prev_start = start
|
||||
|
||||
return ret
|
||||
@ -165,6 +297,22 @@ class N64SegCode(N64Segment):
|
||||
def get_default_name(addr):
|
||||
return f"code_{addr:X}"
|
||||
|
||||
def get_ld_files(self):
|
||||
def transform(sub):
|
||||
subdir = sub.get_out_subdir(self.options)
|
||||
obj_type = sub.get_ld_obj_type(".text")
|
||||
ext = sub.get_ext()
|
||||
|
||||
return subdir, f"{sub.name}.{ext}", obj_type, sub.rom_start
|
||||
|
||||
return [transform(file) for file in self.subsegments]
|
||||
|
||||
def get_ld_section_name(self):
|
||||
path = PurePath(self.name)
|
||||
name = path.name if path.name != "" else path.parent
|
||||
|
||||
return f"code_{name}"
|
||||
|
||||
def retrieve_symbol(self, d, k, t):
|
||||
if k not in d:
|
||||
return None
|
||||
@ -239,21 +387,6 @@ class N64SegCode(N64Segment):
|
||||
|
||||
return ret
|
||||
|
||||
def get_asm_header(self):
|
||||
ret = []
|
||||
|
||||
ret.append(".include \"macro.inc\"")
|
||||
ret.append("")
|
||||
ret.append("# assembler directives")
|
||||
ret.append(".set noat # allow manual use of $at")
|
||||
ret.append(".set noreorder # don't insert nops after branches")
|
||||
ret.append(".set gp=64 # allow use of 64-bit general purpose registers")
|
||||
ret.append("")
|
||||
ret.append(".section .text, \"ax\"")
|
||||
ret.append("")
|
||||
|
||||
return ret
|
||||
|
||||
def get_gcc_inc_header(self):
|
||||
ret = []
|
||||
ret.append(".set noat # allow manual use of $at")
|
||||
@ -276,6 +409,7 @@ class N64SegCode(N64Segment):
|
||||
def process_insns(self, insns, rom_addr):
|
||||
ret = OrderedDict()
|
||||
|
||||
func_addr = None
|
||||
func = []
|
||||
end_func = False
|
||||
labels = []
|
||||
@ -781,149 +915,25 @@ class N64SegCode(N64Segment):
|
||||
print(f"Wrote {sub.name} to {c_path}")
|
||||
|
||||
def split(self, rom_bytes, base_path):
|
||||
md = Cs(CS_ARCH_MIPS, CS_MODE_MIPS64 + CS_MODE_BIG_ENDIAN)
|
||||
md.detail = True
|
||||
md.skipdata = True
|
||||
|
||||
palettes = {}
|
||||
|
||||
for sub in self.subsegments:
|
||||
if sub.type in ["asm", "hasm", "c"]:
|
||||
if not sub.should_run(self.options):
|
||||
continue
|
||||
sub.split(self, rom_bytes, base_path)
|
||||
|
||||
if sub.rom_start == sub.rom_end:
|
||||
continue
|
||||
|
||||
asm_out_dir = self.create_split_dir(base_path, "asm")
|
||||
|
||||
rom_addr = sub.rom_start
|
||||
|
||||
insns = [insn for insn in md.disasm(rom_bytes[sub.rom_start: sub.rom_end], sub.vram_start)]
|
||||
|
||||
funcs = self.process_insns(insns, rom_addr)
|
||||
|
||||
# TODO: someday make func a subclass of symbol and store this disasm info there too
|
||||
for func in funcs:
|
||||
self.get_symbol(func, type="func", create=True, define=True, local_only=True)
|
||||
|
||||
funcs = self.determine_symbols(funcs)
|
||||
self.gather_jumptable_labels(rom_bytes)
|
||||
funcs_text = self.add_labels(funcs)
|
||||
|
||||
if sub.type == "c":
|
||||
c_path = os.path.join(
|
||||
base_path,
|
||||
sub.get_out_subdir(self.options),
|
||||
sub.name + "." + sub.get_ext()
|
||||
)
|
||||
|
||||
if os.path.exists(c_path):
|
||||
defined_funcs = get_funcs_defined_in_c(c_path)
|
||||
self.mark_c_funcs_as_defined(defined_funcs)
|
||||
else:
|
||||
defined_funcs = set()
|
||||
|
||||
asm_out_dir = self.create_split_dir(base_path, os.path.join("asm", "nonmatchings"))
|
||||
|
||||
for func in funcs_text:
|
||||
func_name = self.get_symbol(func, type="func", local_only=True).name
|
||||
|
||||
if func_name not in defined_funcs:
|
||||
self.create_c_asm_file(funcs_text, func, asm_out_dir, sub, func_name)
|
||||
|
||||
if not os.path.exists(c_path) and self.options.get("create_new_c_files", True):
|
||||
self.create_c_file(funcs_text, sub, asm_out_dir, base_path, c_path)
|
||||
else:
|
||||
out_lines = self.get_asm_header()
|
||||
for func in funcs_text:
|
||||
out_lines.extend(funcs_text[func][0])
|
||||
out_lines.append("")
|
||||
|
||||
outpath = Path(os.path.join(asm_out_dir, sub.name + ".s"))
|
||||
outpath.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with open(outpath, "w", newline="\n") as f:
|
||||
f.write("\n".join(out_lines))
|
||||
|
||||
elif sub.type in ["data", "rodata", "bss"] and sub.should_run(self.options):
|
||||
asm_out_dir = self.create_split_dir(base_path, os.path.join("asm", "data"))
|
||||
|
||||
outpath = Path(os.path.join(asm_out_dir, sub.name + f".{sub.type}.s"))
|
||||
outpath.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
file_text = self.disassemble_data(sub, rom_bytes)
|
||||
if file_text:
|
||||
with open(outpath, "w", newline="\n") as f:
|
||||
f.write(file_text)
|
||||
|
||||
elif sub.type == "bin" and sub.should_run(self.options):
|
||||
bin_path = os.path.join(
|
||||
base_path,
|
||||
sub.get_out_subdir(self.options),
|
||||
sub.name + "." + sub.get_ext()
|
||||
)
|
||||
|
||||
Path(bin_path).parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(bin_path, "wb") as f:
|
||||
f.write(rom_bytes[sub.rom_start : sub.rom_end])
|
||||
|
||||
elif sub.type in ["i4", "i8", "ia4", "ia8", "ia16", "rgba16", "rgba32", "ci4", "ci8"]:
|
||||
pass
|
||||
|
||||
elif sub.type == "palette":
|
||||
from segtypes.n64.palette import N64SegPalette
|
||||
|
||||
out_path = os.path.join(
|
||||
base_path,
|
||||
sub.get_out_subdir(self.options),
|
||||
sub.name + "." + sub.get_ext()
|
||||
)
|
||||
# TODO hack for images: move at some point
|
||||
for sub in self.subsegments:
|
||||
if sub.type == "ci4" and (sub.should_run(self.options) or "img" in self.options["modes"]):
|
||||
generic_out_path = sub.get_generic_out_path(base_path, self.options)
|
||||
img_bytes = rom_bytes[sub.rom_start : sub.rom_end]
|
||||
|
||||
palette = N64SegPalette.parse_palette(img_bytes)
|
||||
palettes[sub.name] = palette
|
||||
|
||||
import png
|
||||
|
||||
for sub in self.subsegments:
|
||||
img_bytes = rom_bytes[sub.rom_start : sub.rom_end]
|
||||
|
||||
out_path = os.path.join(
|
||||
base_path,
|
||||
sub.get_out_subdir(self.options),
|
||||
sub.name + "." + sub.get_ext()
|
||||
)
|
||||
|
||||
if sub.type == "ci4" and (sub.should_run(self.options) or "img" in self.options["modes"]):
|
||||
from segtypes.n64.ci4 import N64SegCi4
|
||||
|
||||
width, height = sub.args
|
||||
palette = palettes[sub.name]
|
||||
palette = self.palettes[sub.name]
|
||||
image = N64SegCi4.parse_image(img_bytes, width, height)
|
||||
|
||||
w = png.Writer(width, height, palette=palette)
|
||||
|
||||
Path(out_path).parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(out_path, "wb") as f:
|
||||
Path(generic_out_path).parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(generic_out_path, "wb") as f:
|
||||
w.write_array(f, image)
|
||||
|
||||
# TODO other image types
|
||||
|
||||
# TODO write orphaned palettes
|
||||
|
||||
def get_ld_files(self):
|
||||
def transform(sub):
|
||||
subdir = sub.get_out_subdir(self.options)
|
||||
obj_type = sub.get_ld_obj_type(".text")
|
||||
ext = sub.get_ext()
|
||||
|
||||
return subdir, f"{sub.name}.{ext}", obj_type, sub.rom_start
|
||||
|
||||
return [transform(file) for file in self.subsegments]
|
||||
|
||||
def get_ld_section_name(self):
|
||||
path = PurePath(self.name)
|
||||
name = path.name if path.name != "" else path.parent
|
||||
|
||||
return f"code_{name}"
|
||||
|
@ -1,6 +1,7 @@
|
||||
import os
|
||||
from segtypes.n64.segment import N64Segment
|
||||
from pathlib import Path
|
||||
from segtypes.segment import Segment
|
||||
|
||||
class N64SegHeader(N64Segment):
|
||||
def should_run(self):
|
||||
@ -18,7 +19,7 @@ class N64SegHeader(N64Segment):
|
||||
return f".{typ} {dstr} /* {comment} */"
|
||||
|
||||
def split(self, rom_bytes, base_path):
|
||||
out_dir = self.create_split_dir(base_path, "asm")
|
||||
out_dir = Segment.create_split_dir(base_path, "asm")
|
||||
|
||||
encoding = self.options.get("header_encoding", "ASCII")
|
||||
|
||||
|
@ -94,12 +94,14 @@ class Segment:
|
||||
|
||||
return self.rom_start + ram_addr - self.vram_start
|
||||
|
||||
def create_split_dir(self, base_path, subdir):
|
||||
@staticmethod
|
||||
def create_split_dir(base_path, subdir):
|
||||
out_dir = Path(base_path, subdir)
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
return out_dir
|
||||
|
||||
def create_parent_dir(self, base_path, filename):
|
||||
@staticmethod
|
||||
def create_parent_dir(base_path, filename):
|
||||
out_dir = Path(base_path, filename).parent
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
return out_dir
|
||||
|
@ -12,12 +12,13 @@ from segtypes.segment import parse_segment_type
|
||||
from segtypes.n64.code import N64SegCode
|
||||
from util import log
|
||||
from util.symbol import Symbol
|
||||
import sys
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Split a rom given a rom, a config, and output directory")
|
||||
parser.add_argument("rom", help="path to a .z64 rom")
|
||||
parser.add_argument("config", help="path to a compatible config .yaml file")
|
||||
parser.add_argument("outdir", help="a directory in which to extract the rom")
|
||||
parser.add_argument("--rom", help="path to a .z64 rom")
|
||||
parser.add_argument("--outdir", help="a directory in which to extract the rom")
|
||||
parser.add_argument("--modes", nargs="+", default="all")
|
||||
parser.add_argument("--verbose", action="store_true",
|
||||
help="Enable debug logging")
|
||||
@ -133,20 +134,6 @@ def gather_symbols(symbol_addrs_path, undefined_syms_path):
|
||||
rom_addr = int(info.split(":")[1], 0)
|
||||
sym.rom = rom_addr
|
||||
symbols.append(sym)
|
||||
|
||||
# Maybe let's not use this
|
||||
if os.path.exists(undefined_syms_path):
|
||||
with open(undefined_syms_path) as f:
|
||||
us_lines = f.readlines()
|
||||
|
||||
for line in us_lines:
|
||||
line = line.strip()
|
||||
if not line == "" and not line.startswith("//"):
|
||||
line_split = line.split("=")
|
||||
name = line_split[0].strip()
|
||||
addr = int(line_split[1].strip()[:-1], 0)
|
||||
symbols.append(Symbol(addr, given_name=name))
|
||||
|
||||
return symbols
|
||||
|
||||
|
||||
@ -196,11 +183,7 @@ def initialize_segments(options, config_path, config_segments):
|
||||
seen_segment_names = set()
|
||||
ret = []
|
||||
|
||||
for i, segment in enumerate(config_segments):
|
||||
if len(segment) == 1:
|
||||
# We're at the end
|
||||
continue
|
||||
|
||||
for i, segment in enumerate(config_segments[:-1]):
|
||||
seg_type = parse_segment_type(segment)
|
||||
|
||||
platform = get_platform(options)
|
||||
@ -266,13 +249,7 @@ def get_segment_symbols(segment, all_symbols, all_segments):
|
||||
|
||||
return seg_syms, other_syms
|
||||
|
||||
def main(rom_path, config_path, repo_path, modes, verbose, ignore_cache=False):
|
||||
with open(rom_path, "rb") as f:
|
||||
rom_bytes = f.read()
|
||||
|
||||
# Create main output dir
|
||||
Path(repo_path).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
def main(config_path, out_dir, target_path, modes, verbose, ignore_cache=False):
|
||||
# Load config
|
||||
with open(config_path) as f:
|
||||
config = yaml.safe_load(f.read())
|
||||
@ -281,10 +258,31 @@ def main(rom_path, config_path, repo_path, modes, verbose, ignore_cache=False):
|
||||
options["modes"] = modes
|
||||
options["verbose"] = verbose
|
||||
|
||||
symbol_addrs_path = get_symbol_addrs_path(repo_path, options)
|
||||
undefined_syms_path = get_undefined_syms_path(repo_path, options)
|
||||
if not out_dir:
|
||||
out_dir = options.get("out_dir", None)
|
||||
if not out_dir:
|
||||
print("Error: Output dir not specified as a command line arg or via the config yaml (out_dir)")
|
||||
sys.exit(2)
|
||||
else:
|
||||
out_dir = os.path.join(Path(config_path).parent, out_dir)
|
||||
|
||||
if not target_path:
|
||||
target_path = options.get("target_path", None)
|
||||
if not target_path:
|
||||
print("Error: Target binary path not specified as a command line arg or via the config yaml (target_path)")
|
||||
sys.exit(2)
|
||||
else:
|
||||
target_path = os.path.join(out_dir, target_path)
|
||||
|
||||
with open(target_path, "rb") as f:
|
||||
rom_bytes = f.read()
|
||||
|
||||
# Create main output dir
|
||||
Path(out_dir).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
symbol_addrs_path = get_symbol_addrs_path(out_dir, options)
|
||||
undefined_syms_path = get_undefined_syms_path(out_dir, options)
|
||||
all_symbols = gather_symbols(symbol_addrs_path, undefined_syms_path)
|
||||
isolated_symbols = {}
|
||||
symbol_ranges = [s for s in all_symbols if s.size > 4]
|
||||
platform = get_platform(options)
|
||||
|
||||
@ -296,7 +294,7 @@ def main(rom_path, config_path, repo_path, modes, verbose, ignore_cache=False):
|
||||
seg_cached = {}
|
||||
|
||||
# Load cache
|
||||
cache_path = get_cache_path(repo_path, options)
|
||||
cache_path = get_cache_path(out_dir, options)
|
||||
try:
|
||||
with open(cache_path, "rb") as f:
|
||||
cache = pickle.load(f)
|
||||
@ -316,15 +314,15 @@ def main(rom_path, config_path, repo_path, modes, verbose, ignore_cache=False):
|
||||
|
||||
segment.check()
|
||||
|
||||
tp = segment.type
|
||||
typ = segment.type
|
||||
if segment.type == "bin" and segment.is_name_default():
|
||||
tp = "unk"
|
||||
typ = "unk"
|
||||
|
||||
if tp not in seg_sizes:
|
||||
seg_sizes[tp] = 0
|
||||
seg_split[tp] = 0
|
||||
seg_cached[tp] = 0
|
||||
seg_sizes[tp] += segment.size
|
||||
if typ not in seg_sizes:
|
||||
seg_sizes[typ] = 0
|
||||
seg_split[typ] = 0
|
||||
seg_cached[typ] = 0
|
||||
seg_sizes[typ] += segment.size
|
||||
|
||||
if len(segment.errors) == 0:
|
||||
if segment.should_run():
|
||||
@ -332,18 +330,18 @@ def main(rom_path, config_path, repo_path, modes, verbose, ignore_cache=False):
|
||||
cached = segment.cache()
|
||||
if not ignore_cache and cached == cache.get(segment.unique_id()):
|
||||
# Cache hit
|
||||
seg_cached[tp] += 1
|
||||
seg_cached[typ] += 1
|
||||
else:
|
||||
# Cache miss; split
|
||||
cache[segment.unique_id()] = cached
|
||||
|
||||
segment.did_run = True
|
||||
segment.split(rom_bytes, repo_path)
|
||||
segment.split(rom_bytes, out_dir)
|
||||
|
||||
if len(segment.errors) == 0:
|
||||
processed_segments.append(segment)
|
||||
|
||||
seg_split[tp] += 1
|
||||
seg_split[typ] += 1
|
||||
|
||||
log.dot(status=segment.status())
|
||||
ld_sections.append(segment.get_ld_section())
|
||||
@ -356,15 +354,13 @@ def main(rom_path, config_path, repo_path, modes, verbose, ignore_cache=False):
|
||||
if "ld" in options["modes"] or "all" in options["modes"]:
|
||||
if verbose:
|
||||
log.write(f"saving {config['basename']}.ld")
|
||||
write_ldscript(config['basename'], repo_path, ld_sections, options)
|
||||
write_ldscript(config['basename'], out_dir, ld_sections, options)
|
||||
|
||||
undefined_syms_to_write = [s for s in all_symbols if s.referenced and not s.defined and not s.type == "func"]
|
||||
undefined_funcs_to_write = [s for s in all_symbols if s.referenced and not s.defined and s.type == "func"]
|
||||
|
||||
# Write undefined_funcs_auto.txt
|
||||
undefined_funcs_auto_path = get_undefined_funcs_auto_path(repo_path, options)
|
||||
if verbose:
|
||||
log.write(f"saving {undefined_funcs_auto_path}")
|
||||
undefined_funcs_auto_path = get_undefined_funcs_auto_path(out_dir, options)
|
||||
|
||||
to_write = undefined_funcs_to_write
|
||||
if len(to_write) > 0:
|
||||
@ -373,9 +369,8 @@ def main(rom_path, config_path, repo_path, modes, verbose, ignore_cache=False):
|
||||
f.write(f"{symbol.name} = 0x{symbol.vram_start:X};\n")
|
||||
|
||||
# write undefined_syms_auto.txt
|
||||
undefined_syms_auto_path = get_undefined_syms_auto_path(repo_path, options)
|
||||
if verbose:
|
||||
log.write(f"saving {undefined_syms_auto_path}")
|
||||
undefined_syms_auto_path = get_undefined_syms_auto_path(out_dir, options)
|
||||
|
||||
to_write = undefined_syms_to_write
|
||||
if len(to_write) > 0:
|
||||
with open(undefined_syms_auto_path, "w", newline="\n") as f:
|
||||
@ -397,9 +392,9 @@ def main(rom_path, config_path, repo_path, modes, verbose, ignore_cache=False):
|
||||
rest_size = 0
|
||||
total_size = len(rom_bytes)
|
||||
|
||||
for tp in seg_sizes:
|
||||
if tp != "unk":
|
||||
rest_size += seg_sizes[tp]
|
||||
for typ in seg_sizes:
|
||||
if typ != "unk":
|
||||
rest_size += seg_sizes[typ]
|
||||
|
||||
assert(unk_size + rest_size == total_size)
|
||||
|
||||
@ -407,11 +402,11 @@ def main(rom_path, config_path, repo_path, modes, verbose, ignore_cache=False):
|
||||
unk_ratio = unk_size / total_size
|
||||
|
||||
log.write(f"Split {fmt_size(rest_size)} ({known_ratio:.2%}) in defined segments")
|
||||
for tp in seg_sizes:
|
||||
if tp != "unk":
|
||||
tmp_size = seg_sizes[tp]
|
||||
for typ in seg_sizes:
|
||||
if typ != "unk":
|
||||
tmp_size = seg_sizes[typ]
|
||||
tmp_ratio = tmp_size / total_size
|
||||
log.write(f"{tp:>20}: {fmt_size(tmp_size):>8} ({tmp_ratio:.2%}) {Fore.GREEN}{seg_split[tp]} split{Style.RESET_ALL}, {Style.DIM}{seg_cached[tp]} cached")
|
||||
log.write(f"{typ:>20}: {fmt_size(tmp_size):>8} ({tmp_ratio:.2%}) {Fore.GREEN}{seg_split[typ]} split{Style.RESET_ALL}, {Style.DIM}{seg_cached[typ]} cached")
|
||||
log.write(f"{'unknown':>20}: {fmt_size(unk_size):>8} ({unk_ratio:.2%}) from unknown bin files")
|
||||
|
||||
# Save cache
|
||||
@ -425,5 +420,5 @@ def main(rom_path, config_path, repo_path, modes, verbose, ignore_cache=False):
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = parser.parse_args()
|
||||
error_code = main(args.rom, args.config, args.outdir, args.modes, args.verbose, not args.new)
|
||||
error_code = main(args.config, args.outdir, args.rom, args.modes, args.verbose, not args.new)
|
||||
exit(error_code)
|
||||
|
Loading…
Reference in New Issue
Block a user