Exemplo n.º 1
0
        // Methods
        public override void Load(Stream fileStream)
        {
            // BINA Header
            var reader = new BINAReader(fileStream);

            Header = reader.ReadHeader();

            string sig = reader.ReadSignature(4);

            if (sig != Signature)
            {
                throw new InvalidSignatureException(Signature, sig);
            }
            var unknown1 = reader.ReadUInt32(); //Might be part of the Header according to Skyth's spec?

            if (unknown1 != 1)
            {
                Console.WriteLine($"Unknown1 does not equal 1 in this file! It's actually set to {unknown1}");
            }
            var shapeCount = reader.ReadInt64();
            var unknown2   = reader.ReadUInt64();

            if (unknown2 != 24)
            {
                Console.WriteLine($"Unknown1 does not equal 24 in this file! It's actually set to {unknown1}");
            }
            Console.WriteLine(unknown2);

            for (int i = 0; i < shapeCount; i++)
            {
                SvShape shape           = new SvShape();
                var     shapeNameOffset = reader.ReadInt64();
                long    pos             = reader.BaseStream.Position;
                reader.JumpTo(shapeNameOffset, false);
                shape.Name = reader.ReadNullTerminatedString();
                reader.JumpTo(pos, true);

                shape.Unknown1            = reader.ReadUInt32();
                shape.Size                = reader.ReadVector3();
                shape.Position            = reader.ReadVector3();
                shape.Rotation            = reader.ReadQuaternion();
                shape.BoundingBox.Minimum = reader.ReadVector3();
                shape.BoundingBox.Maximum = reader.ReadVector3();
                shape.Unknown2            = reader.ReadUInt32();

                var sectorCount      = reader.ReadInt64();
                var sectorListOffset = reader.ReadInt64();
                pos = reader.BaseStream.Position;
                reader.JumpTo(sectorListOffset, false);
                for (int s = 0; s < sectorCount; s++)
                {
                    SvSector sector = new SvSector();
                    sector.SectorIndex = reader.Read();
                    sector.Visible     = reader.ReadBoolean();
                    shape.Sectors.Add(sector);
                }
                reader.JumpTo(pos, true);
                SvShapes.Add(shape);
            }
        }
Exemplo n.º 2
0
            public void Read(BINAReader reader)
            {
                uint nodesCount            = reader.ReadUInt32();
                uint dataNodeCount         = reader.ReadUInt32();
                long nodesOffset           = reader.ReadInt64();
                long dataNodeIndicesOffset = reader.ReadInt64();

                // Nodes
                reader.JumpTo(nodesOffset);
                Nodes.Capacity = (int)nodesCount;

                for (int i = 0; i < nodesCount; ++i)
                {
                    Nodes.Add(new Node(reader, false));
                }

                // Data Node Indices
                reader.JumpTo(dataNodeIndicesOffset);
                DataNodeIndices.Capacity = (int)dataNodeCount;

                for (int i = 0; i < dataNodeCount; ++i)
                {
                    DataNodeIndices.Add(reader.ReadInt32());
                }
            }
Exemplo n.º 3
0
        public static void ExportFont(Stream stream, string outputFile)
        {
            var reader = new BINAReader(stream);

            reader.ReadHeader();

            var sig = reader.ReadSignature(8);

            if (sig != "KFCS1000")
            {
                throw new InvalidSignatureException("KFCS1000", sig);
            }

            var fontName = reader.GetString();

            reader.JumpAhead(4);
            reader.JumpAhead(0x10);
            var length = reader.ReadInt64();
            var fntSig = reader.ReadSignature();

            outputFile = fntSig == "OTTO" ? Path.ChangeExtension(outputFile, ".otf")
                : Path.ChangeExtension(outputFile, ".ttf");
            var data = reader.ReadBytes((int)length - 4);

            using (var fntStream = File.Create(outputFile))
            {
                fntStream.Write(Encoding.Default.GetBytes(fntSig), 0, 4);
                fntStream.Write(data, 0, data.Length);
            }
        }
