def extractARC(infolder, outfolder): common.makeFolder(outfolder) common.logMessage("Extracting ARC to", outfolder, "...") files = common.getFiles(infolder, ".arc") for file in common.showProgress(files): common.logDebug("Processing", file, "...") common.execute( "wszst EXTRACT " + infolder + file + " -D " + outfolder + file, False) common.logMessage("Done! Extracted", len(files), "files")
def extractTPL(infolder, outfolder, splitName=True): common.makeFolder(outfolder) common.logMessage("Extracting TPL to", outfolder, "...") files = common.getFiles(infolder, ".tpl") for file in common.showProgress(files): common.logDebug("Processing", file, "...") filename = file.split("/")[0] if splitName else file common.execute( "wimgt DECODE " + infolder + file + " -D " + outfolder + filename + "/" + os.path.basename(file).replace(".tpl", ".png"), False) common.logMessage("Done! Extracted", len(files), "files")
def repackIso(isofile, isopatch, workfolder, patchfile=""): common.logMessage("Repacking ISO", isopatch, "...") common.copyFile(isofile, isopatch) iso = pycdlib.PyCdlib() iso.open(isopatch, "r+b") files = common.getFiles(workfolder) for file in common.showProgress(files): filelen = os.path.getsize(workfolder + file) with open(workfolder + file, "rb") as f: iso.modify_file_in_place(f, filelen, "/" + file) iso.close() common.logMessage("Done!") # Create xdelta patch if patchfile != "": common.xdeltaPatch(patchfile, isofile, isopatch)
def extractBREFT(infolder, tempfolder, outfolder): common.makeFolder(tempfolder) common.makeFolder(outfolder) common.logMessage("Extracting BREFT to", outfolder, "...") files = common.getFiles(infolder, ".breft") for file in common.showProgress(files): common.logDebug("Processing", file, "...") outfile = file.split("/") outfile = "/" + outfile[1] + "/" + outfile[3] common.execute( "wszst EXTRACT " + infolder + file + " -D " + tempfolder + outfile, False) for imgfile in os.listdir(tempfolder + outfile + "/files"): common.execute( "wimgt DECODE " + tempfolder + outfile + "/files/" + imgfile + " -D " + outfolder + outfile + "/" + imgfile + ".png", False) common.logMessage("Done! Extracted", len(files), "files")
def run(): infolder = "data/extract/data/graphics/" outfolder = "data/out_IMG/" common.logMessage("Extracting KBG to", outfolder, "...") files = common.getFiles(infolder, ".kbg") for file in common.showProgress(files): common.logDebug("Processing", file, "...") with common.Stream(infolder + file, "rb") as f: palettes, ncgr = game.readKBG(f) tiledata = f.read(ncgr.tilelen) nitro.readNCGRTiles(ncgr, tiledata) # Export img common.makeFolders(outfolder + os.path.dirname(file)) outfile = outfolder + file.replace(".kbg", ".png") nitro.drawNCGR(outfile, None, ncgr, palettes, ncgr.width, ncgr.height) common.logMessage("Done! Extracted", len(files), "files")
def repackUMD(isofile, isopatch, workfolder, patchfile=""): common.logMessage("Repacking ISO", isopatch, "...") common.copyFile(isofile, isopatch) umdreplace = common.bundledExecutable("UMD-replace.exe") if not os.path.isfile(umdreplace): common.logError("UMD-replace not found") return files = common.getFiles(workfolder) for file in common.showProgress(files): common.execute( umdreplace + " \"{imagename}\" \"/{filename}\" \"{newfile}\"".format( imagename=isopatch, filename=file, newfile=workfolder + file), False) common.logMessage("Done!") # Create xdelta patch if patchfile != "": common.xdeltaPatch(patchfile, isofile, isopatch)
def run(): infolder = "data/extract_NFP/NFP2D.NFP/" outfolder = "data/out_VSC/" common.makeFolder(outfolder) common.logMessage("Extracting VSC to", outfolder, "...") files = common.getFiles(infolder, ".VSC") for file in common.showProgress(files): common.logDebug("Processing", file, "...") with common.Stream(infolder + file, "rb") as f: # Read header f.seek(4) unk1 = f.readUShort() unk2 = f.readUShort() size = f.readUInt() unk3 = f.readUInt() bpp = 4 if f.readUInt() == 1 else 8 width = f.readUInt() height = f.readUInt() unk4 = f.readUInt() mapsize = f.readUInt() unk5 = f.readUInt() tilesize = f.readUInt() common.logDebug("size:", size, "width:", width, "height:", height, "mapsize:", mapsize, "tilesize:", tilesize, "bpp:", bpp) common.logDebug("unk1:", unk1, "unk2:", unk2, "unk3:", unk3, "unk4:", unk4, "unk5:", unk5) # Read data common.logDebug("mapoffset:", f.tell()) mapdata = f.read(mapsize) common.logDebug("tileoffset:", f.tell()) tiledata = f.read(tilesize) common.logDebug("paloffset:", f.tell()) cpal = f.readString(4) if cpal != "CPAL": common.logError("Palette header", cpal) continue f.seek(20, 1) palnum = f.readUShort() f.seek(4, 1) paldata = f.read(palnum * 32) # Draw the image img = game.drawMappedImage(width, height, mapdata, tiledata, paldata, 8, bpp) img.save(outfolder + file.replace(".VSC", ".png"), "PNG") common.logMessage("Done! Extracted", len(files), "files")
def run(): infolder = "data/extract/data/graphics/" outfolder = "data/repack/data/graphics/" workfolder = "data/work_IMG/" common.logMessage("Repacking KBG from", workfolder, "...") files = common.getFiles(infolder, ".kbg") for file in common.showProgress(files): pngfile = workfolder + file.replace(".kbg", ".png") if not os.path.isfile(pngfile): continue common.logDebug("Processing", file, "...") with common.Stream(infolder + file, "rb") as fin: with common.Stream(outfolder + file, "wb") as f: palettes, ncgr = game.readKBG(fin) fin.seek(0) f.write(fin.read(ncgr.tileoffset)) nitro.writeNCGR(outfolder + file, ncgr, pngfile, palettes) common.logMessage("Done!")
def extractTIM(infolder, outfolder, extensions=".tim", readfunc=None): common.makeFolder(outfolder) common.logMessage("Extracting TIM to", outfolder, "...") files = common.getFiles(infolder, extensions) for file in common.showProgress(files): common.logDebug("Processing", file, "...") extension = os.path.splitext(file)[1] if readfunc is not None: tim, transp, forcepal = readfunc(infolder + file) else: transp = False forcepal = -1 with common.Stream(infolder + file, "rb") as f: tim = readTIM(f) if tim is None: continue # Export img common.makeFolders(outfolder + os.path.dirname(file)) outfile = outfolder + file.replace(extension, ".png") drawTIM(outfile, tim, transp, forcepal) common.logMessage("Done! Extracted", len(files), "files")
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")
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): infolder = "data/extract/data/data/" outfile = "data/dat_output.txt" ignorefiles = ["route.dat", "debttable.dat", "errandgossip.dat", "market.dat", "traderexptable.dat"] if not firstgame: ignorefiles.append("love_event.dat") encoding = "shift_jis" if firstgame else "shift_jisx0213" game.monthsection = game.skipsection = None common.logMessage("Extracting DAT to", outfile, "...") with codecs.open(outfile, "w", "utf-8") as out: files = common.getFiles(infolder, ".dat") for file in common.showProgress(files): if file in ignorefiles: continue common.logDebug("Processing", file, "...") first = True foundstrings = [] size = os.path.getsize(infolder + file) # The file contains several strings, padded with 0s with common.Stream(infolder + file, "rb") as f: while f.tell() < size - 2: pos = f.tell() check = game.detectShiftJIS(f, encoding) if check != "" and check != "%": # Check for repeated strings, only within a single file if check not in foundstrings: common.logDebug("Found string at", pos) if first: out.write("!FILE:" + file + "\n") first = False out.write(check + "=\n") foundstrings.append(check) pos = f.tell() - 1 f.seek(pos + 1) common.logMessage("Done! Extracted", len(files), "files")
def run(): infolder = "data/extract/data/Rom/event/script/" outfile = "data/cnut_output.txt" common.logMessage("Extracting CNUT to", outfile, "...") with codecs.open(outfile, "w", "utf-8") as out: files = common.getFiles(infolder, ".cnut") for file in common.showProgress(files): common.logDebug("Processing", file, "...") first = True size = os.path.getsize(infolder + file) with common.Stream(infolder + file, "rb") as f: while f.tell() < size - 4: pos = f.tell() b1 = f.readByte() b2 = f.readByte() b3 = f.readByte() b4 = f.readByte() if b1 == 0x10 and b2 == 0x00 and b3 == 0x00 and b4 == 0x08: # Found a string check = game.readShiftJIS(f) # Remove some codes that are found in almost every string start/end if check.startswith("#CLR()"): check = check[6:] if check.startswith("#ARW("): check = check[7:] if check.endswith("#INP()"): check = check[:-6] if not common.isAscii(check): if first: out.write("!FILE:" + file + "\n") first = False out.write(check + "=\n") else: f.seek(pos + 1) common.logMessage("Done! Extracted", len(files), "files")
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")
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 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)
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()
def run(): infolder = "data/extract_NFP/NFP2D.NFP/" outfolder = "data/out_KPC/" common.makeFolder(outfolder) common.logMessage("Extracting KPC to", outfolder, "...") files = common.getFiles(infolder, ".KPC") for file in common.showProgress(files): common.logDebug("Processing", file, "...") with common.Stream(infolder + file, "rb") as f: # Read header f.seek(4) bits = [] for i in range(5): bits.append(f.readByte()) palcompressed = f.readByte() == 1 mapcompressed = f.readByte() == 1 tilecompressed = f.readByte() == 1 width = f.readUShort() * 8 height = f.readUShort() * 8 mapsize = f.readUInt() mapoffset = f.readUInt() f.seek(5, 1) bpp = 8 if f.readUShort() == 1 else 4 f.seek(92) tilesize = f.readUInt() tileoffset = f.readUInt() f.seek(124) unk = f.readUInt() palsize = f.readUInt() paloffset = f.readUInt() common.logDebug("width:", width, "height:", height, "bpp:", bpp) common.logDebug("mapsize:", mapsize, "mapoffset:", mapoffset) common.logDebug("tilesize:", tilesize, "tileoffset:", tileoffset) common.logDebug("palsize:", palsize, "paloffset:", paloffset) common.logDebug("palcompressed:", palcompressed, "mapcompressed:", mapcompressed, "tilecompressed:", tilecompressed) common.logDebug("bits:", bits, "unk:", unk) # Read palette f.seek(paloffset) if palcompressed: paldata = nds.decompress(f, palsize) else: paldata = f.read(palsize) # Read map data f.seek(mapoffset) if mapcompressed: mapdata = nds.decompress(f, mapsize) else: mapdata = f.read(mapsize) # Read tile data f.seek(tileoffset) if tilecompressed: tiledata = nds.decompress(f, tilesize) else: tiledata = f.read(tilesize) # Draw the image img = game.drawMappedImage(width, height, mapdata, tiledata, paldata, 8, bpp) img.save(outfolder + file.replace(".KPC", ".png"), "PNG") common.logMessage("Done! Extracted", len(files), "files")
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))
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))
def run(firstgame, no_redirect): infolder = "data/extract/data/data/" outfolder = "data/repack/data/data/" infile = "data/dat_input.txt" redfile = "data/redirects.asm" fontfile = "data/replace/data/font/lcfont12.NFTR" if not os.path.isfile(infile): common.logError("Input file", infile, "not found") return common.makeFolder(outfolder) chartot = transtot = 0 monthsection, skipsection = game.monthsection, game.skipsection game.monthsection = game.skipsection = None encoding = "shift_jis" if firstgame else "shift_jisx0213" common.logMessage("Repacking DAT 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 fixchars = game.getFixChars() # Copy this txt file if not firstgame and os.path.isfile(infolder + "facilityhelp.txt"): common.copyFile(infolder + "facilityhelp.txt", outfolder + "facilityhelp.txt") redirects = [] with codecs.open(infile, "r", "utf-8") as dat: files = common.getFiles(infolder, ".dat") for file in common.showProgress(files): section = common.getSection(dat, file, fixchars=fixchars) # If there are no lines, just copy the file if len(section) == 0: common.copyFile(infolder + file, outfolder + file) # Part of the AP patch if not firstgame and file == "route.dat": with common.Stream(outfolder + file, "rb+") as f: f.seek(0x5ee8) f.writeByte(0x0) continue i = 0 chartot, transtot = common.getSectionPercentage( section, chartot, transtot) common.logDebug("Processing", file, "...") size = os.path.getsize(infolder + file) with common.Stream(infolder + file, "rb") as fin: with common.Stream(outfolder + file, "wb") as f: f.write(fin.read()) fin.seek(0) # Loop the file and replace strings as needed while fin.tell() < size - 2: pos = fin.tell() check = game.detectShiftJIS(fin, encoding) if check != "": if file == "entrance_icon.dat": # For entrance_icon, just write the string and update the pointer if check in section: # For the first one, seek to the correct position in the output file if i == 0: f.seek(pos) # Write the string newsjis = check if check in section and section[check][ 0] != "": common.logDebug( "Replacing string at", pos) newsjis = section[check][0] startpos = f.tell() game.writeShiftJIS(f, newsjis, False, True, 0, encoding) endpos = f.tell() # Update the pointer f.seek(0x1c98 + 4 * i) f.writeUInt(startpos - 0x1c98) f.seek(endpos) i += 1 else: # Found a SJIS string, check if we have to replace it if check in section and section[check][0] != "": common.logDebug("Replacing string at", pos) f.seek(pos) newsjis = section[check][0] maxlen = 0 if file == "goods.dat": newsjis = common.wordwrap( newsjis, glyphs, 170) maxlen = 60 elif file == "gossip.dat": newsjis = common.wordwrap( newsjis, glyphs, 190) if newsjis.count("<<") > 0: newsjis = common.centerLines( newsjis, glyphs, 190, centercode="<<") if fin.tell() - pos < 35: maxlen = 35 else: maxlen = 160 elif file == "scenarioguide.dat": newsjis = common.wordwrap( newsjis, glyphs, 165) maxlen = 60 if newsjis.count("|") > 1: common.logError( "scenarioguide line", newsjis, "too long") newlen = game.writeShiftJIS( f, newsjis, False, True, maxlen, encoding) if newlen < 0: if file != "gossip.dat" or no_redirect or maxlen != 160: common.logError( "String {} is too long ({}/{})." .format( newsjis, len(newsjis), maxlen)) else: common.logWarning( "String {} is too long ({}/{})." .format( newsjis, len(newsjis), maxlen)) # Doesn't fit, write it shorter f.seek(pos) cutat = 155 if firstgame else 150 while ord(newsjis[cutat]) > 127: cutat -= 1 stringfit = newsjis[:cutat] stringrest = newsjis[cutat:] game.writeShiftJIS( f, stringfit, False, True, maxlen, encoding) f.seek(-1, 1) f.writeByte(0x1f) f.writeByte(len(redirects)) redirects.append(stringrest) # Pad with 0s if the line is shorter while f.tell() < fin.tell(): f.writeByte(0x00) pos = fin.tell() - 1 fin.seek(pos + 1) with codecs.open(redfile, "w", "utf-8") as f: f.write(".ascii \"NDSC\"\n\n") f.write("REDIRECT_START:\n\n") for i in range(len(redirects)): f.write(".dh REDIRECT_{} - REDIRECT_START\n".format(i)) for i in range(len(redirects)): f.write("\nREDIRECT_{}:\n".format(i)) redirect = redirects[i].replace("\"", "\\\"") redirect = redirect.replace("|", "\" :: .db 0xa :: .ascii \"") redirectascii = "" for c in redirect: if ord(c) > 127: sjisc = common.toHex( int.from_bytes(c.encode(encoding), "big")) redirectascii += "\" :: .db 0x" + sjisc[:2] + " :: .db 0x" + sjisc[ 2:] + " :: .ascii \"" else: redirectascii += c f.write(".ascii \"{}\" :: .db 0\n".format(redirectascii)) game.monthsection, game.skipsection = monthsection, skipsection common.logMessage("Done! Translation is at {0:.2f}%".format( (100 * transtot) / chartot))
def run(): infolder = "data/extract/data/Rom/event/script/" outfolder = "data/repack/data/Rom/event/script/" infile = "data/cnut_input.txt" fontfile = "data/repack/data/Rom/font/font0.NFTR" replacefontfile = "data/replace/data/Rom/font/font0.NFTR" chartot = transtot = 0 if not os.path.isfile(infile): common.logError("Input file", infile, "not found") return common.logMessage("Repacking CNUT from", infile, "...") if os.path.isfile(replacefontfile): glyphs = nitro.readNFTR(replacefontfile).glyphs else: glyphs = nitro.readNFTR(fontfile).glyphs with codecs.open(infile, "r", "utf-8") as cnut: files = common.getFiles(infolder, ".cnut") for file in common.showProgress(files): section = common.getSection(cnut, file, "//") if len(section) == 0: common.makeFolders(outfolder + file) common.copyFile(infolder + file, outfolder + file) continue chartot, transtot = common.getSectionPercentage( section, chartot, transtot) # Repack the file common.logDebug("Processing", file, "...") size = os.path.getsize(infolder + file) lastpos = 0 with common.Stream(infolder + file, "rb") as fin: common.makeFolders(infolder + file) with common.Stream(outfolder + file, "wb") as f: while fin.tell() < size - 4: pos = fin.tell() b1 = fin.readByte() b2 = fin.readByte() b3 = fin.readByte() b4 = fin.readByte() if b1 == 0x10 and b2 == 0x00 and b3 == 0x00 and b4 == 0x08: # Found a string check = game.readShiftJIS(fin) pre = post = "" # Add back some codes that are removed from extracted lines if check.startswith("#CLR()"): pre = "#CLR()" check = check[6:] if check.startswith("#ARW("): pre += check[:7] check = check[7:] if check.endswith("#INP()"): post = "#INP()" check = check[:-6] # Check if the line is translated and replace it if check in section: newsjis = section[check].pop(0) if len(section[check]) == 0: del section[check] if newsjis != "": newsjis = pre + newsjis + post if newsjis != check: newsjis = common.wordwrap( newsjis, glyphs, 205, game.detectTextCode) newsjis = newsjis.replace( ">>", "#INP()" + pre).replace( "|", "<0A>") # Copy data up to here endpos = fin.tell() fin.seek(lastpos) f.write(fin.read(pos + 4 - lastpos)) lastpos = endpos common.logDebug(" Repacking at", pos) game.writeShiftJIS(f, newsjis) else: fin.seek(pos + 1) fin.seek(lastpos) f.write(fin.read(size - lastpos)) common.logMessage("Done! Translation is at {0:.2f}%".format( (100 * transtot) / chartot))
def repack(file, outfile, infolder, outfolder): common.logDebug("Processing", file, "...") cpk = readCPK(file) if cpk is None: common.logError("Error reading CPK") return if len(cpk.filetable) == 0: common.logError("No files in CPK filetable") return # Get all the file with the custom extensions idtoext = {} for subfile in common.getFiles(infolder): nameext = os.path.splitext(subfile) idtoext[nameext[0]] = nameext[1] with common.Stream(outfile, "wb") as fout: with common.Stream(file, "rb") as fin: idnewdata = {} # Copy the file up to the ContentOffset contentoffsetentry = cpk.getFileEntry("CONTENT_OFFSET") contentoffset = contentoffsetentry.fileoffset fout.write(fin.read(contentoffset)) # Sort the list by original offset, to keep the file order the same sortedfiletable = sorted(cpk.filetable, key=lambda e: e.fileoffset) for i in common.showProgress(range(len(sortedfiletable))): entry = sortedfiletable[i] if entry.filetype != "FILE": continue folder, filename = entry.getFolderFile(infolder) folder2, _ = entry.getFolderFile(outfolder) if not os.path.isfile(folder + filename): filename += idtoext[filename] if not os.path.isfile(folder + filename): common.logError("Input file", folder + filename, "not found") continue if not os.path.isfile(folder2 + filename): # Read this directly from the CPK so we avoid compressing it again fin.seek(entry.fileoffset) filedata = fin.read(entry.filesize) uncdatalen = entry.extractsize cdatalen = entry.filesize else: with common.Stream(folder2 + filename, "rb") as subf: filedata = subf.read() # TODO: compress the file again if it was compressed originally? uncdatalen = cdatalen = len(filedata) # Write the file data and align fileoffset = fout.tell() - entry.offset fout.write(filedata) # If this is the last file, we don't align if i + 1 < len(sortedfiletable) and cdatalen % cpk.align > 0: fout.writeZero(cpk.align - (cdatalen % cpk.align)) idnewdata[entry.id] = (fileoffset, uncdatalen, cdatalen) # Update TOC tocentry = cpk.getFileEntry("TOC_HDR") itocentry = cpk.getFileEntry("ITOC_HDR") updatetoc = False updateitoc = False for id in idnewdata: newoffset, newuncdatalen, newcdatalen = idnewdata[id] if tocentry is not None: idtocentry = cpk.getIDEntry(id, tocname="TOC") if idtocentry is None: common.logError("TOC entry not found for id", id) else: tocentry.utf.updateColumnDataType(newoffset, idtocentry.fileoffsetpos, idtocentry.fileoffsettype) tocentry.utf.updateColumnDataType(newuncdatalen, idtocentry.extractsizepos, idtocentry.extractsizetype) tocentry.utf.updateColumnDataType(newcdatalen, idtocentry.filesizepos, idtocentry.filesizetype) updatetoc = True if itocentry is not None: iditocentry = cpk.getIDEntry(id, tocname="ITOC") if iditocentry is not None: # TODO: Not sure if only updating the datah here is correct in all cases, also might need to check the offset itocentry.utf.utfdatah.updateColumnDataType(newuncdatalen, iditocentry.extractsizepos, iditocentry.extractsizetype) itocentry.utf.utfdatah.updateColumnDataType(newcdatalen, iditocentry.filesizepos, iditocentry.filesizetype) updateitoc = True # Write the new packets if updatetoc: fout.seek(tocentry.fileoffset + 0x10) tocentry.utf.rawpacket.seek(0) utfpacket = tocentry.utf.rawpacket.read() if tocentry.encrypted: utfpacket = decryptUTF(utfpacket) fout.write(utfpacket) if updateitoc: # TODO: check if this works whern the ITOC is encrypted itocentry.utf.utfdatah.rawpacket.seek(0) utfpacket = itocentry.utf.utfdatah.rawpacket.read() itocentry.utf.rawpacket.seek(itocentry.utf.datahpos) datapos = itocentry.utf.rawpacket.readInt() + itocentry.utf.dataoffset fout.seek(itocentry.fileoffset + 0x10 + datapos) fout.write(utfpacket)
def run(): infolder = "data/extract_NFP/NFP2D.NFP/" workfolder = "data/work_VSC/" outfolder = "data/work_NFP/NFP2D.NFP/" common.logMessage("Repacking VSC from", workfolder, "...") files = common.getFiles(infolder, ".VSC") for file in common.showProgress(files): pngname = file.replace(".VSC", ".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: # Read header fin.seek(16) bpp = 4 if fin.readUInt() == 1 else 8 width = fin.readUInt() height = fin.readUInt() fin.seek(4, 1) mapsize = fin.readUInt() fin.seek(4, 1) tilesize = fin.readUInt() mapdata = fin.read(mapsize) fin.seek(tilesize, 1) fin.seek(24, 1) palnum = fin.readUShort() fin.seek(4, 1) paloffset = fin.tell() paldata = fin.read(palnum * 32) tiles, maps = game.readMappedImage(workfolder + pngname, width, height, paldata) # Copy the header fin.seek(0) f.write(fin.read(44)) # 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() # Write tile data tilestart = f.tell() for tile in tiles: for i in range(32 if bpp == 4 else 64): if bpp == 8: f.writeByte(tile[i]) else: index2 = tile[i * 2] index1 = tile[i * 2 + 1] f.writeByte(((index1) << 4) | index2) tileend = f.tell() # Copy the palette fin.seek(paloffset - 30) f.write(fin.read(30 + palnum * 32)) palend = f.tell() f.writeZero(16 - palend % 16) # Write the new header data f.seek(8) f.writeUInt(palend) f.seek(36) f.writeUInt(mapend - mapstart) f.writeUInt(tileend - tilestart) common.logMessage("Done!")
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!")