diff options
| author | emilybrooks <emilybrooksemilybrooks@gmail.com> | 2024-01-26 16:19:49 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-01-26 16:19:49 -0700 |
| commit | 1d9c5c49c5d5ee1fa0e9d53d02bfd732d9d863fd (patch) | |
| tree | e2068628ed4f9309c9623a09717751249e573784 /tools | |
| parent | cc4355f6adae67c2f90b78659dc6bcf13910cc86 (diff) | |
Second half of tool objects (#136)
* tol_keitai_1
* kenjyu
* evw_anime data extraction works
* forgot to remove this
* added comments for objects
* fixes
* scroll colprim colenv now extract as arrays
* forgot commas
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/splat_ext/af_gfx.py | 281 | ||||
| -rw-r--r-- | tools/splat_ext/af_ia8.py | 22 | ||||
| -rw-r--r-- | tools/splat_ext/ckf_c.py | 4 | ||||
| -rw-r--r-- | tools/splat_ext/ckf_kn.py | 4 | ||||
| -rw-r--r-- | tools/splat_ext/evw_animeptn.py | 38 | ||||
| -rw-r--r-- | tools/splat_ext/evw_colenv.py | 43 | ||||
| -rw-r--r-- | tools/splat_ext/evw_colprim.py | 43 | ||||
| -rw-r--r-- | tools/splat_ext/evw_colreg.py | 57 | ||||
| -rw-r--r-- | tools/splat_ext/evw_data.py | 49 | ||||
| -rw-r--r-- | tools/splat_ext/evw_scroll.py | 43 | ||||
| -rw-r--r-- | tools/splat_ext/evw_texanime.py | 57 | ||||
| -rw-r--r-- | tools/splat_ext/evw_textable.py | 47 |
12 files changed, 684 insertions, 4 deletions
diff --git a/tools/splat_ext/af_gfx.py b/tools/splat_ext/af_gfx.py new file mode 100644 index 0000000..30112db --- /dev/null +++ b/tools/splat_ext/af_gfx.py @@ -0,0 +1,281 @@ +""" +N64 f3dex display list splitter +Dumps out Gfx[] as a .inc.c file. +""" + +import re +from typing import Optional + +from pathlib import Path + +from pygfxd import ( + gfxd_buffer_to_string, + gfxd_cimg_callback, + gfxd_dl_callback, + gfxd_endian, + gfxd_execute, + gfxd_input_buffer, + gfxd_light_callback, + gfxd_lookat_callback, + gfxd_macro_dflt, + gfxd_macro_fn, + gfxd_mtx_callback, + gfxd_output_buffer, + gfxd_printf, + gfxd_puts, + gfxd_target, + gfxd_timg_callback, + gfxd_tlut_callback, + gfxd_vp_callback, + gfxd_vtx_callback, + gfxd_zimg_callback, + GfxdEndian, + gfxd_f3d, + gfxd_f3db, + gfxd_f3dex, + gfxd_f3dexb, + gfxd_f3dex2, +) +from ..segment import Segment + +from ...util import log, options +from ...util.log import error + +from ..common.codesubsegment import CommonSegCodeSubsegment + +from ...util import symbols + +LIGHTS_RE = re.compile(r"\*\(Lightsn \*\)0x[0-9A-F]{8}") + + +class N64SegAf_gfx(CommonSegCodeSubsegment): + def __init__( + self, + rom_start: Optional[int], + rom_end: Optional[int], + type: str, + name: str, + vram_start: Optional[int], + args: list, + yaml, + ): + super().__init__( + rom_start, + rom_end, + type, + name, + vram_start, + args=args, + yaml=yaml, + ) + self.file_text = None + self.data_only = isinstance(yaml, dict) and yaml.get("data_only", False) + self.in_segment = not isinstance(yaml, dict) or yaml.get("in_segment", True) + + def format_sym_name(self, sym) -> str: + return sym.name + + def get_linker_section(self) -> str: + return ".data" + + def out_path(self) -> Path: + return options.opts.asset_path / self.dir / f"{self.name}.gfx.inc.c" + + def scan(self, rom_bytes: bytes): + self.file_text = self.disassemble_data(rom_bytes) + + def get_gfxd_target(self): + opt = options.opts.gfx_ucode + + if opt == "f3d": + return gfxd_f3d + elif opt == "f3db": + return gfxd_f3db + elif opt == "f3dex": + return gfxd_f3dex + elif opt == "f3dexb": + return gfxd_f3dexb + elif opt == "f3dex2": + return gfxd_f3dex2 + else: + log.error(f"Unknown target {opt}") + + def tlut_handler(self, addr, idx, count): + sym = self.get_symbol(addr=addr) + if sym: + gfxd_printf(self.format_sym_name(sym)) + else: + gfxd_printf(f"0x{addr:08X}") + return 1 + + def timg_handler(self, addr, fmt, size, width, height, pal): + sym = self.get_symbol(addr=addr) + if sym: + gfxd_printf(self.format_sym_name(sym)) + else: + gfxd_printf(f"0x{addr:08X}") + return 1 + + def cimg_handler(self, addr, fmt, size, width): + sym = self.get_symbol(addr=addr) + if sym: + gfxd_printf(self.format_sym_name(sym)) + else: + gfxd_printf(f"0x{addr:08X}") + return 1 + + def zimg_handler(self, addr): + sym = self.get_symbol(addr=addr) + if sym: + gfxd_printf(self.format_sym_name(sym)) + else: + gfxd_printf(f"0x{addr:08X}") + return 1 + + def dl_handler(self, addr): + sym = self.get_symbol(addr=addr) + if sym: + gfxd_printf(self.format_sym_name(sym)) + else: + gfxd_printf(f"0x{addr:08X}") + return 1 + + def mtx_handler(self, addr): + sym = self.get_symbol(addr=addr) + if sym: + gfxd_printf(f"&{self.format_sym_name(sym)}") + else: + gfxd_printf(f"0x{addr:08X}") + return 1 + + def lookat_handler(self, addr, count): + sym = self.get_symbol(addr=addr) + if sym: + gfxd_printf(self.format_sym_name(sym)) + else: + gfxd_printf(f"0x{addr:08X}") + return 1 + + def light_handler(self, addr, count): + sym = self.get_symbol(addr=addr) + if sym: + gfxd_printf(self.format_sym_name(sym)) + else: + gfxd_printf(f"0x{addr:08X}") + return 1 + + def vtx_handler(self, addr, count): + # this will grab the symbol for addresses between the start address and end address of the vtx array + sym = self.get_symbol( + addr=addr, + in_segment=self.in_segment, + type="Vtx", + reference=True, + search_ranges=True, + ) + if sym: + index = int((addr - sym.vram_start) / 0x10) + gfxd_printf(f"&{self.format_sym_name(sym)}[{index}]") + else: + gfxd_printf(f"0x{addr:08X}") + return 1 + + def vp_handler(self, addr): + sym = self.get_symbol(addr=addr) + if sym: + gfxd_printf(f"&{self.format_sym_name(sym)}") + else: + gfxd_printf(f"0x{addr:08X}") + return 1 + + def macro_fn(self): + gfxd_puts(" ") + gfxd_macro_dflt() + gfxd_puts(",\n") + return 0 + + def disassemble_data(self, rom_bytes): + assert isinstance(self.rom_start, int) + assert isinstance(self.rom_end, int) + assert isinstance(self.vram_start, int) + + gfx_data = rom_bytes[self.rom_start : self.rom_end] + segment_length = len(gfx_data) + if (segment_length) % 8 != 0: + error( + f"Error: gfx segment {self.name} length ({segment_length}) is not a multiple of 8!" + ) + + out_str = "" if self.data_only else options.opts.generated_c_preamble + "\n\n" + + sym = self.create_symbol( + addr=self.vram_start, in_segment=True, type="data", define=True + ) + + gfxd_input_buffer(gfx_data) + + # TODO terrible guess at the size we'll need - improve this + outb = bytes([0] * segment_length * 100) + outbuf = gfxd_output_buffer(outb, len(outb)) + + gfxd_target(self.get_gfxd_target()) + gfxd_endian( + GfxdEndian.big if options.opts.endianness == "big" else GfxdEndian.little, 4 + ) + + # Callbacks + gfxd_macro_fn(self.macro_fn) + + gfxd_tlut_callback(self.tlut_handler) + gfxd_timg_callback(self.timg_handler) + gfxd_cimg_callback(self.cimg_handler) + gfxd_zimg_callback(self.zimg_handler) + gfxd_dl_callback(self.dl_handler) + gfxd_mtx_callback(self.mtx_handler) + gfxd_lookat_callback(self.lookat_handler) + gfxd_light_callback(self.light_handler) + # gfxd_seg_callback ? + gfxd_vtx_callback(self.vtx_handler) + gfxd_vp_callback(self.vp_handler) + # gfxd_uctext_callback ? + # gfxd_ucdata_callback ? + # gfxd_dram_callback ? + + gfxd_execute() + + if self.data_only: + out_str += gfxd_buffer_to_string(outbuf) + else: + out_str += "Gfx " + self.format_sym_name(sym) + "[] = {\n" + out_str += gfxd_buffer_to_string(outbuf) + out_str += "};\n" + + # Poor man's light fix until we get my libgfxd PR merged + def light_sub_func(match): + light = match.group(0) + addr = int(light[12:], 0) + sym = self.create_symbol( + addr=addr, in_segment=self.in_segment, type="data", reference=True + ) + return self.format_sym_name(sym) + + out_str = re.sub(LIGHTS_RE, light_sub_func, out_str) + + return out_str + + def split(self, rom_bytes: bytes): + if self.file_text and self.out_path(): + self.out_path().parent.mkdir(parents=True, exist_ok=True) + + with open(self.out_path(), "w", newline="\n") as f: + f.write(self.file_text) + + def should_scan(self) -> bool: + return ( + options.opts.is_mode_active("gfx") + and self.rom_start is not None + and self.rom_end is not None + ) + + def should_split(self) -> bool: + return self.extract and options.opts.is_mode_active("gfx") diff --git a/tools/splat_ext/af_ia8.py b/tools/splat_ext/af_ia8.py new file mode 100644 index 0000000..ce5ffe5 --- /dev/null +++ b/tools/splat_ext/af_ia8.py @@ -0,0 +1,22 @@ +from n64img.image import Image, IA8 +from splat.util import log, options +from splat.segtypes.n64.segment import N64Segment + +class N64SegAf_ia8(N64Segment): + def __init__(self, rom_start, rom_end, type, name, vram_start, args, yaml): + super().__init__(rom_start, rom_end, type, name, vram_start, args=args, yaml=yaml), + + self.width = args[0] + self.height = args[1] + + def scan(self, rom_bytes: bytes): + self.n64img: Image = IA8(b"", 0, 0) + self.n64img.width = self.width + self.n64img.height = self.height + self.n64img.data = rom_bytes[self.rom_start : self.rom_end] + + def split(self, rom_bytes: bytes): + path = options.opts.asset_path / self.dir / f"{self.name}.ia8.png" + path.parent.mkdir(parents=True, exist_ok=True) + + self.n64img.write(path) diff --git a/tools/splat_ext/ckf_c.py b/tools/splat_ext/ckf_c.py index 379a000..32ac409 100644 --- a/tools/splat_ext/ckf_c.py +++ b/tools/splat_ext/ckf_c.py @@ -24,8 +24,8 @@ class N64SegCkf_c(CommonSegCodeSubsegment): lines.append("") lines.append(f"s16 {symbol.name}[{count}] = {{") - for byte in struct.iter_unpack(">h", data): - lines.append(f" {byte[0]},") + for short in struct.iter_unpack(">h", data): + lines.append(f" {short[0]},") if not self.data_only: lines.append("};") diff --git a/tools/splat_ext/ckf_kn.py b/tools/splat_ext/ckf_kn.py index 4efa957..ecbf207 100644 --- a/tools/splat_ext/ckf_kn.py +++ b/tools/splat_ext/ckf_kn.py @@ -26,8 +26,8 @@ class N64SegCkf_kn(CommonSegCodeSubsegment): lines.append("") lines.append(f"s16 {symbol.name}[{count}] = {{") - for byte in struct.iter_unpack(">h", data): - lines.append(f" {byte[0]},") + for short in struct.iter_unpack(">h", data): + lines.append(f" {short[0]},") if not self.data_only: lines.append("};") diff --git a/tools/splat_ext/evw_animeptn.py b/tools/splat_ext/evw_animeptn.py new file mode 100644 index 0000000..e9e0727 --- /dev/null +++ b/tools/splat_ext/evw_animeptn.py @@ -0,0 +1,38 @@ +import struct +from typing import Optional +from splat.util import options, log +from splat.segtypes.common.codesubsegment import CommonSegCodeSubsegment + +class N64SegEvw_animeptn(CommonSegCodeSubsegment): + def __init__(self, rom_start, rom_end, type, name, vram_start, args, yaml): + super().__init__(rom_start, rom_end, type, name, vram_start, args=args, yaml=yaml), + + self.file_text: Optional[str] = None + self.data_only = isinstance(yaml, dict) and yaml.get("data_only", False) + + def scan(self, rom_bytes: bytes): + data = rom_bytes[self.rom_start : self.rom_end] + symbol = self.create_symbol(addr=self.vram_start, in_segment=True, type="data", define=True) + lines = [] + + if not self.data_only: + lines.append(options.opts.generated_c_preamble) + lines.append("") + lines.append(f"u8 {symbol.name}[{len(data)}] = {{") + + for byte in struct.iter_unpack(">B", data): + lines.append(f" {byte[0]},") + + if not self.data_only: + lines.append("};") + + lines.append("") + self.file_text = "\n".join(lines) + + def split(self, rom_bytes: bytes): + path = options.opts.asset_path / self.dir / f"{self.name}.inc.c" + + if self.file_text and path: + path.parent.mkdir(parents=True, exist_ok=True) + with open(path, "w", newline="\n") as f: + f.write(self.file_text) diff --git a/tools/splat_ext/evw_colenv.py b/tools/splat_ext/evw_colenv.py new file mode 100644 index 0000000..3d0c345 --- /dev/null +++ b/tools/splat_ext/evw_colenv.py @@ -0,0 +1,43 @@ +import struct +from typing import Optional +from splat.util import options, log +from splat.segtypes.common.codesubsegment import CommonSegCodeSubsegment + +class N64SegEvw_colenv(CommonSegCodeSubsegment): + def __init__(self, rom_start, rom_end, type, name, vram_start, args, yaml): + super().__init__(rom_start, rom_end, type, name, vram_start, args=args, yaml=yaml), + + self.file_text: Optional[str] = None + self.data_only = isinstance(yaml, dict) and yaml.get("data_only", False) + + def scan(self, rom_bytes: bytes): + data = rom_bytes[self.rom_start : self.rom_end] + symbol = self.create_symbol(addr=self.vram_start, in_segment=True, type="data", define=True) + count = len(data) // 4 + lines = [] + + if (len(data)) % 4 != 0: + log.error(f"Error: evw_colenv segment {self.name} length ({len(data)}) is not a multiple of 4!") + + if not self.data_only: + lines.append(options.opts.generated_c_preamble) + lines.append("") + lines.append(f"EvwAnimeColEnv {symbol.name}[{count}] = {{") + + for EvwAnimeColEnv in struct.iter_unpack(">bbbb", data): + r, g, b, a = EvwAnimeColEnv + lines.append(f" {{ {r}, {g}, {b}, {a} }},") + + if not self.data_only: + lines.append("};") + + lines.append("") + self.file_text = "\n".join(lines) + + def split(self, rom_bytes: bytes): + path = options.opts.asset_path / self.dir / f"{self.name}.inc.c" + + if self.file_text and path: + path.parent.mkdir(parents=True, exist_ok=True) + with open(path, "w", newline="\n") as f: + f.write(self.file_text) diff --git a/tools/splat_ext/evw_colprim.py b/tools/splat_ext/evw_colprim.py new file mode 100644 index 0000000..a8c2112 --- /dev/null +++ b/tools/splat_ext/evw_colprim.py @@ -0,0 +1,43 @@ +import struct +from typing import Optional +from splat.util import options, log +from splat.segtypes.common.codesubsegment import CommonSegCodeSubsegment + +class N64SegEvw_colprim(CommonSegCodeSubsegment): + def __init__(self, rom_start, rom_end, type, name, vram_start, args, yaml): + super().__init__(rom_start, rom_end, type, name, vram_start, args=args, yaml=yaml), + + self.file_text: Optional[str] = None + self.data_only = isinstance(yaml, dict) and yaml.get("data_only", False) + + def scan(self, rom_bytes: bytes): + data = rom_bytes[self.rom_start : self.rom_end] + symbol = self.create_symbol(addr=self.vram_start, in_segment=True, type="data", define=True) + count = len(data) // 5 + lines = [] + + if (len(data)) % 5 != 0: + log.error(f"Error: evw_colprim segment {self.name} length ({len(data)}) is not a multiple of 5!") + + if not self.data_only: + lines.append(options.opts.generated_c_preamble) + lines.append("") + lines.append(f"EvwAnimeColPrim {symbol.name}[{count}] = {{") + + for EvwAnimeColPrim in struct.iter_unpack(">bbbbb", data): + r, g, b, a, l = EvwAnimeColPrim + lines.append(f" {{ {r}, {g}, {b}, {a}, {l} }},") + + if not self.data_only: + lines.append("};") + + lines.append("") + self.file_text = "\n".join(lines) + + def split(self, rom_bytes: bytes): + path = options.opts.asset_path / self.dir / f"{self.name}.inc.c" + + if self.file_text and path: + path.parent.mkdir(parents=True, exist_ok=True) + with open(path, "w", newline="\n") as f: + f.write(self.file_text) diff --git a/tools/splat_ext/evw_colreg.py b/tools/splat_ext/evw_colreg.py new file mode 100644 index 0000000..2352bc6 --- /dev/null +++ b/tools/splat_ext/evw_colreg.py @@ -0,0 +1,57 @@ +import struct +from typing import Optional +from splat.util import options, log +from splat.segtypes.common.codesubsegment import CommonSegCodeSubsegment + +class N64SegEvw_colreg(CommonSegCodeSubsegment): + def __init__(self, rom_start, rom_end, type, name, vram_start, args, yaml): + super().__init__(rom_start, rom_end, type, name, vram_start, args=args, yaml=yaml), + + self.file_text: Optional[str] = None + self.data_only = isinstance(yaml, dict) and yaml.get("data_only", False) + + def scan(self, rom_bytes: bytes): + data = rom_bytes[self.rom_start : self.rom_end] + symbol = self.create_symbol(addr=self.vram_start, in_segment=True, type="data", define=True) + lines = [] + + if (len(data)) != 16: + log.error(f"Error: evw_colreg segment {self.name} length ({len(data)}) is not 16 bytes!") + + if not self.data_only: + lines.append(options.opts.generated_c_preamble) + lines.append("\n") + lines.append(f"EvwAnimeColReg {symbol.name} = ") + + frameCount, keyframeCount, prim, env, keyframes = struct.unpack(">HHIII", data) + + if prim: + prim_symbol = self.get_symbol(addr=prim).name + else: + prim_symbol = "NULL" + + if env: + env_symbol = self.get_symbol(addr=env).name + else: + env_symbol = "NULL" + + if keyframes: + keyframes_symbol = self.get_symbol(addr=keyframes).name + else: + keyframes_symbol = "NULL" + + lines.append(f"{{ {frameCount}, {keyframeCount}, {prim_symbol}, {env_symbol}, {keyframes_symbol} }}") + + if not self.data_only: + lines.append(";") + + lines.append("\n") + self.file_text = "".join(lines) + + def split(self, rom_bytes: bytes): + path = options.opts.asset_path / self.dir / f"{self.name}.inc.c" + + if self.file_text and path: + path.parent.mkdir(parents=True, exist_ok=True) + with open(path, "w", newline="\n") as f: + f.write(self.file_text) diff --git a/tools/splat_ext/evw_data.py b/tools/splat_ext/evw_data.py new file mode 100644 index 0000000..f8f7642 --- /dev/null +++ b/tools/splat_ext/evw_data.py @@ -0,0 +1,49 @@ +import struct +from typing import Optional +from splat.util import options, log +from splat.segtypes.common.codesubsegment import CommonSegCodeSubsegment + +class N64SegEvw_data(CommonSegCodeSubsegment): + def __init__(self, rom_start, rom_end, type, name, vram_start, args, yaml): + super().__init__(rom_start, rom_end, type, name, vram_start, args=args, yaml=yaml), + + self.file_text: Optional[str] = None + self.data_only = isinstance(yaml, dict) and yaml.get("data_only", False) + + def scan(self, rom_bytes: bytes): + data = rom_bytes[self.rom_start : self.rom_end] + symbol = self.create_symbol(addr=self.vram_start, in_segment=True, type="data", define=True) + count = len(data) // 8 + lines = [] + + if (len(data)) % 8 != 0: + log.error(f"Error: evw_data segment {self.name} length ({len(data)}) is not a multiple of 8!") + + if not self.data_only: + lines.append(options.opts.generated_c_preamble) + lines.append("") + lines.append(f"EvwAnimeData {symbol.name}[{count}] = {{") + + for evwAnimeData in struct.iter_unpack(">bxhI", data): + segment, type, dataPtr = evwAnimeData + + if dataPtr: + data_ptr_symbol = self.get_symbol(addr=dataPtr).name + else: + data_ptr_symbol = "NULL" + + lines.append(f" {{ {segment}, {type}, &{data_ptr_symbol} }},") + + if not self.data_only: + lines.append("};") + + lines.append("") + self.file_text = "\n".join(lines) + + def split(self, rom_bytes: bytes): + path = options.opts.asset_path / self.dir / f"{self.name}.inc.c" + + if self.file_text and path: + path.parent.mkdir(parents=True, exist_ok=True) + with open(path, "w", newline="\n") as f: + f.write(self.file_text) diff --git a/tools/splat_ext/evw_scroll.py b/tools/splat_ext/evw_scroll.py new file mode 100644 index 0000000..a92bc27 --- /dev/null +++ b/tools/splat_ext/evw_scroll.py @@ -0,0 +1,43 @@ +import struct +from typing import Optional +from splat.util import options, log +from splat.segtypes.common.codesubsegment import CommonSegCodeSubsegment + +class N64SegEvw_scroll(CommonSegCodeSubsegment): + def __init__(self, rom_start, rom_end, type, name, vram_start, args, yaml): + super().__init__(rom_start, rom_end, type, name, vram_start, args=args, yaml=yaml), + + self.file_text: Optional[str] = None + self.data_only = isinstance(yaml, dict) and yaml.get("data_only", False) + + def scan(self, rom_bytes: bytes): + data = rom_bytes[self.rom_start : self.rom_end] + symbol = self.create_symbol(addr=self.vram_start, in_segment=True, type="data", define=True) + count = len(data) // 4 + lines = [] + + if (len(data)) % 4 != 0: + log.error(f"Error: evw_scroll segment {self.name} length ({len(data)}) is not a multiple of 4!") + + if not self.data_only: + lines.append(options.opts.generated_c_preamble) + lines.append("") + lines.append(f"EvwAnimeScroll {symbol.name}[{count}] = {{") + + for EvwAnimeScroll in struct.iter_unpack(">bbBB", data): + x, y, width, height = EvwAnimeScroll + lines.append(f" {{ {x}, {y}, {width}, {height} }},") + + if not self.data_only: + lines.append("};") + + lines.append("") + self.file_text = "\n".join(lines) + + def split(self, rom_bytes: bytes): + path = options.opts.asset_path / self.dir / f"{self.name}.inc.c" + + if self.file_text and path: + path.parent.mkdir(parents=True, exist_ok=True) + with open(path, "w", newline="\n") as f: + f.write(self.file_text) diff --git a/tools/splat_ext/evw_texanime.py b/tools/splat_ext/evw_texanime.py new file mode 100644 index 0000000..8782475 --- /dev/null +++ b/tools/splat_ext/evw_texanime.py @@ -0,0 +1,57 @@ +import struct +from typing import Optional +from splat.util import options, log +from splat.segtypes.common.codesubsegment import CommonSegCodeSubsegment + +class N64SegEvw_texanime(CommonSegCodeSubsegment): + def __init__(self, rom_start, rom_end, type, name, vram_start, args, yaml): + super().__init__(rom_start, rom_end, type, name, vram_start, args=args, yaml=yaml), + + self.file_text: Optional[str] = None + self.data_only = isinstance(yaml, dict) and yaml.get("data_only", False) + + def scan(self, rom_bytes: bytes): + data = rom_bytes[self.rom_start : self.rom_end] + symbol = self.create_symbol(addr=self.vram_start, in_segment=True, type="data", define=True) + lines = [] + + if (len(data)) != 16: + log.error(f"Error: evw_texanime segment {self.name} length ({len(data)}) is not 16 bytes!") + + if not self.data_only: + lines.append(options.opts.generated_c_preamble) + lines.append("\n") + lines.append(f"EvwAnimeTexAnime {symbol.name} = ") + + frameCount, keyframeCount, textureTable, animationPattern, keyframes = struct.unpack(">HHIII", data) + + if textureTable: + texture_table_symbol = self.get_symbol(addr=textureTable).name + else: + texture_table_symbol = "NULL" + + if animationPattern: + animation_pattern_symbol = self.get_symbol(addr=animationPattern).name + else: + animation_pattern_symbol = "NULL" + + if keyframes: + keyframes_symbol = self.get_symbol(addr=keyframes).name + else: + keyframes_symbol = "NULL" + + lines.append(f"{{ {frameCount}, {keyframeCount}, &{texture_table_symbol}, {animation_pattern_symbol}, {keyframes_symbol} }}") + + if not self.data_only: + lines.append(";") + + lines.append("\n") + self.file_text = "".join(lines) + + def split(self, rom_bytes: bytes): + path = options.opts.asset_path / self.dir / f"{self.name}.inc.c" + + if self.file_text and path: + path.parent.mkdir(parents=True, exist_ok=True) + with open(path, "w", newline="\n") as f: + f.write(self.file_text) diff --git a/tools/splat_ext/evw_textable.py b/tools/splat_ext/evw_textable.py new file mode 100644 index 0000000..4ceb0d0 --- /dev/null +++ b/tools/splat_ext/evw_textable.py @@ -0,0 +1,47 @@ +import struct +from typing import Optional +from splat.util import options, log +from splat.segtypes.common.codesubsegment import CommonSegCodeSubsegment + +class N64SegEvw_textable(CommonSegCodeSubsegment): + def __init__(self, rom_start, rom_end, type, name, vram_start, args, yaml): + super().__init__(rom_start, rom_end, type, name, vram_start, args=args, yaml=yaml), + + self.file_text: Optional[str] = None + self.data_only = isinstance(yaml, dict) and yaml.get("data_only", False) + + def scan(self, rom_bytes: bytes): + data = rom_bytes[self.rom_start : self.rom_end] + symbol = self.create_symbol(addr=self.vram_start, in_segment=True, type="data", define=True) + lines = [] + + if (len(data)) != 4: + log.error(f"Error: evw_textable segment {self.name} length ({len(data)}) is not 4 bytes!") + + if not self.data_only: + lines.append(options.opts.generated_c_preamble) + lines.append("\n") + lines.append(f"void* {symbol.name} = ") + + textureTable = struct.unpack(">I", data) + + if textureTable: + texture_table_symbol = self.get_symbol(addr=textureTable[0]).name + else: + texture_table_symbol = "NULL" + + lines.append(f"{texture_table_symbol}") + + if not self.data_only: + lines.append(";") + + lines.append("\n") + self.file_text = "".join(lines) + + def split(self, rom_bytes: bytes): + path = options.opts.asset_path / self.dir / f"{self.name}.inc.c" + + if self.file_text and path: + path.parent.mkdir(parents=True, exist_ok=True) + with open(path, "w", newline="\n") as f: + f.write(self.file_text) |
