def readShiftJIS(f, encoding="shift_jis"): strlen = f.readUInt() sjis = "" i = 0 while i < strlen: b1 = f.readByte() if b1 == 0x0A: sjis += "|" i += 1 else: b2 = f.readByte() if (b1, b2) in fixchars: sjis += fixchars[(b1, b2)] i += 2 elif not common.checkShiftJIS(b1, b2): if b2 == 0x01: sjis += "UNK(" + common.toHex(b1) + common.toHex(b2) + ")" i += 2 else: f.seek(-1, 1) sjis += chr(b1) i += 1 else: f.seek(-2, 1) try: sjis += f.read(2).decode(encoding).replace("〜", "~") except UnicodeDecodeError: common.logError("[ERROR] UnicodeDecodeError") sjis += "[ERROR" + str(f.tell() - 2) + "]" i += 2 return sjis
def readShiftJIS(f): len = f.readShort() pos = f.tell() # Check if the string is all ascii ascii = True for i in range(len - 1): byte = f.readByte() if byte != 0x0A and (byte < 32 or byte > 122): ascii = False break if not ascii: f.seek(pos) sjis = "" i = 0 while i < len - 1: byte = f.readByte() if byte in codes: sjis += "<" + common.toHex(byte) + ">" i += 1 else: f.seek(-1, 1) try: sjis += f.read(2).decode("shift-jis").replace("〜", "~") except UnicodeDecodeError: common.logError("UnicodeDecodeError") sjis += "|" i += 2 return sjis return ""
def readMappedImage(f, outfile, mapstart=0, num=1, bpp=2, width=0, height=0): f.seek(mapstart) maps = [] for j in range(num): map = TileMap() if num > 1: map.name = outfile.replace(".png", "_" + str(j + 1).zfill(2) + ".png") else: map.name = outfile map.offset = f.tell() map.width = width if map.width == 0: map.width = f.readByte() map.height = height if map.height == 0: map.height = f.readByte() map.bpp = bpp common.logDebug(" ", mapstart, vars(map)) map.map = [] for i in range(map.width * map.height): tilemap = TileMap() tilemap.data = f.readUShort() tilemap.tile = tilemap.data & 0x1ff tilemap.pal = (tilemap.data >> 9) & 0xf tilemap.bank = (tilemap.data >> 13) & 1 if tilemap.bank != 0 and bpp == 2: common.logError("Bank is not 0") tilemap.hflip = ((tilemap.data >> 14) & 1) == 1 tilemap.vflip = ((tilemap.data >> 15) & 1) == 1 map.map.append(tilemap) maps.append(map) common.logDebug("Map data ended at", common.toHex(f.tell())) return maps
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 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 writeString(f, s, table, ccodes, maxlen=0, usebigrams=False): s = s.replace("~", "〜") x = i = 0 while x < len(s): c = s[x] if c == "<" and x < len(s) - 4 and s[x:x + 4] in codes: number = int(s[x + 4:].split(">", 1)[0]) f.writeUShort(codes[s[x:x + 4]]) f.writeUShort(number) x += 4 + len(str(number)) i += 4 elif c == "<": code = s[x + 1:].split(">", 1)[0] if code in singlecodes: f.writeUShort(singlecodes[code]) else: f.writeUShort(int(code, 16)) x += 1 + len(code) i += 2 elif c == "U" and x < len(s) - 4 and s[x:x + 4] == "UNK(": code = s[x + 6] + s[x + 7] f.writeByte(int(code, 16)) code = s[x + 4] + s[x + 5] f.writeByte(int(code, 16)) x += 8 i += 2 elif c == ">" and s[x + 1] == ">": f.writeUShort(0xff00) f.writeUShort(0xff04) x += 1 i += 4 elif c == "|": i += 2 f.writeUShort(0xff02) elif c in ccodes or ord(c) < 256: i += 1 if c not in ccodes: common.logError("Character not found:", c, "in string", s) c = " " if x < len(s) - 1 and c + s[x + 1] in ccodes: f.writeByte(int(ccodes[c + s[x + 1]][0], 16)) x += 1 else: f.writeByte(int(ccodes[c][0], 16)) else: if c in table: f.writeUShort(int(table[c], 16)) else: f.writeUShort(0) i += 2 x += 1 if maxlen > 0 and i > maxlen: common.logError("Line too long", str(i) + "/" + str(len(s) - x) + "/" + str(maxlen), s) i = -1 break return i
def readGTOC(f, cpk, tocoffset): f.seek(tocoffset) headercheck = f.readString(4) if headercheck != "GTOC": common.logError("Wrong GTOC header", headercheck) return utfpacket, utfsize, encrypted = readUTFData(f) tocentry = cpk.getFileEntry("GTOC_HDR") tocentry.encrypted = encrypted tocentry.filesize = utfsize files = readUTF(utfpacket, tocoffset)
def extractBRFNT(infile, outfile): brfnt2tpl = common.bundledExecutable("brfnt2tpl.exe") if not os.path.isfile(brfnt2tpl): common.logError("brfnt2tpl not found") return common.execute(brfnt2tpl + " {file}".format(file=infile), False) common.execute( "wimgt DECODE " + infile.replace(".brfnt", ".tpl") + " -D " + outfile, False) os.remove(infile.replace(".brfnt", ".tpl")) os.remove(infile.replace(".brfnt", ".vbfta"))
def extractRom(romfile, extractfolder, workfolder=""): common.logMessage("Extracting ROM", romfile, "...") ndstool = common.bundledExecutable("ndstool.exe") if not os.path.isfile(ndstool): common.logError("ndstool not found") return common.makeFolder(extractfolder) common.execute(ndstool + " -x {rom} -9 {folder}arm9.bin -7 {folder}arm7.bin -y9 {folder}y9.bin -y7 {folder}y7.bin -t {folder}banner.bin -h {folder}header.bin -d {folder}data -y {folder}overlay". format(rom=romfile, folder=extractfolder), False) if workfolder != "": common.copyFolder(extractfolder, workfolder) common.logMessage("Done!")
def readUTF(f, baseoffset, storeraw=False): offset = f.tell() headercheck = f.readString(4) if headercheck != "@UTF": common.logError("Wrong UTF header", headercheck) return None utf = UTF() utf.tablesize = f.readInt() utf.rowsoffset = f.readInt() + offset + 8 utf.stringsoffset = f.readInt() + offset + 8 utf.dataoffset = f.readInt() + offset + 8 utf.tablename = f.readInt() utf.numcolumns = f.readShort() utf.rowlength = f.readShort() utf.numrows = f.readInt() utf.baseoffset = baseoffset if storeraw: utf.rawpacket = f # common.logDebug("UTF", vars(utf)) for i in range(utf.numcolumns): column = UTFColumn() column.flags = f.readByte() if column.flags == 0: common.logDebug("Column flag is 0, skipping 3 bytes") f.seek(3, 1) column.flags = f.readByte() column.storagetype = column.flags & UTFColumnFlags.STORAGE_MASK nameoffset = f.readInt() + utf.stringsoffset # Assume ASCII, might be better to assume UTF8? column.name = f.readNullStringAt(nameoffset) if column.flags & UTFColumnFlags.STORAGE_MASK == UTFColumnFlags.STORAGE_CONSTANT: column.position = f.tell() column.data, column.type = readUTFTypedData(f, utf, column.flags) utf.columns.append(column) utf.columnlookup[column.name] = i common.logDebug("UTFColumn", i, vars(column)) for j in range(utf.numrows): f.seek(utf.rowsoffset + (j * utf.rowlength)) rows = [] for i in range(utf.numcolumns): column = utf.columns[i] row = UTFRow() if column.storagetype == UTFColumnFlags.STORAGE_ZERO: row.data = 0 elif column.storagetype == UTFColumnFlags.STORAGE_CONSTANT: row.data = column.data elif column.storagetype == UTFColumnFlags.STORAGE_PERROW: row.position = f.tell() row.data, row.type = readUTFTypedData(f, utf, column.flags) rows.append(row) # common.logDebug("UTFRow", j, i, column.name, vars(row)) utf.rows.append(rows) return utf
def repackRom(romfile, rompatch, workfolder, patchfile=""): common.logMessage("Repacking ROM", rompatch, "...") ndstool = common.bundledExecutable("ndstool.exe") if not os.path.isfile(ndstool): common.logError("ndstool not found") return common.execute(ndstool + " -c {rom} -9 {folder}arm9.bin -7 {folder}arm7.bin -y9 {folder}y9.bin -y7 {folder}y7.bin -t {folder}banner.bin -h {folder}header.bin -d {folder}data -y {folder}overlay". format(rom=rompatch, folder=workfolder), False) common.logMessage("Done!") # Create xdelta patch if patchfile != "": common.xdeltaPatch(patchfile, romfile, rompatch)
def readSprite(f, spritelen, outfile, spritestart=0, bpp=2, width=0, height=0, ignorepal=False): f.seek(spritestart) xmax = ymax = 0 tiles = [] for i in range(spritelen): spritemap = SpriteData() spritemap.data = f.readUInt() spritemap.tile = spritemap.data & 0x1ff spritemap.pal = (spritemap.data >> 9) & 0x7 spritemap.hflip = ((spritemap.data >> 14) & 1) == 1 spritemap.vflip = ((spritemap.data >> 15) & 1) == 1 spritemap.ypos = (spritemap.data >> 16) & 0xff if spritemap.ypos % 8 != 0: common.logError("Sprite ypos is not a multiple of 8", spritemap.ypos) spritemap.xpos = (spritemap.data >> 24) & 0xff if spritemap.xpos % 8 != 0: common.logError("Sprite xpos is not a multiple of 8", spritemap.xpos) if spritemap.ypos > ymax: ymax = spritemap.ypos if spritemap.xpos > xmax: xmax = spritemap.xpos tiles.append(spritemap) # Convert this to map data map = TileMap() map.name = outfile map.offset = spritestart map.width = (xmax + 8) // 8 map.height = (ymax + 8) // 8 map.bpp = bpp map.map = [] for y in range(map.height): for x in range(map.width): data = TileData(0) # Search for a sprite with these coordinates for i in range(len(tiles)): if tiles[i].ypos == y * 8 and tiles[i].xpos == x * 8: data.tile = tiles[i].tile if not ignorepal: data.pal = tiles[i].pal data.hflip = tiles[i].hflip data.vflip = tiles[i].vflip break map.map.append(data) return [map]
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 writeMappedImage(f, tilestart, maps, palettes, num=1, skipzero=False): maxtile = tilesize = 0 for i in range(num): mapdata = maps[i] if mapdata.width == 0: common.logError("Width is 0") continue if mapdata.height == 0: common.logError("Height is 0") continue imgwidth = mapdata.width * 8 imgheight = mapdata.height * 8 pali = 0 if mapdata.bpp == 4 and palettes != colpalette: imgwidth += 40 for palette in palettes: if palette.count((0x0, 0x0, 0x0, 0xff)) == 16: break pali += 1 imgheight = max(imgheight, pali * 10) img = Image.new("RGB", (imgwidth, imgheight), (0x0, 0x0, 0x0)) pixels = img.load() x = y = 0 for map in mapdata.map: tilesize = (16 if mapdata.bpp == 2 else 32) if map.tile > maxtile: maxtile = map.tile if (map.tile > 0 or not skipzero) and (mapdata.bpp != 2 or map.bank == 0): f.seek(tilestart + map.bank * 0x4000 + map.tile * tilesize) try: readTile( f, pixels, x * 8, y * 8, palettes[map.pal] if map.pal < len(palettes) else palettes[0], map.hflip, map.vflip, mapdata.bpp) except struct.error: pass except IndexError: pass x += 1 if x == mapdata.width: y += 1 x = 0 if pali > 0: palstart = 0 for i in range(pali): pixels = common.drawPalette(pixels, palettes[i], imgwidth - 40, palstart * 10) palstart += 1 img.save(mapdata.name, "PNG") common.logDebug("Tile data ended at", common.toHex(tilestart + maxtile * tilesize + tilesize))
def patchdump(): patchfile = "data/bad_to_good.xdelta" common.logMessage("Creating xdelta patch", patchfile, "...") xdelta = common.bundledFile("xdelta.exe") if not os.path.isfile(xdelta): common.logError("xdelta not found") return common.execute( xdelta + " -f -e -s {rom} {rompatch} {patch}".format( rom=romfile.replace(".nds", "_bad.nds"), rompatch=romfile, patch=patchfile), False) common.logMessage("Done!")
def repackBRFNT(outfile, workfile): brfnt2tpl = common.bundledExecutable("brfnt2tpl.exe") if not os.path.isfile(brfnt2tpl): common.logError("brfnt2tpl not found") return common.execute(brfnt2tpl + " {file}".format(file=outfile), False) tplfile = outfile.replace(".brfnt", ".tpl") tpl = readTPL(tplfile) writeTPL(tplfile, tpl, workfile) common.execute( brfnt2tpl + " {file}".format(file=outfile.replace(".brfnt", ".tpl")), False) os.remove(outfile.replace(".brfnt", ".tpl")) os.remove(outfile.replace(".brfnt", ".vbfta"))
def repackEXE(binrange, freeranges=None, manualptrs=None, readfunc=common.detectEncodedString, writefunc=common.writeEncodedString, encoding="shift_jis", comments="#", exein="", exeout="", ptrfile="data/manualptrs.asm", exefile="data/exe_input.txt"): if not os.path.isfile(exefile): common.logError("Input file", exefile, "not found") return False common.copyFile(exein, exeout) common.logMessage("Repacking EXE from", exefile, "...") section = {} with codecs.open(exefile, "r", "utf-8") as bin: section = common.getSection(bin, "", comments) chartot, transtot = common.getSectionPercentage(section) if type(binrange) == tuple: binrange = [binrange] notfound = common.repackBinaryStrings(section, exein, exeout, binrange, freeranges, readfunc, writefunc, encoding, 0x8000F800) # Handle not found pointers by manually replacing the opcodes if len(notfound) > 0 and manualptrs is not None: with open(ptrfile, "w") as f: for ptr in notfound: if ptr.old not in manualptrs: common.logError("Manual pointer", common.toHex(ptr.old), "->", common.toHex(ptr.new), "not found for string", ptr.str) continue for manualptr in manualptrs[ptr.old]: ptrloc = manualptr[0] ptrreg = manualptr[1] common.logDebug("Reassembling manual pointer", common.toHex(ptr.old), "->", common.toHex(ptr.new), "at", common.toHex(ptrloc), ptrreg) f.write(".org 0x" + common.toHex(ptrloc) + "\n") f.write(".area 0x8,0x0\n") f.write(" li " + ptrreg + ",0x" + common.toHex(ptr.new) + "\n") f.write(".endarea\n\n") common.logMessage("Done! Translation is at {0:.2f}%".format( (100 * transtot) / chartot)) return True
def extractBIN(infolder, outfolder, cuefile, data="data/"): common.logMessage("Extracting BIN", cuefile, "...") if not os.path.isfile("psximager\\psxrip.exe"): common.logError("psximager not found") return common.clearFolder(infolder) common.execute( "psximager\\psxrip.exe \"{iso}\" \"{folder}\"".format( iso=cuefile, folder=infolder[:-1]), False) common.copyFile(data + "extract.sys", data + "repack.sys") with open(data + "extract.cat", "r") as fin: with open(data + "repack.cat", "w") as fout: fout.write(fin.read().replace(data + "extract", data + "repack")) common.copyFolder(infolder, outfolder) common.logMessage("Done!")
def readCPK(file): with common.Stream(file, "rb") as f: magic = f.readString(4) if magic != 'CPK ': common.logError("Wrong magic:", magic) return None cpk = CPK() utfoffset = f.tell() utfpacket, utfsize, encrypted = readUTFData(f) cpak = CPKFileEntry() cpak.filename = "CPK_HDR" cpak.fileoffsetpos = f.tell() + 0x10 cpak.filesize = utfsize cpak.encrypted = encrypted cpak.filetype = "CPK" utf = readUTF(utfpacket, utfoffset, True) if utf is None: common.logError("Error reading first UTF") return None cpak.utf = utf cpk.filetable.append(cpak) tocoffset, tocoffsetpos = utf.getColumnData(0, "TocOffset", UTFStructTypes.DATA_TYPE_UINT64) etocoffset, etocoffsetpos = utf.getColumnData(0, "EtocOffset", UTFStructTypes.DATA_TYPE_UINT64) itocoffset, itocoffsetpos = utf.getColumnData(0, "ItocOffset", UTFStructTypes.DATA_TYPE_UINT64) gtocoffset, gtocoffsetpos = utf.getColumnData(0, "GtocOffset", UTFStructTypes.DATA_TYPE_UINT64) contentoffset, contentoffsetpos = utf.getColumnData(0, "ContentOffset", UTFStructTypes.DATA_TYPE_UINT64) cpk.filetable.append(CPKFileEntry.createEntry("CONTENT_OFFSET", contentoffset, UTFStructTypes.DATA_TYPE_UINT64, contentoffsetpos, "CPK", "CONTENT", False)) files, _ = utf.getColumnData(0, "Files", UTFStructTypes.DATA_TYPE_UINT32) cpk.align, _ = utf.getColumnData(0, "Align", UTFStructTypes.DATA_TYPE_UINT16) common.logDebug("tocoffset", common.toHex(tocoffset), "tocoffsetpos", common.toHex(tocoffsetpos)) common.logDebug("etocoffset", common.toHex(etocoffset), "etocoffsetpos", common.toHex(etocoffsetpos)) common.logDebug("itocoffset", common.toHex(itocoffset), "itocoffsetpos", common.toHex(itocoffsetpos)) common.logDebug("gtocoffset", common.toHex(gtocoffset), "gtocoffsetpos", common.toHex(gtocoffsetpos)) common.logDebug("contentoffset", common.toHex(contentoffset), "contentoffsetpos", common.toHex(contentoffsetpos)) common.logDebug("files", common.toHex(files), "align", common.toHex(cpk.align)) if tocoffset != 0xffffffffffffffff: cpk.filetable.append(CPKFileEntry.createEntry("TOC_HDR", tocoffset, UTFStructTypes.DATA_TYPE_UINT64, tocoffsetpos, "CPK", "HDR", False)) readTOC(f, cpk, tocoffset, contentoffset) if etocoffset != 0xffffffffffffffff: cpk.filetable.append(CPKFileEntry.createEntry("ETOC_HDR", etocoffset, UTFStructTypes.DATA_TYPE_UINT64, etocoffsetpos, "CPK", "HDR", False)) readETOC(f, cpk, etocoffset) if itocoffset != 0xffffffffffffffff: cpk.filetable.append(CPKFileEntry.createEntry("ITOC_HDR", itocoffset, UTFStructTypes.DATA_TYPE_UINT64, itocoffsetpos, "CPK", "HDR", False)) readITOC(f, cpk, itocoffset, contentoffset, cpk.align) if gtocoffset != 0xffffffffffffffff: cpk.filetable.append(CPKFileEntry.createEntry("GTOC_HDR", gtocoffset, UTFStructTypes.DATA_TYPE_UINT64, gtocoffsetpos, "CPK", "HDR", False)) readGTOC(f, cpk, gtocoffset) return cpk
def readTIM(f, forcesize=0): tim = TIM() # Read header header = f.readUInt() if header != 0x10: return None type = f.readUInt() if type == 0x08: tim.bpp = 4 elif type == 0x09: tim.bpp = 8 elif type == 0x02: tim.bpp = 16 elif type == 0x03: tim.bpp = 24 else: common.logError("Unknown TIM type", common.toHex(type)) return None # Read palettes if tim.bpp == 4 or tim.bpp == 8: tim.clutsize = f.readUInt() tim.clutposx = f.readUShort() tim.clutposy = f.readUShort() tim.clutwidth = f.readUShort() tim.clutheight = f.readUShort() tim.clutoff = f.tell() for i in range(tim.clutheight): clut = readCLUTData(f, tim.clutwidth) tim.cluts.append(clut) # Read size tim.size = f.readUInt() tim.posx = f.readUShort() tim.posy = f.readUShort() tim.width = f.readUShort() tim.height = f.readUShort() if tim.bpp == 4: tim.width *= 4 elif tim.bpp == 8: tim.width *= 2 elif tim.bpp == 24: tim.width //= 1.5 tim.dataoff = f.tell() common.logDebug("TIM bpp", tim.bpp, "width", tim.width, "height", tim.height, "size", tim.size) pixelnum = forcesize if forcesize != 0 else (((tim.size - 12) * 8) // tim.bpp) readTIMData(f, tim, pixelnum) return tim
def repackBIN(binfile, binpatch, cuefile, patchfile="", data="data/"): common.logMessage("Repacking BIN", binpatch, "...") if not os.path.isfile("psximager\\psxbuild.exe"): common.logError("psximager not found") return common.execute( "psximager\\psxbuild.exe \"{cat}\" \"{bin}\"".format( cat=data + "repack.cat", bin=binpatch), False) with open(cuefile, "w") as fout: fout.write("FILE \"" + binpatch.replace(data, "") + "\" BINARY\r\n") fout.write(" TRACK 01 MODE2/2352\r\n") fout.write(" INDEX 01 00:00:00\r\n") common.logMessage("Done!") # Create xdelta patch if patchfile != "": common.xdeltaPatch(patchfile, binfile, binpatch)
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 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 repackMappedTiles(f, tilestart, mapdata, palettes): imgname = mapdata.name if not os.path.isfile(mapdata.name): imgname = imgname.replace("work_IMG", "out_IMG") if not os.path.isfile(imgname): common.logError("Image", imgname, "not found") return img = Image.open(imgname) img = img.convert("RGB") pixels = img.load() x = y = 0 for tiledata in mapdata.map: if not tiledata.hflip and not tiledata.vflip: f.seek(tilestart + (tiledata.tile * 16)) writeTile(f, pixels, x * 8, y * 8, palettes[0], mapdata.bpp) x += 1 if x == mapdata.width: y += 1 x = 0
def repackBIN(binrange, freeranges=None, readfunc=common.detectEncodedString, writefunc=common.writeEncodedString, encoding="shift_jis", comments="#", binin="data/extract/arm9.bin", binout="data/repack/arm9.bin", binfile="data/bin_input.txt", fixchars=[]): if not os.path.isfile(binfile): common.logError("Input file", binfile, "not found") return False common.copyFile(binin, binout) common.logMessage("Repacking BIN from", binfile, "...") section = {} with codecs.open(binfile, "r", "utf-8") as bin: section = common.getSection(bin, "", comments, fixchars=fixchars) chartot, transtot = common.getSectionPercentage(section) if type(binrange) == tuple: binrange = [binrange] notfound = common.repackBinaryStrings(section, binin, binout, binrange, freeranges, readfunc, writefunc, encoding, 0x02000000) for pointer in notfound: common.logError("Pointer", common.toHex(pointer.old), "->", common.toHex(pointer.new), "not found for string", pointer.str) common.logMessage("Done! Translation is at {0:.2f}%".format((100 * transtot) / chartot)) return True
def writeTIM(f, tim, infile, transp=False, forcepal=-1): if tim.bpp > 8: common.logError("writeTIM bpp", tim.bpp, "not supported") return clut = forcepal if forcepal != -1 else getUniqueCLUT(tim, transp) img = Image.open(infile) img = img.convert("RGBA") pixels = img.load() f.seek(tim.dataoff) for i in range(tim.height): for j in range(tim.width): index = common.getPaletteIndex(tim.cluts[clut], pixels[j, i], checkalpha=transp, zerotransp=False) if tim.bpp == 4: f.writeHalf(index) else: f.writeByte(index)
def readETOC(f, cpk, tocoffset): f.seek(tocoffset) headercheck = f.readString(4) if headercheck != "ETOC": common.logError("Wrong ETOC header", headercheck) return utfoffset = f.tell() utfpacket, utfsize, encrypted = readUTFData(f) tocentry = cpk.getFileEntry("ETOC_HDR") tocentry.encrypted = encrypted tocentry.filesize = utfsize files = readUTF(utfpacket, utfoffset) entries = cpk.getEntries("FILE") for i in range(len(entries)): entries[i].localdir, _, _ = files.getColumnDataType(i, "LocalDir") updatetime, _, _ = files.getColumnDataType(i, "UpdateDateTime") if updatetime is None: updatetime = 0 entries[i].updatetime = updatetime
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 readTPL(file): tpl = TPL() with common.Stream(file, "rb", False) as f: f.seek(4) # Header tpl.imgnum = f.readUInt() tpl.tableoff = f.readUInt() tpl.images = [] for i in range(tpl.imgnum): image = TPLImage() tpl.images.append(image) f.seek(tpl.tableoff + i * 8) image.imgoff = f.readUInt() image.paloff = f.readUInt() if image.paloff > 0: f.seek(image.paloff) palcount = f.readUShort() f.seek(1, 1) # Unpacked f.seek(1, 1) # Padding image.palformat = f.readUInt() image.paldataoff = f.readUInt() if image.palformat != 0x02: common.logError("Unimplemented palette format:", image.palformat) continue f.seek(image.paldataoff) image.palette = [] for j in range(palcount): image.palette.append(common.readRGB5A3(f.readShort())) f.seek(image.imgoff) image.height = f.readUShort() image.width = f.readUShort() image.format = f.readUInt() image.dataoff = f.readUInt() if image.format != 0x02 and image.format != 0x08 and image.format != 0x09: common.logError("Unimplemented image format:", image.format) continue image.tilewidth = 8 image.tileheight = 8 if image.format == 0x08 else 4 image.blockwidth = math.ceil( image.width / image.tilewidth) * image.tilewidth image.blockheight = math.ceil( image.height / image.tileheight) * image.tileheight return tpl