Example #1
0
def detectShiftJIS(f, encoding="shift_jis"):
    ret = ""
    sjis = 0
    while True:
        b1 = f.readByte()
        if b1 == 0:
            return ret
        if ret != "" and b1 in bincodes:
            ret += "<" + common.toHex(b1) + ">"
            continue
        elif b1 >= 28 and b1 <= 126 and (sjis > 0 or chr(b1) == "#" or
                                         (b1 >= 48 and b1 <= 57)
                                         or ret.startswith("#")):
            ret += chr(b1)
            continue
        b2 = f.readByte()
        if b1 == 0x0D and b2 == 0x0A:
            ret += "|"
        elif common.checkShiftJIS(b1, b2):
            f.seek(-2, 1)
            try:
                ret += f.read(2).decode(encoding).replace("〜", "~")
                sjis += 1
            except UnicodeDecodeError:
                if ret.count("UNK(") >= 5:
                    return ""
                ret += "UNK(" + common.toHex(b1) + common.toHex(b2) + ")"
        elif len(ret) > 1 and ret.count("UNK(") < 5:
            ret += "UNK(" + common.toHex(b1) + common.toHex(b2) + ")"
        else:
            return ""
Example #2
0
def readGIM(file, start=0):
    gim = GIM()
    with common.Stream(file, "rb") as f:
        f.seek(start)
        if f.readString(3) == 'MIG':
            f.seek(start + 16)
            gim.rootoff = f.tell()
            id = f.readUShort()
            if id != 0x02:
                common.logError("Unexpected id in block 0:", common.toHex(id),
                                common.toHex(f.tell() - 2))
                return None
            f.seek(2, 1)
            gim.rootsize = f.readUInt()
            nextblock = gim.rootoff + f.readUInt()
            image = None
            while nextblock > 0 and nextblock < start + gim.rootsize + 16:
                f.seek(nextblock)
                nextblock, image = readGIMBlock(f, gim, image)
        else:
            # This is a TGA file, assuming 32bit RGBA
            image = TGAImage()
            image.rootoff = f.tell()
            f.seek(start + 2)
            image.format = f.readByte()
            f.seek(9, 1)
            image.width = f.readUShort()
            image.height = f.readUShort()
            f.seek(2, 1)
            image.imgoff = f.tell()
            for i in range(image.height):
                for j in range(image.width):
                    image.colors.append(readColor(f, 0x03))
            gim = image
    return gim
Example #3
0
def readShiftJIS(f, encoding="shift_jis"):
    strlen = f.readUInt()
    sjis = ""
    i = 0
    while i < strlen:
        b1 = f.readByte()
        if b1 == 0x0A:
            sjis += "|"
            i += 1
        else:
            b2 = f.readByte()
            if (b1, b2) in fixchars:
                sjis += fixchars[(b1, b2)]
                i += 2
            elif not common.checkShiftJIS(b1, b2):
                if b2 == 0x01:
                    sjis += "UNK(" + common.toHex(b1) + common.toHex(b2) + ")"
                    i += 2
                else:
                    f.seek(-1, 1)
                    sjis += chr(b1)
                    i += 1
            else:
                f.seek(-2, 1)
                try:
                    sjis += f.read(2).decode(encoding).replace("〜", "~")
                except UnicodeDecodeError:
                    common.logError("[ERROR] UnicodeDecodeError")
                    sjis += "[ERROR" + str(f.tell() - 2) + "]"
                i += 2
    return sjis
Example #4
0
def getFontGlyphs(file):
    glyphs = {}
    with common.Stream(file, "rb", False) as f:
        # Header
        f.seek(36)
        hdwcoffset = f.readUInt()
        pamcoffset = f.readUInt()
        common.logDebug("hdwcoffset:", hdwcoffset, "pamcoffset:", pamcoffset)
        # HDWC
        f.seek(hdwcoffset - 4)
        hdwclen = f.readUInt()
        tilenum = (hdwclen - 16) // 3
        firstcode = f.readUShort()
        lastcode = f.readUShort()
        f.seek(4, 1)
        common.logDebug("firstcode:", firstcode, "lastcode:", lastcode,
                        "tilenum", tilenum)
        hdwc = []
        for i in range(tilenum):
            hdwcstart = f.readSByte()
            hdwcwidth = f.readByte()
            hdwclength = f.readByte()
            hdwc.append((hdwcstart, hdwcwidth, hdwclength))
        # PAMC
        nextoffset = pamcoffset
        while nextoffset != 0x00:
            f.seek(nextoffset)
            firstchar = f.readUShort()
            lastchar = f.readUShort()
            sectiontype = f.readUShort()
            f.seek(2, 1)
            nextoffset = f.readUInt()
            common.logDebug("firstchar:", common.toHex(firstchar), "lastchar:",
                            common.toHex(lastchar), "sectiontype:",
                            sectiontype, "nextoffset:", nextoffset)
            if sectiontype == 0:
                firstcode = f.readUShort()
                for i in range(lastchar - firstchar + 1):
                    c = common.codeToChar(firstchar + i)
                    glyphs[c] = common.FontGlyph(hdwc[firstcode + i][0],
                                                 hdwc[firstcode + i][1],
                                                 hdwc[firstcode + i][2], c,
                                                 firstchar + i, firstcode + i)
            elif sectiontype == 1:
                for i in range(lastchar - firstchar + 1):
                    charcode = f.readUShort()
                    if charcode == 0xFFFF or charcode >= len(hdwc):
                        continue
                    c = common.codeToChar(firstchar + i)
                    glyphs[c] = common.FontGlyph(hdwc[charcode][0],
                                                 hdwc[charcode][1],
                                                 hdwc[charcode][2], c,
                                                 firstchar + i, charcode)
            else:
                common.logWarning("Unknown section type", sectiontype)
    return glyphs
Example #5
0
def readGMOChunk(f, gmo, maxsize, nesting=""):
    offset = f.tell()
    id = f.readUShort()
    headerlen = f.readUShort()
    blocklen = f.readUInt()
    common.logDebug(nesting + "GMO ID", common.toHex(id), "at",
                    common.toHex(offset), "len", common.toHex(headerlen),
                    common.toHex(blocklen))
    if id == 0xa:  # Texture name
        f.seek(8, 1)
        texname = f.readEncodedString().replace(":", "")
        while texname in gmo.names:
            texname += "_"
        common.logDebug(nesting + "0x0A at", common.toHex(offset),
                        common.toHex(offset + blocklen), texname)
        gmo.names.append(texname)
    elif id == 0x8013:  # Texture data
        f.seek(4, 1)
        gmo.offsets.append(f.tell())
        common.logDebug(nesting + "0x8013 at", common.toHex(f.tell()),
                        common.toHex(offset), common.toHex(offset + blocklen))
    if id != 0x7 and id != 0xc and headerlen > 0:
        f.seek(offset + headerlen)
        common.logDebug(nesting + "Raeding nested blocks:")
        while f.tell() < offset + blocklen - 1 and f.tell() < maxsize:
            readGMOChunk(f, gmo, maxsize, nesting + " ")
        common.logDebug(nesting + "Done")
        f.seek(offset + blocklen)
    else:
        f.seek(offset + blocklen)
