/// <summary>Creates a mirrored copy of the prototype object (Animated objects)</summary>
 /// <param name="Prototype">The prototype</param>
 /// <returns>The mirrored copy</returns>
 private static UnifiedObject GetMirroredObject(UnifiedObject Prototype)
 {
     if (Prototype is ObjectManager.StaticObject)
     {
         ObjectManager.StaticObject s = (ObjectManager.StaticObject)Prototype;
         return(GetMirroredStaticObject(s));
     }
     if (Prototype is ObjectManager.AnimatedObjectCollection)
     {
         ObjectManager.AnimatedObjectCollection a      = (ObjectManager.AnimatedObjectCollection)Prototype;
         ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection
         {
             Objects = new ObjectManager.AnimatedObject[a.Objects.Length]
         };
         for (int i = 0; i < a.Objects.Length; i++)
         {
             Result.Objects[i] = a.Objects[i].Clone();
             for (int j = 0; j < a.Objects[i].States.Length; j++)
             {
                 Result.Objects[i].States[j].Object = GetMirroredStaticObject(a.Objects[i].States[j].Object);
             }
             Result.Objects[i].TranslateXDirection.X *= -1.0;
             Result.Objects[i].TranslateYDirection.X *= -1.0;
             Result.Objects[i].TranslateZDirection.X *= -1.0;
             Result.Objects[i].RotateXDirection.X    *= -1.0;
             Result.Objects[i].RotateYDirection.X    *= -1.0;
             Result.Objects[i].RotateZDirection.X    *= -1.0;
         }
         return(Result);
     }
     return(null);
 }
Example #2
0
            internal void LoadCarSections(UnifiedObject currentObject)
            {
                int j = CarSections.Length;

                Array.Resize(ref CarSections, j + 1);
                CarSections[j] = new CarSection();
                if (currentObject is ObjectManager.StaticObject)
                {
                    ObjectManager.StaticObject s = (ObjectManager.StaticObject)currentObject;
                    CarSections[j].Elements    = new ObjectManager.AnimatedObject[1];
                    CarSections[j].Elements[0] = new ObjectManager.AnimatedObject
                    {
                        States = new ObjectManager.AnimatedObjectState[1]
                    };
                    CarSections[j].Elements[0].States[0].Position = Vector3.Zero;
                    CarSections[j].Elements[0].States[0].Object   = s;
                    CarSections[j].Elements[0].CurrentState       = 0;
                    CarSections[j].Elements[0].ObjectIndex        = ObjectManager.CreateDynamicObject();
                }
                else if (currentObject is ObjectManager.AnimatedObjectCollection)
                {
                    ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)currentObject;
                    CarSections[j].Elements = new ObjectManager.AnimatedObject[a.Objects.Length];
                    for (int h = 0; h < a.Objects.Length; h++)
                    {
                        CarSections[j].Elements[h]             = a.Objects[h].Clone();
                        CarSections[j].Elements[h].ObjectIndex = ObjectManager.CreateDynamicObject();
                    }
                }
            }
 // parse animated object config
 /// <summary>Loads a collection of animated objects from a file.</summary>
 /// <param name="FileName">The text file to load the animated object from. Must be an absolute file name.</param>
 /// <param name="Encoding">The encoding the file is saved in. If the file uses a byte order mark, the encoding indicated by the byte order mark is used and the Encoding parameter is ignored.</param>
 /// <param name="LoadMode">The texture load mode.</param>
 /// <returns>The collection of animated objects.</returns>
 internal static ObjectManager.AnimatedObjectCollection ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode)
 {
     System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture;
     ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection();
     Result.Objects = new ObjectManager.AnimatedObject[4];
     int ObjectCount = 0;
     // load file
     string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding);
     bool rpnUsed = false;
     for (int i = 0; i < Lines.Length; i++) {
         int j = Lines[i].IndexOf(';');
         if (j >= 0) {
             Lines[i] = Lines[i].Substring(0, j).Trim();
         } else {
             Lines[i] = Lines[i].Trim();
         }
         if (Program.CurrentProgramType == Program.ProgramType.ObjectViewer | Program.CurrentProgramType == Program.ProgramType.RouteViewer) {
             if (Lines[i].IndexOf("functionrpn", StringComparison.OrdinalIgnoreCase) >= 0) {
                 rpnUsed = true;
             }
         }
     }
     if (rpnUsed) {
         Interface.AddMessage(Interface.MessageType.Warning, false, "An animated object file contains non-official RPN functions. Please get rid of them in file " + FileName);
     }
     for (int i = 0; i < Lines.Length; i++) {
         if (Lines[i].Length != 0) {
             switch (Lines[i].ToLowerInvariant()) {
                 case "[include]":
                     {
                         i++;
                         World.Vector3D position = new World.Vector3D(0.0, 0.0, 0.0);
                         ObjectManager.UnifiedObject[] obj = new OpenBve.ObjectManager.UnifiedObject[4];
                         int objCount = 0;
                         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();
                                     string b = Lines[i].Substring(j + 1).TrimStart();
                                     switch (a.ToLowerInvariant()) {
                                         case "position":
                                             {
                                                 string[] s = b.Split(',');
                                                 if (s.Length == 3) {
                                                     double x, y, z;
                                                     if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) {
                                                         Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                     } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) {
                                                         Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                     } else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z)) {
                                                         Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                     } else {
                                                         position = new World.Vector3D(x, y, z);
                                                     }
                                                 } else {
                                                     Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                 }
                                             } break;
                                         default:
                                             Interface.AddMessage(Interface.MessageType.Error, false, "The attribute " + a + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                             break;
                                     }
                                 } else {
                                     string Folder = System.IO.Path.GetDirectoryName(FileName);
                                     if (Interface.ContainsInvalidPathChars(Lines[i])) {
                                         Interface.AddMessage(Interface.MessageType.Error, false, Lines[i] + " contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                     } else {
                                         string file = Interface.GetCombinedFileName(Folder, Lines[i]);
                                         if (System.IO.File.Exists(file)) {
                                             if (obj.Length == objCount) {
                                                 Array.Resize<ObjectManager.UnifiedObject>(ref obj, obj.Length << 1);
                                             }
                                             obj[objCount] = ObjectManager.LoadObject(file, Encoding, LoadMode, false, false, false);
                                             objCount++;
                                         } else {
                                             Interface.AddMessage(Interface.MessageType.Error, true, "File " + file + " not found at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                         }
                                     }
                                 }
                             }
                             i++;
                         }
                         i--;
                         for (int j = 0; j < objCount; j++) {
                             if (obj[j] != null) {
                                 if (obj[j] is ObjectManager.StaticObject) {
                                     ObjectManager.StaticObject s = (ObjectManager.StaticObject)obj[j];
                                     s.Dynamic = 1;
                                     if (ObjectCount >= Result.Objects.Length) {
                                         Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1);
                                     }
                                     ObjectManager.AnimatedObject a = new ObjectManager.AnimatedObject();
                                     ObjectManager.AnimatedObjectState aos = new ObjectManager.AnimatedObjectState();
                                     aos.Object = s;
                                     aos.Position = position;
                                     a.States = new ObjectManager.AnimatedObjectState[] { aos };
                                     Result.Objects[ObjectCount] = a;
                                     ObjectCount++;
                                 } else if (obj[j] is ObjectManager.AnimatedObjectCollection) {
                                     ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)obj[j];
                                     for (int k = 0; k < a.Objects.Length; k++) {
                                         if (ObjectCount >= Result.Objects.Length) {
                                             Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1);
                                         }
                                         for (int h = 0; h < a.Objects[k].States.Length; h++) {
                                             a.Objects[k].States[h].Position.X += position.X;
                                             a.Objects[k].States[h].Position.Y += position.Y;
                                             a.Objects[k].States[h].Position.Z += position.Z;
                                         }
                                         Result.Objects[ObjectCount] = a.Objects[k];
                                         ObjectCount++;
                                     }
                                 }
                             }
                         }
                     }
                     break;
                 case "[object]":
                     {
                         i++;
                         if (Result.Objects.Length == ObjectCount) {
                             Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1);
                         }
                         Result.Objects[ObjectCount] = new ObjectManager.AnimatedObject();
                         Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[] { };
                         Result.Objects[ObjectCount].CurrentState = -1;
                         Result.Objects[ObjectCount].TranslateXDirection = new World.Vector3D(1.0, 0.0, 0.0);
                         Result.Objects[ObjectCount].TranslateYDirection = new World.Vector3D(0.0, 1.0, 0.0);
                         Result.Objects[ObjectCount].TranslateZDirection = new World.Vector3D(0.0, 0.0, 1.0);
                         Result.Objects[ObjectCount].RotateXDirection = new World.Vector3D(1.0, 0.0, 0.0);
                         Result.Objects[ObjectCount].RotateYDirection = new World.Vector3D(0.0, 1.0, 0.0);
                         Result.Objects[ObjectCount].RotateZDirection = new World.Vector3D(0.0, 0.0, 1.0);
                         Result.Objects[ObjectCount].TextureShiftXDirection = new World.Vector2D(1.0, 0.0);
                         Result.Objects[ObjectCount].TextureShiftYDirection = new World.Vector2D(0.0, 1.0);
                         Result.Objects[ObjectCount].RefreshRate = 0.0;
                         Result.Objects[ObjectCount].ObjectIndex = -1;
                         World.Vector3D Position = new World.Vector3D(0.0, 0.0, 0.0);
                         string[] StateFiles = null;
                         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();
                                     string b = Lines[i].Substring(j + 1).TrimStart();
                                     switch (a.ToLowerInvariant()) {
                                         case "position":
                                             {
                                                 string[] s = b.Split(',');
                                                 if (s.Length == 3) {
                                                     double x, y, z;
                                                     if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) {
                                                         Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                     } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) {
                                                         Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                     } else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z)) {
                                                         Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                     } else {
                                                         Position = new World.Vector3D(x, y, z);
                                                     }
                                                 } else {
                                                     Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                 }
                                             } break;
                                         case "states":
                                             {
                                                 string[] s = b.Split(',');
                                                 if (s.Length >= 1) {
                                                     string Folder = System.IO.Path.GetDirectoryName(FileName);
                                                     StateFiles = new string[s.Length];
                                                     for (int k = 0; k < s.Length; k++) {
                                                         s[k] = s[k].Trim();
                                                         if (Interface.ContainsInvalidPathChars(s[k])) {
                                                             Interface.AddMessage(Interface.MessageType.Error, false, "File" + k.ToString(Culture) + " contains illegal characters in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                             StateFiles[k] = null;
                                                         } else {
                                                             StateFiles[k] = Interface.GetCombinedFileName(Folder, s[k]);
                                                             if (!System.IO.File.Exists(StateFiles[k])) {
                                                                 Interface.AddMessage(Interface.MessageType.Error, true, "File " + StateFiles[k] + " not found in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                                 StateFiles[k] = null;
                                                             }
                                                         }
                                                     }
                                                 } else {
                                                     Interface.AddMessage(Interface.MessageType.Error, false, "At least one argument is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                     return null;
                                                 }
                                             } break;
                                         case "statefunction":
                                             try {
                                                 Result.Objects[ObjectCount].StateFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
                                             } catch (Exception ex) {
                                                 Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                             } break;
                                         case "statefunctionrpn":
                                             try {
                                                 Result.Objects[ObjectCount].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
                                             } catch (Exception ex) {
                                                 Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                             } break;
                                         case "translatexdirection":
                                         case "translateydirection":
                                         case "translatezdirection":
                                             {
                                                 string[] s = b.Split(',');
                                                 if (s.Length == 3) {
                                                     double x, y, z;
                                                     if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) {
                                                         Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                     } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) {
                                                         Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                     } else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z)) {
                                                         Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                     } else {
                                                         switch (a.ToLowerInvariant()) {
                                                             case "translatexdirection":
                                                                 Result.Objects[ObjectCount].TranslateXDirection = new World.Vector3D(x, y, z);
                                                                 break;
                                                             case "translateydirection":
                                                                 Result.Objects[ObjectCount].TranslateYDirection = new World.Vector3D(x, y, z);
                                                                 break;
                                                             case "translatezdirection":
                                                                 Result.Objects[ObjectCount].TranslateZDirection = new World.Vector3D(x, y, z);
                                                                 break;
                                                         }
                                                     }
                                                 } else {
                                                     Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                 }
                                             } break;
                                         case "translatexfunction":
                                             try {
                                                 Result.Objects[ObjectCount].TranslateXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
                                             } catch (Exception ex) {
                                                 Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                             } break;
                                         case "translateyfunction":
                                             try {
                                                 Result.Objects[ObjectCount].TranslateYFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
                                             } catch (Exception ex) {
                                                 Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                             } break;
                                         case "translatezfunction":
                                             try {
                                                 Result.Objects[ObjectCount].TranslateZFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
                                             } catch (Exception ex) {
                                                 Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                             } break;
                                         case "translatexfunctionrpn":
                                             try {
                                                 Result.Objects[ObjectCount].TranslateXFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
                                             } catch (Exception ex) {
                                                 Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                             } break;
                                         case "translateyfunctionrpn":
                                             try {
                                                 Result.Objects[ObjectCount].TranslateYFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
                                             } catch (Exception ex) {
                                                 Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                             } break;
                                         case "translatezfunctionrpn":
                                             try {
                                                 Result.Objects[ObjectCount].TranslateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
                                             } catch (Exception ex) {
                                                 Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                             } break;
                                         case "rotatexdirection":
                                         case "rotateydirection":
                                         case "rotatezdirection":
                                             {
                                                 string[] s = b.Split(',');
                                                 if (s.Length == 3) {
                                                     double x, y, z;
                                                     if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) {
                                                         Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                     } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) {
                                                         Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                     } else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z)) {
                                                         Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                     } else if (x == 0.0 & y == 0.0 & z == 0.0) {
                                                         Interface.AddMessage(Interface.MessageType.Error, false, "The direction indicated by X, Y and Z is expected to be non-zero in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                     } else {
                                                         switch (a.ToLowerInvariant()) {
                                                             case "rotatexdirection":
                                                                 Result.Objects[ObjectCount].RotateXDirection = new World.Vector3D(x, y, z);
                                                                 break;
                                                             case "rotateydirection":
                                                                 Result.Objects[ObjectCount].RotateYDirection = new World.Vector3D(x, y, z);
                                                                 break;
                                                             case "rotatezdirection":
                                                                 Result.Objects[ObjectCount].RotateZDirection = new World.Vector3D(x, y, z);
                                                                 break;
                                                         }
                                                     }
                                                 } else {
                                                     Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                 }
                                             } break;
                                         case "rotatexfunction":
                                             try {
                                                 Result.Objects[ObjectCount].RotateXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
                                             } catch (Exception ex) {
                                                 Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                             } break;
                                         case "rotateyfunction":
                                             try {
                                                 Result.Objects[ObjectCount].RotateYFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
                                             } catch (Exception ex) {
                                                 Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                             } break;
                                         case "rotatezfunction":
                                             try {
                                                 Result.Objects[ObjectCount].RotateZFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
                                             } catch (Exception ex) {
                                                 Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                             } break;
                                         case "rotatexfunctionrpn":
                                             try {
                                                 Result.Objects[ObjectCount].RotateXFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
                                             } catch (Exception ex) {
                                                 Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                             } break;
                                         case "rotateyfunctionrpn":
                                             try {
                                                 Result.Objects[ObjectCount].RotateYFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
                                             } catch (Exception ex) {
                                                 Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                             } break;
                                         case "rotatezfunctionrpn":
                                             try {
                                                 Result.Objects[ObjectCount].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
                                             } catch (Exception ex) {
                                                 Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                             } break;
                                         case "rotatexdamping":
                                         case "rotateydamping":
                                         case "rotatezdamping":
                                             {
                                                 string[] s = b.Split(',');
                                                 if (s.Length == 2) {
                                                     double nf, dr;
                                                     if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out nf)) {
                                                         Interface.AddMessage(Interface.MessageType.Error, false, "NaturalFrequency is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                     } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out dr)) {
                                                         Interface.AddMessage(Interface.MessageType.Error, false, "DampingRatio is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                     } else if (nf <= 0.0) {
                                                         Interface.AddMessage(Interface.MessageType.Error, false, "NaturalFrequency is expected to be positive in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                     } else if (dr <= 0.0) {
                                                         Interface.AddMessage(Interface.MessageType.Error, false, "DampingRatio is expected to be positive in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                     } else {
                                                         switch (a.ToLowerInvariant()) {
                                                             case "rotatexdamping":
                                                                 Result.Objects[ObjectCount].RotateXDamping = new ObjectManager.Damping(nf, dr);
                                                                 break;
                                                             case "rotateydamping":
                                                                 Result.Objects[ObjectCount].RotateYDamping = new ObjectManager.Damping(nf, dr);
                                                                 break;
                                                             case "rotatezdamping":
                                                                 Result.Objects[ObjectCount].RotateZDamping = new ObjectManager.Damping(nf, dr);
                                                                 break;
                                                         }
                                                     }
                                                 } else {
                                                     Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 2 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                 }
                                             } break;
                                         case "textureshiftxdirection":
                                         case "textureshiftydirection":
                                             {
                                                 string[] s = b.Split(',');
                                                 if (s.Length == 2) {
                                                     double x, y;
                                                     if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) {
                                                         Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                     } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) {
                                                         Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                     } else {
                                                         switch (a.ToLowerInvariant()) {
                                                             case "textureshiftxdirection":
                                                                 Result.Objects[ObjectCount].TextureShiftXDirection = new World.Vector2D(x, y);
                                                                 break;
                                                             case "textureshiftydirection":
                                                                 Result.Objects[ObjectCount].TextureShiftYDirection = new World.Vector2D(x, y);
                                                                 break;
                                                         }
                                                     }
                                                 } else {
                                                     Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 2 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                 }
                                             } break;
                                         case "textureshiftxfunction":
                                             try {
                                                 Result.Objects[ObjectCount].TextureShiftXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
                                             } catch (Exception ex) {
                                                 Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                             } break;
                                         case "textureshiftyfunction":
                                             try {
                                                 Result.Objects[ObjectCount].TextureShiftYFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
                                             } catch (Exception ex) {
                                                 Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                             } break;
                                         case "textureshiftxfunctionrpn":
                                             try {
                                                 Result.Objects[ObjectCount].TextureShiftXFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
                                             } catch (Exception ex) {
                                                 Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                             } break;
                                         case "textureshiftyfunctionrpn":
                                             try {
                                                 Result.Objects[ObjectCount].TextureShiftYFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
                                             } catch (Exception ex) {
                                                 Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                             } break;
                                         case "refreshrate":
                                             {
                                                 double r;
                                                 if (!double.TryParse(b, System.Globalization.NumberStyles.Float, Culture, out r)) {
                                                     Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                 } else if (r < 0.0) {
                                                     Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be non-negative in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                 } else {
                                                     Result.Objects[ObjectCount].RefreshRate = r;
                                                 }
                                             } break;
                                         default:
                                             Interface.AddMessage(Interface.MessageType.Error, false, "The attribute " + a + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                             break;
                                     }
                                 } else {
                                     Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                     return null;
                                 }
                             }
                             i++;
                         }
                         i--;
                         if (StateFiles != null) {
                             Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[StateFiles.Length];
                             bool ForceTextureRepeatX = Result.Objects[ObjectCount].TextureShiftXFunction != null & Result.Objects[ObjectCount].TextureShiftXDirection.X != 0.0 |
                                 Result.Objects[ObjectCount].TextureShiftYFunction != null & Result.Objects[ObjectCount].TextureShiftYDirection.Y != 0.0;
                             bool ForceTextureRepeatY = Result.Objects[ObjectCount].TextureShiftXFunction != null & Result.Objects[ObjectCount].TextureShiftXDirection.X != 0.0 |
                                 Result.Objects[ObjectCount].TextureShiftYFunction != null & Result.Objects[ObjectCount].TextureShiftYDirection.Y != 0.0;
                             for (int k = 0; k < StateFiles.Length; k++) {
                                 Result.Objects[ObjectCount].States[k].Position = new World.Vector3D(0.0, 0.0, 0.0);
                                 if (StateFiles[k] != null) {
                                     Result.Objects[ObjectCount].States[k].Object = ObjectManager.LoadStaticObject(StateFiles[k], Encoding, LoadMode, false, ForceTextureRepeatX, ForceTextureRepeatY);
                                     Result.Objects[ObjectCount].States[k].Object.Dynamic = 1;
                                 } else {
                                     Result.Objects[ObjectCount].States[k].Object = null;
                                 }
                                 for (int j = 0; j < Result.Objects[ObjectCount].States.Length; j++) {
                                     Result.Objects[ObjectCount].States[j].Position = Position;
                                 }
                             }
                         } else {
                             Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[] { };
                         }
                         ObjectCount++;
                     }
                     break;
                 default:
                     Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                     return null;
             }
         }
     }
     Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, ObjectCount);
     return Result;
 }
Example #4
0
        internal static ObjectManager.AnimatedObjectCollection ReadObject(string fileName)
        {
            MsTsShape shape = new MsTsShape();

            ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection
            {
                Objects = new ObjectManager.AnimatedObject[4]
            };

            currentFolder = Path.GetDirectoryName(fileName);
            Stream fb = new FileStream(fileName, FileMode.Open, FileAccess.Read);

            byte[] buffer = new byte[34];
            fb.Read(buffer, 0, 2);

            bool unicode = (buffer[0] == 0xFF && buffer[1] == 0xFE);

            string headerString;

            if (unicode)
            {
                fb.Read(buffer, 0, 32);
                headerString = Encoding.Unicode.GetString(buffer, 0, 16);
            }
            else
            {
                fb.Read(buffer, 2, 14);
                headerString = Encoding.ASCII.GetString(buffer, 0, 8);
            }

            // SIMISA@F  means compressed
            // SIMISA@@  means uncompressed
            if (headerString.StartsWith("SIMISA@F"))
            {
                fb = new ZlibStream(fb, CompressionMode.Decompress);
            }
            else if (headerString.StartsWith("\r\nSIMISA"))
            {
                // ie us1rd2l1000r10d.s, we are going to allow this but warn
                Console.Error.WriteLine("Improper header in " + fileName);
                fb.Read(buffer, 0, 4);
            }
            else if (!headerString.StartsWith("SIMISA@@"))
            {
                throw new Exception("Unrecognized shape file header " + headerString + " in " + fileName);
            }

            string subHeader;

            if (unicode)
            {
                fb.Read(buffer, 0, 32);
                subHeader = Encoding.Unicode.GetString(buffer, 0, 16);
            }
            else
            {
                fb.Read(buffer, 0, 16);
                subHeader = Encoding.ASCII.GetString(buffer, 0, 8);
            }
            if (subHeader[7] == 't')
            {
                using (BinaryReader reader = new BinaryReader(fb))
                {
                    byte[] newBytes = reader.ReadBytes((int)(fb.Length - fb.Position));
                    string s;
                    if (unicode)
                    {
                        s = Encoding.Unicode.GetString(newBytes);
                    }
                    else
                    {
                        s = Encoding.ASCII.GetString(newBytes);
                    }

                    s = s.Replace("\r\n", " ").Replace("\n", " ").Replace("\r", " ").Replace("\t", " ").Trim();
                    if (!s.StartsWith("shape", StringComparison.InvariantCultureIgnoreCase))
                    {
                        throw new Exception();                         //Shape definition
                    }
                    TextualBlock block = new TextualBlock(s, KujuTokenID.shape);
                    ParseBlock(block, ref shape);
                }
            }
            else if (subHeader[7] != 'b')
            {
                throw new Exception("Unrecognized subHeader \"" + subHeader + "\" in " + fileName);
            }
            else
            {
                using (BinaryReader reader = new BinaryReader(fb))
                {
                    KujuTokenID currentToken = (KujuTokenID)reader.ReadUInt16();
                    if (currentToken != KujuTokenID.shape)
                    {
                        throw new Exception();                         //Shape definition
                    }
                    reader.ReadUInt16();
                    uint        remainingBytes = reader.ReadUInt32();
                    byte[]      newBytes       = reader.ReadBytes((int)remainingBytes);
                    BinaryBlock block          = new BinaryBlock(newBytes, KujuTokenID.shape);
                    ParseBlock(block, ref shape);
                }
            }
            Array.Resize(ref Result.Objects, shape.totalObjects);
            int idx = 0;

            double[] previousLODs = new double[shape.totalObjects];
            for (int i = 0; i < shape.LODs.Count; i++)
            {
                for (int j = 0; j < shape.LODs[i].subObjects.Count; j++)
                {
                    Result.Objects[idx]        = new ObjectManager.AnimatedObject();
                    Result.Objects[idx].States = new AnimatedObjectState[1];
                    AnimatedObjectState aos = new AnimatedObjectState(null, Vector3.Zero);
                    shape.LODs[i].subObjects[j].Apply(out aos.Object);
                    aos.Position = new Vector3(0, 0, 0);
                    Result.Objects[idx].States[0] = aos;
                    previousLODs[idx]             = shape.LODs[i].viewingDistance;
                    int k = idx;
                    while (k > 0)
                    {
                        if (previousLODs[k] < shape.LODs[i].viewingDistance)
                        {
                            break;
                        }

                        k--;
                    }

                    if (k != 0)
                    {
                        Result.Objects[idx].StateFunction = new FunctionScript(Program.CurrentHost, "if[cameraDistance <" + shape.LODs[i].viewingDistance + ",if[cameraDistance >" + previousLODs[k] + ",0,-1],-1]", true);
                    }
                    else
                    {
                        Result.Objects[idx].StateFunction = new FunctionScript(Program.CurrentHost, "if[cameraDistance <" + shape.LODs[i].viewingDistance + ",0,-1]", true);
                    }

                    idx++;
                }
            }
            return(Result);
        }
