internal static void FillRobloxDocument(RobloxDocument document, int objectCount, TypeHeader[] typeHeaders, Dictionary <int, List <PropertyBlock> > propertyData, Tuple <int, int>[] childParentPairs, Dictionary <Instance, PropertyCollection> propertyDict = null) { var serializer = new RobloxSerializer(document); var instances = new List <Instance>(objectCount); var propertyCollections = new List <PropertyCollection>(); // Create instances for described objects foreach (var type in typeHeaders) { for (var i = 0; i < type.InstanceCount; i++) { var instance = InstanceFactory.Create(type.Name, type.AdditionalData != null); var referent = type.Referents[i]; document.ReferentProvider.Add(instance, referent); instances.Add(instance); var propertyCollection = new PropertyCollection(); var propertyDataBlockList = propertyData[type.TypeId]; foreach (var propertyBlock in propertyDataBlockList) { propertyCollection.Add(propertyBlock.GetProperty(i)); } propertyCollections.Add(propertyCollection); } } // Set properties for (var i = 0; i < instances.Count; i++) { var instance = instances[i]; serializer.SetProperties(document, instance, propertyCollections[i]); propertyDict?.Add(instance, propertyCollections[i]); } // Set parents foreach (var pair in childParentPairs) { var child = document.ReferentProvider.GetCached(pair.Item1); if (pair.Item2 == -1) // No parent { document.Children.Add(child); } else { var parent = document.ReferentProvider.GetCached(pair.Item2); child.Parent = parent; } } }
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); }