//*************************************************************
//  Filename:       XurReader.cs
//  Author:         MaesterRowen & Wondro (TeamFSD)
//  Date:           August 10, 2013
//  Description:    Class used to read XURv8 binaries.
//*************************************************************

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
using System.Diagnostics;
using System.Windows.Forms;

namespace XuiWorkshop
{
    public class XurReader : BEBinaryReader
    {
        // XUIBinaryTables Struct holds all of our table information
        private struct XUIBinaryTables
        {
            public List<string> StringTable;
            public List<XUIVECTOR> VectorTable;
            public List<XUIQUATERNION> QuaternionTable;
            public List<float> FloatTable;
            public List<XUICOLOR> ColorTable;
            public List<XUICUSTOM> CustomTable;

            public static XUIBinaryTables Initialize()
            {
                XUIBinaryTables bT = new XUIBinaryTables();
                bT.StringTable = new List<string>();
                bT.VectorTable = new List<XUIVECTOR>();
                bT.QuaternionTable = new List<XUIQUATERNION>();
                bT.FloatTable = new List<float>();
                bT.ColorTable = new List<XUICOLOR>();
                bT.CustomTable = new List<XUICUSTOM>();

                return bT;
            }
        }
        private struct XUIAnimationTables
        {
            public List<XUINAMEDFRAMEDATA> NamedFrameTable;
            public List<XUIKEYFRAMEPROPDATA> KeyframePropertyTable;
            public List<XUIKEYFRAMEDATA> KeyframeDataTable;

            public static XUIAnimationTables Initialize()
            {
                XUIAnimationTables aT = new XUIAnimationTables();

                aT.NamedFrameTable = new List<XUINAMEDFRAMEDATA>();
                aT.KeyframePropertyTable = new List<XUIKEYFRAMEPROPDATA>();
                aT.KeyframeDataTable = new List<XUIKEYFRAMEDATA>();

                return aT;
            }
        }

        private struct XUILoadContext
        {
            public uint ObjectCount;
            public uint MissingPropID;
            public List<XUIOBJECTDATA> ObjectArray;
            public List<XUIPROPERTYDATA> PropertyArray;
            public List<IList> ObjectPropArray;
            public List<XUICOMPOUNDPROPDATA> CompoundPropArray;
            static public XUILoadContext Initialize()
            {
                XUILoadContext lC = new XUILoadContext();

                lC.ObjectCount = 0;
                lC.MissingPropID = 0;
                lC.ObjectArray = new List<XUIOBJECTDATA>();
                lC.PropertyArray = new List<XUIPROPERTYDATA>();
                lC.ObjectPropArray = new List<IList>();
                lC.CompoundPropArray = new List<XUICOMPOUNDPROPDATA>();

                return lC;
            }
        }

        private XUIBinaryTables BinaryTables;
        private XUIAnimationTables AnimationTables;
        private XUILoadContext LoadContext;

        // Method to intializing our reader object
        public XurReader(System.IO.Stream stream) : base(stream) { }

        public XUR8_HEADER GetHeaderData()
        {
            // Reset our stream to the beginning of the file
            BaseStream.Seek(0, SeekOrigin.Begin);

            // Load our header and check our information for validity
            XUR8_HEADER header = ReadHeader();
            Debug.Assert(header.Magic == Constants.Xur8.Magic, "Binary Magic Field != 'XUIB'");
            // We handle these errors in main form
            //Debug.Assert(header.Version == Constants.Xur8.Version, "Binary Version Field != 0x8");
            //Debug.Assert(header.XuiVersion == Constants.Xur8.XuiVersion, "Binary XuiVersion Field != 0xE");
            Debug.Assert(header.NumSections >= 2, "Binary NumSections <= 2", "Requires at least 'STRN' and 'DATA' sections");

            return header;
        }

        public XUR8_HEADER_INFO GetHeaderInfoData()
        {
            // Reset our stream to the beginning of the file
            BaseStream.Seek(0, SeekOrigin.Begin);

            // Load our header to move file forward
            XUR8_HEADER header = ReadHeader();

            // Load our headerInfo struct
            XUR8_HEADER_INFO headerInfo = ReadHeaderInfo();

            return headerInfo;
        }