Exemplo n.º 4
0
            public void Read(BINAReader reader, bool readChildIndices = true)
            {
                long nameOffset            = reader.ReadInt64();
                long dataOffset            = reader.ReadInt64();
                long childIndexTableOffset = reader.ReadInt64();

                ParentIndex = reader.ReadInt32();
                Index       = reader.ReadInt32();
                DataIndex   = reader.ReadInt32();

                ushort childCount   = reader.ReadUInt16();
                bool   hasData      = reader.ReadBoolean();
                byte   fullPathSize = reader.ReadByte(); // Not counting this node in

                // Read name
                if (nameOffset != 0)
                {
                    Name = reader.GetString((uint)nameOffset);
                }

                // (MINOR HACK) Store data offset in Data
                // to be read later, avoiding some Seeks
                if (hasData && dataOffset != 0)
                {
                    Data = dataOffset;
                }

                // Read Child Indices
                if (!readChildIndices)
                {
                    return;
                }

                long curPos = reader.BaseStream.Position;

                reader.JumpTo(childIndexTableOffset);
                ChildIndices.Capacity = childCount;

                for (int i = 0; i < childCount; ++i)
                {
                    ChildIndices.Add(reader.ReadInt32());
                }

                reader.JumpTo(curPos);
            }
        protected NodeTree ReadNodeTree(BINAReader reader)
        {
            var  nodeTree   = new NodeTree();
            uint nodesCount = reader.ReadUInt32();

            nodeTree.DataNodeCount = reader.ReadUInt32();
            long nodesOffset           = reader.ReadInt64();
            long dataNodeIndicesOffset = reader.ReadInt64();

            // Nodes
            reader.JumpTo(nodesOffset);
            for (uint i = 0; i < nodesCount; ++i)
            {
                nodeTree.Nodes.Add(new Node(reader));
            }

            return(nodeTree);
        }
Exemplo n.º 6
0
            // Methods
            public void Read(BINAReader reader)
            {
                uint id       = reader.ReadUInt32();
                long dataSize = reader.ReadInt64();

                reader.JumpAhead(4);

                DataOffset = reader.ReadInt64();
                reader.JumpAhead(8);

                ExtensionOffset = reader.ReadInt64();
                DataType        = (DataEntryTypes)reader.ReadUInt64();

                if (DataType != DataEntryTypes.NotHere)
                {
                    Data = new byte[dataSize];
                }
            }
        public override void Load(Stream fileStream)
        {
            // PACx Header
            var reader = new BINAReader(fileStream);

            Header.Read(reader);

            // Type Names
            var typeTree = ReadNodeTree(reader);
            var names    = new string[typeTree.Nodes.Count];

            for (int i = 0; i < typeTree.Nodes.Count; ++i)
            {
                var typeNode = typeTree.Nodes[i];
                if (typeNode.ChildCount < 1)
                {
                    continue;
                }

                int nameIndex = -1;
                if (typeNode.ChildIDTableOffset > 0)
                {
                    reader.JumpTo(typeNode.ChildIDTableOffset);
                    nameIndex = reader.ReadInt32();
                }

                if (typeNode.NameOffset > 0)
                {
                    reader.JumpTo(typeNode.NameOffset);
                    names[i] = reader.ReadNullTerminatedString();
                }
            }

            // Types
            foreach (var type in typeTree.Nodes)
            {
                if (!type.HasData)
                {
                    continue;
                }

                string name = string.Empty;
                var    n    = type;

                while (n.ParentIndex >= 0)
                {
                    name = $"{names[n.ParentIndex]}{name}";
                    n    = typeTree.Nodes[n.ParentIndex];
                }

                reader.JumpTo(type.DataOffset);
                var fileTree = ReadNodeTree(reader);

                // File Names
                var fileNames = new string[fileTree.Nodes.Count];
                for (int i = 0; i < fileTree.Nodes.Count; ++i)
                {
                    var fileNode = fileTree.Nodes[i];
                    if (fileNode.ChildCount < 1)
                    {
                        continue;
                    }

                    int nameIndex = -1;
                    if (fileNode.ChildIDTableOffset > 0)
                    {
                        reader.JumpTo(fileNode.ChildIDTableOffset);
                        nameIndex = reader.ReadInt32();
                    }

                    if (fileNode.NameOffset > 0)
                    {
                        reader.JumpTo(fileNode.NameOffset);
                        fileNames[i] = reader.ReadNullTerminatedString();
                    }
                }

                // File Nodes
                foreach (var file in fileTree.Nodes)
                {
                    if (!file.HasData)
                    {
                        continue;
                    }

                    name = string.Empty;
                    n    = file;

                    while (n.ParentIndex >= 0)
                    {
                        name = $"{fileNames[n.ParentIndex]}{name}";
                        n    = fileTree.Nodes[n.ParentIndex];
                    }

                    // File Entries
                    reader.JumpTo(file.DataOffset);
                    uint pacID = reader.ReadUInt32();
                    if (pacID != Header.ID)
                    {
                        Console.WriteLine(
                            $"WARNING: Skipped file {name} as its pac ID was missing");
                        continue;
                    }

                    ulong fileSize        = reader.ReadUInt64();
                    uint  padding1        = reader.ReadUInt32();
                    long  fileDataOffset  = reader.ReadInt64();
                    ulong padding2        = reader.ReadUInt64();
                    long  extensionOffset = reader.ReadInt64();
                    uint  pacType         = reader.ReadUInt32();
                    uint  padding3        = reader.ReadUInt32();

                    if (fileDataOffset <= 0 || pacType == 1)
                    {
                        continue;
                    }

                    // File Extension
                    reader.JumpTo(extensionOffset);
                    name += $".{reader.ReadNullTerminatedString()}";

                    // File Data
                    reader.JumpTo(fileDataOffset);
                    var data = reader.ReadBytes((int)fileSize);

                    // BINA Check
                    // TODO: Remove this check
                    if (data[0] == 0x42 && data[1] == 0x49 && data[2] == 0x4E && data[3] == 0x41)
                    {
                        if (pacType != 2)
                        {
                            Console.WriteLine(
                                $"WARNING: FileType ({pacType}) != 2 when file carries BINA Header!");
                        }
                    }
                    else
                    {
                        if (pacType == 2)
                        {
                            Console.WriteLine(
                                $"WARNING: FileType ({pacType}) == 2 when file has no BINA Header!");
                        }
                    }

                    Data.Add(new ArchiveFile(name, data));
                }
            }
        }
