Example #1
0
        public static Car Load(string path, bool suppressFolderCheck)
        {
            DocumentParser file = new DocumentParser(path);
            Car            car  = new Car {
                Name = file.ReadLine()
            };

            if (!suppressFolderCheck)
            {
                if (string.Compare(Path.GetFileName(path).ToUpper(), car.Name) != 0)
                {
                    Logger.LogToFile(Logger.LogLevel.Error, "Not a valid Carmageddon car .txt file, expected {0} but found {1}", Path.GetFileName(path).ToUpper(), car.Name);
                    return(null);
                }
            }

            if (file.ReadLine() != "START OF DRIVABLE STUFF")
            {
                Logger.LogToFile(Logger.LogLevel.Error, "Expected \"{0}\", didn't get it.  Are you sure this is a Car.TXT file?", "START OF DRIVABLE STUFF");
                return(null);
            }

            car.DriversHeadOffset     = file.ReadVector3();
            car.DriversHeadTurnAngles = file.ReadVector2();
            car.MirrorCamera          = file.ReadVector4();
            car.PratcamBorders        = file.ReadStrings();

            if (file.ReadLine() != "END OF DRIVABLE STUFF")
            {
                Logger.LogToFile(Logger.LogLevel.Error, "Expected \"{0}\", didn't get it.  Are you sure this is a Car.TXT file?", "END OF DRIVABLE STUFF");
                return(null);
            }

            car.EngineNoises = file.ReadInts();
            car.Stealworthy  = file.ReadLine().ToLower() == "stealworthy";

            car.ImpactTop    = ImpactSpec.Load("top", file);
            car.ImpactBottom = ImpactSpec.Load("bottom", file);
            car.ImpactLeft   = ImpactSpec.Load("left", file);
            car.ImpactRight  = ImpactSpec.Load("right", file);
            car.ImpactFront  = ImpactSpec.Load("front", file);
            car.ImpactBack   = ImpactSpec.Load("rear", file);

            car.GridImages = file.ReadStrings();

            int pixCount = file.ReadInt();

            for (int i = 0; i < pixCount; i++)
            {
                car.PixelmapsLoMem.Add(file.ReadLine());
            }

            pixCount = file.ReadInt();
            for (int i = 0; i < pixCount; i++)
            {
                car.PixelmapsLoRes.Add(file.ReadLine());
            }

            pixCount = file.ReadInt();
            for (int i = 0; i < pixCount; i++)
            {
                car.PixelmapsHiRes.Add(file.ReadLine());
            }

            int shadeCount = file.ReadInt();

            for (int j = 0; j < shadeCount; j++)
            {
                car.ShadeTables.Add(file.ReadLine());
            }

            int matCount = file.ReadInt();

            for (int i = 0; i < matCount; i++)
            {
                car.MaterialsLoMem.Add(file.ReadLine());
            }

            matCount = file.ReadInt();
            for (int i = 0; i < matCount; i++)
            {
                car.MaterialsLoRes.Add(file.ReadLine());
            }

            matCount = file.ReadInt();
            for (int i = 0; i < matCount; i++)
            {
                car.MaterialsHiRes.Add(file.ReadLine());
            }

            int modelCount = file.ReadInt();

            for (int j = 0; j < modelCount; j++)
            {
                car.Models.Add(file.ReadLine());
            }

            int actorCount = file.ReadInt();

            for (int j = 0; j < actorCount; j++)
            {
                string[] s = file.ReadStrings();
                car.ActorLODs.Add(s[0].ToInt());
                car.Actors.Add(s[1]);
            }

            car.ReflectiveScreenMaterial = file.ReadLine();

            int steerableWheelsCount = file.ReadInt();

            for (int j = 0; j < steerableWheelsCount; j++)
            {
                car.SteerableWheels.Add(file.ReadInt());
            }

            car.LeftFrontSuspension  = file.ReadInts();
            car.RightFrontSuspension = file.ReadInts();
            car.LeftRearSuspension   = file.ReadInts();
            car.RightRearSuspension  = file.ReadInts();
            car.DrivenWheels         = file.ReadInts();
            car.NonDrivenWheels      = file.ReadInts();

            car.DrivenWheelDiameter    = file.ReadSingle();
            car.NonDrivenWheelDiameter = file.ReadSingle();

            if (file.ReadLine() != "START OF FUNK")
            {
                Logger.LogToFile(Logger.LogLevel.Error, "Expected \"{0}\", didn't get it.  Are you sure this is a Car.TXT file?", "START OF FUNK");
                return(null);
            }

            while (file.PeekLine() != "END OF FUNK")
            {
                car.Funks.Add(Funk.Load(file));
                if (file.PeekLine() == "NEXT FUNK")
                {
                    file.ReadLine();
                }
            }
            file.ReadLine();

            if (file.ReadLine() != "START OF GROOVE")
            {
                Logger.LogToFile(Logger.LogLevel.Error, "Expected \"{0}\", didn't get it.  Are you sure this is a Car.TXT file?", "START OF GROOVE");
                return(null);
            }

            while (file.PeekLine() != "END OF GROOVE")
            {
                car.Grooves.Add(Groove.Load(file));
                if (file.PeekLine() == "NEXT GROOVE")
                {
                    file.ReadLine();
                }
            }
            file.ReadLine();

            for (int i = 0; i < car.Actors.Count; i++)
            {
                car.Crushes.Add(Crush.Load(file));
            }

            car.MechanicsVersion = file.ReadLine().Replace("START OF MECHANICS STUFF version ", "", StringComparison.InvariantCultureIgnoreCase).ToInt();

            car.LRWheelPos   = file.ReadVector3();
            car.RRWheelPos   = file.ReadVector3();
            car.LFWheelPos   = file.ReadVector3();
            car.RFWheelPos   = file.ReadVector3();
            car.CentreOfMass = file.ReadVector3();

            switch (car.MechanicsVersion)
            {
            case 2:
                int boundingBoxCount = file.ReadInt();
                for (int i = 0; i < boundingBoxCount; i++)
                {
                    car.BoundingBoxes.Add(new BoundingBox {
                        Min = file.ReadVector3(), Max = file.ReadVector3()
                    });
                }
                break;

            case 3:
            case 4:
                car.BoundingBoxes.Add(new BoundingBox {
                    Min = file.ReadVector3(), Max = file.ReadVector3()
                });

                int additionalPointsCount = file.ReadInt();
                for (int i = 0; i < additionalPointsCount; i++)
                {
                    car.AdditionalPoints.Add(file.ReadVector3());
                }
                break;
            }

            car.MinimumTurningCircle = file.ReadSingle();
            car.SuspensionGive       = file.ReadVector2();
            car.RideHeight           = file.ReadSingle();
            car.DampingFactor        = file.ReadSingle();
            car.Mass = file.ReadSingle();
            car.SlipFrictionReductionFraction = file.ReadSingle();

            if (car.MechanicsVersion == 4)
            {
                car.FrictionAngle = file.ReadVector3();
            }
            else
            {
                car.FrictionAngle = (Vector3)file.ReadVector2();
            }

            car.AngularMomentumProportions    = file.ReadVector3();
            car.TractionFractionalMultiplier  = file.ReadSingle();
            car.DownforceToWeightBalanceSpeed = file.ReadSingle();
            car.BrakeMultiplier           = file.ReadSingle();
            car.BrakingStrengthMultiplier = file.ReadSingle();
            car.RollingResistance         = file.ReadVector2();
            car.NumberOfGears             = file.ReadInt();
            car.TopGearRedlineSpeed       = file.ReadInt();
            car.TopGearAcceleration       = file.ReadSingle();

            if (file.ReadLine() != "END OF MECHANICS STUFF")
            {
                Logger.LogToFile(Logger.LogLevel.Error, "Expected \"{0}\", didn't get it.  Are you sure this is a Car.TXT file?", "END OF MECHANICS STUFF");
                return(null);
            }

            int shrapnelCount = file.ReadInt();

            for (int i = 0; i < shrapnelCount; i++)
            {
                car.Shrapnel.Add(file.ReadLine());
            }

            if (!file.EOF)
            {
                for (int i = 0; i < 12; i++)
                {
                    car.FirePoints.Add(file.ReadInt());
                }
            }

            if (!file.EOF)
            {
                Console.WriteLine("Still data to parse in {0}", path);
            }

            return(car);
        }
