def run(filename, processed=False):
    infolder = "data/work_NFP/SPC.NFP/" if processed else "data/extract_NFP/SPC.NFP/"
    outfile = "data/analyze_spc.txt"
    tablefile = "data/table.txt"
    common.loadTable(tablefile)
    functions = {}
    inversetable = {}
    for bigram, code in common.table.items():
        inversetable[code] = bigram

    common.logMessage("Analyzing", filename, "...")
    with codecs.open(outfile, "w", "utf-8") as out:
        out.write(filename + "\n")
        with common.Stream(infolder + filename, "rb") as f:
            f.seek(12)  # "SCRP" + filesize + "CODE"
            codesize = f.readUInt()
            if codesize > 10:
                f.seek(16 + codesize + 8)
                while True:
                    function = f.readNullString()
                    if function == "":
                        break
                    # Read the pointers until we find 0
                    i = 0
                    while True:
                        pointer = f.readUInt()
                        if pointer == 0:
                            break
                        else:
                            if pointer in functions:
                                functions[pointer] += "," + function + "#" + str(i)
                            else:
                                functions[pointer] = function + "#" + str(i)
                        i += 1
                f.seek(16 + 6)
                while f.tell() < 16 + codesize - 2:
                    pos = f.tell()
                    byte = f.readByte()
                    if byte == 0x10:
                        line = f.readBytes(2)
                        f.seek(-2, 1)
                        convert = ""
                        if processed:
                            sjislen = f.readUShort()
                            try:
                                i = 0
                                while i < sjislen - 1:
                                    strbyte = f.readByte()
                                    if strbyte in game.codes:
                                        convert += "<" + common.toHex(strbyte) + ">"
                                        i += 1
                                    else:
                                        f.seek(-1, 1)
                                        char = common.toHex(f.readByte()) + common.toHex(f.readByte())
                                        convert += inversetable[char]
                                        i += 2
                            except KeyError:
                                convert = ""
                        if convert != "":
                            line += "\"" + convert + "\" "
                        else:
                            f.seek(pos + 1)
                            sjis = game.readShiftJIS(f)
                            if sjis != "":
                                line += "\"" + sjis + "\" "
                            else:
                                f.seek(pos + 1)
                                asciilen = f.readUShort()
                                asciistr = f.read(asciilen - 1)
                                line += "\"" + asciistr.decode("ascii").replace("\r", "").replace("\n", "") + "\" "
                        line += f.readBytes(9)
                        writeLine(out, pos, byte, line, functions)
                    elif byte == 0x15:
                        line = f.readBytes(2)
                        f.seek(-1, 1)
                        bytelen = f.readByte()
                        for i in range(bytelen):
                            line += f.readBytes(8)
                        writeLine(out, pos, byte, line, functions)
                    elif byte in game.spccodes:
                        writeLine(out, pos, byte, f.readBytes(game.spccodes[byte]), functions)
                    else:
                        writeLine(out, pos, byte, "Unknown!", functions)
                for k, v in functions.items():
                    out.write("Missing function pointer " + str(k) + ": " + str(v) + "\n")
    common.logMessage("Done! Open", outfile)
