public static VehicleSetupConfig Load(string pathToFile)
        {
            VehicleSetupConfig setup = new VehicleSetupConfig();

            using (var doc = new DocumentParser(pathToFile))
            {
                string line = doc.ReadFirstLine();

                while (line != null)
                {
                    switch (line)
                    {
                        case "[default_driver]":
                            while (!doc.NextLineIsASection() && !doc.EOF())
                            {
                                setup.Drivers.Add(doc.ReadNextLine());
                            }
                            break;

                        case "[attachment]":
                            setup.Attachments.Add(new VehicleAttachment(doc));
                            break;

                        case "[wheel_module]":
                            setup.WheelModules.Add(new VehicleWheelModule(doc));
                            break;

                        case "[suspension_factors]":
                            setup.SuspensionFactors = new VehicleSuspensionFactors(doc);
                            break;

                        case "[ai_script]":
                            setup.AIScript = doc.ReadNextLine();
                            break;

                        case "[material_map]":
                            setup.MaterialMaps.Add(new VehicleMaterialMap(doc));
                            break;

                        case "[wheel_map]":
                            setup.WheelMaps.Add(new VehicleWheelMap(doc));
                            break;

                        case "[disable_ejection]":
                            setup.EjectDriver = false;
                            break;

                        case "[stats]":
                            setup.Stats.TopSpeed = doc.ReadInt();
                            setup.Stats.Time = doc.ReadFloat();
                            setup.Stats.Weight = doc.ReadFloat();
                            setup.Stats.Toughness = doc.ReadFloat();
                            if (!doc.EOF() && !doc.NextLineIsASection()) { setup.Stats.UnlockLevel = doc.ReadFloat(); }
                            break;

                        case "[decal_points]":
                            while (!doc.NextLineIsASection() && !doc.EOF())
                            {
                                setup.DecalPoints.Add(doc.ReadVector3());
                            }
                            break;

                        default:
                            Console.WriteLine(pathToFile);
                            throw new NotImplementedException("Unexpected [SECTION]: " + line);
                    }

                    line = doc.ReadNextLine();
                }
            }

            return setup;
        }
        public static Routes Load(string pathToFile)
        {
            Routes routes = new Routes();

            using (var doc = new DocumentParser(pathToFile))
            {
                string line = doc.ReadNextLine();

                while (line != null)
                {
                    switch (line)
                    {
                        case "[LUMP]":
                            doc.ReadNextLine();  // level
                            line = doc.ReadNextLine();
                            break;

                        case "[VERSION]":
                            doc.ReadNextLine();  // 2.500000
                            line = doc.ReadNextLine();
                            break;

                        case "[RACE_LAYERS]":
                            line = doc.SkipToNextSection();
                            break;

                        case "[LUA_SCRIPTS]":
                            line = doc.SkipToNextSection();
                            break;

                        case "[RACES]":
                            while (!doc.NextLineIsASection())
                            {
                                routes.races.Add(doc.ReadNextLine());
                            }

                            line = doc.ReadNextLine();
                            break;

                        case "[AINODE]":
                            bool bAINode = true;
                            var node = new AINode();

                            while (bAINode)
                            {
                                line = doc.ReadNextLine();

                                switch (line)
                                {
                                    case "<INDEX>":
                                        node.Index = doc.ReadInt();
                                        break;

                                    case "<TYPE>":
                                        node.Type = doc.ReadInt();
                                        break;

                                    case "<RADIUS>":
                                        node.Radius = doc.ReadFloat();
                                        break;

                                    case "<POS>":
                                        node.Position = doc.ReadVector3();
                                        break;

                                    case "<RACE_LINE>":
                                        node.RaceLine = doc.ReadVector3();
                                        break;

                                    case "<RACE_LINE_OFFSET>":
                                        node.RaceLineOffset = doc.ReadFloat();
                                        break;

                                    default:
                                        bAINode = false;
                                        routes.nodes.Add(node);
                                        if (line != null && !line.StartsWith("[")) { Console.WriteLine("Unexpected [AINODE] line: " + line); }
                                        break;
                                }
                            }
                            break;

                        case "[AILINK]":
                            bool bAILink = true;
                            var link = new AILink();

                            while (bAILink)
                            {
                                line = doc.ReadNextLine();

                                switch (line)
                                {
                                    case "<NODES>":
                                        link.NodeA = doc.ReadInt();
                                        link.NodeB = doc.ReadInt();
                                        break;

                                    case "<WIDTH>":
                                        link.Width = doc.ReadFloat();
                                        break;

                                    case "<VALUE>":
                                        link.Value = doc.ReadNextLine();
                                        break;

                                    case "<ONEWAY>":
                                        link.OneWay = true;
                                        break;

                                    case "<TYPE>":
                                        for (int i = 0; i < routes.races.Count; i++)
                                        {
                                            link.Types.Add(doc.ReadInt());
                                        }
                                        break;

                                    case "<RACE_VALUE>":
                                        link.RaceValueAmount = doc.ReadInt();
                                        link.RaceValue = doc.ReadNextLine();
                                        break;

                                    default:
                                        bAILink = false;
                                        routes.links.Add(link);
                                        if (line != null && !line.StartsWith("[")) { throw new NotImplementedException("Unexpected [AILINK] line: " + line); }
                                        break;
                                }
                            }
                            break;

                        default:
                            Console.WriteLine(pathToFile);
                            throw new NotImplementedException("Unexpected [SECTION]: " + line);
                    }
                }
            }

            return routes;
        }
        public AccessoryShapeComponent(DocumentParser sr)
        {
            points = new List<Vector3>();

            string s = sr.ReadNextLine();
            int pointCount;

            switch (s)
            {
                case "AlignedCuboid":
                    this.componentType = ComponentType.AlignedCuboid;
                    points.Add(sr.ReadVector3());
                    points.Add(sr.ReadVector3());
                    break;

                case "Polyhedron":
                    this.componentType = ComponentType.Polyhedron;
                    pointCount = sr.ReadInt();
                    for (int i = 0; i < pointCount; i++) { points.Add(sr.ReadVector3()); }
                    break;

                case "RoundedPolyhedron":
                    this.componentType = ComponentType.RoundedPolyhedron;
                    radius = sr.ReadFloat();
                    pointCount = sr.ReadInt();
                    for (int i = 0; i < pointCount; i++) { points.Add(sr.ReadVector3()); }
                    break;

                case "Sphere":
                    points.Add(sr.ReadVector3());
                    radius = sr.ReadFloat();
                    break;

                case "TicTac":
                    points.Add(sr.ReadVector3());
                    points.Add(sr.ReadVector3());
                    radius = sr.ReadFloat();
                    break;

                default:
                    throw new NotImplementedException("Unknown ComponentType: " + s);
            }

            while (sr.ReadNextLine() == "form_collision_groups")
            {
                group = sr.ReadInt();
            }

            sr.Rewind();
        }
        public AccessoryShape(DocumentParser sr)
        {
            name = sr.ReadNextLine();

            if (name.Replace("_", " ") != "(no shape)")
            {
                boxCount = sr.ReadInt();

                for (int i = 0; i < boxCount; i++)
                {
                    boxes.Add(new AccessoryShapeComponent(sr));
                }
            }
        }
        public AccessoryJoint(DocumentParser sr)
        {
            string line;
            if (!Accessory.TestLine("joint", sr.ReadNextLine(), out line)) { Console.WriteLine("Unexpected value: {0}", line); }

            vertexNum = sr.ReadInt();
            var f1 = sr.ReadFloat();
            var v1 = sr.ReadVector3();
            var v2 = sr.ReadVector3();
            var v3 = sr.ReadVector3();
            var v4 = sr.ReadVector3();
            var v5 = sr.ReadVector3();
            var v6 = sr.ReadVector3();
            var v7 = sr.ReadVector3();
            var v8 = sr.ReadVector3();
            var i1 = sr.ReadInt();
            for (int i = 0; i < i1; i++)
            {
                var i2 = sr.ReadInt();
                var f2 = sr.ReadFloat();
                var f3 = sr.ReadFloat();
                var v9 = sr.ReadVector3();
                var v10 = sr.ReadVector3();
                var f4 = sr.ReadFloat();
                var s1 = sr.ReadNextLine();
                var v11 = sr.ReadVector3();
                var v12 = sr.ReadVector3();
            }
            if (!Accessory.TestLine("(no_weakness)", sr.ReadNextLine(), out line))
            {
                var s3 = sr.ReadNextLine();
                var v13 = sr.ReadVector3();
                var f5 = sr.ReadFloat();
                var s4 = sr.ReadNextLine();
                var v15 = sr.ReadVector3();

                var t = sr.ReadNextLine();
                if (t.Contains(","))
                {
                    var v_1 = Vector2.Parse(t);
                }
                else
                {
                    var f6 = t.ToSingle();
                }
            }
        }
        public AccessoryDynamics(DocumentParser doc)
        {
            while (!doc.NextLineIsASection() && !doc.EOF())
            {
                string line = doc.ReadNextLine();

                switch (line)
                {
                    case "<lump_name>":
                        lump = doc.ReadNextLine();
                        break;

                    case "<mass>":
                        mass = doc.ReadFloat();
                        break;

                    case "<drivable_on>":
                        bDrivableOn = true;
                        break;

                    case "<solid>":
                        bSolid = true;
                        break;

                    case "<stop_sinking_into_ground>":
                        bStopSinkingIntoGround = true;
                        break;

                    case "<part_of_world>":
                        bPartOfWorld = true;
                        break;

                    case "<ignore_world>":
                        bIgnoreWorld = true;
                        break;

                    case "<inf_mass>":
                        bInfMass = true;
                        break;

                    case "<inf_mi>":
                        bInfMi = true;
                        break;

                    case "<buoyant>":
                        bBuouyant = true;
                        buoyancyCount = doc.ReadFloat();
                        buoyancyVector = doc.ReadVector3();
                        break;

                    case "<substance>":
                        substance = doc.ReadInt();
                        break;

                    case "<group>":
                        group = doc.ReadInt();
                        break;

                    case "<ignore_group>":
                        ignoreGroup = doc.ReadInt();
                        break;

                    case "<moments>":
                        moments = doc.ReadVector3();
                        break;

                    case "<buoyancy_relative_to_com>":
                        bBuoyancyRelativeToCOM = true;
                        break;

                    case "<centre_of_mass>":
                        centreOfMass = doc.ReadVector3();
                        break;

                    case "<sphere_rolling_resistance>":
                        sphereRollingResistance = doc.ReadFloat();
                        break;

                    case "<shape>":
                        shape = new AccessoryShape(doc);
                        break;

                    case "<breakable>":
                        breakable = new AccessoryBreak(doc);
                        break;

                    case "<world_joint>":
                        worldJoint = new AccessoryJoint(doc);
                        break;

                    case "<child_joint>":
                        childJoint = new AccessoryJoint(doc);
                        break;

                    default:
                        throw new NotImplementedException(string.Format("Unknown [DYNAMICS] setting: {0}", line));
                }
            }
        }