Exemple #1
0
        public void Read(BinaryReaderEx br)
        {
            //data that seems to be present in every level
            header = Instance <SceneHeader> .FromReader(br, 0);

            meshinfo = Instance <MeshInfo> .FromReader(br, header.ptrMeshInfo);

            verts = InstanceList <Vertex> .FromReader(br, meshinfo.ptrVertexArray, meshinfo.cntVertex);

            restartPts = InstanceList <PosAng> .FromReader(br, header.ptrRestartPts, header.cntRestartPts);

            visdata = InstanceList <VisData> .FromReader(br, meshinfo.ptrVisDataArray, meshinfo.cntColData);

            quads = InstanceList <QuadBlock> .FromReader(br, meshinfo.ptrQuadBlockArray, meshinfo.cntQuadBlock);

            //optional stuff, can be missing
            if (header.ptrSkybox != 0)
            {
                skybox = Instance <SkyBox> .FromReader(br, header.ptrSkybox);
            }
            if (header.ptrVcolAnim != 0)
            {
                vertanims = InstanceList <VertexAnim> .FromReader(br, header.ptrVcolAnim, header.cntVcolAnim);
            }
            if (header.ptrAiNav != 0)
            {
                nav = Instance <Nav> .FromReader(br, header.ptrAiNav);
            }
            if (header.ptrTrialData != 0)
            {
                trial = Instance <TrialData> .FromReader(br, header.ptrTrialData);
            }

            if (header.cntSpawnPts != 0)
            {
                br.Jump(header.ptrSpawnPts);
                unkadv = new UnkAdv(br, header.cntSpawnPts);
            }

            if (header.cntTrialData != 0)
            {
                br.Jump(header.ptrTrialData);

                posu1_cnt = br.ReadInt32();
                posu1_ptr = br.ReadInt32();

                br.Jump(posu1_ptr);

                for (int i = 0; i < posu1_cnt; i++)
                {
                    posu1.Add(new PosAng(br));
                }
            }

            if (header.cntu2 != 0)
            {
                br.Jump(header.ptru2);

                posu2_cnt = br.ReadInt32();
                posu2_ptr = br.ReadInt32();


                br.Jump(posu2_ptr);

                for (int i = 0; i < posu2_cnt; i++)
                {
                    posu2.Add(new Vector3s(br));
                }
            }

            pickUpHeaderPositionList = new List <uint>();
            //read pickups
            for (int i = 0; i < header.numInstances; i++)
            {
                br.Jump(header.ptrPickupHeadersPtrArray + 4 * i);
                var pickUpHeaderPosition = br.ReadUInt32();
                pickUpHeaderPositionList.Add(pickUpHeaderPosition);
                br.Jump(pickUpHeaderPosition);

                //pickups.Add(new PickupHeader(br));
            }

            //read pickup models
            //starts out right, but anims ruin it

            br.Jump(header.ptrModelsPtr);
            int x = (int)br.BaseStream.Position;

            pickUpModelDataLocation = new List <uint>();
            for (int i = 0; i < header.numModels; i++)
            {
                br.BaseStream.Position = x + 4 * i;
                pickUpModelDataLocation.Add((uint)br.BaseStream.Position);
                br.BaseStream.Position = br.ReadUInt32();
                pickUpModelDataLocation.Add((uint)br.BaseStream.Position);
                //dynamics.Add(new CtrModel(br));
            }

            StringBuilder sb = new StringBuilder();

            int max = 0;
            int min = 99999999;

            foreach (QuadBlock qb in quads)
            {
                foreach (Vector2s v in qb.unk3)
                {
                    if (v.X > max)
                    {
                        max = v.X;
                    }
                    if (v.Y > max)
                    {
                        max = v.Y;
                    }
                    if (v.X < min && v.X != 0)
                    {
                        min = v.X;
                    }
                    if (v.Y < min && v.Y != 0)
                    {
                        min = v.Y;
                    }
                }
            }

            Debug.Log($"{min}, {max}");
            Debug.Log($"{min}, {max}");
            // Console.ReadKey();

            //Helpers.WriteToFile(".\\normals_test.txt", sb.ToString());
        }
