public RobloxBinaryFile(byte[] contents)
        {
            using (MemoryStream file = new MemoryStream(contents))
                using (RobloxBinaryReader reader = new RobloxBinaryReader(file))
                {
                    // Verify the signature of the file.
                    byte[] binSignature = reader.ReadBytes(14);
                    string signature    = Encoding.UTF7.GetString(binSignature);

                    if (signature != MagicHeader)
                    {
                        throw new InvalidDataException("Provided file's signature does not match RobloxBinaryFile.MagicHeader!");
                    }

                    // Read header data.
                    Version      = reader.ReadUInt16();
                    NumTypes     = reader.ReadUInt32();
                    NumInstances = reader.ReadUInt32();
                    Reserved     = reader.ReadBytes(8);

                    // Begin reading the file chunks.
                    bool reading = true;

                    Types     = new INST[NumTypes];
                    Instances = new FileInstance[NumInstances];

                    while (reading)
                    {
                        try
                        {
                            RobloxBinaryChunk chunk = new RobloxBinaryChunk(reader);
                            Chunks.Add(chunk);

                            switch (chunk.ChunkType)
                            {
                            case "INST":
                                INST type = new INST(chunk);
                                type.Allocate(this);
                                break;

                            case "PROP":
                                PROP prop = new PROP(chunk);
                                prop.ReadProperties(this);
                                break;

                            case "PRNT":
                                PRNT hierarchy = new PRNT(chunk);
                                hierarchy.Assemble(this);
                                break;

                            case "META":
                                Metadata = new META(chunk);
                                break;

                            case "END\0":
                                reading = false;
                                break;

                            default:
                                Chunks.Remove(chunk);
                                break;
                            }
                        }
                        catch (EndOfStreamException)
                        {
                            throw new Exception("Unexpected end of file!");
                        }
                    }
                }
        }