Beispiel #2
0
def repack(no_rom, bin, tdg, kpc, spc, vsc, yce, deb, force, analyze):
    all = not bin and not tdg and not kpc and not spc and not vsc and not yce
    if all or bin or spc:
        import repack_font
        repack_font.run()
    if all or bin:
        import repack_bin
        repack_bin.run()
        common.armipsPatch("bin_patch.asm")
    if all or tdg:
        common.copyFile("data/extract_NFP/NFP3D.NFP/MSW_C053.3DG",
                        "data/extract_NFP/NFP3D.NFP/MSW_C083.3DG")
        nitro.repackNSBMD("data/work_3DG/", "data/extract_NFP/NFP3D.NFP/",
                          "data/work_NFP/NFP3D.NFP/", ".3DG", game.write3DG)
        import patch_jnt
        patch_jnt.run()
    if all or kpc:
        import repack_kpc
        repack_kpc.run()
    if all or spc:
        import repack_spc
        repack_spc.run()
    if all or vsc:
        import repack_vsc
        repack_vsc.run()
    if all or yce:
        import repack_yce
        repack_yce.run()

    if not no_rom:
        debfolder = "data/extract_NFP/"
        nfpin = "data/extract/data/"
        nfpwork = "data/work_NFP/"
        nfpout = "data/repack/data/"
        # Debug map
        if deb:
            common.copyFile(debfolder + "SPC.NFP/S_DEBUG.SPC",
                            nfpwork + "SPC.NFP/S_MAIN.SPC")
        else:
            common.copyFile(debfolder + "SPC.NFP/S_MAIN.SPC",
                            nfpwork + "SPC.NFP/S_MAIN.SPC")
        if force != "":
            common.copyFile(nfpwork + "SPC.NFP/" + force + ".SPC",
                            nfpwork + "SPC.NFP/SYS_000.SPC")
        # Repack NFP archives
        if os.path.isdir(replacefolder):
            common.mergeFolder(replacefolder, nfpwork)
        common.logMessage("Repacking NFP ...")
        files = common.getFiles(nfpin, ".NFP")
        for file in common.showProgress(files):
            subfiles = os.listdir(nfpwork + file)
            filenum = len(subfiles)
            # Data starts at header (80 bytes) + entries (24 bytes each)
            datapos = 80 + (24 * filenum)
            # The file list is padded with 0s if it doesn't start at a multiple of 16
            datapos += datapos % 16
            common.logDebug("Repacking", file, "with", filenum, "files ...")
            with common.Stream(nfpout + file, "wb") as f:
                f.writeString("NFP2.0 (c)NOBORI 1997-2006")
                f.writeZero(26)
                f.writeInt(filenum)
                f.writeInt(0x50)
                f.writeInt(datapos)
                for i in range(filenum):
                    subfile = subfiles[i]
                    subfilepath = nfpwork + file + "/" + subfile
                    filesize = os.path.getsize(subfilepath)
                    f.seek(80 + (24 * i))
                    f.writeString(subfile)
                    if len(subfile) < 16:
                        f.writeZero(16 - len(subfile))
                    f.writeInt(datapos)
                    f.writeInt(filesize * 4)
                    f.seek(datapos)
                    with open(subfilepath, "rb") as newf:
                        f.write(newf.read(filesize))
                    datapos += filesize
        # Edit banner and repack ROM
        nds.editBannerTitle(
            bannerfile,
            "Tengen Toppa\nGurren Lagann\nKonami Digital Entertainment")
        nds.repackRom(romfile, rompatch, outfolder, patchfile)
Beispiel #3
0
def extract(rom, bin, tdg, kpc, spc, vsc, yce):
    all = not rom and not bin and not tdg and not kpc and not spc and not vsc and not yce
    if all or rom:
        nds.extractRom(romfile, infolder, outfolder)
        # Extract NFP archives
        nfpin = "data/extract/data/"
        nfpout = "data/extract_NFP/"
        nfpwork = "data/work_NFP/"
        common.logMessage("Extracting NFP ...")
        common.makeFolder(nfpout)
        files = common.getFiles(nfpin, ".NFP")
        for file in common.showProgress(files):
            common.logDebug("Processing", file, "...")
            common.makeFolder(nfpout + file)
            with common.Stream(nfpin + file, "rb") as f:
                f.seek(52)  # Header: NFP2.0 (c)NOBORI 1997-2006
                filenum = f.readInt()
                f.seek(4, 1)  # Always 0x50
                datastart = f.readInt()
                f.seek(16, 1)  # All 0
                common.logDebug("Found", filenum, "files, data starting at",
                                datastart)
                for i in range(filenum):
                    # Filenames are always 16 bytes long, padded with 0s
                    subname = f.readString(16)
                    # Read starting position and size (multiplied by 4)
                    startpos = f.readInt()
                    size = f.readInt() // 4
                    # Extract the file
                    common.logDebug("Extracting", subname, "starting at",
                                    startpos, "with size", size)
                    savepos = f.tell()
                    f.seek(startpos)
                    with common.Stream(nfpout + file + "/" + subname,
                                       "wb") as newf:
                        newf.write(f.read(size))
                    f.seek(savepos)
        # Copy everything to the work folder
        common.copyFolder(nfpout, nfpwork)
        common.logMessage("Done! Extracted", len(files), "archives")
    if all or bin:
        import extract_bin
        extract_bin.run()
    if all or tdg:
        nitro.extractNSBMD("data/extract_NFP/NFP3D.NFP/", "data/out_3DG/",
                           ".3DG")
    if all or kpc:
        import extract_kpc
        extract_kpc.run()
    if all or spc:
        import extract_spc
        extract_spc.run()
    if all or vsc:
        import extract_vsc
        extract_vsc.run()
    if all or yce:
        for i in range(1, 8):
            common.copyFile(
                "data/extract_NFP/NFP2D.NFP/AV01_0" + str(i) + ".YCE",
                "data/extract_NFP/NFP2D.NFP/AV00_0" + str(i) + ".YCE")
        import extract_yce
        extract_yce.run()
