def validate_rom(): """Validate MD5 matches the expected value""" if os.path.exists(DKONG_ZIP): buffer = open(DKONG_ZIP, 'rb').read() md5 = hashlib.md5(buffer).hexdigest() if md5 == "a13e81d6ef342d763dc897fe03893392": # ZIP is verified return True elif md5 in FIX_ALTERNATIVE_MD5: # ZIP has a recognised MD5 and can be converted using patch file alt_zip = os.path.join(ROM_DIR, f"dkong_{md5}.zip") shutil.copy(DKONG_ZIP, alt_zip) if os.path.exists(alt_zip): # Read the alternative ZIP binary with open(alt_zip, 'rb') as f_in: alt_binary = f_in.read() ips = os.path.join(PATCH_DIR, f"fix_{md5}.ips") if os.path.exists(ips): # Apply patch and write the fixed DKONG.ZIP to roms patch = Patch.load(ips) with open(DKONG_ZIP, 'w+b') as f_out: f_out.write(patch.apply(alt_binary)) return True return False else: return False else: # No rom file so a verification error not is returned return True
def __save_reload_and_apply(patch, source): encoded_patch = patch.encode() decoded_patch = None with tempfile.NamedTemporaryFile(delete=False) as f: f.write(encoded_patch) f.close() decoded_patch = Patch.load(f.name) os.unlink(f.name) return decoded_patch.apply(source)
def cmd_apply(args): patch = Patch.load(args.ips_file) in_file = None with open(args.in_file, 'rb') as f: in_file = f.read() out_file = patch.apply(in_file) if 'out_file' in args: with open(args.out_file, 'w+b') as f: f.write(out_file) else: sys.stdout.buffer.write(out_file)
def apply_patches(): applied_patches_list = [] if os.path.exists(DKONG_ZIP): ips_files = glob(os.path.join(PATCH_DIR, "dkong*.ips")) if ips_files: # Read the original ZIP binary with open(DKONG_ZIP, 'rb') as f_in: dkong_binary = f_in.read() # Apply patch and write the resulting hack to a subfolder for ips in ips_files: name = os.path.splitext(os.path.basename(ips))[0] subfolder = os.path.join(ROM_DIR, name) if not os.path.exists(subfolder): os.mkdir(subfolder) patch = Patch.load(ips) with open(os.path.join(subfolder, "dkong.zip"), 'w+b') as f_out: f_out.write(patch.apply(dkong_binary)) applied_patches_list.append(name) return applied_patches_list
def randomize_rom(rom: Rom, flags: Flags, rom_seed: str) -> Rom: rng = random.Random() rng.seed(rom_seed) print(f"Randomize ROM: {flags.text()}, seed='{rom_seed}'") patches_to_load = BASE_PATCHES if flags.encounters is not None: patches_to_load.append("data/FF1EncounterToggle.ips") if flags.default_party is not None: patches_to_load.append("data/RandomDefault.ips") patched_rom_data = rom.rom_data for patch_path in patches_to_load: patch = Patch.load(patch_path) patched_rom_data = patch.apply(patched_rom_data) rom = Rom(data=bytearray(patched_rom_data)) rom = init_free_airship(rom) rom = add_credits(rom) event_text_block = EventTextBlock(rom) event_text_block.shrink() rom = event_text_block.pack(rom) rom = update_xp_requirements(rom, flags.exp_mult) if flags.key_item_shuffle is not None: placement = KeyItemPlacement(rom, rng.randint(0, 0xffffffff)) else: placement = KeyItemPlacement(rom) rom = placement.rom if flags.magic is not None: shuffle_magic = SpellShuffle(rom, rng) rom = shuffle_magic.write(rom) if flags.treasures is not None: if flags.treasures == "shuffle": rom = treasure_shuffle(rom, rng) else: rom = random_bucketed_treasures(rom, rng, flags.wealth) if flags.debug is not None: class_stats_stream = rom.open_bytestream(0x1E1354, 96) class_stats = [] while not class_stats_stream.is_eos(): class_stats.append(JobClass(class_stats_stream)) class_out_stream = OutputStream() for job_class in class_stats: # Set the starting weapon and armor for all classes to something # very fair and balanced: Masamune + Diamond Armlet. :) job_class.weapon_id = 0x28 job_class.armor_id = 0x0e # Write the (very balanced) new data out job_class.write(class_out_stream) rom = rom.apply_patch(0x1E1354, class_out_stream.get_buffer()) if flags.shuffle_formations: formation = FormationRandomization(rom, rng) rom = rom.apply_patches(formation.patches()) if True: enemy_data_stream = rom.open_bytestream(0x1DE044, 0x1860) enemies = [] while not enemy_data_stream.is_eos(): enemies.append(EnemyStats(enemy_data_stream)) # Rebalance (Revisited) Fiend HP enemies[0x78].max_hp = enemies[0x77].max_hp * 2 enemies[0x7a].max_hp = enemies[0x79].max_hp * 2 enemies[0x7c].max_hp = enemies[0x7b].max_hp * 2 enemies[0x7e].max_hp = enemies[0x7d].max_hp * 2 # And Chaos enemies[0x7f].max_hp = enemies[0x7e].max_hp * 2 # Finally, Piscodemons can suck it enemies[0x67].atk = int(enemies[0x67].atk / 2) # We'll also lower everyone's INT just to see how that works for index in range(0x80): enemies[index].intel = int(.666 * enemies[index].intel) # print(f"{hex(index)} HP: {enemies[index].max_hp}, INT: {enemies[index].intel}") out = OutputStream() for enemy in enemies: enemy.write(out) rom = rom.apply_patch(0x1DE044, out.get_buffer()) # Add the seed + flags to the party creation screen. seed_str = TextBlock.encode_text(f"Seed:\n{rom_seed}\nFlags:\n{flags}\x00") pointer = OutputStream() pointer.put_u32(0x8227054) rom = rom.apply_patches({ 0x227054: seed_str, 0x4d8d4: pointer.get_buffer() }) return rom
def cmd_trace(args): patch = Patch.load(args.ips_file) patch.trace()