from typing import Any, Sequence from lib.archive import * def decode( reader: FArchiveReader, type_name: str, size: int, path: str ) -> dict[str, Any]: if type_name != "ArrayProperty": raise Exception(f"Expected ArrayProperty, got {type_name}") value = reader.property(type_name, size, path, allow_custom=False) data_bytes = value["value"]["values"] value["value"] = decode_bytes(data_bytes) return value def decode_bytes(c_bytes: Sequence[int]) -> dict[str, Any]: if len(c_bytes) == 0: return None buf = bytes(c_bytes) reader = FArchiveReader(buf) data = {} data["id"] = { "created_world_id": reader.guid(), "local_id_in_created_world": reader.guid(), "static_id": reader.fstring(), } data["type"] = "unknown" egg_data = try_read_egg(reader) if egg_data != None: data |= egg_data elif (reader.size - reader.data.tell()) == 4: data["type"] = "armor" data["durability"] = reader.float() if not reader.eof(): raise Exception("Warning: EOF not reached") else: cur_pos = reader.data.tell() temp_data = {"type": "weapon"} try: temp_data["durability"] = reader.float() temp_data["remaining_bullets"] = reader.i32() temp_data["passive_skill_list"] = reader.tarray(lambda r: r.fstring()) if not reader.eof(): raise Exception("Warning: EOF not reached") data |= temp_data except Exception as e: print( f"Warning: Failed to parse weapon data, continuing as raw data {buf}: {e}" ) reader.data.seek(cur_pos) data["trailer"] = [int(b) for b in reader.read_to_end()] return data def try_read_egg(reader: FArchiveReader) -> Optional[dict[str, Any]]: cur_pos = reader.data.tell() try: data = {"type": "egg"} data["character_id"] = reader.fstring() data["object"] = reader.properties_until_end() data["unknown_bytes"] = reader.byte_list(4) data["unknown_id"] = reader.guid() if not reader.eof(): raise Exception("Warning: EOF not reached") return data except Exception as e: if e.args[0] == "Warning: EOF not reached": raise e reader.data.seek(cur_pos) return None def encode( writer: FArchiveWriter, property_type: str, properties: dict[str, Any] ) -> int: if property_type != "ArrayProperty": raise Exception(f"Expected ArrayProperty, got {property_type}") del properties["custom_type"] encoded_bytes = encode_bytes(properties["value"]) properties["value"] = {"values": [b for b in encoded_bytes]} return writer.property_inner(property_type, properties) def encode_bytes(p: dict[str, Any]) -> bytes: if p is None: return bytes() writer = FArchiveWriter() writer.guid(p["id"]["created_world_id"]) writer.guid(p["id"]["local_id_in_created_world"]) writer.fstring(p["id"]["static_id"]) if p["type"] == "unknown": writer.write(bytes(p["trailer"])) elif p["type"] == "egg": writer.fstring(p["character_id"]) writer.properties(p["object"]) writer.write(bytes(p["unknown_bytes"])) writer.guid(p["unknown_id"]) elif p["type"] == "armor": writer.float(p["durability"]) elif p["type"] == "weapon": writer.float(p["durability"]) writer.i32(p["remaining_bullets"]) writer.tarray(lambda w, d: w.fstring(d), p["passive_skill_list"]) encoded_bytes = writer.bytes() return encoded_bytes