def get_modded_map(map_unit: Union[Map, tuple], tmp_dir: Path) -> Hash: if isinstance(map_unit, tuple): map_unit = Map(*map_unit) map_bytes = None aoc_dir = (tmp_dir / util.get_dlc_path() / ("0010/content" if util.get_settings("wiiu") else "")) if not aoc_dir.exists(): aoc_dir = tmp_dir / util.get_dlc_path() / "content" / "0010" if not aoc_dir.exists(): aoc_dir = tmp_dir / util.get_dlc_path() / "0010" if (aoc_dir / "Pack" / "AocMainField.pack").exists(): try: map_pack = oead.Sarc( (aoc_dir / "Pack" / "AocMainField.pack").read_bytes()) except (RuntimeError, ValueError, oead.InvalidDataError): pass else: try: map_bytes = bytes( map_pack.get_file( f"Map/MainField/{map_unit.section}/" f"{map_unit.section}_{map_unit.type}.smubin").data) except AttributeError: pass if not map_bytes: if (aoc_dir / "Map" / "MainField" / map_unit.section / f"{map_unit.section}_{map_unit.type}.smubin").exists(): map_bytes = ( aoc_dir / "Map" / "MainField" / map_unit.section / f"{map_unit.section}_{map_unit.type}.smubin").read_bytes() elif (tmp_dir / util.get_content_path() / "Map" / "MainField" / map_unit.section / f"{map_unit.section}_{map_unit.type}.smubin").exists(): map_bytes = ( tmp_dir / util.get_content_path() / "Map" / "MainField" / map_unit.section / f"{map_unit.section}_{map_unit.type}.smubin").read_bytes() if not map_bytes: raise FileNotFoundError( f"Oddly, the modded map {map_unit.section}_{map_unit.type}.smubin " "could not be found.") map_bytes = util.decompress(map_bytes) return oead.byml.from_binary(map_bytes)
def perform_merge(self): no_del = self._options.get("no_del", False) shutil.rmtree( str(util.get_master_modpack_dir() / util.get_dlc_path() / ("0010" if util.get_settings("wiiu") else "") / "Map" / "MainField"), ignore_errors=True, ) shutil.rmtree( str(util.get_master_modpack_dir() / util.get_content_path() / "Map" / "MainField"), ignore_errors=True, ) log_path = util.get_master_modpack_dir() / "logs" / "map.log" if log_path.exists(): log_path.unlink() print("Loading map mods...") map_diffs = self.consolidate_diffs(self.get_all_diffs()) util.vprint("All map diffs:") util.vprint(map_diffs) if not map_diffs: print("No map merge necessary") return aoc_pack = (util.get_master_modpack_dir() / util.get_dlc_path() / ("0010" if util.get_settings("wiiu") else "") / "Pack" / "AocMainField.pack") if not aoc_pack.exists() or aoc_pack.stat().st_size > 0: print("Emptying AocMainField.pack...") aoc_pack.parent.mkdir(parents=True, exist_ok=True) aoc_pack.write_bytes(b"") rstb_vals = {} rstb_calc = rstb.SizeCalculator() print("Merging modded map units...") pool = self._pool or Pool(maxtasksperchild=500) rstb_results = pool.map( partial(merge_map, rstb_calc=rstb_calc, no_del=no_del), map_diffs.items(), ) for result in rstb_results: rstb_vals[result[util.get_dlc_path()][0]] = result[ util.get_dlc_path()][1] rstb_vals[result["main"][0]] = result["main"][1] if not self._pool: pool.close() pool.join() print("Adjusting RSTB...") log_path.parent.mkdir(parents=True, exist_ok=True) with log_path.open("w", encoding="utf-8") as l_file: for canon, val in rstb_vals.items(): l_file.write(f"{canon},{val}\n") print("Map merge complete")
def generate_diff(self, mod_dir: Path, modded_files: List[Union[str, Path]]): mod_data: bytes stock_data: bytes if (mod_dir / util.get_dlc_path() / ("0010" if util.get_settings("wiiu") else "") / STATIC_PATH) in modded_files: mod_data = (mod_dir / util.get_dlc_path() / ("0010" if util.get_settings("wiiu") else "") / STATIC_PATH).read_bytes() stock_data = util.get_game_file("Map/MainField/Static.smubin", aoc=True).read_bytes() elif (f"{util.get_content_path()}/Pack/Bootup.pack//Map/MainField/Static.smubin" ) in modded_files: mod_data = util.get_nested_file_bytes( (str(mod_dir / util.get_content_path() / "Pack" / "Bootup.pack") + "//Map/MainField/Static.smubin"), unyaz=False, ) stock_data = util.get_nested_file_bytes( (str(util.get_game_file("Pack/Bootup.pack")) + "//Map/MainField/Static.smubin"), unyaz=False, ) else: return None stock_static: Hash = oead.byml.from_binary(util.decompress(stock_data)) mod_static: Hash = oead.byml.from_binary(util.decompress(mod_data)) diffs = Hash() for cat in stock_static: if cat not in stock_static: continue stock_items = {get_id(item): item for item in stock_static[cat]} mod_items = {get_id(item): item for item in mod_static[cat]} diffs[cat] = Hash({ item_id: item for item_id, item in mod_items.items() if item_id not in stock_items or item != stock_items[item_id] }) for item_id, item in [(i1, i2) for i1, i2 in stock_items.items() if i1 not in mod_items]: item["remove"] = True diffs[cat][item_id] = item if not diffs[cat]: del diffs[cat] return diffs
def perform_merge(self): shutil.rmtree( str(util.get_master_modpack_dir() / util.get_dlc_path() / ("0010" if util.get_settings("wiiu") else "") / "Map" / "MainField"), ignore_errors=True, ) shutil.rmtree( str(util.get_master_modpack_dir() / util.get_content_path() / "Map" / "MainField"), ignore_errors=True, ) log_path = util.get_master_modpack_dir() / "logs" / "map.log" if log_path.exists(): log_path.unlink() print("Loading map mods...") map_diffs = self.consolidate_diffs(self.get_all_diffs()) util.vprint("All map diffs:") util.vprint(map_diffs) if not map_diffs: print("No map merge necessary") return aoc_pack = (util.get_master_modpack_dir() / util.get_dlc_path() / ("0010" if util.get_settings("wiiu") else "") / "Pack" / "AocMainField.pack") if not aoc_pack.exists() or aoc_pack.stat().st_size > 0: print("Emptying AocMainField.pack...") aoc_pack.parent.mkdir(parents=True, exist_ok=True) aoc_pack.write_bytes(b"") rstb_vals = {} rstb_calc = rstb.SizeCalculator() print("Merging modded map units...") pool = self._pool or Pool(maxtasksperchild=500) rstb_results = pool.map( partial(merge_map, rstb_calc=rstb_calc), map_diffs.items(), ) for result in rstb_results: rstb_vals[result[util.get_dlc_path()][0]] = result[ util.get_dlc_path()][1] rstb_vals[result["main"][0]] = result["main"][1] if not self._pool: pool.close() pool.join() stock_static = [m for m in map_diffs if m[1] == "Static"] if stock_static: title_path = (util.get_master_modpack_dir() / util.get_content_path() / "Pack" / "TitleBG.pack") if not title_path.exists(): title_path.parent.mkdir(parents=True, exist_ok=True) shutil.copyfile(util.get_game_file("Pack/TitleBG.pack"), title_path) title_bg: oead.SarcWriter = oead.SarcWriter.from_sarc( oead.Sarc(title_path.read_bytes())) for static in stock_static: del title_bg.files[ f"Map/MainField/{static[0]}/{static[0]}_Static.smubin"] title_path.write_bytes(title_bg.write()[1]) print("Adjusting RSTB...") log_path.parent.mkdir(parents=True, exist_ok=True) with log_path.open("w", encoding="utf-8") as l_file: for canon, val in rstb_vals.items(): l_file.write(f"{canon},{val}\n") print("Map merge complete")
def merge_map(map_pair: tuple, rstb_calc: rstb.SizeCalculator) -> Dict[str, Tuple[str, int]]: map_unit, changes = map_pair[0], map_pair[1] util.vprint(f'Merging {len(changes)} versions of {"_".join(map_unit)}...') new_map = get_stock_map(map_unit) stock_obj_hashes = [int(obj["HashId"]) for obj in new_map["Objs"]] for hash_id, actor in changes["Objs"]["mod"].items(): try: new_map["Objs"][stock_obj_hashes.index(int(hash_id))] = actor except ValueError: changes["Objs"]["add"].append(actor) for map_del in sorted( changes["Objs"]["del"], key=lambda change: stock_obj_hashes.index(change) if change in stock_obj_hashes else -1, reverse=True, ): if int(map_del) in stock_obj_hashes: try: new_map["Objs"].pop(stock_obj_hashes.index(map_del)) except IndexError: try: obj_to_delete = next( iter([ actor for actor in new_map["Objs"] if actor["HashId"] == map_del ])) new_map["Objs"].remove(obj_to_delete) except (StopIteration, ValueError): util.vprint( f"Could not delete actor with HashId {map_del}") new_map["Objs"].extend([ change for change in changes["Objs"]["add"] if int(change["HashId"]) not in stock_obj_hashes ]) new_map["Objs"] = sorted(new_map["Objs"], key=lambda actor: int(actor["HashId"])) if len(new_map["Rails"]): stock_rail_hashes = [int(rail["HashId"]) for rail in new_map["Rails"]] for hash_id, rail in changes["Rails"]["mod"].items(): try: new_map["Rails"][stock_rail_hashes.index(int(hash_id))] = rail except ValueError: changes["Rails"]["add"].append(rail) for map_del in sorted( changes["Rails"]["del"], key=lambda change: stock_rail_hashes.index(int(change)) if int(change) in stock_rail_hashes else -1, reverse=True, ): if int(map_del) in stock_rail_hashes: try: new_map["Rails"].pop(stock_rail_hashes.index(int(map_del))) except IndexError: try: obj_to_delete = next( iter([ rail for rail in new_map["Rails"] if rail["HashId"] == map_del ])) new_map["Rails"].remove(obj_to_delete) except (StopIteration, ValueError): util.vprint( f"Could not delete rail with HashId {map_del}") new_map["Rails"].extend([ change for change in changes["Rails"]["add"] if int(change["HashId"]) not in stock_rail_hashes ]) new_map["Rails"] = sorted(new_map["Rails"], key=lambda rail: int(rail["HashId"])) aoc_out: Path = (util.get_master_modpack_dir() / util.get_dlc_path() / ("0010" if util.get_settings("wiiu") else "") / "Map" / "MainField" / map_unit.section / f"{map_unit.section}_{map_unit.type}.smubin") aoc_out.parent.mkdir(parents=True, exist_ok=True) aoc_bytes = oead.byml.to_binary(new_map, big_endian=util.get_settings("wiiu")) aoc_out.write_bytes(util.compress(aoc_bytes)) new_map["Objs"] = [ obj for obj in new_map["Objs"] if not str(obj["UnitConfigName"]).startswith("DLC") ] (util.get_master_modpack_dir() / util.get_content_path() / "Map" / "MainField" / map_unit.section).mkdir(parents=True, exist_ok=True) base_out = (util.get_master_modpack_dir() / util.get_content_path() / "Map" / "MainField" / map_unit.section / f"{map_unit.section}_{map_unit.type}.smubin") base_out.parent.mkdir(parents=True, exist_ok=True) base_bytes = oead.byml.to_binary(new_map, big_endian=util.get_settings("wiiu")) base_out.write_bytes(util.compress(base_bytes)) return { util.get_dlc_path(): ( f"Aoc/0010/Map/MainField/{map_unit.section}/{map_unit.section}_{map_unit.type}.mubin", rstb_calc.calculate_file_size_with_ext(bytes(aoc_bytes), util.get_settings("wiiu"), ".mubin"), ), "main": ( f"Map/MainField/{map_unit.section}/{map_unit.section}_{map_unit.type}.mubin", rstb_calc.calculate_file_size_with_ext(bytes(base_bytes), util.get_settings("wiiu"), ".mubin"), ), }