Example #5
0
 // apply route data
 private static void ApplyRouteData(string FileName, System.Text.Encoding Encoding, ref RouteData Data, bool PreviewOnly)
 {
     string SignalPath, LimitPath, LimitGraphicsPath, TransponderPath;
     ObjectManager.StaticObject SignalPost, LimitPostStraight, LimitPostLeft, LimitPostRight, LimitPostInfinite;
     ObjectManager.StaticObject LimitOneDigit, LimitTwoDigits, LimitThreeDigits, StopPost;
     ObjectManager.StaticObject TransponderS, TransponderSN, TransponderFalseStart, TransponderPOrigin, TransponderPStop;
     if (!PreviewOnly) {
         string CompatibilityFolder = Program.FileSystem.GetDataFolder("Compatibility");
         // load compatibility objects
         SignalPath = OpenBveApi.Path.CombineDirectory(CompatibilityFolder, "Signals");
         SignalPost = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalPath, "signal_post.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false);
         LimitPath = OpenBveApi.Path.CombineDirectory(CompatibilityFolder, "Limits");
         LimitGraphicsPath = OpenBveApi.Path.CombineDirectory(LimitPath, "Graphics");
         LimitPostStraight = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_straight.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false);
         LimitPostLeft = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_left.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false);
         LimitPostRight = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_right.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false);
         LimitPostInfinite = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_infinite.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false);
         LimitOneDigit = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_1_digit.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false);
         LimitTwoDigits = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_2_digits.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false);
         LimitThreeDigits = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_3_digits.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false);
         StopPost = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(CompatibilityFolder, "stop.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false);
         TransponderPath = OpenBveApi.Path.CombineDirectory(CompatibilityFolder, "Transponders");
         TransponderS = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(TransponderPath, "s.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false);
         TransponderSN = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(TransponderPath, "sn.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false);
         TransponderFalseStart = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(TransponderPath, "falsestart.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false);
         TransponderPOrigin = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(TransponderPath, "porigin.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false);
         TransponderPStop = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(TransponderPath, "pstop.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false);
     } else {
         SignalPath = null;
         LimitPath = null;
         LimitGraphicsPath = null;
         TransponderPath = null;
         SignalPost = null;
         LimitPostStraight = null;
         LimitPostLeft = null;
         LimitPostRight = null;
         LimitPostInfinite = null;
         LimitOneDigit = null;
         LimitTwoDigits = null;
         LimitThreeDigits = null;
         StopPost = null;
         TransponderS = null;
         TransponderSN = null;
         TransponderFalseStart = null;
         TransponderPOrigin = null;
         TransponderPStop = null;
     }
     // initialize
     System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture;
     int LastBlock = (int)Math.Floor((Data.TrackPosition + 600.0) / Data.BlockInterval + 0.001) + 1;
     int BlocksUsed = Data.Blocks.Length;
     CreateMissingBlocks(ref Data, ref BlocksUsed, LastBlock, PreviewOnly);
     Array.Resize<Block>(ref Data.Blocks, BlocksUsed);
     // interpolate height
     if (!PreviewOnly) {
         int z = 0;
         for (int i = 0; i < Data.Blocks.Length; i++) {
             if (!double.IsNaN(Data.Blocks[i].Height)) {
                 for (int j = i - 1; j >= 0; j--) {
                     if (!double.IsNaN(Data.Blocks[j].Height)) {
                         double a = Data.Blocks[j].Height;
                         double b = Data.Blocks[i].Height;
                         double d = (b - a) / (double)(i - j);
                         for (int k = j + 1; k < i; k++) {
                             a += d;
                             Data.Blocks[k].Height = a;
                         }
                         break;
                     }
                 }
                 z = i;
             }
         }
         for (int i = z + 1; i < Data.Blocks.Length; i++) {
             Data.Blocks[i].Height = Data.Blocks[z].Height;
         }
     }
     // background
     if (!PreviewOnly) {
         if (Data.Blocks[0].Background >= 0 & Data.Blocks[0].Background < Data.Backgrounds.Length) {
             World.CurrentBackground = Data.Backgrounds[Data.Blocks[0].Background];
         } else {
             World.CurrentBackground = new World.Background(null, 6, false);
         }
         World.TargetBackground = World.CurrentBackground;
     }
     // brightness
     int CurrentBrightnessElement = -1;
     int CurrentBrightnessEvent = -1;
     float CurrentBrightnessValue = 1.0f;
     double CurrentBrightnessTrackPosition = (double)Data.FirstUsedBlock * Data.BlockInterval;
     if (!PreviewOnly) {
         for (int i = Data.FirstUsedBlock; i < Data.Blocks.Length; i++) {
             if (Data.Blocks[i].Brightness != null && Data.Blocks[i].Brightness.Length != 0) {
                 CurrentBrightnessValue = Data.Blocks[i].Brightness[0].Value;
                 CurrentBrightnessTrackPosition = Data.Blocks[i].Brightness[0].Value;
                 break;
             }
         }
     }
     // create objects and track
     Vector3 Position = new Vector3(0.0, 0.0, 0.0);
     World.Vector2D Direction = new World.Vector2D(0.0, 1.0);
     TrackManager.CurrentTrack = new TrackManager.Track();
     TrackManager.CurrentTrack.Elements = new TrackManager.TrackElement[] { };
     double CurrentSpeedLimit = double.PositiveInfinity;
     int CurrentRunIndex = 0;
     int CurrentFlangeIndex = 0;
     if (Data.FirstUsedBlock < 0) Data.FirstUsedBlock = 0;
     TrackManager.CurrentTrack.Elements = new TrackManager.TrackElement[256];
     int CurrentTrackLength = 0;
     int PreviousFogElement = -1;
     int PreviousFogEvent = -1;
     Game.Fog PreviousFog = new Game.Fog(Game.NoFogStart, Game.NoFogEnd, new Color24(128, 128, 128), -Data.BlockInterval);
     Game.Fog CurrentFog = new Game.Fog(Game.NoFogStart, Game.NoFogEnd, new Color24(128, 128, 128), 0.0);
     // process blocks
     double progressFactor = Data.Blocks.Length - Data.FirstUsedBlock == 0 ? 0.5 : 0.5 / (double)(Data.Blocks.Length - Data.FirstUsedBlock);
     for (int i = Data.FirstUsedBlock; i < Data.Blocks.Length; i++) {
         Loading.RouteProgress = 0.6667 + (double)(i - Data.FirstUsedBlock) * progressFactor;
         if ((i & 15) == 0) {
             System.Threading.Thread.Sleep(1);
             if (Loading.Cancel) return;
         }
         double StartingDistance = (double)i * Data.BlockInterval;
         double EndingDistance = StartingDistance + Data.BlockInterval;
         // normalize
         World.Normalize(ref Direction.X, ref Direction.Y);
         // track
         if (!PreviewOnly) {
             if (Data.Blocks[i].Cycle.Length == 1 && Data.Blocks[i].Cycle[0] == -1) {
                 if (Data.Structure.Cycle.Length == 0 || Data.Structure.Cycle[0] == null) {
                     Data.Blocks[i].Cycle = new int[] { 0 };
                 } else {
                     Data.Blocks[i].Cycle = Data.Structure.Cycle[0];
                 }
             }
         }
         TrackManager.TrackElement WorldTrackElement = Data.Blocks[i].CurrentTrackState;
         int n = CurrentTrackLength;
         if (n >= TrackManager.CurrentTrack.Elements.Length) {
             Array.Resize<TrackManager.TrackElement>(ref TrackManager.CurrentTrack.Elements, TrackManager.CurrentTrack.Elements.Length << 1);
         }
         CurrentTrackLength++;
         TrackManager.CurrentTrack.Elements[n] = WorldTrackElement;
         TrackManager.CurrentTrack.Elements[n].WorldPosition = Position;
         TrackManager.CurrentTrack.Elements[n].WorldDirection = World.GetVector3(Direction, Data.Blocks[i].Pitch);
         TrackManager.CurrentTrack.Elements[n].WorldSide = new Vector3(Direction.Y, 0.0, -Direction.X);
         World.Cross(TrackManager.CurrentTrack.Elements[n].WorldDirection.X, TrackManager.CurrentTrack.Elements[n].WorldDirection.Y, TrackManager.CurrentTrack.Elements[n].WorldDirection.Z, TrackManager.CurrentTrack.Elements[n].WorldSide.X, TrackManager.CurrentTrack.Elements[n].WorldSide.Y, TrackManager.CurrentTrack.Elements[n].WorldSide.Z, out TrackManager.CurrentTrack.Elements[n].WorldUp.X, out TrackManager.CurrentTrack.Elements[n].WorldUp.Y, out TrackManager.CurrentTrack.Elements[n].WorldUp.Z);
         TrackManager.CurrentTrack.Elements[n].StartingTrackPosition = StartingDistance;
         TrackManager.CurrentTrack.Elements[n].Events = new TrackManager.GeneralEvent[] { };
         TrackManager.CurrentTrack.Elements[n].AdhesionMultiplier = Data.Blocks[i].AdhesionMultiplier;
         TrackManager.CurrentTrack.Elements[n].CsvRwAccuracyLevel = Data.Blocks[i].Accuracy;
         // background
         if (!PreviewOnly) {
             if (Data.Blocks[i].Background >= 0) {
                 int typ;
                 if (i == Data.FirstUsedBlock) {
                     typ = Data.Blocks[i].Background;
                 } else {
                     typ = Data.Backgrounds.Length > 0 ? 0 : -1;
                     for (int j = i - 1; j >= Data.FirstUsedBlock; j--) {
                         if (Data.Blocks[j].Background >= 0) {
                             typ = Data.Blocks[j].Background;
                             break;
                         }
                     }
                 }
                 if (typ >= 0 & typ < Data.Backgrounds.Length) {
                     int m = TrackManager.CurrentTrack.Elements[n].Events.Length;
                     Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1);
                     TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.BackgroundChangeEvent(0.0, Data.Backgrounds[typ], Data.Backgrounds[Data.Blocks[i].Background]);
                 }
             }
         }
         // brightness
         if (!PreviewOnly) {
             for (int j = 0; j < Data.Blocks[i].Brightness.Length; j++) {
                 int m = TrackManager.CurrentTrack.Elements[n].Events.Length;
                 Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1);
                 double d = Data.Blocks[i].Brightness[j].TrackPosition - StartingDistance;
                 TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.BrightnessChangeEvent(d, Data.Blocks[i].Brightness[j].Value, CurrentBrightnessValue, Data.Blocks[i].Brightness[j].TrackPosition - CurrentBrightnessTrackPosition, Data.Blocks[i].Brightness[j].Value, 0.0);
                 if (CurrentBrightnessElement >= 0 & CurrentBrightnessEvent >= 0) {
                     TrackManager.BrightnessChangeEvent bce = (TrackManager.BrightnessChangeEvent)TrackManager.CurrentTrack.Elements[CurrentBrightnessElement].Events[CurrentBrightnessEvent];
                     bce.NextBrightness = Data.Blocks[i].Brightness[j].Value;
                     bce.NextDistance = Data.Blocks[i].Brightness[j].TrackPosition - CurrentBrightnessTrackPosition;
                 }
                 CurrentBrightnessElement = n;
                 CurrentBrightnessEvent = m;
                 CurrentBrightnessValue = Data.Blocks[i].Brightness[j].Value;
                 CurrentBrightnessTrackPosition = Data.Blocks[i].Brightness[j].TrackPosition;
             }
         }
         // fog
         if (!PreviewOnly) {
             if (Data.FogTransitionMode) {
                 if (Data.Blocks[i].FogDefined) {
                     Data.Blocks[i].Fog.TrackPosition = StartingDistance;
                     int m = TrackManager.CurrentTrack.Elements[n].Events.Length;
                     Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1);
                     TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.FogChangeEvent(0.0, PreviousFog, Data.Blocks[i].Fog, Data.Blocks[i].Fog);
                     if (PreviousFogElement >= 0 & PreviousFogEvent >= 0) {
                         TrackManager.FogChangeEvent e = (TrackManager.FogChangeEvent)TrackManager.CurrentTrack.Elements[PreviousFogElement].Events[PreviousFogEvent];
                         e.NextFog = Data.Blocks[i].Fog;
                     } else {
                         Game.PreviousFog = PreviousFog;
                         Game.CurrentFog = PreviousFog;
                         Game.NextFog = Data.Blocks[i].Fog;
                     }
                     PreviousFog = Data.Blocks[i].Fog;
                     PreviousFogElement = n;
                     PreviousFogEvent = m;
                 }
             } else {
                 Data.Blocks[i].Fog.TrackPosition = StartingDistance + Data.BlockInterval;
                 int m = TrackManager.CurrentTrack.Elements[n].Events.Length;
                 Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1);
                 TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.FogChangeEvent(0.0, PreviousFog, CurrentFog, Data.Blocks[i].Fog);
                 PreviousFog = CurrentFog;
                 CurrentFog = Data.Blocks[i].Fog;
             }
         }
         // rail sounds
         if (!PreviewOnly) {
             int j = Data.Blocks[i].RailType[0];
             int r = j < Data.Structure.Run.Length ? Data.Structure.Run[j] : 0;
             int f = j < Data.Structure.Flange.Length ? Data.Structure.Flange[j] : 0;
             int m = TrackManager.CurrentTrack.Elements[n].Events.Length;
             Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1);
             TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.RailSoundsChangeEvent(0.0, CurrentRunIndex, CurrentFlangeIndex, r, f);
             CurrentRunIndex = r;
             CurrentFlangeIndex = f;
         }
         // point sound
         if (!PreviewOnly) {
             if (i < Data.Blocks.Length - 1) {
                 bool q = false;
                 for (int j = 0; j < Data.Blocks[i].Rail.Length; j++) {
                     if (Data.Blocks[i].Rail[j].RailStart & Data.Blocks[i + 1].Rail.Length > j) {
                         bool qx = Math.Sign(Data.Blocks[i].Rail[j].RailStartX) != Math.Sign(Data.Blocks[i + 1].Rail[j].RailEndX);
                         bool qy = Data.Blocks[i].Rail[j].RailStartY * Data.Blocks[i + 1].Rail[j].RailEndY <= 0.0;
                         if (qx & qy) {
                             q = true;
                             break;
                         }
                     }
                 }
                 if (q) {
                     int m = TrackManager.CurrentTrack.Elements[n].Events.Length;
                     Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1);
                     TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.SoundEvent(0.0, null, false, false, true, new Vector3(0.0, 0.0, 0.0), 12.5);
                 }
             }
         }
         // station
         if (Data.Blocks[i].Station >= 0) {
             // station
             int s = Data.Blocks[i].Station;
             int m = TrackManager.CurrentTrack.Elements[n].Events.Length;
             Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1);
             TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.StationStartEvent(0.0, s);
             double dx, dy = 3.0;
             if (Game.Stations[s].OpenLeftDoors & !Game.Stations[s].OpenRightDoors) {
                 dx = -5.0;
             } else if (!Game.Stations[s].OpenLeftDoors & Game.Stations[s].OpenRightDoors) {
                 dx = 5.0;
             } else {
                 dx = 0.0;
             }
             Game.Stations[s].SoundOrigin.X = Position.X + dx * TrackManager.CurrentTrack.Elements[n].WorldSide.X + dy * TrackManager.CurrentTrack.Elements[n].WorldUp.X;
             Game.Stations[s].SoundOrigin.Y = Position.Y + dx * TrackManager.CurrentTrack.Elements[n].WorldSide.Y + dy * TrackManager.CurrentTrack.Elements[n].WorldUp.Y;
             Game.Stations[s].SoundOrigin.Z = Position.Z + dx * TrackManager.CurrentTrack.Elements[n].WorldSide.Z + dy * TrackManager.CurrentTrack.Elements[n].WorldUp.Z;
             // passalarm
             if (!PreviewOnly) {
                 if (Data.Blocks[i].StationPassAlarm) {
                     int b = i - 6;
                     if (b >= 0) {
                         int j = b - Data.FirstUsedBlock;
                         if (j >= 0) {
                             m = TrackManager.CurrentTrack.Elements[j].Events.Length;
                             Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[j].Events, m + 1);
                             TrackManager.CurrentTrack.Elements[j].Events[m] = new TrackManager.StationPassAlarmEvent(0.0);
                         }
                     }
                 }
             }
         }
         // stop
         for (int j = 0; j < Data.Blocks[i].Stop.Length; j++) {
             int s = Data.Blocks[i].Stop[j].Station;
             int t = Game.Stations[s].Stops.Length;
             Array.Resize<Game.StationStop>(ref Game.Stations[s].Stops, t + 1);
             Game.Stations[s].Stops[t].TrackPosition = Data.Blocks[i].Stop[j].TrackPosition;
             Game.Stations[s].Stops[t].ForwardTolerance = Data.Blocks[i].Stop[j].ForwardTolerance;
             Game.Stations[s].Stops[t].BackwardTolerance = Data.Blocks[i].Stop[j].BackwardTolerance;
             Game.Stations[s].Stops[t].Cars = Data.Blocks[i].Stop[j].Cars;
             double dx, dy = 2.0;
             if (Game.Stations[s].OpenLeftDoors & !Game.Stations[s].OpenRightDoors) {
                 dx = -5.0;
             } else if (!Game.Stations[s].OpenLeftDoors & Game.Stations[s].OpenRightDoors) {
                 dx = 5.0;
             } else {
                 dx = 0.0;
             }
             Game.Stations[s].SoundOrigin.X = Position.X + dx * TrackManager.CurrentTrack.Elements[n].WorldSide.X + dy * TrackManager.CurrentTrack.Elements[n].WorldUp.X;
             Game.Stations[s].SoundOrigin.Y = Position.Y + dx * TrackManager.CurrentTrack.Elements[n].WorldSide.Y + dy * TrackManager.CurrentTrack.Elements[n].WorldUp.Y;
             Game.Stations[s].SoundOrigin.Z = Position.Z + dx * TrackManager.CurrentTrack.Elements[n].WorldSide.Z + dy * TrackManager.CurrentTrack.Elements[n].WorldUp.Z;
         }
         // limit
         for (int j = 0; j < Data.Blocks[i].Limit.Length; j++) {
             int m = TrackManager.CurrentTrack.Elements[n].Events.Length;
             Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1);
             double d = Data.Blocks[i].Limit[j].TrackPosition - StartingDistance;
             TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.LimitChangeEvent(d, CurrentSpeedLimit, Data.Blocks[i].Limit[j].Speed);
             CurrentSpeedLimit = Data.Blocks[i].Limit[j].Speed;
         }
         // marker
         if (!PreviewOnly) {
             for (int j = 0; j < Data.Markers.Length; j++) {
                 if (Data.Markers[j].StartingPosition >= StartingDistance & Data.Markers[j].StartingPosition < EndingDistance) {
                     int m = TrackManager.CurrentTrack.Elements[n].Events.Length;
                     Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1);
                     double d = Data.Markers[j].StartingPosition - StartingDistance;
                     TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.MarkerStartEvent(d, Data.Markers[j].Texture);
                 }
                 if (Data.Markers[j].EndingPosition >= StartingDistance & Data.Markers[j].EndingPosition < EndingDistance) {
                     int m = TrackManager.CurrentTrack.Elements[n].Events.Length;
                     Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1);
                     double d = Data.Markers[j].EndingPosition - StartingDistance;
                     TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.MarkerEndEvent(d, Data.Markers[j].Texture);
                 }
             }
         }
         // sound
         if (!PreviewOnly) {
             for (int j = 0; j < Data.Blocks[i].Sound.Length; j++) {
                 if (Data.Blocks[i].Sound[j].Type == SoundType.TrainStatic | Data.Blocks[i].Sound[j].Type == SoundType.TrainDynamic) {
                     int m = TrackManager.CurrentTrack.Elements[n].Events.Length;
                     Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1);
                     double d = Data.Blocks[i].Sound[j].TrackPosition - StartingDistance;
                     switch (Data.Blocks[i].Sound[j].Type) {
                         case SoundType.TrainStatic:
                             TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.SoundEvent(d, Data.Blocks[i].Sound[j].SoundBuffer, true, true, false, new Vector3(0.0, 0.0, 0.0), 0.0);
                             break;
                         case SoundType.TrainDynamic:
                             TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.SoundEvent(d, Data.Blocks[i].Sound[j].SoundBuffer, false, false, true, new Vector3(0.0, 0.0, 0.0), Data.Blocks[i].Sound[j].Speed);
                             break;
                     }
                 }
             }
         }
         // turn
         if (Data.Blocks[i].Turn != 0.0) {
             double ag = -Math.Atan(Data.Blocks[i].Turn);
             double cosag = Math.Cos(ag);
             double sinag = Math.Sin(ag);
             World.Rotate(ref Direction, cosag, sinag);
             World.RotatePlane(ref TrackManager.CurrentTrack.Elements[n].WorldDirection, cosag, sinag);
             World.RotatePlane(ref TrackManager.CurrentTrack.Elements[n].WorldSide, cosag, sinag);
             World.Cross(TrackManager.CurrentTrack.Elements[n].WorldDirection.X, TrackManager.CurrentTrack.Elements[n].WorldDirection.Y, TrackManager.CurrentTrack.Elements[n].WorldDirection.Z, TrackManager.CurrentTrack.Elements[n].WorldSide.X, TrackManager.CurrentTrack.Elements[n].WorldSide.Y, TrackManager.CurrentTrack.Elements[n].WorldSide.Z, out TrackManager.CurrentTrack.Elements[n].WorldUp.X, out TrackManager.CurrentTrack.Elements[n].WorldUp.Y, out TrackManager.CurrentTrack.Elements[n].WorldUp.Z);
         }
         // curves
         double a = 0.0;
         double c = Data.BlockInterval;
         double h = 0.0;
         if (WorldTrackElement.CurveRadius != 0.0 & Data.Blocks[i].Pitch != 0.0) {
             double d = Data.BlockInterval;
             double p = Data.Blocks[i].Pitch;
             double r = WorldTrackElement.CurveRadius;
             double s = d / Math.Sqrt(1.0 + p * p);
             h = s * p;
             double b = s / Math.Abs(r);
             c = Math.Sqrt(2.0 * r * r * (1.0 - Math.Cos(b)));
             a = 0.5 * (double)Math.Sign(r) * b;
             World.Rotate(ref Direction, Math.Cos(-a), Math.Sin(-a));
         } else if (WorldTrackElement.CurveRadius != 0.0) {
             double d = Data.BlockInterval;
             double r = WorldTrackElement.CurveRadius;
             double b = d / Math.Abs(r);
             c = Math.Sqrt(2.0 * r * r * (1.0 - Math.Cos(b)));
             a = 0.5 * (double)Math.Sign(r) * b;
             World.Rotate(ref Direction, Math.Cos(-a), Math.Sin(-a));
         } else if (Data.Blocks[i].Pitch != 0.0) {
             double p = Data.Blocks[i].Pitch;
             double d = Data.BlockInterval;
             c = d / Math.Sqrt(1.0 + p * p);
             h = c * p;
         }
         double TrackYaw = Math.Atan2(Direction.X, Direction.Y);
         double TrackPitch = Math.Atan(Data.Blocks[i].Pitch);
         World.Transformation GroundTransformation = new World.Transformation(TrackYaw, 0.0, 0.0);
         World.Transformation TrackTransformation = new World.Transformation(TrackYaw, TrackPitch, 0.0);
         World.Transformation NullTransformation = new World.Transformation(0.0, 0.0, 0.0);
         // ground
         if (!PreviewOnly) {
             int cb = (int)Math.Floor((double)i + 0.001);
             int ci = (cb % Data.Blocks[i].Cycle.Length + Data.Blocks[i].Cycle.Length) % Data.Blocks[i].Cycle.Length;
             int gi = Data.Blocks[i].Cycle[ci];
             if (gi >= 0 & gi < Data.Structure.Ground.Length) {
                 if (Data.Structure.Ground[gi] != null) {
                     ObjectManager.CreateObject(Data.Structure.Ground[Data.Blocks[i].Cycle[ci]], Position + new Vector3(0.0, -Data.Blocks[i].Height, 0.0), GroundTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                 }
             }
         }
         // ground-aligned free objects
         if (!PreviewOnly) {
             for (int j = 0; j < Data.Blocks[i].GroundFreeObj.Length; j++) {
                 int sttype = Data.Blocks[i].GroundFreeObj[j].Type;
                 double d = Data.Blocks[i].GroundFreeObj[j].TrackPosition - StartingDistance;
                 double dx = Data.Blocks[i].GroundFreeObj[j].X;
                 double dy = Data.Blocks[i].GroundFreeObj[j].Y;
                 Vector3 wpos = Position + new Vector3(Direction.X * d + Direction.Y * dx, dy - Data.Blocks[i].Height, Direction.Y * d - Direction.X * dx);
                 double tpos = Data.Blocks[i].GroundFreeObj[j].TrackPosition;
                 ObjectManager.CreateObject(Data.Structure.FreeObj[sttype], wpos, GroundTransformation, new World.Transformation(Data.Blocks[i].GroundFreeObj[j].Yaw, Data.Blocks[i].GroundFreeObj[j].Pitch, Data.Blocks[i].GroundFreeObj[j].Roll), Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, tpos);
             }
         }
         // rail-aligned objects
         if (!PreviewOnly) {
             for (int j = 0; j < Data.Blocks[i].Rail.Length; j++) {
                 if (j > 0 && !Data.Blocks[i].Rail[j].RailStart) continue;
                 // rail
                 Vector3 pos;
                 World.Transformation RailTransformation;
                 double planar, updown;
                 if (j == 0) {
                     // rail 0
                     pos = Position;
                     planar = 0.0;
                     updown = 0.0;
                     RailTransformation = new World.Transformation(TrackTransformation, planar, updown, 0.0);
                     pos = Position;
                 } else {
                     // rails 1-infinity
                     double x = Data.Blocks[i].Rail[j].RailStartX;
                     double y = Data.Blocks[i].Rail[j].RailStartY;
                     Vector3 offset = new Vector3(Direction.Y * x, y, -Direction.X * x);
                     pos = Position + offset;
                     double dh;
                     if (i < Data.Blocks.Length - 1 && Data.Blocks[i + 1].Rail.Length > j) {
                         // take orientation of upcoming block into account
                         World.Vector2D Direction2 = Direction;
                         Vector3 Position2 = Position;
                         Position2.X += Direction.X * c;
                         Position2.Y += h;
                         Position2.Z += Direction.Y * c;
                         if (a != 0.0) {
                             World.Rotate(ref Direction2, Math.Cos(-a), Math.Sin(-a));
                         }
                         if (Data.Blocks[i + 1].Turn != 0.0) {
                             double ag = -Math.Atan(Data.Blocks[i + 1].Turn);
                             double cosag = Math.Cos(ag);
                             double sinag = Math.Sin(ag);
                             World.Rotate(ref Direction2, cosag, sinag);
                         }
                         double a2 = 0.0;
                         double c2 = Data.BlockInterval;
                         double h2 = 0.0;
                         if (Data.Blocks[i + 1].CurrentTrackState.CurveRadius != 0.0 & Data.Blocks[i + 1].Pitch != 0.0) {
                             double d2 = Data.BlockInterval;
                             double p2 = Data.Blocks[i + 1].Pitch;
                             double r2 = Data.Blocks[i + 1].CurrentTrackState.CurveRadius;
                             double s2 = d2 / Math.Sqrt(1.0 + p2 * p2);
                             h2 = s2 * p2;
                             double b2 = s2 / Math.Abs(r2);
                             c2 = Math.Sqrt(2.0 * r2 * r2 * (1.0 - Math.Cos(b2)));
                             a2 = 0.5 * (double)Math.Sign(r2) * b2;
                             World.Rotate(ref Direction2, Math.Cos(-a2), Math.Sin(-a2));
                         } else if (Data.Blocks[i + 1].CurrentTrackState.CurveRadius != 0.0) {
                             double d2 = Data.BlockInterval;
                             double r2 = Data.Blocks[i + 1].CurrentTrackState.CurveRadius;
                             double b2 = d2 / Math.Abs(r2);
                             c2 = Math.Sqrt(2.0 * r2 * r2 * (1.0 - Math.Cos(b2)));
                             a2 = 0.5 * (double)Math.Sign(r2) * b2;
                             World.Rotate(ref Direction2, Math.Cos(-a2), Math.Sin(-a2));
                         } else if (Data.Blocks[i + 1].Pitch != 0.0) {
                             double p2 = Data.Blocks[i + 1].Pitch;
                             double d2 = Data.BlockInterval;
                             c2 = d2 / Math.Sqrt(1.0 + p2 * p2);
                             h2 = c2 * p2;
                         }
                         double TrackYaw2 = Math.Atan2(Direction2.X, Direction2.Y);
                         double TrackPitch2 = Math.Atan(Data.Blocks[i + 1].Pitch);
                         World.Transformation GroundTransformation2 = new World.Transformation(TrackYaw2, 0.0, 0.0);
                         World.Transformation TrackTransformation2 = new World.Transformation(TrackYaw2, TrackPitch2, 0.0);
                         double x2 = Data.Blocks[i + 1].Rail[j].RailEndX;
                         double y2 = Data.Blocks[i + 1].Rail[j].RailEndY;
                         Vector3 offset2 = new Vector3(Direction2.Y * x2, y2, -Direction2.X * x2);
                         Vector3 pos2 = Position2 + offset2;
                         double rx = pos2.X - pos.X;
                         double ry = pos2.Y - pos.Y;
                         double rz = pos2.Z - pos.Z;
                         World.Normalize(ref rx, ref ry, ref rz);
                         RailTransformation.Z = new Vector3(rx, ry, rz);
                         RailTransformation.X = new Vector3(rz, 0.0, -rx);
                         World.Normalize(ref RailTransformation.X.X, ref RailTransformation.X.Z);
                         RailTransformation.Y = Vector3.Cross(RailTransformation.Z, RailTransformation.X);
                         double dx = Data.Blocks[i + 1].Rail[j].RailEndX - Data.Blocks[i].Rail[j].RailStartX;
                         double dy = Data.Blocks[i + 1].Rail[j].RailEndY - Data.Blocks[i].Rail[j].RailStartY;
                         planar = Math.Atan(dx / c);
                         dh = dy / c;
                         updown = Math.Atan(dh);
                     } else {
                         planar = 0.0;
                         dh = 0.0;
                         updown = 0.0;
                         RailTransformation = new World.Transformation(TrackTransformation, 0.0, 0.0, 0.0);
                     }
                 }
                 if (Data.Blocks[i].RailType[j] < Data.Structure.Rail.Length) {
                     if (Data.Structure.Rail[Data.Blocks[i].RailType[j]] != null) {
                         ObjectManager.CreateObject(Data.Structure.Rail[Data.Blocks[i].RailType[j]], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                     }
                 }
                 // points of interest
                 for (int k = 0; k < Data.Blocks[i].PointsOfInterest.Length; k++) {
                     if (Data.Blocks[i].PointsOfInterest[k].RailIndex == j) {
                         double d = Data.Blocks[i].PointsOfInterest[k].TrackPosition - StartingDistance;
                         double x = Data.Blocks[i].PointsOfInterest[k].X;
                         double y = Data.Blocks[i].PointsOfInterest[k].Y;
                         int m = Game.PointsOfInterest.Length;
                         Array.Resize<Game.PointOfInterest>(ref Game.PointsOfInterest, m + 1);
                         Game.PointsOfInterest[m].TrackPosition = Data.Blocks[i].PointsOfInterest[k].TrackPosition;
                         if (i < Data.Blocks.Length - 1 && Data.Blocks[i + 1].Rail.Length > j) {
                             double dx = Data.Blocks[i + 1].Rail[j].RailEndX - Data.Blocks[i].Rail[j].RailStartX;
                             double dy = Data.Blocks[i + 1].Rail[j].RailEndY - Data.Blocks[i].Rail[j].RailStartY;
                             dx = Data.Blocks[i].Rail[j].RailStartX + d / Data.BlockInterval * dx;
                             dy = Data.Blocks[i].Rail[j].RailStartY + d / Data.BlockInterval * dy;
                             Game.PointsOfInterest[m].TrackOffset = new Vector3(x + dx, y + dy, 0.0);
                         } else {
                             double dx = Data.Blocks[i].Rail[j].RailStartX;
                             double dy = Data.Blocks[i].Rail[j].RailStartY;
                             Game.PointsOfInterest[m].TrackOffset = new Vector3(x + dx, y + dy, 0.0);
                         }
                         Game.PointsOfInterest[m].TrackYaw = Data.Blocks[i].PointsOfInterest[k].Yaw + planar;
                         Game.PointsOfInterest[m].TrackPitch = Data.Blocks[i].PointsOfInterest[k].Pitch + updown;
                         Game.PointsOfInterest[m].TrackRoll = Data.Blocks[i].PointsOfInterest[k].Roll;
                         Game.PointsOfInterest[m].Text = Data.Blocks[i].PointsOfInterest[k].Text;
                     }
                 }
                 // poles
                 if (Data.Blocks[i].RailPole.Length > j && Data.Blocks[i].RailPole[j].Exists) {
                     double dz = StartingDistance / Data.Blocks[i].RailPole[j].Interval;
                     dz -= Math.Floor(dz + 0.5);
                     if (dz >= -0.01 & dz <= 0.01) {
                         if (Data.Blocks[i].RailPole[j].Mode == 0) {
                             if (Data.Blocks[i].RailPole[j].Location <= 0.0) {
                                 ObjectManager.CreateObject(Data.Structure.Poles[0][Data.Blocks[i].RailPole[j].Type], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                             } else {
                                 ObjectManager.UnifiedObject Pole = GetMirroredObject(Data.Structure.Poles[0][Data.Blocks[i].RailPole[j].Type]);
                                 ObjectManager.CreateObject(Pole, pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                             }
                         } else {
                             int m = Data.Blocks[i].RailPole[j].Mode;
                             double dx = -Data.Blocks[i].RailPole[j].Location * 3.8;
                             double wa = Math.Atan2(Direction.Y, Direction.X) - planar;
                             double wx = Math.Cos(wa);
                             double wy = Math.Tan(updown);
                             double wz = Math.Sin(wa);
                             World.Normalize(ref wx, ref wy, ref wz);
                             double sx = Direction.Y;
                             double sy = 0.0;
                             double sz = -Direction.X;
                             Vector3 wpos = pos + new Vector3(sx * dx + wx * dz, sy * dx + wy * dz, sz * dx + wz * dz);
                             int type = Data.Blocks[i].RailPole[j].Type;
                             ObjectManager.CreateObject(Data.Structure.Poles[m][type], wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                         }
                     }
                 }
                 // walls
                 if (Data.Blocks[i].RailWall.Length > j && Data.Blocks[i].RailWall[j].Exists) {
                     if (Data.Blocks[i].RailWall[j].Direction <= 0) {
                         ObjectManager.CreateObject(Data.Structure.WallL[Data.Blocks[i].RailWall[j].Type], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                     }
                     if (Data.Blocks[i].RailWall[j].Direction >= 0) {
                         ObjectManager.CreateObject(Data.Structure.WallR[Data.Blocks[i].RailWall[j].Type], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                     }
                 }
                 // dikes
                 if (Data.Blocks[i].RailDike.Length > j && Data.Blocks[i].RailDike[j].Exists) {
                     if (Data.Blocks[i].RailDike[j].Direction <= 0) {
                         ObjectManager.CreateObject(Data.Structure.DikeL[Data.Blocks[i].RailDike[j].Type], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                     }
                     if (Data.Blocks[i].RailDike[j].Direction >= 0) {
                         ObjectManager.CreateObject(Data.Structure.DikeR[Data.Blocks[i].RailDike[j].Type], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                     }
                 }
                 // sounds
                 if (j == 0) {
                     for (int k = 0; k < Data.Blocks[i].Sound.Length; k++) {
                         if (Data.Blocks[i].Sound[k].Type == SoundType.World) {
                             if (Data.Blocks[i].Sound[k].SoundBuffer != null) {
                                 double d = Data.Blocks[i].Sound[k].TrackPosition - StartingDistance;
                                 double dx = Data.Blocks[i].Sound[k].X;
                                 double dy = Data.Blocks[i].Sound[k].Y;
                                 double wa = Math.Atan2(Direction.Y, Direction.X) - planar;
                                 double wx = Math.Cos(wa);
                                 double wy = Math.Tan(updown);
                                 double wz = Math.Sin(wa);
                                 World.Normalize(ref wx, ref wy, ref wz);
                                 double sx = Direction.Y;
                                 double sy = 0.0;
                                 double sz = -Direction.X;
                                 double ux, uy, uz;
                                 World.Cross(wx, wy, wz, sx, sy, sz, out ux, out uy, out uz);
                                 Vector3 wpos = pos + new Vector3(sx * dx + ux * dy + wx * d, sy * dx + uy * dy + wy * d, sz * dx + uz * dy + wz * d);
                                 Sounds.PlaySound(Data.Blocks[i].Sound[k].SoundBuffer, 1.0, 1.0, wpos, true);
                             }
                         }
                     }
                 }
                 // forms
                 for (int k = 0; k < Data.Blocks[i].Form.Length; k++) {
                     // primary rail
                     if (Data.Blocks[i].Form[k].PrimaryRail == j) {
                         if (Data.Blocks[i].Form[k].SecondaryRail == Form.SecondaryRailStub) {
                             if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormL.Length || Data.Structure.FormL[Data.Blocks[i].Form[k].FormType] == null) {
                                 Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + ".");
                             } else {
                                 ObjectManager.CreateObject(Data.Structure.FormL[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                                 if (Data.Blocks[i].Form[k].RoofType > 0) {
                                     if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofL.Length || Data.Structure.RoofL[Data.Blocks[i].Form[k].RoofType] == null) {
                                         Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + ".");
                                     } else {
                                         ObjectManager.CreateObject(Data.Structure.RoofL[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                                     }
                                 }
                             }
                         } else if (Data.Blocks[i].Form[k].SecondaryRail == Form.SecondaryRailL) {
                             if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormL.Length || Data.Structure.FormL[Data.Blocks[i].Form[k].FormType] == null) {
                                 Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + ".");
                             } else {
                                 ObjectManager.CreateObject(Data.Structure.FormL[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                             }
                             if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormCL.Length || Data.Structure.FormCL[Data.Blocks[i].Form[k].FormType] == null) {
                                 Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormCL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + ".");
                             } else {
                                 ObjectManager.CreateStaticObject(Data.Structure.FormCL[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                             }
                             if (Data.Blocks[i].Form[k].RoofType > 0) {
                                 if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofL.Length || Data.Structure.RoofL[Data.Blocks[i].Form[k].RoofType] == null) {
                                     Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + ".");
                                 } else {
                                     ObjectManager.CreateObject(Data.Structure.RoofL[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                                 }
                                 if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofCL.Length || Data.Structure.RoofCL[Data.Blocks[i].Form[k].RoofType] == null) {
                                     Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofCL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + ".");
                                 } else {
                                     ObjectManager.CreateStaticObject(Data.Structure.RoofCL[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                                 }
                             }
                         } else if (Data.Blocks[i].Form[k].SecondaryRail == Form.SecondaryRailR) {
                             if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormR.Length || Data.Structure.FormR[Data.Blocks[i].Form[k].FormType] == null) {
                                 Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + ".");
                             } else {
                                 ObjectManager.CreateObject(Data.Structure.FormR[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                             }
                             if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormCR.Length || Data.Structure.FormCR[Data.Blocks[i].Form[k].FormType] == null) {
                                 Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormCR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + ".");
                             } else {
                                 ObjectManager.CreateStaticObject(Data.Structure.FormCR[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                             }
                             if (Data.Blocks[i].Form[k].RoofType > 0) {
                                 if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofR.Length || Data.Structure.RoofR[Data.Blocks[i].Form[k].RoofType] == null) {
                                     Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + ".");
                                 } else {
                                     ObjectManager.CreateObject(Data.Structure.RoofR[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                                 }
                                 if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofCR.Length || Data.Structure.RoofCR[Data.Blocks[i].Form[k].RoofType] == null) {
                                     Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofCR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + ".");
                                 } else {
                                     ObjectManager.CreateStaticObject(Data.Structure.RoofCR[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                                 }
                             }
                         } else if (Data.Blocks[i].Form[k].SecondaryRail > 0) {
                             int p = Data.Blocks[i].Form[k].PrimaryRail;
                             double px0 = p > 0 ? Data.Blocks[i].Rail[p].RailStartX : 0.0;
                             double px1 = p > 0 ? Data.Blocks[i + 1].Rail[p].RailEndX : 0.0;
                             int s = Data.Blocks[i].Form[k].SecondaryRail;
                             if (s < 0 || s >= Data.Blocks[i].Rail.Length || !Data.Blocks[i].Rail[s].RailStart) {
                                 Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex2 is out of range in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName);
                             } else {
                                 double sx0 = Data.Blocks[i].Rail[s].RailStartX;
                                 double sx1 = Data.Blocks[i + 1].Rail[s].RailEndX;
                                 double d0 = sx0 - px0;
                                 double d1 = sx1 - px1;
                                 if (d0 < 0.0) {
                                     if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormL.Length || Data.Structure.FormL[Data.Blocks[i].Form[k].FormType] == null) {
                                         Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + ".");
                                     } else {
                                         ObjectManager.CreateObject(Data.Structure.FormL[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                                     }
                                     if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormCL.Length || Data.Structure.FormCL[Data.Blocks[i].Form[k].FormType] == null) {
                                         Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormCL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + ".");
                                     } else {
                                         ObjectManager.StaticObject FormC = GetTransformedStaticObject(Data.Structure.FormCL[Data.Blocks[i].Form[k].FormType], d0, d1);
                                         ObjectManager.CreateStaticObject(FormC, pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                                     }
                                     if (Data.Blocks[i].Form[k].RoofType > 0) {
                                         if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofL.Length || Data.Structure.RoofL[Data.Blocks[i].Form[k].RoofType] == null) {
                                             Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + ".");
                                         } else {
                                             ObjectManager.CreateObject(Data.Structure.RoofL[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                                         }
                                         if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofCL.Length || Data.Structure.RoofCL[Data.Blocks[i].Form[k].RoofType] == null) {
                                             Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofCL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + ".");
                                         } else {
                                             ObjectManager.StaticObject RoofC = GetTransformedStaticObject(Data.Structure.RoofCL[Data.Blocks[i].Form[k].RoofType], d0, d1);
                                             ObjectManager.CreateStaticObject(RoofC, pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                                         }
                                     }
                                 } else if (d0 > 0.0) {
                                     if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormR.Length || Data.Structure.FormR[Data.Blocks[i].Form[k].FormType] == null) {
                                         Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + ".");
                                     } else {
                                         ObjectManager.CreateObject(Data.Structure.FormR[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                                     }
                                     if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormCR.Length || Data.Structure.FormCR[Data.Blocks[i].Form[k].FormType] == null) {
                                         Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormCR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + ".");
                                     } else {
                                         ObjectManager.StaticObject FormC = GetTransformedStaticObject(Data.Structure.FormCR[Data.Blocks[i].Form[k].FormType], d0, d1);
                                         ObjectManager.CreateStaticObject(FormC, pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                                     }
                                     if (Data.Blocks[i].Form[k].RoofType > 0) {
                                         if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofR.Length || Data.Structure.RoofR[Data.Blocks[i].Form[k].RoofType] == null) {
                                             Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + ".");
                                         } else {
                                             ObjectManager.CreateObject(Data.Structure.RoofR[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                                         }
                                         if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofCR.Length || Data.Structure.RoofCR[Data.Blocks[i].Form[k].RoofType] == null) {
                                             Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofCR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + ".");
                                         } else {
                                             ObjectManager.StaticObject RoofC = GetTransformedStaticObject(Data.Structure.RoofCR[Data.Blocks[i].Form[k].RoofType], d0, d1);
                                             ObjectManager.CreateStaticObject(RoofC, pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                                         }
                                     }
                                 }
                             }
                         }
                     }
                     // secondary rail
                     if (Data.Blocks[i].Form[k].SecondaryRail == j) {
                         int p = Data.Blocks[i].Form[k].PrimaryRail;
                         double px = p > 0 ? Data.Blocks[i].Rail[p].RailStartX : 0.0;
                         int s = Data.Blocks[i].Form[k].SecondaryRail;
                         double sx = Data.Blocks[i].Rail[s].RailStartX;
                         double d = px - sx;
                         if (d < 0.0) {
                             if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormL.Length || Data.Structure.FormL[Data.Blocks[i].Form[k].FormType] == null) {
                                 Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + ".");
                             } else {
                                 ObjectManager.CreateObject(Data.Structure.FormL[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                             }
                             if (Data.Blocks[i].Form[k].RoofType > 0) {
                                 if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofL.Length || Data.Structure.RoofL[Data.Blocks[i].Form[k].RoofType] == null) {
                                     Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + ".");
                                 } else {
                                     ObjectManager.CreateObject(Data.Structure.RoofL[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                                 }
                             }
                         } else {
                             if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormR.Length || Data.Structure.FormR[Data.Blocks[i].Form[k].FormType] == null) {
                                 Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + ".");
                             } else {
                                 ObjectManager.CreateObject(Data.Structure.FormR[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                             }
                             if (Data.Blocks[i].Form[k].RoofType > 0) {
                                 if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofR.Length || Data.Structure.RoofR[Data.Blocks[i].Form[k].RoofType] == null) {
                                     Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + ".");
                                 } else {
                                     ObjectManager.CreateObject(Data.Structure.RoofR[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                                 }
                             }
                         }
                     }
                 }
                 // cracks
                 for (int k = 0; k < Data.Blocks[i].Crack.Length; k++) {
                     if (Data.Blocks[i].Crack[k].PrimaryRail == j) {
                         int p = Data.Blocks[i].Crack[k].PrimaryRail;
                         double px0 = p > 0 ? Data.Blocks[i].Rail[p].RailStartX : 0.0;
                         double px1 = p > 0 ? Data.Blocks[i + 1].Rail[p].RailEndX : 0.0;
                         int s = Data.Blocks[i].Crack[k].SecondaryRail;
                         if (s < 0 || s >= Data.Blocks[i].Rail.Length || !Data.Blocks[i].Rail[s].RailStart) {
                             Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex2 is out of range in Track.Crack at track position " + StartingDistance.ToString(Culture) + " in file " + FileName);
                         } else {
                             double sx0 = Data.Blocks[i].Rail[s].RailStartX;
                             double sx1 = Data.Blocks[i + 1].Rail[s].RailEndX;
                             double d0 = sx0 - px0;
                             double d1 = sx1 - px1;
                             if (d0 < 0.0) {
                                 if (Data.Blocks[i].Crack[k].Type >= Data.Structure.CrackL.Length || Data.Structure.CrackL[Data.Blocks[i].Crack[k].Type] == null) {
                                     Interface.AddMessage(Interface.MessageType.Error, false, "CrackStructureIndex references a CrackL not loaded in Track.Crack at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + ".");
                                 } else {
                                     ObjectManager.StaticObject Crack = GetTransformedStaticObject(Data.Structure.CrackL[Data.Blocks[i].Crack[k].Type], d0, d1);
                                     ObjectManager.CreateStaticObject(Crack, pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                                 }
                             } else if (d0 > 0.0) {
                                 if (Data.Blocks[i].Crack[k].Type >= Data.Structure.CrackR.Length || Data.Structure.CrackR[Data.Blocks[i].Crack[k].Type] == null) {
                                     Interface.AddMessage(Interface.MessageType.Error, false, "CrackStructureIndex references a CrackR not loaded in Track.Crack at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + ".");
                                 } else {
                                     ObjectManager.StaticObject Crack = GetTransformedStaticObject(Data.Structure.CrackR[Data.Blocks[i].Crack[k].Type], d0, d1);
                                     ObjectManager.CreateStaticObject(Crack, pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance);
                                 }
                             }
                         }
                     }
                 }
                 // free objects
                 if (Data.Blocks[i].RailFreeObj.Length > j && Data.Blocks[i].RailFreeObj[j] != null) {
                     for (int k = 0; k < Data.Blocks[i].RailFreeObj[j].Length; k++) {
                         int sttype = Data.Blocks[i].RailFreeObj[j][k].Type;
                         double dx = Data.Blocks[i].RailFreeObj[j][k].X;
                         double dy = Data.Blocks[i].RailFreeObj[j][k].Y;
                         double dz = Data.Blocks[i].RailFreeObj[j][k].TrackPosition - StartingDistance;
                         Vector3 wpos = pos;
                         wpos.X += dx * RailTransformation.X.X + dy * RailTransformation.Y.X + dz * RailTransformation.Z.X;
                         wpos.Y += dx * RailTransformation.X.Y + dy * RailTransformation.Y.Y + dz * RailTransformation.Z.Y;
                         wpos.Z += dx * RailTransformation.X.Z + dy * RailTransformation.Y.Z + dz * RailTransformation.Z.Z;
                         double tpos = Data.Blocks[i].RailFreeObj[j][k].TrackPosition;
                         ObjectManager.CreateObject(Data.Structure.FreeObj[sttype], wpos, RailTransformation, new World.Transformation(Data.Blocks[i].RailFreeObj[j][k].Yaw, Data.Blocks[i].RailFreeObj[j][k].Pitch, Data.Blocks[i].RailFreeObj[j][k].Roll), -1, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, tpos, 1.0, false);
                     }
                 }
                 // transponder objects
                 if (j == 0) {
                     for (int k = 0; k < Data.Blocks[i].Transponder.Length; k++) {
                         ObjectManager.UnifiedObject obj = null;
                         if (Data.Blocks[i].Transponder[k].ShowDefaultObject) {
                             switch (Data.Blocks[i].Transponder[k].Type) {
                                     case 0: obj = TransponderS; break;
                                     case 1: obj = TransponderSN; break;
                                     case 2: obj = TransponderFalseStart; break;
                                     case 3: obj = TransponderPOrigin; break;
                                     case 4: obj = TransponderPStop; break;
                             }
                         } else {
                             int b = Data.Blocks[i].Transponder[k].BeaconStructureIndex;
                             if (b >= 0 & b < Data.Structure.Beacon.Length) {
                                 obj = Data.Structure.Beacon[b];
                             }
                         }
                         if (obj != null) {
                             double dx = Data.Blocks[i].Transponder[k].X;
                             double dy = Data.Blocks[i].Transponder[k].Y;
                             double dz = Data.Blocks[i].Transponder[k].TrackPosition - StartingDistance;
                             Vector3 wpos = pos;
                             wpos.X += dx * RailTransformation.X.X + dy * RailTransformation.Y.X + dz * RailTransformation.Z.X;
                             wpos.Y += dx * RailTransformation.X.Y + dy * RailTransformation.Y.Y + dz * RailTransformation.Z.Y;
                             wpos.Z += dx * RailTransformation.X.Z + dy * RailTransformation.Y.Z + dz * RailTransformation.Z.Z;
                             double tpos = Data.Blocks[i].Transponder[k].TrackPosition;
                             if (Data.Blocks[i].Transponder[k].ShowDefaultObject) {
                                 double b = 0.25 + 0.75 * GetBrightness(ref Data, tpos);
                                 ObjectManager.CreateObject(obj, wpos, RailTransformation, new World.Transformation(Data.Blocks[i].Transponder[k].Yaw, Data.Blocks[i].Transponder[k].Pitch, Data.Blocks[i].Transponder[k].Roll), -1, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, false);
                             } else {
                                 ObjectManager.CreateObject(obj, wpos, RailTransformation, new World.Transformation(Data.Blocks[i].Transponder[k].Yaw, Data.Blocks[i].Transponder[k].Pitch, Data.Blocks[i].Transponder[k].Roll), Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, tpos);
                             }
                         }
                     }
                 }
                 // sections/signals/transponders
                 if (j == 0) {
                     // signals
                     for (int k = 0; k < Data.Blocks[i].Signal.Length; k++) {
                         SignalData sd;
                         if (Data.Blocks[i].Signal[k].SignalCompatibilityObjectIndex >= 0) {
                             sd = Data.CompatibilitySignalData[Data.Blocks[i].Signal[k].SignalCompatibilityObjectIndex];
                         } else {
                             sd = Data.SignalData[Data.Blocks[i].Signal[k].SignalObjectIndex];
                         }
                         // objects
                         double dz = Data.Blocks[i].Signal[k].TrackPosition - StartingDistance;
                         if (Data.Blocks[i].Signal[k].ShowPost) {
                             // post
                             double dx = Data.Blocks[i].Signal[k].X;
                             Vector3 wpos = pos;
                             wpos.X += dx * RailTransformation.X.X + dz * RailTransformation.Z.X;
                             wpos.Y += dx * RailTransformation.X.Y + dz * RailTransformation.Z.Y;
                             wpos.Z += dx * RailTransformation.X.Z + dz * RailTransformation.Z.Z;
                             double tpos = Data.Blocks[i].Signal[k].TrackPosition;
                             double b = 0.25 + 0.75 * GetBrightness(ref Data, tpos);
                             ObjectManager.CreateStaticObject(SignalPost, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, false);
                         }
                         if (Data.Blocks[i].Signal[k].ShowObject) {
                             // signal object
                             double dx = Data.Blocks[i].Signal[k].X;
                             double dy = Data.Blocks[i].Signal[k].Y;
                             Vector3 wpos = pos;
                             wpos.X += dx * RailTransformation.X.X + dy * RailTransformation.Y.X + dz * RailTransformation.Z.X;
                             wpos.Y += dx * RailTransformation.X.Y + dy * RailTransformation.Y.Y + dz * RailTransformation.Z.Y;
                             wpos.Z += dx * RailTransformation.X.Z + dy * RailTransformation.Y.Z + dz * RailTransformation.Z.Z;
                             double tpos = Data.Blocks[i].Signal[k].TrackPosition;
                             if (sd is AnimatedObjectSignalData) {
                                 AnimatedObjectSignalData aosd = (AnimatedObjectSignalData)sd;
                                 ObjectManager.CreateObject(aosd.Objects, wpos, RailTransformation, new World.Transformation(Data.Blocks[i].Signal[k].Yaw, Data.Blocks[i].Signal[k].Pitch, Data.Blocks[i].Signal[k].Roll), Data.Blocks[i].Signal[k].Section, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, tpos, 1.0, false);
                             } else if (sd is CompatibilitySignalData) {
                                 CompatibilitySignalData csd = (CompatibilitySignalData)sd;
                                 if (csd.Numbers.Length != 0) {
                                     double brightness = 0.25 + 0.75 * GetBrightness(ref Data, tpos);
                                     ObjectManager.AnimatedObjectCollection aoc = new ObjectManager.AnimatedObjectCollection();
                                     aoc.Objects = new ObjectManager.AnimatedObject[1];
                                     aoc.Objects[0] = new ObjectManager.AnimatedObject();
                                     aoc.Objects[0].States = new ObjectManager.AnimatedObjectState[csd.Numbers.Length];
                                     for (int l = 0; l < csd.Numbers.Length; l++) {
                                         aoc.Objects[0].States[l].Object = ObjectManager.CloneObject(csd.Objects[l]);
                                     }
                                     string expr = "";
                                     for (int l = 0; l < csd.Numbers.Length - 1; l++) {
                                         expr += "section " + csd.Numbers[l].ToString(Culture) + " <= " + l.ToString(Culture) + " ";
                                     }
                                     expr += (csd.Numbers.Length - 1).ToString(Culture);
                                     for (int l = 0; l < csd.Numbers.Length - 1; l++) {
                                         expr += " ?";
                                     }
                                     aoc.Objects[0].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(expr);
                                     aoc.Objects[0].RefreshRate = 1.0 + 0.01 * Program.RandomNumberGenerator.NextDouble();
                                     ObjectManager.CreateObject(aoc, wpos, RailTransformation, new World.Transformation(Data.Blocks[i].Signal[k].Yaw, Data.Blocks[i].Signal[k].Pitch, Data.Blocks[i].Signal[k].Roll), Data.Blocks[i].Signal[k].Section, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, tpos, brightness, false);
                                 }
                             } else if (sd is Bve4SignalData) {
                                 Bve4SignalData b4sd = (Bve4SignalData)sd;
                                 if (b4sd.SignalTextures.Length != 0) {
                                     int m = Math.Max(b4sd.SignalTextures.Length, b4sd.GlowTextures.Length);
                                     int zn = 0;
                                     for (int l = 0; l < m; l++) {
                                         if (l < b4sd.SignalTextures.Length && b4sd.SignalTextures[l] != null || l < b4sd.GlowTextures.Length && b4sd.GlowTextures[l] != null) {
                                             zn++;
                                         }
                                     }
                                     ObjectManager.AnimatedObjectCollection aoc = new ObjectManager.AnimatedObjectCollection();
                                     aoc.Objects = new ObjectManager.AnimatedObject[1];
                                     aoc.Objects[0] = new ObjectManager.AnimatedObject();
                                     aoc.Objects[0].States = new ObjectManager.AnimatedObjectState[zn];
                                     int zi = 0;
                                     string expr = "";
                                     for (int l = 0; l < m; l++) {
                                         bool qs = l < b4sd.SignalTextures.Length && b4sd.SignalTextures[l] != null;
                                         bool qg = l < b4sd.GlowTextures.Length && b4sd.GlowTextures[l] != null;
                                         if (qs & qg) {
                                             ObjectManager.StaticObject so = ObjectManager.CloneObject(b4sd.BaseObject, b4sd.SignalTextures[l], null);
                                             ObjectManager.StaticObject go = ObjectManager.CloneObject(b4sd.GlowObject, b4sd.GlowTextures[l], null);
                                             ObjectManager.JoinObjects(ref so, go);
                                             aoc.Objects[0].States[zi].Object = so;
                                         } else if (qs) {
                                             ObjectManager.StaticObject so = ObjectManager.CloneObject(b4sd.BaseObject, b4sd.SignalTextures[l], null);
                                             aoc.Objects[0].States[zi].Object = so;
                                         } else if (qg) {
                                             ObjectManager.StaticObject go = ObjectManager.CloneObject(b4sd.GlowObject, b4sd.GlowTextures[l], null);
                                             aoc.Objects[0].States[zi].Object = go;
                                         }
                                         if (qs | qg) {
                                             if (zi < zn - 1) {
                                                 expr += "section " + l.ToString(Culture) + " <= " + zi.ToString(Culture) + " ";
                                             } else {
                                                 expr += zi.ToString(Culture);
                                             }
                                             zi++;
                                         }
                                     }
                                     for (int l = 0; l < zn - 1; l++) {
                                         expr += " ?";
                                     }
                                     aoc.Objects[0].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(expr);
                                     aoc.Objects[0].RefreshRate = 1.0 + 0.01 * Program.RandomNumberGenerator.NextDouble();
                                     ObjectManager.CreateObject(aoc, wpos, RailTransformation, new World.Transformation(Data.Blocks[i].Signal[k].Yaw, Data.Blocks[i].Signal[k].Pitch, Data.Blocks[i].Signal[k].Roll), Data.Blocks[i].Signal[k].Section, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, tpos, 1.0, false);
                                 }
                             }
                         }
                     }
                     // sections
                     for (int k = 0; k < Data.Blocks[i].Section.Length; k++) {
                         int m = Game.Sections.Length;
                         Array.Resize<Game.Section>(ref Game.Sections, m + 1);
                         Game.Sections[m].SignalIndices = new int[] { };
                         // create associated transponders
                         for (int g = 0; g <= i; g++) {
                             for (int l = 0; l < Data.Blocks[g].Transponder.Length; l++) {
                                 if (Data.Blocks[g].Transponder[l].Type != -1 & Data.Blocks[g].Transponder[l].Section == m) {
                                     int o = TrackManager.CurrentTrack.Elements[n - i + g].Events.Length;
                                     Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n - i + g].Events, o + 1);
                                     double dt = Data.Blocks[g].Transponder[l].TrackPosition - StartingDistance + (double)(i - g) * Data.BlockInterval;
                                     TrackManager.CurrentTrack.Elements[n - i + g].Events[o] = new TrackManager.TransponderEvent(dt, Data.Blocks[g].Transponder[l].Type, Data.Blocks[g].Transponder[l].Data, m);
                                     Data.Blocks[g].Transponder[l].Type = -1;
                                 }
                             }
                         }
                         // create section
                         Game.Sections[m].TrackPosition = Data.Blocks[i].Section[k].TrackPosition;
                         Game.Sections[m].Aspects = new Game.SectionAspect[Data.Blocks[i].Section[k].Aspects.Length];
                         for (int l = 0; l < Data.Blocks[i].Section[k].Aspects.Length; l++) {
                             Game.Sections[m].Aspects[l].Number = Data.Blocks[i].Section[k].Aspects[l];
                             if (Data.Blocks[i].Section[k].Aspects[l] >= 0 & Data.Blocks[i].Section[k].Aspects[l] < Data.SignalSpeeds.Length) {
                                 Game.Sections[m].Aspects[l].Speed = Data.SignalSpeeds[Data.Blocks[i].Section[k].Aspects[l]];
                             } else {
                                 Game.Sections[m].Aspects[l].Speed = double.PositiveInfinity;
                             }
                         }
                         Game.Sections[m].Type = Data.Blocks[i].Section[k].Type;
                         Game.Sections[m].CurrentAspect = -1;
                         if (m > 0) {
                             Game.Sections[m].PreviousSection = m - 1;
                             Game.Sections[m - 1].NextSection = m;
                         } else {
                             Game.Sections[m].PreviousSection = -1;
                         }
                         Game.Sections[m].NextSection = -1;
                         Game.Sections[m].StationIndex = Data.Blocks[i].Section[k].DepartureStationIndex;
                         Game.Sections[m].Invisible = Data.Blocks[i].Section[k].Invisible;
                         Game.Sections[m].Trains = new TrainManager.Train[] { };
                         // create section change event
                         double d = Data.Blocks[i].Section[k].TrackPosition - StartingDistance;
                         int p = TrackManager.CurrentTrack.Elements[n].Events.Length;
                         Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, p + 1);
                         TrackManager.CurrentTrack.Elements[n].Events[p] = new TrackManager.SectionChangeEvent(d, m - 1, m);
                     }
                     // transponders introduced after corresponding sections
                     for (int l = 0; l < Data.Blocks[i].Transponder.Length; l++) {
                         if (Data.Blocks[i].Transponder[l].Type != -1) {
                             int t = Data.Blocks[i].Transponder[l].Section;
                             if (t >= 0 & t < Game.Sections.Length) {
                                 int m = TrackManager.CurrentTrack.Elements[n].Events.Length;
                                 Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1);
                                 double dt = Data.Blocks[i].Transponder[l].TrackPosition - StartingDistance;
                                 TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.TransponderEvent(dt, Data.Blocks[i].Transponder[l].Type, Data.Blocks[i].Transponder[l].Data, t);
                                 Data.Blocks[i].Transponder[l].Type = -1;
                             }
                         }
                     }
                 }
                 // limit
                 if (j == 0) {
                     for (int k = 0; k < Data.Blocks[i].Limit.Length; k++) {
                         if (Data.Blocks[i].Limit[k].Direction != 0) {
                             double dx = 2.2 * (double)Data.Blocks[i].Limit[k].Direction;
                             double dz = Data.Blocks[i].Limit[k].TrackPosition - StartingDistance;
                             Vector3 wpos = pos;
                             wpos.X += dx * RailTransformation.X.X + dz * RailTransformation.Z.X;
                             wpos.Y += dx * RailTransformation.X.Y + dz * RailTransformation.Z.Y;
                             wpos.Z += dx * RailTransformation.X.Z + dz * RailTransformation.Z.Z;
                             double tpos = Data.Blocks[i].Limit[k].TrackPosition;
                             double b = 0.25 + 0.75 * GetBrightness(ref Data, tpos);
                             if (Data.Blocks[i].Limit[k].Speed <= 0.0 | Data.Blocks[i].Limit[k].Speed >= 1000.0) {
                                 ObjectManager.CreateStaticObject(LimitPostInfinite, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, false);
                             } else {
                                 if (Data.Blocks[i].Limit[k].Cource < 0) {
                                     ObjectManager.CreateStaticObject(LimitPostLeft, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, false);
                                 } else if (Data.Blocks[i].Limit[k].Cource > 0) {
                                     ObjectManager.CreateStaticObject(LimitPostRight, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, false);
                                 } else {
                                     ObjectManager.CreateStaticObject(LimitPostStraight, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, false);
                                 }
                                 double lim = Data.Blocks[i].Limit[k].Speed / Data.UnitOfSpeed;
                                 if (lim < 10.0) {
                                     int d0 = (int)Math.Round(lim);
                                     int o = ObjectManager.CreateStaticObject(LimitOneDigit, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, true);
                                     if (ObjectManager.Objects[o].Mesh.Materials.Length >= 1) {
                                         Textures.RegisterTexture(OpenBveApi.Path.CombineFile(LimitGraphicsPath, "limit_" + d0 + ".png"), out ObjectManager.Objects[o].Mesh.Materials[0].DaytimeTexture);
                                     }
                                 } else if (lim < 100.0) {
                                     int d1 = (int)Math.Round(lim);
                                     int d0 = d1 % 10;
                                     d1 /= 10;
                                     int o = ObjectManager.CreateStaticObject(LimitTwoDigits, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, true);
                                     if (ObjectManager.Objects[o].Mesh.Materials.Length >= 1) {
                                         Textures.RegisterTexture(OpenBveApi.Path.CombineFile(LimitGraphicsPath, "limit_" + d1 + ".png"), out ObjectManager.Objects[o].Mesh.Materials[0].DaytimeTexture);
                                     }
                                     if (ObjectManager.Objects[o].Mesh.Materials.Length >= 2) {
                                         Textures.RegisterTexture(OpenBveApi.Path.CombineFile(LimitGraphicsPath, "limit_" + d0 + ".png"), out ObjectManager.Objects[o].Mesh.Materials[1].DaytimeTexture);
                                     }
                                 } else {
                                     int d2 = (int)Math.Round(lim);
                                     int d0 = d2 % 10;
                                     int d1 = (d2 / 10) % 10;
                                     d2 /= 100;
                                     int o = ObjectManager.CreateStaticObject(LimitThreeDigits, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, true);
                                     if (ObjectManager.Objects[o].Mesh.Materials.Length >= 1) {
                                         Textures.RegisterTexture(OpenBveApi.Path.CombineFile(LimitGraphicsPath, "limit_" + d2 + ".png"), out ObjectManager.Objects[o].Mesh.Materials[0].DaytimeTexture);
                                     }
                                     if (ObjectManager.Objects[o].Mesh.Materials.Length >= 2) {
                                         Textures.RegisterTexture(OpenBveApi.Path.CombineFile(LimitGraphicsPath, "limit_" + d1 + ".png"), out ObjectManager.Objects[o].Mesh.Materials[1].DaytimeTexture);
                                     }
                                     if (ObjectManager.Objects[o].Mesh.Materials.Length >= 3) {
                                         Textures.RegisterTexture(OpenBveApi.Path.CombineFile(LimitGraphicsPath, "limit_" + d0 + ".png"), out ObjectManager.Objects[o].Mesh.Materials[2].DaytimeTexture);
                                     }
                                 }
                             }
                         }
                     }
                 }
                 // stop
                 if (j == 0) {
                     for (int k = 0; k < Data.Blocks[i].Stop.Length; k++) {
                         if (Data.Blocks[i].Stop[k].Direction != 0) {
                             double dx = 1.8 * (double)Data.Blocks[i].Stop[k].Direction;
                             double dz = Data.Blocks[i].Stop[k].TrackPosition - StartingDistance;
                             Vector3 wpos = pos;
                             wpos.X += dx * RailTransformation.X.X + dz * RailTransformation.Z.X;
                             wpos.Y += dx * RailTransformation.X.Y + dz * RailTransformation.Z.Y;
                             wpos.Z += dx * RailTransformation.X.Z + dz * RailTransformation.Z.Z;
                             double tpos = Data.Blocks[i].Stop[k].TrackPosition;
                             double b = 0.25 + 0.75 * GetBrightness(ref Data, tpos);
                             ObjectManager.CreateStaticObject(StopPost, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, false);
                         }
                     }
                 }
             }
         }
         // finalize block
         Position.X += Direction.X * c;
         Position.Y += h;
         Position.Z += Direction.Y * c;
         if (a != 0.0) {
             World.Rotate(ref Direction, Math.Cos(-a), Math.Sin(-a));
         }
     }
     // orphaned transponders
     if (!PreviewOnly) {
         for (int i = Data.FirstUsedBlock; i < Data.Blocks.Length; i++) {
             for (int j = 0; j < Data.Blocks[i].Transponder.Length; j++) {
                 if (Data.Blocks[i].Transponder[j].Type != -1) {
                     int n = i - Data.FirstUsedBlock;
                     int m = TrackManager.CurrentTrack.Elements[n].Events.Length;
                     Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1);
                     double d = Data.Blocks[i].Transponder[j].TrackPosition - TrackManager.CurrentTrack.Elements[n].StartingTrackPosition;
                     int s = Data.Blocks[i].Transponder[j].Section;
                     if (s >= 0) s = -1;
                     TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.TransponderEvent(d, Data.Blocks[i].Transponder[j].Type, Data.Blocks[i].Transponder[j].Data, s);
                     Data.Blocks[i].Transponder[j].Type = -1;
                 }
             }
         }
     }
     // insert station end events
     for (int i = 0; i < Game.Stations.Length; i++) {
         int j = Game.Stations[i].Stops.Length - 1;
         if (j >= 0) {
             double p = Game.Stations[i].Stops[j].TrackPosition + Game.Stations[i].Stops[j].ForwardTolerance + Data.BlockInterval;
             int k = (int)Math.Floor(p / (double)Data.BlockInterval) - Data.FirstUsedBlock;
             if (k >= 0 & k < Data.Blocks.Length) {
                 double d = p - (double)(k + Data.FirstUsedBlock) * (double)Data.BlockInterval;
                 int m = TrackManager.CurrentTrack.Elements[k].Events.Length;
                 Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[k].Events, m + 1);
                 TrackManager.CurrentTrack.Elements[k].Events[m] = new TrackManager.StationEndEvent(d, i);
             }
         }
     }
     // create default point of interests
     if (Game.PointsOfInterest.Length == 0) {
         Game.PointsOfInterest = new OpenBve.Game.PointOfInterest[Game.Stations.Length];
         int n = 0;
         for (int i = 0; i < Game.Stations.Length; i++) {
             if (Game.Stations[i].Stops.Length != 0) {
                 Game.PointsOfInterest[n].Text = Game.Stations[i].Name;
                 Game.PointsOfInterest[n].TrackPosition = Game.Stations[i].Stops[0].TrackPosition;
                 Game.PointsOfInterest[n].TrackOffset = new Vector3(0.0, 2.8, 0.0);
                 if (Game.Stations[i].OpenLeftDoors & !Game.Stations[i].OpenRightDoors) {
                     Game.PointsOfInterest[n].TrackOffset.X = -2.5;
                 } else if (!Game.Stations[i].OpenLeftDoors & Game.Stations[i].OpenRightDoors) {
                     Game.PointsOfInterest[n].TrackOffset.X = 2.5;
                 }
                 n++;
             }
         }
         Array.Resize<Game.PointOfInterest>(ref Game.PointsOfInterest, n);
     }
     // convert block-based cant into point-based cant
     for (int i = CurrentTrackLength - 1; i >= 1; i--) {
         if (TrackManager.CurrentTrack.Elements[i].CurveCant == 0.0) {
             TrackManager.CurrentTrack.Elements[i].CurveCant = TrackManager.CurrentTrack.Elements[i - 1].CurveCant;
         } else if (TrackManager.CurrentTrack.Elements[i - 1].CurveCant != 0.0) {
             if (Math.Sign(TrackManager.CurrentTrack.Elements[i - 1].CurveCant) == Math.Sign(TrackManager.CurrentTrack.Elements[i].CurveCant)) {
                 if (Math.Abs(TrackManager.CurrentTrack.Elements[i - 1].CurveCant) > Math.Abs(TrackManager.CurrentTrack.Elements[i].CurveCant)) {
                     TrackManager.CurrentTrack.Elements[i].CurveCant = TrackManager.CurrentTrack.Elements[i - 1].CurveCant;
                 }
             } else {
                 TrackManager.CurrentTrack.Elements[i].CurveCant = 0.5 * (TrackManager.CurrentTrack.Elements[i].CurveCant + TrackManager.CurrentTrack.Elements[i - 1].CurveCant);
             }
         }
     }
     // finalize
     Array.Resize<TrackManager.TrackElement>(ref TrackManager.CurrentTrack.Elements, CurrentTrackLength);
     for (int i = 0; i < Game.Stations.Length; i++) {
         if (Game.Stations[i].Stops.Length == 0 & Game.Stations[i].StopMode != Game.StationStopMode.AllPass) {
             Interface.AddMessage(Interface.MessageType.Warning, false, "Station " + Game.Stations[i].Name + " expects trains to stop but does not define stop points at track position " + Game.Stations[i].DefaultTrackPosition.ToString(Culture) + " in file " + FileName);
             Game.Stations[i].StopMode = Game.StationStopMode.AllPass;
         }
         if (Game.Stations[i].StationType == Game.StationType.ChangeEnds) {
             if (i < Game.Stations.Length - 1) {
                 if (Game.Stations[i + 1].StopMode != Game.StationStopMode.AllStop) {
                     Interface.AddMessage(Interface.MessageType.Warning, false, "Station " + Game.Stations[i].Name + " is marked as \"change ends\" but the subsequent station does not expect all trains to stop in file " + FileName);
                     Game.Stations[i + 1].StopMode = Game.StationStopMode.AllStop;
                 }
             } else {
                 Interface.AddMessage(Interface.MessageType.Warning, false, "Station " + Game.Stations[i].Name + " is marked as \"change ends\" but there is no subsequent station defined in file " + FileName);
                 Game.Stations[i].StationType = Game.StationType.Terminal;
             }
         }
     }
     if (Game.Stations.Length != 0) {
         Game.Stations[Game.Stations.Length - 1].StationType = Game.StationType.Terminal;
     }
     if (TrackManager.CurrentTrack.Elements.Length != 0) {
         int n = TrackManager.CurrentTrack.Elements.Length - 1;
         int m = TrackManager.CurrentTrack.Elements[n].Events.Length;
         Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1);
         TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.TrackEndEvent(Data.BlockInterval);
     }
     // insert compatibility beacons
     if (!PreviewOnly) {
         List<TrackManager.TransponderEvent> transponders = new List<TrackManager.TransponderEvent>();
         bool atc = false;
         for (int i = 0; i < TrackManager.CurrentTrack.Elements.Length; i++) {
             for (int j = 0; j < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) {
                 if (!atc) {
                     if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) {
                         TrackManager.StationStartEvent station = (TrackManager.StationStartEvent)TrackManager.CurrentTrack.Elements[i].Events[j];
                         if (Game.Stations[station.StationIndex].SafetySystem == Game.SafetySystem.Atc) {
                             Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[i].Events, TrackManager.CurrentTrack.Elements[i].Events.Length + 2);
                             TrackManager.CurrentTrack.Elements[i].Events[TrackManager.CurrentTrack.Elements[i].Events.Length - 2] = new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcTrackStatus, 0, 0);
                             TrackManager.CurrentTrack.Elements[i].Events[TrackManager.CurrentTrack.Elements[i].Events.Length - 1] = new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcTrackStatus, 1, 0);
                             atc = true;
                         }
                     }
                 } else {
                     if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) {
                         TrackManager.StationStartEvent station = (TrackManager.StationStartEvent)TrackManager.CurrentTrack.Elements[i].Events[j];
                         if (Game.Stations[station.StationIndex].SafetySystem == Game.SafetySystem.Ats) {
                             Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[i].Events, TrackManager.CurrentTrack.Elements[i].Events.Length + 2);
                             TrackManager.CurrentTrack.Elements[i].Events[TrackManager.CurrentTrack.Elements[i].Events.Length - 2] = new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcTrackStatus, 2, 0);
                             TrackManager.CurrentTrack.Elements[i].Events[TrackManager.CurrentTrack.Elements[i].Events.Length - 1] = new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcTrackStatus, 3, 0);
                         }
                     } else if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationEndEvent) {
                         TrackManager.StationEndEvent station = (TrackManager.StationEndEvent)TrackManager.CurrentTrack.Elements[i].Events[j];
                         if (Game.Stations[station.StationIndex].SafetySystem == Game.SafetySystem.Atc) {
                             Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[i].Events, TrackManager.CurrentTrack.Elements[i].Events.Length + 2);
                             TrackManager.CurrentTrack.Elements[i].Events[TrackManager.CurrentTrack.Elements[i].Events.Length - 2] = new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcTrackStatus, 1, 0);
                             TrackManager.CurrentTrack.Elements[i].Events[TrackManager.CurrentTrack.Elements[i].Events.Length - 1] = new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcTrackStatus, 2, 0);
                         } else if (Game.Stations[station.StationIndex].SafetySystem == Game.SafetySystem.Ats) {
                             Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[i].Events, TrackManager.CurrentTrack.Elements[i].Events.Length + 2);
                             TrackManager.CurrentTrack.Elements[i].Events[TrackManager.CurrentTrack.Elements[i].Events.Length - 2] = new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcTrackStatus, 3, 0);
                             TrackManager.CurrentTrack.Elements[i].Events[TrackManager.CurrentTrack.Elements[i].Events.Length - 1] = new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcTrackStatus, 0, 0);
                             atc = false;
                         }
                     } else if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.LimitChangeEvent) {
                         TrackManager.LimitChangeEvent limit = (TrackManager.LimitChangeEvent)TrackManager.CurrentTrack.Elements[i].Events[j];
                         int speed = (int)Math.Round(Math.Min(4095.0, 3.6 * limit.NextSpeedLimit));
                         int distance = Math.Min(1048575, (int)Math.Round(TrackManager.CurrentTrack.Elements[i].StartingTrackPosition + limit.TrackPositionDelta));
                         unchecked {
                             int value = (int)((uint)speed | ((uint)distance << 12));
                             transponders.Add(new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcSpeedLimit, value, 0));
                         }
                     }
                 }
                 if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.TransponderEvent) {
                     TrackManager.TransponderEvent transponder = TrackManager.CurrentTrack.Elements[i].Events[j] as TrackManager.TransponderEvent;
                     if (transponder.Type == TrackManager.SpecialTransponderTypes.InternalAtsPTemporarySpeedLimit) {
                         int speed = Math.Min(4095, transponder.Data);
                         int distance = Math.Min(1048575, (int)Math.Round(TrackManager.CurrentTrack.Elements[i].StartingTrackPosition + transponder.TrackPositionDelta));
                         unchecked {
                             int value = (int)((uint)speed | ((uint)distance << 12));
                             transponder.DontTriggerAnymore = true;
                         }
                     }
                 }
             }
         }
         int n = TrackManager.CurrentTrack.Elements[0].Events.Length;
         Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[0].Events, n + transponders.Count);
         for (int i = 0; i < transponders.Count; i++) {
             TrackManager.CurrentTrack.Elements[0].Events[n + i] = transponders[i];
         }
     }
     // cant
     if (!PreviewOnly) {
         ComputeCantTangents();
         int subdivisions = (int)Math.Floor(Data.BlockInterval / 5.0);
         if (subdivisions >= 2) {
             SmoothenOutTurns(subdivisions);
             ComputeCantTangents();
         }
     }
 }
		// parse animated object config
		/// <summary>Loads a collection of animated objects from a file.</summary>
		/// <param name="FileName">The text file to load the animated object from. Must be an absolute file name.</param>
		/// <param name="Encoding">The encoding the file is saved in. If the file uses a byte order mark, the encoding indicated by the byte order mark is used and the Encoding parameter is ignored.</param>
		/// <param name="LoadMode">The texture load mode.</param>
		/// <returns>The collection of animated objects.</returns>
		internal static ObjectManager.AnimatedObjectCollection ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode) {
			ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection();
			Result.Objects = new ObjectManager.AnimatedObject[4];
			int ObjectCount = 0;
			// load file
			string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding);
			bool rpnUsed = false;
			for (int i = 0; i < Lines.Length; i++) {
				int j = Lines[i].IndexOf(';');
				// cut comments out
				Lines[i] = j >= 0 ? Lines[i].Substring(0, j).Trim() : Lines[i].Trim();
				rpnUsed = Lines[i].IndexOf("functionrpn", StringComparison.OrdinalIgnoreCase) >= 0;
			}
			if (rpnUsed) {
				Debug.AddMessage(Debug.MessageType.Error, false, "An animated object file contains RPN functions. These were never meant to be used directly, only for debugging. They won't be supported indefinately. Please get rid of them in file " + FileName);
			}
			for (int i = 0; i < Lines.Length; i++) {
				if (Lines[i].Length != 0) {
					switch (Lines[i].ToLowerInvariant()) {
						case "[include]":
							{
								i++;
								Vector3D position = new Vector3D(0.0, 0.0, 0.0);
								ObjectManager.UnifiedObject[] obj = new ObjectManager.UnifiedObject[4];
								int objCount = 0;
								while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) && Lines[i].EndsWith("]", StringComparison.Ordinal))) {
									if (Lines[i].Length != 0) {
										int equals = Lines[i].IndexOf("=", StringComparison.Ordinal);
										if (equals > 0) {
											/*
											 * Process key-value pair, the only supported key is position.
											 */
											string before = Lines[i].Substring(0, equals).TrimEnd();
											string after = Lines[i].Substring(equals + 1).TrimStart();
											switch (before.ToLowerInvariant()) {
												case "position":
													ParsePosition(after, ref position, before, i + 1, FileName);
													break;
												default:
													Debug.AddMessage(Debug.MessageType.Error, false, "The attribute " + before + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													break;
											}
										} else {
											/*
											 * Process object with file name relative to the location of this ANIMATED file.
											 */
											string Folder = System.IO.Path.GetDirectoryName(FileName);
											if (Path.ContainsInvalidPathChars(Lines[i])) {
												Debug.AddMessage(Debug.MessageType.Error, false, Lines[i] + " contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName);
											} else {
												string file = OpenBveApi.Path.CombineFile(Folder, Lines[i]);
												if (System.IO.File.Exists(file)) {
													if (obj.Length == objCount) {
														Array.Resize<ObjectManager.UnifiedObject>(ref obj, obj.Length << 1);
													}
													obj[objCount] = ObjectManager.LoadObject(file, Encoding, LoadMode, false, false, false);
													objCount++;
												} else {
													Debug.AddMessage(Debug.MessageType.Error, true, "File " + file + " not found at line " + (i + 1).ToString(Culture) + " in file " + FileName);
												}
											}
										}
									}
									i++;
								}
								i--;
								for (int j = 0; j < objCount; j++) {
									if (obj[j] != null) {
										if (obj[j] is ObjectManager.StaticObject) {
											ObjectManager.StaticObject s = (ObjectManager.StaticObject)obj[j];
											s.Dynamic = true;
											if (ObjectCount >= Result.Objects.Length) {
												Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1);
											}
											ObjectManager.AnimatedObject a = new ObjectManager.AnimatedObject();
											ObjectManager.AnimatedObjectState aos = new ObjectManager.AnimatedObjectState();
											aos.Object = s;
											aos.Position = position;
											a.States = new[] { aos };
											Result.Objects[ObjectCount] = a;
											ObjectCount++;
										} else if (obj[j] is ObjectManager.AnimatedObjectCollection) {
											ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)obj[j];
											for (int k = 0; k < a.Objects.Length; k++) {
												if (ObjectCount >= Result.Objects.Length) {
													Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1);
												}
												for (int h = 0; h < a.Objects[k].States.Length; h++) {
													a.Objects[k].States[h].Position.X += position.X;
													a.Objects[k].States[h].Position.Y += position.Y;
													a.Objects[k].States[h].Position.Z += position.Z;
												}
												Result.Objects[ObjectCount] = a.Objects[k];
												ObjectCount++;
											}
										}
									}
								}
							}
							break;
						case "[object]":
							{
								i++;
								if (Result.Objects.Length == ObjectCount) {
									Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1);
								}
								Result.Objects[ObjectCount] = new ObjectManager.AnimatedObject();
								Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[] { };
								Result.Objects[ObjectCount].CurrentState = -1;
								Result.Objects[ObjectCount].TranslateXDirection = new Vector3D(1.0, 0.0, 0.0);
								Result.Objects[ObjectCount].TranslateYDirection = new Vector3D(0.0, 1.0, 0.0);
								Result.Objects[ObjectCount].TranslateZDirection = new Vector3D(0.0, 0.0, 1.0);
								Result.Objects[ObjectCount].RotateXDirection = new Vector3D(1.0, 0.0, 0.0);
								Result.Objects[ObjectCount].RotateYDirection = new Vector3D(0.0, 1.0, 0.0);
								Result.Objects[ObjectCount].RotateZDirection = new Vector3D(0.0, 0.0, 1.0);
								Result.Objects[ObjectCount].TextureShiftXDirection = new Vector2D(1.0, 0.0);
								Result.Objects[ObjectCount].TextureShiftYDirection = new Vector2D(0.0, 1.0);
								Result.Objects[ObjectCount].RefreshRate = 0.0;
								Result.Objects[ObjectCount].ObjectIndex = -1;
								Vector3D Position = new Vector3D(0.0, 0.0, 0.0);
								bool timetableUsed = false;
								string[] StateFiles = null;
								string StateFunctionRpn = null;
								int StateFunctionLine = -1;
								while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) && Lines[i].EndsWith("]", StringComparison.Ordinal))) {
									if (Lines[i].Length != 0) {
										int equals = Lines[i].IndexOf("=", StringComparison.Ordinal);
										if (equals > 0) {
											string before = Lines[i].Substring(0, equals).TrimEnd();
											string after = Lines[i].Substring(equals + 1).TrimStart();
											switch (before.ToLowerInvariant()) {
												case "position":
													ParsePosition(after, ref Position, before, i + 1, FileName);
													break;
												case "states":
													if (!ParseState(after, ref StateFiles, before, i + 1, FileName))
														return null;
													break;
												case "statefunction":
													try {
														StateFunctionLine = i;
														StateFunctionRpn = FunctionScripts.GetPostfixNotationFromInfixNotation(after);
													} catch (Exception ex) {
														Debug.AddMessage(Debug.MessageType.Error, false, ex.Message + " in " + before + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													} break;
												case "statefunctionrpn":
													{
														StateFunctionLine = i;
														StateFunctionRpn = after;
													} break;
												case "translatexdirection":
													ParseTranslateDirection(after, ref Result.Objects[ObjectCount].TranslateXDirection,
														before, i + 1, FileName);
													break;
												case "translateydirection":
													ParseTranslateDirection(after, ref Result.Objects[ObjectCount].TranslateYDirection,
														before, i + 1, FileName);
													break;
												case "translatezdirection":
													ParseTranslateDirection(after, ref Result.Objects[ObjectCount].TranslateZDirection,
														before, i + 1, FileName);
													break;
												case "translatexfunction":
													ParseInfixFunc(after, ref Result.Objects[ObjectCount].TranslateXFunction, before, i + 1, FileName);
													break;
												case "translateyfunction":
													ParseInfixFunc(after, ref Result.Objects[ObjectCount].TranslateYFunction, before, i + 1, FileName);
													break;
												case "translatezfunction":
													ParseInfixFunc(after, ref Result.Objects[ObjectCount].TranslateZFunction, before, i + 1, FileName);
													break;
												case "translatexfunctionrpn":
													ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TranslateXFunction, before, i + 1, FileName);
													break;
												case "translateyfunctionrpn":
													ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TranslateYFunction, before, i + 1, FileName);
													break;
												case "translatezfunctionrpn":
													ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TranslateZFunction, before, i + 1, FileName);
													break;
												case "rotatexdirection":
													ParseRotateDirection(after, ref Result.Objects[ObjectCount].RotateXDirection, before, i + 1, FileName);
													break;
												case "rotateydirection":
													ParseRotateDirection(after, ref Result.Objects[ObjectCount].RotateYDirection, before, i + 1, FileName);
													break;
												case "rotatezdirection":
													ParseRotateDirection(after, ref Result.Objects[ObjectCount].RotateZDirection, before, i + 1, FileName);
													break;
												case "rotatexfunction":
													ParseInfixFunc(after,ref Result.Objects[ObjectCount].RotateXFunction, before,i+1,FileName);
													break;
												case "rotateyfunction":
													ParseInfixFunc(after,ref Result.Objects[ObjectCount].RotateYFunction, before,i+1,FileName);
													break;
												case "rotatezfunction":
													ParseInfixFunc(after,ref Result.Objects[ObjectCount].RotateZFunction, before,i+1,FileName);
													break;
												case "rotatexfunctionrpn":
													ParsePostfixFunc(after,ref Result.Objects[ObjectCount].RotateXFunction, before,i+1,FileName);
													break;
												case "rotateyfunctionrpn":
													ParsePostfixFunc(after,ref Result.Objects[ObjectCount].RotateYFunction, before,i+1,FileName);
													break;
												case "rotatezfunctionrpn":
													ParsePostfixFunc(after,ref Result.Objects[ObjectCount].RotateZFunction, before,i+1,FileName);
													break;
												case "rotatexdamping":
													ParseRotateDamping(after, ref Result.Objects[ObjectCount].RotateXDamping,before, i + 1, FileName);
													break;
												case "rotateydamping":
													ParseRotateDamping(after, ref Result.Objects[ObjectCount].RotateYDamping,before, i + 1, FileName);
													break;
												case "rotatezdamping":
													ParseRotateDamping(after, ref Result.Objects[ObjectCount].RotateZDamping,before, i + 1, FileName);
													break;
												case "textureshiftxdirection":
													ParseTextureShift(after, ref Result.Objects[ObjectCount].TextureShiftXDirection, before, i + 1, FileName);
													break;
												case "textureshiftydirection":
													ParseTextureShift(after, ref Result.Objects[ObjectCount].TextureShiftYDirection, before, i + 1, FileName);
													break;
												case "textureshiftxfunction":
													ParseInfixFunc(after, ref Result.Objects[ObjectCount].TextureShiftXFunction, before, i + 1, FileName);
													break;
												case "textureshiftyfunction":
													ParseInfixFunc(after, ref Result.Objects[ObjectCount].TextureShiftYFunction, before, i + 1, FileName);
													break;
												case "textureshiftxfunctionrpn":
													ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TextureShiftXFunction, before, i + 1, FileName);
													break;
												case "textureshiftyfunctionrpn":
													ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TextureShiftYFunction, before, i + 1, FileName);
													break;
												case "textureoverride":
													switch (after.ToLowerInvariant()) {
														case "none":
															break;
														case "timetable":
															if (!timetableUsed) {
																Timetable.AddObjectForCustomTimetable(Result.Objects[ObjectCount]);
																timetableUsed = true;
															}
															break;
														default:
															Debug.AddMessage(Debug.MessageType.Error, false, "Unrecognized value in " + before + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
															break;
													}
													break;
												case "refreshrate":
													{
														double r;
														if (!double.TryParse(after, System.Globalization.NumberStyles.Float, Culture, out r)) {
															Debug.AddMessage(Debug.MessageType.Error, false, "Value is invalid in " + before + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
														} else if (r < 0.0) {
															Debug.AddMessage(Debug.MessageType.Error, false, "Value is expected to be non-negative in " + before + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
														} else {
															Result.Objects[ObjectCount].RefreshRate = r;
														}
													} break;
												default:
													Debug.AddMessage(Debug.MessageType.Error, false, "The attribute " + before + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													break;
											}
										} else {
											Debug.AddMessage(Debug.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
											return null;
										}
									}
									i++;
								}
								i--;
								if (StateFiles != null) {
									// create the object
									if (timetableUsed) {
										if (StateFunctionRpn != null) {
											StateFunctionRpn = "timetable 0 == " + StateFunctionRpn + " -1 ?";
										} else {
											StateFunctionRpn = "timetable";
										}
									}
									if (StateFunctionRpn != null) {
										try {
											Result.Objects[ObjectCount].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(StateFunctionRpn);
										} catch (Exception ex) {
											Debug.AddMessage(Debug.MessageType.Error, false, ex.Message + " in StateFunction at line " + (StateFunctionLine + 1).ToString(Culture) + " in file " + FileName);
										}
									}
									Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[StateFiles.Length];
									bool ForceTextureRepeatX = Result.Objects[ObjectCount].TextureShiftXFunction != null && Result.Objects[ObjectCount].TextureShiftXDirection.X != 0.0 ||
										Result.Objects[ObjectCount].TextureShiftYFunction != null && Result.Objects[ObjectCount].TextureShiftYDirection.Y != 0.0;
									bool ForceTextureRepeatY = Result.Objects[ObjectCount].TextureShiftXFunction != null && Result.Objects[ObjectCount].TextureShiftXDirection.X != 0.0 ||
										Result.Objects[ObjectCount].TextureShiftYFunction != null && Result.Objects[ObjectCount].TextureShiftYDirection.Y != 0.0;
									for (int k = 0; k < StateFiles.Length; k++) {
										Result.Objects[ObjectCount].States[k].Position = new Vector3D(0.0, 0.0, 0.0);
										if (StateFiles[k] != null) {
											Result.Objects[ObjectCount].States[k].Object = ObjectManager.LoadStaticObject(StateFiles[k], Encoding, LoadMode, false, ForceTextureRepeatX, ForceTextureRepeatY);
											if (Result.Objects[ObjectCount].States[k].Object != null) {
												Result.Objects[ObjectCount].States[k].Object.Dynamic = true;
											}
										} else {
											Result.Objects[ObjectCount].States[k].Object = null;
										}
										for (int j = 0; j < Result.Objects[ObjectCount].States.Length; j++) {
											Result.Objects[ObjectCount].States[j].Position = Position;
										}
									}
								} else {
									Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[] { };
								}
								ObjectCount++;
							}
							break;
						default:
							Debug.AddMessage(Debug.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
							return null;
					}
				}
			}
			Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, ObjectCount);
			return Result;
		}