Beispiel #4
0
def run(firstgame, analyzefile):
    infolder = "data/extract/data/script/"
    outfile = "data/wsb_output.txt"
    commonfile = "data/common.txt"
    analyzeout = "data/wsb_analysis.txt"

    commonstr = {}
    # Read common strings from another file
    if os.path.isfile(commonfile):
        with codecs.open(commonfile, "r", "utf-8") as commonf:
            commonstr = common.getSection(commonf, "COMMON")
    encoding = "shift_jis" if firstgame else "shift_jisx0213"
    common.logMessage("Extracting WSB to", outfile, "...")
    with codecs.open(analyzeout, "w", "utf-8") as a:
        with codecs.open(outfile, "w", "utf-8") as out:
            if len(commonstr) > 0:
                out.write("!FILE:COMMON\n")
                for s in commonstr:
                    out.write(s + "=\n")
            files = common.getFiles(infolder, ".wsb")
            for file in common.showProgress(files):
                analyze = analyzefile != "" and file.endswith(analyzefile)
                if analyzefile != "" and not analyze:
                    continue
                first = True
                common.logDebug("Processing", file, "...")
                with common.Stream(infolder + file, "rb") as f:
                    f.seek(4)  # 0x10
                    codeoffset = f.readUInt()
                    f.seek(8, 1)  # all 0xFF
                    unk = f.readUInt()
                    textoffset = f.readUInt()
                    codeoffset2 = f.readUInt()
                    common.logDebug("codeoffset:", codeoffset, "unk:", unk,
                                    "textoffset:", textoffset, "codeoffset2:",
                                    codeoffset2)
                    f.seek(4, 1)
                    # Parse the various code blocks while looking for strings
                    while f.tell() < codeoffset:
                        pos = f.tell()
                        b1 = f.readByte()
                        b2 = f.readByte()
                        if (b1 == 0x55 and b2 == 0x08) or (b1 == 0x95
                                                           and b2 == 0x10):
                            # Found a string pointer
                            if analyze:
                                lenline = f.readBytes(4 if b1 == 0x95 else 2)
                                f.seek(-(4 if b1 == 0x95 else 2), 1)
                            sjis, strlen = game.readShiftJIS(
                                f, b1 == 0x95, False, encoding)
                            if sjis != "" and sjis != ">>" and sjis != " ":
                                sjissplit = sjis.split(">>")
                                for sjisline in sjissplit:
                                    if sjisline != "" and sjisline != " " and sjisline not in commonstr:
                                        if first:
                                            out.write("!FILE:" + file + "\n")
                                            first = False
                                        out.write(sjisline + "=\n")
                            if analyze:
                                # Try to calculate the length to check if the calculation is correct
                                addlen = ""
                                with common.Stream() as test:
                                    game.writeShiftJIS(test, sjis, b1 == 0x95,
                                                       False, 0, encoding,
                                                       firstgame)
                                    testlen = test.tell()
                                    test.seek(0)
                                    addlen = test.readBytes(4 if b1 ==
                                                            0x95 else 2)
                                    if lenline != addlen:
                                        test.seek(0)
                                        addlen += "\nDIFF\n" + test.readBytes(
                                            testlen) + "\n"
                                        fpos = f.tell()
                                        f.seek(pos + 2)
                                        addlen += f.readBytes(fpos - f.tell())
                                writeLine(a, pos, b1, b2,
                                          lenline + sjis + " " + addlen)
                        elif (b1, b2) in game.wsbcodes:
                            if analyze:
                                ptrstr = ""
                                if (b1, b2) in game.wsbpointers:
                                    ptrstr += "ptr"
                                writeLine(
                                    a, pos, b1, b2,
                                    f.readBytes(game.wsbcodes[(b1, b2)]) +
                                    ptrstr)
                            else:
                                f.seek(game.wsbcodes[(b1, b2)], 1)
                        else:
                            if analyze:
                                writeLine(a, pos, b1, b2, "Unknown!")
                    if codeoffset > 0:
                        codenum = f.readUInt()
                        for i in range(codenum):
                            f.seek(codeoffset + 4 + 4 * i)
                            codepointer = f.readUInt()
                            f.seek(codeoffset + codepointer)
                            sjis, codelen = game.readShiftJIS(
                                f, False, True, encoding)
                            # Ignore ASCII strings and a particular debug line found in every file
                            if not common.isAscii(sjis) and sjis.find(
                                    "%d, %d") < 0 and sjis not in commonstr:
                                if first:
                                    out.write("!FILE:" + file + "\n")
                                    first = False
                                out.write(sjis + "=\n")
                            if analyze:
                                writeLine(a, i, 0, 0,
                                          str(codepointer) + " " + sjis)
    common.logMessage("Done! Extracted", len(files), "files")
