107 lines
3.6 KiB
Python
107 lines
3.6 KiB
Python
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
|