internal object InitializeObject(object instance, Type type, STUInstanceField[] writtenFields, BinaryReader reader) { Dictionary <uint, FieldInfo> fieldMap = CreateFieldMap(GetValidFields(type)); uint?instanceChecksum = (Attribute.GetCustomAttribute(instance.GetType(), typeof(STUAttribute), false) as STUAttribute)?.Checksum; if (instance is STUInstance stuInstance && instanceChecksum != null) { stuInstance.InstanceChecksum = (uint)instanceChecksum; } foreach (STUInstanceField writtenField in writtenFields) { if (!fieldMap.ContainsKey(writtenField.FieldChecksum)) { Debugger.Log(0, "STU", $"[STU:{type}]: Unknown field {writtenField.FieldChecksum:X8} ({writtenField.FieldSize} bytes)\n"); if (writtenField.FieldSize == 0) { uint size = reader.ReadUInt32(); reader.BaseStream.Position += size; continue; } reader.BaseStream.Position += writtenField.FieldSize; continue; } FieldInfo field = fieldMap[writtenField.FieldChecksum]; STUFieldAttribute element = field.GetCustomAttribute <STUFieldAttribute>() ?? new STUFieldAttribute(); bool skip = false; if (element?.STUVersionOnly != null) { skip = element.STUVersionOnly.All(version => version != Version); } if (element?.IgnoreVersion != null && skip) { skip = !element.IgnoreVersion.Any(stufield => stufield == writtenField.FieldChecksum); } if (skip) { continue; } if (!CheckCompatVersion(field, BuildVersion)) { continue; } if (field.FieldType.IsArray) { field.SetValue(instance, InitializeObjectArray(writtenField, field.FieldType.GetElementType(), reader, element)); } else { if (writtenField.FieldSize == 0) { if (field.FieldType.IsClass || field.FieldType.IsValueType) { STUInlineInfo inline = reader.Read <STUInlineInfo>(); object inlineInstance = Activator.CreateInstance(field.FieldType); if (inline.FieldListIndex < 0 || inline.FieldListIndex >= InstanceFields.Length) { continue; } field.SetValue(instance, InitializeObject(inlineInstance, field.FieldType, InstanceFields[inline.FieldListIndex], reader)); STUInstance fieldInstance = field.GetValue(instance) as STUInstance; if (fieldInstance != null) { fieldInstance.Usage = InstanceUsage.Inline; } HiddenInstances.Add(fieldInstance); continue; } } if (writtenField.FieldSize == 4 && field.FieldType.IsClass && !IsSimple(field.FieldType) && typeof(STUInstance).IsAssignableFrom(field.FieldType)) { int instanceIndex = reader.ReadInt32(); // sometimes the inline isn't defined in the instance EmbedRequests.Add(new KeyValuePair <object, FieldInfo>(instance, field), instanceIndex); // this is embedded, don't initialise the class continue; } reader.BaseStream.Position += element.Padding; long position = -1; if (element?.ReferenceValue == true) { long offset = reader.ReadInt64(); if (offset == 0) { continue; } position = reader.BaseStream.Position; reader.BaseStream.Position = offset; } field.SetValue(instance, GetValue(instance, writtenField, field.FieldType, reader, element, field)); if (position > -1) { reader.BaseStream.Position = position; } } if (element?.Verify != null) { if (!field.GetValue(instance).Equals(element.Verify)) { throw new Exception("Verification failed"); } } if (element.Demangle) { DemangleInstance(instance, writtenField.FieldChecksum); } } return(instance); }
internal object GetValue(object fieldOwner, Type type, BinaryReader reader, STUFieldAttribute element, FieldInfo fieldInfo, bool isArray = false, bool isInlineInstance = false) { if (type.IsArray) { long offset = reader.ReadInt64(); if (offset == 0) { return(Array.CreateInstance(type.GetElementType(), 0)); } long position = reader.BaseStream.Position; reader.BaseStream.Position = offset + Start; if (element.EmbeddedInstance) { throw new NotImplementedException(); } object value = GetValueArray(type.GetElementType(), reader, element, fieldInfo); reader.BaseStream.Position = position + 8; return(value); } switch (type.Name) { case "String": { long offset = reader.ReadInt64(); if (offset == 0) { return(string.Empty); } long position = reader.BaseStream.Position; reader.BaseStream.Position = offset + Start; STUString stringData = reader.Read <STUString>(); if (stringData.Size == 0) { return(string.Empty); } reader.BaseStream.Position = stringData.Offset + Start; string @string = new string(reader.ReadChars((int)stringData.Size)); reader.BaseStream.Position = position; return(@string); } case "Single": return(reader.ReadSingle()); case "Boolean": return(reader.ReadByte() != 0); case "Int16": return(reader.ReadInt16()); case "UInt16": return(reader.ReadUInt16()); case "Int32": return(reader.ReadInt32()); case "UInt32": return(reader.ReadUInt32()); case "Int64": return(reader.ReadInt64()); case "UInt64": return(reader.ReadUInt64()); case "Byte": return(reader.ReadByte()); case "SByte": return(reader.ReadSByte()); case "Char": return(reader.ReadChar()); default: if (type.GetInterfaces().Contains(typeof(ISTUCustomSerializable))) { if (fieldInfo.FieldType.IsArray) { return(((ISTUCustomSerializable)Activator.CreateInstance(type)).DeserializeArray(fieldOwner, this, fieldInfo, reader, null)); } return(((ISTUCustomSerializable)Activator.CreateInstance(type)).Deserialize(fieldOwner, this, fieldInfo, reader, null)); } if (type.IsEnum) { return(Enum.ToObject(type, GetValue(fieldOwner, type.GetEnumUnderlyingType(), reader, element, fieldInfo, isArray))); } if (type.IsClass || type.IsValueType) { if (!element.EmbeddedInstance) { return(InitializeObject(Activator.CreateInstance(type), type, reader, isArray, isInlineInstance && typeof(STUInstance).IsAssignableFrom(type))); } int index = reader.ReadInt32(); EmbedRequests.Add(new KeyValuePair <object, FieldInfo>(fieldOwner, fieldInfo), index); return(null); } return(null); } }