Example #6
0
def readShiftJIS(f, len2=False, untilZero=False, encoding="shift_jis"):
    if untilZero:
        strlen2 = 999
    else:
        if len2:
            strlen = f.readUShort()
            strlen2 = f.readUShort()
        else:
            strlen = f.readByte()
            strlen2 = f.readByte()
    sjis = ""
    i = j = 0
    padding = 0
    while i < strlen2:
        b1 = f.readByte()
        if b1 == 0x00:
            i += 1
            j += 1
            padding += 1
            if untilZero:
                return sjis, i
        else:
            b2 = f.readByte()
            if b1 == 0x0d and b2 == 0x0a:
                sjis += "|"
                i += 2
                j += 2
            elif b1 == 0x81 and b2 == 0xa5:
                sjis += ">>"
                i += 2
                j += 1
            elif not common.checkShiftJIS(b1, b2):
                f.seek(-1, 1)
                sjis += chr(b1)
                i += 1
                j += 1
            else:
                f.seek(-2, 1)
                try:
                    sjis += f.read(2).decode(encoding).replace("〜", "~")
                except UnicodeDecodeError:
                    common.logDebug("UnicodeDecodeError at", f.tell() - 2)
                    sjis += "UNK(" + common.toHex(b1) + common.toHex(b2) + ")"
                i += 2
                j += 1
    if not untilZero and j != strlen:
        common.logWarning("Wrong strlen", strlen, j)
    return sjis, i
Example #7
0
def run(data, allfile=False):
    infolder = data + "extract/"
    outfile = data + "bin_output.txt"
    with codecs.open(data + "table_input.txt", "r", "utf-8") as tablef:
        table = common.getSection(tablef, "")
    if allfile:
        game.fileranges = {"bank_1d.bin": [(0x0, 0xfff0)]}

    with codecs.open(outfile, "w", "utf-8") as out:
        common.logMessage("Extracting bin to", outfile, "...")
        for file in common.showProgress(game.fileranges):
            out.write("!FILE:" + file + "\n")
            with common.Stream(infolder + file, "rb") as f:
                for range in game.fileranges[file]:
                    f.seek(range[0])
                    while f.tell() < range[1]:
                        if (len(range) >= 3):
                            f.seek(range[2], 1)
                        pos = f.tell()
                        binstr = game.readString(f, table, True)
                        if allfile and len(binstr) > 50:
                            f.seek(pos + 2)
                            continue
                        if binstr.startswith("|"):
                            f.seek(pos + 2)
                            continue
                        if binstr != "":
                            common.logDebug("Found string at", common.toHex(pos), binstr)
                            out.write(binstr + "=\n")
        common.logMessage("Done!")
def writeLine(out, pos, byte, line, functions):
    pos -= 16
    function = ""
    if pos in functions:
        function = functions[pos] + "  "
        del functions[pos]
    out.write(str(pos).zfill(5) + " 0x" + common.toHex(byte) + ": " + line + " " + function + "\n")
Example #9
0
def readMappedImage(f, outfile, mapstart=0, num=1, bpp=2, width=0, height=0):
    f.seek(mapstart)
    maps = []
    for j in range(num):
        map = TileMap()
        if num > 1:
            map.name = outfile.replace(".png",
                                       "_" + str(j + 1).zfill(2) + ".png")
        else:
            map.name = outfile
        map.offset = f.tell()
        map.width = width
        if map.width == 0:
            map.width = f.readByte()
        map.height = height
        if map.height == 0:
            map.height = f.readByte()
        map.bpp = bpp
        common.logDebug(" ", mapstart, vars(map))
        map.map = []
        for i in range(map.width * map.height):
            tilemap = TileMap()
            tilemap.data = f.readUShort()
            tilemap.tile = tilemap.data & 0x1ff
            tilemap.pal = (tilemap.data >> 9) & 0xf
            tilemap.bank = (tilemap.data >> 13) & 1
            if tilemap.bank != 0 and bpp == 2:
                common.logError("Bank is not 0")
            tilemap.hflip = ((tilemap.data >> 14) & 1) == 1
            tilemap.vflip = ((tilemap.data >> 15) & 1) == 1
            map.map.append(tilemap)
        maps.append(map)
    common.logDebug("Map data ended at", common.toHex(f.tell()))
    return maps
Example #10
0
def repackBinaryStrings(elf,
                        section,
                        infile,
                        outfile,
                        readfunc,
                        writefunc,
                        encoding="shift_jis",
                        elfsections=[".rodata"]):
    with common.Stream(infile, "rb") as fi:
        with common.Stream(outfile, "r+b") as fo:
            for sectionname in elfsections:
                rodata = elf.sectionsdict[sectionname]
                fi.seek(rodata.offset)
                while fi.tell() < rodata.offset + rodata.size:
                    pos = fi.tell()
                    check = readfunc(fi, encoding)
                    if check != "":
                        if check in section and section[check][0] != "":
                            common.logDebug("Replacing string", check, "at",
                                            common.toHex(pos), "with",
                                            section[check][0])
                            fo.seek(pos)
                            endpos = fi.tell() - 1
                            newlen = writefunc(fo, section[check][0],
                                               endpos - pos + 1)
                            if newlen < 0:
                                fo.writeZero(1)
                                common.logError("String", section[check][0],
                                                "is too long.")
                            else:
                                fo.writeZero(endpos - fo.tell())
                        else:
                            pos = fi.tell() - 1
                    fi.seek(pos + 1)
Example #11
0
def readShiftJIS(f):
    len = f.readShort()
    pos = f.tell()
    # Check if the string is all ascii
    ascii = True
    for i in range(len - 1):
        byte = f.readByte()
        if byte != 0x0A and (byte < 32 or byte > 122):
            ascii = False
            break
    if not ascii:
        f.seek(pos)
        sjis = ""
        i = 0
        while i < len - 1:
            byte = f.readByte()
            if byte in codes:
                sjis += "<" + common.toHex(byte) + ">"
                i += 1
            else:
                f.seek(-1, 1)
                try:
                    sjis += f.read(2).decode("shift-jis").replace("〜", "~")
                except UnicodeDecodeError:
                    common.logError("UnicodeDecodeError")
                    sjis += "|"
                i += 2
        return sjis
    return ""
Example #12
0
def repackBIN(binrange, freeranges=None, readfunc=common.detectEncodedString, writefunc=common.writeEncodedString, encoding="shift_jis", comments="#",
              binin="data/extract/arm9.bin", binout="data/repack/arm9.bin", binfile="data/bin_input.txt", fixchars=[]):
    if not os.path.isfile(binfile):
        common.logError("Input file", binfile, "not found")
        return False

    common.copyFile(binin, binout)
    common.logMessage("Repacking BIN from", binfile, "...")
    section = {}
    with codecs.open(binfile, "r", "utf-8") as bin:
        section = common.getSection(bin, "", comments, fixchars=fixchars)
        chartot, transtot = common.getSectionPercentage(section)
    if type(binrange) == tuple:
        binrange = [binrange]
    notfound = common.repackBinaryStrings(section, binin, binout, binrange, freeranges, readfunc, writefunc, encoding, 0x02000000)
    for pointer in notfound:
        common.logError("Pointer", common.toHex(pointer.old), "->", common.toHex(pointer.new), "not found for string", pointer.str)
    common.logMessage("Done! Translation is at {0:.2f}%".format((100 * transtot) / chartot))
    return True
