def shop_merge(base: ParameterIO, ext: str, adds: ParameterList, rems: ParameterList) -> ParameterIO: base_sorted = get_named_pio(base, ext) base_sorted = util.pio_subtract(base_sorted, rems) base_sorted = util.pio_merge(base_sorted, adds) if ext == "bshop": fix_itemsorts(base_sorted) merged = ParameterIO() merged.objects["Header"] = ParameterObject() merged.objects["Header"].params["TableNum"] = Parameter( len(base_sorted.lists)) table_no = 1 for table_key, table_list in base_sorted.lists.items(): merged.objects["Header"].params[f"Table{table_no:02}"] = Parameter( base_sorted.objects["TableNames"].params[table_key].v) table_no += 1 merged_table_obj = ParameterObject() merged_table_obj.params["ColumnNum"] = Parameter( len(table_list.objects)) for _, item_obj in table_list.objects.items(): if ext == "brecipe": entry_key = "%02d" % (item_obj.params["ItemSort"].v + 1) elif ext == "bshop": entry_key = "%03d" % (item_obj.params["ItemSort"].v + 1) else: raise KeyError(ext) for param_key in EXT_PARAMS[ext]: param_name = param_key + entry_key merged_table_obj.params[param_name] = item_obj.params[ param_key] merged.objects[table_key] = merged_table_obj return merged
def __init__(self, def_name: str, type: str, name: str = "", group: str = ""): super().__init__() defs = util.get_ai_defs() key = ("AIs" if type == "ai" else "Actions" if type == "action" else "Behaviors" if type == "behavior" else "Querys" if type == "query" else "") if def_name in defs[key]: ai_def = defs[key][def_name] self.lists = {} self.objects = {crc32(b"Def"): ParameterObject()} self.objects["Def"].params["ClassName"] = Parameter( oead.FixedSafeString32(def_name)) self.objects["Def"].params["Name"] = Parameter(name) self.objects["Def"].params["GroupName"] = Parameter(group) if "childs" in ai_def: self.objects["ChildIdx"] = ParameterObject() for child in ai_def["childs"]: self.objects["ChildIdx"].params[child] = Parameter( oead.S32(-1)) if "StaticInstParams" in ai_def: self.objects["SInst"] = ParameterObject() for sinst in ai_def["StaticInstParams"]: if "Value" in sinst: val = sinst["Value"] else: val = _param_type_map[sinst["Type"]]() self.objects["SInst"].params[sinst["Name"]] = Parameter( val) else: raise ValueError(f"{def_name} is not a valid AI")
def make_bshop(plist: ParameterList) -> ParameterIO: bshop = ParameterIO() bshop.type = "xml" tables: List[str] = [ str(t.v) for _, t in plist.objects["TableNames"].params.items() ] bshop.objects["Header"] = ParameterObject() bshop.objects["Header"].params["TableNum"] = Parameter(len(tables)) for i, table in enumerate(tables, 1): table_hash = crc32(table.encode()) bshop.objects["Header"].params[f"Table{i:02d}"] = Parameter( FixedSafeString64(table) ) table_pobj = ParameterObject() table_pobj.params["ColumnNum"] = Parameter( len(plist.lists[table_hash].objects) ) for j, item in enumerate( [item for _, item in plist.lists[table_hash].objects.items()], 1 ): table_pobj.params[f"ItemSort{j:03d}"] = Parameter(j - 1) for shop_key in shop_keys: table_pobj.params[f"{shop_key}{j:03d}"] = item.params[shop_key] if table_pobj.params: bshop.objects[table_hash] = table_pobj return bshop
def _dict_to_drop(drop_dict: dict) -> ParameterIO: pio = ParameterIO() pio.type = "xml" header = ParameterObject() header.params["TableNum"] = Parameter(len(drop_dict)) for i, table in enumerate(drop_dict.keys()): header.params[f"Table{i + 1:02}"] = Parameter( oead.FixedSafeString64(table)) pio.objects["Header"] = header for i, (table, contents) in enumerate(drop_dict.items()): header.params[f"Table{i:02}"] = table table_obj = ParameterObject() table_obj.params["RepeatNumMin"] = Parameter( contents["repeat_num_min"]) table_obj.params["RepeatNumMax"] = Parameter( contents["repeat_num_max"]) table_obj.params["ApproachType"] = Parameter(contents["approach_type"]) table_obj.params["OccurrenceSpeedType"] = Parameter( contents["occurrence_speed_type"]) table_obj.params["ColumnNum"] = Parameter(len(contents["items"])) for idx, item in enumerate(contents["items"]): table_obj.params[f"ItemName{idx + 1:02}"] = Parameter( oead.FixedSafeString64(item)) table_obj.params[f"ItemProbability{idx + 1:02}"] = Parameter( contents["items"][item]) pio.objects[table] = table_obj return pio
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 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 generate_diff(self, mod_dir: Path, modded_files: List[Union[Path, str]]) -> ParameterIO: print("Logging changes to shop files...") diffs = ParameterIO() file_names = ParameterObject() for file in [ file for file in modded_files if Path(file).suffix in EXT_FOLDERS ]: try: mod_bytes = util.get_nested_file_bytes( str(mod_dir) + "/" + str(file)) nests = str(file).split("//", 1) try: ref_path = str(util.get_game_file(Path( nests[0]))) + "//" + nests[1] except FileNotFoundError: continue try: ref_bytes = util.get_nested_file_bytes(ref_path) except AttributeError: continue shop_type = str(file).split(".")[-1] mod_pio = get_named_pio(ParameterIO.from_binary(mod_bytes), shop_type) ref_pio = get_named_pio(ParameterIO.from_binary(ref_bytes), shop_type) file_names.params[oead.aamp.Name(file).hash] = Parameter(file) diffs.lists[file] = gen_diffs(ref_pio, mod_pio) except (KeyError, AttributeError) as err: raise err diffs.objects["Filenames"] = file_names return diffs
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 _to_param(self, obj) -> Parameter: enc_map = { "Int": lambda p: int(p["Int"]), "StringRef": lambda p: str(p["StringRef"]), "F32": lambda p: float(p["F32"]), "Float": lambda p: float(p["Float"]), "String32": lambda p: oead.FixedSafeString32(str(p["String32"])), "Bool": lambda p: bool(p["Bool"]), "bool": lambda p: bool(p["Bool"]), "Vec3": lambda p: self._construct_vec3(p["Vec3"]), } return Parameter(enc_map.get(next(iter(obj)), lambda x: x)(obj))
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 generate_diff(self, mod_dir: Path, modded_files: List[Union[str, Path]]): print("Detecting general changes to AAMP files...") aamps = { m for m in modded_files if isinstance(m, str) and m[m.rindex("."):] in ( util.AAMP_EXTS - HANDLED) and "Dummy" not in m and "CDungeon" not in m } if not aamps: return None consolidated: Dict[str, Any] = {} for aamp in aamps: util.dict_merge( consolidated, reduce( lambda res, cur: {cur: res if res is not None else {} }, # type: ignore reversed(aamp.split("//")), None, ), ) this_pool = self._pool or Pool(maxtasksperchild=500) results = this_pool.starmap(partial(get_aamp_diffs, tmp_dir=mod_dir), list(consolidated.items())) if not self._pool: this_pool.close() this_pool.join() del consolidated del aamps diffs = ParameterIO() diffs.objects["FileTable"] = ParameterObject() i: int = 0 for file, diff in sorted( (k, v) for r in [r for r in results if r is not None] for k, v in r.items()): diffs.objects["FileTable"].params[f"File{i}"] = Parameter(file) diffs.lists[file] = diff i += 1 return diffs
def generate_diff(self, mod_dir: Path, modded_files: List[Union[str, Path]]): print("Detecting general changes to AAMP files...") aamps = { m for m in modded_files if isinstance(m, str) and m[m.rindex("."):] in (util.AAMP_EXTS - HANDLED) } if not aamps: return None consolidated = {} for aamp in aamps: util.dict_merge( consolidated, reduce( lambda res, cur: {cur: res} if res is not None else [cur], reversed(aamp.split("//")), None, ), ) this_pool = self._pool or Pool() results = this_pool.starmap(partial(get_aamp_diffs, tmp_dir=mod_dir), list(consolidated.items())) if not self._pool: this_pool.close() this_pool.join() del consolidated del aamps diffs = ParameterIO() diffs.objects["FileTable"] = ParameterObject() i: int = 0 for result in results: if not result: continue for file, diff in result.items(): diffs.objects["FileTable"].params[f"File{i}"] = Parameter(file) diffs.lists[file] = diff i += 1 return diffs