예제 #1
0
        public void Save(BinaryRobloxFileWriter writer)
        {
            BinaryRobloxFile file = writer.File;

            File = file;

            INST inst  = file.Classes[ClassIndex];
            var  props = new List <Property>();

            foreach (int instId in inst.InstanceIds)
            {
                Instance instance  = file.Instances[instId];
                var      instProps = instance.Properties;

                if (!instProps.TryGetValue(Name, out Property prop))
                {
                    throw new Exception($"Property {Name} must be defined in {instance.GetFullName()}!");
                }
                else if (prop.Type != Type)
                {
                    throw new Exception($"Property {Name} is not using the correct type in {instance.GetFullName()}!");
                }

                props.Add(prop);
            }

            writer.Write(ClassIndex);
            writer.WriteString(Name);
            writer.Write(TypeId);

            switch (Type)
            {
            case PropertyType.String:
                props.ForEach(prop =>
                {
                    byte[] buffer = prop.HasRawBuffer ? prop.RawBuffer : null;

                    if (buffer == null)
                    {
                        string value = prop.CastValue <string>();
                        buffer       = Encoding.UTF8.GetBytes(value);
                    }

                    writer.Write(buffer.Length);
                    writer.Write(buffer);
                });

                break;

            case PropertyType.Bool:
                props.ForEach(prop =>
                {
                    bool value = prop.CastValue <bool>();
                    writer.Write(value);
                });

                break;

            case PropertyType.Int:
                var ints = new List <int>();

                props.ForEach(prop =>
                {
                    int value = prop.CastValue <int>();
                    ints.Add(value);
                });

                writer.WriteInts(ints);
                break;

            case PropertyType.Float:
                var floats = new List <float>();

                props.ForEach(prop =>
                {
                    float value = prop.CastValue <float>();
                    floats.Add(value);
                });

                writer.WriteFloats(floats);
                break;

            case PropertyType.Double:
                props.ForEach(prop =>
                {
                    double value = prop.CastValue <double>();
                    writer.Write(BinaryRobloxFileWriter.GetBytes(value));
                });

                break;

            case PropertyType.UDim:
                var UDim_Scales  = new List <float>();
                var UDim_Offsets = new List <int>();

                props.ForEach(prop =>
                {
                    UDim value = prop.CastValue <UDim>();
                    UDim_Scales.Add(value.Scale);
                    UDim_Offsets.Add(value.Offset);
                });

                writer.WriteFloats(UDim_Scales);
                writer.WriteInts(UDim_Offsets);

                break;

            case PropertyType.UDim2:
                var UDim2_Scales_X = new List <float>();
                var UDim2_Scales_Y = new List <float>();

                var UDim2_Offsets_X = new List <int>();
                var UDim2_Offsets_Y = new List <int>();

                props.ForEach(prop =>
                {
                    UDim2 value = prop.CastValue <UDim2>();

                    UDim2_Scales_X.Add(value.X.Scale);
                    UDim2_Scales_Y.Add(value.Y.Scale);

                    UDim2_Offsets_X.Add(value.X.Offset);
                    UDim2_Offsets_Y.Add(value.Y.Offset);
                });

                writer.WriteFloats(UDim2_Scales_X);
                writer.WriteFloats(UDim2_Scales_Y);

                writer.WriteInts(UDim2_Offsets_X);
                writer.WriteInts(UDim2_Offsets_Y);

                break;

            case PropertyType.Ray:
                props.ForEach(prop =>
                {
                    Ray ray = prop.CastValue <Ray>();

                    Vector3 pos = ray.Origin;
                    writer.Write(pos.X);
                    writer.Write(pos.Y);
                    writer.Write(pos.Z);

                    Vector3 dir = ray.Direction;
                    writer.Write(dir.X);
                    writer.Write(dir.Y);
                    writer.Write(dir.Z);
                });

                break;

            case PropertyType.Faces:
            case PropertyType.Axes:
                props.ForEach(prop =>
                {
                    byte value = prop.CastValue <byte>();
                    writer.Write(value);
                });

                break;

            case PropertyType.BrickColor:
                var BrickColorIds = new List <int>();

                props.ForEach(prop =>
                {
                    BrickColor value = prop.CastValue <BrickColor>();
                    BrickColorIds.Add(value.Number);
                });

                writer.WriteInts(BrickColorIds);
                break;

            case PropertyType.Color3:
                var Color3_R = new List <float>();
                var Color3_G = new List <float>();
                var Color3_B = new List <float>();

                props.ForEach(prop =>
                {
                    Color3 value = prop.CastValue <Color3>();
                    Color3_R.Add(value.R);
                    Color3_G.Add(value.G);
                    Color3_B.Add(value.B);
                });

                writer.WriteFloats(Color3_R);
                writer.WriteFloats(Color3_G);
                writer.WriteFloats(Color3_B);

                break;

            case PropertyType.Vector2:
                var Vector2_X = new List <float>();
                var Vector2_Y = new List <float>();

                props.ForEach(prop =>
                {
                    Vector2 value = prop.CastValue <Vector2>();
                    Vector2_X.Add(value.X);
                    Vector2_Y.Add(value.Y);
                });

                writer.WriteFloats(Vector2_X);
                writer.WriteFloats(Vector2_Y);

                break;

            case PropertyType.Vector3:
                var Vector3_X = new List <float>();
                var Vector3_Y = new List <float>();
                var Vector3_Z = new List <float>();

                props.ForEach(prop =>
                {
                    Vector3 value = prop.CastValue <Vector3>();
                    Vector3_X.Add(value.X);
                    Vector3_Y.Add(value.Y);
                    Vector3_Z.Add(value.Z);
                });

                writer.WriteFloats(Vector3_X);
                writer.WriteFloats(Vector3_Y);
                writer.WriteFloats(Vector3_Z);

                break;

            case PropertyType.CFrame:
            case PropertyType.Quaternion:
                var CFrame_X = new List <float>();
                var CFrame_Y = new List <float>();
                var CFrame_Z = new List <float>();

                props.ForEach(prop =>
                {
                    CFrame value = null;

                    if (prop.Value is Quaternion)
                    {
                        Quaternion q = prop.CastValue <Quaternion>();
                        value        = q.ToCFrame();
                    }
                    else
                    {
                        value = prop.CastValue <CFrame>();
                    }

                    Vector3 pos = value.Position;
                    CFrame_X.Add(pos.X);
                    CFrame_Y.Add(pos.Y);
                    CFrame_Z.Add(pos.Z);

                    int orientId = value.GetOrientId();
                    writer.Write((byte)(orientId + 1));

                    if (orientId == -1)
                    {
                        if (Type == PropertyType.Quaternion)
                        {
                            Quaternion quat = new Quaternion(value);
                            writer.Write(quat.X);
                            writer.Write(quat.Y);
                            writer.Write(quat.Z);
                            writer.Write(quat.W);
                        }
                        else
                        {
                            float[] components = value.GetComponents();

                            for (int i = 3; i < 12; i++)
                            {
                                float component = components[i];
                                writer.Write(component);
                            }
                        }
                    }
                });

                writer.WriteFloats(CFrame_X);
                writer.WriteFloats(CFrame_Y);
                writer.WriteFloats(CFrame_Z);

                break;

            case PropertyType.Enum:
                var Enums = new List <uint>();

                props.ForEach(prop =>
                {
                    if (prop.Value is uint)
                    {
                        uint raw = prop.CastValue <uint>();
                        Enums.Add(raw);
                        return;
                    }

                    int signed = (int)prop.Value;
                    uint value = (uint)signed;
                    Enums.Add(value);
                });

                writer.WriteInterleaved(Enums);
                break;

            case PropertyType.Ref:
                var InstanceIds = new List <int>();

                props.ForEach(prop =>
                {
                    int referent = -1;

                    if (prop.Value != null)
                    {
                        Instance value = prop.CastValue <Instance>();
                        referent       = int.Parse(value.Referent);
                    }

                    InstanceIds.Add(referent);
                });

                writer.WriteInstanceIds(InstanceIds);
                break;

            case PropertyType.Vector3int16:
                props.ForEach(prop =>
                {
                    Vector3int16 value = prop.CastValue <Vector3int16>();
                    writer.Write(value.X);
                    writer.Write(value.Y);
                    writer.Write(value.Z);
                });

                break;

            case PropertyType.NumberSequence:
                props.ForEach(prop =>
                {
                    NumberSequence value = prop.CastValue <NumberSequence>();

                    var keyPoints = value.Keypoints;
                    writer.Write(keyPoints.Length);

                    foreach (var keyPoint in keyPoints)
                    {
                        writer.Write(keyPoint.Time);
                        writer.Write(keyPoint.Value);
                        writer.Write(keyPoint.Envelope);
                    }
                });

                break;

            case PropertyType.ColorSequence:
                props.ForEach(prop =>
                {
                    ColorSequence value = prop.CastValue <ColorSequence>();

                    var keyPoints = value.Keypoints;
                    writer.Write(keyPoints.Length);

                    foreach (var keyPoint in keyPoints)
                    {
                        Color3 color = keyPoint.Value;
                        writer.Write(keyPoint.Time);

                        writer.Write(color.R);
                        writer.Write(color.G);
                        writer.Write(color.B);

                        writer.Write(keyPoint.Envelope);
                    }
                });

                break;

            case PropertyType.NumberRange:
                props.ForEach(prop =>
                {
                    NumberRange value = prop.CastValue <NumberRange>();
                    writer.Write(value.Min);
                    writer.Write(value.Max);
                });

                break;

            case PropertyType.Rect:
                var Rect_X0 = new List <float>();
                var Rect_Y0 = new List <float>();

                var Rect_X1 = new List <float>();
                var Rect_Y1 = new List <float>();

                props.ForEach(prop =>
                {
                    Rect value = prop.CastValue <Rect>();

                    Vector2 min = value.Min;
                    Rect_X0.Add(min.X);
                    Rect_Y0.Add(min.Y);

                    Vector2 max = value.Max;
                    Rect_X1.Add(max.X);
                    Rect_Y1.Add(max.Y);
                });

                writer.WriteFloats(Rect_X0);
                writer.WriteFloats(Rect_Y0);

                writer.WriteFloats(Rect_X1);
                writer.WriteFloats(Rect_Y1);

                break;

            case PropertyType.PhysicalProperties:
                props.ForEach(prop =>
                {
                    bool custom = (prop.Value != null);
                    writer.Write(custom);

                    if (custom)
                    {
                        PhysicalProperties value = prop.CastValue <PhysicalProperties>();

                        writer.Write(value.Density);
                        writer.Write(value.Friction);
                        writer.Write(value.Elasticity);

                        writer.Write(value.FrictionWeight);
                        writer.Write(value.ElasticityWeight);
                    }
                });

                break;

            case PropertyType.Color3uint8:
                var Color3uint8_R = new List <byte>();
                var Color3uint8_G = new List <byte>();
                var Color3uint8_B = new List <byte>();

                props.ForEach(prop =>
                {
                    Color3uint8 value = prop.CastValue <Color3uint8>();
                    Color3uint8_R.Add(value.R);
                    Color3uint8_G.Add(value.G);
                    Color3uint8_B.Add(value.B);
                });

                byte[] rBuffer = Color3uint8_R.ToArray();
                writer.Write(rBuffer);

                byte[] gBuffer = Color3uint8_G.ToArray();
                writer.Write(gBuffer);

                byte[] bBuffer = Color3uint8_B.ToArray();
                writer.Write(bBuffer);

                break;

            case PropertyType.Int64:
                var Int64s = new List <long>();

                props.ForEach(prop =>
                {
                    long value = prop.CastValue <long>();
                    Int64s.Add(value);
                });

                writer.WriteInterleaved(Int64s, value =>
                {
                    // Move the sign bit to the front.
                    return((value << 1) ^ (value >> 63));
                });

                break;

            case PropertyType.SharedString:
                var  sharedKeys = new List <uint>();
                SSTR sstr       = file.SSTR;

                if (sstr == null)
                {
                    sstr      = new SSTR();
                    file.SSTR = sstr;
                }

                props.ForEach(prop =>
                {
                    var shared = prop.CastValue <SharedString>();

                    if (shared == null)
                    {
                        byte[] empty = Array.Empty <byte>();
                        shared       = SharedString.FromBuffer(empty);
                    }

                    string key = shared.Key;

                    if (!sstr.Lookup.ContainsKey(key))
                    {
                        uint id = (uint)(sstr.Lookup.Count);
                        sstr.Strings.Add(id, shared);
                        sstr.Lookup.Add(key, id);
                    }

                    uint hashId = sstr.Lookup[key];
                    sharedKeys.Add(hashId);
                });

                writer.WriteInterleaved(sharedKeys);
                break;

            case PropertyType.ProtectedString:
                props.ForEach(prop =>
                {
                    var protect   = prop.CastValue <ProtectedString>();
                    byte[] buffer = protect.RawBuffer;

                    writer.Write(buffer.Length);
                    writer.Write(buffer);
                });

                break;

            default: break;
            }
        }