diff --git a/OpenBVE.sln b/OpenBVE.sln index 2558fd474..97bc33742 100644 --- a/OpenBVE.sln +++ b/OpenBVE.sln @@ -44,6 +44,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Texture.Dds", "source\Plugi EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Formats.Msts", "source\Plugins\Formats.Msts\Formats.Msts.csproj", "{E81B7BD8-A326-47D3-B7EE-E9C7D4D119FA}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sound.MP3", "source\Plugins\Sound.MP3\Sound.MP3.csproj", "{A2FC4D71-1ED9-40D4-B746-FE6AB3C7D55E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -223,15 +225,29 @@ Global {EEC75E83-E284-44E0-B321-3F5C6512074F}.Release|x86.ActiveCfg = Release|x86 {EEC75E83-E284-44E0-B321-3F5C6512074F}.Release|x86.Build.0 = Release|x86 {E81B7BD8-A326-47D3-B7EE-E9C7D4D119FA}.Debug|Any CPU.ActiveCfg = Debug|x86 + {E81B7BD8-A326-47D3-B7EE-E9C7D4D119FA}.Debug|Any CPU.Build.0 = Debug|x86 {E81B7BD8-A326-47D3-B7EE-E9C7D4D119FA}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 {E81B7BD8-A326-47D3-B7EE-E9C7D4D119FA}.Debug|Mixed Platforms.Build.0 = Debug|x86 {E81B7BD8-A326-47D3-B7EE-E9C7D4D119FA}.Debug|x86.ActiveCfg = Debug|x86 {E81B7BD8-A326-47D3-B7EE-E9C7D4D119FA}.Debug|x86.Build.0 = Debug|x86 {E81B7BD8-A326-47D3-B7EE-E9C7D4D119FA}.Release|Any CPU.ActiveCfg = Release|x86 + {E81B7BD8-A326-47D3-B7EE-E9C7D4D119FA}.Release|Any CPU.Build.0 = Release|x86 {E81B7BD8-A326-47D3-B7EE-E9C7D4D119FA}.Release|Mixed Platforms.ActiveCfg = Release|x86 {E81B7BD8-A326-47D3-B7EE-E9C7D4D119FA}.Release|Mixed Platforms.Build.0 = Release|x86 {E81B7BD8-A326-47D3-B7EE-E9C7D4D119FA}.Release|x86.ActiveCfg = Release|x86 {E81B7BD8-A326-47D3-B7EE-E9C7D4D119FA}.Release|x86.Build.0 = Release|x86 + {A2FC4D71-1ED9-40D4-B746-FE6AB3C7D55E}.Debug|Any CPU.ActiveCfg = Debug|x86 + {A2FC4D71-1ED9-40D4-B746-FE6AB3C7D55E}.Debug|Any CPU.Build.0 = Debug|x86 + {A2FC4D71-1ED9-40D4-B746-FE6AB3C7D55E}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {A2FC4D71-1ED9-40D4-B746-FE6AB3C7D55E}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {A2FC4D71-1ED9-40D4-B746-FE6AB3C7D55E}.Debug|x86.ActiveCfg = Debug|x86 + {A2FC4D71-1ED9-40D4-B746-FE6AB3C7D55E}.Debug|x86.Build.0 = Debug|x86 + {A2FC4D71-1ED9-40D4-B746-FE6AB3C7D55E}.Release|Any CPU.ActiveCfg = Release|x86 + {A2FC4D71-1ED9-40D4-B746-FE6AB3C7D55E}.Release|Any CPU.Build.0 = Release|x86 + {A2FC4D71-1ED9-40D4-B746-FE6AB3C7D55E}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {A2FC4D71-1ED9-40D4-B746-FE6AB3C7D55E}.Release|Mixed Platforms.Build.0 = Release|x86 + {A2FC4D71-1ED9-40D4-B746-FE6AB3C7D55E}.Release|x86.ActiveCfg = Release|x86 + {A2FC4D71-1ED9-40D4-B746-FE6AB3C7D55E}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -245,6 +261,7 @@ Global {E9B64673-65B7-4D77-A4DB-4B441A7C9197} = {3D6660F8-52D2-4A30-8F1E-1922DDCF10F9} {EEC75E83-E284-44E0-B321-3F5C6512074F} = {16553295-E70F-4596-AA78-848EEA576C4A} {E81B7BD8-A326-47D3-B7EE-E9C7D4D119FA} = {16553295-E70F-4596-AA78-848EEA576C4A} + {A2FC4D71-1ED9-40D4-B746-FE6AB3C7D55E} = {16553295-E70F-4596-AA78-848EEA576C4A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {50B52A67-69B1-4B19-A59C-FF058FD9EBDF} diff --git a/assets/Plugins/Sound.MP3.dll.config b/assets/Plugins/Sound.MP3.dll.config new file mode 100644 index 000000000..e5b9685d5 --- /dev/null +++ b/assets/Plugins/Sound.MP3.dll.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/assets/Plugins/Sound.RiffWave.dll.config b/assets/Plugins/Sound.RiffWave.dll.config new file mode 100644 index 000000000..e5b9685d5 --- /dev/null +++ b/assets/Plugins/Sound.RiffWave.dll.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/dependencies/AtsPluginProxy.dll b/dependencies/AtsPluginProxy.dll index ac9f3d962..61db6e283 100644 Binary files a/dependencies/AtsPluginProxy.dll and b/dependencies/AtsPluginProxy.dll differ diff --git a/dependencies/NAudio.dll b/dependencies/NAudio.dll new file mode 100644 index 000000000..9425d4029 Binary files /dev/null and b/dependencies/NAudio.dll differ diff --git a/licenses/NAudio.txt b/licenses/NAudio.txt new file mode 100644 index 000000000..622a544b7 --- /dev/null +++ b/licenses/NAudio.txt @@ -0,0 +1,31 @@ +Microsoft Public License (Ms-PL) + +This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. + +1. Definitions + +The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. + +A "contribution" is the original software, or any additions or changes to the software. + +A "contributor" is any person that distributes its contribution under this license. + +"Licensed patents" are a contributor's patent claims that read directly on its contribution. + +2. Grant of Rights + +(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. + +(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. + +3. Conditions and Limitations + +(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. + +(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. + +(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. + +(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. + +(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. \ No newline at end of file diff --git a/makefile b/makefile index aa0814428..ccd4bbb63 100644 --- a/makefile +++ b/makefile @@ -88,6 +88,9 @@ SOUND_FLAC_FILE :=Data/Plugins/Sound.Flac.dll SOUND_RIFFWAVE_ROOT :=source/Plugins/Sound.RiffWave SOUND_RIFFWAVE_FILE :=Data/Plugins/Sound.RiffWave.dll +SOUND_MP3_ROOT :=source/Plugins/Sound.MP3 +SOUND_MP3_FILE :=Data/Plugins/Sound.MP3.dll + TEXTURE_ACE_ROOT :=source/Plugins/Texture.Ace TEXTURE_ACE_FILE :=Data/Plugins/Texture.Ace.dll @@ -238,6 +241,7 @@ clean: rm -f bin*/Data/Plugins/OpenBveAts.dll* bin*/Data/Plugins/OpenBveAts.pdb rm -f bin*/Data/Plugins/Sound.Flac.dll* bin*/Data/Plugins/Sound.Flac.pdb rm -f bin*/Data/Plugins/Sound.RiffWave.dll* bin*/Data/Plugins/Sound.RiffWave.pdb + rm -f bin*/Data/Plugins/Sound.MP3.dll* bin*/Data/Plugins/Sound.MP3.pdb rm -f bin*/Data/Plugins/Texture.Ace.dll* bin*/Data/Plugins/Texture.Ace.pdb rm -f bin*/Data/Plugins/Texture.BmpGifJpegPngTiff.dll* bin*/Data/Plugins/Texture.BmpGifJpegPngTiff.pdb rm -f bin*/Data/Plugins/Texture.Dds.dll* bin*/Data/Plugins/Texture.Dds.pdb @@ -333,6 +337,7 @@ $(DEBUG_DIR)/$(OPEN_BVE_FILE): $(DEBUG_DIR)/$(OPEN_BVE_API_FILE) $(DEBUG_DIR)/$(OPEN_BVE_FILE): $(DEBUG_DIR)/$(OPEN_BVE_ATS_FILE) $(DEBUG_DIR)/$(OPEN_BVE_FILE): $(DEBUG_DIR)/$(SOUND_FLAC_FILE) $(DEBUG_DIR)/$(OPEN_BVE_FILE): $(DEBUG_DIR)/$(SOUND_RIFFWAVE_FILE) +$(DEBUG_DIR)/$(OPEN_BVE_FILE): $(DEBUG_DIR)/$(SOUND_MP3_FILE) $(DEBUG_DIR)/$(OPEN_BVE_FILE): $(DEBUG_DIR)/$(TEXTURE_ACE_FILE) $(DEBUG_DIR)/$(OPEN_BVE_FILE): $(DEBUG_DIR)/$(TEXTURE_BGJPT_FILE) $(DEBUG_DIR)/$(OPEN_BVE_FILE): $(DEBUG_DIR)/$(TEXTURE_DDS_FILE) @@ -343,6 +348,7 @@ $(RELEASE_DIR)/$(OPEN_BVE_FILE): $(RELEASE_DIR)/$(OPEN_BVE_API_FILE) $(RELEASE_DIR)/$(OPEN_BVE_FILE): $(RELEASE_DIR)/$(OPEN_BVE_ATS_FILE) $(RELEASE_DIR)/$(OPEN_BVE_FILE): $(RELEASE_DIR)/$(SOUND_FLAC_FILE) $(RELEASE_DIR)/$(OPEN_BVE_FILE): $(RELEASE_DIR)/$(SOUND_RIFFWAVE_FILE) +$(RELEASE_DIR)/$(OPEN_BVE_FILE): $(RELEASE_DIR)/$(SOUND_MP3_FILE) $(RELEASE_DIR)/$(OPEN_BVE_FILE): $(RELEASE_DIR)/$(TEXTURE_ACE_FILE) $(RELEASE_DIR)/$(OPEN_BVE_FILE): $(RELEASE_DIR)/$(TEXTURE_BGJPT_FILE) $(RELEASE_DIR)/$(OPEN_BVE_FILE): $(RELEASE_DIR)/$(TEXTURE_DDS_FILE) @@ -473,7 +479,29 @@ $(RELEASE_DIR)/$(SOUND_RIFFWAVE_FILE): $(RELEASE_DIR)/$(OPEN_BVE_API_FILE) $(DEBUG_DIR)/$(SOUND_RIFFWAVE_FILE) $(RELEASE_DIR)/$(SOUND_RIFFWAVE_FILE): $(SOUND_RIFFWAVE_SRC) $(SOUND_RIFFWAVE_RESOURCE) @echo $(COLOR_MAGENTA)Building $(COLOR_CYAN)$(SOUND_RIFFWAVE_OUT)$(COLOR_END) @$(CSC) /out:$(SOUND_RIFFWAVE_OUT) /target:library $(SOUND_RIFFWAVE_SRC) $(ARGS) $(SOUND_RIFFWAVE_DOC) \ - /reference:$(OPEN_BVE_API_OUT) $(addprefix /resource:, $(SOUND_RIFFWAVE_RESOURCE)) + /reference:$(OPEN_BVE_API_OUT) /reference:$(OUTPUT_DIR)/NAudio.dll $(addprefix /resource:, $(SOUND_RIFFWAVE_RESOURCE)) + +############# +# Sound.MP3 # +############# + +SOUND_MP3_FOLDERS := . Properties +SOUND_MP3_FOLDERS := $(addprefix $(SOUND_MP3_ROOT)/, $(SOUND_MP3_FOLDERS)) +SOUND_MP3_SRC := $(foreach sdir, $(SOUND_MP3_FOLDERS), $(wildcard $(sdir)/*.cs)) +SOUND_MP3_DOC := $(addprefix /doc:, $(foreach sdir, $(SOUND_MP3_FOLDERS), $(wildcard $(sdir)/*.xml))) +SOUND_MP3_RESX := $(foreach sdir, $(SOUND_MP3_FOLDERS), $(wildcard $(sdir)/*.resx)) +SOUND_MP3_RESOURCE := $(addprefix $(SOUND_MP3_ROOT)/, $(subst /,., $(subst /./,/, $(patsubst $(dir $(SOUND_MP3_ROOT))%.resx, %.resources, $(SOUND_MP3_RESX))))) +SOUND_MP3_OUT =$(OUTPUT_DIR)/$(SOUND_MP3_FILE) + +$(call create_resource, $(SOUND_MP3_RESOURCE), $(SOUND_MP3_RESX)) + +$(DEBUG_DIR)/$(SOUND_MP3_FILE): $(DEBUG_DIR)/$(OPEN_BVE_API_FILE) +$(RELEASE_DIR)/$(SOUND_MP3_FILE): $(RELEASE_DIR)/$(OPEN_BVE_API_FILE) + +$(DEBUG_DIR)/$(SOUND_MP3_FILE) $(RELEASE_DIR)/$(SOUND_MP3_FILE): $(SOUND_MP3_SRC) $(SOUND_MP3_RESOURCE) + @echo $(COLOR_MAGENTA)Building $(COLOR_CYAN)$(SOUND_MP3_OUT)$(COLOR_END) + @$(CSC) /out:$(SOUND_MP3_OUT) /target:library $(SOUND_MP3_SRC) $(ARGS) $(SOUND_MP3_DOC) \ + /reference:$(OPEN_BVE_API_OUT) /reference:$(OUTPUT_DIR)/NAudio.dll $(addprefix /resource:, $(SOUND_MP3_RESOURCE)) ############### # Texture.Ace # diff --git a/source/ObjectViewer/InterfaceS.cs b/source/ObjectViewer/InterfaceS.cs index 35b0daefb..c0a22557f 100644 --- a/source/ObjectViewer/InterfaceS.cs +++ b/source/ObjectViewer/InterfaceS.cs @@ -6,6 +6,7 @@ // ╚══════════════════════════════════════════════════════════════╝ using System; +using OpenBveApi; using OpenBveApi.Math; namespace OpenBve { @@ -145,8 +146,9 @@ internal struct Options { // try parse time - internal static bool TryParseTime(string Expression, out double Value) { - Expression = TrimInside(Expression); + internal static bool TryParseTime(string Expression, out double Value) + { + Expression = Expression.TrimInside(); if (Expression.Length != 0) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; int i = Expression.IndexOf('.'); @@ -178,19 +180,6 @@ internal static bool TryParseTime(string Expression, out double Value) { return false; } - // trim inside - private static string TrimInside(string Expression) { - System.Text.StringBuilder Builder = new System.Text.StringBuilder(Expression.Length); - for (int i = 0; i < Expression.Length; i++) { - char c = Expression[i]; - if (!char.IsWhiteSpace(c)) { - Builder.Append(c); - } - } return Builder.ToString(); - } - - // ================================ - // round to power of two internal static int RoundToPowerOfTwo(int Value) { Value -= 1; @@ -198,29 +187,5 @@ internal static int RoundToPowerOfTwo(int Value) { Value = Value | Value >> i; } return Value + 1; } - - // convert newlines to crlf - internal static string ConvertNewlinesToCrLf(string Text) { - System.Text.StringBuilder Builder = new System.Text.StringBuilder(); - for (int i = 0; i < Text.Length; i++) { - int a = char.ConvertToUtf32(Text, i); - if (a == 0xD & i < Text.Length - 1) { - int b = char.ConvertToUtf32(Text, i + 1); - if (b == 0xA) { - Builder.Append("\r\n"); - i++; - } else { - Builder.Append("\r\n"); - } - } else if (a == 0xA | a == 0xC | a == 0xD | a == 0x85 | a == 0x2028 | a == 0x2029) { - Builder.Append("\r\n"); - } else if (a < 0x10000) { - Builder.Append(Text[i]); - } else { - Builder.Append(Text.Substring(i, 2)); - i++; - } - } return Builder.ToString(); - } } } diff --git a/source/ObjectViewer/ObjectManager.cs b/source/ObjectViewer/ObjectManager.cs index c1760d958..732849c65 100644 --- a/source/ObjectViewer/ObjectManager.cs +++ b/source/ObjectViewer/ObjectManager.cs @@ -1685,7 +1685,11 @@ internal static UnifiedObject LoadObject(string FileName, System.Text.Encoding E Interface.AddMessage(Interface.MessageType.Error, false, "The file extension is not supported: " + FileName); return null; } - Result.OptimizeObject(PreserveVertices); + + if (Result != null) + { + Result.OptimizeObject(PreserveVertices); + } return Result; #if !DEBUG } catch (Exception ex) { diff --git a/source/ObjectViewer/Parsers/WavefrontObjParser.cs b/source/ObjectViewer/Parsers/WavefrontObjParser.cs index 186af8ae3..2b245fb4d 100644 --- a/source/ObjectViewer/Parsers/WavefrontObjParser.cs +++ b/source/ObjectViewer/Parsers/WavefrontObjParser.cs @@ -39,12 +39,12 @@ internal Material() } private class MeshBuilder { - internal List Vertices; + internal List Vertices; internal List Faces; internal Material[] Materials; internal MeshBuilder() { - this.Vertices = new List(); + this.Vertices = new List(); this.Faces = new List(); this.Materials = new Material[] { }; } @@ -165,26 +165,43 @@ internal static ObjectManager.StaticObject ReadObject(string FileName, System.Te //Creates a new face //Create the temp list to hook out the vertices - List vertices = new List(); + List vertices = new List(); List normals = new List(); 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) || idx > tempVertices.Count) + if (!int.TryParse(faceArguments[0], out idx)) { Interface.AddMessage(Interface.MessageType.Warning, false, "Invalid Vertex index in Face " + f + " at Line " + i); continue; } - newVertex.Coordinates = tempVertices[idx - 1]; + + 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(Interface.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) || idx > tempCoords.Count) + if (!int.TryParse(faceArguments[1], out idx)) { if (!string.IsNullOrEmpty(faceArguments[1])) { @@ -194,7 +211,22 @@ internal static ObjectManager.StaticObject ReadObject(string FileName, System.Te } else { - newVertex.TextureCoordinates = tempCoords[idx - 1]; + int currentCoord = tempCoords.Count; + if (idx != Math.Abs(idx)) + { + //Offset, so we seem to need to add one.... + currentCoord++; + currentCoord += idx; + } + if (currentCoord > tempCoords.Count) + { + Interface.AddMessage(Interface.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) @@ -203,7 +235,7 @@ internal static ObjectManager.StaticObject ReadObject(string FileName, System.Te } else { - if (!int.TryParse(faceArguments[2], out idx) || idx > tempNormals.Count) + if (!int.TryParse(faceArguments[2], out idx)) { if (!string.IsNullOrEmpty(faceArguments[2])) { @@ -213,7 +245,22 @@ internal static ObjectManager.StaticObject ReadObject(string FileName, System.Te } else { - normals.Add(tempNormals[idx - 1]); + int currentNormal = tempNormals.Count; + if (idx != Math.Abs(idx)) + { + //Offset, so we seem to need to add one.... + currentNormal++; + currentNormal += idx; + } + if (currentNormal > tempNormals.Count) + { + Interface.AddMessage(Interface.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); @@ -221,17 +268,8 @@ internal static ObjectManager.StaticObject ReadObject(string FileName, System.Te World.MeshFaceVertex[] Vertices = new World.MeshFaceVertex[vertices.Count]; for (int k = 0; k < vertices.Count; k++) { - int v = Builder.Vertices.FindIndex(a => a.Equals(vertices[k])); - if (v != -1) - { - Vertices[k].Index = (ushort)v; - } - else - { - Builder.Vertices.Add(vertices[k]); - Vertices[k].Index = (ushort)(Builder.Vertices.Count -1); - } - + Builder.Vertices.Add(vertices[k]); + Vertices[k].Index = (ushort)(Builder.Vertices.Count -1); Vertices[k].Normal = normals[k]; } Builder.Faces.Add(currentMaterial == -1 ? new World.MeshFace(Vertices, 0) : new World.MeshFace(Vertices, (ushort)currentMaterial)); diff --git a/source/ObjectViewer/Parsers/XObjectParser.cs b/source/ObjectViewer/Parsers/XObjectParser.cs index 99b88ad43..3f9794893 100644 --- a/source/ObjectViewer/Parsers/XObjectParser.cs +++ b/source/ObjectViewer/Parsers/XObjectParser.cs @@ -166,14 +166,14 @@ private static Template GetTemplate(string Name, bool binary) { return Templates[i]; } } - if (Name.StartsWith("Frame ")) + if (Name.ToLowerInvariant().StartsWith("frame ")) { //Enclosing frame for the model //Appears in Blender exported stuff - return Templates[11]; + return Templates[13]; } - if (Name.StartsWith("Mesh ")) + if (Name.ToLowerInvariant().StartsWith("mesh ")) { //Named material, just ignore the name for the minute //Appears in Blender exported stuff @@ -2221,7 +2221,7 @@ private static bool ProcessStructure(string FileName, Structure Structure, out O Object.Mesh.Faces[mf + j].Vertices = new World.MeshFaceVertex[Faces[j].Length]; for (int k = 0; k < Faces[j].Length; k++) { - Object.Mesh.Faces[mf + j].Vertices[mv + k] = new World.MeshFaceVertex(mv + Faces[j][k], FaceNormals[j][k]); + Object.Mesh.Faces[mf + j].Vertices[k] = new World.MeshFaceVertex(mv + Faces[j][k], FaceNormals[j][k]); } } for (int j = 0; j < Vertices.Length; j++) diff --git a/source/ObjectViewer/RendererS.cs b/source/ObjectViewer/RendererS.cs index 4d1880de2..4c0c163b0 100644 --- a/source/ObjectViewer/RendererS.cs +++ b/source/ObjectViewer/RendererS.cs @@ -587,11 +587,27 @@ private static void RenderFace(ref World.MeshMaterial Material, VertexTemplate[] if (Material.GlowAttenuationData != 0) { float alphafactor = (float)GetDistanceFactor(Vertices, ref Face, Material.GlowAttenuationData, CameraX, CameraY, CameraZ); - GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, inv255 * (float)Material.Color.A * alphafactor); + if (OptionWireframe) + { + GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, 1.0f); + } + else + { + GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, inv255 * (float)Material.Color.A * alphafactor); + } + } else { - GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, inv255 * (float)Material.Color.A); + if (OptionWireframe) + { + GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, 1.0f); + } + else + { + GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, inv255 * (float)Material.Color.A); + } + } if ((Material.Flags & World.MeshMaterial.EmissiveColorMask) != 0) { @@ -692,7 +708,16 @@ private static void RenderFace(ref World.MeshMaterial Material, VertexTemplate[] alphafactor = inv255 * (float)Material.DaytimeNighttimeBlend + 1.0f - OptionLightingResultingAmount; if (alphafactor > 1.0f) alphafactor = 1.0f; } - GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, inv255 * (float)Material.Color.A * alphafactor); + + if (OptionWireframe) + { + GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, 1.0f); + } + else + { + GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, inv255 * (float)Material.Color.A * alphafactor); + } + if ((Material.Flags & World.MeshMaterial.EmissiveColorMask) != 0) { GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Emission, new float[] { inv255 * (float)Material.EmissiveColor.R, inv255 * (float)Material.EmissiveColor.G, inv255 * (float)Material.EmissiveColor.B, 1.0f }); diff --git a/source/OpenBVE/Audio/Sounds.Update.cs b/source/OpenBVE/Audio/Sounds.Update.cs index 92ac6e3fd..953a3bbd3 100644 --- a/source/OpenBVE/Audio/Sounds.Update.cs +++ b/source/OpenBVE/Audio/Sounds.Update.cs @@ -29,13 +29,13 @@ private static void UpdateLinearModel(double timeElapsed) { /* * Set up the listener * */ - OpenBveApi.Math.Vector3 listenerPosition = World.AbsoluteCameraPosition; - OpenBveApi.Math.Orientation3 listenerOrientation = new OpenBveApi.Math.Orientation3(World.AbsoluteCameraSide, World.AbsoluteCameraUp, World.AbsoluteCameraDirection); - OpenBveApi.Math.Vector3 listenerVelocity; + Vector3 listenerPosition = World.AbsoluteCameraPosition; + Orientation3 listenerOrientation = new Orientation3(World.AbsoluteCameraSide, World.AbsoluteCameraUp, World.AbsoluteCameraDirection); + Vector3 listenerVelocity; if (World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead | World.CameraMode == World.CameraViewMode.Exterior) { TrainManager.Car car = TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar]; - OpenBveApi.Math.Vector3 diff = car.FrontAxle.Follower.WorldPosition - car.RearAxle.Follower.WorldPosition; - listenerVelocity = car.Specs.CurrentSpeed * OpenBveApi.Math.Vector3.Normalize(diff) + World.CameraAlignmentSpeed.Position; + Vector3 diff = car.FrontAxle.Follower.WorldPosition - car.RearAxle.Follower.WorldPosition; + listenerVelocity = car.Specs.CurrentSpeed * Vector3.Normalize(diff) + World.CameraAlignmentSpeed.Position; } else { listenerVelocity = World.CameraAlignmentSpeed.Position; } @@ -102,9 +102,9 @@ private static void UpdateLinearModel(double timeElapsed) { * The sound is to be played or is already playing. * Calculate the sound gain. * */ - OpenBveApi.Math.Vector3 direction; - OpenBveApi.Math.Vector3 position; - OpenBveApi.Math.Vector3 velocity; + Vector3 direction; + Vector3 position; + Vector3 velocity; switch (Sources[i].Type) { @@ -124,7 +124,7 @@ private static void UpdateLinearModel(double timeElapsed) { velocity = Vector3.Null; break; } - OpenBveApi.Math.Vector3 positionDifference = position - listenerPosition; + Vector3 positionDifference = position - listenerPosition; double gain; if (GlobalMute) { gain = 0.0; @@ -287,19 +287,19 @@ private static void UpdateInverseModel(double timeElapsed) { /* * Set up the listener. * */ - OpenBveApi.Math.Vector3 listenerPosition = World.AbsoluteCameraPosition; - OpenBveApi.Math.Orientation3 listenerOrientation = new OpenBveApi.Math.Orientation3(World.AbsoluteCameraSide, World.AbsoluteCameraUp, World.AbsoluteCameraDirection); - OpenBveApi.Math.Vector3 listenerVelocity; + Vector3 listenerPosition = World.AbsoluteCameraPosition; + Orientation3 listenerOrientation = new Orientation3(World.AbsoluteCameraSide, World.AbsoluteCameraUp, World.AbsoluteCameraDirection); + Vector3 listenerVelocity; if (World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead | World.CameraMode == World.CameraViewMode.Exterior) { TrainManager.Car car = TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar]; - OpenBveApi.Math.Vector3 diff = car.FrontAxle.Follower.WorldPosition - car.RearAxle.Follower.WorldPosition; + Vector3 diff = car.FrontAxle.Follower.WorldPosition - car.RearAxle.Follower.WorldPosition; if (diff.IsNullVector()) { - listenerVelocity = car.Specs.CurrentSpeed * OpenBveApi.Math.Vector3.Forward; + listenerVelocity = car.Specs.CurrentSpeed * Vector3.Forward; } else { - listenerVelocity = car.Specs.CurrentSpeed * OpenBveApi.Math.Vector3.Normalize(diff); + listenerVelocity = car.Specs.CurrentSpeed * Vector3.Normalize(diff); } } else { - listenerVelocity = OpenBveApi.Math.Vector3.Null; + listenerVelocity = Vector3.Null; } AL.Listener(ALListener3f.Position, 0.0f, 0.0f, 0.0f); AL.Listener(ALListener3f.Velocity, (float)listenerVelocity.X, (float)listenerVelocity.Y, (float)listenerVelocity.Z); @@ -385,11 +385,11 @@ private static void UpdateInverseModel(double timeElapsed) { * Calculate the gain, then add the sound * to the list of sounds to be played. * */ - OpenBveApi.Math.Vector3 position; + Vector3 position; switch (Sources[i].Type) { case SoundType.TrainCar: - OpenBveApi.Math.Vector3 direction; + Vector3 direction; var Train = (TrainManager.Train)Sources[i].Parent; Train.Cars[Sources[i].Car].CreateWorldCoordinates(Sources[i].Position.X, Sources[i].Position.Y, Sources[i].Position.Z, out position.X, out position.Y, out position.Z, out direction.X, out direction.Y, out direction.Z); break; @@ -401,7 +401,7 @@ private static void UpdateInverseModel(double timeElapsed) { position = Sources[i].Position; break; } - OpenBveApi.Math.Vector3 positionDifference = position - listenerPosition; + Vector3 positionDifference = position - listenerPosition; double distance = positionDifference.Norm(); double radius = Sources[i].Radius; if (World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead) { @@ -525,12 +525,12 @@ private static void UpdateInverseModel(double timeElapsed) { continue; } } - OpenBveApi.Math.Vector3 position; - OpenBveApi.Math.Vector3 velocity; + Vector3 position; + Vector3 velocity; switch (source.Type) { case SoundType.TrainCar: - OpenBveApi.Math.Vector3 direction; + Vector3 direction; var Train = (TrainManager.Train)source.Parent; Train.Cars[source.Car].CreateWorldCoordinates(source.Position.X, source.Position.Y, source.Position.Z, out position.X, out position.Y, out position.Z, out direction.X, out direction.Y, out direction.Z); velocity = Train.Cars[source.Car].Specs.CurrentSpeed * direction; @@ -538,11 +538,11 @@ private static void UpdateInverseModel(double timeElapsed) { case SoundType.AnimatedObject: var WorldSound = (ObjectManager.WorldSound)source.Parent; position = WorldSound.Follower.WorldPosition + WorldSound.Position; - velocity = OpenBveApi.Math.Vector3.Null; + velocity = Vector3.Null; break; default: position = source.Position; - velocity = OpenBveApi.Math.Vector3.Null; + velocity = Vector3.Null; break; } position -= listenerPosition; diff --git a/source/OpenBVE/Game/AI/AI.PreTrain.cs b/source/OpenBVE/Game/AI/AI.PreTrain.cs index afe8c20cf..811091ac8 100644 --- a/source/OpenBVE/Game/AI/AI.PreTrain.cs +++ b/source/OpenBVE/Game/AI/AI.PreTrain.cs @@ -18,14 +18,16 @@ internal class BogusPretrainAI : GeneralAI { private double TimeLastProcessed; private double CurrentInterval; + private readonly TrainManager.Train Train; - internal BogusPretrainAI(TrainManager.Train Train) + internal BogusPretrainAI(TrainManager.Train train) { this.TimeLastProcessed = 0.0; this.CurrentInterval = 1.0; + this.Train = train; } - internal override void Trigger(TrainManager.Train Train, double TimeElapsed) + internal override void Trigger(double TimeElapsed) { if (SecondsSinceMidnight - TimeLastProcessed >= CurrentInterval) { diff --git a/source/OpenBVE/Game/AI/AI.SimpleHuman.cs b/source/OpenBVE/Game/AI/AI.SimpleHuman.cs index 0243b2fe7..77f7c0a5f 100644 --- a/source/OpenBVE/Game/AI/AI.SimpleHuman.cs +++ b/source/OpenBVE/Game/AI/AI.SimpleHuman.cs @@ -16,9 +16,12 @@ internal class SimpleHumanDriverAI : GeneralAI private readonly double PersonalitySpeedFactor; private int PowerNotchAtWhichWheelSlipIsObserved; private int LastStation; + + private readonly TrainManager.Train Train; // functions - internal SimpleHumanDriverAI(TrainManager.Train Train) + internal SimpleHumanDriverAI(TrainManager.Train train) { + this.Train = train; this.TimeLastProcessed = 0.0; this.CurrentInterval = 1.0; this.BrakeMode = false; @@ -34,24 +37,24 @@ internal SimpleHumanDriverAI(TrainManager.Train Train) this.LastStation = -1; } } - private OpenBveApi.Runtime.AIResponse PerformPlugin(TrainManager.Train Train) + private AIResponse PerformPlugin() { - OpenBveApi.Runtime.AIResponse response = Train.Plugin.UpdateAI(); - if (response == OpenBveApi.Runtime.AIResponse.Short) + AIResponse response = Train.Plugin.UpdateAI(); + if (response == AIResponse.Short) { CurrentInterval = 0.2 + 0.1 * Program.RandomNumberGenerator.NextDouble(); } - else if (response == OpenBveApi.Runtime.AIResponse.Medium) + else if (response == AIResponse.Medium) { CurrentInterval = 0.4 + 0.2 * Program.RandomNumberGenerator.NextDouble(); } - else if (response == OpenBveApi.Runtime.AIResponse.Long) + else if (response == AIResponse.Long) { CurrentInterval = 0.8 + 0.4 * Program.RandomNumberGenerator.NextDouble(); } return response; } - private void PerformDefault(TrainManager.Train Train) + private void PerformDefault() { // personality double spd = Train.Specs.CurrentAverageSpeed; @@ -846,7 +849,7 @@ private void PerformDefault(TrainManager.Train Train) } } } - internal override void Trigger(TrainManager.Train Train, double TimeElapsed) + internal override void Trigger(double TimeElapsed) { if (TimeLastProcessed > SecondsSinceMidnight) { @@ -857,12 +860,12 @@ internal override void Trigger(TrainManager.Train Train, double TimeElapsed) TimeLastProcessed = SecondsSinceMidnight; if (Train.Plugin != null && Train.Plugin.SupportsAI) { - if (PerformPlugin(Train) != OpenBveApi.Runtime.AIResponse.None) + if (PerformPlugin() != AIResponse.None) { return; } } - PerformDefault(Train); + PerformDefault(); } } } diff --git a/source/OpenBVE/Game/AI/AI.cs b/source/OpenBVE/Game/AI/AI.cs index 508ec82bf..d678a3f70 100644 --- a/source/OpenBVE/Game/AI/AI.cs +++ b/source/OpenBVE/Game/AI/AI.cs @@ -1,13 +1,11 @@ -using System; - -namespace OpenBve +namespace OpenBve { internal static partial class Game { /// An abstract class representing a general purpose AI internal abstract class GeneralAI { - internal abstract void Trigger(TrainManager.Train Train, double TimeElapsed); + internal abstract void Trigger(double TimeElapsed); } internal static bool InitialAIDriver; diff --git a/source/OpenBVE/Game/Game.cs b/source/OpenBVE/Game/Game.cs index 35d1224f6..110b91be7 100644 --- a/source/OpenBVE/Game/Game.cs +++ b/source/OpenBVE/Game/Game.cs @@ -1,6 +1,5 @@ using System; using OpenBveApi.Colors; -using OpenBveApi.Math; namespace OpenBve { internal static partial class Game { diff --git a/source/OpenBVE/Game/Menu.cs b/source/OpenBVE/Game/Menu.cs index c494f5306..6ee8e30d9 100644 --- a/source/OpenBVE/Game/Menu.cs +++ b/source/OpenBVE/Game/Menu.cs @@ -65,20 +65,9 @@ public enum MenuType }; // components of the semi-transparent screen overlay - private const float ovlR = 0.00f; - private const float ovlG = 0.00f; - private const float ovlB = 0.00f; - private const float ovlA = 0.20f; - // components of the menu background colour - private const float bkgMenuR = 0.00f; - private const float bkgMenuG = 0.00f; - private const float bkgMenuB = 0.00f; - private const float bkgMenuA = 1.00f; - // components of the highlighted item background - private const float bkgHgltR = 1.00f; - private const float bkgHgltG = 0.69f; - private const float bkgHgltB = 0.00f; - private const float bkgHgltA = 1.00f; + private readonly Color128 overlayColor = new Color128(0.0f, 0.0f, 0.0f, 0.2f); + private readonly Color128 backgroundColor = new Color128(0.0f, 0.0f, 0.0f, 1.0f); + private readonly Color128 highlightColor = new Color128(1.0f, 0.69f, 0.0f, 1.0f); // text colours private static readonly Color128 ColourCaption = new Color128(0.750f, 0.750f, 0.875f, 1.0f); private static readonly Color128 ColourDimmed = new Color128(1.000f, 1.000f, 1.000f, 0.5f); @@ -135,13 +124,14 @@ private class SingleMenu /******************** MENU FIELDS *********************/ - public Renderer.TextAlignment Align; - public MenuEntry[] Items = { }; - public int ItemWidth = 0; - public int Height = 0; - public int Selection = SelectionNone; + public readonly Renderer.TextAlignment Align; + public readonly MenuEntry[] Items = { }; + public readonly int ItemWidth = 0; + public readonly int Width = 0; + public readonly int Height = 0; + public int Selection; public int TopItem; // the top displayed menu item - public int Width = 0; + /******************** MENU C'TOR @@ -366,7 +356,7 @@ internal Fonts.OpenGlFont MenuFont } } - internal OpenTK.Input.Key MenuBackKey; + internal Key MenuBackKey; /******************** MENU SYSTEM SINGLETON C'TOR @@ -735,7 +725,7 @@ internal void Draw() SingleMenu menu = Menus[CurrMenu]; // overlay background - GL.Color4(ovlR, ovlG, ovlB, ovlA); + GL.Color4(overlayColor.R, overlayColor.G, overlayColor.B, overlayColor.A); Renderer.RenderOverlaySolid(0.0, 0.0, (double)Screen.Width, (double)Screen.Height); GL.Color4(1.0f, 1.0f, 1.0f, 1.0f); @@ -747,19 +737,19 @@ internal void Draw() int menuBottomItem = menu.TopItem + visibleItems - 1; // draw the menu background - GL.Color4(bkgMenuR, bkgMenuG, bkgMenuB, bkgMenuA); + GL.Color4(backgroundColor.R, backgroundColor.G, backgroundColor.B, backgroundColor.A); Renderer.RenderOverlaySolid(menuXmin - MenuBorderX, menuYmin - MenuBorderY, menuXmax + MenuBorderX, menuYmax + MenuBorderY); // if not starting from the top of the menu, draw a dimmed ellipsis item if (menu.Selection == menu.TopItem - 1 && !isCustomisingControl) { - GL.Color4(bkgHgltR, bkgHgltG, bkgHgltB, bkgHgltA); + GL.Color4(highlightColor.R, highlightColor.G, highlightColor.B, highlightColor.A); Renderer.RenderOverlaySolid(itemLeft - MenuItemBorderX, menuYmin/*-MenuItemBorderY*/, itemLeft + menu.ItemWidth + MenuItemBorderX, menuYmin + em + MenuItemBorderY * 2); } if (menu.TopItem > 0) - Renderer.DrawString(MenuFont, "...", new System.Drawing.Point(itemX, menuYmin), + Renderer.DrawString(MenuFont, "...", new Point(itemX, menuYmin), menu.Align, ColourDimmed, false); // draw the items int itemY = topItemY; @@ -774,18 +764,18 @@ internal void Draw() // draw a solid highlight rectangle under the text // HACK! the highlight rectangle has to be shifted a little down to match // the text body. OpenGL 'feature'? - GL.Color4(bkgHgltR, bkgHgltG, bkgHgltB, bkgHgltA); + GL.Color4(highlightColor.R, highlightColor.G, highlightColor.B, highlightColor.A); Renderer.RenderOverlaySolid(itemLeft - MenuItemBorderX, itemY/*-MenuItemBorderY*/, itemLeft + menu.ItemWidth + MenuItemBorderX, itemY + em + MenuItemBorderY * 2); // draw the text - Renderer.DrawString(MenuFont, menu.Items[i].Text, new System.Drawing.Point(itemX, itemY), + Renderer.DrawString(MenuFont, menu.Items[i].Text, new Point(itemX, itemY), menu.Align, ColourHighlight, false); } else if (menu.Items[i] is MenuCaption) - Renderer.DrawString(MenuFont, menu.Items[i].Text, new System.Drawing.Point(itemX, itemY), + Renderer.DrawString(MenuFont, menu.Items[i].Text, new Point(itemX, itemY), menu.Align, ColourCaption, false); else - Renderer.DrawString(MenuFont, menu.Items[i].Text, new System.Drawing.Point(itemX, itemY), + Renderer.DrawString(MenuFont, menu.Items[i].Text, new Point(itemX, itemY), menu.Align, ColourNormal, false); itemY += lineHeight; } @@ -793,13 +783,13 @@ internal void Draw() if (menu.Selection == menu.TopItem + visibleItems) { - GL.Color4(bkgHgltR, bkgHgltG, bkgHgltB, bkgHgltA); + GL.Color4(highlightColor.R, highlightColor.G, highlightColor.B, highlightColor.A); Renderer.RenderOverlaySolid(itemLeft - MenuItemBorderX, itemY/*-MenuItemBorderY*/, itemLeft + menu.ItemWidth + MenuItemBorderX, itemY + em + MenuItemBorderY * 2); } // if not at the end of the menu, draw a dimmed ellipsis item at the bottom if (i < menu.Items.Length - 1) - Renderer.DrawString(MenuFont, "...", new System.Drawing.Point(itemX, itemY), + Renderer.DrawString(MenuFont, "...", new Point(itemX, itemY), menu.Align, ColourDimmed, false); } diff --git a/source/OpenBVE/Game/MessageManager.cs b/source/OpenBVE/Game/MessageManager.cs index ae8f7ebe2..2dd71a806 100644 --- a/source/OpenBVE/Game/MessageManager.cs +++ b/source/OpenBVE/Game/MessageManager.cs @@ -40,7 +40,7 @@ internal static void AddMessage(Message message) for (int i = TextualMessages.Count -1; i >= 0; i--) { var c = TextualMessages[i] as GameMessage; - if (c != null && c.Depencency == MessageDependency.SectionLimit || c.Depencency == MessageDependency.RouteLimit) + if (c != null && (c.Depencency == MessageDependency.SectionLimit || c.Depencency == MessageDependency.RouteLimit)) { TextualMessages.RemoveAt(i); } diff --git a/source/OpenBVE/Game/ObjectManager/ObjectManager.StaticObject.cs b/source/OpenBVE/Game/ObjectManager/ObjectManager.StaticObject.cs index d953323a7..2e7979761 100644 --- a/source/OpenBVE/Game/ObjectManager/ObjectManager.StaticObject.cs +++ b/source/OpenBVE/Game/ObjectManager/ObjectManager.StaticObject.cs @@ -326,10 +326,10 @@ internal void ApplyShear(double dx, double dy, double dz, double sx, double sy, nx -= dx * n; ny -= dy * n; nz -= dz * n; - World.Normalize(ref nx, ref ny, ref nz); Mesh.Faces[j].Vertices[k].Normal.X = (float) nx; Mesh.Faces[j].Vertices[k].Normal.Y = (float) ny; Mesh.Faces[j].Vertices[k].Normal.Z = (float) nz; + Mesh.Faces[j].Vertices[k].Normal.Normalize(); } } } diff --git a/source/OpenBVE/Game/Timetable.cs b/source/OpenBVE/Game/Timetable.cs index de36cfaf8..a90a0d011 100644 --- a/source/OpenBVE/Game/Timetable.cs +++ b/source/OpenBVE/Game/Timetable.cs @@ -1,5 +1,6 @@ using System; using System.Drawing; +using OpenBveApi; using OpenBveApi.Runtime; namespace OpenBve { @@ -76,7 +77,7 @@ internal static void CollectData(out Table Table) { Array.Resize(ref Table.Stations, Table.Stations.Length << 1); } Table.Stations[n].Name = Game.Stations[sse.StationIndex].Name; - Table.Stations[n].NameJapanese = Interface.IsJapanese(Game.Stations[sse.StationIndex].Name); + Table.Stations[n].NameJapanese = Game.Stations[sse.StationIndex].Name.IsJapanese(); Table.Stations[n].Pass = !Game.PlayerStopsAtStation(sse.StationIndex); Table.Stations[n].Terminal = Game.Stations[sse.StationIndex].Type != StationType.Normal; double x; diff --git a/source/OpenBVE/Game/TrackManager/TrackManager.TrackFollower.cs b/source/OpenBVE/Game/TrackManager/TrackManager.TrackFollower.cs index 7aadec329..2898f3770 100644 --- a/source/OpenBVE/Game/TrackManager/TrackManager.TrackFollower.cs +++ b/source/OpenBVE/Game/TrackManager/TrackManager.TrackFollower.cs @@ -78,7 +78,7 @@ internal void Update(double NewTrackPosition, bool UpdateWorldCoordinates, bool double c = (double)Math.Sign(db) * Math.Sqrt(f >= 0.0 ? f : 0.0); double a = 0.5 * (double)Math.Sign(r) * b; Vector3 D = new Vector3(CurrentTrack.Elements[i].WorldDirection.X, 0.0, CurrentTrack.Elements[i].WorldDirection.Z); - World.Normalize(ref D.X, ref D.Y, ref D.Z); + D.Normalize(); double cosa = Math.Cos(a); double sina = Math.Sin(a); World.Rotate(ref D, 0.0, 1.0, 0.0, cosa, sina); @@ -89,7 +89,7 @@ internal void Update(double NewTrackPosition, bool UpdateWorldCoordinates, bool WorldDirection.X = D.X; WorldDirection.Y = p; WorldDirection.Z = D.Z; - World.Normalize(ref WorldDirection.X, ref WorldDirection.Y, ref WorldDirection.Z); + WorldDirection.Normalize(); double cos2a = Math.Cos(2.0 * a); double sin2a = Math.Sin(2.0 * a); WorldSide = CurrentTrack.Elements[i].WorldSide; diff --git a/source/OpenBVE/Graphics/Renderer.cs b/source/OpenBVE/Graphics/Renderer.cs index cb426b54c..be3b718c0 100644 --- a/source/OpenBVE/Graphics/Renderer.cs +++ b/source/OpenBVE/Graphics/Renderer.cs @@ -601,11 +601,26 @@ private static void RenderFace(ref World.MeshMaterial Material, VertexTemplate[] if (Material.GlowAttenuationData != 0) { float alphafactor = (float)GetDistanceFactor(Vertices, ref Face, Material.GlowAttenuationData, CameraX, CameraY, CameraZ); - GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, inv255 * (float)Material.Color.A * alphafactor); + if (OptionWireframe) + { + GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, 1.0f); + } + else + { + GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, inv255 * (float)Material.Color.A * alphafactor); + } } else { - GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, inv255 * (float)Material.Color.A); + if (OptionWireframe) + { + GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, 1.0f); + } + else + { + GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, inv255 * (float)Material.Color.A); + } + } if ((Material.Flags & World.MeshMaterial.EmissiveColorMask) != 0) { @@ -721,7 +736,15 @@ private static void RenderFace(ref World.MeshMaterial Material, VertexTemplate[] alphafactor = inv255 * (float)Material.DaytimeNighttimeBlend + 1.0f - OptionLightingResultingAmount; if (alphafactor > 1.0f) alphafactor = 1.0f; } - GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, inv255 * (float)Material.Color.A * alphafactor); + if (OptionWireframe) + { + GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, 1.0f); + } + else + { + GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, inv255 * (float)Material.Color.A * alphafactor); + } + if ((Material.Flags & World.MeshMaterial.EmissiveColorMask) != 0) { GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Emission, new float[] { inv255 * (float)Material.EmissiveColor.R, inv255 * (float)Material.EmissiveColor.G, inv255 * (float)Material.EmissiveColor.B, 1.0f }); diff --git a/source/OpenBVE/Graphics/Renderer/MotionBlur.cs b/source/OpenBVE/Graphics/Renderer/MotionBlur.cs index 18865d80a..cc0b3a63a 100644 --- a/source/OpenBVE/Graphics/Renderer/MotionBlur.cs +++ b/source/OpenBVE/Graphics/Renderer/MotionBlur.cs @@ -34,6 +34,14 @@ internal static void InitializeMotionBlur() /// This function renderers full-screen motion blur if selected private static void RenderFullscreenMotionBlur() { + if(Screen.Minimized) + { + /* + * HACK: + * This breaks if minimized, even if we don't reset the W / H values + */ + return; + } int w = Interface.CurrentOptions.NoTextureResize ? Screen.Width : Textures.RoundUpToPowerOfTwo(Screen.Width); int h = Interface.CurrentOptions.NoTextureResize ? Screen.Height : Textures.RoundUpToPowerOfTwo(Screen.Height); // render diff --git a/source/OpenBVE/Graphics/Screen.cs b/source/OpenBVE/Graphics/Screen.cs index 251b5099e..c89b3a5f9 100644 --- a/source/OpenBVE/Graphics/Screen.cs +++ b/source/OpenBVE/Graphics/Screen.cs @@ -19,7 +19,9 @@ internal static class Screen /// Whether the screen is set to fullscreen mode. internal static bool Fullscreen = false; - + + /// Whether the window is currently minimized + internal static bool Minimized = false; // --- functions --- diff --git a/source/OpenBVE/OldCode/Interface.cs b/source/OpenBVE/OldCode/Interface.cs index 13bd19496..9e83b68b6 100644 --- a/source/OpenBVE/OldCode/Interface.cs +++ b/source/OpenBVE/OldCode/Interface.cs @@ -2,6 +2,7 @@ using System.Globalization; using System.Linq; using System.Windows.Forms; +using OpenBveApi; using OpenBveApi.Colors; using OpenTK.Input; @@ -41,460 +42,7 @@ internal static void ClearMessages() { Messages = new Message[] { }; MessageCount = 0; } - - internal static CommandInfo[] CommandInfos = { - new CommandInfo(Command.PowerIncrease, CommandType.Digital, "POWER_INCREASE"), - new CommandInfo(Command.PowerDecrease, CommandType.Digital, "POWER_DECREASE"), - new CommandInfo(Command.PowerHalfAxis, CommandType.AnalogHalf, "POWER_HALFAXIS"), - new CommandInfo(Command.PowerFullAxis, CommandType.AnalogFull, "POWER_FULLAXIS"), - new CommandInfo(Command.BrakeDecrease, CommandType.Digital, "BRAKE_DECREASE"), - new CommandInfo(Command.BrakeIncrease, CommandType.Digital, "BRAKE_INCREASE"), - new CommandInfo(Command.LocoBrakeDecrease, CommandType.Digital, "LOCOBRAKE_DECREASE"), - new CommandInfo(Command.LocoBrakeIncrease, CommandType.Digital, "LOCOBRAKE_INCREASE"), - new CommandInfo(Command.BrakeHalfAxis, CommandType.AnalogHalf, "BRAKE_HALFAXIS"), - new CommandInfo(Command.BrakeFullAxis, CommandType.AnalogFull, "BRAKE_FULLAXIS"), - new CommandInfo(Command.BrakeEmergency, CommandType.Digital, "BRAKE_EMERGENCY"), - new CommandInfo(Command.SinglePower, CommandType.Digital, "SINGLE_POWER"), - new CommandInfo(Command.SingleNeutral, CommandType.Digital, "SINGLE_NEUTRAL"), - new CommandInfo(Command.SingleBrake, CommandType.Digital, "SINGLE_BRAKE"), - new CommandInfo(Command.SingleEmergency, CommandType.Digital, "SINGLE_EMERGENCY"), - new CommandInfo(Command.SingleFullAxis, CommandType.AnalogFull, "SINGLE_FULLAXIS"), - new CommandInfo(Command.ReverserForward, CommandType.Digital, "REVERSER_FORWARD"), - new CommandInfo(Command.ReverserBackward, CommandType.Digital, "REVERSER_BACKWARD"), - new CommandInfo(Command.ReverserFullAxis, CommandType.AnalogFull, "REVERSER_FULLAXIS"), - new CommandInfo(Command.DoorsLeft, CommandType.Digital, "DOORS_LEFT"), - new CommandInfo(Command.DoorsRight, CommandType.Digital, "DOORS_RIGHT"), - new CommandInfo(Command.HornPrimary, CommandType.Digital, "HORN_PRIMARY"), - new CommandInfo(Command.HornSecondary, CommandType.Digital, "HORN_SECONDARY"), - new CommandInfo(Command.HornMusic, CommandType.Digital, "HORN_MUSIC"), - new CommandInfo(Command.DeviceConstSpeed, CommandType.Digital, "DEVICE_CONSTSPEED"), - -//We only want to mark these as obsolete for new users of the API -#pragma warning disable 618 - new CommandInfo(Command.SecurityS, CommandType.Digital, "SECURITY_S"), - new CommandInfo(Command.SecurityA1, CommandType.Digital, "SECURITY_A1"), - new CommandInfo(Command.SecurityA2, CommandType.Digital, "SECURITY_A2"), - new CommandInfo(Command.SecurityB1, CommandType.Digital, "SECURITY_B1"), - new CommandInfo(Command.SecurityB2, CommandType.Digital, "SECURITY_B2"), - new CommandInfo(Command.SecurityC1, CommandType.Digital, "SECURITY_C1"), - new CommandInfo(Command.SecurityC2, CommandType.Digital, "SECURITY_C2"), - new CommandInfo(Command.SecurityD, CommandType.Digital, "SECURITY_D"), - new CommandInfo(Command.SecurityE, CommandType.Digital, "SECURITY_E"), - new CommandInfo(Command.SecurityF, CommandType.Digital, "SECURITY_F"), - new CommandInfo(Command.SecurityG, CommandType.Digital, "SECURITY_G"), - new CommandInfo(Command.SecurityH, CommandType.Digital, "SECURITY_H"), - new CommandInfo(Command.SecurityI, CommandType.Digital, "SECURITY_I"), - new CommandInfo(Command.SecurityJ, CommandType.Digital, "SECURITY_J"), - new CommandInfo(Command.SecurityK, CommandType.Digital, "SECURITY_K"), - new CommandInfo(Command.SecurityL, CommandType.Digital, "SECURITY_L"), - new CommandInfo(Command.SecurityM, CommandType.Digital, "SECURITY_M"), - new CommandInfo(Command.SecurityN, CommandType.Digital, "SECURITY_N"), - new CommandInfo(Command.SecurityO, CommandType.Digital, "SECURITY_O"), - new CommandInfo(Command.SecurityP, CommandType.Digital, "SECURITY_P"), -#pragma warning restore 618 - - //Common Keys - new CommandInfo(Command.WiperSpeedUp, CommandType.Digital, "WIPER_SPEED_UP"), - new CommandInfo(Command.WiperSpeedDown, CommandType.Digital, "WIPER_SPEED_DOWN"), - new CommandInfo(Command.FillFuel, CommandType.Digital, "FILL_FUEL"), - new CommandInfo(Command.Headlights, CommandType.Digital, "HEADLIGHTS"), - //Steam locomotive - new CommandInfo(Command.LiveSteamInjector, CommandType.Digital, "LIVE_STEAM_INJECTOR"), - new CommandInfo(Command.ExhaustSteamInjector, CommandType.Digital, "EXHAUST_STEAM_INJECTOR"), - new CommandInfo(Command.IncreaseCutoff, CommandType.Digital, "INCREASE_CUTOFF"), - new CommandInfo(Command.DecreaseCutoff, CommandType.Digital, "DECREASE_CUTOFF"), - new CommandInfo(Command.Blowers, CommandType.Digital, "BLOWERS"), - //Diesel Locomotive - new CommandInfo(Command.EngineStart, CommandType.Digital, "ENGINE_START"), - new CommandInfo(Command.EngineStop, CommandType.Digital, "ENGINE_STOP"), - new CommandInfo(Command.GearUp, CommandType.Digital, "GEAR_UP"), - new CommandInfo(Command.GearDown, CommandType.Digital, "GEAR_DOWN"), - - //Electric Locomotive - new CommandInfo(Command.RaisePantograph, CommandType.Digital, "RAISE_PANTOGRAPH"), - new CommandInfo(Command.LowerPantograph, CommandType.Digital, "LOWER_PANTOGRAPH"), - new CommandInfo(Command.MainBreaker, CommandType.Digital, "MAIN_BREAKER"), - - //Simulation controls - new CommandInfo(Command.CameraInterior, CommandType.Digital, "CAMERA_INTERIOR"), - new CommandInfo(Command.CameraExterior, CommandType.Digital, "CAMERA_EXTERIOR"), - new CommandInfo(Command.CameraTrack, CommandType.Digital, "CAMERA_TRACK"), - new CommandInfo(Command.CameraFlyBy, CommandType.Digital, "CAMERA_FLYBY"), - new CommandInfo(Command.CameraMoveForward, CommandType.AnalogHalf, "CAMERA_MOVE_FORWARD"), - new CommandInfo(Command.CameraMoveBackward, CommandType.AnalogHalf, "CAMERA_MOVE_BACKWARD"), - new CommandInfo(Command.CameraMoveLeft, CommandType.AnalogHalf, "CAMERA_MOVE_LEFT"), - new CommandInfo(Command.CameraMoveRight, CommandType.AnalogHalf, "CAMERA_MOVE_RIGHT"), - new CommandInfo(Command.CameraMoveUp, CommandType.AnalogHalf, "CAMERA_MOVE_UP"), - new CommandInfo(Command.CameraMoveDown, CommandType.AnalogHalf, "CAMERA_MOVE_DOWN"), - new CommandInfo(Command.CameraRotateLeft, CommandType.AnalogHalf, "CAMERA_ROTATE_LEFT"), - new CommandInfo(Command.CameraRotateRight, CommandType.AnalogHalf, "CAMERA_ROTATE_RIGHT"), - new CommandInfo(Command.CameraRotateUp, CommandType.AnalogHalf, "CAMERA_ROTATE_UP"), - new CommandInfo(Command.CameraRotateDown, CommandType.AnalogHalf, "CAMERA_ROTATE_DOWN"), - new CommandInfo(Command.CameraRotateCCW, CommandType.AnalogHalf, "CAMERA_ROTATE_CCW"), - new CommandInfo(Command.CameraRotateCW, CommandType.AnalogHalf, "CAMERA_ROTATE_CW"), - new CommandInfo(Command.CameraZoomIn, CommandType.AnalogHalf, "CAMERA_ZOOM_IN"), - new CommandInfo(Command.CameraZoomOut, CommandType.AnalogHalf, "CAMERA_ZOOM_OUT"), - new CommandInfo(Command.CameraPreviousPOI, CommandType.Digital, "CAMERA_POI_PREVIOUS"), - new CommandInfo(Command.CameraNextPOI, CommandType.Digital, "CAMERA_POI_NEXT"), - new CommandInfo(Command.CameraReset, CommandType.Digital, "CAMERA_RESET"), - new CommandInfo(Command.CameraRestriction, CommandType.Digital, "CAMERA_RESTRICTION"), - new CommandInfo(Command.TimetableToggle, CommandType.Digital, "TIMETABLE_TOGGLE"), - new CommandInfo(Command.TimetableUp, CommandType.AnalogHalf, "TIMETABLE_UP"), - new CommandInfo(Command.TimetableDown, CommandType.AnalogHalf, "TIMETABLE_DOWN"), - new CommandInfo(Command.MenuActivate, CommandType.Digital, "MENU_ACTIVATE"), - new CommandInfo(Command.MenuUp, CommandType.Digital, "MENU_UP"), - new CommandInfo(Command.MenuDown, CommandType.Digital, "MENU_DOWN"), - new CommandInfo(Command.MenuEnter, CommandType.Digital, "MENU_ENTER"), - new CommandInfo(Command.MenuBack, CommandType.Digital, "MENU_BACK"), - new CommandInfo(Command.MiscClock, CommandType.Digital, "MISC_CLOCK"), - new CommandInfo(Command.MiscSpeed, CommandType.Digital, "MISC_SPEED"), - new CommandInfo(Command.MiscGradient, CommandType.Digital, "MISC_GRADIENT"), - new CommandInfo(Command.MiscFps, CommandType.Digital, "MISC_FPS"), - new CommandInfo(Command.MiscAI, CommandType.Digital, "MISC_AI"), - new CommandInfo(Command.MiscFullscreen, CommandType.Digital, "MISC_FULLSCREEN"), - new CommandInfo(Command.MiscMute, CommandType.Digital, "MISC_MUTE"), - new CommandInfo(Command.MiscPause, CommandType.Digital, "MISC_PAUSE"), - new CommandInfo(Command.MiscTimeFactor, CommandType.Digital, "MISC_TIMEFACTOR"), - new CommandInfo(Command.MiscQuit, CommandType.Digital, "MISC_QUIT"), - new CommandInfo(Command.MiscInterfaceMode, CommandType.Digital, "MISC_INTERFACE"), - new CommandInfo(Command.MiscBackfaceCulling, CommandType.Digital, "MISC_BACKFACE"), - new CommandInfo(Command.MiscCPUMode, CommandType.Digital, "MISC_CPUMODE"), - new CommandInfo(Command.DebugWireframe, CommandType.Digital, "DEBUG_WIREFRAME"), - new CommandInfo(Command.DebugNormals, CommandType.Digital, "DEBUG_NORMALS"), - new CommandInfo(Command.DebugBrakeSystems, CommandType.Digital, "DEBUG_BRAKE"), - new CommandInfo(Command.DebugATS, CommandType.Digital, "DEBUG_ATS"), - new CommandInfo(Command.RouteInformation, CommandType.Digital, "ROUTE_INFORMATION"), - new CommandInfo(Command.ShowEvents, CommandType.Digital, "SHOW_EVENTS"), - }; - internal static Control[] CurrentControls = { }; - - // try get command info - internal static bool TryGetCommandInfo(Command Value, out CommandInfo Info) { - for (int i = 0; i < CommandInfos.Length; i++) { - if (CommandInfos[i].Command == Value) { - Info = CommandInfos[i]; - return true; - } - } - Info.Command = Value; - Info.Type = CommandType.Digital; - Info.Name = "N/A"; - Info.Description = "N/A"; - return false; - } - - /// Saves a control configuration to disk - /// An absolute file path if we are exporting the controls, or a null reference to save to the default configuration location - /// The list of controls to save - internal static void SaveControls(string FileOrNull, Control[] controlsToSave) { - CultureInfo Culture = CultureInfo.InvariantCulture; - System.Text.StringBuilder Builder = new System.Text.StringBuilder(); - Builder.AppendLine("; Current control configuration"); - Builder.AppendLine("; ============================="); - Builder.AppendLine("; This file was automatically generated. Please modify only if you know what you're doing."); - Builder.AppendLine("; This file is INCOMPATIBLE with versions older than 1.4.4."); - Builder.AppendLine(); - for (int i = 0; i < controlsToSave.Length; i++) { - CommandInfo Info; - TryGetCommandInfo(controlsToSave[i].Command, out Info); - Builder.Append(Info.Name + ", "); - switch (controlsToSave[i].Method) { - case ControlMethod.Keyboard: - Builder.Append("keyboard, " + controlsToSave[i].Key + ", " + ((int)controlsToSave[i].Modifier).ToString(Culture)); - break; - case ControlMethod.Joystick: - Builder.Append("joystick, " + controlsToSave[i].Device.ToString(Culture) + ", "); - switch (controlsToSave[i].Component) { - case JoystickComponent.Axis: - Builder.Append("axis, " + controlsToSave[i].Element.ToString(Culture) + ", " + controlsToSave[i].Direction.ToString(Culture)); - break; - case JoystickComponent.Ball: - Builder.Append("ball, " + controlsToSave[i].Element.ToString(Culture) + ", " + controlsToSave[i].Direction.ToString(Culture)); - break; - case JoystickComponent.Hat: - Builder.Append("hat, " + controlsToSave[i].Element.ToString(Culture) + ", " + controlsToSave[i].Direction.ToString(Culture)); - break; - case JoystickComponent.Button: - Builder.Append("button, " + controlsToSave[i].Element.ToString(Culture)); - break; - default: - Builder.Append("invalid"); - break; - } - break; - case ControlMethod.RailDriver: - Builder.Append("raildriver, 0, "); - switch (controlsToSave[i].Component) { - case JoystickComponent.Axis: - Builder.Append("axis, " + controlsToSave[i].Element.ToString(Culture) + ", " + controlsToSave[i].Direction.ToString(Culture)); - break; - case JoystickComponent.Button: - Builder.Append("button, " + controlsToSave[i].Element.ToString(Culture)); - break; - default: - Builder.Append("invalid"); - break; - } - break; - } - Builder.Append("\n"); - } - string File; - if (FileOrNull == null) { - File = OpenBveApi.Path.CombineFile(Program.FileSystem.SettingsFolder, "1.5.0/controls.cfg"); - } else { - File = FileOrNull; - } - System.IO.File.WriteAllText(File, Builder.ToString(), new System.Text.UTF8Encoding(true)); - } - - - private static bool ControlsReset; - - /// Loads the current controls from the controls.cfg file - /// An absolute path reference to a saved controls.cfg file, or a null reference to check the default locations - /// The current controls array - internal static void LoadControls(string FileOrNull, out Control[] Controls) - { - string File; - if (FileOrNull == null) { - File = OpenBveApi.Path.CombineFile(Program.FileSystem.SettingsFolder, "1.5.0/controls.cfg"); - if (!System.IO.File.Exists(File)) - { - File = OpenBveApi.Path.CombineFile(Program.FileSystem.SettingsFolder, "controls.cfg"); - } - if (!System.IO.File.Exists(File)) { - //Load the default key assignments if the user settings don't exist - File = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Controls"), "Default keyboard assignment.controls"); - if (!System.IO.File.Exists(File)) - { - MessageBox.Show(Interface.GetInterfaceString("errors_warning") + Environment.NewLine + Interface.GetInterfaceString("errors_controls_missing"), - Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); - Controls = new Control[0]; - return; - } - } - } else { - File = FileOrNull; - } - Controls = new Control[256]; - int Length = 0; - CultureInfo Culture = CultureInfo.InvariantCulture; - if (System.IO.File.Exists(File)) { - string[] Lines = System.IO.File.ReadAllLines(File, new System.Text.UTF8Encoding()); - for (int i = 0; i < Lines.Length; i++) { - Lines[i] = Lines[i].Trim(); - if (Lines[i].Length != 0 && !Lines[i].StartsWith(";", StringComparison.OrdinalIgnoreCase)) { - string[] Terms = Lines[i].Split(','); - for (int j = 0; j < Terms.Length; j++) { - Terms[j] = Terms[j].Trim(); - } - if (Terms.Length >= 2) { - if (Length >= Controls.Length) { - Array.Resize(ref Controls, Controls.Length << 1); - } - int j; - for (j = 0; j < CommandInfos.Length; j++) { - if (string.Compare(CommandInfos[j].Name, Terms[0], StringComparison.OrdinalIgnoreCase) == 0) break; - } - if (j == CommandInfos.Length) { - Controls[Length].Command = Command.None; - Controls[Length].InheritedType = CommandType.Digital; - Controls[Length].Method = ControlMethod.Invalid; - Controls[Length].Device = -1; - Controls[Length].Component = JoystickComponent.Invalid; - Controls[Length].Element = -1; - Controls[Length].Direction = 0; - Controls[Length].Modifier = KeyboardModifier.None; - } else { - Controls[Length].Command = CommandInfos[j].Command; - Controls[Length].InheritedType = CommandInfos[j].Type; - string Method = Terms[1].ToLowerInvariant(); - bool Valid = false; - if (Method == "keyboard" & Terms.Length == 4) - { - Key CurrentKey; - int SDLTest; - if (int.TryParse(Terms[2], out SDLTest)) - { - //We've discovered a SDL keybinding is present, so reset the loading process with the default keyconfig & show an appropriate error message - if (ControlsReset == false) - { - MessageBox.Show(Interface.GetInterfaceString("errors_controls_oldversion") + Environment.NewLine + Interface.GetInterfaceString("errors_controls_reset"), Application.ProductName, - MessageBoxButtons.OK, MessageBoxIcon.Hand); - } - var DefaultControls = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Controls"),"Default keyboard assignment.controls"); - if (System.IO.File.Exists(DefaultControls)) - { - if (ControlsReset == false) - { - LoadControls(DefaultControls, out CurrentControls); - ControlsReset = true; - } - else - { - MessageBox.Show(Interface.GetInterfaceString("errors_warning") + Environment.NewLine + Interface.GetInterfaceString("errors_controls_default_oldversion"), - Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); - Controls = new Control[0]; - } - - } - else - { - MessageBox.Show(Interface.GetInterfaceString("errors_warning") + Environment.NewLine + Interface.GetInterfaceString("errors_controls_default_missing"), - Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); - Controls = new Control[0]; - } - return; - } - if (Enum.TryParse(Terms[2], true, out CurrentKey)) - { - int Modifiers; - if (int.TryParse(Terms[3], NumberStyles.Integer, Culture, out Modifiers)) - { - Controls[Length].Method = ControlMethod.Keyboard; - Controls[Length].Device = -1; - Controls[Length].Component = JoystickComponent.Invalid; - Controls[Length].Key = CurrentKey; - Controls[Length].Direction = 0; - Controls[Length].Modifier = (KeyboardModifier) Modifiers; - Valid = true; - } - } - } - - - else if (Method == "joystick" & Terms.Length >= 4) { - int Device; - if (int.TryParse(Terms[2], NumberStyles.Integer, Culture, out Device)) { - string Component = Terms[3].ToLowerInvariant(); - if (Component == "axis" & Terms.Length == 6) - { - int CurrentAxis; - if (Int32.TryParse(Terms[4], out CurrentAxis)) - { - int Direction; - if (int.TryParse(Terms[5], NumberStyles.Integer, Culture, out Direction)) - { - - Controls[Length].Method = ControlMethod.Joystick; - Controls[Length].Device = Device; - Controls[Length].Component = JoystickComponent.Axis; - Controls[Length].Element = CurrentAxis; - Controls[Length].Direction = Direction; - Controls[Length].Modifier = KeyboardModifier.None; - Valid = true; - } - } - } - else if (Component == "hat" & Terms.Length == 6) - { - int CurrentHat; - if (Int32.TryParse(Terms[4], out CurrentHat)) - { - int HatDirection; - if (Int32.TryParse(Terms[5], out HatDirection)) - { - Controls[Length].Method = ControlMethod.Joystick; - Controls[Length].Device = Device; - Controls[Length].Component = JoystickComponent.Hat; - Controls[Length].Element = CurrentHat; - Controls[Length].Direction = HatDirection; - Controls[Length].Modifier = KeyboardModifier.None; - Valid = true; - } - - } - } - else if (Component == "button" & Terms.Length == 5) - { - int CurrentButton; - if (Int32.TryParse(Terms[4], out CurrentButton)) - { - Controls[Length].Method = ControlMethod.Joystick; - Controls[Length].Device = Device; - Controls[Length].Component = JoystickComponent.Button; - Controls[Length].Element = CurrentButton; - Controls[Length].Direction = 0; - Controls[Length].Modifier = KeyboardModifier.None; - Valid = true; - } - } - - } - } - else if (Method == "raildriver" & Terms.Length >= 4) { - int Device; - if (int.TryParse(Terms[2], NumberStyles.Integer, Culture, out Device)) { - string Component = Terms[3].ToLowerInvariant(); - if (Component == "axis" & Terms.Length == 6) - { - int CurrentAxis; - if (Int32.TryParse(Terms[4], out CurrentAxis)) - { - int Direction; - if (int.TryParse(Terms[5], NumberStyles.Integer, Culture, out Direction)) - { - - Controls[Length].Method = ControlMethod.RailDriver; - Controls[Length].Device = Device; - Controls[Length].Component = JoystickComponent.Axis; - Controls[Length].Element = CurrentAxis; - Controls[Length].Direction = Direction; - Controls[Length].Modifier = KeyboardModifier.None; - Valid = true; - } - } - } - else if (Component == "button" & Terms.Length == 5) - { - int CurrentButton; - if (Int32.TryParse(Terms[4], out CurrentButton)) - { - Controls[Length].Method = ControlMethod.RailDriver; - Controls[Length].Device = Device; - Controls[Length].Component = JoystickComponent.Button; - Controls[Length].Element = CurrentButton; - Controls[Length].Direction = 0; - Controls[Length].Modifier = KeyboardModifier.None; - Valid = true; - } - } - - } - } - - if (!Valid) { - Controls[Length].Method = ControlMethod.Invalid; - Controls[Length].Device = -1; - Controls[Length].Component = JoystickComponent.Invalid; - Controls[Length].Element = -1; - Controls[Length].Direction = 0; - Controls[Length].Modifier = KeyboardModifier.None; - } - } - Length++; - } - } - } - } - Array.Resize(ref Controls, Length); - } - - // add controls - internal static void AddControls(ref Control[] Base, Control[] Add) { - for (int i = 0; i < Add.Length; i++) { - int j; - for (j = 0; j < Base.Length; j++) { - if (Add[i].Command == Base[j].Command) break; - } - if (j == Base.Length) { - Array.Resize(ref Base, Base.Length + 1); - Base[Base.Length - 1] = Add[i]; - } - } - } // ================================ @@ -931,8 +479,9 @@ internal static void LoadHUD() { /// The time in string format /// The number of seconds since midnight on the first day this represents, updated via 'out' /// True if the parse succeeds, false if it does not - internal static bool TryParseTime(string Expression, out double Value) { - Expression = TrimInside(Expression); + internal static bool TryParseTime(string Expression, out double Value) + { + Expression = Expression.TrimInside(); if (Expression.Length != 0) { CultureInfo Culture = CultureInfo.InvariantCulture; int i = Expression.IndexOf('.'); @@ -985,138 +534,5 @@ internal static bool TryParseTime(string Expression, out double Value) { Value = 0.0; return false; } - - - - // trim inside - private static string TrimInside(string Expression) { - System.Text.StringBuilder Builder = new System.Text.StringBuilder(Expression.Length); - foreach (char c in Expression.Where(c => !char.IsWhiteSpace(c))) - { - Builder.Append(c); - } return Builder.ToString(); - } - - /// Determines whether the specified string is encoded using Shift_JIS (Japanese) - /// The string to check - /// True if Shift_JIS encoded, false otherwise - internal static bool IsJapanese(string Name) { - for (int i = 0; i < Name.Length; i++) { - int a = char.ConvertToUtf32(Name, i); - if (a < 0x10000) { - bool q = false; - while (true) { - if (a >= 0x2E80 & a <= 0x2EFF) break; - if (a >= 0x3000 & a <= 0x30FF) break; - if (a >= 0x31C0 & a <= 0x4DBF) break; - if (a >= 0x4E00 & a <= 0x9FFF) break; - if (a >= 0xF900 & a <= 0xFAFF) break; - if (a >= 0xFE30 & a <= 0xFE4F) break; - if (a >= 0xFF00 & a <= 0xFFEF) break; - q = true; break; - } if (q) return false; - } else { - return false; - } - } return true; - } - - /// Unescapes control characters used in a language file - /// The raw string on which unescaping should be performed - /// The unescaped string - 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(); - } - - /// Converts various line-endings to CR-LF format - /// The string for which all line-endings should be converted to CR-LF - /// The converted StringBuilder - internal static string ConvertNewlinesToCrLf(string Text) { - System.Text.StringBuilder Builder = new System.Text.StringBuilder(); - for (int i = 0; i < Text.Length; i++) { - int a = char.ConvertToUtf32(Text, i); - if (a == 0xD & i < Text.Length - 1) { - int b = char.ConvertToUtf32(Text, i + 1); - if (b == 0xA) { - Builder.Append("\r\n"); - i++; - } else { - Builder.Append("\r\n"); - } - } else if (a == 0xA | a == 0xC | a == 0xD | a == 0x85 | a == 0x2028 | a == 0x2029) { - Builder.Append("\r\n"); - } else if (a < 0x10000) { - Builder.Append(Text[i]); - } else { - Builder.Append(Text.Substring(i, 2)); - i++; - } - } return Builder.ToString(); - } } } diff --git a/source/OpenBVE/OldCode/World.cs b/source/OpenBVE/OldCode/World.cs index feac6659f..e46d43a3f 100644 --- a/source/OpenBVE/OldCode/World.cs +++ b/source/OpenBVE/OldCode/World.cs @@ -9,9 +9,6 @@ namespace OpenBve { internal static partial class World { - // vertices - /// Represents a vertex consisting of 3D coordinates and 2D texture coordinates. - // mesh material /// Represents material properties. internal struct MeshMaterial { @@ -289,47 +286,46 @@ internal struct DriverBody { internal double FastY; internal double Pitch; internal ObjectManager.Damping PitchDamping; - } - internal static DriverBody CurrentDriverBody; - internal static void UpdateDriverBody(double TimeElapsed) { + + internal void Update(double TimeElapsed) { if (CameraRestriction == CameraRestrictionMode.NotAvailable) { { // pitch double targetY = TrainManager.PlayerTrain.Specs.CurrentAverageAcceleration; const double accelerationSlow = 0.25; const double accelerationFast = 2.0; - if (CurrentDriverBody.SlowY < targetY) { - CurrentDriverBody.SlowY += accelerationSlow * TimeElapsed; - if (CurrentDriverBody.SlowY > targetY) { - CurrentDriverBody.SlowY = targetY; + if (SlowY < targetY) { + SlowY += accelerationSlow * TimeElapsed; + if (SlowY > targetY) { + SlowY = targetY; } - } else if (CurrentDriverBody.SlowY > targetY) { - CurrentDriverBody.SlowY -= accelerationSlow * TimeElapsed; - if (CurrentDriverBody.SlowY < targetY) { - CurrentDriverBody.SlowY = targetY; + } else if (SlowY > targetY) { + SlowY -= accelerationSlow * TimeElapsed; + if (SlowY < targetY) { + SlowY = targetY; } } - if (CurrentDriverBody.FastY < targetY) { - CurrentDriverBody.FastY += accelerationFast * TimeElapsed; - if (CurrentDriverBody.FastY > targetY) { - CurrentDriverBody.FastY = targetY; + if (FastY < targetY) { + FastY += accelerationFast * TimeElapsed; + if (FastY > targetY) { + FastY = targetY; } - } else if (CurrentDriverBody.FastY > targetY) { - CurrentDriverBody.FastY -= accelerationFast * TimeElapsed; - if (CurrentDriverBody.FastY < targetY) { - CurrentDriverBody.FastY = targetY; + } else if (FastY > targetY) { + FastY -= accelerationFast * TimeElapsed; + if (FastY < targetY) { + FastY = targetY; } } - double diffY = CurrentDriverBody.FastY - CurrentDriverBody.SlowY; + double diffY = FastY - SlowY; diffY = (double)Math.Sign(diffY) * diffY * diffY; - CurrentDriverBody.Pitch = 0.5 * Math.Atan(0.1 * diffY); - if (CurrentDriverBody.Pitch > 0.1) { - CurrentDriverBody.Pitch = 0.1; + Pitch = 0.5 * Math.Atan(0.1 * diffY); + if (Pitch > 0.1) { + Pitch = 0.1; } - if (CurrentDriverBody.PitchDamping == null) { - CurrentDriverBody.PitchDamping = new ObjectManager.Damping(6.0, 0.3); + if (PitchDamping == null) { + PitchDamping = new ObjectManager.Damping(6.0, 0.3); } - CurrentDriverBody.PitchDamping.Update(TimeElapsed, ref CurrentDriverBody.Pitch, true); + PitchDamping.Update(TimeElapsed, ref Pitch, true); } { // roll @@ -359,38 +355,41 @@ internal static void UpdateDriverBody(double TimeElapsed) { } const double accelerationSlow = 1.0; const double accelerationFast = 10.0; - if (CurrentDriverBody.SlowX < targetX) { - CurrentDriverBody.SlowX += accelerationSlow * TimeElapsed; - if (CurrentDriverBody.SlowX > targetX) { - CurrentDriverBody.SlowX = targetX; + if (SlowX < targetX) { + SlowX += accelerationSlow * TimeElapsed; + if (SlowX > targetX) { + SlowX = targetX; } - } else if (CurrentDriverBody.SlowX > targetX) { - CurrentDriverBody.SlowX -= accelerationSlow * TimeElapsed; - if (CurrentDriverBody.SlowX < targetX) { - CurrentDriverBody.SlowX = targetX; + } else if (SlowX > targetX) { + SlowX -= accelerationSlow * TimeElapsed; + if (SlowX < targetX) { + SlowX = targetX; } } - if (CurrentDriverBody.FastX < targetX) { - CurrentDriverBody.FastX += accelerationFast * TimeElapsed; - if (CurrentDriverBody.FastX > targetX) { - CurrentDriverBody.FastX = targetX; + if (FastX < targetX) { + FastX += accelerationFast * TimeElapsed; + if (FastX > targetX) { + FastX = targetX; } - } else if (CurrentDriverBody.FastX > targetX) { - CurrentDriverBody.FastX -= accelerationFast * TimeElapsed; - if (CurrentDriverBody.FastX < targetX) { - CurrentDriverBody.FastX = targetX; + } else if (FastX > targetX) { + FastX -= accelerationFast * TimeElapsed; + if (FastX < targetX) { + FastX = targetX; } } - double diffX = CurrentDriverBody.SlowX - CurrentDriverBody.FastX; + double diffX = SlowX - FastX; diffX = (double)Math.Sign(diffX) * diffX * diffX; - CurrentDriverBody.Roll = 0.5 * Math.Atan(0.3 * diffX); - if (CurrentDriverBody.RollDamping == null) { - CurrentDriverBody.RollDamping = new ObjectManager.Damping(6.0, 0.3); + Roll = 0.5 * Math.Atan(0.3 * diffX); + if (RollDamping == null) { + RollDamping = new ObjectManager.Damping(6.0, 0.3); } - CurrentDriverBody.RollDamping.Update(TimeElapsed, ref CurrentDriverBody.Roll, true); + RollDamping.Update(TimeElapsed, ref Roll, true); } } } + } + internal static DriverBody CurrentDriverBody; + // mouse grab internal static bool MouseGrabEnabled = false; @@ -711,7 +710,7 @@ internal static void UpdateAbsoluteCamera(double TimeElapsed) { dz *= ti; AbsoluteCameraDirection = new Vector3(dx, dy, dz); AbsoluteCameraSide = new Vector3(dz, 0.0, -dx); - Normalize(ref AbsoluteCameraSide.X, ref AbsoluteCameraSide.Y, ref AbsoluteCameraSide.Z); + AbsoluteCameraSide.Normalize(); World.Cross(dx, dy, dz, AbsoluteCameraSide.X, AbsoluteCameraSide.Y, AbsoluteCameraSide.Z, out AbsoluteCameraUp.X, out AbsoluteCameraUp.Y, out AbsoluteCameraUp.Z); UpdateViewingDistances(); if (CameraMode == CameraViewMode.FlyByZooming) { diff --git a/source/OpenBVE/OpenBve.csproj b/source/OpenBVE/OpenBve.csproj index 5d8b3bf35..bfd24110a 100644 --- a/source/OpenBVE/OpenBve.csproj +++ b/source/OpenBVE/OpenBve.csproj @@ -284,7 +284,9 @@ + + diff --git a/source/OpenBVE/Parsers/Object/BVE/CsvB3dObjectParser.cs b/source/OpenBVE/Parsers/Object/BVE/CsvB3dObjectParser.cs index 02cdfd18b..1e79f6e28 100644 --- a/source/OpenBVE/Parsers/Object/BVE/CsvB3dObjectParser.cs +++ b/source/OpenBVE/Parsers/Object/BVE/CsvB3dObjectParser.cs @@ -67,6 +67,7 @@ private class MeshBuilder { internal VertexTemplate[] Vertices; internal World.MeshFace[] Faces; internal Material[] Materials; + internal bool isCylinder = false; internal MeshBuilder() { this.Vertices = new VertexTemplate[] { }; this.Faces = new World.MeshFace[] { }; @@ -477,6 +478,13 @@ internal static ObjectManager.StaticObject ReadObject(string FileName, System.Te Builder.Faces[f].Vertices[j].Index = (ushort)a[j]; Builder.Faces[f].Vertices[j].Normal = Normals[a[j]]; } + if (Builder.isCylinder && Interface.CurrentOptions.EnableBveTsHacks && CsvRwRouteParser.CylinderHack) + { + int l = Builder.Faces[f].Vertices.Length; + World.MeshFaceVertex v = Builder.Faces[f].Vertices[l - 1]; + Builder.Faces[f].Vertices[l - 1] = Builder.Faces[f].Vertices[l - 2]; + Builder.Faces[f].Vertices[l - 2] = v; + } if (cmd == "addface2" | cmd == "face2") { Builder.Faces[f].Flags = (byte)World.MeshFace.Face2Mask; } @@ -532,6 +540,7 @@ internal static ObjectManager.StaticObject ReadObject(string FileName, System.Te h = 1.0; } CreateCylinder(ref Builder, n, r1, r2, h); + Builder.isCylinder = true; } break; case "translate": case "translateall": diff --git a/source/OpenBVE/Parsers/Object/Generic/WavefrontObjParser.cs b/source/OpenBVE/Parsers/Object/Generic/WavefrontObjParser.cs index 1b94f7066..4eb8fdf63 100644 --- a/source/OpenBVE/Parsers/Object/Generic/WavefrontObjParser.cs +++ b/source/OpenBVE/Parsers/Object/Generic/WavefrontObjParser.cs @@ -172,19 +172,36 @@ internal static ObjectManager.StaticObject ReadObject(string FileName, System.Te Vertex newVertex = new Vertex(); string[] faceArguments = Arguments[f].Split(new char[] {'/'} , StringSplitOptions.None); int idx; - if (!int.TryParse(faceArguments[0], out idx) || idx > tempVertices.Count) + if (!int.TryParse(faceArguments[0], out idx)) { Interface.AddMessage(Interface.MessageType.Warning, false, "Invalid Vertex index in Face " + f + " at Line " + i); continue; } - newVertex.Coordinates = tempVertices[idx - 1]; + + 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(Interface.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) || idx > tempCoords.Count) + if (!int.TryParse(faceArguments[1], out idx)) { if (!string.IsNullOrEmpty(faceArguments[1])) { @@ -194,7 +211,22 @@ internal static ObjectManager.StaticObject ReadObject(string FileName, System.Te } else { - newVertex.TextureCoordinates = tempCoords[idx - 1]; + int currentCoord = tempCoords.Count; + if (idx != Math.Abs(idx)) + { + //Offset, so we seem to need to add one.... + currentCoord++; + currentCoord += idx; + } + if (currentCoord > tempCoords.Count) + { + Interface.AddMessage(Interface.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) @@ -203,7 +235,7 @@ internal static ObjectManager.StaticObject ReadObject(string FileName, System.Te } else { - if (!int.TryParse(faceArguments[2], out idx) || idx > tempNormals.Count) + if (!int.TryParse(faceArguments[2], out idx)) { if (!string.IsNullOrEmpty(faceArguments[2])) { @@ -213,7 +245,22 @@ internal static ObjectManager.StaticObject ReadObject(string FileName, System.Te } else { - normals.Add(tempNormals[idx - 1]); + int currentNormal = tempNormals.Count; + if (idx != Math.Abs(idx)) + { + //Offset, so we seem to need to add one.... + currentNormal++; + currentNormal += idx; + } + if (currentNormal > tempNormals.Count) + { + Interface.AddMessage(Interface.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); @@ -221,17 +268,8 @@ internal static ObjectManager.StaticObject ReadObject(string FileName, System.Te World.MeshFaceVertex[] Vertices = new World.MeshFaceVertex[vertices.Count]; for (int k = 0; k < vertices.Count; k++) { - int v = Builder.Vertices.FindIndex(a => a.Equals(vertices[k])); - if (v != -1) - { - Vertices[k].Index = (ushort)v; - } - else - { - Builder.Vertices.Add(vertices[k]); - Vertices[k].Index = (ushort)(Builder.Vertices.Count -1); - } - + Builder.Vertices.Add(vertices[k]); + Vertices[k].Index = (ushort)(Builder.Vertices.Count -1); Vertices[k].Normal = normals[k]; } Builder.Faces.Add(currentMaterial == -1 ? new World.MeshFace(Vertices, 0) : new World.MeshFace(Vertices, (ushort)currentMaterial)); diff --git a/source/OpenBVE/Parsers/Object/Generic/XObjectParser.cs b/source/OpenBVE/Parsers/Object/Generic/XObjectParser.cs index 28018139c..7a5b889f1 100644 --- a/source/OpenBVE/Parsers/Object/Generic/XObjectParser.cs +++ b/source/OpenBVE/Parsers/Object/Generic/XObjectParser.cs @@ -166,14 +166,14 @@ private static Template GetTemplate(string Name, bool binary) { return Templates[i]; } } - if (Name.StartsWith("Frame ")) + if (Name.ToLowerInvariant().StartsWith("frame ")) { //Enclosing frame for the model //Appears in Blender exported stuff - return Templates[11]; + return Templates[13]; } - if (Name.StartsWith("Mesh ")) + if (Name.ToLowerInvariant().StartsWith("mesh ")) { //Named material, just ignore the name for the minute //Appears in Blender exported stuff @@ -2221,7 +2221,7 @@ private static bool ProcessStructure(string FileName, Structure Structure, out O Object.Mesh.Faces[mf + j].Vertices = new World.MeshFaceVertex[Faces[j].Length]; for (int k = 0; k < Faces[j].Length; k++) { - Object.Mesh.Faces[mf + j].Vertices[mv + k] = new World.MeshFaceVertex(mv + Faces[j][k], FaceNormals[j][k]); + Object.Mesh.Faces[mf + j].Vertices[k] = new World.MeshFaceVertex(mv + Faces[j][k], FaceNormals[j][k]); } } for (int j = 0; j < Vertices.Length; j++) diff --git a/source/OpenBVE/Parsers/Panel/Panel2CfgParser.cs b/source/OpenBVE/Parsers/Panel/Panel2CfgParser.cs index 6e93f0798..9cfd25c58 100644 --- a/source/OpenBVE/Parsers/Panel/Panel2CfgParser.cs +++ b/source/OpenBVE/Parsers/Panel/Panel2CfgParser.cs @@ -147,6 +147,16 @@ internal static void ParsePanel2Config(string PanelFile, string TrainPath, Syste { switch ((int)PanelCenterY) { + case 180: + switch (trainName.ToUpperInvariant()) + { + case "LT_C69_77": + case "LT_C69_77_V2": + // Broken initial zoom + PanelCenterY = 350; + break; + } + break; case 229: if (PanelBottom == 768 && PanelResolution == 1024) { @@ -155,10 +165,27 @@ internal static void ParsePanel2Config(string PanelFile, string TrainPath, Syste } break; case 255: - if (PanelBottom == 1024 && PanelResolution == 1024 && trainName == "LT1938" ) + if (PanelBottom == 1024 && PanelResolution == 1024) { - // LT1938 stock: Broken initial zoom - PanelCenterY = 350; + switch (trainName.ToUpperInvariant()) + { + case "PARIS_MF67": + case "PARIS_MF88": + case "PARIS_MP73": + case "PARIS_MP89": + case "PARIS_MP89AUTO": + case "LT1938": + case "LT1973 UNREFURB": + // Broken initial zoom + PanelCenterY = 350; + break; + case "LT_A60_62": + case "LT1972 MKII": + // Broken initial zoom and black patch at bottom of panel + PanelCenterY = 350; + PanelBottom = 792; + break; + } } break; } diff --git a/source/OpenBVE/Parsers/Route/BVE/CsvRwRouteParser.ApplyRouteData.cs b/source/OpenBVE/Parsers/Route/BVE/CsvRwRouteParser.ApplyRouteData.cs index d2d4a4555..d92314acf 100644 --- a/source/OpenBVE/Parsers/Route/BVE/CsvRwRouteParser.ApplyRouteData.cs +++ b/source/OpenBVE/Parsers/Route/BVE/CsvRwRouteParser.ApplyRouteData.cs @@ -173,7 +173,7 @@ private static void ApplyRouteData(string FileName, ref RouteData Data, bool Pre double StartingDistance = (double)i * Data.BlockInterval; double EndingDistance = StartingDistance + Data.BlockInterval; // normalize - World.Normalize(ref Direction.X, ref Direction.Y); + Direction.Normalize(); // track if (!PreviewOnly) { diff --git a/source/OpenBVE/Parsers/Route/BVE/CsvRwRouteParser.Functions.cs b/source/OpenBVE/Parsers/Route/BVE/CsvRwRouteParser.Functions.cs index 07ab111fa..b64a91a2a 100644 --- a/source/OpenBVE/Parsers/Route/BVE/CsvRwRouteParser.Functions.cs +++ b/source/OpenBVE/Parsers/Route/BVE/CsvRwRouteParser.Functions.cs @@ -1,4 +1,6 @@ using System; +using System.IO; +using System.Security.Cryptography; using OpenBveApi.Colors; namespace OpenBve @@ -179,5 +181,15 @@ private static void InvertLightness(byte[] bytes) } } } + + private static string GetChecksum(string file) + { + using (FileStream stream = File.OpenRead(file)) + { + SHA256Managed sha = new SHA256Managed(); + byte[] checksum = sha.ComputeHash(stream); + return BitConverter.ToString(checksum).Replace("-", string.Empty); + } + } } } diff --git a/source/OpenBVE/Parsers/Route/BVE/CsvRwRouteParser.RouteFixes.cs b/source/OpenBVE/Parsers/Route/BVE/CsvRwRouteParser.RouteFixes.cs index 66fafef7e..9691b583e 100644 --- a/source/OpenBVE/Parsers/Route/BVE/CsvRwRouteParser.RouteFixes.cs +++ b/source/OpenBVE/Parsers/Route/BVE/CsvRwRouteParser.RouteFixes.cs @@ -1,6 +1,4 @@ -using System.IO; - -namespace OpenBve +namespace OpenBve { internal partial class CsvRwRouteParser { @@ -10,110 +8,93 @@ private static void CheckRouteSpecificFixes(string FileName, ref RouteData Data, { return; } - FileInfo f = new FileInfo(FileName); - switch (f.Length) + string fileHash = GetChecksum(FileName); + switch (fileHash) { - case 63652: + case "F0D6AC84D94F63144F9ED5497CDF7697BDB45FF11223E2C001CF8BDA943D4E66": //Jundiai-Francisco Morato.rw - if (Game.RouteComment == "Jundiai-Francisco Morato\nExtensão Operacional Linha A\nCPTM - Cia. Paulista de Trens Metropolitanos") - { - Interface.AddMessage(Interface.MessageType.Warning, false, "Jundiai - Francisco Morato routefile detected- Applying fix to line endings."); - Data.LineEndingFix = true; - } + Interface.AddMessage(Interface.MessageType.Warning, false, "Jundiai - Francisco Morato routefile detected- Applying fix to line endings."); + Data.LineEndingFix = true; break; - case 67729: + case "7C21D03D487E36CCA2D9D1003732614BEEF682E421CB861E8632418CCD8D9D41": //kurra_fine1.csv - if (Game.RouteComment == "Kurrajong Line, 1963\r\nLocal\r\n2 Cars\r\nRichmond - Kurrajong\r\n\r\n(C) 2001 Spot") - { - Data.IgnorePitchRoll = true; - Interface.AddMessage(Interface.MessageType.Warning, false, "Richmond- Kurrajong routefile detected- Applying fix to yaw / roll."); - } + Data.IgnorePitchRoll = true; + Interface.AddMessage(Interface.MessageType.Warning, false, "Richmond- Kurrajong routefile detected- Applying fix to yaw / roll."); break; - case 70625: + case "FD99B78D5A1847070A3ED3DFEE3E3B6BD56CE578DE1CEB056AFDA799989BF14B": //FVES3.rw - if (Game.RouteComment == "Linie S3 der FVE\nFarge - Vegesack\nHöchstgeschwindigkeit\nder Strecke 80Km/h\nvon Hans-Martin Finken") - { - Data.IgnorePitchRoll = true; - Interface.AddMessage(Interface.MessageType.Warning, false, "Linie S3 (FVE) routefile detected- Applying fix to yaw / roll."); - } + Data.IgnorePitchRoll = true; + Interface.AddMessage(Interface.MessageType.Warning, false, "Linie S3 (FVE) routefile detected- Applying fix to yaw / roll."); break; - case 73262: + case "FF2B19C253C09CB541E57AB144CE67792E069F6FC2022D918ED7CBD9B59A1994": //kurrajong.csv - if (Game.RouteComment == "Kurrajong Line, 1953\r\nLocal\r\n2 Cars\r\nRichmond - Kurrajong\r\n\r\n(C) 2001 Spot") - { - Data.IgnorePitchRoll = true; - Interface.AddMessage(Interface.MessageType.Warning, false, "Richmond- Kurrajong routefile detected- Applying fix to yaw / roll."); - } + Data.IgnorePitchRoll = true; + Interface.AddMessage(Interface.MessageType.Warning, false, "Richmond- Kurrajong routefile detected- Applying fix to yaw / roll."); break; - case 75400: + case "6BFDD2746C56A64FCB5BE116C64530FFA0AD7C2889B8F77DCFFBFAD526070704": //camden_17.csv - if (Game.RouteComment == "Camden Line, 1963\r\nLocal\r\n2 Cars\r\nCampbelltown - Camden\r\n\r\n(C) 2001 Spot") - { - Data.IgnorePitchRoll = true; - Interface.AddMessage(Interface.MessageType.Warning, false, "Campbelltown- Camden routefile detected- Applying fix to yaw / roll."); - } + Data.IgnorePitchRoll = true; + Interface.AddMessage(Interface.MessageType.Warning, false, "Campbelltown- Camden routefile detected- Applying fix to yaw / roll."); break; - case 86723: + case "83EE400BA3A9FE0112AD5146D12968B8BE981B28E5D27449027EFBBB6583B68A": //Zwolle-Vlissingen.rw - if (Game.RouteComment == "Zwolle - Vlissingen \n(Part Two of Groningen-Vlissingen)\nEarly Evening Express") - { - Data.IgnorePitchRoll = true; - Interface.AddMessage(Interface.MessageType.Warning, false, "Zwolle - Vlissingen routefile detected- Applying fix to yaw / roll."); - } + Data.IgnorePitchRoll = true; + Interface.AddMessage(Interface.MessageType.Warning, false, "Zwolle - Vlissingen routefile detected- Applying fix to yaw / roll."); break; - case 101882: - case 101930: - case 102150: - case 102405: - case 102493: - case 102575: + case "54281BEA1964A11925E3B1E9F6CF8DBFF39156CBE6272977150A2B7F08799DD1": + case "D8B88EE63CF98D271EC8A75577539752E84A8B74A46EF1B49D57FCA6A53BDBB4": + case "FD63747CDD10E501D68E7A3FAB193B191F818CE950FB714B2FF79439A11AD427": + case "661C2E3EEE663192529A761631F02C0F0288340DCF69AB80622CA7E1B37BB126": + case "18A7BD8BB969E79D140112C4246B62EED8111A82509720C466174248674B20B6": + case "A29A91698F03A3C0F258A4E6C6DC72C4A15F3828849C929F25AF67FD0747A3FF": //Sanbie-663-bve4.csv - if (Game.RouteComment == "Linea Santhià-Biella completa Km 26.724. Partenza ore 13:22 Arrivo 13:51 Non ferma a Brianco. \r\n Line Santhià-Biella Departure: Santhià Arrival: Biella km 26.724. The train doesn't stops at Brianco.") - { - Data.IgnorePitchRoll = true; - Interface.AddMessage(Interface.MessageType.Warning, false, "Sanbie routefile detected- Applying fix to yaw / roll."); - } //Sanbie-663-nonstop-bve4.csv //Sanbie-663-rain-nonstop-bve4.csv - if (Game.RouteComment == "Linea Santhià-Biella completa Km 26.724. Partenza ore 07:37 Arrivo 07:58 Non fa fermate intermedie. \r\n Line Santhià-Biella Departure: Santhià Arrival: Biella km 26.724. Non stop train.") - { - Data.IgnorePitchRoll = true; - Interface.AddMessage(Interface.MessageType.Warning, false, "Sanbie routefile detected- Applying fix to yaw / roll."); - } //Sanbie-773-bve4.csv - if (Game.RouteComment == "Linea Santhià-Biella completa Km 26.724. Partenza ore 13:22 Arrivo 13:51 Non ferma a Brianco. \r\n Line Santhià-Biella Departure: Santhià Arrival: Biella km 26.724. The train doesn't stops at Brianco.Variant of Peter Shotz") - { - Data.IgnorePitchRoll = true; - Interface.AddMessage(Interface.MessageType.Warning, false, "Sanbie routefile detected- Applying fix to yaw / roll."); - } //Sanbie-773-nonstop-bve4.csv - if (Game.RouteComment == "Linea Santhià-Biella completa Km 26.724. Partenza ore 07:37 Arrivo 07:58 Non fa fermate intermedie. \r\n Line Santhià-Biella Departure: Santhià Arrival: Biella km 26.724. Non stop train. variant of Peter Shotz.") - { - Data.IgnorePitchRoll = true; - Interface.AddMessage(Interface.MessageType.Warning, false, "Sanbie routefile detected- Applying fix to yaw / roll."); - } //Sanbie-773-rain-nonstop-bve4.csv - if (Game.RouteComment == "Linea Santhià-Biella completa Km 26.724. Partenza ore 07:37 Arrivo 07:58 Non fa fermate intermedie. \r\n Line Santhià-Biella Departure: Santhià Arrival: Biella km 26.724. Non stop train. variant of Peter Shotz. Rain.") - { - Data.IgnorePitchRoll = true; - Interface.AddMessage(Interface.MessageType.Warning, false, "Sanbie routefile detected- Applying fix to yaw / roll."); - } + Data.IgnorePitchRoll = true; + Interface.AddMessage(Interface.MessageType.Warning, false, "Sanbie routefile detected- Applying fix to yaw / roll."); break; - case 14297: + case "DDBE5CFDE20F0AD7D03AFC187F70B1B6044B637109758B392B9EBA61FA169F69": //目蒲線普.csv - //Trackwork on exit to second station is broken without this - if (Game.RouteComment == "東急目蒲線\r\n奥沢-多摩川園\r\nver1.01\r\n\r\n(C)2004 こば") - { - if (Expressions[596].Text == ".rail 1;7.5:0") - { - Expressions[596].Text = ".rail 1;7.5;0"; - } - - if (Expressions[600].Text == ".rail 1;5.5:0") - { - Expressions[600].Text = ".rail 1;5.5;0"; - } - } + Expressions[596].Text = ".rail 1;7.5;0"; + Expressions[600].Text = ".rail 1;5.5;0"; + break; + case "2654DAF8B0FEEAAB928C8CC9E1D68279E361C7C2150D82860CCBB31DFD48A7C8": + case "D2382ED7D67DF6B9D6F32E8A89B1C21A7689EA18AEADED6BFE8EFE53BD78D2BD": + case "9062F55797622F66E2FED2C7E2A6EB6F661F6C8C6EE89A249AEAC0E6CB6B1FF9": + case "C2AC8A277D08AED9505A339759EDD5BBB0E4C541E0226EFFD8615DCBDCE8A446": + case "1175ABDC20934050DBF3DF6E186874D9A7315358235D2CE4CC3096512BF17514": + case "84F32BB8CFAB5D63E611C261822A55F22AE444401A04B826E2529449E3431728": + //Elephant to Harrow 1972Mk2TS.csv + //Elephant to Queen's Park 1972Mk2TS.csv + //Queen's Park to Harrow SILVERLINK Cl313.csv + //Followed by BVE4 versions, same filenames + CylinderHack = true; + Interface.AddMessage(Interface.MessageType.Warning, false, "Bakerloo v3 routefile detected- Applying cylinder hack."); + break; + case "AA6528402BE457A20DF77A8B7CAFBC2580F3F8326BB56B9A691A4558174AE152": + case "2EB087770AEC2C6A0F2AADC0A8D531117502B1AB33E94CBBC11940DA4FFF4A30": + //V.2.1.1 Aldwych BVE4- no fog.csv + //V.2.1.1 Aldwych BVE4.csv + CylinderHack = true; + Interface.AddMessage(Interface.MessageType.Warning, false, "Aldwych v2.1.1 routefile detected- Applying cylinder hack."); + break; + case "9D87539BAC426DE5B1ECB638A935EF8AC37B31AD6B16D645B1D3F2A7E2D1B23F": + case "078324311EC9048F313513849B5EBDCF3C3CAF193520F947258324FEDDD6A2BD": + case "939E18B5AB5645FF25CF5C4CE72D0F0567C9A0C9ABDF6D21AB5DC3278D180032": + case "3E673AAD446439A42FA81AA37D6C51FE69DC32C93D2FDC1EE3B8DA73EDC61B81": + case "60F9CF7F40E21808072DAEF3F316ECB0BEA2AA224F5AD414BCA084F6AE0BD1E8": + case "179EB3C4F653DBC8FF98CF1F6F44693DE5AE6EB8A196CE89D2B5681BA6D972EB": + //Barons Ct to Arnos 38ts.csv + //Barons Ct to Arnos Peak.csv + //Barons Ct to Arnos Weedkill.csv + //Barons Ct to Arnos.csv + //Barons Ct to Wood Green.csv + //Hyde Pk Cnr to Wood Green.csv + CylinderHack = true; + Interface.AddMessage(Interface.MessageType.Warning, false, "Picadilly v5.2 routefile detected- Applying cylinder hack."); break; } } diff --git a/source/OpenBVE/Parsers/Route/BVE/CsvRwRouteParser.cs b/source/OpenBVE/Parsers/Route/BVE/CsvRwRouteParser.cs index 930d4c3b3..1abb88576 100644 --- a/source/OpenBVE/Parsers/Route/BVE/CsvRwRouteParser.cs +++ b/source/OpenBVE/Parsers/Route/BVE/CsvRwRouteParser.cs @@ -12,6 +12,7 @@ internal partial class CsvRwRouteParser { internal static string SoundPath; internal static string TrainPath; internal static string CompatibilityFolder; + internal static bool CylinderHack = false; // parse route internal static void ParseRoute(string FileName, bool IsRW, System.Text.Encoding Encoding, string trainPath, string objectPath, string soundPath, bool PreviewOnly) { @@ -1016,6 +1017,8 @@ private static void ParseRouteForData(string FileName, bool IsRW, System.Text.En bool ValueBasedSections = false; double progressFactor = Expressions.Length == 0 ? 0.3333 : 0.3333 / (double)Expressions.Length; // process non-track namespaces + //Check for any special-cased fixes we might need + CheckRouteSpecificFixes(FileName, ref Data, ref Expressions); for (int j = 0; j < Expressions.Length; j++) { Loading.RouteProgress = (double)j * progressFactor; if ((j & 255) == 0) { @@ -2333,22 +2336,26 @@ private static void ParseRouteForData(string FileName, bool IsRW, System.Text.En } if (!System.IO.File.Exists(f) && !System.IO.Path.HasExtension(f)) { + string ff; bool notFound = false; while (true) { - f = Path.CombineFile(ObjectPath, f + ".x"); - if (System.IO.File.Exists(f)) + ff = Path.CombineFile(ObjectPath, f + ".x"); + if (System.IO.File.Exists(ff)) { + f = ff; break; } - f = Path.CombineFile(ObjectPath, f + ".csv"); - if (System.IO.File.Exists(f)) + ff = Path.CombineFile(ObjectPath, f + ".csv"); + if (System.IO.File.Exists(ff)) { + f = ff; break; } - f = Path.CombineFile(ObjectPath, f + ".b3d"); - if (System.IO.File.Exists(f)) + ff = Path.CombineFile(ObjectPath, f + ".b3d"); + if (System.IO.File.Exists(ff)) { + f = ff; break; } Interface.AddMessage(Interface.MessageType.Error, false, "SignalFileWithoutExtension does not exist in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); @@ -2377,13 +2384,46 @@ private static void ParseRouteForData(string FileName, bool IsRW, System.Text.En } else { f = Arguments[1]; - LocateObject(ref f, ObjectPath); - Signal.GlowObject = ObjectManager.LoadStaticObject(f, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); - if (Signal.GlowObject != null) { - Signal.GlowTextures = LoadAllTextures(f, true); - for (int p = 0; p < Signal.GlowObject.Mesh.Materials.Length; p++) { - Signal.GlowObject.Mesh.Materials[p].BlendMode = World.MeshMaterialBlendMode.Additive; - Signal.GlowObject.Mesh.Materials[p].GlowAttenuationData = World.GetGlowAttenuationData(200.0, World.GlowAttenuationMode.DivisionExponent4); + bool notFound = false; + if (!System.IO.File.Exists(f) && !System.IO.Path.HasExtension(f)) + { + string ff; + while (true) + { + ff = Path.CombineFile(ObjectPath, f + ".x"); + if (System.IO.File.Exists(ff)) + { + f = ff; + break; + } + ff = Path.CombineFile(ObjectPath, f + ".csv"); + if (System.IO.File.Exists(ff)) + { + f = ff; + break; + } + ff = Path.CombineFile(ObjectPath, f + ".b3d"); + if (System.IO.File.Exists(ff)) + { + f = ff; + break; + } + Interface.AddMessage(Interface.MessageType.Error, false, "GlowFileWithoutExtension does not exist in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); + notFound = true; + break; + } + } + if (!notFound) + { + Signal.GlowObject = ObjectManager.LoadStaticObject(f, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); + if (Signal.GlowObject != null) + { + Signal.GlowTextures = LoadAllTextures(f, true); + for (int p = 0; p < Signal.GlowObject.Mesh.Materials.Length; p++) + { + Signal.GlowObject.Mesh.Materials[p].BlendMode = World.MeshMaterialBlendMode.Additive; + Signal.GlowObject.Mesh.Materials[p].GlowAttenuationData = World.GetGlowAttenuationData(200.0, World.GlowAttenuationMode.DivisionExponent4); + } } } } @@ -2574,8 +2614,7 @@ private static void ParseRouteForData(string FileName, bool IsRW, System.Text.En } } } - //Check for any special-cased fixes we might need - CheckRouteSpecificFixes(FileName, ref Data, ref Expressions); + // process track namespace for (int j = 0; j < Expressions.Length; j++) { Loading.RouteProgress = 0.3333 + (double)j * progressFactor; @@ -3765,12 +3804,14 @@ private static void ParseRouteForData(string FileName, bool IsRW, System.Text.En Interface.AddMessage(Interface.MessageType.Error, false, "ArrivalTime is invalid in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); arr = -1.0; } - } else if (!Interface.TryParseTime(Arguments[1], out arr)) { + } else if(Arguments[1].Length == 1 && Arguments[1][0] == '.') + { /* Treat a single period as a blank space */ } + else if (!Interface.TryParseTime(Arguments[1], out arr)) { Interface.AddMessage(Interface.MessageType.Error, false, "ArrivalTime is invalid in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); arr = -1.0; } } - if (Arguments.Length >= 3 && Arguments[2].Length > 0) { + if (Arguments.Length >= 3 && (Arguments[2].Length > 0)) { if (string.Equals(Arguments[2], "T", StringComparison.OrdinalIgnoreCase) | string.Equals(Arguments[2], "=", StringComparison.OrdinalIgnoreCase)) { Game.Stations[CurrentStation].Type = StationType.Terminal; } else if (Arguments[2].StartsWith("T:", StringComparison.InvariantCultureIgnoreCase)) { @@ -3787,7 +3828,9 @@ private static void ParseRouteForData(string FileName, bool IsRW, System.Text.En Interface.AddMessage(Interface.MessageType.Error, false, "DepartureTime is invalid in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); dep = -1.0; } - } else if (!Interface.TryParseTime(Arguments[2], out dep)) { + } else if(Arguments[2].Length == 1 && Arguments[2][0] == '.') + { /* Treat a single period as a blank space */ } + else if (!Interface.TryParseTime(Arguments[2], out dep)) { Interface.AddMessage(Interface.MessageType.Error, false, "DepartureTime is invalid in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); dep = -1.0; } diff --git a/source/OpenBVE/Parsers/Train/TrainDatParser.Formats.cs b/source/OpenBVE/Parsers/Train/TrainDatParser.Formats.cs index c42b862a3..7a58f0563 100644 --- a/source/OpenBVE/Parsers/Train/TrainDatParser.Formats.cs +++ b/source/OpenBVE/Parsers/Train/TrainDatParser.Formats.cs @@ -14,6 +14,8 @@ private enum TrainDatFormats BVE1220000 = 2, /// The train.dat was written for BVE 2 BVE2000000 = 3, + /// The train.dat was written for BVE 2.0.6 + BVE2060000 = 4, /// The train.dat was written for openBVE openBVE = 3, } diff --git a/source/OpenBVE/Parsers/Train/TrainDatParser.cs b/source/OpenBVE/Parsers/Train/TrainDatParser.cs index 18664188c..e3b2d213a 100644 --- a/source/OpenBVE/Parsers/Train/TrainDatParser.cs +++ b/source/OpenBVE/Parsers/Train/TrainDatParser.cs @@ -56,6 +56,9 @@ internal static void ParseTrainData(string FileName, System.Text.Encoding Encodi case "bve2000000": currentFormat = TrainDatFormats.BVE2000000; break; + case "bve2060000": + currentFormat = TrainDatFormats.BVE2060000; + break; case "openbve": currentFormat = TrainDatFormats.openBVE; break; @@ -461,7 +464,7 @@ internal static void ParseTrainData(string FileName, System.Text.Encoding Encodi switch (n) { case 0: Train.Handles.SingleHandle = a == 1; break; case 1: - if (a >= 0) + if (a > 0) { powerNotches = a; } @@ -472,7 +475,7 @@ internal static void ParseTrainData(string FileName, System.Text.Encoding Encodi } break; case 2: - if (a >= 0) + if (a > 0) { brakeNotches = a; } @@ -486,7 +489,7 @@ internal static void ParseTrainData(string FileName, System.Text.Encoding Encodi powerReduceSteps = a; break; case 4: - if (a > 0 || a < 3) + if (a < 0 || a > 3) { Interface.AddMessage(Interface.MessageType.Error, false, "EbHandleBehaviour is invalid at line " + (i + 1).ToString(Culture) + " in " + FileName); break; diff --git a/source/OpenBVE/Simulation/TrainManager/Car/Car.cs b/source/OpenBVE/Simulation/TrainManager/Car/Car.cs index 27ae669ae..3722e6875 100644 --- a/source/OpenBVE/Simulation/TrainManager/Car/Car.cs +++ b/source/OpenBVE/Simulation/TrainManager/Car/Car.cs @@ -966,7 +966,7 @@ internal void UpdateTopplingCantAndSpring(double TimeElapsed) * line is to the rails, the more flange noise there will be. * */ Vector3 d = FrontAxle.Follower.WorldPosition - RearAxle.Follower.WorldPosition; - World.Normalize(ref d.X, ref d.Y, ref d.Z); + d.Normalize(); double b0 = d.X * RearAxle.Follower.WorldSide.X + d.Y * RearAxle.Follower.WorldSide.Y + d.Z * RearAxle.Follower.WorldSide.Z; double b1 = d.X * FrontAxle.Follower.WorldSide.X + d.Y * FrontAxle.Follower.WorldSide.Y + d.Z * FrontAxle.Follower.WorldSide.Z; double spd = Math.Abs(Specs.CurrentSpeed); diff --git a/source/OpenBVE/Simulation/TrainManager/Train/Train.cs b/source/OpenBVE/Simulation/TrainManager/Train/Train.cs index df8e5dd1e..2db4c8e8a 100644 --- a/source/OpenBVE/Simulation/TrainManager/Train/Train.cs +++ b/source/OpenBVE/Simulation/TrainManager/Train/Train.cs @@ -399,7 +399,7 @@ internal void Update(double TimeElapsed) } if (AI != null) { - AI.Trigger(this, TimeElapsed); + AI.Trigger(TimeElapsed); } } else if (State == TrainState.Bogus) @@ -407,7 +407,7 @@ internal void Update(double TimeElapsed) // bogus train if (AI != null) { - AI.Trigger(this, TimeElapsed); + AI.Trigger(TimeElapsed); } } } diff --git a/source/OpenBVE/System/Functions/Illustrations.cs b/source/OpenBVE/System/Functions/Illustrations.cs index 98901f85e..8672434d5 100644 --- a/source/OpenBVE/System/Functions/Illustrations.cs +++ b/source/OpenBVE/System/Functions/Illustrations.cs @@ -1,5 +1,6 @@ using System; using System.Drawing; +using OpenBveApi; namespace OpenBve { internal static class Illustrations { @@ -448,7 +449,7 @@ internal static Bitmap CreateRouteGradientProfile(int Width, int Height, bool in if (Game.Stations[e.StationIndex].Name != string.Empty) { bool stop = Game.PlayerStopsAtStation(e.StationIndex); - if (Interface.IsJapanese(Game.Stations[e.StationIndex].Name)) + if (Game.Stations[e.StationIndex].Name.IsJapanese()) { m.Alignment = StringAlignment.Near; m.LineAlignment = StringAlignment.Near; diff --git a/source/OpenBVE/System/GameWindow.cs b/source/OpenBVE/System/GameWindow.cs index c65c23cae..53f6da76b 100644 --- a/source/OpenBVE/System/GameWindow.cs +++ b/source/OpenBVE/System/GameWindow.cs @@ -106,7 +106,7 @@ protected override void OnRenderFrame(FrameEventArgs e) } if (World.CameraRestriction == World.CameraRestrictionMode.NotAvailable) { - World.UpdateDriverBody(TimeElapsed); + World.CurrentDriverBody.Update(TimeElapsed); } //Check if we are running at an accelerated time factor- //Camera motion speed should be the same whatever the game speed is @@ -273,6 +273,19 @@ protected override void OnUpdateFrame(FrameEventArgs e) protected override void OnResize(EventArgs e) { + if(Width == 0 && Height == 0) + { + /* + * HACK: Don't resize if minimized + * + * Unsure if this is an openTK bug, Windows or what + * but setting our width / height to zero breaks + * stuff..... + */ + Screen.Minimized = true; + return; + } + Screen.Minimized = false; Screen.WindowResize(Width,Height); } diff --git a/source/OpenBVE/System/Input/Commands.CommandInfo.cs b/source/OpenBVE/System/Input/Commands.CommandInfo.cs new file mode 100644 index 000000000..560c8f5c5 --- /dev/null +++ b/source/OpenBVE/System/Input/Commands.CommandInfo.cs @@ -0,0 +1,166 @@ +namespace OpenBve +{ + internal static partial class Interface + { + /// Information on an in-game command + internal struct CommandInfo + { + /// The actual command to be performed + internal Command Command; + /// Whether this is a digital or analog control + internal CommandType Type; + /// The command name + internal string Name; + /// The command's description + internal string Description; + internal CommandInfo(Command Command, CommandType Type, string Name) + { + this.Command = Command; + this.Type = Type; + this.Name = Name; + this.Description = "N/A"; + } + } + + /// Gets the command info for the specified command + /// The array of command infos + /// The command + /// The command info for Value, or a new command info if this is not in the array + internal static CommandInfo TryGetInfo(this CommandInfo[] commandInfos, Command Value) + { + for (int i = 0; i < commandInfos.Length; i++) { + if (commandInfos[i].Command == Value) + { + return commandInfos[i]; + } + } + return new CommandInfo(Value, CommandType.Digital, "N/A"); + } + + /// Contains the list of all known command infos + internal static CommandInfo[] CommandInfos = { + new CommandInfo(Command.PowerIncrease, CommandType.Digital, "POWER_INCREASE"), + new CommandInfo(Command.PowerDecrease, CommandType.Digital, "POWER_DECREASE"), + new CommandInfo(Command.PowerHalfAxis, CommandType.AnalogHalf, "POWER_HALFAXIS"), + new CommandInfo(Command.PowerFullAxis, CommandType.AnalogFull, "POWER_FULLAXIS"), + new CommandInfo(Command.BrakeDecrease, CommandType.Digital, "BRAKE_DECREASE"), + new CommandInfo(Command.BrakeIncrease, CommandType.Digital, "BRAKE_INCREASE"), + new CommandInfo(Command.LocoBrakeDecrease, CommandType.Digital, "LOCOBRAKE_DECREASE"), + new CommandInfo(Command.LocoBrakeIncrease, CommandType.Digital, "LOCOBRAKE_INCREASE"), + new CommandInfo(Command.BrakeHalfAxis, CommandType.AnalogHalf, "BRAKE_HALFAXIS"), + new CommandInfo(Command.BrakeFullAxis, CommandType.AnalogFull, "BRAKE_FULLAXIS"), + new CommandInfo(Command.BrakeEmergency, CommandType.Digital, "BRAKE_EMERGENCY"), + new CommandInfo(Command.SinglePower, CommandType.Digital, "SINGLE_POWER"), + new CommandInfo(Command.SingleNeutral, CommandType.Digital, "SINGLE_NEUTRAL"), + new CommandInfo(Command.SingleBrake, CommandType.Digital, "SINGLE_BRAKE"), + new CommandInfo(Command.SingleEmergency, CommandType.Digital, "SINGLE_EMERGENCY"), + new CommandInfo(Command.SingleFullAxis, CommandType.AnalogFull, "SINGLE_FULLAXIS"), + new CommandInfo(Command.ReverserForward, CommandType.Digital, "REVERSER_FORWARD"), + new CommandInfo(Command.ReverserBackward, CommandType.Digital, "REVERSER_BACKWARD"), + new CommandInfo(Command.ReverserFullAxis, CommandType.AnalogFull, "REVERSER_FULLAXIS"), + new CommandInfo(Command.DoorsLeft, CommandType.Digital, "DOORS_LEFT"), + new CommandInfo(Command.DoorsRight, CommandType.Digital, "DOORS_RIGHT"), + new CommandInfo(Command.HornPrimary, CommandType.Digital, "HORN_PRIMARY"), + new CommandInfo(Command.HornSecondary, CommandType.Digital, "HORN_SECONDARY"), + new CommandInfo(Command.HornMusic, CommandType.Digital, "HORN_MUSIC"), + new CommandInfo(Command.DeviceConstSpeed, CommandType.Digital, "DEVICE_CONSTSPEED"), + +//We only want to mark these as obsolete for new users of the API +#pragma warning disable 618 + new CommandInfo(Command.SecurityS, CommandType.Digital, "SECURITY_S"), + new CommandInfo(Command.SecurityA1, CommandType.Digital, "SECURITY_A1"), + new CommandInfo(Command.SecurityA2, CommandType.Digital, "SECURITY_A2"), + new CommandInfo(Command.SecurityB1, CommandType.Digital, "SECURITY_B1"), + new CommandInfo(Command.SecurityB2, CommandType.Digital, "SECURITY_B2"), + new CommandInfo(Command.SecurityC1, CommandType.Digital, "SECURITY_C1"), + new CommandInfo(Command.SecurityC2, CommandType.Digital, "SECURITY_C2"), + new CommandInfo(Command.SecurityD, CommandType.Digital, "SECURITY_D"), + new CommandInfo(Command.SecurityE, CommandType.Digital, "SECURITY_E"), + new CommandInfo(Command.SecurityF, CommandType.Digital, "SECURITY_F"), + new CommandInfo(Command.SecurityG, CommandType.Digital, "SECURITY_G"), + new CommandInfo(Command.SecurityH, CommandType.Digital, "SECURITY_H"), + new CommandInfo(Command.SecurityI, CommandType.Digital, "SECURITY_I"), + new CommandInfo(Command.SecurityJ, CommandType.Digital, "SECURITY_J"), + new CommandInfo(Command.SecurityK, CommandType.Digital, "SECURITY_K"), + new CommandInfo(Command.SecurityL, CommandType.Digital, "SECURITY_L"), + new CommandInfo(Command.SecurityM, CommandType.Digital, "SECURITY_M"), + new CommandInfo(Command.SecurityN, CommandType.Digital, "SECURITY_N"), + new CommandInfo(Command.SecurityO, CommandType.Digital, "SECURITY_O"), + new CommandInfo(Command.SecurityP, CommandType.Digital, "SECURITY_P"), +#pragma warning restore 618 + + //Common Keys + new CommandInfo(Command.WiperSpeedUp, CommandType.Digital, "WIPER_SPEED_UP"), + new CommandInfo(Command.WiperSpeedDown, CommandType.Digital, "WIPER_SPEED_DOWN"), + new CommandInfo(Command.FillFuel, CommandType.Digital, "FILL_FUEL"), + new CommandInfo(Command.Headlights, CommandType.Digital, "HEADLIGHTS"), + //Steam locomotive + new CommandInfo(Command.LiveSteamInjector, CommandType.Digital, "LIVE_STEAM_INJECTOR"), + new CommandInfo(Command.ExhaustSteamInjector, CommandType.Digital, "EXHAUST_STEAM_INJECTOR"), + new CommandInfo(Command.IncreaseCutoff, CommandType.Digital, "INCREASE_CUTOFF"), + new CommandInfo(Command.DecreaseCutoff, CommandType.Digital, "DECREASE_CUTOFF"), + new CommandInfo(Command.Blowers, CommandType.Digital, "BLOWERS"), + //Diesel Locomotive + new CommandInfo(Command.EngineStart, CommandType.Digital, "ENGINE_START"), + new CommandInfo(Command.EngineStop, CommandType.Digital, "ENGINE_STOP"), + new CommandInfo(Command.GearUp, CommandType.Digital, "GEAR_UP"), + new CommandInfo(Command.GearDown, CommandType.Digital, "GEAR_DOWN"), + + //Electric Locomotive + new CommandInfo(Command.RaisePantograph, CommandType.Digital, "RAISE_PANTOGRAPH"), + new CommandInfo(Command.LowerPantograph, CommandType.Digital, "LOWER_PANTOGRAPH"), + new CommandInfo(Command.MainBreaker, CommandType.Digital, "MAIN_BREAKER"), + + //Simulation controls + new CommandInfo(Command.CameraInterior, CommandType.Digital, "CAMERA_INTERIOR"), + new CommandInfo(Command.CameraExterior, CommandType.Digital, "CAMERA_EXTERIOR"), + new CommandInfo(Command.CameraTrack, CommandType.Digital, "CAMERA_TRACK"), + new CommandInfo(Command.CameraFlyBy, CommandType.Digital, "CAMERA_FLYBY"), + new CommandInfo(Command.CameraMoveForward, CommandType.AnalogHalf, "CAMERA_MOVE_FORWARD"), + new CommandInfo(Command.CameraMoveBackward, CommandType.AnalogHalf, "CAMERA_MOVE_BACKWARD"), + new CommandInfo(Command.CameraMoveLeft, CommandType.AnalogHalf, "CAMERA_MOVE_LEFT"), + new CommandInfo(Command.CameraMoveRight, CommandType.AnalogHalf, "CAMERA_MOVE_RIGHT"), + new CommandInfo(Command.CameraMoveUp, CommandType.AnalogHalf, "CAMERA_MOVE_UP"), + new CommandInfo(Command.CameraMoveDown, CommandType.AnalogHalf, "CAMERA_MOVE_DOWN"), + new CommandInfo(Command.CameraRotateLeft, CommandType.AnalogHalf, "CAMERA_ROTATE_LEFT"), + new CommandInfo(Command.CameraRotateRight, CommandType.AnalogHalf, "CAMERA_ROTATE_RIGHT"), + new CommandInfo(Command.CameraRotateUp, CommandType.AnalogHalf, "CAMERA_ROTATE_UP"), + new CommandInfo(Command.CameraRotateDown, CommandType.AnalogHalf, "CAMERA_ROTATE_DOWN"), + new CommandInfo(Command.CameraRotateCCW, CommandType.AnalogHalf, "CAMERA_ROTATE_CCW"), + new CommandInfo(Command.CameraRotateCW, CommandType.AnalogHalf, "CAMERA_ROTATE_CW"), + new CommandInfo(Command.CameraZoomIn, CommandType.AnalogHalf, "CAMERA_ZOOM_IN"), + new CommandInfo(Command.CameraZoomOut, CommandType.AnalogHalf, "CAMERA_ZOOM_OUT"), + new CommandInfo(Command.CameraPreviousPOI, CommandType.Digital, "CAMERA_POI_PREVIOUS"), + new CommandInfo(Command.CameraNextPOI, CommandType.Digital, "CAMERA_POI_NEXT"), + new CommandInfo(Command.CameraReset, CommandType.Digital, "CAMERA_RESET"), + new CommandInfo(Command.CameraRestriction, CommandType.Digital, "CAMERA_RESTRICTION"), + new CommandInfo(Command.TimetableToggle, CommandType.Digital, "TIMETABLE_TOGGLE"), + new CommandInfo(Command.TimetableUp, CommandType.AnalogHalf, "TIMETABLE_UP"), + new CommandInfo(Command.TimetableDown, CommandType.AnalogHalf, "TIMETABLE_DOWN"), + new CommandInfo(Command.MenuActivate, CommandType.Digital, "MENU_ACTIVATE"), + new CommandInfo(Command.MenuUp, CommandType.Digital, "MENU_UP"), + new CommandInfo(Command.MenuDown, CommandType.Digital, "MENU_DOWN"), + new CommandInfo(Command.MenuEnter, CommandType.Digital, "MENU_ENTER"), + new CommandInfo(Command.MenuBack, CommandType.Digital, "MENU_BACK"), + new CommandInfo(Command.MiscClock, CommandType.Digital, "MISC_CLOCK"), + new CommandInfo(Command.MiscSpeed, CommandType.Digital, "MISC_SPEED"), + new CommandInfo(Command.MiscGradient, CommandType.Digital, "MISC_GRADIENT"), + new CommandInfo(Command.MiscFps, CommandType.Digital, "MISC_FPS"), + new CommandInfo(Command.MiscAI, CommandType.Digital, "MISC_AI"), + new CommandInfo(Command.MiscFullscreen, CommandType.Digital, "MISC_FULLSCREEN"), + new CommandInfo(Command.MiscMute, CommandType.Digital, "MISC_MUTE"), + new CommandInfo(Command.MiscPause, CommandType.Digital, "MISC_PAUSE"), + new CommandInfo(Command.MiscTimeFactor, CommandType.Digital, "MISC_TIMEFACTOR"), + new CommandInfo(Command.MiscQuit, CommandType.Digital, "MISC_QUIT"), + new CommandInfo(Command.MiscInterfaceMode, CommandType.Digital, "MISC_INTERFACE"), + new CommandInfo(Command.MiscBackfaceCulling, CommandType.Digital, "MISC_BACKFACE"), + new CommandInfo(Command.MiscCPUMode, CommandType.Digital, "MISC_CPUMODE"), + new CommandInfo(Command.DebugWireframe, CommandType.Digital, "DEBUG_WIREFRAME"), + new CommandInfo(Command.DebugNormals, CommandType.Digital, "DEBUG_NORMALS"), + new CommandInfo(Command.DebugBrakeSystems, CommandType.Digital, "DEBUG_BRAKE"), + new CommandInfo(Command.DebugATS, CommandType.Digital, "DEBUG_ATS"), + new CommandInfo(Command.RouteInformation, CommandType.Digital, "ROUTE_INFORMATION"), + new CommandInfo(Command.ShowEvents, CommandType.Digital, "SHOW_EVENTS"), + }; + } +} diff --git a/source/OpenBVE/System/Input/Commands.cs b/source/OpenBVE/System/Input/Commands.cs index c942e0909..b108968ed 100644 --- a/source/OpenBVE/System/Input/Commands.cs +++ b/source/OpenBVE/System/Input/Commands.cs @@ -287,25 +287,7 @@ internal enum CommandType AnalogFull } - /// Information on an in-game command - internal struct CommandInfo - { - /// The actual command to be performed - internal Command Command; - /// Whether this is a digital or analog control - internal CommandType Type; - /// The command name - internal string Name; - /// The command's description - internal string Description; - internal CommandInfo(Command Command, CommandType Type, string Name) - { - this.Command = Command; - this.Type = Type; - this.Name = Name; - this.Description = "N/A"; - } - } + /// Converts the specified security command to a virtual key. /// Virtual key for plugins. diff --git a/source/OpenBVE/System/Input/Controls.Control.cs b/source/OpenBVE/System/Input/Controls.Control.cs new file mode 100644 index 000000000..35d3e3474 --- /dev/null +++ b/source/OpenBVE/System/Input/Controls.Control.cs @@ -0,0 +1,61 @@ +using System; +using OpenTK.Input; + +namespace OpenBve +{ + internal static partial class Interface + { + /// The method by which a control is activated + internal enum ControlMethod + { + /// This control is invalid + Invalid = 0, + /// This control is activated using a keyboard button + Keyboard = 1, + /// This control is activated using a joystick axis or button + Joystick = 2, + /// This control is activated using the RailDriver cab controller + RailDriver = 3 + } + + /// Keyboard modifiers + [Flags] + internal enum KeyboardModifier + { + None = 0, + Shift = 1, + Ctrl = 2, + Alt = 4 + } + + /// The joystick component which will activate this control + internal enum JoystickComponent { Invalid, Axis, FullAxis, Ball, Hat, Button } + + /// The possible states of a digital control (button) + internal enum DigitalControlState + { + ReleasedAcknowledged = 0, + Released = 1, + Pressed = 2, + PressedAcknowledged = 3 + } + + /// Information on an in-game control + internal struct Control + { + /// The internal command which this control performs + internal Command Command; + internal CommandType InheritedType; + internal ControlMethod Method; + internal KeyboardModifier Modifier; + internal int Device; + internal JoystickComponent Component; + internal int Element; + internal int Direction; + internal DigitalControlState DigitalState; + internal double AnalogState; + internal Key Key; + internal string LastState; + } + } +} diff --git a/source/OpenBVE/System/Input/Controls.cs b/source/OpenBVE/System/Input/Controls.cs index 35d3e3474..5e44402c1 100644 --- a/source/OpenBVE/System/Input/Controls.cs +++ b/source/OpenBVE/System/Input/Controls.cs @@ -1,61 +1,325 @@ using System; +using System.Globalization; +using System.Windows.Forms; using OpenTK.Input; namespace OpenBve { - internal static partial class Interface - { - /// The method by which a control is activated - internal enum ControlMethod - { - /// This control is invalid - Invalid = 0, - /// This control is activated using a keyboard button - Keyboard = 1, - /// This control is activated using a joystick axis or button - Joystick = 2, - /// This control is activated using the RailDriver cab controller - RailDriver = 3 - } - - /// Keyboard modifiers - [Flags] - internal enum KeyboardModifier - { - None = 0, - Shift = 1, - Ctrl = 2, - Alt = 4 - } - - /// The joystick component which will activate this control - internal enum JoystickComponent { Invalid, Axis, FullAxis, Ball, Hat, Button } - - /// The possible states of a digital control (button) - internal enum DigitalControlState - { - ReleasedAcknowledged = 0, - Released = 1, - Pressed = 2, - PressedAcknowledged = 3 - } - - /// Information on an in-game control - internal struct Control - { - /// The internal command which this control performs - internal Command Command; - internal CommandType InheritedType; - internal ControlMethod Method; - internal KeyboardModifier Modifier; - internal int Device; - internal JoystickComponent Component; - internal int Element; - internal int Direction; - internal DigitalControlState DigitalState; - internal double AnalogState; - internal Key Key; - internal string LastState; - } - } + internal static partial class Interface + { + /// The list of current in-game controls + internal static Control[] CurrentControls = { }; + + /// Saves a control configuration to disk + /// An absolute file path if we are exporting the controls, or a null reference to save to the default configuration location + /// The list of controls to save + internal static void SaveControls(string FileOrNull, Control[] controlsToSave) { + CultureInfo Culture = CultureInfo.InvariantCulture; + System.Text.StringBuilder Builder = new System.Text.StringBuilder(); + Builder.AppendLine("; Current control configuration"); + Builder.AppendLine("; ============================="); + Builder.AppendLine("; This file was automatically generated. Please modify only if you know what you're doing."); + Builder.AppendLine("; This file is INCOMPATIBLE with versions older than 1.4.4."); + Builder.AppendLine(); + for (int i = 0; i < controlsToSave.Length; i++) { + CommandInfo Info = CommandInfos.TryGetInfo(controlsToSave[i].Command); + Builder.Append(Info.Name + ", "); + switch (controlsToSave[i].Method) { + case ControlMethod.Keyboard: + Builder.Append("keyboard, " + controlsToSave[i].Key + ", " + ((int)controlsToSave[i].Modifier).ToString(Culture)); + break; + case ControlMethod.Joystick: + Builder.Append("joystick, " + controlsToSave[i].Device.ToString(Culture) + ", "); + switch (controlsToSave[i].Component) { + case JoystickComponent.Axis: + Builder.Append("axis, " + controlsToSave[i].Element.ToString(Culture) + ", " + controlsToSave[i].Direction.ToString(Culture)); + break; + case JoystickComponent.Ball: + Builder.Append("ball, " + controlsToSave[i].Element.ToString(Culture) + ", " + controlsToSave[i].Direction.ToString(Culture)); + break; + case JoystickComponent.Hat: + Builder.Append("hat, " + controlsToSave[i].Element.ToString(Culture) + ", " + controlsToSave[i].Direction.ToString(Culture)); + break; + case JoystickComponent.Button: + Builder.Append("button, " + controlsToSave[i].Element.ToString(Culture)); + break; + default: + Builder.Append("invalid"); + break; + } + break; + case ControlMethod.RailDriver: + Builder.Append("raildriver, 0, "); + switch (controlsToSave[i].Component) { + case JoystickComponent.Axis: + Builder.Append("axis, " + controlsToSave[i].Element.ToString(Culture) + ", " + controlsToSave[i].Direction.ToString(Culture)); + break; + case JoystickComponent.Button: + Builder.Append("button, " + controlsToSave[i].Element.ToString(Culture)); + break; + default: + Builder.Append("invalid"); + break; + } + break; + } + Builder.Append("\n"); + } + string File; + if (FileOrNull == null) { + File = OpenBveApi.Path.CombineFile(Program.FileSystem.SettingsFolder, "1.5.0/controls.cfg"); + } else { + File = FileOrNull; + } + System.IO.File.WriteAllText(File, Builder.ToString(), new System.Text.UTF8Encoding(true)); + } + + /// Loads the current controls from the controls.cfg file + /// An absolute path reference to a saved controls.cfg file, or a null reference to check the default locations + /// The current controls array + internal static void LoadControls(string FileOrNull, out Control[] Controls) + { + string File; + bool ControlsReset = false; + if (FileOrNull == null) { + File = OpenBveApi.Path.CombineFile(Program.FileSystem.SettingsFolder, "1.5.0/controls.cfg"); + if (!System.IO.File.Exists(File)) + { + File = OpenBveApi.Path.CombineFile(Program.FileSystem.SettingsFolder, "controls.cfg"); + } + if (!System.IO.File.Exists(File)) { + //Load the default key assignments if the user settings don't exist + File = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Controls"), "Default keyboard assignment.controls"); + if (!System.IO.File.Exists(File)) + { + MessageBox.Show(Interface.GetInterfaceString("errors_warning") + Environment.NewLine + Interface.GetInterfaceString("errors_controls_missing"), + Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); + Controls = new Control[0]; + return; + } + } + } else { + File = FileOrNull; + } + Controls = new Control[256]; + int Length = 0; + CultureInfo Culture = CultureInfo.InvariantCulture; + if (System.IO.File.Exists(File)) { + string[] Lines = System.IO.File.ReadAllLines(File, new System.Text.UTF8Encoding()); + for (int i = 0; i < Lines.Length; i++) { + Lines[i] = Lines[i].Trim(); + if (Lines[i].Length != 0 && !Lines[i].StartsWith(";", StringComparison.OrdinalIgnoreCase)) { + string[] Terms = Lines[i].Split(','); + for (int j = 0; j < Terms.Length; j++) { + Terms[j] = Terms[j].Trim(); + } + if (Terms.Length >= 2) { + if (Length >= Controls.Length) { + Array.Resize(ref Controls, Controls.Length << 1); + } + int j; + for (j = 0; j < CommandInfos.Length; j++) { + if (string.Compare(CommandInfos[j].Name, Terms[0], StringComparison.OrdinalIgnoreCase) == 0) break; + } + if (j == CommandInfos.Length) { + Controls[Length].Command = Command.None; + Controls[Length].InheritedType = CommandType.Digital; + Controls[Length].Method = ControlMethod.Invalid; + Controls[Length].Device = -1; + Controls[Length].Component = JoystickComponent.Invalid; + Controls[Length].Element = -1; + Controls[Length].Direction = 0; + Controls[Length].Modifier = KeyboardModifier.None; + } else { + Controls[Length].Command = CommandInfos[j].Command; + Controls[Length].InheritedType = CommandInfos[j].Type; + string Method = Terms[1].ToLowerInvariant(); + bool Valid = false; + if (Method == "keyboard" & Terms.Length == 4) + { + Key CurrentKey; + // ReSharper disable once NotAccessedVariable + int SDLTest; + if (int.TryParse(Terms[2], out SDLTest)) + { + //We've discovered a SDL keybinding is present, so reset the loading process with the default keyconfig & show an appropriate error message + if (ControlsReset == false) + { + MessageBox.Show(Interface.GetInterfaceString("errors_controls_oldversion") + Environment.NewLine + Interface.GetInterfaceString("errors_controls_reset"), Application.ProductName, + MessageBoxButtons.OK, MessageBoxIcon.Hand); + } + var DefaultControls = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Controls"),"Default keyboard assignment.controls"); + if (System.IO.File.Exists(DefaultControls)) + { + if (ControlsReset == false) + { + LoadControls(DefaultControls, out CurrentControls); + ControlsReset = true; + } + else + { + MessageBox.Show(Interface.GetInterfaceString("errors_warning") + Environment.NewLine + Interface.GetInterfaceString("errors_controls_default_oldversion"), + Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); + Controls = new Control[0]; + } + + } + else + { + MessageBox.Show(Interface.GetInterfaceString("errors_warning") + Environment.NewLine + Interface.GetInterfaceString("errors_controls_default_missing"), + Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); + Controls = new Control[0]; + } + return; + } + if (Enum.TryParse(Terms[2], true, out CurrentKey)) + { + int Modifiers; + if (int.TryParse(Terms[3], NumberStyles.Integer, Culture, out Modifiers)) + { + Controls[Length].Method = ControlMethod.Keyboard; + Controls[Length].Device = -1; + Controls[Length].Component = JoystickComponent.Invalid; + Controls[Length].Key = CurrentKey; + Controls[Length].Direction = 0; + Controls[Length].Modifier = (KeyboardModifier) Modifiers; + Valid = true; + } + } + } + + + else if (Method == "joystick" & Terms.Length >= 4) { + int Device; + if (int.TryParse(Terms[2], NumberStyles.Integer, Culture, out Device)) { + string Component = Terms[3].ToLowerInvariant(); + if (Component == "axis" & Terms.Length == 6) + { + int CurrentAxis; + if (Int32.TryParse(Terms[4], out CurrentAxis)) + { + int Direction; + if (int.TryParse(Terms[5], NumberStyles.Integer, Culture, out Direction)) + { + + Controls[Length].Method = ControlMethod.Joystick; + Controls[Length].Device = Device; + Controls[Length].Component = JoystickComponent.Axis; + Controls[Length].Element = CurrentAxis; + Controls[Length].Direction = Direction; + Controls[Length].Modifier = KeyboardModifier.None; + Valid = true; + } + } + } + else if (Component == "hat" & Terms.Length == 6) + { + int CurrentHat; + if (Int32.TryParse(Terms[4], out CurrentHat)) + { + int HatDirection; + if (Int32.TryParse(Terms[5], out HatDirection)) + { + Controls[Length].Method = ControlMethod.Joystick; + Controls[Length].Device = Device; + Controls[Length].Component = JoystickComponent.Hat; + Controls[Length].Element = CurrentHat; + Controls[Length].Direction = HatDirection; + Controls[Length].Modifier = KeyboardModifier.None; + Valid = true; + } + + } + } + else if (Component == "button" & Terms.Length == 5) + { + int CurrentButton; + if (Int32.TryParse(Terms[4], out CurrentButton)) + { + Controls[Length].Method = ControlMethod.Joystick; + Controls[Length].Device = Device; + Controls[Length].Component = JoystickComponent.Button; + Controls[Length].Element = CurrentButton; + Controls[Length].Direction = 0; + Controls[Length].Modifier = KeyboardModifier.None; + Valid = true; + } + } + + } + } + else if (Method == "raildriver" & Terms.Length >= 4) { + int Device; + if (int.TryParse(Terms[2], NumberStyles.Integer, Culture, out Device)) { + string Component = Terms[3].ToLowerInvariant(); + if (Component == "axis" & Terms.Length == 6) + { + int CurrentAxis; + if (Int32.TryParse(Terms[4], out CurrentAxis)) + { + int Direction; + if (int.TryParse(Terms[5], NumberStyles.Integer, Culture, out Direction)) + { + + Controls[Length].Method = ControlMethod.RailDriver; + Controls[Length].Device = Device; + Controls[Length].Component = JoystickComponent.Axis; + Controls[Length].Element = CurrentAxis; + Controls[Length].Direction = Direction; + Controls[Length].Modifier = KeyboardModifier.None; + Valid = true; + } + } + } + else if (Component == "button" & Terms.Length == 5) + { + int CurrentButton; + if (Int32.TryParse(Terms[4], out CurrentButton)) + { + Controls[Length].Method = ControlMethod.RailDriver; + Controls[Length].Device = Device; + Controls[Length].Component = JoystickComponent.Button; + Controls[Length].Element = CurrentButton; + Controls[Length].Direction = 0; + Controls[Length].Modifier = KeyboardModifier.None; + Valid = true; + } + } + + } + } + + if (!Valid) { + Controls[Length].Method = ControlMethod.Invalid; + Controls[Length].Device = -1; + Controls[Length].Component = JoystickComponent.Invalid; + Controls[Length].Element = -1; + Controls[Length].Direction = 0; + Controls[Length].Modifier = KeyboardModifier.None; + } + } + Length++; + } + } + } + } + Array.Resize(ref Controls, Length); + } + + /// Adds an array of controls to an existing control array + /// The base control array + /// The new controls to add + internal static void AddControls(ref Control[] Base, Control[] Add) { + for (int i = 0; i < Add.Length; i++) { + int j; + for (j = 0; j < Base.Length; j++) { + if (Add[i].Command == Base[j].Command) break; + } + if (j == Base.Length) { + Array.Resize(ref Base, Base.Length + 1); + Base[Base.Length - 1] = Add[i]; + } + } + } + } } diff --git a/source/OpenBVE/System/Translations/LanguageFile.cs b/source/OpenBVE/System/Translations/LanguageFile.cs index 9ce9a437f..fbbc2c136 100644 --- a/source/OpenBVE/System/Translations/LanguageFile.cs +++ b/source/OpenBVE/System/Translations/LanguageFile.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Windows.Forms; +using OpenBveApi; namespace OpenBve { internal static partial class Interface { @@ -45,7 +46,7 @@ internal static void LoadLanguage(string File) if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd().ToLowerInvariant(); - string b = Interface.Unescape(Lines[i].Substring(j + 1).TrimStart()); + string b = Lines[i].Substring(j + 1).TrimStart().Unescape(); switch (Section) { case "handles": @@ -156,7 +157,7 @@ internal static void AddLanguage(string File) if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd().ToLowerInvariant(); - string b = Interface.Unescape(Lines[i].Substring(j + 1).TrimStart()); + string b = Lines[i].Substring(j + 1).TrimStart().Unescape(); switch (Section) { case "handles": diff --git a/source/OpenBVE/UserInterface/formMain.Controls.cs b/source/OpenBVE/UserInterface/formMain.Controls.cs index 2c2b1e8a4..3915980f6 100644 --- a/source/OpenBVE/UserInterface/formMain.Controls.cs +++ b/source/OpenBVE/UserInterface/formMain.Controls.cs @@ -28,13 +28,20 @@ private void listviewControls_SelectedIndexChanged(object sender, EventArgs e) { } } // data - if (Interface.CurrentControls[i].Method == Interface.ControlMethod.Keyboard) { - radiobuttonKeyboard.Checked = true; - } else if (Interface.CurrentControls[i].Method == Interface.ControlMethod.Joystick || Interface.CurrentControls[i].Method == Interface.ControlMethod.RailDriver) { - radiobuttonJoystick.Checked = true; - } else { - radiobuttonKeyboard.Checked = false; - radiobuttonJoystick.Checked = false; + switch (Interface.CurrentControls[i].Method) + { + case Interface.ControlMethod.Keyboard: + radiobuttonKeyboard.Checked = true; + break; + case Interface.ControlMethod.Joystick: + case Interface.ControlMethod.RailDriver: + radiobuttonJoystick.Checked = true; + break; + default: + radiobuttonKeyboard.Checked = false; + radiobuttonJoystick.Checked = false; + textboxJoystickGrab.Enabled = false; + break; } panelKeyboard.Enabled = radiobuttonKeyboard.Checked; if (radiobuttonKeyboard.Checked) @@ -83,9 +90,9 @@ private void listviewControls_SelectedIndexChanged(object sender, EventArgs e) { buttonControlDown.Enabled = false; } } - private void UpdateControlListElement(ListViewItem Item, int Index, bool ResizeColumns) { - Interface.CommandInfo Info; - Interface.TryGetCommandInfo(Interface.CurrentControls[Index].Command, out Info); + private void UpdateControlListElement(ListViewItem Item, int Index, bool ResizeColumns) + { + Interface.CommandInfo Info = Interface.CommandInfos.TryGetInfo(Interface.CurrentControls[Index].Command); Item.SubItems[0].Text = Info.Name; switch (Info.Type) { case Interface.CommandType.Digital: Item.SubItems[1].Text = Interface.GetInterfaceString("controls_list_type_digital"); break; @@ -295,8 +302,7 @@ private void comboboxCommand_SelectedIndexChanged(object sender, EventArgs e) { int j = comboboxCommand.SelectedIndex; if (j >= 0) { Interface.CurrentControls[i].Command = Interface.CommandInfos[j].Command; - Interface.CommandInfo Info; - Interface.TryGetCommandInfo(Interface.CommandInfos[j].Command, out Info); + Interface.CommandInfo Info = Interface.CommandInfos.TryGetInfo(Interface.CommandInfos[j].Command); Interface.CurrentControls[i].InheritedType = Info.Type; UpdateControlListElement(listviewControls.Items[i], i, true); } @@ -309,6 +315,14 @@ private void comboboxCommand_SelectedIndexChanged(object sender, EventArgs e) { // keyboard private void radiobuttonKeyboard_CheckedChanged(object sender, EventArgs e) { + if (radiobuttonJoystick.Checked || radiobuttonKeyboard.Checked) + { + textboxJoystickGrab.Enabled = true; + } + else + { + textboxJoystickGrab.Enabled = false; + } if (this.Tag == null & listviewControls.SelectedIndices.Count == 1) { int i = listviewControls.SelectedIndices[0]; Interface.CurrentControls[i].Method = Interface.ControlMethod.Keyboard; @@ -372,6 +386,14 @@ private void radiobuttonJoystick_CheckedChanged(object sender, EventArgs e) { UpdateControlListElement(listviewControls.Items[i], i, true); } panelJoystick.Enabled = radiobuttonJoystick.Checked; + if (radiobuttonJoystick.Checked || radiobuttonKeyboard.Checked) + { + textboxJoystickGrab.Enabled = true; + } + else + { + textboxJoystickGrab.Enabled = false; + } if (radiobuttonJoystick.Checked) { textboxJoystickGrab.Text = Interface.GetInterfaceString("controls_selection_joystick_assignment_grab"); diff --git a/source/OpenBVE/UserInterface/formMain.Start.cs b/source/OpenBVE/UserInterface/formMain.Start.cs index dc7521e80..a47b466e4 100644 --- a/source/OpenBVE/UserInterface/formMain.Start.cs +++ b/source/OpenBVE/UserInterface/formMain.Start.cs @@ -4,6 +4,7 @@ using System.IO; using System.Text; using System.Windows.Forms; +using OpenBveApi; namespace OpenBve { @@ -25,7 +26,7 @@ private void textboxRouteFolder_TextChanged(object sender, EventArgs e) return; } string Folder = textboxRouteFolder.Text; - while (!Directory.Exists(Folder) && Path.IsPathRooted(Folder)) + while (!Directory.Exists(Folder) && System.IO.Path.IsPathRooted(Folder)) { Folder = Directory.GetParent(Folder).ToString(); } @@ -408,7 +409,7 @@ private void textboxTrainFolder_TextChanged(object sender, EventArgs e) return; } string Folder = textboxTrainFolder.Text; - while (!Directory.Exists(Folder) && Path.IsPathRooted(Folder)) + while (!Directory.Exists(Folder) && System.IO.Path.IsPathRooted(Folder) && Folder.Length > 2) { Folder = Directory.GetParent(Folder).ToString(); } @@ -798,7 +799,7 @@ private void routeWorkerThread_completed(object sender, RunWorkerCompletedEventA } // description - string Description = Interface.ConvertNewlinesToCrLf(Game.RouteComment); + string Description = Game.RouteComment.ConvertNewlinesToCrLf(); if (Description.Length != 0) { textboxRouteDescription.Text = Description; @@ -807,7 +808,8 @@ private void routeWorkerThread_completed(object sender, RunWorkerCompletedEventA { textboxRouteDescription.Text = System.IO.Path.GetFileNameWithoutExtension(Result.RouteFile); } - textboxRouteEncodingPreview.Text = Interface.ConvertNewlinesToCrLf(Description); + + textboxRouteEncodingPreview.Text = Description.ConvertNewlinesToCrLf(); if (Game.TrainName != null) { checkboxTrainDefault.Text = Interface.GetInterfaceString("start_train_usedefault") + @" (" + Game.TrainName + @")"; @@ -1054,7 +1056,7 @@ private void ShowTrain(bool UserSelectedEncoding) { if (System.IO.File.Exists(File)) { try { string trainText = System.IO.File.ReadAllText(File, Result.TrainEncoding); - trainText = Interface.ConvertNewlinesToCrLf(trainText); + trainText = trainText.ConvertNewlinesToCrLf(); textboxTrainDescription.Text = trainText; textboxTrainEncodingPreview.Text = trainText; } catch { @@ -1158,7 +1160,7 @@ private void ShowDefaultTrain() { // train not found Result.TrainFolder = null; TryLoadImage(pictureboxTrainImage, "train_error.png"); - textboxTrainDescription.Text = Interface.ConvertNewlinesToCrLf(Interface.GetInterfaceString("start_train_notfound") + Game.TrainName); + textboxTrainDescription.Text = (Interface.GetInterfaceString("start_train_notfound") + Game.TrainName).ConvertNewlinesToCrLf(); comboboxTrainEncoding.Tag = new object(); comboboxTrainEncoding.SelectedIndex = 0; comboboxTrainEncoding.Tag = null; diff --git a/source/OpenBveApi/OpenBveApi.csproj b/source/OpenBveApi/OpenBveApi.csproj index 101f78c41..ac6ec21c3 100644 --- a/source/OpenBveApi/OpenBveApi.csproj +++ b/source/OpenBveApi/OpenBveApi.csproj @@ -78,6 +78,7 @@ + diff --git a/source/OpenBveApi/Text.cs b/source/OpenBveApi/Text.cs new file mode 100644 index 000000000..78f4d8c9d --- /dev/null +++ b/source/OpenBveApi/Text.cs @@ -0,0 +1,169 @@ +using System; +using System.Linq; + +namespace OpenBveApi +{ + /// Provides various extension methods for working with text + public static class Text + { + /// Unescapes control characters used in a string + /// The raw string on which unescaping should be performed + /// The unescaped string + public static string Unescape(this 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 { + return Text; + } i++; + } else { + 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 { + 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 { + return Text; + } break; + default: + return Text; + } + i++; + Start = i + 1; + } else { + return Text; + } + } + } + Builder.Append(Text, Start, Text.Length - Start); + return Builder.ToString(); + } + + /// Determines whether the specified string is encoded using Shift_JIS (Japanese) + /// The string to check + /// True if Shift_JIS encoded, false otherwise + public static bool IsJapanese(this string Name) { + for (int i = 0; i < Name.Length; i++) { + int a = char.ConvertToUtf32(Name, i); + if (a < 0x10000) { + bool q = false; + while (true) { + if (a >= 0x2E80 & a <= 0x2EFF) break; + if (a >= 0x3000 & a <= 0x30FF) break; + if (a >= 0x31C0 & a <= 0x4DBF) break; + if (a >= 0x4E00 & a <= 0x9FFF) break; + if (a >= 0xF900 & a <= 0xFAFF) break; + if (a >= 0xFE30 & a <= 0xFE4F) break; + if (a >= 0xFF00 & a <= 0xFFEF) break; + q = true; break; + } if (q) return false; + } else { + return false; + } + } return true; + } + + /// Converts various line-endings to CR-LF format + /// The string for which all line-endings should be converted to CR-LF + /// The converted StringBuilder + public static string ConvertNewlinesToCrLf(this string Text) { + System.Text.StringBuilder Builder = new System.Text.StringBuilder(); + for (int i = 0; i < Text.Length; i++) { + int a = char.ConvertToUtf32(Text, i); + if (a == 0xD & i < Text.Length - 1) { + int b = char.ConvertToUtf32(Text, i + 1); + if (b == 0xA) { + Builder.Append("\r\n"); + i++; + } else { + Builder.Append("\r\n"); + } + } else if (a == 0xA | a == 0xC | a == 0xD | a == 0x85 | a == 0x2028 | a == 0x2029) { + Builder.Append("\r\n"); + } else if (a < 0x10000) { + Builder.Append(Text[i]); + } else { + Builder.Append(Text.Substring(i, 2)); + i++; + } + } return Builder.ToString(); + } + + /// Trims any multiple whitespace characters within the string + /// The string to trim + /// The trimmed string + public static string TrimInside(this string Expression) { + System.Text.StringBuilder Builder = new System.Text.StringBuilder(Expression.Length); + foreach (char c in Expression.Where(c => !char.IsWhiteSpace(c))) + { + Builder.Append(c); + } return Builder.ToString(); + } + + /// Trims BVE5 comments from a string. + /// The string to trim. + public static string TrimBVE5Comments(this string String) + { + int j = String.IndexOf('#'); + if (j >= 0) + { + String = String.Substring(0, j); + } + int k = String.IndexOf("//", StringComparison.Ordinal); + if (k >= 0) + { + String = String.Substring(0, k); + } + return String; + } + + /// Removes single or double quotes enclosing a string. + /// The string to trim. + public static string RemoveEnclosingQuotes(this string String) + { + if (String.StartsWith("'") && String.EndsWith("'")) + { + String = String.Substring(1, String.Length - 2); + } + + if (String.StartsWith("\"") && String.EndsWith("\"")) + { + String = String.Substring(1, String.Length - 2); + } + return String; + } + } +} diff --git a/source/Plugins/AtsPluginProxy/AtsPluginProxy/AtsPluginProxy.cpp b/source/Plugins/AtsPluginProxy/AtsPluginProxy/AtsPluginProxy.cpp index f32451383..010301dbc 100644 --- a/source/Plugins/AtsPluginProxy/AtsPluginProxy/AtsPluginProxy.cpp +++ b/source/Plugins/AtsPluginProxy/AtsPluginProxy/AtsPluginProxy.cpp @@ -60,10 +60,28 @@ typedef ATS_API void (__stdcall *SETBEACONDATA) (ATS_BEACONDATA beaconData); SET // --- load the plugin --- int _stdcall LoadDLL(LPCWSTR fileUnicode, LPCSTR fileAnsi) { - dllhandle = LoadLibraryW(fileUnicode); + try + { + dllhandle = LoadLibraryW(fileUnicode); + } + catch (...) + { + //Blank try / catch in case our library will only load with ANSI & crashes with Unicode + //This seems to be the case with some specific versions of OS_ATS1.dll + //These were likely compiled on XP with Bloodshed Dev C++ (??) + } + if (dllhandle == NULL) { - dllhandle = LoadLibraryA(fileAnsi); - if (dllhandle == NULL) return 0; + try + { + dllhandle = LoadLibraryA(fileAnsi); + } + catch (...) + { + //Presumably both Unicode and ANSI crashed + return 0; + } + if (dllhandle == NULL) return 0; } { // --- Load --- FARPROC functionhandle = GetProcAddress(dllhandle, "Load"); diff --git a/source/Plugins/AtsPluginProxy/AtsPluginProxy/AtsPluginProxy.vcxproj b/source/Plugins/AtsPluginProxy/AtsPluginProxy/AtsPluginProxy.vcxproj index 10fe4a82e..b503bca16 100644 --- a/source/Plugins/AtsPluginProxy/AtsPluginProxy/AtsPluginProxy.vcxproj +++ b/source/Plugins/AtsPluginProxy/AtsPluginProxy/AtsPluginProxy.vcxproj @@ -131,6 +131,8 @@ true WIN32;NDEBUG;_WINDOWS;_USRDLL;ATSPLUGINPROXY_EXPORTS;%(PreprocessorDefinitions) true + Async + MultiThreaded Windows diff --git a/source/Plugins/Sound.MP3/Plugin.Parser.cs b/source/Plugins/Sound.MP3/Plugin.Parser.cs new file mode 100644 index 000000000..1add6ac33 --- /dev/null +++ b/source/Plugins/Sound.MP3/Plugin.Parser.cs @@ -0,0 +1,39 @@ +using System; +using OpenBveApi.Sounds; +using NAudio.Wave; + +namespace Plugin { + public partial class Plugin : SoundInterface { + + + + /// Reads sound data from a MP3 file. + /// The file name of the MP3 file. + /// The raw sound data. + private static Sound LoadFromFile(string fileName) + { + using (var reader = new Mp3FileReader(fileName)) + { + var pcmLength = (int)reader.Length; + var leftBuffer = new byte[pcmLength / 2]; + var rightBuffer = new byte[pcmLength / 2]; + var buffer = new byte[pcmLength]; + var bytesRead = reader.Read(buffer, 0, pcmLength); + + int index = 0; + //For simplicity just use let NLayer internally convert into raw 16-bit 2 channel PCM + for (int i = 0; i < bytesRead; i += 4) + { + leftBuffer[index] = buffer[i]; + rightBuffer[index] = buffer[i + 2]; + index++; + leftBuffer[index] = buffer[i + 1]; + rightBuffer[index] = buffer[i + 3]; + index++; + } + return new Sound(44100, 16, new byte[][] { leftBuffer, rightBuffer}); + + } + } + } +} diff --git a/source/Plugins/Sound.MP3/Plugin.cs b/source/Plugins/Sound.MP3/Plugin.cs new file mode 100644 index 000000000..81bb321c1 --- /dev/null +++ b/source/Plugins/Sound.MP3/Plugin.cs @@ -0,0 +1,38 @@ +using System; +using System.IO; +using OpenBveApi.Hosts; +using OpenBveApi.Sounds; + +namespace Plugin { + public partial class Plugin : SoundInterface { + + /// Called when the plugin is loaded. + /// The host that loaded the plugin. + public override void Load(HostInterface host) { + // CurrentHost = host; + } + + /// Checks whether the plugin can load the specified sound. + /// The path to the file or folder that contains the sound. + /// Whether the plugin can load the specified sound. + public override bool CanLoadSound(string path) { + if (File.Exists(path)) { + if (path.ToLowerInvariant().EndsWith(".mp3")) + { + return true; + } + } + return false; + } + + /// Loads the specified sound. + /// The path to the file or folder that contains the sound. + /// Receives the sound. + /// Whether loading the sound was successful. + public override bool LoadSound(string path, out Sound sound) { + sound = LoadFromFile(path); + return true; + } + + } +} diff --git a/source/Plugins/Sound.MP3/Properties/AssemblyInfo.cs b/source/Plugins/Sound.MP3/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..7354a5c0c --- /dev/null +++ b/source/Plugins/Sound.MP3/Properties/AssemblyInfo.cs @@ -0,0 +1,12 @@ +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("MP3 Sound Loader")] +[assembly: AssemblyDescription("Supports loading MP3 sounds.")] +[assembly: AssemblyProduct("openBVE")] +[assembly: AssemblyCopyright("The openBVE Project")] +[assembly: ComVisible(false)] +[assembly: CLSCompliant(true)] + +[assembly: AssemblyVersion("0.0.0.0")] diff --git a/source/Plugins/Sound.MP3/Sound.MP3.csproj b/source/Plugins/Sound.MP3/Sound.MP3.csproj new file mode 100644 index 000000000..5c7906191 --- /dev/null +++ b/source/Plugins/Sound.MP3/Sound.MP3.csproj @@ -0,0 +1,73 @@ + + + + {A2FC4D71-1ED9-40D4-B746-FE6AB3C7D55E} + Debug + x86 + Library + Plugin + Sound.MP3 + v4.0 + Properties + C:\Documents and Settings\Administrator\Application Data\ICSharpCode/SharpDevelop3.0\Settings.SourceAnalysis + False + False + 4 + false + + + + + AnyCPU + False + Auto + 4194304 + 4096 + + + bin\Debug\Data\Plugins\ + True + Full + False + True + DEBUG;TRACE + + + bin\Release\Data\Plugins\ + false + None + True + False + TRACE + + + ..\..\..\bin_debug\Data\Plugins\ + x86 + + + ..\..\..\bin_release\Data\Plugins\ + x86 + + + + + ..\..\..\dependencies\NAudio.dll + False + + + + + + Plugin.cs + + + + + + + {27134980-4415-4375-A564-40A9014DFA5F} + OpenBveApi + False + + + \ No newline at end of file diff --git a/source/Plugins/Sound.RiffWave/Plugin.Parser.cs b/source/Plugins/Sound.RiffWave/Plugin.Parser.cs index 3174b1b4a..74cfd8aa8 100644 --- a/source/Plugins/Sound.RiffWave/Plugin.Parser.cs +++ b/source/Plugins/Sound.RiffWave/Plugin.Parser.cs @@ -1,6 +1,7 @@ #pragma warning disable 0660, 0661 using System.IO; +using NAudio.Wave; using OpenBveApi.Sounds; namespace Plugin { @@ -197,6 +198,9 @@ private static Sound LoadFromFile(string fileName) { format.Channels = (int)wChannels; adpcmData.BlockSize = wBlockAlign; data = adpcmData; + } else if (wFormatTag == 85) { + //This is actually a WAV encapsulated mp3 file.... + return NlayerLoadFromFile(fileName); } else { // unsupported format throw new InvalidDataException("Unsupported wFormatTag"); @@ -333,7 +337,36 @@ private static Sound LoadFromFile(string fileName) { } } } - + + /// Reads sound data from a MP3 file. + /// The file name of the MP3 file. + /// The raw sound data. + private static Sound NlayerLoadFromFile(string fileName) + { + using (var reader = new Mp3FileReader(fileName)) + { + var pcmLength = (int)reader.Length; + var leftBuffer = new byte[pcmLength / 2]; + var rightBuffer = new byte[pcmLength / 2]; + var buffer = new byte[pcmLength]; + var bytesRead = reader.Read(buffer, 0, pcmLength); + + int index = 0; + //For simplicity just use let NLayer internally convert into raw 16-bit 2 channel PCM + for (int i = 0; i < bytesRead; i += 4) + { + leftBuffer[index] = buffer[i]; + rightBuffer[index] = buffer[i + 2]; + index++; + leftBuffer[index] = buffer[i + 1]; + rightBuffer[index] = buffer[i + 3]; + index++; + } + return new Sound(44100, 16, new byte[][] { leftBuffer, rightBuffer }); + + } + } + /// Reads a System.UInt32 from a binary reader with the specified endianness. /// The binary reader. /// The endianness. @@ -366,4 +399,4 @@ private static ushort ReadUInt16(BinaryReader reader, Endianness endianness) { } -} \ No newline at end of file +} diff --git a/source/Plugins/Sound.RiffWave/Properties/AssemblyInfo.cs b/source/Plugins/Sound.RiffWave/Properties/AssemblyInfo.cs index 55bf35ce9..94ab2a5d2 100644 --- a/source/Plugins/Sound.RiffWave/Properties/AssemblyInfo.cs +++ b/source/Plugins/Sound.RiffWave/Properties/AssemblyInfo.cs @@ -9,4 +9,4 @@ [assembly: ComVisible(false)] [assembly: CLSCompliant(true)] -[assembly: AssemblyVersion("0.0.0.0")] \ No newline at end of file +[assembly: AssemblyVersion("0.0.0.1")] diff --git a/source/Plugins/Sound.RiffWave/Sound.RiffWave.csproj b/source/Plugins/Sound.RiffWave/Sound.RiffWave.csproj index 4f95cfdf3..a6f2176ea 100644 --- a/source/Plugins/Sound.RiffWave/Sound.RiffWave.csproj +++ b/source/Plugins/Sound.RiffWave/Sound.RiffWave.csproj @@ -50,6 +50,11 @@ + + False + ..\..\..\dependencies\NAudio.dll + False + diff --git a/source/RouteViewer/InterfaceR.cs b/source/RouteViewer/InterfaceR.cs index 11c617e37..43a41394f 100644 --- a/source/RouteViewer/InterfaceR.cs +++ b/source/RouteViewer/InterfaceR.cs @@ -7,6 +7,7 @@ using System; using System.Globalization; +using OpenBveApi; namespace OpenBve { @@ -93,8 +94,9 @@ internal static void ClearMessages() { // try parse time - internal static bool TryParseTime(string Expression, out double Value) { - Expression = TrimInside(Expression); + internal static bool TryParseTime(string Expression, out double Value) + { + Expression = Expression.TrimInside(); if (Expression.Length != 0) { CultureInfo Culture = CultureInfo.InvariantCulture; int i = Expression.IndexOf('.'); @@ -130,108 +132,6 @@ internal static bool TryParseTime(string Expression, out double Value) { Value = 0.0; return false; } - - // trim inside - private static string TrimInside(string Expression) { - System.Text.StringBuilder Builder = new System.Text.StringBuilder(Expression.Length); - for (int i = 0; i < Expression.Length; i++) { - char c = Expression[i]; - if (!char.IsWhiteSpace(c)) { - Builder.Append(c); - } - } - return Builder.ToString(); - } - - // is japanese - internal static bool IsJapanese(string Name) { - for (int i = 0; i < Name.Length; i++) { - int a = char.ConvertToUtf32(Name, i); - if (a < 0x10000) { - bool q = false; - while (true) { - if (a >= 0x2E80 & a <= 0x2EFF) break; - if (a >= 0x3000 & a <= 0x30FF) break; - if (a >= 0x31C0 & a <= 0x4DBF) break; - if (a >= 0x4E00 & a <= 0x9FFF) break; - if (a >= 0xF900 & a <= 0xFAFF) break; - if (a >= 0xFE30 & a <= 0xFE4F) break; - if (a >= 0xFF00 & a <= 0xFFEF) break; - q = true; break; - } if (q) return false; - } else { - return false; - } - } return true; - } - - // 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(); - } - - // ================================ - // round to power of two internal static int RoundToPowerOfTwo(int Value) { Value -= 1; @@ -239,29 +139,5 @@ internal static int RoundToPowerOfTwo(int Value) { Value = Value | Value >> i; } return Value + 1; } - - // convert newlines to crlf - internal static string ConvertNewlinesToCrLf(string Text) { - System.Text.StringBuilder Builder = new System.Text.StringBuilder(); - for (int i = 0; i < Text.Length; i++) { - int a = char.ConvertToUtf32(Text, i); - if (a == 0xD & i < Text.Length - 1) { - int b = char.ConvertToUtf32(Text, i + 1); - if (b == 0xA) { - Builder.Append("\r\n"); - i++; - } else { - Builder.Append("\r\n"); - } - } else if (a == 0xA | a == 0xC | a == 0xD | a == 0x85 | a == 0x2028 | a == 0x2029) { - Builder.Append("\r\n"); - } else if (a < 0x10000) { - Builder.Append(Text[i]); - } else { - Builder.Append(Text.Substring(i, 2)); - i++; - } - } return Builder.ToString(); - } } -} \ No newline at end of file +} diff --git a/source/RouteViewer/Parsers/WavefrontObjParser.cs b/source/RouteViewer/Parsers/WavefrontObjParser.cs index 0c2954b7a..a6bc14aee 100644 --- a/source/RouteViewer/Parsers/WavefrontObjParser.cs +++ b/source/RouteViewer/Parsers/WavefrontObjParser.cs @@ -172,19 +172,36 @@ internal static ObjectManager.StaticObject ReadObject(string FileName, System.Te Vertex newVertex = new Vertex(); string[] faceArguments = Arguments[f].Split(new char[] {'/'} , StringSplitOptions.None); int idx; - if (!int.TryParse(faceArguments[0], out idx) || idx > tempVertices.Count) + if (!int.TryParse(faceArguments[0], out idx)) { Interface.AddMessage(Interface.MessageType.Warning, false, "Invalid Vertex index in Face " + f + " at Line " + i); continue; } - newVertex.Coordinates = tempVertices[idx - 1]; + + 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(Interface.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) || idx > tempCoords.Count) + if (!int.TryParse(faceArguments[1], out idx)) { if (!string.IsNullOrEmpty(faceArguments[1])) { @@ -194,7 +211,22 @@ internal static ObjectManager.StaticObject ReadObject(string FileName, System.Te } else { - newVertex.TextureCoordinates = tempCoords[idx - 1]; + int currentCoord = tempCoords.Count; + if (idx != Math.Abs(idx)) + { + //Offset, so we seem to need to add one.... + currentCoord++; + currentCoord += idx; + } + if (currentCoord > tempCoords.Count) + { + Interface.AddMessage(Interface.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) @@ -203,7 +235,7 @@ internal static ObjectManager.StaticObject ReadObject(string FileName, System.Te } else { - if (!int.TryParse(faceArguments[2], out idx) || idx > tempNormals.Count) + if (!int.TryParse(faceArguments[2], out idx)) { if (!string.IsNullOrEmpty(faceArguments[2])) { @@ -213,7 +245,22 @@ internal static ObjectManager.StaticObject ReadObject(string FileName, System.Te } else { - normals.Add(tempNormals[idx - 1]); + int currentNormal = tempNormals.Count; + if (idx != Math.Abs(idx)) + { + //Offset, so we seem to need to add one.... + currentNormal++; + currentNormal += idx; + } + if (currentNormal > tempNormals.Count) + { + Interface.AddMessage(Interface.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); @@ -221,17 +268,8 @@ internal static ObjectManager.StaticObject ReadObject(string FileName, System.Te World.MeshFaceVertex[] Vertices = new World.MeshFaceVertex[vertices.Count]; for (int k = 0; k < vertices.Count; k++) { - int v = Builder.Vertices.FindIndex(a => a.Equals(vertices[k])); - if (v != -1) - { - Vertices[k].Index = (ushort)v; - } - else - { - Builder.Vertices.Add(vertices[k]); - Vertices[k].Index = (ushort)(Builder.Vertices.Count -1); - } - + Builder.Vertices.Add(vertices[k]); + Vertices[k].Index = (ushort)(Builder.Vertices.Count -1); Vertices[k].Normal = normals[k]; } Builder.Faces.Add(currentMaterial == -1 ? new World.MeshFace(Vertices, 0) : new World.MeshFace(Vertices, (ushort)currentMaterial)); diff --git a/source/RouteViewer/Parsers/XObjectParser.cs b/source/RouteViewer/Parsers/XObjectParser.cs index e1a648d7f..215ada7b4 100644 --- a/source/RouteViewer/Parsers/XObjectParser.cs +++ b/source/RouteViewer/Parsers/XObjectParser.cs @@ -166,14 +166,14 @@ private static Template GetTemplate(string Name, bool binary) { return Templates[i]; } } - if (Name.StartsWith("Frame ")) + if (Name.ToLowerInvariant().StartsWith("frame ")) { //Enclosing frame for the model //Appears in Blender exported stuff - return Templates[11]; + return Templates[13]; } - if (Name.StartsWith("Mesh ")) + if (Name.ToLowerInvariant().StartsWith("mesh ")) { //Named material, just ignore the name for the minute //Appears in Blender exported stuff @@ -2249,7 +2249,7 @@ private static bool ProcessStructure(string FileName, Structure Structure, out O Object.Mesh.Faces[mf + j].Vertices = new World.MeshFaceVertex[Faces[j].Length]; for (int k = 0; k < Faces[j].Length; k++) { - Object.Mesh.Faces[mf + j].Vertices[mv + k] = new World.MeshFaceVertex(mv + Faces[j][k], FaceNormals[j][k]); + Object.Mesh.Faces[mf + j].Vertices[k] = new World.MeshFaceVertex(mv + Faces[j][k], FaceNormals[j][k]); } } for (int j = 0; j < Vertices.Length; j++) diff --git a/source/RouteViewer/RendererR.cs b/source/RouteViewer/RendererR.cs index 452dc9099..0fc04f1eb 100644 --- a/source/RouteViewer/RendererR.cs +++ b/source/RouteViewer/RendererR.cs @@ -486,9 +486,24 @@ private static void RenderFace(ref World.MeshMaterial Material, VertexTemplate[] } if (Material.GlowAttenuationData != 0) { float alphafactor = (float)GetDistanceFactor(Vertices, ref Face, Material.GlowAttenuationData, CameraX, CameraY, CameraZ); - GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, inv255 * (float)Material.Color.A * alphafactor); + if (OptionWireframe) + { + GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, 1.0f); + } + else + { + GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, inv255 * (float)Material.Color.A * alphafactor); + } } else { - GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, inv255 * (float)Material.Color.A); + if (OptionWireframe) + { + GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, 1.0f); + } + else + { + GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, inv255 * (float)Material.Color.A); + } + } if ((Material.Flags & World.MeshMaterial.EmissiveColorMask) != 0) { GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Emission, new float[] { inv255 * (float)Material.EmissiveColor.R, inv255 * (float)Material.EmissiveColor.G, inv255 * (float)Material.EmissiveColor.B, 1.0f }); @@ -582,7 +597,15 @@ private static void RenderFace(ref World.MeshMaterial Material, VertexTemplate[] alphafactor = inv255 * (float)Material.DaytimeNighttimeBlend + 1.0f - OptionLightingResultingAmount; if (alphafactor > 1.0f) alphafactor = 1.0f; } - GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, inv255 * (float)Material.Color.A * alphafactor); + + if (OptionWireframe) + { + GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, 1.0f); + } + else + { + GL.Color4(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, inv255 * (float)Material.Color.A * alphafactor); + } if ((Material.Flags & World.MeshMaterial.EmissiveColorMask) != 0) { GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Emission, new float[] { inv255 * (float)Material.EmissiveColor.R, inv255 * (float)Material.EmissiveColor.G, inv255 * (float)Material.EmissiveColor.B, 1.0f }); EmissiveEnabled = true;