예제 #1
0
 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)))
예제 #2
0
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
예제 #3
0
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")