Example #7
0
        /// <summary>Loads a Loksim3D GruppenObject</summary>
        /// <param name="FileName">The filename to load</param>
        /// <param name="Encoding">The text encoding of the containing file (Currently ignored, REMOVE??)</param>
        /// <param name="Rotation">A three-dimemsional vector describing the rotation to be applied</param>
        /// <returns>A new animated object collection, containing the GruppenObject's meshes etc.</returns>
        internal static ObjectManager.AnimatedObjectCollection ReadObject(string FileName, Encoding Encoding, Vector3 Rotation)
        {
            XmlDocument currentXML = new XmlDocument();

            ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection();
            Result.Objects = new ObjectManager.AnimatedObject[0];
            try
            {
                currentXML.Load(FileName);
            }
            catch (Exception ex)
            {
                //The XML is not strictly valid
                string[] Lines = File.ReadAllLines(FileName);
                using (var stringReader = new StringReader(Lines[0]))
                {
                    var settings = new XmlReaderSettings {
                        ConformanceLevel = ConformanceLevel.Fragment
                    };
                    using (var xmlReader = XmlReader.Create(stringReader, settings))
                    {
                        if (xmlReader.Read())
                        {
                            //Attempt to find the text encoding and re-read the file
                            var result = xmlReader.GetAttribute("encoding");
                            if (result != null)
                            {
                                var e = System.Text.Encoding.GetEncoding(result);
                                Lines = File.ReadAllLines(FileName, e);
                                //Turf out the old encoding, as our string array should now be UTF-8
                                Lines[0] = "<?xml version=\"1.0\"?>";
                            }
                        }
                    }
                }
                for (int i = 0; i < Lines.Length; i++)
                {
                    while (Lines[i].IndexOf("\"\"", StringComparison.InvariantCulture) != -1)
                    {
                        //Loksim parser tolerates multiple quotes, strict XML does not
                        Lines[i] = Lines[i].Replace("\"\"", "\"");
                    }
                    while (Lines[i].IndexOf("  ", StringComparison.InvariantCulture) != -1)
                    {
                        //Replace double-spaces with singles
                        Lines[i] = Lines[i].Replace("  ", " ");
                    }
                }
                bool tryLoad = false;
                try
                {
                    //Horrible hack: Write out our string array to a new memory stream, then load from this stream
                    //Why can't XmlDocument.Load() just take a string array......
                    using (var stream = new MemoryStream())
                    {
                        var sw = new StreamWriter(stream);
                        foreach (var line in Lines)
                        {
                            sw.Write(line);
                            sw.Flush();
                        }
                        sw.Flush();
                        stream.Position = 0;
                        currentXML.Load(stream);
                        tryLoad = true;
                    }
                }
                catch
                {
                    //Generic catch-all clause
                }
                if (!tryLoad)
                {
                    //Pass out the *original* XML error, not anything generated when we've tried to correct it
                    Interface.AddMessage(MessageType.Error, false, "Error parsing Loksim3D XML: " + ex.Message);
                    return(null);
                }
            }

            string BaseDir = System.IO.Path.GetDirectoryName(FileName);

            GruppenObject[] CurrentObjects = new GruppenObject[0];
            //Check for null
            if (currentXML.DocumentElement != null)
            {
                UnifiedObject[] obj           = new UnifiedObject[0];
                XmlNodeList     DocumentNodes = currentXML.DocumentElement.SelectNodes("/GRUPPENOBJECT");
                if (DocumentNodes != null)
                {
                    foreach (XmlNode outerNode in DocumentNodes)
                    {
                        if (outerNode.ChildNodes.OfType <XmlElement>().Any())
                        {
                            foreach (XmlNode node in outerNode.ChildNodes)
                            {
                                if (node.Name == "Object" && node.ChildNodes.OfType <XmlElement>().Any())
                                {
                                    foreach (XmlNode childNode in node.ChildNodes)
                                    {
                                        if (childNode.Name == "Props" && childNode.Attributes != null)
                                        {
                                            GruppenObject Object = new GruppenObject
                                            {
                                                Rotation = Rotation
                                            };
                                            foreach (XmlAttribute attribute in childNode.Attributes)
                                            {
                                                switch (attribute.Name)
                                                {
                                                case "Name":
                                                    string ObjectFile = OpenBveApi.Path.Loksim3D.CombineFile(BaseDir, attribute.Value, Program.FileSystem.LoksimPackageInstallationDirectory);
                                                    if (!File.Exists(ObjectFile))
                                                    {
                                                        Object.Name = null;
                                                        Interface.AddMessage(MessageType.Warning, true, "Ls3d Object file " + attribute.Value + " not found.");
                                                    }
                                                    else
                                                    {
                                                        Object.Name = ObjectFile;
                                                    }
                                                    break;

                                                case "Position":
                                                    string[] SplitPosition = attribute.Value.Split(';');
                                                    double.TryParse(SplitPosition[0], out Object.Position.X);
                                                    double.TryParse(SplitPosition[1], out Object.Position.Y);
                                                    double.TryParse(SplitPosition[2], out Object.Position.Z);
                                                    break;

                                                case "Rotation":
                                                    string[] SplitRotation = attribute.Value.Split(';');
                                                    Vector3  r;
                                                    double.TryParse(SplitRotation[0], out r.X);
                                                    double.TryParse(SplitRotation[1], out r.Y);
                                                    double.TryParse(SplitRotation[2], out r.Z);
                                                    Object.Rotation += r;
                                                    break;

                                                case "ShowOn":
                                                    //Defines when the object should be shown
                                                    Object.FunctionScript = FunctionScriptNotation.GetPostfixNotationFromInfixNotation(GetAnimatedFunction(attribute.Value, false));
                                                    break;

                                                case "HideOn":
                                                    //Defines when the object should be hidden
                                                    Object.FunctionScript = FunctionScriptNotation.GetPostfixNotationFromInfixNotation(GetAnimatedFunction(attribute.Value, true));
                                                    break;

                                                case "FixedDynamicVisibility":
                                                    if (attribute.Value.ToLowerInvariant() == "true")
                                                    {
                                                        Object.FixedDynamicVisibility = true;
                                                    }
                                                    else
                                                    {
                                                        Object.FixedDynamicVisibility = false;
                                                    }
                                                    break;

                                                case "DynamicVisibility":
                                                    if (Object.FixedDynamicVisibility)
                                                    {
                                                        Object.FunctionScript = FunctionScriptNotation.GetPostfixNotationFromInfixNotation(GetDynamicFunction(attribute.Value));
                                                    }
                                                    break;
                                                }
                                            }
                                            if (Object.Name != null)
                                            {
                                                Array.Resize <GruppenObject>(ref CurrentObjects, CurrentObjects.Length + 1);
                                                CurrentObjects[CurrentObjects.Length - 1] = Object;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    //We've loaded the XML references, now load the objects into memory

                    //Single mesh object, containing all static components of the LS3D object
                    //If we use multiples, the Z-sorting throws a wobbly
                    StaticObject staticObject = null;
                    for (int i = 0; i < CurrentObjects.Length; i++)
                    {
                        if (CurrentObjects[i] == null || string.IsNullOrEmpty(CurrentObjects[i].Name))
                        {
                            continue;
                        }
                        StaticObject Object = null;
                        ObjectManager.AnimatedObjectCollection AnimatedObject = null;
                        try
                        {
                            if (CurrentObjects[i].Name.ToLowerInvariant().EndsWith(".l3dgrp"))
                            {
                                AnimatedObject = ReadObject(CurrentObjects[i].Name, Encoding, CurrentObjects[i].Rotation);
                            }
                            else if (CurrentObjects[i].Name.ToLowerInvariant().EndsWith(".l3dobj"))
                            {
                                Object = (StaticObject)ObjectManager.LoadObject(CurrentObjects[i].Name, Encoding, false, false, false, CurrentObjects[i].Rotation);
                            }
                            else
                            {
                                throw new Exception("Format " + System.IO.Path.GetExtension(CurrentObjects[i].Name) + " is not currently supported by the Loksim3D object parser");
                            }
                        }
                        catch (Exception ex) {
                            Interface.AddMessage(MessageType.Error, false, ex.Message);
                        }
                        if (Object != null)
                        {
                            if (!string.IsNullOrEmpty(CurrentObjects[i].FunctionScript))
                            {
                                //If the function script is not empty, this is a new animated object bit
                                Array.Resize <UnifiedObject>(ref obj, obj.Length + 1);
                                obj[obj.Length - 1] = Object;
                                int aL = Result.Objects.Length;
                                Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, aL + 1);
                                ObjectManager.AnimatedObject a   = new ObjectManager.AnimatedObject();
                                AnimatedObjectState          aos = new AnimatedObjectState(Object, CurrentObjects[i].Position);
                                a.States           = new AnimatedObjectState[] { aos };
                                Result.Objects[aL] = a;
                                Result.Objects[aL].StateFunction = new FunctionScript(Program.CurrentHost, CurrentObjects[i].FunctionScript + " 1 == --", false);
                            }
                            else
                            {
                                //Otherwise, join to the main static mesh & update co-ords
                                for (int j = 0; j < Object.Mesh.Vertices.Length; j++)
                                {
                                    Object.Mesh.Vertices[j].Coordinates += CurrentObjects[i].Position;
                                }
                                staticObject.JoinObjects(Object);
                            }
                        }
                        else if (AnimatedObject != null)
                        {
                            int rl = Result.Objects.Length;
                            int l  = AnimatedObject.Objects.Length;
                            Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length + l);
                            for (int o = rl; o < rl + l; o++)
                            {
                                if (AnimatedObject.Objects[o - rl] != null)
                                {
                                    Result.Objects[o] = AnimatedObject.Objects[o - rl].Clone();
                                    for (int si = 0; si < Result.Objects[o].States.Length; si++)
                                    {
                                        Result.Objects[o].States[si].Position += CurrentObjects[i].Position;
                                    }
                                }
                                else
                                {
                                    Result.Objects[o]        = new ObjectManager.AnimatedObject();
                                    Result.Objects[o].States = new AnimatedObjectState[0];
                                }
                            }
                        }
                    }
                    if (staticObject != null)
                    {
                        Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length + 1);
                        ObjectManager.AnimatedObject a   = new ObjectManager.AnimatedObject();
                        AnimatedObjectState          aos = new AnimatedObjectState(staticObject, Vector3.Zero);
                        a.States = new AnimatedObjectState[] { aos };
                        Result.Objects[Result.Objects.Length - 1] = a;
                    }
                }
                return(Result);
            }
            //Didn't find an acceptable XML object
            //Probably will cause things to throw an absolute wobbly somewhere....
            return(null);
        }
        // parse animated object config
        /// <summary>Loads a collection of animated objects from a file.</summary>
        /// <param name="FileName">The text file to load the animated object from. Must be an absolute file name.</param>
        /// <param name="Encoding">The encoding the file is saved in. If the file uses a byte order mark, the encoding indicated by the byte order mark is used and the Encoding parameter is ignored.</param>
        /// <param name="LoadMode">The texture load mode.</param>
        /// <returns>The collection of animated objects.</returns>
        internal static ObjectManager.AnimatedObjectCollection ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode)
        {
            System.Globalization.CultureInfo       Culture = System.Globalization.CultureInfo.InvariantCulture;
            ObjectManager.AnimatedObjectCollection Result  = new ObjectManager.AnimatedObjectCollection
            {
                Objects = new ObjectManager.AnimatedObject[4]
            };
            int ObjectCount = 0;

            // load file
            string[] Lines   = System.IO.File.ReadAllLines(FileName, Encoding);
            bool     rpnUsed = false;

            for (int i = 0; i < Lines.Length; i++)
            {
                int j = Lines[i].IndexOf(';');
                if (j >= 0)
                {
                    Lines[i] = Lines[i].Substring(0, j).Trim();
                }
                else
                {
                    Lines[i] = Lines[i].Trim();
                }
                if (Lines[i].IndexOf("functionrpn", StringComparison.OrdinalIgnoreCase) >= 0)
                {
                    rpnUsed = true;
                }
            }
            if (rpnUsed)
            {
                Interface.AddMessage(Interface.MessageType.Error, false, "An animated object file contains RPN functions. These were never meant to be used directly, only for debugging. They won't be supported indefinately. Please get rid of them in file " + FileName);
            }
            for (int i = 0; i < Lines.Length; i++)
            {
                if (Lines[i].Length != 0)
                {
                    switch (Lines[i].ToLowerInvariant())
                    {
                    case "[include]":
                    {
                        i++;
                        Vector3 position = new Vector3(0.0, 0.0, 0.0);
                        ObjectManager.UnifiedObject[] obj = new OpenBve.ObjectManager.UnifiedObject[4];
                        int objCount = 0;
                        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();
                                    string b = Lines[i].Substring(j + 1).TrimStart();
                                    switch (a.ToLowerInvariant())
                                    {
                                    case "position":
                                    {
                                        string[] s = b.Split(',');
                                        if (s.Length == 3)
                                        {
                                            double x, y, z;
                                            if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x))
                                            {
                                                Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                            }
                                            else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y))
                                            {
                                                Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                            }
                                            else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z))
                                            {
                                                Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                            }
                                            else
                                            {
                                                position = new Vector3(x, y, z);
                                            }
                                        }
                                        else
                                        {
                                            Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        }
                                    } break;

                                    default:
                                        Interface.AddMessage(Interface.MessageType.Error, false, "The attribute " + a + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        break;
                                    }
                                }
                                else
                                {
                                    string Folder = System.IO.Path.GetDirectoryName(FileName);
                                    if (Path.ContainsInvalidChars(Lines[i]))
                                    {
                                        Interface.AddMessage(Interface.MessageType.Error, false, Lines[i] + " contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                    }
                                    else
                                    {
                                        string file = OpenBveApi.Path.CombineFile(Folder, Lines[i]);
                                        if (System.IO.File.Exists(file))
                                        {
                                            if (obj.Length == objCount)
                                            {
                                                Array.Resize <ObjectManager.UnifiedObject>(ref obj, obj.Length << 1);
                                            }
                                            obj[objCount] = ObjectManager.LoadObject(file, Encoding, LoadMode, false, false, false);
                                            objCount++;
                                        }
                                        else
                                        {
                                            Interface.AddMessage(Interface.MessageType.Error, true, "File " + file + " not found at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        }
                                    }
                                }
                            }
                            i++;
                        }
                        i--;
                        for (int j = 0; j < objCount; j++)
                        {
                            if (obj[j] != null)
                            {
                                if (obj[j] is ObjectManager.StaticObject)
                                {
                                    ObjectManager.StaticObject s = (ObjectManager.StaticObject)obj[j];
                                    s.Dynamic = true;
                                    if (ObjectCount >= Result.Objects.Length)
                                    {
                                        Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1);
                                    }
                                    ObjectManager.AnimatedObject      a   = new ObjectManager.AnimatedObject();
                                    ObjectManager.AnimatedObjectState aos = new ObjectManager.AnimatedObjectState
                                    {
                                        Object   = s,
                                        Position = position
                                    };
                                    a.States = new ObjectManager.AnimatedObjectState[] { aos };
                                    Result.Objects[ObjectCount] = a;
                                    ObjectCount++;
                                }
                                else if (obj[j] is ObjectManager.AnimatedObjectCollection)
                                {
                                    ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)obj[j];
                                    for (int k = 0; k < a.Objects.Length; k++)
                                    {
                                        if (ObjectCount >= Result.Objects.Length)
                                        {
                                            Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1);
                                        }
                                        for (int h = 0; h < a.Objects[k].States.Length; h++)
                                        {
                                            a.Objects[k].States[h].Position.X += position.X;
                                            a.Objects[k].States[h].Position.Y += position.Y;
                                            a.Objects[k].States[h].Position.Z += position.Z;
                                        }
                                        Result.Objects[ObjectCount] = a.Objects[k];
                                        ObjectCount++;
                                    }
                                }
                            }
                        }
                    }
                    break;

                    case "[object]":
                    {
                        i++;
                        if (Result.Objects.Length == ObjectCount)
                        {
                            Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1);
                        }
                        Result.Objects[ObjectCount] = new ObjectManager.AnimatedObject
                        {
                            States                 = new ObjectManager.AnimatedObjectState[] {},
                            CurrentState           = -1,
                            TranslateXDirection    = new Vector3(1.0, 0.0, 0.0),
                            TranslateYDirection    = new Vector3(0.0, 1.0, 0.0),
                            TranslateZDirection    = new Vector3(0.0, 0.0, 1.0),
                            RotateXDirection       = new Vector3(1.0, 0.0, 0.0),
                            RotateYDirection       = new Vector3(0.0, 1.0, 0.0),
                            RotateZDirection       = new Vector3(0.0, 0.0, 1.0),
                            TextureShiftXDirection = new Vector2(1.0, 0.0),
                            TextureShiftYDirection = new Vector2(0.0, 1.0),
                            RefreshRate            = 0.0,
                            ObjectIndex            = -1
                        };
                        Vector3  Position          = new Vector3(0.0, 0.0, 0.0);
                        bool     timetableUsed     = false;
                        string[] StateFiles        = null;
                        string   StateFunctionRpn  = null;
                        int      StateFunctionLine = -1;
                        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();
                                    string b = Lines[i].Substring(j + 1).TrimStart();
                                    switch (a.ToLowerInvariant())
                                    {
                                    case "position":
                                    {
                                        string[] s = b.Split(',');
                                        if (s.Length == 3)
                                        {
                                            double x, y, z;
                                            if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x))
                                            {
                                                Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                            }
                                            else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y))
                                            {
                                                Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                            }
                                            else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z))
                                            {
                                                Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                            }
                                            else
                                            {
                                                Position = new Vector3(x, y, z);
                                            }
                                        }
                                        else
                                        {
                                            Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        }
                                    } break;

                                    case "states":
                                    {
                                        string[] s = b.Split(',');
                                        if (s.Length >= 1)
                                        {
                                            string Folder = System.IO.Path.GetDirectoryName(FileName);
                                            StateFiles = new string[s.Length];
                                            for (int k = 0; k < s.Length; k++)
                                            {
                                                s[k] = s[k].Trim();
                                                if (s[k].Length == 0)
                                                {
                                                    Interface.AddMessage(Interface.MessageType.Error, false, "File" + k.ToString(Culture) + " is an empty string - did you mean something else? - in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                    StateFiles[k] = null;
                                                }
                                                else if (Path.ContainsInvalidChars(s[k]))
                                                {
                                                    Interface.AddMessage(Interface.MessageType.Error, false, "File" + k.ToString(Culture) + " contains illegal characters in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                    StateFiles[k] = null;
                                                }
                                                else
                                                {
                                                    StateFiles[k] = OpenBveApi.Path.CombineFile(Folder, s[k]);
                                                    if (!System.IO.File.Exists(StateFiles[k]))
                                                    {
                                                        Interface.AddMessage(Interface.MessageType.Error, true, "File " + StateFiles[k] + " not found in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                        StateFiles[k] = null;
                                                    }
                                                }
                                            }
                                        }
                                        else
                                        {
                                            Interface.AddMessage(Interface.MessageType.Error, false, "At least one argument is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                            return(null);
                                        }
                                    } break;

                                    case "statefunction":
                                        try {
                                            StateFunctionLine = i;
                                            StateFunctionRpn  = FunctionScripts.GetPostfixNotationFromInfixNotation(b);
                                        } catch (Exception ex) {
                                            Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        } break;

                                    case "statefunctionrpn":
                                    {
                                        StateFunctionLine = i;
                                        StateFunctionRpn  = b;
                                    } break;

                                    case "translatexdirection":
                                    case "translateydirection":
                                    case "translatezdirection":
                                    {
                                        string[] s = b.Split(',');
                                        if (s.Length == 3)
                                        {
                                            double x, y, z;
                                            if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x))
                                            {
                                                Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                            }
                                            else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y))
                                            {
                                                Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                            }
                                            else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z))
                                            {
                                                Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                            }
                                            else
                                            {
                                                switch (a.ToLowerInvariant())
                                                {
                                                case "translatexdirection":
                                                    Result.Objects[ObjectCount].TranslateXDirection = new Vector3(x, y, z);
                                                    break;

                                                case "translateydirection":
                                                    Result.Objects[ObjectCount].TranslateYDirection = new Vector3(x, y, z);
                                                    break;

                                                case "translatezdirection":
                                                    Result.Objects[ObjectCount].TranslateZDirection = new Vector3(x, y, z);
                                                    break;
                                                }
                                            }
                                        }
                                        else
                                        {
                                            Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        }
                                    } break;

                                    case "translatexfunction":
                                        try {
                                            Result.Objects[ObjectCount].TranslateXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
                                        } catch (Exception ex) {
                                            Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        } break;

                                    case "translateyfunction":
                                        try {
                                            Result.Objects[ObjectCount].TranslateYFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
                                        } catch (Exception ex) {
                                            Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        } break;

                                    case "translatezfunction":
                                        try {
                                            Result.Objects[ObjectCount].TranslateZFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
                                        } catch (Exception ex) {
                                            Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        } break;

                                    case "translatexfunctionrpn":
                                        try {
                                            Result.Objects[ObjectCount].TranslateXFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
                                        } catch (Exception ex) {
                                            Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        } break;

                                    case "translateyfunctionrpn":
                                        try {
                                            Result.Objects[ObjectCount].TranslateYFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
                                        } catch (Exception ex) {
                                            Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        } break;

                                    case "translatezfunctionrpn":
                                        try {
                                            Result.Objects[ObjectCount].TranslateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
                                        } catch (Exception ex) {
                                            Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        } break;

                                    case "rotatexdirection":
                                    case "rotateydirection":
                                    case "rotatezdirection":
                                    {
                                        string[] s = b.Split(',');
                                        if (s.Length == 3)
                                        {
                                            double x, y, z;
                                            if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x))
                                            {
                                                Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                            }
                                            else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y))
                                            {
                                                Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                            }
                                            else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z))
                                            {
                                                Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                            }
                                            else if (x == 0.0 & y == 0.0 & z == 0.0)
                                            {
                                                Interface.AddMessage(Interface.MessageType.Error, false, "The direction indicated by X, Y and Z is expected to be non-zero in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                            }
                                            else
                                            {
                                                switch (a.ToLowerInvariant())
                                                {
                                                case "rotatexdirection":
                                                    Result.Objects[ObjectCount].RotateXDirection = new Vector3(x, y, z);
                                                    break;

                                                case "rotateydirection":
                                                    Result.Objects[ObjectCount].RotateYDirection = new Vector3(x, y, z);
                                                    break;

                                                case "rotatezdirection":
                                                    Result.Objects[ObjectCount].RotateZDirection = new Vector3(x, y, z);
                                                    break;
                                                }
                                            }
                                        }
                                        else
                                        {
                                            Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        }
                                    } break;

                                    case "rotatexfunction":
                                        try {
                                            Result.Objects[ObjectCount].RotateXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
                                        } catch (Exception ex) {
                                            Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        } break;

                                    case "rotateyfunction":
                                        try {
                                            Result.Objects[ObjectCount].RotateYFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
                                        } catch (Exception ex) {
                                            Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        } break;

                                    case "rotatezfunction":
                                        try {
                                            Result.Objects[ObjectCount].RotateZFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
                                        } catch (Exception ex) {
                                            Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        } break;

                                    case "rotatexfunctionrpn":
                                        try {
                                            Result.Objects[ObjectCount].RotateXFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
                                        } catch (Exception ex) {
                                            Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        } break;

                                    case "rotateyfunctionrpn":
                                        try {
                                            Result.Objects[ObjectCount].RotateYFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
                                        } catch (Exception ex) {
                                            Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        } break;

                                    case "rotatezfunctionrpn":
                                        try {
                                            Result.Objects[ObjectCount].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
                                        } catch (Exception ex) {
                                            Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        } break;

                                    case "rotatexdamping":
                                    case "rotateydamping":
                                    case "rotatezdamping":
                                    {
                                        string[] s = b.Split(',');
                                        if (s.Length == 2)
                                        {
                                            double nf, dr;
                                            if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out nf))
                                            {
                                                Interface.AddMessage(Interface.MessageType.Error, false, "NaturalFrequency is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                            }
                                            else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out dr))
                                            {
                                                Interface.AddMessage(Interface.MessageType.Error, false, "DampingRatio is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                            }
                                            else if (nf <= 0.0)
                                            {
                                                Interface.AddMessage(Interface.MessageType.Error, false, "NaturalFrequency is expected to be positive in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                            }
                                            else if (dr <= 0.0)
                                            {
                                                Interface.AddMessage(Interface.MessageType.Error, false, "DampingRatio is expected to be positive in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                            }
                                            else
                                            {
                                                switch (a.ToLowerInvariant())
                                                {
                                                case "rotatexdamping":
                                                    Result.Objects[ObjectCount].RotateXDamping = new ObjectManager.Damping(nf, dr);
                                                    break;

                                                case "rotateydamping":
                                                    Result.Objects[ObjectCount].RotateYDamping = new ObjectManager.Damping(nf, dr);
                                                    break;

                                                case "rotatezdamping":
                                                    Result.Objects[ObjectCount].RotateZDamping = new ObjectManager.Damping(nf, dr);
                                                    break;
                                                }
                                            }
                                        }
                                        else
                                        {
                                            Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 2 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        }
                                    } break;

                                    case "textureshiftxdirection":
                                    case "textureshiftydirection":
                                    {
                                        string[] s = b.Split(',');
                                        if (s.Length == 2)
                                        {
                                            double x, y;
                                            if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x))
                                            {
                                                Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                            }
                                            else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y))
                                            {
                                                Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                            }
                                            else
                                            {
                                                switch (a.ToLowerInvariant())
                                                {
                                                case "textureshiftxdirection":
                                                    Result.Objects[ObjectCount].TextureShiftXDirection = new Vector2(x, y);
                                                    break;

                                                case "textureshiftydirection":
                                                    Result.Objects[ObjectCount].TextureShiftYDirection = new Vector2(x, y);
                                                    break;
                                                }
                                            }
                                        }
                                        else
                                        {
                                            Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 2 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        }
                                    } break;

                                    case "textureshiftxfunction":
                                        try {
                                            Result.Objects[ObjectCount].TextureShiftXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
                                        } catch (Exception ex) {
                                            Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        } break;

                                    case "textureshiftyfunction":
                                        try {
                                            Result.Objects[ObjectCount].TextureShiftYFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
                                        } catch (Exception ex) {
                                            Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        } break;

                                    case "textureshiftxfunctionrpn":
                                        try {
                                            Result.Objects[ObjectCount].TextureShiftXFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
                                        } catch (Exception ex) {
                                            Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        } break;

                                    case "textureshiftyfunctionrpn":
                                        try {
                                            Result.Objects[ObjectCount].TextureShiftYFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
                                        } catch (Exception ex) {
                                            Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        } break;

                                    case "textureoverride":
                                        switch (b.ToLowerInvariant())
                                        {
                                        case "none":
                                            break;

                                        case "timetable":
                                            if (!timetableUsed)
                                            {
                                                Timetable.AddObjectForCustomTimetable(Result.Objects[ObjectCount]);
                                                timetableUsed = true;
                                            }
                                            break;

                                        default:
                                            Interface.AddMessage(Interface.MessageType.Error, false, "Unrecognized value in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                            break;
                                        }
                                        break;

                                    case "refreshrate":
                                    {
                                        double r;
                                        if (!double.TryParse(b, System.Globalization.NumberStyles.Float, Culture, out r))
                                        {
                                            Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        }
                                        else if (r < 0.0)
                                        {
                                            Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be non-negative in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        }
                                        else
                                        {
                                            Result.Objects[ObjectCount].RefreshRate = r;
                                        }
                                    } break;

                                    default:
                                        Interface.AddMessage(Interface.MessageType.Error, false, "The attribute " + a + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        break;
                                    }
                                }
                                else
                                {
                                    Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                    return(null);
                                }
                            }
                            i++;
                        }
                        i--;
                        if (StateFiles != null)
                        {
                            // create the object
                            if (timetableUsed)
                            {
                                if (StateFunctionRpn != null)
                                {
                                    StateFunctionRpn = "timetable 0 == " + StateFunctionRpn + " -1 ?";
                                }
                                else
                                {
                                    StateFunctionRpn = "timetable";
                                }
                            }
                            if (StateFunctionRpn != null)
                            {
                                try {
                                    Result.Objects[ObjectCount].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(StateFunctionRpn);
                                } catch (Exception ex) {
                                    Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in StateFunction at line " + (StateFunctionLine + 1).ToString(Culture) + " in file " + FileName);
                                }
                            }
                            Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[StateFiles.Length];
                            bool ForceTextureRepeatX = Result.Objects[ObjectCount].TextureShiftXFunction != null & Result.Objects[ObjectCount].TextureShiftXDirection.X != 0.0 |
                                                       Result.Objects[ObjectCount].TextureShiftYFunction != null & Result.Objects[ObjectCount].TextureShiftYDirection.X != 0.0;
                            bool ForceTextureRepeatY = Result.Objects[ObjectCount].TextureShiftXFunction != null & Result.Objects[ObjectCount].TextureShiftXDirection.Y != 0.0 |
                                                       Result.Objects[ObjectCount].TextureShiftYFunction != null & Result.Objects[ObjectCount].TextureShiftYDirection.Y != 0.0;
                            for (int k = 0; k < StateFiles.Length; k++)
                            {
                                Result.Objects[ObjectCount].States[k].Position = new Vector3(0.0, 0.0, 0.0);
                                if (StateFiles[k] != null)
                                {
                                    Result.Objects[ObjectCount].States[k].Object = ObjectManager.LoadStaticObject(StateFiles[k], Encoding, LoadMode, false, ForceTextureRepeatX, ForceTextureRepeatY);
                                    if (Result.Objects[ObjectCount].States[k].Object != null)
                                    {
                                        Result.Objects[ObjectCount].States[k].Object.Dynamic = true;
                                    }
                                }
                                else
                                {
                                    Result.Objects[ObjectCount].States[k].Object = null;
                                }
                                for (int j = 0; j < Result.Objects[ObjectCount].States.Length; j++)
                                {
                                    Result.Objects[ObjectCount].States[j].Position = Position;
                                }
                            }
                        }
                        else
                        {
                            Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[] { };
                        }
                        ObjectCount++;
                    }
                    break;

                    case "[sound]":
                    case "[statechangesound]":
                        //Only show the sound nag once per route, otherwise this could cause spam...
                        if (!Program.SoundError)
                        {
                            Interface.AddMessage(Interface.MessageType.Information, false, "Animated objects containing sounds are only supported in openBVE v1.5.2.4+");
                            Interface.AddMessage(Interface.MessageType.Information, false, "Object Viewer does not support sounds. Please use the main game to test these!");
                            Program.SoundError = true;
                        }
                        i++;
                        while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal)))
                        {
                            i++;
                        }
                        break;

                    default:
                        Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                        return(null);
                    }
                }
            }
            Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, ObjectCount);
            return(Result);
        }
