papermario/tools/splat_ext/pm_map_data.py
Ethan Roseman 473bfa0692
dead_sqrtf, data migration, etc (#790)
* more goods

* move dead map data to dir

* data migration / splatification

* new splat

* git subrepo pull --force tools/splat

subrepo:
  subdir:   "tools/splat"
  merged:   "d93b6550c4"
upstream:
  origin:   "https://github.com/ethteck/splat.git"
  branch:   "master"
  commit:   "d93b6550c4"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo"
  commit:   "2f68596"

* splat fix

* dead_sqrtf bye bye

* fix

* Clover comments

* comment
2022-09-29 05:52:12 +09:00

218 lines
6.9 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
import n64img.image
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.opts.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 str(options.opts.target_path):
w = 200
h = 112
img = n64img.image.RGBA32(
data=bytes[0x2210 : 0x2210 + w * h * 4], width=w, height=h
)
img.write(fs_dir / "title/logotype.png")
w = 144
h = 32
img = n64img.image.IA8(
data=bytes[0x10 : 0x10 + w * h], width=w, height=h
)
img.write(fs_dir / "title/copyright.png")
w = 128
h = 32
img = n64img.image.IA8(
data=bytes[0x1210 : 0x1210 + w * h], width=w, height=h
)
img.write(fs_dir / "title/press_start.png")
else:
w = 272
h = 88
img = n64img.image.RGBA32(
data=bytes[0x1830 : 0x1830 + w * h * 4], width=w, height=h
)
img.write(fs_dir / "title/logotype.png")
w = 128
h = 32
img = n64img.image.CI4(
data=bytes[0x10 : 0x10 + w * h], width=w, height=h
)
img.palette = parse_palette(bytes[0x810:0x830])
img.write(fs_dir / "title/copyright.png")
w = 128
h = 32
img = n64img.image.IA8(
data=bytes[0x830 : 0x830 + w * h], width=w, height=h
)
img.write(fs_dir / "title/press_start.png")
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.opts.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",
),
]