papermario/tools/splat_ext/PaperMarioMapFS.py
Ethan Roseman 3315d6010f
Splat refactor (#257)
* all non-world rodata migrated

* data disasm

* kinda working

* updated yaml

* bloop

* linker header

* configure 2.0

* bin

* mass rename to remove code_

* pause rename

* battle partner stuff

* whew

* more renames

* more renames

* more renaming

* it builds!

* updates

* remove main prefix

* one more thing

* crc, yay0

* .data, .rodata, .bss

* img

* dead_atan2

* it buildsgit add -A

* split battle/partner/6FAD10

* rm &s on sleepy_sheep syms

* sha1sum ninja rule description

* OK but commented out PaperMarioMapFS and PaperMarioNpcSprites

* uncomment

* fix mapfs

* match func_8003CFB4

* .

* clean up and name npc_iter_no_op

* npc.c

* enable cc warnings

* name npc_find_near

* use singular options.asset_path

* smores

* cc_dsl only when needed

* kinda fix configure for splat refactor2

* ok!

* new msg format

* remove old msg format docs

* slight bug fixes, splat adjustment

* git subrepo pull (merge) --force tools/splat

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

* git subrepo pull (merge) --force tools/splat

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

* Update symbol addrs

* git subrepo pull tools/splat

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

Co-authored-by: Alex Bates <hi@imalex.xyz>
2021-04-13 16:47:52 +09:00

116 lines
3.9 KiB
Python

from segtypes.n64.segment import N64Segment
from util.n64 import Yay0decompress
from util.color import unpack_color
from util.iter import iter_in_groups
from util import options
import png
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 N64SegPaperMarioMapFS(N64Segment):
def __init__(self, segment, rom_start, rom_end):
super().__init__(segment, rom_start, rom_end)
self.files = segment["files"]
def split(self, rom_bytes):
fs_dir = options.get_asset_path() / self.dir / self.name
fs_dir.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.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"),
]