def patchdump(): patchfile = "data/bad_to_good.xdelta" common.logMessage("Creating xdelta patch", patchfile, "...") xdelta = common.bundledFile("xdelta.exe") if not os.path.isfile(xdelta): common.logError("xdelta not found") return common.execute( xdelta + " -f -e -s {rom} {rompatch} {patch}".format( rom=romfile.replace(".nds", "_bad.nds"), rompatch=romfile, patch=patchfile), False) common.logMessage("Done!")
def run(data): infolder = data + "extract/" outfolder = data + "out_IMG/" common.logMessage("Extracting images to", outfolder, "...") common.makeFolder(outfolder) files = common.getFiles(infolder) extracted = 0 with codecs.open(common.bundledFile("images.txt"), "r", "utf-8") as imagef: for file in files: section = common.getSection(imagef, file) with common.Stream(infolder + file, "rb") as f: for imgname in section.keys(): imgdata = section[imgname][0].split(",") mapstart = int(imgdata[1], 16) imgnum = int(imgdata[2]) if len(imgdata) >= 3 else 1 readpal = len(imgdata) >= 4 and imgdata[3] == "1" extracted += imgnum if "-" in imgdata[0]: tilestart = int(imgdata[0].split("-")[0], 16) tileend = int(imgdata[0].split("-")[1], 16) for i in common.showProgress(range(tilestart, tileend + 1, 1)): ws.extractMappedImage(f, outfolder + imgname + "_" + hex(i) + ".png", i, mapstart, imgnum, readpal) else: tilestart = int(imgdata[0], 16) ws.extractMappedImage(f, outfolder + imgname + ".png", tilestart, mapstart, imgnum, readpal) if file == "bank_09.bin": map = game.getBerserkMap(outfolder) ws.writeMappedImage(f, 0xf080, [map], ws.bwpalette) extracted += 1 # Extract ramen stand image with common.Stream(infolder + "bank_03.bin", "rb") as f: map = game.getRamenMap(outfolder) ws.writeMappedImage(f, 0x3748, [map], ws.bwpalette) map = game.getLanternMap(outfolder) ws.writeMappedImage(f, 0x3748, [map], ws.bwpalette) common.logMessage("Done! Extracted", extracted, "files")
def run(data): workfolder = data + "work_IMG/" infolder = data + "extract/" outfolder = data + "repack/" common.logMessage("Repacking images from", workfolder, "...") files = common.getFiles(infolder) repacked = 0 with codecs.open(common.bundledFile("images.txt"), "r", "utf-8") as imagef: for file in files: section = common.getSection(imagef, file) if len(section) > 0: with common.Stream(outfolder + file, "rb+") as f: for imgname in section.keys(): imgdata = section[imgname][0].split(",") mapstart = int(imgdata[1], 16) imgnum = int(imgdata[2]) if len(imgdata) >= 3 else 1 readpal = len(imgdata) >= 4 and imgdata[3] == "1" writepal = len(imgdata) >= 5 and imgdata[4] == "1" tilestart = int(imgdata[0], 16) ws.repackMappedImage(f, workfolder + imgname + ".png", tilestart, mapstart, imgnum, readpal, writepal) repacked += imgnum if file == "bank_09.bin": map = game.getBerserkMap(workfolder) ws.repackMappedTiles(f, 0xf080, map, ws.bwpalette) repacked += 1 # Repack ramen stand images with common.Stream(outfolder + "bank_03.bin", "rb+") as f: map = game.getRamenMap(workfolder) ws.repackMappedTiles(f, 0x3748, map, ws.bwpalette) map = game.getLanternMap(workfolder) ws.repackMappedTiles(f, 0x3748, map, ws.bwpalette) repacked += 2 common.logMessage("Done! Repacked", repacked, "files")
def run(firstgame): binin = "data/extract/arm9.bin" binout = "data/repack/arm9.bin" binfile = "data/bin_input.txt" patchfile = "" if not os.path.isfile(binfile): common.logError("Input file", binfile, "not found") return fixchars = game.getFixChars() patchfile = "bin_patch.asm" fontfile = "data/replace/data/font/lcfont12.NFTR" injectfile = "data/extract/data/font/digit8.NFTR" # Set the appropriate range depending on the game if firstgame: binrange = game.binrange[0] freeranges = game.freeranges[0] game.monthsection = game.monthsection[0] game.skipsection = game.skipsection[0] else: binrange = game.binrange[1] freeranges = game.freeranges[1] game.monthsection = game.monthsection[1] game.skipsection = game.skipsection[1] binin = binin.replace(".bin", "_dec.bin") binout = binout.replace(".bin", "_dec.bin") nds.repackBIN(binrange, freeranges, game.detectShiftJIS, game.writeBINShiftJIS, "cp932", "#", binin, binout, fixchars=fixchars) # Check that the redirects file is created, or create an empty one redfile = "data/redirects.asm" if not os.path.isfile(redfile): with codecs.open(redfile, "w", "utf-8") as f: f.write(".ascii \"NDSC\"\n\n") f.write("REDIRECT_START:\n\n") # Extract font data if not os.path.isfile(fontfile): fontfile = fontfile.replace("replace/", "extract/") common.copyFile(injectfile, injectfile.replace("extract/", "repack/")) nitro.extractFontData(fontfile, "data/font_data.bin") # Run armips common.armipsPatch(common.bundledFile(patchfile)) if not firstgame: # Compress the binary compfile = binout.replace("_dec.bin", ".bin") common.logMessage("Compressing BIN ...") nds.compressBinary(binout, compfile) common.logMessage("Done!") # Apply AP patch apin = "data/extract/overlay/overlay_0000.bin" apout = "data/repack/overlay/overlay_0000.bin" common.copyFile(apin, apout) with common.Stream(apout, "rb+") as f: f.seek(0x432) f.writeByte(0x36) f.seek(0x58b) f.writeByte(0x7b)
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"))
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")