Exemple #2
0
        public void Read(BinaryReaderEx br)
        {
            //data that seems to be present in every level
            header = Instance<SceneHeader>.FromReader(br, 0);

            if (header.ptrRestartPts != 0) restartPts = InstanceList<Pose>.FromReader(br, header.ptrRestartPts, header.cntRestartPts);

            if (header.ptrMeshInfo != 0)
            {
                meshinfo = Instance<MeshInfo>.FromReader(br, header.ptrMeshInfo);

                if (meshinfo.ptrVertexArray != 0) verts = InstanceList<Vertex>.FromReader(br, meshinfo.ptrVertexArray, meshinfo.cntVertex);
                if (meshinfo.ptrVisDataArray != 0) visdata = InstanceList<VisData>.FromReader(br, meshinfo.ptrVisDataArray, meshinfo.cntColData);
                if (meshinfo.ptrQuadBlockArray != 0) quads = InstanceList<QuadBlock>.FromReader(br, meshinfo.ptrQuadBlockArray, meshinfo.cntQuadBlock);
            }

            //optional stuff, can be missing
            if (header.ptrSkybox != 0) skybox = Instance<SkyBox>.FromReader(br, header.ptrSkybox);
            if (header.ptrVcolAnim != 0) vertanims = InstanceList<VertexAnim>.FromReader(br, header.ptrVcolAnim, header.cntVcolAnim);
            if (header.ptrAiNav != 0) nav = Instance<Nav>.FromReader(br, header.ptrAiNav);
            if (header.ptrTrialData != 0) trial = Instance<TrialData>.FromReader(br, header.ptrTrialData);

            if (header.cntSpawnPts != 0)
            {
                br.Jump(header.ptrSpawnPts);
                unkadv = new UnkAdv(br, (int)header.cntSpawnPts);
            }


            if (header.cntTrialData != 0)
            {
                br.Jump(header.ptrTrialData);

                int cnt = br.ReadInt32();
                int ptr = br.ReadInt32();

                br.Jump(ptr);

                for (int i = 0; i < cnt; i++)
                    posu1.Add(new Pose(br));
            }


            if (header.cntu2 != 0)
            {
                br.Jump(header.ptru2);

                int cnt = br.ReadInt32();
                int ptr = br.ReadInt32();


                br.Jump(ptr);

                for (int i = 0; i < cnt; i++)
                    posu2.Add(new Vector3s(br));
            }


            //find all water quads in visdata
            foreach (VisData v in visdata)
            {
                if (v.IsLeaf)
                {
                    if (v.flag.HasFlag(VisDataFlags.Water))
                    {
                        int z = (int)((v.ptrQuadBlock - meshinfo.ptrQuadBlockArray) / 0x5C);

                        for (int i = z; i < z + v.numQuadBlock; i++)
                            quads[i].isWater = true;
                    }
                }
            }


            /*
            //texture defs
            br.Jump(header.ptrTexArray);
            br.Skip(8);
            int ptrTexList = br.ReadInt32();
            br.Jump(ptrTexList);

            Console.WriteLine(ptrTexList.ToString("X8"));
            Console.ReadKey();

            texmaps = new List<TexMap>();

            TexMap mp;

            do
            {
                mp = new TexMap(br, "none");

                Console.WriteLine(mp.name);
                Console.ReadKey();

                if (mp.name != "")
                {
                    texmaps.Add(mp);
                }

            }
            while (mp.name != "");
            */


            /*
             //water texture
            br.BaseStream.Position = header.ptrWater;

            List<uint> vptr = new List<uint>();
            List<uint> wptr = new List<uint>();

            for (int i = 0; i < header.cntWater; i++)
            {
                vptr.Add(br.ReadUInt32());
                wptr.Add(br.ReadUInt32());
            }

            wptr.Sort();

            foreach(uint u in wptr)
            {
                Console.WriteLine(u.ToString("X8"));
            }

            Console.ReadKey();
            */

            //read pickups
            for (int i = 0; i < header.numInstances; i++)
            {
                br.Jump(header.ptrInstancesPtr + 4 * i);
                br.Jump(br.ReadUInt32());

                pickups.Add(new PickupHeader(br));
            }


            br.Jump(header.ptrModelsPtr);
            int x = (int)br.BaseStream.Position;

            for (int i = 0; i < header.numModels; i++)
            {
                br.BaseStream.Position = x + 4 * i;
                br.BaseStream.Position = br.ReadUInt32();

                Models.Add(CtrModel.FromReader(br));
            }


            /*
            quads = quads.OrderBy(o => o.mosaicPtr1).ToList();

            StringBuilder sb = new StringBuilder();

            foreach (QuadBlock qb in quads)
            {
                sb.AppendLine(
                    $"{qb.id.ToString("X4")}\t" +
                    $"{(qb.mosaicPtr1 & 0xFFFFFFFC).ToString("X8")} ({Helpers.TestPointer(qb.mosaicPtr1)})\t" +
                    $"{(qb.mosaicPtr2 & 0xFFFFFFFC).ToString("X8")} ({Helpers.TestPointer(qb.mosaicPtr2)})\t" +
                    $"{(qb.mosaicPtr3 & 0xFFFFFFFC).ToString("X8")} ({Helpers.TestPointer(qb.mosaicPtr3)})\t" +
                    $"{(qb.mosaicPtr4 & 0xFFFFFFFC).ToString("X8")} ({Helpers.TestPointer(qb.mosaicPtr4)})"
                    );
            }

            Helpers.WriteToFile(".\\mosaic_test.txt", sb.ToString());

            */
        }
