Skip to content

Instantly share code, notes, and snippets.

@RoadrunnerWMC
Last active September 17, 2022 20:57
Show Gist options
  • Save RoadrunnerWMC/1adf49dfbf5b4572b06714a63d783c7b to your computer and use it in GitHub Desktop.
Save RoadrunnerWMC/1adf49dfbf5b4572b06714a63d783c7b to your computer and use it in GitHub Desktop.
A script to batch-fix tilesets with darkened edges
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# Script to batch-fix tilesets with darkened edges, produced by buggy
# versions of Puzzle. By RoadrunnerWMC, 2022-09-17.
# Put it in the source directory of Puzzle Updated (the folder with
# puzzle.py), and specify the target folder as the first CLI argument.
from pathlib import Path
import sys
import nsmblib
import archive
import puzzle
def fix_tileset_data(data):
arc = archive.U8()
arc._load(data)
files = {key: value for key, value in arc.files}
for key in files:
if key.startswith('BG_tex/') and key.endswith('_tex.bin.LZ'):
texture_fn = key
break
else:
raise ValueError(f"Couldn't find the texture file: {set(files)}")
texture_data = bytearray(
nsmblib.decodeTilesetNoPremultiplication(
nsmblib.decompress11LZS(
files[texture_fn])))
assert len(texture_data) == 1024 * 256 * 4
stride = 1024 * 4
for row in range(8):
for col in range(32):
tile_data = bytearray()
offs = row * 32 * stride + 4 * stride + col * (32 * 4) + (4 * 4)
for src_y in range(24):
tile_data.extend(texture_data[offs: offs + 24 * 4])
offs += stride
assert len(tile_data) == 24 * 24 * 4
puzzle.color_transparent_pixels_around_edges_24_24(tile_data)
offs = row * 32 * stride + col * (32 * 4)
for src_y in range(24):
row_data = tile_data[src_y * (24 * 4) : (src_y + 1) * (24 * 4)]
# Clamp left/right pixels of the row
row_data = row_data[:4] * 4 + row_data + row_data[-4:] * 4
assert len(row_data) == 32 * 4
texture_data[offs : offs + (32 * 4)] = row_data
offs += stride
if src_y == 0 or src_y == 23:
# Clamp top/bottom rows of the tile
texture_data[offs : offs + (32 * 4)] = row_data
offs += stride
texture_data[offs : offs + (32 * 4)] = row_data
offs += stride
texture_data[offs : offs + (32 * 4)] = row_data
offs += stride
texture_data[offs : offs + (32 * 4)] = row_data
offs += stride
assert len(texture_data) == 1024 * 256 * 4
files[texture_fn] = nsmblib.compress11LZS(
puzzle.RGB4A3Encode(
bytes(texture_data)))
arc = archive.U8()
for name in sorted(files):
arc[name] = files[name]
return arc._dump()
def fix_tileset(path):
path.write_bytes(fix_tileset_data(path.read_bytes()))
def main():
if len(sys.argv) < 2:
print(f'usage: {Path(sys.argv[0]).name} path/to/tilesets')
for path in Path(sys.argv[1]).glob('*.arc'):
print(f'Fixing {path}...')
fix_tileset(path)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment