diff --git a/Readme.md b/Readme.md index 35dafd7a1..f70860968 100644 --- a/Readme.md +++ b/Readme.md @@ -60,17 +60,25 @@ Please also see the following issue for further discussion on the topic: https:/ **Third-Party Libraries** -- openBVE uses the [**CoreFX**](https://github.com/dotnet/corefx). This is licensed under the _MIT License_, which may be found in [here](licenses/CoreFX.txt). -- openBVE uses the [**CS Script**](https://github.com/oleg-shilo/cs-script) for animation scripting. This is licensed under the _MIT License_, which may be found in [here](licenses/CS-Script.txt). -- openBVE uses the [**DotNetZip**](https://github.com/haf/DotNetZip.Semverd) for loading compressed DirectX file. This is licensed under the *Microsoft Public License*, which may be found in [here](licenses/DotNetZip.txt). -- openBVE uses the [**NAudio**](https://github.com/naudio/NAudio) for decoding sound file. This is licensed under the *Microsoft Public License*, which may be found in [here](licenses/NAudio.txt). -- openBVE uses the [**NAudio.Vorbis**](https://github.com/naudio/Vorbis) for decoding Vorbis file. This is licensed under the *Microsoft Public License*, which may be found in [here](licenses/NAudio.Vorbis.txt). -- openBVE uses the [**NLayer**](https://github.com/naudio/NLayer) for decoding MP3 file. This is licensed under the *MIT License*, which may be found in [here](licenses/NLayer.txt). -- openBVE uses the [**NVorbis**](https://github.com/NVorbis/NVorbis) for decoding Vorbis file. This is licensed under the *Microsoft Public License*, which may be found in [here](licenses/NVorbis.txt). -- openBVE uses the [**OpenTK**](https://github.com/opentk/opentk) library for windowing and input handling. This is licensed under the _Open Toolkit Library License_, which may be found in [here](licenses/OpenTK.txt). -- openBVE uses the [**Prism**](https://github.com/PrismLibrary/Prism). This is licensed under the *MIT License*, which may be found in [here](licenses/Prism.txt). -- openBVE uses the [**ReactiveProperty**](https://github.com/runceel/ReactiveProperty). This is licensed under the *MIT License*, which may be found in [here](licenses/ReactiveProperty.txt). -- openBVE uses the [**SharpCompress**](https://github.com/adamhathcock/sharpcompress) for archive handling. This is licensed under the _MIT License_, which may be found in [here](licenses/SharpCompress.txt). -- openBVE uses the [**Reactive Extensions**](https://github.com/dotnet/reactive). This is licensed under the *Apache License, Version 2.0*, which may be found in [here](licenses/ReactiveExtensions.txt). -- openBVE uses the [**Ude**](https://github.com/yinyue200/ude) for character set detection. This is tri-licensed under the _Mozilla Public License v1.1_, _GPL 2.0_ and _LGPL 2.0_, which may be found in [here](licenses/Ude.txt). -- openBVE uses the [**XamlBehaviors for WPF**](https://github.com/microsoft/XamlBehaviorsWpf). This is licensed under the *MIT License*, which may be found in [here](licenses/XamlBehaviorsForWPF.txt). +openBVE uses the following third party libraries. + +| Name | License | Usage | +| --------------------------------------------------------------------------- | --------------------------------------------------------------------- | ------------------------------- | +| [**CS Script**](https://github.com/oleg-shilo/cs-script) | [MIT License](licenses/CS-Script.txt) | Animation scripting | +| [**.NET Runtime**](https://github.com/dotnet/runtime) | [MIT License](licenses/dotnet_runtime.txt) | | +| [**DotNetZip**](https://github.com/haf/DotNetZip.Semverd) | [Microsoft Public License](licenses/DotNetZip.txt) | Loading compressed DirectX file | +| [**Json.NET**](https://github.com/JamesNK/Newtonsoft.Json) | [MIT License](licenses/Newtonsoft.Json.txt) | Loading JSON file | +| [**Namotion.Reflection**](https://github.com/RicoSuter/Namotion.Reflection) | [MIT License](licenses/Namotion.Reflection.txt) | | +| [**NAudio**](https://github.com/naudio/NAudio) | [Microsoft Public License](licenses/NAudio.txt) | Decoding sound file | +| [**NAudio.Vorbis**](https://github.com/naudio/Vorbis) | [Microsoft Public License](licenses/NAudio.Vorbis.txt) | Decoding Vorbis file | +| [**NJsonSchema for .NET**](https://github.com/RicoSuter/NJsonSchema) | [MIT License](licenses/NJsonSchema) | Validating JSON file | +| [**NLayer**](https://github.com/naudio/NLayer) | [MIT License](licenses/NLayer.txt) | Decoding MP3 file | +| [**NVorbis**](https://github.com/NVorbis/NVorbis) | [Microsoft Public License](licenses/NVorbis.txt) | Decoding Vorbis file | +| [**OpenAL Soft**](https://github.com/kcat/openal-soft) | [LGPL 2.0](licenses/OpenALSoft.txt) | Playing sound | +| [**OpenTK**](https://github.com/opentk/opentk) | [Open Toolkit Library License](licenses/OpenTK.txt) | Windowing and input handling | +| [**Prism**](https://github.com/PrismLibrary/Prism) | [MIT License](licenses/Prism.txt) | | +| [**Reactive Extensions**](https://github.com/dotnet/reactive) | [Apache License, Version 2.0](licenses/ReactiveExtensions.txt) | | +| [**ReactiveProperty**](https://github.com/runceel/ReactiveProperty) | [MIT License](licenses/ReactiveProperty.txt) | | +| [**SharpCompress**](https://github.com/adamhathcock/sharpcompress) | [MIT License](licenses/SharpCompress.txt) | Archive handling | +| [**Ude**](https://github.com/yinyue200/ude) | [Mozilla Public License v1.1, GPL 2.0 and LGPL 2.0](licenses/Ude.txt) | Character set detection | + diff --git a/assets/Schemas/ats.json b/assets/Schemas/ats.json new file mode 100644 index 000000000..5aeceb270 --- /dev/null +++ b/assets/Schemas/ats.json @@ -0,0 +1,147 @@ +{ + "$schema": "http://json-schema.org/schema", + "definitions": { + "index": { + "type": "integer", + "minimum": -1 + }, + "indexMap": { + "type": "object", + "properties": { + "offset": { + "type": "integer" + }, + "entries": { + "type": "array", + "items": { + "type": "object", + "properties": { + "src": { + "$ref": "#/definitions/index" + }, + "dest": { + "$ref": "#/definitions/index" + } + }, + "required": [ + "src", + "dest" + ], + "additionalProperties": false + } + } + }, + "additionalProperties": false + }, + "key": { + "type": "string", + "enum": [ + "None", + "S", + "A1", + "A2", + "B1", + "B2", + "C1", + "C2", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "WiperSpeedUp", + "WiperSpeedDown", + "FillFuel", + "LiveSteamInjector", + "ExhaustSteamInjector", + "IncreaseCutoff", + "DecreaseCutoff", + "Blowers", + "EngineStart", + "EngineStop", + "GearUp", + "GearDown", + "RaisePantograph", + "LowerPantograph", + "MainBreaker", + "LeftDoors", + "RightDoors" + ] + }, + "keyMap": { + "type": "object", + "properties": { + "entries": { + "type": "array", + "items": { + "type": "object", + "properties": { + "src": { + "$ref": "#/definitions/key" + }, + "dest": { + "$ref": "#/definitions/key" + } + }, + "required": [ + "src", + "dest" + ], + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + }, + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "Ats" + }, + "version": { + "type": "string", + "const": "1.0" + }, + "plugins": { + "type": "array", + "items": { + "type": "object", + "properties": { + "filePath": { + "type": "string", + "format": "uri-reference" + }, + "panelIndexMap": { + "$ref": "#/definitions/indexMap" + }, + "soundIndexMap": { + "$ref": "#/definitions/indexMap" + }, + "keyMap": { + "$ref": "#/definitions/keyMap" + } + }, + "required": [ + "filePath" + ], + "additionalProperties": false + } + } + }, + "required": [ + "type", + "version", + "plugins" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/dependencies/AtsPluginProxy.dll b/dependencies/AtsPluginProxy.dll deleted file mode 100644 index 61db6e283..000000000 Binary files a/dependencies/AtsPluginProxy.dll and /dev/null differ diff --git a/licenses/AssimpParser.txt b/licenses/AssimpParser.txt new file mode 100644 index 000000000..0e1d55090 --- /dev/null +++ b/licenses/AssimpParser.txt @@ -0,0 +1,40 @@ +AssimpParser is a cross-ported copy of Open Asset Import Library (assimp) by assimp team. + +The original repository / source code may be found at the following address: +https://github.com/assimp/assimp + + +Open Asset Import Library (assimp) + +Copyright (c) 2006-2016, assimp team, 2018, The openBVE Project +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/licenses/NJsonSchema.txt b/licenses/NJsonSchema.txt new file mode 100644 index 000000000..7671aadb3 --- /dev/null +++ b/licenses/NJsonSchema.txt @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright (c) 2016 Rico Suter + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/licenses/Namotion.Reflection.txt b/licenses/Namotion.Reflection.txt new file mode 100644 index 000000000..c63fb83ce --- /dev/null +++ b/licenses/Namotion.Reflection.txt @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2019 Rico Suter + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/licenses/Newtonsoft.Json.txt b/licenses/Newtonsoft.Json.txt new file mode 100644 index 000000000..ed11c37a1 --- /dev/null +++ b/licenses/Newtonsoft.Json.txt @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright (c) 2007 James Newton-King + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/licenses/XamlBehaviorsForWPF.txt b/licenses/XamlBehaviorsForWPF.txt deleted file mode 100644 index 49d21669a..000000000 --- a/licenses/XamlBehaviorsForWPF.txt +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Microsoft - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/licenses/CoreFX.txt b/licenses/dotnet_runtime.txt similarity index 100% rename from licenses/CoreFX.txt rename to licenses/dotnet_runtime.txt diff --git a/source/AssimpParser/Wavefront/Mesh.cs b/source/AssimpParser/Wavefront/Mesh.cs index 02fd95ba4..9c5b31464 100644 --- a/source/AssimpParser/Wavefront/Mesh.cs +++ b/source/AssimpParser/Wavefront/Mesh.cs @@ -32,50 +32,6 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// -// -// ****************************************************************************** -// -// AN EXCEPTION applies to all files in the ./test/models-nonbsd folder. -// These are 3d models for testing purposes, from various free sources -// on the internet. They are - unless otherwise stated - copyright of -// their respective creators, which may impose additional requirements -// on the use of their work. For any of these models, see -// .source.txt for more legal information. Contact us if you -// are a copyright holder and believe that we credited you inproperly or -// if you don't want your files to appear in the repository. -// -// -// ****************************************************************************** -// -// Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors -// http://code.google.com/p/poly2tri/ -// -// All rights reserved. -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of Poly2Tri nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace AssimpNET.Obj { diff --git a/source/AssimpParser/Wavefront/ObjFileData.cs b/source/AssimpParser/Wavefront/ObjFileData.cs index e6ebe7978..4d185f406 100644 --- a/source/AssimpParser/Wavefront/ObjFileData.cs +++ b/source/AssimpParser/Wavefront/ObjFileData.cs @@ -32,50 +32,6 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// -// -// ****************************************************************************** -// -// AN EXCEPTION applies to all files in the ./test/models-nonbsd folder. -// These are 3d models for testing purposes, from various free sources -// on the internet. They are - unless otherwise stated - copyright of -// their respective creators, which may impose additional requirements -// on the use of their work. For any of these models, see -// .source.txt for more legal information. Contact us if you -// are a copyright holder and believe that we credited you inproperly or -// if you don't want your files to appear in the repository. -// -// -// ****************************************************************************** -// -// Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors -// http://code.google.com/p/poly2tri/ -// -// All rights reserved. -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of Poly2Tri nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using System.Collections.Generic; using System.Linq; diff --git a/source/AssimpParser/Wavefront/ObjFileMtlImporter.cs b/source/AssimpParser/Wavefront/ObjFileMtlImporter.cs index 77b34357e..2606078f0 100644 --- a/source/AssimpParser/Wavefront/ObjFileMtlImporter.cs +++ b/source/AssimpParser/Wavefront/ObjFileMtlImporter.cs @@ -32,50 +32,6 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// -// -// ****************************************************************************** -// -// AN EXCEPTION applies to all files in the ./test/models-nonbsd folder. -// These are 3d models for testing purposes, from various free sources -// on the internet. They are - unless otherwise stated - copyright of -// their respective creators, which may impose additional requirements -// on the use of their work. For any of these models, see -// .source.txt for more legal information. Contact us if you -// are a copyright holder and believe that we credited you inproperly or -// if you don't want your files to appear in the repository. -// -// -// ****************************************************************************** -// -// Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors -// http://code.google.com/p/poly2tri/ -// -// All rights reserved. -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of Poly2Tri nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using System; using System.Collections.Generic; diff --git a/source/AssimpParser/Wavefront/ObjFileParser.cs b/source/AssimpParser/Wavefront/ObjFileParser.cs index d9383eaee..64d123a3e 100644 --- a/source/AssimpParser/Wavefront/ObjFileParser.cs +++ b/source/AssimpParser/Wavefront/ObjFileParser.cs @@ -32,50 +32,6 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// -// -// ****************************************************************************** -// -// AN EXCEPTION applies to all files in the ./test/models-nonbsd folder. -// These are 3d models for testing purposes, from various free sources -// on the internet. They are - unless otherwise stated - copyright of -// their respective creators, which may impose additional requirements -// on the use of their work. For any of these models, see -// .source.txt for more legal information. Contact us if you -// are a copyright holder and believe that we credited you inproperly or -// if you don't want your files to appear in the repository. -// -// -// ****************************************************************************** -// -// Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors -// http://code.google.com/p/poly2tri/ -// -// All rights reserved. -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of Poly2Tri nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using System; using System.Collections.Generic; diff --git a/source/AssimpParser/Wavefront/ObjTools.cs b/source/AssimpParser/Wavefront/ObjTools.cs index 28fb453a9..c0da5828c 100644 --- a/source/AssimpParser/Wavefront/ObjTools.cs +++ b/source/AssimpParser/Wavefront/ObjTools.cs @@ -32,50 +32,6 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// -// -// ****************************************************************************** -// -// AN EXCEPTION applies to all files in the ./test/models-nonbsd folder. -// These are 3d models for testing purposes, from various free sources -// on the internet. They are - unless otherwise stated - copyright of -// their respective creators, which may impose additional requirements -// on the use of their work. For any of these models, see -// .source.txt for more legal information. Contact us if you -// are a copyright holder and believe that we credited you inproperly or -// if you don't want your files to appear in the repository. -// -// -// ****************************************************************************** -// -// Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors -// http://code.google.com/p/poly2tri/ -// -// All rights reserved. -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of Poly2Tri nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using System.Collections.Generic; using System.Globalization; diff --git a/source/AssimpParser/X/XFileHelper.cs b/source/AssimpParser/X/XFileHelper.cs index c48280467..63b86b996 100644 --- a/source/AssimpParser/X/XFileHelper.cs +++ b/source/AssimpParser/X/XFileHelper.cs @@ -32,50 +32,6 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// -// -// ****************************************************************************** -// -// AN EXCEPTION applies to all files in the ./test/models-nonbsd folder. -// These are 3d models for testing purposes, from various free sources -// on the internet. They are - unless otherwise stated - copyright of -// their respective creators, which may impose additional requirements -// on the use of their work. For any of these models, see -// .source.txt for more legal information. Contact us if you -// are a copyright holder and believe that we credited you inproperly or -// if you don't want your files to appear in the repository. -// -// -// ****************************************************************************** -// -// Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors -// http://code.google.com/p/poly2tri/ -// -// All rights reserved. -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of Poly2Tri nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using System.Collections.Generic; using OpenBveApi.Colors; diff --git a/source/AssimpParser/X/XFileParser.cs b/source/AssimpParser/X/XFileParser.cs index e7fdd9e95..da13827d5 100644 --- a/source/AssimpParser/X/XFileParser.cs +++ b/source/AssimpParser/X/XFileParser.cs @@ -32,50 +32,6 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// -// -// ****************************************************************************** -// -// AN EXCEPTION applies to all files in the ./test/models-nonbsd folder. -// These are 3d models for testing purposes, from various free sources -// on the internet. They are - unless otherwise stated - copyright of -// their respective creators, which may impose additional requirements -// on the use of their work. For any of these models, see -// .source.txt for more legal information. Contact us if you -// are a copyright holder and believe that we credited you inproperly or -// if you don't want your files to appear in the repository. -// -// -// ****************************************************************************** -// -// Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors -// http://code.google.com/p/poly2tri/ -// -// All rights reserved. -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of Poly2Tri nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using System; using System.Collections.Generic; diff --git a/source/OpenBVE/Game/AI/AI.SimpleHuman.cs b/source/OpenBVE/Game/AI/AI.SimpleHuman.cs index 409f6489e..a8b2f30d7 100644 --- a/source/OpenBVE/Game/AI/AI.SimpleHuman.cs +++ b/source/OpenBVE/Game/AI/AI.SimpleHuman.cs @@ -176,7 +176,7 @@ private void PerformDefault() if (Train.Station >= 0 && Program.CurrentRoute.Stations[Train.Station].Type != StationType.Normal && Program.CurrentRoute.Stations[Train.Station].Type != StationType.RequestStop && Train.IsPlayerTrain) { // player's terminal station - if (Train.Plugin == null || Train.Plugin.LastReverser == -2) + if (!Train.Plugin.Enable || Train.Plugin.LastReverser == -2) { Train.ApplyReverser(0, false); } @@ -278,7 +278,7 @@ private void PerformDefault() else if (Train.Station >= 0 && stopIndex >= 0 && Program.CurrentRoute.Stations[Train.Station].Type != StationType.Normal && Program.CurrentRoute.Stations[Train.Station].Type != StationType.RequestStop && Train.IsPlayerTrain && Train.StationDistanceToStopPoint < Program.CurrentRoute.Stations[Train.Station].Stops[stopIndex].BackwardTolerance && -Train.StationDistanceToStopPoint < Program.CurrentRoute.Stations[Train.Station].Stops[stopIndex].ForwardTolerance && Math.Abs(Train.CurrentSpeed) < 0.25) { // player's terminal station (not boarding any longer) - if (Train.Plugin != null || Train.Plugin.LastReverser == -2) + if (Train.Plugin.Enable || Train.Plugin.LastReverser == -2) { Train.ApplyReverser(0, false); } @@ -891,7 +891,7 @@ public override void Trigger(double TimeElapsed) else if (Program.CurrentRoute.SecondsSinceMidnight - TimeLastProcessed >= CurrentInterval) { TimeLastProcessed = Program.CurrentRoute.SecondsSinceMidnight; - if (Train.Plugin != null && Train.Plugin.SupportsAI) + if (Train.Plugin.Enable && Train.Plugin.SupportsAI) { if (PerformPlugin() != AIResponse.None) { diff --git a/source/OpenBVE/Game/ObjectManager/AnimatedObjects/FunctionScripts.cs b/source/OpenBVE/Game/ObjectManager/AnimatedObjects/FunctionScripts.cs index 56716618c..7881a41c1 100644 --- a/source/OpenBVE/Game/ObjectManager/AnimatedObjects/FunctionScripts.cs +++ b/source/OpenBVE/Game/ObjectManager/AnimatedObjects/FunctionScripts.cs @@ -1106,22 +1106,18 @@ internal static void ExecuteFunctionScript(FunctionScript Function, TrainManager break; // safety case Instructions.SafetyPluginAvailable: - if (Train != null && Train.IsPlayerTrain && Train.Plugin != null) { + if (Train != null && Train.IsPlayerTrain && Train.Plugin.Enable) { Function.Stack[s] = TrainManager.PlayerTrain.Plugin.IsDefault ? 0.0 : 1.0; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.SafetyPluginState: - if (Train == null || Train.Plugin == null) { + if (Train == null || !Train.Plugin.Enable) { Function.Stack[s - 1] = 0.0; } else { int n = (int)Math.Round(Function.Stack[s - 1]); - if (n >= 0 & n < Train.Plugin.Panel.Length) { - Function.Stack[s - 1] = (double)Train.Plugin.Panel[n]; - } else { - Function.Stack[s - 1] = 0.0; - } + Function.Stack[s - 1] = Train.Plugin.GetPanelValue(n); } break; // timetable case Instructions.TimetableVisible: diff --git a/source/OpenBVE/Graphics/Renderers/Overlays.ATS.cs b/source/OpenBVE/Graphics/Renderers/Overlays.ATS.cs index f28110cf7..3dc4c0b7d 100644 --- a/source/OpenBVE/Graphics/Renderers/Overlays.ATS.cs +++ b/source/OpenBVE/Graphics/Renderers/Overlays.ATS.cs @@ -64,90 +64,90 @@ private void RenderATSLamps(HUD.Element Element, double TimeElapsed) HUD.Image Middle = o < 0 ? Element.TopMiddle : o == 0 ? Element.CenterMiddle : Element.BottomMiddle; HUD.Image Right = o < 0 ? Element.TopRight : o == 0 ? Element.CenterRight : Element.BottomRight; MessageColor sc = MessageColor.Gray; - if (TrainManager.PlayerTrain.Plugin.Panel.Length >= 272) + if (TrainManager.PlayerTrain.Plugin.PanelLength >= 272) { switch (CurrentLampCollection.Lamps[j].Type) { case LampType.Ats: - if (TrainManager.PlayerTrain.Plugin.Panel[256] != 0) + if (TrainManager.PlayerTrain.Plugin.GetPanelValue(256) != 0) { sc = MessageColor.Orange; } break; case LampType.AtsOperation: - if (TrainManager.PlayerTrain.Plugin.Panel[258] != 0) + if (TrainManager.PlayerTrain.Plugin.GetPanelValue(258) != 0) { sc = MessageColor.Red; } break; case LampType.AtsPPower: - if (TrainManager.PlayerTrain.Plugin.Panel[259] != 0) + if (TrainManager.PlayerTrain.Plugin.GetPanelValue(259) != 0) { sc = MessageColor.Green; } break; case LampType.AtsPPattern: - if (TrainManager.PlayerTrain.Plugin.Panel[260] != 0) + if (TrainManager.PlayerTrain.Plugin.GetPanelValue(260) != 0) { sc = MessageColor.Orange; } break; case LampType.AtsPBrakeOverride: - if (TrainManager.PlayerTrain.Plugin.Panel[261] != 0) + if (TrainManager.PlayerTrain.Plugin.GetPanelValue(261) != 0) { sc = MessageColor.Orange; } break; case LampType.AtsPBrakeOperation: - if (TrainManager.PlayerTrain.Plugin.Panel[262] != 0) + if (TrainManager.PlayerTrain.Plugin.GetPanelValue(262) != 0) { sc = MessageColor.Orange; } break; case LampType.AtsP: - if (TrainManager.PlayerTrain.Plugin.Panel[263] != 0) + if (TrainManager.PlayerTrain.Plugin.GetPanelValue(263) != 0) { sc = MessageColor.Green; } break; case LampType.AtsPFailure: - if (TrainManager.PlayerTrain.Plugin.Panel[264] != 0) + if (TrainManager.PlayerTrain.Plugin.GetPanelValue(264) != 0) { sc = MessageColor.Red; } break; case LampType.Atc: - if (TrainManager.PlayerTrain.Plugin.Panel[265] != 0) + if (TrainManager.PlayerTrain.Plugin.GetPanelValue(265) != 0) { sc = MessageColor.Orange; } break; case LampType.AtcPower: - if (TrainManager.PlayerTrain.Plugin.Panel[266] != 0) + if (TrainManager.PlayerTrain.Plugin.GetPanelValue(266) != 0) { sc = MessageColor.Orange; } break; case LampType.AtcUse: - if (TrainManager.PlayerTrain.Plugin.Panel[267] != 0) + if (TrainManager.PlayerTrain.Plugin.GetPanelValue(267) != 0) { sc = MessageColor.Orange; } break; case LampType.AtcEmergency: - if (TrainManager.PlayerTrain.Plugin.Panel[268] != 0) + if (TrainManager.PlayerTrain.Plugin.GetPanelValue(268) != 0) { sc = MessageColor.Red; } break; case LampType.Eb: - if (TrainManager.PlayerTrain.Plugin.Panel[270] != 0) + if (TrainManager.PlayerTrain.Plugin.GetPanelValue(270) != 0) { sc = MessageColor.Green; } break; case LampType.ConstSpeed: - if (TrainManager.PlayerTrain.Plugin.Panel[269] != 0) + if (TrainManager.PlayerTrain.Plugin.GetPanelValue(269) != 0) { sc = MessageColor.Orange; } diff --git a/source/OpenBVE/Graphics/Renderers/Overlays.Debug.cs b/source/OpenBVE/Graphics/Renderers/Overlays.Debug.cs index 1f8dc1d6e..c376a9dfc 100644 --- a/source/OpenBVE/Graphics/Renderers/Overlays.Debug.cs +++ b/source/OpenBVE/Graphics/Renderers/Overlays.Debug.cs @@ -183,8 +183,8 @@ private void RenderDebugOverlays() "", "=debug", "bvets hacks: " + (Interface.CurrentOptions.EnableBveTsHacks ? "enabled" : "disabled"), - "train plugin status: " + (TrainManager.PlayerTrain.Plugin != null ? (TrainManager.PlayerTrain.Plugin.PluginValid ? "ok" : "error") : "n/a"), - "train plugin message: " + (TrainManager.PlayerTrain.Plugin != null ? (TrainManager.PlayerTrain.Plugin.PluginMessage ?? "n/a") : "n/a"), + "train plugin status: " + (TrainManager.PlayerTrain.Plugin.Enable ? (TrainManager.PlayerTrain.Plugin.Valid ? "ok" : "error") : "n/a"), + "train plugin message: " + (TrainManager.PlayerTrain.Plugin.Enable ? (TrainManager.PlayerTrain.Plugin.Message ?? "n/a") : "n/a"), Game.InfoDebugString ?? "" }; double x = 4.0; @@ -223,14 +223,14 @@ private void RenderATSDebugOverlay() // debug renderer.Rectangle.Draw(null, new Vector2(0.0f, 0.0f), new Vector2(renderer.Screen.Width, renderer.Screen.Height), new Color128(0.5f, 0.5f, 0.5f, 0.5f)); string[] Lines; - if (TrainManager.PlayerTrain.Plugin.Panel.Length > 0) + if (TrainManager.PlayerTrain.Plugin.PanelLength > 0) { - Lines = new string[TrainManager.PlayerTrain.Plugin.Panel.Length + 2]; + Lines = new string[TrainManager.PlayerTrain.Plugin.PanelLength + 2]; Lines[0] = "=ATS Plugin Variables"; Lines[1] = ""; - for (int i = 2; i < TrainManager.PlayerTrain.Plugin.Panel.Length + 2; i++) + for (int i = 2; i < TrainManager.PlayerTrain.Plugin.PanelLength + 2; i++) { - Lines[i] = (i - 2).ToString("000") + " : " + TrainManager.PlayerTrain.Plugin.Panel[i - 2]; + Lines[i] = (i - 2).ToString("000") + " : " + TrainManager.PlayerTrain.Plugin.GetPanelValue(i - 2); } } else @@ -293,12 +293,12 @@ private void RenderBrakeSystemDebug() renderer.Rectangle.Draw(null, new Vector2((float)x, (float)y), new Vector2(r * w, h), Color128.Yellow); } x += w + 8.0; - // auxillary reservoir + // auxilliary reservoir if (TrainManager.PlayerTrain.Cars[i].CarBrake is AutomaticAirBrake | TrainManager.PlayerTrain.Cars[i].CarBrake is ElectromagneticStraightAirBrake) { if (!heading[1]) { - renderer.OpenGlString.Draw(Fonts.SmallFont, "Auxillary reservoir", new Point((int)x, (int)(oy - 16)), TextAlignment.TopLeft, Color128.White, true); + renderer.OpenGlString.Draw(Fonts.SmallFont, "Auxiliary reservoir", new Point((int)x, (int)(oy - 16)), TextAlignment.TopLeft, Color128.White, true); heading[1] = true; } renderer.Rectangle.Draw(null, new Vector2(x, y), new Vector2(w, h), Color128.Black); diff --git a/source/OpenBVE/Graphics/Renderers/Overlays.Lamp.cs b/source/OpenBVE/Graphics/Renderers/Overlays.Lamp.cs index f51aff4fb..1e4410371 100644 --- a/source/OpenBVE/Graphics/Renderers/Overlays.Lamp.cs +++ b/source/OpenBVE/Graphics/Renderers/Overlays.Lamp.cs @@ -109,7 +109,7 @@ internal LampCollection(TrainManager.Train Train) Width = 0.0f; Lamps = new Lamp[17]; int Count; - if (Train.Plugin == null || !Train.Plugin.IsDefault) + if (!Train.Plugin.Enable || !Train.Plugin.IsDefault) { Count = 0; } @@ -174,7 +174,7 @@ internal LampCollection(TrainManager.Train Train) Count = 0; } - if (Train.Plugin != null && Train.Plugin.IsDefault) + if (Train.Plugin.Enable && Train.Plugin.IsDefault) { if (Count != 0 & (eb | Train.Specs.HasConstSpeed)) { diff --git a/source/OpenBVE/OpenBve.csproj b/source/OpenBVE/OpenBve.csproj index 2396cbfbf..054cb8416 100644 --- a/source/OpenBVE/OpenBve.csproj +++ b/source/OpenBVE/OpenBve.csproj @@ -47,7 +47,7 @@ TRACE;DEBUG prompt 4 - 6 + 7.3 false @@ -62,7 +62,7 @@ true ..\..\bin_release\OpenBve.XML true - 6 + 7.3 false @@ -87,6 +87,15 @@ + + ..\..\packages\Namotion.Reflection.1.0.12\lib\net45\Namotion.Reflection.dll + + + ..\..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll + + + ..\..\packages\NJsonSchema.10.1.24\lib\net45\NJsonSchema.dll + ..\..\packages\OpenTK.3.2\lib\net20\OpenTK.dll @@ -104,6 +113,7 @@ ..\..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + ..\..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll @@ -246,7 +256,6 @@ - @@ -265,7 +274,11 @@ + + + + Form @@ -343,16 +356,9 @@ - - PluginManager.cs - - - PluginManager.cs - - diff --git a/source/OpenBVE/Simulation/TrainManager/Doors.cs b/source/OpenBVE/Simulation/TrainManager/Doors.cs index edd4cc33f..6929bbe75 100644 --- a/source/OpenBVE/Simulation/TrainManager/Doors.cs +++ b/source/OpenBVE/Simulation/TrainManager/Doors.cs @@ -153,7 +153,7 @@ private static void UpdateTrainDoors(Train Train, double TimeElapsed) if (oldState != newState) { - if (Train.Plugin != null) + if (Train.Plugin.Enable) { Train.Plugin.DoorChange(oldState, newState); } diff --git a/source/OpenBVE/Simulation/TrainManager/Functions.cs b/source/OpenBVE/Simulation/TrainManager/Functions.cs index c9c1ea100..9e74d27c0 100644 --- a/source/OpenBVE/Simulation/TrainManager/Functions.cs +++ b/source/OpenBVE/Simulation/TrainManager/Functions.cs @@ -57,7 +57,7 @@ internal static void JumpTrain(Train train, int stationIndex) { if (train.IsPlayerTrain) { - if (train.Plugin != null) + if (train.Plugin.Enable) { train.Plugin.BeginJump((OpenBveApi.Runtime.InitializationModes)Interface.CurrentOptions.TrainStart); } @@ -145,7 +145,7 @@ internal static void JumpTrain(Train train, int stationIndex) TrainManager.UpdateTrainObjects(0.0, true); if (train.IsPlayerTrain) { - if (train.Plugin != null) + if (train.Plugin.Enable) { train.Plugin.EndJump(); } diff --git a/source/OpenBVE/Simulation/TrainManager/Train/Plugin.cs b/source/OpenBVE/Simulation/TrainManager/Train/Plugin.cs deleted file mode 100644 index 8756fdcea..000000000 --- a/source/OpenBVE/Simulation/TrainManager/Train/Plugin.cs +++ /dev/null @@ -1,291 +0,0 @@ -using System; -using System.Reflection; -using OpenBveApi.Hosts; -using OpenBveApi.Interface; -using OpenBveApi.Runtime; - -namespace OpenBve -{ - /// The TrainManager is the root class containing functions to load and manage trains within the simulation world. - public static partial class TrainManager - { - /// The root class for a train within the simulation - public partial class Train - { - /// Loads a custom plugin for the specified train. - /// The absolute path to the train folder. - /// The encoding to be used. - /// Whether the plugin was loaded successfully. - internal bool LoadCustomPlugin(string trainFolder, System.Text.Encoding encoding) - { - string config = OpenBveApi.Path.CombineFile(trainFolder, "ats.cfg"); - if (!System.IO.File.Exists(config)) - { - return false; - } - - string Text = System.IO.File.ReadAllText(config, encoding); - Text = Text.Replace("\r", "").Replace("\n", ""); - if (Text.Length > 260) - { - /* - * String length is over max Windows path length, so - * comments or ATS plugin docs have been included in here - * e.g dlg70v40 - */ - string[] fileLines = System.IO.File.ReadAllLines(config); - for (int i = 0; i < fileLines.Length; i++) - { - int commentStart = fileLines[i].IndexOf(';'); - if (commentStart != -1) - { - fileLines[i] = fileLines[i].Substring(0, commentStart); - } - - fileLines[i] = fileLines[i].Trim(); - if (fileLines[i].Length != 0) - { - Text = fileLines[i]; - break; - } - } - } - string file; - try - { - file = OpenBveApi.Path.CombineFile(trainFolder, Text); - } - catch - { - Interface.AddMessage(MessageType.Error, true, "The train plugin path was malformed in " + config); - return false; - } - - string title = System.IO.Path.GetFileName(file); - if (!System.IO.File.Exists(file)) - { - if (Text.EndsWith(".dll") && encoding.Equals(System.Text.Encoding.Unicode)) - { - // Our filename ends with .dll so probably is not mangled Unicode - Interface.AddMessage(MessageType.Error, true, "The train plugin " + title + " could not be found in " + config); - return false; - } - - // Try again with ASCII encoding - Text = System.IO.File.ReadAllText(config, System.Text.Encoding.GetEncoding(1252)); - Text = Text.Replace("\r", "").Replace("\n", ""); - try - { - file = OpenBveApi.Path.CombineFile(trainFolder, Text); - } - catch - { - Interface.AddMessage(MessageType.Error, true, "The train plugin path was malformed in " + config); - return false; - } - - title = System.IO.Path.GetFileName(file); - if (!System.IO.File.Exists(file)) - { - // Nope, still not found - Interface.AddMessage(MessageType.Error, true, "The train plugin " + title + " could not be found in " + config); - return false; - } - - } - - Program.FileSystem.AppendToLogFile("Loading train plugin: " + file); - bool success = LoadPlugin(file, trainFolder); - if (success == false) - { - Loading.PluginError = Translations.GetInterfaceString("errors_plugin_failure1").Replace("[plugin]", file); - } - else - { - Program.FileSystem.AppendToLogFile("Train plugin loaded successfully."); - } - - return success; - } - - internal VehicleSpecs vehicleSpecs() - { - BrakeTypes brakeType = (BrakeTypes) Cars[DriverCar].CarBrake.brakeType; - int brakeNotches; - int powerNotches; - bool hasHoldBrake; - if (brakeType == BrakeTypes.AutomaticAirBrake) - { - brakeNotches = 2; - powerNotches = Handles.Power.MaximumNotch; - hasHoldBrake = false; - } - else - { - brakeNotches = Handles.Brake.MaximumNotch + (Handles.HasHoldBrake ? 1 : 0); - powerNotches = Handles.Power.MaximumNotch; - hasHoldBrake = Handles.HasHoldBrake; - } - - bool hasLocoBrake = Handles.HasLocoBrake; - int cars = Cars.Length; - return new VehicleSpecs(powerNotches, brakeType, brakeNotches, hasHoldBrake, hasLocoBrake, cars); - } - - /// Loads the specified plugin for the specified train. - /// The file to the plugin. - /// The train folder. - /// Whether the plugin was loaded successfully. - internal bool LoadPlugin(string pluginFile, string trainFolder) - { - string pluginTitle = System.IO.Path.GetFileName(pluginFile); - if (!System.IO.File.Exists(pluginFile)) - { - Interface.AddMessage(MessageType.Error, true, "The train plugin " + pluginTitle + " could not be found."); - return false; - } - - /* - * Unload plugin if already loaded. - * */ - if (Plugin != null) - { - UnloadPlugin(); - } - - /* - * Prepare initialization data for the plugin. - * */ - - InitializationModes mode = (InitializationModes) Interface.CurrentOptions.TrainStart; - /* - * Check if the plugin is a .NET plugin. - * */ - Assembly assembly; - try - { - assembly = Assembly.LoadFile(pluginFile); - } - catch (BadImageFormatException) - { - assembly = null; - } - catch (Exception ex) - { - Interface.AddMessage(MessageType.Error, false, "The train plugin " + pluginTitle + " could not be loaded due to the following exception: " + ex.Message); - return false; - } - - if (assembly != null) - { - Type[] types; - try - { - types = assembly.GetTypes(); - } - catch (ReflectionTypeLoadException ex) - { - foreach (Exception e in ex.LoaderExceptions) - { - Interface.AddMessage(MessageType.Error, false, "The train plugin " + pluginTitle + " raised an exception on loading: " + e.Message); - } - - return false; - } - - foreach (Type type in types) - { - if (typeof(IRuntime).IsAssignableFrom(type)) - { - if (type.FullName == null) - { - //Should never happen, but static code inspection suggests that it's possible.... - throw new InvalidOperationException(); - } - - IRuntime api = assembly.CreateInstance(type.FullName) as IRuntime; - Plugin = new NetPlugin(pluginFile, trainFolder, api, this); - if (Plugin.Load(vehicleSpecs(), mode)) - { - return true; - } - else - { - Plugin = null; - return false; - } - } - } - - Interface.AddMessage(MessageType.Error, false, "The train plugin " + pluginTitle + " does not export a train interface and therefore cannot be used with openBVE."); - return false; - } - - /* - * Check if the plugin is a Win32 plugin. - * - */ - try - { - if (!PluginManager.CheckWin32Header(pluginFile)) - { - Interface.AddMessage(MessageType.Error, false, "The train plugin " + pluginTitle + " is of an unsupported binary format and therefore cannot be used with openBVE."); - return false; - } - } - catch (Exception ex) - { - Interface.AddMessage(MessageType.Error, false, "The train plugin " + pluginTitle + " could not be read due to the following reason: " + ex.Message); - return false; - } - - if (Program.CurrentHost.Platform != HostPlatform.MicrosoftWindows | IntPtr.Size != 4) - { - Interface.AddMessage(MessageType.Warning, false, "The train plugin " + pluginTitle + " can only be used on 32-bit Microsoft Windows or compatible."); - return false; - } - - if (Program.CurrentHost.Platform == HostPlatform.MicrosoftWindows && !System.IO.File.Exists(AppDomain.CurrentDomain.BaseDirectory + "\\AtsPluginProxy.dll")) - { - Interface.AddMessage(MessageType.Warning, false, "AtsPluginProxy.dll is missing or corrupt- Please reinstall."); - return false; - } - - Plugin = new Win32Plugin(pluginFile, this); - if (Plugin.Load(vehicleSpecs(), mode)) - { - return true; - } - else - { - Plugin = null; - Interface.AddMessage(MessageType.Error, false, "The train plugin " + pluginTitle + " does not export a train interface and therefore cannot be used with openBVE."); - return false; - } - } - - /// Loads the default plugin for the specified train. - /// The train folder. - /// Whether the plugin was loaded successfully. - internal void LoadDefaultPlugin(string trainFolder) - { - string file = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Plugins"), "OpenBveAts.dll"); - bool success = LoadPlugin(file, trainFolder); - if (success) - { - Plugin.IsDefault = true; - } - } - - /// Unloads the currently loaded plugin, if any. - internal void UnloadPlugin() - { - if (Plugin != null) - { - Plugin.Unload(); - Plugin = null; - } - } - } - } -} diff --git a/source/OpenBVE/Simulation/TrainManager/Train/Train.Handles.cs b/source/OpenBVE/Simulation/TrainManager/Train/Train.Handles.cs index 1651f5bef..15a62127b 100644 --- a/source/OpenBVE/Simulation/TrainManager/Train/Train.Handles.cs +++ b/source/OpenBVE/Simulation/TrainManager/Train/Train.Handles.cs @@ -179,7 +179,7 @@ internal void ApplyNotch(int PowerValue, bool PowerRelative, int BrakeValue, boo Handles.Brake.Driver = b; Game.AddBlackBoxEntry(Game.BlackBoxEventToken.None); // plugin - if (Plugin != null) + if (Plugin.Enable) { Plugin.UpdatePower(); Plugin.UpdateBrake(); @@ -275,7 +275,7 @@ internal void ApplyReverser(int Value, bool Relative) if (a != r) { Handles.Reverser.Driver = (ReverserPosition)r; - if (Plugin != null) + if (Plugin.Enable) { Plugin.UpdateReverser(); } @@ -354,7 +354,7 @@ internal void ApplyEmergencyBrake() } // plugin - if (Plugin == null) return; + if (!Plugin.Enable) return; Plugin.UpdatePower(); Plugin.UpdateBrake(); } @@ -393,7 +393,7 @@ internal void UnapplyEmergencyBrake() } Handles.EmergencyBrake.Driver = false; // plugin - if (Plugin == null) return; + if (!Plugin.Enable) return; Plugin.UpdatePower(); Plugin.UpdateBrake(); } @@ -404,7 +404,7 @@ internal void UnapplyEmergencyBrake() internal void ApplyHoldBrake(bool Value) { Handles.HoldBrake.Driver = Value; - if (Plugin == null) return; + if (!Plugin.Enable) return; Plugin.UpdatePower(); Plugin.UpdateBrake(); } @@ -517,7 +517,7 @@ internal void ApplyAirBrakeHandle(AirBrakeHandleState newState) Handles.Brake.Driver = (int) newState; Game.AddBlackBoxEntry(Game.BlackBoxEventToken.None); // plugin - if (Plugin != null) + if (Plugin.Enable) { Plugin.UpdatePower(); Plugin.UpdateBrake(); @@ -602,7 +602,7 @@ internal void ApplyLocoAirBrakeHandle(AirBrakeHandleState newState) Handles.LocoBrake.Actual = (int) newState; //TODO: FIXME Game.AddBlackBoxEntry(Game.BlackBoxEventToken.None); // plugin - if (Plugin != null) + if (Plugin.Enable) { Plugin.UpdatePower(); Plugin.UpdateBrake(); diff --git a/source/OpenBVE/Simulation/TrainManager/Train/Train.cs b/source/OpenBVE/Simulation/TrainManager/Train/Train.cs index 836591da5..1d2a578ec 100644 --- a/source/OpenBVE/Simulation/TrainManager/Train/Train.cs +++ b/source/OpenBVE/Simulation/TrainManager/Train/Train.cs @@ -29,7 +29,7 @@ public partial class Train : AbstractTrain /// Holds the safety systems for the train internal TrainSafetySystems SafetySystems; /// The plugin used by this train. - internal PluginManager.Plugin Plugin; + internal readonly PluginManager Plugin; /// The driver body internal DriverBody DriverBody; @@ -79,6 +79,8 @@ internal Train(TrainState state) Specs.DoorOpenMode = DoorMode.AutomaticManualOverride; Specs.DoorCloseMode = DoorMode.AutomaticManualOverride; + + Plugin = new PluginManager(this); } @@ -307,7 +309,7 @@ public override void SectionChange() /// public override void UpdateBeacon(int transponderType, int sectionIndex, int optional) { - if (Plugin != null) + if (Plugin.Enable) { Plugin.UpdateBeacon(transponderType, sectionIndex, optional); } @@ -524,7 +526,7 @@ private void UpdatePhysicsAndControls(double TimeElapsed) UpdateTrainStation(this, TimeElapsed); UpdateTrainDoors(this, TimeElapsed); // delayed handles - if (Plugin == null) + if (!Plugin.Enable) { Handles.Power.Safety = Handles.Power.Driver; Handles.Brake.Safety = Handles.Brake.Driver; @@ -837,7 +839,7 @@ internal void UpdateAtmosphericConstants() /// Updates the safety system plugin for this train internal void UpdateSafetySystem() { - if (Plugin != null) + if (Plugin.Enable) { SignalData[] data = new SignalData[16]; int count = 0; diff --git a/source/OpenBVE/System/Functions/CrashHandler.cs b/source/OpenBVE/System/Functions/CrashHandler.cs index 3c41e2399..3c083ff69 100644 --- a/source/OpenBVE/System/Functions/CrashHandler.cs +++ b/source/OpenBVE/System/Functions/CrashHandler.cs @@ -111,15 +111,15 @@ internal static void LogCrash(string ExceptionText) //Route and train if (Program.CurrentRoute.Information.RouteFile != null) { - outputFile.WriteLine("Current routefile is: " + Program.CurrentRoute.Information.RouteFile); + outputFile.WriteLine("Current route file is: " + Program.CurrentRoute.Information.RouteFile); } if (Program.CurrentRoute.Information.TrainFolder != null) { outputFile.WriteLine("Current train is: " + Program.CurrentRoute.Information.TrainFolder); } - if (TrainManager.PlayerTrain != null && TrainManager.PlayerTrain.Plugin != null) + if (TrainManager.PlayerTrain != null && TrainManager.PlayerTrain.Plugin.Enable) { - outputFile.WriteLine("Current train plugin is: " + TrainManager.PlayerTrain.Plugin.PluginTitle); + outputFile.WriteLine($"Current train plugin is: {TrainManager.PlayerTrain.Plugin.Title}"); } //Errors and Warnings if (Program.CurrentRoute.Information.FilesNotFound != null) @@ -194,9 +194,9 @@ internal static void LoadingCrash(string ExceptionText, bool Train) { //We need the try/ catch block in order to catch errors which may have occured before initing the current route, train or plugin //These may occur if we feed dud data to the sim - outputFile.WriteLine("Current routefile is: " + Program.CurrentRoute.Information.RouteFile); + outputFile.WriteLine("Current route file is: " + Program.CurrentRoute.Information.RouteFile); outputFile.WriteLine("Current train is: " + Program.CurrentRoute.Information.TrainFolder); - outputFile.WriteLine("Current train plugin is: " + TrainManager.PlayerTrain.Plugin.PluginTitle); + outputFile.WriteLine($"Current train plugin is: {TrainManager.PlayerTrain.Plugin.Title}"); } catch { @@ -216,7 +216,7 @@ internal static void LoadingCrash(string ExceptionText, bool Train) } else { - outputFile.WriteLine("The current routefile caused the following exception: "); + outputFile.WriteLine("The current route file caused the following exception: "); } outputFile.WriteLine(ExceptionText); double MemoryUsed; diff --git a/source/OpenBVE/System/Functions/Timers.cs b/source/OpenBVE/System/Functions/Timers.cs index f703cfcd3..e381fc2a0 100644 --- a/source/OpenBVE/System/Functions/Timers.cs +++ b/source/OpenBVE/System/Functions/Timers.cs @@ -7,13 +7,6 @@ public static class CPreciseTimer { private static readonly bool UseEnvTicks; - //UNSAFE ZONE// - [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall), System.Security.SuppressUnmanagedCodeSecurity] - private static extern bool QueryPerformanceFrequency(ref long PerformanceFrequency); //gets the clock frequency for ticks per second - [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall), System.Security.SuppressUnmanagedCodeSecurity] - private static extern bool QueryPerformanceCounter(ref long PerformanceCount); //gets the number of elapsed ticks for future calculations - //UNSAFE ZONE// - static readonly long _ticksPerSecond = 0; //initialize variables static long _previousElapsedTime = 0; @@ -22,7 +15,7 @@ static CPreciseTimer() //Enclose this in a try/ catch block, and if it barfs, we're on Linux or OSX try { - QueryPerformanceFrequency(ref _ticksPerSecond); + NativeMethods.QueryPerformanceFrequency(ref _ticksPerSecond); //gets the number of ticks per second (frequency) after calling the C function in the constructor GetElapsedTime(); //Get rid of first rubbish result } @@ -58,7 +51,7 @@ public static double GetElapsedTime() return DeltaTime; } long time = 0; - QueryPerformanceCounter(ref time); //gets the number of ticks elapsed, pulled from the cloop + NativeMethods.QueryPerformanceCounter(ref time); //gets the number of ticks elapsed, pulled from the cloop double elapsedTime = (double) (time - _previousElapsedTime)/(double) _ticksPerSecond; //gets the total elapsed ticks by subtracting the current number of ticks from the last elapsed number of ticks. it then divides it by ticks per second to get the actual amount of time that has passed. _previousElapsedTime = time; //sets the previous elapsed ticks for the next calculation @@ -74,4 +67,4 @@ public static int GetClockTicks() } -} \ No newline at end of file +} diff --git a/source/OpenBVE/System/GameWindow.cs b/source/OpenBVE/System/GameWindow.cs index b72e369ec..cb312a3f5 100644 --- a/source/OpenBVE/System/GameWindow.cs +++ b/source/OpenBVE/System/GameWindow.cs @@ -24,7 +24,6 @@ using OpenBveApi.Math; using OpenBveApi.Routes; using OpenTK.Graphics.OpenGL; -using RouteManager2; using RouteManager2.MessageManager; using Path = System.IO.Path; using Vector2 = OpenTK.Vector2; @@ -297,12 +296,9 @@ protected override void OnUpdateFrame(FrameEventArgs e) MessageManager.UpdateMessages(); Game.UpdateScoreMessages(TimeElapsed); - for (int i = 0; i < InputDevicePlugin.AvailablePluginInfos.Count; i++) + foreach (InputDevicePlugin.Info info in Program.InputDevicePlugin.AvailableInfos.Where(x => x.Status == InputDevicePlugin.Status.Enable)) { - if (InputDevicePlugin.AvailablePluginInfos[i].Status == InputDevicePlugin.PluginInfo.PluginStatus.Enable) - { - InputDevicePlugin.AvailablePlugins[i].OnUpdateFrame(); - } + info.Api.OnUpdateFrame(); } } RenderTimeElapsed += TimeElapsed; @@ -350,32 +346,24 @@ protected override void OnLoad(EventArgs e) MouseMove += MainLoop.mouseMoveEvent; MouseWheel += MainLoop.mouseWheelEvent; - for (int i = 0; i < InputDevicePlugin.AvailablePluginInfos.Count; i++) + foreach (InputDevicePlugin.Info info in Program.InputDevicePlugin.AvailableInfos.Where(x => x.Status == InputDevicePlugin.Status.Enable)) { - if (InputDevicePlugin.AvailablePluginInfos[i].Status == InputDevicePlugin.PluginInfo.PluginStatus.Enable) + int addControlsLength = info.Api.Controls.Length; + Interface.Control[] addControls = new Interface.Control[addControlsLength]; + + for (int i = 0; i < addControlsLength; i++) { - int AddControlsLength = InputDevicePlugin.AvailablePlugins[i].Controls.Length; - Interface.Control[] AddControls = new Interface.Control[AddControlsLength]; - for (int j = 0; j < AddControlsLength; j++) - { - AddControls[j].Command = InputDevicePlugin.AvailablePlugins[i].Controls[j].Command; - AddControls[j].Method = Interface.ControlMethod.InputDevicePlugin; - AddControls[j].Option = InputDevicePlugin.AvailablePlugins[i].Controls[j].Option; - } - Interface.CurrentControls = Interface.CurrentControls.Concat(AddControls).ToArray(); - foreach (var Train in TrainManager.Trains) - { - if (Train.State != TrainState.Bogus) - { - if (Train.IsPlayerTrain) - { - InputDevicePlugin.AvailablePlugins[i].SetMaxNotch(Train.Handles.Power.MaximumDriverNotch, Train.Handles.Brake.MaximumDriverNotch); - } - } - } - InputDevicePlugin.AvailablePlugins[i].KeyDown += MainLoop.InputDevicePluginKeyDown; - InputDevicePlugin.AvailablePlugins[i].KeyUp += MainLoop.InputDevicePluginKeyUp; + addControls[i].Command = info.Api.Controls[i].Command; + addControls[i].Method = Interface.ControlMethod.InputDevicePlugin; + addControls[i].Option = info.Api.Controls[i].Option; } + + Interface.CurrentControls = Interface.CurrentControls.Concat(addControls).ToArray(); + + info.Api.SetMaxNotch(TrainManager.PlayerTrain.Handles.Power.MaximumDriverNotch, TrainManager.PlayerTrain.Handles.Brake.MaximumDriverNotch); + + info.Api.KeyDown += MainLoop.InputDevicePluginKeyDown; + info.Api.KeyUp += MainLoop.InputDevicePluginKeyUp; } } protected override void OnClosing(CancelEventArgs e) @@ -403,14 +391,11 @@ protected override void OnClosing(CancelEventArgs e) { if (TrainManager.Trains[i].State != TrainState.Bogus) { - TrainManager.Trains[i].UnloadPlugin(); + TrainManager.Trains[i].Plugin.Unload(); } } Program.Renderer.TextureManager.UnloadAllTextures(); - for (int i = 0; i < InputDevicePlugin.AvailablePluginInfos.Count; i++) - { - InputDevicePlugin.CallPluginUnload(i); - } + Program.InputDevicePlugin.CallPluginUnload(); if (MainLoop.Quit == MainLoop.QuitMode.ContinueGame && Program.CurrentHost.MonoRuntime) { //More forcefully close under Mono, stuff *still* hanging around.... @@ -801,11 +786,11 @@ private void SetupSimulation() Program.Renderer.CurrentTimetable = DisplayedTimetable.Custom; } } - //Create AI driver for the player train if specified via the commmand line - if (Game.InitialAIDriver == true) + //Create AI driver for the player train if specified via the command line + if (Game.InitialAIDriver) { TrainManager.PlayerTrain.AI = new Game.SimpleHumanDriverAI(TrainManager.PlayerTrain, Double.PositiveInfinity); - if (TrainManager.PlayerTrain.Plugin != null && !TrainManager.PlayerTrain.Plugin.SupportsAI) + if (TrainManager.PlayerTrain.Plugin.Enable && !TrainManager.PlayerTrain.Plugin.SupportsAI) { MessageManager.AddMessage(Translations.GetInterfaceString("notification_aiunable"),MessageDependency.None, GameMode.Expert, OpenBveApi.Colors.MessageColor.White, Program.CurrentRoute.SecondsSinceMidnight + 10.0, null); @@ -858,12 +843,12 @@ private void SetupSimulation() } Program.CurrentRoute.Information.FilesNotFound = NotFound; Program.CurrentRoute.Information.ErrorsAndWarnings = Messages; + //Print the plugin error encountered (If any) for 10s //This must be done after the simulation has init, as otherwise the timeout doesn't work - if (Loading.PluginError != null) + foreach (string pluginError in Loading.PluginErrors) { - MessageManager.AddMessage(Loading.PluginError, MessageDependency.None, GameMode.Expert, OpenBveApi.Colors.MessageColor.Red, Program.CurrentRoute.SecondsSinceMidnight + 5.0, null); - MessageManager.AddMessage(Translations.GetInterfaceString("errors_plugin_failure2"), MessageDependency.None, GameMode.Expert, OpenBveApi.Colors.MessageColor.Red, Program.CurrentRoute.SecondsSinceMidnight + 5.0, null); + MessageManager.AddMessage(pluginError, MessageDependency.None, GameMode.Expert, MessageColor.Red, Program.CurrentRoute.SecondsSinceMidnight + 5.0, null); } } loadComplete = true; diff --git a/source/OpenBVE/System/Input/ProcessControls.cs b/source/OpenBVE/System/Input/ProcessControls.cs index b44597a3c..efea2dc64 100644 --- a/source/OpenBVE/System/Input/ProcessControls.cs +++ b/source/OpenBVE/System/Input/ProcessControls.cs @@ -108,7 +108,7 @@ internal static void ProcessControls(double TimeElapsed) Program.Renderer.Camera.CurrentMode = CameraViewMode.InteriorLookAhead; } TrainManager.PlayerTrain.AI = new Game.SimpleHumanDriverAI(TrainManager.PlayerTrain, Double.PositiveInfinity); - if (TrainManager.PlayerTrain.Plugin != null && !TrainManager.PlayerTrain.Plugin.SupportsAI) + if (TrainManager.PlayerTrain.Plugin.Enable && !TrainManager.PlayerTrain.Plugin.SupportsAI) { MessageManager.AddMessage(Translations.GetInterfaceString("notification_aiunable"), MessageDependency.None, GameMode.Expert, MessageColor.White, Program.CurrentRoute.SecondsSinceMidnight + 10.0, null); } @@ -1415,13 +1415,9 @@ internal static void ProcessControls(double TimeElapsed) if (TrainManager.PlayerTrain.Cars[d].Horns.Length > j) { TrainManager.PlayerTrain.Cars[d].Horns[j].Play(); - if (TrainManager.PlayerTrain.Plugin != null) + if (TrainManager.PlayerTrain.Plugin.Enable) { - TrainManager.PlayerTrain.Plugin.HornBlow(j == 0 - ? OpenBveApi.Runtime.HornTypes.Primary - : j == 1 - ? OpenBveApi.Runtime.HornTypes.Secondary - : OpenBveApi.Runtime.HornTypes.Music); + TrainManager.PlayerTrain.Plugin.HornBlow((HornTypes)(j + 1)); } } } @@ -1447,7 +1443,7 @@ internal static void ProcessControls(double TimeElapsed) TrainManager.CloseTrainDoors(TrainManager.PlayerTrain, true, false); } } - if (TrainManager.PlayerTrain.Plugin != null) + if (TrainManager.PlayerTrain.Plugin.Enable) { TrainManager.PlayerTrain.Plugin.KeyDown(VirtualKeys.LeftDoors); } @@ -1475,7 +1471,7 @@ internal static void ProcessControls(double TimeElapsed) TrainManager.CloseTrainDoors(TrainManager.PlayerTrain, false, true); } } - if (TrainManager.PlayerTrain.Plugin != null) + if (TrainManager.PlayerTrain.Plugin.Enable) { TrainManager.PlayerTrain.Plugin.KeyDown(VirtualKeys.RightDoors); } @@ -1522,7 +1518,7 @@ internal static void ProcessControls(double TimeElapsed) case Translations.Command.RaisePantograph: case Translations.Command.LowerPantograph: case Translations.Command.MainBreaker: - if (TrainManager.PlayerTrain.Plugin != null) + if (TrainManager.PlayerTrain.Plugin.Enable) { TrainManager.PlayerTrain.Plugin.KeyDown( Translations.SecurityToVirtualKey(Interface.CurrentControls[i].Command)); @@ -1604,7 +1600,7 @@ internal static void ProcessControls(double TimeElapsed) { TrainManager.PlayerTrain.AI = new Game.SimpleHumanDriverAI(TrainManager.PlayerTrain, Double.PositiveInfinity); - if (TrainManager.PlayerTrain.Plugin != null && + if (TrainManager.PlayerTrain.Plugin.Enable && !TrainManager.PlayerTrain.Plugin.SupportsAI) { MessageManager.AddMessage( @@ -1700,7 +1696,7 @@ internal static void ProcessControls(double TimeElapsed) break; case Translations.Command.MiscTimeFactor: // time factor - if (!PluginManager.Plugin.DisableTimeAcceleration) + if (!PluginManager.DisableTimeAcceleration) { if (Interface.CurrentOptions.GameMode == GameMode.Expert) { @@ -1888,7 +1884,7 @@ internal static void ProcessControls(double TimeElapsed) case Translations.Command.RaisePantograph: case Translations.Command.LowerPantograph: case Translations.Command.MainBreaker: - if (TrainManager.PlayerTrain.Plugin != null) + if (TrainManager.PlayerTrain.Plugin.Enable) { TrainManager.PlayerTrain.Plugin.KeyUp( Translations.SecurityToVirtualKey(Interface.CurrentControls[i].Command)); @@ -1911,14 +1907,14 @@ internal static void ProcessControls(double TimeElapsed) break; case Translations.Command.DoorsLeft: TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].Doors[0].ButtonPressed = false; - if (TrainManager.PlayerTrain.Plugin != null) + if (TrainManager.PlayerTrain.Plugin.Enable) { TrainManager.PlayerTrain.Plugin.KeyUp(VirtualKeys.LeftDoors); } break; case Translations.Command.DoorsRight: TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].Doors[1].ButtonPressed = false; - if (TrainManager.PlayerTrain.Plugin != null) + if (TrainManager.PlayerTrain.Plugin.Enable) { TrainManager.PlayerTrain.Plugin.KeyUp(VirtualKeys.RightDoors); } diff --git a/source/OpenBVE/System/Loading.cs b/source/OpenBVE/System/Loading.cs index e094ffe16..7efd17ddc 100644 --- a/source/OpenBVE/System/Loading.cs +++ b/source/OpenBVE/System/Loading.cs @@ -1,11 +1,10 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; -using LibRender2; using OpenBve.Parsers.Train; -using OpenBveApi; using OpenBveApi.Interface; using OpenBveApi.Objects; using OpenBveApi.Runtime; @@ -64,7 +63,7 @@ internal static bool Cancel internal static double TrainProgressCurrentSum; internal static double TrainProgressCurrentWeight; /// Stores the plugin error message string, or a null reference if no error encountered - internal static string PluginError; + internal static readonly List PluginErrors = new List(); // load /// Initializes loading the route and train asynchronously. Set the Loading.Cancel member to cancel loading. Check the Loading.Complete member to see when loading has finished. @@ -159,16 +158,32 @@ private static void LoadThreaded() { try { LoadEverythingThreaded(); } catch (Exception ex) { - for (int i = 0; i < TrainManager.Trains.Length; i++) { - if (TrainManager.Trains[i] != null && TrainManager.Trains[i].Plugin != null) { - if (TrainManager.Trains[i].Plugin.LastException != null) { - Interface.AddMessage(MessageType.Critical, false, "The train plugin " + TrainManager.Trains[i].Plugin.PluginTitle + " caused a critical error in the route and train loader: " + TrainManager.Trains[i].Plugin.LastException.Message); - CrashHandler.LoadingCrash(TrainManager.Trains[i].Plugin.LastException + Environment.StackTrace, true); - Program.RestartArguments = " "; - Cancel = true; - return; - } + foreach (TrainManager.Train train in TrainManager.Trains) + { + if (train == null || !train.Plugin.Enable) + { + continue; + } + + var lastExceptions = train.Plugin.LastExceptions; + + if (lastExceptions.Length == 0) + { + continue; + } + + StringBuilder exceptionTexts = new StringBuilder(); + + foreach ((string title, Exception exception) in lastExceptions) + { + Interface.AddMessage(MessageType.Critical, false, $"The train plugin {title} caused a critical error in the route and train loader: {exception.Message}"); + exceptionTexts.AppendLine(exception.ToString()); } + + CrashHandler.LoadingCrash(exceptionTexts + Environment.StackTrace, true); + Program.RestartArguments = " "; + Cancel = true; + return; } if (ex is System.DllNotFoundException) { @@ -488,23 +503,9 @@ private static void LoadEverythingThreaded() { Program.CurrentRoute.UpdateAllSections(); } // load plugin - for (int i = 0; i < TrainManager.Trains.Length; i++) { - if (TrainManager.Trains[i].State != TrainState.Bogus) { - if (TrainManager.Trains[i].IsPlayerTrain) { - if (!TrainManager.Trains[i].LoadCustomPlugin(TrainManager.Trains[i].TrainFolder, CurrentTrainEncoding)) { - TrainManager.Trains[i].LoadDefaultPlugin( TrainManager.Trains[i].TrainFolder); - } - } else { - TrainManager.Trains[i].LoadDefaultPlugin(TrainManager.Trains[i].TrainFolder); - } - for (int j = 0; j < InputDevicePlugin.AvailablePluginInfos.Count; j++) { - if (InputDevicePlugin.AvailablePluginInfos[j].Status == InputDevicePlugin.PluginInfo.PluginStatus.Enable && InputDevicePlugin.AvailablePluginInfos[j] is ITrainInputDevice) - { - ITrainInputDevice trainInputDevice = (ITrainInputDevice)InputDevicePlugin.AvailablePlugins[j]; - trainInputDevice.SetVehicleSpecs(TrainManager.Trains[i].vehicleSpecs()); - } - } - } + foreach (TrainManager.Train train in TrainManager.Trains.Where(x => x.State != TrainState.Bogus)) + { + train.Plugin.Load(CurrentTrainEncoding, !train.IsPlayerTrain); } } diff --git a/source/OpenBVE/System/NativeMethods.cs b/source/OpenBVE/System/NativeMethods.cs new file mode 100644 index 000000000..14993d07b --- /dev/null +++ b/source/OpenBVE/System/NativeMethods.cs @@ -0,0 +1,37 @@ +using System; +using System.Runtime.InteropServices; + +namespace OpenBve +{ + internal static class NativeMethods + { + /// Gets the UID of the current user if running on a Unix based system + /// The UID + /// Used for checking if we are running as ROOT (don't!) + [DllImport("libc")] +#pragma warning disable IDE1006 // Suppress the VS2017 naming style rule, as this is an external syscall + internal static extern uint getuid(); +#pragma warning restore IDE1006 + + // gets the clock frequency for ticks per second + [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall), System.Security.SuppressUnmanagedCodeSecurity] + internal static extern bool QueryPerformanceFrequency(ref long PerformanceFrequency); + + // gets the number of elapsed ticks for future calculations + [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall), System.Security.SuppressUnmanagedCodeSecurity] + internal static extern bool QueryPerformanceCounter(ref long PerformanceCount); + + [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)] + internal static extern IntPtr LoadLibraryA([MarshalAs(UnmanagedType.LPStr), In] string lpLibFileName); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern IntPtr LoadLibraryW([MarshalAs(UnmanagedType.LPWStr), In] string lpLibFileName); + + [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)] + internal static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr), In] string lpProcName); + + [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool FreeLibrary(IntPtr hModule); + } +} diff --git a/source/OpenBVE/System/Plugins/LegacyPlugin.cs b/source/OpenBVE/System/Plugins/LegacyPlugin.cs deleted file mode 100644 index fc137f918..000000000 --- a/source/OpenBVE/System/Plugins/LegacyPlugin.cs +++ /dev/null @@ -1,431 +0,0 @@ -using System; -using System.ComponentModel; -using System.Runtime.InteropServices; -using System.Threading; -using OpenBveApi.Interface; -using OpenBveApi.Runtime; -using SoundManager; - -namespace OpenBve { - /// Represents a legacy Win32 plugin. - internal class Win32Plugin : PluginManager.Plugin { - - // --- win32 proxy calls --- - - [DllImport("AtsPluginProxy.dll", EntryPoint = "LoadDLL", ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)] - private static extern int Win32LoadDLL([MarshalAs(UnmanagedType.LPWStr)]string UnicodeFileName, [MarshalAs(UnmanagedType.LPStr)]string AnsiFileName); - - [DllImport("AtsPluginProxy.dll", EntryPoint = "UnloadDLL", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] - private static extern int Win32UnloadDLL(); - - [DllImport("AtsPluginProxy.dll", EntryPoint = "Load", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] - private static extern void Win32Load(); - - [DllImport("AtsPluginProxy.dll", EntryPoint = "Dispose", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] - private static extern void Win32Dispose(); - - [DllImport("AtsPluginProxy.dll", EntryPoint = "GetPluginVersion", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] - private static extern int Win32GetPluginVersion(); - - [DllImport("AtsPluginProxy.dll", EntryPoint = "SetVehicleSpec", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] - private static extern void Win32SetVehicleSpec(ref int spec); - - [DllImport("AtsPluginProxy.dll", EntryPoint = "Initialize", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] - private static extern void Win32Initialize(int brake); - - [DllImport("AtsPluginProxy.dll", EntryPoint = "Elapse", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] - private static extern void Win32Elapse(ref int handles, ref double state, ref int panel, ref int sound); - - [DllImport("AtsPluginProxy.dll", EntryPoint = "SetPower", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] - private static extern void Win32SetPower(int notch); - - [DllImport("AtsPluginProxy.dll", EntryPoint = "SetBrake", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] - private static extern void Win32SetBrake(int notch); - - [DllImport("AtsPluginProxy.dll", EntryPoint = "SetReverser", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] - private static extern void Win32SetReverser(int pos); - - [DllImport("AtsPluginProxy.dll", EntryPoint = "KeyDown", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] - private static extern void Win32KeyDown(int atsKeyCode); - - [DllImport("AtsPluginProxy.dll", EntryPoint = "KeyUp", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] - private static extern void Win32KeyUp(int atsKeyCode); - - [DllImport("AtsPluginProxy.dll", EntryPoint = "HornBlow", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] - private static extern void Win32HornBlow(int hornType); - - [DllImport("AtsPluginProxy.dll", EntryPoint = "DoorOpen", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] - private static extern void Win32DoorOpen(); - - [DllImport("AtsPluginProxy.dll", EntryPoint = "DoorClose", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] - private static extern void Win32DoorClose(); - - [DllImport("AtsPluginProxy.dll", EntryPoint = "SetSignal", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] - private static extern void Win32SetSignal(int signal); - - [DllImport("AtsPluginProxy.dll", EntryPoint = "SetBeaconData", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] - private static extern void Win32SetBeaconData(ref int beacon); - - [StructLayout(LayoutKind.Sequential, Size = 20)] - private struct Win32VehicleSpec { - internal int BrakeNotches; - internal int PowerNotches; - internal int AtsNotch; - internal int B67Notch; - internal int Cars; - } - - [StructLayout(LayoutKind.Sequential, Size = 40)] - private struct Win32VehicleState { - internal double Location; - internal float Speed; - internal int Time; - internal float BcPressure; - internal float MrPressure; - internal float ErPressure; - internal float BpPressure; - internal float SapPressure; - internal float Current; - } - - [StructLayout(LayoutKind.Sequential, Size = 16)] - private struct Win32Handles { - internal int Brake; - internal int Power; - internal int Reverser; - internal int ConstantSpeed; - } - - [StructLayout(LayoutKind.Sequential, Size = 16)] - private struct Win32BeaconData { - internal int Type; - internal int Signal; - internal float Distance; - internal int Optional; - } - - private static class SoundInstructions { - internal const int Stop = -10000; - internal const int PlayLooping = 0; - internal const int PlayOnce = 1; - internal const int Continue = 2; - } - - private static class ConstSpeedInstructions { - internal const int Continue = 0; - internal const int Enable = 1; - internal const int Disable = 2; - } - - // --- members --- - private readonly string PluginFile; - private readonly int[] Sound; - private readonly int[] LastSound; - private GCHandle PanelHandle; - private GCHandle SoundHandle; - - // --- constructors --- - internal Win32Plugin(string pluginFile, TrainManager.Train train) { - base.PluginTitle = System.IO.Path.GetFileName(pluginFile); - base.PluginValid = true; - base.PluginMessage = null; - base.Train = train; - base.Panel = new int[256]; - base.SupportsAI = false; - base.LastTime = 0.0; - base.LastReverser = -2; - base.LastPowerNotch = -1; - base.LastBrakeNotch = -1; - base.LastAspects = new int[] { }; - base.LastSection = -1; - base.LastException = null; - this.PluginFile = pluginFile; - this.Sound = new int[256]; - this.LastSound = new int[256]; - this.PanelHandle = new GCHandle(); - this.SoundHandle = new GCHandle(); - } - - // --- functions --- - internal override bool Load(VehicleSpecs specs, InitializationModes mode) { - int result; - bool retry = true; - retryLoad: - try { - result = Win32LoadDLL(this.PluginFile, this.PluginFile); - } catch (Exception ex) { - base.LastException = ex; - throw; - } - if (result == 0) { - if (retry) - { - /* - * Win32 plugin loading is unreliable on some systems - * Unable to reproduce this, but let's try a sleep & single retry attempt - */ - Thread.Sleep(100); - retry = false; - goto retryLoad; - } - int errorCode = Marshal.GetLastWin32Error(); - string errorMessage = new Win32Exception(errorCode).Message; - Interface.AddMessage(MessageType.Error, true, - String.Format("Error loading Win32 plugin: {0} (0x{1})", errorMessage, errorCode.ToString("x"))); - return false; - } - try { - Win32Load(); - } catch (Exception ex) { - base.LastException = ex; - throw; - } - int version; - try { - version = Win32GetPluginVersion(); - } catch (Exception ex) { - base.LastException = ex; - throw; - } - - if (version == 0 && System.IO.Path.GetFileName(PluginFile).ToLowerInvariant() != "ats2.dll" || version != 131072) { - Interface.AddMessage(MessageType.Error, false, "The train plugin " + base.PluginTitle + " is of an unsupported version."); - try { - Win32Dispose(); - } catch (Exception ex) { - base.LastException = ex; - throw; - } - Win32UnloadDLL(); - return false; - } - try { - Win32VehicleSpec win32Spec; - win32Spec.BrakeNotches = specs.BrakeNotches; - win32Spec.PowerNotches = specs.PowerNotches; - win32Spec.AtsNotch = specs.AtsNotch; - win32Spec.B67Notch = specs.B67Notch; - win32Spec.Cars = specs.Cars; - Win32SetVehicleSpec(ref win32Spec.BrakeNotches); - } catch (Exception ex) { - base.LastException = ex; - throw; - } - try { - Win32Initialize((int)mode); - } catch (Exception ex) { - base.LastException = ex; - throw; - } - UpdatePower(); - UpdateBrake(); - UpdateReverser(); - if (PanelHandle.IsAllocated) { - PanelHandle.Free(); - } - if (SoundHandle.IsAllocated) { - SoundHandle.Free(); - } - PanelHandle = GCHandle.Alloc(Panel, GCHandleType.Pinned); - SoundHandle = GCHandle.Alloc(Sound, GCHandleType.Pinned); - return true; - } - internal override void Unload() { - if (PanelHandle.IsAllocated) { - PanelHandle.Free(); - } - if (SoundHandle.IsAllocated) { - SoundHandle.Free(); - } - try { - Win32UnloadDLL(); - } catch (Exception ex) { - base.LastException = ex; - throw; - } - } - internal override void BeginJump(InitializationModes mode) { - try { - Win32Initialize((int)mode); - } catch (Exception ex) { - base.LastException = ex; - throw; - } - } - internal override void EndJump() { } - - protected override void Elapse(ElapseData data) { - try { - double time = data.TotalTime.Milliseconds; - Win32VehicleState win32State; - win32State.Location = data.Vehicle.Location; - win32State.Speed = (float)data.Vehicle.Speed.KilometersPerHour; - win32State.Time = (int)Math.Floor(time - 2073600000.0 * Math.Floor(time / 2073600000.0)); - win32State.BcPressure = (float)data.Vehicle.BcPressure; - win32State.MrPressure = (float)data.Vehicle.MrPressure; - win32State.ErPressure = (float)data.Vehicle.ErPressure; - win32State.BpPressure = (float)data.Vehicle.BpPressure; - win32State.SapPressure = (float)data.Vehicle.SapPressure; - win32State.Current = 0.0f; - Win32Handles win32Handles; - win32Handles.Brake = data.Handles.BrakeNotch; - win32Handles.Power = data.Handles.PowerNotch; - win32Handles.Reverser = data.Handles.Reverser; - win32Handles.ConstantSpeed = data.Handles.ConstSpeed ? ConstSpeedInstructions.Enable : ConstSpeedInstructions.Disable; - Win32Elapse(ref win32Handles.Brake, ref win32State.Location, ref base.Panel[0], ref this.Sound[0]); - data.Handles.Reverser = win32Handles.Reverser; - data.Handles.PowerNotch = win32Handles.Power; - data.Handles.BrakeNotch = win32Handles.Brake; - if (win32Handles.ConstantSpeed == ConstSpeedInstructions.Enable) { - data.Handles.ConstSpeed = true; - } else if (win32Handles.ConstantSpeed == ConstSpeedInstructions.Disable) { - data.Handles.ConstSpeed = false; - } else if (win32Handles.ConstantSpeed != ConstSpeedInstructions.Continue) { - this.PluginValid = false; - } - /* - * Process the sound instructions - * */ - for (int i = 0; i < this.Sound.Length; i++) { - if (this.Sound[i] != this.LastSound[i]) { - if (this.Sound[i] == SoundInstructions.Stop) { - if (i < base.Train.Cars[base.Train.DriverCar].Sounds.Plugin.Length) { - Program.Sounds.StopSound(Train.Cars[Train.DriverCar].Sounds.Plugin[i]); - } - } else if (this.Sound[i] > SoundInstructions.Stop & this.Sound[i] <= SoundInstructions.PlayLooping) { - if (i < base.Train.Cars[base.Train.DriverCar].Sounds.Plugin.Length) { - SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.Plugin[i].Buffer; - if (buffer != null) { - double volume = (double)(this.Sound[i] - SoundInstructions.Stop) / (double)(SoundInstructions.PlayLooping - SoundInstructions.Stop); - if (Program.Sounds.IsPlaying(Train.Cars[Train.DriverCar].Sounds.Plugin[i].Source)) - { - Train.Cars[Train.DriverCar].Sounds.Plugin[i].Source.Volume = volume; - } - else - { - Train.Cars[Train.DriverCar].Sounds.Plugin[i].Source = Program.Sounds.PlaySound(buffer, 1.0, volume, Train.Cars[Train.DriverCar].Sounds.Plugin[i].Position, Train.Cars[Train.DriverCar], true); - } - } - } - } else if (this.Sound[i] == SoundInstructions.PlayOnce) { - if (i < base.Train.Cars[base.Train.DriverCar].Sounds.Plugin.Length) { - SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.Plugin[i].Buffer; - if (buffer != null) - { - Train.Cars[Train.DriverCar].Sounds.Plugin[i].Source = Program.Sounds.PlaySound(buffer, 1.0, 1.0, Train.Cars[Train.DriverCar].Sounds.Plugin[i].Position, Train.Cars[Train.DriverCar], false); - } - } - this.Sound[i] = SoundInstructions.Continue; - } else if (this.Sound[i] != SoundInstructions.Continue) { - this.PluginValid = false; - } - this.LastSound[i] = this.Sound[i]; - } else { - if ((this.Sound[i] < SoundInstructions.Stop | this.Sound[i] > SoundInstructions.PlayLooping) && this.Sound[i] != SoundInstructions.PlayOnce & this.Sound[i] != SoundInstructions.Continue) { - this.PluginValid = false; - } - } - } - } catch (Exception ex) { - base.LastException = ex; - throw; - } - } - - protected override void SetReverser(int reverser) { - try { - Win32SetReverser(reverser); - } catch (Exception ex) { - base.LastException = ex; - throw; - } - } - - protected override void SetPower(int powerNotch) { - try { - Win32SetPower(powerNotch); - } catch (Exception ex) { - base.LastException = ex; - throw; - } - } - - protected override void SetBrake(int brakeNotch) { - try { - Win32SetBrake(brakeNotch); - } catch (Exception ex) { - base.LastException = ex; - throw; - } - } - internal override void KeyDown(VirtualKeys key) { - try { - Win32KeyDown((int)key); - } catch (Exception ex) { - base.LastException = ex; - throw; - } - } - internal override void KeyUp(VirtualKeys key) { - try { - Win32KeyUp((int)key); - } catch (Exception ex) { - base.LastException = ex; - throw; - } - } - internal override void HornBlow(HornTypes type) { - try { - Win32HornBlow((int)type); - } catch (Exception ex) { - base.LastException = ex; - throw; - } - } - internal override void DoorChange(DoorStates oldState, DoorStates newState) { - if (oldState == DoorStates.None & newState != DoorStates.None) { - try { - Win32DoorOpen(); - } catch (Exception ex) { - base.LastException = ex; - throw; - } - } else if (oldState != DoorStates.None & newState == DoorStates.None) { - try { - Win32DoorClose(); - } catch (Exception ex) { - base.LastException = ex; - throw; - } - } - } - - protected override void SetSignal(SignalData[] signal) { - if (base.LastAspects.Length == 0 || signal[0].Aspect != base.LastAspects[0]) { - try { - Win32SetSignal(signal[0].Aspect); - } catch (Exception ex) { - base.LastException = ex; - throw; - } - } - } - - protected override void SetBeacon(BeaconData beacon) { - try { - Win32BeaconData win32Beacon; - win32Beacon.Type = beacon.Type; - win32Beacon.Signal = beacon.Signal.Aspect; - win32Beacon.Distance = (float)beacon.Signal.Distance; - win32Beacon.Optional = beacon.Optional; - Win32SetBeaconData(ref win32Beacon.Type); - } catch (Exception ex) { - base.LastException = ex; - throw; - } - } - - protected override void PerformAI(AIData data) { } - - } -} diff --git a/source/OpenBVE/System/Plugins/NetPlugin.cs b/source/OpenBVE/System/Plugins/NetPlugin.cs deleted file mode 100644 index 7b4591967..000000000 --- a/source/OpenBVE/System/Plugins/NetPlugin.cs +++ /dev/null @@ -1,373 +0,0 @@ -using System; -using System.Threading; -using OpenBveApi; -using OpenBveApi.Colors; -using OpenBveApi.Interface; -using OpenBveApi.Math; -using OpenBveApi.Runtime; -using OpenBveApi.Sounds; -using RouteManager2.MessageManager; -using SoundManager; -using SoundHandle = OpenBveApi.Runtime.SoundHandle; - -namespace OpenBve { - /// Represents a .NET assembly plugin. - internal class NetPlugin : PluginManager.Plugin { - - // sound handle - internal class SoundHandleEx : SoundHandle { - internal readonly SoundSource Source; - internal SoundHandleEx(double volume, double pitch, SoundSource source) - { - base.MyVolume = volume; - base.MyPitch = pitch; - base.MyValid = true; - this.Source = source; - } - - internal new void Stop() - { - base.MyValid = false; - Source.Stop(); - } - } - - // --- members --- - /// The absolute on-disk path of the plugin's folder - private readonly string PluginFolder; - /// The absolute on-disk path of the train's folder - private readonly string TrainFolder; - private readonly IRuntime Api; - /// An array containing all of the plugin's current sound handles - private SoundHandleEx[] SoundHandles; - /// The total number of sound handles currently in use - private int SoundHandlesCount; - - // --- constructors --- - /// Initialises a new instance of a .Net based plugin - /// The absolute on-disk path of the plugin to load - /// The absolute on-disk path of the train's folder - /// The base OpenBVE runtime interface - /// The base train - internal NetPlugin(string pluginFile, string trainFolder, IRuntime api, TrainManager.Train train) { - base.PluginTitle = System.IO.Path.GetFileName(pluginFile); - base.PluginValid = true; - base.PluginMessage = null; - base.Train = train; - base.Panel = null; - base.SupportsAI = false; - base.LastTime = 0.0; - base.LastReverser = -2; - base.LastPowerNotch = -1; - base.LastBrakeNotch = -1; - base.LastAspects = new int[] { }; - base.LastSection = -1; - base.LastException = null; - this.PluginFolder = System.IO.Path.GetDirectoryName(pluginFile); - this.TrainFolder = trainFolder; - this.Api = api; - this.SoundHandles = new SoundHandleEx[16]; - this.SoundHandlesCount = 0; - } - - // --- functions --- - internal override bool Load(VehicleSpecs specs, InitializationModes mode) { - LoadProperties properties = new LoadProperties(this.PluginFolder, this.TrainFolder, this.PlaySound, this.PlaySound, this.AddInterfaceMessage, this.AddScore); - bool success; - try { - success = this.Api.Load(properties); - base.SupportsAI = properties.AISupport == AISupport.Basic; - } catch (Exception ex) { - if (ex is ThreadStateException) - { - //TTC plugin, broken when multi-threading is used - success = false; - properties.FailureReason = "This plugin does not function correctly with current versions of openBVE. Please ask the plugin developer to fix this."; - } - else - { - success = false; - properties.FailureReason = ex.Message; - } - } - if (success) { - base.Panel = properties.Panel ?? new int[] { }; - #if !DEBUG - try { - #endif - Api.SetVehicleSpecs(specs); - Api.Initialize(mode); - #if !DEBUG - } catch (Exception ex) { - base.LastException = ex; - throw; - } - #endif - UpdatePower(); - UpdateBrake(); - UpdateReverser(); - return true; - } else if (properties.FailureReason != null) { - Interface.AddMessage(MessageType.Error, false, "The train plugin " + base.PluginTitle + " failed to load for the following reason: " + properties.FailureReason); - return false; - } else { - Interface.AddMessage(MessageType.Error, false, "The train plugin " + base.PluginTitle + " failed to load for an unspecified reason."); - return false; - } - } - internal override void Unload() { - #if !DEBUG - try { - #endif - this.Api.Unload(); - #if !DEBUG - } catch (Exception ex) { - base.LastException = ex; - throw; - } - #endif - } - internal override void BeginJump(InitializationModes mode) { - #if !DEBUG - try { - #endif - this.Api.Initialize(mode); - #if !DEBUG - } catch (Exception ex) { - base.LastException = ex; - throw; - } - #endif - } - internal override void EndJump() { } - - protected override void Elapse(ElapseData data) { - #if !DEBUG - try { - #endif - this.Api.Elapse(data); - for (int i = 0; i < this.SoundHandlesCount; i++) { - if (this.SoundHandles[i].Stopped | this.SoundHandles[i].Source.State == SoundSourceState.Stopped) { - this.SoundHandles[i].Stop(); - this.SoundHandles[i] = this.SoundHandles[this.SoundHandlesCount - 1]; - this.SoundHandlesCount--; - i--; - } else { - this.SoundHandles[i].Source.Pitch = Math.Max(0.01, this.SoundHandles[i].Pitch); - this.SoundHandles[i].Source.Volume = Math.Max(0.0, this.SoundHandles[i].Volume); - } - } - #if !DEBUG - } catch (Exception ex) { - base.LastException = ex; - throw; - } - #endif - } - - protected override void SetReverser(int reverser) { - #if !DEBUG - try { - #endif - this.Api.SetReverser(reverser); - #if !DEBUG - } catch (Exception ex) { - base.LastException = ex; - throw; - } - #endif - } - - protected override void SetPower(int powerNotch) { - #if !DEBUG - try { - #endif - this.Api.SetPower(powerNotch); - #if !DEBUG - } catch (Exception ex) { - base.LastException = ex; - throw; - } - #endif - } - - protected override void SetBrake(int brakeNotch) { - #if !DEBUG - try { - #endif - this.Api.SetBrake(brakeNotch); - #if !DEBUG - } catch (Exception ex) { - base.LastException = ex; - throw; - } - #endif - } - internal override void KeyDown(VirtualKeys key) { - #if !DEBUG - try { - #endif - this.Api.KeyDown(key); - #if !DEBUG - } catch (Exception ex) { - base.LastException = ex; - throw; - } - #endif - } - internal override void KeyUp(VirtualKeys key) { - #if !DEBUG - try { - #endif - this.Api.KeyUp(key); - #if !DEBUG - } catch (Exception ex) { - base.LastException = ex; - throw; - } - #endif - } - internal override void HornBlow(HornTypes type) { - #if !DEBUG - try { - #endif - this.Api.HornBlow(type); - #if !DEBUG - } catch (Exception ex) { - base.LastException = ex; - throw; - } - #endif - } - internal override void DoorChange(DoorStates oldState, DoorStates newState) { - #if !DEBUG - try { - #endif - this.Api.DoorChange(oldState, newState); - #if !DEBUG - } catch (Exception ex) { - base.LastException = ex; - throw; - } - #endif - } - - protected override void SetSignal(SignalData[] signal) { - #if !DEBUG - try { - #endif - this.Api.SetSignal(signal); - #if !DEBUG - } catch (Exception ex) { - base.LastException = ex; - throw; - } - #endif - } - - protected override void SetBeacon(BeaconData beacon) { - #if !DEBUG - try { - #endif - this.Api.SetBeacon(beacon); - #if !DEBUG - } catch (Exception ex) { - base.LastException = ex; - throw; - } - #endif - } - - protected override void PerformAI(AIData data) { - #if !DEBUG - try { - #endif - this.Api.PerformAI(data); - #if !DEBUG - } catch (Exception ex) { - base.LastException = ex; - throw; - } - #endif - } - - /// May be called from a .Net plugin, in order to add a message to the in-game display - /// The message to display - /// The color in which to display the message - /// The time in seconds for which to display the message - internal void AddInterfaceMessage(string Message, MessageColor Color, double Time) - { - MessageManager.AddMessage(Message, MessageDependency.Plugin, GameMode.Expert, Color, Program.CurrentRoute.SecondsSinceMidnight + Time, null); - } - - /// May be called from a .Net plugin, in order to add a score to the post-game log - /// The score to add - /// The message to display in the post-game log - /// The color of the in-game message - /// The time in seconds for which to display the in-game message - internal void AddScore(int Score, string Message, MessageColor Color, double Timeout) - { - Game.CurrentScore.CurrentValue += Score; - - int n = Game.ScoreMessages.Length; - Array.Resize(ref Game.ScoreMessages, n + 1); - Game.ScoreMessages[n] = new Game.ScoreMessage - { - Value = Score, - Color = Color, - RendererPosition = new Vector2(0, 0), - RendererAlpha = 0.0, - Text = Message, - Timeout = Timeout - }; - } - - /// May be called from a .Net plugin, in order to play a sound from the driver's car of a train - /// The plugin-based of the sound to play - /// The volume of the sound- A volume of 1.0 represents nominal volume - /// The pitch of the sound- A pitch of 1.0 represents nominal pitch - /// Whether the sound is looped - /// The sound handle, or null if not successful - internal SoundHandleEx PlaySound(int index, double volume, double pitch, bool looped) - { - if (index >= 0 && index < this.Train.Cars[this.Train.DriverCar].Sounds.Plugin.Length && this.Train.Cars[this.Train.DriverCar].Sounds.Plugin[index].Buffer != null) { - SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.Plugin[index].Buffer; - OpenBveApi.Math.Vector3 position = this.Train.Cars[this.Train.DriverCar].Sounds.Plugin[index].Position; - SoundSource source = Program.Sounds.PlaySound(buffer, pitch, volume, position, Train.Cars[Train.DriverCar], looped); - if (this.SoundHandlesCount == this.SoundHandles.Length) { - Array.Resize(ref this.SoundHandles, this.SoundHandles.Length << 1); - } - this.SoundHandles[this.SoundHandlesCount] = new SoundHandleEx(volume, pitch, source); - this.SoundHandlesCount++; - return this.SoundHandles[this.SoundHandlesCount - 1]; - } - return null; - } - - /// May be called from a .Net plugin, in order to play a sound from a specific car of a train - /// The plugin-based of the sound to play - /// The volume of the sound- A volume of 1.0 represents nominal volume - /// The pitch of the sound- A pitch of 1.0 represents nominal pitch - /// Whether the sound is looped - /// The index of the car which is to emit the sound - /// The sound handle, or null if not successful - internal SoundHandleEx PlaySound(int index, double volume, double pitch, bool looped, int CarIndex) - { - if (index >= 0 && index < this.Train.Cars[this.Train.DriverCar].Sounds.Plugin.Length && this.Train.Cars[this.Train.DriverCar].Sounds.Plugin[index].Buffer != null && CarIndex < this.Train.Cars.Length && CarIndex >= 0) - { - SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.Plugin[index].Buffer; - OpenBveApi.Math.Vector3 position = this.Train.Cars[this.Train.DriverCar].Sounds.Plugin[index].Position; - SoundSource source = Program.Sounds.PlaySound(buffer, pitch, volume, position, Train.Cars[CarIndex], looped); - if (this.SoundHandlesCount == this.SoundHandles.Length) - { - Array.Resize(ref this.SoundHandles, this.SoundHandles.Length << 1); - } - this.SoundHandles[this.SoundHandlesCount] = new SoundHandleEx(volume, pitch, source); - this.SoundHandlesCount++; - return this.SoundHandles[this.SoundHandlesCount - 1]; - } - return null; - } - } - -} diff --git a/source/OpenBVE/System/Plugins/PluginManager.PluginEntry.cs b/source/OpenBVE/System/Plugins/PluginManager.PluginEntry.cs new file mode 100644 index 000000000..0d7e916f6 --- /dev/null +++ b/source/OpenBVE/System/Plugins/PluginManager.PluginEntry.cs @@ -0,0 +1,750 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using Newtonsoft.Json; +using OpenBveApi; +using OpenBveApi.Colors; +using OpenBveApi.Interface; +using OpenBveApi.Math; +using OpenBveApi.Runtime; +using OpenBveApi.Sounds; +using RouteManager2.MessageManager; +using SoundManager; +using Path = System.IO.Path; +using SoundHandle = OpenBveApi.Runtime.SoundHandle; + +namespace OpenBve +{ + internal partial class PluginManager + { + [JsonObject(MemberSerialization.OptIn)] + private abstract class Map + { + [JsonObject(MemberSerialization.OptIn)] + protected class Entry + { + [JsonProperty("src", Required = Required.Always)] + internal T Src + { + get; + set; + } + + [JsonProperty("dest", Required = Required.Always)] + internal T Dest + { + get; + set; + } + } + + [JsonProperty("entries")] + protected List Entries + { + get; + set; + } + + protected Map() + { + Entries = new List(); + } + } + + [JsonObject(MemberSerialization.OptIn)] + private class IndexMap : Map + { + [JsonProperty("offset")] + private int Offset + { + get; + set; + } + + internal (int[] dest, bool listed) ConvertToDest(int src) + { + int[] destArray = Entries + .Where(x => x.Src == src) + .Select(x => x.Dest) + .Distinct() + .ToArray(); + + if (destArray.Length != 0) + { + return (destArray.Where(x => x >= 0).ToArray(), true); + } + + int dest = Offset + src; + return (dest >= 0 ? new[] { dest } : new int[0], false); + } + + internal bool TryConvertToSrc(int dest, out int src) + { + for (int i = Entries.Count - 1; i >= 0; i--) + { + if (Entries[i].Dest != dest) + { + continue; + } + + src = Entries[i].Src; + return src >= 0; + } + + src = dest - Offset; + return src >= 0; + } + } + + [JsonObject(MemberSerialization.OptIn)] + private class KeyMap : Map + { + internal (VirtualKeys[] dest, bool listed) ConvertToDest(VirtualKeys src) + { + string[] destArray = Entries + .Where(x => x.Src.Equals(src.ToString())) + .Select(x => x.Dest) + .Distinct() + .ToArray(); + + if (destArray.Length != 0) + { + return ( + destArray + .Where(x => !x.Equals("None")) + .Select(x => (VirtualKeys)Enum.Parse(typeof(VirtualKeys), x)) + .ToArray(), + true + ); + } + + return (new[] { src }, false); + } + + internal bool TryConvertToSrc(VirtualKeys dest, out VirtualKeys src) + { + for (int i = Entries.Count - 1; i >= 0; i--) + { + if (!Entries[i].Dest.Equals(dest.ToString())) + { + continue; + } + + return Enum.TryParse(Entries[i].Src, out src); + } + + src = dest; + return true; + } + } + + private class Plugin + { + [JsonObject(MemberSerialization.OptIn)] + internal class Entry + { + [JsonProperty("filePath", Required = Required.Always)] + internal string FilePath + { + get; + set; + } + + [JsonProperty("panelIndexMap")] + internal IndexMap PanelIndexMap + { + get; + set; + } + + [JsonProperty("soundIndexMap")] + internal IndexMap SoundIndexMap + { + get; + set; + } + + [JsonProperty("keyMap")] + internal KeyMap KeyMap + { + get; + set; + } + + internal Entry() + { + PanelIndexMap = new IndexMap(); + SoundIndexMap = new IndexMap(); + KeyMap = new KeyMap(); + } + } + + private class SoundHandleEx : SoundHandle + { + internal readonly SoundSource[] Sources; + + internal SoundHandleEx(double volume, double pitch, SoundSource[] sources) + { + MyVolume = volume; + MyPitch = pitch; + MyValid = true; + Sources = sources; + } + + internal new void Stop() + { + MyValid = false; + + foreach (SoundSource source in Sources) + { + source.Stop(); + } + } + } + + private readonly PluginManager manager; + + private readonly Entry entry; + + private readonly IRuntime api; //.NET&C++ + + /// The file title of the plugin, including the file extension. + internal readonly string Title; + + /// The array of panel variables. + internal int[] Panel + { + get; + private set; + } + + internal int[] LastPanel + { + get; + private set; + } + + /// The last reverser reported to the plugin. + private int lastReverser; + + /// The last power notch reported to the plugin. + private int lastPowerNotch; + + /// The last brake notch reported to the plugin. + private int lastBrakeNotch; + + private Dictionary soundIndexDictionary; + + /// An array containing all of the plugin's current sound handles + private SoundHandleEx[] soundHandles; + + /// The total number of sound handles currently in use + private int soundHandlesCount; + + /// Whether the plugin supports the AI. + internal bool SupportsAI + { + get; + private set; + } + + /// The last exception the plugin raised. + internal Exception LastException + { + get; + // ReSharper disable once UnusedAutoPropertyAccessor.Local + private set; + } + + internal Plugin(PluginManager manager, Entry entry, IRuntime api) + { + this.manager = manager; + this.entry = entry; + this.api = api; + Title = Path.GetFileName(entry.FilePath); + soundIndexDictionary = new Dictionary(); + soundHandles = new SoundHandleEx[16]; + } + + /// Called to load and initialize the plugin. + /// The train specifications. + /// The initialization mode of the train. + /// Whether loading the plugin was successful. + internal bool Load(VehicleSpecs specs, InitializationModes mode) + { + LoadProperties properties = new LoadProperties(Path.GetDirectoryName(entry.FilePath), manager.train.TrainFolder, PlaySound, PlaySound, AddInterfaceMessage, AddScore); + + bool success; + try + { + success = api.Load(properties); + } + catch (Exception ex) + { + if (ex is ThreadStateException) + { + // TTC plugin, broken when multi-threading is used + success = false; + properties.FailureReason = "This plugin does not function correctly with current versions of openBVE. Please ask the plugin developer to fix this."; + } + else + { + success = false; + properties.FailureReason = ex.Message; + } + } + + if (!success) + { + if (properties.FailureReason != null) + { + Interface.AddMessage(MessageType.Error, false, $"The train plugin {Title} failed to load for the following reason: {properties.FailureReason}"); + } + else + { + Interface.AddMessage(MessageType.Error, false, $"The train plugin {Title} failed to load for an unspecified reason."); + } + + return false; + } + + lastReverser = -2; + lastPowerNotch = -1; + lastBrakeNotch = -1; + Panel = properties.Panel ?? new int[0]; + LastPanel = new int[Panel.Length]; + CreateSoundIndexDictionary(); + SupportsAI = properties.AISupport == AISupport.Basic; + +#if !DEBUG + try +#endif + { + api.SetVehicleSpecs(specs); + api.Initialize(mode); + } +#if !DEBUG + catch (Exception ex) + { + LastException = ex; + } +#endif + + return true; + } + + /// Called to unload the plugin. + internal void Unload() + { +#if !DEBUG + try +#endif + { + api.Unload(); + } +#if !DEBUG + catch (Exception ex) + { + LastException = ex; + } +#endif + } + + /// Called before the train jumps to a different location. + /// The initialization mode of the train. + internal void BeginJump(InitializationModes mode) + { +#if !DEBUG + try +#endif + { + api.Initialize(mode); + } +#if !DEBUG + catch (Exception ex) + { + LastException = ex; + } +#endif + } + + /// Called when the train has finished jumping to a different location. + internal void EndJump() + { + } + + /// Called every frame to update the plugin. + /// The data passed to the plugin on Elapse. + /// This function should not be called directly. Call UpdatePlugin instead. + internal void Elapse(ElapseData data) + { + for (int i = 0; i < Panel.Length; i++) + { + LastPanel[i] = Panel[i]; + } + +#if !DEBUG + try +#endif + { + api.Elapse(data); + } +#if !DEBUG + catch (Exception ex) + { + LastException = ex; + } +#endif + + for (int i = 0; i < soundHandlesCount; i++) + { + if (soundHandles[i].Stopped | soundHandles[i].Sources.All(x => x.State == SoundSourceState.Stopped)) + { + soundHandles[i].Stop(); + soundHandles[i] = soundHandles[soundHandlesCount - 1]; + soundHandlesCount--; + i--; + } + else + { + double pitch = Math.Max(0.01, soundHandles[i].Pitch); + double volume = Math.Max(0.0, soundHandles[i].Volume); + + foreach (SoundSource source in soundHandles[i].Sources) + { + source.Pitch = pitch; + source.Volume = volume; + } + } + } + } + + /// Called to indicate a change of the reverser. + /// The reverser. + /// This function should not be called directly. Call UpdateReverser instead. + internal void SetReverser(int reverser) + { + if (reverser == lastReverser) + { + return; + } + +#if !DEBUG + try +#endif + { + api.SetReverser(reverser); + } +#if !DEBUG + catch (Exception ex) + { + LastException = ex; + } +#endif + + lastReverser = reverser; + } + + /// Called to indicate a change of the power notch. + /// The power notch. + /// This function should not be called directly. Call UpdatePower instead. + internal void SetPower(int powerNotch) + { + if (powerNotch == lastPowerNotch) + { + return; + } + +#if !DEBUG + try +#endif + { + api.SetPower(powerNotch); + } +#if !DEBUG + catch (Exception ex) + { + LastException = ex; + } +#endif + + lastPowerNotch = powerNotch; + } + + /// Called to indicate a change of the brake notch. + /// The brake notch. + /// This function should not be called directly. Call UpdateBrake instead. + internal void SetBrake(int brakeNotch) + { + if (brakeNotch == lastBrakeNotch) + { + return; + } + +#if !DEBUG + try +#endif + { + api.SetBrake(brakeNotch); + } +#if !DEBUG + catch (Exception ex) + { + LastException = ex; + } +#endif + + lastBrakeNotch = brakeNotch; + } + + /// Called when a virtual key is pressed. + /// The virtual key that was pressed. + internal void KeyDown(VirtualKeys key) + { +#if !DEBUG + try +#endif + { + if (entry.KeyMap.TryConvertToSrc(key, out VirtualKeys convertedKey)) + { + api.KeyDown(convertedKey); + } + } +#if !DEBUG + catch (Exception ex) + { + LastException = ex; + } +#endif + } + + /// Called when a virtual key is released. + /// The virtual key that was released. + internal void KeyUp(VirtualKeys key) + { +#if !DEBUG + try +#endif + { + if (entry.KeyMap.TryConvertToSrc(key, out VirtualKeys convertedKey)) + { + api.KeyUp(convertedKey); + } + } +#if !DEBUG + catch (Exception ex) + { + LastException = ex; + } +#endif + } + + /// Called when a horn is played or stopped. + /// The type of horn. + internal void HornBlow(HornTypes type) + { +#if !DEBUG + try +#endif + { + api.HornBlow(type); + } +#if !DEBUG + catch (Exception ex) + { + LastException = ex; + } +#endif + } + + /// Called when the state of the doors changes. + /// The old state of the doors. + /// The new state of the doors. + internal void DoorChange(DoorStates oldState, DoorStates newState) + { +#if !DEBUG + try +#endif + { + api.DoorChange(oldState, newState); + } +#if !DEBUG + catch (Exception ex) + { + LastException = ex; + } +#endif + } + + /// Is called when the aspect in the current or any of the upcoming sections changes. + /// Signal information per section. In the array, index 0 is the current section, index 1 the upcoming section, and so on. + /// This function should not be called directly. Call UpdateSignal instead. + internal void SetSignal(SignalData[] signal) + { +#if !DEBUG + try +#endif + { + api.SetSignal(signal); + } +#if !DEBUG + catch (Exception ex) + { + LastException = ex; + } +#endif + } + + /// Called when the train passes a beacon. + /// The beacon data. + /// This function should not be called directly. Call UpdateBeacon instead. + internal void SetBeacon(BeaconData beacon) + { +#if !DEBUG + try +#endif + { + api.SetBeacon(beacon); + } +#if !DEBUG + catch (Exception ex) + { + LastException = ex; + } +#endif + } + + /// Called when the AI should be performed. + /// The AI data. + /// This function should not be called directly. Call UpdateAI instead. + internal void PerformAI(AIData data) + { +#if !DEBUG + try +#endif + { + api.PerformAI(data); + } +#if !DEBUG + catch (Exception ex) + { + LastException = ex; + } +#endif + } + + internal Dictionary GetPanelIndexDictionary() + { + Dictionary dictionary = new Dictionary(); + + for (int srcIndex = 0; srcIndex < Panel.Length; srcIndex++) + { + (int[] destIndices, bool listed) = entry.PanelIndexMap.ConvertToDest(srcIndex); + + foreach (int destIndex in destIndices) + { + if (dictionary.ContainsKey(destIndex)) + { + if (dictionary[destIndex].listed) + { + continue; + } + + dictionary[destIndex] = (srcIndex, listed); + } + else + { + dictionary.Add(destIndex, (srcIndex, listed)); + } + } + } + + return dictionary.ToDictionary(x => x.Key, x => x.Value.srcIndex); + } + + private void CreateSoundIndexDictionary() + { + soundIndexDictionary = Enumerable.Range(0, manager.train.Cars[manager.train.DriverCar].Sounds.Plugin.Length) + .Select(x => entry.SoundIndexMap.TryConvertToSrc(x, out int y) ? y : -1) + .Select((x, i) => new { SrcIndex = x, DestIndex = i }) + .Where(x => x.SrcIndex >= 0) + .GroupBy(x => x.SrcIndex, x => x.DestIndex, (x, y) => new { Key = x, Value = y.ToArray() }) + .ToDictionary(x => x.Key, x => x.Value); + } + + /// May be called from a .Net plugin, in order to play a sound from the driver's car of a train + /// The plugin-based of the sound to play + /// The volume of the sound- A volume of 1.0 represents nominal volume + /// The pitch of the sound- A pitch of 1.0 represents nominal pitch + /// Whether the sound is looped + /// The sound handle, or null if not successful + private SoundHandleEx PlaySound(int index, double volume, double pitch, bool looped) + { + return PlaySound(index, volume, pitch, looped, manager.train.DriverCar); + } + + /// May be called from a .Net plugin, in order to play a sound from a specific car of a train + /// The plugin-based of the sound to play + /// The volume of the sound- A volume of 1.0 represents nominal volume + /// The pitch of the sound- A pitch of 1.0 represents nominal pitch + /// Whether the sound is looped + /// The index of the car which is to emit the sound + /// The sound handle, or null if not successful + private SoundHandleEx PlaySound(int index, double volume, double pitch, bool looped, int carIndex) + { + if (index < 0 || !soundIndexDictionary.ContainsKey(index) || carIndex >= manager.train.Cars.Length || carIndex < 0) + { + return null; + } + + SoundSource[] sources = soundIndexDictionary[index] + .Select(x => manager.train.Cars[manager.train.DriverCar].Sounds.Plugin[x]) + .Where(x => x.Buffer != null) + .Select(x => Program.Sounds.PlaySound(x.Buffer, pitch, volume, x.Position, manager.train.Cars[carIndex], looped)) + .ToArray(); + + if (soundHandlesCount == soundHandles.Length) + { + Array.Resize(ref soundHandles, soundHandles.Length << 1); + } + soundHandles[soundHandlesCount] = new SoundHandleEx(volume, pitch, sources); + soundHandlesCount++; + + return soundHandles[soundHandlesCount - 1]; + } + + /// May be called from a .Net plugin, in order to add a message to the in-game display + /// The message to display + /// The color in which to display the message + /// The time in seconds for which to display the message + private void AddInterfaceMessage(string message, MessageColor color, double time) + { + MessageManager.AddMessage(message, MessageDependency.Plugin, GameMode.Expert, color, Program.CurrentRoute.SecondsSinceMidnight + time, null); + } + + /// May be called from a .Net plugin, in order to add a score to the post-game log + /// The score to add + /// The message to display in the post-game log + /// The color of the in-game message + /// The time in seconds for which to display the in-game message + private void AddScore(int score, string message, MessageColor color, double timeout) + { + Game.CurrentScore.CurrentValue += score; + + int n = Game.ScoreMessages.Length; + Array.Resize(ref Game.ScoreMessages, n + 1); + Game.ScoreMessages[n] = new Game.ScoreMessage + { + Value = score, + Color = color, + RendererPosition = new Vector2(0, 0), + RendererAlpha = 0.0, + Text = message, + Timeout = timeout + }; + } + + } + } +} diff --git a/source/OpenBVE/System/Plugins/PluginManager.Win32Runtime.cs b/source/OpenBVE/System/Plugins/PluginManager.Win32Runtime.cs new file mode 100644 index 000000000..3c42c3d30 --- /dev/null +++ b/source/OpenBVE/System/Plugins/PluginManager.Win32Runtime.cs @@ -0,0 +1,499 @@ +using System; +using System.ComponentModel; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; +using OpenBveApi.Runtime; + +namespace OpenBve +{ + internal partial class PluginManager + { + private class Win32Runtime : IRuntime, IDisposable + { + private enum InitialHandlePosition + { + Service = 0, + Emergency = 1, + Removed = 2 + } + + private static class SoundInstruction + { + internal const int Stop = -10000; + internal const int PlayLooping = 0; + internal const int Play = 1; + internal const int Continue = 2; + } + + private enum ConstSpeedInstruction + { + Continue = 0, + Enable = 1, + Disable = 2 + } + + private enum Win32HornType + { + Primary = 0, + Secondary = 1, + Music = 2 + } + + [StructLayout(LayoutKind.Sequential)] + private struct Win32VehicleSpec + { + internal int BrakeNotches; + internal int PowerNotches; + internal int AtsNotch; + internal int B67Notch; + internal int Cars; + } + + [StructLayout(LayoutKind.Sequential)] + private struct Win32VehicleState + { + internal double Location; + internal float Speed; + internal int Time; + internal float BcPressure; + internal float MrPressure; + internal float ErPressure; + internal float BpPressure; + internal float SapPressure; + internal float Current; + } + + [StructLayout(LayoutKind.Sequential)] + private readonly struct Win32Handles + { + internal readonly int Brake; + internal readonly int Power; + internal readonly int Reverser; + internal readonly ConstSpeedInstruction ConstantSpeed; + } + + [StructLayout(LayoutKind.Sequential)] + private struct Win32BeaconData + { + internal int Type; + internal int Signal; + internal float Distance; + internal int Optional; + } + + #region Definition of Win32 plugin's delegate + + private delegate void Win32Load(); + + private delegate void Win32Dispose(); + + private delegate int Win32GetPluginVersion(); + + private delegate void Win32SetVehicleSpec([In] Win32VehicleSpec vehicleSpec); + + private delegate void Win32Initialize(InitialHandlePosition brake); + + private delegate Win32Handles Win32Elapse([In] Win32VehicleState vehicleState, [In, Out] int[] panel, [In, Out] int[] sound); + + private delegate void Win32SetPower(int notch); + + private delegate void Win32SetBrake(int notch); + + private delegate void Win32SetReverser(int pos); + + private delegate void Win32KeyDown(VirtualKeys atsKeyCode); + + private delegate void Win32KeyUp(VirtualKeys atsKeyCode); + + private delegate void Win32HornBlow(Win32HornType hornType); + + private delegate void Win32DoorOpen(); + + private delegate void Win32DoorClose(); + + private delegate void Win32SetSignal(int signal); + + private delegate void Win32SetBeaconData([In] Win32BeaconData beaconData); + + #endregion + + private const int PLUGIN_VERSION = 0x20000; + + private string pluginFilePath; + + private IntPtr dllHandle; + + private bool disposed; + + #region Instance of Win32 plugin's delegate + + private Win32Load win32Load; + + private Win32Dispose win32Dispose; + + private Win32GetPluginVersion win32GetPluginVersion; + + private Win32SetVehicleSpec win32SetVehicleSpec; + + private Win32Initialize win32Initialize; + + private Win32Elapse win32Elapse; + + private Win32SetPower win32SetPower; + + private Win32SetBrake win32SetBrake; + + private Win32SetReverser win32SetReverser; + + private Win32KeyDown win32KeyDown; + + private Win32KeyUp win32KeyUp; + + private Win32HornBlow win32HornBlow; + + private Win32DoorOpen win32DoorOpen; + + private Win32DoorClose win32DoorClose; + + private Win32SetSignal win32SetSignal; + + private Win32SetBeaconData win32SetBeaconData; + + #endregion + + private readonly int[] lastAspects; + + private LoadProperties loadProperties; + + private int[] soundInstructions; + + private int[] lastSoundInstructions; + + private SoundHandle[] soundHandles; + + internal Win32Runtime(string pluginFilePath, int[] lastAspects) + { + this.pluginFilePath = pluginFilePath; + dllHandle = IntPtr.Zero; + this.lastAspects = lastAspects; + + LoadDll(true); + } + + private void LoadDll(bool retry) + { + try + { + dllHandle = NativeMethods.LoadLibraryW(pluginFilePath); + } + 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 == IntPtr.Zero) + { + try + { + dllHandle = NativeMethods.LoadLibraryA(pluginFilePath); + } + catch + { + // Presumably both Unicode and ANSI crashed + } + + if (dllHandle == IntPtr.Zero) + { + if (!retry) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + /* + * Win32 plugin loading is unreliable on some systems + * Unable to reproduce this, but let's try a sleep & single retry attempt + * */ + Thread.Sleep(100); + LoadDll(false); + return; + } + } + + win32Load = GetDelegate("Load"); + win32Dispose = GetDelegate("Dispose"); + win32GetPluginVersion = GetDelegate("GetPluginVersion"); + win32SetVehicleSpec = GetDelegate("SetVehicleSpec"); + win32Initialize = GetDelegate("Initialize"); + win32Elapse = GetDelegate("Elapse"); + win32SetPower = GetDelegate("SetPower"); + win32SetBrake = GetDelegate("SetBrake"); + win32SetReverser = GetDelegate("SetReverser"); + win32KeyDown = GetDelegate("KeyDown"); + win32KeyUp = GetDelegate("KeyUp"); + win32HornBlow = GetDelegate("HornBlow"); + win32DoorOpen = GetDelegate("DoorOpen"); + win32DoorClose = GetDelegate("DoorClose"); + win32SetSignal = GetDelegate("SetSignal"); + win32SetBeaconData = GetDelegate("SetBeaconData"); + } + + private T GetDelegate(string lpProcName) where T : Delegate + { + IntPtr functionHandle = NativeMethods.GetProcAddress(dllHandle, lpProcName); + + if (functionHandle == IntPtr.Zero) + { + return null; + } + + return Marshal.GetDelegateForFunctionPointer(functionHandle); + } + + public bool Load(LoadProperties properties) + { + loadProperties = properties; + loadProperties.Panel = new int[256]; + soundInstructions = new int[256]; + lastSoundInstructions = new int[256]; + soundHandles = new SoundHandle[256]; + + win32Load?.Invoke(); + + if (win32GetPluginVersion != null && win32GetPluginVersion() == PLUGIN_VERSION) + { + return true; + } + + if (win32GetPluginVersion == null && Path.GetFileName(pluginFilePath).ToLowerInvariant() == "ats2.dll") + { + return true; + } + + Unload(); + return false; + } + + public void Unload() + { + win32Dispose?.Invoke(); + } + + public void SetVehicleSpecs(VehicleSpecs specs) + { + win32SetVehicleSpec?.Invoke( + new Win32VehicleSpec + { + BrakeNotches = specs.BrakeNotches, + PowerNotches = specs.PowerNotches, + AtsNotch = specs.AtsNotch, + B67Notch = specs.B67Notch, + Cars = specs.Cars + } + ); + } + + public void Initialize(InitializationModes mode) + { + win32Initialize?.Invoke((InitialHandlePosition)((int)mode + 1)); + } + + public void Elapse(ElapseData data) + { + if (win32Elapse == null) + { + return; + } + + double time = data.TotalTime.Milliseconds; + + Win32Handles win32Handles = win32Elapse( + new Win32VehicleState + { + Location = data.Vehicle.Location, + Speed = (float)data.Vehicle.Speed.KilometersPerHour, + Time = (int)Math.Floor(time - 2073600000.0 * Math.Floor(time / 2073600000.0)), + BcPressure = (float)data.Vehicle.BcPressure, + MrPressure = (float)data.Vehicle.MrPressure, + ErPressure = (float)data.Vehicle.ErPressure, + BpPressure = (float)data.Vehicle.BpPressure, + SapPressure = (float)data.Vehicle.SapPressure, + Current = 0.0f + }, + loadProperties.Panel, + soundInstructions + ); + + data.Handles.BrakeNotch = win32Handles.Brake; + data.Handles.PowerNotch = win32Handles.Power; + data.Handles.Reverser = win32Handles.Reverser; + + switch (win32Handles.ConstantSpeed) + { + case ConstSpeedInstruction.Continue: + // Keep the last state. + break; + case ConstSpeedInstruction.Enable: + data.Handles.ConstSpeed = true; + break; + case ConstSpeedInstruction.Disable: + data.Handles.ConstSpeed = false; + break; + default: + throw new ArgumentOutOfRangeException(); + } + + /* + * Process the sound instructions + * */ + for (int i = 0; i < soundInstructions.Length; i++) + { + if (soundInstructions[i] == lastSoundInstructions[i]) + { + if (soundInstructions[i] < SoundInstruction.Stop || soundInstructions[i] > SoundInstruction.Continue) + { + throw new InvalidOperationException($"The value {soundInstructions[i]} of sound instruction {i} is out of range."); + } + + continue; + } + + switch (soundInstructions[i]) + { + case SoundInstruction.Stop: + soundHandles[i]?.Stop(); + break; + case SoundInstruction.Play: + soundHandles[i] = loadProperties.PlaySound(i, 1.0, 1.0, false); + break; + case SoundInstruction.Continue: + // Keep the last state. + break; + default: + if (soundInstructions[i] > SoundInstruction.Stop && soundInstructions[i] <= SoundInstruction.PlayLooping) + { + double volume = (soundInstructions[i] - SoundInstruction.Stop) / (double)(SoundInstruction.PlayLooping - SoundInstruction.Stop); + + if (soundHandles[i] == null || soundHandles[i].Stopped) + { + soundHandles[i] = loadProperties.PlaySound(i, volume, 1.0, true); + } + else + { + soundHandles[i].Volume = volume; + } + } + else + { + throw new InvalidOperationException($"The value {soundInstructions[i]} of sound instruction {i} is out of range."); + } + break; + } + + lastSoundInstructions[i] = soundInstructions[i]; + } + } + + public void SetReverser(int reverser) + { + win32SetReverser?.Invoke(reverser); + } + + public void SetPower(int powerNotch) + { + win32SetPower?.Invoke(powerNotch); + } + + public void SetBrake(int brakeNotch) + { + win32SetBrake?.Invoke(brakeNotch); + } + + public void KeyDown(VirtualKeys key) + { + win32KeyDown?.Invoke(key); + } + + public void KeyUp(VirtualKeys key) + { + win32KeyUp?.Invoke(key); + } + + public void HornBlow(HornTypes type) + { + win32HornBlow?.Invoke((Win32HornType)((int)type - 1)); + } + + public void DoorChange(DoorStates oldState, DoorStates newState) + { + if (oldState == DoorStates.None && newState != DoorStates.None) + { + win32DoorOpen?.Invoke(); + } + else if (oldState != DoorStates.None && newState == DoorStates.None) + { + win32DoorClose?.Invoke(); + } + } + + public void SetSignal(SignalData[] data) + { + if (lastAspects.Length == 0 || data[0].Aspect != lastAspects[0]) + { + win32SetSignal?.Invoke(data[0].Aspect); + } + } + + public void SetBeacon(BeaconData data) + { + win32SetBeaconData?.Invoke( + new Win32BeaconData + { + Type = data.Type, + Signal = data.Signal.Aspect, + Distance = (float)data.Signal.Distance, + Optional = data.Optional + } + ); + } + + public void PerformAI(AIData data) + { + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (disposed) + { + return; + } + + if (disposing) + { + // managed resources + } + + if (dllHandle != IntPtr.Zero) + { + NativeMethods.FreeLibrary(dllHandle); + dllHandle = IntPtr.Zero; + } + + disposed = true; + } + + ~Win32Runtime() + { + Dispose(false); + } + } + } +} diff --git a/source/OpenBVE/System/Plugins/PluginManager.cs b/source/OpenBVE/System/Plugins/PluginManager.cs index b432294d8..36da86720 100644 --- a/source/OpenBVE/System/Plugins/PluginManager.cs +++ b/source/OpenBVE/System/Plugins/PluginManager.cs @@ -1,505 +1,1170 @@ using System; using System.Collections.Generic; -using OpenBveApi.Runtime; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using NJsonSchema; +using NJsonSchema.Validation; +using OpenBveApi; +using OpenBveApi.Hosts; using OpenBveApi.Interface; +using OpenBveApi.Resources; +using OpenBveApi.Runtime; using OpenBveApi.Trains; using RouteManager2.Stations; +using Path = System.IO.Path; + +namespace OpenBve +{ + internal partial class PluginManager + { + private static readonly JsonSchema jsonSchema; + + /// The train plugins is attached to. + private readonly TrainManager.Train train; + + private readonly List plugins; + + /// The last in-game time reported to plugins. + private double lastTime; + + /// The last aspects per relative section reported to the plugin. Section 0 is the current section, section 1 the upcoming section, and so on. + private int[] lastAspects; + + private bool stationsLoaded; + + private readonly List currentRouteStations; + + private Dictionary panelIndexDictionary; + + private Dictionary panelValueDictionary; + + /// The file title of the plugin, including the file extension. + internal string Title => string.Join(", ", plugins.Select(x => x.Title)); + + internal bool Enable + { + get; + private set; + } + + /// Whether the plugin returned valid information in the last Elapse call. + internal bool Valid + { + get; + private set; + } + + /// The debug message the plugin returned in the last Elapse call. + internal string Message + { + get; + private set; + } + + /// Whether plugins can disable time acceleration. + internal static bool DisableTimeAcceleration + { + get; + private set; + } + + /// The last reverser reported to the plugin. + internal int LastReverser + { + get; + private set; + } + + /// The absolute section the train was last. + internal int LastSection; + + internal int PanelLength + { + get; + private set; + } + + /// Whether the plugin supports the AI. + internal bool SupportsAI + { + get; + private set; + } + + /// Whether the plugin is the default ATS/ATC plugin. + internal bool IsDefault + { + get; + private set; + } + + /// The last exception the plugin raised. + internal (string title, Exception exception)[] LastExceptions => plugins.Where(x => x.LastException != null).Select(x => (x.Title, x.LastException)).ToArray(); -namespace OpenBve { - internal static class PluginManager { - - /// Represents an abstract plugin. - internal abstract class Plugin { - - // --- members --- - /// The file title of the plugin, including the file extension. - internal string PluginTitle; - /// Whether the plugin is the default ATS/ATC plugin. - internal bool IsDefault; - /// Whether the plugin returned valid information in the last Elapse call. - internal bool PluginValid; - /// The debug message the plugin returned in the last Elapse call. - internal string PluginMessage; - /// The train the plugin is attached to. - internal TrainManager.Train Train; - /// The array of panel variables. - internal int[] Panel; - /// Whether the plugin supports the AI. - internal bool SupportsAI; - /// The last in-game time reported to the plugin. - internal double LastTime; - /// The last reverser reported to the plugin. - internal int LastReverser; - /// The last power notch reported to the plugin. - internal int LastPowerNotch; - /// The last brake notch reported to the plugin. - internal int LastBrakeNotch; - /// The last aspects per relative section reported to the plugin. Section 0 is the current section, section 1 the upcoming section, and so on. - internal int[] LastAspects; - /// The absolute section the train was last. - internal int LastSection; - /// The last exception the plugin raised. - internal Exception LastException; - //NEW: Whether this plugin can disable the time acceleration factor - /// Whether this plugin can disable time acceleration. - internal static bool DisableTimeAcceleration; - - private List currentRouteStations; - private bool StationsLoaded; - // --- functions --- - /// Called to load and initialize the plugin. - /// The train specifications. - /// The initialization mode of the train. - /// Whether loading the plugin was successful. - internal abstract bool Load(VehicleSpecs specs, InitializationModes mode); - /// Called to unload the plugin. - internal abstract void Unload(); - /// Called before the train jumps to a different location. - /// The initialization mode of the train. - internal abstract void BeginJump(InitializationModes mode); - /// Called when the train has finished jumping to a different location. - internal abstract void EndJump(); - /// Called every frame to update the plugin. - internal void UpdatePlugin() { - if (Train.Cars == null || Train.Cars.Length == 0) + static PluginManager() + { + jsonSchema = JsonSchema.FromJsonAsync(Schemas.ats).Result; + } + + internal PluginManager(TrainManager.Train train) + { + this.train = train; + plugins = new List(); + currentRouteStations = new List(); + panelIndexDictionary = new Dictionary(); + panelValueDictionary = new Dictionary(); + } + + private VehicleSpecs GetVehicleSpecs() + { + BrakeTypes brakeType = (BrakeTypes)train.Cars[train.DriverCar].CarBrake.brakeType; + int brakeNotches; + int powerNotches; + bool hasHoldBrake; + if (brakeType == BrakeTypes.AutomaticAirBrake) + { + brakeNotches = 2; + powerNotches = train.Handles.Power.MaximumNotch; + hasHoldBrake = false; + } + else + { + brakeNotches = train.Handles.Brake.MaximumNotch + (train.Handles.HasHoldBrake ? 1 : 0); + powerNotches = train.Handles.Power.MaximumNotch; + hasHoldBrake = train.Handles.HasHoldBrake; + } + + bool hasLocoBrake = train.Handles.HasLocoBrake; + int cars = train.Cars.Length; + return new VehicleSpecs(powerNotches, brakeType, brakeNotches, hasHoldBrake, hasLocoBrake, cars); + } + + /// Gets the driver handles. + /// The driver handles. + private Handles GetHandles() + { + int reverser = (int)train.Handles.Reverser.Driver; + int powerNotch = train.Handles.Power.Driver; + int brakeNotch; + if (train.Handles.Brake is TrainManager.AirBrakeHandle) + { + brakeNotch = train.Handles.EmergencyBrake.Driver ? 3 : train.Handles.Brake.Driver == (int)TrainManager.AirBrakeHandleState.Service ? 2 : train.Handles.Brake.Driver == (int)TrainManager.AirBrakeHandleState.Lap ? 1 : 0; + } + else + { + if (train.Handles.HasHoldBrake) { - return; + brakeNotch = train.Handles.EmergencyBrake.Driver ? train.Handles.Brake.MaximumNotch + 2 : train.Handles.Brake.Driver > 0 ? train.Handles.Brake.Driver + 1 : train.Handles.HoldBrake.Driver ? 1 : 0; + } + else + { + brakeNotch = train.Handles.EmergencyBrake.Driver ? train.Handles.Brake.MaximumNotch + 1 : train.Handles.Brake.Driver; + } + } + + return new Handles(reverser, powerNotch, brakeNotch, train.Handles.LocoBrake.Driver, train.Specs.CurrentConstSpeed, train.Handles.HoldBrake.Driver); + } + + /// Sets the driver handles or the virtual handles. + /// The handles. + /// Whether to set the virtual handles. + private void SetHandles(Handles handles, bool virtualHandles) + { + /* + * Process the handles. + */ + if (train.Handles.SingleHandle & handles.BrakeNotch != 0) + { + handles.PowerNotch = 0; + } + + /* + * Process the reverser. + */ + if (handles.Reverser >= -1 & handles.Reverser <= 1) + { + if (virtualHandles) + { + train.Handles.Reverser.Actual = (TrainManager.ReverserPosition)handles.Reverser; + } + else + { + train.ApplyReverser(handles.Reverser, false); } - /* - * Prepare the vehicle state. - * */ - double location = this.Train.Cars[0].FrontAxle.Follower.TrackPosition - this.Train.Cars[0].FrontAxle.Position + 0.5 * this.Train.Cars[0].Length; - //Curve Radius, Cant and Pitch Added - double CurrentRadius = this.Train.Cars[0].FrontAxle.Follower.CurveRadius; - double CurrentCant = this.Train.Cars[0].FrontAxle.Follower.CurveCant; - double CurrentPitch = this.Train.Cars[0].FrontAxle.Follower.Pitch; - //If the list of stations has not been loaded, do so - if (!StationsLoaded) - { - currentRouteStations = new List(); - int s = 0; - foreach (RouteStation selectedStation in Program.CurrentRoute.Stations) + } + else + { + if (virtualHandles) + { + train.Handles.Reverser.Actual = train.Handles.Reverser.Driver; + } + + Valid = false; + } + + /* + * Process the power. + * */ + if (handles.PowerNotch >= 0 & handles.PowerNotch <= train.Handles.Power.MaximumNotch) + { + if (virtualHandles) + { + train.Handles.Power.Safety = handles.PowerNotch; + } + else + { + train.ApplyNotch(handles.PowerNotch, false, 0, true, true); + } + } + else + { + if (virtualHandles) + { + train.Handles.Power.Safety = train.Handles.Power.Driver; + } + + Valid = false; + } + + /* + * Process the brakes. + * */ + if (virtualHandles) + { + train.Handles.EmergencyBrake.Safety = false; + train.Handles.HoldBrake.Actual = false; + } + if (train.Handles.Brake is TrainManager.AirBrakeHandle) + { + switch (handles.BrakeNotch) + { + case 0 when virtualHandles: + train.Handles.Brake.Safety = (int)TrainManager.AirBrakeHandleState.Release; + break; + case 0: + train.UnapplyEmergencyBrake(); + train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Release); + break; + case 1 when virtualHandles: + train.Handles.Brake.Safety = (int)TrainManager.AirBrakeHandleState.Lap; + break; + case 1: + train.UnapplyEmergencyBrake(); + train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Lap); + break; + case 2 when virtualHandles: + train.Handles.Brake.Safety = (int)TrainManager.AirBrakeHandleState.Service; + break; + case 2: + train.UnapplyEmergencyBrake(); + train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Release); + break; + case 3 when virtualHandles: + train.Handles.Brake.Safety = (int)TrainManager.AirBrakeHandleState.Service; + train.Handles.EmergencyBrake.Safety = true; + break; + case 3: + train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Service); + train.ApplyEmergencyBrake(); + break; + default: + Valid = false; + break; + } + } + else + { + if (train.Handles.HasHoldBrake) + { + if (handles.BrakeNotch == train.Handles.Brake.MaximumNotch + 2) + { + if (virtualHandles) + { + train.Handles.EmergencyBrake.Safety = true; + train.Handles.Brake.Safety = train.Handles.Brake.MaximumNotch; + } + else + { + train.ApplyHoldBrake(false); + train.ApplyNotch(0, true, train.Handles.Brake.MaximumNotch, false, true); + train.ApplyEmergencyBrake(); + } + } + else if (handles.BrakeNotch >= 2 & handles.BrakeNotch <= train.Handles.Brake.MaximumNotch + 1) { - double stopPosition = -1; - int stopIdx = Program.CurrentRoute.Stations[s].GetStopIndex(Train.NumberOfCars); - if (selectedStation.Stops.Length != 0) + if (virtualHandles) { - stopPosition = selectedStation.Stops[stopIdx].TrackPosition; + train.Handles.Brake.Safety = handles.BrakeNotch - 1; + } + else + { + train.UnapplyEmergencyBrake(); + train.ApplyHoldBrake(false); + train.ApplyNotch(0, true, handles.BrakeNotch - 1, false, true); } - Station i = new Station(selectedStation, stopPosition); - currentRouteStations.Add(i); - s++; } - StationsLoaded = true; - - } - //End of additions - double speed = this.Train.Cars[this.Train.DriverCar].Specs.CurrentPerceivedSpeed; - double bcPressure = this.Train.Cars[this.Train.DriverCar].CarBrake.brakeCylinder.CurrentPressure; - double mrPressure = this.Train.Cars[this.Train.DriverCar].CarBrake.mainReservoir.CurrentPressure; - double erPressure = this.Train.Cars[this.Train.DriverCar].CarBrake.equalizingReservoir.CurrentPressure; - double bpPressure = this.Train.Cars[this.Train.DriverCar].CarBrake.brakePipe.CurrentPressure; - double sapPressure = this.Train.Cars[this.Train.DriverCar].CarBrake.straightAirPipe.CurrentPressure; - VehicleState vehicle = new VehicleState(location, new Speed(speed), bcPressure, mrPressure, erPressure, bpPressure, sapPressure, CurrentRadius, CurrentCant, CurrentPitch); - /* - * Prepare the preceding vehicle state. - * */ - double bestLocation = double.MaxValue; - double bestSpeed = 0.0; - PrecedingVehicleState precedingVehicle; - try - { - for (int i = 0; i < TrainManager.Trains.Length; i++) + else if (handles.BrakeNotch == 1) { - if (TrainManager.Trains[i] != this.Train & TrainManager.Trains[i].State == TrainState.Available & Train.Cars.Length > 0) + if (virtualHandles) { - int c = TrainManager.Trains[i].Cars.Length - 1; - double z = TrainManager.Trains[i].Cars[c].RearAxle.Follower.TrackPosition - TrainManager.Trains[i].Cars[c].RearAxle.Position - 0.5 * TrainManager.Trains[i].Cars[c].Length; - if (z >= location & z < bestLocation) - { - bestLocation = z; - bestSpeed = TrainManager.Trains[i].CurrentSpeed; - } + train.Handles.Brake.Safety = 0; + train.Handles.HoldBrake.Actual = true; + } + else + { + train.UnapplyEmergencyBrake(); + train.ApplyNotch(0, true, 0, false, true); + train.ApplyHoldBrake(true); } } - precedingVehicle = bestLocation != double.MaxValue ? new PrecedingVehicleState(bestLocation, bestLocation - location, new Speed(bestSpeed)) : null; - } - catch - { - precedingVehicle = null; - } - /* - * Get the driver handles. - * */ - Handles handles = GetHandles(); - /* - * Update the plugin. - * */ - double totalTime = Program.CurrentRoute.SecondsSinceMidnight; - double elapsedTime = Program.CurrentRoute.SecondsSinceMidnight - LastTime; - - ElapseData data = new ElapseData(vehicle, precedingVehicle, handles, this.Train.SafetySystems.DoorInterlockState, new Time(totalTime), new Time(elapsedTime), currentRouteStations, Program.Renderer.Camera.CurrentMode, Translations.CurrentLanguageCode, this.Train.Destination); - ElapseData inputDevicePluginData = data; - LastTime = Program.CurrentRoute.SecondsSinceMidnight; - Elapse(data); - this.PluginMessage = data.DebugMessage; - this.Train.SafetySystems.DoorInterlockState = data.DoorInterlockState; - DisableTimeAcceleration = data.DisableTimeAcceleration; - for (int i = 0; i < InputDevicePlugin.AvailablePluginInfos.Count; i++) { - if (InputDevicePlugin.AvailablePluginInfos[i].Status == InputDevicePlugin.PluginInfo.PluginStatus.Enable) { - InputDevicePlugin.AvailablePlugins[i].SetElapseData(inputDevicePluginData); + else if (handles.BrakeNotch == 0) + { + if (virtualHandles) + { + train.Handles.Brake.Safety = 0; + } + else + { + train.UnapplyEmergencyBrake(); + train.ApplyNotch(0, true, 0, false, true); + train.ApplyHoldBrake(false); + } } - } - /* - * Set the virtual handles. - * */ - this.PluginValid = true; - SetHandles(data.Handles, true); - this.Train.Destination = data.Destination; - } - /// Gets the driver handles. - /// The driver handles. - private Handles GetHandles() { - int reverser = (int)this.Train.Handles.Reverser.Driver; - int powerNotch = this.Train.Handles.Power.Driver; - int brakeNotch; - if (this.Train.Handles.Brake is TrainManager.AirBrakeHandle) { - brakeNotch = this.Train.Handles.EmergencyBrake.Driver ? 3 : this.Train.Handles.Brake.Driver == (int)TrainManager.AirBrakeHandleState.Service ? 2 : this.Train.Handles.Brake.Driver == (int)TrainManager.AirBrakeHandleState.Lap ? 1 : 0; - } else { - if (this.Train.Handles.HasHoldBrake) { - brakeNotch = this.Train.Handles.EmergencyBrake.Driver ? this.Train.Handles.Brake.MaximumNotch + 2 : this.Train.Handles.Brake.Driver > 0 ? this.Train.Handles.Brake.Driver + 1 : this.Train.Handles.HoldBrake.Driver ? 1 : 0; - } else { - brakeNotch = this.Train.Handles.EmergencyBrake.Driver ? this.Train.Handles.Brake.MaximumNotch + 1 : this.Train.Handles.Brake.Driver; + else + { + if (virtualHandles) + { + train.Handles.Brake.Safety = train.Handles.Brake.Driver; + } + + Valid = false; } } - return new Handles(reverser, powerNotch, brakeNotch, this.Train.Handles.LocoBrake.Driver, this.Train.Specs.CurrentConstSpeed, this.Train.Handles.HoldBrake.Driver); - } - /// Sets the driver handles or the virtual handles. - /// The handles. - /// Whether to set the virtual handles. - private void SetHandles(Handles handles, bool virtualHandles) { - /* - * Process the handles. - */ - if (this.Train.Handles.SingleHandle & handles.BrakeNotch != 0) { - handles.PowerNotch = 0; - } - /* - * Process the reverser. - */ - if (handles.Reverser >= -1 & handles.Reverser <= 1) { - if (virtualHandles) { - this.Train.Handles.Reverser.Actual = (TrainManager.ReverserPosition)handles.Reverser; - } else { - this.Train.ApplyReverser(handles.Reverser, false); - } - } else { - if (virtualHandles) { - this.Train.Handles.Reverser.Actual = this.Train.Handles.Reverser.Driver; - } - this.PluginValid = false; - } - /* - * Process the power. - * */ - if (handles.PowerNotch >= 0 & handles.PowerNotch <= this.Train.Handles.Power.MaximumNotch) { - if (virtualHandles) { - this.Train.Handles.Power.Safety = handles.PowerNotch; - } else { - Train.ApplyNotch(handles.PowerNotch, false, 0, true, true); - } - } else { - if (virtualHandles) { - this.Train.Handles.Power.Safety = this.Train.Handles.Power.Driver; - } - this.PluginValid = false; - } - /* - * Process the brakes. - * */ - if (virtualHandles) { - this.Train.Handles.EmergencyBrake.Safety = false; - this.Train.Handles.HoldBrake.Actual = false; - } - if (this.Train.Handles.Brake is TrainManager.AirBrakeHandle) { - if (handles.BrakeNotch == 0) { - if (virtualHandles) { - this.Train.Handles.Brake.Safety = (int)TrainManager.AirBrakeHandleState.Release; - } else { - this.Train.UnapplyEmergencyBrake(); - this.Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Release); + else + { + if (handles.BrakeNotch == train.Handles.Brake.MaximumNotch + 1) + { + if (virtualHandles) + { + train.Handles.EmergencyBrake.Safety = true; + train.Handles.Brake.Safety = train.Handles.Brake.MaximumNotch; } - } else if (handles.BrakeNotch == 1) { - if (virtualHandles) { - this.Train.Handles.Brake.Safety = (int)TrainManager.AirBrakeHandleState.Lap; - } else { - this.Train.UnapplyEmergencyBrake(); - this.Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Lap); + else + { + train.ApplyHoldBrake(false); + train.ApplyEmergencyBrake(); } - } else if (handles.BrakeNotch == 2) { - if (virtualHandles) { - this.Train.Handles.Brake.Safety = (int)TrainManager.AirBrakeHandleState.Service; - } else { - this.Train.UnapplyEmergencyBrake(); - this.Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Release); + } + else if (handles.BrakeNotch >= 0 & handles.BrakeNotch <= train.Handles.Brake.MaximumNotch | train.Handles.Brake.DelayedChanges.Length == 0) + { + if (virtualHandles) + { + train.Handles.Brake.Safety = handles.BrakeNotch; } - } else if (handles.BrakeNotch == 3) { - if (virtualHandles) { - this.Train.Handles.Brake.Safety = (int)TrainManager.AirBrakeHandleState.Service; - this.Train.Handles.EmergencyBrake.Safety = true; - } else { - this.Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Service); - this.Train.ApplyEmergencyBrake(); + else + { + train.UnapplyEmergencyBrake(); + train.ApplyNotch(0, true, handles.BrakeNotch, false, true); } - } else { - this.PluginValid = false; } - } else { - if (this.Train.Handles.HasHoldBrake) { - if (handles.BrakeNotch == this.Train.Handles.Brake.MaximumNotch + 2) { - if (virtualHandles) { - this.Train.Handles.EmergencyBrake.Safety = true; - this.Train.Handles.Brake.Safety = this.Train.Handles.Brake.MaximumNotch; - } else { - this.Train.ApplyHoldBrake(false); - Train.ApplyNotch(0, true, this.Train.Handles.Brake.MaximumNotch, false, true); - this.Train.ApplyEmergencyBrake(); - } - } else if (handles.BrakeNotch >= 2 & handles.BrakeNotch <= this.Train.Handles.Brake.MaximumNotch + 1) { - if (virtualHandles) { - this.Train.Handles.Brake.Safety = handles.BrakeNotch - 1; - } else { - this.Train.UnapplyEmergencyBrake(); - this.Train.ApplyHoldBrake(false); - Train.ApplyNotch(0, true, handles.BrakeNotch - 1, false, true); - } - } else if (handles.BrakeNotch == 1) { - if (virtualHandles) { - this.Train.Handles.Brake.Safety = 0; - this.Train.Handles.HoldBrake.Actual = true; - } else { - this.Train.UnapplyEmergencyBrake(); - Train.ApplyNotch(0, true, 0, false, true); - this.Train.ApplyHoldBrake(true); - } - } else if (handles.BrakeNotch == 0) { - if (virtualHandles) { - this.Train.Handles.Brake.Safety = 0; - } else { - this.Train.UnapplyEmergencyBrake(); - Train.ApplyNotch(0, true, 0, false, true); - this.Train.ApplyHoldBrake(false); - } - } else { - if (virtualHandles) { - this.Train.Handles.Brake.Safety = this.Train.Handles.Brake.Driver; - } - this.PluginValid = false; - } - } else { - if (handles.BrakeNotch == this.Train.Handles.Brake.MaximumNotch + 1) { - if (virtualHandles) { - this.Train.Handles.EmergencyBrake.Safety = true; - this.Train.Handles.Brake.Safety = this.Train.Handles.Brake.MaximumNotch; - } else { - this.Train.ApplyHoldBrake(false); - this.Train.ApplyEmergencyBrake(); - } - } else if (handles.BrakeNotch >= 0 & handles.BrakeNotch <= this.Train.Handles.Brake.MaximumNotch | this.Train.Handles.Brake.DelayedChanges.Length == 0) { - if (virtualHandles) { - this.Train.Handles.Brake.Safety = handles.BrakeNotch; - } else { - this.Train.UnapplyEmergencyBrake(); - Train.ApplyNotch(0, true, handles.BrakeNotch, false, true); - } - } else { - if (virtualHandles) { - this.Train.Handles.Brake.Safety = this.Train.Handles.Brake.Driver; - } - this.PluginValid = false; + else + { + if (virtualHandles) + { + train.Handles.Brake.Safety = train.Handles.Brake.Driver; } + + Valid = false; } } - /* - * Process the const speed system. - * */ - this.Train.Specs.CurrentConstSpeed = handles.ConstSpeed & this.Train.Specs.HasConstSpeed; - this.Train.Handles.HoldBrake.Actual = handles.HoldBrake & this.Train.Handles.HasHoldBrake; - } - /// Called every frame to update the plugin. - /// The data passed to the plugin on Elapse. - /// This function should not be called directly. Call UpdatePlugin instead. - protected abstract void Elapse(ElapseData data); - /// Called to update the reverser. This invokes a call to SetReverser only if a change actually occured. - internal void UpdateReverser() { - int reverser = (int)this.Train.Handles.Reverser.Driver; - if (reverser != this.LastReverser) { - this.LastReverser = reverser; - SetReverser(reverser); - } - } - /// Called to indicate a change of the reverser. - /// The reverser. - /// This function should not be called directly. Call UpdateReverser instead. - protected abstract void SetReverser(int reverser); - /// Called to update the power notch. This invokes a call to SetPower only if a change actually occured. - internal void UpdatePower() { - int powerNotch = this.Train.Handles.Power.Driver; - if (powerNotch != this.LastPowerNotch) { - this.LastPowerNotch = powerNotch; - SetPower(powerNotch); - } - } - /// Called to indicate a change of the power notch. - /// The power notch. - /// This function should not be called directly. Call UpdatePower instead. - protected abstract void SetPower(int powerNotch); - /// Called to update the brake notch. This invokes a call to SetBrake only if a change actually occured. - internal void UpdateBrake() { - int brakeNotch; - if (this.Train.Handles.Brake is TrainManager.AirBrakeHandle) { - if (this.Train.Handles.HasHoldBrake) { - brakeNotch = this.Train.Handles.EmergencyBrake.Driver ? 4 : this.Train.Handles.Brake.Driver == (int)TrainManager.AirBrakeHandleState.Service ? 3 : this.Train.Handles.Brake.Driver == (int)TrainManager.AirBrakeHandleState.Lap ? 2 : this.Train.Handles.HoldBrake.Driver ? 1 : 0; - } else { - brakeNotch = this.Train.Handles.EmergencyBrake.Driver ? 3 : this.Train.Handles.Brake.Driver == (int)TrainManager.AirBrakeHandleState.Service ? 2 : this.Train.Handles.Brake.Driver == (int)TrainManager.AirBrakeHandleState.Lap ? 1 : 0; + } + + /* + * Process the const speed system. + * */ + train.Specs.CurrentConstSpeed = handles.ConstSpeed & train.Specs.HasConstSpeed; + train.Handles.HoldBrake.Actual = handles.HoldBrake & train.Handles.HasHoldBrake; + } + + private bool ParseSetting(Encoding encoding, ICollection entries) + { + string jsonFilePath = OpenBveApi.Path.CombineFile(train.TrainFolder, "ats.json"); + + if (File.Exists(jsonFilePath)) + { + Program.FileSystem.AppendToLogFile($"Loading train plugin json config file: {jsonFilePath}"); + + if (ParseSettingJson(jsonFilePath, entries)) + { + Program.FileSystem.AppendToLogFile("Train plugin json config loaded successfully."); + return true; + } + + Program.FileSystem.AppendToLogFile("The ats.json file failed to load. Falling back to text config."); + } + + string cfgFilePath = OpenBveApi.Path.CombineFile(train.TrainFolder, "ats.cfg"); + + if (File.Exists(cfgFilePath)) + { + Program.FileSystem.AppendToLogFile($"Loading train plugin config file: {cfgFilePath}"); + + if (ParseSettingCfg(encoding, cfgFilePath, entries)) + { + Program.FileSystem.AppendToLogFile("Train plugin text config loaded successfully."); + return true; + } + + Program.FileSystem.AppendToLogFile("The ats.cfg file failed to load. Falling back to default plugin."); + } + + return false; + } + + private bool ParseSettingJson(string filePath, ICollection entries) + { + JObject jObject; + + try + { + jObject = JObject.Parse(File.ReadAllText(filePath)); + } + catch (Exception ex) + { + Interface.AddMessage(MessageType.Error, false, $"Parse error: {ex.Message}"); + return false; + } + + IEnumerable validationErrors = jsonSchema.Validate(jObject); + + if (validationErrors.Any()) + { + DisplayValidationErrors(validationErrors); + return false; + } + + foreach (JToken pluginJToken in jObject["plugins"].Children()) + { + Plugin.Entry entry = JsonConvert.DeserializeObject(pluginJToken.ToString(), new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); + + string pluginFilePath; + + try + { + pluginFilePath = OpenBveApi.Path.CombineFile(train.TrainFolder, entry.FilePath); + } + catch + { + Interface.AddMessage(MessageType.Error, true, $"The train plugin path {entry.FilePath} was malformed in {filePath}. This plugin will be ignored."); + continue; + } + + if (!File.Exists(pluginFilePath)) + { + Interface.AddMessage(MessageType.Error, true, $"The train plugin {pluginFilePath} could not be found in {filePath}. This plugin will be ignored."); + continue; + } + + entry.FilePath = pluginFilePath; + + entries.Add(entry); + } + + return true; + } + + private bool ParseSettingCfg(Encoding encoding, string filePath, ICollection entries) + { + string[] lines = File.ReadAllLines(filePath, TextEncoding.GetSystemEncodingFromFile(filePath, encoding)); + + for (int i = 0; i < lines.Length; i++) + { + int j = lines[i].IndexOf(';'); + + if (j >= 0) + { + lines[i] = lines[i].Substring(0, j).Trim(); + } + else + { + lines[i] = lines[i].Trim(); + } + } + + foreach (string line in lines.Where(x => !string.IsNullOrEmpty(x))) + { + string pluginFilePath; + + try + { + pluginFilePath = OpenBveApi.Path.CombineFile(train.TrainFolder, line); + } + catch + { + Interface.AddMessage(MessageType.Error, true, $"The train plugin path {line} was malformed in {filePath}. This plugin will be ignored."); + continue; + } + + if (!File.Exists(pluginFilePath)) + { + Interface.AddMessage(MessageType.Error, true, $"The train plugin {pluginFilePath} could not be found in {filePath}. This plugin will be ignored."); + continue; + } + + entries.Add(new Plugin.Entry { FilePath = pluginFilePath }); + } + + if (entries.Any()) + { + return true; + } + + return !encoding.Equals(Encoding.UTF8) && ParseSettingCfg(Encoding.UTF8, filePath, entries); + } + + internal void Load(Encoding encoding, bool forcedDefault) + { + /* + * Unload plugin if already loaded. + * */ + Unload(); + + Valid = true; + lastTime = 0.0; + LastReverser = -2; + lastAspects = new int[] { }; + LastSection = -1; + stationsLoaded = false; + currentRouteStations.Clear(); + panelIndexDictionary.Clear(); + + List entries = new List(); + + if (!forcedDefault && ParseSetting(encoding, entries)) + { + foreach (Plugin.Entry entry in entries) + { + Program.FileSystem.AppendToLogFile($"Loading train plugin: {entry.FilePath}"); + Plugin plugin = Load(entry); + + if (plugin == null) + { + Loading.PluginErrors.Add(Translations.GetInterfaceString("errors_plugin_failure1").Replace("[plugin]", entry.FilePath)); + continue; } - } else { - if (this.Train.Handles.HasHoldBrake) { - brakeNotch = this.Train.Handles.EmergencyBrake.Driver ? this.Train.Handles.Brake.MaximumNotch + 2 : this.Train.Handles.Brake.Driver > 0 ? this.Train.Handles.Brake.Driver + 1 : this.Train.Handles.HoldBrake.Driver ? 1 : 0; - } else { - brakeNotch = this.Train.Handles.EmergencyBrake.Driver ? this.Train.Handles.Brake.MaximumNotch + 1 : this.Train.Handles.Brake.Driver; + + plugins.Add(plugin); + Program.FileSystem.AppendToLogFile($"Train plugin {plugin.Title} loaded successfully."); + } + } + + if (plugins.Count == 0) + { + Program.FileSystem.AppendToLogFile("Loading default train plugin."); + Plugin.Entry entry = new Plugin.Entry { FilePath = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Plugins"), "OpenBveAts.dll") }; + Plugin plugin = Load(entry); + + if (plugin == null) + { + Enable = false; + Loading.PluginErrors.Add(Translations.GetInterfaceString("errors_plugin_failure1").Replace("[plugin]", entry.FilePath)); + return; + } + + plugins.Add(plugin); + IsDefault = true; + Loading.PluginErrors.Add(Translations.GetInterfaceString("errors_plugin_failure2")); + } + + Enable = true; + UpdatePower(); + UpdateBrake(); + UpdateReverser(); + CreatePanelIndexDictionary(); + SupportsAI = plugins.Any(x => x.SupportsAI); + + if (!train.IsPlayerTrain) + { + return; + } + + foreach (ITrainInputDevice api in Program.InputDevicePlugin.AvailableInfos.Where(x => x.Status == InputDevicePlugin.Status.Enable).Select(x => x.Api).OfType()) + { + api.SetVehicleSpecs(GetVehicleSpecs()); + } + } + + private Plugin Load(Plugin.Entry entry) + { + if (string.IsNullOrEmpty(entry.FilePath)) + { + Interface.AddMessage(MessageType.Error, true, "The train plugin file path is not specified."); + return null; + } + + string title = Path.GetFileName(entry.FilePath); + if (!File.Exists(entry.FilePath)) + { + Interface.AddMessage(MessageType.Error, true, $"The train plugin {title} could not be found."); + return null; + } + + /* + * Prepare initialization data for the plugin. + * */ + VehicleSpecs specs = GetVehicleSpecs(); + InitializationModes mode = (InitializationModes)Interface.CurrentOptions.TrainStart; + + /* + * Check if the plugin is a .NET plugin. + * */ + IRuntime api; + Plugin plugin; + Assembly assembly; + try + { + assembly = Assembly.LoadFile(entry.FilePath); + } + catch (BadImageFormatException) + { + assembly = null; + } + catch (Exception ex) + { + Interface.AddMessage(MessageType.Error, false, $"The train plugin {title} could not be loaded due to the following exception: {ex.Message}"); + return null; + } + + if (assembly != null) + { + Type[] types; + try + { + types = assembly.GetTypes(); + } + catch (ReflectionTypeLoadException ex) + { + foreach (Exception e in ex.LoaderExceptions) + { + Interface.AddMessage(MessageType.Error, false, $"The train plugin {title} raised an exception on loading: {e.Message}"); } + + return null; } - if (brakeNotch != this.LastBrakeNotch) { - this.LastBrakeNotch = brakeNotch; - SetBrake(brakeNotch); - } - } - /// Called to indicate a change of the brake notch. - /// The brake notch. - /// This function should not be called directly. Call UpdateBrake instead. - protected abstract void SetBrake(int brakeNotch); - /// Called when a virtual key is pressed. - internal abstract void KeyDown(VirtualKeys key); - /// Called when a virtual key is released. - internal abstract void KeyUp(VirtualKeys key); - /// Called when a horn is played or stopped. - internal abstract void HornBlow(HornTypes type); - /// Called when the state of the doors changes. - internal abstract void DoorChange(DoorStates oldState, DoorStates newState); - /// Called to update the aspects of the section. This invokes a call to SetSignal only if a change in aspect occured or when changing section boundaries. - /// The sections to submit to the plugin. - internal void UpdateSignals(SignalData[] data) { - if (data.Length != 0) { - bool update; - if (this.Train.CurrentSectionIndex != this.LastSection) { - update = true; - } else if (data.Length != this.LastAspects.Length) { - update = true; - } else { - update = false; - for (int i = 0; i < data.Length; i++) { - if (data[i].Aspect != this.LastAspects[i]) { - update = true; - break; - } + + foreach (Type type in types) + { + if (typeof(IRuntime).IsAssignableFrom(type)) + { + if (type.FullName == null) + { + //Should never happen, but static code inspection suggests that it's possible.... + throw new InvalidOperationException(); } + + api = assembly.CreateInstance(type.FullName) as IRuntime; + plugin = new Plugin(this, entry, api); + return plugin.Load(specs, mode) ? plugin : null; } - if (update) { - SetSignal(data); - this.LastAspects = new int[data.Length]; - for (int i = 0; i < data.Length; i++) { - this.LastAspects[i] = data[i].Aspect; - } + } + + Interface.AddMessage(MessageType.Error, false, $"The train plugin {title} does not export a train interface and therefore cannot be used with openBVE."); + return null; + } + + /* + * Check if the plugin is a Win32 plugin. + */ + try + { + if (!CheckWin32Header(entry.FilePath)) + { + Interface.AddMessage(MessageType.Error, false, $"The train plugin {title} is of an unsupported binary format and therefore cannot be used with openBVE."); + return null; + } + } + catch (Exception ex) + { + Interface.AddMessage(MessageType.Error, false, $"The train plugin {title} could not be read due to the following reason: {ex.Message}"); + return null; + } + + if (Program.CurrentHost.Platform != HostPlatform.MicrosoftWindows | IntPtr.Size != 4) + { + Interface.AddMessage(MessageType.Warning, false, $"The train plugin {title} can only be used on 32-bit Microsoft Windows or compatible."); + return null; + } + + try + { + api = new Win32Runtime(entry.FilePath, lastAspects); + } + catch (Win32Exception ex) + { + Interface.AddMessage(MessageType.Error, false, $"The train Win32 plugin {title} raised an exception on loading: {ex.Message} (0x{ex.NativeErrorCode:x})"); + return null; + } + + plugin = new Plugin(this, entry, api); + if (plugin.Load(specs, mode)) + { + return plugin; + } + + Interface.AddMessage(MessageType.Error, false, $"The train plugin {title} does not export a train interface and therefore cannot be used with openBVE."); + return null; + } + + internal void Unload() + { + foreach (Plugin plugin in plugins) + { + plugin.Unload(); + } + + plugins.Clear(); + } + + /// Called before the train jumps to a different location. + /// The initialization mode of the train. + internal void BeginJump(InitializationModes mode) + { + foreach (Plugin plugin in plugins) + { + plugin.BeginJump(mode); + } + } + + /// Called when the train has finished jumping to a different location. + internal void EndJump() + { + foreach (Plugin plugin in plugins) + { + plugin.EndJump(); + } + } + + /// Called every frame to update the plugin. + internal void UpdatePlugin() + { + if (train.Cars == null || train.Cars.Length == 0 || plugins.Count == 0) + { + return; + } + + //If the list of stations has not been loaded, do so + if (!stationsLoaded) + { + foreach (RouteStation selectedStation in Program.CurrentRoute.Stations) + { + double stopPosition = -1; + int stopIdx = selectedStation.GetStopIndex(train.NumberOfCars); + + if (selectedStation.Stops.Length != 0) + { + stopPosition = selectedStation.Stops[stopIdx].TrackPosition; } + + currentRouteStations.Add(new Station(selectedStation, stopPosition)); } + + stationsLoaded = true; } - /// Is called when the aspect in the current or any of the upcoming sections changes. - /// Signal information per section. In the array, index 0 is the current section, index 1 the upcoming section, and so on. - /// This function should not be called directly. Call UpdateSignal instead. - protected abstract void SetSignal(SignalData[] signal); - /// Called when the train passes a beacon. - /// The beacon type. - /// The section the beacon is attached to, or -1 for the next red signal. - /// Optional data attached to the beacon. - internal void UpdateBeacon(int type, int sectionIndex, int optional) { - if (sectionIndex == -1) { - sectionIndex = this.Train.CurrentSectionIndex + 1; - SignalData signal = null; - while (sectionIndex < Program.CurrentRoute.Sections.Length) { - signal = Program.CurrentRoute.Sections[sectionIndex].GetPluginSignal(this.Train); - if (signal.Aspect == 0) break; - sectionIndex++; + + /* + * Prepare the vehicle state. + * */ + double location = train.Cars[0].FrontAxle.Follower.TrackPosition - train.Cars[0].FrontAxle.Position + 0.5 * train.Cars[0].Length; + double currentRadius = train.Cars[0].FrontAxle.Follower.CurveRadius; + double currentCant = train.Cars[0].FrontAxle.Follower.CurveCant; + double currentPitch = train.Cars[0].FrontAxle.Follower.Pitch; + double speed = train.Cars[train.DriverCar].Specs.CurrentPerceivedSpeed; + double bcPressure = train.Cars[train.DriverCar].CarBrake.brakeCylinder.CurrentPressure; + double mrPressure = train.Cars[train.DriverCar].CarBrake.mainReservoir.CurrentPressure; + double erPressure = train.Cars[train.DriverCar].CarBrake.equalizingReservoir.CurrentPressure; + double bpPressure = train.Cars[train.DriverCar].CarBrake.brakePipe.CurrentPressure; + double sapPressure = train.Cars[train.DriverCar].CarBrake.straightAirPipe.CurrentPressure; + VehicleState vehicle = new VehicleState(location, new Speed(speed), bcPressure, mrPressure, erPressure, bpPressure, sapPressure, currentRadius, currentCant, currentPitch); + + /* + * Prepare the preceding vehicle state. + * */ + double bestLocation = double.MaxValue; + double bestSpeed = 0.0; + PrecedingVehicleState precedingVehicle; + try + { + foreach (TrainManager.Train otherTrain in TrainManager.Trains) + { + if (otherTrain == train || otherTrain.State != TrainState.Available || train.Cars.Length <= 0) + { + continue; } - if (sectionIndex < Program.CurrentRoute.Sections.Length) { - SetBeacon(new BeaconData(type, optional, signal)); - } else { - SetBeacon(new BeaconData(type, optional, new SignalData(-1, double.MaxValue))); + + int c = otherTrain.Cars.Length - 1; + double z = otherTrain.Cars[c].RearAxle.Follower.TrackPosition - otherTrain.Cars[c].RearAxle.Position - 0.5 * otherTrain.Cars[c].Length; + + if (z < location || z >= bestLocation) + { + continue; } + + bestLocation = z; + bestSpeed = otherTrain.CurrentSpeed; } - if (sectionIndex >= 0) { - SignalData signal; - if (sectionIndex < Program.CurrentRoute.Sections.Length) { - signal = Program.CurrentRoute.Sections[sectionIndex].GetPluginSignal(this.Train); - } else { - signal = new SignalData(0, double.MaxValue); - } - SetBeacon(new BeaconData(type, optional, signal)); - } else { - SetBeacon(new BeaconData(type, optional, new SignalData(-1, double.MaxValue))); - } - } - /// Called when the train passes a beacon. - /// The beacon data. - /// This function should not be called directly. Call UpdateBeacon instead. - protected abstract void SetBeacon(BeaconData beacon); - /// Updates the AI. - /// The AI response. - internal AIResponse UpdateAI() - { - if (this.SupportsAI) { - AIData data = new AIData(GetHandles()); - this.PerformAI(data); - if (data.Response != AIResponse.None) { - SetHandles(data.Handles, false); + + precedingVehicle = bestLocation < double.MaxValue ? new PrecedingVehicleState(bestLocation, bestLocation - location, new Speed(bestSpeed)) : null; + } + catch + { + precedingVehicle = null; + } + + /* + * Get the driver handles. + * */ + Handles handles = GetHandles(); + + /* + * Update the plugin. + * */ + double totalTime = Program.CurrentRoute.SecondsSinceMidnight; + double elapsedTime = Program.CurrentRoute.SecondsSinceMidnight - lastTime; + + ElapseData data = new ElapseData(vehicle, precedingVehicle, handles, train.SafetySystems.DoorInterlockState, new Time(totalTime), new Time(elapsedTime), currentRouteStations, Program.Renderer.Camera.CurrentMode, Translations.CurrentLanguageCode, train.Destination); + lastTime = Program.CurrentRoute.SecondsSinceMidnight; + + foreach (Plugin plugin in plugins) + { + plugin.SetReverser(handles.Reverser); + plugin.SetPower(handles.PowerNotch); + plugin.SetBrake(handles.BrakeNotch); + plugin.Elapse(data); + } + + Message = data.DebugMessage; + train.SafetySystems.DoorInterlockState = data.DoorInterlockState; + DisableTimeAcceleration = data.DisableTimeAcceleration; + + foreach (var entry in panelIndexDictionary) + { + var updateValue = entry.Value + .Select(x => new { Current = plugins[x.PluginIndex].Panel[x.SrcPanelIndex], Last = plugins[x.PluginIndex].LastPanel[x.SrcPanelIndex] }) + .LastOrDefault(x => x.Current != x.Last); + + if (updateValue == null) + { + continue; + } + + panelValueDictionary[entry.Key] = updateValue.Current; + } + + /* + * Update the input device plugin. + * */ + foreach (IInputDevice api in Program.InputDevicePlugin.AvailableInfos.Where(x => x.Status == InputDevicePlugin.Status.Enable).Select(x => x.Api)) + { + api.SetElapseData(data); + } + + /* + * Set the virtual handles. + * */ + Valid = true; + SetHandles(data.Handles, true); + train.Destination = data.Destination; + } + + /// Called to update the reverser. This invokes a call to SetReverser only if a change actually occurred. + internal void UpdateReverser() + { + int reverser = (int)train.Handles.Reverser.Driver; + + foreach (Plugin plugin in plugins) + { + plugin.SetReverser(reverser); + } + + LastReverser = reverser; + } + + /// Called to update the power notch. This invokes a call to SetPower only if a change actually occurred. + internal void UpdatePower() + { + int powerNotch = train.Handles.Power.Driver; + + foreach (Plugin plugin in plugins) + { + plugin.SetPower(powerNotch); + } + } + + /// Called to update the brake notch. This invokes a call to SetBrake only if a change actually occurred. + internal void UpdateBrake() + { + int brakeNotch; + if (train.Handles.Brake is TrainManager.AirBrakeHandle) + { + if (train.Handles.HasHoldBrake) + { + brakeNotch = train.Handles.EmergencyBrake.Driver ? 4 : train.Handles.Brake.Driver == (int)TrainManager.AirBrakeHandleState.Service ? 3 : train.Handles.Brake.Driver == (int)TrainManager.AirBrakeHandleState.Lap ? 2 : train.Handles.HoldBrake.Driver ? 1 : 0; + } + else + { + brakeNotch = train.Handles.EmergencyBrake.Driver ? 3 : train.Handles.Brake.Driver == (int)TrainManager.AirBrakeHandleState.Service ? 2 : train.Handles.Brake.Driver == (int)TrainManager.AirBrakeHandleState.Lap ? 1 : 0; + } + } + else + { + if (train.Handles.HasHoldBrake) + { + brakeNotch = train.Handles.EmergencyBrake.Driver ? train.Handles.Brake.MaximumNotch + 2 : train.Handles.Brake.Driver > 0 ? train.Handles.Brake.Driver + 1 : train.Handles.HoldBrake.Driver ? 1 : 0; + } + else + { + brakeNotch = train.Handles.EmergencyBrake.Driver ? train.Handles.Brake.MaximumNotch + 1 : train.Handles.Brake.Driver; + } + } + + foreach (Plugin plugin in plugins) + { + plugin.SetBrake(brakeNotch); + } + } + + /// Called when a virtual key is pressed. + /// The virtual key that was pressed. + internal void KeyDown(VirtualKeys key) + { + foreach (Plugin plugin in plugins) + { + plugin.KeyDown(key); + } + } + + /// Called when a virtual key is released. + /// The virtual key that was released. + internal void KeyUp(VirtualKeys key) + { + foreach (Plugin plugin in plugins) + { + plugin.KeyUp(key); + } + } + + /// Called when a horn is played or stopped. + /// The type of horn. + internal void HornBlow(HornTypes type) + { + foreach (Plugin plugin in plugins) + { + plugin.HornBlow(type); + } + } + + /// Called when the state of the doors changes. + /// The old state of the doors. + /// The new state of the doors. + internal void DoorChange(DoorStates oldState, DoorStates newState) + { + foreach (Plugin plugin in plugins) + { + plugin.DoorChange(oldState, newState); + } + } + + /// Called to update the aspects of the section. This invokes a call to SetSignal only if a change in aspect occurred or when changing section boundaries. + /// The sections to submit to the plugin. + internal void UpdateSignals(SignalData[] data) + { + if (data.Length == 0) + { + return; + } + + bool update = false; + if (train.CurrentSectionIndex != LastSection) + { + update = true; + } + else if (data.Length != lastAspects.Length) + { + update = true; + } + else + { + for (int i = 0; i < data.Length; i++) + { + if (data[i].Aspect != lastAspects[i]) + { + update = true; + break; } - return data.Response; } - return AIResponse.None; } - /// Called when the AI should be performed. - /// The AI data. - /// This function should not be called directly. Call UpdateAI instead. - protected abstract void PerformAI(AIData data); - + + if (!update) + { + return; + } + + foreach (Plugin plugin in plugins) + { + plugin.SetSignal(data); + } + + lastAspects = new int[data.Length]; + for (int i = 0; i < data.Length; i++) + { + lastAspects[i] = data[i].Aspect; + } + } + + /// Called when the train passes a beacon. + /// The beacon type. + /// The section the beacon is attached to, or -1 for the next red signal. + /// Optional data attached to the beacon. + internal void UpdateBeacon(int type, int sectionIndex, int optional) + { + SignalData signal = null; + + if (sectionIndex == -1) + { + sectionIndex = train.CurrentSectionIndex + 1; + while (sectionIndex < Program.CurrentRoute.Sections.Length) + { + signal = Program.CurrentRoute.Sections[sectionIndex].GetPluginSignal(train); + if (signal.Aspect == 0) break; + sectionIndex++; + } + + if (sectionIndex >= Program.CurrentRoute.Sections.Length) + { + signal = new SignalData(-1, double.MaxValue); + } + } + else if (sectionIndex >= 0) + { + if (sectionIndex < Program.CurrentRoute.Sections.Length) + { + signal = Program.CurrentRoute.Sections[sectionIndex].GetPluginSignal(train); + } + else + { + signal = new SignalData(0, double.MaxValue); + } + } + else + { + signal = new SignalData(-1, double.MaxValue); + } + + BeaconData beacon = new BeaconData(type, optional, signal); + + foreach (Plugin plugin in plugins) + { + plugin.SetBeacon(beacon); + } + } + + /// Updates the AI. + /// The AI response. + internal AIResponse UpdateAI() + { + AIData data = new AIData(GetHandles()); + + foreach (Plugin plugin in plugins.Where(x => x.SupportsAI)) + { + plugin.PerformAI(data); + } + + if (data.Response != AIResponse.None) + { + SetHandles(data.Handles, false); + } + + return data.Response; + } + + private void CreatePanelIndexDictionary() + { + panelIndexDictionary = plugins + .SelectMany((x, i) => x.GetPanelIndexDictionary().Select(y => new { DestIndex = y.Key, PluginIndex = i, SrcIndex = y.Value })) + .GroupBy(x => x.DestIndex, x => (x.PluginIndex, x.SrcIndex), (x, y) => new { Key = x, Value = y.ToArray() }) + .ToDictionary(x => x.Key, x => x.Value); + + panelValueDictionary = panelIndexDictionary.ToDictionary(x => x.Key, _ => 0); + + PanelLength = panelIndexDictionary.Keys.Max() + 1; + } + + internal int GetPanelValue(int index) + { + return panelValueDictionary.ContainsKey(index) ? panelValueDictionary[index] : 0; + } + + private static void DisplayValidationErrors(IEnumerable errors) + { + foreach (ValidationError error in errors) + { + switch (error) + { + case ChildSchemaValidationError childSchemaError: + foreach (IEnumerable childErrors in childSchemaError.Errors.Values) + { + DisplayValidationErrors(childErrors); + } + break; + case MultiTypeValidationError multiTypeError: + foreach (IEnumerable childErrors in multiTypeError.Errors.Values) + { + DisplayValidationErrors(childErrors); + } + break; + default: + Interface.AddMessage(MessageType.Error, false, $"Validation error: ({error.Kind}: {error.Path}) at character {error.LinePosition} on line {error.LineNumber}"); + break; + } + } } - + /// Checks whether a specified file is a valid Win32 plugin. /// The file to check. /// Whether the file is a valid Win32 plugin. - internal static bool CheckWin32Header(string file) { - using (System.IO.FileStream stream = new System.IO.FileStream(file, System.IO.FileMode.Open, System.IO.FileAccess.Read)) { - using (System.IO.BinaryReader reader = new System.IO.BinaryReader(stream)) { - if (reader.ReadUInt16() != 0x5A4D) { - /* Not MZ signature */ - return false; - } - stream.Position = 0x3C; - stream.Position = reader.ReadInt32(); - if (reader.ReadUInt32() != 0x00004550) { - /* Not PE signature */ - return false; - } - if (reader.ReadUInt16() != 0x014C) { - /* Not IMAGE_FILE_MACHINE_I386 */ - return false; - } + private static bool CheckWin32Header(string file) + { + using (FileStream stream = new FileStream(file, FileMode.Open, FileAccess.Read)) + using (BinaryReader reader = new BinaryReader(stream)) + { + if (reader.ReadUInt16() != 0x5A4D) + { + /* Not MZ signature */ + return false; + } + + stream.Position = 0x3C; + stream.Position = reader.ReadInt32(); + + if (reader.ReadUInt32() != 0x00004550) + { + /* Not PE signature */ + return false; + } + + if (reader.ReadUInt16() != 0x014C) + { + /* Not IMAGE_FILE_MACHINE_I386 */ + return false; } } + return true; } - - - } } diff --git a/source/OpenBVE/System/Program.cs b/source/OpenBVE/System/Program.cs index f36d1601f..4fb99c26b 100644 --- a/source/OpenBVE/System/Program.cs +++ b/source/OpenBVE/System/Program.cs @@ -1,9 +1,6 @@ using System; -using System.ComponentModel; -using System.Diagnostics; -using System.IO; using System.Reflection; -using System.Runtime.InteropServices; +using System.Text; using System.Windows.Forms; using OpenBve.Graphics; using OpenTK; @@ -17,14 +14,6 @@ namespace OpenBve { /// Provides methods for starting the program, including the Main procedure. internal static partial class Program { - /// Gets the UID of the current user if running on a Unix based system - /// The UID - /// Used for checking if we are running as ROOT (don't!) - [DllImport("libc")] -#pragma warning disable IDE1006 // Suppress the VS2017 naming style rule, as this is an external syscall - private static extern uint getuid(); -#pragma warning restore IDE1006 - /// Stores the current CPU architecture internal static ImageFileMachine CurrentCPUArchitecture; @@ -53,6 +42,8 @@ internal static partial class Program { internal static CurrentRoute CurrentRoute; + internal static InputDevicePlugin InputDevicePlugin; + // --- functions --- /// Is executed when the program starts. @@ -89,11 +80,11 @@ private static void Main(string[] args) { Renderer = new NewRenderer(); Sounds = new Sounds(); CurrentRoute = new CurrentRoute(Renderer); - + InputDevicePlugin = new InputDevicePlugin(FileSystem); //Platform specific startup checks // --- Check if we're running as root, and prompt not to --- - if (CurrentHost.Platform == HostPlatform.GNULinux && getuid() == 0) + if (CurrentHost.Platform == HostPlatform.GNULinux && NativeMethods.getuid() == 0) { MessageBox.Show( "You are currently running as the root user." + System.Environment.NewLine + @@ -124,7 +115,7 @@ private static void Main(string[] args) { Interface.LoadControls(file, out controls); Interface.AddControls(ref Interface.CurrentControls, controls); - InputDevicePlugin.LoadPlugins(Program.FileSystem); + InputDevicePlugin.LoadPlugins(); // --- check the command-line arguments for route and train --- formMain.MainDialogResult result = new formMain.MainDialogResult(); @@ -230,16 +221,32 @@ private static void Main(string[] args) { #if !DEBUG } catch (Exception ex) { bool found = false; - for (int i = 0; i < TrainManager.Trains.Length; i++) { - if (TrainManager.Trains[i] != null && TrainManager.Trains[i].Plugin != null) { - if (TrainManager.Trains[i].Plugin.LastException != null) { - CrashHandler.LoadingCrash(ex.Message, true); - MessageBox.Show("The train plugin " + TrainManager.Trains[i].Plugin.PluginTitle + " caused a runtime exception: " + TrainManager.Trains[i].Plugin.LastException.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); - found = true; - RestartArguments = ""; - break; - } + foreach (TrainManager.Train train in TrainManager.Trains) + { + if (train == null || !train.Plugin.Enable) + { + continue; + } + + var lastExceptions = train.Plugin.LastExceptions; + + if (lastExceptions.Length == 0) + { + continue; } + + StringBuilder exceptionTexts = new StringBuilder(); + + foreach ((string title, Exception exception) in lastExceptions) + { + MessageBox.Show($@"The train plugin {title} caused a runtime exception: {exception.Message}", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); + exceptionTexts.AppendLine(exception.ToString()); + } + + CrashHandler.LoadingCrash(exceptionTexts.ToString(), true); + found = true; + RestartArguments = ""; + break; } if (!found) { diff --git a/source/OpenBVE/System/Scripting.cs b/source/OpenBVE/System/Scripting.cs index 5eb8051bf..21ae3c2af 100644 --- a/source/OpenBVE/System/Scripting.cs +++ b/source/OpenBVE/System/Scripting.cs @@ -650,7 +650,7 @@ public static bool hasConstantSpeed(TrainManager.Train Train) public static bool hasPlugin(TrainManager.Train Train) { if (Train == null) return false; - if (Train.IsPlayerTrain && Train.Plugin != null) + if (Train.IsPlayerTrain && Train.Plugin.Enable) { return TrainManager.PlayerTrain.Plugin.IsDefault; } @@ -663,16 +663,12 @@ public static bool hasPlugin(TrainManager.Train Train) /// The plugin state value public static int pluginState(TrainManager.Train Train, int pluginState) { - if (Train == null || Train.Plugin == null) + if (Train == null || !Train.Plugin.Enable) { return 0; } - if (pluginState >= 0 & pluginState < Train.Plugin.Panel.Length) - { - return Train.Plugin.Panel[pluginState]; - } - return 0; + return Train.Plugin.GetPanelValue(pluginState); } } diff --git a/source/OpenBVE/UserInterface/formAbout.cs b/source/OpenBVE/UserInterface/formAbout.cs index bb0170cc2..6b8a62e4b 100644 --- a/source/OpenBVE/UserInterface/formAbout.cs +++ b/source/OpenBVE/UserInterface/formAbout.cs @@ -34,72 +34,88 @@ public formAbout() catch { } StringBuilder builder = new StringBuilder(); - builder.AppendLine("CoreFX:"); - builder.AppendLine(OpenBveApi.Resource.CoreFX); + builder.AppendLine("AssimpParser:"); + builder.AppendLine(OpenBveApi.Resources.Licenses.AssimpParser); builder.AppendLine(); builder.AppendLine(new string('-', 80)); builder.AppendLine("CS Script:"); - builder.AppendLine(OpenBveApi.Resource.CS_Script); + builder.AppendLine(OpenBveApi.Resources.Licenses.CS_Script); builder.AppendLine(); builder.AppendLine(new string('-', 80)); builder.AppendLine("Cursors:"); - builder.AppendLine(OpenBveApi.Resource.Cursors); + builder.AppendLine(OpenBveApi.Resources.Licenses.Cursors); + builder.AppendLine(); + builder.AppendLine(new string('-', 80)); + builder.AppendLine(".NET Runtime:"); + builder.AppendLine(OpenBveApi.Resources.Licenses.dotnet_runtime); builder.AppendLine(); builder.AppendLine(new string('-', 80)); builder.AppendLine("DotNetZip:"); - builder.AppendLine(OpenBveApi.Resource.DotNetZip); + builder.AppendLine(OpenBveApi.Resources.Licenses.DotNetZip); + builder.AppendLine(); + builder.AppendLine(new string('-', 80)); + builder.AppendLine("Namotion.Reflection:"); + builder.AppendLine(OpenBveApi.Resources.Licenses.Namotion_Reflection); builder.AppendLine(); builder.AppendLine(new string('-', 80)); builder.AppendLine("NAudio:"); - builder.AppendLine(OpenBveApi.Resource.NAudio); + builder.AppendLine(OpenBveApi.Resources.Licenses.NAudio); builder.AppendLine(); builder.AppendLine(new string('-', 80)); builder.AppendLine("NAudio.Vorbis:"); - builder.AppendLine(OpenBveApi.Resource.NAudio_Vorbis); + builder.AppendLine(OpenBveApi.Resources.Licenses.NAudio_Vorbis); + builder.AppendLine(); + builder.AppendLine(new string('-', 80)); + builder.AppendLine("Newtonsoft.Json:"); + builder.AppendLine(OpenBveApi.Resources.Licenses.Newtonsoft_Json); + builder.AppendLine(); + builder.AppendLine(new string('-', 80)); + builder.AppendLine("NJsonSchema:"); + builder.AppendLine(OpenBveApi.Resources.Licenses.NJsonSchema); builder.AppendLine(); builder.AppendLine(new string('-', 80)); builder.AppendLine("NLayer:"); - builder.AppendLine(OpenBveApi.Resource.NLayer); + builder.AppendLine(OpenBveApi.Resources.Licenses.NLayer); builder.AppendLine(); builder.AppendLine(new string('-', 80)); builder.AppendLine("NVorbis:"); - builder.AppendLine(OpenBveApi.Resource.NVorbis); + builder.AppendLine(OpenBveApi.Resources.Licenses.NVorbis); + builder.AppendLine(); + builder.AppendLine(new string('-', 80)); + builder.AppendLine("OpenAL Soft:"); + builder.AppendLine(OpenBveApi.Resources.Licenses.OpenALSoft); builder.AppendLine(); builder.AppendLine(new string('-', 80)); builder.AppendLine("OpenTK:"); - builder.AppendLine(OpenBveApi.Resource.OpenTK); + builder.AppendLine(OpenBveApi.Resources.Licenses.OpenTK); builder.AppendLine(); builder.AppendLine(new string('-', 80)); builder.AppendLine("Prism:"); - builder.AppendLine(OpenBveApi.Resource.Prism); + builder.AppendLine(OpenBveApi.Resources.Licenses.Prism); builder.AppendLine(); builder.AppendLine(new string('-', 80)); builder.AppendLine("ReactiveExtensions:"); - builder.AppendLine(OpenBveApi.Resource.ReactiveExtensions); + builder.AppendLine(OpenBveApi.Resources.Licenses.ReactiveExtensions); builder.AppendLine(); builder.AppendLine(new string('-', 80)); builder.AppendLine("ReactiveProperty:"); - builder.AppendLine(OpenBveApi.Resource.ReactiveProperty); + builder.AppendLine(OpenBveApi.Resources.Licenses.ReactiveProperty); builder.AppendLine(); builder.AppendLine(new string('-', 80)); builder.AppendLine("SanYing Input:"); - builder.AppendLine(OpenBveApi.Resource.SanYing_Input); + builder.AppendLine(OpenBveApi.Resources.Licenses.SanYing_Input); builder.AppendLine(); builder.AppendLine(new string('-', 80)); builder.AppendLine("SharpCompress:"); - builder.AppendLine(OpenBveApi.Resource.SharpCompress); + builder.AppendLine(OpenBveApi.Resources.Licenses.SharpCompress); builder.AppendLine(); builder.AppendLine(new string('-', 80)); builder.AppendLine("TrainEditor2 Icons:"); - builder.AppendLine(OpenBveApi.Resource.TrainEditor2_Icons); + builder.AppendLine(OpenBveApi.Resources.Licenses.TrainEditor2_Icons); builder.AppendLine(); builder.AppendLine(new string('-', 80)); builder.AppendLine("Ude:"); - builder.AppendLine(OpenBveApi.Resource.Ude); - builder.AppendLine(); - builder.AppendLine(new string('-', 80)); - builder.AppendLine("XamlBehaviors for WPF:"); - builder.AppendLine(OpenBveApi.Resource.XamlBehaviorsForWPF); + builder.AppendLine(OpenBveApi.Resources.Licenses.Ude); textBoxOpenSourceLicences.Text = builder.ToString(); } diff --git a/source/OpenBVE/UserInterface/formMain.Options.cs b/source/OpenBVE/UserInterface/formMain.Options.cs index ed66fd1c7..73c199875 100644 --- a/source/OpenBVE/UserInterface/formMain.Options.cs +++ b/source/OpenBVE/UserInterface/formMain.Options.cs @@ -51,63 +51,70 @@ private void checkboxJoysticksUsed_CheckedChanged(object sender, EventArgs e) { - private void ListInputDevicePlugins() { - ListViewItem[] Items = new ListViewItem[InputDevicePlugin.AvailablePluginInfos.Count]; - for (int i = 0; i < Items.Length; i++) { - InputDevicePlugin.PluginInfo Info = InputDevicePlugin.AvailablePluginInfos[i]; - if (Array.Exists(Interface.CurrentOptions.EnableInputDevicePlugins, element => element.Equals(Info.FileName))) { - InputDevicePlugin.CallPluginLoad(i); + private void ListInputDevicePlugins() + { + ListViewItem[] Items = new ListViewItem[Program.InputDevicePlugin.AvailableInfos.Count]; + + for (int i = 0; i < Items.Length; i++) + { + InputDevicePlugin.Info info = Program.InputDevicePlugin.AvailableInfos[i]; + + if (Array.Exists(Interface.CurrentOptions.EnableInputDevicePlugins, element => element.Equals(info.FileName))) + { + Program.InputDevicePlugin.CallPluginLoad(info); } - Items[i] = new ListViewItem(new string[] { "", "", "", "", "" }); + + Items[i] = new ListViewItem(new [] { "", "", "", "", "" }); UpdateInputDeviceListViewItem(Items[i], i, false); } + listviewInputDevice.Items.AddRange(Items); - if (Items.Length != 0) - { - listviewInputDevice.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent); - } - else - { - listviewInputDevice.AutoResizeColumns(ColumnHeaderAutoResizeStyle.None); - } + listviewInputDevice.AutoResizeColumns(Items.Length != 0 ? ColumnHeaderAutoResizeStyle.ColumnContent : ColumnHeaderAutoResizeStyle.None); } - private void UpdateInputDeviceListViewItem(ListViewItem Item, int Index, bool ResizeColumns) { - Item.SubItems[0].Text = InputDevicePlugin.AvailablePluginInfos[Index].Name.Title; - switch (InputDevicePlugin.AvailablePluginInfos[Index].Status) + private void UpdateInputDeviceListViewItem(ListViewItem Item, int Index, bool ResizeColumns) + { + InputDevicePlugin.Info info = Program.InputDevicePlugin.AvailableInfos[Index]; + Item.SubItems[0].Text = info.Name.Title; + + switch (info.Status) { - case InputDevicePlugin.PluginInfo.PluginStatus.Failure: + case InputDevicePlugin.Status.Failure: Item.SubItems[1].Text = Translations.GetInterfaceString("options_input_device_plugin_status_failure"); break; - case InputDevicePlugin.PluginInfo.PluginStatus.Disable: + case InputDevicePlugin.Status.Disable: Item.SubItems[1].Text = Translations.GetInterfaceString("options_input_device_plugin_status_disable"); break; - case InputDevicePlugin.PluginInfo.PluginStatus.Enable: + case InputDevicePlugin.Status.Enable: Item.SubItems[1].Text = Translations.GetInterfaceString("options_input_device_plugin_status_enable"); break; } - Item.SubItems[2].Text = InputDevicePlugin.AvailablePluginInfos[Index].Version.Version; - Item.SubItems[3].Text = InputDevicePlugin.AvailablePluginInfos[Index].Provider.Copyright; - Item.SubItems[4].Text = InputDevicePlugin.AvailablePluginInfos[Index].FileName; - if (ResizeColumns) { + + Item.SubItems[2].Text = info.Version.Version; + Item.SubItems[3].Text = info.Provider.Copyright; + Item.SubItems[4].Text = info.FileName; + + if (ResizeColumns) + { listviewInputDevice.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent); } } - private void UpdateInputDeviceComponent(InputDevicePlugin.PluginInfo.PluginStatus Status) { + private void UpdateInputDeviceComponent(InputDevicePlugin.Status Status) + { switch (Status) { - case InputDevicePlugin.PluginInfo.PluginStatus.Failure: + case InputDevicePlugin.Status.Failure: checkBoxInputDeviceEnable.Enabled = false; checkBoxInputDeviceEnable.Checked = false; buttonInputDeviceConfig.Enabled = false; break; - case InputDevicePlugin.PluginInfo.PluginStatus.Disable: + case InputDevicePlugin.Status.Disable: checkBoxInputDeviceEnable.Enabled = true; checkBoxInputDeviceEnable.Checked = false; buttonInputDeviceConfig.Enabled = false; break; - case InputDevicePlugin.PluginInfo.PluginStatus.Enable: + case InputDevicePlugin.Status.Enable: checkBoxInputDeviceEnable.Enabled = true; checkBoxInputDeviceEnable.Checked = true; buttonInputDeviceConfig.Enabled = true; @@ -115,14 +122,18 @@ private void UpdateInputDeviceComponent(InputDevicePlugin.PluginInfo.PluginStatu } } - private void listviewInputDevice_SelectedIndexChanged(object sender, EventArgs e) { - if (listviewInputDevice.SelectedIndices.Count == 1) { + private void listviewInputDevice_SelectedIndexChanged(object sender, EventArgs e) + { + if (listviewInputDevice.SelectedIndices.Count == 1) + { int index = listviewInputDevice.SelectedIndices[0]; this.Tag = new object(); - UpdateInputDeviceComponent(InputDevicePlugin.AvailablePluginInfos[index].Status); + UpdateInputDeviceComponent(Program.InputDevicePlugin.AvailableInfos[index].Status); // finalize this.Tag = null; - } else { + } + else + { this.Tag = new object(); checkBoxInputDeviceEnable.Enabled = false; checkBoxInputDeviceEnable.Checked = false; @@ -131,23 +142,32 @@ private void listviewInputDevice_SelectedIndexChanged(object sender, EventArgs e } } - private void checkBoxInputDeviceEnable_CheckedChanged(object sender, EventArgs e) { - if (this.Tag == null && listviewInputDevice.SelectedIndices.Count == 1) { + private void checkBoxInputDeviceEnable_CheckedChanged(object sender, EventArgs e) + { + if (this.Tag == null && listviewInputDevice.SelectedIndices.Count == 1) + { int index = listviewInputDevice.SelectedIndices[0]; - if (checkBoxInputDeviceEnable.Checked) { - InputDevicePlugin.CallPluginLoad(index); - } else { - InputDevicePlugin.CallPluginUnload(index); + + if (checkBoxInputDeviceEnable.Checked) + { + Program.InputDevicePlugin.CallPluginLoad(index); + } + else + { + Program.InputDevicePlugin.CallPluginUnload(index); } - UpdateInputDeviceComponent(InputDevicePlugin.AvailablePluginInfos[index].Status); + + UpdateInputDeviceComponent(Program.InputDevicePlugin.AvailableInfos[index].Status); UpdateInputDeviceListViewItem(listviewInputDevice.Items[index], index, true); } } // Input Device Plugin Config - private void buttonInputDeviceConfig_Click(object sender, EventArgs e) { - if (listviewInputDevice.SelectedIndices.Count == 1) { - InputDevicePlugin.CallPluginConfig(this, listviewInputDevice.SelectedIndices[0]); + private void buttonInputDeviceConfig_Click(object sender, EventArgs e) + { + if (listviewInputDevice.SelectedIndices.Count == 1) + { + Program.InputDevicePlugin.CallPluginConfig(this, listviewInputDevice.SelectedIndices[0]); } } diff --git a/source/OpenBVE/UserInterface/formMain.cs b/source/OpenBVE/UserInterface/formMain.cs index 1e2388971..3e9e554a6 100644 --- a/source/OpenBVE/UserInterface/formMain.cs +++ b/source/OpenBVE/UserInterface/formMain.cs @@ -2,6 +2,7 @@ using System.ComponentModel; using System.Diagnostics; using System.Drawing; +using System.Linq; using System.Net; using System.Windows.Forms; using System.Xml; @@ -608,7 +609,7 @@ private void ApplyLanguage() listviewInputDevice.Columns[4].Text = Translations.GetInterfaceString("options_input_device_plugin_file_name"); { listviewInputDevice.Items.Clear(); - ListViewItem[] Items = new ListViewItem[InputDevicePlugin.AvailablePluginInfos.Count]; + ListViewItem[] Items = new ListViewItem[Program.InputDevicePlugin.AvailableInfos.Count]; for (int i = 0; i < Items.Length; i++) { Items[i] = new ListViewItem(new string[] { "", "", "", "", "" }); @@ -1077,20 +1078,19 @@ private void formMain_FormClosing() } { int n = 0; - string[] a = new string[InputDevicePlugin.AvailablePluginInfos.Count]; - for (int i = 0; i < InputDevicePlugin.AvailablePluginInfos.Count; i++) + string[] a = new string[Program.InputDevicePlugin.AvailableInfos.Count]; + + foreach (InputDevicePlugin.Info info in Program.InputDevicePlugin.AvailableInfos.Where(x => x.Status == InputDevicePlugin.Status.Enable)) { - InputDevicePlugin.PluginInfo Info = InputDevicePlugin.AvailablePluginInfos[i]; - if (Info.Status != InputDevicePlugin.PluginInfo.PluginStatus.Enable) { - continue; - } - string PluginPath = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("InputDevicePlugins"), Info.FileName); - if (System.IO.File.Exists(PluginPath)) + string pluginPath = Path.CombineFile(Program.FileSystem.GetDataFolder("InputDevicePlugins"), info.FileName); + + if (System.IO.File.Exists(pluginPath)) { - a[n] = Info.FileName; + a[n] = info.FileName; n++; } } + Array.Resize(ref a, n); Interface.CurrentOptions.EnableInputDevicePlugins = a; } diff --git a/source/OpenBVE/app.config b/source/OpenBVE/app.config index 261082b2c..01d6a20a2 100644 --- a/source/OpenBVE/app.config +++ b/source/OpenBVE/app.config @@ -12,6 +12,14 @@ + + + + + + + + diff --git a/source/OpenBVE/packages.config b/source/OpenBVE/packages.config index bb491ac49..d84844312 100644 --- a/source/OpenBVE/packages.config +++ b/source/OpenBVE/packages.config @@ -1,5 +1,8 @@  + + + diff --git a/source/OpenBveApi/Interface/Input/InputDevice.cs b/source/OpenBveApi/Interface/Input/InputDevice.cs index 641489dea..eeb70a111 100644 --- a/source/OpenBveApi/Interface/Input/InputDevice.cs +++ b/source/OpenBveApi/Interface/Input/InputDevice.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Reflection; using System.Windows.Forms; using OpenBveApi.Runtime; @@ -16,15 +17,14 @@ public class InputEventArgs : EventArgs /// /// Control's information public InputEventArgs(InputControl control) - : base() { - this.Control = control; + Control = control; } /// /// Control's information /// - public InputControl Control { get; private set; } + public InputControl Control { get; } } /// @@ -66,7 +66,7 @@ public interface IInputDevice /// /// A function called when the plugin is loading /// - /// The instance of FileSytem class + /// The instance of FileSystem class /// Check the plugin loading process is successfully bool Load(FileSystem.FileSystem fileSystem); @@ -92,7 +92,7 @@ public interface IInputDevice /// The function what notify to the plugin that the train existing status /// /// Data - void SetElapseData(Runtime.ElapseData data); + void SetElapseData(ElapseData data); /// /// A function that calls each frame @@ -113,197 +113,243 @@ public interface ITrainInputDevice : IInputDevice /// /// The class of the input device plugin /// - public static class InputDevicePlugin + public class InputDevicePlugin { /// - /// The class of the plugin's information + /// Enumerators of the plugin status /// - public class PluginInfo + public enum Status { /// - /// Enumerators of the plugin status + /// Failed the loading /// - public enum PluginStatus - { - /// - /// Failed the loading - /// - Failure = 0, - - /// - /// Disabled - /// - Disable = 1, - - /// - /// Enabled - /// - Enable = 2 - }; + Failure = 0, + + /// + /// Disabled + /// + Disable = 1, + /// + /// Enabled + /// + Enable = 2 + }; + + /// + /// The class of the plugin's information + /// + public class Info + { /// /// Plugin's name /// - public AssemblyTitleAttribute Name { get; private set; } + public AssemblyTitleAttribute Name { get; } /// /// Plugin's state /// - public PluginStatus Status { get; internal set; } + public Status Status { get; internal set; } /// /// Version information of the plugin /// - public AssemblyFileVersionAttribute Version { get; private set; } + public AssemblyFileVersionAttribute Version { get; } /// /// Provider of the plugin /// - public AssemblyCopyrightAttribute Provider { get; private set; } + public AssemblyCopyrightAttribute Provider { get; } /// /// Filename of the plugin /// - public string FileName { get; private set; } + public string FileName { get; } + + /// + /// The instance of the plugin + /// + public IInputDevice Api { get; } /// /// Constructor /// - /// Asssembly information of the plugin - internal PluginInfo(Assembly File) + /// Assembly information of the plugin + /// The instance of the plugin + internal Info(Assembly assembly, IInputDevice api) { - Name = (AssemblyTitleAttribute)Attribute.GetCustomAttribute(File, typeof(AssemblyTitleAttribute)); - Status = PluginStatus.Disable; - Version = (AssemblyFileVersionAttribute)Attribute.GetCustomAttribute(File, typeof(AssemblyFileVersionAttribute)); - Provider = (AssemblyCopyrightAttribute)Attribute.GetCustomAttribute(File, typeof(AssemblyCopyrightAttribute)); - FileName = System.IO.Path.GetFileName(File.Location); + Name = (AssemblyTitleAttribute)Attribute.GetCustomAttribute(assembly, typeof(AssemblyTitleAttribute)); + Status = Status.Disable; + Version = (AssemblyFileVersionAttribute)Attribute.GetCustomAttribute(assembly, typeof(AssemblyFileVersionAttribute)); + Provider = (AssemblyCopyrightAttribute)Attribute.GetCustomAttribute(assembly, typeof(AssemblyCopyrightAttribute)); + FileName = System.IO.Path.GetFileName(assembly.Location); + Api = api; } } /// /// The instance of FileSystem class /// - private static FileSystem.FileSystem FileSystem = null; + private readonly FileSystem.FileSystem fileSystem; + + /// + /// The entity of AvailableInfos + /// + private readonly List availableInfos; /// /// The storing list of the plugin information that can use /// - public static readonly List AvailablePluginInfos = new List(); + public ReadOnlyCollection AvailableInfos { get; } /// - /// The storing list of the plugin instance that can use + /// Constructor /// - public static readonly List AvailablePlugins = new List(); + /// The instance of FileSystem class + public InputDevicePlugin(FileSystem.FileSystem fileSystem) + { + this.fileSystem = fileSystem; + availableInfos = new List(); + AvailableInfos = availableInfos.AsReadOnly(); + } /// /// The function that is load the plugin what can use /// - /// The instance of FileSystem class - public static void LoadPlugins(FileSystem.FileSystem fileSystem) + public void LoadPlugins() { - if (fileSystem == null) - { - return; - } - FileSystem = fileSystem; - string PluginsFolder = FileSystem.GetDataFolder("InputDevicePlugins"); - if (!System.IO.Directory.Exists(PluginsFolder)) + string pluginsFolder = fileSystem.GetDataFolder("InputDevicePlugins"); + + if (!System.IO.Directory.Exists(pluginsFolder)) { return; } - string[] PluginFiles = System.IO.Directory.GetFiles(PluginsFolder, "*.dll"); - foreach (var File in PluginFiles) + + foreach (string File in System.IO.Directory.GetFiles(pluginsFolder, "*.dll")) { - Assembly Plugin; + Assembly assembly; try { - Plugin = Assembly.LoadFrom(File); + assembly = Assembly.LoadFrom(File); } catch { continue; } - Type[] Types; + + Type[] types; try { - Types = Plugin.GetTypes(); + types = assembly.GetTypes(); } catch { continue; } - foreach (var Type in Types) + + foreach (Type type in types) { - if (typeof(IInputDevice).IsAssignableFrom(Type)) + if (typeof(IInputDevice).IsAssignableFrom(type)) { - if (Type.FullName == null) + if (type.FullName == null) { continue; } - AvailablePluginInfos.Add(new PluginInfo(Plugin)); - AvailablePlugins.Add(Plugin.CreateInstance(Type.FullName) as IInputDevice); + + availableInfos.Add(new Info(assembly, assembly.CreateInstance(type.FullName) as IInputDevice)); } } } } /// - /// The function that calls the plugin's load funcion + /// The function that calls the plugin's load function /// /// The index number which can use the plugins - public static void CallPluginLoad(int index) + public void CallPluginLoad(int index) { - if (index < 0 || index >= AvailablePlugins.Count || index >= AvailablePluginInfos.Count) + if (index < 0 || index >= AvailableInfos.Count) { return; } - if (AvailablePluginInfos[index].Status == PluginInfo.PluginStatus.Enable) + + CallPluginLoad(AvailableInfos[index]); + } + + /// + /// The function that calls the plugin's load function + /// + /// The instance of Info class + public void CallPluginLoad(Info info) + { + if (info.Status == Status.Enable) { return; } - if (AvailablePlugins[index].Load(FileSystem)) - { - AvailablePluginInfos[index].Status = PluginInfo.PluginStatus.Enable; - } - else + + info.Status = info.Api.Load(fileSystem) ? Status.Enable : Status.Failure; + } + + /// + /// The function that calls the plugin's unload function + /// + public void CallPluginUnload() + { + foreach (Info info in AvailableInfos) { - AvailablePluginInfos[index].Status = PluginInfo.PluginStatus.Failure; + CallPluginUnload(info); } } /// - /// The function that calls the plugin's unload funcion + /// The function that calls the plugin's unload function /// /// The index number which can use the plugins - public static void CallPluginUnload(int index) + public void CallPluginUnload(int index) { - if (index < 0 || index >= AvailablePlugins.Count || index >= AvailablePluginInfos.Count) + if (index < 0 || index >= AvailableInfos.Count) { return; } - if (AvailablePluginInfos[index].Status != PluginInfo.PluginStatus.Enable) + + CallPluginUnload(AvailableInfos[index]); + } + + /// + /// The function that calls the plugin's unload function + /// + /// The instance of Info class + public void CallPluginUnload(Info info) + { + if (info.Status != Status.Enable) { return; } - AvailablePlugins[index].Unload(); - AvailablePluginInfos[index].Status = PluginInfo.PluginStatus.Disable; + + info.Api.Unload(); + info.Status = Status.Disable; } /// - /// The function that calls the plugin's configration funcion + /// The function that calls the plugin's configuration function /// /// The owner of the window /// The index number which can use the plugins - public static void CallPluginConfig(IWin32Window owner, int index) + public void CallPluginConfig(IWin32Window owner, int index) { - if (index < 0 || index >= AvailablePlugins.Count) { + if (index < 0 || index >= AvailableInfos.Count) + { return; } - if (AvailablePluginInfos[index].Status != PluginInfo.PluginStatus.Enable) + + Info info = AvailableInfos[index]; + + if (info.Status != Status.Enable) { return; } - AvailablePlugins[index].Config(owner); + + info.Api.Config(owner); } } } diff --git a/source/OpenBveApi/Interface/Translations/LanguageFile.cs b/source/OpenBveApi/Interface/Translations/LanguageFile.cs index 855082848..e9c1384c3 100644 --- a/source/OpenBveApi/Interface/Translations/LanguageFile.cs +++ b/source/OpenBveApi/Interface/Translations/LanguageFile.cs @@ -2,6 +2,7 @@ using System.IO; using System.Text; using System.Windows.Forms; +using OpenBveApi.Resources; namespace OpenBveApi.Interface { public static partial class Translations { @@ -46,7 +47,7 @@ public static void LoadLanguageFiles(string LanguageFolder) { /// Loads the embedded default language private static void LoadEmbeddedLanguage() { - using (TextReader reader = new StringReader(Resource.en_US)) + using (TextReader reader = new StringReader(Languages.en_US)) { Language l = new Language(reader, "en-US"); AvailableLanguages.Add(l); diff --git a/source/OpenBveApi/OpenBveApi.csproj b/source/OpenBveApi/OpenBveApi.csproj index bdefc096b..22ba38f23 100644 --- a/source/OpenBveApi/OpenBveApi.csproj +++ b/source/OpenBveApi/OpenBveApi.csproj @@ -130,10 +130,20 @@ - + True True - Resource.resx + Languages.resx + + + True + True + Licenses.resx + + + True + True + Schemas.resx @@ -236,16 +246,23 @@ - + + PublicResXFileCodeGenerator + Languages.Designer.cs + + + PublicResXFileCodeGenerator + Licenses.Designer.cs + + PublicResXFileCodeGenerator - Resource.Designer.cs + Schemas.Designer.cs - diff --git a/source/OpenBveApi/Resources/Languages.Designer.cs b/source/OpenBveApi/Resources/Languages.Designer.cs new file mode 100644 index 000000000..60135bbef --- /dev/null +++ b/source/OpenBveApi/Resources/Languages.Designer.cs @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +// +// このコードはツールによって生成されました。 +// ランタイム バージョン:4.0.30319.42000 +// +// このファイルへの変更は、以下の状況下で不正な動作の原因になったり、 +// コードが再生成されるときに損失したりします。 +// +//------------------------------------------------------------------------------ + +namespace OpenBveApi.Resources { + using System; + + + /// + /// ローカライズされた文字列などを検索するための、厳密に型指定されたリソース クラスです。 + /// + // このクラスは StronglyTypedResourceBuilder クラスが ResGen + // または Visual Studio のようなツールを使用して自動生成されました。 + // メンバーを追加または削除するには、.ResX ファイルを編集して、/str オプションと共に + // ResGen を実行し直すか、または VS プロジェクトをビルドし直します。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class Languages { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Languages() { + } + + /// + /// このクラスで使用されているキャッシュされた ResourceManager インスタンスを返します。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("OpenBveApi.Resources.Languages", typeof(Languages).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// すべてについて、現在のスレッドの CurrentUICulture プロパティをオーバーライドします + /// 現在のスレッドの CurrentUICulture プロパティをオーバーライドします。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// <?xml version="1.0" encoding="utf-8" standalone="yes"?> + ///<xliff version="1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2.xsd" xmlns="urn:oasis:names:tc:xliff:document:1.2"> + /// <file datatype="plaintext" original="en-US.xlf" source-language="en-US"> + /// <body> + /// <group id="language"> + /// <trans-unit id="name"> + /// <source>English (United States)</source> + /// </trans-unit> + /// <trans-unit id="flag"> + /// <source>US.png</ [残りの文字列は切り詰められました]"; に類似しているローカライズされた文字列を検索します。 + /// + public static string en_US { + get { + return ResourceManager.GetString("en_US", resourceCulture); + } + } + } +} diff --git a/source/OpenBveApi/Resources/Languages.resx b/source/OpenBveApi/Resources/Languages.resx new file mode 100644 index 000000000..9704e16e4 --- /dev/null +++ b/source/OpenBveApi/Resources/Languages.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\..\..\assets\Languages\en-US.xlf;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + \ No newline at end of file diff --git a/source/OpenBveApi/Resource.Designer.cs b/source/OpenBveApi/Resources/Licenses.Designer.cs similarity index 63% rename from source/OpenBveApi/Resource.Designer.cs rename to source/OpenBveApi/Resources/Licenses.Designer.cs index 9634a7cfd..21783886a 100644 --- a/source/OpenBveApi/Resource.Designer.cs +++ b/source/OpenBveApi/Resources/Licenses.Designer.cs @@ -1,45 +1,45 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // -// ���̃R�[�h�̓c�[���ɂ���Đ�������܂����B -// �����^�C�� �o�[�W����:4.0.30319.42000 +// このコードはツールによって生成されました。 +// ランタイム バージョン:4.0.30319.42000 // -// ���̃t�@�C���ւ̕ύX�́A�ȉ��̏󋵉��ŕs���ȓ���̌����ɂȂ�����A -// �R�[�h���Đ��������Ƃ��ɑ��������肵�܂��B +// このファイルへの変更は、以下の状況下で不正な動作の原因になったり、 +// コードが再生成されるときに損失したりします。 // //------------------------------------------------------------------------------ -namespace OpenBveApi { +namespace OpenBveApi.Resources { using System; /// - /// ���[�J���C�Y���ꂽ������Ȃǂ��������邽�߂́A�����Ɍ^�w�肳�ꂽ���\�[�X �N���X�ł��B + /// ローカライズされた文字列などを検索するための、厳密に型指定されたリソース クラスです。 /// - // ���̃N���X�� StronglyTypedResourceBuilder �N���X�� ResGen - // �܂��� Visual Studio �̂悤�ȃc�[�����g�p���Ď�����������܂����B - // �����o�[��lj��܂��͍폜����ɂ́A.ResX �t�@�C����ҏW���āA/str �I�v�V�����Ƌ��� - // ResGen �����s���������A�܂��� VS �v���W�F�N�g���r���h�������܂��B + // このクラスは StronglyTypedResourceBuilder クラスが ResGen + // または Visual Studio のようなツールを使用して自動生成されました。 + // メンバーを追加または削除するには、.ResX ファイルを編集して、/str オプションと共に + // ResGen を実行し直すか、または VS プロジェクトをビルドし直します。 [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class Resource { + public class Licenses { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resource() { + internal Licenses() { } /// - /// ���̃N���X�Ŏg�p����Ă���L���b�V�����ꂽ ResourceManager �C���X�^���X��Ԃ��܂��B + /// このクラスで使用されているキャッシュされた ResourceManager インスタンスを返します。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("OpenBveApi.Resource", typeof(Resource).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("OpenBveApi.Resources.Licenses", typeof(Licenses).Assembly); resourceMan = temp; } return resourceMan; @@ -47,8 +47,8 @@ internal Resource() { } /// - /// ���ׂĂɂ‚��āA���݂̃X���b�h�� CurrentUICulture �v���p�e�B���I�[�o�[���C�h���܂� - /// ���݂̃X���b�h�� CurrentUICulture �v���p�e�B���I�[�o�[���C�h���܂��B + /// すべてについて、現在のスレッドの CurrentUICulture プロパティをオーバーライドします + /// 現在のスレッドの CurrentUICulture プロパティをオーバーライドします。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Globalization.CultureInfo Culture { @@ -61,22 +61,26 @@ internal Resource() { } /// - /// The MIT License (MIT) + /// AssimpParser is a cross-ported copy of Open Asset Import Library (assimp) by assimp team. /// - ///Copyright (c) .NET Foundation and Contributors + ///The original repository / source code may be found at the following address: + ///https://github.com/assimp/assimp + /// + /// + ///Open Asset Import Library (assimp) /// + ///Copyright (c) 2006-2016, assimp team, 2018, The openBVE Project ///All rights reserved. /// - ///Permission is hereby granted, free of charge, to any person obtaining a copy - ///of this software and associated documentation files (the "Software"), to deal - ///in the Software without restriction, including without limitation the rights - ///to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - ///copies of the Software, and to permit persons to whom the Software is - ///furnished to do so, subject to t [�c��̕�����͐؂�l�߂��܂���]"; �ɗގ����Ă��郍�[�J���C�Y���ꂽ��������������܂��B + ///Redistribution and use of this software in source and binary forms, + ///with or without modification, are permitted provided that the + ///following conditions are met: + /// + ///* Redistrib [残りの文字列は切り詰められました]"; に類似しているローカライズされた文字列を検索します。 /// - public static string CoreFX { + public static string AssimpParser { get { - return ResourceManager.GetString("CoreFX", resourceCulture); + return ResourceManager.GetString("AssimpParser", resourceCulture); } } @@ -92,7 +96,7 @@ public static string CoreFX { ///copies of the Software, and to permit persons to whom the Software is ///furnished to do so, subject to the following conditions: /// - ///The above cop [�c��̕�����͐؂�l�߂��܂���]"; �ɗގ����Ă��郍�[�J���C�Y���ꂽ��������������܂��B + ///The above cop [残りの文字列は切り詰められました]"; に類似しているローカライズされた文字列を検索します。 /// public static string CS_Script { get { @@ -111,7 +115,7 @@ public static string CS_Script { /// this list of conditions and the following disclaimer. ///2. Redistributions in binary form must reproduce the above copyright notice, /// this list of conditions and the following disclaimer in the documentation - /// and/or other ma [�c��̕�����͐؂�l�߂��܂���]"; �ɗގ����Ă��郍�[�J���C�Y���ꂽ��������������܂��B + /// and/or other ma [残りの文字列は切り詰められました]"; に類似しているローカライズされた文字列を検索します。 /// public static string Cursors { get { @@ -119,6 +123,26 @@ public static string Cursors { } } + /// + /// The MIT License (MIT) + /// + ///Copyright (c) .NET Foundation and Contributors + /// + ///All rights reserved. + /// + ///Permission is hereby granted, free of charge, to any person obtaining a copy + ///of this software and associated documentation files (the "Software"), to deal + ///in the Software without restriction, including without limitation the rights + ///to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + ///copies of the Software, and to permit persons to whom the Software is + ///furnished to do so, subject to t [残りの文字列は切り詰められました]"; に類似しているローカライズされた文字列を検索します。 + /// + public static string dotnet_runtime { + get { + return ResourceManager.GetString("dotnet_runtime", resourceCulture); + } + } + /// /// Software Licenses that apply to the DotNetZip library and tools /// @@ -129,7 +153,7 @@ public static string Cursors { ///Original intellectual property in DotNetZip is provided under the Ms-PL: /// /// Copyright (c) 2006 - 2011 Dino Chiesa - /// Copyright (c) 2006, 200 [�c��̕�����͐؂�l�߂��܂���]"; �ɗގ����Ă��郍�[�J���C�Y���ꂽ��������������܂��B + /// Copyright (c) 2006, 200 [残りの文字列は切り詰められました]"; に類似しているローカライズされた文字列を検索します。 /// public static string DotNetZip { get { @@ -138,20 +162,17 @@ public static string DotNetZip { } /// - /// <?xml version="1.0" encoding="utf-8" standalone="yes"?> - ///<xliff version="1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2.xsd" xmlns="urn:oasis:names:tc:xliff:document:1.2"> - /// <file datatype="plaintext" original="en-US.xlf" source-language="en-US"> - /// <body> - /// <group id="language"> - /// <trans-unit id="name"> - /// <source>English (United States)</source> - /// </trans-unit> - /// <trans-unit id="flag"> - /// <source>US.png</ [�c��̕�����͐؂�l�߂��܂���]"; �ɗގ����Ă��郍�[�J���C�Y���ꂽ��������������܂��B + /// MIT License + /// + ///Copyright (c) 2019 Rico Suter + /// + ///Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + /// + ///The above copyright notice a [残りの文字列は切り詰められました]"; に類似しているローカライズされた文字列を検索します。 /// - public static string en_US { + public static string Namotion_Reflection { get { - return ResourceManager.GetString("en_US", resourceCulture); + return ResourceManager.GetString("Namotion_Reflection", resourceCulture); } } @@ -166,7 +187,7 @@ public static string en_US { /// ///A "contribution" is the original software, or any additions or changes to the software. /// - ///A "contributor" is any person that distributes its contribution [�c��̕�����͐؂�l�߂��܂���]"; �ɗގ����Ă��郍�[�J���C�Y���ꂽ��������������܂��B + ///A "contributor" is any person that distributes its contribution [残りの文字列は切り詰められました]"; に類似しているローカライズされた文字列を検索します。 /// public static string NAudio { get { @@ -184,7 +205,7 @@ public static string NAudio { ///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 t [�c��̕�����͐؂�l�߂��܂���]"; �ɗގ����Ă��郍�[�J���C�Y���ꂽ��������������܂��B + ///A "contributor" is any person t [残りの文字列は切り詰められました]"; に類似しているローカライズされた文字列を検索します。 /// public static string NAudio_Vorbis { get { @@ -192,6 +213,36 @@ public static string NAudio_Vorbis { } } + /// + /// The MIT License (MIT) + /// + ///Copyright (c) 2007 James Newton-King + /// + ///Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + /// + ///The above c [残りの文字列は切り詰められました]"; に類似しているローカライズされた文字列を検索します。 + /// + public static string Newtonsoft_Json { + get { + return ResourceManager.GetString("Newtonsoft_Json", resourceCulture); + } + } + + /// + /// The MIT License (MIT) + /// + ///Copyright (c) 2016 Rico Suter + /// + ///Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + /// + ///The above copyrigh [残りの文字列は切り詰められました]"; に類似しているローカライズされた文字列を検索します。 + /// + public static string NJsonSchema { + get { + return ResourceManager.GetString("NJsonSchema", resourceCulture); + } + } + /// /// MIT License /// @@ -202,7 +253,7 @@ public static string NAudio_Vorbis { ///in the Software without restriction, including without limitation the rights ///to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ///copies of the Software, and to permit persons to whom the Software is - ///furnished to do so, subject to the following conditions [�c��̕�����͐؂�l�߂��܂���]"; �ɗގ����Ă��郍�[�J���C�Y���ꂽ��������������܂��B + ///furnished to do so, subject to the following conditions [残りの文字列は切り詰められました]"; に類似しているローカライズされた文字列を検索します。 /// public static string NLayer { get { @@ -220,7 +271,7 @@ public static string NLayer { ///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 t [�c��̕�����͐؂�l�߂��܂���]"; �ɗގ����Ă��郍�[�J���C�Y���ꂽ��������������܂��B + ///A "contributor" is any person t [残りの文字列は切り詰められました]"; に類似しているローカライズされた文字列を検索します。 /// public static string NVorbis { get { @@ -228,12 +279,33 @@ public static string NVorbis { } } + /// + /// GNU LIBRARY GENERAL PUBLIC LICENSE + /// Version 2, June 1991 + /// + /// Copyright (C) 1991 Free Software Foundation, Inc. + /// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + /// Everyone is permitted to copy and distribute verbatim copies + /// of this license document, but changing it is not allowed. + /// + ///[This is the first released version of the library GPL. It is + /// numbered 2 because it goes with version 2 of the ordinary GPL.] + /// + /// Preamble + /// /// [残りの文字列は切り詰められました]"; に類似しているローカライズされた文字列を検索します。 + /// + public static string OpenALSoft { + get { + return ResourceManager.GetString("OpenALSoft", resourceCulture); + } + } + /// /// The Open Toolkit library license /// ///Copyright (c) 2006 - 2014 Stefanos Apostolopoulos <stapostol@gmail.com> for the Open Toolkit library. /// - ///Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Softwar [�c��̕�����͐؂�l�߂��܂���]"; �ɗގ����Ă��郍�[�J���C�Y���ꂽ��������������܂��B + ///Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Softwar [残りの文字列は切り詰められました]"; に類似しているローカライズされた文字列を検索します。 /// public static string OpenTK { get { @@ -246,7 +318,7 @@ public static string OpenTK { /// ///Copyright (c) .NET Foundation /// - ///All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: [�c��̕�����͐؂�l�߂��܂���]"; �ɗގ����Ă��郍�[�J���C�Y���ꂽ��������������܂��B + ///All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: [残りの文字列は切り詰められました]"; に類似しているローカライズされた文字列を検索します。 /// public static string Prism { get { @@ -267,7 +339,7 @@ public static string Prism { ///Unless required by applicable law or agreed to in writing, software ///distributed under the License is distributed on an "AS IS" BASIS, ///WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - ///implied. See the Lic [�c��̕�����͐؂�l�߂��܂���]"; �ɗގ����Ă��郍�[�J���C�Y���ꂽ��������������܂��B + ///implied. See the Lic [残りの文字列は切り詰められました]"; に類似しているローカライズされた文字列を検索します。 /// public static string ReactiveExtensions { get { @@ -287,7 +359,7 @@ public static string ReactiveExtensions { ///copies of the Software, and to permit persons to whom the Software is ///furnished to do so, subject to the following conditions: /// - /// [�c��̕�����͐؂�l�߂��܂���]"; �ɗގ����Ă��郍�[�J���C�Y���ꂽ��������������܂��B + /// [残りの文字列は切り詰められました]"; に類似しているローカライズされた文字列を検索します。 /// public static string ReactiveProperty { get { @@ -309,7 +381,7 @@ public static string ReactiveProperty { /// ///Permission is hereby granted, free of charge, to any person obtaining a copy of ///this software and associated documentation files (the "Software"), to deal in - ///the Sof [�c��̕�����͐؂�l�߂��܂���]"; �ɗގ����Ă��郍�[�J���C�Y���ꂽ��������������܂��B + ///the Sof [残りの文字列は切り詰められました]"; に類似しているローカライズされた文字列を検索します。 /// public static string SanYing_Input { get { @@ -329,7 +401,7 @@ public static string SanYing_Input { ///copies of the Software, and to permit persons to whom the Software is ///furnished to do so, subject to the following conditions: /// - ///The above [�c��̕�����͐؂�l�߂��܂���]"; �ɗގ����Ă��郍�[�J���C�Y���ꂽ��������������܂��B + ///The above [残りの文字列は切り詰められました]"; に類似しているローカライズされた文字列を検索します。 /// public static string SharpCompress { get { @@ -348,7 +420,7 @@ public static string SharpCompress { /// this list of conditions and the following disclaimer. ///2. Redistributions in binary form must reproduce the above copyright notice, /// this list of conditions and the following disclaimer in the documentation - /// and/or other ma [�c��̕�����͐؂�l�߂��܂���]"; �ɗގ����Ă��郍�[�J���C�Y���ꂽ��������������܂��B + /// and/or other ma [残りの文字列は切り詰められました]"; に類似しているローカライズされた文字列を検索します。 /// public static string TrainEditor2_Icons { get { @@ -371,32 +443,12 @@ public static string TrainEditor2_Icons { /// the creation of Modifications. /// /// 1.2. "Contributor Version" means the combination of the Original - /// Code, prior Modifications used by a [�c��̕�����͐؂�l�߂��܂���]"; �ɗގ����Ă��郍�[�J���C�Y���ꂽ��������������܂��B + /// Code, prior Modifications used by a [残りの文字列は切り詰められました]"; に類似しているローカライズされた文字列を検索します。 /// public static string Ude { get { return ResourceManager.GetString("Ude", resourceCulture); } } - - /// - /// The MIT License (MIT) - /// - ///Copyright (c) 2015 Microsoft - /// - ///Permission is hereby granted, free of charge, to any person obtaining a copy - ///of this software and associated documentation files (the "Software"), to deal - ///in the Software without restriction, including without limitation the rights - ///to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - ///copies of the Software, and to permit persons to whom the Software is - ///furnished to do so, subject to the following conditions: - /// - ///The above copy [�c��̕�����͐؂�l�߂��܂���]"; �ɗގ����Ă��郍�[�J���C�Y���ꂽ��������������܂��B - /// - public static string XamlBehaviorsForWPF { - get { - return ResourceManager.GetString("XamlBehaviorsForWPF", resourceCulture); - } - } } } diff --git a/source/OpenBveApi/Resource.resx b/source/OpenBveApi/Resources/Licenses.resx similarity index 70% rename from source/OpenBveApi/Resource.resx rename to source/OpenBveApi/Resources/Licenses.resx index 0725ac60a..d7ddb425d 100644 --- a/source/OpenBveApi/Resource.resx +++ b/source/OpenBveApi/Resources/Licenses.resx @@ -1,4 +1,4 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\..\..\assets\Schemas\ats.json;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + \ No newline at end of file diff --git a/source/Plugins/Object.Animated/packages.config b/source/Plugins/Object.Animated/packages.config deleted file mode 100644 index 53e2b343a..000000000 --- a/source/Plugins/Object.Animated/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/source/Plugins/Object.LokSim/packages.config b/source/Plugins/Object.LokSim/packages.config deleted file mode 100644 index 53e2b343a..000000000 --- a/source/Plugins/Object.LokSim/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file