def writeRow(y, symbolY): baseOffset = y * nginMapData.sizeScreens[0] start = baseOffset end = start + nginMapData.sizeScreens[0] slice = nginMapData.map[start:end] # Duplicate the last element to add a sentinel value at the # right side to simplify engine code. slice.append(slice[-1]) f.write(" row{}: .byte {}\n".format( symbolY, listToString(slice)))
def parseInstruction(instruction): parsed = {"m1": 0, "m2": 0, "m3": 0} instructionList = list(instruction) instructionLenght = len(instructionList) if instructionLenght == 1: parsed["opCode"] = int(instruction) return parsed opCode = listToString(instructionList[-2:]) parsed["opCode"] = int(opCode) # Remove last two digits and reverse modes = instructionList[:-2][::-1] for i, mode in enumerate(modes): parsed["m{}".format(i + 1)] = int(mode) return parsed
def writeNginData(nginCommonMapData, outPrefix, symbols, segments, segmentGranularity, dataSegment): with open(outPrefix + ".s", "w") as f: f.write("; Data generated by Ngin tiled-map-importer.py\n\n") outPrefixBase = os.path.basename(outPrefix) f.write('.include "{}.inc"\n'.format(outPrefixBase)) # User-specified extra includes for include in nginCommonMapData.includes: f.write('.include "{}"\n'.format(include)) f.write('.include "ngin/ngin.inc"\n\n') f.write('.segment "{}"\n\n'.format(dataSegment)) f.write(".scope screens\n") for screenIndex, screen in enumerate(nginCommonMapData.uniqueScreens): f.write(" .proc screen{}\n".format(screenIndex)) for y in xrange(8): start = 8 * y end = start + 8 line = screen[start:end] lineLo = map(lambda x: x & 0xFF, line) writeByteArray(f, " ", lineLo) hiBits = map(lambda x: x >> 8, screen) f.write(" ; Packed MSB:\n") writeByteArray(f, " ", packBits(hiBits)) f.write(" .endproc\n\n") f.write(".endscope\n\n") kScreenPointersTemplate = """\ .scope screenPointers .define screenPointers_ {} lo: .lobytes screenPointers_ hi: .hibytes screenPointers_ .undefine screenPointers_ .endscope """ f.write( kScreenPointersTemplate.format( listToString( map(lambda x: "screens::screen{}".format(x), range(len(nginCommonMapData.uniqueScreens)))))) # Divide to sets of 256 metatiles. # \todo If there's < 256 metatiles in total, the other pointers # can be pointed to the first one, and then the MSB row of the # screen data can be removed, since any data will do. kSetSize = 256 numSets = (len(nginCommonMapData.uniqueMt32) + kSetSize - 1) // kSetSize # Have to have at least two sets because ngin_MapData_Header # contains two references. kMinNumSets = 2 numSets = max([numSets, kMinNumSets]) for i in xrange(numSets): setData = nginCommonMapData.uniqueMt32[kSetSize * i:kSetSize * i + kSetSize] f.write(".scope _32x32Metatiles{}\n".format(i)) writeByteArray(f, " topLeft: ", map(lambda x: x[0], setData)) writeByteArray(f, " topRight: ", map(lambda x: x[1], setData)) writeByteArray(f, " bottomLeft: ", map(lambda x: x[2], setData)) writeByteArray(f, " bottomRight: ", map(lambda x: x[3], setData)) f.write(".endscope\n\n") kBytesPer8x8Tile = 16 bytesLeft = len(nginCommonMapData.uniqueTiles) * kBytesPer8x8Tile for i, segment in enumerate(segments): f.write('ngin_pushSeg "{}"\n'.format(segment)) f.write(".proc chrData_{}\n".format(i)) # Can't .incbin with start, size if nothing to include (ca65 # will error out). if bytesLeft > 0: f.write(' .incbin "{}.chr", {}, {}\n'.format( outPrefixBase, i * segmentGranularity, segmentGranularity if segmentGranularity < bytesLeft else bytesLeft)) f.write(".endproc\n") f.write("ngin_popSeg\n\n") bytesLeft -= segmentGranularity if bytesLeft < 0: bytesLeft = 0 def withBase(index): return lambda x: "B_{}+{}".format( x[index] * kBytesPer8x8Tile // segmentGranularity, x[index] % (segmentGranularity // kBytesPer8x8Tile), ) f.write(".scope _16x16Metatiles\n") for i in range(len(segments)): f.write(" B_{} = .lobyte( chrData_{}/ppu::kBytesPer8x8Tile )\n". format(i, i)) writeByteArray(f, " topLeft: ", map(withBase(0), nginCommonMapData.uniqueMt16)) writeByteArray(f, " topRight: ", map(withBase(1), nginCommonMapData.uniqueMt16)) writeByteArray(f, " bottomLeft: ", map(withBase(2), nginCommonMapData.uniqueMt16)) writeByteArray(f, " bottomRight: ", map(withBase(3), nginCommonMapData.uniqueMt16)) writeByteArray( f, " attributes0: ", map(lambda x: "|".join(x[4]), nginCommonMapData.uniqueMt16)) f.write(".endscope\n\n") # --------------------------------------------------------------------- for mapIndex, nginMapData in enumerate(nginCommonMapData.maps): f.write(".scope map{}\n".format(mapIndex)) # \todo Add some comment lines to results indicating from what # files the results were generated. def writeRow(y, symbolY): baseOffset = y * nginMapData.sizeScreens[0] start = baseOffset end = start + nginMapData.sizeScreens[0] slice = nginMapData.map[start:end] # Duplicate the last element to add a sentinel value at the # right side to simplify engine code. slice.append(slice[-1]) f.write(" row{}: .byte {}\n".format( symbolY, listToString(slice))) # Screen rows f.write(" .scope screenRows\n") for y in xrange(nginMapData.sizeScreens[1]): writeRow(y, y) # Add a sentinel row. writeRow(nginMapData.sizeScreens[1] - 1, nginMapData.sizeScreens[1]) f.write(" .endscope\n\n") # Screen row pointers kScreenRowPointersTemplate = """\ .scope screenRowPointers .define screenRowPointers_ {} lo: .lobytes screenRowPointers_ hi: .hibytes screenRowPointers_ .undefine screenRowPointers_ .endscope """ f.write( kScreenRowPointersTemplate.format( listToString( map( lambda x: "screenRows::row{}".format(x), # 1 is added because of the sentinel row. range(nginMapData.sizeScreens[1] + 1))))) # Objects kObjectsTemplate = """\ .scope objects ; Object names: {} .define objectX {} .define objectY {} {} xLo: .lobytes objectX xHi: .hibytes objectX yLo: .lobytes objectY yHi: .hibytes objectY type: .byte {} xToYIndex: .byte {} ySorted: .byte {} .undefine objectX .undefine objectY .endscope """ objects = nginMapData.objects # \todo Should we handle object size here? # Add a sentinel object at the top-left and bottom-right corners. # Both positions are outside the map boundaries. # (Use (0,0) for top-left, and $FFFF, $FFFF for bottom right) objects.append( Object("sentinelTopLeft", "ngin_Object_kInvalidTypeId", None, nginMapData.nginWorldPointToMapPixel((0, 0)), size=None)) objects.append( Object("sentinelBottomRight", "ngin_Object_kInvalidTypeId", None, nginMapData.nginWorldPointToMapPixel((0xFFFF, 0xFFFF)), size=None)) # Sort the object list based on the X coordinate. objects.sort(key=lambda object: object.position[0]) # Create a list of indices into the X list, then sort that based on the # Y coordinate. ySortedObjects = list(enumerate(objects)) ySortedObjects.sort(key=lambda x: x[1].position[1]) # Create a mapping from X index to Y index. xToYIndex = [None] * len(ySortedObjects) for yIndex, (xIndex, blah) in enumerate(ySortedObjects): assert xToYIndex[xIndex] is None xToYIndex[xIndex] = yIndex objectTypes = map(lambda x: x.type, objects) # Get the unique object types for import. uniqueObjectTypes = set(objectTypes) uniqueObjectTypes.remove("ngin_Object_kInvalidTypeId") objectNginWorldPoints = map( lambda x: nginMapData.mapPixelToNginWorldPoint(x.position), objects) importZp = ".importzp " if len(uniqueObjectTypes) == 0: importZp = "; No imports" f.write( kObjectsTemplate.format( listToString(map(lambda x: x.name, objects), width=1), listToString( map(lambda x: int(round(x[0])), objectNginWorldPoints)), listToString( map(lambda x: int(round(x[1])), objectNginWorldPoints)), importZp + listToString(uniqueObjectTypes), listToString(objectTypes), listToString(xToYIndex), listToString(map(lambda x: x[0], ySortedObjects)), )) # Calculate the map boundaries. boundaryLeftTop = nginMapData.mapPixelToNginWorldPoint((0, 0)) boundaryRightBottom = nginMapData.mapPixelToNginWorldPoint(( nginMapData.sizeScreens[0] * kScreenSizeX, nginMapData.sizeScreens[1] * kScreenSizeY, )) if nginMapData.boundaryLeftTop is not None and \ nginMapData.boundaryRightBottom is not None: boundaryLeftTop = nginMapData.mapPixelToNginWorldPoint( nginMapData.boundaryLeftTop) boundaryRightBottom = nginMapData.mapPixelToNginWorldPoint( nginMapData.boundaryRightBottom) # Map header # \todo Should probably add a level of indirection for the metatile # set. kHeaderTemplate = """\ ; ngin_MapData_Header .proc header ; widthScreens .byte {} ; heightScreens .byte {} ; boundaryLeft .word ${:04X} ; boundaryRight .word ${:04X} - ngin_cfg_MapData_viewWidth - 1 ; Inclusive ; boundaryTop .word ${:04X} ; boundaryBottom .word ${:04X} - ngin_cfg_MapData_viewHeight - 1 ; Inclusive ; numObjects .byte {} ; ngin_MapData_Pointers .addr screenRowPointers::lo .addr screenRowPointers::hi .addr screenPointers::lo .addr screenPointers::hi .addr _16x16Metatiles::topLeft .addr _16x16Metatiles::topRight .addr _16x16Metatiles::bottomLeft .addr _16x16Metatiles::bottomRight .addr _16x16Metatiles::attributes0 .addr _32x32Metatiles0::topLeft .addr _32x32Metatiles0::topRight .addr _32x32Metatiles0::bottomLeft .addr _32x32Metatiles0::bottomRight .addr _32x32Metatiles1::topLeft .addr _32x32Metatiles1::topRight .addr _32x32Metatiles1::bottomLeft .addr _32x32Metatiles1::bottomRight .addr objects::xLo .addr objects::xHi .addr objects::yLo .addr objects::yHi .addr objects::type .addr objects::xToYIndex .addr objects::ySorted .endproc """ f.write( kHeaderTemplate.format(nginMapData.sizeScreens[0], nginMapData.sizeScreens[1], boundaryLeftTop[0], boundaryRightBottom[0], boundaryLeftTop[1], boundaryRightBottom[1], len(objects))) f.write(".endscope\n\n") f.write("{} = map{}::header\n\n".format(symbols[mapIndex], mapIndex)) with open(outPrefix + ".chr", "wb") as f: for tile in nginCommonMapData.uniqueTiles: packed = common.packNesTile(tile) f.write(packed) with open(outPrefix + ".inc", "w") as f: uniqueSymbol = "NGIN_TILED_MAP_IMPORTER_" + \ str( uuid.uuid4() ).upper().replace( "-", "_" ) f.write(".if .not .defined( {} )\n".format(uniqueSymbol)) f.write("{} = 1\n\n".format(uniqueSymbol)) f.write('.include "ngin/ngin.inc"\n\n') def writeMarker(name, position): worldPoint = nginMapData.mapPixelToNginWorldPoint(position) f.write(" {} = ngin_immVector2_16 {}, {}\n".format( name, worldPoint[0], worldPoint[1])) for symbol, nginMapData in zip(symbols, nginCommonMapData.maps): f.write(".global {}\n".format(symbol)) f.write(".scope {}\n".format(symbol)) f.write(" .scope markers\n") writeMarker("topLeft", (0, 0)) for marker in nginMapData.markers: assert marker[0] != "topLeft" writeMarker(marker[0], marker[1]) f.write(" .endscope\n") f.write(".endscope\n") f.write("\n.endif\n")