mirror of
https://github.com/pmret/papermario.git
synced 2024-11-13 22:43:00 +01:00
3315d6010f
* 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>
133 lines
4.3 KiB
Python
Executable File
133 lines
4.3 KiB
Python
Executable File
#! /usr/bin/python3
|
|
|
|
from math import floor, ceil
|
|
from sys import argv, path
|
|
from pathlib import Path
|
|
path.append(str(Path(__file__).parent.parent.parent / "splat"))
|
|
path.append(str(Path(__file__).parent.parent.parent / "splat_ext"))
|
|
from PaperMarioNpcSprites import Sprite
|
|
|
|
def pack_color(r, g, b, a):
|
|
r = floor(31 * (r / 255))
|
|
g = floor(31 * (g / 255))
|
|
b = floor(31 * (b / 255))
|
|
|
|
s = round(a / 0xFF)
|
|
s |= (r & 0x1F) << 11
|
|
s |= (g & 0x1F) << 6
|
|
s |= (b & 0x1F) << 1
|
|
|
|
return s
|
|
|
|
def iter_in_groups(iterable, n, fillvalue=None):
|
|
from itertools import zip_longest
|
|
args = [iter(iterable)] * n
|
|
return zip_longest(*args, fillvalue=fillvalue)
|
|
|
|
if __name__ == "__main__":
|
|
if len(argv) != 3:
|
|
print("usage: compile_npc_sprite.py [OUTBIN] [DIR]")
|
|
exit(1)
|
|
|
|
_, outfile, sprite_dir = argv
|
|
|
|
try:
|
|
sprite = Sprite.from_dir(Path(sprite_dir))
|
|
except AssertionError as e:
|
|
print("error:", e)
|
|
exit(1)
|
|
|
|
with open(outfile, "wb") as f:
|
|
f.seek(0x10) # leave space for header
|
|
|
|
# leave space for animation offset list
|
|
f.seek((len(sprite.animations) + 1) * 4, 1)
|
|
animation_offsets = []
|
|
|
|
# write animations
|
|
for i, components in enumerate(sprite.animations):
|
|
animation_offsets.append(f.tell())
|
|
|
|
# leave space for component offset list
|
|
f.seek((len(components) + 1) * 4, 1)
|
|
component_offsets = []
|
|
|
|
for comp in components:
|
|
offset = f.tell()
|
|
|
|
for command in comp.commands:
|
|
f.write(command.to_bytes(2, byteorder="big"))
|
|
|
|
f.seek(f.tell() % 4, 1)
|
|
component_offsets.append(f.tell())
|
|
|
|
f.write(offset.to_bytes(4, byteorder="big"))
|
|
f.write((len(comp.commands) * 2).to_bytes(2, byteorder="big"))
|
|
f.write(comp.x.to_bytes(2, byteorder="big", signed=True))
|
|
f.write(comp.y.to_bytes(2, byteorder="big", signed=True))
|
|
f.write(comp.z.to_bytes(2, byteorder="big", signed=True))
|
|
|
|
next_anim = f.tell()
|
|
|
|
# write component offset list
|
|
f.seek(animation_offsets[i])
|
|
component_offsets.append(-1)
|
|
for offset in component_offsets:
|
|
f.write(offset.to_bytes(4, byteorder="big", signed=True))
|
|
|
|
f.seek(next_anim)
|
|
|
|
# palettes start 8-byte aligned
|
|
if (f.tell() & 7) == 4:
|
|
f.seek(4, 1)
|
|
|
|
# write palettes
|
|
palette_offsets = []
|
|
for i, palette in enumerate(sprite.palettes):
|
|
palette_offsets.append(f.tell())
|
|
for rgba in palette:
|
|
if rgba[3] not in (0, 0xFF):
|
|
print("error: translucent pixels not allowed in palette {sprite.palette_names[i]}")
|
|
exit(1)
|
|
|
|
color = pack_color(*rgba)
|
|
f.write(color.to_bytes(2, byteorder="big"))
|
|
|
|
# write images/rasters
|
|
image_offsets = []
|
|
for image in sprite.images:
|
|
offset = f.tell()
|
|
|
|
for a, b in iter_in_groups(image.raster, 2):
|
|
byte = (a << 4) | b
|
|
f.write(byte.to_bytes(1, byteorder="big"))
|
|
|
|
image_offsets.append(f.tell())
|
|
|
|
f.write(offset.to_bytes(4, byteorder="big"))
|
|
f.write(bytes([image.width, image.height, image.palette_index, 0xFF]))
|
|
|
|
# write image offset list
|
|
image_offset_list_offset = f.tell()
|
|
image_offsets.append(-1)
|
|
for offset in image_offsets:
|
|
f.write(offset.to_bytes(4, byteorder="big", signed=True))
|
|
|
|
# write palette offset list
|
|
palette_offset_list_offset = f.tell()
|
|
palette_offsets.append(-1)
|
|
for offset in palette_offsets:
|
|
f.write(offset.to_bytes(4, byteorder="big", signed=True))
|
|
|
|
# write header
|
|
f.seek(0)
|
|
f.write(image_offset_list_offset.to_bytes(4, byteorder="big"))
|
|
f.write(palette_offset_list_offset.to_bytes(4, byteorder="big"))
|
|
f.write(sprite.max_components.to_bytes(4, byteorder="big"))
|
|
f.write(sprite.num_variations.to_bytes(4, byteorder="big"))
|
|
|
|
# write animation offset list
|
|
animation_offsets.append(-1)
|
|
for offset in animation_offsets:
|
|
f.write(offset.to_bytes(4, byteorder="big", signed=True))
|