public static dataBlockData ReadDataBlock(BufferedStreamReader streamReader, int offset, dataBlock offsetBlock)
        {
            dataBlockData data = new dataBlockData();

            data.rawBlock = offsetBlock;
            data.unkInt0  = offsetBlock.unkInt0;
            streamReader.Seek(offsetBlock.internalName0Offset + offset, System.IO.SeekOrigin.Begin);
            data.internalName0 = AquaObjectMethods.ReadCString(streamReader);
            streamReader.Seek(offsetBlock.chatCommandOffset + offset, System.IO.SeekOrigin.Begin);
            data.chatCommand = AquaObjectMethods.ReadCString(streamReader);
            streamReader.Seek(offsetBlock.internalName1Offset + offset, System.IO.SeekOrigin.Begin);
            data.internalName1 = AquaObjectMethods.ReadCString(streamReader);

            streamReader.Seek(offsetBlock.lobbyActionIdOffset + offset, System.IO.SeekOrigin.Begin);
            data.lobbyActionId = AquaObjectMethods.ReadCString(streamReader);
            streamReader.Seek(offsetBlock.commonReferenceOffset0 + offset, System.IO.SeekOrigin.Begin);
            data.commonReference0 = AquaObjectMethods.ReadCString(streamReader);
            streamReader.Seek(offsetBlock.commonReferenceOffset1 + offset, System.IO.SeekOrigin.Begin);
            data.commonReference1 = AquaObjectMethods.ReadCString(streamReader);
            streamReader.Seek(offsetBlock.unkIntOffset0 + offset, System.IO.SeekOrigin.Begin);
            data.unkOffsetInt0 = streamReader.Read <int>();

            streamReader.Seek(offsetBlock.unkIntOffset1 + offset, System.IO.SeekOrigin.Begin);
            data.unkOffsetInt1 = streamReader.Read <int>();
            streamReader.Seek(offsetBlock.unkIntOffset2 + offset, System.IO.SeekOrigin.Begin);
            data.unkOffsetInt2 = streamReader.Read <int>();
            streamReader.Seek(offsetBlock.iceNameOffset + offset, System.IO.SeekOrigin.Begin);
            data.iceName = AquaObjectMethods.ReadCString(streamReader);
            streamReader.Seek(offsetBlock.humanAqmOffset + offset, System.IO.SeekOrigin.Begin);
            data.humanAqm = AquaObjectMethods.ReadCString(streamReader);

            streamReader.Seek(offsetBlock.castAqmOffset1 + offset, System.IO.SeekOrigin.Begin);
            data.castAqm1 = AquaObjectMethods.ReadCString(streamReader);
            streamReader.Seek(offsetBlock.castAqmOffset2 + offset, System.IO.SeekOrigin.Begin);
            data.castAqm2 = AquaObjectMethods.ReadCString(streamReader);
            streamReader.Seek(offsetBlock.kmnAqmOffset + offset, System.IO.SeekOrigin.Begin);
            data.kmnAqm = AquaObjectMethods.ReadCString(streamReader);
            streamReader.Seek(offsetBlock.vfxOffset + offset, System.IO.SeekOrigin.Begin);
            data.vfxIce = AquaObjectMethods.ReadCString(streamReader);

            return(data);
        }
        //Takes in bytes of a *n.rel file from PSO
        //To convert to PSO2's units, we set the scale to 1/10th scale
        public PSONRelConvert(byte[] file, string fileName = null, float scale = 0.1f, string outFolder = null)
        {
            fileSize  = file.Length;
            rootScale = scale;
            List <dSection> dSections = new List <dSection>();

            streamReader = new BufferedStreamReader(new MemoryStream(file), 8192);

            //Get header offset
            streamReader.Seek(file.Length - 0x10, SeekOrigin.Begin);

            //Check Endianness. No offset should ever come close to half of the int max value.
            be = streamReader.PeekBigEndianPrimitiveUInt32() < streamReader.Peek <uint>();
            if (be)
            {
                MessageBox.Show("Sorry, Gamecube n.rel files are not supported at this time.");
            }
            uint tableOfs = streamReader.ReadBE <uint>(be);

            //Read header
            streamReader.Seek(tableOfs, SeekOrigin.Begin);
            var header = ReadRelHeader(streamReader, be);

            //Read draw Sections
            streamReader.Seek(header.drawOffset, SeekOrigin.Begin);
            for (int i = 0; i < header.drawCount; i++)
            {
                dSection section = new dSection();
                section.id  = streamReader.ReadBE <int>(be);
                section.pos = streamReader.ReadBEV3(be);
                var rotX = streamReader.ReadBE <int>(be);
                var rotY = streamReader.ReadBE <int>(be);
                var rotZ = streamReader.ReadBE <int>(be);
                section.rot            = new Vector3((float)(rotX * BAMSvalue), (float)(rotY * BAMSvalue), (float)(rotZ * BAMSvalue));
                section.radius         = streamReader.ReadBE <float>(be);
                section.staticOffset   = streamReader.ReadBE <uint>(be);
                section.animatedOffset = streamReader.ReadBE <uint>(be);
                section.staticCount    = streamReader.ReadBE <uint>(be);
                section.animatedCount  = streamReader.ReadBE <uint>(be);
                section.end            = streamReader.ReadBE <uint>(be);

                dSections.Add(section);
            }

            //Get texture names
            streamReader.Seek(header.nameInfoOffset, SeekOrigin.Begin);
            var nameOffset = streamReader.ReadBE <uint>(be);
            var nameCount  = streamReader.ReadBE <uint>(be);

            streamReader.Seek(nameOffset, SeekOrigin.Begin);
            List <uint> nameOffsets = new List <uint>();

            for (int i = 0; i < nameCount; i++)
            {
                nameOffsets.Add(streamReader.ReadBE <uint>(be));
                var unk0 = streamReader.ReadBE <uint>(be);
                var unk1 = streamReader.ReadBE <uint>(be);

                if (unk0 != 0)
                {
                    Console.WriteLine($"Iteration {i} unk0 == {unk0}");
                }
                if (unk1 != 0)
                {
                    Console.WriteLine($"Iteration {i} unk1 == {unk1}");
                }
            }
            foreach (uint offset in nameOffsets)
            {
                streamReader.Seek(offset, SeekOrigin.Begin);
                texNames.Add(AquaObjectMethods.ReadCString(streamReader));
            }

            //If there's an .xvm, dump that too with texture names from the .rel
            if (fileName != null)
            {
                //Naming patterns for *n.rel files are *_12n.rel for example or *n.rel  vs *.xvm. We can determine which we have, edit, and proceed
                var    basename = fileName.Substring(0, fileName.Length - 5);
                string xvmName  = null;

                if (basename.ElementAt(basename.Length - 3) == '_')
                {
                    xvmName = basename.Substring(0, basename.Length - 3) + ".xvm";
                }
                else
                {
                    xvmName = basename + ".xvm";
                }

                ExtractXVM(xvmName, texNames, outFolder);
            }



            //Create root AQN node
            NODE aqNode = new NODE();

            aqNode.animatedFlag = 1;
            aqNode.parentId     = -1;
            aqNode.unkNode      = -1;
            aqNode.pos          = new Vector3();
            aqNode.eulRot       = new Vector3();
            aqNode.scale        = new Vector3(1, 1, 1);
            aqNode.m1           = new Vector4(1, 0, 0, 0);
            aqNode.m2           = new Vector4(0, 1, 0, 0);
            aqNode.m3           = new Vector4(0, 0, 1, 0);
            aqNode.m4           = new Vector4(0, 0, 0, 1);
            aqNode.boneName.SetString("RootNode");
            nodes.Add(aqNode);

            //Loop through nodes and parse geometry
            for (int i = 0; i < dSections.Count; i++)
            {
                var matrix = Matrix4x4.Identity;

                matrix *= Matrix4x4.CreateScale(1, 1, 1);

                var rotation = Matrix4x4.CreateRotationX(dSections[i].rot.X) *
                               Matrix4x4.CreateRotationY(dSections[i].rot.Y) *
                               Matrix4x4.CreateRotationZ(dSections[i].rot.Z);

                matrix *= rotation;

                matrix *= Matrix4x4.CreateTranslation(dSections[i].pos * rootScale);

                //Read static meshes
                List <staticMeshOffset> staticMeshOffsets = new List <staticMeshOffset>();
                streamReader.Seek(dSections[i].staticOffset, SeekOrigin.Begin);
                for (int st = 0; st < dSections[i].staticCount; st++)
                {
                    staticMeshOffsets.Add(ReadStaticMeshOffset(streamReader, be));
                }
                for (int ofs = 0; ofs < staticMeshOffsets.Count; ofs++)
                {
                    streamReader.Seek(staticMeshOffsets[ofs].offset, SeekOrigin.Begin);
                    readNode(matrix, 0);
                }


                //Read animated meshes
                List <animMeshOffset> animatedMeshOffsets = new List <animMeshOffset>();
                streamReader.Seek(dSections[i].animatedOffset, SeekOrigin.Begin);
                for (int st = 0; st < dSections[i].animatedCount; st++)
                {
                    animatedMeshOffsets.Add(ReadAnimMeshOffset(streamReader, be));
                }
                for (int ofs = 0; ofs < animatedMeshOffsets.Count; ofs++)
                {
                    streamReader.Seek(animatedMeshOffsets[ofs].offset, SeekOrigin.Begin);
                    readNode(matrix, 0);
                }
            }

            //Set material names
            for (int i = 0; i < aqObj.tempMats.Count; i++)
            {
                aqObj.tempMats[i].matName = $"PSOMat {i}";
            }
        }