summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authoremilybrooks <emilybrooksemilybrooks@gmail.com>2024-01-26 16:19:49 -0700
committerGitHub <noreply@github.com>2024-01-26 16:19:49 -0700
commit1d9c5c49c5d5ee1fa0e9d53d02bfd732d9d863fd (patch)
treee2068628ed4f9309c9623a09717751249e573784 /tools
parentcc4355f6adae67c2f90b78659dc6bcf13910cc86 (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.py281
-rw-r--r--tools/splat_ext/af_ia8.py22
-rw-r--r--tools/splat_ext/ckf_c.py4
-rw-r--r--tools/splat_ext/ckf_kn.py4
-rw-r--r--tools/splat_ext/evw_animeptn.py38
-rw-r--r--tools/splat_ext/evw_colenv.py43
-rw-r--r--tools/splat_ext/evw_colprim.py43
-rw-r--r--tools/splat_ext/evw_colreg.py57
-rw-r--r--tools/splat_ext/evw_data.py49
-rw-r--r--tools/splat_ext/evw_scroll.py43
-rw-r--r--tools/splat_ext/evw_texanime.py57
-rw-r--r--tools/splat_ext/evw_textable.py47
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)