private static void STUv2ProcessInstance(AssetRecord record, STUInstance instance) { var fields = GetFields(instance.GetType(), true); foreach (FieldInfo field in fields) { object fieldValue = field.GetValue(instance); Type fieldType = field.FieldType; if (fieldValue == null) { continue; } var fieldAttribute = field.GetCustomAttribute <STUFieldAttribute>(); if (fieldAttribute != null) { if (fieldAttribute.ReaderType == typeof(InlineInstanceFieldReader)) { if (!fieldType.IsArray) { STUv2ProcessInstance(record, (STUInstance)fieldValue); } else { IEnumerable enumerable = (IEnumerable)fieldValue; foreach (object val in enumerable) { STUv2ProcessInstance(record, (STUInstance)val); } } return; } } if (fieldType.IsArray) { Type elementType = fieldType.GetElementType(); IEnumerable enumerable = (IEnumerable)fieldValue; foreach (object val in enumerable) { STUv2ProcessField(record, val, elementType); } } else { STUv2ProcessField(record, fieldValue, fieldType); } } }
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); }
private object InitializeObjectArray(STUInstanceField field, Type type, BinaryReader reader, STUFieldAttribute element) { if (field.FieldSize == 0 && field.FieldChecksum != 0) { STUInlineArrayInfo inlineInfo = reader.Read <STUInlineArrayInfo>(); if (inlineInfo.Size > 600000) { return(null); // i feel that it's unlikley that there will be an inline with this count, exceptions if this isn't checked. } Array array = Array.CreateInstance(type, (uint)inlineInfo.Count); // if (inlineInfo.FieldListIndex == 0) return array; for (uint i = 0; i < (uint)inlineInfo.Count; ++i) { Stream.Position += element.Padding; uint fieldIndex = reader.ReadUInt32(); object instance = Activator.CreateInstance(type); if (fieldIndex >= InstanceFieldLists.Length) { continue; } array.SetValue(InitializeObject(instance, type, InstanceFields[fieldIndex], reader), i); STUInstance fieldInstance = array.GetValue(i) as STUInstance; if (fieldInstance != null) { fieldInstance.Usage = InstanceUsage.InlineArray; } HiddenInstances.Add(fieldInstance); } return(array); } if (typeof(STUInstance).IsAssignableFrom(type)) { int embedArrayOffset = reader.ReadInt32(); Metadata.Position = embedArrayOffset; STUEmbedArrayInfo embedArrayInfo = MetadataReader.Read <STUEmbedArrayInfo>(); if (embedArrayInfo.Count == 0) { return(null); } Metadata.Position = embedArrayInfo.Offset; Array array = Array.CreateInstance(type, embedArrayInfo.Count); List <int> request = new List <int>(); for (int i = 0; i < embedArrayInfo.Count; i++) { int instanceIndex = MetadataReader.ReadInt32(); MetadataReader.ReadInt32(); // Padding if (instanceIndex == -1) { return(null); } request.Add(instanceIndex); } EmbedArrayRequests[array] = request.ToArray(); return(array); } int offset = reader.ReadInt32(); Metadata.Position = offset; STUArrayInfo arrayInfo = MetadataReader.Read <STUArrayInfo>(); Metadata.Position = arrayInfo.Offset; return(GetValueArray(type, element, arrayInfo, field)); }