Example #13
0
def mpstopmf(infile, outfile, duration):
    with common.Stream(infile, "rb", False) as fin:
        # Check header
        check1 = fin.readUInt()
        check2 = fin.readByte()
        if check1 != 0x1ba or check2 != 0x44:
            common.logError("Input header is wrong", common.toHex(check1),
                            common.toHeck(check2))
            return
        fin.seek(0)
        mpsdata = fin.read()
    # https://github.com/TeamPBCN/pmftools/blob/main/mps2pmf/mps2pmf.cpp
    with common.Stream(outfile, "wb", False) as f:
        # Magic
        f.writeString("PSMF")
        f.writeString("0012")
        # Header size
        f.writeUInt(0x800)
        # MPS size
        f.writeUInt(len(mpsdata))
        f.seek(0x50)
        # Other header values
        f.writeUInt(0x4e)
        f.writeUInt(1)
        f.writeUShort(0x5f90)
        f.writeUShort(0)
        f.writeUInt(duration)
        f.writeUInt(0x61a8)
        f.writeUShort(1)
        f.writeUShort(0x5f90)
        f.writeUShort(0x201)
        f.writeUShort(0)
        f.writeUShort(0x34)
        f.writeUShort(0)
        f.writeUShort(1)
        f.writeUShort(0x5f90)
        f.writeUShort(0)
        f.writeUInt(duration)
        f.writeUShort(1)
        f.writeUInt(0x22)
        f.writeUShort(0x2)
        f.writeUShort(0xe000)
        f.writeUShort(0x21ef)
        f.writeUShort(0)
        f.writeUInt(0x0)
        f.writeUInt(0x1e11)
        f.writeUInt(0xbd00)
        f.writeUShort(0x2004)
        f.seek(0xa0)
        f.writeUShort(0x202)
        # Everything else is 0, write the MPS data
        f.seek(0x800)
        f.write(mpsdata)
Example #14
0
def decompress(f, complength):
    header = f.readUInt()
    type = header & 0xFF
    decomplength = ((header & 0xFFFFFF00) >> 8)
    common.logDebug("Compression header:", common.toHex(header), "type:", common.toHex(type), "length:", decomplength)
    with common.Stream() as data:
        data.write(f.read(complength))
        data.seek(0)
        if type == CompressionType.LZ10:
            return compression.decompressLZ10(data, complength, decomplength)
        elif type == CompressionType.LZ11:
            return compression.decompressLZ11(data, complength, decomplength)
        elif type == CompressionType.Huff4:
            return compression.decompressHuffman(data, complength, decomplength, 4)
        elif type == CompressionType.Huff8:
            return compression.decompressHuffman(data, complength, decomplength, 8)
        elif type == CompressionType.RLE:
            return compression.decompressRLE(data, complength, decomplength)
        else:
            common.logError("Unsupported compression type", common.toHex(type))
            return data.read()
Example #15
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))
Example #16
0
def detectShiftJIS(f):
    ret = ""
    while True:
        b1 = f.readByte()
        if ret != "" and b1 == 0:
            return ret
        if ret != "" and b1 in bincodes:
            ret += "<" + common.toHex(b1) + ">"
            continue
        b2 = f.readByte()
        if common.checkShiftJIS(b1, b2):
            f.seek(-2, 1)
            try:
                ret += f.read(2).decode("cp932").replace("〜", "~")
            except UnicodeDecodeError:
                if ret.count("UNK(") >= 5:
                    return ""
                ret += "UNK(" + common.toHex(b1) + common.toHex(b2) + ")"
        elif len(ret) > 0 and ret.count("UNK(") < 5:
            ret += "UNK(" + common.toHex(b1) + common.toHex(b2) + ")"
        else:
            return ""
Example #17
0
def readGMO(file):
    gmo = GMO()
    with common.Stream(file, "rb") as f:
        f.seek(16 + 4)
        gmo.size = f.readUInt()
        f.seek(8, 1)
        while f.tell() < gmo.size + 16:
            readGMOChunk(f, gmo, gmo.size + 16)
    for gimoffset in gmo.offsets:
        common.logDebug("Reading GIM at", common.toHex(gimoffset))
        gim = readGIM(file, gimoffset)
        gmo.gims.append(gim)
    return gmo
Example #18
0
def extractBIN(binrange, readfunc=common.detectEncodedString, encoding="shift_jis", binin="data/extract/arm9.bin", binfile="data/bin_output.txt", writepos=False, writedupes=False):
    common.logMessage("Extracting BIN to", binfile, "...")
    if type(binrange) == tuple:
        binrange = [binrange]
    strings, positions = common.extractBinaryStrings(binin, binrange, readfunc, encoding)
    with codecs.open(binfile, "w", "utf-8") as out:
        for i in range(len(strings)):
            if writepos:
                allpositions = []
                for strpos in positions[i]:
                    allpositions.append(common.toHex(strpos))
                out.write(str(allpositions) + "!")
            for j in range(1 if writedupes is False else len(positions[i])):
                out.write(strings[i] + "=\n")
    common.logMessage("Done! Extracted", len(strings), "lines")
Example #19
0
def readTIM(f, forcesize=0):
    tim = TIM()
    # Read header
    header = f.readUInt()
    if header != 0x10:
        return None
    type = f.readUInt()
    if type == 0x08:
        tim.bpp = 4
    elif type == 0x09:
        tim.bpp = 8
    elif type == 0x02:
        tim.bpp = 16
    elif type == 0x03:
        tim.bpp = 24
    else:
        common.logError("Unknown TIM type", common.toHex(type))
        return None
    # Read palettes
    if tim.bpp == 4 or tim.bpp == 8:
        tim.clutsize = f.readUInt()
        tim.clutposx = f.readUShort()
        tim.clutposy = f.readUShort()
        tim.clutwidth = f.readUShort()
        tim.clutheight = f.readUShort()
        tim.clutoff = f.tell()
        for i in range(tim.clutheight):
            clut = readCLUTData(f, tim.clutwidth)
            tim.cluts.append(clut)
    # Read size
    tim.size = f.readUInt()
    tim.posx = f.readUShort()
    tim.posy = f.readUShort()
    tim.width = f.readUShort()
    tim.height = f.readUShort()
    if tim.bpp == 4:
        tim.width *= 4
    elif tim.bpp == 8:
        tim.width *= 2
    elif tim.bpp == 24:
        tim.width //= 1.5
    tim.dataoff = f.tell()
    common.logDebug("TIM bpp", tim.bpp, "width", tim.width, "height",
                    tim.height, "size", tim.size)
    pixelnum = forcesize if forcesize != 0 else (((tim.size - 12) * 8) //
                                                 tim.bpp)
    readTIMData(f, tim, pixelnum)
    return tim
