def get_named_pio(shop: ParameterIO, shop_type: str) -> ParameterIO: named_pio = ParameterIO() shop_keys = EXT_PARAMS[shop_type] for table_key, table_obj in shop.objects.items(): if table_key.hash == oead.aamp.Name("Header").hash: tablenames = ParameterObject() named_pio.objects["TableNames"] = ParameterObject() tablenum = table_obj.params["TableNum"].v for idx in range(1, tablenum + 1): table_name = str(table_obj.params[f"Table{idx:02}"].v) tablenames.params[oead.aamp.Name(table_name)] = table_name named_pio.objects["TableNames"] = tablenames continue table_max = table_obj.params["ColumnNum"].v table_obj_new = ParameterList() for idx in range(1, table_max + 1): if shop_type == "brecipe": entry_key = "%02d" % idx elif shop_type == "bshop": entry_key = "%03d" % idx else: raise KeyError(shop_type) entry_value = ParameterObject() try: for curr_shop_key in shop_keys: no_shop_key = curr_shop_key + entry_key entry_value.params[curr_shop_key] = table_obj.params[ no_shop_key] except KeyError: continue table_obj_new.objects[str( entry_value.params["ItemName"].v)] = entry_value named_pio.lists[table_key] = table_obj_new return named_pio
def diff_addres(addres: ParameterList, ref_addres: ParameterList) -> ParameterList: diff = ParameterList() bfres: List[str] = [] for _, pobj in addres.objects.items(): bfres.append(pobj.params["Anim"].v) for _, ref_pobj in ref_addres.objects.items(): try: bfres.remove(ref_pobj.params["Anim"].v) except ValueError: continue for i, v in enumerate(bfres): key = f"AddRes_{i}" diff.objects[key] = ParameterObject() diff.objects[key].params["Anim"] = Parameter(v) return diff
def make_shopdata(pio: ParameterIO) -> ParameterList: shopdata = ParameterList() tables: List[Parameter] = [ str(t.v) for _, t in pio.objects["Header"].params.items() if is_string(t) ] if not tables: raise InvalidDataError("A shop file is invalid: has no tables") shopdata.objects["TableNames"] = ParameterObject() for table in tables: table_plist = ParameterList() shopdata.objects["TableNames"].params[table] = Parameter(FixedSafeString64(table)) table_hash = crc32(table.encode()) items: Dict[str, List[int]] = { str(p.v): [k.hash, i] for i, (k, p) in enumerate(pio.objects[table_hash].params.items()) if is_string(p) } for item in items.keys(): item_no = int( name_table.get_name(items[item][0], items[item][1], table_hash).replace( "ItemName", "" ) ) item_obj = ParameterObject() for shop_key in shop_keys: try: item_obj.params[shop_key] = pio.objects[table_hash].params[ f"{shop_key}{item_no:03d}" ] except KeyError: raise KeyError(f"{shop_key}{item_no:03d}") table_plist.objects[item] = item_obj if table_plist.objects: shopdata.lists[table_hash] = table_plist return shopdata
def merge_addres(plist: ParameterList, other_plist: ParameterList): bfres: List[str] = [] for _, pobj in plist.objects.items(): bfres.append(str(pobj.params["Anim"].v)) for _, other_pobj in other_plist.objects.items(): bfres.append(str(other_pobj.params["Anim"].v)) for i, v in enumerate(list(dict.fromkeys(bfres))): key = f"AddRes_{i}" if not key in plist.objects: plist.objects[key] = ParameterObject() plist.objects[key].params["Anim"] = Parameter(FixedSafeString64(v))
def diff_asdefine(asdef: ParameterList, ref_asdef: ParameterList) -> ParameterList: diff = ParameterList() defs: Dict[str, str] = {} for _, pobj in asdef.objects.items(): defs[str(pobj.params["Name"].v)] = str(pobj.params["Filename"].v) for _, ref_pobj in ref_asdef.objects.items(): try: if (defs[str(ref_pobj.params["Name"].v)] == str( ref_pobj.params["Filename"].v)): defs.pop(str(ref_pobj.params["Name"].v)) except (ValueError, KeyError): continue for i, (k, v) in enumerate(defs.items()): key = f"ASDefine_{i}" diff.objects[key] = ParameterObject() diff.objects[key].params["Name"] = Parameter(FixedSafeString64(k)) diff.objects[key].params["Filename"] = Parameter( FixedSafeString64(v)) return diff
def gen_diffs(ref: ParameterIO, mod: ParameterIO) -> ParameterList: diffs = ParameterList() tablenames_key = oead.aamp.Name("TableNames") # generate additions, modifications additions = nand_pio_into_plist(ref, mod) if len(additions.lists) != 0: additions.objects[tablenames_key] = ParameterObject() add_names = additions.objects[tablenames_key] mod_names = mod.objects[tablenames_key] for table_key in additions.lists.keys(): add_names.params[table_key] = mod_names.params[table_key] diffs.lists["Additions"] = additions # generate deletions removals = nand_pio_into_plist(mod, ref) if len(removals.lists) != 0: removals.objects[tablenames_key] = ParameterObject() rem_names = removals.objects[tablenames_key] ref_names = ref.objects[tablenames_key] for table_key in removals.lists.keys(): rem_names.params[table_key] = ref_names.params[table_key] diffs.lists["Removals"] = removals return diffs
def merge_asdefine(plist: ParameterList, other_plist: ParameterList): defs: Dict[str, str] = {} for _, pobj in plist.objects.items(): defs[str(pobj.params["Name"].v)] = pobj.params["Filename"].v for _, other_pobj in other_plist.objects.items(): defs[str( other_pobj.params["Name"].v)] = other_pobj.params["Filename"].v for i, (k, v) in enumerate(defs.items()): key = f"ASDefine_{i}" if not key in plist.objects: plist.objects[key] = ParameterObject() plist.objects[key].params["Name"] = Parameter(FixedSafeString64(k)) plist.objects[key].params["Filename"] = Parameter(v)
def get_shop_diff(pio: ParameterIO, ref_pio: ParameterIO) -> ParameterList: def diff_plist( plist: Union[ParameterList, ParameterIO], ref_plist: Union[ParameterIO, ParameterList], ) -> ParameterList: diff = ParameterList() for key, sublist in plist.lists.items(): if key not in ref_plist.lists: diff.lists[key] = sublist elif ref_plist.lists[key] != sublist: diff.lists[key] = diff_plist(sublist, ref_plist.lists[key]) for key, obj in plist.objects.items(): if key not in ref_plist.objects: diff.objects[key] = obj elif ref_plist.objects[key] != obj: diff.objects[key] = diff_pobj(obj, ref_plist.objects[key]) return diff def diff_pobj(pobj: ParameterObject, ref_pobj: ParameterObject) -> ParameterObject: diff = ParameterObject() for param, value in pobj.params.items(): if param not in ref_pobj.params or ref_pobj.params[param] != value: diff.params[param] = value return diff diff = ParameterList() shopdata = make_shopdata(pio) ref_shopdata = make_shopdata(ref_pio) adds = diff_plist(shopdata, ref_shopdata) if adds: diff.lists["Additions"] = adds rems = diff_plist(ref_shopdata, shopdata) if rems: subtract_plists(rems, adds) diff.lists["Removals"] = rems return diff
def diff_plist( plist: Union[ParameterList, ParameterIO], ref_plist: Union[ParameterIO, ParameterList], ) -> ParameterList: diff = ParameterList() for key, sublist in plist.lists.items(): if key.hash == 2777926231: # "AddReses" diff.lists[key] = diff_addres(sublist, ref_plist.lists[key]) elif key.hash == 3752287078: # "ASDefines" diff.lists[key] = diff_asdefine(sublist, ref_plist.lists[key]) elif key.hash == 3305786543: # "CFDefines" diff.lists[key] = sublist elif key not in ref_plist.lists: diff.lists[key] = sublist elif ref_plist.lists[key] != sublist: diff.lists[key] = diff_plist(sublist, ref_plist.lists[key]) for key, obj in plist.objects.items(): if key not in ref_plist.objects: diff.objects[key] = obj elif ref_plist.objects[key] != obj: diff.objects[key] = diff_pobj(obj, ref_plist.objects[key]) return diff
def diff_plist( plist: Union[ParameterList, ParameterIO], ref_plist: Union[ParameterIO, ParameterList], ) -> ParameterList: diff = ParameterList() for key, sublist in plist.lists.items(): if key not in ref_plist.lists: diff.lists[key] = sublist elif ref_plist.lists[key] != sublist: diff.lists[key] = diff_plist(sublist, ref_plist.lists[key]) for key, obj in plist.objects.items(): if key not in ref_plist.objects: diff.objects[key] = obj elif ref_plist.objects[key] != obj: diff.objects[key] = diff_pobj(obj, ref_plist.objects[key]) return diff
def _to_plist(self, obj) -> ParameterList: plist = ParameterList() if isinstance(obj, ParameterList): return obj if "lists" in obj and obj["lists"]: for name, content in obj["lists"].items(): if name.isnumeric(): plist.lists[int(name)] = self._to_plist(content) else: plist.lists[name] = self._to_plist(content) if "objects" in obj and obj["objects"]: for name, content in obj["objects"].items(): if content["params"]: if name.isnumeric(): plist.objects[int(name)] = self._to_pobj(content) else: plist.objects[name] = self._to_pobj(content) return plist
def nand_pio_into_plist( ref: Union[ParameterIO, ParameterList], mod: Union[ParameterIO, ParameterList], ) -> ParameterList: res_plist = ParameterList() for key, plist in mod.lists.items(): if key not in ref.lists: res_plist.lists[key] = plist else: processed_list = nand_pio_into_plist(ref.lists[key], plist) if processed_list.lists or processed_list.objects: res_plist.lists[key] = processed_list for key, pobj in mod.objects.items(): if key not in ref.objects: res_plist.objects[key] = pobj else: ref_pobj = ref.objects[key] res_plist.objects[key] = ref_pobj for param_key, param_value in pobj.params.items(): if param_key not in ref_pobj.params: continue # new keys are garbage the game can't use, skip them if not param_value.v == ref_pobj.params[param_key].v: res_plist.objects[key].params[param_key] = param_value return res_plist
def generate_pio(self) -> ParameterIO: from zlib import crc32 pio = ParameterIO.from_binary(self._aiprog.to_binary()) if self._ais: ais_list = ParameterList() for idx, ai in enumerate(self._ais): ais_list.lists[f"AI_{idx}"] = ai pio.lists["AI"] = ais_list if self._actions: actions_list = ParameterList() for idx, action in enumerate(self._actions): actions_list.lists[f"Action_{idx}"] = action pio.lists["Action"] = actions_list if self._behaviors: behaviors_list = ParameterList() for idx, behavior in enumerate(self._behaviors): behaviors_list.lists[f"Behavior_{idx}"] = behavior pio.lists["Behavior"] = behaviors_list if self._queries: queries_list = ParameterList() for idx, query in enumerate(self._queries): queries_list.lists[f"Query_{idx}"] = query pio.lists["Query"] = queries_list self._aiprog = pio return pio