def extract(rom, font, bin, script, credits, img): all = not rom and not font and not bin and not script and not credits and not img if all or rom: ws.extractRom(romfile, infolder, outfolder) if all or bin: import extract_bin extract_bin.run(data) if all or font: with common.Stream(infolder + "bank_10.bin", "rb") as f: ws.extractTiledImage(f, data + "font_output.png", 16 * 4, 16 * 244) with common.Stream(data + "table_output.txt", "w") as f: columns = ("00", "40", "80", "c0") for row in range(244): for column in range(4): if row < 0x10: f.write("0") f.write(format(row, "x") + columns[column] + "=\n") f.write("\n") if all or script: import extract_script extract_script.run(data) if all or credits: import extract_credits extract_credits.run(data) if all or img: import extract_img extract_img.run(data)
def repackBinaryStrings(elf, section, infile, outfile, readfunc, writefunc, encoding="shift_jis", elfsections=[".rodata"]): with common.Stream(infile, "rb") as fi: with common.Stream(outfile, "r+b") as fo: for sectionname in elfsections: rodata = elf.sectionsdict[sectionname] fi.seek(rodata.offset) while fi.tell() < rodata.offset + rodata.size: pos = fi.tell() check = readfunc(fi, encoding) if check != "": if check in section and section[check][0] != "": common.logDebug("Replacing string", check, "at", common.toHex(pos), "with", section[check][0]) fo.seek(pos) endpos = fi.tell() - 1 newlen = writefunc(fo, section[check][0], endpos - pos + 1) if newlen < 0: fo.writeZero(1) common.logError("String", section[check][0], "is too long.") else: fo.writeZero(endpos - fo.tell()) else: pos = fi.tell() - 1 fi.seek(pos + 1)
def extract(file, outfolder, guessextension=None): common.logDebug("Processing", file, "...") common.makeFolder(outfolder) 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 with common.Stream(file, "rb") as f: for entry in cpk.filetable: if entry.filetype != "FILE": continue folder, filename = entry.getFolderFile(outfolder) f.seek(entry.fileoffset) data = f.read(entry.filesize) f.seek(entry.fileoffset) checkcomp = f.readString(8) if checkcomp == "CRILAYLA": extractsize = entry.extractsize if entry.extractsize != 0 else entry.filesize if extractsize != 0: data = compression.decompressCRILAYLA(f, entry.fileoffset) if guessextension is not None: filename = guessextension(data, entry, filename) if not os.path.isdir(folder): common.makeFolders(folder) with common.Stream(folder + filename, "wb") as fout: fout.write(data)
def repackRom(romfile, rompatch, workfolder, patchfile="", banksize=0x4000): common.logMessage("Repacking ROM", rompatch, "...") filesize = os.path.getsize(romfile) banknum = filesize // banksize common.logMessage("Repacking", banknum, "banks ...") with common.Stream(rompatch, "wb") as fout: for i in range(banknum): bankname = "bank_" if i < 0x10: bankname += "0" bankname += format(i, 'x') with common.Stream(workfolder + bankname + ".bin", "rb") as f: fout.write(f.read()) # Calculate and write the global checksum with common.Stream(rompatch, "rb+", False) as fout: checksum = sum(fout.read(0x14e)) fout.seek(0x150) checksum += sum(fout.read(filesize - 0x150)) fout.seek(0x14e) fout.writeUShort(checksum & 0xffff) common.logMessage("Done!") # Create patch if patchfile != "": common.xdeltaPatch(patchfile, romfile, rompatch) common.ipsPatch(patchfile.replace(".xdelta", ".ips"), romfile, rompatch)
def mpstopmf(infile, outfile, duration): with common.Stream(infile, "rb", False) as fin: # Check header check1 = fin.readUInt() check2 = fin.readByte() if check1 != 0x1ba or check2 != 0x44: common.logError("Input header is wrong", common.toHex(check1), common.toHeck(check2)) return fin.seek(0) mpsdata = fin.read() # https://github.com/TeamPBCN/pmftools/blob/main/mps2pmf/mps2pmf.cpp with common.Stream(outfile, "wb", False) as f: # Magic f.writeString("PSMF") f.writeString("0012") # Header size f.writeUInt(0x800) # MPS size f.writeUInt(len(mpsdata)) f.seek(0x50) # Other header values f.writeUInt(0x4e) f.writeUInt(1) f.writeUShort(0x5f90) f.writeUShort(0) f.writeUInt(duration) f.writeUInt(0x61a8) f.writeUShort(1) f.writeUShort(0x5f90) f.writeUShort(0x201) f.writeUShort(0) f.writeUShort(0x34) f.writeUShort(0) f.writeUShort(1) f.writeUShort(0x5f90) f.writeUShort(0) f.writeUInt(duration) f.writeUShort(1) f.writeUInt(0x22) f.writeUShort(0x2) f.writeUShort(0xe000) f.writeUShort(0x21ef) f.writeUShort(0) f.writeUInt(0x0) f.writeUInt(0x1e11) f.writeUInt(0xbd00) f.writeUShort(0x2004) f.seek(0xa0) f.writeUShort(0x202) # Everything else is 0, write the MPS data f.seek(0x800) f.write(mpsdata)
def run(file, addframes): infile = "data/extract_NFP/NFP2D.NFP/" + file outfile = "data/work_YCE/" + file common.logMessage("Expanding", infile, "to", outfile, "...") with common.Stream(outfile, "wb") as f: with common.Stream(infile, "rb") as fin: # Copy header f.write(fin.read(28)) # Image number num = fin.readUInt() fin.seek(num * 4, 1) f.writeUInt(num + addframes) # Make room for the positions offsetpos = f.tell() for i in range(num + addframes): f.writeUInt(0) # Copy the existing images for i in range(num): newpos = f.tell() f.seek(offsetpos + i * 4) f.writeUInt(newpos - 24) f.seek(newpos) size = fin.readUInt() fin.seek(-4, 1) data = fin.read(size) f.write(data) # Add the new frames for i in range(num, num + addframes): newpos = f.tell() f.seek(offsetpos + i * 4) f.writeUInt(newpos - 24) f.seek(newpos) f.write(data) # Read the animation frames from another file animoffset = f.tell() with common.Stream(animfiles[num + addframes], "rb") as fin: fin.seek(20) animoffset2 = fin.readUInt() fin.seek(animoffset2) animsize = fin.readUInt() fin.seek(-4, 1) f.write(fin.read(animsize)) totsize = f.tell() # Pad with 0s f.writeZero(16 - (f.tell() % 16)) # Write new sizes and offsets f.seek(8) f.writeUInt(totsize) f.seek(20) f.writeUInt(animoffset) f.writeUInt(animoffset - 32) common.logMessage("Done!")
def compressBinary(infile, outfile): with common.Stream(infile, "rb") as fin: data = bytearray(fin.read()) compdata = bytearray(codeCompression.compress(data, True)) codeoffset = 0 for i in range(0, 0x8000, 4): if compdata[i:i+8] == b'\x21\x06\xC0\xDE\xDE\xC0\x06\x21': codeoffset = i - 0x1C break if codeoffset > 0: struct.pack_into("<I", compdata, codeoffset + 0x14, 0x02000000 + len(compdata)) with common.Stream(outfile, "wb") as f: f.write(compdata)
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 readELF(infile): elf = ELF() with common.Stream(infile, "rb") as f: f.seek(0x20) sectionsoff = f.readUInt() f.seek(0x2E) sectionsize = f.readUShort() sectionnum = f.readUShort() shstrndx = f.readUShort() common.logDebug("sectionsoff:", sectionsoff, "sectionsize:", sectionsize, "sectionnum", sectionnum, "shstrndx", shstrndx) # Read section headers f.seek(sectionsoff) for i in range(sectionnum): section = ELFSection() section.nameoff = f.readUInt() section.type = f.readUInt() section.flags = f.readUInt() section.addr = f.readUInt() section.offset = f.readUInt() section.size = f.readUInt() section.link = f.readUInt() section.info = f.readUInt() section.addralign = f.readUInt() section.entsize = f.readUInt() elf.sections.append(section) # Read section names for section in elf.sections: f.seek(elf.sections[shstrndx].offset + section.nameoff) section.name = f.readNullString() elf.sectionsdict[section.name] = section for i in range(sectionnum): common.logDebug(i, vars(elf.sections[i])) return elf
def extractPGFData(file, outfile, bitmapout="", justadvance=False): pgf = readPGFData(file) with common.Stream(file, "rb") as fin: with codecs.open(outfile, "w", "utf-8") as f: for glyph in pgf.glyphs: char = glyph.char.replace("=", "<3D>") if justadvance: f.write(char + "=" + str(glyph.advance["x"]) + "\n") else: data = json.dumps({ "width": glyph.width, "height": glyph.height, "left": glyph.left, "top": glyph.top, "dimension": glyph.dimension, "bearingx": glyph.bearingx, "bearingy": glyph.bearingy, "advance": glyph.advance }) f.write(char + "=" + data + "\n") if bitmapout != "" and glyph.width > 0 and glyph.height > 0: fin.seek(pgf.glyphpos + pgf.charptr[glyph.index] + glyph.totlen // 8) buf = fin.read(1024) extractPGFBitmap( buf, glyph, bitmapout + str(glyph.index).zfill(4) + ".png")
def readGIM(file, start=0): gim = GIM() with common.Stream(file, "rb") as f: f.seek(start) if f.readString(3) == 'MIG': f.seek(start + 16) gim.rootoff = f.tell() id = f.readUShort() if id != 0x02: common.logError("Unexpected id in block 0:", common.toHex(id), common.toHex(f.tell() - 2)) return None f.seek(2, 1) gim.rootsize = f.readUInt() nextblock = gim.rootoff + f.readUInt() image = None while nextblock > 0 and nextblock < start + gim.rootsize + 16: f.seek(nextblock) nextblock, image = readGIMBlock(f, gim, image) else: # This is a TGA file, assuming 32bit RGBA image = TGAImage() image.rootoff = f.tell() f.seek(start + 2) image.format = f.readByte() f.seek(9, 1) image.width = f.readUShort() image.height = f.readUShort() f.seek(2, 1) image.imgoff = f.tell() for i in range(image.height): for j in range(image.width): image.colors.append(readColor(f, 0x03)) gim = image return gim
def repack(fin, f, archive, infolder): # Copy everything up to dataoff fin.seek(0) f.seek(0) f.write(fin.read(archive.dataoff)) # Loop the files dataoff = 0 for i in range(archive.filenum): subfile = archive.files[i] filepath = infolder + subfile.name if not os.path.isfile(filepath): # Just update the offset and copy the file f.seek(archive.fatoff + i * 16) f.seek(8, 1) f.writeUInt(dataoff) fin.seek(archive.dataoff + subfile.offset) f.seek(archive.dataoff + dataoff) f.write(fin.read(subfile.length)) else: # Set the file as not encoded and copy it size = os.path.getsize(filepath) size += size % 16 f.seek(archive.fatoff + i * 16) f.writeUInt(size) f.writeUInt(size) f.writeUInt(dataoff) f.seek(2, 1) f.writeUShort(0) f.seek(archive.dataoff + dataoff) with common.Stream(filepath, "rb") as subf: f.write(subf.read()) # Align with 0s if f.tell() % 16 > 0: f.writeZero(16 - (f.tell() % 16)) dataoff = f.tell() - archive.dataoff
def repack(no_rom, bin, cnut, ncgr): all = not bin and not cnut and not ncgr if all or bin: nds.repackBIN(game.binrange, game.freeranges, game.detectShiftJIS, game.writeBINShiftJIS, "shift_jis", "//") common.logMessage("Patching BIN ...") with common.Stream(binfile, "r+b") as fo: # Patch the text rendering function to allow more than 15 characters per line (Thanks StorMyu!) fo.seek(0x6680C) fo.writeByte(0xFF) common.logMessage("Done!") if all or cnut: import repack_cnut repack_cnut.run() if all or ncgr: nitro.repackIMG("data/work_NCGR/", "data/extract/data/Rom/", "data/repack/data/Rom/", [".NCGR", ".NCBR"], game.readImage) if not no_rom: if os.path.isdir(replacefolder): common.mergeFolder(replacefolder, outfolder) nds.editBannerTitle(bannerfile, "Soul Eater\nMedusa's Plot\nBandai Namco Games") nds.repackRom(romfile, rompatch, outfolder, patchfile)
def run(): infile = "data/extract/arm9.bin" outfile = "data/bin_output.txt" # Set to False to analyze the whole file limit = True common.logMessage("Extracting BIN to", outfile, "...") with codecs.open(outfile, "w", "utf-8") as out: with common.Stream(infile, "rb") as f: # Skip the beginning and end of the file to avoid false-positives f.seek(992000 if limit else 900000) foundstrings = [] while f.tell() < 1180000: pos = f.tell() if not limit or pos < 1010000 or pos > 1107700: check = game.detectShiftJIS(f) # Save the string if we detected one if check != "": if check not in foundstrings: common.logDebug("Found string at", pos) foundstrings.append(check) out.write(check + "=\n") pos = f.tell() - 1 f.seek(pos + 1) common.logMessage("Done! Extracted", len(foundstrings), "lines")
def repack(font, bin, script, img, credits, debug, angel, no_rom): all = not font and not bin and not script and not img and not credits if all or font: import repack_font repack_font.run(data) if all or bin: import repack_bin repack_bin.run(data) if all or script or bin: import repack_script repack_script.run(data) if all or img: import repack_img repack_img.run(data) if all or credits: import repack_credits repack_credits.run(data) # https://tcrf.net/Neon_Genesis_Evangelion:_Shito_Ikusei with common.Stream(outfolder + "bank_14.bin", "rb+") as f: if debug or angel: if debug: f.seek(0x97) f.writeUInt(0x4000cc6e) f.seek(0xa6b9) f.writeByte(0x0e if not angel else 0x0d) else: f.seek(0x97) f.writeUInt(0x0) f.seek(0xa6b9) f.writeByte(0x3) if not no_rom: if os.path.isdir(replacefolder): common.mergeFolder(replacefolder, outfolder) ws.repackRom(romfile, rompatch, outfolder, patchfile)
def repackFontData(infile, outfile, datafile): common.logMessage("Repacking font data from", datafile, "...") common.copyFile(infile, outfile) glyphs = getFontGlyphs(infile) with codecs.open(datafile, "r", "utf-8") as f: section = common.getSection(f, "") if len(section) == 0: return with common.Stream(outfile, "rb+", False) as f: # Header f.seek(36) hdwcoffset = f.readUInt() # HDWC f.seek(hdwcoffset - 4) hdwclen = f.readUInt() tilenum = (hdwclen - 16) // 3 f.seek(8, 1) for i in range(tilenum): found = False for glyph in glyphs.values(): if glyph.index == i: sectionglyph = glyph.char if glyph.char != "=" else "<3D>" if sectionglyph in section: common.logDebug("Writing", section[sectionglyph][0], "at", f.tell()) fontdata = section[sectionglyph][0].split(",") f.writeSByte(int(fontdata[0])) f.writeByte(int(fontdata[1])) f.writeByte(int(fontdata[2])) found = True break if not found: f.seek(3, 1) common.logMessage("Done!")
def asmPatch(file, workfolder, banks=[0x0], banksize=0x4000): common.logMessage("Applying ASM patch ...") wlagb = common.bundledExecutable("wla-gb.exe") if not os.path.isfile(wlagb): common.logError("wla-gb not found") return wlalink = common.bundledExecutable("wlalink.exe") if not os.path.isfile(wlalink): common.logError("wlalink not found") return # Create the output file ofile = file.replace(".asm", ".o") if os.path.isfile(ofile): os.remove(ofile) common.execute( wlagb + " -o {ofile} {binpatch}".format(binpatch=file, ofile=ofile), False) if not os.path.isfile(ofile): return # Run the linker and create a temporary patched ROM tempfile = file.replace(".asm", ".txt") deletetemp = False if not os.path.isfile(tempfile): deletetemp = True with open(tempfile, "w") as f: f.write("[objects]\n") f.write(ofile + "\n") temprom = "temprom.gb" common.execute( wlalink + " -r {tempfile} {temprom}".format(tempfile=tempfile, temprom=temprom), False) if deletetemp: os.remove(tempfile) os.remove(ofile) # Extract the banks we're interested in from the temp ROM with common.Stream(temprom, "rb") as f: for i in banks: bankname = "bank_" if i < 0x10: bankname += "0" bankname += format(i, 'x') f.seek(i * banksize) with common.Stream(workfolder + bankname + ".bin", "wb") as fout: fout.write(f.read(banksize)) os.remove(temprom) common.logMessage("Done!")
def shiftPointers(file, ptrs, pointerdiff): with common.Stream(file, "rb+") as f: for binptr in ptrs: f.seek(binptr[0]) newpointer = common.shiftPointer( binptr[1], pointerdiff["bank_11.bin" if binptr[2] == 0xf1 else "bank_12.bin"]) f.writeUShort(newpointer)
def decompressCRILAYLA(f, fileoffset): def deflatelevels(): for v in [2, 3, 5, 8]: yield v while True: yield 8 uncsize = f.readUInt() uncheaderoffset = f.readUInt() # common.logDebug("decompressCRILAYLA uncsize", uncsize, "uncheaderoffset", uncheaderoffset) with common.Stream() as decmp: cmp = CompressedBitInput(f.read(uncheaderoffset)) while True: bit = cmp.read01(1) if bit == '': break if int(bit, 2): offset = cmp.readnum(13) + 3 refc = 3 for lv in deflatelevels(): bits = cmp.read(lv) refc += int(bits.to01(), 2) if not bits.all(): break while refc > 0: decmp.seek(-offset, 1) ref = decmp.read(refc) decmp.seek(0, 2) decmp.write(ref) refc -= len(ref) else: b = cmp.readbyte() decmp.write(b) cmp.close() with common.Stream() as result: # Copy the uncompressed 0x100 header f.seek(fileoffset + 0x10 + uncheaderoffset) result.write(f.read(0x100)) # Copy the uncompressed data, reversed decmp.seek(0) result.write(decmp.read()[:uncsize][::-1]) result.seek(0) return result.read()
def extractRom(romfile, extractfolder, workfolder="", banksize=0x4000): common.logMessage("Extracting ROM", romfile, "...") common.makeFolder(extractfolder) filesize = os.path.getsize(romfile) banknum = filesize // banksize common.logMessage("Extracting", banknum, "banks ...") with common.Stream(romfile, "rb") as f: for i in range(banknum): bankname = "bank_" if i < 0x10: bankname += "0" bankname += format(i, 'x') with common.Stream(extractfolder + bankname + ".bin", "wb") as fout: fout.write(f.read(banksize)) if workfolder != "": common.copyFolder(extractfolder, workfolder) common.logMessage("Done!")
def getFontGlyphs(file): glyphs = {} with common.Stream(file, "rb", False) as f: # Header f.seek(36) hdwcoffset = f.readUInt() pamcoffset = f.readUInt() common.logDebug("hdwcoffset:", hdwcoffset, "pamcoffset:", pamcoffset) # HDWC f.seek(hdwcoffset - 4) hdwclen = f.readUInt() tilenum = (hdwclen - 16) // 3 firstcode = f.readUShort() lastcode = f.readUShort() f.seek(4, 1) common.logDebug("firstcode:", firstcode, "lastcode:", lastcode, "tilenum", tilenum) hdwc = [] for i in range(tilenum): hdwcstart = f.readSByte() hdwcwidth = f.readByte() hdwclength = f.readByte() hdwc.append((hdwcstart, hdwcwidth, hdwclength)) # PAMC nextoffset = pamcoffset while nextoffset != 0x00: f.seek(nextoffset) firstchar = f.readUShort() lastchar = f.readUShort() sectiontype = f.readUShort() f.seek(2, 1) nextoffset = f.readUInt() common.logDebug("firstchar:", common.toHex(firstchar), "lastchar:", common.toHex(lastchar), "sectiontype:", sectiontype, "nextoffset:", nextoffset) if sectiontype == 0: firstcode = f.readUShort() for i in range(lastchar - firstchar + 1): c = common.codeToChar(firstchar + i) glyphs[c] = common.FontGlyph(hdwc[firstcode + i][0], hdwc[firstcode + i][1], hdwc[firstcode + i][2], c, firstchar + i, firstcode + i) elif sectiontype == 1: for i in range(lastchar - firstchar + 1): charcode = f.readUShort() if charcode == 0xFFFF or charcode >= len(hdwc): continue c = common.codeToChar(firstchar + i) glyphs[c] = common.FontGlyph(hdwc[charcode][0], hdwc[charcode][1], hdwc[charcode][2], c, firstchar + i, charcode) else: common.logWarning("Unknown section type", sectiontype) return glyphs
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 extract(f, archive, outfolder): for subfile in archive.files: with common.Stream(outfolder + subfile.name, "wb") as fout: f.seek(archive.dataoff + subfile.offset) if not subfile.encoded: fout.write(f.read(subfile.length)) else: # Based on Tinke's ARCH implementation startpos = f.tell() buffer1 = [] buffer2 = [] for i in range(0x100): buffer1.append(0) buffer2.append(0) while f.tell() - startpos < subfile.length: # InitBuffer for i in range(0x100): buffer2[i] = i # FillBuffer index = 0 while index != 0x100: id = f.readByte() numloops = id if id > 0x7F: numloops = 0 index += id - 0x7F if index == 0x100: break if numloops < 0: continue for i in range(numloops + 1): byte = f.readByte() buffer2[index] = byte if byte != index: buffer1[index] = f.readByte() index += 1 # Process numloops = (f.readByte() << 8) + f.readByte() nextsamples = [] while True: if len(nextsamples) == 0: if numloops == 0: break numloops -= 1 index = f.readByte() else: index = nextsamples.pop() if buffer2[index] == index: fout.writeByte(index) else: nextsamples.append(buffer1[index]) nextsamples.append(buffer2[index]) index = len(nextsamples)
def repack(no_rom, dat, bin, img, wsb, sub, no_redirect, force): all = not sub and not dat and not bin and not img and not wsb firstgame = nds.getHeaderID(headerfile) == "YU5J2J" if all or dat: import repack_dat repack_dat.run(firstgame, no_redirect) if all or dat or bin: import repack_bin repack_bin.run(firstgame) if all or wsb: import repack_wsb repack_wsb.run(firstgame) if all or dat or sub: import repack_sub repack_sub.run(firstgame) if all or img: ncgrfolder = "data/repack/data/graphic/" if firstgame else "data/repack/data/graphics/" ncgrfolderin = ncgrfolder.replace("repack", "extract") common.copyFolder(ncgrfolderin, ncgrfolder) nitro.repackIMG("data/work_IMG/", ncgrfolderin, ncgrfolder, ".NCGR", game.readImage) if not firstgame: import repack_kbg repack_kbg.run() # Part of the AP patch with common.Stream(ncgrfolder + "doubleinfo/SDLawrence_01.NCGR", "rb+") as f: f.seek(0x26f1) f.writeByte(0x17) if not no_rom: if os.path.isdir(replacefolder): common.mergeFolder(replacefolder, outfolder) if force != "": if not force.endswith(".wsb"): force += ".wsb" if firstgame: common.copyFile( outfolder + "data/script/" + force, outfolder + "data/script/event/ev_act/act_010_opening.wsb") else: common.copyFile( outfolder + "data/script/" + force, outfolder + "data/script/event/ev_main/main_010_prologue.wsb") subtitle = "My Year with Holo" if firstgame else "The Wind that Spans the Sea" nds.editBannerTitle( bannerfile, "Spice & Wolf\n" + subtitle + "\nASCII MEDIA WORKS") romf = romfile if os.path.isfile(romfile) else romfile.replace( "holo", "holo2") romp = rompatch if os.path.isfile(romfile) else rompatch.replace( "holo", "holo2") nds.repackRom(romf, romp, outfolder, patchfile)
def compressLZ10(indata, mindisp=1): with common.Stream() as out: inlength = len(indata) compressedlength = 0 instart = 0 # we do need to buffer the output, as the first byte indicates which blocks are compressed. # this version does not use a look-ahead, so we do not need to buffer more than 8 blocks at a time. outbuffer = bytearray(8 * 2 + 1) outbuffer[0] = 0 bufferlength = 1 bufferedblocks = 0 readbytes = 0 while readbytes < inlength: # If 8 blocks are buffered, write them and reset the buffer # we can only buffer 8 blocks at a time. if bufferedblocks == 8: out.write(outbuffer[:bufferlength]) compressedlength += bufferlength # reset the buffer outbuffer[0] = 0 bufferlength = 1 bufferedblocks = 0 # determine if we're dealing with a compressed or raw block. # it is a compressed block when the next 3 or more bytes can be copied from # somewhere in the set of already compressed bytes. oldlength = min(readbytes, 0x1000) length, disp = getOccurrenceLength(indata, instart + readbytes, min(inlength - readbytes, 0x12), instart + readbytes - oldlength, oldlength, mindisp) # length not 3 or more? next byte is raw data if length < 3: outbuffer[bufferlength] = indata[instart + readbytes] bufferlength += 1 readbytes += 1 else: # 3 or more bytes can be copied? next (length) bytes will be compressed into 2 bytes readbytes += length # mark the next block as compressed outbuffer[0] |= (1 << (7 - bufferedblocks)) outbuffer[bufferlength] = ((length - 3) << 4) & 0xF0 outbuffer[bufferlength] |= ((disp - 1) >> 8) & 0x0F bufferlength += 1 outbuffer[bufferlength] = (disp - 1) & 0xFF bufferlength += 1 bufferedblocks += 1 # copy the remaining blocks to the output if bufferedblocks > 0: out.write(outbuffer[:bufferlength]) compressedlength += bufferlength out.seek(0) return out.read()
def editBannerTitle(file, title): with common.Stream(file, "r+b") as f: for i in range(6): # Write new text for all languages f.seek(576 + 256 * i) for char in title: f.writeByte(ord(char)) f.writeByte(0x00) # Compute CRC f.seek(32) crc = crcmod.predefined.mkCrcFun("modbus")(f.read(2080)) f.seek(2) f.writeUShort(crc)
def readGMO(file): gmo = GMO() with common.Stream(file, "rb") as f: f.seek(16 + 4) gmo.size = f.readUInt() f.seek(8, 1) while f.tell() < gmo.size + 16: readGMOChunk(f, gmo, gmo.size + 16) for gimoffset in gmo.offsets: common.logDebug("Reading GIM at", common.toHex(gimoffset)) gim = readGIM(file, gimoffset) gmo.gims.append(gim) return gmo
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 readUTFData(f): f.setEndian(True) unk1 = f.readInt() utfsize = f.readLong() # common.logDebug("readUTFData unk1", common.toHex(unk1), "size", common.toHex(utfsize)) utfpacket = f.read(utfsize) encrypted = False if utfpacket[:4].decode("ascii", "ignore") != "@UTF": utfpacket = decryptUTF(utfpacket) encrypted = True f.setEndian(False) packetstream = common.Stream(little=False).__enter__() packetstream.write(utfpacket) packetstream.seek(0) return packetstream, utfsize, encrypted