Exemplo n.º 8
0
        // Methods
        public override void Load(Stream fileStream)
        {
            // BINA Header
            var reader = new BINAReader(fileStream);

            Header = reader.ReadHeader();

            // Header
            byte unknown1     = reader.ReadByte(); // Always 3?
            byte sheetCount   = reader.ReadByte(); // ?
            byte unknown2     = reader.ReadByte(); // Always 0?
            byte unknown3     = reader.ReadByte(); // Always 0?
            uint unknown4     = reader.ReadUInt32();
            long sheetsOffset = reader.ReadInt64();

            // Sheets
            reader.JumpTo(sheetsOffset, false);
            for (uint i = 0; i < sheetCount; ++i)
            {
                Sheets.Add(new Sheet()
                {
                    NameOffset  = reader.ReadInt64(),
                    CellCount   = reader.ReadUInt64(),
                    CellsOffset = reader.ReadInt64()
                });
            }

            // Cells
            for (int i = 0; i < sheetCount; ++i)
            {
                var sheet = Sheets[i];
                reader.JumpTo(sheet.CellsOffset, false);

                for (uint i2 = 0; i2 < sheet.CellCount; ++i2)
                {
                    sheet.Cells.Add(new Cell()
                    {
                        UUID              = reader.ReadUInt64(),
                        NameOffset        = reader.ReadInt64(),
                        SecondEntryOffset = reader.ReadInt64(),
                        DataOffset        = reader.ReadInt64()
                    });
                }
            }

            // Data
            foreach (var sheet in Sheets)
            {
                for (int i = 0; i < sheet.Cells.Count; ++i)
                {
                    var cell = sheet.Cells[i];
                    reader.JumpTo(cell.DataOffset.Value, false);

                    var  chars           = new List <byte>();
                    bool isReadingButton = false;

                    // Read Unicode strings
                    do
                    {
                        byte b1 = reader.ReadByte();
                        byte b2 = reader.ReadByte();

                        if (b1 != 0 && ((b2 & 0xE0) == 0xE0))
                        {
                            isReadingButton = true;
                        }

                        if (b1 == 0 && b2 == 0)
                        {
                            if (isReadingButton)
                            {
                                isReadingButton = false;
                                chars.Add(0x57); // NullReplaceChar byte1
                                chars.Add(0x2B); // NullReplaceChar byte2
                                continue;
                            }
                            else
                            {
                                break;
                            }
                        }

                        chars.Add(b1);
                        chars.Add(b2);
                    }while (fileStream.Position < fileStream.Length);
                    cell.Data = Encoding.Unicode.GetString(chars.ToArray());
                }
            }

            // Second Entries
            var typeOffsets = new Dictionary <long, string>();

            foreach (var sheet in Sheets)
            {
                for (int i = 0; i < sheet.Cells.Count; ++i)
                {
                    var cell = sheet.Cells[i];
                    reader.JumpTo(cell.SecondEntryOffset.Value, false);

                    long nameOffset = reader.ReadInt64();
                    long typeOffset = reader.ReadInt64();
                    cell.LayoutOffset = reader.ReadInt64();

                    if (nameOffset != cell.NameOffset)
                    {
                        Console.WriteLine(
                            "WARNING: Second name offset ({0:X}) != first ({1:X})!",
                            nameOffset, cell.NameOffset);
                    }

                    // Caption Type
                    if (typeOffsets.ContainsKey(typeOffset))
                    {
                        cell.TypeName = typeOffsets[typeOffset];
                        continue;
                    }

                    var type = new CellType();
                    reader.JumpTo(typeOffset, false);

                    long typeNameOffset      = reader.ReadInt64();
                    long typeNamespaceOffset = reader.ReadInt64();
                    long unknownFloat1Offset = reader.ReadInt64();
                    long unknownFloat2Offset = reader.ReadInt64();
                    long unknownFloat3Offset = reader.ReadInt64();
                    long unknownInt1Offset   = reader.ReadInt64();
                    long unknownInt2Offset   = reader.ReadInt64();
                    long unknownOffset1      = reader.ReadInt64();
                    long unknownULong2Offset = reader.ReadInt64();
                    long unknownOffset2      = reader.ReadInt64();
                    long unknownOffset3      = reader.ReadInt64();
                    long unknownOffset4      = reader.ReadInt64();
                    long unknownULong1Offset = reader.ReadInt64();
                    long unknownOffset5      = reader.ReadInt64();

                    // UnknownFloat1
                    if (unknownFloat1Offset > 0)
                    {
                        reader.JumpTo(unknownFloat1Offset, false);
                        type.UnknownFloat1 = reader.ReadSingle();
                        uint padding1 = reader.ReadUInt32();

                        if (padding1 != 0)
                        {
                            Console.WriteLine("WARNING: Type Padding1 != 0 (0x{0:X})", padding1);
                        }
                    }

                    // UnknownFloat2
                    if (unknownFloat2Offset > 0)
                    {
                        reader.JumpTo(unknownFloat2Offset, false);
                        type.UnknownFloat2 = reader.ReadSingle();
                        uint padding2 = reader.ReadUInt32();

                        if (padding2 != 0)
                        {
                            Console.WriteLine("WARNING: Type Padding2 != 0 (0x{0:X})", padding2);
                        }
                    }

                    // UnknownFloat3
                    if (unknownFloat3Offset > 0)
                    {
                        reader.JumpTo(unknownFloat3Offset, false);
                        type.UnknownFloat3 = reader.ReadSingle();
                        uint padding3 = reader.ReadUInt32();

                        if (padding3 != 0)
                        {
                            Console.WriteLine("WARNING: Type Padding3 != 0 (0x{0:X})", padding3);
                        }
                    }

                    // UnknownInt1
                    if (unknownInt1Offset > 0)
                    {
                        reader.JumpTo(unknownInt1Offset, false);
                        type.UnknownInt1 = reader.ReadInt32();
                        uint padding4 = reader.ReadUInt32();

                        if (padding4 != 0)
                        {
                            Console.WriteLine("WARNING: Type Padding4 != 0 (0x{0:X})", padding4);
                        }
                    }

                    // UnknownInt2
                    if (unknownInt2Offset > 0)
                    {
                        reader.JumpTo(unknownInt2Offset, false);
                        type.UnknownInt2 = reader.ReadInt32();
                        uint padding5 = reader.ReadUInt32();

                        if (padding5 != 0)
                        {
                            Console.WriteLine("WARNING: Type Padding5 != 0 (0x{0:X})", padding5);
                        }
                    }

                    if (unknownOffset1 != 0)
                    {
                        Console.WriteLine("WARNING: Type ukOff1 != 0 (0x{0:X})", unknownOffset1);
                    }

                    // UnknownULong2Offset
                    if (unknownULong2Offset > 0)
                    {
                        reader.JumpTo(unknownULong2Offset, false);
                        type.UnknownULong2 = reader.ReadUInt64();
                    }

                    if (unknownOffset2 != 0)
                    {
                        Console.WriteLine("WARNING: Type ukOff2 != 0 (0x{0:X})", unknownOffset2);
                    }

                    if (unknownOffset3 != 0)
                    {
                        Console.WriteLine("WARNING: Type ukOff3 != 0 (0x{0:X})", unknownOffset3);
                    }

                    if (unknownOffset4 != 0)
                    {
                        Console.WriteLine("WARNING: Type ukOff4 != 0 (0x{0:X})", unknownOffset4);
                    }

                    // UnknownULong1Offset
                    if (unknownULong1Offset > 0)
                    {
                        reader.JumpTo(unknownULong1Offset, false);
                        type.UnknownULong1 = reader.ReadUInt64();
                    }

                    if (unknownOffset5 != 0)
                    {
                        Console.WriteLine("WARNING: Type ukOff5 != 0 (0x{0:X})", unknownOffset5);
                    }

                    // Strings
                    reader.JumpTo(typeNameOffset, false);
                    string typeName = reader.ReadNullTerminatedString();

                    reader.JumpTo(typeNamespaceOffset, false);
                    type.Namespace = reader.ReadNullTerminatedString();

                    cell.TypeName = typeName;
                    typeOffsets.Add(typeOffset, typeName);
                    Types.Add(typeName, type);
                }
            }

            // Layouts
            var layoutOffsets = new List <long>();

            foreach (var sheet in Sheets)
            {
                foreach (var cell in sheet.Cells)
                {
                    if (!layoutOffsets.Contains(cell.LayoutOffset.Value))
                    {
                        var layout = new Layout();
                        reader.JumpTo(cell.LayoutOffset.Value, false);

                        long layoutNameOffset   = reader.ReadInt64();
                        long unknownData1Offset = reader.ReadInt64();
                        long unknownData2Offset = reader.ReadInt64();
                        long unknownData3Offset = reader.ReadInt64();
                        long unknownData4Offset = reader.ReadInt64();
                        long unknownData5Offset = reader.ReadInt64();
                        long unknownData6Offset = reader.ReadInt64();
                        long unknownData7Offset = reader.ReadInt64();
                        layout.UnknownData8 = reader.ReadInt64(); // Always 0?

                        if (layout.UnknownData8 != 0)
                        {
                            Console.WriteLine(
                                "WARNING: Layout UnknownData8 != 0! ({0:X})",
                                layout.UnknownData8);
                        }

                        // Unknown Data 1
                        if (unknownData1Offset > 0)
                        {
                            reader.JumpTo(unknownData1Offset, false);
                            layout.UnknownData1 = reader.ReadInt32(); // Always 1?
                        }

                        // Unknown Data 2
                        if (unknownData2Offset > 0)
                        {
                            reader.JumpTo(unknownData2Offset, false);
                            layout.UnknownData2 = reader.ReadSingle();
                            uint padding = reader.ReadUInt32();

                            if (padding != 0)
                            {
                                Console.WriteLine(
                                    "WARNING: Layout UnknownData2 Padding != 0! ({0:X})",
                                    padding);
                            }
                        }

                        // Unknown Data 3
                        if (unknownData3Offset > 0)
                        {
                            reader.JumpTo(unknownData3Offset, false);
                            layout.UnknownData3 = reader.ReadSingle();
                            uint padding = reader.ReadUInt32();

                            if (padding != 0)
                            {
                                Console.WriteLine(
                                    "WARNING: Layout UnknownData3 Padding != 0! ({0:X})",
                                    padding);
                            }
                        }

                        // Unknown Data 4
                        if (unknownData4Offset > 0)
                        {
                            reader.JumpTo(unknownData4Offset, false);
                            layout.UnknownData4 = reader.ReadInt32(); // Always 0?
                        }

                        // Unknown Data 5
                        if (unknownData5Offset > 0)
                        {
                            reader.JumpTo(unknownData5Offset, false);
                            layout.UnknownData5 = reader.ReadInt32();
                        }

                        // Unknown Data 6
                        if (unknownData6Offset > 0)
                        {
                            reader.JumpTo(unknownData6Offset, false);
                            layout.UnknownData6 = reader.ReadInt32(); // Always 1?
                        }

                        // Unknown Data 7
                        if (unknownData7Offset > 0)
                        {
                            reader.JumpTo(unknownData7Offset, false);
                            layout.UnknownData7 = reader.ReadInt32(); // Always 2?
                        }

                        // Layout Name
                        reader.JumpTo(layoutNameOffset, false);
                        layout.Name = reader.ReadNullTerminatedString();

                        layout.Offset    = cell.LayoutOffset;
                        cell.LayoutIndex = layoutOffsets.Count;
                        layoutOffsets.Add(cell.LayoutOffset.Value);
                        Layouts.Add(layout);
                    }
                    else
                    {
                        cell.LayoutIndex = layoutOffsets.IndexOf(
                            cell.LayoutOffset.Value);
                    }
                }
            }

            // Names
            foreach (var sheet in Sheets)
            {
                reader.JumpTo(sheet.NameOffset, false);
                sheet.Name = reader.ReadNullTerminatedString();

                for (int i = 0; i < sheet.Cells.Count; ++i)
                {
                    var cell = sheet.Cells[i];
                    reader.JumpTo(cell.NameOffset.Value, false);
                    cell.Name = reader.ReadNullTerminatedString();
                }
            }
        }
        protected SetObject ReadObject(BINAReader reader,
                                       Dictionary <string, SetObjectType> objectTemplates)
        {
            var   obj           = new SetObject();
            ulong padding1      = reader.ReadUInt64();
            long  objTypeOffset = reader.ReadInt64();
            long  objNameOffset = reader.ReadInt64();

            ushort id            = reader.ReadUInt16();
            ushort groupID       = reader.ReadUInt16();
            ushort parentID      = reader.ReadUInt16();
            ushort parentGroupID = reader.ReadUInt16();

            obj.CustomData.Add("ParentID", new SetObjectParam(
                                   typeof(ushort), parentID));
            obj.CustomData.Add("ParentGroupID", new SetObjectParam(
                                   typeof(ushort), parentGroupID));

            obj.ObjectID = id;
            obj.CustomData.Add("GroupID", new SetObjectParam(
                                   typeof(ushort), groupID));

            var pos            = reader.ReadVector3();
            var rot            = reader.ReadVector3();
            var childPosOffset = reader.ReadVector3();
            var childRotOffset = reader.ReadVector3();

            obj.CustomData.Add("ChildPosOffset", new SetObjectParam(
                                   typeof(Vector3), childPosOffset));

            obj.CustomData.Add("ChildRotOffset", new SetObjectParam(
                                   typeof(Vector3), childRotOffset));

            obj.Transform.Position = pos;
            obj.Transform.Rotation = new Quaternion(rot, true);

            long  extraParamsOffset = reader.ReadInt64();
            ulong unknownCount1     = reader.ReadUInt64();
            ulong unknownCount2     = reader.ReadUInt64();
            ulong padding3          = reader.ReadUInt64();

            long objParamsOffset = reader.ReadInt64();

            // Unknown Count Checks
            if (unknownCount1 != unknownCount2)
            {
                Console.WriteLine(
                    "WARNING: UnknownCount1 ({0}) != UnknownCount2 ({1})",
                    unknownCount1, unknownCount2);
            }

            if (unknownCount1 > 1)
            {
                Console.WriteLine(
                    "WARNING: UnknownCount1 > 1 ({0})",
                    unknownCount1);
            }

            // Extra Parameter Offsets
            var extraParamOffsets = new long[unknownCount1];

            reader.JumpTo(extraParamsOffset, false);

            for (uint i = 0; i < unknownCount1; ++i)
            {
                extraParamOffsets[i] = reader.ReadInt64();
                ulong padding5 = reader.ReadUInt64(); // TODO: Make sure this is correct
            }

            // Extra Parameters
            for (uint i = 0; i < unknownCount1; ++i)
            {
                reader.JumpTo(extraParamOffsets[i], false);
                ulong padding6 = reader.ReadUInt64();

                long  extraParamNameOffset = reader.ReadInt64();
                ulong extraParamLength     = reader.ReadUInt64();
                long  extraParamDataOffset = reader.ReadInt64();

                // Extra Parameter Data
                reader.JumpTo(extraParamDataOffset, false);
                var data = reader.ReadBytes((int)extraParamLength);

                // Extra Parameter Name
                reader.JumpTo(extraParamNameOffset, false);
                string name = reader.ReadNullTerminatedString();

                // Parse Data
                switch (name)
                {
                case "RangeSpawning":
                {
                    obj.CustomData.Add("RangeIn", new SetObjectParam(
                                           typeof(float), BitConverter.ToSingle(data, 0)));

                    obj.CustomData.Add("RangeOut", new SetObjectParam(
                                           typeof(float), BitConverter.ToSingle(data, 4)));
                    continue;
                }
                }

                Console.WriteLine($"WARNING: Unknown extra parameter type {name}");
                obj.CustomData.Add(name, new SetObjectParam(
                                       data.GetType(), data));
            }

            // Object Name
            reader.JumpTo(objNameOffset, false);
            string objName = reader.ReadNullTerminatedString();

            obj.CustomData.Add("Name", new SetObjectParam(typeof(string), objName));

            // Object Type
            reader.JumpTo(objTypeOffset, false);
            string objType = reader.ReadNullTerminatedString();

            obj.ObjectType = objType;

            if (!objectTemplates.ContainsKey(objType))
            {
                Console.WriteLine(
                    "WARNING: Skipped {0} because there is no template for type {1}!",
                    objName, objType);
                Console.WriteLine("Params at: {0:X}",
                                  objParamsOffset + reader.Offset);
                return(null);
            }

            //Console.WriteLine("\"{1}\" Params at: {0:X}",
            //    objParamsOffset + reader.Offset, objName);
            var template = objectTemplates[objType];

            // Object Parameters
            reader.JumpTo(objParamsOffset, false);
            foreach (var param in template.Parameters)
            {
                obj.Parameters.Add(ReadParameter(reader, param));
            }

            // Additional Padding
            var rawDataLenExtra = template.GetExtra("RawByteLength");

            if (uint.TryParse(rawDataLenExtra?.Value, out var rawLength))
            {
                uint paramLen = (uint)(reader.BaseStream.Position -
                                       objParamsOffset - reader.Offset);

                if (paramLen != rawLength)
                {
                    obj.CustomData.Add("RawByteLength", new SetObjectParam(
                                           typeof(uint), rawLength));
                }
            }

            // Padding Checks
            if (padding1 != 0)
            {
                Console.WriteLine($"WARNING: Obj Padding1 != 0 ({padding1})");
            }

            if (padding3 != 0)
            {
                Console.WriteLine($"WARNING: Obj Padding3 != 0 ({padding3})");
            }

            return(obj);
        }
        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);
        }
        // Methods
        public override void Load(Stream fileStream,
                                  Dictionary <string, SetObjectType> objectTemplates)
        {
            if (objectTemplates == null)
            {
                throw new ArgumentNullException("objectTemplates",
                                                "Cannot load Forces set data without object templates.");
            }

            // BINA Header
            var reader = new BINAReader(fileStream);

            Header = reader.ReadHeader();

            // Set Data Header
            ulong padding1 = reader.ReadUInt64();
            ulong padding2 = reader.ReadUInt64();

            long  objectTableOffset = reader.ReadInt64();
            ulong objectCount       = reader.ReadUInt64();
            ulong objectCount2      = reader.ReadUInt64();
            ulong padding3          = reader.ReadUInt64();

            // Padding/Unknown Value Checks
            if (padding1 != 0)
            {
                Console.WriteLine($"WARNING: Padding1 != 0 ({padding1})");
            }

            if (padding2 != 0)
            {
                Console.WriteLine($"WARNING: Padding2 != 0 ({padding2})");
            }

            if (padding3 != 0)
            {
                Console.WriteLine($"WARNING: Padding3 != 0 ({padding3})");
            }

            if (objectCount != objectCount2)
            {
                Console.WriteLine(
                    "WARNING: ObjectCount ({0}) != ObjectCount2 ({1})",
                    objectCount, objectCount2);
            }

            // Object Offsets
            var objectOffsets = new long[objectCount];

            reader.JumpTo(objectTableOffset, false);

            for (uint i = 0; i < objectCount; ++i)
            {
                objectOffsets[i] = reader.ReadInt64();
            }

            // Objects
            for (uint i = 0; i < objectCount; ++i)
            {
                reader.JumpTo(objectOffsets[i], false);

                var obj = ReadObject(reader, objectTemplates);
                if (obj == null)
                {
                    continue;
                }
                Objects.Add(obj);
            }
        }