public void Write_AdditionalData() { var typeHeader = new TypeHeader("Part", 0x13, new[] { 0x2, 0x1, 0x3 }, new byte[] { 0x1, 0x1, 0x1 }); var expectedBytes = new byte[] { 0x13, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x50, 0x61, 0x72, 0x74, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x04, 0x01, 0x01, 0x01 }; var bytes = typeHeader.Serialize(); Assert.AreEqual(expectedBytes, bytes); }
public void Serialziation() { var rand = new Random(20121230); for (int i = 0; i < 1000; i += 1) { int kind = rand.Next(0, 3); int ordinal = rand.Next(1, 32767); int length = 0; if (kind == 3) { length = rand.Next(8, 2147483647); } var head1 = new TypeHeader { Kind = (TypeKind)kind, Size = length, Ordinal = ordinal, }; var head2 = new TypeHeader { }; Assert.Equal((TypeKind)kind, head1.Kind); Assert.Equal(length, head1.Size); Assert.Equal(ordinal, head1.Ordinal); Assert.NotEqual(head1, head2); Assert.Equal((TypeKind)0, head2.Kind); Assert.Equal(0, head2.Size); Assert.Equal(0, head2.Ordinal); Guid buf = Guid.Empty; int size = head1.Serialize((byte *)&buf, sizeof(Guid)); int read = head2.Deserialize((byte *)&buf, sizeof(Guid)); Assert.Equal(size, read); Assert.Equal(head1, head2, new TypeHeaderComparer()); } }
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); }