Example #2
0
        public static Car Load(string path)
        {
            DocumentParser file = new DocumentParser(path);
            Car            car  = new Car();

            string version = file.ReadLine();

            if (!version.StartsWith("VERSION"))
            {
                Logger.LogToFile(Logger.LogLevel.Error, "Not a valid Carmageddon 2 car .txt file");
                return(null);
            }

            car.Version = version.Replace("VERSION ", "").ToInt();

            if (file.PeekLine() == "GIRL")
            {
                car.FemaleDriver = true;
                file.ReadLine();
            }

            car.Name           = file.ReadLine();
            car.SoftnessFactor = file.ReadSingle();

            if (file.ReadLine() != "START OF DRIVABLE STUFF")
            {
                Logger.LogToFile(Logger.LogLevel.Error, "Expected \"{0}\", didn't get it.  Are you sure this is a Car.TXT file?", "START OF DRIVABLE STUFF");
                return(null);
            }

            car.DriversHeadOffset     = file.ReadVector3();
            car.DriversHeadTurnAngles = file.ReadVector2();
            car.MirrorCamera          = file.ReadVector4();
            car.PratcamBorders        = file.ReadStrings();

            if (file.ReadLine() != "END OF DRIVABLE STUFF")
            {
                Logger.LogToFile(Logger.LogLevel.Error, "Expected \"{0}\", didn't get it.  Are you sure this is a Car.TXT file?", "END OF DRIVABLE STUFF");
                return(null);
            }

            car.EngineNoises = file.ReadInts();
            car.Stealworthy  = file.ReadLine().ToLower() == "stealworthy";

            car.ImpactTop    = ImpactSpec.Load("top", file);
            car.ImpactBottom = ImpactSpec.Load("bottom", file);
            car.ImpactLeft   = ImpactSpec.Load("left", file);
            car.ImpactRight  = ImpactSpec.Load("right", file);
            car.ImpactFront  = ImpactSpec.Load("front", file);
            car.ImpactBack   = ImpactSpec.Load("rear", file);

            car.GridImages = file.ReadStrings();

            int extraLevelsOfDetail = file.ReadInt();

            for (int j = 0; j < extraLevelsOfDetail; j++)
            {
                car.ExtraLevelsOfDetail.Add(file.ReadInt());
            }

            car.WAM = file.ReadLine();

            car.ReflectiveScreenMaterial = file.ReadLine();
            car.TransparencyOfWindscreen = file.ReadSingle();

            int steerableWheelsCount = file.ReadInt();

            for (int j = 0; j < steerableWheelsCount; j++)
            {
                car.SteerableWheels.Add(file.ReadInt());
            }

            car.LeftFrontSuspension  = file.ReadInts();
            car.RightFrontSuspension = file.ReadInts();
            car.LeftRearSuspension   = file.ReadInts();
            car.RightRearSuspension  = file.ReadInts();
            car.DrivenWheels         = file.ReadInts();
            car.NonDrivenWheels      = file.ReadInts();

            car.DrivenWheelDiameter    = file.ReadSingle();
            car.NonDrivenWheelDiameter = file.ReadSingle();

            if (file.ReadLine() != "START OF FUNK")
            {
                Logger.LogToFile(Logger.LogLevel.Error, "Expected \"{0}\", didn't get it.  Are you sure this is a Car.TXT file?", "START OF FUNK");
                return(null);
            }

            while (file.PeekLine() != "END OF FUNK")
            {
                car.Funks.Add(Funk.Load(file));
                if (file.PeekLine() == "NEXT FUNK")
                {
                    file.ReadLine();
                }
            }
            file.ReadLine();

            if (file.ReadLine() != "START OF GROOVE")
            {
                Logger.LogToFile(Logger.LogLevel.Error, "Expected \"{0}\", didn't get it.  Are you sure this is a Car.TXT file?", "START OF GROOVE");
                return(null);
            }

            while (file.PeekLine() != "END OF GROOVE")
            {
                car.Grooves.Add(Groove.Load(file));
                if (file.PeekLine() == "NEXT GROOVE")
                {
                    file.ReadLine();
                }
            }
            file.ReadLine();

            int _ = file.ReadLine().Replace("START OF MECHANICS STUFF version ", "", StringComparison.InvariantCultureIgnoreCase).ToInt();

            car.MinimumTurningCircle      = file.ReadSingle();
            car.BrakeMultiplier           = file.ReadSingle();
            car.BrakingStrengthMultiplier = file.ReadSingle();
            car.NumberOfGears             = file.ReadSingle();
            car.TopGearRedlineSpeed       = file.ReadSingle();
            car.TopGearAcceleration       = file.ReadSingle();

            car.RootPartType       = file.ReadEnum <PartType>();
            car.RootPartIdentifier = file.ReadLine();
            car.RootPartActor      = file.ReadLine();

            car.SubPartType = file.ReadEnum <PartType>();

            car.CentreOfMass = file.ReadVector3();
            car.Mass         = file.ReadSingle();
            car.AngularMomentumProportions    = file.ReadVector3();
            car.DownforceToWeightBalanceSpeed = file.ReadSingle();

            int numberOfWheels = file.ReadInt();

            for (int j = 0; j < numberOfWheels; j++)
            {
                Wheel wheel = new Wheel
                {
                    Type           = file.ReadEnum <Wheel.WheelType>(),
                    Identifier     = file.ReadLine(),
                    Actor          = file.ReadLine(),
                    Position       = file.ReadVector3(),
                    SteerableFlags = file.ReadInt(),
                    DrivenFlags    = file.ReadInt(),
                    SuspensionGive = file.ReadSingle(),
                    DampingFactor  = file.ReadSingle(),
                    SlipFrictionReductionFraction = file.ReadSingle(),
                    FrictionAngles = file.ReadVector2(),
                    TractionFractionalMultiplier = file.ReadSingle(),
                    RollingResistance            = file.ReadSingle()
                };

                car.Wheels.Add(wheel);
            }

            int boundingShapes = file.ReadInt();

            for (int j = 0; j < boundingShapes; j++)
            {
                BoundingShape shape = new BoundingShape
                {
                    Type = file.ReadEnum <BoundingShape.BoundingShapeType>()
                };

                int shapePoints = file.ReadInt();

                for (int k = 0; k < shapePoints; k++)
                {
                    shape.Points.Add(file.ReadVector3());
                }

                car.BoundingShapes.Add(shape);
            }

            int subParts = file.ReadInt();

            for (int j = 0; j < subParts; j++)
            {
                throw new NotImplementedException("subparts?!");
            }

            if (file.ReadLine() != "END OF MECHANICS STUFF")
            {
                Logger.LogToFile(Logger.LogLevel.Error, "Expected \"{0}\", didn't get it.  Are you sure this is a Car.TXT file?", "END OF MECHANICS STUFF");
                return(null);
            }

            int shrapnelCount = file.ReadInt();

            for (int i = 0; i < shrapnelCount; i++)
            {
                car.Shrapnel.Add(file.ReadLine());
            }

            for (int i = 0; i < 12; i++)
            {
                car.FirePoints.Add(file.ReadInt());
            }

            while (file.PeekLine() != "END")
            {
                string keyWord = file.ReadLine();

                switch (keyWord)
                {
                case "CAMERA_POSITIONS":
                    car.Keywords.Add(new CameraPosition
                    {
                        BumperPosition  = file.ReadVector3(),
                        CockpitPosition = file.ReadVector3()
                    });
                    break;

                case "CAMERA_TURN_OFF_MATERIALS":
                {
                    CameraTurnOffMaterials ctom = new CameraTurnOffMaterials();

                    int materialCount = file.ReadInt();

                    for (int i = 0; i < materialCount; i++)
                    {
                        CameraTurnOffMaterialEntry ctomEntry = new CameraTurnOffMaterialEntry
                        {
                            MaterialName = file.ReadLine()
                        };

                        int entryCount = file.ReadInt();

                        for (int j = 0; j < entryCount; j++)
                        {
                            ctomEntry.Materials.Add(file.ReadLine());
                        }

                        ctom.Entries.Add(ctomEntry);
                    }

                    car.Keywords.Add(ctom);
                }
                break;

                default:
                    throw new NotImplementedException($"{keyWord} not supported!");
                }
            }

            return(car);
        }