        public XUIOBJECTDATA LoadObjectsFromBinary()
        {  
            // Reset our stream to the beginning of the file
            BaseStream.Seek(0, SeekOrigin.Begin);

            // Initialize our structs
            BinaryTables = XUIBinaryTables.Initialize();
            AnimationTables = XUIAnimationTables.Initialize();
            LoadContext = XUILoadContext.Initialize();
            
            // Load our header and check our information for validity
            XUR8_HEADER header = ReadHeader();
            Debug.Assert(header.Magic == 0x58554942, "Binary Magic Field != 'XUIB'");
            Debug.Assert(header.Version == 0x8, "Binary Version Field != 0x8");
            Debug.Assert(header.XuiVersion == 0xE, "Binary XuiVersion Field != 0xE");
            Debug.Assert(header.NumSections >= 2, "Binary NumSections <= 2", "Requires at least 'STRN' and 'DATA' sections");

            // Load our headerInfo struct (not really used here, but need to move file forward)
            XUR8_HEADER_INFO headerInfo = ReadHeaderInfo();         

            // Load our sections
            XUR8_SECTION [] sectionInfo = new XUR8_SECTION[(int)XUR8_SECTION_NAME.SECTION_COUNT];
            for (int sectionIdx = 0; sectionIdx < header.NumSections; sectionIdx++)
            {
                // Read the next section in the file
                XUR8_SECTION currentSection = ReadSectionInfo();

                switch (currentSection.Name)
                {
                    case 0x4B455944:  //KEYD
                        sectionInfo[(int)XUR8_SECTION_NAME.SECTION_KEYD] = currentSection;
                        break;
                    case 0x434F4C52:  //COLR
                        sectionInfo[(int)XUR8_SECTION_NAME.SECTION_COLR] = currentSection;
                        break;
                    case 0x43555354:  //CUST
                        sectionInfo[(int)XUR8_SECTION_NAME.SECTION_CUST] = currentSection;
                        break;
                    case 0x44415441:  //DATA
                        sectionInfo[(int)XUR8_SECTION_NAME.SECTION_DATA] = currentSection;
                        break;
                    case 0x44425547:  //DBUG
                        sectionInfo[(int)XUR8_SECTION_NAME.SECTION_DBUG] = currentSection;
                        break;
                    case 0x464C4F54:  //FLOT
                        sectionInfo[(int)XUR8_SECTION_NAME.SECTION_FLOT] = currentSection;
                        break;
                    case 0x4B455950:  //KEYP
                        sectionInfo[(int)XUR8_SECTION_NAME.SECTION_KEYP] = currentSection;
                        break;
                    case 0x4E414D45:  //NAME
                        sectionInfo[(int)XUR8_SECTION_NAME.SECTION_NAME] = currentSection;
                        break;
                    case 0x51554154:  //QUAT
                        sectionInfo[(int)XUR8_SECTION_NAME.SECTION_QUAT] = currentSection;
                        break;
                    case 0x5354524E:  //STRN
                        sectionInfo[(int)XUR8_SECTION_NAME.SECTION_STRN] = currentSection;
                        break;
                    case 0x56454354:  //VECT
                        sectionInfo[(int)XUR8_SECTION_NAME.SECTION_VECT] = currentSection;
                        break;
                }
            }

            // Check that we have the STRN section, then DATA
            Debug.Assert(sectionInfo[(int)XUR8_SECTION_NAME.SECTION_STRN].Name != 0, "No STRN section found");
            Debug.Assert(sectionInfo[(int)XUR8_SECTION_NAME.SECTION_DATA].Name != 0, "No DATA section found");
            
            // Load our binary tables
            if (LoadBinaryTables(sectionInfo) == ERROR.S_ERROR)
                return null;

            // Load our Animation Tables
            if (LoadAnimationTables(sectionInfo) == ERROR.S_ERROR)
                return null;
       
            // Load our Object Data next
            BaseStream.Position = sectionInfo[(int)XUR8_SECTION_NAME.SECTION_DATA].Offset;
            XUIOBJECTDATA rootObject = new XUIOBJECTDATA();
            XUIOBJECTDATA dummyObject = new XUIOBJECTDATA();
            if (LoadObjectFromBinary(ref rootObject, ref dummyObject) == ERROR.S_OK)
                return rootObject;
            else
                return null;
        }
        private ERROR LoadObjectFromBinary(ref XUIOBJECTDATA baseObjectData, ref XUIOBJECTDATA parentData )
        {
            baseObjectData.ObjectId = LoadContext.ObjectCount;
            LoadContext.ObjectCount++;
            
            uint strId = ReadPackedUlong();
            uint flag = ReadByte();

            string className = (string)BinaryTables.StringTable[(int)strId];
            XUI_CLASS pClass = XuiClass.Instance.FindClass(className);

            baseObjectData.ClassName = className;
            baseObjectData.Flags = flag;

            // Start checking the objects flags
            if ((baseObjectData.Flags & 0x1) == (uint)0x1)
            {
                // Object has its own properties
                uint numProps = ReadPackedUlong();
                if (LoadObjectPropsFromBinary(className, numProps, ref baseObjectData.PropertyArray) == ERROR.S_ERROR)
                    return ERROR.S_ERROR;

                LoadContext.ObjectPropArray.Add(baseObjectData.PropertyArray);
            }
            else if ((baseObjectData.Flags & 0x8) == (uint)0x8)
            {
                // Loop through our object array and find a matching id
                uint objectIdx = ReadPackedUlong();
                baseObjectData.PropertyArray = (List<XUIPROPERTYDATA>)LoadContext.ObjectPropArray[(int)objectIdx];
            }

            // If oroperty Has no Id. Store Generic Id using class name and int
            if (baseObjectData.GetPropVal("Id").ToString() == "")
            {
                if (LoadContext.MissingPropID == 0)
                    baseObjectData.AddPropVal("Id", baseObjectData.ClassName);
                else
                    baseObjectData.AddPropVal("Id", baseObjectData.ClassName + LoadContext.MissingPropID);

                LoadContext.MissingPropID++;
            }

            // Store Heriarchy ID Path
            if (baseObjectData.ObjectId == 0)
                baseObjectData.HeriarchyId = baseObjectData.ClassName;
            else
                baseObjectData.HeriarchyId = parentData.HeriarchyId + '#' + baseObjectData.GetPropVal("Id").ToString();


            if ((baseObjectData.Flags & 0x2) == (uint)0x2)
            {
                // Object has children
                uint numChildren = ReadPackedUlong();
                for (uint childIdx = 0; childIdx < numChildren; childIdx++)
                {
                    XUIOBJECTDATA newChild = new XUIOBJECTDATA();

                    if (LoadObjectFromBinary(ref newChild, ref baseObjectData) == ERROR.S_ERROR)
                        return ERROR.S_ERROR;
                        
                    baseObjectData.ChildrenArray.Add(newChild);  
                }
            }

            if ((baseObjectData.Flags & 0x4) == (uint)0x4)
            {
                // Object has timelines
                LoadTimelineDataFromBinary(className, ref baseObjectData);
            }


            // Add this object to our array for later use
            LoadContext.ObjectArray.Add(baseObjectData);

            return ERROR.S_OK;
        }
        private ERROR LoadObjectPropsFromBinary(string className, uint numProps, ref List<XUIPROPERTYDATA> propertyData)
        {
            List<IList> classArray = XuiClass.Instance.GetHierarchy(className);
            // We are missing a class break out
            if (classArray == null)
                return ERROR.S_ERROR;

            foreach (List<XUIELEM_PROP_DEF> propArray in classArray)
            {
                // Load property data for each class 
                if (propArray != null)
                    LoadPropertyDataFromBinary(propArray, ref propertyData);
                else
                    Console.WriteLine("Prop Error in " + className + " Missing base class?");
            }

            return ERROR.S_OK;
        }
        private void LoadPropertyDataFromBinary(List<XUIELEM_PROP_DEF> propArray, ref List<XUIPROPERTYDATA> propertyData)
        {
            uint propMask = ReadPackedUlong();
            int propIndex = 0;
            foreach (XUIELEM_PROP_DEF propEntry in propArray)
            {
                // Check if this property is included
                int flag = 1 << propIndex;
                if (((propMask & flag) == (uint)flag))
                {
                    // Check if this property is indexed
                    uint indexCount = 0;
                    if ((propEntry.Flags & 0x1) == (uint)0x1)
                    {
                        // Indexed so read the number of indices
                        indexCount = ReadPackedUlong();
                    }
                    else
                    {
                        indexCount = 1;
                    }

                    // Loop through each index and add to array
                    for (uint curIdx = 0; curIdx < indexCount; curIdx++)
                    {
                        // Now we read our prop vals and add to our property data array
                        XUIPROPERTYDATA propData = new XUIPROPERTYDATA();
                        propData.Flags = propEntry.Flags;
                        propData.PropType = propEntry.Type;
                        propData.PropDef = propEntry;

                        // Update our index
                        propData.Index = curIdx;

                        // Load our value
                        if (propData.PropType == XUIELEM_PROP_TYPE.XUI_EPT_BOOL)
                        {
                            propData.PropValue = ReadByte() > 0 ? true : false;
                            propertyData.Add(propData);
                        }
                        else if (propData.PropType == XUIELEM_PROP_TYPE.XUI_EPT_INTEGER)
                        {
                            propData.PropValue = (int)(ReadPackedUlong());
                            propertyData.Add(propData);
                        }
                        else if (propData.PropType == XUIELEM_PROP_TYPE.XUI_EPT_UNSIGNED)
                        {
                            propData.PropValue = (uint)(ReadPackedUlong());
                            propertyData.Add(propData);
                        }
                        else if (propData.PropType == XUIELEM_PROP_TYPE.XUI_EPT_STRING)
                        {
                            uint stringIdx = (uint)(ReadPackedUlong());
                            propData.PropValue = (string)BinaryTables.StringTable[(int)stringIdx];
                            propertyData.Add(propData);
                        }
                        else if (propData.PropType == XUIELEM_PROP_TYPE.XUI_EPT_FLOAT)
                        {
                            uint floatIdx = (uint)ReadPackedUlong();
                            propData.PropValue = (float)BinaryTables.FloatTable[(int)floatIdx];
                            propertyData.Add(propData);
                        }
                        else if (propData.PropType == XUIELEM_PROP_TYPE.XUI_EPT_QUATERNION)
                        {
                            uint quatIdx = (uint)ReadPackedUlong();
                            propData.PropValue = (XUIQUATERNION)BinaryTables.QuaternionTable[(int)quatIdx];
                            propertyData.Add(propData);
                        }
                        else if (propData.PropType == XUIELEM_PROP_TYPE.XUI_EPT_VECTOR)
                        {
                            uint vecIdx = (uint)ReadPackedUlong();
                            propData.PropValue = (XUIVECTOR)BinaryTables.VectorTable[(int)vecIdx];
                            propertyData.Add(propData);
                        }
                        else if (propData.PropType == XUIELEM_PROP_TYPE.XUI_EPT_COLOR)
                        {
                            uint colIdx = (uint)ReadPackedUlong();
                            propData.PropValue = (XUICOLOR)BinaryTables.ColorTable[(int)colIdx];
                            propertyData.Add(propData);
                        }
                        else if (propData.PropType == XUIELEM_PROP_TYPE.XUI_EPT_CUSTOM)
                        {
                            uint cusOffset = (uint)ReadPackedUlong();
                            foreach (XUICUSTOM cust in BinaryTables.CustomTable)
                            {
                                if (cusOffset == cust.Offset)
                                {
                                    propData.PropValue = cust;
                                    propertyData.Add(propData);
                                }

                            }
                        }
                        else if (propData.PropType == XUIELEM_PROP_TYPE.XUI_EPT_OBJECT)
                        {
                            // First we read our compound prop id
                            uint compoundPropId = ReadPackedUlong();

                            // Now let's search our load context for an existing id
                            List<XUICOMPOUNDPROPDATA> compoundPropArray = LoadContext.CompoundPropArray;
                            bool foundMatch = false;
                            foreach (XUICOMPOUNDPROPDATA compoundPropData in compoundPropArray)
                            {
                                // Found a matching entry, so let's add all these properties to our new object
                                if (compoundPropData.Index == compoundPropId)
                                {
                                    foundMatch = true;
                                    List<XUIPROPERTYDATA> compoundprop = new List<XUIPROPERTYDATA>();
                                    foreach (XUIPROPERTYDATA pData in compoundPropData.PropertyArray)
                                    {
                                        compoundprop.Add(pData);
                                    }
                                    propData.PropValue = compoundprop;
                                    propertyData.Add(propData);
                                }

                                if (foundMatch == true) break;
                            }

                            if (foundMatch == false)
                            {
                                List<XUIELEM_PROP_DEF> subPropArray = new List<XUIELEM_PROP_DEF>();
                                switch (propData.PropDef.PropName)
                                {
                                    case "Fill":
                                        subPropArray = XuiClass.Instance.GetFillPropArray();
                                        break;
                                    case "Gradient":
                                        subPropArray = XuiClass.Instance.GetGradientPropArray();
                                        break;
                                    case "Stroke":
                                        subPropArray = XuiClass.Instance.GetStrokePropArray();
                                        break;
                                }

                                uint numProps = ReadPackedUlong();
                                List<XUIPROPERTYDATA> newcompoundprop = new List<XUIPROPERTYDATA>();
                                LoadPropertyDataFromBinary(subPropArray, ref newcompoundprop);
                                propData.PropValue = newcompoundprop;
                                propertyData.Add(propData);

                                // Finally let's add this new compound prop structure to our load context
                                XUICOMPOUNDPROPDATA cpd = new XUICOMPOUNDPROPDATA();
                                cpd.Index = compoundPropId;
                                cpd.PropertyArray = newcompoundprop;
                                LoadContext.CompoundPropArray.Add(cpd);
                            }
                        }
                    }
                }
                propIndex++;
            }
        }
        private void LoadTimelineDataFromBinary(string className, ref XUIOBJECTDATA objectData)
        {
            // Read our number of named frames and our array index
            uint numNamedFrames = ReadPackedUlong();
            if (numNamedFrames > 0)
            {
                uint namedFrameIdx = ReadPackedUlong();

                // Add our named frames into our objectData's named frame array
                objectData.NumNamedFrames = numNamedFrames;
                for (uint nfIdx = 0; nfIdx < numNamedFrames; nfIdx++)
                {
                    objectData.NamedFrameArray.Add( AnimationTables.NamedFrameTable[(int)(namedFrameIdx + nfIdx)] );
                }
            }

            // If number children is 0, no need to load timeline data
            if (objectData.ChildrenArray.Count == 0) return;


            uint numSubtimelines = ReadPackedUlong();
            objectData.NumSubTimelines = numSubtimelines;
            objectData.SubTimelines.Clear();
            // If we don't have any subtimelines, return
            if(objectData.NumSubTimelines == 0 ) return;

            for (uint subIdx = 0; subIdx < numSubtimelines; subIdx++)
            {
                XUISUBTIMELINEDATA timelineData = new XUISUBTIMELINEDATA();
                
                // Start of a new timeline
                uint idStringIdx = ReadPackedUlong();
                timelineData.ElementStringIdx = idStringIdx;
                timelineData.ElementId = (string)BinaryTables.StringTable[(int)idStringIdx];

                uint numPropPaths = ReadPackedUlong();
                timelineData.NumPropPaths = numPropPaths;
                for (uint ppIdx = 0; ppIdx < numPropPaths; ppIdx++)
                {
                    // Create a new proppath
                    XUITIMELINEPROPPATH proppathData = new XUITIMELINEPROPPATH();

                    uint indexVal = 0;
                    uint ret = LoadSubTimelinePropertiesFromBinary(timelineData.ElementId, ref proppathData, ref objectData, ref indexVal);

                    // Add 
                    timelineData.IndexArray.Add(indexVal);
                    proppathData.Index = indexVal;
                    timelineData.PropPathArray.Add(proppathData);
                    if (ret < 0) return;
                }

                uint numKeyframes = ReadPackedUlong();
                uint keyframeIdx = ReadPackedUlong();

                for (uint kfIdx = 0; kfIdx < numKeyframes; kfIdx++)
                {
                    XUIKEYFRAMEDATA keyframeData = (XUIKEYFRAMEDATA)AnimationTables.KeyframeDataTable[(int)(keyframeIdx + kfIdx)];

                    for (uint propIdx = 0; propIdx < numPropPaths; propIdx++)
                    {
                        // Grab our current KeyFramePropData
                        XUIKEYFRAMEPROPDATA keyframeProp = (XUIKEYFRAMEPROPDATA)AnimationTables.KeyframePropertyTable[(int)keyframeData.KeyframePropIdx + (int)propIdx];
                        // Grab a copy of our current TimelinePropPath and verify we have some definitions
                        XUITIMELINEPROPPATH propPath = (XUITIMELINEPROPPATH)timelineData.PropPathArray[(int)propIdx];
                        Debug.Assert(propPath.PropDefArray.Count != 0, "No property definitions");

                        // Creating a new property data element
                        XUIPROPERTYDATA propData = new XUIPROPERTYDATA();

                        // Set our propData's property defintion and type/flags from the definition
                        propData.PropDef = (XUIELEM_PROP_DEF)propPath.PropDefArray[propPath.PropDefArray.Count - 1];
                        propData.PropType = propData.PropDef.Type;
                        propData.Flags = propData.PropDef.Flags;
                        propData.Index = propPath.Index;  // (Index is defaulted to 0 and only used if Flags & 0x1 is true)
                            
                        if (propData.PropType == XUIELEM_PROP_TYPE.XUI_EPT_INTEGER || propData.PropType == XUIELEM_PROP_TYPE.XUI_EPT_UNSIGNED)
                        {
                            // Property value is just the value located at the keyframeprop index;
                            propData.PropValue = keyframeProp.PropertyIndex;
                        }
                        else if( propData.PropType == XUIELEM_PROP_TYPE.XUI_EPT_BOOL)
                        {
                            // Property value is just the value located at hte keyframeprop index cast to boolean
                            uint val = keyframeProp.PropertyIndex;
                            propData.PropValue = val == 0 ? "false" : "true";
                        }
                        else if (propData.PropType == XUIELEM_PROP_TYPE.XUI_EPT_STRING)
                        {
                            // Property value is the string value located at the index provided by the keyframe prop
                            propData.PropValue = (string)BinaryTables.StringTable[(int)keyframeProp.PropertyIndex];
                        }
                        else if (propData.PropType == XUIELEM_PROP_TYPE.XUI_EPT_FLOAT)
                        {
                            // Property value is the float value located at the index provided by the keyframe prop
                            propData.PropValue = (float)BinaryTables.FloatTable[(int)keyframeProp.PropertyIndex];
                        }
                        else if (propData.PropType == XUIELEM_PROP_TYPE.XUI_EPT_COLOR)
                        {
                            // Property value is the color value located at the index provided by the keyframe prop
                            propData.PropValue = (XUICOLOR)BinaryTables.ColorTable[(int)keyframeProp.PropertyIndex];
                        }
                        else if (propData.PropType == XUIELEM_PROP_TYPE.XUI_EPT_VECTOR)
                        {
                            // Property value is the vector value located at the index provided by the keyframe prop
                            propData.PropValue = (XUIVECTOR)BinaryTables.VectorTable[(int)keyframeProp.PropertyIndex];
                        }
                        else if (propData.PropType == XUIELEM_PROP_TYPE.XUI_EPT_QUATERNION)
                        {
                            // Property value is the quaternion value located at the index provided by the keyframe prop
                            propData.PropValue = (XUIQUATERNION)BinaryTables.QuaternionTable[(int)keyframeProp.PropertyIndex];
                        }
                        else if( propData.PropType == XUIELEM_PROP_TYPE.XUI_EPT_CUSTOM)
                        {
                            Debug.Assert( propData.PropType != XUIELEM_PROP_TYPE.XUI_EPT_CUSTOM, "PropType = XUI_EPT_CUSTOM; Unexpected Property Type" );
                        }
                        else if (propData.PropType == XUIELEM_PROP_TYPE.XUI_EPT_OBJECT)
                        {
                            // This property has depth, so we need to build our prop path
                            Debug.Assert(propData.PropType != XUIELEM_PROP_TYPE.XUI_EPT_OBJECT, "PropType = XUI_EPT_OBJECT; Unexpected Property Type");
                        }

                        // All done add this propData to our proplist
                        keyframeData.PropList.Add(propData);
                    }

                    timelineData.KeyframeDataArray.Add(keyframeData);
                }

                // Add this full timeline to our object
                objectData.SubTimelines.Add(timelineData);
            }
        }
        private uint LoadSubTimelinePropertiesFromBinary(string szId, ref XUITIMELINEPROPPATH proppathData, ref XUIOBJECTDATA objectData, ref uint indexVal )
        {
            // Read our first byte to determine our flags/depth
            byte bitPackedVar = ReadByte();
            proppathData.Flags |= 0x1;  // not necessary
            uint pathDepth = (uint)(bitPackedVar & 0x7F);
            uint upperBit = (uint)(bitPackedVar & 0x80);        // Determines whether this property is indexed

            proppathData.Depth = pathDepth;

            List<XUIOBJECTDATA> childArray = objectData.ChildrenArray;
            int numChildren = childArray.Count;
            if (numChildren <= 0) return (uint)0x8070000D;

            bool foundElement = false;
            string className = "";
            foreach (XUIOBJECTDATA childObj in childArray)
            {
                List<XUIPROPERTYDATA> propArray = childObj.PropertyArray;
                foreach (XUIPROPERTYDATA childProp in propArray)
                {
                    XUIELEM_PROP_DEF propDef = childProp.PropDef;
                    if (propDef.Type == XUIELEM_PROP_TYPE.XUI_EPT_STRING)
                    {
                        if (propDef.PropName == "Id")
                        {
                            if (childProp.PropValue.ToString() == szId)
                            {
                                foundElement = true;
                                className = childObj.ClassName;
                                break;
                            }
                        }
                    }
                }
                // if we found our element, we can break here too
                if (foundElement == true) break;
            }

            // If we didnt find our element in the child array
            if (foundElement == false) return 0x8070000E;

            // Set up our property list for this class
            XUI_CLASS pClass = XuiClass.Instance.FindClass(className);
            if (pClass.szClassName == null) return 0x80004005;

            XUIELEM_PROP_DEF propDeff = new XUIELEM_PROP_DEF();
            if (pathDepth != 0)
            {
                byte depth = ReadByte();
                byte index = ReadByte();

                // get prop from depth and index
                List<IList> classList = XuiClass.Instance.GetHierarchy(className);
                if (classList.Count == 0) return 0x80070057;
                classList.Reverse();

                List<XUIELEM_PROP_DEF> propList = (List<XUIELEM_PROP_DEF>)classList[(int)depth];
                propDeff = propList[index];
                proppathData.PropDefArray.Add(propDeff);
            }

            // Check if we need to handle compound properties
            if (pathDepth > 1)  //(!pathDepth <= 1)
            {
                for (uint cpd = 0; cpd < (pathDepth - 1); cpd++)
                {
                    // This has to be type OBJECT
                    Debug.Assert(propDeff.Type == XUIELEM_PROP_TYPE.XUI_EPT_OBJECT, "Invalid Prop Type");

                    // Grab the new array list
                    List<XUIELEM_PROP_DEF> propList = propDeff.GetPropDefs();
                    
                    // Load the next byte
                    uint propIdx = ReadByte();
                    propDeff = propList[(int)propIdx];
                    proppathData.PropDefArray.Add(propDeff);
                }
            }

            // set up our indexVal
            if (upperBit > 0)
            {
                uint idxVal = ReadPackedUlong();
                indexVal = idxVal;
            }

            // return successfully
            return 0;
        }
        public uint ReadPackedUlong()
        {
            // Read the next byte in our stream
            uint firstVal = ReadByte();

            uint finalResult = 0;
            if (firstVal != 0xFF)
            {
                if (firstVal < 0xF0)
                {
                    // Set this byte as our result
                    finalResult = firstVal;
                }
                else
                {
                    // Read the next byte and combine 
                    uint secondVal = ReadByte();
                    uint highPart = (firstVal << 8) & 0xF00;
                    finalResult = highPart | secondVal;
                }
            }
            else
            {
                // Return the next 4 bytes
                finalResult = ReadUInt32();
            }
            // Return result
            return finalResult;
        }