Beispiel #2
0
        public void ReadProperties(RobloxBinaryFile file)
        {
            INST type = file.Types[TypeIndex];

            FileProperty[] props = new FileProperty[type.NumInstances];

            int[] ids       = type.InstanceIds;
            int   instCount = type.NumInstances;

            for (int i = 0; i < instCount; i++)
            {
                int          id       = ids[i];
                FileInstance instance = file.Instances[id];

                FileProperty prop = new FileProperty();
                prop.Instance = instance;
                prop.Name     = Name;
                prop.Type     = Type;

                props[i] = prop;
                instance.AddProperty(ref prop);
            }

            // Setup some short-hand functions for actions frequently used during the read procedure.
            var readInts   = new Func <int[]>(() => Reader.ReadInts(instCount));
            var readFloats = new Func <float[]>(() => Reader.ReadFloats(instCount));

            var loadProperties = new Action <Func <int, object> >(read =>
            {
                for (int i = 0; i < instCount; i++)
                {
                    object result  = read(i);
                    props[i].Value = result;
                }
            });

            // Read the property data based on the property type.
            // NOTE: This only reads the property types needed for Rbx2Source.
            //       You can visit the following link for a full implementation:
            //       https://github.com/CloneTrooper1019/Roblox-File-Format/blob/master/BinaryFormat/ChunkTypes/PROP.cs

            switch (Type)
            {
            case PropertyType.String:
                loadProperties(i => Reader.ReadString());
                break;

            case PropertyType.Bool:
                loadProperties(i => Reader.ReadBoolean());
                break;

            case PropertyType.Int:
                int[] ints = readInts();
                loadProperties(i => ints[i]);
                break;

            case PropertyType.Float:
                float[] floats = readFloats();
                loadProperties(i => floats[i]);
                break;

            case PropertyType.Double:
                loadProperties(i => Reader.ReadDouble());
                break;

            case PropertyType.Vector3:
                float[] Vector3_X = readFloats(),
                Vector3_Y = readFloats(),
                Vector3_Z = readFloats();

                loadProperties(i =>
                {
                    float x = Vector3_X[i],
                    y       = Vector3_Y[i],
                    z       = Vector3_Z[i];

                    return(new Vector3(x, y, z));
                });

                break;

            case PropertyType.BrickColor:
                int[] brickColorIds = readInts();

                loadProperties(i =>
                {
                    int brickColorId = brickColorIds[i];
                    return(BrickColor.FromNumber(brickColorId));
                });

                break;

            case PropertyType.CFrame:
            case PropertyType.Quaternion:
                // Temporarily load the rotation matrices into their properties.
                // We'll update them to CFrames once we iterate over the position data.

                loadProperties(i =>
                {
                    int normXY = Reader.ReadByte();

                    if (normXY > 0)
                    {
                        // Make sure this value is in a safe range.
                        normXY = (normXY - 1) % 36;

                        NormalId normX = (NormalId)(normXY / 6);
                        Vector3 R0     = Vector3.FromNormalId(normX);

                        NormalId normY = (NormalId)(normXY % 6);
                        Vector3 R1     = Vector3.FromNormalId(normY);

                        // Compute R2 using the cross product of R0 and R1.
                        Vector3 R2 = R0.Cross(R1);

                        // Generate the rotation matrix and return it.
                        return(new float[9]
                        {
                            R0.X, R0.Y, R0.Z,
                            R1.X, R1.Y, R1.Z,
                            R2.X, R2.Y, R2.Z,
                        });
                    }
                    else if (Type == PropertyType.Quaternion)
                    {
                        float qx = Reader.ReadFloat(), qy = Reader.ReadFloat(),
                        qz       = Reader.ReadFloat(), qw = Reader.ReadFloat();

                        Quaternion quaternion = new Quaternion(qx, qy, qz, qw);
                        var rotation          = quaternion.ToCFrame();

                        return(rotation.GetComponents());
                    }
                    else
                    {
                        float[] matrix = new float[9];

                        for (int m = 0; m < 9; m++)
                        {
                            float value = Reader.ReadFloat();
                            matrix[m]   = value;
                        }

                        return(matrix);
                    }
                });

                float[] CFrame_X = readFloats(),
                CFrame_Y = readFloats(),
                CFrame_Z = readFloats();

                loadProperties(i =>
                {
                    float[] matrix = props[i].Value as float[];

                    float x = CFrame_X[i],
                    y       = CFrame_Y[i],
                    z       = CFrame_Z[i];

                    float[] position = new float[3] {
                        x, y, z
                    };
                    float[] components = position
                                         .Concat(matrix)
                                         .ToArray();

                    return(new CFrame(components));
                });

                break;

            case PropertyType.Enum:
                uint[] enums = Reader.ReadInterleaved(instCount, BitConverter.ToUInt32);
                loadProperties(i => enums[i]);

                break;

            case PropertyType.Ref:
                int[] instIds = Reader.ReadInstanceIds(instCount);

                loadProperties(i =>
                {
                    int instId = instIds[i];
                    return(instId >= 0 ? file.Instances[instId] : null);
                });

                break;

            case PropertyType.Color3uint8:
                byte[] Color3uint8_R = Reader.ReadBytes(instCount),
                Color3uint8_G = Reader.ReadBytes(instCount),
                Color3uint8_B = Reader.ReadBytes(instCount);

                loadProperties(i =>
                {
                    byte r = Color3uint8_R[i],
                    g      = Color3uint8_G[i],
                    b      = Color3uint8_B[i];

                    return(Color.FromArgb(r, g, b));
                });

                break;

            case PropertyType.Int64:
                long[] int64s = Reader.ReadInterleaved(instCount, (buffer, start) =>
                {
                    long result = BitConverter.ToInt64(buffer, start);
                    return((long)((ulong)result >> 1) ^ (-(result & 1)));
                });

                loadProperties(i => int64s[i]);
                break;
            }

            Reader.Dispose();
        }