Example #3
0
        public static Map Load(string path)
        {
            DocumentParser file = new DocumentParser(path);
            Map            map  = new Map();

            string version = file.ReadLine();

            if (!version.StartsWith("VERSION"))
            {
                Logger.LogToFile(Logger.LogLevel.Error, "Not a valid Carmageddon 2 race .txt file");
                return(null);
            }

            map.Version = version.Replace("VERSION ", "").ToInt();

            if (map.Version == 1)
            {
                // V1 has global lighting data
                map.LightColour       = file.ReadVector3(); // RGB for main directional light-source
                map.DiffuseLight0     = file.ReadVector2(); // Ambient/Diffuse light to be used when plaything ambient says 0
                map.DiffuseLight1     = file.ReadVector2(); // Ambient/Diffuse light to be used when plaything ambient says 1
                map.DiffuseLightOther = file.ReadVector2(); // Ambient/Diffuse light to be used when plaything ambient says anything else
            }

            map.GridPosition = file.ReadVector3();          // Position of centre of start of grid
            map.GridRotation = file.ReadInt();              // Direction that grid faces in

            //string t = getNextLine(sr);

            //if (version == 8)
            //{
            //    // alpha and demo v8 files had an extra block here, we'll test if it
            //    if (t.Contains(","))
            //    {
            //        getNextLine(sr); // # laps
            //        getNextLine(sr); // Race completed bonus (all laps raced) for each skill level
            //        getNextLine(sr); // Race completed bonus (all peds killed) for each skill level
            //        getNextLine(sr); // Race completed bonus (all oppos wasted) for each skill level

            //        t = getNextLine(sr);
            //    }
            //}

            int checkpointCount = file.ReadInt();

            for (int i = 0; i < checkpointCount; i++)
            {
                Checkpoint cp = new Checkpoint()
                {
                    TimerIncrements = file.ReadVector3()
                };
                int quadCount = file.ReadInt();

                for (int j = 0; j < quadCount; j++)
                {
                    cp.Points.Add(file.ReadVector3());
                    cp.Points.Add(file.ReadVector3());
                    cp.Points.Add(file.ReadVector3());
                    cp.Points.Add(file.ReadVector3());
                }

                map.Checkpoints.Add(cp);
            }

            int smashSpecs = file.ReadInt();

            for (int i = 0; i < smashSpecs; i++)
            {
                SmashData smashable = new SmashData
                {
                    Flags   = file.ReadInt(),
                    Trigger = file.ReadLine()
                };

                if (smashable.Trigger.Length == 3 && smashable.Trigger.StartsWith("&"))
                {
                    smashable.TriggerFlags = file.ReadInt();
                }

                smashable.TriggerMode = file.ReadEnum <SmashData.SmashTriggerMode>();

                switch (smashable.TriggerMode)
                {
                case SmashData.SmashTriggerMode.nochange:
                case SmashData.SmashTriggerMode.remove:
                    smashable.RemovalThreshold = file.ReadSingle();
                    smashable.Connotations.Load(file);
                    break;

                case SmashData.SmashTriggerMode.replacemodel:
                    smashable.RemovalThreshold = file.ReadSingle();
                    smashable.Connotations.Load(file);
                    smashable.NewModel     = file.ReadLine();
                    smashable.ChanceOfFire = file.ReadInt();

                    if (smashable.ChanceOfFire > 0)
                    {
                        smashable.NumFires   = file.ReadInt();
                        smashable.SmokeLevel = file.ReadInts();
                    }
                    break;

                case SmashData.SmashTriggerMode.texturechange:
                    smashable.IntactMaterial = file.ReadLine();
                    int textureLevels = file.ReadInt();

                    for (int j = 0; j < textureLevels; j++)
                    {
                        SmashDataTextureLevel textureLevel = new SmashDataTextureLevel()
                        {
                            TriggerThreshold = file.ReadSingle(),
                            Flags            = file.ReadInt(),
                            CollisionType    = file.ReadEnum <SmashDataTextureLevel.TextureLevelCollisionType>()
                        };

                        textureLevel.Connotations.Load(file);

                        int pixelmaps = file.ReadInt();
                        for (int k = 0; k < pixelmaps; k++)
                        {
                            textureLevel.Pixelmaps.Add(file.ReadLine());
                        }

                        smashable.Levels.Add(textureLevel);
                    }
                    break;

                default:
                    throw new NotImplementedException($"Unknown TriggerMode '{smashable.TriggerMode}'");
                }

                smashable.Reserved1 = file.ReadInt();
                smashable.Reserved2 = file.ReadInt();
                smashable.Reserved3 = file.ReadInt();
                smashable.Reserved4 = file.ReadInt();

                map.Smashables.Add(smashable);
            }

            int pedSpecs = file.ReadInt();

            for (int i = 0; i < pedSpecs; i++)
            {
                PedSpec ps = new PedSpec()
                {
                    MaterialName           = file.ReadLine(),
                    MovementIndex          = file.ReadInt(),
                    GroupIndex             = file.ReadInt(),
                    PedsPer100SquareMetres = file.ReadSingle()
                };

                int exclusionCount = file.ReadInt();
                for (int j = 0; j < exclusionCount; j++)
                {
                    ps.ExclusionMaterials.Add(new PedExclusionMaterial
                    {
                        Flags = file.ReadInt(),
                        Name  = file.ReadLine()
                    });
                }

                int exceptionCount = file.ReadInt();
                for (int j = 0; j < exceptionCount; j++)
                {
                    ps.ExceptionMaterials.Add(file.ReadLine());
                }

                map.PedSpecs.Add(ps);
            }

            map.AdditionalActor = file.ReadLine();

            map.SkyPixelmap           = file.ReadLine();
            map.HorizontalRepetitions = file.ReadInt();
            map.VerticalSize          = file.ReadInt();
            map.PositionOfHorizon     = file.ReadInt();
            map.DepthCueMode          = file.ReadLine();
            map.FogDarkness           = file.ReadVector2();
            map.DepthCueColour        = file.ReadVector3();

            map.DefaultEngineNoise = file.ReadInt();

            int specialEffectVolumeCount = file.ReadInt();

            for (int i = 0; i < specialEffectVolumeCount; i++)
            {
                SpecialEffectVolume sev = new SpecialEffectVolume {
                    Type = file.ReadLine()
                };

                if (sev.Type.ToUpper() == "BOX")
                {
                    sev.Corners.Add(file.ReadVector3());
                    sev.Corners.Add(file.ReadVector3());
                    sev.Corners.Add(file.ReadVector3());
                    sev.Corners.Add(file.ReadVector3());
                }

                sev.GravityMultiplier       = file.ReadSingle();
                sev.ViscosityMultiplier     = file.ReadSingle();
                sev.CarDamagePerMillisecond = file.ReadSingle();
                sev.PedDamagePerMillisecond = file.ReadSingle();
                sev.CameraEffectIndex       = file.ReadInt();

                if (file.PeekLine().Contains(","))
                {
                    sev.SkyColourRGB = file.ReadVector3();
                }
                else
                {
                    sev.SkyColour = file.ReadInt();
                }

                sev.WindscreenMaterial = file.ReadLine();
                sev.EntrySoundID       = file.ReadInt();
                sev.ExitSoundID        = file.ReadInt();
                sev.EngineNoiseIndex   = file.ReadInt();
                sev.MaterialIndex      = file.ReadInt();

                if (sev.Type.ToUpper() == "BOX")
                {
                    sev.SoundType = file.ReadEnum <SpecialEffectVolume.SpecVolSoundType>();

                    if (sev.SoundType != SpecialEffectVolume.SpecVolSoundType.NONE)
                    {
                        sev.SoundSpec = SoundSpec.Load(file);
                    }
                }

                map.SpecialVolumes.Add(sev);
            }

            int soundGeneratorCount = file.ReadInt();

            if (soundGeneratorCount > 0)
            {
                throw new NotImplementedException("Can't handle sound generators yet!");
            }

            map.MaterialDefault  = file.ReadLine();
            map.MaterialDarkness = file.ReadLine();
            map.MaterialFog      = file.ReadLine();

            file.ReadLine();    // (ignore) # areas with different screens

            map.MapPixelmap       = file.ReadLine();
            map.WorldMapTransform = new Matrix3D(
                file.ReadVector3(),
                file.ReadVector3(),
                file.ReadVector3(),
                file.ReadVector3()
                );

            if (file.ReadLine() != "START OF FUNK")
            {
                Logger.LogToFile(Logger.LogLevel.Error, "Expected \"{0}\", didn't get it.  Are you sure this is a Map.TXT file?", "START OF FUNK");
                return(null);
            }

            while (file.PeekLine() != "END OF FUNK")
            {
                map.Funks.Add(Funk.Load(file));
                if (file.PeekLine() == "NEXT FUNK")
                {
                    file.ReadLine();
                }
            }
            file.ReadLine();

            if (file.ReadLine() != "START OF GROOVE")
            {
                Logger.LogToFile(Logger.LogLevel.Error, "Expected \"{0}\", didn't get it.  Are you sure this is a Map.TXT file?", "START OF GROOVE");
                return(null);
            }

            while (file.PeekLine() != "END OF GROOVE")
            {
                map.Grooves.Add(Groove.Load(file));
                if (file.PeekLine() == "NEXT GROOVE")
                {
                    file.ReadLine();
                }
            }
            file.ReadLine();

            if (file.ReadLine() != "START OF OPPONENT PATHS")
            {
                Logger.LogToFile(Logger.LogLevel.Error, "Not a valid Carmageddon 2 race .txt file");
                return(null);
            }

            int nodeCount = file.ReadInt();

            for (int i = 0; i < nodeCount; i++)
            {
                map.Nodes.Add(file.ReadVector3());
            }

            int pathCount = file.ReadInt();

            for (int i = 0; i < pathCount; i++)
            {
                string[] parts = file.ReadLine().Split(',');

                map.Paths.Add(new OpponentPathSection
                {
                    StartNode   = parts[0].ToInt(),
                    EndNode     = parts[1].ToInt(),
                    Unknown1    = parts[2].ToInt(),
                    Unknown2    = parts[3].ToInt(),
                    Unknown3    = parts[4].ToInt(),
                    Unknown4    = parts[5].ToInt(),
                    Width       = parts[6].ToSingle(),
                    SectionType = parts[7].ToInt()
                });
            }

            int copStartPoints = file.ReadInt();

            if (copStartPoints > 0)
            {
                throw new NotImplementedException("Cop start points?  Really?");
            }

            if (file.ReadLine() != "END OF OPPONENT PATHS")
            {
                Logger.LogToFile(Logger.LogLevel.Error, "Not a valid Carmageddon 2 race .txt file");
                return(null);
            }

            if (file.ReadLine() != "START OF DRONE PATHS")
            {
                Logger.LogToFile(Logger.LogLevel.Error, "Not a valid Carmageddon 2 race .txt file");
                return(null);
            }

            map.DronePathVersion = file.ReadInt();

            int dronepathNodeCount = file.ReadInt();

            for (int i = 0; i < dronepathNodeCount; i++)
            {
                DronePathNode node = new DronePathNode
                {
                    Position  = file.ReadVector3(),
                    DroneName = file.ReadLine()
                };

                if (map.DronePathVersion == 0)
                {
                    node.UnknownVector = file.ReadVector3();
                }
                else
                {
                    node.UnknownInt = file.ReadInt();
                }

                int nextNodeCount = file.ReadInt();
                for (int j = 0; j < nextNodeCount; j++)
                {
                    node.Destinations.Add(file.ReadLine());
                }

                map.DronePaths.Add(node);
            }

            if (file.ReadLine() != "END OF DRONE PATHS")
            {
                Logger.LogToFile(Logger.LogLevel.Error, "Not a valid Carmageddon 2 race .txt file");
                return(null);
            }

            int materialModifierCount = file.ReadInt();

            for (int i = 0; i < materialModifierCount; i++)
            {
                map.MaterialModifiers.Add(new MaterialModifier
                {
                    CarWallFriction  = file.ReadSingle(),
                    TyreRoadFriction = file.ReadSingle(),
                    DownForce        = file.ReadSingle(),
                    Bumpiness        = file.ReadSingle(),
                    TyreSoundIndex   = file.ReadInt(),
                    CrashSoundIndex  = file.ReadInt(),
                    ScrapeNoiseIndex = file.ReadInt(),
                    Sparkiness       = file.ReadSingle(),
                    RoomForExpansion = file.ReadInt(),
                    SkidmarkMaterial = file.ReadLine()
                });
            }

            int noncarCount = file.ReadInt();

            for (int i = 0; i < noncarCount; i++)
            {
                map.Noncars.Add(file.ReadLine());
            }

            int shadetableCount = file.ReadInt();

            for (int i = 0; i < shadetableCount; i++)
            {
                map.ShadeTables.Add(new ShadeTable
                {
                    RGB       = file.ReadVector3(),
                    Strengths = file.ReadVector3()
                });
            }

            int networkStartCount = file.ReadInt();

            for (int i = 0; i < networkStartCount; i++)
            {
                map.NetworkStarts.Add(new NetworkStart
                {
                    Position = file.ReadVector3(),
                    Rotation = file.ReadInt()
                });
            }

            int splashfileCount = file.ReadInt();

            for (int i = 0; i < splashfileCount; i++)
            {
                map.SplashFiles.Add(file.ReadLine());
            }

            int txtfileCount = file.ReadInt();

            for (int i = 0; i < txtfileCount; i++)
            {
                map.TxtFiles.Add(file.ReadLine());
            }

            return(map);
        }