def handleIps(self, plandoName, romDataJson): romDataJson = self.vars.romData romDataRaw = json.loads(romDataJson) # everything is string in json, cast to int romData = {} for addr in romDataRaw: romData[int(addr)] = int(romDataRaw[addr]) # dict: address -> value, transform it dict: address -> [values] ipsData = {} prevAddr = -0xff curRecord = [] curRecordAddr = -1 for addr in sorted(romData): if addr == prevAddr + 1: curRecord.append(romData[addr]) else: if len(curRecord) > 0: # save current record ipsData[curRecordAddr] = bytearray(curRecord) # start a new one curRecordAddr = addr curRecord = [romData[addr]] prevAddr = addr # save last record ipsData[curRecordAddr] = bytearray(curRecord) # generate ips using the records ipsPatch = IPS_Patch(ipsData) maxSize = ipsPatch.max_size # store ips in the repository ipsPatch.save(os.path.join(ipsBasePath, "{}.ips".format(plandoName))) return maxSize
def applyIPSPatch(self, patchName, patchDict=None, ipsDir=None): if patchDict is None: patchDict = self.patchAccess.getDictPatches() print("Apply patch {}".format(patchName)) if patchName in patchDict: patch = IPS_Patch(patchDict[patchName]) else: # look for ips file if ipsDir is None: patch = IPS_Patch.load(self.patchAccess.getPatchPath(patchName)) else: patch = IPS_Patch.load(os.path.join(appDir, ipsDir, patchName)) self.ipsPatches.append(patch)
def applyIPSPatch(self, patchName, patchDict=None, ipsDir="rando/patches"): if patchDict is None: patchDict = patches print("Apply patch {}".format(patchName)) if patchName in patchDict: patch = IPS_Patch(patchDict[patchName]) else: # look for ips file if os.path.exists(patchName): patch = IPS_Patch.load(patchName) else: patch = IPS_Patch.load(os.path.join(appDir, ipsDir, patchName)) self.ipsPatches.append(patch)
def ipsPatch(self, ipsPatches): mergedIPS = IPS_Patch() for ips in ipsPatches: mergedIPS.append(ips) # generate records for ips from self data groupedData = {} startAddress = -1 prevAddress = -1 curData = [] for address in sorted(self.data): if address == prevAddress + 1: curData.append(self.data[address]) prevAddress = address else: if len(curData) > 0: groupedData[startAddress] = curData startAddress = address prevAddress = address curData = [self.data[startAddress]] if startAddress != -1: groupedData[startAddress] = curData patch = IPS_Patch(groupedData) mergedIPS.append(patch) patchData = mergedIPS.encode() self.data = {} self.data["ips"] = base64.b64encode(patchData).decode() if mergedIPS.truncate_length is not None: self.data["truncate_length"] = mergedIPS.truncate_length self.data["max_size"] = mergedIPS.max_size
def writeAsm(patch, outAsm): print("Writing " + outAsm + "...") p = IPS_Patch.load(patch).toDict() with open(outAsm, 'w') as f: f.write("lorom\narch snes.cpu\n\n") for addr, bytez in p.items(): f.write("org $%06x\nprint pc\n" % pc_to_snes(addr)) for i in range(0, len(bytez), 8): blist = ["$%02x" % b for b in bytez[i:min(len(bytez), i + 8)]] f.write("\tdb %s\n" % ','.join(blist)) f.write('print pc\n')
def close(self): mergedIPS = IPS_Patch() for ips in self.ipsPatches: mergedIPS.append(ips) mergedIPS.append(self.ips()) patchData = mergedIPS.encode() self.data = {} self.data["ips"] = base64.b64encode(patchData).decode() if mergedIPS.truncate_length is not None: self.data["truncate_length"] = mergedIPS.truncate_length self.data["max_size"] = mergedIPS.max_size
def ips(self): groupedData = {} startAddress = -1 prevAddress = -1 curData = [] for address in sorted(self.data): if address == prevAddress + 1: curData.append(self.data[address]) prevAddress = address else: if len(curData) > 0: groupedData[startAddress] = curData startAddress = address prevAddress = address curData = [self.data[startAddress]] if startAddress != -1: groupedData[startAddress] = curData return IPS_Patch(groupedData)
for r in patch.getRanges(): ips_ranges.append({'name': name, 'range': r}) def loadPatchPy(): from rando.patches import patches as patches_py for name, patch in patches_py.items(): addRanges(name, IPS_Patch(patch)) for patch in sys.argv[2:]: baseName = os.path.basename(patch) if baseName == "patches.py": loadPatchPy() else: addRanges(baseName, IPS_Patch.load(patch)) overlaps = {} last, lstop = None, -1 for rg in sorted(ips_ranges, key=lambda r: r['range'].start): thisStart = rg['range'].start if last and lstop > thisStart and lstop != 0x7fe0: # overlap, skip checksum k = (last['name'], rg['name']) if k not in overlaps: overlaps[k] = [] overlaps[k].append((lstop, thisStart)) last = rg lstop = last['range'].stop for k, v in overlaps.items(): addresses = ["(0x%x, 0x%x)" % a for a in v]
sys.path.append(os.path.dirname(sys.path[0])) from rom.ips import IPS_Patch from rom.rom import RealROM from patches.common import patches as common_patches vanilla = sys.argv[1] ipsToReverse = sys.argv[2:] rom = RealROM(vanilla) for ips in ipsToReverse: if ips not in common_patches.patches: ipsPath = os.path.abspath(ips) destDir, destFile = os.path.split(ipsPath) patch = IPS_Patch.load(ips) patchDict = patch.toDict() else: patchDict = common_patches.patches[ips] destDir = sys.path[0] + "/../patches/common/ips" destFile = ips + ".ips" destFile = "remove_" + destFile destPath = os.path.join(destDir, destFile) reversePatchDict = {} for addr, bytez in patchDict.items(): sz = len(bytez) origBytes = [] rom.seek(addr) for i in range(sz): origBytes.append(rom.readByte()) assert len(origBytes) == sz
# the output is a dict data = eval(response.text) # generate randomized rom romFileName = args.rom outFileName = data["fileName"] shutil.copyfile(romFileName, outFileName) romFile = RealROM(outFileName) ipsData = data["ips"] ipsData = base64.b64decode(ipsData.encode('ascii')) # our ips patcher need a file (or a dict), not the content of the ips file (fd, ipsFileName) = tempfile.mkstemp() os.close(fd) with open(ipsFileName, 'wb+') as ipsFile: ipsFile.write(ipsData) romFile.ipsPatch([IPS_Patch.load(ipsFileName)]) os.remove(ipsFileName) # write dest rom romFile.close() print("permalink: {}/customizer/{}".format(baseUrl, data["seedKey"])) print("additional message: {}".format(data["errorMsg"])) print("{} generated succesfully".format(outFileName))
for name, diff in needCopy.items(): if not diff: continue print("copy {} from hack".format(name)) tmpBytes = [] addrs = addresses[name] for addr in range(addrs["hack"][0], addrs["hack"][1]): tmpBytes.append(hackRom.readByte(addr)) vanillaRom.seek(addrs["vanilla"][0]) for byte in tmpBytes: vanillaRom.writeByte(byte) # also copy last line of ship tiles in escape sequence rowSize = 1024 hAddr = addresses["tilesAddr"]["hack"][0] + rowSize * 3 vAddr = snes_to_pc(0x94C800) hackRom.seek(hAddr) vanillaRom.seek(vAddr) for i in range(rowSize): vanillaRom.writeByte(hackRom.readByte()) vanillaRom.close() # generate ips between vanilla & tempfile patch = IPS_Patch.create( open(vanilla, 'rb').read(), open(tmpfile, 'rb').read()) out = hack + '.ips' patch.save(out) print("ips generated: {}".format(out))
#!/usr/bin/python3 import sys, os # now that we're in directory 'tools/' we have to update sys.path sys.path.append(os.path.dirname(sys.path[0])) from rom.ips import IPS_Patch original=sys.argv[1] patched=sys.argv[2] target=sys.argv[3] patch = IPS_Patch.create(open(original, 'rb').read(), open(patched, 'rb').read()) patch.save(target)
} meta["dynamic_patches"] = { "track_id": [snes_to_pc(0xA497DE)] } meta = getBossMeta("Spore Spawn") meta["pc_addresses"] = [499165] meta = getBossMeta("Botwoon") meta["pc_addresses"] = [514420] # Add patches to silence song-specific sfx that don't work anymore when changed, # and can generate horrible sounds or crashes patchDir = os.path.abspath(sys.path[0])+"/../patches/common/ips/custom_music_specific" assert os.path.exists(patchDir) for trackName, trackData in metadata.items(): patchName = re.sub('[^a-zA-Z0-9\._]', '_', trackName) patchPath = os.path.join(patchDir, patchName+".ips") if os.path.exists(patchPath): print("Adding SFX patch for track "+trackName) patch = IPS_Patch.load(patchPath) if "static_patches" not in trackData: trackData["static_patches"] = {} trackData["static_patches"].update(patch.toDict()) print("Writing %s ..." % json_path) with open(json_path, 'w') as fp: json.dump(metadata, fp, indent=4)
def applyIPSPatchDict(self, patchDict): for patchName in patchDict.keys(): # print("Apply patch {}".format(patchName)) patch = IPS_Patch(patchDict[patchName]) self.ipsPatches.append(patch)
def loadPatchPy(): from rando.patches import patches as patches_py for name, patch in patches_py.items(): addRanges(name, IPS_Patch(patch))
#!/usr/bin/python3 import sys, os, json # we're in directory 'tools/' we have to update sys.path sys.path.append(os.path.dirname(sys.path[0])) from rom.ips import IPS_Patch elev = IPS_Patch.load("patches/common/ips/elevators_speed.ips") doors = IPS_Patch.load("patches/common/ips/fast_doors.ips") merged = IPS_Patch() merged.append(elev) merged.append(doors) merged.save("patches/common/ips/elevators_doors_speed.ips")
from rom.ips import IPS_Patch from rom.rom import snes_to_pc, pc_to_snes ips=sys.argv[1] low=snes_to_pc(int(sys.argv[2], 16)) high=snes_to_pc(int(sys.argv[3], 16)) ext = os.path.splitext(ips)[-1].lower() if ext != ".ips": sys.stderr.write("Wrong ips extension") sys.exit(1) # load ips patch = IPS_Patch.load(ips) patch_dict = patch.toDict() # remove overlapping data filtered_dict = {} for keyLow, data in patch_dict.items(): dataLength = len(data) keyHigh = keyLow + dataLength if keyHigh < low or keyLow > high: print("[{}, {}] we have {} bytes of data".format(hex(pc_to_snes(keyLow)), hex(pc_to_snes(keyHigh)), dataLength)) filtered_dict[keyLow] = data else: print("[{}, {}] we have {} bytes of data overlaping with [{}, {}]".format(hex(pc_to_snes(keyLow)), hex(pc_to_snes(keyHigh)),