Beispiel #1
0
def drawTIM(outfile,
            tim,
            transp=False,
            forcepal=-1,
            allpalettes=False,
            nopal=False):
    if tim.width == 0 or tim.height == 0:
        return
    clutwidth = clutheight = 0
    if tim.bpp == 4 or tim.bpp == 8:
        clut = forcepal if forcepal != -1 else getUniqueCLUT(tim, transp)
        if not nopal:
            clutwidth = 40
            clutheight = 5 * (len(tim.cluts[clut]) // 8)
            if allpalettes:
                clutheight *= len(tim.cluts)
    img = Image.new("RGBA",
                    (tim.width + clutwidth, max(tim.height, clutheight)),
                    (0, 0, 0, 0))
    pixels = img.load()
    x = 0
    for i in range(tim.height):
        for j in range(tim.width):
            if x >= len(tim.data):
                common.logWarning("Out of TIM data")
                break
            if tim.bpp == 4 or tim.bpp == 8:
                if len(tim.cluts[clut]) > tim.data[x]:
                    color = tim.cluts[clut][tim.data[x]]
                else:
                    common.logWarning("Index", tim.data[x], "not in CLUT")
                    color = (0, 0, 0, 0)
            else:
                color = tim.data[x]
            if not transp:
                color = (color[0], color[1], color[2], 255)
            pixels[j, i] = color
            x += 1
    if (tim.bpp == 4 or tim.bpp == 8) and not nopal:
        if allpalettes:
            for i in range(len(tim.cluts)):
                pixels = common.drawPalette(pixels, tim.cluts[i], tim.width,
                                            i * (clutheight // len(tim.cluts)),
                                            transp)
        else:
            pixels = common.drawPalette(pixels, tim.cluts[clut], tim.width, 0,
                                        transp)
    if outfile == "":
        return img
    img.save(outfile, "PNG")
Beispiel #2
0
def drawGIM(outfile, gim):
    width = 0
    height = 0
    palette = False
    if isinstance(gim, GIM):
        for image in gim.images:
            width = max(width, image.width)
            if len(image.palette) > 0:
                palette = True
                palsize = 5 * (len(image.palette) // 8)
                height += max(image.height, palsize)
            else:
                height += image.height
    else:
        width = gim.width
        height = gim.height
    img = Image.new("RGBA", (width + (40 if palette else 0), height),
                    (0, 0, 0, 0))
    pixels = img.load()
    currheight = 0
    if isinstance(gim, GIM):
        for image in gim.images:
            i = 0
            if image.tiled == 0x00:
                for y in range(image.height):
                    for x in range(image.width):
                        drawGIMPixel(image, pixels, x, currheight + y, i)
                        i += 1
            else:
                for blocky in range(image.blockedheight // image.tileheight):
                    for blockx in range(image.blockedwidth // image.tilewidth):
                        for y in range(image.tileheight):
                            for x in range(image.tilewidth):
                                pixelx = blockx * image.tilewidth + x
                                pixely = currheight + blocky * image.tileheight + y
                                if pixelx >= image.width or pixely >= currheight + image.height:
                                    i += 1
                                    continue
                                drawGIMPixel(image, pixels, pixelx, pixely, i)
                                i += 1
            if len(image.palette) > 0:
                pixels = common.drawPalette(pixels, image.palette, image.width,
                                            currheight)
                palsize = 5 * (len(image.palette) // 8)
                currheight += max(image.height, palsize)
            else:
                currheight += image.height
    else:
        i = 0
        for y in range(gim.height):
            for x in range(gim.width):
                pixels[x, gim.height - 1 - y] = gim.colors[i]
                i += 1
    img.save(outfile, "PNG")
Beispiel #3
0
def writeMappedImage(f, tilestart, maps, palettes, num=1, skipzero=False):
    maxtile = tilesize = 0
    for i in range(num):
        mapdata = maps[i]
        if mapdata.width == 0:
            common.logError("Width is 0")
            continue
        if mapdata.height == 0:
            common.logError("Height is 0")
            continue
        imgwidth = mapdata.width * 8
        imgheight = mapdata.height * 8
        pali = 0
        if mapdata.bpp == 4 and palettes != colpalette:
            imgwidth += 40
            for palette in palettes:
                if palette.count((0x0, 0x0, 0x0, 0xff)) == 16:
                    break
                pali += 1
            imgheight = max(imgheight, pali * 10)
        img = Image.new("RGB", (imgwidth, imgheight), (0x0, 0x0, 0x0))
        pixels = img.load()
        x = y = 0
        for map in mapdata.map:
            tilesize = (16 if mapdata.bpp == 2 else 32)
            if map.tile > maxtile:
                maxtile = map.tile
            if (map.tile > 0 or not skipzero) and (mapdata.bpp != 2
                                                   or map.bank == 0):
                f.seek(tilestart + map.bank * 0x4000 + map.tile * tilesize)
                try:
                    readTile(
                        f, pixels, x * 8, y * 8, palettes[map.pal]
                        if map.pal < len(palettes) else palettes[0], map.hflip,
                        map.vflip, mapdata.bpp)
                except struct.error:
                    pass
                except IndexError:
                    pass
            x += 1
            if x == mapdata.width:
                y += 1
                x = 0
        if pali > 0:
            palstart = 0
            for i in range(pali):
                pixels = common.drawPalette(pixels, palettes[i], imgwidth - 40,
                                            palstart * 10)
                palstart += 1
        img.save(mapdata.name, "PNG")
    common.logDebug("Tile data ended at",
                    common.toHex(tilestart + maxtile * tilesize + tilesize))
Beispiel #4
0
def drawMappedImage(width, height, mapdata, tiledata, paldata, tilesize=8, bpp=4):
    palnum = len(paldata) // 32
    img = Image.new("RGBA", (width + 40, max(height, palnum * 10)), (0, 0, 0, 0))
    pixels = img.load()
    # Maps
    maps = []
    for i in range(0, len(mapdata), 2):
        map = struct.unpack("<h", mapdata[i:i+2])[0]
        pal = (map >> 12) & 0xF
        xflip = (map >> 10) & 1
        yflip = (map >> 11) & 1
        tile = map & 0x3FF
        maps.append((pal, xflip, yflip, tile))
    common.logDebug("Loaded", len(maps), "maps")
    # Tiles
    tiles = []
    for i in range(len(tiledata) // (32 if bpp == 4 else 64)):
        singletile = []
        for j in range(tilesize * tilesize):
            x = i * (tilesize * tilesize) + j
            if bpp == 4:
                index = (tiledata[x // 2] >> ((x % 2) << 2)) & 0x0f
            else:
                index = tiledata[x]
            singletile.append(index)
        tiles.append(singletile)
    common.logDebug("Loaded", len(tiles), "tiles")
    # Palette
    palettes = readPaletteData(paldata)
    pals = []
    for palette in palettes:
        pals += palette
    # Draw the image
    i = j = 0
    for map in maps:
        try:
            pal = map[0]
            xflip = map[1]
            yflip = map[2]
            tile = tiles[map[3]]
            for i2 in range(tilesize):
                for j2 in range(tilesize):
                    pixels[j + j2, i + i2] = pals[16 * pal + tile[i2 * tilesize + j2]]
            # Very inefficient way to flip pixels
            if xflip or yflip:
                sub = img.crop(box=(j, i, j + tilesize, i + tilesize))
                if yflip:
                    sub = ImageOps.flip(sub)
                if xflip:
                    sub = ImageOps.mirror(sub)
                img.paste(sub, box=(j, i))
        except (KeyError, IndexError):
            common.logWarning("Tile or palette", str(map), "not found")
        j += tilesize
        if j >= width:
            j = 0
            i += tilesize
    # Draw palette
    if len(palettes) > 0:
        for i in range(len(palettes)):
            pixels = common.drawPalette(pixels, palettes[i], width, i * 10)
    return img
def run():
    infolder = "data/extract_NFP/NFP2D.NFP/"
    altinfolder = "data/work_YCE/"
    outfolder = "data/out_YCE/"
    outfile = "data/yce_data.txt"
    common.makeFolder(outfolder)

    common.logMessage("Extracting YCE to", outfolder, "...")
    with open(outfile, "w") as yce:
        files = common.getFiles(infolder, ".YCE")
        for file in common.showProgress(files):
            common.logDebug("Processing", file, "...")
            filepath = infolder + file
            if os.path.isfile(altinfolder + file):
                filepath = altinfolder + file
            with common.Stream(filepath, "rb") as f:
                # Read header
                f.seek(8)
                size = f.readUInt()  # size - header (7)
                f.seek(4, 1)  # Always 0
                f.seek(4, 1)  # Always 24
                f.readUInt()  # Animation data offset
                f.readUInt()  # ?
                num = f.readUInt()  # Number of images
                images = []
                for i in range(num):
                    img = game.YCETexture()
                    img.offset = f.readUInt() + 24  # Image data offset
                    images.append(img)
                for img in images:
                    common.logDebug("Reading image at offset", img.offset,
                                    "...")
                    f.seek(img.offset)
                    img.size = f.readUInt()  # Image data size
                    constant = f.readUInt()  # 0x1C
                    if constant != 0x1C:
                        common.logDebug("Constant is not 0x1C!",
                                        common.toHex(constant))
                    img.oamnum = f.readUInt()  # Number of OAMs
                    img.oamsize = f.readUInt()  # OAM data size
                    img.tilesize = f.readUInt()  # Tile data size
                    img.paloffset = f.readUInt(
                    ) + img.offset  # Palette data offset (relative to offset)
                    constant = f.readUInt()  # 0x01
                    if constant != 0x01:
                        common.logDebug("Constant 2 is not 0x01!",
                                        common.toHex(constant))
                    common.logDebug("size:", img.size, "oamnum:", img.oamnum,
                                    "oamsize:", img.oamsize)
                    common.logDebug("tilesize:", img.tilesize, "paloffset:",
                                    img.paloffset)
                    img.oams = []
                    for j in range(img.oamnum):
                        oam = game.OAM()
                        oam.x = f.readShort(
                        )  # X position of the cell (-128 to 127)
                        oam.y = f.readShort(
                        )  # Y position of the cell (-256 to 255)
                        for x in range(8):
                            unkbyte = f.readByte()
                            if unkbyte != 0x00:
                                common.logDebug("unkbyte", x, "is not 0x00!",
                                                common.toHex(unkbyte))
                        shape = f.readByte()  # NCER OBJ Shape
                        size = f.readByte()  # NCER OBJ Size
                        for x in range(2):
                            unkbyte = f.readByte()
                            if unkbyte != 0x00:
                                common.logDebug("unkbyte2", x, "is not 0x00!",
                                                common.toHex(unkbyte))
                        oam.offset = f.readUInt()
                        # Table from http://www.romhacking.net/documents/%5B469%5Dnds_formats.htm#NCER
                        if shape == 0:
                            if size == 0:
                                tilesize = (8, 8)
                            elif size == 1:
                                tilesize = (16, 16)
                            elif size == 2:
                                tilesize = (32, 32)
                            elif size == 3:
                                tilesize = (64, 64)
                        elif shape == 1:
                            if size == 0:
                                tilesize = (16, 8)
                            elif size == 1:
                                tilesize = (32, 8)
                            elif size == 2:
                                tilesize = (32, 16)
                            elif size == 3:
                                tilesize = (64, 32)
                        elif shape == 2:
                            if size == 0:
                                tilesize = (8, 16)
                            elif size == 1:
                                tilesize = (8, 32)
                            elif size == 2:
                                tilesize = (16, 32)
                            elif size == 3:
                                tilesize = (32, 64)
                        oam.width = tilesize[0]
                        oam.height = tilesize[1]
                        img.oams.append(oam)
                    # Calculate width and height
                    minx = miny = 512
                    maxx = maxy = -512
                    for oam in img.oams:
                        minx = min(minx, oam.x)
                        miny = min(miny, oam.y)
                        maxx = max(maxx, oam.x + oam.width)
                        maxy = max(maxy, oam.y + oam.height)
                    img.width = maxx - minx
                    img.height = maxy - miny
                    for oam in img.oams:
                        oam.x -= minx
                        oam.y -= miny
                    common.logDebug("width:", img.width, "height:", img.height)
                    common.logDebug("oams:", img.oams)
                # Create image
                width = height = 0
                for img in images:
                    width = max(width, img.width + 40)
                    height += max(img.height, 10)
                outimg = Image.new("RGBA", (width, height), (0, 0, 0, 0))
                pixels = outimg.load()
                # Read images
                currheight = 0
                for img in images:
                    # Load palette
                    palette = []
                    f.seek(img.paloffset)
                    paldata = f.read(32)
                    for i in range(0, 32, 2):
                        p = struct.unpack("<H", paldata[i:i + 2])[0]
                        palette.append(common.readPalette(p))
                    # Read tile data
                    f.seek(img.offset + img.oamsize)
                    tiledata = f.read(img.tilesize)
                    for oam in img.oams:
                        x = oam.offset * 64
                        for i in range(oam.height // 8):
                            for j in range(oam.width // 8):
                                for i2 in range(8):
                                    for j2 in range(8):
                                        index = (tiledata[x // 2] >>
                                                 ((x % 2) << 2)) & 0x0f
                                        pixels[oam.x + j * 8 + j2,
                                               currheight + oam.y + i * 8 +
                                               i2] = palette[index]
                                        x += 1
                    # Draw palette
                    pixels = common.drawPalette(pixels, palette, width - 40,
                                                currheight)
                    currheight += max(img.height, 10)
                outimg.save(outfolder + file.replace(".YCE", ".png"), "PNG")
            yce.write(
                file + "=" +
                base64.standard_b64encode(pickle.dumps(images)).decode() +
                "\n")
    common.logMessage("Done! Extracted", len(files), "files")