mirror of
https://github.com/spicyjpeg/573in1.git
synced 2025-02-02 20:47:17 +01:00
172 lines
4.5 KiB
Python
Executable File
172 lines
4.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
__version__ = "0.4.2"
|
|
__author__ = "spicyjpeg"
|
|
|
|
import json
|
|
from argparse import ArgumentParser, FileType, Namespace
|
|
from pathlib import Path
|
|
from typing import Any, ByteString
|
|
from zipfile import ZIP_DEFLATED, ZIP_STORED, ZipFile
|
|
|
|
import lz4.block
|
|
from common.assets import *
|
|
from PIL import Image
|
|
|
|
## Main
|
|
|
|
def createParser() -> ArgumentParser:
|
|
parser = ArgumentParser(
|
|
description = \
|
|
"Parses a JSON file containing a list of resources to convert, "
|
|
"generates the respective files and packs them into a ZIP archive.",
|
|
add_help = False
|
|
)
|
|
|
|
group = parser.add_argument_group("Tool options")
|
|
group.add_argument(
|
|
"-h", "--help",
|
|
action = "help",
|
|
help = "Show this help message and exit"
|
|
)
|
|
|
|
group = parser.add_argument_group("Compression options")
|
|
group.add_argument(
|
|
"-c", "--compression",
|
|
type = str,
|
|
choices = ( "none", "deflate", "lz4" ),
|
|
default = "deflate",
|
|
help = "Set default compression algorithm (default DEFLATE)"
|
|
)
|
|
group.add_argument(
|
|
"-l", "--compress-level",
|
|
type = int,
|
|
default = 9,
|
|
help = "Set default DEFLATE and LZ4 compression level (default 9)",
|
|
metavar = "0-9"
|
|
)
|
|
|
|
group = parser.add_argument_group("File paths")
|
|
group.add_argument(
|
|
"-s", "--source-dir",
|
|
type = Path,
|
|
help = \
|
|
"Set path to directory containing source files (same directory as "
|
|
"resource list by default)",
|
|
metavar = "dir"
|
|
)
|
|
group.add_argument(
|
|
"configFile",
|
|
type = FileType("rt"),
|
|
help = "Path to JSON configuration file",
|
|
)
|
|
group.add_argument(
|
|
"output",
|
|
type = Path,
|
|
help = "Path to ZIP file to generate"
|
|
)
|
|
|
|
return parser
|
|
|
|
def main():
|
|
parser: ArgumentParser = createParser()
|
|
args: Namespace = parser.parse_args()
|
|
|
|
with args.configFile as _file:
|
|
configFile: dict[str, Any] = json.load(_file)
|
|
sourceDir: Path = \
|
|
args.source_dir or Path(_file.name).parent
|
|
|
|
assetList: list[dict[str, Any]] = configFile["resources"]
|
|
|
|
with ZipFile(args.output, "w", allowZip64 = False) as _zip:
|
|
for asset in assetList:
|
|
match asset.get("type", "file").strip():
|
|
case "empty":
|
|
data: ByteString = bytes(int(asset.get("size", 0)))
|
|
|
|
case "text":
|
|
with open(sourceDir / asset["source"], "rt") as _file:
|
|
data: ByteString = _file.read().encode("ascii")
|
|
|
|
case "binary":
|
|
with open(sourceDir / asset["source"], "rb") as _file:
|
|
data: ByteString = _file.read()
|
|
|
|
case "tim":
|
|
ix: int = int(asset["imagePos"]["x"])
|
|
iy: int = int(asset["imagePos"]["y"])
|
|
cx: int = int(asset["clutPos"]["x"])
|
|
cy: int = int(asset["clutPos"]["y"])
|
|
|
|
image: Image.Image = Image.open(sourceDir / asset["source"])
|
|
image.load()
|
|
|
|
if image.mode != "P":
|
|
image = image.quantize(
|
|
int(asset["quantize"]), dither = Image.NONE
|
|
)
|
|
|
|
data: ByteString = generateIndexedTIM(image, ix, iy, cx, cy)
|
|
|
|
case "metrics":
|
|
if "metrics" in asset:
|
|
metrics: dict = asset["metrics"]
|
|
else:
|
|
with open(sourceDir / asset["source"], "rt") as _file:
|
|
metrics: dict = json.load(_file)
|
|
|
|
data: ByteString = generateFontMetrics(metrics)
|
|
|
|
case "palette":
|
|
if "palette" in asset:
|
|
palette: dict = asset["palette"]
|
|
else:
|
|
with open(sourceDir / asset["source"], "rt") as _file:
|
|
palette: dict = json.load(_file)
|
|
|
|
data: ByteString = generateColorPalette(palette)
|
|
|
|
case "strings":
|
|
if "strings" in asset:
|
|
strings: dict = asset["strings"]
|
|
else:
|
|
with open(sourceDir / asset["source"], "rt") as _file:
|
|
strings: dict = json.load(_file)
|
|
|
|
data: ByteString = generateStringTable(strings)
|
|
|
|
case _type:
|
|
raise KeyError(f"unsupported asset type '{_type}'")
|
|
|
|
compressLevel: int | None = \
|
|
asset.get("compressLevel", args.compress_level)
|
|
|
|
match asset.get("compression", args.compression).strip():
|
|
case "none" | None:
|
|
_zip.writestr(asset["name"], data, ZIP_STORED)
|
|
|
|
case "deflate":
|
|
_zip.writestr(
|
|
asset["name"], data, ZIP_DEFLATED, compressLevel
|
|
)
|
|
|
|
case "lz4":
|
|
# ZIP archives do not "officially" support LZ4 compression,
|
|
# so the entry is stored as an uncompressed file.
|
|
compressed: bytes = lz4.block.compress(
|
|
data,
|
|
mode = "high_compression",
|
|
compression = compressLevel,
|
|
store_size = False
|
|
)
|
|
|
|
_zip.writestr(asset["name"], compressed, ZIP_STORED)
|
|
|
|
case _type:
|
|
raise KeyError(f"unsupported compression type '{_type}'")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|