Exemple #3
0
        public void Read(BinaryReaderEx br)
        {
            //data that seems to be present in every level
            header = Instance <SceneHeader> .FromReader(br, 0);

            meshinfo = Instance <MeshInfo> .FromReader(br, header.ptrMeshInfo);

            verts = InstanceList <Vertex> .FromReader(br, meshinfo.ptrVertexArray, meshinfo.cntVertex);

            restartPts = InstanceList <PosAng> .FromReader(br, header.ptrRestartPts, header.cntRestartPts);

            visdata = InstanceList <VisData> .FromReader(br, meshinfo.ptrVisDataArray, meshinfo.cntColData);

            quads = InstanceList <QuadBlock> .FromReader(br, meshinfo.ptrQuadBlockArray, meshinfo.cntQuadBlock);

            //optional stuff, can be missing
            if (header.ptrSkybox != 0)
            {
                skybox = Instance <SkyBox> .FromReader(br, header.ptrSkybox);
            }
            if (header.ptrVcolAnim != 0)
            {
                vertanims = InstanceList <VertexAnim> .FromReader(br, header.ptrVcolAnim, header.cntVcolAnim);
            }
            if (header.ptrAiNav != 0)
            {
                nav = Instance <Nav> .FromReader(br, header.ptrAiNav);
            }
            if (header.ptrTrialData != 0)
            {
                trial = Instance <TrialData> .FromReader(br, header.ptrTrialData);
            }

            if (header.cntSpawnPts != 0)
            {
                br.Jump(header.ptrSpawnPts);
                unkadv = new UnkAdv(br, (int)header.cntSpawnPts);
            }


            if (header.cntTrialData != 0)
            {
                br.Jump(header.ptrTrialData);

                int cnt = br.ReadInt32();
                int ptr = br.ReadInt32();

                br.Jump(ptr);

                for (int i = 0; i < cnt; i++)
                {
                    posu1.Add(new PosAng(br));
                }
            }


            if (header.cntu2 != 0)
            {
                br.Jump(header.ptru2);

                int cnt = br.ReadInt32();
                int ptr = br.ReadInt32();


                br.Jump(ptr);

                for (int i = 0; i < cnt; i++)
                {
                    posu2.Add(new Vector3s(br));
                }
            }


            foreach (VisData v in visdata)
            {
                if (v.IsLeaf)
                {
                    if (v.flag.HasFlag(VisDataFlags.Water))
                    {
                        int z = (int)((v.ptrQuadBlock - meshinfo.ptrQuadBlockArray) / 0x5C);

                        for (int i = z; i < z + v.numQuadBlock; i++)
                        {
                            quads[i].isWater = true;
                        }
                    }
                }
            }


            /*
             * //texture defs
             * br.Jump(header.ptrTexArray);
             * br.Skip(8);
             * int ptrTexList = br.ReadInt32();
             * br.Jump(ptrTexList);
             *
             * Console.WriteLine(ptrTexList.ToString("X8"));
             * Console.ReadKey();
             *
             * texmaps = new List<TexMap>();
             *
             * TexMap mp;
             *
             * do
             * {
             *  mp = new TexMap(br, "none");
             *
             *  Console.WriteLine(mp.name);
             *  Console.ReadKey();
             *
             *  if (mp.name != "")
             *  {
             *      texmaps.Add(mp);
             *  }
             *
             * }
             * while (mp.name != "");
             */


            /*
             * for (int i = 0; i < visdata.Count; i++)
             * {
             *  if (File.Exists($"visdata_{i}.obj"))
             *      File.Delete($"visdata_{i}.obj");
             *
             *  File.AppendAllText($"visdata_{i}.obj", visdata[i].ToObj());
             * }
             */

            /*
             * //water texture
             * br.BaseStream.Position = header.ptrWater;
             *
             * List<uint> vptr = new List<uint>();
             * List<uint> wptr = new List<uint>();
             *
             * for (int i = 0; i < header.cntWater; i++)
             * {
             *  vptr.Add(br.ReadUInt32());
             *  wptr.Add(br.ReadUInt32());
             * }
             *
             * wptr.Sort();
             *
             * foreach(uint u in wptr)
             * {
             *  Console.WriteLine(u.ToString("X8"));
             * }
             *
             * Console.ReadKey();
             */

            //read pickups
            for (int i = 0; i < header.numInstances; i++)
            {
                br.Jump(header.ptrPickupHeadersPtrArray + 4 * i);
                br.Jump(br.ReadUInt32());

                pickups.Add(new PickupHeader(br));
            }

            //read pickup models
            //starts out right, but anims ruin it

            br.Jump(header.ptrModelsPtr);
            int x = (int)br.BaseStream.Position;

            for (int i = 0; i < header.numModels; i++)
            {
                br.BaseStream.Position = x + 4 * i;
                br.BaseStream.Position = br.ReadUInt32();

                dynamics.Add(new CtrModel(br));
            }

            /*
             * //check why itdoesn't work in export function
             * foreach (var d in dynamics)
             * {
             *  d.Export(".\\models\\");
             * }
             */

            /*
             * foreach (QuadBlock qb in quads)
             * {
             *  for (int i = 0; i < 4; i++)
             *  {
             *      List<CTRFramework.Vertex> list = qb.GetVertexListq(verts, i);
             *
             *      foreach (CTRFramework.Vertex v in list)
             *          Console.WriteLine(v.ToString());
             *
             *      Console.WriteLine(qb.unk3[i].ToString());
             *
             *      /*
             *      //requires 4.6
             *      System.Numerics.Vector3 a = new Vector3(list[3].coord.X - list[0].coord.X, list[3].coord.Y - list[0].coord.Y, list[3].coord.Z - list[0].coord.Z);
             *      System.Numerics.Vector3 b = new Vector3(list[2].coord.X - list[0].coord.X, list[2].coord.Y - list[0].coord.Y, list[2].coord.Z - list[0].coord.Z);
             *
             *      Vector3 cross = Vector3.Cross(a, b);
             *
             *      Console.WriteLine(cross.Length());
             */

            /*
             *  }
             *
             *  Console.WriteLine();
             *  Console.ReadKey();
             * }
             */


            quads = quads.OrderBy(o => o.mosaicPtr1).ToList();

            StringBuilder sb = new StringBuilder();

            foreach (QuadBlock qb in quads)
            {
                sb.AppendLine(
                    $"{qb.id.ToString("X4")}\t" +
                    $"{(qb.mosaicPtr1 & 0xFFFFFFFC).ToString("X8")} ({Helpers.TestPointer(qb.mosaicPtr1)})\t" +
                    $"{(qb.mosaicPtr2 & 0xFFFFFFFC).ToString("X8")} ({Helpers.TestPointer(qb.mosaicPtr2)})\t" +
                    $"{(qb.mosaicPtr3 & 0xFFFFFFFC).ToString("X8")} ({Helpers.TestPointer(qb.mosaicPtr3)})\t" +
                    $"{(qb.mosaicPtr4 & 0xFFFFFFFC).ToString("X8")} ({Helpers.TestPointer(qb.mosaicPtr4)})"
                    );
            }

            Helpers.WriteToFile(".\\mosaic_test.txt", sb.ToString());
        }