        // Functions for reading data
        private XUR8_HEADER ReadHeader()
        {
            // Read in our header
            XUR8_HEADER headerData;
            headerData.Magic = ReadUInt32();
            headerData.Version = ReadUInt32();
            headerData.Flags = ReadUInt32();
            headerData.XuiVersion = ReadUInt16();
            headerData.BinSize = ReadUInt32();
            headerData.NumSections = ReadUInt16();

            // Return our header
            return headerData;
        }
        private XUR8_HEADER_INFO ReadHeaderInfo()
        {
            // Read in our header info
            XUR8_HEADER_INFO headerInfoData;
            headerInfoData.ObjectCount = ReadPackedUlong();
            headerInfoData.PropertyCount = ReadPackedUlong();
            headerInfoData.PropertyArrayCount = ReadPackedUlong();
            headerInfoData.CompoundObjectPropCount = ReadPackedUlong();
            headerInfoData.CompoundObjectPropArrayCount = ReadPackedUlong();
            headerInfoData.PropPathDepthCount = ReadPackedUlong();
            headerInfoData.TimelinePropPathCount = ReadPackedUlong();
            headerInfoData.SubTimelineCount = ReadPackedUlong();
            headerInfoData.KeyframePropCount = ReadPackedUlong();
            headerInfoData.KeyframeDataCount = ReadPackedUlong();
            headerInfoData.NamedFrameCount = ReadPackedUlong();
            headerInfoData.ObjectsWithChildrenCount = ReadPackedUlong();

            // Return our header info
            return headerInfoData;
        }
        private XUR8_SECTION ReadSectionInfo()
        {
            XUR8_SECTION sectionData;
//            sectionData.FilePos = (uint)BaseStream.Position;
            sectionData.Name = ReadUInt32();
            sectionData.Offset = ReadUInt32();
            sectionData.Size = ReadUInt32();
            
            // Return
            return sectionData;
        }
        private ERROR LoadBinaryTables(XUR8_SECTION [] sectionInfo)
        {
            XUR8_SECTION section;
         
            // Read our string table, if we have one
            section = sectionInfo[(int)XUR8_SECTION_NAME.SECTION_STRN];
            if (section.Name != 0) ReadStringTable(section);
            // Read our vector table, if we have one
            section = sectionInfo[(int)XUR8_SECTION_NAME.SECTION_VECT];
            if (section.Name != 0) ReadVectorTable(section);
            // Read our quaternion table, if we have one
            section = sectionInfo[(int)XUR8_SECTION_NAME.SECTION_QUAT];
            if (section.Name != 0) ReadQuaternionTable(section);
            // Read our customprop table, if we have one
            section = sectionInfo[(int)XUR8_SECTION_NAME.SECTION_CUST];
            if (section.Name != 0) ReadCustomPropertyTable(section);
            // Read our float table, if we have one
            section = sectionInfo[(int)XUR8_SECTION_NAME.SECTION_FLOT];
            if (section.Name != 0) ReadFloatTable(section);
            // Read our float table, if we have one
            section = sectionInfo[(int)XUR8_SECTION_NAME.SECTION_COLR];
            if (section.Name != 0) ReadColorTable(section);

            return ERROR.S_OK;
        }
        private ERROR LoadAnimationTables(XUR8_SECTION[] sectionInfo)
        {
            XUR8_SECTION section;

            // Read our keyframe property table, if we have one
            section = sectionInfo[(int)XUR8_SECTION_NAME.SECTION_KEYP];
            if (section.Name != 0) ReadKeyframePropertyTable(section);
            // Read our keyframe data table, if we have one
            section = sectionInfo[(int)XUR8_SECTION_NAME.SECTION_KEYD];
            if (section.Name != 0)
            {
                if (ReadKeyframeDataTable(section) == ERROR.S_ERROR)
                    return ERROR.S_ERROR;
            }
            // Read our named frame table, if we have one
            section = sectionInfo[(int)XUR8_SECTION_NAME.SECTION_NAME];
            if (section.Name != 0) ReadNamedFrameTable(section);

            return ERROR.S_OK;
        }
        private void ReadStringTable(XUR8_SECTION section)
        {
            // Seek to the offset for this section
            BaseStream.Seek(section.Offset, SeekOrigin.Begin);

            // Clear our string tables
            BinaryTables.StringTable.Clear();

            // First entry is ""
            BinaryTables.StringTable.Add("");

            // Read in our strings
            uint strLen = ReadUInt32();
            ushort strCount = ReadUInt16();

            string newString = "";
            char data = '\0';
            while (strCount > 0)
            {
                // Build our string 1 byte at a time
                data = ReadChar();
                if (data != '\0') newString += data.ToString();
                if (data == '\0')
                {
                    // Add the completed string to our table
                    BinaryTables.StringTable.Add(newString);
                    strCount--;
                    newString = "";
                }
            }
        }
        private void ReadVectorTable(XUR8_SECTION section)
        {
            // Seek to the offset for this section
            BaseStream.Seek(section.Offset, SeekOrigin.Begin);

            // Clear our string tables
            BinaryTables.VectorTable.Clear();

            // Loop through each antry and add to our table
            ulong entries = section.Size / 12;
            while (entries > 0)
            {
                XUIVECTOR vector;
                vector.x = ReadSingle();
                vector.y = ReadSingle();
                vector.z = ReadSingle();

                // Add this vector to our table
                BinaryTables.VectorTable.Add(vector);

                entries--;
            }
        }
        private void ReadQuaternionTable(XUR8_SECTION section)
        {
            // Seek to the offset for this section
            BaseStream.Seek(section.Offset, SeekOrigin.Begin);

            // Clear our string tables
            BinaryTables.QuaternionTable.Clear();

            // Loop through each antry and add to our table
            ulong entries = section.Size / 16;
            while (entries > 0)
            {
                XUIQUATERNION quat;
                quat.x = ReadSingle();
                quat.y = ReadSingle();
                quat.z = ReadSingle();
                quat.w = ReadSingle();

                // Add this quaternion to our table
                BinaryTables.QuaternionTable.Add(quat);

                entries--;
            }
        }
        private void ReadFloatTable(XUR8_SECTION section)
        {
            // Seek to the offset for this section
            BaseStream.Seek(section.Offset, SeekOrigin.Begin);

            // Clear our string tables
            BinaryTables.FloatTable.Clear();

            // Loop through each antry and add to our table
            ulong entries = section.Size / 4;
            while (entries > 0)
            {
                float fVal = ReadSingle();

                // Add this float to our table
                BinaryTables.FloatTable.Add(fVal);

                entries--;
            }
        }
        private void ReadColorTable(XUR8_SECTION section)
        {
            // Seek to the offset for this section
            BaseStream.Seek(section.Offset, SeekOrigin.Begin);

            // Clear our string tables
            BinaryTables.ColorTable.Clear();

            // Loop through each antry and add to our table
            ulong entries = section.Size / 4;
            while (entries > 0)
            {
                XUICOLOR colVal;
                // Add this float to our table
                colVal.argb = ReadUInt32();
                BinaryTables.ColorTable.Add(colVal);
                entries--;
            }
        }
        private void ReadCustomPropertyTable(XUR8_SECTION section)
        {
            // Seek to the offset for this section
            BaseStream.Seek(section.Offset, SeekOrigin.Begin);

            // Clear our string tables
            BinaryTables.CustomTable.Clear();

            // Determine how many bytes we have and loop until we're out of bytes
            uint bytesRemaining = section.Size;
            uint currentOffset = 0;
            while (bytesRemaining > 0)
            {
                // Initailize our struct
                XUICUSTOM customVal = new XUICUSTOM();

                // Read in the first part of our data
                customVal.DataLen = ReadUInt32();
                customVal.BoundingBox.x = ReadSingle();
                customVal.BoundingBox.y = ReadSingle();
                customVal.NumPoints = ReadUInt32();
                customVal.Offset = currentOffset;
                currentOffset += (customVal.DataLen + 4);

                // Loop through and add our points
                for (uint ptsIdx = 0; ptsIdx < customVal.NumPoints; ptsIdx++)
                {
                    XUIBEZIERPOINT bPoint;

                    bPoint.vecPoint.x = ReadSingle();
                    bPoint.vecPoint.y = ReadSingle();
                    bPoint.vecCtrl1.x = ReadSingle();
                    bPoint.vecCtrl1.y = ReadSingle();
                    bPoint.vecCtrl2.x = ReadSingle();
                    bPoint.vecCtrl2.y = ReadSingle();

                    customVal.Points.Add(bPoint);
                }

                // Add this entire table entry to our table
                BinaryTables.CustomTable.Add(customVal);

                // Done with this custom table entry, so deduct our bytesRemaining
                bytesRemaining -= (customVal.DataLen + 4);
            }
        }
        private void ReadNamedFrameTable(XUR8_SECTION section)
        {
            // Seek to the offset for this section
            BaseStream.Seek(section.Offset, SeekOrigin.Begin);

            // Clear our string tables
            AnimationTables.NamedFrameTable.Clear();

            // Determine how many bytes we have and loop until we're out of bytes
            uint bytesRemaining = section.Size; 
            while (bytesRemaining > 0)
            {
                long curPos = BaseStream.Position;

                XUINAMEDFRAMEDATA namedFrame = new XUINAMEDFRAMEDATA();
                
                namedFrame.NameStringIndex = ReadPackedUlong();
                namedFrame.Name = (string)BinaryTables.StringTable[(int)namedFrame.NameStringIndex];
                namedFrame.Time = ReadPackedUlong();
                namedFrame.Command = (XUI_NAMEDFRAME_COMMAND)ReadByte();
                if ( namedFrame.Command >= XUI_NAMEDFRAME_COMMAND.XUI_CMD_GOTO)
                {
                    namedFrame.TargetStringIndex = ReadPackedUlong();
                    namedFrame.Target = (string)BinaryTables.StringTable[(int)namedFrame.TargetStringIndex];
                }

                long bytesRead = BaseStream.Position - curPos;
                bytesRemaining -= (uint)bytesRead;

                // Add this entry to our table
                AnimationTables.NamedFrameTable.Add(namedFrame);
            }
        }
        private void ReadKeyframePropertyTable(XUR8_SECTION section)
        {
            // Seek to the offset for this section
            BaseStream.Seek(section.Offset, SeekOrigin.Begin);

            // Clear our string tables
            AnimationTables.KeyframePropertyTable.Clear();

            // Determine how many bytes we have and loop until we're out of bytes
            uint bytesRemaining = section.Size;
            long bytesWritten = 0;
            while (bytesRemaining > 0)
            {
                long curPos = BaseStream.Position;

                XUIKEYFRAMEPROPDATA keyframeProp = new XUIKEYFRAMEPROPDATA(); 

                keyframeProp.PropertyIndex = ReadPackedUlong();

                long bytesRead = BaseStream.Position - curPos;
                bytesRemaining -= (uint)bytesRead;
                bytesWritten += bytesRead;

                // Add this entry to our table
                AnimationTables.KeyframePropertyTable.Add(keyframeProp);
            }
        }
        private ERROR ReadKeyframeDataTable(XUR8_SECTION section)
        {
            // Seek to the offset for this section
            BaseStream.Seek(section.Offset, SeekOrigin.Begin);

            // Clear our string tables
            AnimationTables.KeyframeDataTable.Clear();

            uint bytesRemaining = section.Size;
            while( bytesRemaining > 0)
            {
                long curPos = BaseStream.Position;

                XUIKEYFRAMEDATA keyframeData = new XUIKEYFRAMEDATA();

                keyframeData.Frame = ReadPackedUlong();

                // Read our flags
                byte flagData = (byte)ReadByte();

                keyframeData.Flags = (byte)(flagData & 0x3F);
                keyframeData.Unknown1 = (byte)(flagData >> 6);

                if (keyframeData.Flags == 0x0)
                {
                    keyframeData.EaseIn = 0x00;
                    keyframeData.EaseOut = 0x00;
                    keyframeData.EaseScale = 0x00;
                    keyframeData.InterpolateType = XUI_INTERPOLATE.XUI_INTERPOLATE_LINEAR;
                }
                else if (keyframeData.Flags == 0x1)
                {
                    keyframeData.EaseIn = 0x00;
                    keyframeData.EaseOut = 0x00;
                    keyframeData.EaseScale = 0x00;
                    keyframeData.InterpolateType = XUI_INTERPOLATE.XUI_INTERPOLATE_NONE;
                }
                else if( keyframeData.Flags == 0x2 )
                {
                    keyframeData.EaseIn = ReadByte();
                    keyframeData.EaseOut = ReadByte();
                    keyframeData.EaseScale = ReadByte();
                    keyframeData.InterpolateType = XUI_INTERPOLATE.XUI_INTERPOLATE_EASE;
                }
                else if (keyframeData.Flags == 0x3)
                {
                    keyframeData.EaseIn = 0x00;
                    keyframeData.EaseOut = 0x00;
                    keyframeData.EaseScale = 0x00;
                    keyframeData.InterpolateType = XUI_INTERPOLATE.XUI_INTERPOLATE_LINEAR;
                }
                else if (keyframeData.Flags == 0xA)
                {
                    keyframeData.EaseIn = 0x00;
                    keyframeData.EaseOut = 0x00;
                    keyframeData.EaseScale = 0x00;
                    keyframeData.InterpolateType = XUI_INTERPOLATE.XUI_INTERPOLATE_LINEAR;
                    uint vectorIdx = ReadPackedUlong();
                    keyframeData.VectorIdx = vectorIdx;
                    keyframeData.VectorRef = (XUIVECTOR)BinaryTables.VectorTable[(int)keyframeData.VectorIdx];
                }
                else
                {
                    if (Array.IndexOf(Log.Instance.flagErrors.ToArray(), keyframeData.Flags) == -1)
                        Log.Instance.flagErrors.Add(keyframeData.Flags);
                    return ERROR.S_ERROR;
                }

                uint propIndex = ReadPackedUlong();
                keyframeData.KeyframePropIdx = propIndex;

                long bytesRead = BaseStream.Position - curPos;
                bytesRemaining -= (uint)bytesRead;

                AnimationTables.KeyframeDataTable.Add( keyframeData );
            }

            return ERROR.S_OK;
        }
    }
}
