Esempio n. 1
0
        // parse extensions config
        internal static void ParseExtensionsConfig(string TrainPath, System.Text.Encoding Encoding, ref UnifiedObject[] CarObjects, ref UnifiedObject[] BogieObjects, ref UnifiedObject[] CouplerObjects, TrainManager.Train Train, bool LoadObjects)
        {
            bool[] CarObjectsReversed   = new bool[Train.Cars.Length];
            bool[] BogieObjectsReversed = new bool[Train.Cars.Length * 2];

            bool[] CarsDefined   = new bool[Train.Cars.Length];
            bool[] BogiesDefined = new bool[Train.Cars.Length * 2];
            System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture;
            string FileName = OpenBveApi.Path.CombineFile(TrainPath, "extensions.cfg");

            if (System.IO.File.Exists(FileName))
            {
                Encoding = TextEncoding.GetSystemEncodingFromFile(FileName, Encoding);

                string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding);
                for (int i = 0; i < Lines.Length; i++)
                {
                    int j = Lines[i].IndexOf(';');
                    if (j >= 0)
                    {
                        Lines[i] = Lines[i].Substring(0, j).Trim(new char[] { });
                    }
                    else
                    {
                        Lines[i] = Lines[i].Trim(new char[] { });
                    }
                }
                for (int i = 0; i < Lines.Length; i++)
                {
                    if (Lines[i].Length != 0)
                    {
                        switch (Lines[i].ToLowerInvariant())
                        {
                        case "[exterior]":
                            // exterior
                            i++;
                            while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal) & !Lines[i].EndsWith("]", StringComparison.Ordinal))
                            {
                                if (Lines[i].Length != 0)
                                {
                                    int j = Lines[i].IndexOf("=", StringComparison.Ordinal);
                                    if (j >= 0)
                                    {
                                        string a = Lines[i].Substring(0, j).TrimEnd(new char[] { });
                                        string b = Lines[i].Substring(j + 1).TrimStart(new char[] { });
                                        int    n;
                                        if (int.TryParse(a, System.Globalization.NumberStyles.Integer, Culture, out n))
                                        {
                                            if (n >= 0 & n < Train.Cars.Length)
                                            {
                                                if (Path.ContainsInvalidChars(b))
                                                {
                                                    Interface.AddMessage(MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                }
                                                else
                                                {
                                                    string File = OpenBveApi.Path.CombineFile(TrainPath, b);
                                                    if (System.IO.File.Exists(File))
                                                    {
                                                        if (LoadObjects)
                                                        {
                                                            Program.CurrentHost.LoadObject(File, Encoding, out CarObjects[n]);
                                                        }
                                                    }
                                                    else
                                                    {
                                                        Interface.AddMessage(MessageType.Error, true, "The car object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                    }
                                                }
                                            }
                                            else
                                            {
                                                Interface.AddMessage(MessageType.Error, false, "The car index " + a + " does not reference an existing car at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                            }
                                        }
                                        else
                                        {
                                            Interface.AddMessage(MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        }
                                    }
                                    else
                                    {
                                        Interface.AddMessage(MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                    }
                                }
                                i++;
                            }
                            i--;
                            break;

                        default:
                            if (Lines[i].StartsWith("[car", StringComparison.OrdinalIgnoreCase) & Lines[i].EndsWith("]", StringComparison.Ordinal))
                            {
                                // car
                                string t = Lines[i].Substring(4, Lines[i].Length - 5);
                                int    n; if (int.TryParse(t, System.Globalization.NumberStyles.Integer, Culture, out n))
                                {
                                    if (n >= 0 & n < Train.Cars.Length)
                                    {
                                        if (CarsDefined[n])
                                        {
                                            Interface.AddMessage(MessageType.Error, false, "Car " + n.ToString(Culture) + " has already been declared at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        }
                                        CarsDefined[n] = true;
                                        bool DefinedLength = false;
                                        bool DefinedAxles  = false;
                                        i++;
                                        while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal) & !Lines[i].EndsWith("]", StringComparison.Ordinal))
                                        {
                                            if (Lines[i].Length != 0)
                                            {
                                                int j = Lines[i].IndexOf("=", StringComparison.Ordinal);
                                                if (j >= 0)
                                                {
                                                    string a = Lines[i].Substring(0, j).TrimEnd(new char[] { });
                                                    string b = Lines[i].Substring(j + 1).TrimStart(new char[] { });
                                                    switch (a.ToLowerInvariant())
                                                    {
                                                    case "object":
                                                        if (string.IsNullOrEmpty(b))
                                                        {
                                                            Interface.AddMessage(MessageType.Error, true, "An empty car object was supplied at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                            break;
                                                        }
                                                        if (Path.ContainsInvalidChars(b))
                                                        {
                                                            Interface.AddMessage(MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                        }
                                                        else
                                                        {
                                                            string File = OpenBveApi.Path.CombineFile(TrainPath, b);
                                                            if (System.IO.File.Exists(File))
                                                            {
                                                                if (LoadObjects)
                                                                {
                                                                    Program.CurrentHost.LoadObject(File, Encoding, out CarObjects[n]);
                                                                }
                                                            }
                                                            else
                                                            {
                                                                Interface.AddMessage(MessageType.Error, true, "The car object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                            }
                                                        }
                                                        break;

                                                    case "length":
                                                    {
                                                        double m;
                                                        if (double.TryParse(b, System.Globalization.NumberStyles.Float, Culture, out m))
                                                        {
                                                            if (m > 0.0)
                                                            {
                                                                Train.Cars[n].Length = m;
                                                                Train.Cars[n].BeaconReceiverPosition = 0.5 * m;
                                                                DefinedLength = true;
                                                            }
                                                            else
                                                            {
                                                                Interface.AddMessage(MessageType.Error, false, "Value is expected to be a positive floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                            }
                                                        }
                                                        else
                                                        {
                                                            Interface.AddMessage(MessageType.Error, false, "Value is expected to be a positive floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                        }
                                                    }
                                                    break;

                                                    case "axles":
                                                    {
                                                        int k = b.IndexOf(',');
                                                        if (k >= 0)
                                                        {
                                                            string c = b.Substring(0, k).TrimEnd(new char[] { });
                                                            string d = b.Substring(k + 1).TrimStart(new char[] { });
                                                            double rear, front;
                                                            if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out rear))
                                                            {
                                                                Interface.AddMessage(MessageType.Error, false, "Rear is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                            }
                                                            else if (!double.TryParse(d, System.Globalization.NumberStyles.Float, Culture, out front))
                                                            {
                                                                Interface.AddMessage(MessageType.Error, false, "Front is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                            }
                                                            else if (rear >= front)
                                                            {
                                                                Interface.AddMessage(MessageType.Error, false, "Rear is expected to be less than Front in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                            }
                                                            else
                                                            {
                                                                Train.Cars[n].RearAxle.Position  = rear;
                                                                Train.Cars[n].FrontAxle.Position = front;
                                                                DefinedAxles = true;
                                                            }
                                                        }
                                                        else
                                                        {
                                                            Interface.AddMessage(MessageType.Error, false, "An argument-separating comma is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                        }
                                                    }
                                                    break;

                                                    case "reversed":
                                                        CarObjectsReversed[n] = b.Equals("true", StringComparison.OrdinalIgnoreCase);
                                                        break;

                                                    case "loadingsway":
                                                        Train.Cars[n].EnableLoadingSway = b.Equals("true", StringComparison.OrdinalIgnoreCase);
                                                        break;

                                                    default:
                                                        Interface.AddMessage(MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                        break;
                                                    }
                                                }
                                                else
                                                {
                                                    Interface.AddMessage(MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                }
                                            }
                                            i++;
                                        }
                                        i--;
                                        if (DefinedLength & !DefinedAxles)
                                        {
                                            double AxleDistance = 0.4 * Train.Cars[n].Length;
                                            Train.Cars[n].RearAxle.Position  = -AxleDistance;
                                            Train.Cars[n].FrontAxle.Position = AxleDistance;
                                        }
                                    }
                                    else
                                    {
                                        Interface.AddMessage(MessageType.Error, false, "The car index " + t + " does not reference an existing car at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                    }
                                }
                                else
                                {
                                    Interface.AddMessage(MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                }
                            }
                            else if (Lines[i].StartsWith("[coupler", StringComparison.OrdinalIgnoreCase) & Lines[i].EndsWith("]", StringComparison.Ordinal))
                            {
                                // coupler
                                string t = Lines[i].Substring(8, Lines[i].Length - 9);
                                int    n; if (int.TryParse(t, System.Globalization.NumberStyles.Integer, Culture, out n))
                                {
                                    if (n >= 0 & n < Train.Cars.Length - 1)
                                    {
                                        i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal) & !Lines[i].EndsWith("]", StringComparison.Ordinal))
                                        {
                                            if (Lines[i].Length != 0)
                                            {
                                                int j = Lines[i].IndexOf("=", StringComparison.Ordinal);
                                                if (j >= 0)
                                                {
                                                    string a = Lines[i].Substring(0, j).TrimEnd(new char[] { });
                                                    string b = Lines[i].Substring(j + 1).TrimStart(new char[] { });
                                                    switch (a.ToLowerInvariant())
                                                    {
                                                    case "distances":
                                                    {
                                                        int k = b.IndexOf(',');
                                                        if (k >= 0)
                                                        {
                                                            string c = b.Substring(0, k).TrimEnd(new char[] { });
                                                            string d = b.Substring(k + 1).TrimStart(new char[] { });
                                                            double min, max;
                                                            if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out min))
                                                            {
                                                                Interface.AddMessage(MessageType.Error, false, "Minimum is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                            }
                                                            else if (!double.TryParse(d, System.Globalization.NumberStyles.Float, Culture, out max))
                                                            {
                                                                Interface.AddMessage(MessageType.Error, false, "Maximum is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                            }
                                                            else if (min > max)
                                                            {
                                                                Interface.AddMessage(MessageType.Error, false, "Minimum is expected to be less than Maximum in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                            }
                                                            else
                                                            {
                                                                Train.Cars[n].Coupler.MinimumDistanceBetweenCars = min;
                                                                Train.Cars[n].Coupler.MaximumDistanceBetweenCars = max;
                                                            }
                                                        }
                                                        else
                                                        {
                                                            Interface.AddMessage(MessageType.Error, false, "An argument-separating comma is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                        }
                                                    } break;

                                                    case "object":
                                                        if (string.IsNullOrEmpty(b))
                                                        {
                                                            Interface.AddMessage(MessageType.Error, true, "An empty coupler object was supplied at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                            break;
                                                        }
                                                        if (Path.ContainsInvalidChars(b))
                                                        {
                                                            Interface.AddMessage(MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                        }
                                                        else
                                                        {
                                                            string File = OpenBveApi.Path.CombineFile(TrainPath, b);
                                                            if (System.IO.File.Exists(File))
                                                            {
                                                                if (LoadObjects)
                                                                {
                                                                    Program.CurrentHost.LoadObject(File, Encoding, out CouplerObjects[n]);
                                                                }
                                                            }
                                                            else
                                                            {
                                                                Interface.AddMessage(MessageType.Error, true, "The coupler object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                            }
                                                        }
                                                        break;

                                                    default:
                                                        Interface.AddMessage(MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                        break;
                                                    }
                                                }
                                                else
                                                {
                                                    Interface.AddMessage(MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                }
                                            }
                                            i++;
                                        }
                                        i--;
                                    }
                                    else
                                    {
                                        Interface.AddMessage(MessageType.Error, false, "The coupler index " + t + " does not reference an existing coupler at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                    }
                                }
                                else
                                {
                                    Interface.AddMessage(MessageType.Error, false, "The coupler index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                }
                            }
                            else if (Lines[i].StartsWith("[bogie", StringComparison.OrdinalIgnoreCase) & Lines[i].EndsWith("]", StringComparison.Ordinal))
                            {
                                // car
                                string t = Lines[i].Substring(6, Lines[i].Length - 7);
                                int    n; if (int.TryParse(t, System.Globalization.NumberStyles.Integer, Culture, out n))
                                {
                                    if (n > BogiesDefined.Length - 1)
                                    {
                                        continue;
                                    }
                                    if (BogiesDefined[n])
                                    {
                                        Interface.AddMessage(MessageType.Error, false, "Bogie " + n.ToString(Culture) + " has already been declared at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                    }
                                    BogiesDefined[n] = true;
                                    //Assuming that there are two bogies per car
                                    bool IsOdd    = (n % 2 != 0);
                                    int  CarIndex = n / 2;
                                    if (n >= 0 & n < Train.Cars.Length * 2)
                                    {
                                        bool DefinedAxles = false;
                                        i++;
                                        while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal) & !Lines[i].EndsWith("]", StringComparison.Ordinal))
                                        {
                                            if (Lines[i].Length != 0)
                                            {
                                                int j = Lines[i].IndexOf("=", StringComparison.Ordinal);
                                                if (j >= 0)
                                                {
                                                    string a = Lines[i].Substring(0, j).TrimEnd(new char[] { });
                                                    string b = Lines[i].Substring(j + 1).TrimStart(new char[] { });
                                                    switch (a.ToLowerInvariant())
                                                    {
                                                    case "object":
                                                        if (Path.ContainsInvalidChars(b))
                                                        {
                                                            Interface.AddMessage(MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                        }
                                                        else
                                                        {
                                                            if (string.IsNullOrEmpty(b))
                                                            {
                                                                Interface.AddMessage(MessageType.Error, true, "An empty bogie object was supplied at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                                break;
                                                            }
                                                            string File = OpenBveApi.Path.CombineFile(TrainPath, b);
                                                            if (System.IO.File.Exists(File))
                                                            {
                                                                if (LoadObjects)
                                                                {
                                                                    Program.CurrentHost.LoadObject(File, Encoding, out BogieObjects[n]);
                                                                }
                                                            }
                                                            else
                                                            {
                                                                Interface.AddMessage(MessageType.Error, true, "The bogie object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                            }
                                                        }
                                                        break;

                                                    case "length":
                                                    {
                                                        Interface.AddMessage(MessageType.Error, false, "A defined length is not supported for bogies at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                    }
                                                    break;

                                                    case "axles":
                                                    {
                                                        int k = b.IndexOf(',');
                                                        if (k >= 0)
                                                        {
                                                            string c = b.Substring(0, k).TrimEnd(new char[] { });
                                                            string d = b.Substring(k + 1).TrimStart(new char[] { });
                                                            double rear, front;
                                                            if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out rear))
                                                            {
                                                                Interface.AddMessage(MessageType.Error, false, "Rear is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                            }
                                                            else if (!double.TryParse(d, System.Globalization.NumberStyles.Float, Culture, out front))
                                                            {
                                                                Interface.AddMessage(MessageType.Error, false, "Front is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                            }
                                                            else if (rear >= front)
                                                            {
                                                                Interface.AddMessage(MessageType.Error, false, "Rear is expected to be less than Front in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                            }
                                                            else
                                                            {
                                                                if (IsOdd)
                                                                {
                                                                    Train.Cars[CarIndex].FrontBogie.RearAxle.Position  = rear;
                                                                    Train.Cars[CarIndex].FrontBogie.FrontAxle.Position = front;
                                                                }
                                                                else
                                                                {
                                                                    Train.Cars[CarIndex].RearBogie.RearAxle.Position  = rear;
                                                                    Train.Cars[CarIndex].RearBogie.FrontAxle.Position = front;
                                                                }
                                                                DefinedAxles = true;
                                                            }
                                                        }
                                                        else
                                                        {
                                                            Interface.AddMessage(MessageType.Error, false, "An argument-separating comma is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                        }
                                                    }
                                                    break;

                                                    case "reversed":
                                                        BogieObjectsReversed[n] = b.Equals("true", StringComparison.OrdinalIgnoreCase);
                                                        break;

                                                    default:
                                                        Interface.AddMessage(MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                        break;
                                                    }
                                                }
                                                else
                                                {
                                                    Interface.AddMessage(MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                }
                                            }
                                            i++;
                                        }
                                        i--;
                                        if (!DefinedAxles)
                                        {
                                            if (IsOdd)
                                            {
                                                double AxleDistance = 0.4 * Train.Cars[CarIndex].FrontBogie.Length;
                                                Train.Cars[CarIndex].FrontBogie.RearAxle.Position  = -AxleDistance;
                                                Train.Cars[CarIndex].FrontBogie.FrontAxle.Position = AxleDistance;
                                            }
                                            else
                                            {
                                                double AxleDistance = 0.4 * Train.Cars[CarIndex].RearBogie.Length;
                                                Train.Cars[CarIndex].RearBogie.RearAxle.Position  = -AxleDistance;
                                                Train.Cars[CarIndex].RearBogie.FrontAxle.Position = AxleDistance;
                                            }
                                        }
                                    }
                                    else
                                    {
                                        Interface.AddMessage(MessageType.Error, false, "The car index " + t + " does not reference an existing car at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                    }
                                }
                                else
                                {
                                    Interface.AddMessage(MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                }
                            }
                            else
                            {
                                // default
                                if (Lines.Length == 1 && Encoding.Equals(Encoding.Unicode))
                                {
                                    /*
                                     * If only one line, there's a good possibility that our file is NOT Unicode at all
                                     * and that the misdetection has turned it into garbage
                                     *
                                     * Try again with ASCII instead
                                     */
                                    ParseExtensionsConfig(TrainPath, Encoding.GetEncoding(1252), ref CarObjects, ref BogieObjects, ref CouplerObjects, Train, LoadObjects);
                                    return;
                                }
                                Interface.AddMessage(MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                            }
                            break;
                        }
                    }
                }

                // check for car objects and reverse if necessary
                int carObjects = 0;
                for (int i = 0; i < Train.Cars.Length; i++)
                {
                    if (CarObjects[i] != null)
                    {
                        carObjects++;
                        if (CarObjectsReversed[i] && LoadObjects)
                        {
                            {
                                // reverse axle positions
                                double temp = Train.Cars[i].FrontAxle.Position;
                                Train.Cars[i].FrontAxle.Position = -Train.Cars[i].RearAxle.Position;
                                Train.Cars[i].RearAxle.Position  = -temp;
                            }
                            if (CarObjects[i] is StaticObject)
                            {
                                StaticObject obj = (StaticObject)CarObjects[i];
                                obj.ApplyScale(-1.0, 1.0, -1.0);
                            }
                            else if (CarObjects[i] is AnimatedObjectCollection)
                            {
                                AnimatedObjectCollection obj = (AnimatedObjectCollection)CarObjects[i];
                                for (int j = 0; j < obj.Objects.Length; j++)
                                {
                                    for (int h = 0; h < obj.Objects[j].States.Length; h++)
                                    {
                                        if (obj.Objects[j].States[h].Prototype == null)
                                        {
                                            continue;                                             //object failed to load?
                                        }
                                        obj.Objects[j].States[h].Prototype.ApplyScale(-1.0, 1.0, -1.0);
                                        Matrix4D t = obj.Objects[j].States[h].Translation;
                                        t.Row3.X *= -1.0f;
                                        t.Row3.Z *= -1.0f;
                                        obj.Objects[j].States[h].Translation = t;
                                    }
                                    obj.Objects[j].TranslateXDirection.X *= -1.0;
                                    obj.Objects[j].TranslateXDirection.Z *= -1.0;
                                    obj.Objects[j].TranslateYDirection.X *= -1.0;
                                    obj.Objects[j].TranslateYDirection.Z *= -1.0;
                                    obj.Objects[j].TranslateZDirection.X *= -1.0;
                                    obj.Objects[j].TranslateZDirection.Z *= -1.0;
                                }
                            }
                            else
                            {
                                throw new NotImplementedException();
                            }
                        }
                    }
                }

                //Check for bogie objects and reverse if necessary.....
                int bogieObjects = 0;
                for (int i = 0; i < Train.Cars.Length * 2; i++)
                {
                    bool IsOdd    = (i % 2 != 0);
                    int  CarIndex = i / 2;
                    if (BogieObjects[i] != null)
                    {
                        bogieObjects++;
                        if (BogieObjectsReversed[i] && LoadObjects)
                        {
                            {
                                // reverse axle positions
                                if (IsOdd)
                                {
                                    double temp = Train.Cars[CarIndex].FrontBogie.FrontAxle.Position;
                                    Train.Cars[CarIndex].FrontBogie.FrontAxle.Position = -Train.Cars[CarIndex].FrontBogie.RearAxle.Position;
                                    Train.Cars[CarIndex].FrontBogie.RearAxle.Position  = -temp;
                                }
                                else
                                {
                                    double temp = Train.Cars[CarIndex].RearBogie.FrontAxle.Position;
                                    Train.Cars[CarIndex].RearBogie.FrontAxle.Position = -Train.Cars[CarIndex].RearBogie.RearAxle.Position;
                                    Train.Cars[CarIndex].RearBogie.RearAxle.Position  = -temp;
                                }
                            }
                            if (BogieObjects[i] is StaticObject)
                            {
                                StaticObject obj = (StaticObject)BogieObjects[i];
                                obj.ApplyScale(-1.0, 1.0, -1.0);
                            }
                            else if (BogieObjects[i] is AnimatedObjectCollection)
                            {
                                AnimatedObjectCollection obj = (AnimatedObjectCollection)BogieObjects[i];
                                for (int j = 0; j < obj.Objects.Length; j++)
                                {
                                    for (int h = 0; h < obj.Objects[j].States.Length; h++)
                                    {
                                        if (obj.Objects[j].States[h].Prototype == null)
                                        {
                                            continue;                                             //object failed to load?
                                        }
                                        obj.Objects[j].States[h].Prototype.ApplyScale(-1.0, 1.0, -1.0);
                                        Matrix4D t = obj.Objects[j].States[h].Translation;
                                        t.Row3.X *= -1.0f;
                                        t.Row3.Z *= -1.0f;
                                        obj.Objects[j].States[h].Translation = t;
                                    }
                                    obj.Objects[j].TranslateXDirection.X *= -1.0;
                                    obj.Objects[j].TranslateXDirection.Z *= -1.0;
                                    obj.Objects[j].TranslateYDirection.X *= -1.0;
                                    obj.Objects[j].TranslateYDirection.Z *= -1.0;
                                    obj.Objects[j].TranslateZDirection.X *= -1.0;
                                    obj.Objects[j].TranslateZDirection.Z *= -1.0;
                                }
                            }
                            else
                            {
                                throw new NotImplementedException();
                            }
                        }
                    }
                }

                if (carObjects > 0 & carObjects < Train.Cars.Length)
                {
                    Interface.AddMessage(MessageType.Warning, false, "An incomplete set of exterior objects was provided in file " + FileName);
                }

                if (bogieObjects > 0 & bogieObjects < Train.Cars.Length * 2)
                {
                    Interface.AddMessage(MessageType.Warning, false, "An incomplete set of bogie objects was provided in file " + FileName);
                }
            }
        }
Esempio n. 2
0
        /// <summary>Parses a base track following object node</summary>
        /// <param name="Element">The XElement to parse</param>
        /// <param name="FileName">The filename of the containing XML file</param>
        /// <param name="Path">The path of the containing XML file</param>
        /// <param name="Train">The track following object to parse this node into</param>
        private static void ParseTrackFollowingObjectNode(XElement Element, string FileName, string Path, string ObjectPath, TrainManager.TrackFollowingObject Train)
        {
            System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture;
            string TrainDirectory       = string.Empty;
            bool   consistReversed      = false;
            List <Game.TravelData> Data = new List <Game.TravelData>();

            foreach (XElement SectionElement in Element.Elements())
            {
                string Section = SectionElement.Name.LocalName;

                switch (SectionElement.Name.LocalName.ToLowerInvariant())
                {
                case "stops":
                    ParseStopNode(SectionElement, FileName, Data);
                    break;

                case "train":
                    foreach (XElement KeyNode in SectionElement.Elements())
                    {
                        string Key        = KeyNode.Name.LocalName;
                        string Value      = KeyNode.Value;
                        int    LineNumber = ((IXmlLineInfo)KeyNode).LineNumber;

                        switch (Key.ToLowerInvariant())
                        {
                        case "directory":
                        {
                            string trainDirectory = OpenBveApi.Path.CombineDirectory(Path, Value);
                            if (!System.IO.Directory.Exists(trainDirectory))
                            {
                                trainDirectory = OpenBveApi.Path.CombineFile(Program.FileSystem.InitialTrainFolder, Value);
                            }
                            if (!System.IO.Directory.Exists(trainDirectory))
                            {
                                trainDirectory = OpenBveApi.Path.CombineFile(Program.FileSystem.TrainInstallationDirectory, Value);
                            }
                            if (!System.IO.Directory.Exists(trainDirectory))
                            {
                                trainDirectory = OpenBveApi.Path.CombineFile(ObjectPath, Value);
                            }

                            if (!System.IO.Directory.Exists(trainDirectory))
                            {
                                Interface.AddMessage(MessageType.Error, false, "Directory was not found in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName);
                            }
                            else
                            {
                                TrainDirectory = trainDirectory;
                            }
                        }
                        break;

                        case "reversed":
                            int n;
                            NumberFormats.TryParseIntVb6(Value, out n);
                            if (n == 1 || Value.ToLowerInvariant() == "true")
                            {
                                consistReversed = true;
                            }
                            break;
                        }
                    }
                    break;

                case "definition":
                    foreach (XElement KeyNode in SectionElement.Elements())
                    {
                        string Key        = KeyNode.Name.LocalName;
                        string Value      = KeyNode.Value;
                        int    LineNumber = ((IXmlLineInfo)KeyNode).LineNumber;

                        switch (Key.ToLowerInvariant())
                        {
                        case "appearancetime":
                            if (Value.Length != 0 && !Interface.TryParseTime(Value, out Train.AppearanceTime))
                            {
                                Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName);
                            }
                            break;

                        case "appearancestartposition":
                            if (Value.Length != 0 && !NumberFormats.TryParseDoubleVb6(Value, out Train.AppearanceStartPosition))
                            {
                                Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName);
                            }
                            break;

                        case "appearanceendposition":
                            if (Value.Length != 0 && !NumberFormats.TryParseDoubleVb6(Value, out Train.AppearanceEndPosition))
                            {
                                Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName);
                            }
                            break;

                        case "leavetime":
                            if (Value.Length != 0 && !Interface.TryParseTime(Value, out Train.LeaveTime))
                            {
                                Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName);
                            }
                            break;
                        }
                    }
                    break;
                }
            }

            if (!Data.Any() || string.IsNullOrEmpty(TrainDirectory))
            {
                return;
            }

            /*
             * First check for a train.ai file- Functionally identical, but allows for differently configured AI
             * trains not to show up as driveable
             */
            string TrainData = OpenBveApi.Path.CombineFile(TrainDirectory, "train.ai");

            if (!System.IO.File.Exists(TrainData))
            {
                // Check for the standard driveable train.dat
                TrainData = OpenBveApi.Path.CombineFile(TrainDirectory, "train.dat");
            }
            string ExteriorFile = OpenBveApi.Path.CombineFile(TrainDirectory, "extensions.cfg");

            if (!System.IO.File.Exists(TrainData) || !System.IO.File.Exists(ExteriorFile))
            {
                Interface.AddMessage(MessageType.Error, true, "The supplied train folder in TrackFollowingObject " + FileName + " did not contain a complete set of data.");
                return;
            }
            TrainDatParser.ParseTrainData(TrainData, TextEncoding.GetSystemEncodingFromFile(TrainData), Train);
            SoundCfgParser.ParseSoundConfig(TrainDirectory, Train);
            Train.AI = new Game.TrackFollowingObjectAI(Train, Data);

            UnifiedObject[] CarObjects     = new UnifiedObject[Train.Cars.Length];
            UnifiedObject[] BogieObjects   = new UnifiedObject[Train.Cars.Length * 2];
            UnifiedObject[] CouplerObjects = new UnifiedObject[Train.Cars.Length - 1];
            ExtensionsCfgParser.ParseExtensionsConfig(System.IO.Path.GetDirectoryName(ExteriorFile), TextEncoding.GetSystemEncodingFromFile(ExteriorFile), ref CarObjects, ref BogieObjects, ref CouplerObjects, Train, true);

            int currentBogieObject = 0;

            for (int i = 0; i < Train.Cars.Length; i++)
            {
                if (CarObjects[i] == null)
                {
                    // load default exterior object
                    string       file = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Compatibility"), "exterior.csv");
                    StaticObject so   = ObjectManager.LoadStaticObject(file, System.Text.Encoding.UTF8, false);
                    if (so == null)
                    {
                        CarObjects[i] = null;
                    }
                    else
                    {
                        double sx = Train.Cars[i].Width;
                        double sy = Train.Cars[i].Height;
                        double sz = Train.Cars[i].Length;
                        so.ApplyScale(sx, sy, sz);
                        CarObjects[i] = so;
                    }
                }
                if (CarObjects[i] != null)
                {
                    // add object
                    Train.Cars[i].LoadCarSections(CarObjects[i]);
                }

                //Load bogie objects
                if (BogieObjects[currentBogieObject] != null)
                {
                    Train.Cars[i].FrontBogie.LoadCarSections(BogieObjects[currentBogieObject]);
                }
                currentBogieObject++;
                if (BogieObjects[currentBogieObject] != null)
                {
                    Train.Cars[i].RearBogie.LoadCarSections(BogieObjects[currentBogieObject]);
                }
                currentBogieObject++;
            }

            // door open/close speed
            foreach (var Car in Train.Cars)
            {
                if (Car.Specs.DoorOpenFrequency <= 0.0)
                {
                    if (Car.Doors[0].OpenSound.Buffer != null & Car.Doors[1].OpenSound.Buffer != null)
                    {
                        Program.Sounds.LoadBuffer(Car.Doors[0].OpenSound.Buffer);
                        Program.Sounds.LoadBuffer(Car.Doors[1].OpenSound.Buffer);
                        double a = Car.Doors[0].OpenSound.Buffer.Duration;
                        double b = Car.Doors[1].OpenSound.Buffer.Duration;
                        Car.Specs.DoorOpenFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8;
                    }
                    else if (Car.Doors[0].OpenSound.Buffer != null)
                    {
                        Program.Sounds.LoadBuffer(Car.Doors[0].OpenSound.Buffer);
                        double a = Car.Doors[0].OpenSound.Buffer.Duration;
                        Car.Specs.DoorOpenFrequency = a > 0.0 ? 1.0 / a : 0.8;
                    }
                    else if (Car.Doors[1].OpenSound.Buffer != null)
                    {
                        Program.Sounds.LoadBuffer(Car.Doors[0].OpenSound.Buffer);
                        double b = Car.Doors[1].OpenSound.Buffer.Duration;
                        Car.Specs.DoorOpenFrequency = b > 0.0 ? 1.0 / b : 0.8;
                    }
                    else
                    {
                        Car.Specs.DoorOpenFrequency = 0.8;
                    }
                }
                if (Car.Specs.DoorCloseFrequency <= 0.0)
                {
                    if (Car.Doors[0].CloseSound.Buffer != null & Car.Doors[1].CloseSound.Buffer != null)
                    {
                        Program.Sounds.LoadBuffer(Car.Doors[0].CloseSound.Buffer);
                        Program.Sounds.LoadBuffer(Car.Doors[1].CloseSound.Buffer);
                        double a = Car.Doors[0].CloseSound.Buffer.Duration;
                        double b = Car.Doors[1].CloseSound.Buffer.Duration;
                        Car.Specs.DoorCloseFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8;
                    }
                    else if (Car.Doors[0].CloseSound.Buffer != null)
                    {
                        Program.Sounds.LoadBuffer(Car.Doors[0].CloseSound.Buffer);
                        double a = Car.Doors[0].CloseSound.Buffer.Duration;
                        Car.Specs.DoorCloseFrequency = a > 0.0 ? 1.0 / a : 0.8;
                    }
                    else if (Car.Doors[1].CloseSound.Buffer != null)
                    {
                        Program.Sounds.LoadBuffer(Car.Doors[0].CloseSound.Buffer);
                        double b = Car.Doors[1].CloseSound.Buffer.Duration;
                        Car.Specs.DoorCloseFrequency = b > 0.0 ? 1.0 / b : 0.8;
                    }
                    else
                    {
                        Car.Specs.DoorCloseFrequency = 0.8;
                    }
                }
                const double f = 0.015;
                const double g = 2.75;
                Car.Specs.DoorOpenPitch       = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5)));
                Car.Specs.DoorClosePitch      = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5)));
                Car.Specs.DoorOpenFrequency  /= Car.Specs.DoorOpenPitch;
                Car.Specs.DoorCloseFrequency /= Car.Specs.DoorClosePitch;

                /*
                 * Remove the following two lines, then the pitch at which doors play
                 * takes their randomized opening and closing times into account.
                 * */
                Car.Specs.DoorOpenPitch  = 1.0;
                Car.Specs.DoorClosePitch = 1.0;
            }

            foreach (var Car in Train.Cars)
            {
                Car.FrontAxle.Follower.TrackIndex            = Data[0].RailIndex;
                Car.RearAxle.Follower.TrackIndex             = Data[0].RailIndex;
                Car.FrontBogie.FrontAxle.Follower.TrackIndex = Data[0].RailIndex;
                Car.FrontBogie.RearAxle.Follower.TrackIndex  = Data[0].RailIndex;
                Car.RearBogie.FrontAxle.Follower.TrackIndex  = Data[0].RailIndex;
                Car.RearBogie.RearAxle.Follower.TrackIndex   = Data[0].RailIndex;
            }

            if (consistReversed)
            {
                Train.Reverse();
            }
            Train.PlaceCars(Data[0].StopPosition);
        }
Esempio n. 3
0
        private static void LoadEverythingThreaded()
        {
            Program.FileSystem.AppendToLogFile("Loading route file: " + CurrentRouteFile);
            Program.FileSystem.AppendToLogFile("INFO: Route file hash " + CsvRwRouteParser.GetChecksum(CurrentRouteFile));
            string RailwayFolder = GetRailwayFolder(CurrentRouteFile);
            string ObjectFolder  = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Object");
            string SoundFolder   = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Sound");

            Game.Reset(true, false);
            Game.MinimalisticSimulation = true;
            // screen
            Program.Renderer.Camera.CurrentMode = CameraViewMode.Interior;
            //First, check the format of the route file
            //RW routes were written for BVE1 / 2, and have a different command syntax
            bool IsRW = CsvRwRouteParser.isRWFile(CurrentRouteFile);

            Program.FileSystem.AppendToLogFile("Route file format is: " + (IsRW ? "RW" : "CSV"));
            CsvRwRouteParser.ParseRoute(CurrentRouteFile, IsRW, CurrentRouteEncoding, CurrentTrainFolder, ObjectFolder, SoundFolder, false);
            Thread createIllustrations = new Thread(Game.RouteInformation.LoadInformation)
            {
                IsBackground = true
            };

            createIllustrations.Start();
            System.Threading.Thread.Sleep(1); if (Cancel)
            {
                return;
            }
            Program.CurrentRoute.Atmosphere.CalculateSeaLevelConstants();
            if (Program.CurrentRoute.BogusPreTrainInstructions.Length != 0)
            {
                double t = Program.CurrentRoute.BogusPreTrainInstructions[0].Time;
                double p = Program.CurrentRoute.BogusPreTrainInstructions[0].TrackPosition;
                for (int i = 1; i < Program.CurrentRoute.BogusPreTrainInstructions.Length; i++)
                {
                    if (Program.CurrentRoute.BogusPreTrainInstructions[i].Time > t)
                    {
                        t = Program.CurrentRoute.BogusPreTrainInstructions[i].Time;
                    }
                    else
                    {
                        t += 1.0;
                        Program.CurrentRoute.BogusPreTrainInstructions[i].Time = t;
                    }
                    if (Program.CurrentRoute.BogusPreTrainInstructions[i].TrackPosition > p)
                    {
                        p = Program.CurrentRoute.BogusPreTrainInstructions[i].TrackPosition;
                    }
                    else
                    {
                        p += 1.0;
                        Program.CurrentRoute.BogusPreTrainInstructions[i].TrackPosition = p;
                    }
                }
            }
            World.CameraTrackFollower = new TrackFollower(Program.CurrentHost)
            {
                Train = null, Car = null
            };
            if (Program.CurrentRoute.Stations.Length == 1)
            {
                //Log the fact that only a single station is present, as this is probably not right
                Program.FileSystem.AppendToLogFile("The processed route file only contains a single station.");
            }
            Program.FileSystem.AppendToLogFile("Route file loaded successfully.");
            RouteProgress = 1.0;
            // initialize trains
            System.Threading.Thread.Sleep(1); if (Cancel)
            {
                return;
            }
            TrainManager.Trains = new TrainManager.Train[Game.PrecedingTrainTimeDeltas.Length + 1 + (Program.CurrentRoute.BogusPreTrainInstructions.Length != 0 ? 1 : 0)];
            for (int k = 0; k < TrainManager.Trains.Length; k++)
            {
                if (k == TrainManager.Trains.Length - 1 & Program.CurrentRoute.BogusPreTrainInstructions.Length != 0)
                {
                    TrainManager.Trains[k] = new TrainManager.Train(TrainState.Bogus);
                }
                else
                {
                    TrainManager.Trains[k] = new TrainManager.Train(TrainState.Pending);
                }
            }
            TrainManager.PlayerTrain = TrainManager.Trains[Game.PrecedingTrainTimeDeltas.Length];

            UnifiedObject[] CarObjects     = null;
            UnifiedObject[] BogieObjects   = null;
            UnifiedObject[] CouplerObjects = null;

            // load trains
            double TrainProgressMaximum = 0.7 + 0.3 * (double)TrainManager.Trains.Length;

            for (int k = 0; k < TrainManager.Trains.Length; k++)
            {
                //Sleep for 20ms to allow route loading locks to release
                Thread.Sleep(20);
                if (TrainManager.Trains[k].State == TrainState.Bogus)
                {
                    // bogus train
                    string TrainData = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Compatibility", "PreTrain"), "train.dat");
                    TrainDatParser.ParseTrainData(TrainData, System.Text.Encoding.UTF8, TrainManager.Trains[k]);
                    System.Threading.Thread.Sleep(1); if (Cancel)
                    {
                        return;
                    }
                    TrainManager.Trains[k].InitializeCarSounds();
                    System.Threading.Thread.Sleep(1); if (Cancel)
                    {
                        return;
                    }
                    TrainProgressCurrentWeight = 0.3 / TrainProgressMaximum;
                    TrainProgressCurrentSum   += TrainProgressCurrentWeight;
                }
                else
                {
                    TrainManager.Trains[k].TrainFolder = CurrentTrainFolder;
                    // real train
                    if (TrainManager.Trains[k].IsPlayerTrain)
                    {
                        Program.FileSystem.AppendToLogFile("Loading player train: " + TrainManager.Trains[k].TrainFolder);
                    }
                    else
                    {
                        Program.FileSystem.AppendToLogFile("Loading AI train: " + TrainManager.Trains[k].TrainFolder);
                    }
                    TrainProgressCurrentWeight = 0.1 / TrainProgressMaximum;
                    string TrainData = OpenBveApi.Path.CombineFile(TrainManager.Trains[k].TrainFolder, "train.dat");
                    TrainDatParser.ParseTrainData(TrainData, CurrentTrainEncoding, TrainManager.Trains[k]);
                    TrainProgressCurrentSum += TrainProgressCurrentWeight;
                    System.Threading.Thread.Sleep(1); if (Cancel)
                    {
                        return;
                    }
                    TrainProgressCurrentWeight = 0.2 / TrainProgressMaximum;
                    SoundCfgParser.ParseSoundConfig(TrainManager.Trains[k].TrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]);
                    TrainProgressCurrentSum += TrainProgressCurrentWeight;
                    System.Threading.Thread.Sleep(1); if (Cancel)
                    {
                        return;
                    }
                    // door open/close speed
                    for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++)
                    {
                        if (TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency <= 0.0)
                        {
                            if (TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer != null & TrainManager.Trains[k].Cars[i].Doors[1].OpenSound.Buffer != null)
                            {
                                Program.Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer);
                                Program.Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[1].OpenSound.Buffer);
                                double a = TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer.Duration;
                                double b = TrainManager.Trains[k].Cars[i].Doors[1].OpenSound.Buffer.Duration;
                                TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8;
                            }
                            else if (TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer != null)
                            {
                                Program.Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer);
                                double a = TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer.Duration;
                                TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = a > 0.0 ? 1.0 / a : 0.8;
                            }
                            else if (TrainManager.Trains[k].Cars[i].Doors[1].OpenSound.Buffer != null)
                            {
                                Program.Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer);
                                double b = TrainManager.Trains[k].Cars[i].Doors[1].OpenSound.Buffer.Duration;
                                TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = b > 0.0 ? 1.0 / b : 0.8;
                            }
                            else
                            {
                                TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = 0.8;
                            }
                        }
                        if (TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency <= 0.0)
                        {
                            if (TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer != null & TrainManager.Trains[k].Cars[i].Doors[1].CloseSound.Buffer != null)
                            {
                                Program.Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer);
                                Program.Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[1].CloseSound.Buffer);
                                double a = TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer.Duration;
                                double b = TrainManager.Trains[k].Cars[i].Doors[1].CloseSound.Buffer.Duration;
                                TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8;
                            }
                            else if (TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer != null)
                            {
                                Program.Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer);
                                double a = TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer.Duration;
                                TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = a > 0.0 ? 1.0 / a : 0.8;
                            }
                            else if (TrainManager.Trains[k].Cars[i].Doors[1].CloseSound.Buffer != null)
                            {
                                Program.Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer);
                                double b = TrainManager.Trains[k].Cars[i].Doors[1].CloseSound.Buffer.Duration;
                                TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = b > 0.0 ? 1.0 / b : 0.8;
                            }
                            else
                            {
                                TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = 0.8;
                            }
                        }
                        const double f = 0.015;
                        const double g = 2.75;
                        TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch       = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5)));
                        TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch      = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5)));
                        TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency  /= TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch;
                        TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency /= TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch;

                        /*
                         * Remove the following two lines, then the pitch at which doors play
                         * takes their randomized opening and closing times into account.
                         * */
                        TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch  = 1.0;
                        TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch = 1.0;
                    }
                }
                // add panel section
                if (TrainManager.Trains[k].IsPlayerTrain)
                {
                    TrainProgressCurrentWeight = 0.7 / TrainProgressMaximum;
                    TrainManager.ParsePanelConfig(TrainManager.Trains[k].TrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]);
                    TrainProgressCurrentSum += TrainProgressCurrentWeight;
                    System.Threading.Thread.Sleep(1); if (Cancel)
                    {
                        return;
                    }
                    Program.FileSystem.AppendToLogFile("Train panel loaded sucessfully.");
                }
                // add exterior section
                if (TrainManager.Trains[k].State != TrainState.Bogus)
                {
                    bool LoadObjects = false;
                    if (CarObjects == null)
                    {
                        CarObjects     = new UnifiedObject[TrainManager.Trains[k].Cars.Length];
                        BogieObjects   = new UnifiedObject[TrainManager.Trains[k].Cars.Length * 2];
                        CouplerObjects = new UnifiedObject[TrainManager.Trains[k].Cars.Length];
                        LoadObjects    = true;
                    }
                    string tXml = OpenBveApi.Path.CombineFile(TrainManager.Trains[k].TrainFolder, "train.xml");
                    if (System.IO.File.Exists(tXml))
                    {
                        TrainXmlParser.Parse(tXml, TrainManager.Trains[k], ref CarObjects, ref BogieObjects, ref CouplerObjects);
                    }
                    else
                    {
                        ExtensionsCfgParser.ParseExtensionsConfig(TrainManager.Trains[k].TrainFolder, CurrentTrainEncoding, ref CarObjects, ref BogieObjects, ref CouplerObjects, TrainManager.Trains[k], LoadObjects);
                    }
                    World.CameraCar = TrainManager.Trains[k].DriverCar;
                    System.Threading.Thread.Sleep(1); if (Cancel)
                    {
                        return;
                    }
                    //Stores the current array index of the bogie object to add
                    //Required as there are two bogies per car, and we're using a simple linear array....
                    int currentBogieObject = 0;
                    for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++)
                    {
                        if (CarObjects[i] == null)
                        {
                            // load default exterior object
                            string       file = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Compatibility"), "exterior.csv");
                            StaticObject so   = ObjectManager.LoadStaticObject(file, System.Text.Encoding.UTF8, false);
                            if (so == null)
                            {
                                CarObjects[i] = null;
                            }
                            else
                            {
                                double sx = TrainManager.Trains[k].Cars[i].Width;
                                double sy = TrainManager.Trains[k].Cars[i].Height;
                                double sz = TrainManager.Trains[k].Cars[i].Length;
                                so.ApplyScale(sx, sy, sz);
                                CarObjects[i] = so;
                            }
                        }
                        if (CarObjects[i] != null)
                        {
                            // add object
                            TrainManager.Trains[k].Cars[i].LoadCarSections(CarObjects[i]);
                        }

                        if (CouplerObjects[i] != null)
                        {
                            TrainManager.Trains[k].Cars[i].Coupler.LoadCarSections(CouplerObjects[i]);
                        }
                        //Load bogie objects
                        if (BogieObjects[currentBogieObject] != null)
                        {
                            TrainManager.Trains[k].Cars[i].FrontBogie.LoadCarSections(BogieObjects[currentBogieObject]);
                        }
                        currentBogieObject++;
                        if (BogieObjects[currentBogieObject] != null)
                        {
                            TrainManager.Trains[k].Cars[i].RearBogie.LoadCarSections(BogieObjects[currentBogieObject]);
                        }
                        currentBogieObject++;
                    }
                }
                // place cars
                TrainManager.Trains[k].PlaceCars(0.0);

                // configure ai / timetable
                if (TrainManager.Trains[k].IsPlayerTrain)
                {
                    TrainManager.Trains[k].TimetableDelta = 0.0;
                }
                else if (TrainManager.Trains[k].State != TrainState.Bogus)
                {
                    TrainManager.Trains[k].AI                  = new Game.SimpleHumanDriverAI(TrainManager.Trains[k]);
                    TrainManager.Trains[k].TimetableDelta      = Game.PrecedingTrainTimeDeltas[k];
                    TrainManager.Trains[k].Specs.DoorOpenMode  = TrainManager.DoorMode.Manual;
                    TrainManager.Trains[k].Specs.DoorCloseMode = TrainManager.DoorMode.Manual;
                }
            }

            /*
             * HACK: Store the TrainManager.Trains reference in the RouteManager also
             *		 Note that this may change when the TrainManager is separated from the lump
             *       Remember not to modify via this ref
             */
            // ReSharper disable once CoVariantArrayConversion
            Program.CurrentRoute.Trains = TrainManager.Trains;
            TrainProgress = 1.0;
            // finished created objects
            System.Threading.Thread.Sleep(1); if (Cancel)
            {
                return;
            }
            Array.Resize(ref ObjectManager.AnimatedWorldObjects, ObjectManager.AnimatedWorldObjectsUsed);
            // update sections
            if (Program.CurrentRoute.Sections.Length > 0)
            {
                Program.CurrentRoute.UpdateAllSections();
            }
            // load plugin
            for (int i = 0; i < TrainManager.Trains.Length; i++)
            {
                if (TrainManager.Trains[i].State != TrainState.Bogus)
                {
                    if (TrainManager.Trains[i].IsPlayerTrain)
                    {
                        if (!TrainManager.Trains[i].LoadCustomPlugin(TrainManager.Trains[i].TrainFolder, CurrentTrainEncoding))
                        {
                            TrainManager.Trains[i].LoadDefaultPlugin(TrainManager.Trains[i].TrainFolder);
                        }
                    }
                    else
                    {
                        TrainManager.Trains[i].LoadDefaultPlugin(TrainManager.Trains[i].TrainFolder);
                    }
                }
            }
        }
Esempio n. 4
0
        internal static void ParseExtensionsConfig(string filePath, Encoding encoding, out UnifiedObject[] carObjects, out UnifiedObject[] bogieObjects, out double[] axleLocations, out TrainManager.Train train, bool loadObjects)
        {
            CultureInfo Culture = CultureInfo.InvariantCulture;

            carObjects    = new UnifiedObject[] { };
            bogieObjects  = new UnifiedObject[] { };
            axleLocations = new double[] { };
            train         = new TrainManager.Train();

            if (!System.IO.File.Exists(filePath))
            {
                return;
            }

            train.Cars = new TrainManager.Car[] { };
            bool[] carObjectsReversed   = new bool[train.Cars.Length];
            bool[] bogieObjectsReversed = new bool[train.Cars.Length * 2];
            bool[] carsDefined          = new bool[train.Cars.Length];
            bool[] bogiesDefined        = new bool[train.Cars.Length * 2];
            axleLocations = new double[train.Cars.Length * 2];

            string trainPath = System.IO.Path.GetDirectoryName(filePath);

            System.Globalization.CultureInfo culture     = System.Globalization.CultureInfo.InvariantCulture;
            TextEncoding.Encoding            newEncoding = TextEncoding.GetEncodingFromFile(filePath);
            if (newEncoding != TextEncoding.Encoding.Unknown)
            {
                switch (newEncoding)
                {
                case TextEncoding.Encoding.Utf7:
                    encoding = Encoding.UTF7;
                    break;

                case TextEncoding.Encoding.Utf8:
                    encoding = Encoding.UTF8;
                    break;

                case TextEncoding.Encoding.Utf16Le:
                    encoding = Encoding.Unicode;
                    break;

                case TextEncoding.Encoding.Utf16Be:
                    encoding = Encoding.BigEndianUnicode;
                    break;

                case TextEncoding.Encoding.Utf32Le:
                    encoding = Encoding.UTF32;
                    break;

                case TextEncoding.Encoding.Utf32Be:
                    encoding = Encoding.GetEncoding(12001);
                    break;

                case TextEncoding.Encoding.Shift_JIS:
                    encoding = Encoding.GetEncoding(932);
                    break;
                }
            }

            string[] lines = System.IO.File.ReadAllLines(filePath, encoding);
            for (int i = 0; i < lines.Length; i++)
            {
                int j = lines[i].IndexOf(';');
                if (j >= 0)
                {
                    lines[i] = lines[i].Substring(0, j).Trim(new char[] { });
                }
                else
                {
                    lines[i] = lines[i].Trim(new char[] { });
                }
            }

            for (int i = 0; i < lines.Length; i++)
            {
                if (lines[i].Length != 0)
                {
                    switch (lines[i].ToLowerInvariant())
                    {
                    case "[exterior]":
                        // exterior
                        i++;
                        while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal))
                        {
                            if (lines[i].Length != 0)
                            {
                                int j = lines[i].IndexOf("=", StringComparison.Ordinal);
                                if (j >= 0)
                                {
                                    string a = lines[i].Substring(0, j).TrimEnd(new char[] { });
                                    string b = lines[i].Substring(j + 1).TrimStart(new char[] { });
                                    int    n;
                                    if (int.TryParse(a, System.Globalization.NumberStyles.Integer, culture, out n))
                                    {
                                        if (n >= 0)
                                        {
                                            if (n >= train.Cars.Length)
                                            {
                                                Array.Resize(ref train.Cars, n + 1);
                                                train.Cars[n] = new TrainManager.Car(train);
                                                Array.Resize(ref carObjects, n + 1);
                                                Array.Resize(ref bogieObjects, (n + 1) * 2);
                                                Array.Resize(ref carObjectsReversed, n + 1);
                                                Array.Resize(ref bogieObjectsReversed, (n + 1) * 2);
                                                Array.Resize(ref carsDefined, n + 1);
                                                Array.Resize(ref bogiesDefined, (n + 1) * 2);
                                                Array.Resize(ref axleLocations, (n + 1) * 2);
                                            }
                                            if (Path.ContainsInvalidChars(b))
                                            {
                                                Interface.AddMessage(MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(culture) + " in file " + filePath);
                                            }
                                            else
                                            {
                                                string file = OpenBveApi.Path.CombineFile(trainPath, b);
                                                if (System.IO.File.Exists(file))
                                                {
                                                    if (loadObjects)
                                                    {
                                                        carObjects[n] = ObjectManager.LoadObject(file, encoding, false);
                                                    }
                                                }
                                                else
                                                {
                                                    Interface.AddMessage(MessageType.Error, true, "The car object " + file + " does not exist at line " + (i + 1).ToString(culture) + " in file " + filePath);
                                                }
                                            }
                                        }
                                        else
                                        {
                                            Interface.AddMessage(MessageType.Error, false, "The car index " + a + " does not reference an existing car at line " + (i + 1).ToString(culture) + " in file " + filePath);
                                        }
                                    }
                                    else
                                    {
                                        Interface.AddMessage(MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(culture) + " in file " + filePath);
                                    }
                                }
                                else
                                {
                                    Interface.AddMessage(MessageType.Error, false, "Invalid statement " + lines[i] + " encountered at line " + (i + 1).ToString(culture) + " in file " + filePath);
                                }
                            }
                            i++;
                        }
                        i--;
                        break;

                    default:
                        if (lines[i].StartsWith("[car", StringComparison.OrdinalIgnoreCase) & lines[i].EndsWith("]", StringComparison.Ordinal))
                        {
                            // car
                            string t = lines[i].Substring(4, lines[i].Length - 5);
                            int    n;
                            if (int.TryParse(t, System.Globalization.NumberStyles.Integer, culture, out n))
                            {
                                if (n >= 0)
                                {
                                    if (n >= train.Cars.Length)
                                    {
                                        Array.Resize(ref train.Cars, n + 1);
                                        train.Cars[n] = new TrainManager.Car(train);
                                        Array.Resize(ref carObjects, n + 1);
                                        Array.Resize(ref bogieObjects, (n + 1) * 2);
                                        Array.Resize(ref carObjectsReversed, n + 1);
                                        Array.Resize(ref bogieObjectsReversed, (n + 1) * 2);
                                        Array.Resize(ref carsDefined, n + 1);
                                        Array.Resize(ref bogiesDefined, (n + 1) * 2);
                                        Array.Resize(ref axleLocations, (n + 1) * 2);
                                    }
                                    if (carsDefined[n])
                                    {
                                        Interface.AddMessage(MessageType.Error, false, "Car " + n.ToString(culture) + " has already been declared at line " + (i + 1).ToString(culture) + " in file " + filePath);
                                    }
                                    carsDefined[n] = true;
                                    i++;
                                    while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal))
                                    {
                                        if (lines[i].Length != 0)
                                        {
                                            int j = lines[i].IndexOf("=", StringComparison.Ordinal);
                                            if (j >= 0)
                                            {
                                                string a = lines[i].Substring(0, j).TrimEnd(new char[] { });
                                                string b = lines[i].Substring(j + 1).TrimStart(new char[] { });
                                                switch (a.ToLowerInvariant())
                                                {
                                                case "object":
                                                    if (string.IsNullOrEmpty(b))
                                                    {
                                                        Interface.AddMessage(MessageType.Error, true, "An empty car object was supplied at line " + (i + 1).ToString(culture) + " in file " + filePath);
                                                        break;
                                                    }
                                                    if (Path.ContainsInvalidChars(b))
                                                    {
                                                        Interface.AddMessage(MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(culture) + " in file " + filePath);
                                                    }
                                                    else
                                                    {
                                                        string file = OpenBveApi.Path.CombineFile(trainPath, b);
                                                        if (System.IO.File.Exists(file))
                                                        {
                                                            if (loadObjects)
                                                            {
                                                                carObjects[n] = ObjectManager.LoadObject(file, encoding, false);
                                                            }
                                                        }
                                                        else
                                                        {
                                                            Interface.AddMessage(MessageType.Error, true, "The car object " + file + " does not exist at line " + (i + 1).ToString(culture) + " in file " + filePath);
                                                        }
                                                    }
                                                    break;

                                                case "length":
                                                {
                                                    double m;
                                                    if (double.TryParse(b, System.Globalization.NumberStyles.Float, culture, out m))
                                                    {
                                                        if (m > 0.0)
                                                        {
                                                            train.Cars[n].Length = m;
                                                        }
                                                        else
                                                        {
                                                            Interface.AddMessage(MessageType.Error, false, "Value is expected to be a positive floating-point number in " + a + " at line " + (i + 1).ToString(culture) + " in file " + filePath);
                                                        }
                                                    }
                                                    else
                                                    {
                                                        Interface.AddMessage(MessageType.Error, false, "Value is expected to be a positive floating-point number in " + a + " at line " + (i + 1).ToString(culture) + " in file " + filePath);
                                                    }
                                                }
                                                break;

                                                case "reversed":
                                                    carObjectsReversed[n] = b.Equals("true", StringComparison.OrdinalIgnoreCase);
                                                    break;

                                                case "axles":
                                                    int k = b.IndexOf(',');
                                                    if (k >= 0)
                                                    {
                                                        string c = b.Substring(0, k).TrimEnd(new char[] { });
                                                        string d = b.Substring(k + 1).TrimStart(new char[] { });
                                                        double rear, front;
                                                        if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out rear))
                                                        {
                                                            Interface.AddMessage(MessageType.Error, false, "Rear is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + filePath);
                                                        }
                                                        else if (!double.TryParse(d, System.Globalization.NumberStyles.Float, Culture, out front))
                                                        {
                                                            Interface.AddMessage(MessageType.Error, false, "Front is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + filePath);
                                                        }
                                                        else if (rear >= front)
                                                        {
                                                            Interface.AddMessage(MessageType.Error, false, "Rear is expected to be less than Front in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + filePath);
                                                        }
                                                        else
                                                        {
                                                            if (n == 0)
                                                            {
                                                                axleLocations[n]     = rear;
                                                                axleLocations[n + 1] = front;
                                                            }
                                                            else
                                                            {
                                                                axleLocations[n * 2]     = rear;
                                                                axleLocations[n * 2 + 1] = front;
                                                            }
                                                        }
                                                    }
                                                    else
                                                    {
                                                        Interface.AddMessage(MessageType.Error, false, "An argument-separating comma is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + filePath);
                                                    }
                                                    break;

                                                default:
                                                    Interface.AddMessage(MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(culture) + " in file " + filePath);
                                                    break;
                                                }
                                            }
                                            else
                                            {
                                                Interface.AddMessage(MessageType.Error, false, "Invalid statement " + lines[i] + " encountered at line " + (i + 1).ToString(culture) + " in file " + filePath);
                                            }
                                        }
                                        i++;
                                    }
                                    i--;
                                }
                                else
                                {
                                    Interface.AddMessage(MessageType.Error, false, "The car index " + t + " does not reference an existing car at line " + (i + 1).ToString(culture) + " in file " + filePath);
                                }
                            }
                            else
                            {
                                Interface.AddMessage(MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(culture) + " in file " + filePath);
                            }
                        }
                        else if (lines[i].StartsWith("[bogie", StringComparison.OrdinalIgnoreCase) & lines[i].EndsWith("]", StringComparison.Ordinal))
                        {
                            // car
                            string t = lines[i].Substring(6, lines[i].Length - 7);
                            int    n;
                            if (int.TryParse(t, System.Globalization.NumberStyles.Integer, culture, out n))
                            {
                                if (n >= train.Cars.Length * 2)
                                {
                                    Array.Resize(ref train.Cars, n / 2 + 1);
                                    if (n == 0)
                                    {
                                        train.Cars[0] = new TrainManager.Car(train);
                                        Array.Resize(ref axleLocations, 2);
                                    }
                                    else
                                    {
                                        train.Cars[n / 2] = new TrainManager.Car(train);
                                        Array.Resize(ref axleLocations, ((n / 2) + 1) * 2);
                                    }

                                    Array.Resize(ref carObjects, n / 2 + 1);
                                    Array.Resize(ref bogieObjects, n + 2);
                                    Array.Resize(ref carObjectsReversed, n / 2 + 1);
                                    Array.Resize(ref bogieObjectsReversed, n + 2);
                                    Array.Resize(ref carsDefined, n / 2 + 1);
                                    Array.Resize(ref bogiesDefined, n + 2);
                                }

                                if (n > bogiesDefined.Length - 1)
                                {
                                    continue;
                                }
                                if (bogiesDefined[n])
                                {
                                    Interface.AddMessage(MessageType.Error, false, "Bogie " + n.ToString(culture) + " has already been declared at line " + (i + 1).ToString(culture) + " in file " + filePath);
                                }
                                bogiesDefined[n] = true;
                                //Assuming that there are two bogies per car
                                if (n >= 0 & n < train.Cars.Length * 2)
                                {
                                    i++;
                                    while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal))
                                    {
                                        if (lines[i].Length != 0)
                                        {
                                            int j = lines[i].IndexOf("=", StringComparison.Ordinal);
                                            if (j >= 0)
                                            {
                                                string a = lines[i].Substring(0, j).TrimEnd(new char[] { });
                                                string b = lines[i].Substring(j + 1).TrimStart(new char[] { });
                                                switch (a.ToLowerInvariant())
                                                {
                                                case "object":
                                                    if (Path.ContainsInvalidChars(b))
                                                    {
                                                        Interface.AddMessage(MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(culture) + " in file " + filePath);
                                                    }
                                                    else
                                                    {
                                                        if (string.IsNullOrEmpty(b))
                                                        {
                                                            Interface.AddMessage(MessageType.Error, true, "An empty bogie object was supplied at line " + (i + 1).ToString(culture) + " in file " + filePath);
                                                            break;
                                                        }
                                                        string file = OpenBveApi.Path.CombineFile(trainPath, b);
                                                        if (System.IO.File.Exists(file))
                                                        {
                                                            if (loadObjects)
                                                            {
                                                                bogieObjects[n] = ObjectManager.LoadObject(file, encoding, false);
                                                            }
                                                        }
                                                        else
                                                        {
                                                            Interface.AddMessage(MessageType.Error, true, "The bogie object " + file + " does not exist at line " + (i + 1).ToString(culture) + " in file " + filePath);
                                                        }
                                                    }
                                                    break;

                                                case "length":
                                                {
                                                    Interface.AddMessage(MessageType.Error, false, "A defined length is not supported for bogies at line " + (i + 1).ToString(culture) + " in file " + filePath);
                                                }
                                                break;

                                                case "reversed":
                                                    bogieObjectsReversed[n] = b.Equals("true", StringComparison.OrdinalIgnoreCase);
                                                    break;

                                                case "axles":
                                                    //Axles aren't used in bogie positioning, just in rotation
                                                    break;

                                                default:
                                                    Interface.AddMessage(MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(culture) + " in file " + filePath);
                                                    break;
                                                }
                                            }
                                            else
                                            {
                                                Interface.AddMessage(MessageType.Error, false, "Invalid statement " + lines[i] + " encountered at line " + (i + 1).ToString(culture) + " in file " + filePath);
                                            }
                                        }
                                        i++;
                                    }
                                    i--;
                                }
                                else
                                {
                                    Interface.AddMessage(MessageType.Error, false, "The car index " + t + " does not reference an existing car at line " + (i + 1).ToString(culture) + " in file " + filePath);
                                }
                            }
                            else
                            {
                                Interface.AddMessage(MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(culture) + " in file " + filePath);
                            }
                        }
                        else if (lines[i].StartsWith("[coupler", StringComparison.OrdinalIgnoreCase) & lines[i].EndsWith("]", StringComparison.Ordinal))
                        {
                            i++;
                            while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal))
                            {
                                /*
                                 * Coupler statments are currently not supported in Object Viewer
                                 */
                                i++;
                            }

                            i--;
                        }
                        else
                        {
                            // default
                            if (lines.Length == 1 && encoding.Equals(Encoding.Unicode))
                            {
                                /*
                                 * If only one line, there's a good possibility that our file is NOT Unicode at all
                                 * and that the misdetection has turned it into garbage
                                 *
                                 * Try again with ASCII instead
                                 */
                                ParseExtensionsConfig(filePath, Encoding.GetEncoding(1252), out carObjects, out bogieObjects, out axleLocations, out train, loadObjects);
                                return;
                            }
                            Interface.AddMessage(MessageType.Error, false, "Invalid statement " + lines[i] + " encountered at line " + (i + 1).ToString(culture) + " in file " + filePath);
                        }
                        break;
                    }
                }
            }

            // check for car objects and reverse if necessary
            int carObjectsCount = 0;

            for (int i = 0; i < train.Cars.Length; i++)
            {
                if (carObjects[i] != null)
                {
                    carObjectsCount++;
                    if (carObjectsReversed[i] && loadObjects)
                    {
                        if (carObjects[i] is StaticObject)
                        {
                            StaticObject obj = (StaticObject)carObjects[i];
                            obj.ApplyScale(-1.0, 1.0, -1.0);
                        }
                        else if (carObjects[i] is AnimatedObjectCollection)
                        {
                            AnimatedObjectCollection obj = (AnimatedObjectCollection)carObjects[i];
                            for (int j = 0; j < obj.Objects.Length; j++)
                            {
                                for (int h = 0; h < obj.Objects[j].States.Length; h++)
                                {
                                    obj.Objects[j].States[h].Prototype.ApplyScale(-1.0, 1.0, -1.0);
                                    obj.Objects[j].States[h].Translation.Row3.X *= -1.0f;
                                    obj.Objects[j].States[h].Translation.Row3.Z *= -1.0f;
                                }
                                obj.Objects[j].TranslateXDirection.X *= -1.0;
                                obj.Objects[j].TranslateXDirection.Z *= -1.0;
                                obj.Objects[j].TranslateYDirection.X *= -1.0;
                                obj.Objects[j].TranslateYDirection.Z *= -1.0;
                                obj.Objects[j].TranslateZDirection.X *= -1.0;
                                obj.Objects[j].TranslateZDirection.Z *= -1.0;
                            }
                        }
                        else
                        {
                            throw new NotImplementedException();
                        }
                    }
                }
            }

            //Check for bogie objects and reverse if necessary.....
            int bogieObjectsCount = 0;

            for (int i = 0; i < train.Cars.Length * 2; i++)
            {
                if (bogieObjects[i] != null)
                {
                    bogieObjectsCount++;
                    if (bogieObjectsReversed[i] && loadObjects)
                    {
                        if (bogieObjects[i] is StaticObject)
                        {
                            StaticObject obj = (StaticObject)bogieObjects[i];
                            obj.ApplyScale(-1.0, 1.0, -1.0);
                        }
                        else if (bogieObjects[i] is AnimatedObjectCollection)
                        {
                            AnimatedObjectCollection obj = (AnimatedObjectCollection)bogieObjects[i];
                            for (int j = 0; j < obj.Objects.Length; j++)
                            {
                                for (int h = 0; h < obj.Objects[j].States.Length; h++)
                                {
                                    obj.Objects[j].States[h].Prototype.ApplyScale(-1.0, 1.0, -1.0);
                                    obj.Objects[j].States[h].Translation.Row3.X *= -1.0f;
                                    obj.Objects[j].States[h].Translation.Row3.Z *= -1.0f;
                                }
                                obj.Objects[j].TranslateXDirection.X *= -1.0;
                                obj.Objects[j].TranslateXDirection.Z *= -1.0;
                                obj.Objects[j].TranslateYDirection.X *= -1.0;
                                obj.Objects[j].TranslateYDirection.Z *= -1.0;
                                obj.Objects[j].TranslateZDirection.X *= -1.0;
                                obj.Objects[j].TranslateZDirection.Z *= -1.0;
                            }
                        }
                        else
                        {
                            throw new NotImplementedException();
                        }
                    }
                }
            }

            if (carObjectsCount > 0 & carObjectsCount < train.Cars.Length)
            {
                Interface.AddMessage(MessageType.Warning, false, "An incomplete set of exterior objects was provided in file " + filePath);
            }

            if (bogieObjectsCount > 0 & bogieObjectsCount < train.Cars.Length * 2)
            {
                Interface.AddMessage(MessageType.Warning, false, "An incomplete set of bogie objects was provided in file " + filePath);
            }
        }
Esempio n. 5
0
        internal void Parse(string fileName, TrainBase Train, ref UnifiedObject[] CarObjects, ref UnifiedObject[] BogieObjects, ref UnifiedObject[] CouplerObjects, out bool[] interiorVisible)
        {
            //The current XML file to load
            XmlDocument currentXML = new XmlDocument();

            //Load the marker's XML file
            currentXML.Load(fileName);
            currentPath = System.IO.Path.GetDirectoryName(fileName);
            if (System.IO.File.Exists(OpenBveApi.Path.CombineFile(currentPath, "train.dat")))
            {
                for (int i = 0; i < Train.Cars.Length; i++)
                {
                    if (Train.Cars[i].Specs.IsMotorCar)
                    {
                        AccelerationCurves = new BveAccelerationCurve[Train.Cars[i].Specs.AccelerationCurves.Length];
                        for (int j = 0; j < Train.Cars[i].Specs.AccelerationCurves.Length; j++)
                        {
                            BveAccelerationCurve c = (BveAccelerationCurve)Train.Cars[i].Specs.AccelerationCurves[j];
                            AccelerationCurves[j] = c.Clone(c.Multiplier);
                        }
                    }
                }
            }
            CarObjectsReversed   = new bool[Train.Cars.Length];
            BogieObjectsReversed = new bool[Train.Cars.Length * 2];
            interiorVisible      = new bool[Train.Cars.Length];
            if (currentXML.DocumentElement != null)
            {
                XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/openBVE/Train/*[self::Car or self::Coupler]");
                if (DocumentNodes == null || DocumentNodes.Count == 0)
                {
                    Plugin.currentHost.AddMessage(MessageType.Error, false, "No car nodes defined in XML file " + fileName);
                    //If we have no appropriate nodes specified, return false and fallback to loading the legacy Sound.cfg file
                    throw new Exception("Empty train.xml file");
                }

                int carIndex = 0;
                //Use the index here for easy access to the car count
                for (int i = 0; i < DocumentNodes.Count; i++)
                {
                    if (carIndex > Train.Cars.Length)
                    {
                        Plugin.currentHost.AddMessage(MessageType.Warning, false, "WARNING: A total of " + DocumentNodes.Count + " cars were specified in XML file " + fileName + " whilst only " + Train.Cars.Length + " were specified in the train.dat file.");
                        break;
                    }
                    if (DocumentNodes[i].ChildNodes.OfType <XmlElement>().Any())
                    {
                        if (DocumentNodes[i].Name == "Car")
                        {
                            ParseCarNode(DocumentNodes[i], fileName, carIndex, ref Train, ref CarObjects, ref BogieObjects, ref interiorVisible[carIndex]);
                        }
                        else
                        {
                            if (carIndex - 1 > Train.Cars.Length - 2)
                            {
                                Plugin.currentHost.AddMessage(MessageType.Error, false, "Unexpected extra coupler encountered in XML file " + fileName);
                                continue;
                            }
                            foreach (XmlNode c in DocumentNodes[i].ChildNodes)
                            {
                                switch (c.Name.ToLowerInvariant())
                                {
                                case "minimum":
                                    if (!NumberFormats.TryParseDoubleVb6(c.InnerText, out Train.Cars[carIndex - 1].Coupler.MinimumDistanceBetweenCars))
                                    {
                                        Plugin.currentHost.AddMessage(MessageType.Error, false, "MinimumDistanceBetweenCars is invalid for coupler " + carIndex + "in XML file " + fileName);
                                    }
                                    break;

                                case "maximum":
                                    if (!NumberFormats.TryParseDoubleVb6(c.InnerText, out Train.Cars[carIndex - 1].Coupler.MaximumDistanceBetweenCars))
                                    {
                                        Plugin.currentHost.AddMessage(MessageType.Error, false, "MaximumDistanceBetweenCars is invalid for coupler " + carIndex + "in XML file " + fileName);
                                    }
                                    break;

                                case "object":
                                    if (string.IsNullOrEmpty(c.InnerText))
                                    {
                                        Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid object path for Coupler " + (carIndex - 1) + " in XML file " + fileName);
                                        break;
                                    }
                                    string f = OpenBveApi.Path.CombineFile(currentPath, c.InnerText);
                                    if (System.IO.File.Exists(f))
                                    {
                                        Plugin.currentHost.LoadObject(f, Encoding.Default, out CouplerObjects[carIndex - 1]);
                                    }
                                    break;
                                }
                            }
                        }
                    }
                    else if (!String.IsNullOrEmpty(DocumentNodes[i].InnerText))
                    {
                        try
                        {
                            string      childFile = OpenBveApi.Path.CombineFile(currentPath, DocumentNodes[i].InnerText);
                            XmlDocument childXML  = new XmlDocument();
                            childXML.Load(childFile);
                            XmlNodeList childNodes = childXML.DocumentElement.SelectNodes("/openBVE/Car");
                            //We need to save and restore the current path to make relative paths within the child file work correctly
                            string savedPath = currentPath;
                            currentPath = System.IO.Path.GetDirectoryName(childFile);
                            ParseCarNode(childNodes[0], fileName, i, ref Train, ref CarObjects, ref BogieObjects, ref interiorVisible[carIndex]);
                            currentPath = savedPath;
                        }
                        catch
                        {
                            Plugin.currentHost.AddMessage(MessageType.Error, false, "Failed to load the child Car XML file specified in " + DocumentNodes[i].InnerText);
                        }
                    }
                    if (i == DocumentNodes.Count && carIndex < Train.Cars.Length)
                    {
                        //If this is the case, the number of motor cars is the primary thing which may get confused....
                        //Not a lot to be done about this until a full replacement is built for the train.dat file & we can dump it entirely
                        Plugin.currentHost.AddMessage(MessageType.Warning, false, "WARNING: The number of cars specified in the train.xml file does not match that in the train.dat- Some properties may be invalid.");
                    }
                    if (DocumentNodes[i].Name == "Car")
                    {
                        carIndex++;
                    }
                }
                if (Train.Cars[Train.DriverCar].CameraRestrictionMode != CameraRestrictionMode.NotSpecified)
                {
                    Plugin.Renderer.Camera.CurrentRestriction = Train.Cars[Train.DriverCar].CameraRestrictionMode;
                }
                DocumentNodes = currentXML.DocumentElement.SelectNodes("/openBVE/Train/NotchDescriptions");
                if (DocumentNodes != null && DocumentNodes.Count > 0)
                {
                    //Optional section
                    for (int i = 0; i < DocumentNodes.Count; i++)
                    {
                        if (DocumentNodes[i].ChildNodes.OfType <XmlElement>().Any())
                        {
                            foreach (XmlNode c in DocumentNodes[i].ChildNodes)
                            {
                                switch (c.Name.ToLowerInvariant())
                                {
                                case "power":
                                    Train.Handles.Power.NotchDescriptions = c.InnerText.Split(';');
                                    for (int j = 0; j < Train.Handles.Power.NotchDescriptions.Length; j++)
                                    {
                                        Size s = Plugin.Renderer.Fonts.NormalFont.MeasureString(Train.Handles.Power.NotchDescriptions[j]);
                                        if (s.Width > Train.Handles.Power.MaxWidth)
                                        {
                                            Train.Handles.Power.MaxWidth = s.Width;
                                        }
                                    }
                                    break;

                                case "brake":
                                    Train.Handles.Brake.NotchDescriptions = c.InnerText.Split(';');
                                    for (int j = 0; j < Train.Handles.Brake.NotchDescriptions.Length; j++)
                                    {
                                        Size s = Plugin.Renderer.Fonts.NormalFont.MeasureString(Train.Handles.Brake.NotchDescriptions[j]);
                                        if (s.Width > Train.Handles.Brake.MaxWidth)
                                        {
                                            Train.Handles.Brake.MaxWidth = s.Width;
                                        }
                                    }
                                    break;

                                case "locobrake":
                                    if (Train.Handles.LocoBrake == null)
                                    {
                                        continue;
                                    }
                                    Train.Handles.LocoBrake.NotchDescriptions = c.InnerText.Split(';');
                                    for (int j = 0; j < Train.Handles.LocoBrake.NotchDescriptions.Length; j++)
                                    {
                                        Size s = Plugin.Renderer.Fonts.NormalFont.MeasureString(Train.Handles.LocoBrake.NotchDescriptions[j]);
                                        if (s.Width > Train.Handles.LocoBrake.MaxWidth)
                                        {
                                            Train.Handles.LocoBrake.MaxWidth = s.Width;
                                        }
                                    }
                                    break;

                                case "reverser":
                                    Train.Handles.Reverser.NotchDescriptions = c.InnerText.Split(';');
                                    for (int j = 0; j < Train.Handles.Reverser.NotchDescriptions.Length; j++)
                                    {
                                        Size s = Plugin.Renderer.Fonts.NormalFont.MeasureString(Train.Handles.Reverser.NotchDescriptions[j]);
                                        if (s.Width > Train.Handles.Reverser.MaxWidth)
                                        {
                                            Train.Handles.Reverser.MaxWidth = s.Width;
                                        }
                                    }
                                    break;
                                }
                            }
                        }
                    }
                }
                for (int i = 0; i < Train.Cars.Length; i++)
                {
                    if (CarObjects[i] != null)
                    {
                        if (CarObjectsReversed[i])
                        {
                            {
                                // reverse axle positions
                                double temp = Train.Cars[i].FrontAxle.Position;
                                Train.Cars[i].FrontAxle.Position = -Train.Cars[i].RearAxle.Position;
                                Train.Cars[i].RearAxle.Position  = -temp;
                            }
                            if (CarObjects[i] is StaticObject)
                            {
                                StaticObject obj = (StaticObject)CarObjects[i].Clone();
                                obj.ApplyScale(-1.0, 1.0, -1.0);
                                CarObjects[i] = obj;
                            }
                            else if (CarObjects[i] is AnimatedObjectCollection)
                            {
                                AnimatedObjectCollection obj = (AnimatedObjectCollection)CarObjects[i].Clone();
                                obj.Reverse();
                                CarObjects[i] = obj;
                            }
                            else
                            {
                                throw new NotImplementedException();
                            }
                        }
                    }
                }

                //Check for bogie objects and reverse if necessary.....
                for (int i = 0; i < Train.Cars.Length * 2; i++)
                {
                    bool IsOdd    = (i % 2 != 0);
                    int  CarIndex = i / 2;
                    if (BogieObjects[i] != null)
                    {
                        if (BogieObjectsReversed[i])
                        {
                            {
                                // reverse axle positions
                                if (IsOdd)
                                {
                                    double temp = Train.Cars[CarIndex].FrontBogie.FrontAxle.Position;
                                    Train.Cars[CarIndex].FrontBogie.FrontAxle.Position = -Train.Cars[CarIndex].FrontBogie.RearAxle.Position;
                                    Train.Cars[CarIndex].FrontBogie.RearAxle.Position  = -temp;
                                }
                                else
                                {
                                    double temp = Train.Cars[CarIndex].RearBogie.FrontAxle.Position;
                                    Train.Cars[CarIndex].RearBogie.FrontAxle.Position = -Train.Cars[CarIndex].RearBogie.RearAxle.Position;
                                    Train.Cars[CarIndex].RearBogie.RearAxle.Position  = -temp;
                                }
                            }
                            if (BogieObjects[i] is StaticObject)
                            {
                                StaticObject obj = (StaticObject)BogieObjects[i].Clone();
                                obj.ApplyScale(-1.0, 1.0, -1.0);
                                BogieObjects[i] = obj;
                            }
                            else if (BogieObjects[i] is AnimatedObjectCollection)
                            {
                                AnimatedObjectCollection obj = (AnimatedObjectCollection)BogieObjects[i].Clone();
                                obj.Reverse();
                                BogieObjects[i] = obj;
                            }
                            else
                            {
                                throw new NotImplementedException();
                            }
                        }
                    }
                }
            }
        }
Esempio n. 6
0
        public override bool LoadTrain(Encoding Encoding, string trainPath, ref AbstractTrain train, ref Control[] currentControls)
        {
            CurrentControls = currentControls;
            TrainBase currentTrain = train as TrainBase;

            if (currentTrain == null)
            {
                currentHost.ReportProblem(ProblemType.InvalidData, "Train was not valid");
                return(false);
            }

            if (currentTrain.State == TrainState.Bogus)
            {
                // bogus train
                string TrainData = Path.CombineFile(FileSystem.GetDataFolder("Compatibility", "PreTrain"), "train.dat");
                TrainDatParser.Parse(TrainData, Encoding.UTF8, currentTrain);
                Thread.Sleep(1);
                if (Cancel)
                {
                    return(false);
                }
            }
            else
            {
                currentTrain.TrainFolder = trainPath;
                // real train
                if (currentTrain.IsPlayerTrain)
                {
                    FileSystem.AppendToLogFile("Loading player train: " + currentTrain.TrainFolder);
                }
                else
                {
                    FileSystem.AppendToLogFile("Loading AI train: " + currentTrain.TrainFolder);
                }

                string TrainData = Path.CombineFile(currentTrain.TrainFolder, "train.dat");
                TrainDatParser.Parse(TrainData, Encoding, currentTrain);
                Thread.Sleep(1);
                if (Cancel)
                {
                    return(false);
                }
                SoundCfgParser.ParseSoundConfig(currentTrain);
                Thread.Sleep(1);
                if (Cancel)
                {
                    return(false);
                }
                // door open/close speed
                for (int i = 0; i < currentTrain.Cars.Length; i++)
                {
                    currentTrain.Cars[i].DetermineDoorClosingSpeed();
                }
            }
            // add panel section
            if (currentTrain.IsPlayerTrain)
            {
                ParsePanelConfig(currentTrain, Encoding);
                Thread.Sleep(1); if (Cancel)
                {
                    return(false);
                }
                FileSystem.AppendToLogFile("Train panel loaded sucessfully.");
            }
            // add exterior section
            if (currentTrain.State != TrainState.Bogus)
            {
                bool[]          VisibleFromInterior = new bool[currentTrain.Cars.Length];
                UnifiedObject[] CarObjects          = new UnifiedObject[currentTrain.Cars.Length];
                UnifiedObject[] BogieObjects        = new UnifiedObject[currentTrain.Cars.Length * 2];
                UnifiedObject[] CouplerObjects      = new UnifiedObject[currentTrain.Cars.Length];

                string tXml = Path.CombineFile(currentTrain.TrainFolder, "train.xml");
                if (File.Exists(tXml))
                {
                    TrainXmlParser.Parse(tXml, currentTrain, ref CarObjects, ref BogieObjects, ref CouplerObjects, ref VisibleFromInterior);
                }
                else
                {
                    ExtensionsCfgParser.ParseExtensionsConfig(currentTrain.TrainFolder, Encoding, ref CarObjects, ref BogieObjects, ref CouplerObjects, ref VisibleFromInterior, currentTrain);
                }

                currentTrain.CameraCar = currentTrain.DriverCar;
                Thread.Sleep(1);
                if (Cancel)
                {
                    return(false);
                }
                //Stores the current array index of the bogie object to add
                //Required as there are two bogies per car, and we're using a simple linear array....
                int currentBogieObject = 0;
                for (int i = 0; i < currentTrain.Cars.Length; i++)
                {
                    if (CarObjects[i] == null)
                    {
                        // load default exterior object
                        string       file = Path.CombineFile(FileSystem.GetDataFolder("Compatibility"), "exterior.csv");
                        StaticObject so;
                        currentHost.LoadStaticObject(file, Encoding.UTF8, false, out so);
                        if (so == null)
                        {
                            CarObjects[i] = null;
                        }
                        else
                        {
                            StaticObject c = (StaticObject)so.Clone();                              //Clone as otherwise the cached object doesn't scale right
                            c.ApplyScale(currentTrain.Cars[i].Width, currentTrain.Cars[i].Height, currentTrain.Cars[i].Length);
                            CarObjects[i] = c;
                        }
                    }

                    if (CarObjects[i] != null)
                    {
                        // add object
                        currentTrain.Cars[i].LoadCarSections(CarObjects[i], VisibleFromInterior[i]);
                    }

                    if (CouplerObjects[i] != null)
                    {
                        currentTrain.Cars[i].Coupler.LoadCarSections(CouplerObjects[i], VisibleFromInterior[i]);
                    }

                    //Load bogie objects
                    if (BogieObjects[currentBogieObject] != null)
                    {
                        currentTrain.Cars[i].FrontBogie.LoadCarSections(BogieObjects[currentBogieObject], VisibleFromInterior[i]);
                    }

                    currentBogieObject++;
                    if (BogieObjects[currentBogieObject] != null)
                    {
                        currentTrain.Cars[i].RearBogie.LoadCarSections(BogieObjects[currentBogieObject], VisibleFromInterior[i]);
                    }

                    currentBogieObject++;
                }
            }
            // place cars
            currentTrain.PlaceCars(0.0);
            currentControls = CurrentControls;
            return(true);
        }
Esempio n. 7
0
        private static void ParseExtensionsConfig(string filePath, Encoding encoding, out UnifiedObject[] carObjects, out UnifiedObject[] bogieObjects, out UnifiedObject[] couplerObjects, out double[] axleLocations, out double[] couplerDistances, out TrainManager.Train train, bool loadObjects)
        {
            CultureInfo culture = CultureInfo.InvariantCulture;

            carObjects       = new UnifiedObject[0];
            bogieObjects     = new UnifiedObject[0];
            couplerObjects   = new UnifiedObject[0];
            axleLocations    = new double[0];
            couplerDistances = new double[0];
            train            = new TrainManager.Train {
                Cars = new TrainManager.Car[0]
            };

            if (!File.Exists(filePath))
            {
                return;
            }

            bool[] carObjectsReversed   = new bool[0];
            bool[] bogieObjectsReversed = new bool[0];
            bool[] carsDefined          = new bool[0];
            bool[] bogiesDefined        = new bool[0];
            bool[] couplerDefined       = new bool[0];

            string trainPath = System.IO.Path.GetDirectoryName(filePath);

            if (encoding == null)
            {
                encoding = TextEncoding.GetSystemEncodingFromFile(trainPath);
            }

            string[] lines = File.ReadAllLines(filePath, encoding);
            for (int i = 0; i < lines.Length; i++)
            {
                int j = lines[i].IndexOf(';');
                if (j >= 0)
                {
                    lines[i] = lines[i].Substring(0, j).Trim(new char[] { });
                }
                else
                {
                    lines[i] = lines[i].Trim(new char[] { });
                }
            }

            for (int i = 0; i < lines.Length; i++)
            {
                if (lines[i].Length != 0)
                {
                    switch (lines[i].ToLowerInvariant())
                    {
                    case "[exterior]":
                        // exterior
                        i++;
                        while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal))
                        {
                            if (lines[i].Length != 0)
                            {
                                int j = lines[i].IndexOf("=", StringComparison.Ordinal);
                                if (j >= 0)
                                {
                                    // ReSharper disable RedundantExplicitParamsArrayCreation
                                    string a = lines[i].Substring(0, j).TrimEnd(new char[] { });
                                    string b = lines[i].Substring(j + 1).TrimStart(new char[] { });
                                    // ReSharper restore RedundantExplicitParamsArrayCreation

                                    int n;
                                    if (int.TryParse(a, NumberStyles.Integer, culture, out n))
                                    {
                                        if (n >= 0)
                                        {
                                            if (n >= train.Cars.Length)
                                            {
                                                Array.Resize(ref train.Cars, n + 1);
                                                train.Cars[n] = new TrainManager.Car(train);
                                                Array.Resize(ref carObjects, n + 1);
                                                Array.Resize(ref bogieObjects, (n + 1) * 2);
                                                Array.Resize(ref couplerObjects, n);
                                                Array.Resize(ref carObjectsReversed, n + 1);
                                                Array.Resize(ref bogieObjectsReversed, (n + 1) * 2);
                                                Array.Resize(ref carsDefined, n + 1);
                                                Array.Resize(ref bogiesDefined, (n + 1) * 2);
                                                Array.Resize(ref couplerDefined, n);
                                                Array.Resize(ref axleLocations, (n + 1) * 2);
                                                Array.Resize(ref couplerDistances, n);
                                            }
                                            if (Path.ContainsInvalidChars(b))
                                            {
                                                Interface.AddMessage(MessageType.Error, false, $"File contains illegal characters at line {(i + 1).ToString(culture)} in file {filePath}");
                                            }
                                            else
                                            {
                                                string file = Path.CombineFile(trainPath, b);
                                                if (File.Exists(file))
                                                {
                                                    if (loadObjects)
                                                    {
                                                        Program.CurrentHost.LoadObject(file, encoding, out carObjects[n]);
                                                    }
                                                }
                                                else
                                                {
                                                    Interface.AddMessage(MessageType.Error, true, $"The car object {file} does not exist at line {(i + 1).ToString(culture)} in file {filePath}");
                                                }
                                            }
                                        }
                                        else
                                        {
                                            Interface.AddMessage(MessageType.Error, false, $"The car index {a} does not reference an existing car at line {(i + 1).ToString(culture)} in file {filePath}");
                                        }
                                    }
                                    else
                                    {
                                        Interface.AddMessage(MessageType.Error, false, $"The car index is expected to be an integer at line {(i + 1).ToString(culture)} in file {filePath}");
                                    }
                                }
                                else
                                {
                                    Interface.AddMessage(MessageType.Error, false, $"Invalid statement {lines[i]} encountered at line {(i + 1).ToString(culture)} in file {filePath}");
                                }
                            }
                            i++;
                        }
                        i--;
                        break;

                    default:
                        if (lines[i].StartsWith("[car", StringComparison.OrdinalIgnoreCase) & lines[i].EndsWith("]", StringComparison.Ordinal))
                        {
                            // car
                            string t = lines[i].Substring(4, lines[i].Length - 5);
                            int    n;
                            if (int.TryParse(t, NumberStyles.Integer, culture, out n))
                            {
                                if (n >= 0)
                                {
                                    if (n >= train.Cars.Length)
                                    {
                                        Array.Resize(ref train.Cars, n + 1);
                                        train.Cars[n] = new TrainManager.Car(train);
                                        Array.Resize(ref carObjects, n + 1);
                                        Array.Resize(ref bogieObjects, (n + 1) * 2);
                                        Array.Resize(ref carObjectsReversed, n + 1);
                                        Array.Resize(ref bogieObjectsReversed, (n + 1) * 2);
                                        Array.Resize(ref couplerObjects, n);
                                        Array.Resize(ref carsDefined, n + 1);
                                        Array.Resize(ref bogiesDefined, (n + 1) * 2);
                                        Array.Resize(ref couplerDefined, n);
                                        Array.Resize(ref axleLocations, (n + 1) * 2);
                                        Array.Resize(ref couplerDistances, n);
                                    }
                                    if (carsDefined[n])
                                    {
                                        Interface.AddMessage(MessageType.Error, false, $"Car {n.ToString(culture)} has already been declared at line {(i + 1).ToString(culture)} in file {filePath}");
                                    }
                                    carsDefined[n] = true;
                                    i++;
                                    while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal))
                                    {
                                        if (lines[i].Length != 0)
                                        {
                                            int j = lines[i].IndexOf("=", StringComparison.Ordinal);
                                            if (j >= 0)
                                            {
                                                // ReSharper disable RedundantExplicitParamsArrayCreation
                                                string a = lines[i].Substring(0, j).TrimEnd(new char[] { });
                                                string b = lines[i].Substring(j + 1).TrimStart(new char[] { });
                                                // ReSharper restore RedundantExplicitParamsArrayCreation

                                                switch (a.ToLowerInvariant())
                                                {
                                                case "object":
                                                    if (string.IsNullOrEmpty(b))
                                                    {
                                                        Interface.AddMessage(MessageType.Error, true, $"An empty car object was supplied at line {(i + 1).ToString(culture)} in file {filePath}");
                                                        break;
                                                    }
                                                    if (Path.ContainsInvalidChars(b))
                                                    {
                                                        Interface.AddMessage(MessageType.Error, false, $"File contains illegal characters at line {(i + 1).ToString(culture)} in file {filePath}");
                                                    }
                                                    else
                                                    {
                                                        string file = Path.CombineFile(trainPath, b);
                                                        if (File.Exists(file))
                                                        {
                                                            if (loadObjects)
                                                            {
                                                                Program.CurrentHost.LoadObject(file, encoding, out carObjects[n]);
                                                            }
                                                        }
                                                        else
                                                        {
                                                            Interface.AddMessage(MessageType.Error, true, $"The car object {file} does not exist at line {(i + 1).ToString(culture)} in file {filePath}");
                                                        }
                                                    }
                                                    break;

                                                case "length":
                                                {
                                                    double m;
                                                    if (double.TryParse(b, NumberStyles.Float, culture, out m))
                                                    {
                                                        if (m > 0.0)
                                                        {
                                                            train.Cars[n].Length = m;
                                                        }
                                                        else
                                                        {
                                                            Interface.AddMessage(MessageType.Error, false, $"Value is expected to be a positive floating-point number in {a} at line {(i + 1).ToString(culture)} in file {filePath}");
                                                        }
                                                    }
                                                    else
                                                    {
                                                        Interface.AddMessage(MessageType.Error, false, $"Value is expected to be a positive floating-point number in {a} at line {(i + 1).ToString(culture)} in file {filePath}");
                                                    }
                                                }
                                                break;

                                                case "reversed":
                                                    carObjectsReversed[n] = b.Equals("true", StringComparison.OrdinalIgnoreCase);
                                                    break;

                                                case "axles":
                                                    int k = b.IndexOf(',');
                                                    if (k >= 0)
                                                    {
                                                        // ReSharper disable RedundantExplicitParamsArrayCreation
                                                        string c = b.Substring(0, k).TrimEnd(new char[] { });
                                                        string d = b.Substring(k + 1).TrimStart(new char[] { });
                                                        // ReSharper restore RedundantExplicitParamsArrayCreation

                                                        double rear, front;
                                                        if (!double.TryParse(c, NumberStyles.Float, culture, out rear))
                                                        {
                                                            Interface.AddMessage(MessageType.Error, false, $"Rear is expected to be a floating-point number in {a} at line {(i + 1).ToString(culture)} in file {filePath}");
                                                        }
                                                        else if (!double.TryParse(d, NumberStyles.Float, culture, out front))
                                                        {
                                                            Interface.AddMessage(MessageType.Error, false, $"Front is expected to be a floating-point number in {a} at line {(i + 1).ToString(culture)} in file {filePath}");
                                                        }
                                                        else if (rear >= front)
                                                        {
                                                            Interface.AddMessage(MessageType.Error, false, $"Rear is expected to be less than Front in {a} at line {(i + 1).ToString(culture)} in file {filePath}");
                                                        }
                                                        else
                                                        {
                                                            if (n == 0)
                                                            {
                                                                axleLocations[n]     = rear;
                                                                axleLocations[n + 1] = front;
                                                            }
                                                            else
                                                            {
                                                                axleLocations[n * 2]     = rear;
                                                                axleLocations[n * 2 + 1] = front;
                                                            }
                                                        }
                                                    }
                                                    else
                                                    {
                                                        Interface.AddMessage(MessageType.Error, false, $"An argument-separating comma is expected in {a} at line {(i + 1).ToString(culture)} in file {filePath}");
                                                    }
                                                    break;

                                                default:
                                                    Interface.AddMessage(MessageType.Warning, false, $"Unsupported key-value pair {a} encountered at line {(i + 1).ToString(culture)} in file {filePath}");
                                                    break;
                                                }
                                            }
                                            else
                                            {
                                                Interface.AddMessage(MessageType.Error, false, $"Invalid statement {lines[i]} encountered at line {(i + 1).ToString(culture)} in file {filePath}");
                                            }
                                        }
                                        i++;
                                    }
                                    i--;
                                }
                                else
                                {
                                    Interface.AddMessage(MessageType.Error, false, $"The car index {t} does not reference an existing car at line {(i + 1).ToString(culture)} in file {filePath}");
                                }
                            }
                            else
                            {
                                Interface.AddMessage(MessageType.Error, false, $"The car index is expected to be an integer at line {(i + 1).ToString(culture)} in file {filePath}");
                            }
                        }
                        else if (lines[i].StartsWith("[bogie", StringComparison.OrdinalIgnoreCase) & lines[i].EndsWith("]", StringComparison.Ordinal))
                        {
                            // bogie
                            string t = lines[i].Substring(6, lines[i].Length - 7);
                            int    n;
                            if (int.TryParse(t, NumberStyles.Integer, culture, out n))
                            {
                                if (n >= train.Cars.Length * 2)
                                {
                                    Array.Resize(ref train.Cars, n / 2 + 1);
                                    if (n == 0)
                                    {
                                        train.Cars[0] = new TrainManager.Car(train);
                                        Array.Resize(ref axleLocations, 2);
                                    }
                                    else
                                    {
                                        train.Cars[n / 2] = new TrainManager.Car(train);
                                        Array.Resize(ref axleLocations, (n / 2 + 1) * 2);
                                    }

                                    Array.Resize(ref carObjects, n / 2 + 1);
                                    Array.Resize(ref bogieObjects, n + 2);
                                    Array.Resize(ref couplerObjects, n / 2);
                                    Array.Resize(ref carObjectsReversed, n / 2 + 1);
                                    Array.Resize(ref bogieObjectsReversed, n + 2);
                                    Array.Resize(ref carsDefined, n / 2 + 1);
                                    Array.Resize(ref bogiesDefined, n + 2);
                                    Array.Resize(ref couplerDefined, n / 2);
                                    Array.Resize(ref couplerDistances, n / 2);
                                }

                                if (n > bogiesDefined.Length - 1)
                                {
                                    continue;
                                }
                                if (bogiesDefined[n])
                                {
                                    Interface.AddMessage(MessageType.Error, false, $"Bogie {n.ToString(culture)} has already been declared at line {(i + 1).ToString(culture)} in file {filePath}");
                                }
                                bogiesDefined[n] = true;
                                //Assuming that there are two bogies per car
                                if (n >= 0 & n < train.Cars.Length * 2)
                                {
                                    i++;
                                    while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal))
                                    {
                                        if (lines[i].Length != 0)
                                        {
                                            int j = lines[i].IndexOf("=", StringComparison.Ordinal);
                                            if (j >= 0)
                                            {
                                                // ReSharper disable RedundantExplicitParamsArrayCreation
                                                string a = lines[i].Substring(0, j).TrimEnd(new char[] { });
                                                string b = lines[i].Substring(j + 1).TrimStart(new char[] { });
                                                // ReSharper restore RedundantExplicitParamsArrayCreation

                                                switch (a.ToLowerInvariant())
                                                {
                                                case "object":
                                                    if (Path.ContainsInvalidChars(b))
                                                    {
                                                        Interface.AddMessage(MessageType.Error, false, $"File contains illegal characters at line {(i + 1).ToString(culture)} in file {filePath}");
                                                    }
                                                    else
                                                    {
                                                        if (string.IsNullOrEmpty(b))
                                                        {
                                                            Interface.AddMessage(MessageType.Error, true, $"An empty bogie object was supplied at line {(i + 1).ToString(culture)} in file {filePath}");
                                                            break;
                                                        }
                                                        string file = Path.CombineFile(trainPath, b);
                                                        if (File.Exists(file))
                                                        {
                                                            if (loadObjects)
                                                            {
                                                                Program.CurrentHost.LoadObject(file, encoding, out bogieObjects[n]);
                                                            }
                                                        }
                                                        else
                                                        {
                                                            Interface.AddMessage(MessageType.Error, true, $"The bogie object {file} does not exist at line {(i + 1).ToString(culture)} in file {filePath}");
                                                        }
                                                    }
                                                    break;

                                                case "length":
                                                {
                                                    Interface.AddMessage(MessageType.Error, false, $"A defined length is not supported for bogies at line {(i + 1).ToString(culture)} in file {filePath}");
                                                }
                                                break;

                                                case "reversed":
                                                    bogieObjectsReversed[n] = b.Equals("true", StringComparison.OrdinalIgnoreCase);
                                                    break;

                                                case "axles":
                                                    //Axles aren't used in bogie positioning, just in rotation
                                                    break;

                                                default:
                                                    Interface.AddMessage(MessageType.Warning, false, $"Unsupported key-value pair {a} encountered at line {(i + 1).ToString(culture)} in file {filePath}");
                                                    break;
                                                }
                                            }
                                            else
                                            {
                                                Interface.AddMessage(MessageType.Error, false, $"Invalid statement {lines[i]} encountered at line {(i + 1).ToString(culture)} in file {filePath}");
                                            }
                                        }
                                        i++;
                                    }
                                    i--;
                                }
                                else
                                {
                                    Interface.AddMessage(MessageType.Error, false, $"The bogie index {t} does not reference an existing bogie at line {(i + 1).ToString(culture)} in file {filePath}");
                                }
                            }
                            else
                            {
                                Interface.AddMessage(MessageType.Error, false, $"The bogie index is expected to be an integer at line {(i + 1).ToString(culture)} in file {filePath}");
                            }
                        }
                        else if (lines[i].StartsWith("[coupler", StringComparison.OrdinalIgnoreCase) & lines[i].EndsWith("]", StringComparison.Ordinal))
                        {
                            // coupler
                            string t = lines[i].Substring(8, lines[i].Length - 9);
                            int    n;
                            if (int.TryParse(t, NumberStyles.Integer, culture, out n))
                            {
                                if (n >= 0)
                                {
                                    if (n >= train.Cars.Length - 1)
                                    {
                                        Array.Resize(ref train.Cars, n + 2);
                                        train.Cars[n + 1] = new TrainManager.Car(train);
                                        Array.Resize(ref carObjects, n + 2);
                                        Array.Resize(ref bogieObjects, (n + 2) * 2);
                                        Array.Resize(ref carObjectsReversed, n + 2);
                                        Array.Resize(ref bogieObjectsReversed, (n + 2) * 2);
                                        Array.Resize(ref couplerObjects, n + 1);
                                        Array.Resize(ref carsDefined, n + 2);
                                        Array.Resize(ref bogiesDefined, (n + 2) * 2);
                                        Array.Resize(ref couplerDefined, n + 1);
                                        Array.Resize(ref axleLocations, (n + 2) * 2);
                                        Array.Resize(ref couplerDistances, n + 1);
                                    }
                                    if (couplerDefined[n])
                                    {
                                        Interface.AddMessage(MessageType.Error, false, $"Coupler {n.ToString(culture)} has already been declared at line {(i + 1).ToString(culture)} in file {filePath}");
                                    }
                                    couplerDefined[n] = true;
                                    i++;
                                    while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal))
                                    {
                                        if (lines[i].Length != 0)
                                        {
                                            int j = lines[i].IndexOf("=", StringComparison.Ordinal);
                                            if (j >= 0)
                                            {
                                                // ReSharper disable RedundantExplicitParamsArrayCreation
                                                string a = lines[i].Substring(0, j).TrimEnd(new char[] { });
                                                string b = lines[i].Substring(j + 1).TrimStart(new char[] { });
                                                // ReSharper restore RedundantExplicitParamsArrayCreation

                                                switch (a.ToLowerInvariant())
                                                {
                                                case "distances":
                                                {
                                                    int k = b.IndexOf(',');
                                                    if (k >= 0)
                                                    {
                                                        // ReSharper disable RedundantExplicitParamsArrayCreation
                                                        string c = b.Substring(0, k).TrimEnd(new char[] { });
                                                        string d = b.Substring(k + 1).TrimStart(new char[] { });
                                                        // ReSharper restore RedundantExplicitParamsArrayCreation

                                                        double min, max;
                                                        if (!double.TryParse(c, NumberStyles.Float, culture, out min))
                                                        {
                                                            Interface.AddMessage(MessageType.Error, false, $"Minimum is expected to be a floating-point number in {a} at line {(i + 1).ToString(culture)} in file {filePath}");
                                                        }
                                                        else if (!double.TryParse(d, NumberStyles.Float, culture, out max))
                                                        {
                                                            Interface.AddMessage(MessageType.Error, false, $"Maximum is expected to be a floating-point number in {a} at line {(i + 1).ToString(culture)} in file {filePath}");
                                                        }
                                                        else if (min > max)
                                                        {
                                                            Interface.AddMessage(MessageType.Error, false, $"Minimum is expected to be less than Maximum in {a} at line {(i + 1).ToString(culture)} in file {filePath}");
                                                        }
                                                        else
                                                        {
                                                            couplerDistances[n] = 0.5 * (min + max);
                                                        }
                                                    }
                                                    else
                                                    {
                                                        Interface.AddMessage(MessageType.Error, false, $"An argument-separating comma is expected in {a} at line {(i + 1).ToString(culture)} in file {filePath}");
                                                    }
                                                }
                                                break;

                                                case "object":
                                                    if (string.IsNullOrEmpty(b))
                                                    {
                                                        Interface.AddMessage(MessageType.Error, true, $"An empty coupler object was supplied at line {(i + 1).ToString(culture)} in file {filePath}");
                                                        break;
                                                    }
                                                    if (Path.ContainsInvalidChars(b))
                                                    {
                                                        Interface.AddMessage(MessageType.Error, false, $"File contains illegal characters at line {(i + 1).ToString(culture)} in file {filePath}");
                                                    }
                                                    else
                                                    {
                                                        string file = Path.CombineFile(trainPath, b);
                                                        if (File.Exists(file))
                                                        {
                                                            if (loadObjects)
                                                            {
                                                                Program.CurrentHost.LoadObject(file, encoding, out couplerObjects[n]);
                                                            }
                                                        }
                                                        else
                                                        {
                                                            Interface.AddMessage(MessageType.Error, true, $"The coupler object {file} does not exist at line {(i + 1).ToString(culture)} in file {filePath}");
                                                        }
                                                    }
                                                    break;

                                                default:
                                                    Interface.AddMessage(MessageType.Warning, false, $"Unsupported key-value pair {a} encountered at line {(i + 1).ToString(culture)} in file {filePath}");
                                                    break;
                                                }
                                            }
                                            else
                                            {
                                                Interface.AddMessage(MessageType.Error, false, $"Invalid statement {lines[i]} encountered at line {(i + 1).ToString(culture)} in file {filePath}");
                                            }
                                        }
                                        i++;
                                    }
                                    i--;
                                }
                                else
                                {
                                    Interface.AddMessage(MessageType.Error, false, $"The coupler index {t} does not reference an existing coupler at line {(i + 1).ToString(culture)} in file {filePath}");
                                }
                            }
                            else
                            {
                                Interface.AddMessage(MessageType.Error, false, $"The coupler index is expected to be an integer at line {(i + 1).ToString(culture)} in file {filePath}");
                            }
                        }
                        else
                        {
                            // default
                            if (lines.Length == 1 && encoding.Equals(Encoding.Unicode))
                            {
                                /*
                                 * If only one line, there's a good possibility that our file is NOT Unicode at all
                                 * and that the misdetection has turned it into garbage
                                 *
                                 * Try again with ASCII instead
                                 */
                                ParseExtensionsConfig(filePath, Encoding.GetEncoding(1252), out carObjects, out bogieObjects, out couplerObjects, out axleLocations, out couplerDistances, out train, loadObjects);
                                return;
                            }
                            Interface.AddMessage(MessageType.Error, false, $"Invalid statement {lines[i]} encountered at line {(i + 1).ToString(culture)} in file {filePath}");
                        }
                        break;
                    }
                }
            }

            // check for car objects and reverse if necessary
            int carObjectsCount = 0;

            for (int i = 0; i < train.Cars.Length; i++)
            {
                if (carObjects[i] != null)
                {
                    carObjectsCount++;
                    if (carObjectsReversed[i] && loadObjects)
                    {
                        if (carObjects[i] is StaticObject)
                        {
                            StaticObject obj = (StaticObject)carObjects[i];
                            obj.ApplyScale(-1.0, 1.0, -1.0);
                        }
                        else if (carObjects[i] is AnimatedObjectCollection)
                        {
                            AnimatedObjectCollection obj = (AnimatedObjectCollection)carObjects[i];
                            foreach (AnimatedObject animatedObj in obj.Objects)
                            {
                                foreach (ObjectState state in animatedObj.States)
                                {
                                    state.Prototype.ApplyScale(-1.0, 1.0, -1.0);
                                    Matrix4D t = state.Translation;
                                    t.Row3.X         *= -1.0f;
                                    t.Row3.Z         *= -1.0f;
                                    state.Translation = t;
                                }
                                animatedObj.TranslateXDirection.X *= -1.0;
                                animatedObj.TranslateXDirection.Z *= -1.0;
                                animatedObj.TranslateYDirection.X *= -1.0;
                                animatedObj.TranslateYDirection.Z *= -1.0;
                                animatedObj.TranslateZDirection.X *= -1.0;
                                animatedObj.TranslateZDirection.Z *= -1.0;
                            }
                        }
                        else
                        {
                            throw new NotImplementedException();
                        }
                    }
                }
            }

            //Check for bogie objects and reverse if necessary.....
            int bogieObjectsCount = 0;

            for (int i = 0; i < train.Cars.Length * 2; i++)
            {
                if (bogieObjects[i] != null)
                {
                    bogieObjectsCount++;
                    if (bogieObjectsReversed[i] && loadObjects)
                    {
                        if (bogieObjects[i] is StaticObject)
                        {
                            StaticObject obj = (StaticObject)bogieObjects[i];
                            obj.ApplyScale(-1.0, 1.0, -1.0);
                        }
                        else if (bogieObjects[i] is AnimatedObjectCollection)
                        {
                            AnimatedObjectCollection obj = (AnimatedObjectCollection)bogieObjects[i];
                            foreach (AnimatedObject animatedObj in obj.Objects)
                            {
                                foreach (ObjectState state in animatedObj.States)
                                {
                                    state.Prototype.ApplyScale(-1.0, 1.0, -1.0);
                                    Matrix4D t = state.Translation;
                                    t.Row3.X         *= -1.0f;
                                    t.Row3.Z         *= -1.0f;
                                    state.Translation = t;
                                }
                                animatedObj.TranslateXDirection.X *= -1.0;
                                animatedObj.TranslateXDirection.Z *= -1.0;
                                animatedObj.TranslateYDirection.X *= -1.0;
                                animatedObj.TranslateYDirection.Z *= -1.0;
                                animatedObj.TranslateZDirection.X *= -1.0;
                                animatedObj.TranslateZDirection.Z *= -1.0;
                            }
                        }
                        else
                        {
                            throw new NotImplementedException();
                        }
                    }
                }
            }

            if (carObjectsCount > 0 & carObjectsCount < train.Cars.Length)
            {
                Interface.AddMessage(MessageType.Warning, false, $"An incomplete set of exterior objects was provided in file {filePath}");
            }

            if (bogieObjectsCount > 0 & bogieObjectsCount < train.Cars.Length * 2)
            {
                Interface.AddMessage(MessageType.Warning, false, $"An incomplete set of bogie objects was provided in file {filePath}");
            }
        }
Esempio n. 8
0
        private static void LoadEverythingThreaded()
        {
            string RailwayFolder = GetRailwayFolder(CurrentRouteFile);
            string ObjectFolder  = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Object");
            string SoundFolder   = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Sound");

            Game.Reset(true);
            Game.MinimalisticSimulation = true;
            // screen
            Program.Renderer.Camera.CurrentMode = CameraViewMode.Interior;

            Program.CurrentRoute.TrackFollowingObjects = TrainManager.TFOs;
            bool loaded = false;

            Program.FileSystem.AppendToLogFile("INFO: " + Program.CurrentHost.AvailableRoutePluginCount + " Route loading plugins available.");
            Program.FileSystem.AppendToLogFile("INFO: " + Program.CurrentHost.AvailableObjectPluginCount + " Object loading plugins available.");
            Program.FileSystem.AppendToLogFile("INFO: " + Program.CurrentHost.AvailableRoutePluginCount + " Sound loading plugins available.");
            for (int i = 0; i < Program.CurrentHost.Plugins.Length; i++)
            {
                if (Program.CurrentHost.Plugins[i].Route != null && Program.CurrentHost.Plugins[i].Route.CanLoadRoute(CurrentRouteFile))
                {
                    object Route = (object)Program.CurrentRoute;                     //must cast to allow us to use the ref keyword.
                    if (Program.CurrentHost.Plugins[i].Route.LoadRoute(CurrentRouteFile, CurrentRouteEncoding, CurrentTrainFolder, ObjectFolder, SoundFolder, false, ref Route))
                    {
                        Program.CurrentRoute = (CurrentRoute)Route;
                        Program.Renderer.Lighting.OptionAmbientColor  = Program.CurrentRoute.Atmosphere.AmbientLightColor;
                        Program.Renderer.Lighting.OptionDiffuseColor  = Program.CurrentRoute.Atmosphere.DiffuseLightColor;
                        Program.Renderer.Lighting.OptionLightPosition = Program.CurrentRoute.Atmosphere.LightPosition;
                        loaded = true;
                        break;
                    }
                    var currentError = Translations.GetInterfaceString("errors_critical_file");
                    currentError = currentError.Replace("[file]", System.IO.Path.GetFileName(CurrentRouteFile));
                    MessageBox.Show(currentError, @"OpenBVE", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                    Interface.AddMessage(MessageType.Critical, false, "The route and train loader encountered the following critical error: " + Program.CurrentHost.Plugins[i].Route.LastException.Message);
                    CrashHandler.LoadingCrash(Program.CurrentHost.Plugins[i].Route.LastException.Message, false);
                    Program.RestartArguments = " ";
                    Cancel = true;
                    return;
                }
            }

            TrainManager.TFOs = Program.CurrentRoute.TrackFollowingObjects;
            if (!loaded)
            {
                throw new Exception("No plugins capable of loading routefile " + CurrentRouteFile + " were found.");
            }
            Thread createIllustrations = new Thread(Program.CurrentRoute.Information.LoadInformation)
            {
                IsBackground = true
            };

            createIllustrations.Start();
            System.Threading.Thread.Sleep(1); if (Cancel)
            {
                return;
            }
            Program.CurrentRoute.Atmosphere.CalculateSeaLevelConstants();
            if (Program.CurrentRoute.BogusPreTrainInstructions.Length != 0)
            {
                double t = Program.CurrentRoute.BogusPreTrainInstructions[0].Time;
                double p = Program.CurrentRoute.BogusPreTrainInstructions[0].TrackPosition;
                for (int i = 1; i < Program.CurrentRoute.BogusPreTrainInstructions.Length; i++)
                {
                    if (Program.CurrentRoute.BogusPreTrainInstructions[i].Time > t)
                    {
                        t = Program.CurrentRoute.BogusPreTrainInstructions[i].Time;
                    }
                    else
                    {
                        t += 1.0;
                        Program.CurrentRoute.BogusPreTrainInstructions[i].Time = t;
                    }
                    if (Program.CurrentRoute.BogusPreTrainInstructions[i].TrackPosition > p)
                    {
                        p = Program.CurrentRoute.BogusPreTrainInstructions[i].TrackPosition;
                    }
                    else
                    {
                        p += 1.0;
                        Program.CurrentRoute.BogusPreTrainInstructions[i].TrackPosition = p;
                    }
                }
            }
            Program.Renderer.CameraTrackFollower = new TrackFollower(Program.CurrentHost)
            {
                Train = null, Car = null
            };
            if (Program.CurrentRoute.Stations.Length == 1)
            {
                //Log the fact that only a single station is present, as this is probably not right
                Program.FileSystem.AppendToLogFile("The processed route file only contains a single station.");
            }
            Program.FileSystem.AppendToLogFile("Route file loaded successfully.");
            // initialize trains
            System.Threading.Thread.Sleep(1); if (Cancel)
            {
                return;
            }
            TrainManager.Trains = new TrainManager.Train[Program.CurrentRoute.PrecedingTrainTimeDeltas.Length + 1 + (Program.CurrentRoute.BogusPreTrainInstructions.Length != 0 ? 1 : 0)];
            for (int k = 0; k < TrainManager.Trains.Length; k++)
            {
                if (k == TrainManager.Trains.Length - 1 & Program.CurrentRoute.BogusPreTrainInstructions.Length != 0)
                {
                    TrainManager.Trains[k] = new TrainManager.Train(TrainState.Bogus);
                }
                else
                {
                    TrainManager.Trains[k] = new TrainManager.Train(TrainState.Pending);
                }
            }
            TrainManager.PlayerTrain = TrainManager.Trains[Program.CurrentRoute.PrecedingTrainTimeDeltas.Length];

            UnifiedObject[] CarObjects     = null;
            UnifiedObject[] BogieObjects   = null;
            UnifiedObject[] CouplerObjects = null;

            // load trains
            double TrainProgressMaximum = 0.7 + 0.3 * (double)TrainManager.Trains.Length;

            for (int k = 0; k < TrainManager.Trains.Length; k++)
            {
                //Sleep for 20ms to allow route loading locks to release
                Thread.Sleep(20);
                if (TrainManager.Trains[k].State == TrainState.Bogus)
                {
                    // bogus train
                    string TrainData = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Compatibility", "PreTrain"), "train.dat");
                    TrainDatParser.ParseTrainData(TrainData, System.Text.Encoding.UTF8, TrainManager.Trains[k]);
                    System.Threading.Thread.Sleep(1); if (Cancel)
                    {
                        return;
                    }
                    TrainManager.Trains[k].InitializeCarSounds();
                    System.Threading.Thread.Sleep(1); if (Cancel)
                    {
                        return;
                    }
                    TrainProgressCurrentWeight = 0.3 / TrainProgressMaximum;
                    TrainProgressCurrentSum   += TrainProgressCurrentWeight;
                }
                else
                {
                    TrainManager.Trains[k].TrainFolder = CurrentTrainFolder;
                    // real train
                    if (TrainManager.Trains[k].IsPlayerTrain)
                    {
                        Program.FileSystem.AppendToLogFile("Loading player train: " + TrainManager.Trains[k].TrainFolder);
                    }
                    else
                    {
                        Program.FileSystem.AppendToLogFile("Loading AI train: " + TrainManager.Trains[k].TrainFolder);
                    }
                    TrainProgressCurrentWeight = 0.1 / TrainProgressMaximum;
                    string TrainData = OpenBveApi.Path.CombineFile(TrainManager.Trains[k].TrainFolder, "train.dat");
                    TrainDatParser.ParseTrainData(TrainData, CurrentTrainEncoding, TrainManager.Trains[k]);
                    TrainProgressCurrentSum += TrainProgressCurrentWeight;
                    System.Threading.Thread.Sleep(1); if (Cancel)
                    {
                        return;
                    }
                    TrainProgressCurrentWeight = 0.2 / TrainProgressMaximum;
                    SoundCfgParser.ParseSoundConfig(TrainManager.Trains[k].TrainFolder, TrainManager.Trains[k]);
                    TrainProgressCurrentSum += TrainProgressCurrentWeight;
                    System.Threading.Thread.Sleep(1); if (Cancel)
                    {
                        return;
                    }
                    // door open/close speed
                    for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++)
                    {
                        if (TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency <= 0.0)
                        {
                            if (TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer != null & TrainManager.Trains[k].Cars[i].Doors[1].OpenSound.Buffer != null)
                            {
                                Program.Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer);
                                Program.Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[1].OpenSound.Buffer);
                                double a = TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer.Duration;
                                double b = TrainManager.Trains[k].Cars[i].Doors[1].OpenSound.Buffer.Duration;
                                TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8;
                            }
                            else if (TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer != null)
                            {
                                Program.Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer);
                                double a = TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer.Duration;
                                TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = a > 0.0 ? 1.0 / a : 0.8;
                            }
                            else if (TrainManager.Trains[k].Cars[i].Doors[1].OpenSound.Buffer != null)
                            {
                                Program.Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer);
                                double b = TrainManager.Trains[k].Cars[i].Doors[1].OpenSound.Buffer.Duration;
                                TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = b > 0.0 ? 1.0 / b : 0.8;
                            }
                            else
                            {
                                TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = 0.8;
                            }
                        }
                        if (TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency <= 0.0)
                        {
                            if (TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer != null & TrainManager.Trains[k].Cars[i].Doors[1].CloseSound.Buffer != null)
                            {
                                Program.Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer);
                                Program.Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[1].CloseSound.Buffer);
                                double a = TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer.Duration;
                                double b = TrainManager.Trains[k].Cars[i].Doors[1].CloseSound.Buffer.Duration;
                                TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8;
                            }
                            else if (TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer != null)
                            {
                                Program.Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer);
                                double a = TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer.Duration;
                                TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = a > 0.0 ? 1.0 / a : 0.8;
                            }
                            else if (TrainManager.Trains[k].Cars[i].Doors[1].CloseSound.Buffer != null)
                            {
                                Program.Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer);
                                double b = TrainManager.Trains[k].Cars[i].Doors[1].CloseSound.Buffer.Duration;
                                TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = b > 0.0 ? 1.0 / b : 0.8;
                            }
                            else
                            {
                                TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = 0.8;
                            }
                        }
                        const double f = 0.015;
                        const double g = 2.75;
                        TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch       = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5)));
                        TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch      = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5)));
                        TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency  /= TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch;
                        TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency /= TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch;

                        /*
                         * Remove the following two lines, then the pitch at which doors play
                         * takes their randomized opening and closing times into account.
                         * */
                        TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch  = 1.0;
                        TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch = 1.0;
                    }
                }
                // add panel section
                if (TrainManager.Trains[k].IsPlayerTrain)
                {
                    TrainProgressCurrentWeight = 0.7 / TrainProgressMaximum;
                    TrainManager.Trains[k].ParsePanelConfig(TrainManager.Trains[k].TrainFolder, CurrentTrainEncoding);
                    TrainProgressCurrentSum += TrainProgressCurrentWeight;
                    System.Threading.Thread.Sleep(1); if (Cancel)
                    {
                        return;
                    }
                    Program.FileSystem.AppendToLogFile("Train panel loaded sucessfully.");
                }
                // add exterior section
                if (TrainManager.Trains[k].State != TrainState.Bogus)
                {
                    bool   LoadObjects         = false;
                    bool[] VisibleFromInterior = new bool[TrainManager.Trains[k].Cars.Length];
                    if (CarObjects == null)
                    {
                        CarObjects     = new UnifiedObject[TrainManager.Trains[k].Cars.Length];
                        BogieObjects   = new UnifiedObject[TrainManager.Trains[k].Cars.Length * 2];
                        CouplerObjects = new UnifiedObject[TrainManager.Trains[k].Cars.Length];
                        LoadObjects    = true;
                    }
                    string tXml = OpenBveApi.Path.CombineFile(TrainManager.Trains[k].TrainFolder, "train.xml");
                    if (System.IO.File.Exists(tXml))
                    {
                        TrainXmlParser.Parse(tXml, TrainManager.Trains[k], ref CarObjects, ref BogieObjects, ref CouplerObjects, ref VisibleFromInterior);
                    }
                    else
                    {
                        ExtensionsCfgParser.ParseExtensionsConfig(TrainManager.Trains[k].TrainFolder, CurrentTrainEncoding, ref CarObjects, ref BogieObjects, ref CouplerObjects, ref VisibleFromInterior, TrainManager.Trains[k], LoadObjects);
                    }
                    TrainManager.PlayerTrain.CameraCar = TrainManager.Trains[k].DriverCar;
                    System.Threading.Thread.Sleep(1); if (Cancel)
                    {
                        return;
                    }
                    //Stores the current array index of the bogie object to add
                    //Required as there are two bogies per car, and we're using a simple linear array....
                    int currentBogieObject = 0;
                    for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++)
                    {
                        if (CarObjects[i] == null)
                        {
                            // load default exterior object
                            string       file = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Compatibility"), "exterior.csv");
                            StaticObject so;
                            Program.CurrentHost.LoadStaticObject(file, System.Text.Encoding.UTF8, false, out so);
                            if (so == null)
                            {
                                CarObjects[i] = null;
                            }
                            else
                            {
                                StaticObject c = (StaticObject)so.Clone();                                 //Clone as otherwise the cached object doesn't scale right
                                c.ApplyScale(TrainManager.Trains[k].Cars[i].Width, TrainManager.Trains[k].Cars[i].Height, TrainManager.Trains[k].Cars[i].Length);
                                CarObjects[i] = c;
                            }
                        }
                        if (CarObjects[i] != null)
                        {
                            // add object
                            TrainManager.Trains[k].Cars[i].LoadCarSections(CarObjects[i], VisibleFromInterior[i]);
                        }

                        if (CouplerObjects[i] != null)
                        {
                            TrainManager.Trains[k].Cars[i].Coupler.LoadCarSections(CouplerObjects[i], VisibleFromInterior[i]);
                        }
                        //Load bogie objects
                        if (BogieObjects[currentBogieObject] != null)
                        {
                            TrainManager.Trains[k].Cars[i].FrontBogie.LoadCarSections(BogieObjects[currentBogieObject], VisibleFromInterior[i]);
                        }
                        currentBogieObject++;
                        if (BogieObjects[currentBogieObject] != null)
                        {
                            TrainManager.Trains[k].Cars[i].RearBogie.LoadCarSections(BogieObjects[currentBogieObject], VisibleFromInterior[i]);
                        }
                        currentBogieObject++;
                    }
                }
                // place cars
                TrainManager.Trains[k].PlaceCars(0.0);

                // configure other properties
                if (TrainManager.Trains[k].IsPlayerTrain)
                {
                    TrainManager.Trains[k].TimetableDelta = 0.0;
                    if (Game.InitialReversedConsist)
                    {
                        TrainManager.Trains[k].Reverse();
                        TrainManager.PlayerTrain.CameraCar         = TrainManager.Trains[k].DriverCar;
                        Program.Renderer.Camera.CurrentRestriction = TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].CameraRestrictionMode;
                    }
                }
                else if (TrainManager.Trains[k].State != TrainState.Bogus)
                {
                    TrainManager.Trains[k].AI                  = new Game.SimpleHumanDriverAI(TrainManager.Trains[k], Interface.CurrentOptions.PrecedingTrainSpeedLimit);
                    TrainManager.Trains[k].TimetableDelta      = Program.CurrentRoute.PrecedingTrainTimeDeltas[k];
                    TrainManager.Trains[k].Specs.DoorOpenMode  = DoorMode.Manual;
                    TrainManager.Trains[k].Specs.DoorCloseMode = DoorMode.Manual;
                }
            }

            /*
             * HACK: Store the TrainManager.Trains reference in the RouteManager also
             *		 Note that this may change when the TrainManager is separated from the lump
             *       Remember not to modify via this ref
             */
            // ReSharper disable once CoVariantArrayConversion
            Program.CurrentRoute.Trains = TrainManager.Trains;
            TrainProgress = 1.0;
            // finished created objects
            System.Threading.Thread.Sleep(1); if (Cancel)
            {
                return;
            }
            Array.Resize(ref ObjectManager.AnimatedWorldObjects, ObjectManager.AnimatedWorldObjectsUsed);
            // update sections
            if (Program.CurrentRoute.Sections.Length > 0)
            {
                Program.CurrentRoute.UpdateAllSections();
            }
            // load plugin
            for (int i = 0; i < TrainManager.Trains.Length; i++)
            {
                if (TrainManager.Trains[i].State != TrainState.Bogus)
                {
                    if (TrainManager.Trains[i].IsPlayerTrain)
                    {
                        if (!TrainManager.Trains[i].LoadCustomPlugin(TrainManager.Trains[i].TrainFolder, CurrentTrainEncoding))
                        {
                            TrainManager.Trains[i].LoadDefaultPlugin(TrainManager.Trains[i].TrainFolder);
                        }
                    }
                    else
                    {
                        TrainManager.Trains[i].LoadDefaultPlugin(TrainManager.Trains[i].TrainFolder);
                    }
                    for (int j = 0; j < InputDevicePlugin.AvailablePluginInfos.Count; j++)
                    {
                        if (InputDevicePlugin.AvailablePluginInfos[j].Status == InputDevicePlugin.PluginInfo.PluginStatus.Enable && InputDevicePlugin.AvailablePlugins[j] is ITrainInputDevice)
                        {
                            ITrainInputDevice trainInputDevice = (ITrainInputDevice)InputDevicePlugin.AvailablePlugins[j];
                            trainInputDevice.SetVehicleSpecs(TrainManager.Trains[i].vehicleSpecs());
                        }
                    }
                }
            }
        }