Example #9
0
        /// <summary>Attempts to load and parse the current train's panel configuration file.</summary>
        /// <param name="TrainPath">The absolute on-disk path to the train folder.</param>
        /// <param name="Encoding">The automatically detected or manually set encoding of the panel configuration file.</param>
        /// <param name="Train">The base train on which to apply the panel configuration.</param>
        internal static void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, Train Train)
        {
            Train.Cars[Train.DriverCar].CarSections    = new CarSection[1];
            Train.Cars[Train.DriverCar].CarSections[0] = new CarSection
            {
                Groups = new ElementsGroup[1]
            };
            Train.Cars[Train.DriverCar].CarSections[0].Groups[0] = new ElementsGroup
            {
                Elements = new ObjectManager.AnimatedObject[] { },
                Overlay  = true
            };
            string File = OpenBveApi.Path.CombineFile(TrainPath, "panel.xml");

            if (!System.IO.File.Exists(File))
            {
                //Try animated variant too
                File = OpenBveApi.Path.CombineFile(TrainPath, "panel.animated.xml");
            }
            if (System.IO.File.Exists(File))
            {
                Program.FileSystem.AppendToLogFile("Loading train panel: " + File);
                try
                {
                    /*
                     * First load the XML. We use this to determine
                     * whether this is a 2D or a 3D animated panel
                     */
                    XDocument CurrentXML = XDocument.Load(File, LoadOptions.SetLineInfo);

                    // Check for null
                    if (CurrentXML.Root != null)
                    {
                        IEnumerable <XElement> DocumentElements = CurrentXML.Root.Elements("PanelAnimated");
                        if (DocumentElements.Any())
                        {
                            PanelAnimatedXmlParser.ParsePanelAnimatedXml(System.IO.Path.GetFileName(File), TrainPath, Train, Train.DriverCar);
                            Train.Cars[Train.DriverCar].CameraRestrictionMode = Camera.RestrictionMode.NotAvailable;
                            World.CameraRestriction = Camera.RestrictionMode.NotAvailable;
                        }

                        DocumentElements = CurrentXML.Root.Elements("Panel");
                        if (DocumentElements.Any())
                        {
                            PanelXmlParser.ParsePanelXml(System.IO.Path.GetFileName(File), TrainPath, Train, Train.DriverCar);
                            Train.Cars[Train.DriverCar].CameraRestrictionMode = Camera.RestrictionMode.On;
                            World.CameraRestriction = Camera.RestrictionMode.On;
                        }
                    }
                }
                catch
                {
                    var currentError = Translations.GetInterfaceString("errors_critical_file");
                    currentError = currentError.Replace("[file]", "panel.xml");
                    MessageBox.Show(currentError, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand);
                    Program.RestartArguments = " ";
                    Loading.Cancel           = true;
                    return;
                }

                if (Train.Cars[Train.DriverCar].CarSections[0].Groups[0].Elements.Any())
                {
                    World.UpdateViewingDistances();
                    return;
                }
                Interface.AddMessage(MessageType.Error, false, "The panel.xml file " + File + " failed to load. Falling back to legacy panel.");
            }
            else
            {
                File = OpenBveApi.Path.CombineFile(TrainPath, "panel.animated");
                if (System.IO.File.Exists(File))
                {
                    Program.FileSystem.AppendToLogFile("Loading train panel: " + File);
                    if (System.IO.File.Exists(OpenBveApi.Path.CombineFile(TrainPath, "panel2.cfg")) || System.IO.File.Exists(OpenBveApi.Path.CombineFile(TrainPath, "panel.cfg")))
                    {
                        Program.FileSystem.AppendToLogFile("INFO: This train contains both a 2D and a 3D panel. The 3D panel will always take precedence");
                    }
                    ObjectManager.AnimatedObjectCollection a = AnimatedObjectParser.ReadObject(File, Encoding);
                    if (a != null)
                    {
                        //HACK: If a == null , loading our animated object completely failed (Missing objects?). Fallback to trying the panel2.cfg
                        try
                        {
                            for (int i = 0; i < a.Objects.Length; i++)
                            {
                                a.Objects[i].ObjectIndex = ObjectManager.CreateDynamicObject();
                            }
                            Train.Cars[Train.DriverCar].CarSections[0].Groups[0].Elements = a.Objects;
                            Train.Cars[Train.DriverCar].CameraRestrictionMode             = Camera.RestrictionMode.NotAvailable;
                            World.CameraRestriction = Camera.RestrictionMode.NotAvailable;
                            World.UpdateViewingDistances();
                            return;
                        }
                        catch
                        {
                            var currentError = Translations.GetInterfaceString("errors_critical_file");
                            currentError = currentError.Replace("[file]", "panel.animated");
                            MessageBox.Show(currentError, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand);
                            Program.RestartArguments = " ";
                            Loading.Cancel           = true;
                            return;
                        }
                    }
                    Interface.AddMessage(MessageType.Error, false, "The panel.animated file " + File + " failed to load. Falling back to 2D panel.");
                }
            }

            var Panel2 = false;

            try
            {
                File = OpenBveApi.Path.CombineFile(TrainPath, "panel2.cfg");
                if (System.IO.File.Exists(File))
                {
                    Program.FileSystem.AppendToLogFile("Loading train panel: " + File);
                    Panel2 = true;
                    Panel2CfgParser.ParsePanel2Config("panel2.cfg", TrainPath, Encoding, Train, Train.DriverCar);
                    Train.Cars[Train.DriverCar].CameraRestrictionMode = Camera.RestrictionMode.On;
                    World.CameraRestriction = Camera.RestrictionMode.On;
                }
                else
                {
                    File = OpenBveApi.Path.CombineFile(TrainPath, "panel.cfg");
                    if (System.IO.File.Exists(File))
                    {
                        Program.FileSystem.AppendToLogFile("Loading train panel: " + File);
                        PanelCfgParser.ParsePanelConfig(TrainPath, Encoding, Train);
                        Train.Cars[Train.DriverCar].CameraRestrictionMode = Camera.RestrictionMode.On;
                        World.CameraRestriction = Camera.RestrictionMode.On;
                    }
                    else
                    {
                        World.CameraRestriction = Camera.RestrictionMode.NotAvailable;
                    }
                }
            }
            catch
            {
                var currentError = Translations.GetInterfaceString("errors_critical_file");
                currentError = currentError.Replace("[file]", Panel2 ? "panel2.cfg" : "panel.cfg");
                MessageBox.Show(currentError, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand);
                Program.RestartArguments = " ";
                Loading.Cancel           = true;
            }
        }
