Example #1
0
        public bool ReadProperty(Property prop, XmlNode token)
        {
            if (XmlPropertyTokens.ReadPropertyGeneric(token, out uint value))
            {
                uint r = (value >> 16) & 0xFF;
                uint g = (value >> 8) & 0xFF;
                uint b = value & 0xFF;

                Color3uint8 result = Color3.FromRGB(r, g, b);
                prop.Value = result;

                return(true);
            }

            return(false);
        }
        public void Load(BinaryRobloxFileReader reader)
        {
            File       = reader.File;
            ClassIndex = reader.ReadInt32();
            Name       = reader.ReadString();

            try
            {
                byte propType = reader.ReadByte();
                Type = (PropertyType)propType;
            }
            catch (EndOfStreamException)
            {
                RobloxFile.LogError($"Got corrupted PROP chunk (@ {this})!");
                return;
            }

            if (Class == null)
            {
                RobloxFile.LogError($"Unknown class index {ClassIndex} (@ {this})!");
                return;
            }

            var ids       = Class.InstanceIds;
            int instCount = Class.NumInstances;
            var props     = new Property[instCount];

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

                if (instance == null)
                {
                    RobloxFile.LogError($"PROP: No instance @{id} for property {ClassName}.{Name}");
                    continue;
                }

                var prop = new Property(instance, this);
                props[i] = prop;

                instance.AddProperty(ref prop);
            }

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

            var readProperties = new Action <Func <int, object> >(read =>
            {
                for (int i = 0; i < instCount; i++)
                {
                    var prop = props[i];

                    if (prop == null)
                    {
                        continue;
                    }

                    prop.Value = read(i);
                }
            });

            switch (Type)
            {
            case PropertyType.String:
            {
                readProperties(i =>
                    {
                        string value = reader.ReadString();

                        // Leave an access point for the original byte sequence, in case this is a BinaryString.
                        // This will allow the developer to read the sequence without any mangling from C# strings.
                        byte[] buffer      = reader.GetLastStringBuffer();
                        props[i].RawBuffer = buffer;

                        // Check if this is going to be casted as a BinaryString.
                        // BinaryStrings should use a type of byte[] instead.

                        switch (Name)
                        {
                        case "Tags":
                        case "AttributesSerialize":
                            {
                                return(buffer);
                            }

                        default:
                            {
                                Property prop     = props[i];
                                Instance instance = prop.Instance;

                                Type instType = instance.GetType();
                                var member    = ImplicitMember.Get(instType, Name);

                                if (member != null)
                                {
                                    object result   = value;
                                    Type memberType = member.MemberType;

                                    if (memberType == typeof(byte[]))
                                    {
                                        result = buffer;
                                    }

                                    return(result);
                                }

                                return(value);
                            }
                        }
                    });

                break;
            }

            case PropertyType.Bool:
            {
                readProperties(i => reader.ReadBoolean());
                break;
            }

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

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

            case PropertyType.Double:
            {
                readProperties(i => reader.ReadDouble());
                break;
            }

            case PropertyType.UDim:
            {
                float[] UDim_Scales  = readFloats();
                int[]   UDim_Offsets = readInts();

                readProperties(i =>
                    {
                        float scale = UDim_Scales[i];
                        int offset  = UDim_Offsets[i];
                        return(new UDim(scale, offset));
                    });

                break;
            }

            case PropertyType.UDim2:
            {
                float[] UDim2_Scales_X = readFloats(),
                UDim2_Scales_Y = readFloats();

                int[] UDim2_Offsets_X = readInts(),
                UDim2_Offsets_Y = readInts();

                readProperties(i =>
                    {
                        float scaleX = UDim2_Scales_X[i],
                        scaleY       = UDim2_Scales_Y[i];

                        int offsetX = UDim2_Offsets_X[i],
                        offsetY     = UDim2_Offsets_Y[i];

                        return(new UDim2(scaleX, offsetX, scaleY, offsetY));
                    });

                break;
            }

            case PropertyType.Ray:
            {
                readProperties(i =>
                    {
                        float posX = reader.ReadFloat(),
                        posY       = reader.ReadFloat(),
                        posZ       = reader.ReadFloat();

                        float dirX = reader.ReadFloat(),
                        dirY       = reader.ReadFloat(),
                        dirZ       = reader.ReadFloat();

                        var origin    = new Vector3(posX, posY, posZ);
                        var direction = new Vector3(dirX, dirY, dirZ);

                        return(new Ray(origin, direction));
                    });

                break;
            }

            case PropertyType.Faces:
            {
                readProperties(i =>
                    {
                        byte faces = reader.ReadByte();
                        return((Faces)faces);
                    });

                break;
            }

            case PropertyType.Axes:
            {
                readProperties(i =>
                    {
                        byte axes = reader.ReadByte();
                        return((Axes)axes);
                    });

                break;
            }

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

                readProperties(i =>
                    {
                        BrickColor color = BrickColorIds[i];
                        return(color);
                    });

                break;
            }

            case PropertyType.Color3:
            {
                float[] Color3_R = readFloats(),
                Color3_G = readFloats(),
                Color3_B = readFloats();

                readProperties(i =>
                    {
                        float r = Color3_R[i],
                        g       = Color3_G[i],
                        b       = Color3_B[i];

                        return(new Color3(r, g, b));
                    });

                break;
            }

            case PropertyType.Vector2:
            {
                float[] Vector2_X = readFloats(),
                Vector2_Y = readFloats();

                readProperties(i =>
                    {
                        float x = Vector2_X[i],
                        y       = Vector2_Y[i];

                        return(new Vector2(x, y));
                    });

                break;
            }

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

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

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

                break;
            }

            case PropertyType.CFrame:
            case PropertyType.Quaternion:
            case PropertyType.OptionalCFrame:
            {
                float[][] matrices = new float[instCount][];

                if (Type == PropertyType.OptionalCFrame)
                {
                    byte cframeType = (byte)PropertyType.CFrame;
                    byte readType   = reader.ReadByte();

                    if (readType != cframeType)
                    {
                        RobloxFile.LogError($"Unexpected property type in OptionalCFrame (expected {cframeType}, got {readType})");
                        readProperties(i => null);
                        break;
                    }
                }

                for (int i = 0; i < instCount; i++)
                {
                    byte rawOrientId = reader.ReadByte();

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

                        NormalId xColumn = (NormalId)(orientId / 6);
                        Vector3  R0      = Vector3.FromNormalId(xColumn);

                        NormalId yColumn = (NormalId)(orientId % 6);
                        Vector3  R1      = Vector3.FromNormalId(yColumn);

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

                        // Generate the rotation matrix.
                        matrices[i] = 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();

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

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

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

                        matrices[i] = matrix;
                    }
                }

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

                var CFrames = new CFrame[instCount];

                for (int i = 0; i < instCount; i++)
                {
                    float[] matrix = matrices[i];

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

                    float[] components;

                    if (matrix.Length == 12)
                    {
                        matrix[0] = x;
                        matrix[1] = y;
                        matrix[2] = z;

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

                    CFrames[i] = new CFrame(components);
                }

                if (Type == PropertyType.OptionalCFrame)
                {
                    byte boolType = (byte)PropertyType.Bool;
                    byte readType = reader.ReadByte();

                    if (readType != boolType)
                    {
                        RobloxFile.LogError($"Unexpected property type in OptionalCFrame (expected {boolType}, got {readType})");
                        readProperties(i => null);
                        break;
                    }

                    for (int i = 0; i < instCount; i++)
                    {
                        CFrame cf         = CFrames[i];
                        bool   archivable = reader.ReadBoolean();

                        if (!archivable)
                        {
                            cf = null;
                        }

                        CFrames[i] = new Optional <CFrame>(cf);
                    }
                }

                readProperties(i => CFrames[i]);
                break;
            }

            case PropertyType.Enum:
            {
                uint[] enums = reader.ReadUInts(instCount);

                readProperties(i =>
                    {
                        Property prop     = props[i];
                        Instance instance = prop.Instance;

                        Type instType = instance.GetType();
                        uint value    = enums[i];

                        try
                        {
                            var info = ImplicitMember.Get(instType, Name);

                            if (info == null)
                            {
                                RobloxFile.LogError($"Enum cast failed for {ClassName}.{Name} using value {value}!");
                                return(value);
                            }

                            return(Enum.Parse(info.MemberType, value.ToInvariantString()));
                        }
                        catch
                        {
                            RobloxFile.LogError($"Enum cast failed for {ClassName}.{Name} using value {value}!");
                            return(value);
                        }
                    });

                break;
            }

            case PropertyType.Ref:
            {
                var instIds = reader.ReadInstanceIds(instCount);

                readProperties(i =>
                    {
                        int instId = instIds[i];

                        if (instId >= File.NumInstances)
                        {
                            RobloxFile.LogError($"Got out of bounds referent index in {ClassName}.{Name}!");
                            return(null);
                        }

                        return(instId >= 0 ? File.Instances[instId] : null);
                    });

                break;
            }

            case PropertyType.Vector3int16:
            {
                readProperties(i =>
                    {
                        short x = reader.ReadInt16(),
                        y       = reader.ReadInt16(),
                        z       = reader.ReadInt16();

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

                break;
            }

            case PropertyType.NumberSequence:
            {
                readProperties(i =>
                    {
                        int numKeys   = reader.ReadInt32();
                        var keypoints = new NumberSequenceKeypoint[numKeys];

                        for (int key = 0; key < numKeys; key++)
                        {
                            float Time = reader.ReadFloat(),
                            Value      = reader.ReadFloat(),
                            Envelope   = reader.ReadFloat();

                            keypoints[key] = new NumberSequenceKeypoint(Time, Value, Envelope);
                        }

                        return(new NumberSequence(keypoints));
                    });

                break;
            }

            case PropertyType.ColorSequence:
            {
                readProperties(i =>
                    {
                        int numKeys   = reader.ReadInt32();
                        var keypoints = new ColorSequenceKeypoint[numKeys];

                        for (int key = 0; key < numKeys; key++)
                        {
                            float Time = reader.ReadFloat(),
                            R          = reader.ReadFloat(),
                            G          = reader.ReadFloat(),
                            B          = reader.ReadFloat();

                            Color3 Value = new Color3(R, G, B);
                            int Envelope = reader.ReadInt32();

                            keypoints[key] = new ColorSequenceKeypoint(Time, Value, Envelope);
                        }

                        return(new ColorSequence(keypoints));
                    });

                break;
            }

            case PropertyType.NumberRange:
            {
                readProperties(i =>
                    {
                        float min = reader.ReadFloat();
                        float max = reader.ReadFloat();

                        return(new NumberRange(min, max));
                    });

                break;
            }

            case PropertyType.Rect:
            {
                float[] Rect_X0 = readFloats(), Rect_Y0 = readFloats(),
                Rect_X1 = readFloats(), Rect_Y1 = readFloats();

                readProperties(i =>
                    {
                        float x0 = Rect_X0[i], y0 = Rect_Y0[i],
                        x1       = Rect_X1[i], y1 = Rect_Y1[i];

                        return(new Rect(x0, y0, x1, y1));
                    });

                break;
            }

            case PropertyType.PhysicalProperties:
            {
                readProperties(i =>
                    {
                        bool custom = reader.ReadBoolean();

                        if (custom)
                        {
                            float Density    = reader.ReadFloat(),
                            Friction         = reader.ReadFloat(),
                            Elasticity       = reader.ReadFloat(),
                            FrictionWeight   = reader.ReadFloat(),
                            ElasticityWeight = reader.ReadFloat();

                            return(new PhysicalProperties
                                   (
                                       Density,
                                       Friction,
                                       Elasticity,
                                       FrictionWeight,
                                       ElasticityWeight
                                   ));
                        }

                        return(null);
                    });

                break;
            }

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

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

                        Color3uint8 result = Color3.FromRGB(r, g, b);
                        return(result);
                    });

                break;
            }

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

                readProperties(i => longs[i]);
                break;
            }

            case PropertyType.SharedString:
            {
                uint[] SharedKeys = reader.ReadUInts(instCount);

                readProperties(i =>
                    {
                        uint key = SharedKeys[i];
                        return(File.SharedStrings[key]);
                    });

                break;
            }

            case PropertyType.ProtectedString:
            {
                readProperties(i =>
                    {
                        int length    = reader.ReadInt32();
                        byte[] buffer = reader.ReadBytes(length);

                        return(new ProtectedString(buffer));
                    });

                break;
            }

            case PropertyType.UniqueId:
            {
                readProperties(i =>
                    {
                        var buffer = reader.ReadBytes(16);
                        return(new Guid(buffer));
                    });

                break;
            }

            default:
            {
                RobloxFile.LogError($"Unhandled property type: {Type} in {this}!");
                break;
            }
            }

            reader.Dispose();
        }
