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:
Ethan Roseman 2021-02-08 19:57:56 +09:00
parent e578b7d90a
commit 39ea4c7a86
7 changed files with 256 additions and 240 deletions

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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}"

View File

@ -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")

View File

@ -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

View File

@ -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)