def perform_merge(self): merged_quests = util.get_master_modpack_dir() / "logs" / "quests.byml" print("Loading quest mods...") diffs = self.consolidate_diffs(self.get_all_diffs()) if not diffs: print("No quest merging necessary") if merged_quests.exists(): merged_quests.unlink() try: util.inject_file_into_sarc( "Quest/QuestProduct.sbquestpack", util.get_nested_file_bytes( (f'{str(util.get_game_file("Pack/TitleBG.pack"))}' "//Quest/QuestProduct.sbquestpack"), unyaz=False, ), "Pack/TitleBG.pack", ) except FileNotFoundError: pass return print("Loading stock quests...") quests = get_stock_quests() stock_names = [q["Name"] for q in quests] print("Merging quest mods...") for name, mod in diffs["mod"].items(): try: quests[stock_names.index(name)] = mod except (IndexError, ValueError): diffs["add"].append(mod) for delete in reversed(diffs["del"]): try: quests.remove(quests[stock_names.index(delete)]) except ValueError: pass except IndexError: raise RuntimeError( f"An error occurred when attempting to remove a quest from your " "game. Most of the time this means the mod was accidentally made " "using the base game 1.0 TitleBG.pack instead of the latest updated " "version. Please contact the mod author for assistance.") added_names = set() for add in diffs["add"]: if add["Name"] not in added_names: quests.append(add) added_names.add(add["Name"]) print("Writing new quest pack...") data = oead.byml.to_binary(quests, big_endian=util.get_settings("wiiu")) merged_quests.parent.mkdir(parents=True, exist_ok=True) merged_quests.write_bytes(data) util.inject_file_into_sarc( "Quest/QuestProduct.sbquestpack", util.compress(data), "Pack/TitleBG.pack", create_sarc=True, )
def perform_merge(self): merged_events = util.get_master_modpack_dir() / "logs" / "eventinfo.byml" event_merge_log = util.get_master_modpack_dir() / "logs" / "eventinfo.log" print("Loading event info mods...") modded_events = self.consolidate_diffs(self.get_all_diffs()) event_mod_hash = hash(str(modded_events)) if not modded_events: print("No event info merging necessary") if merged_events.exists(): merged_events.unlink() event_merge_log.unlink() try: stock_eventinfo = util.get_nested_file_bytes( ( str(util.get_game_file("Pack/Bootup.pack")) + "//Event/EventInfo.product.sbyml" ), unyaz=False, ) util.inject_file_into_sarc( "Event/EventInfo.product.sbyml", stock_eventinfo, "Pack/Bootup.pack", ) except FileNotFoundError: pass return if event_merge_log.exists() and event_merge_log.read_text() == event_mod_hash: print("No event info merging necessary") return new_events = get_stock_eventinfo() for event, data in modded_events.items(): new_events[event] = data del modded_events print("Writing new event info...") event_bytes = oead.byml.to_binary( new_events, big_endian=util.get_settings("wiiu") ) del new_events util.inject_file_into_sarc( "Event/EventInfo.product.sbyml", util.compress(event_bytes), "Pack/Bootup.pack", create_sarc=True, ) print("Saving event info merge log...") event_merge_log.parent.mkdir(parents=True, exist_ok=True) event_merge_log.write_text(str(event_mod_hash)) merged_events.write_bytes(event_bytes) print("Updating RSTB...") rstb_size = rstb.SizeCalculator().calculate_file_size_with_ext( bytes(event_bytes), True, ".byml" ) del event_bytes rstable.set_size("Event/EventInfo.product.byml", rstb_size)
def perform_merge(self): merged_effects = util.get_master_modpack_dir() / "logs" / "effects.byml" print("Loading status effect mods...") diffs = self.consolidate_diffs(self.get_all_diffs()) if not diffs: print("No status effect merging necessary...") if merged_effects.exists(): merged_effects.unlink() try: stock_effects = util.get_nested_file_bytes( ( str(util.get_game_file("Pack/Bootup.pack")) + "//Ecosystem/StatusEffectList.sbyml" ), unyaz=False, ) util.inject_file_into_sarc( "Ecosystem/StatusEffectList.sbyml", stock_effects, "Pack/Bootup.pack", ) del stock_effects except FileNotFoundError: pass return util.vprint("All status effect diffs:") util.vprint(diffs) effects = get_stock_effects() util.dict_merge(effects, diffs, overwrite_lists=True) del diffs print("Writing new effects list...") effect_bytes = oead.byml.to_binary( oead.byml.Array([effects]), big_endian=util.get_settings("wiiu") ) del effects util.inject_file_into_sarc( "Ecosystem/StatusEffectList.sbyml", util.compress(effect_bytes), "Pack/Bootup.pack", create_sarc=True, ) print("Saving status effect merge log...") merged_effects.parent.mkdir(parents=True, exist_ok=True) merged_effects.write_bytes(effect_bytes) print("Updating RSTB...") rstb_size = rstb.SizeCalculator().calculate_file_size_with_ext( effect_bytes, True, ".byml" ) del effect_bytes rstable.set_size("Ecosystem/StatusEffectList.byml", rstb_size)
def perform_merge(self): merged_quests = util.get_master_modpack_dir() / "logs" / "quests.byml" print("Loading quest mods...") diffs = self.consolidate_diffs(self.get_all_diffs()) if not diffs: print("No quest merging necessary") if merged_quests.exists(): merged_quests.unlink() try: util.inject_file_into_sarc( "Quest/QuestProduct.sbquestpack", util.get_nested_file_bytes( (f'{str(util.get_game_file("Pack/TitleBG.pack"))}' "//Quest/QuestProduct.sbquestpack"), unyaz=False, ), "Pack/TitleBG.pack", ) except FileNotFoundError: pass return print("Loading stock quests...") quests = get_stock_quests() stock_names = [q["Name"] for q in quests] print("Merging quest mods...") for name, mod in diffs["mod"].items(): try: quests[stock_names.index(name)] = mod except (IndexError, ValueError): diffs["add"].append(mod) for delete in reversed(diffs["del"]): try: quests.remove(quests[stock_names.index(delete)]) except ValueError: pass added_names = set() for add in diffs["add"]: if add["Name"] not in added_names: quests.append(add) added_names.add(add["Name"]) print("Writing new quest pack...") data = oead.byml.to_binary(quests, big_endian=util.get_settings("wiiu")) merged_quests.parent.mkdir(parents=True, exist_ok=True) merged_quests.write_bytes(data) util.inject_file_into_sarc( "Quest/QuestProduct.sbquestpack", util.compress(data), "Pack/TitleBG.pack", create_sarc=True, )
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] 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 not in new_entries["del"] }.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( oead.byml.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 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()) util.vprint("All gamedata diffs:") util.vprint(modded_entries) 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: oead.byml.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 = oead.byml.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( oead.byml.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 perform_merge(self): merged_areadata = util.get_master_modpack_dir() / "logs" / "areadata.byml" areadata_merge_log = util.get_master_modpack_dir() / "logs" / "areadata.log" print("Loading area data mods...") modded_areadata = self.consolidate_diffs(self.get_all_diffs()) areadata_mod_hash = hash(str(modded_areadata)) if not modded_areadata: print("No area data merging necessary") if merged_areadata.exists(): merged_areadata.unlink() areadata_merge_log.unlink() try: stock_areadata = util.get_nested_file_bytes( ( str(util.get_game_file("Pack/Bootup.pack")) + "//Ecosystem/AreaData.sbyml" ), unyaz=False, ) util.inject_file_into_sarc( "Ecosystem/AreaData.sbyml", stock_areadata, "Pack/Bootup.pack", ) except FileNotFoundError: pass return if ( areadata_merge_log.exists() and areadata_merge_log.read_text() == areadata_mod_hash ): print("No area data merging necessary") return new_areadata = get_stock_areadata() util.dict_merge(new_areadata, modded_areadata, overwrite_lists=True) print("Writing new area data...") areadata_bytes = oead.byml.to_binary( oead.byml.Array( [v for _, v in sorted(new_areadata.items(), key=lambda x: int(x[0]))] ), big_endian=util.get_settings("wiiu"), ) del new_areadata util.inject_file_into_sarc( "Ecosystem/AreaData.sbyml", util.compress(areadata_bytes), "Pack/Bootup.pack", create_sarc=True, ) print("Saving area data merge log...") areadata_merge_log.parent.mkdir(parents=True, exist_ok=True) areadata_merge_log.write_text(str(areadata_mod_hash)) merged_areadata.write_bytes(areadata_bytes) print("Updating RSTB...") rstb_size = rstb.SizeCalculator().calculate_file_size_with_ext( bytes(areadata_bytes), True, ".byml" ) del areadata_bytes rstable.set_size("Ecosystem/AreaData.byml", rstb_size)