Example #10
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();
                }
                else
                {
                    lines[i] = lines[i].Trim();
                }
            }

            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();
                                    string b = lines[i].Substring(j + 1).TrimStart();
                                    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);
                                                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);
                                            }
                                            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, false, 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);
                                        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();
                                                string b = lines[i].Substring(j + 1).TrimStart();
                                                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, false, 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();
                                                        string d = b.Substring(k + 1).TrimStart();
                                                        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
                                                        {
                                                            axleLocations[n]     = rear;
                                                            axleLocations[n + 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);
                                    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();
                                                string b = lines[i].Substring(j + 1).TrimStart();
                                                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, false, 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 ObjectManager.StaticObject)
                        {
                            ObjectManager.StaticObject obj = (ObjectManager.StaticObject)carObjects[i];
                            obj.ApplyScale(-1.0, 1.0, -1.0);
                        }
                        else if (carObjects[i] is ObjectManager.AnimatedObjectCollection)
                        {
                            ObjectManager.AnimatedObjectCollection obj = (ObjectManager.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].Object.ApplyScale(-1.0, 1.0, -1.0);
                                    obj.Objects[j].States[h].Position.X *= -1.0;
                                    obj.Objects[j].States[h].Position.Z *= -1.0;
                                }
                                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 ObjectManager.StaticObject)
                        {
                            ObjectManager.StaticObject obj = (ObjectManager.StaticObject)bogieObjects[i];
                            obj.ApplyScale(-1.0, 1.0, -1.0);
                        }
                        else if (bogieObjects[i] is ObjectManager.AnimatedObjectCollection)
                        {
                            ObjectManager.AnimatedObjectCollection obj = (ObjectManager.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].Object.ApplyScale(-1.0, 1.0, -1.0);
                                    obj.Objects[j].States[h].Position.X *= -1.0;
                                    obj.Objects[j].States[h].Position.Z *= -1.0;
                                }
                                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);
            }
        }
