164 lines
5.9 KiB
Python
164 lines
5.9 KiB
Python
import sys
|
|
import os
|
|
from args import args
|
|
from xml.etree import ElementTree as ET
|
|
from processtileset import processTileset
|
|
from assetcache import assetCache, assetGetCache
|
|
from assethelpers import getAssetRelativePath
|
|
|
|
def processMap(asset):
|
|
cache = assetGetCache(asset['path'])
|
|
if cache is not None:
|
|
return cache
|
|
|
|
# Load the TMX file
|
|
tree = ET.parse(asset['path'])
|
|
root = tree.getroot()
|
|
|
|
# Root needs to be "map" element.
|
|
if root.tag != 'map':
|
|
print(f"Error: TMX file {asset['path']} does not have a <map> root element")
|
|
sys.exit(1)
|
|
|
|
# Root needs to be orientation="orthogonal"
|
|
if 'orientation' not in root.attrib or root.attrib['orientation'] != 'orthogonal':
|
|
print(f"Error: TMX file {asset['path']} does not have orientation='orthogonal'")
|
|
sys.exit(1)
|
|
|
|
# Extract width, height, tilewidth, tileheight attributes
|
|
if 'width' not in root.attrib or 'height' not in root.attrib or 'tilewidth' not in root.attrib or 'tileheight' not in root.attrib:
|
|
print(f"Error: TMX file {asset['path']} is missing required attributes (width, height, tilewidth, tileheight)")
|
|
sys.exit(1)
|
|
|
|
mapWidth = int(root.attrib['width'])
|
|
mapHeight = int(root.attrib['height'])
|
|
tileWidth = int(root.attrib['tilewidth'])
|
|
tileHeight = int(root.attrib['tileheight'])
|
|
|
|
# Find all tileset elements
|
|
tilesets = []
|
|
for tilesetElement in root.findall('tileset'):
|
|
# Tileset must have a source attribute
|
|
if 'source' not in tilesetElement.attrib:
|
|
print(f"Error: <tileset> element in {asset['path']} is missing a source attribute")
|
|
sys.exit(1)
|
|
# Must have a firstgid attribute
|
|
if 'firstgid' not in tilesetElement.attrib:
|
|
print(f"Error: <tileset> element in {asset['path']} is missing a firstgid attribute")
|
|
sys.exit(1)
|
|
|
|
firstGid = int(tilesetElement.attrib['firstgid'])
|
|
source = tilesetElement.attrib['source']
|
|
|
|
# Get source path relative to the tmx file's working directory.
|
|
# Needs normalizing also since ".." is often used.
|
|
source = os.path.normpath(os.path.join(os.path.dirname(asset['path']), source))
|
|
tileset = processTileset({ 'path': source, 'type': 'tileset', 'options': {} })
|
|
|
|
tilesets.append({
|
|
'firstGid': firstGid,
|
|
'source': source,
|
|
'tileset': tileset
|
|
})
|
|
|
|
# Sort tilesets by firstGid, highest first
|
|
tilesets.sort(key=lambda x: x['firstGid'], reverse=True)
|
|
|
|
# Layer types
|
|
# objectLayers = [] # Not implemented
|
|
tileLayers = []
|
|
for layerElement in root.findall('layer'):
|
|
# Assume tile layer for now
|
|
# Must have id, name, width, height attributes
|
|
if 'id' not in layerElement.attrib or 'name' not in layerElement.attrib or 'width' not in layerElement.attrib or 'height' not in layerElement.attrib:
|
|
print(f"Error: <layer> element in {asset['path']} is missing required attributes (id, name, width, height)")
|
|
sys.exit(1)
|
|
|
|
id = int(layerElement.attrib['id'])
|
|
name = layerElement.attrib['name']
|
|
width = int(layerElement.attrib['width'])
|
|
height = int(layerElement.attrib['height'])
|
|
|
|
# Need exactly one data element
|
|
dataElements = layerElement.findall('data')
|
|
if len(dataElements) != 1:
|
|
print(f"Error: <layer> element in {asset['path']} must have exactly one <data> child element")
|
|
sys.exit(1)
|
|
|
|
# Get text, remove whitespace, split by comman and convert to int
|
|
dataElement = dataElements[0]
|
|
if dataElement.attrib.get('encoding', '') != 'csv':
|
|
print(f"Error: <data> element in {asset['path']} must have encoding='csv'")
|
|
sys.exit(1)
|
|
|
|
dataText = dataElement.text.strip()
|
|
data = [int(gid) for gid in dataText.split(',') if gid.strip().isdigit()]
|
|
|
|
# Should be exactly width * height entries
|
|
if len(data) != width * height:
|
|
print(f"Error: <data> element in {asset['path']} has {len(data)} entries but expected {width * height} (width * height)")
|
|
sys.exit(1)
|
|
|
|
tileLayers.append({
|
|
'id': id,
|
|
'name': name,
|
|
'width': width,
|
|
'height': height,
|
|
'data': data,
|
|
})
|
|
|
|
# Now we have our layers all parsed out.
|
|
data = bytearray()
|
|
data += b'drm' # Dusk RPG Map
|
|
data += mapWidth.to_bytes(4, 'little') # Map width in tiles
|
|
data += mapHeight.to_bytes(4, 'little') # Map height in tiles
|
|
data += len(tilesets).to_bytes(4, 'little') # Number of tilesets
|
|
data += len(tileLayers).to_bytes(4, 'little') # Number of layers
|
|
|
|
# For each tileset
|
|
for tileset in tilesets:
|
|
data += tileset['firstGid'].to_bytes(4, 'little') # First GID
|
|
|
|
# For each layer...
|
|
for layer in tileLayers:
|
|
for gid in layer['data']:
|
|
# Get tileset for this gid, since the tilesets are already sorted we can
|
|
# simply find the first one that has firstGid <= gid
|
|
if gid == 0:
|
|
# Empty tile
|
|
localIndex = 0xFF
|
|
tilesetIndex = 0xFFFFFFFF
|
|
else:
|
|
for tileset in tilesets:
|
|
if gid >= tileset['firstGid']:
|
|
tilesetIndex = tilesets.index(tileset)
|
|
localIndex = gid - tileset['firstGid']
|
|
break
|
|
else:
|
|
# If no tileset was found, this is an invalid gid
|
|
print(f"Error: Invalid tile GID {gid} in {asset['path']}")
|
|
sys.exit(1)
|
|
|
|
if localIndex > 255:
|
|
print(f"Error: Local tile index {localIndex} exceeds 255 in {asset['path']}")
|
|
sys.exit(1)
|
|
|
|
data += tilesetIndex.to_bytes(4, 'little') # Tileset index
|
|
data += localIndex.to_bytes(1, 'little') # Local tile index
|
|
|
|
relative = getAssetRelativePath(asset['path'])
|
|
fileNameWithoutExt = os.path.splitext(os.path.basename(asset['path']))[0]
|
|
outputFileRelative = os.path.join(os.path.dirname(relative), f"{fileNameWithoutExt}.drm")
|
|
outputFilePath = os.path.join(args.output_assets, outputFileRelative)
|
|
os.makedirs(os.path.dirname(outputFilePath), exist_ok=True)
|
|
with open(outputFilePath, "wb") as f:
|
|
f.write(data)
|
|
|
|
outMap = {
|
|
'mapPath': outputFileRelative,
|
|
'files': [ outputFilePath ],
|
|
'width': mapWidth,
|
|
'height': mapHeight,
|
|
}
|
|
|
|
return assetCache(asset['path'], outMap) |