summaryrefslogtreecommitdiff
path: root/src/exporter.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/exporter.py')
-rw-r--r--src/exporter.py335
1 files changed, 291 insertions, 44 deletions
diff --git a/src/exporter.py b/src/exporter.py
index fec2f86..5233cdd 100644
--- a/src/exporter.py
+++ b/src/exporter.py
@@ -1,7 +1,54 @@
from common import *
import array
import sys
+from ctypes import create_string_buffer
+def RGB4A3Encode(tex):
+ tex = tex.toImage()
+ w, h = tex.width(), tex.height()
+ padW = (w + 3) & ~3
+ padH = (h + 3) & ~3
+
+ destBuffer = create_string_buffer(padW * padH * 2)
+
+ shortstruct = struct.Struct('>H')
+ sspack = shortstruct.pack_into
+ offset = 0
+
+ for ytile in xrange(0, padW, 4):
+ for xtile in xrange(0, padH, 4):
+ for ypixel in xrange(ytile, ytile + 4):
+ for xpixel in xrange(xtile, xtile + 4):
+
+ if xpixel >= w or ypixel >= h:
+ rgbDAT = 0x7FFF
+ else:
+ pixel = tex.pixel(xpixel, ypixel)
+
+ a = pixel >> 24
+ r = (pixel >> 16) & 0xFF
+ g = (pixel >> 8) & 0xFF
+ b = pixel & 0xFF
+
+ if a < 245: #RGB4A3
+ alpha = a/32
+ red = r/16
+ green = g/16
+ blue = b/16
+
+ rgbDAT = (blue) | (green << 4) | (red << 8) | (alpha << 12)
+
+ else: # RGB555
+ red = r/8
+ green = g/8
+ blue = b/8
+
+ rgbDAT = (blue) | (green << 5) | (red << 10) | (0x8000) # 0rrrrrgggggbbbbb
+
+ sspack(destBuffer, offset, rgbDAT)
+ offset += 2
+
+ return destBuffer.raw
class KPMapExporter:
class LayerExporter:
@@ -10,6 +57,7 @@ class KPMapExporter:
def buildSectors(self, sectors, indices):
# we'll use the cache held by the layer: why reinvent the wheel?
+ layer = self.layer
layer.updateCache()
cache = layer.cache
@@ -34,13 +82,13 @@ class KPMapExporter:
destY = worldY % 16
destRow = rawSectors[sectorY]
-
- for srcX in xrange(layerX, layerX+layerWidth):
+
+ for srcX in xrange(layerWidth):
worldX = srcX + layerX
sectorX = worldX / 16
destX = worldX % 16
- tile = srcRow[i]
+ tile = srcRow[srcX]
if tile == -1: continue
destSector = destRow[sectorX]
@@ -51,80 +99,279 @@ class KPMapExporter:
destSector[destY][destX] = tile
# now add the created sectors to the data
- sectorMap = [-1 for i in xrange(len(self.rawSectors))]
+ count = reduce(lambda x,y: x+len(y), rawSectors, 0)
+ sectorMap = [-1 for i in xrange(count)]
+ destIdx = 0
- for srcRow, mapRow in zip(self.rawSectors, sectorMap):
- for index, sector in enumerate(srcRow):
+ for srcRow in rawSectors:
+ for sector in srcRow:
if sector is not None:
# see if it's a duplicate or not
sectorKey = '|'.join(map(lambda x: ','.join(map(str, x)), sector))
try:
- mapRow[index] = sectorIndices[sectorKey]
- except ValueError:
- sectorIndices[sectorKey] = len(sectors)
- mapRow[index] = len(sectors)
+ sectorMap[destIdx] = indices[sectorKey]
+ except KeyError:
+ indices[sectorKey] = len(sectors)
+ sectorMap[destIdx] = len(sectors)
sectors.append(sector)
+ destIdx += 1
+
self.sectorBounds = (sectorLeft, sectorTop, sectorRight, sectorBottom)
self.sectorMap = sectorMap
-
+
def __init__(self, mapObj):
self.map = mapObj
self.layers = map(KPMapExporter.LayerExporter, self.map.layers)
- self.archive = WiiArchiveU8()
-
- def buildAndPack(self, handle=None):
- # make the BG data
+ def build(self):
+ u32 = struct.Struct('>I')
+ u16 = struct.Struct('>H')
+ zero32 = u32.pack(0)
+
+ requiredFixUps = []
+ stringsToAdd = set()
+ textures = set()
+ offsets = {None: 0xFFFFFFFF}
+
+ # first off, build the sectors
sectors = []
sectorIndices = {}
for layer in self.layers:
- layer.buildSectors(sectors, sectorIndices)
+ if isinstance(layer.layer, KPTileLayer):
+ layer.buildSectors(sectors, sectorIndices)
- # pack it into the arc
- self.archive.resolvePath('/sectors.bin').data = self._packSectorData(sectors)
- self.archive.resolvePath('/sectorMaps.bin').data = self._packSectorMaps()
+ sectorData = self._packSectorData(sectors)
- return self.archive.pack(handle)
+ # now that we've got that, we can pack the first part of the file
+ data = bytearray(struct.pack('>IIII', len(self.layers), 16 + len(sectorData), 0, 0))
+ # list of layer pointers goes here.. or will, later
+ data += sectorData
- def _packSectorData(self, sectors):
- rowStruct = struct.Struct('>16h')
- output = []
+ for layer in self.layers:
+ requiredFixUps.append((len(data), layer))
+ data += zero32
- for sector in sectors:
- for row in sector:
- output.append(rowStruct.pack(*row))
+ # now build the layers
+ for eLayer in self.layers:
+ layer = eLayer.layer
- return ''.join(output)
+ offsets[eLayer] = len(data)
+ offsets[layer] = len(data)
+
+ if isinstance(layer, KPTileLayer):
+ data += u32.pack(0)
+
+ # tileset name
+ stringsToAdd.add(layer.tileset)
+ requiredFixUps.append((len(data), layer.tileset))
+ data += zero32
+
+ # sector info
+ data += struct.pack('>IIII', *eLayer.sectorBounds)
+ data += ''.join(map(u16.pack, eLayer.sectorMap))
+
+ pad = (4 - (len(data) & 3)) % 4
+ data += ('\0' * pad)
+
+ elif isinstance(layer, KPDoodadLayer):
+ data += u32.pack(1)
+
+ # doodad list
+ data += u32.pack(len(layer.objects))
+ for doodad in layer.objects:
+ requiredFixUps.append((len(data), doodad))
+ data += zero32
+
+ # now pack them ...
+ for doodad in layer.objects:
+ offsets[doodad] = len(data)
+
+ x, y = doodad.position
+ w, h = doodad.size
+ data += struct.pack('>fffffii', x, y, w, h, doodad.angle, 0, len(doodad.animations))
+
+ texture = doodad.source[1]
+ textures.add(texture)
+ requiredFixUps.append((len(data) - 8, texture))
+
+ for anim in doodad.animations:
+ rLoop, rCurve, rFrames, rType, rStart, rEnd = anim
+
+ loopid = self.ANIM_LOOPS.index(rLoop)
+ curveid = self.ANIM_CURVES.index(rCurve)
+ typeid = self.ANIM_TYPES.index(rType)
+ data += struct.pack('>iififf', loopid, curveid, rFrames, typeid, rStart, rEnd)
+
+ elif isinstance(layer, KPPathLayer):
+ data += u32.pack(2)
+
+ # lists
+ current = len(data)
+ nodeArray = current + 16
+ pathArray = nodeArray + (len(layer.nodes) * 4)
+
+ data += struct.pack('>IIII', len(layer.nodes), nodeArray, len(layer.paths), pathArray)
+
+ for node in layer.nodes:
+ requiredFixUps.append((len(data), node))
+ data += zero32
+ for path in layer.paths:
+ requiredFixUps.append((len(data), path))
+ data += zero32
+
+ # now do the actual structs
+ for node in layer.nodes:
+ offsets[node] = len(data)
- def _packSectorMaps(self):
- offsets = array.array('I')
- assert offsets.itemsize == 4
+ x, y = node.position
+ current = len(data)
+ data += struct.pack('>hhiiii', x, y, 0, 0, 0, 0)
- currentOffset = len(self.layers) * 4
+ exits = node.exits + [None,None,None,None] # TODO
+ requiredFixUps.append((current+4, exits[0]))
+ requiredFixUps.append((current+8, exits[1]))
+ requiredFixUps.append((current+12, exits[2]))
+ requiredFixUps.append((current+16, exits[3]))
- data = []
+ if node.isStop():
+ if node.level:
+ level1, level2 = node.level
+ data += struct.pack('>ibbbb', 2, level1, level2, 0, 0)
+ typeid = 2
- for index, layer in enumerate(self.layers):
- offsets.append(currentOffset)
+ elif node.mapChange:
+ data += u32.pack(3)
- data.append(struct.pack('>hhhh', *layer.sectorBounds))
- currentOffset += 8
-
- first = layer.sectorMap[0]
- rowStruct = struct.Struct('>%dh' % len(first))
- for row in layer.sectorMap:
- data.append(rowStruct.pack(*row))
+ destMap = node.mapChange
+ requiredFixUps.append((len(data), destMap))
+ stringsToAdd.add(destMap)
- currentOffset += (len(first) * len(layer.sectorMap) * 2)
+ data += struct.pack('>ibbbb', 0, node.mapID, node.foreignID, node.transition, 0)
- if sys.byteorder == 'little': offsets.byteswap()
- return offsets.tostring() + ''.join(data)
+ else:
+ data += u32.pack(1)
+ else:
+ data += u32.pack(0)
+
+ for path in layer.paths:
+ offsets[path] = len(data)
+
+ start = path._startNodeRef()
+ end = path._endNodeRef()
+ current = len(data)
+
+ requiredFixUps.append((current, start))
+ requiredFixUps.append((current+4, end))
+ requiredFixUps.append((current+8, path.linkedLayer))
+ data += (zero32 * 3)
+
+ data += struct.pack('>fi', path.movementSpeed, path.animation)
+
+ # unlocking paths/nodes!
+ unlocks = []
+ us = struct.Struct('>bbbbi')
+
+ for node in self.map.pathLayer.nodes:
+ if not node.level: continue
+
+ checked = set()
+ affected = []
+ self._findUnlocksForNode(node, checked, affected, True)
+
+ level1, level2 = node.level
+
+ for item, secret in affected:
+ unlocks.append(us.pack(secret, level1, level2, 0, offsets[item]))
+
+ struct.pack_into('>ii', data, 8, len(unlocks), len(data))
+ data += ''.join(unlocks)
+
+ # now that we're almost done... pack the strings
+ for string in stringsToAdd:
+ offsets[string] = len(data)
+ data += str(string)
+ data += '\0'
+
+ # textures
+ texHeaderStartOffset = len(data)
+ texHeaderEndOffset = texHeaderStartOffset + (len(textures) * 8)
+
+ texDataStartOffset = (texHeaderEndOffset + 0x1F) & ~0x1F
+ texPadding = texDataStartOffset - texHeaderEndOffset
+
+ currentTexOffset = texDataStartOffset
+
+ imageData = []
+
+ for tex in textures:
+ offsets[tex] = len(data)
+ data += struct.pack('>hhi', tex.width(), tex.height(), currentTexOffset)
+
+ converted = RGB4A3Encode(tex)
+ imageData.append(converted)
+ currentTexOffset += len(converted)
+
+ data += ('\0' * texPadding)
+ for piece in imageData:
+ data += piece
+
+ # to finish up, correct every offset
+ for offset, target in requiredFixUps:
+ u32.pack_into(data, offset, offsets[target])
+
+ return data
+
+ ANIM_LOOPS = ['Contiguous', 'Loop', 'Reversible Loop']
+ ANIM_CURVES = ['Linear', 'Sinusoidial', 'Cosinoidial']
+ ANIM_TYPES = ['X Position', 'Y Position', 'Angle', 'X Scale', 'Y Scale', 'Opacity']
+
+
+ def _findUnlocksForNode(self, node, checked, affected, isFirstBranch=False, secret=None):
+ if node in checked: return
+
+ checked.add(node)
+
+ for path in node.exits:
+ if path not in checked:
+ checked.add(path)
+ self._findUnlocksForPath(path, node, checked, affected, isFirstBranch, secret)
+
+
+ def _findUnlocksForPath(self, path, sourceNode, checked, affected, isFirstBranch, secret=None):
+ start, end = path._startNodeRef(), path._endNodeRef()
+ if start == sourceNode:
+ destNode = end
+ if isFirstBranch and path.unlocks != 1:
+ return
+ else:
+ destNode = start
+ if isFirstBranch and path.unlocks != 2:
+ return
+
+ if secret is None:
+ secret = path.secret
+ affected.append((path, secret))
+
+ if not destNode.isStop():
+ self._findUnlocksForNode(destNode, checked, affected, False, secret)
+
+
+
+ def _packSectorData(self, sectors):
+ rowStruct = struct.Struct('>16h')
+ output = []
+
+ for sector in sectors:
+ for row in sector:
+ output.append(rowStruct.pack(*row))
+
+ return ''.join(output)