public void Deserialize(BinaryReader reader, PackageFile package) { PropertyTags = new List <PropertyTag>(); Properties = new List <ISerializable>(); while (true) { var pos = reader.BaseStream.Position; var name = new FName(); name.Deserialize(reader, package); if (name.Value == "None") { break; } reader.BaseStream.Position = pos; var tag = new PropertyTag(); tag.Deserialize(reader, package); PropertyTags.Add(tag); Type?propertyType; if (!PackageFile.KnownTypes.TryGetValue(tag.Type.Value, out propertyType)) { if (!PackageFile.KnownTypes.TryGetValue("F" + tag.Type.Value, out propertyType)) { throw new Exception($"UAsset uses unknown struct type {tag.Type.Value}!"); } } var prop = Activator.CreateInstance(propertyType) as ISerializable; prop.Deserialize(reader, package); Properties.Add(prop); } Reserved = reader.ReadUInt32(); // ObjectProperty data comes after the properties/propertytags section // I don't know if any other properties are handled this way, probably not, doing this is likely wrong for 99% of other UE4 properties PropertiesData = new Dictionary <FName, ISerializableText>(); foreach (var prop in Properties) { if (prop.GetType() != typeof(ObjectProperty)) { continue; } var objectProperty = prop as ObjectProperty; Type?propertyType; if (!PackageFile.KnownTypes.TryGetValue(objectProperty.Value.ImportObject.ObjectName.Value, out propertyType)) { if (!PackageFile.KnownTypes.TryGetValue("F" + objectProperty.Value.ImportObject.ObjectName.Value, out propertyType)) { throw new Exception($"UAsset uses unknown struct type {objectProperty.Value.ImportObject.ObjectName.Value}!"); } } if (propertyType == typeof(FJackDataTableNativizationAsset) || propertyType == typeof(FJackDataTableBlueprintClass)) { Program.DoFNameCleanup = false; // contains a bunch of unreferenced FNames, can't cleanup properly :( } if (package.SkipPropertyDataLoad) { continue; } int count = reader.ReadInt32(); for (int i = 0; i < count; i++) { var rowName = new FName(); rowName.Deserialize(reader, package); var position = reader.BaseStream.Position; if (propertyType.BaseType.GetInterfaces().Contains(typeof(ISerializableText))) { int structSize = 0; var settings = propertyType.GetCustomAttribute <SerializerAttribute>(); if (settings == null || !settings.NoStructSize) { structSize = reader.ReadInt32(); position = reader.BaseStream.Position; } var propData = Activator.CreateInstance(propertyType) as ISerializableText; propData.Deserialize(reader, package); var length = reader.BaseStream.Position - position; if (structSize != 0 && length != structSize) { throw new Exception($"DataTable read incorrect number of bytes (read {length}, expected {structSize}!)"); reader.BaseStream.Position = position + structSize; } PropertiesData.Add(rowName, propData); } } } }
public object DeserializeValue(Type type, SerializerAttribute settings, BinaryReader reader, PackageFile package, bool isListElement = false) { switch (type.Name) { case "String": // FString return(reader.ReadFString()); case "Byte[]": int sz = settings?.Size ?? 0; byte[] data = reader.ReadBytes(sz); return(data); case "Boolean": return(DeserializeBool(settings, reader, package, isListElement)); case "Byte": return(reader.ReadByte()); case "Int16": return(reader.ReadInt16()); case "UInt16": return(reader.ReadUInt16()); case "Int32": return(reader.ReadInt32()); case "UInt32": return(reader.ReadUInt32()); case "Single": return(reader.ReadSingle()); case "Double": return(reader.ReadDouble()); case "Int64": return(reader.ReadInt64()); case "UInt64": return(reader.ReadUInt64()); case "List`1": // TArray var elementType = type.GetGenericArguments()[0]; var listType = typeof(List <>).MakeGenericType(elementType); var list = Activator.CreateInstance(listType) as IList; int count = reader.ReadInt32(); for (int i = 0; i < count; i++) { list.Add(DeserializeValue(elementType, settings, reader, package, true)); } return(list); case "FName": var name = new FName(); name.Deserialize(reader, package); return(name); default: if (type.IsEnum) { var enumType = Enum.GetUnderlyingType(type); return(DeserializeValue(enumType, settings, reader, package)); } if (type.GetInterfaces().Contains(typeof(ISerializable))) { var obj = Activator.CreateInstance(type) as ISerializable; obj.Deserialize(reader, package); return(obj); } break; } return(null); }
public void Serialize(BinaryWriter writer, PackageFile package) { for (int i = 0; i < PropertyTags.Count; i++) { var tag = PropertyTags[i]; ISerializable property = null; if (Properties.Count >= i) { property = Properties[i]; } tag.Serialize(writer, package); if (property != null) { property.Serialize(writer, package); } } var endName = new FName(); endName.Value = "None"; endName.Serialize(writer, package); writer.Write(Reserved); foreach (var prop in Properties) { if (prop.GetType() != typeof(ObjectProperty)) { continue; } writer.Write(PropertiesData.Count); foreach (var kvp in PropertiesData) { kvp.Key.Serialize(writer, package); bool writeStructSize = true; var settings = kvp.Value.GetType().GetCustomAttribute <SerializerAttribute>(); if (settings != null && settings.NoStructSize) { writeStructSize = false; } long structSizePosition = 0; if (writeStructSize) { structSizePosition = writer.BaseStream.Position; writer.Write((int)0);// dummy val, we'll fix it later } var structStartPosition = writer.BaseStream.Position; kvp.Value.Serialize(writer, package); var structEndPosition = writer.BaseStream.Position; if (writeStructSize) { var structSize = structEndPosition - structStartPosition; writer.BaseStream.Position = structSizePosition; writer.Write((uint)structSize); writer.BaseStream.Position = structEndPosition; } } } }