mirror of
https://github.com/pmret/papermario.git
synced 2024-11-10 04:52:34 +01:00
fab07c1c8c
* dro work * nok varstash dupes * p1 * rest * documentin * fixes * imports sorted * dro_01 cleaned up * dro done * kmr_06 * cleanup * mergin * warnings * really fix warnings * git subrepo pull --force tools/splat subrepo: subdir: "tools/splat" merged: "ebadd64f07" upstream: origin: "https://github.com/ethteck/splat.git" branch: "master" commit: "ebadd64f07" git-subrepo: version: "0.4.5" origin: "https://github.com/ingydotnet/git-subrepo" commit: "aa416e4" * dead map reorg I * dead reorg II * dead reorg III * dead reorg IV * got a func * part of area_kmr * oops Co-authored-by: HailSanta <Hail2Santa@gmail.com> Co-authored-by: Ethan Roseman <ethteck@gmail.com>
400 lines
16 KiB
Python
400 lines
16 KiB
Python
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
from typing import Dict, List, Mapping, Optional, Type, TypeVar
|
|
|
|
from util import compiler
|
|
from util.compiler import Compiler
|
|
|
|
|
|
@dataclass
|
|
class SplatOpts:
|
|
# Debug / logging
|
|
verbose: bool
|
|
dump_symbols: bool
|
|
modes: List[str]
|
|
|
|
# Project configuration
|
|
|
|
# Determines the base path of the project. Everything is relative to this path
|
|
base_path: Path
|
|
# Determines the path to the target binary
|
|
target_path: Path
|
|
# Determines the platform of the target binary
|
|
platform: str
|
|
# Determines the compiler used to compile the target binary
|
|
compiler: Compiler
|
|
# Determines the endianness of the target binary
|
|
endianness: str
|
|
# Determines the default section order of the target binary
|
|
# this can be overridden per-segment
|
|
section_order: List[str]
|
|
# Determines the code that is inserted by default in generated .c files
|
|
generated_c_preamble: str
|
|
# Determines the code that is inserted by default in generated .s files
|
|
generated_s_preamble: str
|
|
# Determines whether to use .o as the suffix for all binary files?... TODO document
|
|
use_o_as_suffix: bool
|
|
# the value of the $gp register to correctly calculate offset to %gp_rel relocs
|
|
gp: Optional[int]
|
|
|
|
# Paths
|
|
asset_path: Path
|
|
# Determines the path to the symbol addresses file(s)
|
|
# A symbol_addrs file is to be updated/curated manually and contains addresses of symbols
|
|
# as well as optional metadata such as rom address, type, and more
|
|
#
|
|
# It's possible to use more than one file by supplying a list instead of a string
|
|
symbol_addrs_paths: List[Path]
|
|
# Determines the path to the project build directory
|
|
build_path: Path
|
|
# Determines the path to the source code directory
|
|
src_path: Path
|
|
# Determines the path to the asm code directory
|
|
asm_path: Path
|
|
# Determines the path to the asm data directory
|
|
data_path: Path
|
|
# Determines the path to the asm nonmatchings directory
|
|
nonmatchings_path: Path
|
|
# Determines the path to the cache file (used when supplied --use-cache via the CLI)
|
|
cache_path: Path
|
|
|
|
# Determines whether to create an automatically-generated undefined functions file
|
|
# this file stores all functions that are referenced in the code but are not defined as seen by splat
|
|
create_undefined_funcs_auto: bool
|
|
# Determines the path to the undefined_funcs_auto file
|
|
undefined_funcs_auto_path: Path
|
|
|
|
# Determines whether to create an automatically-generated undefined symbols file
|
|
# this file stores all symbols that are referenced in the code but are not defined as seen by splat
|
|
create_undefined_syms_auto: bool
|
|
# Determines the path to the undefined_symbols_auto file
|
|
undefined_syms_auto_path: Path
|
|
|
|
# Determines the path in which to search for custom splat extensions
|
|
extensions_path: Optional[Path]
|
|
|
|
# Determines the path to library files that are to be linked into the target binary
|
|
lib_path: Path
|
|
|
|
# TODO document
|
|
elf_section_list_path: Optional[Path]
|
|
|
|
# Linker script
|
|
# Determines the default subalign value to be specified in the generated linker script
|
|
subalign: int
|
|
# The following option determines whether to automatically configure the linker script to link against
|
|
# specified sections for all "base" (asm/c) files when the yaml doesn't have manual configurations
|
|
# for these sections.
|
|
auto_all_sections: List[str]
|
|
# Determines the desired path to the linker script that splat will generate
|
|
ld_script_path: Path
|
|
# Determines the desired path to the linker symbol header,
|
|
# which exposes externed definitions for all segment ram/rom start/end locations
|
|
ld_symbol_header_path: Optional[Path]
|
|
# Determines whether to add a discard section to the linker script
|
|
ld_discard_section: bool
|
|
# Determines the list of section labels that are to be added to the linker script
|
|
ld_section_labels: List[str]
|
|
|
|
################################################################################
|
|
# C file options
|
|
################################################################################
|
|
# Determines whether to create new c files if they don't exist
|
|
create_c_files: bool
|
|
# Determines whether to "auto-decompile" empty functions
|
|
auto_decompile_empty_functions: bool
|
|
# Determines whether to detect matched/unmatched functions in existing c files
|
|
# so we can avoid creating .s files for already-decompiled functions
|
|
do_c_func_detection: bool
|
|
# Determines the newline char(s) to be used in c files
|
|
c_newline: str
|
|
|
|
################################################################################
|
|
# (Dis)assembly-related options
|
|
################################################################################
|
|
# The following options determine the format that symbols should be named by default
|
|
symbol_name_format: str
|
|
# Same as above but for symbols with no rom address
|
|
symbol_name_format_no_rom: str
|
|
# Determines whether to detect and hint to the user about likely file splits when disassembling
|
|
find_file_boundaries: bool
|
|
# Determines whether to attempt to automatically migrate rodata into functions
|
|
# (only works in certain circumstances)
|
|
migrate_rodata_to_functions: bool
|
|
# Determines the header to be used in every asm file that's included from c files
|
|
asm_inc_header: str
|
|
# Determines the macro used to declare functions in asm files
|
|
asm_function_macro: str
|
|
# Determines the macro used to declare data symbols in asm files
|
|
asm_data_macro: str
|
|
# Determines the macro used at the end of a function, such as endlabel or .end
|
|
asm_end_label: str
|
|
# Determines the number of characters to left align before the TODO finish documenting
|
|
mnemonic_ljust: int
|
|
# Determines whether to pad the rom address
|
|
rom_address_padding: bool
|
|
# Determines which ABI names to use for general purpose registers
|
|
mips_abi_gpr: str
|
|
# Determines which ABI names to use for floating point registers
|
|
# Valid values: 'numeric', 'o32', 'n32', 'n64'
|
|
# o32 is highly recommended, as it provides logically named registers for floating point instructions
|
|
# For more info, see https://gist.github.com/EllipticEllipsis/27eef11205c7a59d8ea85632bc49224d
|
|
mips_abi_float_regs: str
|
|
# Determines whether to ad ".set gp=64 to asm/hasm files"
|
|
add_set_gp_64: bool
|
|
# Generate .asmproc.d dependency files for each C file which still reference functions in assembly files
|
|
create_asm_dependencies: bool
|
|
# Global option for rodata string encoding. This can be overriden per segment
|
|
string_encoding: Optional[str]
|
|
|
|
################################################################################
|
|
# N64-specific options
|
|
################################################################################
|
|
# Determines the encoding of the header
|
|
header_encoding: str
|
|
# Determines the type gfx ucode (used by gfx segments)
|
|
# Valid options are ['f3d', 'f3db', 'f3dex', 'f3dexb', 'f3dex2']
|
|
gfx_ucode: str
|
|
# Use named libultra symbols by default. Those will need to be added to a linker script manually by the user
|
|
libultra_symbols: bool
|
|
# Use named hardware register symbols by default. Those will need to be added to a linker script manually by the user
|
|
hardware_regs: bool
|
|
|
|
################################################################################
|
|
# Gamecube-specific options
|
|
################################################################################
|
|
# Path where the iso's filesystem will be extracted to
|
|
filesystem_path: Path
|
|
|
|
################################################################################
|
|
# Compiler-specific options
|
|
################################################################################
|
|
# Determines whether to use a legacy INCLUDE_ASM macro format in c files
|
|
# only applies to GCC/SN64
|
|
use_legacy_include_asm: bool
|
|
|
|
# Returns whether the given mode is currently enabled
|
|
def is_mode_active(self, mode: str) -> bool:
|
|
return mode in self.modes or "all" in self.modes
|
|
|
|
|
|
opts: SplatOpts
|
|
|
|
|
|
def parse_yaml(
|
|
yaml: Dict,
|
|
basename: str,
|
|
config_paths: List[str],
|
|
modes: List[str],
|
|
verbose: bool = False,
|
|
) -> SplatOpts:
|
|
T = TypeVar("T")
|
|
|
|
def yaml_as_type(value: object, t: Type[T]) -> T:
|
|
if isinstance(value, t):
|
|
return value
|
|
raise ValueError(f"Expected {t}, got {type(value)}")
|
|
|
|
def parse_opt(
|
|
yaml: Mapping[str, object],
|
|
opt: str,
|
|
t: Type[T],
|
|
default: Optional[T] = None,
|
|
) -> T:
|
|
value = yaml.get(opt)
|
|
if isinstance(value, t):
|
|
# Fast path
|
|
return value
|
|
if value is None and opt not in yaml:
|
|
if default is not None:
|
|
return default
|
|
raise ValueError(f"Missing required option {opt}")
|
|
return yaml_as_type(value, t)
|
|
|
|
def parse_optional_opt(
|
|
yaml: Mapping[str, object],
|
|
opt: str,
|
|
t: Type[T],
|
|
default: Optional[T] = None,
|
|
) -> Optional[T]:
|
|
value = yaml.get(opt)
|
|
if isinstance(value, t):
|
|
# Fast path
|
|
return value
|
|
if value is None and opt not in yaml:
|
|
return default
|
|
return yaml_as_type(value, t)
|
|
|
|
def parse_opt_within(
|
|
yaml: Mapping[str, object],
|
|
opt: str,
|
|
t: Type[T],
|
|
within: List[T],
|
|
default: Optional[T] = None,
|
|
) -> T:
|
|
value = parse_opt(yaml, opt, t, default)
|
|
if value not in within:
|
|
raise ValueError(f"Invalid value for {opt}: {value}")
|
|
return value
|
|
|
|
def parse_path(
|
|
yaml: Mapping[str, object], opt: str, default: Optional[str] = None
|
|
) -> Path:
|
|
value = parse_opt(yaml, opt, str, default)
|
|
return Path(value)
|
|
|
|
def parse_optional_path(yaml: Mapping[str, object], opt: str, base_path: Path):
|
|
value = yaml.get(opt)
|
|
if value is None:
|
|
return None
|
|
if not isinstance(value, str):
|
|
raise ValueError(f"Expected str, got {type(value)}")
|
|
return base_path / Path(value)
|
|
|
|
def parse_symbol_addrs_paths(yaml: Mapping[str, object]) -> List[Path]:
|
|
paths = yaml.get("symbol_addrs_path", "symbol_addrs.txt")
|
|
|
|
if isinstance(paths, str):
|
|
return [base_path / paths]
|
|
elif isinstance(paths, list):
|
|
return [base_path / path for path in paths]
|
|
else:
|
|
raise ValueError(f"Expected str or list, got {type(paths)}")
|
|
|
|
platform = parse_opt_within(yaml, "platform", str, ["n64", "psx", "gc"])
|
|
comp = compiler.for_name(parse_opt(yaml, "compiler", str, "IDO"))
|
|
|
|
base_path = Path(config_paths[0]).parent / parse_opt(yaml, "base_path", str)
|
|
asm_path: Path = base_path / parse_path(yaml, "asm_path", "asm")
|
|
|
|
return SplatOpts(
|
|
verbose=verbose,
|
|
dump_symbols=parse_opt(yaml, "dump_symbols", bool, False),
|
|
modes=modes,
|
|
base_path=base_path,
|
|
target_path=base_path / parse_path(yaml, "target_path"),
|
|
platform=platform,
|
|
compiler=comp,
|
|
endianness=parse_opt(
|
|
yaml,
|
|
"endianness",
|
|
str,
|
|
"little" if platform.lower() == "psx" else "big",
|
|
),
|
|
section_order=parse_opt(
|
|
yaml, "section_order", list, [".text", ".data", ".rodata", ".bss"]
|
|
),
|
|
generated_c_preamble=parse_opt(
|
|
yaml, "generated_c_preamble", str, '#include "common.h"'
|
|
),
|
|
generated_s_preamble=parse_opt(yaml, "generated_s_preamble", str, ""),
|
|
use_o_as_suffix=parse_opt(yaml, "o_as_suffix", bool, False),
|
|
gp=parse_opt(yaml, "gp_value", int, 0),
|
|
asset_path=base_path / parse_path(yaml, "asset_path", "assets"),
|
|
symbol_addrs_paths=parse_symbol_addrs_paths(yaml),
|
|
build_path=base_path / parse_path(yaml, "build_path", "build"),
|
|
src_path=base_path / parse_path(yaml, "src_path", "src"),
|
|
asm_path=asm_path,
|
|
data_path=asm_path / parse_path(yaml, "data_path", "data"),
|
|
nonmatchings_path=asm_path
|
|
/ parse_path(yaml, "nonmatchings_path", "nonmatchings"),
|
|
cache_path=base_path / parse_path(yaml, "cache_path", ".splache"),
|
|
create_undefined_funcs_auto=parse_opt(
|
|
yaml, "create_undefined_funcs_auto", bool, True
|
|
),
|
|
undefined_funcs_auto_path=base_path
|
|
/ parse_path(yaml, "undefined_funcs_auto_path", "undefined_funcs_auto.txt"),
|
|
create_undefined_syms_auto=parse_opt(
|
|
yaml, "create_undefined_syms_auto", bool, True
|
|
),
|
|
undefined_syms_auto_path=base_path
|
|
/ parse_path(yaml, "undefined_syms_auto_path", "undefined_syms_auto.txt"),
|
|
extensions_path=parse_optional_path(yaml, "extensions_path", base_path),
|
|
lib_path=base_path / parse_path(yaml, "lib_path", "lib"),
|
|
elf_section_list_path=parse_optional_path(
|
|
yaml, "elf_section_list_path", base_path
|
|
),
|
|
subalign=parse_opt(yaml, "subalign", int, 16),
|
|
auto_all_sections=parse_opt(
|
|
yaml, "auto_all_sections", list, [".data", ".rodata", ".bss"]
|
|
),
|
|
ld_script_path=base_path / parse_path(yaml, "ld_script_path", f"{basename}.ld"),
|
|
ld_symbol_header_path=parse_optional_path(
|
|
yaml, "ld_symbol_header_path", base_path
|
|
),
|
|
ld_discard_section=parse_opt(yaml, "ld_discard_section", bool, True),
|
|
ld_section_labels=parse_opt(
|
|
yaml,
|
|
"ld_section_labels",
|
|
list,
|
|
[".text", ".data", ".rodata", ".bss"],
|
|
),
|
|
create_c_files=parse_opt(yaml, "create_c_files", bool, True),
|
|
auto_decompile_empty_functions=parse_opt(
|
|
yaml, "auto_decompile_empty_functions", bool, True
|
|
),
|
|
do_c_func_detection=parse_opt(yaml, "do_c_func_detection", bool, True),
|
|
c_newline=parse_opt(yaml, "c_newline", str, comp.c_newline),
|
|
symbol_name_format=parse_opt(yaml, "symbol_name_format", str, "$VRAM"),
|
|
symbol_name_format_no_rom=parse_opt(
|
|
yaml, "symbol_name_format_no_rom", str, "$VRAM_$SEG"
|
|
),
|
|
find_file_boundaries=parse_opt(yaml, "find_file_boundaries", bool, True),
|
|
migrate_rodata_to_functions=parse_opt(
|
|
yaml, "migrate_rodata_to_functions", bool, True
|
|
),
|
|
asm_inc_header=parse_opt(yaml, "asm_inc_header", str, comp.asm_inc_header),
|
|
asm_function_macro=parse_opt(
|
|
yaml, "asm_function_macro", str, comp.asm_function_macro
|
|
),
|
|
asm_data_macro=parse_opt(yaml, "asm_data_macro", str, comp.asm_data_macro),
|
|
asm_end_label=parse_opt(yaml, "asm_end_label", str, comp.asm_end_label),
|
|
mnemonic_ljust=parse_opt(yaml, "mnemonic_ljust", int, 11),
|
|
rom_address_padding=parse_opt(yaml, "rom_address_padding", bool, False),
|
|
mips_abi_gpr=parse_opt_within(
|
|
yaml,
|
|
"mips_abi_gpr",
|
|
str,
|
|
["numeric", "o32", "n32", "n64"],
|
|
"o32",
|
|
),
|
|
mips_abi_float_regs=parse_opt_within(
|
|
yaml,
|
|
"mips_abi_float_regs",
|
|
str,
|
|
["numeric", "o32", "n32", "n64"],
|
|
"numeric",
|
|
),
|
|
add_set_gp_64=parse_opt(yaml, "add_set_gp_64", bool, True),
|
|
create_asm_dependencies=parse_opt(yaml, "create_asm_dependencies", bool, False),
|
|
string_encoding=parse_optional_opt(yaml, "string_encoding", str, None),
|
|
header_encoding=parse_opt(yaml, "header_encoding", str, "ASCII"),
|
|
gfx_ucode=parse_opt_within(
|
|
yaml,
|
|
"gfx_ucode",
|
|
str,
|
|
["f3d", "f3db", "f3dex", "f3dexb", "f3dex2"],
|
|
"f3dex2",
|
|
),
|
|
libultra_symbols=parse_opt(yaml, "libultra_symbols", bool, False),
|
|
hardware_regs=parse_opt(yaml, "hardware_regs", bool, False),
|
|
use_legacy_include_asm=parse_opt(yaml, "use_legacy_include_asm", bool, True),
|
|
filesystem_path=parse_optional_path(yaml, "filesystem_path", base_path),
|
|
)
|
|
|
|
|
|
def initialize(
|
|
config: Dict,
|
|
config_paths: List[str],
|
|
modes: Optional[List[str]] = None,
|
|
verbose=False,
|
|
):
|
|
global opts
|
|
|
|
if not modes:
|
|
modes = ["all"]
|
|
|
|
opts = parse_yaml(
|
|
config["options"], config["options"]["basename"], config_paths, modes, verbose
|
|
)
|