Example #11
0
        /// <summary>Loads a Loksim3D GruppenObject</summary>
        /// <param name="FileName">The filename to load</param>
        /// <param name="Encoding">The text encoding of the containing file (Currently ignored, REMOVE??)</param>
        /// <param name="LoadMode">The object load mode</param>
        /// <returns>A new animated object collection, containing the GruppenObject's meshes etc.</returns>
        internal static ObjectManager.AnimatedObjectCollection ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode)
        {
            XmlDocument currentXML = new XmlDocument();

            //May need to be changed to use de-DE
            System.Globalization.CultureInfo       Culture = System.Globalization.CultureInfo.InvariantCulture;
            ObjectManager.AnimatedObjectCollection Result  = new ObjectManager.AnimatedObjectCollection();
            Result.Objects = new ObjectManager.AnimatedObject[0];
            try
            {
                currentXML.Load(FileName);
            }
            catch (Exception ex)
            {
                //The XML is not strictly valid
                string[] Lines = File.ReadAllLines(FileName);
                using (var stringReader = new StringReader(Lines[0]))
                {
                    var settings = new XmlReaderSettings {
                        ConformanceLevel = ConformanceLevel.Fragment
                    };
                    using (var xmlReader = XmlReader.Create(stringReader, settings))
                    {
                        if (xmlReader.Read())
                        {
                            //Attempt to find the text encoding and re-read the file
                            var result = xmlReader.GetAttribute("encoding");
                            var e      = System.Text.Encoding.GetEncoding(result);
                            Lines = File.ReadAllLines(FileName, e);
                            //Turf out the old encoding, as our string array should now be UTF-8
                            Lines[0] = "<?xml version=\"1.0\"?>";
                        }
                    }
                }
                for (int i = 0; i < Lines.Length; i++)
                {
                    while (Lines[i].IndexOf("\"\"") != -1)
                    {
                        //Loksim parser tolerates multiple quotes, strict XML does not
                        Lines[i] = Lines[i].Replace("\"\"", "\"");
                    }
                    while (Lines[i].IndexOf("  ") != -1)
                    {
                        //Replace double-spaces with singles
                        Lines[i] = Lines[i].Replace("  ", " ");
                    }
                }
                bool tryLoad = false;
                try
                {
                    //Horrible hack: Write out our string array to a new memory stream, then load from this stream
                    //Why can't XmlDocument.Load() just take a string array......
                    using (var stream = new MemoryStream())
                    {
                        var sw = new StreamWriter(stream);
                        foreach (var line in Lines)
                        {
                            sw.Write(line);
                            sw.Flush();
                        }
                        sw.Flush();
                        stream.Position = 0;
                        currentXML.Load(stream);
                        tryLoad = true;
                    }
                }
                catch
                {
                    //Generic catch-all clause
                }
                if (!tryLoad)
                {
                    //Pass out the *original* XML error, not anything generated when we've tried to correct it
                    Interface.AddMessage(Interface.MessageType.Error, false, "Error parsing Loksim3D XML: " + ex.Message);
                    return(null);
                }
            }

            string BaseDir = System.IO.Path.GetDirectoryName(FileName);

            GruppenObject[] CurrentObjects = new GruppenObject[0];
            //Check for null
            if (currentXML.DocumentElement != null)
            {
                ObjectManager.UnifiedObject[] obj = new OpenBve.ObjectManager.UnifiedObject[0];
                XmlNodeList DocumentNodes         = currentXML.DocumentElement.SelectNodes("/GRUPPENOBJECT");
                if (DocumentNodes != null)
                {
                    foreach (XmlNode outerNode in DocumentNodes)
                    {
                        if (outerNode.HasChildNodes)
                        {
                            foreach (XmlNode node in outerNode.ChildNodes)
                            {
                                if (node.Name == "Object" && node.HasChildNodes)
                                {
                                    foreach (XmlNode childNode in node.ChildNodes)
                                    {
                                        if (childNode.Name == "Props" && childNode.Attributes != null)
                                        {
                                            GruppenObject Object = new GruppenObject();
                                            foreach (XmlAttribute attribute in childNode.Attributes)
                                            {
                                                switch (attribute.Name)
                                                {
                                                case "Name":
                                                    string ObjectFile = OpenBveApi.Path.Loksim3D.CombineFile(BaseDir, attribute.Value, Program.FileSystem.LoksimPackageInstallationDirectory);
                                                    if (!File.Exists(ObjectFile))
                                                    {
                                                        Object.Name = null;
                                                        Interface.AddMessage(Interface.MessageType.Warning, true, "Ls3d Object file " + attribute.Value + " not found.");
                                                    }
                                                    else
                                                    {
                                                        Object.Name = ObjectFile;
                                                    }
                                                    break;

                                                case "Position":
                                                    string[] SplitPosition = attribute.Value.Split(';');
                                                    double.TryParse(SplitPosition[0], out Object.Position.X);
                                                    double.TryParse(SplitPosition[1], out Object.Position.Y);
                                                    double.TryParse(SplitPosition[2], out Object.Position.Z);
                                                    break;

                                                case "Rotation":
                                                    string[] SplitRotation = attribute.Value.Split(';');

                                                    double.TryParse(SplitRotation[0], out Object.Rotation.X);
                                                    double.TryParse(SplitRotation[1], out Object.Rotation.Y);
                                                    double.TryParse(SplitRotation[2], out Object.Rotation.Z);
                                                    break;

                                                case "ShowOn":
                                                    //Defines when the object should be shown
                                                    Object.FunctionScript = FunctionScripts.GetPostfixNotationFromInfixNotation(GetAnimatedFunction(attribute.Value, false));
                                                    break;

                                                case "HideOn":
                                                    //Defines when the object should be hidden
                                                    Object.FunctionScript = FunctionScripts.GetPostfixNotationFromInfixNotation(GetAnimatedFunction(attribute.Value, true));
                                                    break;
                                                }
                                            }
                                            if (Object.Name != null)
                                            {
                                                Array.Resize <GruppenObject>(ref CurrentObjects, CurrentObjects.Length + 1);
                                                CurrentObjects[CurrentObjects.Length - 1] = Object;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    //We've loaded the XML references, now load the objects into memory
                    for (int i = 0; i < CurrentObjects.Length; i++)
                    {
                        if (CurrentObjects[i] == null || string.IsNullOrEmpty(CurrentObjects[i].Name))
                        {
                            continue;
                        }
                        var Object = (ObjectManager.StaticObject)ObjectManager.LoadObject(CurrentObjects[i].Name, Encoding, LoadMode, false, false, false, CurrentObjects[i].Rotation);
                        if (Object != null)
                        {
                            Array.Resize <ObjectManager.UnifiedObject>(ref obj, obj.Length + 1);
                            obj[obj.Length - 1] = Object;

                            Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length + 1);
                            ObjectManager.AnimatedObject      a   = new ObjectManager.AnimatedObject();
                            ObjectManager.AnimatedObjectState aos = new ObjectManager.AnimatedObjectState
                            {
                                Object   = Object,
                                Position = CurrentObjects[i].Position,
                            };
                            a.States          = new ObjectManager.AnimatedObjectState[] { aos };
                            Result.Objects[i] = a;
                            if (!string.IsNullOrEmpty(CurrentObjects[i].FunctionScript))
                            {
                                Result.Objects[i].StateFunction =
                                    FunctionScripts.GetFunctionScriptFromPostfixNotation(CurrentObjects[i].FunctionScript + " 1 == --");
                            }
                        }
                    }
                }
                return(Result);
            }
            //Didn't find an acceptable XML object
            //Probably will cause things to throw an absolute wobbly somewhere....
            return(null);
        }
Example #12
0
        internal static ObjectManager.AnimatedObjectCollection ReadObject(string FileName, System.Text.Encoding Encoding,
                                                                          ObjectManager.ObjectLoadMode LoadMode)
        {
            XmlDocument currentXML = new XmlDocument();

            //May need to be changed to use de-DE
            System.Globalization.CultureInfo       Culture = System.Globalization.CultureInfo.InvariantCulture;
            ObjectManager.AnimatedObjectCollection Result  = new ObjectManager.AnimatedObjectCollection();
            Result.Objects = new ObjectManager.AnimatedObject[0];
            int ObjectCount = 0;

            currentXML.Load(FileName);
            string BaseDir = System.IO.Path.GetDirectoryName(FileName);

            GruppenObject[] CurrentObjects = new GruppenObject[0];
            //Check for null
            if (currentXML.DocumentElement != null)
            {
                ObjectManager.UnifiedObject[] obj = new OpenBve.ObjectManager.UnifiedObject[0];
                XmlNodeList DocumentNodes         = currentXML.DocumentElement.SelectNodes("/GRUPPENOBJECT");
                if (DocumentNodes != null)
                {
                    foreach (XmlNode outerNode in DocumentNodes)
                    {
                        if (outerNode.HasChildNodes)
                        {
                            foreach (XmlNode node in outerNode.ChildNodes)
                            {
                                if (node.Name == "Object" && node.HasChildNodes)
                                {
                                    foreach (XmlNode childNode in node.ChildNodes)
                                    {
                                        if (childNode.Name == "Props" && childNode.Attributes != null)
                                        {
                                            Array.Resize <GruppenObject>(ref CurrentObjects, CurrentObjects.Length + 1);
                                            GruppenObject Object = new GruppenObject();
                                            foreach (XmlAttribute attribute in childNode.Attributes)
                                            {
                                                switch (attribute.Name)
                                                {
                                                case "Name":
                                                    string ObjectFile = OpenBveApi.Path.CombineFile(BaseDir, attribute.Value);

                                                    Object.Name = ObjectFile;
                                                    ObjectCount++;
                                                    break;

                                                case "Position":
                                                    string[] SplitPosition = attribute.Value.Split(';');
                                                    double.TryParse(SplitPosition[0], out Object.Position.X);
                                                    double.TryParse(SplitPosition[1], out Object.Position.Y);
                                                    double.TryParse(SplitPosition[2], out Object.Position.Z);
                                                    break;

                                                case "Rotation":
                                                    string[] SplitRotation = attribute.Value.Split(';');

                                                    double.TryParse(SplitRotation[0], out Object.RotationX);
                                                    double.TryParse(SplitRotation[1], out Object.RotationY);
                                                    double.TryParse(SplitRotation[2], out Object.RotationZ);
                                                    break;
                                                }
                                            }
                                            CurrentObjects[CurrentObjects.Length - 1] = Object;
                                        }
                                    }
                                }
                            }
                        }
                    }

                    //We've loaded the XML references, now load the objects into memory
                    for (int i = 0; i < CurrentObjects.Length; i++)
                    {
                        var Object = ObjectManager.LoadObject(CurrentObjects[i].Name, Encoding, LoadMode, false, false, false, CurrentObjects[i].RotationX, CurrentObjects[i].RotationY, CurrentObjects[i].RotationZ);
                        if (Object != null)
                        {
                            Array.Resize <ObjectManager.UnifiedObject>(ref obj, obj.Length + 1);
                            obj[obj.Length - 1] = Object;
                        }
                    }
                    for (int j = 0; j < obj.Length; j++)
                    {
                        if (obj[j] != null)
                        {
                            Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length + 1);
                            if (obj[j] is ObjectManager.StaticObject)
                            {
                                ObjectManager.StaticObject s = (ObjectManager.StaticObject)obj[j];
                                s.Dynamic = true;
                                ObjectManager.AnimatedObject      a   = new ObjectManager.AnimatedObject();
                                ObjectManager.AnimatedObjectState aos = new ObjectManager.AnimatedObjectState
                                {
                                    Object   = s,
                                    Position = CurrentObjects[j].Position
                                };
                                a.States          = new ObjectManager.AnimatedObjectState[] { aos };
                                Result.Objects[j] = a;
                                ObjectCount++;
                            }
                            else if (obj[j] is ObjectManager.AnimatedObjectCollection)
                            {
                                ObjectManager.AnimatedObjectCollection a =
                                    (ObjectManager.AnimatedObjectCollection)obj[j];
                                for (int k = 0; k < a.Objects.Length; k++)
                                {
                                    for (int h = 0; h < a.Objects[k].States.Length; h++)
                                    {
                                        a.Objects[k].States[h].Position.X += CurrentObjects[j].Position.X;
                                        a.Objects[k].States[h].Position.Y += CurrentObjects[j].Position.Y;
                                        a.Objects[k].States[h].Position.Z += CurrentObjects[j].Position.Z;
                                    }
                                    Result.Objects[j] = a.Objects[k];
                                    ObjectCount++;
                                }
                            }
                        }
                    }
                }
                return(Result);
            }
            //Didn't find an acceptable XML object
            //Probably will cause things to throw an absolute wobbly somewhere....
            return(null);
        }
Example #13
0
        internal static ObjectManager.AnimatedObjectCollection ReadObject(string FileName, System.Text.Encoding Encoding,
            ObjectManager.ObjectLoadMode LoadMode)
        {
            XmlDocument currentXML = new XmlDocument();
            //May need to be changed to use de-DE
            System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture;
            ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection();
            Result.Objects = new ObjectManager.AnimatedObject[0];
            int ObjectCount = 0;
            currentXML.Load(FileName);
            string BaseDir = System.IO.Path.GetDirectoryName(FileName);

            GruppenObject[] CurrentObjects = new GruppenObject[0];
            //Check for null
            if (currentXML.DocumentElement != null)
            {
                ObjectManager.UnifiedObject[] obj = new OpenBve.ObjectManager.UnifiedObject[0];
                XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/GRUPPENOBJECT");
                if (DocumentNodes != null)
                {
                    foreach (XmlNode outerNode in DocumentNodes)
                    {
                        if (outerNode.HasChildNodes)
                        {
                            foreach (XmlNode node in outerNode.ChildNodes)
                            {
                                if (node.Name == "Object" && node.HasChildNodes)
                                {
                                    foreach (XmlNode childNode in node.ChildNodes)
                                    {
                                        if (childNode.Name == "Props" && childNode.Attributes != null)
                                        {
                                            Array.Resize<GruppenObject>(ref CurrentObjects, CurrentObjects.Length + 1);
                                            GruppenObject Object = new GruppenObject();
                                            foreach (XmlAttribute attribute in childNode.Attributes)
                                            {
                                                switch (attribute.Name)
                                                {
                                                    case "Name":
                                                        string ObjectFile = OpenBveApi.Path.CombineFile(BaseDir,attribute.Value);
                                                        
                                                        Object.Name = ObjectFile;
                                                        ObjectCount++;
                                                        break;
                                                    case "Position":
                                                        string[] SplitPosition = attribute.Value.Split(';');
                                                        double.TryParse(SplitPosition[0], out Object.Position.X);
                                                        double.TryParse(SplitPosition[1], out Object.Position.Y);
                                                        double.TryParse(SplitPosition[2], out Object.Position.Z);
                                                        break;
                                                    case "Rotation":
                                                        string[] SplitRotation = attribute.Value.Split(';');

                                                        double.TryParse(SplitRotation[0], out Object.RotationX);
                                                        double.TryParse(SplitRotation[1], out Object.RotationY);
                                                        double.TryParse(SplitRotation[2], out Object.RotationZ);
                                                        break;
                                                }
                                            }
                                            CurrentObjects[CurrentObjects.Length - 1] = Object;
                                        }
                                    }
                                }
                            }
                        }
                    }
                    
                    //We've loaded the XML references, now load the objects into memory
                    for (int i = 0; i < CurrentObjects.Length; i++)
                    {
                        var Object = ObjectManager.LoadObject(CurrentObjects[i].Name, Encoding, LoadMode, false, false, false, CurrentObjects[i].RotationX, CurrentObjects[i].RotationY, CurrentObjects[i].RotationZ);
                        if (Object != null)
                        {
                            Array.Resize<ObjectManager.UnifiedObject>(ref obj, obj.Length +1);
                            obj[obj.Length - 1] = Object;
                        }
                    }
                    for (int j = 0; j < obj.Length; j++)
                    {
                        if (obj[j] != null)
                        {
                            Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length + 1);
                            if (obj[j] is ObjectManager.StaticObject)
                            {
                                ObjectManager.StaticObject s = (ObjectManager.StaticObject) obj[j];
                                s.Dynamic = true;
                                ObjectManager.AnimatedObject a = new ObjectManager.AnimatedObject();
                                ObjectManager.AnimatedObjectState aos = new ObjectManager.AnimatedObjectState
                                {
                                    Object = s,
                                    Position = CurrentObjects[j].Position
                                };
                                a.States = new ObjectManager.AnimatedObjectState[] {aos};
                                Result.Objects[j] = a;
                                ObjectCount++;
                            }
                            else if (obj[j] is ObjectManager.AnimatedObjectCollection)
                            {
                                ObjectManager.AnimatedObjectCollection a =
                                    (ObjectManager.AnimatedObjectCollection) obj[j];
                                for (int k = 0; k < a.Objects.Length; k++)
                                {
                                    for (int h = 0; h < a.Objects[k].States.Length; h++)
                                    {
                                        a.Objects[k].States[h].Position.X += CurrentObjects[j].Position.X;
                                        a.Objects[k].States[h].Position.Y += CurrentObjects[j].Position.Y;
                                        a.Objects[k].States[h].Position.Z += CurrentObjects[j].Position.Z;
                                    }
                                    Result.Objects[j] = a.Objects[k];
                                    ObjectCount++;
                                }
                            }
                        }
                    }
                }
                return Result;
            }
            //Didn't find an acceptable XML object
            //Probably will cause things to throw an absolute wobbly somewhere....
            return null;
        }
Example #14
0
        // parse animated object config
        /// <summary>Loads a collection of animated objects from a file.</summary>
        /// <param name="FileName">The text file to load the animated object from. Must be an absolute file name.</param>
        /// <param name="Encoding">The encoding the file is saved in. If the file uses a byte order mark, the encoding indicated by the byte order mark is used and the Encoding parameter is ignored.</param>
        /// <param name="LoadMode">The texture load mode.</param>
        /// <returns>The collection of animated objects.</returns>
        internal static ObjectManager.AnimatedObjectCollection ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode)
        {
            ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection();
            Result.Objects = new ObjectManager.AnimatedObject[4];
            int ObjectCount = 0;

            // load file
            string[] Lines   = System.IO.File.ReadAllLines(FileName, Encoding);
            bool     rpnUsed = false;

            for (int i = 0; i < Lines.Length; i++)
            {
                int j = Lines[i].IndexOf(';');
                // cut comments out
                Lines[i] = j >= 0 ? Lines[i].Substring(0, j).Trim() : Lines[i].Trim();
                rpnUsed  = Lines[i].IndexOf("functionrpn", StringComparison.OrdinalIgnoreCase) >= 0;
            }
            if (rpnUsed)
            {
                Debug.AddMessage(Debug.MessageType.Error, false, "An animated object file contains RPN functions. These were never meant to be used directly, only for debugging. They won't be supported indefinately. Please get rid of them in file " + FileName);
            }
            for (int i = 0; i < Lines.Length; i++)
            {
                if (Lines[i].Length != 0)
                {
                    switch (Lines[i].ToLowerInvariant())
                    {
                    case "[include]":
                    {
                        i++;
                        Vector3D position = new Vector3D(0.0, 0.0, 0.0);
                        ObjectManager.UnifiedObject[] obj = new ObjectManager.UnifiedObject[4];
                        int objCount = 0;
                        while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) && Lines[i].EndsWith("]", StringComparison.Ordinal)))
                        {
                            if (Lines[i].Length != 0)
                            {
                                int equals = Lines[i].IndexOf("=", StringComparison.Ordinal);
                                if (equals > 0)
                                {
                                    /*
                                     * Process key-value pair, the only supported key is position.
                                     */
                                    string before = Lines[i].Substring(0, equals).TrimEnd();
                                    string after  = Lines[i].Substring(equals + 1).TrimStart();
                                    switch (before.ToLowerInvariant())
                                    {
                                    case "position":
                                        ParsePosition(after, ref position, before, i + 1, FileName);
                                        break;

                                    default:
                                        Debug.AddMessage(Debug.MessageType.Error, false, "The attribute " + before + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        break;
                                    }
                                }
                                else
                                {
                                    /*
                                     * Process object with file name relative to the location of this ANIMATED file.
                                     */
                                    string Folder = System.IO.Path.GetDirectoryName(FileName);
                                    if (Path.ContainsInvalidPathChars(Lines[i]))
                                    {
                                        Debug.AddMessage(Debug.MessageType.Error, false, Lines[i] + " contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                    }
                                    else
                                    {
                                        string file = OpenBveApi.Path.CombineFile(Folder, Lines[i]);
                                        if (System.IO.File.Exists(file))
                                        {
                                            if (obj.Length == objCount)
                                            {
                                                Array.Resize <ObjectManager.UnifiedObject>(ref obj, obj.Length << 1);
                                            }
                                            obj[objCount] = ObjectManager.LoadObject(file, Encoding, LoadMode, false, false, false);
                                            objCount++;
                                        }
                                        else
                                        {
                                            Debug.AddMessage(Debug.MessageType.Error, true, "File " + file + " not found at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        }
                                    }
                                }
                            }
                            i++;
                        }
                        i--;
                        for (int j = 0; j < objCount; j++)
                        {
                            if (obj[j] != null)
                            {
                                if (obj[j] is ObjectManager.StaticObject)
                                {
                                    ObjectManager.StaticObject s = (ObjectManager.StaticObject)obj[j];
                                    s.Dynamic = true;
                                    if (ObjectCount >= Result.Objects.Length)
                                    {
                                        Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1);
                                    }
                                    ObjectManager.AnimatedObject      a   = new ObjectManager.AnimatedObject();
                                    ObjectManager.AnimatedObjectState aos = new ObjectManager.AnimatedObjectState();
                                    aos.Object   = s;
                                    aos.Position = position;
                                    a.States     = new[] { aos };
                                    Result.Objects[ObjectCount] = a;
                                    ObjectCount++;
                                }
                                else if (obj[j] is ObjectManager.AnimatedObjectCollection)
                                {
                                    ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)obj[j];
                                    for (int k = 0; k < a.Objects.Length; k++)
                                    {
                                        if (ObjectCount >= Result.Objects.Length)
                                        {
                                            Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1);
                                        }
                                        for (int h = 0; h < a.Objects[k].States.Length; h++)
                                        {
                                            a.Objects[k].States[h].Position.X += position.X;
                                            a.Objects[k].States[h].Position.Y += position.Y;
                                            a.Objects[k].States[h].Position.Z += position.Z;
                                        }
                                        Result.Objects[ObjectCount] = a.Objects[k];
                                        ObjectCount++;
                                    }
                                }
                            }
                        }
                    }
                    break;

                    case "[object]":
                    {
                        i++;
                        if (Result.Objects.Length == ObjectCount)
                        {
                            Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1);
                        }
                        Result.Objects[ObjectCount]                        = new ObjectManager.AnimatedObject();
                        Result.Objects[ObjectCount].States                 = new ObjectManager.AnimatedObjectState[] { };
                        Result.Objects[ObjectCount].CurrentState           = -1;
                        Result.Objects[ObjectCount].TranslateXDirection    = new Vector3D(1.0, 0.0, 0.0);
                        Result.Objects[ObjectCount].TranslateYDirection    = new Vector3D(0.0, 1.0, 0.0);
                        Result.Objects[ObjectCount].TranslateZDirection    = new Vector3D(0.0, 0.0, 1.0);
                        Result.Objects[ObjectCount].RotateXDirection       = new Vector3D(1.0, 0.0, 0.0);
                        Result.Objects[ObjectCount].RotateYDirection       = new Vector3D(0.0, 1.0, 0.0);
                        Result.Objects[ObjectCount].RotateZDirection       = new Vector3D(0.0, 0.0, 1.0);
                        Result.Objects[ObjectCount].TextureShiftXDirection = new Vector2D(1.0, 0.0);
                        Result.Objects[ObjectCount].TextureShiftYDirection = new Vector2D(0.0, 1.0);
                        Result.Objects[ObjectCount].RefreshRate            = 0.0;
                        Result.Objects[ObjectCount].ObjectIndex            = -1;
                        Vector3D Position          = new Vector3D(0.0, 0.0, 0.0);
                        bool     timetableUsed     = false;
                        string[] StateFiles        = null;
                        string   StateFunctionRpn  = null;
                        int      StateFunctionLine = -1;
                        while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) && Lines[i].EndsWith("]", StringComparison.Ordinal)))
                        {
                            if (Lines[i].Length != 0)
                            {
                                int equals = Lines[i].IndexOf("=", StringComparison.Ordinal);
                                if (equals > 0)
                                {
                                    string before = Lines[i].Substring(0, equals).TrimEnd();
                                    string after  = Lines[i].Substring(equals + 1).TrimStart();
                                    switch (before.ToLowerInvariant())
                                    {
                                    case "position":
                                        ParsePosition(after, ref Position, before, i + 1, FileName);
                                        break;

                                    case "states":
                                        if (!ParseState(after, ref StateFiles, before, i + 1, FileName))
                                        {
                                            return(null);
                                        }
                                        break;

                                    case "statefunction":
                                        try {
                                            StateFunctionLine = i;
                                            StateFunctionRpn  = FunctionScripts.GetPostfixNotationFromInfixNotation(after);
                                        } catch (Exception ex) {
                                            Debug.AddMessage(Debug.MessageType.Error, false, ex.Message + " in " + before + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        } break;

                                    case "statefunctionrpn":
                                    {
                                        StateFunctionLine = i;
                                        StateFunctionRpn  = after;
                                    } break;

                                    case "translatexdirection":
                                        ParseTranslateDirection(after, ref Result.Objects[ObjectCount].TranslateXDirection,
                                                                before, i + 1, FileName);
                                        break;

                                    case "translateydirection":
                                        ParseTranslateDirection(after, ref Result.Objects[ObjectCount].TranslateYDirection,
                                                                before, i + 1, FileName);
                                        break;

                                    case "translatezdirection":
                                        ParseTranslateDirection(after, ref Result.Objects[ObjectCount].TranslateZDirection,
                                                                before, i + 1, FileName);
                                        break;

                                    case "translatexfunction":
                                        ParseInfixFunc(after, ref Result.Objects[ObjectCount].TranslateXFunction, before, i + 1, FileName);
                                        break;

                                    case "translateyfunction":
                                        ParseInfixFunc(after, ref Result.Objects[ObjectCount].TranslateYFunction, before, i + 1, FileName);
                                        break;

                                    case "translatezfunction":
                                        ParseInfixFunc(after, ref Result.Objects[ObjectCount].TranslateZFunction, before, i + 1, FileName);
                                        break;

                                    case "translatexfunctionrpn":
                                        ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TranslateXFunction, before, i + 1, FileName);
                                        break;

                                    case "translateyfunctionrpn":
                                        ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TranslateYFunction, before, i + 1, FileName);
                                        break;

                                    case "translatezfunctionrpn":
                                        ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TranslateZFunction, before, i + 1, FileName);
                                        break;

                                    case "rotatexdirection":
                                        ParseRotateDirection(after, ref Result.Objects[ObjectCount].RotateXDirection, before, i + 1, FileName);
                                        break;

                                    case "rotateydirection":
                                        ParseRotateDirection(after, ref Result.Objects[ObjectCount].RotateYDirection, before, i + 1, FileName);
                                        break;

                                    case "rotatezdirection":
                                        ParseRotateDirection(after, ref Result.Objects[ObjectCount].RotateZDirection, before, i + 1, FileName);
                                        break;

                                    case "rotatexfunction":
                                        ParseInfixFunc(after, ref Result.Objects[ObjectCount].RotateXFunction, before, i + 1, FileName);
                                        break;

                                    case "rotateyfunction":
                                        ParseInfixFunc(after, ref Result.Objects[ObjectCount].RotateYFunction, before, i + 1, FileName);
                                        break;

                                    case "rotatezfunction":
                                        ParseInfixFunc(after, ref Result.Objects[ObjectCount].RotateZFunction, before, i + 1, FileName);
                                        break;

                                    case "rotatexfunctionrpn":
                                        ParsePostfixFunc(after, ref Result.Objects[ObjectCount].RotateXFunction, before, i + 1, FileName);
                                        break;

                                    case "rotateyfunctionrpn":
                                        ParsePostfixFunc(after, ref Result.Objects[ObjectCount].RotateYFunction, before, i + 1, FileName);
                                        break;

                                    case "rotatezfunctionrpn":
                                        ParsePostfixFunc(after, ref Result.Objects[ObjectCount].RotateZFunction, before, i + 1, FileName);
                                        break;

                                    case "rotatexdamping":
                                        ParseRotateDamping(after, ref Result.Objects[ObjectCount].RotateXDamping, before, i + 1, FileName);
                                        break;

                                    case "rotateydamping":
                                        ParseRotateDamping(after, ref Result.Objects[ObjectCount].RotateYDamping, before, i + 1, FileName);
                                        break;

                                    case "rotatezdamping":
                                        ParseRotateDamping(after, ref Result.Objects[ObjectCount].RotateZDamping, before, i + 1, FileName);
                                        break;

                                    case "textureshiftxdirection":
                                        ParseTextureShift(after, ref Result.Objects[ObjectCount].TextureShiftXDirection, before, i + 1, FileName);
                                        break;

                                    case "textureshiftydirection":
                                        ParseTextureShift(after, ref Result.Objects[ObjectCount].TextureShiftYDirection, before, i + 1, FileName);
                                        break;

                                    case "textureshiftxfunction":
                                        ParseInfixFunc(after, ref Result.Objects[ObjectCount].TextureShiftXFunction, before, i + 1, FileName);
                                        break;

                                    case "textureshiftyfunction":
                                        ParseInfixFunc(after, ref Result.Objects[ObjectCount].TextureShiftYFunction, before, i + 1, FileName);
                                        break;

                                    case "textureshiftxfunctionrpn":
                                        ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TextureShiftXFunction, before, i + 1, FileName);
                                        break;

                                    case "textureshiftyfunctionrpn":
                                        ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TextureShiftYFunction, before, i + 1, FileName);
                                        break;

                                    case "textureoverride":
                                        switch (after.ToLowerInvariant())
                                        {
                                        case "none":
                                            break;

                                        case "timetable":
                                            if (!timetableUsed)
                                            {
                                                Timetable.AddObjectForCustomTimetable(Result.Objects[ObjectCount]);
                                                timetableUsed = true;
                                            }
                                            break;

                                        default:
                                            Debug.AddMessage(Debug.MessageType.Error, false, "Unrecognized value in " + before + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                            break;
                                        }
                                        break;

                                    case "refreshrate":
                                    {
                                        double r;
                                        if (!double.TryParse(after, System.Globalization.NumberStyles.Float, Culture, out r))
                                        {
                                            Debug.AddMessage(Debug.MessageType.Error, false, "Value is invalid in " + before + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        }
                                        else if (r < 0.0)
                                        {
                                            Debug.AddMessage(Debug.MessageType.Error, false, "Value is expected to be non-negative in " + before + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        }
                                        else
                                        {
                                            Result.Objects[ObjectCount].RefreshRate = r;
                                        }
                                    } break;

                                    default:
                                        Debug.AddMessage(Debug.MessageType.Error, false, "The attribute " + before + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        break;
                                    }
                                }
                                else
                                {
                                    Debug.AddMessage(Debug.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                    return(null);
                                }
                            }
                            i++;
                        }
                        i--;
                        if (StateFiles != null)
                        {
                            // create the object
                            if (timetableUsed)
                            {
                                if (StateFunctionRpn != null)
                                {
                                    StateFunctionRpn = "timetable 0 == " + StateFunctionRpn + " -1 ?";
                                }
                                else
                                {
                                    StateFunctionRpn = "timetable";
                                }
                            }
                            if (StateFunctionRpn != null)
                            {
                                try {
                                    Result.Objects[ObjectCount].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(StateFunctionRpn);
                                } catch (Exception ex) {
                                    Debug.AddMessage(Debug.MessageType.Error, false, ex.Message + " in StateFunction at line " + (StateFunctionLine + 1).ToString(Culture) + " in file " + FileName);
                                }
                            }
                            Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[StateFiles.Length];
                            bool ForceTextureRepeatX = Result.Objects[ObjectCount].TextureShiftXFunction != null && Result.Objects[ObjectCount].TextureShiftXDirection.X != 0.0 ||
                                                       Result.Objects[ObjectCount].TextureShiftYFunction != null && Result.Objects[ObjectCount].TextureShiftYDirection.Y != 0.0;
                            bool ForceTextureRepeatY = Result.Objects[ObjectCount].TextureShiftXFunction != null && Result.Objects[ObjectCount].TextureShiftXDirection.X != 0.0 ||
                                                       Result.Objects[ObjectCount].TextureShiftYFunction != null && Result.Objects[ObjectCount].TextureShiftYDirection.Y != 0.0;
                            for (int k = 0; k < StateFiles.Length; k++)
                            {
                                Result.Objects[ObjectCount].States[k].Position = new Vector3D(0.0, 0.0, 0.0);
                                if (StateFiles[k] != null)
                                {
                                    Result.Objects[ObjectCount].States[k].Object = ObjectManager.LoadStaticObject(StateFiles[k], Encoding, LoadMode, false, ForceTextureRepeatX, ForceTextureRepeatY);
                                    if (Result.Objects[ObjectCount].States[k].Object != null)
                                    {
                                        Result.Objects[ObjectCount].States[k].Object.Dynamic = true;
                                    }
                                }
                                else
                                {
                                    Result.Objects[ObjectCount].States[k].Object = null;
                                }
                                for (int j = 0; j < Result.Objects[ObjectCount].States.Length; j++)
                                {
                                    Result.Objects[ObjectCount].States[j].Position = Position;
                                }
                            }
                        }
                        else
                        {
                            Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[] { };
                        }
                        ObjectCount++;
                    }
                    break;

                    default:
                        Debug.AddMessage(Debug.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                        return(null);
                    }
                }
            }
            Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, ObjectCount);
            return(Result);
        }
Example #15
0
        /// <summary>Attempts to load and parse the current train's panel configuration file.</summary>
        /// <param name="TrainPath">The absolute on-disk path to the train folder.</param>
        /// <param name="Encoding">The automatically detected or manually set encoding of the panel configuration file.</param>
        /// <param name="Train">The base train on which to apply the panel configuration.</param>
        internal static void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, TrainManager.Train Train)
        {
            string File = OpenBveApi.Path.CombineFile(TrainPath, "panel.animated");

            if (System.IO.File.Exists(File))
            {
                Program.AppendToLogFile("Loading train panel: " + File);
                ObjectManager.AnimatedObjectCollection a = AnimatedObjectParser.ReadObject(File, Encoding, ObjectManager.ObjectLoadMode.DontAllowUnloadOfTextures);
                try
                {
                    for (int i = 0; i < a.Objects.Length; i++)
                    {
                        a.Objects[i].ObjectIndex = ObjectManager.CreateDynamicObject();
                    }
                    Train.Cars[Train.DriverCar].CarSections[0].Elements = a.Objects;
                    Train.Cars[Train.DriverCar].CameraRestrictionMode   = World.CameraRestrictionMode.NotAvailable;
                    World.CameraRestriction = World.CameraRestrictionMode.NotAvailable;
                    World.UpdateViewingDistances();
                }
                catch
                {
                    var currentError = Interface.GetInterfaceString("error_critical_file");
                    currentError = currentError.Replace("[file]", "panel.animated");
                    MessageBox.Show(currentError, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand);
                    Program.RestartArguments = " ";
                    Loading.Cancel           = true;
                }
            }
            else
            {
                var Panel2 = false;
                try
                {
                    File = OpenBveApi.Path.CombineFile(TrainPath, "panel2.cfg");
                    if (System.IO.File.Exists(File))
                    {
                        Program.AppendToLogFile("Loading train panel: " + File);
                        Panel2 = true;
                        Panel2CfgParser.ParsePanel2Config("panel2.cfg", TrainPath, Encoding, Train, Train.DriverCar);
                        Train.Cars[Train.DriverCar].CameraRestrictionMode = World.CameraRestrictionMode.On;
                        World.CameraRestriction = World.CameraRestrictionMode.On;
                    }
                    else
                    {
                        File = OpenBveApi.Path.CombineFile(TrainPath, "panel.cfg");
                        if (System.IO.File.Exists(File))
                        {
                            Program.AppendToLogFile("Loading train panel: " + File);
                            PanelCfgParser.ParsePanelConfig(TrainPath, Encoding, Train);
                            Train.Cars[Train.DriverCar].CameraRestrictionMode = World.CameraRestrictionMode.On;
                            World.CameraRestriction = World.CameraRestrictionMode.On;
                        }
                        else
                        {
                            World.CameraRestriction = World.CameraRestrictionMode.NotAvailable;
                        }
                    }
                }
                catch
                {
                    var currentError = Interface.GetInterfaceString("errors_critical_file");
                    currentError = currentError.Replace("[file]", Panel2 == true ? "panel2.cfg" : "panel.cfg");
                    MessageBox.Show(currentError, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand);
                    Program.RestartArguments = " ";
                    Loading.Cancel           = true;
                }
            }
        }
