public void Save(Stream stream) { var writer = new EndianAwareBinaryWriter(stream); var serializer = new RobloxSerializer(this); ReferentProvider.ClearCache(); // Clearing existing referent cache guarantees that referents won't be fragmented var instances = GetChildFirstInstanceEnumerator().ToArray(); var typeGroups = instances.GroupBy(n => n.ClassName).OrderBy(n => n.Key).ToDictionary(n => n.Key, n => n.ToArray()); var typeCount = typeGroups.Count; var objectCount = typeGroups.Aggregate(0, (acc, pair) => acc + pair.Value.Length); writer.WriteBytes(Signatures.Signature); // File signature writer.WriteInt32(typeCount); // Generic header values writer.WriteInt32(objectCount); writer.WriteInt32(0); // Reserved writer.WriteInt32(0); // Reserved // Write type headers var typeHeaders = new TypeHeader[typeCount]; var nextTypeId = 0; foreach (var typeGroup in typeGroups) { var typeHeader = new TypeHeader(typeGroup.Key, nextTypeId, typeGroup.Value.Select(n => ReferentProvider.GetReferent(n)).ToArray()); if (IsSingleton(typeGroup)) { typeHeader.AdditionalData = new byte[typeHeader.InstanceCount]; for (var i = 0; i < typeHeader.InstanceCount; i++) { typeHeader.AdditionalData[i] = 0x1; } } typeHeaders[nextTypeId] = typeHeader; var bytes = typeHeader.Serialize(); writer.WriteBytes(Signatures.TypeHeaderSignature); RobloxLZ4.WriteBlock(stream, bytes); nextTypeId++; } // Write property data foreach (var typeGroup in typeGroups) { var typeHeader = typeHeaders.First(n => n.Name == typeGroup.Key); var instanceTypes = serializer.GetUniqueProperties(typeGroup.Value); var propertyBlocks = instanceTypes.Select(propertyDescriptor => serializer.FillPropertyBlock(propertyDescriptor.Name, propertyDescriptor.Type, typeHeader.TypeId, typeGroup.Value, ReferentProvider)).ToList(); foreach (var propertyBlock in propertyBlocks) { var bytes = propertyBlock.Serialize(); writer.WriteBytes(Signatures.PropBlockSignature); RobloxLZ4.WriteBlock(stream, bytes); } } // Build parent child referent arrays var parentData = Util.BuildParentData(instances, ReferentProvider); var parentDataBytes = Util.SerializeParentData(parentData); writer.WriteBytes(Signatures.ParentDataSignature); RobloxLZ4.WriteBlock(stream, parentDataBytes); // Write ending signature writer.WriteBytes(Signatures.EndSignature); writer.WriteBytes(Signatures.FileEndSignature); }
public RobloxDocument() { Children = new ChildCollection(null); ReferentProvider = new ReferentProvider(); }
private T GetPropertyValue <T>(string propertyName, Instance instance, Type instanceType, PropertyType propertyType, ReferentProvider referentProvider) { if (ReflectionMappingManager.HasMappedClrProperty(instanceType, propertyName)) { var propertyTuple = ReflectionMappingManager.PropertyCache[instanceType][propertyName]; if (propertyType != PropertyType.Referent) { var propertyValue = propertyTuple.Item1.GetValue(instance); if (propertyValue != null) { return((T)propertyValue); } else { return((T)ReflectionMappingManager.GetDefaultValue(instanceType, propertyName, propertyType)); } } else { var referentObj = propertyTuple.Item1.GetValue(instance); if (referentObj != null) { return ((T)(object)referentProvider.GetReferent((Instance)propertyTuple.Item1.GetValue(instance))); } else { return((T)ReflectionMappingManager.GetDefaultValueForType(PropertyType.Referent)); } } } else if (instance.UnmanagedProperties.ContainsKey(propertyName)) { var objValue = instance.UnmanagedProperties[propertyName].Get(); if (objValue != null) { return((T)objValue); } else { return((T)ReflectionMappingManager.GetDefaultValueForType(propertyType)); } } else { return((T)ReflectionMappingManager.GetDefaultValueForType(propertyType)); } }
public PropertyBlock FillPropertyBlock(string propertyName, PropertyType propertyType, int typeId, Instance[] instances, ReferentProvider referentProvider) { switch (propertyType) { case PropertyType.String: return(FillPropertyBlock <string>(propertyName, propertyType, typeId, instances, referentProvider)); case PropertyType.Boolean: return(FillPropertyBlock <bool>(propertyName, propertyType, typeId, instances, referentProvider)); case PropertyType.Int32: return(FillPropertyBlock <int>(propertyName, propertyType, typeId, instances, referentProvider)); case PropertyType.Float: return(FillPropertyBlock <float>(propertyName, propertyType, typeId, instances, referentProvider)); case PropertyType.Double: return(FillPropertyBlock <double>(propertyName, propertyType, typeId, instances, referentProvider)); case PropertyType.UDim2: return(FillPropertyBlock <UDim2>(propertyName, propertyType, typeId, instances, referentProvider)); case PropertyType.Ray: return(FillPropertyBlock <Ray>(propertyName, propertyType, typeId, instances, referentProvider)); case PropertyType.Faces: return(FillPropertyBlock <Faces>(propertyName, propertyType, typeId, instances, referentProvider)); case PropertyType.Axis: return(FillPropertyBlock <Axis>(propertyName, propertyType, typeId, instances, referentProvider)); case PropertyType.BrickColor: return(FillPropertyBlock <BrickColor>(propertyName, propertyType, typeId, instances, referentProvider)); case PropertyType.Color3: return(FillPropertyBlock <Color3>(propertyName, propertyType, typeId, instances, referentProvider)); case PropertyType.Vector2: return(FillPropertyBlock <Vector2>(propertyName, propertyType, typeId, instances, referentProvider)); case PropertyType.Vector3: return(FillPropertyBlock <Vector3>(propertyName, propertyType, typeId, instances, referentProvider)); case PropertyType.CFrame: return(FillPropertyBlock <CFrame>(propertyName, propertyType, typeId, instances, referentProvider)); case PropertyType.Enumeration: return(FillPropertyBlock <int>(propertyName, propertyType, typeId, instances, referentProvider)); case PropertyType.Referent: return(FillPropertyBlock <int>(propertyName, propertyType, typeId, instances, referentProvider)); case PropertyType.NumberSequence: return(FillPropertyBlock <NumberSequence>(propertyName, propertyType, typeId, instances, referentProvider)); case PropertyType.ColorSequence: return(FillPropertyBlock <ColorSequence>(propertyName, propertyType, typeId, instances, referentProvider)); case PropertyType.NumberRange: return(FillPropertyBlock <NumberRange>(propertyName, propertyType, typeId, instances, referentProvider)); case PropertyType.Rectangle: return(FillPropertyBlock <Rectangle>(propertyName, propertyType, typeId, instances, referentProvider)); case PropertyType.PhysicalProperties: return(FillPropertyBlock <PhysicalProperties>(propertyName, propertyType, typeId, instances, referentProvider)); default: throw new ArgumentOutOfRangeException(nameof(propertyType), propertyType, null); } }
public PropertyBlock <T> FillPropertyBlock <T>(string propertyName, PropertyType propertyType, int typeId, Instance[] instances, ReferentProvider referentProvider) { var propertyBlock = new PropertyBlock <T>(propertyName, propertyType, typeId, instances.Length); var instanceType = instances[0].GetType(); for (var i = 0; i < instances.Length; i++) { propertyBlock.Values.Add(GetPropertyValue <T>(propertyName, instances[i], instanceType, propertyType, referentProvider)); } return(propertyBlock); }