def diff_objs() -> Hash: base_hashes = [int(obj["HashId"]) for obj in base_map["Objs"]] base_links = (set() if link_del else { int(link["DestUnitHashId"]) for obj in base_map["Objs"] for link in obj.get("LinksToObj", Array()) if "LinksToObj" in obj }) mod_hashes = [int(obj["HashId"]) for obj in mod_map["Objs"]] diffs = Hash() diffs["add"] = Array([ obj for obj in mod_map["Objs"] if int(obj["HashId"]) not in base_hashes ]) diffs["mod"] = Hash({ str(obj["HashId"]): obj for obj in mod_map["Objs"] if int(obj["HashId"]) in base_hashes and obj != base_map["Objs"][base_hashes.index(int(obj["HashId"]))] }) diffs["del"] = Array([ oead.U32(h) for h in { hash_id for hash_id in base_hashes if hash_id not in {*mod_hashes, *base_links} } ] if not no_del else set()) return diffs
def diff_objs() -> Hash: base_hashes = [int(obj["HashId"]) for obj in base_map["Objs"]] mod_hashes = [int(obj["HashId"]) for obj in mod_map["Objs"]] diffs = Hash() diffs["add"] = Array([ obj for obj in mod_map["Objs"] if int(obj["HashId"]) not in base_hashes ]) diffs["mod"] = Hash({ str(obj["HashId"]): obj for obj in mod_map["Objs"] if int(obj["HashId"]) in base_hashes and obj != base_map["Objs"][base_hashes.index(int(obj["HashId"]))] }) diffs["del"] = Array([ oead.U32(h) for h in {hash_id for hash_id in base_hashes if hash_id not in mod_hashes} ]) if new_hashes: hash_map: Dict[int, int] = {} for obj in diffs["add"]: new_hash = crc32(oead.byml.to_text(obj).encode("utf8")) hash_map[obj["HashId"].v] = new_hash obj["HashId"] = oead.U32(new_hash) for obj in [*diffs["add"], *[v for k, v in diffs["mod"].items()]]: if "LinksToObj" in obj: for link in obj["LinksToObj"]: if link["DestUnitHashId"].v in hash_map: link["DestUnitHashId"] = oead.U32( hash_map[link["DestUnitHashId"].v]) return diffs
def diff_gamedata_type(data_type: str, mod_data: dict, stock_data: dict) -> Hash: stock_entries = {entry["DataName"]: entry for entry in stock_data} del stock_data mod_entries = {entry["DataName"] for entry in mod_data} diffs = Hash( { "add": Hash( { entry["DataName"]: entry for entry in mod_data if ( entry["DataName"] not in stock_entries or entry != stock_entries[entry["DataName"]] ) } ), "del": oead.byml.Array( {entry for entry in stock_entries if entry not in mod_entries} ), } ) del stock_entries del mod_entries del mod_data return Hash({data_type: diffs})
def parse_legacy_diff(text: str) -> Hash: diff = oead.byml.from_text(text) return Hash({ unit: Hash({ "Objs": changes, "Rails": Hash() }) for unit, changes in diff.items() })
def to_Hash(self) -> Hash: r = super(Vec2ArrayFlag, self).to_Hash() vec_array = Array() vec_array.append(Hash()) vec_array[0]["Values"] = Array() for i in range(len(self._init_value)): vector = self._init_value[i] vec = Array() vec.append(F32(vector[0])) vec.append(F32(vector[1])) vec_array[0]["Values"].append(Array()) vec_array[0]["Values"][i].append(vec) r["InitValue"] = vec_array vec_array = Array() vec = Array() vec.append(F32(self._max_value[0])) vec.append(F32(self._max_value[1])) vec_array.append(vec) r["MaxValue"] = vec_array vec_array = Array() vec = Array() vec.append(F32(self._min_value[0])) vec.append(F32(self._min_value[1])) vec_array.append(vec) r["MinValue"] = vec_array return r
def get_modded_savedata_entries(savedata: oead.Sarc) -> Hash: ref_savedata = get_stock_savedata().get_files() ref_hashes = { int(item["HashValue"]) for file in sorted(ref_savedata, key=lambda f: f.name)[0:-2] for item in oead.byml.from_binary(file.data)["file_list"][1] } new_entries = oead.byml.Array() mod_hashes = set() for file in savedata.get_files(): data = oead.byml.from_binary(file.data) if data["file_list"][0]["file_name"] != "game_data.sav": continue entries = data["file_list"][1] mod_hashes |= {int(item["HashValue"]) for item in entries} new_entries.extend( [item for item in entries if int(item["HashValue"]) not in ref_hashes] ) del ref_savedata return Hash( { "add": new_entries, "del": oead.byml.Array( oead.S32(item) for item in {item for item in ref_hashes if item not in mod_hashes} ), } )
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 add_flags_from_Hash(self, name: str, data: Hash) -> None: is_revival = bool("revival" in name) for ftype, flags in data.items(): for flag in flags: self._store[ftype][flag["HashValue"].v] = FLAG_MAPPING[ftype]( flag, revival=is_revival) self._orig_store[ftype][ flag["HashValue"].v] = FLAG_MAPPING[ftype]( flag, revival=is_revival)
def to_Hash(self) -> Hash: r = super(F32ArrayFlag, self).to_Hash() array_one = Array() array_one.append(Hash()) array_two = Array([F32(value) for value in self._init_value]) array_one[0]["Values"] = array_two r["InitValue"] = array_one r["MaxValue"] = F32(self._max_value) r["MinValue"] = F32(self._min_value) return r
def diff_objs() -> Hash: base_hashes = [int(obj["HashId"]) for obj in base_map["Objs"]] mod_hashes = [int(obj["HashId"]) for obj in mod_map["Objs"]] diffs = Hash() diffs["add"] = Array([ obj for obj in mod_map["Objs"] if int(obj["HashId"]) not in base_hashes ]) diffs["mod"] = Hash({ str(obj["HashId"]): obj for obj in mod_map["Objs"] if int(obj["HashId"]) in base_hashes and obj != base_map["Objs"][base_hashes.index(int(obj["HashId"]))] }) diffs["del"] = Array([ oead.U32(h) for h in {hash_id for hash_id in base_hashes if hash_id not in mod_hashes} ]) return diffs
def get_map_diff(map_unit: Map, tmp_dir: Path, no_del: bool = False, link_del: bool = False) -> Hash: mod_map = get_modded_map(map_unit, tmp_dir) stock_map = True for obj in mod_map["Objs"]: str_obj = oead.byml.to_text(obj) if "IsHardModeActor" in str_obj or "AoC_HardMode_Enabled" in str_obj: stock_map = False break base_map = get_stock_map(map_unit, force_vanilla=stock_map) base_hashes = [int(obj["HashId"]) for obj in base_map["Objs"]] base_links = (set() if link_del else { int(link["DestUnitHashId"]) for obj in base_map["Objs"] for link in obj.get("LinksToObj", Array()) if "LinksToObj" in obj }) mod_hashes = [int(obj["HashId"]) for obj in mod_map["Objs"]] diffs = Hash() diffs["add"] = Array({ obj for obj in mod_map["Objs"] if int(obj["HashId"]) not in base_hashes }) diffs["mod"] = Hash({ str(obj["HashId"]): obj for obj in mod_map["Objs"] if int(obj["HashId"]) in base_hashes and obj != base_map["Objs"][base_hashes.index(int(obj["HashId"]))] }) diffs["del"] = Array({ oead.U32(hash_id) for hash_id in base_hashes if hash_id not in {*mod_hashes, *base_links} } if not no_del else set()) del mod_map del base_map del base_links del mod_hashes return "_".join(map_unit), oead.byml.to_text(diffs)
def to_Hash(self) -> Hash: r = Hash() r["DataName"] = self._data_name r["DeleteRev"] = S32(self._delete_rev) r["HashValue"] = S32(self._hash_value) r["IsEventAssociated"] = self._is_event_associated r["IsOneTrigger"] = self._is_one_trigger r["IsProgramReadable"] = self._is_program_readable r["IsProgramWritable"] = self._is_program_writable r["IsSave"] = self._is_save r["ResetType"] = S32(self._reset_type) return r
def add_flags_from_Hash(self, name: str, data: Hash, overwrite_ok: bool = True) -> None: is_revival = bool("revival" in name) for ftype, flags in data.items(): for flag in flags: hash = flag["HashValue"].v if hash in self._store[ftype] and not overwrite_ok: continue self._store[ftype][hash] = FLAG_MAPPING[ftype]( flag, revival=is_revival)
def diff_rails() -> Hash: base_hashes = [int(rail["HashId"]) for rail in base_map["Rails"]] mod_hashes = [int(rail["HashId"]) for rail in mod_map["Rails"]] diffs = Hash() diffs["add"] = Array([ rail for rail in mod_map["Rails"] if int(rail["HashId"]) not in base_hashes ]) diffs["mod"] = Hash({ str(rail["HashId"]): rail for rail in mod_map["Rails"] if int(rail["HashId"]) in base_hashes and rail != base_map["Rails"][base_hashes.index(int(rail["HashId"]))] }) diffs["del"] = Array([ oead.U32(h) for h in {hash_id for hash_id in base_hashes if hash_id not in mod_hashes} ]) return diffs
def to_Hash(self) -> Hash: r = super(String256ArrayFlag, self).to_Hash() array_one = Array() array_one.append(Hash()) # array_two = [FixedSafeString256(value) for value in self._init_value] array_two = Array(self._init_value) array_one[0]["Values"] = array_two r["InitValue"] = array_one # r["MaxValue"] = FixedSafeString256(self._max_value) r["MaxValue"] = self._max_value # r["MinValue"] = FixedSafeString256(self._min_value) r["MinValue"] = self._min_value return r
def consolidate_diffs(self, diffs: list): if not diffs: return {} all_diffs = Hash({"add": oead.byml.Array(), "del": oead.byml.Array()}) hashes = set() for diff in reversed(diffs): for entry in diff["add"]: if entry["HashValue"].v not in hashes: all_diffs["add"].append(entry) hashes.add(entry["HashValue"].v) for entry in diff["del"]: if entry not in all_diffs["del"]: all_diffs["del"].append(entry) del hashes return all_diffs
def perform_merge(self): diffs = self.consolidate_diffs(self.get_all_diffs()) output: Path static_data: Path try: util.get_aoc_dir() output = (util.get_master_modpack_dir() / util.get_dlc_path() / ("0010" if util.get_settings("wiiu") else "") / STATIC_PATH) static_data = util.get_game_file("Map/MainField/Static.smubin", aoc=True).read_bytes() except FileNotFoundError: output = util.get_master_modpack_dir( ) / "logs" / "mainstatic.smubin" static_data = util.get_nested_file_bytes( (str(util.get_game_file("Pack/Bootup.pack")) + "//Map/MainField/Static.smubin"), unyaz=False, ) if not diffs: try: output.unlink() except: pass return stock_static = oead.byml.from_binary(util.decompress(static_data)) merged = Hash() for cat in stock_static: if cat in diffs: items = {get_id(item): item for item in stock_static[cat]} util.dict_merge(items, diffs[cat]) merged[cat] = Array([ item for _, item in items.items() if "remove" not in item ]) else: merged[cat] = stock_static[cat] data = util.compress( oead.byml.to_binary(merged, big_endian=util.get_settings("wiiu"))) output.parent.mkdir(parents=True, exist_ok=True) output.write_bytes(data) if "mainstatic" in str(output): util.inject_file_into_sarc( "Map/MainField/Static.smubin", data, "Pack/Bootup.pack", create_sarc=True, )
def get_modded_gamedata_entries(gamedata: oead.Sarc, pool: pool.Pool = None) -> Hash: this_pool = pool or Pool(maxtasksperchild=500) stock_data = consolidate_gamedata(get_stock_gamedata()) mod_data = consolidate_gamedata(gamedata) del gamedata results = this_pool.starmap( diff_gamedata_type, ((key, mod_data[key], stock_data[key]) for key in mod_data), ) diffs = Hash({data_type: diff for d in results for data_type, diff in d.items()}) del results if not pool: this_pool.close() this_pool.join() del stock_data del mod_data return diffs
def get_mod_diff(self, mod: util.BcmlMod): diffs = Hash() if self.is_mod_logged(mod): util.dict_merge( diffs, oead.byml.from_text( (mod.path / "logs" / self._log_name).read_text(encoding="utf-8")), overwrite_lists=True, ) for opt in {d for d in (mod.path / "options").glob("*") if d.is_dir()}: if (opt / "logs" / self._log_name).exists(): util.dict_merge( diffs, oead.byml.from_text( (opt / "logs" / self._log_name).read_text("utf-8")), overwrite_lists=True, ) return diffs
def to_sv_Hash(self) -> Hash: r = Hash() r["DataName"] = self._data_name r["HashValue"] = S32(self._hash_value) return r
def get_map_diff(map_unit: Map, tmp_dir: Path, new_hashes: bool = False) -> Hash: mod_map = get_modded_map(map_unit, tmp_dir) stock_map = True for obj in mod_map["Objs"]: str_obj = oead.byml.to_text(obj) if "IsHardModeActor" in str_obj or "AoC_HardMode_Enabled" in str_obj: stock_map = False break base_map = get_stock_map(map_unit, force_vanilla=stock_map) def diff_objs() -> Hash: base_hashes = [int(obj["HashId"]) for obj in base_map["Objs"]] mod_hashes = [int(obj["HashId"]) for obj in mod_map["Objs"]] diffs = Hash() diffs["add"] = Array([ obj for obj in mod_map["Objs"] if int(obj["HashId"]) not in base_hashes ]) diffs["mod"] = Hash({ str(obj["HashId"]): obj for obj in mod_map["Objs"] if int(obj["HashId"]) in base_hashes and obj != base_map["Objs"][base_hashes.index(int(obj["HashId"]))] }) diffs["del"] = Array([ oead.U32(h) for h in {hash_id for hash_id in base_hashes if hash_id not in mod_hashes} ]) if new_hashes: hash_map: Dict[int, int] = {} for obj in diffs["add"]: new_hash = crc32(oead.byml.to_text(obj).encode("utf8")) hash_map[obj["HashId"].v] = new_hash obj["HashId"] = oead.U32(new_hash) for obj in [*diffs["add"], *[v for k, v in diffs["mod"].items()]]: if "LinksToObj" in obj: for link in obj["LinksToObj"]: if link["DestUnitHashId"].v in hash_map: link["DestUnitHashId"] = oead.U32( hash_map[link["DestUnitHashId"].v]) return diffs def diff_rails() -> Hash: base_hashes = [int(rail["HashId"]) for rail in base_map["Rails"]] mod_hashes = [int(rail["HashId"]) for rail in mod_map["Rails"]] diffs = Hash() diffs["add"] = Array([ rail for rail in mod_map["Rails"] if int(rail["HashId"]) not in base_hashes ]) diffs["mod"] = Hash({ str(rail["HashId"]): rail for rail in mod_map["Rails"] if int(rail["HashId"]) in base_hashes and rail != base_map["Rails"][base_hashes.index(int(rail["HashId"]))] }) diffs["del"] = Array([ oead.U32(h) for h in {hash_id for hash_id in base_hashes if hash_id not in mod_hashes} ]) return diffs return ( "_".join(map_unit), oead.byml.to_text( Hash({ "Objs": diff_objs(), "Rails": diff_rails() if map_unit.type == "Static" else Hash(), })), )
def consolidate_gamedata(gamedata: oead.Sarc) -> Hash: data = Hash() for file in gamedata.get_files(): util.dict_merge(data, oead.byml.from_binary(file.data)) del gamedata return data
def perform_merge(self): force = self._options.get("force", False) slog_path = util.get_master_modpack_dir() / "logs" / "savedata.log" new_entries = self.consolidate_diffs(self.get_all_diffs()) if not new_entries: print("No savedata merging necessary.") if slog_path.exists(): slog_path.unlink() if (util.get_master_modpack_dir() / "logs" / "savedata.sarc").exists(): (util.get_master_modpack_dir() / "logs" / "savedata.sarc").unlink() return if slog_path.exists() and not force: with slog_path.open("r") as l_file: if xxhash.xxh64_hexdigest(str(new_entries)) == l_file.read(): print("No savedata merging necessary.") return savedata = get_stock_savedata() save_files = sorted(savedata.get_files(), key=lambda f: f.name)[0:-2] del_ids = {item.v for item in new_entries["del"]} print("Merging changes...") merged_entries = oead.byml.Array( sorted( { entry["HashValue"].v: entry for entry in [ *[ e for file in save_files for e in oead.byml.from_binary(file.data)["file_list"][1] ], *new_entries["add"], ] if entry["HashValue"].v not in del_ids }.values(), key=itemgetter("HashValue"), ) ) print("Creating and injecting new savedataformat.sarc...") new_savedata = oead.SarcWriter( endian=oead.Endianness.Big if util.get_settings("wiiu") else oead.Endianness.Little ) num_files = ceil(len(merged_entries) / 8192) for i in range(num_files): end_pos = (i + 1) * 8192 if end_pos > len(merged_entries): end_pos = len(merged_entries) data = oead.byml.to_binary( Hash( { "file_list": oead.byml.Array( [ { "IsCommon": False, "IsCommonAtSameAccount": False, "IsSaveSecureCode": True, "file_name": "game_data.sav", }, oead.byml.Array(merged_entries[i * 8192 : end_pos]), ] ), "save_info": oead.byml.Array( [ { "directory_num": oead.S32(8), "is_build_machine": True, "revision": oead.S32(18203), } ] ), } ), big_endian=util.get_settings("wiiu"), ) new_savedata.files[f"/saveformat_{i}.bgsvdata"] = data new_savedata.files[f"/saveformat_{num_files}.bgsvdata"] = oead.Bytes( savedata.get_file("/saveformat_6.bgsvdata").data ) new_savedata.files[f"/saveformat_{num_files + 1}.bgsvdata"] = oead.Bytes( savedata.get_file("/saveformat_7.bgsvdata").data ) del savedata new_save_bytes = new_savedata.write()[1] del new_savedata util.inject_file_into_sarc( "GameData/savedataformat.ssarc", util.compress(new_save_bytes), "Pack/Bootup.pack", create_sarc=True, ) (util.get_master_modpack_dir() / "logs").mkdir(parents=True, exist_ok=True) ( (util.get_master_modpack_dir() / "logs" / "savedata.sarc").write_bytes( new_save_bytes ) ) print("Updating RSTB...") rstable.set_size( "GameData/savedataformat.sarc", rstable.calculate_size("GameData/savedataformat.sarc", new_save_bytes), ) del new_save_bytes slog_path.parent.mkdir(parents=True, exist_ok=True) with slog_path.open("w", encoding="utf-8") as l_file: l_file.write(xxhash.xxh64_hexdigest(str(new_entries)))
def consolidate_diffs(self, diffs: list): a_diffs = Hash() for mod_diff in diffs: for file, diff in mod_diff.items(): # a_map = Map(*file.split("_")) if file not in a_diffs: a_diffs[file] = Array() a_diffs[file].append(diff) c_diffs = Hash() for file, mods in a_diffs.items(): c_diffs[file] = Hash({ "Objs": Hash({ "add": Array(), "mod": Hash(), "del": Array([ oead.U32(h) for h in set([ hash_id.v for hashes in [ mod["Objs"]["del"] for mod in mods if "Objs" in mod and "del" in mod["Objs"] ] for hash_id in hashes ]) ]), }), "Rails": Hash({ "add": Array(), "mod": Hash(), "del": Array([ oead.U32(h) for h in set([ hash_id.v for hashes in [ mod["Rails"]["del"] for mod in mods if "Rails" in mod and "del" in mod["Rails"] ] for hash_id in hashes ]) ]), }), }) for mod in [m for m in mods if "Objs" in m and "mod" in m["Objs"]]: for hash_id, actor in mod["Objs"]["mod"].items(): c_diffs[file]["Objs"]["mod"][hash_id] = actor for mod in [ m for m in mods if "Rails" in m and "mod" in m["Rails"] ]: for hash_id, actor in mod["Rails"]["mod"].items(): c_diffs[file]["Rails"]["mod"][hash_id] = actor add_obj_hashes = [] add_rail_hashes = [] for mod in reversed(mods): if "add" in mod["Objs"]: for actor in mod["Objs"]["add"]: if actor["HashId"] not in add_obj_hashes: add_obj_hashes.append(actor["HashId"]) c_diffs[file]["Objs"]["add"].append(actor) if "add" in mod["Rails"]: for actor in mod["Rails"]["add"]: if actor["HashId"] not in add_rail_hashes: add_rail_hashes.append(actor["HashId"]) c_diffs[file]["Rails"]["add"].append(actor) return c_diffs
def perform_merge(self): force = self._options.get("force", False) glog_path = util.get_master_modpack_dir() / "logs" / "gamedata.log" modded_entries = self.consolidate_diffs(self.get_all_diffs()) if not modded_entries: print("No gamedata merging necessary.") if glog_path.exists(): glog_path.unlink() if (util.get_master_modpack_dir() / "logs" / "gamedata.sarc").exists(): (util.get_master_modpack_dir() / "logs" / "gamedata.sarc").unlink() return if glog_path.exists() and not force: with glog_path.open("r") as l_file: if xxhash.xxh64_hexdigest(str(modded_entries)) == l_file.read(): print("No gamedata merging necessary.") return print("Loading stock gamedata...") gamedata = consolidate_gamedata(get_stock_gamedata()) merged_entries = { data_type: Hash({entry["DataName"]: entry for entry in entries}) for data_type, entries in gamedata.items() } del gamedata print("Merging changes...") for data_type in {d for d in merged_entries if d in modded_entries}: util.dict_merge( merged_entries[data_type], modded_entries[data_type]["add"], shallow=True, ) for entry in modded_entries[data_type]["del"]: try: del merged_entries[data_type][entry] except KeyError: continue merged_entries = Hash( { data_type: oead.byml.Array([value for _, value in entries.items()]) for data_type, entries in merged_entries.items() } ) print("Creating and injecting new gamedata.sarc...") new_gamedata = oead.SarcWriter( endian=oead.Endianness.Big if util.get_settings("wiiu") else oead.Endianness.Little ) for data_type in merged_entries: num_files = ceil(len(merged_entries[data_type]) / 4096) for i in range(num_files): end_pos = (i + 1) * 4096 if end_pos > len(merged_entries[data_type]): end_pos = len(merged_entries[data_type]) new_gamedata.files[f"/{data_type}_{i}.bgdata"] = oead.byml.to_binary( Hash({data_type: merged_entries[data_type][i * 4096 : end_pos]}), big_endian=util.get_settings("wiiu"), ) new_gamedata_bytes = new_gamedata.write()[1] del new_gamedata util.inject_file_into_sarc( "GameData/gamedata.ssarc", util.compress(new_gamedata_bytes), "Pack/Bootup.pack", create_sarc=True, ) (util.get_master_modpack_dir() / "logs").mkdir(parents=True, exist_ok=True) (util.get_master_modpack_dir() / "logs" / "gamedata.sarc").write_bytes( new_gamedata_bytes ) print("Updating RSTB...") rstable.set_size( "GameData/gamedata.sarc", rstable.calculate_size("GameData/gamedata.sarc", new_gamedata_bytes), ) del new_gamedata_bytes glog_path.parent.mkdir(parents=True, exist_ok=True) with glog_path.open("w", encoding="utf-8") as l_file: l_file.write(xxhash.xxh64_hexdigest(str(modded_entries)))
def consolidate_diffs(self, diffs: list): all_diffs = Hash() for diff in diffs: util.dict_merge(all_diffs, diff, overwrite_lists=True) return all_diffs
def find_name(item: Hash) -> str: for k, v in item.items(): if "name" in k.lower(): return v else: return ""
def get_map_diff(map_unit: Map, tmp_dir: Path) -> Hash: mod_map = get_modded_map(map_unit, tmp_dir) stock_map = True for obj in mod_map["Objs"]: str_obj = oead.byml.to_text(obj) if "IsHardModeActor" in str_obj or "AoC_HardMode_Enabled" in str_obj: stock_map = False break base_map = get_stock_map(map_unit, force_vanilla=stock_map) def diff_objs() -> Hash: base_hashes = [int(obj["HashId"]) for obj in base_map["Objs"]] mod_hashes = [int(obj["HashId"]) for obj in mod_map["Objs"]] diffs = Hash() diffs["add"] = Array([ obj for obj in mod_map["Objs"] if int(obj["HashId"]) not in base_hashes ]) diffs["mod"] = Hash({ str(obj["HashId"]): obj for obj in mod_map["Objs"] if int(obj["HashId"]) in base_hashes and obj != base_map["Objs"][base_hashes.index(int(obj["HashId"]))] }) diffs["del"] = Array([ oead.U32(h) for h in {hash_id for hash_id in base_hashes if hash_id not in mod_hashes} ]) return diffs def diff_rails() -> Hash: base_hashes = [int(rail["HashId"]) for rail in base_map["Rails"]] mod_hashes = [int(rail["HashId"]) for rail in mod_map["Rails"]] diffs = Hash() diffs["add"] = Array([ rail for rail in mod_map["Rails"] if int(rail["HashId"]) not in base_hashes ]) diffs["mod"] = Hash({ str(rail["HashId"]): rail for rail in mod_map["Rails"] if int(rail["HashId"]) in base_hashes and rail != base_map["Rails"][base_hashes.index(int(rail["HashId"]))] }) diffs["del"] = Array([ oead.U32(h) for h in {hash_id for hash_id in base_hashes if hash_id not in mod_hashes} ]) return diffs return ( "_".join(map_unit), oead.byml.to_text( Hash({ "Objs": diff_objs(), "Rails": diff_rails() if map_unit.type == "Static" else Hash(), })), )