/// <summary> /// Tries to read all properties that resides in this object instance. /// </summary> protected void DeserializeProperties() { Default = this; _Properties = new DefaultPropertiesCollection(); while (true) { var tag = new UDefaultProperty(Default); if (!tag.Deserialize()) { break; } _Properties.Add(tag); } // We need to keep the MemoryStream alive, // because we first deserialize the defaultproperties but we skip the values, which we'll deserialize later on by demand. if (Properties.Count == 0) { _Properties = null; } }
/// <summary> /// Deserialize a default property value of a specified type. /// </summary> /// <param name="type">Kind of type to try deserialize.</param> /// <returns>The deserialized value if any.</returns> private string DeserializeDefaultPropertyValue(PropertyType type, ref DeserializeFlags deserializeFlags) { if (_Buffer.Position - _ValueOffset > Size) { throw new DeserializationException("End of defaultproperties stream reached..."); } var orgOuter = _Outer; string propertyValue = String.Empty; try { // Deserialize Value switch (type) { case PropertyType.BoolProperty: { var value = _BoolValue; if (Size == 1 && _Buffer.Version < V3) { value = _Buffer.ReadByte() > 0; } propertyValue = value ? "true" : "false"; break; } case PropertyType.StrProperty: propertyValue = $"\"{_Buffer.ReadText()}\""; break; case PropertyType.NameProperty: propertyValue = $"\"{_Buffer.ReadName()}\""; break; case PropertyType.IntProperty: propertyValue = _Buffer.ReadInt32().ToString(CultureInfo.InvariantCulture); break; #if BIOSHOCK case PropertyType.QwordProperty: propertyValue = _Buffer.ReadInt64().ToString(CultureInfo.InvariantCulture); break; case PropertyType.XWeakReferenceProperty: propertyValue = $"\"XWeakReference\": \"?\":{_Buffer.ReadName()},\"?\":\"{_Buffer.ReadName()}\",\"?\":\"{_Buffer.ReadByte()}\",\"?\":\"{_Buffer.ReadName()}\""; break; #endif case PropertyType.FloatProperty: propertyValue = _Buffer.ReadFloat().ToUFloat(); break; case PropertyType.ByteProperty: if (_Buffer.Version >= V3 && Size == 8) { var enumValue = _Buffer.ReadName(); propertyValue = enumValue; if (_Buffer.Version >= VEnumName) { propertyValue = $"\"{EnumName}.{propertyValue}\""; } } else { propertyValue = _Buffer.ReadByte().ToString(CultureInfo.InvariantCulture); } break; case PropertyType.InterfaceProperty: case PropertyType.ComponentProperty: case PropertyType.ObjectProperty: { var obj = _Buffer.ReadObject(); _Container.Record("object", obj); if (obj != null) { bool inline = false; // If true, object is an archetype or subobject. if (obj.Outer == _Container && (deserializeFlags & DeserializeFlags.WithinStruct) == 0) { // Unknown objects are only deserialized on demand. obj.BeginDeserializing(); if (obj.Properties != null && obj.Properties.Count > 0) { inline = true; propertyValue = obj.Decompile() + "\r\n" + UDecompilingState.Tabs; _TempFlags |= DoNotAppendName; if ((deserializeFlags & DeserializeFlags.WithinArray) != 0) { _TempFlags |= ReplaceNameMarker; propertyValue += $"\"%ARRAYNAME%\":\"{obj.Name}\""; } else { propertyValue += $"\"{Name}\":\"{obj.Name}\""; } } } if (!inline) { // =CLASS'Package.Group(s)+.Name' propertyValue = $"{{\"Class\":\"{obj.GetClassName()}\",\"Value\":\"{obj.GetOuterGroup()}\"}}"; } } else { // =none propertyValue = "null"; } break; } case PropertyType.ClassProperty: { var obj = _Buffer.ReadObject(); _Container.Record("object", obj); propertyValue = (obj != null ? $"{{\r\n\"Name\":\"{Name}\",\r\n\"Class\":\"{obj.Class.Name}\",\r\n" : "null"); break; } case PropertyType.DelegateProperty: { _TempFlags |= DoNotAppendName; int outerIndex = _Buffer.ReadObjectIndex(); // Where the assigned delegate property exists. var delegateValue = _Buffer.ReadName(); string delegateName = ((string)(Name)).Substring(2, Name.Length - 12); propertyValue = $"\"{delegateName}\":\"{delegateValue}\""; break; } #region HardCoded Struct Types case PropertyType.Color: { string b = DeserializeDefaultPropertyValue(PropertyType.ByteProperty, ref deserializeFlags); string g = DeserializeDefaultPropertyValue(PropertyType.ByteProperty, ref deserializeFlags); string r = DeserializeDefaultPropertyValue(PropertyType.ByteProperty, ref deserializeFlags); string a = DeserializeDefaultPropertyValue(PropertyType.ByteProperty, ref deserializeFlags); propertyValue += $"{{\"r\":{r},\"g\":{g},\"b\":{b},\"a\":{a}}}"; break; } case PropertyType.LinearColor: { string b = DeserializeDefaultPropertyValue(PropertyType.FloatProperty, ref deserializeFlags); string g = DeserializeDefaultPropertyValue(PropertyType.FloatProperty, ref deserializeFlags); string r = DeserializeDefaultPropertyValue(PropertyType.FloatProperty, ref deserializeFlags); string a = DeserializeDefaultPropertyValue(PropertyType.FloatProperty, ref deserializeFlags); propertyValue += $"{{\"r\":{r},\"g\":{g},\"b\":{b},\"a\":{a}}}"; break; } case PropertyType.Vector: { string x = DeserializeDefaultPropertyValue(PropertyType.FloatProperty, ref deserializeFlags); string y = DeserializeDefaultPropertyValue(PropertyType.FloatProperty, ref deserializeFlags); string z = DeserializeDefaultPropertyValue(PropertyType.FloatProperty, ref deserializeFlags); propertyValue += $"{{\"x\":{x},\"y\":{y},\"z\":{z}}}"; break; } case PropertyType.TwoVectors: { string v1 = DeserializeDefaultPropertyValue(PropertyType.Vector, ref deserializeFlags); string v2 = DeserializeDefaultPropertyValue(PropertyType.Vector, ref deserializeFlags); propertyValue += $"{{\"v1\":{v1},\"v2\":{v2}}}"; break; } case PropertyType.Vector4: { string plane = DeserializeDefaultPropertyValue(PropertyType.Plane, ref deserializeFlags); propertyValue += plane; break; } case PropertyType.Vector2D: { string x = DeserializeDefaultPropertyValue(PropertyType.FloatProperty, ref deserializeFlags); string y = DeserializeDefaultPropertyValue(PropertyType.FloatProperty, ref deserializeFlags); propertyValue += $"{{\"x\":{x},\"y\":{y}}}"; break; } case PropertyType.Rotator: { string pitch = DeserializeDefaultPropertyValue(PropertyType.IntProperty, ref deserializeFlags); string yaw = DeserializeDefaultPropertyValue(PropertyType.IntProperty, ref deserializeFlags); string roll = DeserializeDefaultPropertyValue(PropertyType.IntProperty, ref deserializeFlags); propertyValue += $"{{\"Pitch\":{pitch},\"Yaw\":{yaw},\"Roll\":{roll}}}"; break; } case PropertyType.Guid: { string a = DeserializeDefaultPropertyValue(PropertyType.IntProperty, ref deserializeFlags); string b = DeserializeDefaultPropertyValue(PropertyType.IntProperty, ref deserializeFlags); string c = DeserializeDefaultPropertyValue(PropertyType.IntProperty, ref deserializeFlags); string d = DeserializeDefaultPropertyValue(PropertyType.IntProperty, ref deserializeFlags); propertyValue += new Guid( int.Parse(a) , short.Parse(b) , short.Parse(c) , BitConverter.GetBytes(int.Parse(d))) .ToString(); break; } case PropertyType.Sphere: case PropertyType.Plane: { string v = DeserializeDefaultPropertyValue(PropertyType.Vector, ref deserializeFlags); string w = DeserializeDefaultPropertyValue(PropertyType.FloatProperty, ref deserializeFlags); propertyValue += $"{{\"v\":{v},\"w\":{w}}}"; break; } case PropertyType.Scale: { string scale = DeserializeDefaultPropertyValue(PropertyType.Vector, ref deserializeFlags); string sheerRate = DeserializeDefaultPropertyValue(PropertyType.FloatProperty, ref deserializeFlags); string sheerAxis = DeserializeDefaultPropertyValue(PropertyType.ByteProperty, ref deserializeFlags); propertyValue += $"{{\"Scale\":{scale},\"SheerRate\":{sheerRate},\"SheerAxis\":{sheerAxis}}}"; break; } case PropertyType.Box: { string min = DeserializeDefaultPropertyValue(PropertyType.Vector, ref deserializeFlags); string max = DeserializeDefaultPropertyValue(PropertyType.Vector, ref deserializeFlags); string isValid = DeserializeDefaultPropertyValue(PropertyType.ByteProperty, ref deserializeFlags); propertyValue += $"{{\"Min\":{min},\"Max\":{max},\"IsValid\":{isValid}}}"; break; } /*case PropertyType.InterpCurve: * { * // HACK: * UPropertyTag tag = new UPropertyTag( _Owner ); * tag.Serialize(); * buffer.Seek( tag.ValueOffset, System.IO.SeekOrigin.Begin ); * * int curvescount = buffer.ReadIndex(); * if( curvescount <= 0 ) * { * break; * } * propertyvalue += tag.Name + "=("; * for( int i = 0; i < curvescount; ++ i ) * { * propertyvalue += "(" + SerializeDefaultPropertyValue( PropertyType.InterpCurvePoint, buffer, ref serializeFlags ) + ")"; * if( i != curvescount - 1 ) * { * propertyvalue += ","; * } * } * propertyvalue += ")"; * break; * }*/ /*case PropertyType.InterpCurvePoint: * { * string InVal = SerializeDefaultPropertyValue( PropertyType.Float, buffer, ref serializeFlags ); * string OutVal = SerializeDefaultPropertyValue( PropertyType.Float, buffer, ref serializeFlags ); * * propertyvalue += "InVal=" + InVal + * ",OutVal=" + OutVal; * break; * }*/ case PropertyType.Quat: { propertyValue += DeserializeDefaultPropertyValue(PropertyType.Plane, ref deserializeFlags); break; } case PropertyType.Matrix: { string xPlane = DeserializeDefaultPropertyValue(PropertyType.Plane, ref deserializeFlags); string yPlane = DeserializeDefaultPropertyValue(PropertyType.Plane, ref deserializeFlags); string zPlane = DeserializeDefaultPropertyValue(PropertyType.Plane, ref deserializeFlags); string wPlane = DeserializeDefaultPropertyValue(PropertyType.Plane, ref deserializeFlags); propertyValue += $"{{\"XPlane\":{xPlane},\"YPlane\":{yPlane},\"ZPlane\":{zPlane},\"WPlane\":{wPlane}}}"; break; } case PropertyType.IntPoint: { string x = DeserializeDefaultPropertyValue(PropertyType.IntProperty, ref deserializeFlags); string y = DeserializeDefaultPropertyValue(PropertyType.IntProperty, ref deserializeFlags); propertyValue += $"{{\"X\":{x},\"Y\":{y}}}"; break; } #endregion case PropertyType.PointerProperty: case PropertyType.StructProperty: { deserializeFlags |= DeserializeFlags.WithinStruct; bool isHardCoded = false; var hardcodedStructs = (PropertyType[])Enum.GetValues(typeof(PropertyType)); for (var i = (byte)PropertyType.StructOffset; i < hardcodedStructs.Length; ++i) { var structType = Enum.GetName(typeof(PropertyType), (byte)hardcodedStructs[i]); if (string.Compare(ItemName, structType, StringComparison.OrdinalIgnoreCase) != 0) { continue; } isHardCoded = true; propertyValue += DeserializeDefaultPropertyValue(hardcodedStructs[i], ref deserializeFlags); break; } if (!isHardCoded) { // We have to modify the outer so that dynamic arrays within this struct // will be able to find its variables to determine the array type. FindProperty(out _Outer); propertyValue += "{"; while (true) { var tag = new UDefaultProperty(_Container, _Outer); if (tag.Deserialize()) { // commented below was for inserting a [0] [1] etc. after the tag name // var t = (tag.ArrayIndex > 0 && tag.Type != PropertyType.BoolProperty // ? $"{tag.ArrayIndex.ToString()}" // : string.Empty); propertyValue += $"\"{tag.Name}\":{tag.DeserializeValue(deserializeFlags)},"; } else { if (propertyValue.EndsWith(",")) { propertyValue = $"{propertyValue.Remove(propertyValue.Length - 1, 1)}}}"; } break; } } } propertyValue = propertyValue.Length != 0 ? propertyValue : ""; break; } case PropertyType.ArrayProperty: { int arraySize = _Buffer.ReadIndex(); _Container.Record("arraySize", arraySize.ToString()); if (arraySize == 0) { propertyValue = "null"; break; } // Find the property within the outer/owner or its inheritances. // If found it has to modify the outer so structs within this array can find their array variables. // Additionally we need to know the property to determine the array's type. var arrayType = PropertyType.None; if (FindProperty(out _Outer) is UArrayProperty property && property.InnerProperty != null) { arrayType = property.InnerProperty.Type; } // If we did not find a reference to the associated property(because of imports) // then try to determine the array's type by scanning the definined array types. else if (UnrealConfig.VariableTypes != null && UnrealConfig.VariableTypes.ContainsKey(Name)) { var varTuple = UnrealConfig.VariableTypes[Name]; if (varTuple != null) { arrayType = varTuple.Item2; } } if (arrayType == PropertyType.None) { propertyValue = "null"; break; } deserializeFlags |= DeserializeFlags.WithinArray; if ((deserializeFlags & DeserializeFlags.WithinStruct) != 0) { // Hardcoded fix for InterpCurve and InterpCurvePoint. if (string.Compare(Name, "Points", StringComparison.OrdinalIgnoreCase) == 0) { arrayType = PropertyType.StructProperty; } for (int i = 0; i < arraySize; ++i) { propertyValue += DeserializeDefaultPropertyValue(arrayType, ref deserializeFlags) + (i != arraySize - 1 ? "," : string.Empty); } propertyValue = $"{propertyValue}"; } else { for (var i = 0; i < arraySize; ++i) { var elementValue = DeserializeDefaultPropertyValue(arrayType, ref deserializeFlags); if ((_TempFlags & ReplaceNameMarker) != 0) { propertyValue += elementValue.Replace("%ARRAYNAME%", $"{Name}"); _TempFlags = 0x00; } else { propertyValue += $"\"{Name}\":{elementValue}"; } if (i == arraySize - 1) { propertyValue += "]\r\n" + UDecompilingState.Tabs; } } } _TempFlags |= DoNotAppendName; break; }