def run():
    infolder = "data/extract_NFP/NFP2D.NFP/"
    workfolder = "data/work_KPC/"
    outfolder = "data/work_NFP/NFP2D.NFP/"
    common.copyFolder(infolder, outfolder)

    common.logMessage("Repacking KPC from", workfolder, "...")
    files = common.getFiles(infolder, ".KPC")
    for file in common.showProgress(files):
        pngname = file.replace(".KPC", ".png")
        if not os.path.isfile(workfolder + pngname):
            common.copyFile(infolder + file, outfolder + file)
            continue
        common.logDebug("Processing", file, "...")
        with common.Stream(infolder + file, "rb") as fin:
            with common.Stream(outfolder + file, "wb") as f:
                # Find palette offset
                fin.seek(9)
                palcompressed = fin.readByte() == 1
                fin.seek(2, 1)
                width = fin.readUShort() * 8
                height = fin.readUShort() * 8
                fin.seek(128)
                palsize = fin.readUInt()
                paloffset = fin.readUInt()
                # Read palette
                fin.seek(paloffset)
                if palcompressed:
                    paldata = common.decompress(fin, palsize)
                else:
                    paldata = fin.read(palsize)
                # Fix transparency for EQ_M0* files since their palette colors 0 and 1 are the same.
                tiles, maps = game.readMappedImage(workfolder + pngname, width,
                                                   height, paldata,
                                                   file.startswith("EQ_M0"))
                # Copy the header
                fin.seek(0)
                f.write(fin.read(192))
                # Write map data
                mapstart = f.tell()
                for map in maps:
                    mapdata = (map[0] << 12) + (map[1] << 11) + (
                        map[2] << 10) + map[3]
                    f.writeUShort(mapdata)
                mapend = f.tell()
                f.writeByte(0)
                # Write tile data
                tilestart = f.tell()
                for tile in tiles:
                    for i in range(32):
                        index2 = tile[i * 2]
                        index1 = tile[i * 2 + 1]
                        f.writeByte(((index1) << 4) | index2)
                tileend = f.tell()
                f.writeByte(0)
                # Write palette
                palstart = f.tell()
                f.write(paldata)
                palend = f.tell()
                f.writeByte(0)
                # Write header data
                f.seek(9)
                f.writeByte(0)
                f.writeByte(0)
                f.writeByte(0)
                f.seek(16)
                f.writeUInt(mapend - mapstart)
                f.writeUInt(mapstart)
                f.seek(92)
                f.writeUInt(tileend - tilestart)
                f.writeUInt(tilestart)
                f.seek(128)
                f.writeUInt(palend - palstart)
                f.writeUInt(palstart)
    common.logMessage("Done!")
