papermario/tools/splat/util/options.py
HailSanta fab07c1c8c
area_dro (#821)
* 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>
2022-10-15 12:04:53 -04:00

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
)