/******************** PUBLIC METHODS *********************/ // // PROCESS COMMAND // /// <summary>Processes commands.</summary> /// <returns><c>true</c>, if command was processed, <c>false</c> otherwise.</returns> /// <param name="command">The Interface.Command command to process.</param> internal bool ProcessCommand(Interface.Command command) { if (command != Interface.Command.RouteInformation) // only accept RouteInformation command return false; // cycle through available state setState( (state)((int)(currentState + 1) % (int)state.numOf) ); return true; }
/// <summary>Adds a message to the in-game interface render queue</summary> /// <param name="Text">The text of the message</param> /// <param name="Depencency"></param> /// <param name="Mode"></param> /// <param name="Color">The color of the message text</param> /// <param name="Timeout">The time this message will display for</param> internal static void AddMessage(string Text, MessageDependency Depencency, Interface.GameMode Mode, MessageColor Color, double Timeout) { if (Interface.CurrentOptions.GameMode <= Mode) { if (Depencency == MessageDependency.RouteLimit | Depencency == MessageDependency.SectionLimit) { for (int i = 0; i < Messages.Length; i++) { if (Messages[i].Depencency == Depencency) return; } } int n = Messages.Length; Array.Resize<Message>(ref Messages, n + 1); Messages[n].InternalText = Text; Messages[n].DisplayText = ""; Messages[n].Depencency = Depencency; Messages[n].Timeout = Timeout; Messages[n].Color = Color; Messages[n].RendererPosition = new Vector2(0.0, 0.0); Messages[n].RendererAlpha = 0.0; } }
private static void LoadMaterials(string FileName, ref Material[] Materials) { string[] Lines = File.ReadAllLines(FileName); Material mm = new Material(); bool fm = false; //Preprocess for (int i = 0; i < Lines.Length; i++) { // Strip hash comments int c = Lines[i].IndexOf("#", StringComparison.Ordinal); if (c >= 0) { Lines[i] = Lines[i].Substring(0, c); } // collect arguments List <string> Arguments = new List <string>(Lines[i].Split(new char[] { ' ', '\t' }, StringSplitOptions.None)); for (int j = Arguments.Count - 1; j >= 0; j--) { Arguments[j] = Arguments[j].Trim(); if (Arguments[j] == string.Empty) { Arguments.RemoveAt(j); } } if (Arguments.Count == 0) { continue; } switch (Arguments[0].ToLowerInvariant()) { case "newmtl": if (fm == true) { Array.Resize(ref Materials, Materials.Length + 1); Materials[Materials.Length - 1] = mm; } mm = new Material(); mm.Key = Arguments[1]; fm = true; break; case "ka": //Ambient color not supported break; case "kd": //Equivilant to SetColor double r = 1, g = 1, b = 1; if (Arguments.Count >= 2 && !double.TryParse(Arguments[1], out r)) { Interface.AddMessage(MessageType.Warning, false, "Invalid Ambient Color R in Material Definition for " + mm.Key); } if (Arguments.Count >= 3 && !double.TryParse(Arguments[2], out g)) { Interface.AddMessage(MessageType.Warning, false, "Invalid Ambient Color G in Material Definition for " + mm.Key); } if (Arguments.Count >= 4 && !double.TryParse(Arguments[3], out b)) { Interface.AddMessage(MessageType.Warning, false, "Invalid Ambient Color B in Material Definition for " + mm.Key); } r = 255 * r; g = 255 * g; b = 255 * b; mm.Color = new Color32((byte)r, (byte)g, (byte)b); break; case "ks": //Specular color not supported break; case "ke": //Emissive color not supported break; case "d": //Sets the alpha value for the face double a = 1; if (Arguments.Count >= 2 && !double.TryParse(Arguments[1], out a)) { Interface.AddMessage(MessageType.Warning, false, "Invalid Alpha in Material Definition for " + mm.Key); } mm.Color.A = (byte)((1 - a) * 255); break; case "map_kd": case "map_ka": string tday = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), Arguments[Arguments.Count - 1]); if (File.Exists(tday)) { mm.DaytimeTexture = tday; } else { Interface.AddMessage(MessageType.Error, true, "Material texture file " + Arguments[Arguments.Count - 1] + " was not found."); } break; case "map_ke": //Emissive color map not supported break; case "illum": //Illumination mode not supported break; } } Array.Resize(ref Materials, Materials.Length + 1); Materials[Materials.Length - 1] = mm; }
private void CloseButton_Click(object sender, EventArgs e) { InterpolationMode previousInterpolationMode = Interface.CurrentOptions.Interpolation; int previousAntialasingLevel = Interface.CurrentOptions.AntiAliasingLevel; int previousAnsiotropicLevel = Interface.CurrentOptions.AnisotropicFilteringLevel; //Interpolation mode switch (InterpolationMode.SelectedIndex) { case 0: Interface.CurrentOptions.Interpolation = OpenBveApi.Graphics.InterpolationMode.NearestNeighbor; break; case 1: Interface.CurrentOptions.Interpolation = OpenBveApi.Graphics.InterpolationMode.Bilinear; break; case 2: Interface.CurrentOptions.Interpolation = OpenBveApi.Graphics.InterpolationMode.NearestNeighborMipmapped; break; case 3: Interface.CurrentOptions.Interpolation = OpenBveApi.Graphics.InterpolationMode.BilinearMipmapped; break; case 4: Interface.CurrentOptions.Interpolation = OpenBveApi.Graphics.InterpolationMode.TrilinearMipmapped; break; case 5: Interface.CurrentOptions.Interpolation = OpenBveApi.Graphics.InterpolationMode.AnisotropicFiltering; break; } //Ansiotropic filtering level Interface.CurrentOptions.AnisotropicFilteringLevel = (int)AnsiotropicLevel.Value; //Antialiasing level Interface.CurrentOptions.AntiAliasingLevel = (int)AntialiasingLevel.Value; if (Interface.CurrentOptions.AntiAliasingLevel != previousAntialasingLevel) { Program.currentGraphicsMode = new GraphicsMode(new ColorFormat(8, 8, 8, 8), 24, 8, Interface.CurrentOptions.AntiAliasingLevel); } //Transparency quality switch (TransparencyQuality.SelectedIndex) { case 0: Interface.CurrentOptions.TransparencyMode = TransparencyMode.Performance; break; default: Interface.CurrentOptions.TransparencyMode = TransparencyMode.Quality; break; } //Set width and height if (Program.Renderer.Screen.Width != width.Value || Program.Renderer.Screen.Height != height.Value) { if (width.Value >= 300) { Program.Renderer.Screen.Width = (int)width.Value; Program.currentGameWindow.Width = (int)width.Value; } if (height.Value >= 300) { Program.Renderer.Screen.Height = (int)height.Value; Program.currentGameWindow.Height = (int)height.Value; } Program.Renderer.UpdateViewport(); } //Check if interpolation mode or ansiotropic filtering level has changed, and trigger a reload if (previousInterpolationMode != Interface.CurrentOptions.Interpolation || previousAnsiotropicLevel != Interface.CurrentOptions.AnisotropicFilteringLevel) { Program.LightingRelative = -1.0; Game.Reset(); for (int i = 0; i < Program.Files.Length; i++) { try { UnifiedObject o; Program.CurrentHost.LoadObject(Program.Files[i], System.Text.Encoding.UTF8, out o); o.CreateObject(Vector3.Zero, 0.0, 0.0, 0.0); } catch (Exception ex) { Interface.AddMessage(MessageType.Critical, false, "Unhandled error (" + ex.Message + ") encountered while processing the file " + Program.Files[i] + "."); } } Program.Renderer.InitializeVisibility(); Program.Renderer.UpdateVisibility(0.0, true); ObjectManager.UpdateAnimatedWorldObjects(0.01, true); } Interface.CurrentOptions.CurrentXParser = (XParsers)comboBoxNewXParser.SelectedIndex; Interface.CurrentOptions.CurrentObjParser = (ObjParsers)comboBoxNewObjParser.SelectedIndex; for (int i = 0; i < Program.CurrentHost.Plugins.Length; i++) { if (Program.CurrentHost.Plugins[i].Object != null) { Program.CurrentHost.Plugins[i].Object.SetObjectParser(Interface.CurrentOptions.CurrentXParser); Program.CurrentHost.Plugins[i].Object.SetObjectParser(Interface.CurrentOptions.CurrentObjParser); } } if (checkBoxOptimizeObjects.Checked) { Interface.CurrentOptions.ObjectOptimizationBasicThreshold = 1000; Interface.CurrentOptions.ObjectOptimizationFullThreshold = 250; } else { Interface.CurrentOptions.ObjectOptimizationBasicThreshold = 0; Interface.CurrentOptions.ObjectOptimizationFullThreshold = 0; } Options.SaveOptions(); this.Close(); }
/// <summary>Loads a custom plugin for the specified train.</summary> /// <param name="train">The train to attach the plugin to.</param> /// <param name="trainFolder">The absolute path to the train folder.</param> /// <param name="encoding">The encoding to be used.</param> /// <returns>Whether the plugin was loaded successfully.</returns> internal static bool LoadCustomPlugin(TrainManager.Train train, string trainFolder, System.Text.Encoding encoding) { string config = OpenBveApi.Path.CombineFile(trainFolder, "ats.cfg"); if (!System.IO.File.Exists(config)) { return(false); } string Text = System.IO.File.ReadAllText(config, encoding); Text = Text.Replace("\r", "").Replace("\n", ""); string file; try { file = OpenBveApi.Path.CombineFile(trainFolder, Text); } catch { Interface.AddMessage(MessageType.Error, true, "The train plugin path was malformed in " + config); return(false); } string title = System.IO.Path.GetFileName(file); if (!System.IO.File.Exists(file)) { if (Text.EndsWith(".dll") && encoding.Equals(System.Text.Encoding.Unicode)) { // Our filename ends with .dll so probably is not mangled Unicode Interface.AddMessage(MessageType.Error, true, "The train plugin " + title + " could not be found in " + config); return(false); } // Try again with ASCII encoding Text = System.IO.File.ReadAllText(config, System.Text.Encoding.GetEncoding(1252)); Text = Text.Replace("\r", "").Replace("\n", ""); try { file = OpenBveApi.Path.CombineFile(trainFolder, Text); } catch { Interface.AddMessage(MessageType.Error, true, "The train plugin path was malformed in " + config); return(false); } title = System.IO.Path.GetFileName(file); if (!System.IO.File.Exists(file)) { // Nope, still not found Interface.AddMessage(MessageType.Error, true, "The train plugin " + title + " could not be found in " + config); return(false); } } Program.FileSystem.AppendToLogFile("Loading train plugin: " + file); bool success = LoadPlugin(train, file, trainFolder); if (success == false) { Loading.PluginError = Translations.GetInterfaceString("errors_plugin_failure1").Replace("[plugin]", file); } else { Program.FileSystem.AppendToLogFile("Train plugin loaded successfully."); } return(success); }
/******************** * MENU C'TOR *********************/ public SingleMenu(MenuType menuType, int data = 0) { int i, menuItem; int jump = 0; Size size; Align = Renderer.TextAlignment.TopMiddle; Height = Width = 0; Selection = 0; // defaults to first menu item switch (menuType) { case MenuType.Top: // top level menu for (i = 0; i < Game.Stations.Length; i++) { if (Game.PlayerStopsAtStation(i) & Game.Stations[i].Stops.Length > 0) { jump = 1; break; } } Items = new MenuEntry[4 + jump]; Items[0] = new MenuCommand(Interface.GetInterfaceString("menu_resume"), MenuTag.BackToSim, 0); if (jump > 0) { Items[1] = new MenuCommand(Interface.GetInterfaceString("menu_jump"), MenuTag.MenuJumpToStation, 0); } Items[1 + jump] = new MenuCommand(Interface.GetInterfaceString("menu_exit"), MenuTag.MenuExitToMainMenu, 0); Items[2 + jump] = new MenuCommand(Interface.GetInterfaceString("menu_customize_controls"), MenuTag.MenuControls, 0); Items[3 + jump] = new MenuCommand(Interface.GetInterfaceString("menu_quit"), MenuTag.MenuQuit, 0); break; case MenuType.JumpToStation: // list of stations to jump to // count the number of available stations menuItem = 0; for (i = 0; i < Game.Stations.Length; i++) { if (Game.PlayerStopsAtStation(i) & Game.Stations [i].Stops.Length > 0) { menuItem++; } } // list available stations, selecting the next station as predefined choice jump = 0; // no jump found yet Items = new MenuEntry[menuItem + 1]; Items[0] = new MenuCommand(Interface.GetInterfaceString("menu_back"), MenuTag.MenuBack, 0); menuItem = 1; for (i = 0; i < Game.Stations.Length; i++) { if (Game.PlayerStopsAtStation(i) & Game.Stations[i].Stops.Length > 0) { Items[menuItem] = new MenuCommand(Game.Stations[i].Name, MenuTag.JumpToStation, i); // if no preferred jump-to-station found yet and this station is // after the last station the user stopped at, select this item if (jump == 0 && i > TrainManager.PlayerTrain.LastStation) { jump = i; Selection = menuItem; } menuItem++; } } Align = Renderer.TextAlignment.TopLeft; break; case MenuType.ExitToMainMenu: Items = new MenuEntry[3]; Items[0] = new MenuCaption(Interface.GetInterfaceString("menu_exit_question")); Items[1] = new MenuCommand(Interface.GetInterfaceString("menu_exit_no"), MenuTag.MenuBack, 0); Items[2] = new MenuCommand(Interface.GetInterfaceString("menu_exit_yes"), MenuTag.ExitToMainMenu, 0); Selection = 1; break; case MenuType.Quit: // ask for quit confirmation Items = new MenuEntry[3]; Items[0] = new MenuCaption(Interface.GetInterfaceString("menu_quit_question")); Items[1] = new MenuCommand(Interface.GetInterfaceString("menu_quit_no"), MenuTag.MenuBack, 0); Items[2] = new MenuCommand(Interface.GetInterfaceString("menu_quit_yes"), MenuTag.Quit, 0); Selection = 1; break; case MenuType.Controls: //Refresh the joystick list Joysticks.RefreshJoysticks(); Items = new MenuEntry[Interface.CurrentControls.Length + 1]; Items[0] = new MenuCommand(Interface.GetInterfaceString("menu_back"), MenuTag.MenuBack, 0); for (i = 0; i < Interface.CurrentControls.Length; i++) { Items[i + 1] = new MenuCommand(Interface.CurrentControls[i].Command.ToString(), MenuTag.Control, i); } Align = Renderer.TextAlignment.TopLeft; break; case MenuType.Control: //Refresh the joystick list Joysticks.RefreshJoysticks(); Selection = SelectionNone; Items = new MenuEntry[4]; // get code name and description Interface.Control loadedControl = Interface.CurrentControls[data]; for (int h = 0; h < Interface.CommandInfos.Length; h++) { if (Interface.CommandInfos[h].Command == loadedControl.Command) { Items[0] = new MenuCommand(loadedControl.Command.ToString() + " - " + Interface.CommandInfos[h].Description, MenuTag.None, 0); break; } } // get assignment String str = ""; switch (loadedControl.Method) { case Interface.ControlMethod.Keyboard: if (loadedControl.Modifier != Interface.KeyboardModifier.None) { str = Interface.GetInterfaceString("menu_keyboard") + " [" + loadedControl.Modifier + "-" + loadedControl.Key + "]"; } else { str = Interface.GetInterfaceString("menu_keyboard") + " [" + loadedControl.Key + "]"; } break; case Interface.ControlMethod.Joystick: str = Interface.GetInterfaceString("menu_joystick") + " " + loadedControl.Device + " [" + loadedControl.Component + " " + loadedControl.Element + "]"; switch (loadedControl.Component) { case Interface.JoystickComponent.FullAxis: case Interface.JoystickComponent.Axis: str += " " + (loadedControl.Direction == 1 ? Interface.GetInterfaceString("menu_joystickdirection_positive") : Interface.GetInterfaceString("menu_joystickdirection_negative")); break; // case Interface.JoystickComponent.Button: // NOTHING TO DO FOR THIS CASE! // str = str; // break; case Interface.JoystickComponent.Hat: str += " " + (OpenTK.Input.HatPosition)loadedControl.Direction; break; case Interface.JoystickComponent.Invalid: str = Interface.GetInterfaceString("menu_joystick_notavailable"); break; } break; case Interface.ControlMethod.Invalid: str = Interface.GetInterfaceString("menu_joystick_notavailable"); break; } Items[1] = new MenuCommand(Interface.GetInterfaceString("menu_assignment_current") + " " + str, MenuTag.None, 0); Items[2] = new MenuCommand(" ", MenuTag.None, 0); Items[3] = new MenuCommand(Interface.GetInterfaceString("menu_assign"), MenuTag.None, 0); break; } // compute menu extent for (i = 0; i < Items.Length; i++) { size = Renderer.MeasureString(Game.Menu.MenuFont, Items [i].Text); if (size.Width > Width) { Width = size.Width; } if (!(Items[i] is MenuCaption) && size.Width > ItemWidth) { ItemWidth = size.Width; } } Height = Items.Length * Game.Menu.LineHeight; TopItem = 0; }
//Parses an XML background definition public static BackgroundHandle ReadBackgroundXML(string fileName) { List <StaticBackground> Backgrounds = new List <StaticBackground>(); //The current XML file to load XmlDocument currentXML = new XmlDocument(); //Load the object's XML file currentXML.Load(fileName); string Path = System.IO.Path.GetDirectoryName(fileName); double[] UnitOfLength = { 1.0 }; //Check for null if (currentXML.DocumentElement != null) { XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/openBVE/Background"); //Check this file actually contains OpenBVE light definition nodes if (DocumentNodes != null) { foreach (XmlNode n in DocumentNodes) { if (n.ChildNodes.OfType <XmlElement>().Any()) { double DisplayTime = -1; //The time to transition between backgrounds in seconds double TransitionTime = 0.8; //The texture to use (if static) Texture t = null; //The object to use (if object based) StaticObject o = null; //The transition mode between backgrounds BackgroundTransitionMode mode = BackgroundTransitionMode.FadeIn; //The number of times the texture is repeated around the viewing frustrum (if appropriate) double repetitions = 6; foreach (XmlNode c in n.ChildNodes) { string[] Arguments = c.InnerText.Split(new char[] { ',' }); switch (c.Name.ToLowerInvariant()) { case "mode": switch (c.InnerText.ToLowerInvariant()) { case "fadein": mode = BackgroundTransitionMode.FadeIn; break; case "fadeout": mode = BackgroundTransitionMode.FadeOut; break; case "none": mode = BackgroundTransitionMode.None; break; default: Interface.AddMessage(MessageType.Error, true, c.InnerText + "is not a valid background fade mode in file " + fileName); break; } break; case "object": string f; try { f = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(fileName), c.InnerText); } catch { Interface.AddMessage(MessageType.Error, true, "BackgroundObject FileName is malformed in file " + fileName); break; } if (!System.IO.File.Exists(f)) { Interface.AddMessage(MessageType.Error, true, "FileName " + f + " not found in file " + fileName); } else { UnifiedObject obj; Program.CurrentHost.LoadObject(f, System.Text.Encoding.Default, out obj); o = (StaticObject)obj; } break; case "repetitions": if (!NumberFormats.TryParseDoubleVb6(Arguments[0], UnitOfLength, out repetitions)) { Interface.AddMessage(MessageType.Error, false, c.InnerText + " does not parse to a valid number of repetitions in " + fileName); } break; case "texture": string file; try { file = OpenBveApi.Path.CombineFile(Path, c.InnerText); } catch { Interface.AddMessage(MessageType.Error, true, "BackgroundTexture FileName is malformed in file " + fileName); break; } if (!System.IO.File.Exists(file)) { Interface.AddMessage(MessageType.Error, false, "The background texture file " + c.InnerText + " does not exist in " + fileName); } else { Program.CurrentHost.RegisterTexture(file, new TextureParameters(null, null), out t); } break; case "time": if (!Interface.TryParseTime(Arguments[0].Trim(new char[] { }), out DisplayTime)) { Interface.AddMessage(MessageType.Error, false, c.InnerText + " does not parse to a valid time in file " + fileName); } break; case "transitiontime": if (!NumberFormats.TryParseDoubleVb6(Arguments[0], UnitOfLength, out TransitionTime)) { Interface.AddMessage(MessageType.Error, false, c.InnerText + " is not a valid background transition time in " + fileName); } break; } } //Create background if texture is not null if (t != null && o == null) { Backgrounds.Add(new StaticBackground(t, repetitions, false, TransitionTime, mode, DisplayTime)); } if (t == null && o != null) { //All other parameters are ignored if an object has been defined //TODO: Error message stating they have been ignored return(new BackgroundObject(o)); } } } if (Backgrounds.Count == 1) { return(Backgrounds[0]); } if (Backgrounds.Count > 1) { //Sort list- Not worried about when they start or end, so use simple LINQ Backgrounds = Backgrounds.OrderBy(o => o.Time).ToList(); //If more than 2 backgrounds, convert to array and return a new dynamic background return(new DynamicBackground(Backgrounds.ToArray())); } } } //We couldn't find any valid XML, so return false throw new InvalidDataException(); }
internal static ObjectManager.StaticObject ReadObject(string FileName) { currentFolder = System.IO.Path.GetDirectoryName(FileName); currentFile = FileName; rootMatrix = Matrix4D.NoTransformation; #if !DEBUG try { #endif XFileParser parser = new XFileParser(System.IO.File.ReadAllBytes(FileName)); Scene scene = parser.GetImportedData(); ObjectManager.StaticObject obj = new ObjectManager.StaticObject(); MeshBuilder builder = new MeshBuilder(); // Global foreach (var mesh in scene.GlobalMeshes) { MeshBuilder(ref obj, ref builder, mesh); } if (scene.RootNode != null) { // Root Node if (scene.RootNode.TrafoMatrix != OpenTK.Matrix4.Zero) { rootMatrix = ConvertMatrix(scene.RootNode.TrafoMatrix); } foreach (var mesh in scene.RootNode.Meshes) { MeshBuilder(ref obj, ref builder, mesh); } // Children Node foreach (var node in scene.RootNode.Children) { ChildrenNode(ref obj, ref builder, node); } } builder.Apply(ref obj); obj.Mesh.CreateNormals(); if (rootMatrix != Matrix4D.NoTransformation) { for (int i = 0; i < obj.Mesh.Vertices.Length; i++) { obj.Mesh.Vertices[i].Coordinates.Transform(rootMatrix); } } return(obj); #if !DEBUG } catch (Exception e) { Interface.AddMessage(MessageType.Error, false, e.Message + " in " + FileName); return(null); } #endif }
internal void SetControlJoyCustomData(int device, Interface.JoystickComponent component, int element, int dir) { if (isCustomisingControl && CustomControlIdx < Interface.CurrentControls.Length) { Interface.CurrentControls[CustomControlIdx].Method = Interface.ControlMethod.Joystick; Interface.CurrentControls[CustomControlIdx].Device = device; Interface.CurrentControls[CustomControlIdx].Component = component; Interface.CurrentControls[CustomControlIdx].Element = element; Interface.CurrentControls[CustomControlIdx].Direction = dir; Interface.SaveControls(null); PopMenu(); isCustomisingControl = false; } }
// // DRAW LOADING SCREEN // /// <summary>Draws on OpenGL canvas the route/train loading screen</summary> internal static void DrawLoadingScreen() { // begin HACK // if (!BlendEnabled) { GL.Enable(EnableCap.Blend); GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha); BlendEnabled = true; } if (LightingEnabled) { GL.Disable(EnableCap.Lighting); LightingEnabled = false; } GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); GL.PushMatrix(); // fill the screen with background colour GL.Color4(bkgR, bkgG, bkgB, bkgA); Renderer.RenderOverlaySolid(0.0, 0.0, (double)Screen.Width, (double)Screen.Height); GL.Color4(1.0f, 1.0f, 1.0f, 1.0f); // BACKGROUND IMAGE int bkgHeight, bkgWidth; int fontHeight = (int)Fonts.SmallFont.FontSize; int logoBottom; // int versionTop; int halfWidth = Screen.Width / 2; if (TextureLoadingBkg != null) { // stretch the background image to fit at least one screen dimension double ratio = (double)TextureLoadingBkg.Width / (double)TextureLoadingBkg.Height; if (Screen.Width / ratio > Screen.Height) // if screen ratio is shorter than bkg... { bkgHeight = Screen.Height; // set height to screen height bkgWidth = (int)(Screen.Height * ratio); // and scale width proprtionally } else // if screen ratio is wider than bkg... { bkgWidth = Screen.Width; // set width to screen width bkgHeight = (int)(Screen.Width / ratio); // and scale height accordingly } // draw the background image down from the top screen edge DrawRectangle(TextureLoadingBkg, new Point((Screen.Width - bkgWidth) / 2, 0), new Size(bkgWidth, bkgHeight), Color128.White); } // if the route has no custom loading image, add the openBVE logo // (the route custom image is loaded in OldParsers/CsvRwRouteParser.cs) if (!customLoadScreen) { if (TextureLogo != null) { // place the centre of the logo at from the screen top int logoTop = (int)(Screen.Height * logoCentreYFactor - TextureLogo.Height / 2); logoBottom = logoTop + TextureLogo.Height; DrawRectangle(TextureLogo, new Point((Screen.Width - TextureLogo.Width) / 2, logoTop), new Size(TextureLogo.Width, TextureLogo.Height), Color128.White); } } else { // if custom route image, no logo and leave a conventional black area below the potential logo } logoBottom = Screen.Height / 2; // take the height remaining below the logo and divide in 3 horiz. parts int blankHeight = (Screen.Height - logoBottom) / 3; // VERSION NUMBER // place the version above the first division int versionTop = logoBottom + blankHeight - fontHeight; DrawString(Fonts.SmallFont, "Version " + typeof(Renderer).Assembly.GetName().Version, new Point(halfWidth, versionTop), TextAlignment.TopMiddle, Color128.White); // for the moment, do not show any URL; would go right below the first division // DrawString(Fonts.SmallFont, "https://sites.google.com/site/openbvesim/home", // new Point(halfWidth, versionTop + fontHeight+2), // TextAlignment.TopMiddle, Color128.White); // PROGRESS MESSAGE AND BAR // place progress bar right below the second division int progressTop = Screen.Height - blankHeight; int progressWidth = Screen.Width - progrMargin * 2; double routeProgress = Math.Max(0.0, Math.Min(1.0, Loading.RouteProgress)); double trainProgress = Math.Max(0.0, Math.Min(1.0, Loading.TrainProgress)); // draw progress message right above the second division string text = Interface.GetInterfaceString( routeProgress < 1.0 ? "loading_loading_route" : (trainProgress < 1.0 ? "loading_loading_train" : "message_loading")); DrawString(Fonts.SmallFont, text, new Point(halfWidth, progressTop - fontHeight - 6), TextAlignment.TopMiddle, Color128.White); // sum of route progress and train progress arrives up to 2.0: // => times 50.0 to convert to % double percent = 50.0 * (routeProgress + trainProgress); string percStr = percent.ToString("0") + "%"; // progress frame DrawRectangle(null, new Point(progrMargin - progrBorder, progressTop - progrBorder), new Size(progressWidth + progrBorder * 2, fontHeight + 6), Color128.White); // progress bar DrawRectangle(null, new Point(progrMargin, progressTop), new Size(progressWidth * (int)percent / 100, fontHeight + 4), ColourProgressBar); // progress percent DrawString(Fonts.SmallFont, percStr, new Point(halfWidth, progressTop), TextAlignment.TopMiddle, Color128.Black); GL.PopMatrix(); }
private void button1_Click(object sender, EventArgs e) { InterpolationMode previousInterpolationMode = Interface.CurrentOptions.Interpolation; int previousAntialasingLevel = Interface.CurrentOptions.AntialiasingLevel; int previousAnsiotropicLevel = Interface.CurrentOptions.AnisotropicFilteringLevel; //Interpolation mode switch (InterpolationMode.SelectedIndex) { case 0: Interface.CurrentOptions.Interpolation = OpenBveApi.Graphics.InterpolationMode.NearestNeighbor; break; case 1: Interface.CurrentOptions.Interpolation = OpenBveApi.Graphics.InterpolationMode.Bilinear; break; case 2: Interface.CurrentOptions.Interpolation = OpenBveApi.Graphics.InterpolationMode.NearestNeighborMipmapped; break; case 3: Interface.CurrentOptions.Interpolation = OpenBveApi.Graphics.InterpolationMode.BilinearMipmapped; break; case 4: Interface.CurrentOptions.Interpolation = OpenBveApi.Graphics.InterpolationMode.TrilinearMipmapped; break; case 5: Interface.CurrentOptions.Interpolation = OpenBveApi.Graphics.InterpolationMode.AnisotropicFiltering; break; } //Ansiotropic filtering level Interface.CurrentOptions.AnisotropicFilteringLevel = (int)AnsiotropicLevel.Value; //Antialiasing level Interface.CurrentOptions.AntialiasingLevel = (int)AntialiasingLevel.Value; if (Interface.CurrentOptions.AntialiasingLevel != previousAntialasingLevel) { Program.currentGraphicsMode = new GraphicsMode(new ColorFormat(8, 8, 8, 8), 24, 8, Interface.CurrentOptions.AntialiasingLevel); } //Transparency quality switch (TransparencyQuality.SelectedIndex) { case 0: Interface.CurrentOptions.TransparencyMode = TransparencyMode.Performance; break; default: Interface.CurrentOptions.TransparencyMode = TransparencyMode.Quality; break; } //Set width and height if (Renderer.ScreenWidth != width.Value || Renderer.ScreenHeight != height.Value) { Renderer.ScreenWidth = (int)width.Value; Renderer.ScreenHeight = (int)height.Value; Program.currentGameWindow.Width = (int)width.Value; Program.currentGameWindow.Height = (int)height.Value; Program.UpdateViewport(); } //Check if interpolation mode or ansiotropic filtering level has changed, and trigger a reload if (previousInterpolationMode != Interface.CurrentOptions.Interpolation || previousAnsiotropicLevel != Interface.CurrentOptions.AnisotropicFilteringLevel) { Program.ReducedMode = false; Program.LightingRelative = -1.0; Game.Reset(); Textures.UnloadAllTextures(); Interface.ClearMessages(); for (int i = 0; i < Program.Files.Length; i++) { #if !DEBUG try { #endif UnifiedObject o = ObjectManager.LoadObject(Program.Files[i], System.Text.Encoding.UTF8, false, false, false); ObjectManager.CreateObject(o, Vector3.Zero, new Transformation(0.0, 0.0, 0.0), new Transformation(0.0, 0.0, 0.0), true, 0.0, 0.0, 25.0, 0.0); #if !DEBUG } catch (Exception ex) { Interface.AddMessage(MessageType.Critical, false, "Unhandled error (" + ex.Message + ") encountered while processing the file " + Program.Files[i] + "."); } #endif } ObjectManager.InitializeVisibility(); ObjectManager.UpdateVisibility(0.0, true); ObjectManager.UpdateAnimatedWorldObjects(0.01, true); } Renderer.TransparentColorDepthSorting = Interface.CurrentOptions.TransparencyMode == TransparencyMode.Quality & Interface.CurrentOptions.Interpolation != OpenBveApi.Graphics.InterpolationMode.NearestNeighbor & Interface.CurrentOptions.Interpolation != OpenBveApi.Graphics.InterpolationMode.Bilinear; Interface.CurrentOptions.CurrentXParser = comboBoxNewXParser.SelectedIndex; Interface.CurrentOptions.CurrentObjParser = comboBoxNewObjParser.SelectedIndex; Options.SaveOptions(); this.Close(); }
// --- functions --- internal override bool Load(VehicleSpecs specs, InitializationModes mode) { int result; try { result = Win32LoadDLL(this.PluginFile, this.PluginFile); } catch (Exception ex) { base.LastException = ex; throw; } if (result == 0) { int errorCode = Marshal.GetLastWin32Error(); string errorMessage = new Win32Exception(errorCode).Message; Interface.AddMessage(MessageType.Error, true, String.Format("Error loading Win32 plugin: {0} (0x{1})", errorMessage, errorCode.ToString("x"))); return(false); } try { Win32Load(); } catch (Exception ex) { base.LastException = ex; throw; } int version; try { version = Win32GetPluginVersion(); } catch (Exception ex) { base.LastException = ex; throw; } if (version != 131072) { Interface.AddMessage(MessageType.Error, false, "The train plugin " + base.PluginTitle + " is of an unsupported version."); try { Win32Dispose(); } catch (Exception ex) { base.LastException = ex; throw; } Win32UnloadDLL(); return(false); } try { Win32VehicleSpec win32Spec; win32Spec.BrakeNotches = specs.BrakeNotches; win32Spec.PowerNotches = specs.PowerNotches; win32Spec.AtsNotch = specs.AtsNotch; win32Spec.B67Notch = specs.B67Notch; win32Spec.Cars = specs.Cars; Win32SetVehicleSpec(ref win32Spec.BrakeNotches); } catch (Exception ex) { base.LastException = ex; throw; } try { Win32Initialize((int)mode); } catch (Exception ex) { base.LastException = ex; throw; } UpdatePower(); UpdateBrake(); UpdateReverser(); if (PanelHandle.IsAllocated) { PanelHandle.Free(); } if (SoundHandle.IsAllocated) { SoundHandle.Free(); } PanelHandle = GCHandle.Alloc(Panel, GCHandleType.Pinned); SoundHandle = GCHandle.Alloc(Sound, GCHandleType.Pinned); return(true); }
// load texture rgba private static void LoadTextureRGBAforData(Bitmap Bitmap, World.ColorRGB TransparentColor, byte TransparentColorUsed, int TextureIndex) { try { // load bytes int Width, Height, Stride; byte[] Data; { if (Textures[TextureIndex].ClipWidth == 0) { Textures[TextureIndex].ClipWidth = Bitmap.Width; } if (Textures[TextureIndex].ClipHeight == 0) { Textures[TextureIndex].ClipHeight = Bitmap.Height; } Width = Textures[TextureIndex].ClipWidth; Height = Textures[TextureIndex].ClipHeight; Bitmap c = new Bitmap(Width, Height, PixelFormat.Format32bppArgb); Graphics g = Graphics.FromImage(c); Rectangle dst = new Rectangle(0, 0, Width, Height); Rectangle src = new Rectangle(Textures[TextureIndex].ClipLeft, Textures[TextureIndex].ClipTop, Textures[TextureIndex].ClipWidth, Textures[TextureIndex].ClipHeight); g.DrawImage(Bitmap, dst, src, GraphicsUnit.Pixel); g.Dispose(); BitmapData d = c.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.ReadOnly, c.PixelFormat); Stride = d.Stride; Data = new byte[Stride * Height]; System.Runtime.InteropServices.Marshal.Copy(d.Scan0, Data, 0, Stride * Height); c.UnlockBits(d); c.Dispose(); } // load mode if (Textures[TextureIndex].LoadMode == TextureLoadMode.Bve4SignalGlow) { // bve 4 signal glow int p = 0, pn = Stride - 4 * Width; byte tr, tg, tb; if (TransparentColorUsed != 0) { tr = TransparentColor.R; tg = TransparentColor.G; tb = TransparentColor.B; } else { tr = 0; tg = 0; tb = 0; } // invert lightness byte[] Temp = new byte[Stride * Height]; for (int y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) { if (Data[p] == tb & Data[p + 1] == tg & Data[p + 2] == tr) { Temp[p] = 0; Temp[p + 1] = 0; Temp[p + 2] = 0; } else if (Data[p] != 255 | Data[p + 1] != 255 | Data[p + 2] != 255) { int b = Data[p], g = Data[p + 1], r = Data[p + 2]; InvertLightness(ref r, ref g, ref b); int l = r >= g & r >= b ? r : g >= b ? g : b; Temp[p] = (byte)(l * b / 255); Temp[p + 1] = (byte)(l * g / 255); Temp[p + 2] = (byte)(l * r / 255); } else { Temp[p] = Data[p]; Temp[p + 1] = Data[p + 1]; Temp[p + 2] = Data[p + 2]; } p += 4; } p += pn; } p = 0; // blur the image and multiply by lightness int s = 4; int n = Stride - (2 * s + 1 << 2); for (int y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) { int q = p - s * (Stride + 4); int r = 0, g = 0, b = 0, c = 0; for (int yr = y - s; yr <= y + s; yr++) { if (yr >= 0 & yr < Height) { for (int xr = x - s; xr <= x + s; xr++) { if (xr >= 0 & xr < Width) { b += (int)Temp[q]; g += (int)Temp[q + 1]; r += (int)Temp[q + 2]; c++; } q += 4; } q += n; } else { q += Stride; } } if (c == 0) { Data[p] = 0; Data[p + 1] = 0; Data[p + 2] = 0; Data[p + 3] = 255; } else { r /= c; g /= c; b /= c; int l = r >= g & r >= b ? r : g >= b ? g : b; Data[p] = (byte)(l * b / 255); Data[p + 1] = (byte)(l * g / 255); Data[p + 2] = (byte)(l * r / 255); Data[p + 3] = 255; } p += 4; } p += pn; } Textures[TextureIndex].Transparency = TextureTransparencyMode.None; Textures[TextureIndex].DontAllowUnload = true; } else if (TransparentColorUsed != 0) { // transparent color int p = 0, pn = Stride - 4 * Width; byte tr = TransparentColor.R; byte tg = TransparentColor.G; byte tb = TransparentColor.B; bool used = false; // check if alpha is actually used int y; for (y = 0; y < Height; y++) { int x; for (x = 0; x < Width; x++) { if (Data[p + 3] != 255) { break; } p += 4; } if (x < Width) { break; } p += pn; } if (y == Height) { Textures[TextureIndex].Transparency = TextureTransparencyMode.TransparentColor; } // duplicate color data from adjacent pixels p = 0; pn = Stride - 4 * Width; for (y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) { if (Data[p] == tb & Data[p + 1] == tg & Data[p + 2] == tr) { used = true; if (x == 0) { int q = p; int v; for (v = y; v < Height; v++) { int u; for (u = v == y ? x + 1 : 0; u < Width; u++) { if (Data[q] != tb | Data[q + 1] != tg | Data[q + 2] != tr) { Data[p] = Data[q]; Data[p + 1] = Data[q + 1]; Data[p + 2] = Data[q + 2]; Data[p + 3] = 0; break; } q += 4; } if (u < Width) { break; } else { q += pn; } } if (v == Height) { if (y == 0) { Data[p] = 128; Data[p + 1] = 128; Data[p + 2] = 128; Data[p + 3] = 0; } else { Data[p] = Data[p - Stride]; Data[p + 1] = Data[p - Stride + 1]; Data[p + 2] = Data[p - Stride + 2]; Data[p + 3] = 0; } } } else { Data[p] = Data[p - 4]; Data[p + 1] = Data[p - 3]; Data[p + 2] = Data[p - 2]; Data[p + 3] = 0; } } p += 4; } p += pn; } // transparent color is not actually used if (!used & Textures[TextureIndex].Transparency == TextureTransparencyMode.TransparentColor) { Textures[TextureIndex].Transparency = TextureTransparencyMode.None; } } else if (Textures[TextureIndex].Transparency == TextureTransparencyMode.Alpha) { // check if alpha is actually used int p = 0, pn = Stride - 4 * Width; int y; for (y = 0; y < Height; y++) { int x; for (x = 0; x < Width; x++) { if (Data[p + 3] != 255) { break; } p += 4; } if (x < Width) { break; } p += pn; } if (y == Height) { Textures[TextureIndex].Transparency = TextureTransparencyMode.None; } } // non-power of two int TargetWidth; int TargetHeight; if (Interface.CurrentOptions.NoTextureResize) { TargetWidth = Width; TargetHeight = Height; } else { TargetWidth = Interface.RoundToPowerOfTwo(Width); TargetHeight = Interface.RoundToPowerOfTwo(Height); if (TargetWidth != Width | TargetHeight != Height) { Bitmap b = new Bitmap(Width, Height, PixelFormat.Format32bppArgb); BitmapData d = b.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.WriteOnly, b.PixelFormat); System.Runtime.InteropServices.Marshal.Copy(Data, 0, d.Scan0, d.Stride * d.Height); b.UnlockBits(d); Bitmap c = new Bitmap(TargetWidth, TargetHeight, PixelFormat.Format32bppArgb); Graphics g = Graphics.FromImage(c); g.DrawImage(b, 0, 0, TargetWidth, TargetHeight); g.Dispose(); b.Dispose(); d = c.LockBits(new Rectangle(0, 0, TargetWidth, TargetHeight), ImageLockMode.ReadOnly, c.PixelFormat); Stride = d.Stride; Data = new byte[Stride * TargetHeight]; System.Runtime.InteropServices.Marshal.Copy(d.Scan0, Data, 0, Stride * TargetHeight); c.UnlockBits(d); c.Dispose(); } } Textures[TextureIndex].Width = TargetWidth; Textures[TextureIndex].Height = TargetHeight; Textures[TextureIndex].Data = Data; } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, "Internal error in TextureManager.cs::LoadTextureRGBAForData: " + ex.Message); throw; } }
// // PROCESS MENU COMMAND // /// <summary>Processes a user command for the current menu</summary> /// <param name="cmd">The command to apply to the current menu</param> /// <param name="timeElapsed">The time elapsed since previous frame</param> internal void ProcessCommand(Interface.Command cmd, double timeElapsed) { if (CurrMenu < 0) { return; } // MenuBack is managed independently from single menu data if (cmd == Interface.Command.MenuBack) { PopMenu(); return; } SingleMenu menu = Menus[CurrMenu]; if (menu.Selection == SelectionNone) // if menu has no selection, do nothing return; switch (cmd) { case Interface.Command.MenuUp: // UP if (menu.Selection > 0 && !(menu.Items[menu.Selection - 1] is MenuCaption)) { menu.Selection--; PositionMenu(); } break; case Interface.Command.MenuDown: // DOWN if (menu.Selection < menu.Items.Length - 1) { menu.Selection++; PositionMenu(); } break; // case Interface.Command.MenuBack: // ESC: managed above // break; case Interface.Command.MenuEnter: // ENTER if (menu.Items[menu.Selection] is MenuCommand) { MenuCommand menuItem = (MenuCommand)menu.Items[menu.Selection]; switch (menuItem.Tag) { // menu management commands case MenuTag.MenuBack: // BACK TO PREVIOUS MENU Menu.instance.PopMenu(); break; case MenuTag.MenuJumpToStation: // TO STATIONS MENU Menu.instance.PushMenu(MenuType.JumpToStation); break; case MenuTag.MenuExitToMainMenu: // TO EXIT MENU Menu.instance.PushMenu(MenuType.ExitToMainMenu); break; case MenuTag.MenuQuit: // TO QUIT MENU Menu.instance.PushMenu(MenuType.Quit); break; case MenuTag.MenuControls: // TO CONTROLS MENU Menu.instance.PushMenu(MenuType.Controls); break; case MenuTag.BackToSim: // OUT OF MENU BACK TO SIMULATION Reset(); Game.CurrentInterface = Game.InterfaceType.Normal; break; // simulation commands case MenuTag.JumpToStation: // JUMP TO STATION Reset(); TrainManager.JumpTrain(TrainManager.PlayerTrain, menuItem.Data); break; case MenuTag.ExitToMainMenu: // BACK TO MAIN MENU Reset(); Program.RestartArguments = Interface.CurrentOptions.GameMode == Interface.GameMode.Arcade ? "/review" : ""; MainLoop.Quit = true; break; case MenuTag.Control: // CONTROL CUSTOMIZATION PushMenu(MenuType.Control, ((MenuCommand)menu.Items[menu.Selection]).Data); isCustomisingControl = true; CustomControlIdx = ((MenuCommand)menu.Items[menu.Selection]).Data; break; case MenuTag.Quit: // QUIT PROGRAMME Reset(); MainLoop.Quit = true; break; } } break; case Interface.Command.MiscFullscreen: // fullscreen Screen.ToggleFullscreen(); break; case Interface.Command.MiscMute: // mute Sounds.GlobalMute = !Sounds.GlobalMute; Sounds.Update(timeElapsed, Interface.CurrentOptions.SoundModel); break; } }
//Parses an XML dynamic lighting definition public static bool ReadLightingXML(string fileName) { //Prep LibRender.Renderer.LightDefinitions = new LightDefinition[0]; //The current XML file to load XmlDocument currentXML = new XmlDocument(); //Load the object's XML file try { currentXML.Load(fileName); } catch { return(false); } bool defined = false; //Check for null if (currentXML.DocumentElement != null) { XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/openBVE/Brightness"); //Check this file actually contains OpenBVE light definition nodes if (DocumentNodes != null) { foreach (XmlNode n in DocumentNodes) { LightDefinition currentLight = new LightDefinition(); if (n.ChildNodes.OfType <XmlElement>().Any()) { bool tf = false, al = false, dl = false, ld = false, cb = false; string ts = null; foreach (XmlNode c in n.ChildNodes) { string[] Arguments = c.InnerText.Split(','); switch (c.Name.ToLowerInvariant()) { case "cablighting": double b; if (NumberFormats.TryParseDoubleVb6(Arguments[0].Trim(), out b)) { cb = true; } if (b > 255 || b < 0) { Interface.AddMessage(MessageType.Error, false, c.InnerText + " is not a valid brightness value in file " + fileName); currentLight.CabBrightness = 255; break; } currentLight.CabBrightness = b; break; case "time": double t; if (Interface.TryParseTime(Arguments[0].Trim(), out t)) { currentLight.Time = (int)t; tf = true; //Keep back for error report later ts = Arguments[0]; } else { Interface.AddMessage(MessageType.Error, false, c.InnerText + " does not parse to a valid time in file " + fileName); } break; case "ambientlight": if (Arguments.Length == 3) { double R, G, B; if (NumberFormats.TryParseDoubleVb6(Arguments[0].Trim(), out R) && NumberFormats.TryParseDoubleVb6(Arguments[1].Trim(), out G) && NumberFormats.TryParseDoubleVb6(Arguments[2].Trim(), out B)) { currentLight.AmbientColor = new Color24((byte)R, (byte)G, (byte)B); al = true; } else { Interface.AddMessage(MessageType.Error, false, c.InnerText + " does not parse to a valid color in file " + fileName); } } else { if (Arguments.Length == 1) { if (Color24.TryParseHexColor(Arguments[0], out currentLight.DiffuseColor)) { al = true; break; } } Interface.AddMessage(MessageType.Error, false, c.InnerText + " does not contain three arguments in file " + fileName); } break; case "directionallight": if (Arguments.Length == 3) { double R, G, B; if (NumberFormats.TryParseDoubleVb6(Arguments[0].Trim(), out R) && NumberFormats.TryParseDoubleVb6(Arguments[1].Trim(), out G) && NumberFormats.TryParseDoubleVb6(Arguments[2].Trim(), out B)) { currentLight.DiffuseColor = new Color24((byte)R, (byte)G, (byte)B); dl = true; } else { Interface.AddMessage(MessageType.Error, false, c.InnerText + " does not parse to a valid color in file " + fileName); } } else { if (Arguments.Length == 1) { if (Color24.TryParseHexColor(Arguments[0], out currentLight.DiffuseColor)) { dl = true; break; } } Interface.AddMessage(MessageType.Error, false, c.InnerText + " does not contain three arguments in file " + fileName); } break; case "cartesianlightdirection": case "lightdirection": if (Arguments.Length == 3) { double X, Y, Z; if (NumberFormats.TryParseDoubleVb6(Arguments[0].Trim(), out X) && NumberFormats.TryParseDoubleVb6(Arguments[1].Trim(), out Y) && NumberFormats.TryParseDoubleVb6(Arguments[2].Trim(), out Z)) { currentLight.LightPosition = new Vector3(X, Y, Z); ld = true; } else { Interface.AddMessage(MessageType.Error, false, c.InnerText + " does not parse to a valid direction in file " + fileName); } } else { Interface.AddMessage(MessageType.Error, false, c.InnerText + " does not contain three arguments in file " + fileName); } break; case "sphericallightdirection": if (Arguments.Length == 2) { double theta, phi; if (NumberFormats.TryParseDoubleVb6(Arguments[0].Trim(), out theta) && NumberFormats.TryParseDoubleVb6(Arguments[1].Trim(), out phi)) { currentLight.LightPosition = new Vector3(Math.Cos(theta) * Math.Sin(phi), -Math.Sin(theta), Math.Cos(theta) * Math.Cos(phi)); ld = true; } else { Interface.AddMessage(MessageType.Error, false, c.InnerText + " does not parse to a valid direction in file " + fileName); } } else { Interface.AddMessage(MessageType.Error, false, c.InnerText + " does not contain two arguments in file " + fileName); } break; } } //We want to be able to add a completely default light element, but not one that's not been defined in the XML properly if (tf || al || ld || dl || cb) { //HACK: No way to break out of the first loop and continue with the second, so we've got to use a variable bool Break = false; int l = LibRender.Renderer.LightDefinitions.Length; for (int i = 0; i > l; i++) { if (LibRender.Renderer.LightDefinitions[i].Time == currentLight.Time) { Break = true; if (ts == null) { Interface.AddMessage(MessageType.Error, false, "Multiple undefined times were encountered in file " + fileName); } else { Interface.AddMessage(MessageType.Error, false, "Duplicate time found: " + ts + " in file " + fileName); } break; } } if (Break) { continue; } //We've got there, so now figure out where to add the new light into our list of light definitions int t = 0; if (l == 1) { t = currentLight.Time > LibRender.Renderer.LightDefinitions[0].Time ? 1 : 0; } else if (l > 1) { for (int i = 1; i < l; i++) { t = i + 1; if (currentLight.Time > LibRender.Renderer.LightDefinitions[i - 1].Time && currentLight.Time < LibRender.Renderer.LightDefinitions[i].Time) { break; } } } //Resize array defined = true; Array.Resize(ref LibRender.Renderer.LightDefinitions, l + 1); if (t == l) { //Straight insert at the end of the array LibRender.Renderer.LightDefinitions[l] = currentLight; } else { for (int u = t; u < l; u++) { //Otherwise, shift all elements to compensate LibRender.Renderer.LightDefinitions[u + 1] = LibRender.Renderer.LightDefinitions[u]; } LibRender.Renderer.LightDefinitions[t] = currentLight; } } } } } } //We couldn't find any valid XML, so return false return(defined); }
// unescape internal static string Unescape(string Text) { System.Text.StringBuilder Builder = new System.Text.StringBuilder(Text.Length); int Start = 0; for (int i = 0; i < Text.Length; i++) { if (Text[i] == '\\') { Builder.Append(Text, Start, i - Start); if (i + 1 <= Text.Length) { switch (Text[i + 1]) { case 'a': Builder.Append('\a'); break; case 'b': Builder.Append('\b'); break; case 't': Builder.Append('\t'); break; case 'n': Builder.Append('\n'); break; case 'v': Builder.Append('\v'); break; case 'f': Builder.Append('\f'); break; case 'r': Builder.Append('\r'); break; case 'e': Builder.Append('\x1B'); break; case 'c': if (i + 2 < Text.Length) { int CodePoint = char.ConvertToUtf32(Text, i + 2); if (CodePoint >= 0x40 & CodePoint <= 0x5F) { Builder.Append(char.ConvertFromUtf32(CodePoint - 64)); } else if (CodePoint == 0x3F) { Builder.Append('\x7F'); } else { Interface.AddMessage(MessageType.Error, false, "Unrecognized control character found in " + Text.Substring(i, 3)); return(Text); } i++; } else { Interface.AddMessage(MessageType.Error, false, "Insufficient characters available in " + Text + " to decode control character escape sequence"); return(Text); } break; case '"': Builder.Append('"'); break; case '\\': Builder.Append('\\'); break; case 'x': if (i + 3 < Text.Length) { Builder.Append(char.ConvertFromUtf32(Convert.ToInt32(Text.Substring(i + 2, 2), 16))); i += 2; } else { Interface.AddMessage(MessageType.Error, false, "Insufficient characters available in " + Text + " to decode hexadecimal escape sequence."); return(Text); } break; case 'u': if (i + 5 < Text.Length) { Builder.Append(char.ConvertFromUtf32(Convert.ToInt32(Text.Substring(i + 2, 4), 16))); i += 4; } else { Interface.AddMessage(MessageType.Error, false, "Insufficient characters available in " + Text + " to decode hexadecimal escape sequence."); return(Text); } break; default: Interface.AddMessage(MessageType.Error, false, "Unrecognized escape sequence found in " + Text + "."); return(Text); } i++; Start = i + 1; } else { Interface.AddMessage(MessageType.Error, false, "Insufficient characters available in " + Text + " to decode escape sequence."); return(Text); } } } Builder.Append(Text, Start, Text.Length - Start); return(Builder.ToString()); }
/// <summary>This function processes the list of expressions for $Char, $Rnd, $If and $Sub directives, and evaluates them into the final expressions dataset</summary> private static void PreprocessChrRndSub(string FileName, bool IsRW, System.Text.Encoding Encoding, ref Expression[] Expressions) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string[] Subs = new string[16]; int openIfs = 0; for (int i = 0; i < Expressions.Length; i++) { string Epilog = " at line " + Expressions[i].Line.ToString(Culture) + ", column " + Expressions[i].Column.ToString(Culture) + " in file " + Expressions[i].File; bool continueWithNextExpression = false; for (int j = Expressions[i].Text.Length - 1; j >= 0; j--) { if (Expressions[i].Text[j] == '$') { int k; for (k = j + 1; k < Expressions[i].Text.Length; k++) { if (Expressions[i].Text[k] == '(') { break; } else if (Expressions[i].Text[k] == '/' | Expressions[i].Text[k] == '\\') { k = Expressions[i].Text.Length + 1; break; } } if (k <= Expressions[i].Text.Length) { string t = Expressions[i].Text.Substring(j, k - j).TrimEnd(); int l = 1, h; for (h = k + 1; h < Expressions[i].Text.Length; h++) { switch (Expressions[i].Text[h]) { case '(': l++; break; case ')': l--; if (l < 0) { continueWithNextExpression = true; Interface.AddMessage(MessageType.Error, false, "Invalid parenthesis structure in " + t + Epilog); } break; } if (l <= 0) { break; } } if (continueWithNextExpression) { break; } if (l != 0) { Interface.AddMessage(MessageType.Error, false, "Invalid parenthesis structure in " + t + Epilog); break; } string s = Expressions[i].Text.Substring(k + 1, h - k - 1).Trim(); switch (t.ToLowerInvariant()) { case "$if": if (j != 0) { Interface.AddMessage(MessageType.Error, false, "The $If directive must not appear within another statement" + Epilog); } else { double num; if (double.TryParse(s, System.Globalization.NumberStyles.Float, Culture, out num)) { openIfs++; Expressions[i].Text = string.Empty; if (num == 0.0) { /* * Blank every expression until the matching $Else or $EndIf * */ i++; int level = 1; while (i < Expressions.Length) { if (Expressions[i].Text.StartsWith("$if", StringComparison.OrdinalIgnoreCase)) { Expressions[i].Text = string.Empty; level++; } else if (Expressions[i].Text.StartsWith("$else", StringComparison.OrdinalIgnoreCase)) { Expressions[i].Text = string.Empty; if (level == 1) { level--; break; } } else if (Expressions[i].Text.StartsWith("$endif", StringComparison.OrdinalIgnoreCase)) { Expressions[i].Text = string.Empty; level--; if (level == 0) { openIfs--; break; } } else { Expressions[i].Text = string.Empty; } i++; } if (level != 0) { Interface.AddMessage(MessageType.Error, false, "$EndIf missing at the end of the file" + Epilog); } } continueWithNextExpression = true; break; } else { Interface.AddMessage(MessageType.Error, false, "The $If condition does not evaluate to a number" + Epilog); } } continueWithNextExpression = true; break; case "$else": /* * Blank every expression until the matching $EndIf * */ Expressions[i].Text = string.Empty; if (openIfs != 0) { i++; int level = 1; while (i < Expressions.Length) { if (Expressions[i].Text.StartsWith("$if", StringComparison.OrdinalIgnoreCase)) { Expressions[i].Text = string.Empty; level++; } else if (Expressions[i].Text.StartsWith("$else", StringComparison.OrdinalIgnoreCase)) { Expressions[i].Text = string.Empty; if (level == 1) { Interface.AddMessage(MessageType.Error, false, "Duplicate $Else encountered" + Epilog); } } else if (Expressions[i].Text.StartsWith("$endif", StringComparison.OrdinalIgnoreCase)) { Expressions[i].Text = string.Empty; level--; if (level == 0) { openIfs--; break; } } else { Expressions[i].Text = string.Empty; } i++; } if (level != 0) { Interface.AddMessage(MessageType.Error, false, "$EndIf missing at the end of the file" + Epilog); } } else { Interface.AddMessage(MessageType.Error, false, "$Else without matching $If encountered" + Epilog); } continueWithNextExpression = true; break; case "$endif": Expressions[i].Text = string.Empty; if (openIfs != 0) { openIfs--; } else { Interface.AddMessage(MessageType.Error, false, "$EndIf without matching $If encountered" + Epilog); } continueWithNextExpression = true; break; case "$include": if (j != 0) { Interface.AddMessage(MessageType.Error, false, "The $Include directive must not appear within another statement" + Epilog); continueWithNextExpression = true; break; } string[] args = s.Split(';'); for (int ia = 0; ia < args.Length; ia++) { args[ia] = args[ia].Trim(); } int count = (args.Length + 1) / 2; string[] files = new string[count]; double[] weights = new double[count]; double[] offsets = new double[count]; double weightsTotal = 0.0; for (int ia = 0; ia < count; ia++) { string file; double offset; int colon = args[2 * ia].IndexOf(':'); if (colon >= 0) { file = args[2 * ia].Substring(0, colon).TrimEnd(); string value = args[2 * ia].Substring(colon + 1).TrimStart(); if (!double.TryParse(value, NumberStyles.Float, Culture, out offset)) { continueWithNextExpression = true; Interface.AddMessage(MessageType.Error, false, "The track position offset " + value + " is invalid in " + t + Epilog); break; } } else { file = args[2 * ia]; offset = 0.0; } files[ia] = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), file); offsets[ia] = offset; if (!System.IO.File.Exists(files[ia])) { continueWithNextExpression = true; Interface.AddMessage(MessageType.Error, false, "The file " + file + " could not be found in " + t + Epilog); for (int ta = i; ta < Expressions.Length - 1; ta++) { Expressions[ta] = Expressions[ta + 1]; } Array.Resize <Expression>(ref Expressions, Expressions.Length - 1); i--; break; } if (2 * ia + 1 < args.Length) { if (!NumberFormats.TryParseDoubleVb6(args[2 * ia + 1], out weights[ia])) { continueWithNextExpression = true; Interface.AddMessage(MessageType.Error, false, "A weight is invalid in " + t + Epilog); break; } if (weights[ia] <= 0.0) { continueWithNextExpression = true; Interface.AddMessage(MessageType.Error, false, "A weight is not positive in " + t + Epilog); break; } weightsTotal += weights[ia]; } else { weights[ia] = 1.0; weightsTotal += 1.0; } } if (count == 0) { continueWithNextExpression = true; Interface.AddMessage(MessageType.Error, false, "No file was specified in " + t + Epilog); break; } if (!continueWithNextExpression) { double number = Program.RandomNumberGenerator.NextDouble() * weightsTotal; double value = 0.0; int chosenIndex = 0; for (int ia = 0; ia < count; ia++) { value += weights[ia]; if (value > number) { chosenIndex = ia; break; } } Expression[] expr; //Get the text encoding of our $Include file System.Text.Encoding includeEncoding = TextEncoding.GetSystemEncodingFromFile(files[chosenIndex]); if (!includeEncoding.Equals(Encoding) && includeEncoding.WindowsCodePage != Encoding.WindowsCodePage) { //If the encodings do not match, add a warning //This is not critical, but it's a bad idea to mix and match character encodings within a routefile, as the auto-detection may sometimes be wrong Interface.AddMessage(MessageType.Warning, false, "The text encoding of the $Include file " + files[chosenIndex] + " does not match that of the base routefile."); } string[] lines = System.IO.File.ReadAllLines(files[chosenIndex], includeEncoding); PreprocessSplitIntoExpressions(files[chosenIndex], IsRW, lines, out expr, false, offsets[chosenIndex] + Expressions[i].TrackPositionOffset); int length = Expressions.Length; if (expr.Length == 0) { for (int ia = i; ia < Expressions.Length - 1; ia++) { Expressions[ia] = Expressions[ia + 1]; } Array.Resize <Expression>(ref Expressions, length - 1); } else { Array.Resize <Expression>(ref Expressions, length + expr.Length - 1); for (int ia = Expressions.Length - 1; ia >= i + expr.Length; ia--) { Expressions[ia] = Expressions[ia - expr.Length + 1]; } for (int ia = 0; ia < expr.Length; ia++) { Expressions[i + ia] = expr[ia]; } } i--; continueWithNextExpression = true; } break; case "$chr": case "$chruni": { int x; if (NumberFormats.TryParseIntVb6(s, out x)) { if (x < 0) { //Must be non-negative continueWithNextExpression = true; Interface.AddMessage(MessageType.Error, false, "Index must be a non-negative character in " + t + Epilog); } else { Expressions[i].Text = Expressions[i].Text.Substring(0, j) + char.ConvertFromUtf32(x) + Expressions[i].Text.Substring(h + 1); } } else { continueWithNextExpression = true; Interface.AddMessage(MessageType.Error, false, "Index is invalid in " + t + Epilog); } } break; case "$chrascii": { int x; if (NumberFormats.TryParseIntVb6(s, out x)) { if (x < 0 || x > 128) { //Standard ASCII characters from 0-128 continueWithNextExpression = true; Interface.AddMessage(MessageType.Error, false, "Index does not correspond to a valid ASCII character in " + t + Epilog); } else { Expressions[i].Text = Expressions[i].Text.Substring(0, j) + char.ConvertFromUtf32(x) + Expressions[i].Text.Substring(h + 1); } } else { continueWithNextExpression = true; Interface.AddMessage(MessageType.Error, false, "Index is invalid in " + t + Epilog); } } break; case "$rnd": { int m = s.IndexOf(";", StringComparison.Ordinal); if (m >= 0) { string s1 = s.Substring(0, m).TrimEnd(); string s2 = s.Substring(m + 1).TrimStart(); int x; if (NumberFormats.TryParseIntVb6(s1, out x)) { int y; if (NumberFormats.TryParseIntVb6(s2, out y)) { int z = x + (int)Math.Floor(Program.RandomNumberGenerator.NextDouble() * (double)(y - x + 1)); Expressions[i].Text = Expressions[i].Text.Substring(0, j) + z.ToString(Culture) + Expressions[i].Text.Substring(h + 1); } else { continueWithNextExpression = true; Interface.AddMessage(MessageType.Error, false, "Index2 is invalid in " + t + Epilog); } } else { continueWithNextExpression = true; Interface.AddMessage(MessageType.Error, false, "Index1 is invalid in " + t + Epilog); } } else { continueWithNextExpression = true; Interface.AddMessage(MessageType.Error, false, "Two arguments are expected in " + t + Epilog); } } break; case "$sub": { l = 0; bool f = false; int m; for (m = h + 1; m < Expressions[i].Text.Length; m++) { switch (Expressions[i].Text[m]) { case '(': l++; break; case ')': l--; break; case '=': if (l == 0) { f = true; } break; default: if (!char.IsWhiteSpace(Expressions[i].Text[m])) { l = -1; } break; } if (f | l < 0) { break; } } if (f) { l = 0; int n; for (n = m + 1; n < Expressions[i].Text.Length; n++) { switch (Expressions[i].Text[n]) { case '(': l++; break; case ')': l--; break; } if (l < 0) { break; } } int x; if (NumberFormats.TryParseIntVb6(s, out x)) { if (x >= 0) { while (x >= Subs.Length) { Array.Resize <string>(ref Subs, Subs.Length << 1); } Subs[x] = Expressions[i].Text.Substring(m + 1, n - m - 1).Trim(); Expressions[i].Text = Expressions[i].Text.Substring(0, j) + Expressions[i].Text.Substring(n); } else { continueWithNextExpression = true; Interface.AddMessage(MessageType.Error, false, "Index is expected to be non-negative in " + t + Epilog); } } else { continueWithNextExpression = true; Interface.AddMessage(MessageType.Error, false, "Index is invalid in " + t + Epilog); } } else { int x; if (NumberFormats.TryParseIntVb6(s, out x)) { if (x >= 0 & x < Subs.Length && Subs[x] != null) { Expressions[i].Text = Expressions[i].Text.Substring(0, j) + Subs[x] + Expressions[i].Text.Substring(h + 1); } else { continueWithNextExpression = true; Interface.AddMessage(MessageType.Error, false, "Index is out of range in " + t + Epilog); } } else { continueWithNextExpression = true; Interface.AddMessage(MessageType.Error, false, "Index is invalid in " + t + Epilog); } } } break; } } } if (continueWithNextExpression) { break; } } } // handle comments introduced via chr, rnd, sub { int length = Expressions.Length; for (int i = 0; i < length; i++) { Expressions[i].Text = Expressions[i].Text.Trim(); if (Expressions[i].Text.Length != 0) { if (Expressions[i].Text[0] == ';') { for (int j = i; j < length - 1; j++) { Expressions[j] = Expressions[j + 1]; } length--; i--; } } else { for (int j = i; j < length - 1; j++) { Expressions[j] = Expressions[j + 1]; } length--; i--; } } if (length != Expressions.Length) { Array.Resize <Expression>(ref Expressions, length); } } }
// --- functions --- /// <summary>Reports a problem to the host application.</summary> /// <param name="type">The type of problem that is reported.</param> /// <param name="text">The textual message that describes the problem.</param> public override void ReportProblem(OpenBveApi.Hosts.ProblemType type, string text) { Interface.AddMessage(Interface.MessageType.Error, false, text); }
private static void PreprocessSortByTrackPosition(bool IsRW, double[] UnitFactors, ref Expression[] Expressions) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; PositionedExpression[] p = new PositionedExpression[Expressions.Length]; int n = 0; double a = -1.0; bool NumberCheck = !IsRW; for (int i = 0; i < Expressions.Length; i++) { if (IsRW) { // only check for track positions in the railway section for RW routes if (Expressions[i].Text.StartsWith("[", StringComparison.Ordinal) & Expressions[i].Text.EndsWith("]", StringComparison.Ordinal)) { string s = Expressions[i].Text.Substring(1, Expressions[i].Text.Length - 2).Trim(); if (string.Compare(s, "Railway", StringComparison.OrdinalIgnoreCase) == 0) { NumberCheck = true; } else { NumberCheck = false; } } } double x; if (NumberCheck && NumberFormats.TryParseDouble(Expressions[i].Text, UnitFactors, out x)) { x += Expressions[i].TrackPositionOffset; if (x >= 0.0) { if (Interface.CurrentOptions.EnableBveTsHacks) { switch (System.IO.Path.GetFileName(Expressions[i].File.ToLowerInvariant())) { case "balloch - dumbarton central special nighttime run.csv": case "balloch - dumbarton central summer 2004 morning run.csv": if (x != 0 || a != 4125) { //Misplaced comma in the middle of the line causes this to be interpreted as a track position a = x; } break; default: a = x; break; } } else { a = x; } } else { Interface.AddMessage(MessageType.Error, false, "Negative track position encountered at line " + Expressions[i].Line.ToString(Culture) + ", column " + Expressions[i].Column.ToString(Culture) + " in file " + Expressions[i].File); } } else { p[n].TrackPosition = a; p[n].Expression = Expressions[i]; int j = n; n++; while (j > 0) { if (p[j].TrackPosition < p[j - 1].TrackPosition) { PositionedExpression t = p[j]; p[j] = p[j - 1]; p[j - 1] = t; j--; } else { break; } } } } a = -1.0; Expression[] e = new Expression[Expressions.Length]; int m = 0; for (int i = 0; i < n; i++) { if (p[i].TrackPosition != a) { a = p[i].TrackPosition; e[m] = new Expression { Text = (a / UnitFactors[UnitFactors.Length - 1]).ToString(Culture), Line = -1, Column = -1 }; m++; } e[m] = p[i].Expression; m++; } Array.Resize <Expression>(ref e, m); Expressions = e; }
private static void MeshBuilder(ref ObjectManager.StaticObject obj, ref MeshBuilder builder, AssimpNET.X.Mesh mesh) { if (builder.Vertices.Length != 0) { builder.Apply(ref obj); builder = new MeshBuilder(); } int nVerts = mesh.Positions.Count; if (nVerts == 0) { //Some null objects contain an empty mesh Interface.AddMessage(MessageType.Warning, false, "nVertices should be greater than zero in Mesh " + mesh.Name); } int v = builder.Vertices.Length; Array.Resize(ref builder.Vertices, v + nVerts); for (int i = 0; i < nVerts; i++) { builder.Vertices[v + i] = new Vertex(mesh.Positions[i].X, mesh.Positions[i].Y, mesh.Positions[i].Z); } int nFaces = mesh.PosFaces.Count; int f = builder.Faces.Length; Array.Resize(ref builder.Faces, f + nFaces); for (int i = 0; i < nFaces; i++) { int fVerts = mesh.PosFaces[i].Indices.Count; if (nFaces == 0) { throw new Exception("fVerts must be greater than zero"); } builder.Faces[f + i] = new MeshFace(); builder.Faces[f + i].Vertices = new MeshFaceVertex[fVerts]; for (int j = 0; j < fVerts; j++) { builder.Faces[f + i].Vertices[j].Index = (ushort)mesh.PosFaces[i].Indices[j]; } } int nMaterials = mesh.Materials.Count; int nFaceIndices = mesh.FaceMaterials.Count; for (int i = 0; i < nFaceIndices; i++) { int fMaterial = (int)mesh.FaceMaterials[i]; builder.Faces[i].Material = (ushort)(fMaterial + 1); } for (int i = 0; i < nMaterials; i++) { int m = builder.Materials.Length; Array.Resize(ref builder.Materials, m + 1); builder.Materials[m] = new Material(); builder.Materials[m].Color = new Color32((byte)(255 * mesh.Materials[i].Diffuse.R), (byte)(255 * mesh.Materials[i].Diffuse.G), (byte)(255 * mesh.Materials[i].Diffuse.B), (byte)(255 * mesh.Materials[i].Diffuse.A)); double mPower = mesh.Materials[i].SpecularExponent; //TODO: Unsure what this does... Color24 mSpecular = new Color24((byte)mesh.Materials[i].Specular.R, (byte)mesh.Materials[i].Specular.G, (byte)mesh.Materials[i].Specular.B); builder.Materials[m].EmissiveColor = new Color24((byte)(255 * mesh.Materials[i].Emissive.R), (byte)(255 * mesh.Materials[i].Emissive.G), (byte)(255 * mesh.Materials[i].Emissive.B)); builder.Materials[m].EmissiveColorUsed = true; //TODO: Check exact behaviour builder.Materials[m].TransparentColor = Color24.Black; //TODO: Check, also can we optimise which faces have the transparent color set? builder.Materials[m].TransparentColorUsed = true; if (mesh.Materials[i].Textures.Count > 0) { builder.Materials[m].DaytimeTexture = OpenBveApi.Path.CombineFile(currentFolder, mesh.Materials[i].Textures[0].Name); if (!System.IO.File.Exists(builder.Materials[m].DaytimeTexture)) { Interface.AddMessage(MessageType.Error, true, "Texure " + builder.Materials[m].DaytimeTexture + " was not found in file " + currentFile); builder.Materials[m].DaytimeTexture = null; } } } if (mesh.TexCoords.Length > 0 && mesh.TexCoords[0] != null) { int nCoords = mesh.TexCoords[0].Count; for (int i = 0; i < nCoords; i++) { builder.Vertices[i].TextureCoordinates = new Vector2(mesh.TexCoords[0][i].X, mesh.TexCoords[0][i].Y); } } int nNormals = mesh.Normals.Count; Vector3[] normals = new Vector3[nNormals]; for (int i = 0; i < nNormals; i++) { normals[i] = new Vector3(mesh.Normals[i].X, mesh.Normals[i].Y, mesh.Normals[i].Z); normals[i].Normalize(); } int nFaceNormals = mesh.NormFaces.Count; if (nFaceNormals > builder.Faces.Length) { throw new Exception("nFaceNormals must match the number of faces in the mesh"); } for (int i = 0; i < nFaceNormals; i++) { int nVertexNormals = mesh.NormFaces[i].Indices.Count; if (nVertexNormals > builder.Faces[i].Vertices.Length) { throw new Exception("nVertexNormals must match the number of verticies in the face"); } for (int j = 0; j < nVertexNormals; j++) { builder.Faces[i].Vertices[j].Normal = normals[(int)mesh.NormFaces[i].Indices[j]]; } } int nVertexColors = (int)mesh.NumColorSets; for (int i = 0; i < nVertexColors; i++) { builder.Vertices[i] = new ColoredVertex((Vertex)builder.Vertices[i], new Color128(mesh.Colors[0][i].R, mesh.Colors[0][i].G, mesh.Colors[0][i].B, mesh.Colors[0][i].A)); } }
private static void PreprocessSplitIntoExpressions(string FileName, bool IsRW, string[] Lines, out Expression[] Expressions, bool AllowRwRouteDescription, double trackPositionOffset) { Expressions = new Expression[4096]; int e = 0; // full-line rw comments if (IsRW) { for (int i = 0; i < Lines.Length; i++) { int Level = 0; for (int j = 0; j < Lines[i].Length; j++) { switch (Lines[i][j]) { case '(': Level++; break; case ')': Level--; break; case ';': if (Level == 0) { Lines[i] = Lines[i].Substring(0, j).TrimEnd(); j = Lines[i].Length; } break; case '=': if (Level == 0) { j = Lines[i].Length; } break; } } } } // parse for (int i = 0; i < Lines.Length; i++) { //Remove empty null characters //Found these in a couple of older routes, harmless but generate errors //Possibly caused by BVE-RR (DOS version) Lines[i] = Lines[i].Replace("\0", ""); if (IsRW & AllowRwRouteDescription) { // ignore rw route description if ( Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].IndexOf("]", StringComparison.Ordinal) > 0 | Lines[i].StartsWith("$") ) { AllowRwRouteDescription = false; Game.RouteComment = Game.RouteComment.Trim(); } else { if (Game.RouteComment.Length != 0) { Game.RouteComment += "\n"; } Game.RouteComment += Lines[i]; continue; } } { // count expressions int n = 0; int Level = 0; for (int j = 0; j < Lines[i].Length; j++) { switch (Lines[i][j]) { case '(': Level++; break; case ')': Level--; break; case ',': if (!IsRW & Level == 0) { n++; } break; case '@': if (IsRW & Level == 0) { n++; } break; } } // create expressions int m = e + n + 1; while (m >= Expressions.Length) { Array.Resize <Expression>(ref Expressions, Expressions.Length << 1); } Level = 0; int a = 0, c = 0; for (int j = 0; j < Lines[i].Length; j++) { switch (Lines[i][j]) { case '(': Level++; break; case ')': if (Interface.CurrentOptions.EnableBveTsHacks) { if (Level > 0) { //Don't decrease the level below zero, as this messes up when extra closing brackets are encountered Level--; } else { Interface.AddMessage(MessageType.Warning, false, "Invalid additional closing parenthesis encountered at line " + i + " character " + j + " in file " + FileName); } } else { Level--; } break; case ',': if (Level == 0 & !IsRW) { string t = Lines[i].Substring(a, j - a).Trim(); if (t.Length > 0 && !t.StartsWith(";")) { Expressions[e] = new Expression { File = FileName, Text = t, Line = i + 1, Column = c + 1, TrackPositionOffset = trackPositionOffset }; e++; } a = j + 1; c++; } break; case '@': if (Level == 1 & IsRW & Interface.CurrentOptions.EnableBveTsHacks) { //BVE2 doesn't care if a bracket is unclosed, fixes various routefiles Level--; } else if (Level == 2 && IsRW & Interface.CurrentOptions.EnableBveTsHacks) { int k = j; while (k > 0) { k--; if (Lines[i][k] == '(') { //Opening bracket has been used instead of closing bracket, again BVE2 ignores this Level -= 2; break; } if (!char.IsWhiteSpace(Lines[i][k])) { //Bracket not found, and this isn't whitespace either, so break out break; } } } if (Level == 0 & IsRW) { string t = Lines[i].Substring(a, j - a).Trim(); if (t.Length > 0 && !t.StartsWith(";")) { Expressions[e] = new Expression { File = FileName, Text = t, Line = i + 1, Column = c + 1, TrackPositionOffset = trackPositionOffset }; e++; } a = j + 1; c++; } break; } } if (Lines[i].Length - a > 0) { string t = Lines[i].Substring(a).Trim(); if (t.Length > 0 && !t.StartsWith(";")) { Expressions[e] = new Expression { File = FileName, Text = t, Line = i + 1, Column = c + 1, TrackPositionOffset = trackPositionOffset }; e++; } } } } Array.Resize <Expression>(ref Expressions, e); }
// create route gradient profile internal static Bitmap CreateRouteGradientProfile(int Width, int Height) { // find first and last used element based on stations int n = TrackManager.CurrentTrack.Elements.Length; int n0 = n - 1; int n1 = 0; for (int i = 0; i < TrackManager.CurrentTrack.Elements.Length; i++) { for (int j = 0; j < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) { if (i < n0) { n0 = i; } if (i > n1) { n1 = i; } } } } n0 -= 4; n1 += 8; if (n0 < 0) { n0 = 0; } if (n1 >= TrackManager.CurrentTrack.Elements.Length) { n1 = TrackManager.CurrentTrack.Elements.Length - 1; } if (n1 <= n0) { n1 = n0 + 1; } // find dimensions double y0 = double.PositiveInfinity, y1 = double.NegativeInfinity; for (int i = n0; i <= n1; i++) { double y = TrackManager.CurrentTrack.Elements[i].WorldPosition.Y; if (y < y0) { y0 = y; } if (y > y1) { y1 = y; } } if (y0 >= y1 - 1.0) { y0 = y1 - 1.0; } double nd = 1.0 / (double)(n1 - n0); double yd = 1.0 / (double)(y1 - y0); double ox = 8.0, oy = 8.0; double w = (double)(Width - 16); double h = (double)(Height - 32); // create bitmap Bitmap b = new Bitmap(Width, Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb); Graphics g = Graphics.FromImage(b); g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; g.Clear(Color.White); // draw below sea level { double y = oy + h * (1.0 - 0.5 * (double)(-Game.RouteInitialElevation - y0) * yd); double x0 = ox - w * (double)(n0) * nd; double x1 = ox + w * (double)(n - n0) * nd; g.FillRectangle(Brushes.PaleGoldenrod, (float)x0, (float)y, (float)x1, (float)(oy + h) - (float)y); g.DrawLine(Pens.Gray, (float)x0, (float)y, (float)x1, (float)y); } // draw route path { PointF[] p = new PointF[n + 2]; p[0] = new PointF((float)(ox - w * (double)n0 * nd), (float)(oy + h)); for (int i = 0; i < n; i++) { double x = ox + w * (double)(i - n0) * nd; double y = oy + h * (1.0 - 0.5 * (double)(TrackManager.CurrentTrack.Elements[i].WorldPosition.Y - y0) * yd); p[i + 1] = new PointF((float)x, (float)y); } p[n + 1] = new PointF((float)(ox + w * (double)(n - n0 - 1) * nd), (float)(oy + h)); g.FillPolygon(Brushes.Tan, p); for (int i = 1; i < n; i++) { g.DrawLine(Pens.Black, p[i], p[i + 1]); } g.DrawLine(Pens.Black, 0.0f, (float)(oy + h), (float)Width, (float)(oy + h)); } // draw station names { Font f = new Font(FontFamily.GenericSansSerif, 12.0f, GraphicsUnit.Pixel); StringFormat m = new StringFormat(); for (int i = 0; i < n; i++) { for (int j = 0; j < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) { TrackManager.StationStartEvent e = (TrackManager.StationStartEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; if (Game.Stations[e.StationIndex].Name != string.Empty) { bool stop = Game.PlayerStopsAtStation(e.StationIndex); if (Interface.IsJapanese(Game.Stations[e.StationIndex].Name)) { m.Alignment = StringAlignment.Near; m.LineAlignment = StringAlignment.Near; double x = ox + w * (double)(i - n0) * nd; double y = oy + h * (1.0 - 0.5 * (double)(TrackManager.CurrentTrack.Elements[i].WorldPosition.Y - y0) * yd); string t = Game.Stations[e.StationIndex].Name; float tx = 0.0f, ty = (float)oy; for (int k = 0; k < t.Length; k++) { SizeF s = g.MeasureString(t.Substring(k, 1), f, 65536, StringFormat.GenericTypographic); if (s.Width > tx) { tx = s.Width; } } for (int k = 0; k < t.Length; k++) { g.DrawString(t.Substring(k, 1), f, stop ? Brushes.Black : Brushes.LightGray, (float)x - 0.5f * tx, ty); SizeF s = g.MeasureString(t.Substring(k, 1), f, 65536, StringFormat.GenericTypographic); ty += s.Height; } g.DrawLine(stop ? Pens.Gray : Pens.LightGray, new PointF((float)x, ty + 4.0f), new PointF((float)x, (float)y)); } else { m.Alignment = StringAlignment.Far; m.LineAlignment = StringAlignment.Near; double x = ox + w * (double)(i - n0) * nd; double y = oy + h * (1.0 - 0.5 * (double)(TrackManager.CurrentTrack.Elements[i].WorldPosition.Y - y0) * yd); g.RotateTransform(-90.0f); g.TranslateTransform((float)x, (float)oy, System.Drawing.Drawing2D.MatrixOrder.Append); g.DrawString(Game.Stations[e.StationIndex].Name, f, stop ? Brushes.Black : Brushes.LightGray, new PointF(0.0f, -5.0f), m); g.ResetTransform(); SizeF s = g.MeasureString(Game.Stations[e.StationIndex].Name, f); g.DrawLine(stop ? Pens.Gray : Pens.LightGray, new PointF((float)x, (float)(oy + s.Width + 4)), new PointF((float)x, (float)y)); } } } } } } // draw route markers { Font f = new Font(FontFamily.GenericSansSerif, 10.0f, GraphicsUnit.Pixel); Font fs = new Font(FontFamily.GenericSansSerif, 9.0f, GraphicsUnit.Pixel); StringFormat m = new StringFormat(); m.Alignment = StringAlignment.Far; m.LineAlignment = StringAlignment.Center; System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; int k = 48 * n / Width; for (int i = 0; i < n; i += k) { double x = ox + w * (double)(i - n0) * nd; double y = (double)(TrackManager.CurrentTrack.Elements[i].WorldPosition.Y - y0) * yd; if (x < w) { string t = ((int)Math.Round(TrackManager.CurrentTrack.Elements[i].StartingTrackPosition)).ToString(Culture); g.DrawString(t + "m", f, Brushes.Black, (float)x, (float)(oy + h + 6.0)); } { y = oy + h * (1.0 - 0.5 * y) + 2.0f; string t = ((int)Math.Round(Game.RouteInitialElevation + TrackManager.CurrentTrack.Elements[i].WorldPosition.Y)).ToString(Culture); SizeF s = g.MeasureString(t, fs); if (y < oy + h - (double)s.Width - 10.0) { g.RotateTransform(-90.0f); g.TranslateTransform((float)x, (float)y + 4.0f, System.Drawing.Drawing2D.MatrixOrder.Append); g.DrawString(t + "m", fs, Brushes.Black, 0.0f, 0.0f, m); g.ResetTransform(); g.DrawLine(Pens.Gray, (float)x, (float)(y + s.Width + 12.0), (float)x, (float)(oy + h)); } } } } // finalize return(b); }
private static void ParseExtensionsConfig(string filePath, Encoding encoding, out UnifiedObject[] carObjects, out UnifiedObject[] bogieObjects, out UnifiedObject[] couplerObjects, out double[] axleLocations, out double[] couplerDistances, out TrainManager.Train train, bool loadObjects) { CultureInfo culture = CultureInfo.InvariantCulture; carObjects = new UnifiedObject[0]; bogieObjects = new UnifiedObject[0]; couplerObjects = new UnifiedObject[0]; axleLocations = new double[0]; couplerDistances = new double[0]; train = new TrainManager.Train { Cars = new CarBase[0] }; if (!File.Exists(filePath)) { return; } bool[] carObjectsReversed = new bool[0]; bool[] bogieObjectsReversed = new bool[0]; bool[] carsDefined = new bool[0]; bool[] bogiesDefined = new bool[0]; bool[] couplerDefined = new bool[0]; string trainPath = System.IO.Path.GetDirectoryName(filePath); if (encoding == null) { encoding = TextEncoding.GetSystemEncodingFromFile(trainPath); } string[] lines = File.ReadAllLines(filePath, encoding); for (int i = 0; i < lines.Length; i++) { int j = lines[i].IndexOf(';'); if (j >= 0) { lines[i] = lines[i].Substring(0, j).Trim(new char[] { }); } else { lines[i] = lines[i].Trim(new char[] { }); } } for (int i = 0; i < lines.Length; i++) { if (lines[i].Length != 0) { switch (lines[i].ToLowerInvariant()) { case "[exterior]": // exterior i++; while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal)) { if (lines[i].Length != 0) { int j = lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { // ReSharper disable RedundantExplicitParamsArrayCreation string a = lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = lines[i].Substring(j + 1).TrimStart(new char[] { }); // ReSharper restore RedundantExplicitParamsArrayCreation int n; if (int.TryParse(a, NumberStyles.Integer, culture, out n)) { if (n >= 0) { if (n >= train.Cars.Length) { Array.Resize(ref train.Cars, n + 1); train.Cars[n] = new CarBase(train, n); Array.Resize(ref carObjects, n + 1); Array.Resize(ref bogieObjects, (n + 1) * 2); Array.Resize(ref couplerObjects, n); Array.Resize(ref carObjectsReversed, n + 1); Array.Resize(ref bogieObjectsReversed, (n + 1) * 2); Array.Resize(ref carsDefined, n + 1); Array.Resize(ref bogiesDefined, (n + 1) * 2); Array.Resize(ref couplerDefined, n); Array.Resize(ref axleLocations, (n + 1) * 2); Array.Resize(ref couplerDistances, n); } if (Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"File contains illegal characters at line {(i + 1).ToString(culture)} in file {filePath}"); } else { string file = Path.CombineFile(trainPath, b); if (File.Exists(file)) { if (loadObjects) { Program.CurrentHost.LoadObject(file, encoding, out carObjects[n]); } } else { Interface.AddMessage(MessageType.Error, true, $"The car object {file} does not exist at line {(i + 1).ToString(culture)} in file {filePath}"); } } } else { Interface.AddMessage(MessageType.Error, false, $"The car index {a} does not reference an existing car at line {(i + 1).ToString(culture)} in file {filePath}"); } } else { Interface.AddMessage(MessageType.Error, false, $"The car index is expected to be an integer at line {(i + 1).ToString(culture)} in file {filePath}"); } } else { Interface.AddMessage(MessageType.Error, false, $"Invalid statement {lines[i]} encountered at line {(i + 1).ToString(culture)} in file {filePath}"); } } i++; } i--; break; default: if (lines[i].StartsWith("[car", StringComparison.OrdinalIgnoreCase) & lines[i].EndsWith("]", StringComparison.Ordinal)) { // car string t = lines[i].Substring(4, lines[i].Length - 5); int n; if (int.TryParse(t, NumberStyles.Integer, culture, out n)) { if (n >= 0) { if (n >= train.Cars.Length) { Array.Resize(ref train.Cars, n + 1); train.Cars[n] = new CarBase(train, n); Array.Resize(ref carObjects, n + 1); Array.Resize(ref bogieObjects, (n + 1) * 2); Array.Resize(ref carObjectsReversed, n + 1); Array.Resize(ref bogieObjectsReversed, (n + 1) * 2); Array.Resize(ref couplerObjects, n); Array.Resize(ref carsDefined, n + 1); Array.Resize(ref bogiesDefined, (n + 1) * 2); Array.Resize(ref couplerDefined, n); Array.Resize(ref axleLocations, (n + 1) * 2); Array.Resize(ref couplerDistances, n); } if (carsDefined[n]) { Interface.AddMessage(MessageType.Error, false, $"Car {n.ToString(culture)} has already been declared at line {(i + 1).ToString(culture)} in file {filePath}"); } carsDefined[n] = true; i++; while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal)) { if (lines[i].Length != 0) { int j = lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { // ReSharper disable RedundantExplicitParamsArrayCreation string a = lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = lines[i].Substring(j + 1).TrimStart(new char[] { }); // ReSharper restore RedundantExplicitParamsArrayCreation switch (a.ToLowerInvariant()) { case "object": if (string.IsNullOrEmpty(b)) { Interface.AddMessage(MessageType.Error, true, $"An empty car object was supplied at line {(i + 1).ToString(culture)} in file {filePath}"); break; } if (Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"File contains illegal characters at line {(i + 1).ToString(culture)} in file {filePath}"); } else { string file = Path.CombineFile(trainPath, b); if (File.Exists(file)) { if (loadObjects) { Program.CurrentHost.LoadObject(file, encoding, out carObjects[n]); } } else { Interface.AddMessage(MessageType.Error, true, $"The car object {file} does not exist at line {(i + 1).ToString(culture)} in file {filePath}"); } } break; case "length": { double m; if (double.TryParse(b, NumberStyles.Float, culture, out m)) { if (m > 0.0) { train.Cars[n].Length = m; } else { Interface.AddMessage(MessageType.Error, false, $"Value is expected to be a positive floating-point number in {a} at line {(i + 1).ToString(culture)} in file {filePath}"); } } else { Interface.AddMessage(MessageType.Error, false, $"Value is expected to be a positive floating-point number in {a} at line {(i + 1).ToString(culture)} in file {filePath}"); } } break; case "reversed": carObjectsReversed[n] = b.Equals("true", StringComparison.OrdinalIgnoreCase); break; case "axles": int k = b.IndexOf(','); if (k >= 0) { // ReSharper disable RedundantExplicitParamsArrayCreation string c = b.Substring(0, k).TrimEnd(new char[] { }); string d = b.Substring(k + 1).TrimStart(new char[] { }); // ReSharper restore RedundantExplicitParamsArrayCreation double rear, front; if (!double.TryParse(c, NumberStyles.Float, culture, out rear)) { Interface.AddMessage(MessageType.Error, false, $"Rear is expected to be a floating-point number in {a} at line {(i + 1).ToString(culture)} in file {filePath}"); } else if (!double.TryParse(d, NumberStyles.Float, culture, out front)) { Interface.AddMessage(MessageType.Error, false, $"Front is expected to be a floating-point number in {a} at line {(i + 1).ToString(culture)} in file {filePath}"); } else if (rear >= front) { Interface.AddMessage(MessageType.Error, false, $"Rear is expected to be less than Front in {a} at line {(i + 1).ToString(culture)} in file {filePath}"); } else { if (n == 0) { axleLocations[n] = rear; axleLocations[n + 1] = front; } else { axleLocations[n * 2] = rear; axleLocations[n * 2 + 1] = front; } } } else { Interface.AddMessage(MessageType.Error, false, $"An argument-separating comma is expected in {a} at line {(i + 1).ToString(culture)} in file {filePath}"); } break; default: Interface.AddMessage(MessageType.Warning, false, $"Unsupported key-value pair {a} encountered at line {(i + 1).ToString(culture)} in file {filePath}"); break; } } else { Interface.AddMessage(MessageType.Error, false, $"Invalid statement {lines[i]} encountered at line {(i + 1).ToString(culture)} in file {filePath}"); } } i++; } i--; } else { Interface.AddMessage(MessageType.Error, false, $"The car index {t} does not reference an existing car at line {(i + 1).ToString(culture)} in file {filePath}"); } } else { Interface.AddMessage(MessageType.Error, false, $"The car index is expected to be an integer at line {(i + 1).ToString(culture)} in file {filePath}"); } } else if (lines[i].StartsWith("[bogie", StringComparison.OrdinalIgnoreCase) & lines[i].EndsWith("]", StringComparison.Ordinal)) { // bogie string t = lines[i].Substring(6, lines[i].Length - 7); int n; if (int.TryParse(t, NumberStyles.Integer, culture, out n)) { if (n >= train.Cars.Length * 2) { Array.Resize(ref train.Cars, n / 2 + 1); if (n == 0) { train.Cars[0] = new CarBase(train, 0); Array.Resize(ref axleLocations, 2); } else { train.Cars[n / 2] = new CarBase(train, n / 2); Array.Resize(ref axleLocations, (n / 2 + 1) * 2); } Array.Resize(ref carObjects, n / 2 + 1); Array.Resize(ref bogieObjects, n + 2); Array.Resize(ref couplerObjects, n / 2); Array.Resize(ref carObjectsReversed, n / 2 + 1); Array.Resize(ref bogieObjectsReversed, n + 2); Array.Resize(ref carsDefined, n / 2 + 1); Array.Resize(ref bogiesDefined, n + 2); Array.Resize(ref couplerDefined, n / 2); Array.Resize(ref couplerDistances, n / 2); } if (n > bogiesDefined.Length - 1) { continue; } if (bogiesDefined[n]) { Interface.AddMessage(MessageType.Error, false, $"Bogie {n.ToString(culture)} has already been declared at line {(i + 1).ToString(culture)} in file {filePath}"); } bogiesDefined[n] = true; //Assuming that there are two bogies per car if (n >= 0 & n < train.Cars.Length * 2) { i++; while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal)) { if (lines[i].Length != 0) { int j = lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { // ReSharper disable RedundantExplicitParamsArrayCreation string a = lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = lines[i].Substring(j + 1).TrimStart(new char[] { }); // ReSharper restore RedundantExplicitParamsArrayCreation switch (a.ToLowerInvariant()) { case "object": if (Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"File contains illegal characters at line {(i + 1).ToString(culture)} in file {filePath}"); } else { if (string.IsNullOrEmpty(b)) { Interface.AddMessage(MessageType.Error, true, $"An empty bogie object was supplied at line {(i + 1).ToString(culture)} in file {filePath}"); break; } string file = Path.CombineFile(trainPath, b); if (File.Exists(file)) { if (loadObjects) { Program.CurrentHost.LoadObject(file, encoding, out bogieObjects[n]); } } else { Interface.AddMessage(MessageType.Error, true, $"The bogie object {file} does not exist at line {(i + 1).ToString(culture)} in file {filePath}"); } } break; case "length": { Interface.AddMessage(MessageType.Error, false, $"A defined length is not supported for bogies at line {(i + 1).ToString(culture)} in file {filePath}"); } break; case "reversed": bogieObjectsReversed[n] = b.Equals("true", StringComparison.OrdinalIgnoreCase); break; case "axles": //Axles aren't used in bogie positioning, just in rotation break; default: Interface.AddMessage(MessageType.Warning, false, $"Unsupported key-value pair {a} encountered at line {(i + 1).ToString(culture)} in file {filePath}"); break; } } else { Interface.AddMessage(MessageType.Error, false, $"Invalid statement {lines[i]} encountered at line {(i + 1).ToString(culture)} in file {filePath}"); } } i++; } i--; } else { Interface.AddMessage(MessageType.Error, false, $"The bogie index {t} does not reference an existing bogie at line {(i + 1).ToString(culture)} in file {filePath}"); } } else { Interface.AddMessage(MessageType.Error, false, $"The bogie index is expected to be an integer at line {(i + 1).ToString(culture)} in file {filePath}"); } } else if (lines[i].StartsWith("[coupler", StringComparison.OrdinalIgnoreCase) & lines[i].EndsWith("]", StringComparison.Ordinal)) { // coupler string t = lines[i].Substring(8, lines[i].Length - 9); int n; if (int.TryParse(t, NumberStyles.Integer, culture, out n)) { if (n >= 0) { if (n >= train.Cars.Length - 1) { Array.Resize(ref train.Cars, n + 2); train.Cars[n + 1] = new CarBase(train, n + 1); Array.Resize(ref carObjects, n + 2); Array.Resize(ref bogieObjects, (n + 2) * 2); Array.Resize(ref carObjectsReversed, n + 2); Array.Resize(ref bogieObjectsReversed, (n + 2) * 2); Array.Resize(ref couplerObjects, n + 1); Array.Resize(ref carsDefined, n + 2); Array.Resize(ref bogiesDefined, (n + 2) * 2); Array.Resize(ref couplerDefined, n + 1); Array.Resize(ref axleLocations, (n + 2) * 2); Array.Resize(ref couplerDistances, n + 1); } if (couplerDefined[n]) { Interface.AddMessage(MessageType.Error, false, $"Coupler {n.ToString(culture)} has already been declared at line {(i + 1).ToString(culture)} in file {filePath}"); } couplerDefined[n] = true; i++; while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal)) { if (lines[i].Length != 0) { int j = lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { // ReSharper disable RedundantExplicitParamsArrayCreation string a = lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = lines[i].Substring(j + 1).TrimStart(new char[] { }); // ReSharper restore RedundantExplicitParamsArrayCreation switch (a.ToLowerInvariant()) { case "distances": { int k = b.IndexOf(','); if (k >= 0) { // ReSharper disable RedundantExplicitParamsArrayCreation string c = b.Substring(0, k).TrimEnd(new char[] { }); string d = b.Substring(k + 1).TrimStart(new char[] { }); // ReSharper restore RedundantExplicitParamsArrayCreation double min, max; if (!double.TryParse(c, NumberStyles.Float, culture, out min)) { Interface.AddMessage(MessageType.Error, false, $"Minimum is expected to be a floating-point number in {a} at line {(i + 1).ToString(culture)} in file {filePath}"); } else if (!double.TryParse(d, NumberStyles.Float, culture, out max)) { Interface.AddMessage(MessageType.Error, false, $"Maximum is expected to be a floating-point number in {a} at line {(i + 1).ToString(culture)} in file {filePath}"); } else if (min > max) { Interface.AddMessage(MessageType.Error, false, $"Minimum is expected to be less than Maximum in {a} at line {(i + 1).ToString(culture)} in file {filePath}"); } else { couplerDistances[n] = 0.5 * (min + max); } } else { Interface.AddMessage(MessageType.Error, false, $"An argument-separating comma is expected in {a} at line {(i + 1).ToString(culture)} in file {filePath}"); } } break; case "object": if (string.IsNullOrEmpty(b)) { Interface.AddMessage(MessageType.Error, true, $"An empty coupler object was supplied at line {(i + 1).ToString(culture)} in file {filePath}"); break; } if (Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"File contains illegal characters at line {(i + 1).ToString(culture)} in file {filePath}"); } else { string file = Path.CombineFile(trainPath, b); if (File.Exists(file)) { if (loadObjects) { Program.CurrentHost.LoadObject(file, encoding, out couplerObjects[n]); } } else { Interface.AddMessage(MessageType.Error, true, $"The coupler object {file} does not exist at line {(i + 1).ToString(culture)} in file {filePath}"); } } break; default: Interface.AddMessage(MessageType.Warning, false, $"Unsupported key-value pair {a} encountered at line {(i + 1).ToString(culture)} in file {filePath}"); break; } } else { Interface.AddMessage(MessageType.Error, false, $"Invalid statement {lines[i]} encountered at line {(i + 1).ToString(culture)} in file {filePath}"); } } i++; } i--; } else { Interface.AddMessage(MessageType.Error, false, $"The coupler index {t} does not reference an existing coupler at line {(i + 1).ToString(culture)} in file {filePath}"); } } else { Interface.AddMessage(MessageType.Error, false, $"The coupler index is expected to be an integer at line {(i + 1).ToString(culture)} in file {filePath}"); } } else { // default if (lines.Length == 1 && encoding.Equals(Encoding.Unicode)) { /* * If only one line, there's a good possibility that our file is NOT Unicode at all * and that the misdetection has turned it into garbage * * Try again with ASCII instead */ ParseExtensionsConfig(filePath, Encoding.GetEncoding(1252), out carObjects, out bogieObjects, out couplerObjects, out axleLocations, out couplerDistances, out train, loadObjects); return; } Interface.AddMessage(MessageType.Error, false, $"Invalid statement {lines[i]} encountered at line {(i + 1).ToString(culture)} in file {filePath}"); } break; } } } // check for car objects and reverse if necessary int carObjectsCount = 0; for (int i = 0; i < train.Cars.Length; i++) { if (carObjects[i] != null) { carObjectsCount++; if (carObjectsReversed[i] && loadObjects) { if (carObjects[i] is StaticObject) { StaticObject obj = (StaticObject)carObjects[i].Clone(); obj.ApplyScale(-1.0, 1.0, -1.0); carObjects[i] = obj; } else if (carObjects[i] is AnimatedObjectCollection) { AnimatedObjectCollection obj = (AnimatedObjectCollection)carObjects[i].Clone(); obj.Reverse(); carObjects[i] = obj; } else { throw new NotImplementedException(); } } } } //Check for bogie objects and reverse if necessary..... int bogieObjectsCount = 0; for (int i = 0; i < train.Cars.Length * 2; i++) { if (bogieObjects[i] != null) { bogieObjectsCount++; if (bogieObjectsReversed[i] && loadObjects) { if (bogieObjects[i] is StaticObject) { StaticObject obj = (StaticObject)bogieObjects[i]; obj.ApplyScale(-1.0, 1.0, -1.0); } else if (bogieObjects[i] is AnimatedObjectCollection) { AnimatedObjectCollection obj = (AnimatedObjectCollection)bogieObjects[i]; obj.Reverse(); } 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}"); } }
private static void LoadEverythingThreaded() { string RailwayFolder = GetRailwayFolder(CurrentRouteFile); string ObjectFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Object"); string SoundFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Sound"); Game.Reset(true); Game.MinimalisticSimulation = true; // screen Program.Renderer.Camera.CurrentMode = CameraViewMode.Interior; bool loaded = false; Program.FileSystem.AppendToLogFile("INFO: " + Program.CurrentHost.AvailableRoutePluginCount + " Route loading plugins available."); Program.FileSystem.AppendToLogFile("INFO: " + Program.CurrentHost.AvailableObjectPluginCount + " Object loading plugins available."); Program.FileSystem.AppendToLogFile("INFO: " + Program.CurrentHost.AvailableRoutePluginCount + " Sound loading plugins available."); for (int i = 0; i < Program.CurrentHost.Plugins.Length; i++) { if (Program.CurrentHost.Plugins[i].Route != null && Program.CurrentHost.Plugins[i].Route.CanLoadRoute(CurrentRouteFile)) { object Route = (object)Program.CurrentRoute; //must cast to allow us to use the ref keyword. if (Program.CurrentHost.Plugins[i].Route.LoadRoute(CurrentRouteFile, CurrentRouteEncoding, CurrentTrainFolder, ObjectFolder, SoundFolder, false, ref Route)) { Program.CurrentRoute = (CurrentRoute)Route; Program.Renderer.Lighting.OptionAmbientColor = Program.CurrentRoute.Atmosphere.AmbientLightColor; Program.Renderer.Lighting.OptionDiffuseColor = Program.CurrentRoute.Atmosphere.DiffuseLightColor; Program.Renderer.Lighting.OptionLightPosition = Program.CurrentRoute.Atmosphere.LightPosition; loaded = true; break; } var currentError = Translations.GetInterfaceString("errors_critical_file"); currentError = currentError.Replace("[file]", System.IO.Path.GetFileName(CurrentRouteFile)); MessageBox.Show(currentError, @"OpenBVE", MessageBoxButtons.OK, MessageBoxIcon.Hand); Interface.AddMessage(MessageType.Critical, false, "The route and train loader encountered the following critical error: " + Program.CurrentHost.Plugins[i].Route.LastException.Message); CrashHandler.LoadingCrash(Program.CurrentHost.Plugins[i].Route.LastException.Message, false); Program.RestartArguments = " "; Cancel = true; return; } } TrainManager.Derailments = Interface.CurrentOptions.Derailments; TrainManager.Toppling = Interface.CurrentOptions.Toppling; TrainManager.CurrentRoute = Program.CurrentRoute; if (!loaded) { throw new Exception("No plugins capable of loading routefile " + CurrentRouteFile + " were found."); } Thread createIllustrations = new Thread(Program.CurrentRoute.Information.LoadInformation) { IsBackground = true }; createIllustrations.Start(); System.Threading.Thread.Sleep(1); if (Cancel) { return; } Program.CurrentRoute.Atmosphere.CalculateSeaLevelConstants(); if (Program.CurrentRoute.BogusPreTrainInstructions.Length != 0) { double t = Program.CurrentRoute.BogusPreTrainInstructions[0].Time; double p = Program.CurrentRoute.BogusPreTrainInstructions[0].TrackPosition; for (int i = 1; i < Program.CurrentRoute.BogusPreTrainInstructions.Length; i++) { if (Program.CurrentRoute.BogusPreTrainInstructions[i].Time > t) { t = Program.CurrentRoute.BogusPreTrainInstructions[i].Time; } else { t += 1.0; Program.CurrentRoute.BogusPreTrainInstructions[i].Time = t; } if (Program.CurrentRoute.BogusPreTrainInstructions[i].TrackPosition > p) { p = Program.CurrentRoute.BogusPreTrainInstructions[i].TrackPosition; } else { p += 1.0; Program.CurrentRoute.BogusPreTrainInstructions[i].TrackPosition = p; } } } Program.Renderer.CameraTrackFollower = new TrackFollower(Program.CurrentHost) { Train = null, Car = null }; if (Program.CurrentRoute.Stations.Length == 1) { //Log the fact that only a single station is present, as this is probably not right Program.FileSystem.AppendToLogFile("The processed route file only contains a single station."); } Program.FileSystem.AppendToLogFile("Route file loaded successfully."); // initialize trains System.Threading.Thread.Sleep(1); if (Cancel) { return; } Program.TrainManager.Trains = new TrainBase[Program.CurrentRoute.PrecedingTrainTimeDeltas.Length + 1 + (Program.CurrentRoute.BogusPreTrainInstructions.Length != 0 ? 1 : 0)]; for (int k = 0; k < Program.TrainManager.Trains.Length; k++) { if (k == Program.TrainManager.Trains.Length - 1 & Program.CurrentRoute.BogusPreTrainInstructions.Length != 0) { Program.TrainManager.Trains[k] = new TrainBase(TrainState.Bogus); } else { Program.TrainManager.Trains[k] = new TrainBase(TrainState.Pending); } } TrainManager.PlayerTrain = Program.TrainManager.Trains[Program.CurrentRoute.PrecedingTrainTimeDeltas.Length]; // load trains for (int k = 0; k < Program.TrainManager.Trains.Length; k++) { AbstractTrain currentTrain = Program.TrainManager.Trains[k]; for (int i = 0; i < Program.CurrentHost.Plugins.Length; i++) { if (Program.CurrentHost.Plugins[i].Train != null && Program.CurrentHost.Plugins[i].Train.CanLoadTrain(CurrentTrainFolder)) { Program.CurrentHost.Plugins[i].Train.LoadTrain(CurrentTrainEncoding, CurrentTrainFolder, ref currentTrain, ref Interface.CurrentControls); break; } } Program.Renderer.UpdateViewingDistances(Program.CurrentRoute.CurrentBackground.BackgroundImageDistance); // configure other properties if (currentTrain.IsPlayerTrain) { currentTrain.TimetableDelta = 0.0; if (Game.InitialReversedConsist) { currentTrain.Reverse(); TrainManager.PlayerTrain.CameraCar = currentTrain.DriverCar; Program.Renderer.Camera.CurrentRestriction = TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].CameraRestrictionMode; } } else if (currentTrain.State != TrainState.Bogus) { TrainBase train = currentTrain as TrainBase; currentTrain.AI = new Game.SimpleHumanDriverAI(train, Interface.CurrentOptions.PrecedingTrainSpeedLimit); currentTrain.TimetableDelta = Program.CurrentRoute.PrecedingTrainTimeDeltas[k]; train.Specs.DoorOpenMode = DoorMode.Manual; train.Specs.DoorCloseMode = DoorMode.Manual; } } // finished created objects System.Threading.Thread.Sleep(1); if (Cancel) { return; } Array.Resize(ref ObjectManager.AnimatedWorldObjects, ObjectManager.AnimatedWorldObjectsUsed); // update sections if (Program.CurrentRoute.Sections.Length > 0) { Program.CurrentRoute.UpdateAllSections(); } // load plugin CurrentTrain = 0; for (int i = 0; i < Program.TrainManager.Trains.Length; i++) { if (Program.TrainManager.Trains[i].State != TrainState.Bogus) { if (Program.TrainManager.Trains[i].IsPlayerTrain) { if (!Program.TrainManager.Trains[i].LoadCustomPlugin(Program.TrainManager.Trains[i].TrainFolder, CurrentTrainEncoding)) { Program.TrainManager.Trains[i].LoadDefaultPlugin(Program.TrainManager.Trains[i].TrainFolder); } } else { Program.TrainManager.Trains[i].LoadDefaultPlugin(Program.TrainManager.Trains[i].TrainFolder); } for (int j = 0; j < InputDevicePlugin.AvailablePluginInfos.Count; j++) { if (InputDevicePlugin.AvailablePluginInfos[j].Status == InputDevicePlugin.PluginInfo.PluginStatus.Enable && InputDevicePlugin.AvailablePlugins[j] is ITrainInputDevice) { ITrainInputDevice trainInputDevice = (ITrainInputDevice)InputDevicePlugin.AvailablePlugins[j]; trainInputDevice.SetVehicleSpecs(Program.TrainManager.Trains[i].vehicleSpecs()); } } } CurrentTrain++; } }
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 = Ls3DObjectParser.ReadObject(CurrentObjects[i].Name, LoadMode, 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); Object.Dynamic = true; 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); }
/// <summary>Loads the specified plugin for the specified train.</summary> /// <param name="train">The train to attach the plugin to.</param> /// <param name="pluginFile">The file to the plugin.</param> /// <param name="trainFolder">The train folder.</param> /// <returns>Whether the plugin was loaded successfully.</returns> private static bool LoadPlugin(TrainManager.Train train, string pluginFile, string trainFolder) { string pluginTitle = System.IO.Path.GetFileName(pluginFile); if (!System.IO.File.Exists(pluginFile)) { Interface.AddMessage(MessageType.Error, true, "The train plugin " + pluginTitle + " could not be found."); return(false); } /* * Unload plugin if already loaded. * */ if (train.Plugin != null) { UnloadPlugin(train); } /* * Prepare initialization data for the plugin. * */ BrakeTypes brakeType = (BrakeTypes)train.Cars[train.DriverCar].CarBrake.brakeType; int brakeNotches; int powerNotches; bool hasHoldBrake; if (brakeType == BrakeTypes.AutomaticAirBrake) { brakeNotches = 2; powerNotches = train.Handles.Power.MaximumNotch; hasHoldBrake = false; } else { brakeNotches = train.Handles.Brake.MaximumNotch + (train.Handles.HasHoldBrake ? 1 : 0); powerNotches = train.Handles.Power.MaximumNotch; hasHoldBrake = train.Handles.HasHoldBrake; } bool hasLocoBrake = train.Handles.HasLocoBrake; int cars = train.Cars.Length; VehicleSpecs specs = new VehicleSpecs(powerNotches, brakeType, brakeNotches, hasHoldBrake, hasLocoBrake, cars); InitializationModes mode = (InitializationModes)Game.TrainStart; /* * Check if the plugin is a .NET plugin. * */ Assembly assembly; try { assembly = Assembly.LoadFile(pluginFile); } catch (BadImageFormatException) { assembly = null; } catch (Exception ex) { Interface.AddMessage(MessageType.Error, false, "The train plugin " + pluginTitle + " could not be loaded due to the following exception: " + ex.Message); return(false); } if (assembly != null) { Type[] types; try { types = assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { foreach (Exception e in ex.LoaderExceptions) { Interface.AddMessage(MessageType.Error, false, "The train plugin " + pluginTitle + " raised an exception on loading: " + e.Message); } return(false); } foreach (Type type in types) { if (typeof(IRuntime).IsAssignableFrom(type)) { if (type.FullName == null) { //Should never happen, but static code inspection suggests that it's possible.... throw new InvalidOperationException(); } IRuntime api = assembly.CreateInstance(type.FullName) as IRuntime; train.Plugin = new NetPlugin(pluginFile, trainFolder, api, train); if (train.Plugin.Load(specs, mode)) { return(true); } else { train.Plugin = null; return(false); } } } Interface.AddMessage(MessageType.Error, false, "The train plugin " + pluginTitle + " does not export a train interface and therefore cannot be used with openBVE."); return(false); } /* * Check if the plugin is a Win32 plugin. * */ try { if (!CheckWin32Header(pluginFile)) { Interface.AddMessage(MessageType.Error, false, "The train plugin " + pluginTitle + " is of an unsupported binary format and therefore cannot be used with openBVE."); return(false); } } catch (Exception ex) { Interface.AddMessage(MessageType.Error, false, "The train plugin " + pluginTitle + " could not be read due to the following reason: " + ex.Message); return(false); } if (!Program.CurrentlyRunningOnWindows | IntPtr.Size != 4) { Interface.AddMessage(MessageType.Warning, false, "The train plugin " + pluginTitle + " can only be used on 32-bit Microsoft Windows or compatible."); return(false); } if (Program.CurrentlyRunningOnWindows && !System.IO.File.Exists(AppDomain.CurrentDomain.BaseDirectory + "\\AtsPluginProxy.dll")) { Interface.AddMessage(MessageType.Warning, false, "AtsPluginProxy.dll is missing or corrupt- Please reinstall."); return(false); } train.Plugin = new Win32Plugin(pluginFile, train); if (train.Plugin.Load(specs, mode)) { return(true); } else { train.Plugin = null; Interface.AddMessage(MessageType.Error, false, "The train plugin " + pluginTitle + " does not export a train interface and therefore cannot be used with openBVE."); return(false); } }
/// <summary>Parses a string into OpenBVE's internal time representation (Seconds since midnight on the first day)</summary> /// <param name="Expression">The time in string format</param> /// <param name="Value">The number of seconds since midnight on the first day this represents, updated via 'out'</param> /// <returns>True if the parse succeeds, false if it does not</returns> internal static bool TryParseTime(string Expression, out double Value) { Expression = Expression.TrimInside(); if (Expression.Length != 0) { CultureInfo Culture = CultureInfo.InvariantCulture; int i = Expression.IndexOf('.'); if (i == -1) { i = Expression.IndexOf(':'); } if (i >= 1) { int h; if (int.TryParse(Expression.Substring(0, i), NumberStyles.Integer, Culture, out h)) { int n = Expression.Length - i - 1; if (n == 1 | n == 2) { uint m; if (uint.TryParse(Expression.Substring(i + 1, n), NumberStyles.None, Culture, out m)) { Value = 3600.0 * (double)h + 60.0 * (double)m; return(true); } } else if (n >= 3) { if (n > 4) { Interface.AddMessage(MessageType.Warning, false, "A maximum of 4 digits of precision are supported in TIME values"); n = 4; } uint m; if (uint.TryParse(Expression.Substring(i + 1, 2), NumberStyles.None, Culture, out m)) { uint s; string ss = Expression.Substring(i + 3, n - 2); if (Interface.CurrentOptions.EnableBveTsHacks) { /* * Handles values in the following format: * HH.MM.SS */ if (ss.StartsWith(".")) { ss = ss.Substring(1, ss.Length - 1); } } if (uint.TryParse(ss, NumberStyles.None, Culture, out s)) { Value = 3600.0 * (double)h + 60.0 * (double)m + (double)s; return(true); } } } } } else if (i == -1) { int h; if (int.TryParse(Expression, NumberStyles.Integer, Culture, out h)) { Value = 3600.0 * (double)h; return(true); } } } Value = 0.0; return(false); }
/// <summary>Loads a Wavefront object 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> /// <returns>The object loaded.</returns> internal static ObjectManager.StaticObject ReadObject(string FileName, Encoding Encoding) { ObjectManager.StaticObject Object = new ObjectManager.StaticObject(); MeshBuilder Builder = new MeshBuilder(); /* * Temporary arrays */ List <Vector3> tempVertices = new List <Vector3>(); List <Vector3> tempNormals = new List <Vector3>(); List <Vector2> tempCoords = new List <Vector2>(); Material[] TempMaterials = new Material[0]; //Stores the current material int currentMaterial = -1; //Read the contents of the file string[] Lines = File.ReadAllLines(FileName, Encoding); //Preprocess for (int i = 0; i < Lines.Length; i++) { // Strip hash comments int c = Lines[i].IndexOf("#", StringComparison.Ordinal); if (c >= 0) { Lines[i] = Lines[i].Substring(0, c); } // collect arguments List <string> Arguments = new List <string>(Lines[i].Split(new char[] { ' ', '\t' }, StringSplitOptions.None)); for (int j = Arguments.Count - 1; j >= 0; j--) { Arguments[j] = Arguments[j].Trim(); if (Arguments[j] == string.Empty) { Arguments.RemoveAt(j); } } if (Arguments.Count == 0) { continue; } switch (Arguments[0].ToLowerInvariant()) { case "v": //Vertex Vector3 vertex = new Vector3(); if (!double.TryParse(Arguments[1], out vertex.X)) { Interface.AddMessage(MessageType.Warning, false, "Invalid X co-ordinate in Vertex at Line " + i); } if (!double.TryParse(Arguments[2], out vertex.Y)) { Interface.AddMessage(MessageType.Warning, false, "Invalid Y co-ordinate in Vertex at Line " + i); } if (!double.TryParse(Arguments[3], out vertex.Z)) { Interface.AddMessage(MessageType.Warning, false, "Invalid Z co-ordinate in Vertex at Line " + i); } tempVertices.Add(vertex); break; case "vt": //Vertex texture co-ords Vector2 coords = new Vector2(); if (!double.TryParse(Arguments[1], out coords.X)) { Interface.AddMessage(MessageType.Warning, false, "Invalid X co-ordinate in Texture Co-ordinates at Line " + i); } if (!double.TryParse(Arguments[2], out coords.Y)) { Interface.AddMessage(MessageType.Warning, false, "Invalid X co-ordinate in Texture Co-Ordinates at Line " + i); } tempCoords.Add(coords); break; case "vn": Vector3 normal = new Vector3(); if (!double.TryParse(Arguments[1], out normal.X)) { Interface.AddMessage(MessageType.Warning, false, "Invalid X co-ordinate in Vertex Normal at Line " + i); } if (!double.TryParse(Arguments[2], out normal.Y)) { Interface.AddMessage(MessageType.Warning, false, "Invalid Y co-ordinate in Vertex Normal at Line " + i); } if (!double.TryParse(Arguments[3], out normal.Z)) { Interface.AddMessage(MessageType.Warning, false, "Invalid Z co-ordinate in Vertex Normal at Line " + i); } tempNormals.Add(normal); //Vertex normals break; case "vp": //Parameter space verticies, not supported throw new NotSupportedException("Parameter space verticies are not supported by this parser"); case "f": //Creates a new face //Create the temp list to hook out the vertices List <Vertex> vertices = new List <Vertex>(); List <Vector3> normals = new List <Vector3>(); for (int f = 1; f < Arguments.Count; f++) { Vertex newVertex = new Vertex(); string[] faceArguments = Arguments[f].Split(new char[] { '/' }, StringSplitOptions.None); int idx; if (!int.TryParse(faceArguments[0], out idx)) { Interface.AddMessage(MessageType.Warning, false, "Invalid Vertex index in Face " + f + " at Line " + i); continue; } int currentVertex = tempVertices.Count; if (idx != Math.Abs(idx)) { //Offset, so we seem to need to add one.... currentVertex++; currentVertex += idx; } else { currentVertex = idx; } if (currentVertex > tempVertices.Count) { Interface.AddMessage(MessageType.Warning, false, "Vertex index " + idx + " was greater than the available number of vertices in Face " + f + " at Line " + i); continue; } newVertex.Coordinates = tempVertices[currentVertex - 1]; if (faceArguments.Length <= 1) { normals.Add(new Vector3()); } else { if (!int.TryParse(faceArguments[1], out idx)) { if (!string.IsNullOrEmpty(faceArguments[1])) { Interface.AddMessage(MessageType.Warning, false, "Invalid Texture Co-ordinate index in Face " + f + " at Line " + i); } newVertex.TextureCoordinates = new Vector2(); } else { int currentCoord = tempCoords.Count; if (idx != Math.Abs(idx)) { //Offset, so we seem to need to add one.... currentCoord++; currentCoord += idx; } else { currentCoord = idx; } if (currentCoord > tempCoords.Count) { Interface.AddMessage(MessageType.Warning, false, "Texture Co-ordinate index " + currentCoord + " was greater than the available number of texture co-ordinates in Face " + f + " at Line " + i); } else { newVertex.TextureCoordinates = tempCoords[currentCoord - 1]; } } } if (faceArguments.Length <= 2) { normals.Add(new Vector3()); } else { if (!int.TryParse(faceArguments[2], out idx)) { if (!string.IsNullOrEmpty(faceArguments[2])) { Interface.AddMessage(MessageType.Warning, false, "Invalid Vertex Normal index in Face " + f + " at Line " + i); } normals.Add(new Vector3()); } else { int currentNormal = tempNormals.Count; if (idx != Math.Abs(idx)) { //Offset, so we seem to need to add one.... currentNormal++; currentNormal += idx; } else { currentNormal = idx; } if (currentNormal > tempNormals.Count) { Interface.AddMessage(MessageType.Warning, false, "Vertex Normal index " + currentNormal + " was greater than the available number of normals in Face " + f + " at Line " + i); normals.Add(new Vector3()); } else { normals.Add(tempNormals[currentNormal - 1]); } } } vertices.Add(newVertex); } MeshFaceVertex[] Vertices = new MeshFaceVertex[vertices.Count]; for (int k = 0; k < vertices.Count; k++) { Builder.Vertices.Add(vertices[k]); Vertices[k].Index = (ushort)(Builder.Vertices.Count - 1); Vertices[k].Normal = normals[k]; } Builder.Faces.Add(currentMaterial == -1 ? new MeshFace(Vertices, 0) : new MeshFace(Vertices, (ushort)currentMaterial)); break; case "g": //Starts a new face group and (normally) applies a new texture ApplyMeshBuilder(ref Object, Builder); Builder = new MeshBuilder(); break; case "s": /* * Changes the smoothing group applied to these vertexes: * 0- Disabled (Overriden by Vertex normals) * Otherwise appears to be a bitmask (32 available groups) * whereby faces within the same groups have their normals averaged * to appear smooth joins * * Not really supported at the minute, probably requires the engine * twiddling to deliberately support specifiying the shading type for a face * */ break; case "mtllib": //Loads the library of materials used by this file string MaterialsPath = OpenBveApi.Path.CombineFile(Path.GetDirectoryName(FileName), Arguments[1]); if (File.Exists(MaterialsPath)) { LoadMaterials(MaterialsPath, ref TempMaterials); } break; case "usemtl": for (int m = 0; m < TempMaterials.Length; m++) { if (TempMaterials[m].Key.ToLowerInvariant() == Arguments[1].ToLowerInvariant()) { bool mf = false; for (int k = 0; k < Builder.Materials.Length; k++) { if (Builder.Materials[k].Key.ToLowerInvariant() == Arguments[1].ToLowerInvariant()) { mf = true; currentMaterial = k; break; } } if (!mf) { Array.Resize(ref Builder.Materials, Builder.Materials.Length + 1); Builder.Materials[Builder.Materials.Length - 1] = TempMaterials[m]; currentMaterial = Builder.Materials.Length - 1; } break; } if (m == TempMaterials.Length) { Interface.AddMessage(MessageType.Error, true, "Material " + Arguments[1] + " was not found."); currentMaterial = -1; } } break; default: Interface.AddMessage(MessageType.Warning, false, "Unrecognised command " + Arguments[0]); break; } } ApplyMeshBuilder(ref Object, Builder); Object.Mesh.CreateNormals(); return(Object); }
/// <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 = CameraRestrictionMode.NotAvailable; Camera.CurrentRestriction = CameraRestrictionMode.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 = CameraRestrictionMode.On; Camera.CurrentRestriction = CameraRestrictionMode.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 = CameraRestrictionMode.NotAvailable; Camera.CurrentRestriction = CameraRestrictionMode.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 = CameraRestrictionMode.On; Camera.CurrentRestriction = CameraRestrictionMode.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 = CameraRestrictionMode.On; Camera.CurrentRestriction = CameraRestrictionMode.On; } else { Camera.CurrentRestriction = CameraRestrictionMode.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; } }
public static bool ReadMarkerXML(string fileName, ref CsvRwRouteParser.Marker marker) { //The current XML file to load XmlDocument currentXML = new XmlDocument(); //Load the marker's XML file currentXML.Load(fileName); string Path = System.IO.Path.GetDirectoryName(fileName); if (currentXML.DocumentElement != null) { bool iM = false; XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/openBVE/TextMarker"); if (DocumentNodes == null || DocumentNodes.Count == 0) { DocumentNodes = currentXML.DocumentElement.SelectNodes("/openBVE/ImageMarker"); iM = true; } if (DocumentNodes == null || DocumentNodes.Count == 0) { Interface.AddMessage(MessageType.Error, false, "No marker nodes defined in XML file " + fileName); return(false); } //marker = new CsvRwRouteParser.Marker(); foreach (XmlNode n in DocumentNodes) { if (n.ChildNodes.OfType <XmlElement>().Any()) { bool EarlyDefined = false, LateDefined = false; string EarlyText = null, Text = null, LateText = null; string[] Trains = null; Texture EarlyTexture = null, Texture = null, LateTexture = null; double EarlyTime = 0.0, LateTime = 0.0, TimeOut = Double.PositiveInfinity, EndingPosition = Double.PositiveInfinity; OpenBveApi.Colors.MessageColor EarlyColor = MessageColor.White, OnTimeColor = MessageColor.White, LateColor = MessageColor.White; foreach (XmlNode c in n.ChildNodes) { switch (c.Name.ToLowerInvariant()) { case "early": if (!c.ChildNodes.OfType <XmlElement>().Any()) { Interface.AddMessage(MessageType.Error, false, "No paramaters defined for the early message in " + fileName); } foreach (XmlNode cc in c.ChildNodes) { switch (cc.Name.ToLowerInvariant()) { case "text": EarlyText = cc.InnerText; break; case "texture": case "image": string f; try { f = OpenBveApi.Path.CombineFile(Path, cc.InnerText); } catch { Interface.AddMessage(MessageType.Error, false, "MessageEarlyTexture path was malformed in file " + fileName); break; } if (System.IO.File.Exists(f)) { if (!Program.CurrentHost.RegisterTexture(f, new TextureParameters(null, null), out EarlyTexture)) { Interface.AddMessage(MessageType.Error, false, "Loading MessageEarlyTexture " + f + " failed."); } } else { Interface.AddMessage(MessageType.Error, false, "MessageEarlyTexture " + f + " does not exist."); } break; case "time": if (!Interface.TryParseTime(cc.InnerText, out EarlyTime)) { Interface.AddMessage(MessageType.Error, false, "Early message time invalid in " + fileName); } EarlyDefined = true; break; case "color": EarlyColor = ParseColor(cc.InnerText, fileName); break; } } break; case "ontime": if (!c.ChildNodes.OfType <XmlElement>().Any()) { Interface.AddMessage(MessageType.Error, false, "No paramaters defined for the on-time message in " + fileName); } foreach (XmlNode cc in c.ChildNodes) { switch (cc.Name.ToLowerInvariant()) { case "text": Text = cc.InnerText; break; case "texture": case "image": string f; try { f = OpenBveApi.Path.CombineFile(Path, cc.InnerText); } catch { Interface.AddMessage(MessageType.Error, false, "MessageTexture path was malformed in file " + fileName); break; } if (System.IO.File.Exists(f)) { if (!Program.CurrentHost.RegisterTexture(f, new TextureParameters(null, null), out Texture)) { Interface.AddMessage(MessageType.Error, false, "Loading MessageTexture " + f + " failed."); } } else { Interface.AddMessage(MessageType.Error, false, "MessageTexture " + f + " does not exist."); } break; case "color": OnTimeColor = ParseColor(cc.InnerText, fileName); break; case "time": Interface.AddMessage(MessageType.Error, false, "OnTime should not contain a TIME declaration in " + fileName); break; } } break; case "late": if (!c.ChildNodes.OfType <XmlElement>().Any()) { Interface.AddMessage(MessageType.Error, false, "No paramaters defined for the late message in " + fileName); } foreach (XmlNode cc in c.ChildNodes) { switch (cc.Name.ToLowerInvariant()) { case "text": LateText = cc.InnerText; break; case "texture": case "image": string f; try { f = OpenBveApi.Path.CombineFile(Path, cc.InnerText); } catch { Interface.AddMessage(MessageType.Error, false, "MessageLateTexture path was malformed in file " + fileName); break; } if (System.IO.File.Exists(f)) { if (!Program.CurrentHost.RegisterTexture(f, new TextureParameters(null, null), out LateTexture)) { Interface.AddMessage(MessageType.Error, false, "Loading MessageLateTexture " + f + " failed."); } } else { Interface.AddMessage(MessageType.Error, false, "MessageLateTexture " + f + " does not exist."); } break; case "time": if (!Interface.TryParseTime(cc.InnerText, out LateTime)) { Interface.AddMessage(MessageType.Error, false, "Early message time invalid in " + fileName); } LateDefined = true; break; case "color": LateColor = ParseColor(cc.InnerText, fileName); break; } } break; case "timeout": if (!NumberFormats.TryParseDouble(c.InnerText, new[] { 1.0 }, out TimeOut)) { Interface.AddMessage(MessageType.Error, false, "Marker timeout invalid in " + fileName); } break; case "distance": if (!NumberFormats.TryParseDouble(c.InnerText, new[] { 1.0 }, out EndingPosition)) { Interface.AddMessage(MessageType.Error, false, "Marker distance invalid in " + fileName); } break; case "trains": Trains = c.InnerText.Split(';'); break; } } //Check this marker is valid if (TimeOut == Double.PositiveInfinity && EndingPosition == Double.PositiveInfinity) { Interface.AddMessage(MessageType.Error, false, "No marker timeout or distance defined in marker XML " + fileName); return(false); } if (EndingPosition != Double.PositiveInfinity) { if (Math.Abs(EndingPosition) == EndingPosition) { //Positive marker.EndingPosition = marker.StartingPosition + EndingPosition; } else { //Negative marker.EndingPosition = marker.StartingPosition; marker.StartingPosition -= EndingPosition; } } MessageManager.TextureMessage t = new MessageManager.TextureMessage(); MessageManager.GeneralMessage m = new MessageManager.GeneralMessage(); //Add variants if (EarlyDefined) { if (iM) { if (EarlyTexture != null) { t.MessageEarlyTime = EarlyTime; t.MessageEarlyTexture = EarlyTexture; } else { Interface.AddMessage(MessageType.Warning, false, "An early time was defined, but no message was specified in MarkerXML " + fileName); } } else { if (EarlyText != null) { m.MessageEarlyTime = EarlyTime; m.MessageEarlyText = EarlyText; m.MessageEarlyColor = EarlyColor; } else { Interface.AddMessage(MessageType.Warning, false, "An early time was defined, but no message was specified in MarkerXML " + fileName); } } } if (LateDefined) { if (iM) { if (LateTexture != null) { t.MessageLateTime = LateTime; t.MessageLateTexture = LateTexture; } else { Interface.AddMessage(MessageType.Warning, false, "A late time was defined, but no message was specified in MarkerXML " + fileName); } } else { if (LateText != null) { m.MessageLateTime = LateTime; m.MessageLateText = LateText; m.MessageLateColor = LateColor; } else { Interface.AddMessage(MessageType.Warning, false, "An early time was defined, but no message was specified in MarkerXML " + fileName); } } } //Final on-time message if (iM) { t.Trains = Trains; if (Texture != null) { t.MessageOnTimeTexture = Texture; } } else { m.Trains = Trains; if (Text != null) { m.MessageOnTimeText = Text; m.Color = OnTimeColor; } } if (iM) { marker.Message = t; } else { marker.Message = m; } } } } return(true); }
/// <summary>Saves the current in-game black box log</summary> internal static void SaveLogs() { if (Interface.CurrentOptions.BlackBox == false) { return; } string BlackBoxFile = OpenBveApi.Path.CombineFile(Program.FileSystem.SettingsFolder, "logs.bin"); try { using (System.IO.FileStream Stream = new System.IO.FileStream(BlackBoxFile, System.IO.FileMode.Create, System.IO.FileAccess.Write)) { //TODO: This code recreates the file every frame..... //It should be possible to spin up a stream in a separate thread which then continously appends using (System.IO.BinaryWriter Writer = new System.IO.BinaryWriter(Stream, System.Text.Encoding.UTF8)) { byte[] Identifier = new byte[] { 111, 112, 101, 110, 66, 86, 69, 95, 76, 79, 71, 83 }; const short Version = 1; Writer.Write(Identifier); Writer.Write(Version); Writer.Write(Game.LogRouteName); Writer.Write(Game.LogTrainName); Writer.Write(Game.LogDateTime.ToBinary()); Writer.Write((short)Interface.CurrentOptions.GameMode); Writer.Write(Game.BlackBoxEntryCount); for (int i = 0; i < Game.BlackBoxEntryCount; i++) { Writer.Write(Game.BlackBoxEntries[i].Time); Writer.Write(Game.BlackBoxEntries[i].Position); Writer.Write(Game.BlackBoxEntries[i].Speed); Writer.Write(Game.BlackBoxEntries[i].Acceleration); Writer.Write(Game.BlackBoxEntries[i].ReverserDriver); Writer.Write(Game.BlackBoxEntries[i].ReverserSafety); Writer.Write((short)Game.BlackBoxEntries[i].PowerDriver); Writer.Write((short)Game.BlackBoxEntries[i].PowerSafety); Writer.Write((short)Game.BlackBoxEntries[i].BrakeDriver); Writer.Write((short)Game.BlackBoxEntries[i].BrakeSafety); Writer.Write((short)Game.BlackBoxEntries[i].EventToken); } Writer.Write(Game.ScoreLogCount); for (int i = 0; i < Game.ScoreLogCount; i++) { Writer.Write(Game.ScoreLogs[i].Time); Writer.Write(Game.ScoreLogs[i].Position); Writer.Write(Game.ScoreLogs[i].Value); Writer.Write((short)Game.ScoreLogs[i].TextToken); } Writer.Write(Game.CurrentScore.Maximum); Identifier = new byte[] { 95, 102, 105, 108, 101, 69, 78, 68 }; Writer.Write(Identifier); Writer.Close(); } Stream.Close(); } } catch { Interface.CurrentOptions.BlackBox = false; Interface.AddMessage(MessageType.Error, false, "An unexpected error occurred whilst attempting to write to the black box log- Black box has been disabled."); } }
/// <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> /// <param name="Rotation">A three-dimemsional vector describing the rotation to be applied</param> internal static ObjectManager.AnimatedObjectCollection ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode, 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(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.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(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(';'); 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 = 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; case "FixedDynamicVisibility": if (attribute.Value.ToLowerInvariant() == "true") { Object.FixedDynamicVisibility = true; } else { Object.FixedDynamicVisibility = false; } break; case "DynamicVisibility": if (Object.FixedDynamicVisibility) { Object.FunctionScript = FunctionScripts.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 ObjectManager.StaticObject staticObject = null; for (int i = 0; i < CurrentObjects.Length; i++) { if (CurrentObjects[i] == null || string.IsNullOrEmpty(CurrentObjects[i].Name)) { continue; } ObjectManager.StaticObject Object = null; ObjectManager.AnimatedObjectCollection AnimatedObject = null; try { if (CurrentObjects[i].Name.ToLowerInvariant().EndsWith(".l3dgrp")) { AnimatedObject = ReadObject(CurrentObjects[i].Name, Encoding, LoadMode, CurrentObjects[i].Rotation); } else if (CurrentObjects[i].Name.ToLowerInvariant().EndsWith(".l3dobj")) { Object = (ObjectManager.StaticObject)ObjectManager.LoadObject(CurrentObjects[i].Name, Encoding, LoadMode, 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(Interface.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 <ObjectManager.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(); ObjectManager.AnimatedObjectState aos = new ObjectManager.AnimatedObjectState { Object = Object, Position = CurrentObjects[i].Position, }; a.States = new ObjectManager.AnimatedObjectState[] { aos }; Result.Objects[aL] = a; Result.Objects[aL].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(CurrentObjects[i].FunctionScript + " 1 == --"); } 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; } ObjectManager.JoinObjects(ref staticObject, 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 ObjectManager.AnimatedObjectState[0]; } } } } if (staticObject != null) { Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length + 1); ObjectManager.AnimatedObject a = new ObjectManager.AnimatedObject(); ObjectManager.AnimatedObjectState aos = new ObjectManager.AnimatedObjectState { Object = staticObject, }; a.States = new ObjectManager.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); }
// --- functions --- /// <summary>Reports a problem to the host application.</summary> /// <param name="type">The type of problem that is reported.</param> /// <param name="text">The textual message that describes the problem.</param> public override void ReportProblem(ProblemType type, string text) { Interface.AddMessage(MessageType.Error, false, text); }
public override void AddMessage(MessageType type, bool FileNotFound, string text) { Interface.AddMessage(type, FileNotFound, text); }
// // SET CONTROL CUSTOM DATA // internal void SetControlKbdCustomData(Key key, Interface.KeyboardModifier keybMod) { //Check that we are customising a key, and that our key is NOT the menu back key if (isCustomisingControl && key != MenuBackKey && CustomControlIdx < Interface.CurrentControls.Length) { Interface.CurrentControls[CustomControlIdx].Method = Interface.ControlMethod.Keyboard; Interface.CurrentControls[CustomControlIdx].Key = key; Interface.CurrentControls[CustomControlIdx].Modifier = keybMod; Interface.SaveControls(null); } PopMenu(); isCustomisingControl = false; }