2024-08-08 16:41:02 -03:00
|
|
|
#!/bin/env python
|
|
|
|
|
|
|
|
# chunkgen.py
|
|
|
|
# Tool for converting 128x128 chunks generated from a .tmx tilemap in Tiled
|
|
|
|
# to a proper .MAP file for the PlayStation engine-psx.
|
2024-09-17 17:49:29 -03:00
|
|
|
# Converts map128.psxcsv -> MAP128.MAP
|
|
|
|
# Make sure you export your .tmx map to .psxcsv before using this tool.
|
|
|
|
import os
|
2024-08-08 16:41:02 -03:00
|
|
|
import sys
|
|
|
|
import pandas as pd
|
|
|
|
import numpy as np
|
|
|
|
import math
|
|
|
|
from ctypes import c_ushort, c_ubyte
|
|
|
|
|
|
|
|
# Set endianness of some types to big endian
|
|
|
|
c_ushort = c_ushort.__ctype_be__
|
|
|
|
|
|
|
|
|
|
|
|
def reshape_dimension(d):
|
|
|
|
if d % 8 > 0:
|
|
|
|
extra = 8 - (d - (math.floor(d / 8) * 8))
|
|
|
|
return d + extra
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
def load_data(csvfile):
|
|
|
|
df = pd.read_csv(csvfile, header=None)
|
|
|
|
cols = df.shape[1]
|
|
|
|
rows = df.shape[0]
|
2024-09-17 17:49:29 -03:00
|
|
|
# print(f"Old shape: {df.shape}")
|
2024-08-08 16:41:02 -03:00
|
|
|
new_cols = reshape_dimension(cols)
|
|
|
|
new_rows = reshape_dimension(rows)
|
|
|
|
if new_cols:
|
|
|
|
while df.shape[1] < new_cols:
|
|
|
|
df.insert(
|
|
|
|
df.shape[1], df.shape[1], np.full(shape=df.shape[0], fill_value=-1)
|
|
|
|
)
|
|
|
|
if new_rows:
|
|
|
|
while df.shape[0] < new_rows:
|
|
|
|
df.loc[len(df)] = np.full(shape=df.shape[1], fill_value=-1)
|
|
|
|
return df
|
|
|
|
|
|
|
|
|
|
|
|
def get_chunk(df, x, y):
|
|
|
|
startx = x * 8
|
|
|
|
endx = startx + 8
|
|
|
|
starty = y * 8
|
|
|
|
endy = starty + 8
|
|
|
|
return df.loc[list(range(starty, endy)), list(range(startx, endx))]
|
|
|
|
|
|
|
|
|
|
|
|
def get_max_grid(df):
|
|
|
|
return (int(df.shape[1] / 8), int(df.shape[0] / 8))
|
|
|
|
|
|
|
|
|
|
|
|
# Binary layout:
|
|
|
|
# --> MUST BE SAVED IN BIG ENDIAN FORMAT.
|
|
|
|
# 1. Tile width: short (16 bits)
|
|
|
|
# 2. Number of tiles: short (16 bits)
|
|
|
|
# 3. Frame rows / columns: short (16 bits)
|
|
|
|
# 4. Array of frame data.
|
2024-09-17 17:49:29 -03:00
|
|
|
# 4.1. Frames: Columns * Rows
|
|
|
|
# 4.1.1 Tilenum: short (16 bits)
|
|
|
|
# 4.1.2 Props: byte (8 bits)
|
2024-11-19 20:45:55 -03:00
|
|
|
def export_binary(f, solid, oneway, nocol, front):
|
2024-09-17 17:49:29 -03:00
|
|
|
grid = get_max_grid(solid)
|
2024-08-08 16:41:02 -03:00
|
|
|
f.write(c_ushort(128))
|
|
|
|
f.write(c_ushort(grid[0] * grid[1]))
|
|
|
|
f.write(c_ushort(8))
|
|
|
|
# Loop for each chunk
|
|
|
|
for cy in range(0, grid[1]):
|
|
|
|
for cx in range(0, grid[0]):
|
2024-09-17 17:49:29 -03:00
|
|
|
chunk_solid = get_chunk(solid, cx, cy)
|
|
|
|
chunk_oneway = None
|
|
|
|
chunk_nocol = None
|
2024-11-19 20:45:55 -03:00
|
|
|
chunk_front = None
|
2024-09-17 17:49:29 -03:00
|
|
|
if oneway is not None:
|
|
|
|
chunk_oneway = get_chunk(oneway, cx, cy)
|
|
|
|
if nocol is not None:
|
|
|
|
chunk_nocol = get_chunk(nocol, cx, cy)
|
2024-11-19 20:45:55 -03:00
|
|
|
if front is not None:
|
|
|
|
chunk_front = get_chunk(front, cx, cy)
|
2024-09-17 17:49:29 -03:00
|
|
|
# chunk_id = (cy * grid[0]) + cx
|
2024-08-18 03:01:52 -03:00
|
|
|
# print(f"Exporting tile {chunk_id}...")
|
2024-08-08 16:41:02 -03:00
|
|
|
# Loop for each piece within chunk
|
|
|
|
for py in range(0, 8):
|
|
|
|
for px in range(0, 8):
|
2024-09-17 17:49:29 -03:00
|
|
|
props = 0
|
|
|
|
index = chunk_solid.iloc[py, px]
|
|
|
|
if index <= 0 and chunk_oneway is not None:
|
|
|
|
index = chunk_oneway.iloc[py, px]
|
|
|
|
props = 1 if index > 0 else 0
|
|
|
|
if index <= 0 and chunk_nocol is not None:
|
|
|
|
index = chunk_nocol.iloc[py, px]
|
|
|
|
props = 2 if index > 0 else 0
|
2024-11-19 20:45:55 -03:00
|
|
|
if index <= 0 and chunk_front is not None:
|
|
|
|
index = chunk_front.iloc[py, px]
|
|
|
|
props = 4 if index > 0 else 0
|
2024-09-17 17:49:29 -03:00
|
|
|
f.write(c_ushort(max(index, 0)))
|
|
|
|
f.write(c_ubyte(props))
|
2024-08-08 16:41:02 -03:00
|
|
|
|
|
|
|
|
|
|
|
# def debug_print(df):
|
|
|
|
# print(df)
|
|
|
|
# grid = get_max_grid(df)
|
|
|
|
# for y in range(0, grid[1]):
|
|
|
|
# for x in range(0, grid[0]):
|
|
|
|
# chunk = get_chunk(df, x, y)
|
|
|
|
# id = (y * grid[0]) + x
|
|
|
|
# if id > 0:
|
|
|
|
# print(f"Tile {id}:")
|
|
|
|
# print(chunk)
|
|
|
|
# print()
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
out = sys.argv[2]
|
2024-09-17 17:49:29 -03:00
|
|
|
solid_layer = None
|
|
|
|
oneway_layer = None
|
|
|
|
none_layer = None
|
|
|
|
if os.path.isfile(sys.argv[1]):
|
|
|
|
solid_layer = load_data(sys.argv[1])
|
|
|
|
else:
|
|
|
|
basepath = os.path.splitext(sys.argv[1])[0]
|
|
|
|
solid_layer = load_data(f"{basepath}_solid.psxcsv")
|
|
|
|
oneway_layer = load_data(f"{basepath}_oneway.psxcsv")
|
|
|
|
none_layer = load_data(f"{basepath}_none.psxcsv")
|
2024-11-19 20:45:55 -03:00
|
|
|
front_layer = load_data(f"{basepath}_front.psxcsv")
|
2024-09-17 17:49:29 -03:00
|
|
|
|
2024-08-08 16:41:02 -03:00
|
|
|
with open(out, "wb") as f:
|
2024-11-19 20:45:55 -03:00
|
|
|
export_binary(f, solid_layer, oneway_layer, none_layer, front_layer)
|
2024-08-08 16:41:02 -03:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|