def run(data, processed):
    infolder = data + ("extract/" if not processed else "repack/")
    files = ["bank_11.bin", "bank_12.bin"]
    outfile = data + "analyze_output.txt"
    with codecs.open(data + "table_input.txt", "r", "utf-8") as tablef:
        table = common.getSection(tablef, "")
    invtable = {}
    with codecs.open(data + "table.txt", "r", "utf-8") as tablef:
        convtable = common.getSection(tablef, "")
        for char in convtable:
            invtable[int(convtable[char][0], 16)] = char

    with codecs.open(outfile, "w", "utf-8") as out:
        for file in common.showProgress(files):
            common.logMessage("Processing", file, "...")
            out.write("!FILE:" + file + "\n")
            size = os.path.getsize(infolder + file)
            with common.Stream(infolder + file, "rb") as f:
                while f.tell() < size - 1:
                    pos = f.tell()
                    opcode = f.readByte()
                    if opcode in game.opcodes and game.opcodes[opcode] != -1:
                        addline = ""
                        if opcode in game.ptropcodes:
                            addline = " Pointer: " + repr(
                                game.ptropcodes[opcode])
                        if opcode == 0x0a:
                            if processed:
                                common.logDebug("Failing at",
                                                common.toHex(pos))
                                writeLine(
                                    out, pos, opcode,
                                    game.readString(f,
                                                    table,
                                                    processed=invtable))
                            else:
                                writeLine(out, pos, opcode,
                                          game.readString(f, table))
                        elif opcode in game.repopcodes:
                            readbytes = ""
                            replen = 0
                            while True:
                                byte = f.readBytes(1)
                                readbytes += byte
                                replen += 1
                                if byte == "FF " and (opcode
                                                      not in game.ptropcodes
                                                      or replen > 2):
                                    break
                            writeLine(out, pos, opcode, readbytes + addline)
                        else:
                            writeLine(
                                out, pos, opcode,
                                f.readBytes(game.opcodes[opcode]) + addline)
                            if opcode == 0xff:
                                out.write("\n")
                                check = f.readUInt()
                                check2 = f.readUInt()
                                f.seek(-8, 1)
                                if check == 0xffffffff and check2 == 0xffffffff:
                                    break
                    else:
                        common.logError("Uknown opcode", common.toHex(opcode),
                                        "at", common.toHex(pos))
                        writeLine(out, pos, opcode, "Unknown")
            out.write("\n\n")
        common.logMessage("Done! Extracted", len(files), "files")
def run(data, allfile=False):
    infolder = data + "extract/"
    outfolder = data + "repack/"
    infile = data + "bin_input.txt"
    infilename = data + "name_input.txt"
    chartot = transtot = 0
    table, invtable, ccodes, glyphs = game.getFontData(data)

    with codecs.open(infile, "r", "utf-8") as bin:
        common.logMessage("Repacking bin from", infile, "...")
        for file in common.showProgress(game.fileranges):
            section = common.getSection(bin, file)
            if len(section) == 0:
                continue
            chartot, transtot = common.getSectionPercentage(
                section, chartot, transtot)
            # Repack the file
            common.logMessage("Processing", file, "...")
            common.copyFile(infolder + file, outfolder + file)
            with common.Stream(outfolder + file, "rb+") as f:
                if file != "bank_1d.bin":
                    for binrange in game.fileranges[file]:
                        f.seek(binrange[0])
                        while f.tell() < binrange[1]:
                            if (len(binrange) >= 3):
                                f.seek(binrange[2], 1)
                            strpos = f.tell()
                            readstr = game.readString(f, table, True)
                            if allfile and len(readstr) > 50:
                                f.seek(strpos + 2)
                                continue
                            if readstr.startswith("|"):
                                f.seek(strpos + 2)
                                continue
                            if readstr != "":
                                newstr = ""
                                if readstr in section:
                                    newstr = section[readstr].pop(0)
                                    if len(section[readstr]) == 0:
                                        del section[readstr]
                                strend = f.tell()
                                if newstr != "":
                                    if newstr == "!":
                                        newstr = ""
                                    common.logDebug("Repacking", newstr, "at",
                                                    common.toHex(strpos))
                                    f.seek(strpos)
                                    game.writeString(f, newstr, invtable,
                                                     ccodes,
                                                     strend - strpos - 2, True)
                                    while f.tell() < strend:
                                        f.writeByte(int(ccodes[" "][0], 16))
                                    f.seek(strend - 2)
                                    f.writeUShort(0xffff)
                else:
                    # String pointers are stored starting at 0xcd00
                    f.seek(0xcd00)
                    ptrs = []
                    for i in range(23):
                        ptrs.append(f.readUShort())
                        f.seek(14, 1)
                    strings = []
                    for ptr in ptrs:
                        f.seek(ptr)
                        strings.append(game.readString(f, table, True))
                    newptrs = []
                    f.seek(0xce70)
                    for i in range(len(strings)):
                        if strings[i] in section:
                            newstr = section[strings[i]].pop(0)
                        else:
                            newstr = strings[i]
                        newstr = common.wordwrap(newstr, glyphs,
                                                 game.wordwrap_angel,
                                                 game.detectTextCode)
                        common.logDebug("Repacking", newstr, "at",
                                        common.toHex(f.tell()))
                        newstr = newstr.split("|")
                        if len(newstr) > 8:
                            common.logError("Line too long",
                                            str(len(newstr)) + "/8", newstr[0])
                            newstr = newstr[:8]
                        while len(newstr) < 8:
                            newstr.append("")
                        for binstr in newstr:
                            if not binstr.startswith("▼"):
                                binstr = " " + binstr
                            newptrs.append(f.tell())
                            game.writeString(f, binstr, invtable, ccodes, -1,
                                             True)
                            f.writeUShort(0xffff)
                    f.seek(0xcd00)
                    for newptr in newptrs:
                        f.writeUShort(newptr)
    # Set the name input selection glyphs in bank 14
    newglyphs = {}
    with codecs.open(infilename, "r", "utf-8") as name:
        nameglyphs = name.read().replace("\r",
                                         "").replace("\n",
                                                     "").replace("#", "")
    with common.Stream(outfolder + "bank_14.bin", "rb+") as f:
        # Write the new name input values
        f.seek(0xc250)
        for nameglyph in nameglyphs:
            if nameglyph in invtable and invtable[nameglyph][:2] != 'ff':
                f.writeUShort(int(invtable[nameglyph], 16))
            else:
                glyphcode = int(ccodes[nameglyph][0], 16) - 0x20
                glyphcode <<= 6
                glyphcode += 0xa300
                newglyphs[nameglyph] = glyphcode
                f.writeUShort(glyphcode)
        # Write "Adam", but using the long glyphs
        f.seek(0x296c + 3)
        f.writeUShort(int(invtable["A"], 16))
        f.seek(3, 1)
        f.writeUShort(newglyphs["d"])
        f.seek(3, 1)
        f.writeUShort(newglyphs["a"])
        f.seek(3, 1)
        f.writeUShort(newglyphs["m"])
    common.logMessage("Done! Translation is at {0:.2f}%".format(
        (100 * transtot) / chartot))

    nasm.run(common.bundledFile("bin_patch.asm"))