Exemple #4
0
        public void ReadScene(BinaryReaderEx br)
        {
            header = Instance <SceneHeader> .FromReader(br, 0);

            if (header == null)
            {
                throw new Exception("Scene header is null. Halt parsing.");
            }

            if (header.ptrMeshInfo != UIntPtr.Zero)
            {
                mesh    = new PtrWrap <MeshInfo>(header.ptrMeshInfo).Get(br);
                quads   = mesh.QuadBlocks;
                verts   = mesh.Vertices;
                visdata = mesh.VisData;
            }

            restartPts = new PtrWrap <Pose>(header.ptrRestartPts).GetList(br, header.numRestartPts);
            vertanims  = new PtrWrap <VertexAnim>(header.ptrVcolAnim).GetList(br, header.numVcolAnim);
            skybox     = new PtrWrap <SkyBox>(header.ptrSkybox).Get(br);
            nav        = new PtrWrap <Nav>(header.ptrAiNav).Get(br);
            iconpack   = new PtrWrap <IconPack>(header.ptrIcons).Get(br);
            trial      = new PtrWrap <TrialData>(header.ptrTrialData).Get(br);

            if (header.numSpawnPts > 0)
            {
                br.Jump(header.ptrSpawnPts);
                unkadv = new UnkAdv(br, (int)header.numSpawnPts);
            }

            if (header.cntTrialData > 0)
            {
                br.Jump(header.ptrTrialData);

                int cnt = br.ReadInt32();
                int ptr = br.ReadInt32();

                br.Jump(ptr);

                for (int i = 0; i < cnt; i++)
                {
                    posu1.Add(new Pose(br));
                }
            }


            if (header.cntu2 > 0)
            {
                br.Jump(header.ptru2);

                int cnt = br.ReadInt32();
                int ptr = br.ReadInt32();


                br.Jump(ptr);

                for (int i = 0; i < cnt; i++)
                {
                    posu2.Add(new Vector3s(br));
                }
            }


            //find all water quads in visdata
            foreach (var node in visdata)
            {
                if (node.IsLeaf)
                {
                    if (node.flag.HasFlag(VisDataFlags.Water))
                    {
                        int z = (int)((node.ptrQuadBlock - mesh.ptrQuadBlocks.ToUInt32()) / 0x5C);

                        for (int i = z; i < z + node.numQuadBlock; i++)
                        {
                            quads[i].isWater = true;
                        }
                    }
                }
            }

            //assign anim color target to vertex
            foreach (var va in vertanims)
            {
                verts[(int)((va.ptrVertex - mesh.ptrVertices.ToUInt32()) / 16)].color_target = va.color;
            }


            /*
             * //water texture
             * br.BaseStream.Position = header.ptrWater;
             *
             * List<uint> vptr = new List<uint>();
             * List<uint> wptr = new List<uint>();
             *
             * for (int i = 0; i < header.cntWater; i++)
             * {
             *  vptr.Add(br.ReadUInt32());
             *  wptr.Add(br.ReadUInt32());
             * }
             *
             * wptr.Sort();
             *
             * foreach(uint u in wptr)
             * {
             *  Console.WriteLine(u.ToString("X8"));
             * }
             *
             * Console.ReadKey();
             */

            //read pickups
            for (int i = 0; i < header.numInstances; i++)
            {
                br.Jump(header.ptrInstancesPtr + 4 * i);
                br.Jump(br.ReadUInt32());

                pickups.Add(PickupHeader.FromReader(br));
            }


            br.Jump(header.ptrModelsPtr);

            List <uint> modelPtr = br.ReadListUInt32(header.numModels);

            foreach (var ptr in modelPtr)
            {
                br.Jump(ptr);

                try
                {
                    CtrModel ctr = CtrModel.FromReader(br);
                    if (ctr != null)
                    {
                        Models.Add(ctr);
                    }
                }
                catch
                {
                    Helpers.Panic(this, PanicType.Error, "Unexpected CtrModel crash.");
                }
            }

            foreach (VertexAnim va in vertanims)
            {
                Helpers.Panic(this, PanicType.Info, va.ToString());
            }

            /*
             * quads = quads.OrderBy(o => o.mosaicPtr1).ToList();
             *
             * StringBuilder sb = new StringBuilder();
             *
             * foreach (QuadBlock qb in quads)
             * {
             *  sb.AppendLine(
             *      $"{qb.id.ToString("X4")}\t" +
             *      $"{(qb.mosaicPtr1 & 0xFFFFFFFC).ToString("X8")} ({Helpers.TestPointer(qb.mosaicPtr1)})\t" +
             *      $"{(qb.mosaicPtr2 & 0xFFFFFFFC).ToString("X8")} ({Helpers.TestPointer(qb.mosaicPtr2)})\t" +
             *      $"{(qb.mosaicPtr3 & 0xFFFFFFFC).ToString("X8")} ({Helpers.TestPointer(qb.mosaicPtr3)})\t" +
             *      $"{(qb.mosaicPtr4 & 0xFFFFFFFC).ToString("X8")} ({Helpers.TestPointer(qb.mosaicPtr4)})"
             *      );
             * }
             *
             * Helpers.WriteToFile(".\\mosaic_test.txt", sb.ToString());
             *
             */
        }
