Beispiel #1
0
        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);
        }
Beispiel #2
0
        internal static void ReadRaw(EndianAwareBinaryReader reader, out int typeCount, out int objectCount, out TypeHeader[] typeHeaders, out Dictionary <int, List <PropertyBlock> > propertyData, out Tuple <int, int>[] childParentPairs)
        {
            // Check file signature
            var signatureBytes = reader.ReadBytes(Signatures.Signature.Length);

            if (!signatureBytes.SequenceEqual(Signatures.Signature))
            {
                throw new InvalidRobloxFileException("The file signature does not match.");
            }

            typeCount   = reader.ReadInt32();
            objectCount = reader.ReadInt32();
            reader.ReadInt32(); // Reserved
            reader.ReadInt32(); // Reserved

            // Deserialize type headers
            typeHeaders = new TypeHeader[typeCount];
            for (var i = 0; i < typeCount; i++)
            {
                var typeHeaderSignature = reader.ReadBytes(Signatures.TypeHeaderSignature.Length);
                if (!typeHeaderSignature.SequenceEqual(Signatures.TypeHeaderSignature))
                {
                    throw new InvalidRobloxFileException("Invalid type header signature.");
                }

                var decompressedBytes = RobloxLZ4.ReadBlock(reader.Stream);
                var typeHeader        = TypeHeader.Deserialize(decompressedBytes);
                typeHeaders[i] = typeHeader;
            }

            // Read property data
            propertyData = new Dictionary <int, List <PropertyBlock> >(); // Key is type id
            byte[] lastPropSignature;

            while (true)
            {
                lastPropSignature = reader.ReadBytes(Signatures.PropBlockSignature.Length);
                if (!lastPropSignature.SequenceEqual(Signatures.PropBlockSignature))
                {
                    break;
                }

                var decompressedBytes = RobloxLZ4.ReadBlock(reader.Stream);
                var propertyBlock     = PropertyBlock.Deserialize(decompressedBytes, typeHeaders);

                if (propertyBlock == null)
                {
                    continue;
                }

                if (!propertyData.ContainsKey(propertyBlock.TypeId))
                {
                    propertyData.Add(propertyBlock.TypeId, new List <PropertyBlock>());
                }
                propertyData[propertyBlock.TypeId].Add(propertyBlock);
            }

            if (!lastPropSignature.SequenceEqual(Signatures.ParentDataSignature))
            {
                throw new InvalidRobloxFileException("Missing parent data section.");
            }
            var parentData = RobloxLZ4.ReadBlock(reader.Stream);

            childParentPairs = Util.ReadParentData(parentData);

            var endSignature = reader.ReadBytes(Signatures.EndSignature.Length);

            if (!endSignature.SequenceEqual(Signatures.EndSignature))
            {
                throw new InvalidRobloxFileException("End signature is missing or invalid.");
            }
        }