public SetObject(SetObjectType type, string typeName, uint objID)
        {
            ObjectType = typeName;
            ObjectID   = objID;

            foreach (var param in type.Parameters)
            {
                if (param is SetObjectTypeParamGroup group)
                {
                    var g           = new SetObjectParamGroup(group.Padding);
                    var groupParams = g.Parameters;

                    foreach (var p in group.Parameters)
                    {
                        groupParams.Add(new SetObjectParam(p.DataType,
                                                           p.DefaultValue));
                    }

                    Parameters.Add(g);
                    continue;
                }

                Parameters.Add(new SetObjectParam(param.DataType,
                                                  param.DefaultValue));
            }
        }
        public void ImportXML(Stream fileStream)
        {
            // Load XML and add loaded data to set data
            var  xml   = XDocument.Load(fileStream);
            uint objID = 0; // For Object elements with no ID attribute.

            foreach (var objElem in xml.Root.Elements("Object"))
            {
                // Generate Object
                var typeAttr  = objElem.Attribute("type");
                var objIDAttr = objElem.Attribute("id");
                if (typeAttr == null)
                {
                    continue;
                }

                var obj = new SetObject()
                {
                    ObjectType = typeAttr.Value,
                    ObjectID   = (objIDAttr == null) ?
                                 objID : Convert.ToUInt32(objIDAttr.Value),
                };

                // Assign CustomData to Object
                var customDataElem = objElem.Element("CustomData");
                if (customDataElem != null)
                {
                    foreach (var customData in customDataElem.Elements())
                    {
                        obj.CustomData.Add(customData.Name.LocalName,
                                           LoadParam(customData));
                    }
                }

                // Assign Parameters to Object
                var parametersElem = objElem.Element("Parameters");
                if (parametersElem != null)
                {
                    foreach (var paramElem in parametersElem.Elements())
                    {
                        obj.Parameters.Add(LoadParam(paramElem));
                    }
                }

                // Assign Transforms to Object
                var transformsElem = objElem.Element("Transforms");
                if (transformsElem != null)
                {
                    var transforms     = transformsElem.Elements("Transform");
                    int transformCount = transforms.Count();

                    if (transformCount > 0)
                    {
                        uint i = 0;
                        obj.Children = new SetObjectTransform[transformCount - 1];

                        foreach (var transformElem in transforms)
                        {
                            var transform = LoadTransform(transformElem);
                            if (i > 0)
                            {
                                obj.Children[i - 1] = transform;
                            }
                            else
                            {
                                obj.Transform = transform;
                            }

                            ++i;
                        }
                    }
                }

                ++objID;
                Objects.Add(obj);
            }

            // Sub-Methods
            SetObjectParam LoadParam(XElement paramElem)
            {
                // Groups
                var dataTypeAttr = paramElem.Attribute("type");

                if (dataTypeAttr == null)
                {
                    var  padAttr = paramElem.Attribute("padding");
                    uint?padding = null;

                    if (uint.TryParse(padAttr?.Value, out var pad))
                    {
                        padding = pad;
                    }

                    var group      = new SetObjectParamGroup(padding);
                    var parameters = group.Parameters;

                    foreach (var param in paramElem.Elements())
                    {
                        parameters.Add(LoadParam(param));
                    }

                    return(group);
                }

                // Parameters
                var    dataType = Types.GetTypeFromString(dataTypeAttr.Value);
                object data     = null;

                if (dataType == typeof(Vector2))
                {
                    data = Helpers.XMLReadVector2(paramElem);
                }
                else if (dataType == typeof(Vector3))
                {
                    data = Helpers.XMLReadVector3(paramElem);
                }
                else if (dataType == typeof(Vector4))
                {
                    data = Helpers.XMLReadVector4(paramElem);
                }
                else if (dataType == typeof(Quaternion))
                {
                    data = Helpers.XMLReadQuat(paramElem);
                }
                else if (dataType == typeof(uint[]))
                {
                    var  countAttr = paramElem.Attribute("count");
                    uint arrLength = 0;

                    if (countAttr != null)
                    {
                        uint.TryParse(countAttr.Value, out arrLength);
                    }

                    var values = paramElem.Value.Split(',');
                    var arr    = new uint[arrLength];
                    for (uint i = 0; i < arrLength; ++i)
                    {
                        if (i >= values.Length)
                        {
                            break;
                        }

                        uint.TryParse(values[i], out arr[i]);
                    }

                    data = arr;
                }
                else if (dataType == typeof(ForcesSetData.ObjectReference[]))
                {
                    var  countAttr = paramElem.Attribute("count");
                    uint arrLength = 0;

                    if (countAttr != null)
                    {
                        uint.TryParse(countAttr.Value, out arrLength);
                    }

                    uint i   = 0;
                    var  arr = new ForcesSetData.ObjectReference[arrLength];

                    foreach (var refElem in paramElem.Elements("ForcesObjectReference"))
                    {
                        var objRef = new ForcesSetData.ObjectReference();
                        objRef.ImportXML(refElem);
                        arr[i] = objRef;
                        ++i;
                    }

                    data = arr;
                }
                else if (dataType == typeof(ForcesSetData.ObjectReference))
                {
                    var objRef = new ForcesSetData.ObjectReference();
                    objRef.ImportXML(paramElem);
                    data = objRef;
                }
                else
                {
                    data = Convert.ChangeType(paramElem.Value, dataType);
                }

                return(new SetObjectParam(dataType, data));
            }

            SetObjectTransform LoadTransform(XElement elem)
            {
                var posElem   = elem.Element("Position");
                var rotElem   = elem.Element("Rotation");
                var scaleElem = elem.Element("Scale");

                return(new SetObjectTransform()
                {
                    Position = Helpers.XMLReadVector3(posElem),
                    Rotation = Helpers.XMLReadQuat(rotElem),
                    Scale = Helpers.XMLReadVector3(scaleElem)
                });
            }
        }
        protected SetObjectParam ReadParameter(BINAReader reader, SetObjectTypeParam param)
        {
            FixPadding(reader, param.DataType);

            // Special Param Types
            if (param is SetObjectTypeParamGroup group)
            {
                var g           = new SetObjectParamGroup(group.Padding);
                var groupParams = g.Parameters;

                foreach (var p in group.Parameters)
                {
                    groupParams.Add(ReadParameter(reader, p));
                }

                reader.FixPadding(group.Padding ?? 16);
                return(g);
            }
            else if (param.DataType == typeof(ObjectReference[]))
            {
                long  arrOffset  = reader.ReadInt64();
                ulong arrLength  = reader.ReadUInt64();
                ulong arrLength2 = reader.ReadUInt64();
                long  curPos     = reader.BaseStream.Position;

                if (arrLength != arrLength2)
                {
                    Console.WriteLine(
                        "WARNING: ArrLength ({0}) != ArrLength2 ({1})",
                        arrLength, arrLength2);
                }

                var arr = new ObjectReference[arrLength];
                if (arrLength > 0 && arrOffset > 0)
                {
                    reader.JumpTo(arrOffset, false);
                    for (uint i = 0; i < arrLength; ++i)
                    {
                        arr[i] = new ObjectReference(reader);
                    }

                    reader.JumpTo(curPos);
                }

                return(new SetObjectParam(param.DataType, arr));
            }
            else if (param.DataType == typeof(ObjectReference))
            {
                return(new SetObjectParam(typeof(ObjectReference),
                                          new ObjectReference(reader)));
            }
            else if (param.DataType == typeof(string))
            {
                var  stringParam   = new SetObjectParam(typeof(string), string.Empty);
                long offset        = reader.ReadInt64();
                long stringPadding = reader.ReadInt64();

                if (offset > 0)
                {
                    long curPos = reader.BaseStream.Position;
                    reader.JumpTo(offset, false);
                    stringParam.Data = reader.ReadNullTerminatedString();
                    reader.JumpTo(curPos);
                }

                if (stringPadding != 0)
                {
                    Console.WriteLine("WARNING: String Padding != 0 ({0:X})!!", stringPadding);
                }

                //reader.FixPadding(16);
                return(stringParam);
            }

            // Data
            var objParam = new SetObjectParam(param.DataType,
                                              reader.ReadByType(param.DataType));

            // Post-Param Padding
            if (param.DataType == typeof(Vector3))
            {
                uint vecPadding = reader.ReadUInt32();
                if (vecPadding != 0)
                {
                    Console.WriteLine("WARNING: Vector Padding != 0 ({0:X})!!", vecPadding);
                }
            }

            return(objParam);
        }