papermario/tools/build/mapfs/combine.py
Ethan Roseman 8837fbdf65
Player sprites & more (#1055)
* WIP work on sprites (sprite_stuff.py)

* cleanup of various stuff

* separate compiler installation into separate script

* wipz

* more

* renames, bugfixes

* more

* very grood

* cleanin

* goods and services

* oopth

* oopth2

* Parse palette data from xml

* more work

* more wipperz

* more

* it working

* git subrepo pull --force tools/splat

subrepo:
  subdir:   "tools/splat"
  merged:   "e72a868f9f"
upstream:
  origin:   "https://github.com/ethteck/splat.git"
  branch:   "master"
  commit:   "e72a868f9f"
git-subrepo:
  version:  "0.4.5"
  origin:   "https://github.com/ingydotnet/git-subrepo"
  commit:   "aa416e4"

* fix symbol_addrs for new splat

* upd8s

* Use generated header, other versions, fixes

* fixes & formatting

* wip fusing npc + player extraction & cleanup

* remove npc_files

* buildin

* fix some bugs

* Cleanup, yay0s separately

* cleen

* cleanup

* Respect stack during build

* jp spritz

* dun

* fix c files

---------

Co-authored-by: pixel-stuck <mathmcclintic@gmail.com>
2023-06-26 19:27:37 +09:00

82 lines
2.6 KiB
Python
Executable File

#!/usr/bin/env python3
from sys import argv
from pathlib import Path
import struct
def next_multiple(pos, multiple):
return pos + pos % multiple
def get_version_date(version):
if version == "us":
return "Map Ver.00/11/07 15:36"
elif version == "jp":
return "Map Ver.00/07/05 19:13"
elif version == "pal":
return "Map Ver.01/03/23 16:30"
else:
return "Map Ver.??/??/?? ??:??"
def build_mapfs(out_bin, assets, version):
# every TOC entry's name field has data after the null terminator made up from all the previous name fields.
# we probably don't have to do this for the game to read the data properly (it doesn't read past the null terminator
# of `string`), but the original devs' equivalent of this script had this bug so we need to replicate it to match.
with open(out_bin, "wb") as f:
f.write(get_version_date(version).encode("ascii"))
next_data_pos = (len(assets) + 1) * 0x1C
asset_idx = 0
lastname = ""
for decompressed, compressed in assets:
toc_entry_pos = 0x20 + asset_idx * 0x1C
# data for TOC entry
name = decompressed.stem + "\0"
offset = next_data_pos
decompressed_size = decompressed.stat().st_size
size = next_multiple(compressed.stat().st_size, 2) if compressed.exists() else decompressed_size
#print(f"{name} {offset:08X} {size:08X} {decompressed_size:08X}")
# write all previously-written names; required to match
lastname = name + lastname[len(name):]
f.seek(toc_entry_pos)
f.write(lastname.encode('ascii'))
# write TOC entry.
f.seek(toc_entry_pos + 0x10)
f.write(struct.pack(">III", offset, size, decompressed_size))
# write data.
f.seek(0x20 + next_data_pos)
f.write(compressed.read_bytes() if compressed.exists() else decompressed.read_bytes())
next_data_pos += size
asset_idx += 1
# end_data
toc_entry_pos = 0x20 + asset_idx * 0x1C
last_name_entry = "end_data\0"
f.seek(toc_entry_pos)
lastname = last_name_entry + lastname[len(last_name_entry):]
f.write(lastname.encode('ascii'))
f.seek(toc_entry_pos + 0x18)
f.write((0x903F0000).to_bytes(4, byteorder="big")) # TODO: figure out purpose
if __name__ == "__main__":
argv.pop(0) # python3
version = argv.pop(0)
out = argv.pop(0)
assets = []
# pairs
for i in range(0, len(argv), 2):
assets.append((Path(argv[i]), Path(argv[i+1])))
build_mapfs(out, assets, version)