def run(data, allfile=False): infolder = data + "extract/" outfile = data + "bin_output.txt" with codecs.open(data + "table_input.txt", "r", "utf-8") as tablef: table = common.getSection(tablef, "") if allfile: game.fileranges = {"bank_1d.bin": [(0x0, 0xfff0)]} with codecs.open(outfile, "w", "utf-8") as out: common.logMessage("Extracting bin to", outfile, "...") for file in common.showProgress(game.fileranges): out.write("!FILE:" + file + "\n") with common.Stream(infolder + file, "rb") as f: for range in game.fileranges[file]: f.seek(range[0]) while f.tell() < range[1]: if (len(range) >= 3): f.seek(range[2], 1) pos = f.tell() binstr = game.readString(f, table, True) if allfile and len(binstr) > 50: f.seek(pos + 2) continue if binstr.startswith("|"): f.seek(pos + 2) continue if binstr != "": common.logDebug("Found string at", common.toHex(pos), binstr) out.write(binstr + "=\n") common.logMessage("Done!")
def run(data, speaker=True, scene=False, merge=False): infolder = data + "extract/" files = ["bank_11.bin", "bank_12.bin"] outfile = data + "script_output.txt" with codecs.open(data + "table_input.txt", "r", "utf-8") as tablef: table = common.getSection(tablef, "") if merge: mergescript = codecs.open(data + "script_input.txt", "r", "utf-8") # Get the scenes list from the debug menu if scene: scenenames = {"bank_11.bin": [], "bank_12.bin": []} with common.Stream(infolder + "bank_1e.bin", "rb") as f: f.seek(0xcd90) while True: unk = f.readUShort() if unk == 0xffff: break scenepos = f.readUShort() scenebank = "bank_11.bin" if f.readUShort( ) == 0x00f1 else "bank_12.bin" scenename = f.readNullString() scenenames[scenebank].append({ 'pos': scenepos, 'name': scenename }) f.seek(1, 1) # Always 0x90 for bankname in scenenames: scenenames[bankname] = sorted(scenenames[bankname], key=lambda i: i['pos']) # Extract the scripts with codecs.open(outfile, "w", "utf-8") as out: common.logMessage("Extracting script to", outfile, "...") for file in common.showProgress(files): common.logMessage("Processing", file, "...") out.write("!FILE:" + file + "\n") size = os.path.getsize(infolder + file) lastspeaker = -1 if scene: nextscene = scenenames[file].pop(0) if merge: mergesection = common.getSection(mergescript, file) with common.Stream(infolder + file, "rb") as f: while f.tell() < size - 1: opcode = f.readByte() if opcode == 0x0a: strpos = f.tell() # Print the scene name if scene: if (nextscene is not None and strpos >= nextscene['pos']): out.write("\n\n##SCENE:" + nextscene['name'] + "\n") nextscene = scenenames[file].pop(0) if len( scenenames[file]) > 0 else None # Add the speaker name if speaker: # Usually 7 bytes before there's a 0x10 byte, followed by the speaker expression and then the speaker sprite speakercode = -1 f.seek(-7, 1) checkspeaker = f.readByte() if checkspeaker == 0x10: f.seek(1, 1) speakercode = f.readByte() else: # Sometimes, it's 6 bytes before instead so check that one too checkspeaker = f.readByte() if checkspeaker == 0x10: f.seek(1, 1) speakercode = f.readByte() if speakercode != -1: if speakercode not in game.speakercodes: common.logDebug("Unknown speaker code", speakercode) else: if speakercode != lastspeaker: out.write( "#" + game.speakercodes[speakercode] + "\n") lastspeaker = speakercode # Read the string f.seek(strpos) readstr = game.readString(f, table) if readstr != "": if readstr[-2:] == ">>": readstr = readstr[:-2] out.write(readstr) out.write("=") if merge and readstr in mergesection: out.write(mergesection[readstr].pop(0)) if len(mergesection[readstr]) == 0: del mergesection[readstr] out.write("\n") common.logDebug("Read string:", readstr) elif opcode in game.repopcodes: while f.readByte() != 0xff: pass else: f.seek(game.opcodes[opcode], 1) out.write("\n\n") common.logMessage("Done! Extracted", len(files), "files")
def run(data): infolder = data + "extract/" outfolder = data + "repack/" files = ["bank_11.bin", "bank_12.bin"] binfile = outfolder + "bank_14.bin" debugfile = outfolder + "bank_1e.bin" infile = data + "script_input.txt" chartot = transtot = 0 table, invtable, ccodes, glyphs = game.getFontData(data) if not os.path.isfile(infile): common.logError("Input file", infile, "not found") return pointers = {} pointerdiff = {} common.logMessage("Repacking script from", infile, "...") with codecs.open(infile, "r", "utf-8") as script: for file in common.showProgress(files): section = common.getSection(script, file) pointers[file] = [] pointerdiff[file] = {} if len(section) == 0: continue chartot, transtot = common.getSectionPercentage( section, chartot, transtot) # Repack the file common.logMessage("Processing", file, "...") size = os.path.getsize(infolder + file) with common.Stream(infolder + file, "rb") as fin: with common.Stream(outfolder + file, "wb") as f: while fin.tell() < size: opcode = fin.readByte() if opcode == 0x0a: f.writeByte(opcode) strpos2 = f.tell() strpos = fin.tell() readstr = game.readString(fin, table) newstr = "" if readstr != "": addend = "" if readstr[-2:] == ">>": addend = ">>" readstr = readstr[:-2] if readstr in section: newstr = section[readstr].pop(0) if len(section[readstr]) == 0: del section[readstr] strend = fin.tell() if newstr != "": newstr = common.wordwrap( newstr, glyphs, game.wordwrap, game.detectTextCode) if newstr == "!": newstr = "" newstr += addend common.logDebug("Writing string at", common.toHex(strpos), common.toHex(strpos2), newstr) game.writeString(f, newstr, invtable, ccodes) f.writeUShort(0xffff) strend2 = f.tell() lendiff = (strend2 - strpos2) - (strend - strpos) if lendiff != 0: common.logDebug("Adding", lendiff, "at", common.toHex(strpos)) pointerdiff[file][strpos + 1] = lendiff else: fin.seek(strpos) f.write(fin.read(strend - strpos)) elif opcode in game.repopcodes: if opcode in game.ptropcodes: pointers[file].append(f.tell()) f.writeByte(opcode) replen = 0 while True: loopbyte = fin.readByte() f.writeByte(loopbyte) replen += 1 if loopbyte == 0xff and (opcode not in game.ptropcodes or replen > 2): break else: if opcode in game.ptropcodes: pointers[file].append(f.tell()) f.writeByte(opcode) f.write(fin.read(game.opcodes[opcode])) if opcode == 0xff: check = fin.readUInt() check2 = fin.readUInt() fin.seek(-8, 1) if check == 0xffffffff and check2 == 0xffffffff: break # Pad with 0xffff f.writeBytes(0xff, 0xffff - f.tell() + 1) common.logMessage("Shifting script pointers ...") for file in common.showProgress(files): with common.Stream(outfolder + file, "rb+") as f: for pointerpos in pointers[file]: f.seek(pointerpos) opcode = f.readByte() for pointeroff in game.ptropcodes[opcode]: pointerfile = file if type(pointeroff) == tuple: f.seek(pointerpos + 1 + pointeroff[1]) bankid = f.readUShort() pointerfile = files[0] if bankid == 0x00f1 else files[1] pointeroff = pointeroff[0] f.seek(pointerpos + 1 + pointeroff) pointer = f.readUShort() if pointer != 0xffff: newpointer = common.shiftPointer( pointer, pointerdiff[pointerfile]) else: newpointer = pointer f.seek(-2, 1) f.writeUShort(newpointer) common.logMessage("Shifting bin pointers ...") # Read pointer tables in the bin file with common.Stream(binfile.replace("/repack/", "/extract/"), "rb+") as f: f.seek(0x23b0) while f.tell() < 0x2740: pos1 = f.tell() ptr1 = f.readUShort() bank1 = f.readUShort() pos2 = f.tell() ptr2 = f.readUShort() bank2 = f.readUShort() game.binptrs.append((pos1, ptr1, bank1)) game.binptrs.append((pos2, ptr2, bank2)) f.seek(8, 1) for binrange in [(0x2ff7, 0x3027), (0x3063, 0x309b), (0x480f, 0x481f), (0x496b, 0x499b), (0x4a01, 0x4a39)]: f.seek(binrange[0]) while f.tell() < binrange[1]: pos = f.tell() ptr = f.readUShort() bank = f.readUShort() game.binptrs.append((pos, ptr, bank)) f.seek(0x902e) while f.tell() < 0x9106: pos = f.tell() ptr = f.readUShort() game.binptrs.append((pos, ptr, 0xf2)) game.binptrs.append((0x26b8, 0x3d7b, 0xf2)) shiftPointers(binfile, game.binptrs, pointerdiff) # Shift debug pointers debugptrs = [] with common.Stream(debugfile.replace("/repack/", "/extract/"), "rb+") as f: f.seek(0xcd92) while f.tell() < 0xd420: pos1 = f.tell() ptr1 = f.readUShort() bank1 = f.readUShort() debugptrs.append((pos1, ptr1, bank1)) f.seek(12, 1) shiftPointers(debugfile, debugptrs, pointerdiff) common.logMessage("Done! Translation is at {0:.2f}%".format( (100 * transtot) / chartot))
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"))