mirror of
https://github.com/pmret/papermario.git
synced 2024-11-08 12:02:30 +01:00
ae66312d8c
* Add Python linter to github actions * wip * Add back splat_ext * Format files * C++ -> C * format 2 files * split workflow into separate file, line length 120, fix excludes * -l 120 in ci * update black locally and apply formatting changes * pyproject.toject --------- Co-authored-by: Ethan Roseman <ethteck@gmail.com>
184 lines
6.1 KiB
Python
184 lines
6.1 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
from sys import path
|
|
from typing import Tuple
|
|
|
|
path.append(str(Path(__file__).parent.parent.parent / "splat"))
|
|
path.append(str(Path(__file__).parent.parent.parent / "build"))
|
|
|
|
from common import get_asset_path
|
|
|
|
path.append(str(Path(__file__).parent.parent.parent))
|
|
from splat_ext.tex_archives import (
|
|
AUX_COMBINE_MODES_INV,
|
|
TILES_BASIC,
|
|
TILES_INDEPENDENT_AUX,
|
|
TILES_MIPMAPS,
|
|
TILES_SHARED_AUX,
|
|
TexImage,
|
|
get_format_code,
|
|
)
|
|
|
|
|
|
# read texture properties from dictionary and load images
|
|
def img_from_json(json_data, tex_name: str, asset_stack: Tuple[Path, ...]) -> TexImage:
|
|
ret = TexImage()
|
|
|
|
ret.img_name = json_data["name"]
|
|
|
|
if "ext" in json_data:
|
|
ret.raw_ext = json_data["ext"]
|
|
else:
|
|
ret.raw_ext = "tif"
|
|
|
|
# read data for main tile
|
|
main_data = json_data.get("main")
|
|
if main_data == None:
|
|
raise Exception(f"Texture {ret.img_name} has no definition for 'main'")
|
|
|
|
(main_fmt_name, ret.main_hwrap, ret.main_vwrap) = ret.read_json_img(main_data, "main", ret.img_name)
|
|
(ret.main_fmt, ret.main_depth) = get_format_code(main_fmt_name)
|
|
|
|
# read main image
|
|
img_path = get_asset_path(Path(f"mapfs/tex/{tex_name}/{ret.img_name}.png"), asset_stack)
|
|
if not os.path.isfile(img_path):
|
|
raise Exception(f"Could not find main image for texture: {ret.img_name}")
|
|
(
|
|
ret.main_img,
|
|
ret.main_pal,
|
|
ret.main_width,
|
|
ret.main_height,
|
|
) = ret.get_img_file(main_fmt_name, str(img_path))
|
|
|
|
# read data for aux tile
|
|
ret.has_aux = "aux" in json_data
|
|
if ret.has_aux:
|
|
aux_data = json_data.get("aux")
|
|
(aux_fmt_name, ret.aux_hwrap, ret.aux_vwrap) = ret.read_json_img(aux_data, "aux", ret.img_name)
|
|
|
|
if aux_fmt_name == "Shared":
|
|
# aux tiles have blank attributes in SHARED mode
|
|
aux_fmt_name = main_fmt_name
|
|
ret.aux_fmt = 0
|
|
ret.aux_depth = 0
|
|
ret.aux_hwrap = 0
|
|
ret.aux_vwrap = 0
|
|
ret.extra_tiles = TILES_SHARED_AUX
|
|
else:
|
|
(ret.aux_fmt, ret.aux_depth) = get_format_code(aux_fmt_name)
|
|
ret.extra_tiles = TILES_INDEPENDENT_AUX
|
|
|
|
# read aux image
|
|
img_path = get_asset_path(Path(f"mapfs/tex/{tex_name}/{ret.img_name}_AUX.png"), asset_stack)
|
|
if not os.path.isfile(img_path):
|
|
raise Exception(f"Could not find AUX image for texture: {ret.img_name}")
|
|
(
|
|
ret.aux_img,
|
|
ret.aux_pal,
|
|
ret.aux_width,
|
|
ret.aux_height,
|
|
) = ret.get_img_file(aux_fmt_name, str(img_path))
|
|
if ret.extra_tiles == TILES_SHARED_AUX:
|
|
# aux tiles have blank sizes in SHARED mode
|
|
ret.main_height *= 2
|
|
ret.aux_width = 0
|
|
ret.aux_height = 0
|
|
|
|
else:
|
|
ret.aux_fmt = 0
|
|
ret.aux_depth = 0
|
|
ret.aux_hwrap = 0
|
|
ret.aux_vwrap = 0
|
|
ret.aux_width = 0
|
|
ret.aux_height = 0
|
|
ret.extra_tiles = TILES_BASIC
|
|
|
|
# read mipmaps
|
|
ret.has_mipmaps = json_data.get("hasMipmaps", False)
|
|
if ret.has_mipmaps:
|
|
ret.mipmaps = []
|
|
mipmap_idx = 1
|
|
divisor = 2
|
|
if ret.main_width >= (32 >> ret.main_depth):
|
|
while True:
|
|
if (ret.main_width // divisor) <= 0:
|
|
break
|
|
mmw = ret.main_width // divisor
|
|
mmh = ret.main_height // divisor
|
|
|
|
img_path = get_asset_path(
|
|
Path(f"mapfs/tex/{tex_name}/{ret.img_name}_MM{mipmap_idx}.png"),
|
|
asset_stack,
|
|
)
|
|
if not os.path.isfile(img_path):
|
|
raise Exception(
|
|
f"Texture {ret.img_name} is missing mipmap level {mipmap_idx} (size = {mmw} x {mmh})"
|
|
)
|
|
|
|
(raster, pal, width, height) = ret.get_img_file(main_fmt_name, str(img_path))
|
|
ret.mipmaps.append(raster)
|
|
if width != mmw or height != mmh:
|
|
raise Exception(
|
|
f"Texture {ret.img_name} has wrong size for mipmap level {mipmap_idx} \n"
|
|
+ f"MM{mipmap_idx} size = {width} x {height}, but should be = {mmw} x {mmh}"
|
|
)
|
|
|
|
divisor = divisor * 2
|
|
mipmap_idx += 1
|
|
if (ret.main_width // divisor) < (16 >> ret.main_depth):
|
|
break
|
|
ret.extra_tiles = TILES_MIPMAPS
|
|
|
|
# read filter mode
|
|
if json_data.get("filter", False):
|
|
ret.filter_mode = 2
|
|
else:
|
|
ret.filter_mode = 0
|
|
|
|
# read tile combine mode
|
|
combine_str = json_data.get("combine", "Missing")
|
|
ret.combine = AUX_COMBINE_MODES_INV.get(combine_str)
|
|
if ret.combine == None:
|
|
raise Exception(f"Texture {ret.img_name} has invalid 'combine'")
|
|
|
|
ret.is_variant = json_data.get("variant", False)
|
|
|
|
return ret
|
|
|
|
|
|
def build(out_path: Path, tex_name: str, asset_stack: Tuple[Path, ...], endian: str = "big"):
|
|
out_bytes = bytearray()
|
|
|
|
json_path = get_asset_path(Path(f"mapfs/tex/{tex_name}.json"), asset_stack)
|
|
|
|
with open(json_path) as json_file:
|
|
json_str = json_file.read()
|
|
json_data = json.loads(json_str)
|
|
|
|
if len(json_data) > 128:
|
|
raise Exception(f"Maximum number of textures (128) exceeded by {tex_name} ({len(json_data)})`")
|
|
|
|
for img_data in json_data:
|
|
img = img_from_json(img_data, tex_name, asset_stack)
|
|
img.add_bytes(tex_name, out_bytes)
|
|
|
|
with open(out_path, "wb") as out_bin:
|
|
out_bin.write(out_bytes)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description="Texture archives")
|
|
parser.add_argument("bin_out", type=Path, help="Output binary file path")
|
|
parser.add_argument("name", help="Name of tex subdirectory")
|
|
parser.add_argument("asset_stack", help="comma-separated asset stack")
|
|
parser.add_argument("--endian", choices=["big", "little"], default="big", help="Output endianness")
|
|
args = parser.parse_args()
|
|
|
|
asset_stack = tuple(Path(d) for d in args.asset_stack.split(","))
|
|
|
|
build(args.bin_out, args.name, asset_stack, args.endian)
|