Example #16
0
        private static void LoadEverythingThreaded()
        {
            string RailwayFolder = GetRailwayFolder(CurrentRouteFile);
//			if (RailwayFolder == null) {
//				Interface.AddMessage(Interface.MessageType.Critical, false, "The Railway folder could not be found. Please check your folder structure.");
//				return;
//			}
            string ObjectFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Object");
            string SoundFolder  = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Sound");

            // reset
            Game.Reset(true);
            Game.MinimalisticSimulation = true;
            // screen
            World.CameraTrackFollower          = new TrackManager.TrackFollower();
            World.CameraTrackFollower.Train    = null;
            World.CameraTrackFollower.CarIndex = -1;
            World.CameraMode = World.CameraViewMode.Interior;
            // load route
            bool IsRW = string.Equals(System.IO.Path.GetExtension(CurrentRouteFile), ".rw", StringComparison.OrdinalIgnoreCase);

            CsvRwRouteParser.ParseRoute(CurrentRouteFile, IsRW, CurrentRouteEncoding, CurrentTrainFolder, ObjectFolder, SoundFolder, false);
            System.Threading.Thread.Sleep(1); if (Cancel)
            {
                return;
            }
            Game.CalculateSeaLevelConstants();
            if (Game.BogusPretrainInstructions.Length != 0)
            {
                double t = Game.BogusPretrainInstructions[0].Time;
                double p = Game.BogusPretrainInstructions[0].TrackPosition;
                for (int i = 1; i < Game.BogusPretrainInstructions.Length; i++)
                {
                    if (Game.BogusPretrainInstructions[i].Time > t)
                    {
                        t = Game.BogusPretrainInstructions[i].Time;
                    }
                    else
                    {
                        t += 1.0;
                        Game.BogusPretrainInstructions[i].Time = t;
                    }
                    if (Game.BogusPretrainInstructions[i].TrackPosition > p)
                    {
                        p = Game.BogusPretrainInstructions[i].TrackPosition;
                    }
                    else
                    {
                        p += 1.0;
                        Game.BogusPretrainInstructions[i].TrackPosition = p;
                    }
                }
            }
            RouteProgress = 1.0;
            // initialize trains
            System.Threading.Thread.Sleep(1); if (Cancel)
            {
                return;
            }
            TrainManager.Trains = new TrainManager.Train[Game.PrecedingTrainTimeDeltas.Length + 1 + (Game.BogusPretrainInstructions.Length != 0 ? 1 : 0)];
            for (int k = 0; k < TrainManager.Trains.Length; k++)
            {
                TrainManager.Trains[k]            = new TrainManager.Train();
                TrainManager.Trains[k].TrainIndex = k;
                if (k == TrainManager.Trains.Length - 1 & Game.BogusPretrainInstructions.Length != 0)
                {
                    TrainManager.Trains[k].State = TrainManager.TrainState.Bogus;
                }
                else
                {
                    TrainManager.Trains[k].State = TrainManager.TrainState.Pending;
                }
            }
            TrainManager.PlayerTrain = TrainManager.Trains[Game.PrecedingTrainTimeDeltas.Length];
            // load trains
            double TrainProgressMaximum = 0.7 + 0.3 * (double)TrainManager.Trains.Length;

            for (int k = 0; k < TrainManager.Trains.Length; k++)
            {
                if (TrainManager.Trains[k].State == TrainManager.TrainState.Bogus)
                {
                    // bogus train
                    string Folder = Program.FileSystem.GetDataFolder("Compatibility", "PreTrain");
                    TrainDatParser.ParseTrainData(Folder, System.Text.Encoding.UTF8, TrainManager.Trains[k]);
                    System.Threading.Thread.Sleep(1); if (Cancel)
                    {
                        return;
                    }
                    SoundCfgParser.LoadNoSound(TrainManager.Trains[k]);
                    System.Threading.Thread.Sleep(1); if (Cancel)
                    {
                        return;
                    }
                    TrainProgressCurrentWeight = 0.3 / TrainProgressMaximum;
                    TrainProgressCurrentSum   += TrainProgressCurrentWeight;
                }
                else
                {
                    // real train
                    TrainProgressCurrentWeight = 0.1 / TrainProgressMaximum;
                    TrainDatParser.ParseTrainData(CurrentTrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]);
                    TrainProgressCurrentSum += TrainProgressCurrentWeight;
                    System.Threading.Thread.Sleep(1); if (Cancel)
                    {
                        return;
                    }
                    TrainProgressCurrentWeight = 0.2 / TrainProgressMaximum;
                    SoundCfgParser.ParseSoundConfig(CurrentTrainFolder, 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].Sounds.DoorOpenL.Buffer != null & TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer != null)
                            {
                                Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer);
                                Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer);
                                double a = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer.Duration;
                                double b = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.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].Sounds.DoorOpenL.Buffer != null)
                            {
                                Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer);
                                double a = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer.Duration;
                                TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = a > 0.0 ? 1.0 / a : 0.8;
                            }
                            else if (TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer != null)
                            {
                                Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer);
                                double b = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.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].Sounds.DoorCloseL.Buffer != null & TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer != null)
                            {
                                Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer);
                                Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer);
                                double a = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer.Duration;
                                double b = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.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].Sounds.DoorCloseL.Buffer != null)
                            {
                                Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer);
                                double a = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer.Duration;
                                TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = a > 0.0 ? 1.0 / a : 0.8;
                            }
                            else if (TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer != null)
                            {
                                Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer);
                                double b = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.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;
                    }
                }
                for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++)
                {
                    TrainManager.Trains[k].Cars[i].FrontAxle.Follower.Train = TrainManager.Trains[k];
                    TrainManager.Trains[k].Cars[i].RearAxle.Follower.Train  = TrainManager.Trains[k];
                    TrainManager.Trains[k].Cars[i].BeaconReceiver.Train     = TrainManager.Trains[k];
                }
                // add panel section
                if (k == TrainManager.PlayerTrain.TrainIndex)
                {
                    TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections             = new TrainManager.CarSection[1];
                    TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections[0].Elements = new ObjectManager.AnimatedObject[] { };
                    TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections[0].Overlay  = true;
                    TrainProgressCurrentWeight = 0.7 / TrainProgressMaximum;
                    TrainManager.ParsePanelConfig(CurrentTrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]);
                    TrainProgressCurrentSum += TrainProgressCurrentWeight;
                    System.Threading.Thread.Sleep(1); if (Cancel)
                    {
                        return;
                    }
                }
                // add exterior section
                if (TrainManager.Trains[k].State != TrainManager.TrainState.Bogus)
                {
                    ObjectManager.UnifiedObject[] CarObjects;
                    ExtensionsCfgParser.ParseExtensionsConfig(CurrentTrainFolder, CurrentTrainEncoding, out CarObjects, TrainManager.Trains[k]);
                    System.Threading.Thread.Sleep(1); if (Cancel)
                    {
                        return;
                    }
                    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");
                            ObjectManager.StaticObject so = ObjectManager.LoadStaticObject(file, System.Text.Encoding.UTF8, ObjectManager.ObjectLoadMode.Normal, false, false, 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;
                                CsvB3dObjectParser.ApplyScale(so, sx, sy, sz);
                                CarObjects[i] = so;
                            }
                        }
                        if (CarObjects[i] != null)
                        {
                            // add object
                            int j = TrainManager.Trains[k].Cars[i].CarSections.Length;
                            Array.Resize <TrainManager.CarSection>(ref TrainManager.Trains[k].Cars[i].CarSections, j + 1);
                            if (CarObjects[i] is ObjectManager.StaticObject)
                            {
                                ObjectManager.StaticObject s = (ObjectManager.StaticObject)CarObjects[i];
                                TrainManager.Trains[k].Cars[i].CarSections[j].Elements                       = new ObjectManager.AnimatedObject[1];
                                TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0]                    = new ObjectManager.AnimatedObject();
                                TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States             = new ObjectManager.AnimatedObjectState[1];
                                TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States[0].Position = new Vector3(0.0, 0.0, 0.0);
                                TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States[0].Object   = s;
                                TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].CurrentState       = 0;
                                TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].ObjectIndex        = ObjectManager.CreateDynamicObject();
                            }
                            else if (CarObjects[i] is ObjectManager.AnimatedObjectCollection)
                            {
                                ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)CarObjects[i];
                                TrainManager.Trains[k].Cars[i].CarSections[j].Elements = new ObjectManager.AnimatedObject[a.Objects.Length];
                                for (int h = 0; h < a.Objects.Length; h++)
                                {
                                    TrainManager.Trains[k].Cars[i].CarSections[j].Elements[h]             = a.Objects[h];
                                    TrainManager.Trains[k].Cars[i].CarSections[j].Elements[h].ObjectIndex = ObjectManager.CreateDynamicObject();
                                }
                            }
                        }
                    }
                }
                // place cars
                {
                    double z = 0.0;
                    for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++)
                    {
                        TrainManager.Trains[k].Cars[i].FrontAxle.Follower.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].FrontAxlePosition;
                        TrainManager.Trains[k].Cars[i].RearAxle.Follower.TrackPosition  = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].RearAxlePosition;
                        TrainManager.Trains[k].Cars[i].BeaconReceiver.TrackPosition     = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].BeaconReceiverPosition;
                        z -= TrainManager.Trains[k].Cars[i].Length;
                        if (i < TrainManager.Trains[k].Cars.Length - 1)
                        {
                            z -= 0.5 * (TrainManager.Trains[k].Couplers[i].MinimumDistanceBetweenCars + TrainManager.Trains[k].Couplers[i].MaximumDistanceBetweenCars);
                        }
                    }
                }
                // configure ai / timetable
                if (TrainManager.Trains[k] == TrainManager.PlayerTrain)
                {
                    TrainManager.Trains[k].TimetableDelta = 0.0;
                }
                else if (TrainManager.Trains[k].State != TrainManager.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;
                }
            }
            TrainProgress = 1.0;
            // finished created objects
            System.Threading.Thread.Sleep(1); if (Cancel)
            {
                return;
            }
            ObjectManager.FinishCreatingObjects();
            // update sections
            if (Game.Sections.Length > 0)
            {
                Game.UpdateSection(Game.Sections.Length - 1);
            }
            // load plugin
            for (int i = 0; i < TrainManager.Trains.Length; i++)
            {
                if (TrainManager.Trains[i].State != TrainManager.TrainState.Bogus)
                {
                    if (TrainManager.Trains[i] == TrainManager.PlayerTrain)
                    {
                        if (!PluginManager.LoadCustomPlugin(TrainManager.Trains[i], CurrentTrainFolder, CurrentTrainEncoding))
                        {
                            PluginManager.LoadDefaultPlugin(TrainManager.Trains[i], CurrentTrainFolder);
                        }
                    }
                    else
                    {
                        PluginManager.LoadDefaultPlugin(TrainManager.Trains[i], CurrentTrainFolder);
                    }
                }
            }
        }
