papermario/tools/splat_ext/pm_map_data.py
Ethan Roseman d31e0b8936
Engine cod (#738)
* a couplez

* func & data cleaning

* sig

* move data and fix nonmatching

* Finish 17D6A0

* Don't use backticks in progress.py

* func_8025995C and clean

* 2 sprite funcs and cleanup

* 3 sprite funcs

* gather_magic_main

* 3 sprite funcs

* more sprite work and cleanup

* sprite funcs

* more clean

* UnkFloatFunc005

* warnings

* cleaning and os stuff

* 2 more funcs + warnings

* yaml work, move map data metadata to separate yml file

* apply_transform_to_children

* cleaningz

* PR comment
2022-07-08 21:57:08 +09:00

187 lines
6.8 KiB
Python

import os
from pathlib import Path
from segtypes.n64.segment import N64Segment
from segtypes.n64.ia8 import N64SegIa8
from segtypes.n64.rgba32 import N64SegRgba32
from segtypes.n64.ci4 import N64SegCi4
from util.n64 import Yay0decompress
from util.color import unpack_color
from util.iter import iter_in_groups
from util import options
import png
import yaml as yaml_loader
script_dir = Path(os.path.dirname(os.path.realpath(__file__)))
def decode_null_terminated_ascii(data):
length = 0
for byte in data:
if byte == 0:
break
length += 1
return data[:length].decode('ascii')
def parse_palette(data):
palette = []
for a, b in iter_in_groups(data, 2):
palette.append(unpack_color([a, b]))
return palette
def add_file_ext(name: str) -> str:
if name.startswith("party_"):
return name + ".png"
elif name.endswith("_hit") or name.endswith("_shape"):
return name + ".bin" # TODO: xml
elif name.endswith("_tex"):
return name + ".bin" # TODO: texture archive
elif name.endswith("_bg"):
return name + ".png"
else:
return name + ".bin"
class N64SegPm_map_data(N64Segment):
def __init__(
self,
rom_start,
rom_end,
type,
name,
vram_start,
extract,
given_subalign,
exclusive_ram_id,
given_dir,
symbol_name_format,
symbol_name_format_no_rom,
args,
yaml,
):
super().__init__(
rom_start,
rom_end,
type,
name,
vram_start,
extract,
given_subalign,
exclusive_ram_id,
given_dir,
symbol_name_format=symbol_name_format,
symbol_name_format_no_rom=symbol_name_format_no_rom,
args=args,
yaml=yaml,
)
with open(script_dir / "map_data.yaml") as f:
self.files = yaml_loader.load(f.read(), Loader=yaml_loader.SafeLoader)
def split(self, rom_bytes):
fs_dir = options.get_asset_path() / self.dir / self.name
(fs_dir / "title").mkdir(parents=True, exist_ok=True)
data = rom_bytes[self.rom_start: self.rom_end]
asset_idx = 0
while True:
asset_data = data[0x20 + asset_idx * 0x1C:]
name = decode_null_terminated_ascii(asset_data[0:])
offset = int.from_bytes(asset_data[0x10:0x14], byteorder="big")
size = int.from_bytes(asset_data[0x14:0x18], byteorder="big")
decompressed_size = int.from_bytes(
asset_data[0x18:0x1C], byteorder="big")
is_compressed = size != decompressed_size
if offset == 0:
path = None
else:
path = fs_dir / add_file_ext(name)
if name == "end_data":
break
bytes_start = self.rom_start + 0x20 + offset
bytes = rom_bytes[bytes_start : bytes_start + size]
if is_compressed:
bytes = Yay0decompress.decompress_yay0(bytes)
if name.startswith("party_"):
with open(path, "wb") as f:
# CI-8
w = png.Writer(150, 105, palette=parse_palette(bytes[:0x200]))
w.write_array(f, bytes[0x200:])
elif name == "title_data":
if "ver/us" in options.opts["target_path"]:
with open(fs_dir / "title/logotype.png", "wb") as f:
width = 200
height = 112
N64SegRgba32.get_writer(width, height).write_array(f, N64SegRgba32.parse_image(bytes[0x2210 : 0x2210 + width * height * 4], width, height))
with open(fs_dir / "title/copyright.png", "wb") as f:
width = 144
height = 32
N64SegIa8.get_writer(width, height).write_array(f, N64SegIa8.parse_image(bytes[0x10 : 0x10 + width * height], width, height))
with open(fs_dir / "title/press_start.png", "wb") as f:
width = 128
height = 32
N64SegIa8.get_writer(width, height).write_array(f, N64SegIa8.parse_image(bytes[0x1210 : 0x1210 + width * height], width, height))
else:
with open(fs_dir / "title/logotype.png", "wb") as f:
width = 272
height = 88
N64SegRgba32.get_writer(width, height).write_array(f, N64SegRgba32.parse_image(bytes[0x1830 : 0x1830 + width * height * 4], width, height))
with open(fs_dir / "title/copyright.png", "wb") as f:
width = 128
height = 32
w = png.Writer(width, height, palette=parse_palette(bytes[0x810:0x830]))
w.write_array(f, N64SegCi4.parse_image(bytes[0x10 : 0x10 + width * height], width, height))
with open(fs_dir / "title/press_start.png", "wb") as f:
width = 128
height = 32
N64SegIa8.get_writer(width, height).write_array(f, N64SegIa8.parse_image(bytes[0x830 : 0x830 + width * height], width, height))
elif name.endswith("_bg"):
def write_bg_png(bytes, path, header_offset=0):
header = bytes[header_offset:header_offset+0x10]
raster_offset = int.from_bytes(header[0:4], byteorder="big") - 0x80200000
palette_offset = int.from_bytes(header[4:8], byteorder="big") - 0x80200000
assert int.from_bytes(header[8:12], byteorder="big") == 0x000C0014 # draw pos
width = int.from_bytes(header[12:14], byteorder="big")
height = int.from_bytes(header[14:16], byteorder="big")
with open(path, "wb") as f:
# CI-8
w = png.Writer(width, height, palette=parse_palette(bytes[palette_offset:palette_offset+512]))
w.write_array(f, bytes[raster_offset:])
write_bg_png(bytes, path)
# sbk_bg has an alternative palette
if name == "sbk_bg":
write_bg_png(bytes, fs_dir / f"{name}.alt.png", header_offset=0x10)
else:
with open(path, "wb") as f:
f.write(bytes)
asset_idx += 1
def get_linker_entries(self):
from segtypes.linker_entry import LinkerEntry
fs_dir = options.get_asset_path() / self.dir / self.name
return [LinkerEntry(
self,
[fs_dir / add_file_ext(name) for name in self.files],
fs_dir.with_suffix(".dat"), ".data"),
]