Exemple #5
0
        public void Read(BinaryReaderEx br)
        {
            //data that seems to be present in every level
            header = Instance <SceneHeader> .FromStream(br, 0);

            meshinfo = Instance <MeshInfo> .FromStream(br, header.ptrMeshInfo);

            verts = InstanceList <Vertex> .FromStream(br, meshinfo.ptrVertexArray, meshinfo.cntVertex);

            restartPts = InstanceList <PosAng> .FromStream(br, header.ptrRestartPts, header.cntRestartPts);

            visdata = InstanceList <VisData> .FromStream(br, meshinfo.ptrColDataArray, meshinfo.cntColData);

            quads = InstanceList <QuadBlock> .FromStream(br, meshinfo.ptrQuadBlockArray, meshinfo.cntQuadBlock);

            //optional stuff, can be missing
            if (header.ptrSkybox != 0)
            {
                skybox = Instance <SkyBox> .FromStream(br, header.ptrSkybox);
            }
            if (header.ptrVcolAnim != 0)
            {
                vertanims = InstanceList <VertexAnim> .FromStream(br, header.ptrVcolAnim, header.cntVcolAnim);
            }
            if (header.ptrAiNav != 0)
            {
                nav = Instance <Nav> .FromStream(br, header.ptrAiNav);
            }
            if (header.ptrTrialData != 0)
            {
                trial = Instance <TrialData> .FromStream(br, header.ptrTrialData);
            }

            if (header.cntSpawnPts != 0)
            {
                br.Jump(header.ptrSpawnPts);
                unkadv = new UnkAdv(br, (int)header.cntSpawnPts);
            }


            if (header.cntTrialData != 0)
            {
                br.Jump(header.ptrTrialData);

                int cnt = br.ReadInt32();
                int ptr = br.ReadInt32();

                br.Jump(ptr);

                for (int i = 0; i < cnt; i++)
                {
                    posu1.Add(new PosAng(br));
                }
            }


            if (header.cntu2 != 0)
            {
                br.Jump(header.ptru2);

                int cnt = br.ReadInt32();
                int ptr = br.ReadInt32();


                br.Jump(ptr);

                for (int i = 0; i < cnt; i++)
                {
                    posu2.Add(new Vector3s(br));
                }
            }

            /*
             * //water texture
             * br.BaseStream.Position = header.ptrWater;
             *
             * List<uint> vptr = new List<uint>();
             * List<uint> wptr = new List<uint>();
             *
             * for (int i = 0; i < header.cntWater; i++)
             * {
             *  vptr.Add(br.ReadUInt32());
             *  wptr.Add(br.ReadUInt32());
             * }
             *
             * wptr.Sort();
             *
             * foreach(uint u in wptr)
             * {
             *  Console.WriteLine(u.ToString("X8"));
             * }
             *
             * Console.ReadKey();
             */

            //read pickups
            for (int i = 0; i < header.numPickupHeaders; i++)
            {
                br.Jump(header.ptrPickupHeadersPtrArray + 4 * i);
                br.Jump(br.ReadUInt32());

                pickups.Add(new PickupHeader(br));
            }

            //read pickup models
            //starts out right, but anims ruin it

            br.Jump(header.ptrPickupModelsPtr);
            int x = (int)br.BaseStream.Position;

            for (int i = 0; i < header.numPickupModels; i++)
            {
                br.BaseStream.Position = x + 4 * i;
                br.BaseStream.Position = br.ReadUInt32();

                dynamics.Add(new LODModel(br));
            }
        }