Example #17
0
        // parse extensions config
        internal static void ParseExtensionsConfig(string TrainPath, System.Text.Encoding Encoding, ref ObjectManager.UnifiedObject[] CarObjects, ref ObjectManager.UnifiedObject[] BogieObjects, 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))
            {
                TextEncoding.Encoding newEncoding = TextEncoding.GetEncodingFromFile(FileName);
                if (newEncoding != TextEncoding.Encoding.Unknown)
                {
                    switch (newEncoding)
                    {
                    case TextEncoding.Encoding.Utf7:
                        Encoding = System.Text.Encoding.UTF7;
                        break;

                    case TextEncoding.Encoding.Utf8:
                        Encoding = System.Text.Encoding.UTF8;
                        break;

                    case TextEncoding.Encoding.Utf16Le:
                        Encoding = System.Text.Encoding.Unicode;
                        break;

                    case TextEncoding.Encoding.Utf16Be:
                        Encoding = System.Text.Encoding.BigEndianUnicode;
                        break;

                    case TextEncoding.Encoding.Utf32Le:
                        Encoding = System.Text.Encoding.UTF32;
                        break;

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

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

                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();
                    }
                    else
                    {
                        Lines[i] = Lines[i].Trim();
                    }
                }
                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();
                                        string b = Lines[i].Substring(j + 1).TrimStart();
                                        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(Interface.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)
                                                        {
                                                            CarObjects[n] = ObjectManager.LoadObject(File, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false);
                                                        }
                                                    }
                                                    else
                                                    {
                                                        Interface.AddMessage(Interface.MessageType.Error, true, "The car object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                    }
                                                }
                                            }
                                            else
                                            {
                                                Interface.AddMessage(Interface.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(Interface.MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        }
                                    }
                                    else
                                    {
                                        Interface.AddMessage(Interface.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(Interface.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();
                                                    string b = Lines[i].Substring(j + 1).TrimStart();
                                                    switch (a.ToLowerInvariant())
                                                    {
                                                    case "object":
                                                        if (string.IsNullOrEmpty(b))
                                                        {
                                                            Interface.AddMessage(Interface.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(Interface.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)
                                                                {
                                                                    CarObjects[n] = ObjectManager.LoadObject(File, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false);
                                                                }
                                                            }
                                                            else
                                                            {
                                                                Interface.AddMessage(Interface.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(Interface.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(Interface.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();
                                                            string d = b.Substring(k + 1).TrimStart();
                                                            double rear, front;
                                                            if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out rear))
                                                            {
                                                                Interface.AddMessage(Interface.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(Interface.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(Interface.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(Interface.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;

                                                    default:
                                                        Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                        break;
                                                    }
                                                }
                                                else
                                                {
                                                    Interface.AddMessage(Interface.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(Interface.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(Interface.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.Couplers.Length)
                                    {
                                        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();
                                                    string b = Lines[i].Substring(j + 1).TrimStart();
                                                    switch (a.ToLowerInvariant())
                                                    {
                                                    case "distances":
                                                    {
                                                        int k = b.IndexOf(',');
                                                        if (k >= 0)
                                                        {
                                                            string c = b.Substring(0, k).TrimEnd();
                                                            string d = b.Substring(k + 1).TrimStart();
                                                            double min, max;
                                                            if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out min))
                                                            {
                                                                Interface.AddMessage(Interface.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(Interface.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(Interface.MessageType.Error, false, "Minimum is expected to be less than Maximum in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                            }
                                                            else
                                                            {
                                                                Train.Couplers[n].MinimumDistanceBetweenCars = min;
                                                                Train.Couplers[n].MaximumDistanceBetweenCars = max;
                                                            }
                                                        }
                                                        else
                                                        {
                                                            Interface.AddMessage(Interface.MessageType.Error, false, "An argument-separating comma is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                        }
                                                    } break;

                                                    default:
                                                        Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                        break;
                                                    }
                                                }
                                                else
                                                {
                                                    Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                }
                                            }
                                            i++;
                                        }
                                        i--;
                                    }
                                    else
                                    {
                                        Interface.AddMessage(Interface.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(Interface.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(Interface.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();
                                                    string b = Lines[i].Substring(j + 1).TrimStart();
                                                    switch (a.ToLowerInvariant())
                                                    {
                                                    case "object":
                                                        if (Path.ContainsInvalidChars(b))
                                                        {
                                                            Interface.AddMessage(Interface.MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                        }
                                                        else
                                                        {
                                                            if (string.IsNullOrEmpty(b))
                                                            {
                                                                Interface.AddMessage(Interface.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)
                                                                {
                                                                    BogieObjects[n] = ObjectManager.LoadObject(File, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false);
                                                                }
                                                            }
                                                            else
                                                            {
                                                                Interface.AddMessage(Interface.MessageType.Error, true, "The bogie object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                            }
                                                        }
                                                        break;

                                                    case "length":
                                                    {
                                                        Interface.AddMessage(Interface.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();
                                                            string d = b.Substring(k + 1).TrimStart();
                                                            double rear, front;
                                                            if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out rear))
                                                            {
                                                                Interface.AddMessage(Interface.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(Interface.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(Interface.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(Interface.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(Interface.MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                        break;
                                                    }
                                                }
                                                else
                                                {
                                                    Interface.AddMessage(Interface.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(Interface.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(Interface.MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                }
                            }
                            else
                            {
                                // default
                                Interface.AddMessage(Interface.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 ObjectManager.StaticObject)
                            {
                                ObjectManager.StaticObject obj = (ObjectManager.StaticObject)CarObjects[i];
                                obj.ApplyScale(-1.0, 1.0, -1.0);
                            }
                            else if (CarObjects[i] is ObjectManager.AnimatedObjectCollection)
                            {
                                ObjectManager.AnimatedObjectCollection obj = (ObjectManager.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].Object.ApplyScale(-1.0, 1.0, -1.0);
                                        obj.Objects[j].States[h].Position.X *= -1.0;
                                        obj.Objects[j].States[h].Position.Z *= -1.0;
                                    }
                                    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 ObjectManager.StaticObject)
                            {
                                ObjectManager.StaticObject obj = (ObjectManager.StaticObject)BogieObjects[i];
                                obj.ApplyScale(-1.0, 1.0, -1.0);
                            }
                            else if (BogieObjects[i] is ObjectManager.AnimatedObjectCollection)
                            {
                                ObjectManager.AnimatedObjectCollection obj = (ObjectManager.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].Object.ApplyScale(-1.0, 1.0, -1.0);
                                        obj.Objects[j].States[h].Position.X *= -1.0;
                                        obj.Objects[j].States[h].Position.Z *= -1.0;
                                    }
                                    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(Interface.MessageType.Warning, false, "An incomplete set of exterior objects was provided in file " + FileName);
                }

                if (bogieObjects > 0 & bogieObjects < Train.Cars.Length * 2)
                {
                    Interface.AddMessage(Interface.MessageType.Warning, false, "An incomplete set of bogie objects was provided in file " + FileName);
                }
            }
        }
Example #18
0
        // parse extensions config
        internal static void ParseExtensionsConfig(string TrainPath, System.Text.Encoding Encoding, out ObjectManager.UnifiedObject[] CarObjects, TrainManager.Train Train)
        {
            CarObjects = new ObjectManager.UnifiedObject[Train.Cars.Length];
            bool[] CarObjectsReversed = new bool[Train.Cars.Length];
            System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture;
            string FileName = OpenBveApi.Path.CombineFile(TrainPath, "extensions.cfg");

            if (System.IO.File.Exists(FileName))
            {
                // load file
                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();
                    }
                    else
                    {
                        Lines[i] = Lines[i].Trim();
                    }
                }
                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();
                                        string b = Lines[i].Substring(j + 1).TrimStart();
                                        int    n;
                                        if (int.TryParse(a, System.Globalization.NumberStyles.Integer, Culture, out n))
                                        {
                                            if (n >= 0 & n < Train.Cars.Length)
                                            {
                                                if (Interface.ContainsInvalidPathChars(b))
                                                {
                                                    Interface.AddMessage(Interface.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))
                                                    {
                                                        CarObjects[n] = ObjectManager.LoadObject(File, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false);
                                                    }
                                                    else
                                                    {
                                                        Interface.AddMessage(Interface.MessageType.Error, true, "The car object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                    }
                                                }
                                            }
                                            else
                                            {
                                                Interface.AddMessage(Interface.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(Interface.MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                        }
                                    }
                                    else
                                    {
                                        Interface.AddMessage(Interface.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)
                                    {
                                        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();
                                                    string b = Lines[i].Substring(j + 1).TrimStart();
                                                    switch (a.ToLowerInvariant())
                                                    {
                                                    case "object":
                                                        if (Interface.ContainsInvalidPathChars(b))
                                                        {
                                                            Interface.AddMessage(Interface.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))
                                                            {
                                                                CarObjects[n] = ObjectManager.LoadObject(File, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false);
                                                            }
                                                            else
                                                            {
                                                                Interface.AddMessage(Interface.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(Interface.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(Interface.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();
                                                            string d = b.Substring(k + 1).TrimStart();
                                                            double rear, front;
                                                            if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out rear))
                                                            {
                                                                Interface.AddMessage(Interface.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(Interface.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(Interface.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].RearAxlePosition  = rear;
                                                                Train.Cars[n].FrontAxlePosition = front;
                                                                DefinedAxles = true;
                                                            }
                                                        }
                                                        else
                                                        {
                                                            Interface.AddMessage(Interface.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;

                                                    default:
                                                        Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                        break;
                                                    }
                                                }
                                                else
                                                {
                                                    Interface.AddMessage(Interface.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].RearAxlePosition  = -AxleDistance;
                                            Train.Cars[n].FrontAxlePosition = AxleDistance;
                                        }
                                    }
                                    else
                                    {
                                        Interface.AddMessage(Interface.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(Interface.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.Couplers.Length)
                                    {
                                        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();
                                                    string b = Lines[i].Substring(j + 1).TrimStart();
                                                    switch (a.ToLowerInvariant())
                                                    {
                                                    case "distances":
                                                    {
                                                        int k = b.IndexOf(',');
                                                        if (k >= 0)
                                                        {
                                                            string c = b.Substring(0, k).TrimEnd();
                                                            string d = b.Substring(k + 1).TrimStart();
                                                            double min, max;
                                                            if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out min))
                                                            {
                                                                Interface.AddMessage(Interface.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(Interface.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(Interface.MessageType.Error, false, "Minimum is expected to be less than Maximum in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                            }
                                                            else
                                                            {
                                                                Train.Couplers[n].MinimumDistanceBetweenCars = min;
                                                                Train.Couplers[n].MaximumDistanceBetweenCars = max;
                                                            }
                                                        }
                                                        else
                                                        {
                                                            Interface.AddMessage(Interface.MessageType.Error, false, "An argument-separating comma is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                        }
                                                    } break;

                                                    default:
                                                        Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                        break;
                                                    }
                                                }
                                                else
                                                {
                                                    Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                                }
                                            }
                                            i++;
                                        }
                                        i--;
                                    }
                                    else
                                    {
                                        Interface.AddMessage(Interface.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(Interface.MessageType.Error, false, "The coupler index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName);
                                }
                            }
                            else
                            {
                                // default
                                Interface.AddMessage(Interface.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])
                        {
                            {
                                // reverse axle positions
                                double temp = Train.Cars[i].FrontAxlePosition;
                                Train.Cars[i].FrontAxlePosition = -Train.Cars[i].RearAxlePosition;
                                Train.Cars[i].RearAxlePosition  = -temp;
                            }
                            if (CarObjects[i] is ObjectManager.StaticObject)
                            {
                                ObjectManager.StaticObject obj = (ObjectManager.StaticObject)CarObjects[i];
                                CsvB3dObjectParser.ApplyScale(obj, -1.0, 1.0, -1.0);
                            }
                            else if (CarObjects[i] is ObjectManager.AnimatedObjectCollection)
                            {
                                ObjectManager.AnimatedObjectCollection obj = (ObjectManager.AnimatedObjectCollection)CarObjects[i];
                                for (int j = 0; j < obj.Objects.Length; j++)
                                {
                                    for (int h = 0; h < obj.Objects[j].States.Length; h++)
                                    {
                                        CsvB3dObjectParser.ApplyScale(obj.Objects[j].States[h].Object, -1.0, 1.0, -1.0);
                                        obj.Objects[j].States[h].Position.X *= -1.0;
                                        obj.Objects[j].States[h].Position.Z *= -1.0;
                                    }
                                    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(Interface.MessageType.Warning, false, "An incomplete set of exterior objects was provided in file " + FileName);
                }
            }
        }
Example #19
0
		// parse animated object config
		/// <summary>Loads a collection of animated objects from a file.</summary>
		/// <param name="FileName">The text file to load the animated object from. Must be an absolute file name.</param>
		/// <param name="Encoding">The encoding the file is saved in. If the file uses a byte order mark, the encoding indicated by the byte order mark is used and the Encoding parameter is ignored.</param>
		/// <param name="LoadMode">The texture load mode.</param>
		/// <returns>The collection of animated objects.</returns>
		internal static ObjectManager.AnimatedObjectCollection ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode)
		{
			System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture;
			ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection
			{
				Objects = new ObjectManager.AnimatedObject[4]
			};
			int ObjectCount = 0;
			// load file
			string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding);
			bool rpnUsed = false;
			for (int i = 0; i < Lines.Length; i++)
			{
				int j = Lines[i].IndexOf(';');
				//Trim out comments
				Lines[i] = j >= 0 ? Lines[i].Substring(0, j).Trim() : Lines[i].Trim();
				//Test whether RPN functions have been used
				rpnUsed = Lines[i].IndexOf("functionrpn", StringComparison.OrdinalIgnoreCase) >= 0;
			}
			if (rpnUsed)
			{
				Interface.AddMessage(Interface.MessageType.Error, false, "An animated object file contains RPN functions. These were never meant to be used directly, only for debugging. They won't be supported indefinately. Please get rid of them in file " + FileName);
			}
			for (int i = 0; i < Lines.Length; i++)
			{
				if (Lines[i].Length != 0)
				{
					switch (Lines[i].ToLowerInvariant())
					{
						case "[include]":
							{
								i++;
								Vector3 position = new Vector3(0.0, 0.0, 0.0);
								ObjectManager.UnifiedObject[] obj = new OpenBve.ObjectManager.UnifiedObject[4];
								int objCount = 0;
								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();
											string b = Lines[i].Substring(j + 1).TrimStart();
											switch (a.ToLowerInvariant())
											{
												case "position":
													{
														string[] s = b.Split(',');
														if (s.Length == 3)
														{
															double x, y, z;
															if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x))
															{
																Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
															}
															else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y))
															{
																Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
															}
															else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z))
															{
																Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
															}
															else
															{
																position = new Vector3(x, y, z);
															}
														}
														else
														{
															Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
														}
													} break;
												default:
													Interface.AddMessage(Interface.MessageType.Error, false, "The attribute " + a + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													break;
											}
										}
										else
										{
											string Folder = System.IO.Path.GetDirectoryName(FileName);
											if (Interface.ContainsInvalidPathChars(Lines[i]))
											{
												Interface.AddMessage(Interface.MessageType.Error, false, Lines[i] + " contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName);
											}
											else
											{
												string file = OpenBveApi.Path.CombineFile(Folder, Lines[i]);
												if (System.IO.File.Exists(file))
												{
													if (obj.Length == objCount)
													{
														Array.Resize<ObjectManager.UnifiedObject>(ref obj, obj.Length << 1);
													}
													obj[objCount] = ObjectManager.LoadObject(file, Encoding, LoadMode, false, false, false);
													objCount++;
												}
												else
												{
													Interface.AddMessage(Interface.MessageType.Error, true, "File " + file + " not found at line " + (i + 1).ToString(Culture) + " in file " + FileName);
												}
											}
										}
									}
									i++;
								}
								i--;
								for (int j = 0; j < objCount; j++)
								{
									if (obj[j] != null)
									{
										if (obj[j] is ObjectManager.StaticObject)
										{
											ObjectManager.StaticObject s = (ObjectManager.StaticObject)obj[j];
											s.Dynamic = true;
											if (ObjectCount >= Result.Objects.Length)
											{
												Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1);
											}
											ObjectManager.AnimatedObject a = new ObjectManager.AnimatedObject();
											ObjectManager.AnimatedObjectState aos = new ObjectManager.AnimatedObjectState
											{
												Object = s,
												Position = position
											};
											a.States = new ObjectManager.AnimatedObjectState[] { aos };
											Result.Objects[ObjectCount] = a;
											ObjectCount++;
										}
										else if (obj[j] is ObjectManager.AnimatedObjectCollection)
										{
											ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)obj[j];
											for (int k = 0; k < a.Objects.Length; k++)
											{
												if (ObjectCount >= Result.Objects.Length)
												{
													Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1);
												}
												for (int h = 0; h < a.Objects[k].States.Length; h++)
												{
													a.Objects[k].States[h].Position.X += position.X;
													a.Objects[k].States[h].Position.Y += position.Y;
													a.Objects[k].States[h].Position.Z += position.Z;
												}
												Result.Objects[ObjectCount] = a.Objects[k];
												ObjectCount++;
											}
										}
									}
								}
							}
							break;
						case "[object]":
							{
								i++;
								if (Result.Objects.Length == ObjectCount)
								{
									Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1);
								}
								Result.Objects[ObjectCount] = new ObjectManager.AnimatedObject
								{
									States = new ObjectManager.AnimatedObjectState[] {},
									CurrentState = -1,
									TranslateXDirection = new Vector3(1.0, 0.0, 0.0),
									TranslateYDirection = new Vector3(0.0, 1.0, 0.0),
									TranslateZDirection = new Vector3(0.0, 0.0, 1.0),
									RotateXDirection = new Vector3(1.0, 0.0, 0.0),
									RotateYDirection = new Vector3(0.0, 1.0, 0.0),
									RotateZDirection = new Vector3(0.0, 0.0, 1.0),
									TextureShiftXDirection = new Vector2(1.0, 0.0),
									TextureShiftYDirection = new Vector2(0.0, 1.0),
									RefreshRate = 0.0,
									ObjectIndex = -1
								};
								Vector3 Position = new Vector3(0.0, 0.0, 0.0);
								double RotateX = 0;
								bool StaticXRotation = false;
								double RotateY = 0;
								bool StaticYRotation = false;
								double RotateZ = 0;
								bool StaticZRotation = false;
								bool timetableUsed = false;
								string[] StateFiles = null;
								string StateFunctionRpn = null;
								int StateFunctionLine = -1;
								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();
											string b = Lines[i].Substring(j + 1).TrimStart();
											switch (a.ToLowerInvariant())
											{
												case "position":
													{
														string[] s = b.Split(',');
														if (s.Length == 3)
														{
															double x, y, z;
															if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x))
															{
																Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
															}
															else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y))
															{
																Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
															}
															else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z))
															{
																Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
															}
															else
															{
																Position = new Vector3(x, y, z);
															}
														}
														else
														{
															Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
														}
													} break;
												case "states":
													{
														string[] s = b.Split(',');
														if (s.Length >= 1)
														{
															string Folder = System.IO.Path.GetDirectoryName(FileName);
															StateFiles = new string[s.Length];
															bool NullObject = true;
															for (int k = 0; k < s.Length; k++)
															{
																s[k] = s[k].Trim();
																if (s[k].Length == 0)
																{
																	Interface.AddMessage(Interface.MessageType.Error, false, "File" + k.ToString(Culture) + " is an empty string - did you mean something else? - in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
																	StateFiles[k] = null;
																}
																else if (Interface.ContainsInvalidPathChars(s[k]))
																{
																	Interface.AddMessage(Interface.MessageType.Error, false, "File" + k.ToString(Culture) + " contains illegal characters in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
																	StateFiles[k] = null;
																}
																else
																{
																	StateFiles[k] = OpenBveApi.Path.CombineFile(Folder, s[k]);
																	if (!System.IO.File.Exists(StateFiles[k]))
																	{
																		Interface.AddMessage(Interface.MessageType.Error, true, "File " + StateFiles[k] + " not found in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
																		StateFiles[k] = null;
																	}
																}
																if (StateFiles[k] != null)
																{
																	NullObject = false;
																}	
															}
															if (NullObject == true)
															{
																Interface.AddMessage(Interface.MessageType.Error, false, "None of the specified files were found in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
																return null;
															}
														}
														else
														{
															Interface.AddMessage(Interface.MessageType.Error, false, "At least one argument is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
															return null;
														}
													} break;
												case "statefunction":
													try
													{
														StateFunctionLine = i;
														StateFunctionRpn = FunctionScripts.GetPostfixNotationFromInfixNotation(b);
													}
													catch (Exception ex)
													{
														Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													} break;
												case "statefunctionrpn":
													{
														StateFunctionLine = i;
														StateFunctionRpn = b;
													} break;
												case "translatexdirection":
												case "translateydirection":
												case "translatezdirection":
													{
														string[] s = b.Split(',');
														if (s.Length == 3)
														{
															double x, y, z;
															if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x))
															{
																Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
															}
															else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y))
															{
																Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
															}
															else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z))
															{
																Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
															}
															else
															{
																switch (a.ToLowerInvariant())
																{
																	case "translatexdirection":
																		Result.Objects[ObjectCount].TranslateXDirection = new Vector3(x, y, z);
																		break;
																	case "translateydirection":
																		Result.Objects[ObjectCount].TranslateYDirection = new Vector3(x, y, z);
																		break;
																	case "translatezdirection":
																		Result.Objects[ObjectCount].TranslateZDirection = new Vector3(x, y, z);
																		break;
																}
															}
														}
														else
														{
															Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
														}
													} break;
												case "translatexfunction":
													try
													{
														double X;
														if (double.TryParse(b, NumberStyles.Float, Culture, out X))
														{
															Position.X = X;
															//A function script must be evaluated every frame, no matter if it is a constant value
															//If we add this to the position instead, this gives a minor speedup
															break;
														}
														Result.Objects[ObjectCount].TranslateXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
													}
													catch (Exception ex)
													{
														Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													} break;
												case "translatexscript":
													try
													{
														Result.Objects[ObjectCount].TranslateXScriptFile = OpenBveApi.Path.CombineDirectory(System.IO.Path.GetDirectoryName(FileName), b);
													}
													catch (Exception ex)
													{
														Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													} break;
												case "translateyfunction":
													try
													{
														double Y;
														if (double.TryParse(b, NumberStyles.Float, Culture, out Y))
														{
															Position.Y = Y;
															//A function script must be evaluated every frame, no matter if it is a constant value
															//If we add this to the position instead, this gives a minor speedup
															break;
														}
														Result.Objects[ObjectCount].TranslateYFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
													}
													catch (Exception ex)
													{
														Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													} break;
												case "translateyscript":
													try
													{
														Result.Objects[ObjectCount].TranslateYScriptFile = OpenBveApi.Path.CombineDirectory(System.IO.Path.GetDirectoryName(FileName), b);
													}
													catch (Exception ex)
													{
														Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													} break;
												case "translatezfunction":
													try
													{
														double Z;
														if (double.TryParse(b, NumberStyles.Float, Culture, out Z))
														{
															Position.Z = Z;
															//A function script must be evaluated every frame, no matter if it is a constant value
															//If we add this to the position instead, this gives a minor speedup
															break;
														}
														Result.Objects[ObjectCount].TranslateZFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
													}
													catch (Exception ex)
													{
														Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													} break;
												case "translatezscript":
													try
													{
														Result.Objects[ObjectCount].TranslateZScriptFile = OpenBveApi.Path.CombineDirectory(System.IO.Path.GetDirectoryName(FileName), b);
													}
													catch (Exception ex)
													{
														Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													} break;
												case "trackfollowerfunction":
													try
													{
														Result.Objects[ObjectCount].TrackFollowerFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
													}
													catch (Exception ex)
													{
														Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													} break;
												case "axles":
													try
													{
														double FrontAxlePosition;
														double RearAxlePosition;
														var splitValue = b.Split(',');
														Double.TryParse(splitValue[0], out FrontAxlePosition);
														Double.TryParse(splitValue[1], out RearAxlePosition);
														if (FrontAxlePosition > RearAxlePosition)
														{
															Result.Objects[ObjectCount].FrontAxlePosition = FrontAxlePosition;
															Result.Objects[ObjectCount].RearAxlePosition = RearAxlePosition;
														}
														else if (FrontAxlePosition < RearAxlePosition)
														{
															Interface.AddMessage(Interface.MessageType.Error, false,"Rear is expected to be less than Front in " + a + " at line " + (i + 1).ToString(Culture) + " in file " +FileName);
														}
														else
														{
															Interface.AddMessage(Interface.MessageType.Error, false, "Rear must not equal Front in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
														}

													}
													catch(Exception ex)
													{
														Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													}
													break;
													/*
													 * RPN Functions were added by Michelle, and she stated that they should not be used other than in debugging
													 * Not aware of any uses, but these should stay there anyway
													 * 
													 */
												case "translatexfunctionrpn":
													try
													{
														Result.Objects[ObjectCount].TranslateXFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
													}
													catch (Exception ex)
													{
														Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													} break;
												case "translateyfunctionrpn":
													try
													{
														Result.Objects[ObjectCount].TranslateYFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
													}
													catch (Exception ex)
													{
														Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													} break;
												case "translatezfunctionrpn":
													try
													{
														Result.Objects[ObjectCount].TranslateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
													}
													catch (Exception ex)
													{
														Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													} break;
												case "rotatexdirection":
												case "rotateydirection":
												case "rotatezdirection":
													{
														string[] s = b.Split(',');
														if (s.Length == 3)
														{
															double x, y, z;
															if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x))
															{
																Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
															}
															else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y))
															{
																Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
															}
															else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z))
															{
																Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
															}
															else if (x == 0.0 & y == 0.0 & z == 0.0)
															{
																Interface.AddMessage(Interface.MessageType.Error, false, "The direction indicated by X, Y and Z is expected to be non-zero in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
															}
															else
															{
																switch (a.ToLowerInvariant())
																{
																	case "rotatexdirection":
																		Result.Objects[ObjectCount].RotateXDirection = new Vector3(x, y, z);
																		break;
																	case "rotateydirection":
																		Result.Objects[ObjectCount].RotateYDirection = new Vector3(x, y, z);
																		break;
																	case "rotatezdirection":
																		Result.Objects[ObjectCount].RotateZDirection = new Vector3(x, y, z);
																		break;
																}
															}
														}
														else
														{
															Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
														}
													} break;
												case "rotatexfunction":
													try
													{
														if (double.TryParse(b, NumberStyles.Float, Culture, out RotateX))
														{
															//A function script must be evaluated every frame, no matter if it is a constant value
															//If we add this to the position instead, this gives a minor speedup
															StaticXRotation = true;
														}
														Result.Objects[ObjectCount].RotateXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
													}
													catch (Exception ex)
													{
														Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													} break;
												case "rotateyfunction":
													try
													{
														if (double.TryParse(b, NumberStyles.Float, Culture, out RotateY))
														{
															StaticYRotation = true;
														}
														Result.Objects[ObjectCount].RotateYFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
													}
													catch (Exception ex)
													{
														Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													} break;
												case "rotatezfunction":
													try
													{
														if (double.TryParse(b, NumberStyles.Float, Culture, out RotateZ))
														{
															StaticZRotation = true;
														}
														Result.Objects[ObjectCount].RotateZFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
													}
													catch (Exception ex)
													{
														Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													} break;
												case "rotatexfunctionrpn":
													try
													{
														Result.Objects[ObjectCount].RotateXFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
													}
													catch (Exception ex)
													{
														Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													} break;
												case "rotateyfunctionrpn":
													try
													{
														Result.Objects[ObjectCount].RotateYFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
													}
													catch (Exception ex)
													{
														Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													} break;
												case "rotatezfunctionrpn":
													try
													{
														Result.Objects[ObjectCount].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
													}
													catch (Exception ex)
													{
														Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													} break;
												case "rotatexdamping":
												case "rotateydamping":
												case "rotatezdamping":
													{
														string[] s = b.Split(',');
														if (s.Length == 2)
														{
															double nf, dr;
															if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out nf))
															{
																Interface.AddMessage(Interface.MessageType.Error, false, "NaturalFrequency is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
															}
															else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out dr))
															{
																Interface.AddMessage(Interface.MessageType.Error, false, "DampingRatio is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
															}
															else if (nf <= 0.0)
															{
																Interface.AddMessage(Interface.MessageType.Error, false, "NaturalFrequency is expected to be positive in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
															}
															else if (dr <= 0.0)
															{
																Interface.AddMessage(Interface.MessageType.Error, false, "DampingRatio is expected to be positive in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
															}
															else
															{
																switch (a.ToLowerInvariant())
																{
																	case "rotatexdamping":
																		Result.Objects[ObjectCount].RotateXDamping = new ObjectManager.Damping(nf, dr);
																		break;
																	case "rotateydamping":
																		Result.Objects[ObjectCount].RotateYDamping = new ObjectManager.Damping(nf, dr);
																		break;
																	case "rotatezdamping":
																		Result.Objects[ObjectCount].RotateZDamping = new ObjectManager.Damping(nf, dr);
																		break;
																}
															}
														}
														else
														{
															Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 2 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
														}
													} break;
												case "textureshiftxdirection":
												case "textureshiftydirection":
													{
														string[] s = b.Split(',');
														if (s.Length == 2)
														{
															double x, y;
															if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x))
															{
																Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
															}
															else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y))
															{
																Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
															}
															else
															{
																switch (a.ToLowerInvariant())
																{
																	case "textureshiftxdirection":
																		Result.Objects[ObjectCount].TextureShiftXDirection = new Vector2(x, y);
																		break;
																	case "textureshiftydirection":
																		Result.Objects[ObjectCount].TextureShiftYDirection = new Vector2(x, y);
																		break;
																}
															}
														}
														else
														{
															Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 2 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
														}
													} break;
												case "textureshiftxfunction":
													try
													{
														Result.Objects[ObjectCount].TextureShiftXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
													}
													catch (Exception ex)
													{
														Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													} break;
												case "textureshiftyfunction":
													try
													{
														Result.Objects[ObjectCount].TextureShiftYFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b);
													}
													catch (Exception ex)
													{
														Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													} break;
												case "textureshiftxfunctionrpn":
													try
													{
														Result.Objects[ObjectCount].TextureShiftXFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
													}
													catch (Exception ex)
													{
														Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													} break;
												case "textureshiftyfunctionrpn":
													try
													{
														Result.Objects[ObjectCount].TextureShiftYFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b);
													}
													catch (Exception ex)
													{
														Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													} break;
												case "textureoverride":
													switch (b.ToLowerInvariant())
													{
														case "none":
															break;
														case "timetable":
															if (!timetableUsed)
															{
																Timetable.AddObjectForCustomTimetable(Result.Objects[ObjectCount]);
																timetableUsed = true;
															}
															break;
														default:
															Interface.AddMessage(Interface.MessageType.Error, false, "Unrecognized value in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
															break;
													}
													break;
												case "refreshrate":
													{
														double r;
														if (!double.TryParse(b, System.Globalization.NumberStyles.Float, Culture, out r))
														{
															Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
														}
														else if (r < 0.0)
														{
															Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be non-negative in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName);
														}
														else
														{
															Result.Objects[ObjectCount].RefreshRate = r;
														}
													} break;
												default:
													Interface.AddMessage(Interface.MessageType.Error, false, "The attribute " + a + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName);
													break;
											}
										}
										else
										{
											Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
											return null;
										}
									}
									i++;
								}
								i--;
								if (StateFiles != null)
								{
									// create the object
									if (timetableUsed)
									{
										if (StateFunctionRpn != null)
										{
											StateFunctionRpn = "timetable 0 == " + StateFunctionRpn + " -1 ?";
										}
										else
										{
											StateFunctionRpn = "timetable";
										}
									}
									if (StateFunctionRpn != null)
									{
										try
										{
											Result.Objects[ObjectCount].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(StateFunctionRpn);
										}
										catch (Exception ex)
										{
											Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in StateFunction at line " + (StateFunctionLine + 1).ToString(Culture) + " in file " + FileName);
										}
									}
									Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[StateFiles.Length];
									bool ForceTextureRepeatX = Result.Objects[ObjectCount].TextureShiftXFunction != null & Result.Objects[ObjectCount].TextureShiftXDirection.X != 0.0 |
										Result.Objects[ObjectCount].TextureShiftYFunction != null & Result.Objects[ObjectCount].TextureShiftYDirection.Y != 0.0;
									bool ForceTextureRepeatY = Result.Objects[ObjectCount].TextureShiftXFunction != null & Result.Objects[ObjectCount].TextureShiftXDirection.X != 0.0 |
										Result.Objects[ObjectCount].TextureShiftYFunction != null & Result.Objects[ObjectCount].TextureShiftYDirection.Y != 0.0;
									for (int k = 0; k < StateFiles.Length; k++)
									{
										Result.Objects[ObjectCount].States[k].Position = new Vector3(0.0, 0.0, 0.0);
										if (StateFiles[k] != null)
										{
											Result.Objects[ObjectCount].States[k].Object = ObjectManager.LoadStaticObject(StateFiles[k], Encoding, LoadMode, false, ForceTextureRepeatX, ForceTextureRepeatY);
											if (Result.Objects[ObjectCount].States[k].Object != null)
											{
												Result.Objects[ObjectCount].States[k].Object.Dynamic = true;
											}
										}
										else
										{
											Result.Objects[ObjectCount].States[k].Object = null;
										}
										
									}
									for (int j = 0; j < Result.Objects[ObjectCount].States.Length; j++)
									{

										//Rotate X
										if (Result.Objects[ObjectCount].States[j].Object == null)
										{
											continue;
										}
										for (int l = 0; l < Result.Objects[ObjectCount].States[j].Object.Mesh.Vertices.Length; l++)
										{
											//Apply position
											Result.Objects[ObjectCount].States[j].Position = Position;
											//Test whether the object contains non static rotation functions
											//If so, the results may be off so don't optimise
											if (!StaticXRotation)
											{
												if (Result.Objects[ObjectCount].RotateXFunction != null)
												{
													break;
												}
											}
											if (!StaticYRotation)
											{
												if (Result.Objects[ObjectCount].RotateYFunction != null)
												{
													break;
												}
											}
											if (!StaticZRotation)
											{
												if (Result.Objects[ObjectCount].RotateZFunction != null)
												{
													break;
												}
											}
											if (StaticXRotation)
											{
												double x = Result.Objects[ObjectCount].RotateXDirection.X;
												double y = Result.Objects[ObjectCount].RotateXDirection.Y;
												double z = Result.Objects[ObjectCount].RotateXDirection.Z;
												double t = x * x + y * y + z * z;
												if (t == 0.0)
												{
													x = 1.0;
													y = 0.0;
													z = 0.0;
													t = 1.0;
												}
												if (RotateX != 0.0)
												{
													t = 1.0 / Math.Sqrt(t);
													x *= t;
													y *= t;
													z *= t;
												}
												double cosX = Math.Cos(RotateX);
												double sinX = Math.Sin(RotateX);
												World.Rotate(ref Result.Objects[ObjectCount].States[j].Object.Mesh.Vertices[l].Coordinates, x, y, z, cosX, sinX);
												Result.Objects[ObjectCount].RotateXFunction = null;
											}
											if (StaticYRotation)
											{
												double x = Result.Objects[ObjectCount].RotateYDirection.X;
												double y = Result.Objects[ObjectCount].RotateYDirection.Y;
												double z = Result.Objects[ObjectCount].RotateYDirection.Z;
												double t = x * x + y * y + z * z;
												if (t == 0.0)
												{
													x = 1.0;
													y = 0.0;
													z = 0.0;
													t = 1.0;
												}
												if (RotateY != 0.0)
												{
													t = 1.0 / Math.Sqrt(t);
													x *= t;
													y *= t;
													z *= t;
												}
												double cosY = Math.Cos(RotateY);
												double sinY = Math.Sin(RotateY);
												World.Rotate(ref Result.Objects[ObjectCount].States[j].Object.Mesh.Vertices[l].Coordinates, x, y, z, cosY, sinY);
												Result.Objects[ObjectCount].RotateYFunction = null;
											}
											if (StaticZRotation)
											{
												double x = Result.Objects[ObjectCount].RotateZDirection.X;
												double y = Result.Objects[ObjectCount].RotateZDirection.Y;
												double z = Result.Objects[ObjectCount].RotateZDirection.Z;
												double t = x * x + y * y + z * z;
												if (t == 0.0)
												{
													x = 1.0;
													y = 0.0;
													z = 0.0;
													t = 1.0;
												}
												if (RotateZ != 0.0)
												{
													t = 1.0 / Math.Sqrt(t);
													x *= t;
													y *= t;
													z *= t;
												}
												double cosZ = Math.Cos(RotateZ);
												double sinZ = Math.Sin(RotateZ);
												World.Rotate(ref Result.Objects[ObjectCount].States[j].Object.Mesh.Vertices[l].Coordinates, x, y, z, cosZ, sinZ);
												Result.Objects[ObjectCount].RotateZFunction = null;
											}

											
										}
									}
								}
								else
								{
									Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[] { };
								}
								ObjectCount++;
							}
							break;
						default:
							Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName);
							return null;
					}
				}
			}
			Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, ObjectCount);
			return Result;
		}
Example #20
0
 // get mirrored object
 private static ObjectManager.UnifiedObject GetMirroredObject(ObjectManager.UnifiedObject Prototype)
 {
     if (Prototype is ObjectManager.StaticObject) {
         ObjectManager.StaticObject s = (ObjectManager.StaticObject)Prototype;
         return GetMirroredStaticObject(s);
     } else if (Prototype is ObjectManager.AnimatedObjectCollection) {
         ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)Prototype;
         ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection();
         Result.Objects = new ObjectManager.AnimatedObject[a.Objects.Length];
         for (int i = 0; i < a.Objects.Length; i++) {
             Result.Objects[i] = a.Objects[i].Clone();
             for (int j = 0; j < a.Objects[i].States.Length; j++) {
                 Result.Objects[i].States[j].Object = GetMirroredStaticObject(a.Objects[i].States[j].Object);
             }
             Result.Objects[i].TranslateXDirection.X *= -1.0;
             Result.Objects[i].TranslateYDirection.X *= -1.0;
             Result.Objects[i].TranslateZDirection.X *= -1.0;
             Result.Objects[i].RotateXDirection.X *= -1.0;
             Result.Objects[i].RotateYDirection.X *= -1.0;
             Result.Objects[i].RotateZDirection.X *= -1.0;
         }
         return Result;
     } else {
         return null;
     }
 }
Example #21
0
        private static void LoadEverythingThreaded()
        {
            Program.AppendToLogFile("Loading route file: " + CurrentRouteFile);
            string RailwayFolder = GetRailwayFolder(CurrentRouteFile);
            string ObjectFolder  = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Object");
            string SoundFolder   = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Sound");

            // reset
            Game.Reset(true);
            Game.MinimalisticSimulation = true;
            // screen
            World.CameraTrackFollower = new TrackManager.TrackFollower {
                Train = null, CarIndex = -1
            };
            World.CameraMode = World.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.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;
            }
            Game.CalculateSeaLevelConstants();
            if (Game.BogusPretrainInstructions.Length != 0)
            {
                double t = Game.BogusPretrainInstructions[0].Time;
                double p = Game.BogusPretrainInstructions[0].TrackPosition;
                for (int i = 1; i < Game.BogusPretrainInstructions.Length; i++)
                {
                    if (Game.BogusPretrainInstructions[i].Time > t)
                    {
                        t = Game.BogusPretrainInstructions[i].Time;
                    }
                    else
                    {
                        t += 1.0;
                        Game.BogusPretrainInstructions[i].Time = t;
                    }
                    if (Game.BogusPretrainInstructions[i].TrackPosition > p)
                    {
                        p = Game.BogusPretrainInstructions[i].TrackPosition;
                    }
                    else
                    {
                        p += 1.0;
                        Game.BogusPretrainInstructions[i].TrackPosition = p;
                    }
                }
            }
            if (Game.Stations.Length == 1)
            {
                //Log the fact that only a single station is present, as this is probably not right
                Program.AppendToLogFile("The processed route file only contains a single station.");
            }
            Program.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 + (Game.BogusPretrainInstructions.Length != 0 ? 1 : 0)];
            for (int k = 0; k < TrainManager.Trains.Length; k++)
            {
                TrainManager.Trains[k] = new TrainManager.Train {
                    TrainIndex = k
                };
                if (k == TrainManager.Trains.Length - 1 & Game.BogusPretrainInstructions.Length != 0)
                {
                    TrainManager.Trains[k].State = TrainManager.TrainState.Bogus;
                }
                else
                {
                    TrainManager.Trains[k].State = TrainManager.TrainState.Pending;
                }
            }
            TrainManager.PlayerTrain = TrainManager.Trains[Game.PrecedingTrainTimeDeltas.Length];
            // load trains
            double TrainProgressMaximum = 0.7 + 0.3 * (double)TrainManager.Trains.Length;

            for (int k = 0; k < TrainManager.Trains.Length; k++)
            {
                //Sleep for 10ms to allow route loading locks to release
                Thread.Sleep(20);
                if (TrainManager.Trains[k].State == TrainManager.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
                {
                    // real train
                    Program.AppendToLogFile("Loading player train: " + CurrentTrainFolder);
                    TrainProgressCurrentWeight = 0.1 / TrainProgressMaximum;
                    string TrainData = OpenBveApi.Path.CombineFile(CurrentTrainFolder, "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(CurrentTrainFolder, 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].Sounds.DoorOpenL.Buffer != null & TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer != null)
                            {
                                Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer);
                                Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer);
                                double a = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer.Duration;
                                double b = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.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].Sounds.DoorOpenL.Buffer != null)
                            {
                                Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer);
                                double a = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer.Duration;
                                TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = a > 0.0 ? 1.0 / a : 0.8;
                            }
                            else if (TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer != null)
                            {
                                Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer);
                                double b = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.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].Sounds.DoorCloseL.Buffer != null & TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer != null)
                            {
                                Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer);
                                Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer);
                                double a = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer.Duration;
                                double b = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.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].Sounds.DoorCloseL.Buffer != null)
                            {
                                Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer);
                                double a = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer.Duration;
                                TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = a > 0.0 ? 1.0 / a : 0.8;
                            }
                            else if (TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer != null)
                            {
                                Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer);
                                double b = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.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;
                    }
                }
                for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++)
                {
                    TrainManager.Trains[k].Cars[i].FrontAxle.Follower.Train = TrainManager.Trains[k];
                    TrainManager.Trains[k].Cars[i].RearAxle.Follower.Train  = TrainManager.Trains[k];
                    TrainManager.Trains[k].Cars[i].BeaconReceiver.Train     = TrainManager.Trains[k];
                }
                // add panel section
                if (k == TrainManager.PlayerTrain.TrainIndex)
                {
                    TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections             = new TrainManager.CarSection[1];
                    TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections[0].Elements = new ObjectManager.AnimatedObject[] { };
                    TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections[0].Overlay  = true;
                    TrainProgressCurrentWeight = 0.7 / TrainProgressMaximum;
                    TrainManager.ParsePanelConfig(CurrentTrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]);
                    TrainProgressCurrentSum += TrainProgressCurrentWeight;
                    System.Threading.Thread.Sleep(1); if (Cancel)
                    {
                        return;
                    }
                    Program.AppendToLogFile("Train panel loaded sucessfully.");
                }
                // add exterior section
                if (TrainManager.Trains[k].State != TrainManager.TrainState.Bogus)
                {
                    ObjectManager.UnifiedObject[] CarObjects;
                    ObjectManager.UnifiedObject[] BogieObjects;
                    ExtensionsCfgParser.ParseExtensionsConfig(CurrentTrainFolder, CurrentTrainEncoding, out CarObjects, out BogieObjects, TrainManager.Trains[k]);
                    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");
                            ObjectManager.StaticObject so = ObjectManager.LoadStaticObject(file, System.Text.Encoding.UTF8, ObjectManager.ObjectLoadMode.Normal, false, false, 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;
                                CsvB3dObjectParser.ApplyScale(so, sx, sy, sz);
                                CarObjects[i] = so;
                            }
                        }
                        if (CarObjects[i] != null)
                        {
                            // add object
                            int j = TrainManager.Trains[k].Cars[i].CarSections.Length;
                            Array.Resize <TrainManager.CarSection>(ref TrainManager.Trains[k].Cars[i].CarSections, j + 1);
                            if (CarObjects[i] is ObjectManager.StaticObject)
                            {
                                ObjectManager.StaticObject s = (ObjectManager.StaticObject)CarObjects[i];
                                TrainManager.Trains[k].Cars[i].CarSections[j].Elements                       = new ObjectManager.AnimatedObject[1];
                                TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0]                    = new ObjectManager.AnimatedObject();
                                TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States             = new ObjectManager.AnimatedObjectState[1];
                                TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States[0].Position = new Vector3(0.0, 0.0, 0.0);
                                TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States[0].Object   = s;
                                TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].CurrentState       = 0;
                                TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].ObjectIndex        = ObjectManager.CreateDynamicObject();
                            }
                            else if (CarObjects[i] is ObjectManager.AnimatedObjectCollection)
                            {
                                ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)CarObjects[i];
                                TrainManager.Trains[k].Cars[i].CarSections[j].Elements = new ObjectManager.AnimatedObject[a.Objects.Length];
                                for (int h = 0; h < a.Objects.Length; h++)
                                {
                                    TrainManager.Trains[k].Cars[i].CarSections[j].Elements[h]             = a.Objects[h];
                                    TrainManager.Trains[k].Cars[i].CarSections[j].Elements[h].ObjectIndex = ObjectManager.CreateDynamicObject();
                                }
                            }
                        }

                        //Load bogie objects
                        if (BogieObjects[currentBogieObject] != null)
                        {
                            int j = TrainManager.Trains[k].Cars[i].FrontBogie.CarSections.Length;
                            Array.Resize <TrainManager.CarSection>(ref TrainManager.Trains[k].Cars[i].FrontBogie.CarSections, j + 1);
                            if (BogieObjects[currentBogieObject] is ObjectManager.StaticObject)
                            {
                                ObjectManager.StaticObject s = (ObjectManager.StaticObject)BogieObjects[currentBogieObject];
                                TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements                       = new ObjectManager.AnimatedObject[1];
                                TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[0]                    = new ObjectManager.AnimatedObject();
                                TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[0].States             = new ObjectManager.AnimatedObjectState[1];
                                TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[0].States[0].Position = new Vector3(0.0, 0.0, 0.0);
                                TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[0].States[0].Object   = s;
                                TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[0].CurrentState       = 0;
                                TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[0].ObjectIndex        = ObjectManager.CreateDynamicObject();
                            }
                            else if (BogieObjects[currentBogieObject] is ObjectManager.AnimatedObjectCollection)
                            {
                                ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)BogieObjects[currentBogieObject];
                                TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements = new ObjectManager.AnimatedObject[a.Objects.Length];
                                for (int h = 0; h < a.Objects.Length; h++)
                                {
                                    TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[h]             = a.Objects[h];
                                    TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[h].ObjectIndex = ObjectManager.CreateDynamicObject();
                                }
                            }
                        }
                        currentBogieObject++;
                        //Can't think of a better way to do this than two functions......
                        if (BogieObjects[currentBogieObject] != null)
                        {
                            int j = TrainManager.Trains[k].Cars[i].RearBogie.CarSections.Length;
                            Array.Resize <TrainManager.CarSection>(ref TrainManager.Trains[k].Cars[i].RearBogie.CarSections, j + 1);
                            if (BogieObjects[currentBogieObject] is ObjectManager.StaticObject)
                            {
                                ObjectManager.StaticObject s = (ObjectManager.StaticObject)BogieObjects[currentBogieObject];
                                TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements                       = new ObjectManager.AnimatedObject[1];
                                TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[0]                    = new ObjectManager.AnimatedObject();
                                TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[0].States             = new ObjectManager.AnimatedObjectState[1];
                                TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[0].States[0].Position = new Vector3(0.0, 0.0, 0.0);
                                TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[0].States[0].Object   = s;
                                TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[0].CurrentState       = 0;
                                TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[0].ObjectIndex        = ObjectManager.CreateDynamicObject();
                            }
                            else if (BogieObjects[currentBogieObject] is ObjectManager.AnimatedObjectCollection)
                            {
                                ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)BogieObjects[currentBogieObject];
                                TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements = new ObjectManager.AnimatedObject[a.Objects.Length];
                                for (int h = 0; h < a.Objects.Length; h++)
                                {
                                    TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[h]             = a.Objects[h];
                                    TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[h].ObjectIndex = ObjectManager.CreateDynamicObject();
                                }
                            }
                        }
                        currentBogieObject++;
                    }
                }
                // place cars
                {
                    double z = 0.0;
                    for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++)
                    {
                        //Front axle track position
                        TrainManager.Trains[k].Cars[i].FrontAxle.Follower.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].FrontAxle.Position;
                        //Bogie for front axle
                        TrainManager.Trains[k].Cars[i].FrontBogie.FrontAxle.Follower.TrackPosition = TrainManager.Trains[k].Cars[i].FrontAxle.Follower.TrackPosition - 0.5 * TrainManager.Trains[k].Cars[i].FrontBogie.Length + TrainManager.Trains[k].Cars[i].FrontBogie.FrontAxle.Position;
                        TrainManager.Trains[k].Cars[i].FrontBogie.RearAxle.Follower.TrackPosition  = TrainManager.Trains[k].Cars[i].FrontAxle.Follower.TrackPosition - 0.5 * TrainManager.Trains[k].Cars[i].FrontBogie.Length + TrainManager.Trains[k].Cars[i].FrontBogie.RearAxle.Position;
                        //Rear axle track position
                        TrainManager.Trains[k].Cars[i].RearAxle.Follower.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].RearAxle.Position;
                        //Bogie for rear axle
                        TrainManager.Trains[k].Cars[i].RearBogie.FrontAxle.Follower.TrackPosition = TrainManager.Trains[k].Cars[i].RearAxle.Follower.TrackPosition - 0.5 * TrainManager.Trains[k].Cars[i].RearBogie.Length + TrainManager.Trains[k].Cars[i].RearBogie.FrontAxle.Position;
                        TrainManager.Trains[k].Cars[i].RearBogie.RearAxle.Follower.TrackPosition  = TrainManager.Trains[k].Cars[i].RearAxle.Follower.TrackPosition - 0.5 * TrainManager.Trains[k].Cars[i].RearBogie.Length + TrainManager.Trains[k].Cars[i].RearBogie.RearAxle.Position;
                        //Beacon reciever (AWS, ATC etc.)
                        TrainManager.Trains[k].Cars[i].BeaconReceiver.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].BeaconReceiverPosition;
                        z -= TrainManager.Trains[k].Cars[i].Length;
                        if (i < TrainManager.Trains[k].Cars.Length - 1)
                        {
                            z -= 0.5 * (TrainManager.Trains[k].Couplers[i].MinimumDistanceBetweenCars + TrainManager.Trains[k].Couplers[i].MaximumDistanceBetweenCars);
                        }
                    }
                }
                // configure ai / timetable
                if (TrainManager.Trains[k] == TrainManager.PlayerTrain)
                {
                    TrainManager.Trains[k].TimetableDelta = 0.0;
                }
                else if (TrainManager.Trains[k].State != TrainManager.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;
                }
            }
            TrainProgress = 1.0;
            // finished created objects
            System.Threading.Thread.Sleep(1); if (Cancel)
            {
                return;
            }
            ObjectManager.FinishCreatingObjects();
            // update sections
            if (Game.Sections.Length > 0)
            {
                Game.UpdateSection(Game.Sections.Length - 1);
            }
            // load plugin
            for (int i = 0; i < TrainManager.Trains.Length; i++)
            {
                if (TrainManager.Trains[i].State != TrainManager.TrainState.Bogus)
                {
                    if (TrainManager.Trains[i] == TrainManager.PlayerTrain)
                    {
                        if (!PluginManager.LoadCustomPlugin(TrainManager.Trains[i], CurrentTrainFolder, CurrentTrainEncoding))
                        {
                            PluginManager.LoadDefaultPlugin(TrainManager.Trains[i], CurrentTrainFolder);
                        }
                    }
                    else
                    {
                        PluginManager.LoadDefaultPlugin(TrainManager.Trains[i], CurrentTrainFolder);
                    }
                }
            }
        }
Example #22
0
        /// <summary>Attempts to load and parse the current train's panel configuration file.</summary>
        /// <param name="TrainPath">The absolute on-disk path to the train folder.</param>
        /// <param name="Encoding">The automatically detected or manually set encoding of the panel configuration file.</param>
        /// <param name="Train">The base train on which to apply the panel configuration.</param>
        internal static void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, Train Train)
        {
            Train.Cars[Train.DriverCar].CarSections    = new CarSection[1];
            Train.Cars[Train.DriverCar].CarSections[0] = new CarSection
            {
                Elements = new ObjectManager.AnimatedObject[] { },
                Overlay  = true
            };
            string File = OpenBveApi.Path.CombineFile(TrainPath, "panel.animated");

            if (System.IO.File.Exists(File))
            {
                Program.FileSystem.AppendToLogFile("Loading train panel: " + File);
                if (System.IO.File.Exists(OpenBveApi.Path.CombineFile(TrainPath, "panel2.cfg")) || System.IO.File.Exists(OpenBveApi.Path.CombineFile(TrainPath, "panel.cfg")))
                {
                    Program.FileSystem.AppendToLogFile("INFO: This train contains both a 2D and a 3D panel. The 3D panel will always take precedence");
                }
                ObjectManager.AnimatedObjectCollection a = AnimatedObjectParser.ReadObject(File, Encoding);
                if (a != null)
                {
                    //HACK: If a == null , loading our animated object completely failed (Missing objects?). Fallback to trying the panel2.cfg
                    try
                    {
                        for (int i = 0; i < a.Objects.Length; i++)
                        {
                            a.Objects[i].ObjectIndex = ObjectManager.CreateDynamicObject();
                        }
                        Train.Cars[Train.DriverCar].CarSections[0].Elements = a.Objects;
                        Train.Cars[Train.DriverCar].CameraRestrictionMode   = Camera.RestrictionMode.NotAvailable;
                        World.CameraRestriction = Camera.RestrictionMode.NotAvailable;
                        World.UpdateViewingDistances();
                        return;
                    }
                    catch
                    {
                        var currentError = Translations.GetInterfaceString("errors_critical_file");
                        currentError = currentError.Replace("[file]", "panel.animated");
                        MessageBox.Show(currentError, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand);
                        Program.RestartArguments = " ";
                        Loading.Cancel           = true;
                        return;
                    }
                }
                Interface.AddMessage(MessageType.Error, false, "The panel.animated file " + File + " failed to load. Falling back to 2D panel.");
            }

            var Panel2 = false;

            try
            {
                File = OpenBveApi.Path.CombineFile(TrainPath, "panel2.cfg");
                if (System.IO.File.Exists(File))
                {
                    Program.FileSystem.AppendToLogFile("Loading train panel: " + File);
                    Panel2 = true;
                    Panel2CfgParser.ParsePanel2Config("panel2.cfg", TrainPath, Encoding, Train, Train.DriverCar);
                    Train.Cars[Train.DriverCar].CameraRestrictionMode = Camera.RestrictionMode.On;
                    World.CameraRestriction = Camera.RestrictionMode.On;
                }
                else
                {
                    File = OpenBveApi.Path.CombineFile(TrainPath, "panel.cfg");
                    if (System.IO.File.Exists(File))
                    {
                        Program.FileSystem.AppendToLogFile("Loading train panel: " + File);
                        PanelCfgParser.ParsePanelConfig(TrainPath, Encoding, Train);
                        Train.Cars[Train.DriverCar].CameraRestrictionMode = Camera.RestrictionMode.On;
                        World.CameraRestriction = Camera.RestrictionMode.On;
                    }
                    else
                    {
                        World.CameraRestriction = Camera.RestrictionMode.NotAvailable;
                    }
                }
            }
            catch
            {
                var currentError = Translations.GetInterfaceString("errors_critical_file");
                currentError = currentError.Replace("[file]", Panel2 == true ? "panel2.cfg" : "panel.cfg");
                MessageBox.Show(currentError, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand);
                Program.RestartArguments = " ";
                Loading.Cancel           = true;
            }
        }