Beispiel #8
0
def run():
    xmlfile = "data/fontdump.xml"
    imgfile = "data/fontdump.png"
    fontfile = "font.png"
    fontconfigfile = "fontconfig.txt"
    outfile = "data/fontout.png"
    infont = "data/extract_NFP/ETC.NFP/GL_12FNT.NFT"
    tempfont = "data/GL_12FNT.NFTR"
    outfont = "data/work_NFP/ETC.NFP/GL_12FNT.NFT"
    binin = "data/bin_input.txt"
    spcin = "data/spc_input.txt"
    table = "data/table.txt"

    common.logMessage("Repacking font ...")
    fontexe = common.bundledFile("NerdFontTerminatoR.exe")
    if not os.path.isfile(fontexe):
        common.logError("NerdFontTerminatoR not found")
        return

    # List of characters
    with codecs.open(fontconfigfile, "r", "utf-8") as f:
        fontconfig = common.getSection(f, "")
        upperchars = fontconfig["upperchars"][0].split("|")
        lowerchars = fontconfig["lowerchars"][0].split("|")
        numbers = fontconfig["numbers"][0].split("|")
        punctuation = fontconfig["punctuation"][0].split("|")
        customs = fontconfig["customs"][0].split("|")
        all = upperchars + lowerchars + numbers + punctuation + customs

    # X Position in the font.png file
    positions = {}
    for i in range(len(upperchars)):
        positions[upperchars[i]] = i * 12
        positions[lowerchars[i]] = (i * 12) + 6
    for i in range(len(numbers)):
        positions[numbers[i]] = (len(upperchars) * 12) + (i * 6)
    for i in range(len(punctuation)):
        positions[punctuation[i]] = (len(upperchars) * 12) + (len(numbers) * 6) + (i * 6)
    for i in range(len(customs)):
        positions[customs[i]] = (len(upperchars) * 12) + (len(numbers) * 6) + (len(punctuation) * 6) + (i * 12)

    # Fix the font size before dumping it
    with common.Stream(infont, "rb") as font:
        with common.Stream(tempfont, "wb") as temp:
            font.seek(8)
            size = font.readUInt()
            font.seek(0)
            temp.write(font.read(size))
    # Dump the font
    common.execute(fontexe + " -e " + tempfont + " " + xmlfile + " " + imgfile, False)

    # Generate the code range
    coderanges = [(0x89, 0x9F), (0xE0, 0xEA)]
    skipcodes = [0x7F]
    charrange = (0x40, 0xFC)
    codes = []
    for coderange in coderanges:
        for i in range(coderange[0], coderange[1] + 1):
            first = charrange[0]
            if i == 0x88:
                first = 0x9F
            last = charrange[1]
            if i == 0xEA:
                last = 0xA4
            for j in range(first, last + 1):
                if j in skipcodes:
                    continue
                hexcode = i * 0x100 + j
                if hexcode > 0x9872 and hexcode < 0x989F:
                    continue
                codes.append(hexcode)

    # Generate a basic bigrams list
    items = ["  "]
    for char1 in upperchars:
        for char2 in lowerchars:
            items.append(char1 + char2)
    for char1 in upperchars:
        items.append(" " + char1)
        items.append(char1 + " ")
        for char2 in upperchars:
            if char1 + char2 not in items:
                items.append(char1 + char2)
    for char1 in lowerchars:
        items.append(" " + char1)
        items.append(char1 + " ")
        for char2 in lowerchars:
            if char1 + char2 not in items:
                items.append(char1 + char2)
    for custom in customs:
        items.append(custom)
    # And a complete one from all the bigrams
    with codecs.open(spcin, "r", "utf-8") as spc:
        inputs = common.getSection(spc, "", "#", game.fixchars)
    with codecs.open(binin, "r", "utf-8") as bin:
        inputs.update(common.getSection(bin, "", "#", game.fixchars))
    for k, input in inputs.items():
        for str in input:
            str = "<0A>".join(str.replace("|", "<0A>").split(">>"))
            if str.startswith("<<"):
                str = str[2:]
                pad = " " * ((20 - len(str)) // 2)
                str = pad + str + pad
            if str.startswith("[") and str[3] == "]":
                str = str[4:]
            i = 0
            while i < len(str):
                if i < len(str) - 1 and str[i+1] == "<":
                    str = str[:i+1] + " " + str[i+1:]
                elif i < len(str) - 4 and (str[i+1:i+5] == "UNK(" or str[i+1:i+5] == "CUS("):
                    str = str[:i+1] + " " + str[i+1:]
                char = str[i]
                if char == "<" and i < len(str) - 3 and str[i+3] == ">":
                    i += 4
                elif char == "U" and i < len(str) - 4 and str[i:i+4] == "UNK(":
                    i += 9
                elif char == "C" and i < len(str) - 4 and str[i:i+4] == "CUS(":
                    i += 9
                else:
                    if i + 1 == len(str):
                        bigram = char + " "
                    else:
                        bigram = char + str[i+1]
                    i += 2
                    if bigram not in items:
                        if bigram[0] not in all or bigram[1] not in all:
                            common.logError("Invalid bigram", bigram, "from phrase", str)
                        else:
                            items.append(bigram)

    # Open the images
    img = Image.open(imgfile)
    pixels = img.load()
    font = Image.open(fontfile)
    fontpixels = font.load()

    # Generate the image and table
    fontx = 106
    fonty = 5644
    x = len(codes) - 1
    tablestr = ""
    for item in items:
        if item in customs:
            for i2 in range(11):
                for j2 in range(11):
                    pixels[fontx + i2, fonty + j2] = fontpixels[positions[item] + i2, j2]
        else:
            for i2 in range(5):
                for j2 in range(11):
                    pixels[fontx + i2, fonty + j2] = fontpixels[positions[item[0]] + i2, j2]
            for j2 in range(11):
                pixels[fontx + 5, fonty + j2] = fontpixels[positions[" "], j2]
            for i2 in range(5):
                for j2 in range(11):
                    pixels[fontx + i2 + 6, fonty + j2] = fontpixels[positions[item[1]] + i2, j2]
        fontx -= 13
        if fontx < 0:
            fontx = 197
            fonty -= 13
        tablestr = (item + "=" + common.toHex(codes[x]) + "\n") + tablestr
        x -= 1
    with codecs.open(table, "w", "utf-8") as f:
        f.write(tablestr)
    img.save(outfile, "PNG")

    # Generate the new font
    common.execute(fontexe + " -i " + xmlfile + " " + outfile + " " + tempfont, False)
    common.copyFile(tempfont, outfont)
    # Clean up the temp files
    os.remove(xmlfile)
    os.remove(imgfile)
    os.remove(outfile)
    os.remove(tempfont)

    if x < len(items):
        common.logMessage("Done! Couldn't fit", len(items) - x, "bigrams")
    else:
        common.logMessage("Done! Room for", x - len(items), "more bigrams")