Example #3
0
        static void Main(string[] args)
        {
            Console.BackgroundColor = ConsoleColor.DarkBlue;
            Console.Clear();
            Console.WriteLine("> Welcome to plentiBlox");
            bool success = true;
            // Choose image path
            string imagePath = "";

            while (imagePath == "")
            {
                Console.WriteLine("> Locate the image you want to port over to Roblox");
                Console.WriteLine("> Example: C:\\Users\\Username\\Pictures\\image.png");
                string tempPath = Console.ReadLine();
                if (File.Exists(tempPath) == true)
                {
                    if (Path.GetExtension(tempPath) == ".png" || Path.GetExtension(tempPath) == ".jpg" || Path.GetExtension(tempPath) == ".jpeg" || Path.GetExtension(tempPath) == ".bmp")
                    {
                        imagePath = tempPath;
                    }
                    else
                    {
                        Console.BackgroundColor = ConsoleColor.DarkRed;
                        Console.ForegroundColor = ConsoleColor.Black;
                        Console.WriteLine("! The file you located isn't an image (or it is but doesn't use a supported format)");
                    }
                }
                else
                {
                    Console.BackgroundColor = ConsoleColor.DarkRed;
                    Console.ForegroundColor = ConsoleColor.Black;
                    Console.WriteLine("! The image you located doesn't exist");
                }
                Console.BackgroundColor = ConsoleColor.DarkBlue;
                Console.ForegroundColor = ConsoleColor.White;
            }

            //Choose path where to save the Roblox place file

            string rbxlPath = "";

            while (rbxlPath == "")
            {
                Console.WriteLine("> Locate where you would like the Roblox place file to be saved");
                Console.WriteLine("> Example: C:\\Users\\Username\\Documents\\place.rbxl");
                string tempPath = Console.ReadLine();
                if (File.Exists(tempPath) == false)
                {
                    if (Path.GetExtension(tempPath) == ".rbxl")
                    {
                        rbxlPath = tempPath;
                    }
                    else
                    {
                        Console.BackgroundColor = ConsoleColor.DarkRed;
                        Console.ForegroundColor = ConsoleColor.Black;
                        Console.WriteLine("! You must locate a file with the .rbxl extension");
                    }
                }
                else
                {
                    Console.BackgroundColor = ConsoleColor.DarkRed;
                    Console.ForegroundColor = ConsoleColor.Black;
                    Console.WriteLine("! The file located already exists");
                }
                Console.BackgroundColor = ConsoleColor.DarkBlue;
                Console.ForegroundColor = ConsoleColor.White;
            }

            // Choose compression level

            int compressionLevel = -1;
            int colorTolerance   = 0;

            while (compressionLevel == -1)
            {
                Console.WriteLine("> Choose compression level");
                Console.WriteLine("  > 0 - Uncompressed (Not recommended, use only if you have to edit individual pixels in Studio)");
                Console.WriteLine("  > 1 - Lossless compression (Compresses groups of pixels into bigger frames)");
                Console.WriteLine("  > 2 - Low lossy compression (Makes 2 similar colors next to eachother turn into 1 color) [Color Tolerance Level 5]");
                Console.WriteLine("  > 3 - Medium lossy compression (Low lossy compression but more tolerant) [Color Tolerance Level 20]");
                Console.WriteLine("  > 4 - High lossy compression (Medium lossy compression but more tolerant) [Color Tolerance Level 50]");
                Console.WriteLine("  > 5 - Custom lossy compression (Set color tolerance level to whatever you want)");
                int tempLevel = Convert.ToInt16(Console.ReadLine());
                if (tempLevel >= 0 && tempLevel <= 5)
                {
                    compressionLevel = tempLevel;
                    if (compressionLevel == 2)
                    {
                        colorTolerance = 5;
                    }
                    else if (compressionLevel == 3)
                    {
                        colorTolerance = 20;
                    }
                    else if (compressionLevel == 4)
                    {
                        colorTolerance = 50;
                    }
                    else if (compressionLevel == 5)
                    {
                        Console.WriteLine("> Choose your color tolerance level:");
                        tempLevel      = Convert.ToInt32(Console.ReadLine());
                        colorTolerance = tempLevel;
                    }
                }
                else
                {
                    Console.BackgroundColor = ConsoleColor.DarkRed;
                    Console.ForegroundColor = ConsoleColor.Black;
                    Console.WriteLine("! You must choose a number from 0 to 5");
                }
                Console.BackgroundColor = ConsoleColor.DarkBlue;
                Console.ForegroundColor = ConsoleColor.White;
            }

            Console.WriteLine("> Reading image file...");

            Bitmap bitmap = new Bitmap(imagePath);

            Console.WriteLine("> Creating Roblox place file...");

            // Create Roblox place file and such

            BinaryRobloxFile testFile = new BinaryRobloxFile();

            StarterGui starterGui = new StarterGui();

            starterGui.Parent = testFile;

            ScreenGui screenGui = new ScreenGui();

            screenGui.Name   = "plentiBlox";
            screenGui.Parent = starterGui;

            Frame imageFrame = new Frame();

            imageFrame.Name                   = "Image";
            imageFrame.Size                   = new UDim2(0, bitmap.Width, 0, bitmap.Height);
            imageFrame.BorderSizePixel        = 0;
            imageFrame.BackgroundTransparency = 1;
            imageFrame.Parent                 = screenGui;

            Console.WriteLine("> Porting over image...");

            // Create frames for each pixel/group of pixels depending on the compression level chosen

            Frame[,] pixelsContainer = new Frame[bitmap.Width, bitmap.Height];

            for (int i = 0; i < bitmap.Width; ++i)
            {
                for (int j = 0; j < bitmap.Height; ++j)
                {
                    Color pixelColor = bitmap.GetPixel(i, j);

                    if (pixelColor.A == 0)
                    {
                        continue;
                    }

                    if (i > 0 && compressionLevel >= 1)
                    {
                        Color previousPixelColor = bitmap.GetPixel(i - 1, j);
                        if (previousPixelColor == pixelColor || isCloseColor(previousPixelColor, pixelColor, colorTolerance) == true)
                        {
                            if (colorTolerance > 0)
                            {
                                Color avgColor = getAverageColor(pixelColor, previousPixelColor);
                                pixelsContainer[i - 1, j].BackgroundColor3       = Color3.FromRGB(avgColor.R, avgColor.G, avgColor.B);
                                pixelsContainer[i - 1, j].BackgroundTransparency = 1f - (float)avgColor.A / 255f;
                            }
                            pixelsContainer[i - 1, j].Size = new UDim2(0, pixelsContainer[i - 1, j].Size.X.Offset + 1, 0, pixelsContainer[i - 1, j].Size.Y.Offset);
                            pixelsContainer[i, j]          = pixelsContainer[i - 1, j];
                            continue;
                        }
                    }

                    Frame pixel = new Frame();
                    pixel.Name                   = "Pixel(" + i + "," + j + ")";
                    pixel.BorderSizePixel        = 0;
                    pixel.Size                   = new UDim2(0, 1, 0, 1);
                    pixel.Position               = new UDim2(0, i, 0, j);
                    pixel.BackgroundColor3       = Color3.FromRGB(pixelColor.R, pixelColor.G, pixelColor.B);
                    pixel.BackgroundTransparency = 1f - (float)pixelColor.A / 255f;
                    pixel.Parent                 = imageFrame;

                    pixelsContainer[i, j] = pixel;
                }
            }

            if (compressionLevel >= 1)
            {
                for (int i = 0; i < bitmap.Width; ++i)
                {
                    for (int j = 0; j < bitmap.Height - 1; ++j)
                    {
                        if (pixelsContainer[i, j] != null && pixelsContainer[i, j + 1] != null && i == pixelsContainer[i, j].Position.X.Offset && pixelsContainer[i, j].Position.X.Offset == pixelsContainer[i, j + 1].Position.X.Offset && pixelsContainer[i, j].Size.X.Offset == pixelsContainer[i, j + 1].Size.X.Offset)
                        {
                            Color pixelColor     = bitmap.GetPixel(i, j);
                            Color nextPixelColor = bitmap.GetPixel(i, j + 1);
                            if (pixelColor != nextPixelColor || isCloseColor(nextPixelColor, pixelColor, colorTolerance) == false)
                            {
                                continue;
                            }
                            if (colorTolerance > 0)
                            {
                                Color avgColor = getAverageColor(pixelColor, nextPixelColor);
                                pixelsContainer[i, j].BackgroundColor3       = Color3.FromRGB(avgColor.R, avgColor.G, avgColor.B);
                                pixelsContainer[i, j].BackgroundTransparency = 1f - (float)avgColor.A / 255f;
                            }
                            pixelsContainer[i, j].Size       = new UDim2(0, pixelsContainer[i, j].Size.X.Offset, 0, pixelsContainer[i, j].Size.Y.Offset + 1);
                            pixelsContainer[i, j + 1].Parent = null;
                            pixelsContainer[i, j + 1]        = pixelsContainer[i, j];
                        }
                    }
                }
            }

            Console.WriteLine("> Saving Roblox place file...");

            //Catching exception just in case saving isn't allowed

            try
            {
                FileStream stream = File.OpenWrite(rbxlPath);
                testFile.Save(stream);
            }
            catch (Exception e)
            {
                success = false;
                Console.BackgroundColor = ConsoleColor.DarkRed;
                Console.ForegroundColor = ConsoleColor.Black;
                Console.WriteLine("! " + e.Message);
                Console.BackgroundColor = ConsoleColor.DarkBlue;
                Console.ForegroundColor = ConsoleColor.White;
            }

            // End of the program

            if (success == true)
            {
                Console.BackgroundColor = ConsoleColor.DarkGreen;
                Console.WriteLine("> Done! Press any key to open the Roblox place file");
                Console.BackgroundColor = ConsoleColor.DarkBlue;
                Console.ReadKey();
                Process.Start(rbxlPath);
            }
            else
            {
                Console.BackgroundColor = ConsoleColor.DarkRed;
                Console.ForegroundColor = ConsoleColor.Black;
                Console.WriteLine("! Porting failed! Please try again.");
                Console.BackgroundColor = ConsoleColor.DarkBlue;
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("> Press any key to exit");
                Console.ReadKey();
            }
        }