Example #20
0
def repackEXE(binrange,
              freeranges=None,
              manualptrs=None,
              readfunc=common.detectEncodedString,
              writefunc=common.writeEncodedString,
              encoding="shift_jis",
              comments="#",
              exein="",
              exeout="",
              ptrfile="data/manualptrs.asm",
              exefile="data/exe_input.txt"):
    if not os.path.isfile(exefile):
        common.logError("Input file", exefile, "not found")
        return False

    common.copyFile(exein, exeout)
    common.logMessage("Repacking EXE from", exefile, "...")
    section = {}
    with codecs.open(exefile, "r", "utf-8") as bin:
        section = common.getSection(bin, "", comments)
        chartot, transtot = common.getSectionPercentage(section)
    if type(binrange) == tuple:
        binrange = [binrange]
    notfound = common.repackBinaryStrings(section, exein, exeout, binrange,
                                          freeranges, readfunc, writefunc,
                                          encoding, 0x8000F800)
    # Handle not found pointers by manually replacing the opcodes
    if len(notfound) > 0 and manualptrs is not None:
        with open(ptrfile, "w") as f:
            for ptr in notfound:
                if ptr.old not in manualptrs:
                    common.logError("Manual pointer", common.toHex(ptr.old),
                                    "->", common.toHex(ptr.new),
                                    "not found for string", ptr.str)
                    continue
                for manualptr in manualptrs[ptr.old]:
                    ptrloc = manualptr[0]
                    ptrreg = manualptr[1]
                    common.logDebug("Reassembling manual pointer",
                                    common.toHex(ptr.old), "->",
                                    common.toHex(ptr.new), "at",
                                    common.toHex(ptrloc), ptrreg)
                    f.write(".org 0x" + common.toHex(ptrloc) + "\n")
                    f.write(".area 0x8,0x0\n")
                    f.write("  li " + ptrreg + ",0x" + common.toHex(ptr.new) +
                            "\n")
                    f.write(".endarea\n\n")
    common.logMessage("Done! Translation is at {0:.2f}%".format(
        (100 * transtot) / chartot))
    return True
Example #21
0
def readSampledCurve(f, startoff, type, samplefunc, add=0):
    startframe = f.readUShort()
    other = f.readUShort()
    endframe = other & 0b111111111111
    width = (other >> 12) & 0b11
    lograte = (other >> 14) & 0b11
    numsamples = 31  # int((endframe - startframe) / math.pow(2, lograte))
    samplesoff = f.readUInt() + startoff
    common.logDebug("  sampled curve", type, "startframe", startframe,
                    "endframe", endframe, "width", width, "lograte", lograte,
                    "numsamples", numsamples, "samplesoff",
                    common.toHex(samplesoff))
    savepos = f.tell()
    f.seek(samplesoff)
    for i in range(numsamples):
        samplefunc(f, width, add)
    f.seek(savepos)
Example #22
0
def extractEXE(binrange,
               readfunc=common.detectEncodedString,
               encoding="shift_jis",
               exein="",
               exefile="data/exe_output.txt",
               writepos=False):
    common.logMessage("Extracting EXE to", exefile, "...")
    if type(binrange) == tuple:
        binrange = [binrange]
    strings, positions = common.extractBinaryStrings(exein, binrange, readfunc,
                                                     encoding)
    with codecs.open(exefile, "w", "utf-8") as out:
        for i in range(len(strings)):
            if writepos:
                out.write(common.toHex(positions[i][0]) + "!")
            out.write(strings[i] + "=\n")
    common.logMessage("Done! Extracted", len(strings), "lines")
Example #23
0
def compress(data, type):
    with common.Stream() as out:
        length = len(data)
        out.writeByte(type.value)
        out.writeByte(length & 0xFF)
        out.writeByte(length >> 8 & 0xFF)
        out.writeByte(length >> 16 & 0xFF)
        if type == CompressionType.LZ10:
            out.write(compression.compressLZ10(data))
        elif type == CompressionType.LZ11:
            out.write(compression.compressLZ11(data))
        elif type == CompressionType.Huff4:
            out.write(compression.compressHuffman(data, 4))
        elif type == CompressionType.Huff8:
            out.write(compression.compressHuffman(data, 8))
        else:
            common.logError("Unsupported compression type", common.toHex(type))
            out.write(data)
        out.seek(0)
        return out.read()
Example #24
0
def extractBinaryStrings(elf,
                         foundstrings,
                         infile,
                         func,
                         encoding="shift_jis",
                         elfsections=[".rodata"]):
    with common.Stream(infile, "rb") as f:
        for sectionname in elfsections:
            rodata = elf.sectionsdict[sectionname]
            f.seek(rodata.offset)
            while f.tell() < rodata.offset + rodata.size:
                pos = f.tell()
                check = func(f, encoding)
                if check != "":
                    if check not in foundstrings:
                        common.logDebug("Found string at", common.toHex(pos),
                                        check)
                        foundstrings.append(check)
                    pos = f.tell() - 1
                f.seek(pos + 1)
    return foundstrings
Example #25
0
def run():
    infolder = "data/extract_NFP/SPC.NFP/"
    outfile = "data/spc_output.txt"

    common.logMessage("Extracting SPC to", outfile, "...")
    with codecs.open(outfile, "w", "utf-8") as out:
        files = common.getFiles(infolder, ".SPC")
        for file in common.showProgress(files):
            common.logDebug("Processing", file, "...")
            first = True
            with common.Stream(infolder + file, "rb") as f:
                f.seek(12)  # "SCRP" + filesize + "CODE"
                codesize = f.readUInt()
                if codesize > 10:
                    f.seek(6, 1)
                    while f.tell() < 16 + codesize - 2:
                        pos = f.tell()
                        byte = f.readByte()
                        if byte == 0x10:
                            try:
                                sjis = game.readShiftJIS(f)
                                if sjis != "":
                                    common.logDebug("Found string at", pos, "with length", len(sjis))
                                    if first:
                                        first = False
                                        out.write("!FILE:" + file + "\n")
                                    out.write(sjis + "=\n")
                                f.seek(9, 1)
                            except UnicodeDecodeError:
                                common.logError("UnicodeDecodeError")
                        elif byte == 0x15:
                            f.seek(1, 1)
                            bytelen = f.readByte()
                            f.seek(8 * bytelen, 1)
                        elif byte in game.spccodes:
                            f.seek(game.spccodes[byte], 1)
                        else:
                            common.logDebug("Unknown byte", common.toHex(byte), "at", pos)
    common.logMessage("Done! Extracted", len(files), "files")