Example #4
0
        public void Load(BinaryRobloxFileReader reader)
        {
            BinaryRobloxFile file = reader.File;

            ClassIndex = reader.ReadInt32();
            Name       = reader.ReadString();

            byte propType = reader.ReadByte();

            Type = (PropertyType)propType;

            INST inst = file.Classes[ClassIndex];

            ClassName = inst.ClassName;

            Property[] props = new Property[inst.NumInstances];

            var ids       = inst.InstanceIds;
            int instCount = inst.NumInstances;

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

                Property prop = new Property(instance, this);
                props[i] = prop;

                instance.AddProperty(ref prop);
            }

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

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

            switch (Type)
            {
            case PropertyType.String:
                readProperties(i =>
                {
                    string value = reader.ReadString();

                    // Leave an access point for the original byte sequence, in case this is a BinaryString.
                    // This will allow the developer to read the sequence without any mangling from C# strings.
                    byte[] buffer      = reader.GetLastStringBuffer();
                    props[i].RawBuffer = buffer;

                    // Check if this is going to be casted as a BinaryString.
                    // BinaryStrings should use a type of byte[] instead.

                    if (Name == "AttributesSerialize")
                    {
                        return(buffer);
                    }

                    Property prop     = props[i];
                    Instance instance = prop.Instance;

                    Type instType   = instance.GetType();
                    FieldInfo field = instType.GetField(Name);

                    if (field != null)
                    {
                        object result  = value;
                        Type fieldType = field.FieldType;

                        if (fieldType == typeof(byte[]))
                        {
                            result = buffer;
                        }

                        return(result);
                    }
                    else
                    {
                        return(value);
                    }
                });

                break;

            case PropertyType.Bool:
                readProperties(i => reader.ReadBoolean());
                break;

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

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

            case PropertyType.Double:
                readProperties(i => reader.ReadDouble());
                break;

            case PropertyType.UDim:
                float[] UDim_Scales  = readFloats();
                int[]   UDim_Offsets = readInts();

                readProperties(i =>
                {
                    float scale = UDim_Scales[i];
                    int offset  = UDim_Offsets[i];
                    return(new UDim(scale, offset));
                });

                break;

            case PropertyType.UDim2:
                float[] UDim2_Scales_X = readFloats(),
                UDim2_Scales_Y = readFloats();

                int[] UDim2_Offsets_X = readInts(),
                UDim2_Offsets_Y = readInts();

                readProperties(i =>
                {
                    float scaleX = UDim2_Scales_X[i],
                    scaleY       = UDim2_Scales_Y[i];

                    int offsetX = UDim2_Offsets_X[i],
                    offsetY     = UDim2_Offsets_Y[i];

                    return(new UDim2(scaleX, offsetX, scaleY, offsetY));
                });

                break;

            case PropertyType.Ray:
                readProperties(i =>
                {
                    float posX = reader.ReadFloat(),
                    posY       = reader.ReadFloat(),
                    posZ       = reader.ReadFloat();

                    float dirX = reader.ReadFloat(),
                    dirY       = reader.ReadFloat(),
                    dirZ       = reader.ReadFloat();

                    Vector3 origin    = new Vector3(posX, posY, posZ);
                    Vector3 direction = new Vector3(dirX, dirY, dirZ);

                    return(new Ray(origin, direction));
                });

                break;

            case PropertyType.Faces:
                readProperties(i =>
                {
                    byte faces = reader.ReadByte();
                    return((Faces)faces);
                });

                break;

            case PropertyType.Axes:
                readProperties(i =>
                {
                    byte axes = reader.ReadByte();
                    return((Axes)axes);
                });

                break;

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

                readProperties(i =>
                {
                    int number = BrickColorIds[i];
                    return(BrickColor.FromNumber(number));
                });

                break;

            case PropertyType.Color3:
                float[] Color3_R = readFloats(),
                Color3_G = readFloats(),
                Color3_B = readFloats();

                readProperties(i =>
                {
                    float r = Color3_R[i],
                    g       = Color3_G[i],
                    b       = Color3_B[i];

                    return(new Color3(r, g, b));
                });

                break;

            case PropertyType.Vector2:
                float[] Vector2_X = readFloats(),
                Vector2_Y = readFloats();

                readProperties(i =>
                {
                    float x = Vector2_X[i],
                    y       = Vector2_Y[i];

                    return(new Vector2(x, y));
                });

                break;

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

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

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

                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.
                float[][] matrices = new float[instCount][];

                for (int i = 0; i < instCount; i++)
                {
                    byte rawOrientId = reader.ReadByte();

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

                        NormalId xColumn = (NormalId)(orientId / 6);
                        Vector3  R0      = Vector3.FromNormalId(xColumn);

                        NormalId yColumn = (NormalId)(orientId % 6);
                        Vector3  R1      = Vector3.FromNormalId(yColumn);

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

                        // Generate the rotation matrix.
                        matrices[i] = 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();
                        matrices[i] = rotation.GetComponents();
                    }
                    else
                    {
                        float[] matrix = new float[9];

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

                        matrices[i] = matrix;
                    }
                }

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

                readProperties(i =>
                {
                    float[] matrix = matrices[i];

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

                    float[] components;

                    if (matrix.Length == 12)
                    {
                        matrix[0] = x;
                        matrix[1] = y;
                        matrix[2] = z;

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

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

                break;

            case PropertyType.Enum:
                uint[] enums = reader.ReadUInts(instCount);

                readProperties(i =>
                {
                    Property prop     = props[i];
                    Instance instance = prop.Instance;

                    Type instType = instance.GetType();
                    uint value    = enums[i];

                    try
                    {
                        FieldInfo info = instType.GetField(Name, Property.BindingFlags);
                        return(Enum.Parse(info.FieldType, value.ToInvariantString()));
                    }
                    catch
                    {
                        //Console.WriteLine($"Enum cast failed for {inst.ClassName}.{Name} using value {value}!"); pretty annoying for output
                        return(value);
                    }
                });

                break;

            case PropertyType.Ref:
                var instIds = reader.ReadInstanceIds(instCount);

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

                break;

            case PropertyType.Vector3int16:
                readProperties(i =>
                {
                    short x = reader.ReadInt16(),
                    y       = reader.ReadInt16(),
                    z       = reader.ReadInt16();

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

                break;

            case PropertyType.NumberSequence:
                readProperties(i =>
                {
                    int numKeys   = reader.ReadInt32();
                    var keypoints = new NumberSequenceKeypoint[numKeys];

                    for (int key = 0; key < numKeys; key++)
                    {
                        float Time = reader.ReadFloat(),
                        Value      = reader.ReadFloat(),
                        Envelope   = reader.ReadFloat();

                        keypoints[key] = new NumberSequenceKeypoint(Time, Value, Envelope);
                    }

                    return(new NumberSequence(keypoints));
                });

                break;

            case PropertyType.ColorSequence:
                readProperties(i =>
                {
                    int numKeys   = reader.ReadInt32();
                    var keypoints = new ColorSequenceKeypoint[numKeys];

                    for (int key = 0; key < numKeys; key++)
                    {
                        float Time = reader.ReadFloat(),
                        R          = reader.ReadFloat(),
                        G          = reader.ReadFloat(),
                        B          = reader.ReadFloat();

                        Color3 Value = new Color3(R, G, B);
                        int Envelope = reader.ReadInt32();

                        keypoints[key] = new ColorSequenceKeypoint(Time, Value, Envelope);
                    }

                    return(new ColorSequence(keypoints));
                });

                break;

            case PropertyType.NumberRange:
                readProperties(i =>
                {
                    float min = reader.ReadFloat();
                    float max = reader.ReadFloat();

                    return(new NumberRange(min, max));
                });

                break;

            case PropertyType.Rect:
                float[] Rect_X0 = readFloats(), Rect_Y0 = readFloats(),
                Rect_X1 = readFloats(), Rect_Y1 = readFloats();

                readProperties(i =>
                {
                    float x0 = Rect_X0[i], y0 = Rect_Y0[i],
                    x1       = Rect_X1[i], y1 = Rect_Y1[i];

                    return(new Rect(x0, y0, x1, y1));
                });

                break;

            case PropertyType.PhysicalProperties:
                readProperties(i =>
                {
                    bool custom = reader.ReadBoolean();

                    if (custom)
                    {
                        float Density    = reader.ReadFloat(),
                        Friction         = reader.ReadFloat(),
                        Elasticity       = reader.ReadFloat(),
                        FrictionWeight   = reader.ReadFloat(),
                        ElasticityWeight = reader.ReadFloat();

                        return(new PhysicalProperties
                               (
                                   Density,
                                   Friction,
                                   Elasticity,
                                   FrictionWeight,
                                   ElasticityWeight
                               ));
                    }

                    return(null);
                });

                break;

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

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

                    Color3uint8 result = Color3.FromRGB(r, g, b);
                    return(result);
                });

                break;

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

                readProperties(i => Int64s[i]);
                break;

            case PropertyType.SharedString:
                uint[] SharedKeys = reader.ReadUInts(instCount);

                readProperties(i =>
                {
                    uint key = SharedKeys[i];
                    return(file.SharedStrings[key]);
                });

                break;

            case PropertyType.ProtectedString:
                readProperties(i =>
                {
                    int length    = reader.ReadInt32();
                    byte[] buffer = reader.ReadBytes(length);

                    return(new ProtectedString(buffer));
                });

                break;

            default:
                Console.Error.WriteLine("Unhandled property type: {0}!", Type);
                break;
                //
            }

            reader.Dispose();
        }