Example #26
0
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")
Example #27
0
def run():
    infolder = "data/extract_NFP/SPC.NFP/"
    outfolder = "data/work_NFP/SPC.NFP/"
    infile = "data/spc_input.txt"
    infixfile = "data/sprite_fix.txt"
    tablefile = "data/table.txt"
    chartot = transtot = 0

    if not os.path.isfile(infile):
        common.logError("Input file", infile, "not found.")
        return
    common.makeFolder(outfolder)

    common.logMessage("Repacking SPC from", infile, "...")
    common.loadTable(tablefile)
    spritefixf = codecs.open(infixfile, "r", "utf-8")
    with codecs.open(infile, "r", "utf-8") as spc:
        files = common.getFiles(infolder, [".SPC", ".SET"])
        for file in common.showProgress(files):
            section = common.getSection(spc, file, "#", game.fixchars)
            spritefix = common.getSection(spritefixf, file, "#")
            if len(section) == 0 and len(spritefix) == 0:
                common.copyFile(infolder + file, outfolder + file)
                continue
            chartot, transtot = common.getSectionPercentage(
                section, chartot, transtot)
            common.logDebug("Repacking", file, "...")
            codepointers = []
            pointerdiff = {}
            funcpointers = {"MswMess": [], "MswHit": []}
            nextstr = None
            addstr = ""
            last29 = []
            oldstrpos = 0
            f = open(outfolder + file, "wb")
            f.close()
            with common.Stream(outfolder + file, "r+b") as f:
                with common.Stream(infolder + file, "rb") as fin:
                    # Write the header
                    f.writeString("SCRP")
                    fin.seek(4)
                    f.writeUInt(fin.readUInt())
                    f.writeString("CODE")
                    fin.seek(4, 1)
                    codesize = fin.readUInt()
                    f.writeUInt(codesize)
                    f.write(fin.read(6))
                    # Loop the file and shift pointers
                    while fin.tell() < 16 + codesize - 2:
                        pos = fin.tell()
                        byte = fin.readByte()
                        f.writeByte(byte)
                        if byte == 0x10:
                            oldlen = fin.readUShort()
                            fin.seek(-2, 1)
                            strpos = fin.tell()
                            strposf = f.tell()
                            sjis = game.readShiftJIS(fin)
                            if (sjis != "" and sjis
                                    in section) or nextstr is not None:
                                common.logDebug("Found SJIS string at",
                                                strpos + 16)
                                # Check if we have a nextstr to inject instead of using the section
                                if nextstr is None:
                                    newsjis = section[sjis].pop(0)
                                    if len(section[sjis]) == 0:
                                        del section[sjis]
                                    if newsjis == "!":
                                        newsjis = ""
                                        # Center the line
                                        savestrpos = f.tell()
                                        f.seek(oldstrpos - 28)
                                        checkbyte = f.readByte()
                                        if checkbyte == 0x02:
                                            f.seek(-1, 1)
                                            f.writeByte(1)
                                        f.seek(savestrpos)
                                    elif newsjis == "":
                                        newsjis = sjis
                                else:
                                    newsjis = nextstr
                                    nextstr = None
                                # If the string starts with <<, pad it with spaces
                                if newsjis.startswith("<<"):
                                    newsjis = newsjis[2:]
                                    pad = " " * ((20 - len(newsjis)) // 2)
                                    newsjis = pad + newsjis + pad
                                # If the string starts with "[xx]", add mouth flapping
                                if newsjis.startswith(
                                        "[") and newsjis[3] == "]":
                                    flapbyte = newsjis[1:3]
                                    newsjis = newsjis[4:]
                                    flappos = f.tell()
                                    f.seek(strposf - 5)
                                    f.writeByte(int(flapbyte, 16))
                                    f.seek(flappos)
                                # If the string contains a >>, split it and save it for later
                                if newsjis.find(">>") > 0:
                                    splitstr = newsjis.split(">>", 1)
                                    newsjis = splitstr[0]
                                    addstr = splitstr[1]
                                # Check if we have a string after
                                savepos = fin.tell()
                                fin.seek(9, 1)
                                b1 = fin.readByte()
                                b2 = fin.readByte()
                                fin.seek(savepos)
                                if b1 == 0x10 and b2 == 0x01:
                                    nextstr = ""
                                # If the string contains a |, try to turn the string into a 2-lines message
                                if newsjis.find("|") > 0:
                                    splitstr = newsjis.split("|", 1)
                                    newsjis = splitstr[0]
                                    nextstr = splitstr[1]
                                    newsjis = newsjis.replace("|", "<0A>")
                                    # Change the byte 0x1C bytes before the string to 2 if it's 1
                                    checkpos = 28
                                    if newsjis.startswith("FIX("):
                                        splitstr = newsjis.split(")", 1)
                                        newsjis = splitstr[1]
                                        checkpos = int(splitstr[0].replace(
                                            "FIX(", ""))
                                    f.seek(-checkpos, 1)
                                    checkbyte = f.readByte()
                                    if checkbyte == 0x01:
                                        f.seek(-1, 1)
                                        f.writeByte(2)
                                    f.seek(checkpos - 1, 1)
                                # Write the SJIS string
                                newlen = game.writeShiftJIS(f, newsjis)
                                lendiff = newlen - oldlen
                                if lendiff != 0:
                                    common.logDebug("Adding", lendiff, "at",
                                                    strpos)
                                    pointerdiff[strpos - 16] = lendiff
                                fin.seek(1, 1)
                            else:
                                common.logDebug(
                                    "Found ASCII or unaltered string at",
                                    strpos + 16)
                                fixPos = pos - 16
                                # Patch RITT_02 to add Dayakka's missing text
                                if file == "RITT_02.SPC" and fixPos == 1775:
                                    fin.seek(strpos + oldlen + 2)
                                    f.writeUShort(0x09)
                                    f.writeString("APP_DAYA")
                                    f.writeByte(0x00)
                                    pointerdiff[strpos - 16] = 8
                                elif file == "RITT_02.SPC" and fixPos == 1810:
                                    fin.seek(strpos + oldlen + 2)
                                    f.writeUShort(0x09)
                                    f.writeString("DAYA_004")
                                    f.writeByte(0x00)
                                    pointerdiff[strpos - 16] = 8
                                elif file == "RITT_02.SPC" and fixPos == 1845:
                                    fin.seek(strpos + oldlen + 2)
                                    f.writeUShort(0x05)
                                    f.writeString("AWAY")
                                    f.writeByte(0x00)
                                    pointerdiff[strpos - 16] = 4
                                elif file == "SYS_054.SPC" and fixPos == 4233:
                                    fin.seek(strpos + oldlen + 2)
                                    f.writeUShort(0x09)
                                    f.writeString("MSW_C083")
                                    f.writeByte(0x00)
                                else:
                                    fin.seek(strpos + 2)
                                    asciistr = fin.readNullString()
                                    if asciistr in spritefix:
                                        fin.seek(strpos + oldlen + 2)
                                        f.writeUShort(0x08)
                                        f.writeString(spritefix[asciistr][0])
                                        f.writeByte(0x00)
                                    else:
                                        fin.seek(strpos)
                                        f.write(fin.read(oldlen + 2))
                            f.write(fin.read(2))
                            pointer = fin.readUInt()
                            f.writeUInt(
                                common.shiftPointer(pointer, pointerdiff))
                            # Check if we have an addstr
                            if addstr != "" and nextstr is None:
                                addstrsplit = addstr.split(">>")
                                for addstr in addstrsplit:
                                    strsplit = addstr.split("|")
                                    startpointer = f.tell()
                                    startpointeri = fin.tell()
                                    f.writeByte(0x28)
                                    f.writeByte(0x00)
                                    funcpointers["MswMess"].append(f.tell() -
                                                                   16)
                                    f.writeByte(0x29)
                                    f.writeUInt(0x03)
                                    f.writeByte(0x80)
                                    f.writeUInt(0x00)
                                    f.writeByte(0x2A)
                                    f.writeByte(0x00)
                                    f.writeByte(0x31)
                                    f.writeByte(0x0F)
                                    f.writeUInt(0x0C)
                                    f.writeByte(0x29)
                                    f.writeUInt(0x00)
                                    funcpointers["MswHit"].append(f.tell() -
                                                                  16)
                                    f.writeByte(0x29)
                                    f.writeUInt(0x01)
                                    f.writeByte(0x80)
                                    f.writeUInt(0x00)
                                    f.writeByte(0x2A)
                                    f.writeByte(0x00)
                                    f.writeByte(0x31)
                                    f.writeByte(0x0F)
                                    f.writeUInt(0x04)
                                    f.writeByte(0x29)
                                    f.writeUInt(last29[len(last29) - 1])
                                    f.writeByte(0x10)
                                    strpointer = f.tell()
                                    game.writeShiftJIS(f, strsplit[0])
                                    f.writeByte(0x22)
                                    f.writeByte(0x00)
                                    f.writeUInt(strpointer - 16 - 4)
                                    f.writeByte(0x28)
                                    f.writeByte(0x00)
                                    f.writeByte(0x10)
                                    strpointer2 = f.tell()
                                    game.writeShiftJIS(
                                        f, strsplit[1]
                                        if len(strsplit) == 2 else "")
                                    f.writeByte(0x22)
                                    f.writeByte(0x00)
                                    f.writeUInt(strpointer2 - 16 - 4)
                                    endpointer = f.tell()
                                    common.logDebug("Adding new str",
                                                    endpointer - startpointer,
                                                    "at", startpointeri)
                                    if startpointeri - 16 not in pointerdiff:
                                        pointerdiff[startpointeri - 16] = 0
                                    pointerdiff[
                                        startpointeri -
                                        16] += endpointer - startpointer
                                addstr = ""
                            oldstrpos = strposf
                        elif byte == 0x15:
                            f.write(fin.read(1))
                            bytelen = fin.readByte()
                            f.writeByte(bytelen)
                            for i in range(bytelen):
                                f.write(fin.read(4))
                                codepointers.append(f.tell())
                                f.write(fin.read(4))
                        elif byte in game.spccodes:
                            if byte == 0x11:
                                codepointers.append(f.tell())
                            elif byte == 0x12:
                                codepointers.append(f.tell() + 1)
                            elif byte == 0x29:
                                last29.append(fin.readUInt())
                                fin.seek(-4, 1)
                            # Patch SYS_046/7 and fix the disappearing cut-in sprites
                            fixPos = pos - 16
                            if (file == "SYS_046.SPC" and byte == 0x29
                                    and fixPos
                                    in [9660, 11356, 11915, 13108, 13646]) or (
                                        file == "SYS_047.SPC" and byte == 0x29
                                        and fixPos == 1607):
                                f.writeUInt(0x0A)
                                fin.seek(4, 1)
                            else:
                                f.write(fin.read(game.spccodes[byte]))
                        common.logDebug("Unknown byte", common.toHex(byte),
                                        "at", pos)
                    f.writeByte(0x8F)
                    f.writeByte(0x00)
                    f.writeByte(0x00)
                    endpos = f.tell()
                    # Shift the other code pointers
                    for codepointer in codepointers:
                        f.seek(codepointer)
                        pointer = f.readUInt()
                        f.seek(-4, 1)
                        f.writeUInt(common.shiftPointer(pointer, pointerdiff))
                    # Write the code section size in the header
                    f.seek(12)
                    f.writeUInt(endpos - 16)
                    f.seek(endpos)
                    # Function section
                    fin.seek(codesize + 16)
                    f.writeString("FUNC")
                    fin.seek(4, 1)
                    funcsize = fin.readUInt()
                    f.writeUInt(funcsize)
                    # Copy the function section while shifting pointers
                    common.logDebug(str(funcpointers))
                    while True:
                        # Read the function name
                        function = fin.readNullString()
                        if function == "":
                            break
                        f.writeString(function)
                        f.writeZero(1)
                        common.logDebug("Found function:", function)
                        # Read the pointers until we find 0
                        while True:
                            pointer = fin.readUInt()
                            if pointer == 0:
                                f.writeUInt(0)
                                break
                            else:
                                pointer = common.shiftPointer(
                                    pointer, pointerdiff)
                                if function in funcpointers and len(
                                        funcpointers[function]) > 0:
                                    for newpointer in funcpointers[function]:
                                        common.logDebug(
                                            function, "new:", newpointer,
                                            "poi:", pointer)
                                        if pointer > newpointer:
                                            f.writeUInt(newpointer)
                                            funcpointers[function].remove(
                                                newpointer)
                                f.writeUInt(pointer)
                    f.writeZero(1)
                    # Write the file size in the header
                    pos = f.tell()
                    f.seek(4)
                    f.writeUInt(pos - 4)
                    f.seek(pos)
                    # Write TERM and pad with 0s
                    f.writeString("TERM")
                    f.writeZero(16 - (f.tell() % 16))
    common.logMessage("Done! Translation is at {0:.2f}%".format(
        (100 * transtot) / chartot))
Example #28
0
def readCPK(file):
    with common.Stream(file, "rb") as f:
        magic = f.readString(4)
        if magic != 'CPK ':
            common.logError("Wrong magic:", magic)
            return None
        cpk = CPK()
        utfoffset = f.tell()
        utfpacket, utfsize, encrypted = readUTFData(f)
        cpak = CPKFileEntry()
        cpak.filename = "CPK_HDR"
        cpak.fileoffsetpos = f.tell() + 0x10
        cpak.filesize = utfsize
        cpak.encrypted = encrypted
        cpak.filetype = "CPK"
        utf = readUTF(utfpacket, utfoffset, True)
        if utf is None:
            common.logError("Error reading first UTF")
            return None
        cpak.utf = utf
        cpk.filetable.append(cpak)
        tocoffset, tocoffsetpos = utf.getColumnData(0, "TocOffset", UTFStructTypes.DATA_TYPE_UINT64)
        etocoffset, etocoffsetpos = utf.getColumnData(0, "EtocOffset", UTFStructTypes.DATA_TYPE_UINT64)
        itocoffset, itocoffsetpos = utf.getColumnData(0, "ItocOffset", UTFStructTypes.DATA_TYPE_UINT64)
        gtocoffset, gtocoffsetpos = utf.getColumnData(0, "GtocOffset", UTFStructTypes.DATA_TYPE_UINT64)
        contentoffset, contentoffsetpos = utf.getColumnData(0, "ContentOffset", UTFStructTypes.DATA_TYPE_UINT64)
        cpk.filetable.append(CPKFileEntry.createEntry("CONTENT_OFFSET", contentoffset, UTFStructTypes.DATA_TYPE_UINT64, contentoffsetpos, "CPK", "CONTENT", False))
        files, _ = utf.getColumnData(0, "Files", UTFStructTypes.DATA_TYPE_UINT32)
        cpk.align, _ = utf.getColumnData(0, "Align", UTFStructTypes.DATA_TYPE_UINT16)
        common.logDebug("tocoffset", common.toHex(tocoffset), "tocoffsetpos", common.toHex(tocoffsetpos))
        common.logDebug("etocoffset", common.toHex(etocoffset), "etocoffsetpos", common.toHex(etocoffsetpos))
        common.logDebug("itocoffset", common.toHex(itocoffset), "itocoffsetpos", common.toHex(itocoffsetpos))
        common.logDebug("gtocoffset", common.toHex(gtocoffset), "gtocoffsetpos", common.toHex(gtocoffsetpos))
        common.logDebug("contentoffset", common.toHex(contentoffset), "contentoffsetpos", common.toHex(contentoffsetpos))
        common.logDebug("files", common.toHex(files), "align", common.toHex(cpk.align))
        if tocoffset != 0xffffffffffffffff:
            cpk.filetable.append(CPKFileEntry.createEntry("TOC_HDR", tocoffset, UTFStructTypes.DATA_TYPE_UINT64, tocoffsetpos, "CPK", "HDR", False))
            readTOC(f, cpk, tocoffset, contentoffset)
        if etocoffset != 0xffffffffffffffff:
            cpk.filetable.append(CPKFileEntry.createEntry("ETOC_HDR", etocoffset, UTFStructTypes.DATA_TYPE_UINT64, etocoffsetpos, "CPK", "HDR", False))
            readETOC(f, cpk, etocoffset)
        if itocoffset != 0xffffffffffffffff:
            cpk.filetable.append(CPKFileEntry.createEntry("ITOC_HDR", itocoffset, UTFStructTypes.DATA_TYPE_UINT64, itocoffsetpos, "CPK", "HDR", False))
            readITOC(f, cpk, itocoffset, contentoffset, cpk.align)
        if gtocoffset != 0xffffffffffffffff:
            cpk.filetable.append(CPKFileEntry.createEntry("GTOC_HDR", gtocoffset, UTFStructTypes.DATA_TYPE_UINT64, gtocoffsetpos, "CPK", "HDR", False))
            readGTOC(f, cpk, gtocoffset)
        return cpk
Example #29
0
def run(data):
    fontfile = data + "font.png"
    fontconfigfile = data + "fontconfig.txt"
    infont = data + "font_output.png"
    outfont = data + "font_input.png"
    outtable = data + "table.txt"
    bankfile = data + "repack/bank_10.bin"

    common.logMessage("Repacking font ...")
    # List of characters and positions in the font.png file
    chars = {}
    positions = {}
    bigrams = []
    with codecs.open(fontconfigfile, "r", "utf-8") as f:
        fontconfig = common.getSection(f, "")
        x = 0
        for c in fontconfig:
            if fontconfig[c][0] == "":
                if c in bigrams:
                    common.logError("Duplicate bigram", c)
                    continue
                bigrams.append(c)
                chars[c] = chars[c[0]] + 1 + chars[c[1]]
                continue
            chars[c] = int(fontconfig[c][0])
            positions[c] = x
            if chars[c] > 7:
                x += 15
            else:
                x += 7
    glyphs = game.readFontGlyphs(fontconfigfile)
    skipcodes = [0x0, 0x40, 0x80, 0xc0]

    # Open the images
    img = Image.open(infont).convert("RGB")
    pixels = img.load()
    font = Image.open(fontfile).convert("RGB")
    fontpixels = font.load()

    # Generate the image and table
    fontx = 0
    fonty = 0xa3 * 16 + 1
    fontwidths = []
    x = 0x20
    tablestr = ""
    for item in glyphs:
        while x in skipcodes:
            fontx += 16
            if fontx == 16 * 4:
                fontx = 0
                fonty += 16
            x += 1
            fontwidths.append(0)
        if item in bigrams:
            for i2 in range(7):
                for j2 in range(15):
                    pixels[fontx + i2,
                           fonty + j2] = fontpixels[positions[item[0]] + i2,
                                                    j2]
            for i2 in range(7):
                for j2 in range(15):
                    pixels[fontx + chars[item[0]] + 1 + i2,
                           fonty + j2] = fontpixels[positions[item[1]] + i2,
                                                    j2]
        else:
            for i2 in range(15 if chars[item] > 7 else 7):
                for j2 in range(15):
                    pixels[fontx + i2,
                           fonty + j2] = fontpixels[positions[item] + i2, j2]
        if chars[item] < 15:
            for i2 in range(15 - chars[item]):
                for j2 in range(15):
                    pixels[fontx + chars[item] + i2,
                           fonty + j2] = fontpixels[positions[" "], j2]
        fontwidths.append(chars[item] + 1)
        fontx += 16
        if fontx == 16 * 4:
            fontx = 0
            fonty += 16
        tablestr += (item + "=" + common.toHex(x) + "\n")
        x += 1
        if fonty >= img.height:
            break
    with codecs.open(outtable, "w", "utf-8") as f:
        f.write(tablestr)
    # Replace the original ASCII character as well
    fontx = 0
    fonty = 1
    asciiglyphs = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    asciiwidths = []
    for asciiglyph in asciiglyphs:
        for i2 in range(7):
            for j2 in range(15):
                pixels[fontx + i2,
                       fonty + j2] = fontpixels[positions[asciiglyph] + i2, j2]
        for i2 in range(15 - chars[asciiglyph]):
            for j2 in range(15):
                pixels[fontx + chars[asciiglyph] + i2,
                       fonty + j2] = fontpixels[positions[" "], j2]
        asciiwidths.append(chars[asciiglyph] + 1)
        fontx += 16
        if fontx == 16 * 4:
            fontx = 0
            fonty += 16
    img.save(outfont, "PNG")

    # Put the font back in the bank and set the font widths
    with common.Stream(bankfile, "rb+") as f:
        ws.repackTiledImage(f, outfont, 16 * 4, 16 * 244)
        f.seek(0)
        for i in range(len(asciiwidths)):
            f.writeByte(asciiwidths[i])
            f.seek(63, 1)
        f.seek((0xa3 * 4) * 64)
        for i in range(len(fontwidths)):
            if 0x20 + i not in skipcodes:
                f.writeByte(fontwidths[i])
            else:
                f.seek(1, 1)
            f.seek(63, 1)

    common.logMessage("Done!")
Example #30
0
def run(firstgame):
    infolder = "data/extract/data/script/"
    outfolder = "data/repack/data/script/"
    infile = "data/wsb_input.txt"
    fontfile = "data/replace/data/font/lcfont12.NFTR"
    if not os.path.isfile(infile):
        common.logError("Input file", infile, "not found")
        return

    encoding = "shift_jis" if firstgame else "shift_jisx0213"
    common.logMessage("Repacking WSB from", infile, "...")
    # Read the glyph size from the font
    if not os.path.isfile(fontfile):
        fontfile = fontfile.replace("replace/", "extract/")
    glyphs = nitro.readNFTR(fontfile).glyphs
    wordwrap = game.wordwrap[0] if firstgame else game.wordwrap[1]
    fixchars = game.getFixChars()
    with codecs.open(infile, "r", "utf-8") as wsb:
        commonsection = common.getSection(wsb, "COMMON", fixchars=fixchars)
        chartot, transtot = common.getSectionPercentage(commonsection)
        files = common.getFiles(infolder, ".wsb")
        for file in common.showProgress(files):
            section = common.getSection(wsb, file, fixchars=fixchars)
            chartot, transtot = common.getSectionPercentage(
                section, chartot, transtot)
            # Repack the file
            pointerdiff = {}
            pointers = {}
            common.logDebug(" Processing", file, "...")
            insize = os.path.getsize(infolder + file)
            with common.Stream(infolder + file, "rb") as fin:
                with common.Stream(outfolder + file, "wb") as f:
                    # Copy header
                    fin.seek(4)  # 0x10
                    codeoffset = fin.readUInt()
                    if codeoffset == 0 and not firstgame:
                        fin.seek(0)
                        f.write(fin.read())
                        continue
                    fin.seek(8, 1)  # all 0xFF
                    unk = fin.readUInt()
                    textoffset = fin.readUInt()
                    codeoffset2 = fin.readUInt()
                    fin.seek(0)
                    f.write(fin.read(32))
                    # Write new strings
                    while fin.tell() < codeoffset:
                        pos = fin.tell()
                        fpos = f.tell()
                        b1 = fin.readByte()
                        b2 = fin.readByte()
                        f.writeByte(b1)
                        f.writeByte(b2)
                        if (b1 == 0x55 and b2 == 0x08) or (b1 == 0x95
                                                           and b2 == 0x10):
                            sjis, oldlen = game.readShiftJIS(
                                fin, b1 == 0x95, False, encoding)
                            # Fix a bugged line with wrong speaker code
                            if file == "event/ev_mou/mou_10.wsb" and sjis == "そうじゃな。|わっちの直感が申すには……>>":
                                f.seek(fpos - 12)
                                f.writeByte(0x53)
                                f.seek(fpos + 2)
                            strreplaced = False
                            if sjis != "" and sjis != ">>":
                                sjissplit = sjis.split(">>")
                                for i in range(len(sjissplit)):
                                    newsjis = sjisline = sjissplit[i]
                                    if sjisline in commonsection:
                                        newsjis = commonsection[sjisline][0]
                                    elif sjisline in section:
                                        newsjis = section[sjisline].pop(0)
                                        if len(section[sjisline]) == 0:
                                            del section[sjisline]
                                    if newsjis != "":
                                        # Disable wordwrap for strings that contain replace codes
                                        if newsjis.count("@<") > 0:
                                            sjissplit[i] = newsjis
                                        # Check for automatic centering
                                        elif newsjis.count("<<") > 0:
                                            sjissplit[i] = common.centerLines(
                                                newsjis,
                                                glyphs,
                                                wordwrap,
                                                centercode="<<")
                                        else:
                                            sjissplit[i] = common.wordwrap(
                                                newsjis, glyphs, wordwrap)
                                            if sjissplit[i].count("|") > 2:
                                                common.logError(
                                                    "Sub-line too long:",
                                                    sjissplit[i])
                                                cutsplit = sjissplit[i].split(
                                                    "|")
                                                sjissplit[i] = cutsplit[
                                                    0] + "|" + cutsplit[
                                                        1] + "|" + cutsplit[2]
                                newsjis = ">>".join(sjissplit)
                                if newsjis != sjis and newsjis != "" and newsjis != ">>":
                                    common.logDebug("Repacking", newsjis, "at",
                                                    common.toHex(pos))
                                    strreplaced = True
                                    if newsjis == "!":
                                        newsjis = ""
                                    newlen = game.writeShiftJIS(
                                        f, newsjis, b1 == 0x95, False, 0,
                                        encoding, firstgame)
                                    lendiff = newlen - oldlen
                                    if newlen > (0x80 if firstgame else
                                                 0x70) and b1 == 0x55:
                                        common.logDebug(
                                            "String is too long", newlen,
                                            "changing to 0x95")
                                        f.seek(fpos)
                                        f.writeByte(0x95)
                                        f.writeByte(0x10)
                                        game.writeShiftJIS(
                                            f, newsjis, True, False, 0,
                                            encoding, firstgame)
                                        lendiff += 2
                                    if lendiff != 0:
                                        common.logDebug(
                                            "Adding", lendiff, "at", pos)
                                        pointerdiff[pos - 16] = lendiff
                            if not strreplaced:
                                fin.seek(pos + 2)
                                f.write(
                                    fin.read(oldlen +
                                             (4 if b1 == 0x95 else 2)))
                        elif (b1, b2) in game.wsbcodes:
                            if (b1, b2) in game.wsbpointers:
                                if b1 == 0x81 and b2 == 0xB9:
                                    f.write(fin.read(2))
                                pointer = fin.readUInt()
                                pointers[f.tell()] = pointer
                                f.writeUInt(pointer)
                            else:
                                f.write(fin.read(game.wsbcodes[(b1, b2)]))
                    # Write code section
                    if codeoffset > 0:
                        newcodeoffset = f.tell()
                        codediff = 0
                        codenum = fin.readUInt()
                        f.writeUInt(codenum)
                        for i in range(codenum):
                            fin.seek(codeoffset + 4 + 4 * i)
                            f.seek(newcodeoffset + 4 + 4 * i)
                            codepointer = fin.readUInt()
                            f.writeUInt(codepointer + codediff)
                            fin.seek(codeoffset + codepointer)
                            f.seek(newcodeoffset + codepointer + codediff)
                            sjis, codelen = game.readShiftJIS(
                                fin, False, True, encoding)
                            strreplaced = False
                            if sjis in section or sjis in commonsection:
                                if sjis in commonsection:
                                    newsjis = commonsection[sjis][0]
                                else:
                                    newsjis = section[sjis].pop(0)
                                    if len(section[sjis]) == 0:
                                        del section[sjis]
                                if newsjis != "":
                                    strreplaced = True
                                    newcodelen = game.writeShiftJIS(
                                        f, newsjis, False, True, 0, encoding,
                                        firstgame)
                                    if codelen != newcodelen:
                                        codediff += newcodelen - codelen
                            if not strreplaced:
                                fin.seek(codeoffset + codepointer)
                                f.write(fin.read(codelen))
                    f.writeZero(insize - fin.tell())
                    # Write new header offsets
                    f.seek(4)
                    f.writeUInt(common.shiftPointer(codeoffset, pointerdiff))
                    f.seek(8, 1)
                    f.writeUInt(common.shiftPointer(unk, pointerdiff))
                    f.writeUInt(common.shiftPointer(textoffset, pointerdiff))
                    f.writeUInt(common.shiftPointer(codeoffset2, pointerdiff))
                    # Shift pointers
                    for k, v in pointers.items():
                        f.seek(k)
                        f.writeUInt(common.shiftPointer(v, pointerdiff))
    common.logMessage("Done! Translation is at {0:.2f}%".format(
        (100 * transtot) / chartot))