diff --git a/OpenBVE.sln b/OpenBVE.sln index 0ba02dcc9a..340dd0a23b 100644 --- a/OpenBVE.sln +++ b/OpenBVE.sln @@ -28,6 +28,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenBve", "source\OpenBVE\O {7D0D7673-C77A-4140-A5C6-075D825AC11D} = {7D0D7673-C77A-4140-A5C6-075D825AC11D} {8DAA1CF4-A29A-42CE-8649-34E0FBC0D97C} = {8DAA1CF4-A29A-42CE-8649-34E0FBC0D97C} {A2FC4D71-1ED9-40D4-B746-FE6AB3C7D55E} = {A2FC4D71-1ED9-40D4-B746-FE6AB3C7D55E} + {A6E3D875-DDFA-446A-AAF5-BFAFF3C9EF45} = {A6E3D875-DDFA-446A-AAF5-BFAFF3C9EF45} {ACAFCA35-01B7-479C-AD5F-9BCE0F8A597B} = {ACAFCA35-01B7-479C-AD5F-9BCE0F8A597B} {B520B1D7-3889-4C88-9E0F-CB96802D5CD1} = {B520B1D7-3889-4C88-9E0F-CB96802D5CD1} {C4BE7A1F-9CCD-4E78-8341-741ABDA8E026} = {C4BE7A1F-9CCD-4E78-8341-741ABDA8E026} @@ -176,6 +177,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Train.OpenBve", "source\Plu EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Win32PluginProxy", "source\Plugins\Win32PluginProxy\Win32PluginProxy.csproj", "{B9014791-7170-4DCD-B3F1-2B7518A85C83}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Train.MsTs", "source\Plugins\Train.MsTs\Train.MsTs.csproj", "{A6E3D875-DDFA-446A-AAF5-BFAFF3C9EF45}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Route.Bve5", "source\Plugins\Route.Bve5\Route.Bve5.csproj", "{C4BE7A1F-9CCD-4E78-8341-741ABDA8E026}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Formats.OpenBve", "source\Plugins\Formats.OpenBve\Formats.OpenBve.csproj", "{7ED7B285-FAE6-4B34-ACC5-87399F27C8BC}" @@ -859,6 +862,24 @@ Global {B9014791-7170-4DCD-B3F1-2B7518A85C83}.Release|Mixed Platforms.Build.0 = Release|Any CPU {B9014791-7170-4DCD-B3F1-2B7518A85C83}.Release|x86.ActiveCfg = Release|Any CPU {B9014791-7170-4DCD-B3F1-2B7518A85C83}.Release|x86.Build.0 = Release|Any CPU + {A6E3D875-DDFA-446A-AAF5-BFAFF3C9EF45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A6E3D875-DDFA-446A-AAF5-BFAFF3C9EF45}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A6E3D875-DDFA-446A-AAF5-BFAFF3C9EF45}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {A6E3D875-DDFA-446A-AAF5-BFAFF3C9EF45}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {A6E3D875-DDFA-446A-AAF5-BFAFF3C9EF45}.Debug|x86.ActiveCfg = Debug|Any CPU + {A6E3D875-DDFA-446A-AAF5-BFAFF3C9EF45}.Debug|x86.Build.0 = Debug|Any CPU + {A6E3D875-DDFA-446A-AAF5-BFAFF3C9EF45}.GLMenu|Any CPU.ActiveCfg = Release|Any CPU + {A6E3D875-DDFA-446A-AAF5-BFAFF3C9EF45}.GLMenu|Any CPU.Build.0 = Release|Any CPU + {A6E3D875-DDFA-446A-AAF5-BFAFF3C9EF45}.GLMenu|Mixed Platforms.ActiveCfg = Release|Any CPU + {A6E3D875-DDFA-446A-AAF5-BFAFF3C9EF45}.GLMenu|Mixed Platforms.Build.0 = Release|Any CPU + {A6E3D875-DDFA-446A-AAF5-BFAFF3C9EF45}.GLMenu|x86.ActiveCfg = Release|Any CPU + {A6E3D875-DDFA-446A-AAF5-BFAFF3C9EF45}.GLMenu|x86.Build.0 = Release|Any CPU + {A6E3D875-DDFA-446A-AAF5-BFAFF3C9EF45}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A6E3D875-DDFA-446A-AAF5-BFAFF3C9EF45}.Release|Any CPU.Build.0 = Release|Any CPU + {A6E3D875-DDFA-446A-AAF5-BFAFF3C9EF45}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {A6E3D875-DDFA-446A-AAF5-BFAFF3C9EF45}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {A6E3D875-DDFA-446A-AAF5-BFAFF3C9EF45}.Release|x86.ActiveCfg = Release|Any CPU + {A6E3D875-DDFA-446A-AAF5-BFAFF3C9EF45}.Release|x86.Build.0 = Release|Any CPU {C4BE7A1F-9CCD-4E78-8341-741ABDA8E026}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C4BE7A1F-9CCD-4E78-8341-741ABDA8E026}.Debug|Any CPU.Build.0 = Debug|Any CPU {C4BE7A1F-9CCD-4E78-8341-741ABDA8E026}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU @@ -930,6 +951,7 @@ Global {7D0D7673-C77A-4140-A5C6-075D825AC11D} = {D75531B7-000E-432E-A168-51846256A9D1} {D3710390-CD0E-4E14-8E4F-80302D797D5E} = {F49789F2-97F3-45B3-BC85-F4E09C0D120D} {B9014791-7170-4DCD-B3F1-2B7518A85C83} = {F49789F2-97F3-45B3-BC85-F4E09C0D120D} + {A6E3D875-DDFA-446A-AAF5-BFAFF3C9EF45} = {F49789F2-97F3-45B3-BC85-F4E09C0D120D} {C4BE7A1F-9CCD-4E78-8341-741ABDA8E026} = {D75531B7-000E-432E-A168-51846256A9D1} {7ED7B285-FAE6-4B34-ACC5-87399F27C8BC} = {16553295-E70F-4596-AA78-848EEA576C4A} EndGlobalSection diff --git a/Readme.md b/Readme.md index d0a59c703c..6584db02f7 100644 --- a/Readme.md +++ b/Readme.md @@ -6,11 +6,15 @@ This repository contains the source code for the Train Simulator OpenBVE, a 3D cab based simulator. -The simulator supports the following route formats: +Supported route formats: * Native CSV / RW. * BVE5 TXT format. * Mechanik DAT format. +Supported train formats: +* BVE2 / BVE4, with native OpenBVE extensions. +* Microsoft Train Simulator (MSTS) + OpenBVE is built in OpenGL, using the OpenTK framework for windowing. ### Fixed Errata diff --git a/assets/Compatibility/numbers.png b/assets/Compatibility/numbers.png new file mode 100644 index 0000000000..1b61e68232 Binary files /dev/null and b/assets/Compatibility/numbers.png differ diff --git a/assets/Languages/ca-ES.xlf b/assets/Languages/ca-ES.xlf index 6d0714b94e..d194a26158 100644 --- a/assets/Languages/ca-ES.xlf +++ b/assets/Languages/ca-ES.xlf @@ -1620,6 +1620,10 @@ Other items installation directory: Carpeta d'instal·lació d'altres paquets: + + MSTS installation directory: + Carpeta d'instal·lació de MSTS: + Package compression format: Format de compressió dels paquets: diff --git a/assets/Languages/cs-CZ.xlf b/assets/Languages/cs-CZ.xlf index 0af0804201..5e3b2d4f53 100644 --- a/assets/Languages/cs-CZ.xlf +++ b/assets/Languages/cs-CZ.xlf @@ -1635,6 +1635,9 @@ Other items installation directory: Other items installation directory: + + MSTS installation directory: + Package compression format: Package compression format: diff --git a/assets/Languages/de-CH.xlf b/assets/Languages/de-CH.xlf index b1d6e59e66..569b331c7c 100644 --- a/assets/Languages/de-CH.xlf +++ b/assets/Languages/de-CH.xlf @@ -1636,6 +1636,9 @@ Other items installation directory: Other items installation directory: + + MSTS installation directory: + Package compression format: Package compression format: diff --git a/assets/Languages/de-DE.xlf b/assets/Languages/de-DE.xlf index 88b3b1535c..1426c2dfb1 100644 --- a/assets/Languages/de-DE.xlf +++ b/assets/Languages/de-DE.xlf @@ -1635,6 +1635,10 @@ Other items installation directory: Installationsordner für sonstiges: + + MSTS installation directory: + MSTS installationsordner: + Package compression format: Paketkomprimierungsformat: diff --git a/assets/Languages/en-GB.xlf b/assets/Languages/en-GB.xlf index b0da83cb1e..a8e7eed9c6 100644 --- a/assets/Languages/en-GB.xlf +++ b/assets/Languages/en-GB.xlf @@ -1636,6 +1636,9 @@ Other items installation directory: Other items installation directory: + + MSTS installation directory: + Package compression format: Package compression format: diff --git a/assets/Languages/en-US.xlf b/assets/Languages/en-US.xlf index d57dd5f404..b21ded3cda 100755 --- a/assets/Languages/en-US.xlf +++ b/assets/Languages/en-US.xlf @@ -1243,6 +1243,9 @@ Other items installation directory: + + MSTS installation directory: + Package compression format: diff --git a/assets/Languages/es-ES.xlf b/assets/Languages/es-ES.xlf index 3eef65cc87..07a4a32f6b 100644 --- a/assets/Languages/es-ES.xlf +++ b/assets/Languages/es-ES.xlf @@ -1636,6 +1636,10 @@ Other items installation directory: Carpeta de instalación de otros paquetes: + + MSTS installation directory: + Carpeta de instalación de MSTS: + Package compression format: Formato de compresión de los paquetes: diff --git a/assets/Languages/fi-FI.xlf b/assets/Languages/fi-FI.xlf index fc825ef21b..1695d37af9 100644 --- a/assets/Languages/fi-FI.xlf +++ b/assets/Languages/fi-FI.xlf @@ -1635,6 +1635,9 @@ Other items installation directory: Other items installation directory: + + MSTS installation directory: + Package compression format: Package compression format: diff --git a/assets/Languages/fr-FR.xlf b/assets/Languages/fr-FR.xlf index 49341868d7..e078483d69 100644 --- a/assets/Languages/fr-FR.xlf +++ b/assets/Languages/fr-FR.xlf @@ -1635,6 +1635,9 @@ Other items installation directory: Other items installation directory: + + MSTS installation directory: + Package compression format: Package compression format: diff --git a/assets/Languages/hu-HU.xlf b/assets/Languages/hu-HU.xlf index a6a3da38a9..ba880ecd2e 100644 --- a/assets/Languages/hu-HU.xlf +++ b/assets/Languages/hu-HU.xlf @@ -1638,6 +1638,10 @@ Other items installation directory: Egyebek telepítési könyvtára: + + MSTS installation directory: + MSTS telepítési könyvtára: + Package compression format: Csomag tömörítésének formátuma: diff --git a/assets/Languages/id-ID.xlf b/assets/Languages/id-ID.xlf index 4556dc1ee4..e45183b8bb 100644 --- a/assets/Languages/id-ID.xlf +++ b/assets/Languages/id-ID.xlf @@ -1609,6 +1609,10 @@ Other items installation directory: Folder konten lainnya: + + MSTS installation directory: + Folder MSTS: + Package compression format: Format file kompresi: diff --git a/assets/Languages/it-IT.xlf b/assets/Languages/it-IT.xlf index 246cd2d9b6..47830c6570 100644 --- a/assets/Languages/it-IT.xlf +++ b/assets/Languages/it-IT.xlf @@ -1636,6 +1636,9 @@ Other items installation directory: Other items installation directory: + + MSTS installation directory: + Package compression format: Package compression format: diff --git a/assets/Languages/ja-JP.xlf b/assets/Languages/ja-JP.xlf index 79774feb22..361ff5c7cc 100644 --- a/assets/Languages/ja-JP.xlf +++ b/assets/Languages/ja-JP.xlf @@ -1636,6 +1636,9 @@ Other items installation directory: その他のアイテムのインストール場所: + + MSTS installation directory: + Package compression format: パッケージの圧縮形式: diff --git a/assets/Languages/ko-KR.xlf b/assets/Languages/ko-KR.xlf index 5e3da0f70a..4860e9d191 100644 --- a/assets/Languages/ko-KR.xlf +++ b/assets/Languages/ko-KR.xlf @@ -1636,6 +1636,9 @@ Other items installation directory: Other items installation directory: + + MSTS installation directory: + Package compression format: Package compression format: diff --git a/assets/Languages/ms_MY.xlf b/assets/Languages/ms_MY.xlf index 63ea3d4c18..ed1e57cf7d 100644 --- a/assets/Languages/ms_MY.xlf +++ b/assets/Languages/ms_MY.xlf @@ -1604,6 +1604,10 @@ Other items installation directory: Direktori pemasangan iitem lain-lain: + + MSTS installation directory: + Direktori pemasangan MSTS: + Package compression format: Format mampatan pakej: diff --git a/assets/Languages/nb_NO.xlf b/assets/Languages/nb_NO.xlf index 3187d30fb5..80bf88be40 100644 --- a/assets/Languages/nb_NO.xlf +++ b/assets/Languages/nb_NO.xlf @@ -1616,6 +1616,10 @@ Other items installation directory: Mappe for installasjon av andre ting: + + MSTS installation directory: + Mappe for installasjon MSTS: + Package compression format: Pakke kompresjonsformat: diff --git a/assets/Languages/nl-NL.xlf b/assets/Languages/nl-NL.xlf index de0b3dc26a..ddd03e622f 100644 --- a/assets/Languages/nl-NL.xlf +++ b/assets/Languages/nl-NL.xlf @@ -1635,6 +1635,9 @@ Other items installation directory: Other items installation directory: + + MSTS installation directory: + Package compression format: Package compression format: diff --git a/assets/Languages/pl-PL.xlf b/assets/Languages/pl-PL.xlf index 95e3b9858b..37237a0dff 100644 --- a/assets/Languages/pl-PL.xlf +++ b/assets/Languages/pl-PL.xlf @@ -1607,6 +1607,10 @@ Other items installation directory: Folder instalacji innych: + + MSTS installation directory: + Folder instalacji MSTS: + Package compression format: Format kompresji pakietów: diff --git a/assets/Languages/pt-BR.xlf b/assets/Languages/pt-BR.xlf index 3794a91ab4..81228d66b5 100644 --- a/assets/Languages/pt-BR.xlf +++ b/assets/Languages/pt-BR.xlf @@ -1636,6 +1636,9 @@ Other items installation directory: Other items installation directory: + + MSTS installation directory: + Package compression format: Package compression format: diff --git a/assets/Languages/pt-PT.xlf b/assets/Languages/pt-PT.xlf index f8bca89a18..2430d50123 100644 --- a/assets/Languages/pt-PT.xlf +++ b/assets/Languages/pt-PT.xlf @@ -1652,6 +1652,10 @@ Other items installation directory: Directório de instalação de outros itens: + + MSTS installation directory: + Directório de instalação da MSTS: + Package compression format: Formato de compressão do pacote: diff --git a/assets/Languages/ro-RO.xlf b/assets/Languages/ro-RO.xlf index 26d530c8b6..018615c026 100644 --- a/assets/Languages/ro-RO.xlf +++ b/assets/Languages/ro-RO.xlf @@ -1636,6 +1636,9 @@ Other items installation directory: Other items installation directory: + + MSTS installation directory: + Package compression format: Package compression format: diff --git a/assets/Languages/ru-RU.xlf b/assets/Languages/ru-RU.xlf index 9aac65118b..938bcafd7c 100644 --- a/assets/Languages/ru-RU.xlf +++ b/assets/Languages/ru-RU.xlf @@ -1635,6 +1635,9 @@ Other items installation directory: Other items installation directory: + + MSTS installation directory: + Package compression format: Package compression format: diff --git a/assets/Languages/sk-SK.xlf b/assets/Languages/sk-SK.xlf index bdb3c99061..98f36c2820 100644 --- a/assets/Languages/sk-SK.xlf +++ b/assets/Languages/sk-SK.xlf @@ -1554,6 +1554,9 @@ Other items installation directory: Other items installation directory: + + MSTS installation directory: + Package compression format: Package compression format: diff --git a/assets/Languages/uk-UA.xlf b/assets/Languages/uk-UA.xlf index ecd76db99d..99984897a7 100644 --- a/assets/Languages/uk-UA.xlf +++ b/assets/Languages/uk-UA.xlf @@ -1637,6 +1637,9 @@ Other items installation directory: Other items installation directory: + + MSTS installation directory: + Package compression format: Package compression format: diff --git a/assets/Languages/zh-CN.xlf b/assets/Languages/zh-CN.xlf index f811002066..ab94d8fec6 100644 --- a/assets/Languages/zh-CN.xlf +++ b/assets/Languages/zh-CN.xlf @@ -1634,6 +1634,9 @@ Other items installation directory: 其他安装目录: + + MSTS installation directory: + Package compression format: 扩展包压缩格式: diff --git a/assets/Languages/zh-HK.xlf b/assets/Languages/zh-HK.xlf index d6343459f3..fb70d5d5ee 100644 --- a/assets/Languages/zh-HK.xlf +++ b/assets/Languages/zh-HK.xlf @@ -1639,6 +1639,9 @@ Other items installation directory: 其他項目安裝資料夾: + + MSTS installation directory: + Package compression format: 擴展包壓縮格式: diff --git a/assets/Languages/zh-TW.xlf b/assets/Languages/zh-TW.xlf index f0c2e34cca..a75ff3dd87 100644 --- a/assets/Languages/zh-TW.xlf +++ b/assets/Languages/zh-TW.xlf @@ -1636,6 +1636,9 @@ Other items installation directory: Other items installation directory: + + MSTS installation directory: + Package compression format: Package compression format: diff --git a/assets/Menu/icon_msts.png b/assets/Menu/icon_msts.png new file mode 100644 index 0000000000..5770a13c6c Binary files /dev/null and b/assets/Menu/icon_msts.png differ diff --git a/source/LibRender2/BaseRenderer.cs b/source/LibRender2/BaseRenderer.cs index c4eb469bad..22e2e84064 100644 --- a/source/LibRender2/BaseRenderer.cs +++ b/source/LibRender2/BaseRenderer.cs @@ -692,19 +692,19 @@ public void InitializeVisibility() { for (int i = 0; i < StaticObjectStates.Count; i++) { - VAOExtensions.CreateVAO(StaticObjectStates[i].Prototype?.Mesh, false, DefaultShader.VertexLayout, this); - if (StaticObjectStates[i].Matricies != null) - { - GL.CreateBuffers(1, out StaticObjectStates[i].MatrixBufferIndex); - } + VAOExtensions.CreateVAO(StaticObjectStates[i].Prototype.Mesh, false, DefaultShader.VertexLayout, this); + /* + * n.b. + * Only create the actual matrix buffer at first frame render time + * I can't find why at the minute, but Object Viewer otherwise doesn't show them, and attempting + * to retrieve previously set matricies from the shader shows all zeros + * + * Probably a timing issue, but it works doing it that way :/ + */ } for (int i = 0; i < DynamicObjectStates.Count; i++) { - VAOExtensions.CreateVAO(DynamicObjectStates[i].Prototype?.Mesh, false, DefaultShader.VertexLayout, this); - if (DynamicObjectStates[i].Matricies != null) - { - GL.CreateBuffers(1, out DynamicObjectStates[i].MatrixBufferIndex); - } + VAOExtensions.CreateVAO(DynamicObjectStates[i].Prototype.Mesh, false, DefaultShader.VertexLayout, this); } } ObjectsSortedByStart = StaticObjectStates.Select((x, i) => new { Index = i, Distance = x.StartingDistance }).OrderBy(x => x.Distance).Select(x => x.Index).ToArray(); diff --git a/source/ObjectViewer/ProgramS.cs b/source/ObjectViewer/ProgramS.cs index 809267a014..d48b6330ec 100644 --- a/source/ObjectViewer/ProgramS.cs +++ b/source/ObjectViewer/ProgramS.cs @@ -315,18 +315,22 @@ internal static void RefreshObjects() { try { - if(Files[i].EndsWith(".dat", StringComparison.InvariantCultureIgnoreCase) || Files[i].EndsWith(".xml", StringComparison.InvariantCultureIgnoreCase) || Files[i].EndsWith(".cfg", StringComparison.InvariantCultureIgnoreCase)) + if(Files[i].EndsWith(".dat", StringComparison.InvariantCultureIgnoreCase) || Files[i].EndsWith(".xml", StringComparison.InvariantCultureIgnoreCase) || Files[i].EndsWith(".cfg", StringComparison.InvariantCultureIgnoreCase) || Files[i].EndsWith(".con", StringComparison.InvariantCultureIgnoreCase)) { - string currentTrainFolder = Path.GetDirectoryName(Files[i]); + string currentTrain = Files[i]; + if (currentTrain.EndsWith("extensions.cfg", StringComparison.InvariantCultureIgnoreCase)) + { + currentTrain = System.IO.Path.GetDirectoryName(currentTrain); + } bool canLoad = false; for (int j = 0; j < Program.CurrentHost.Plugins.Length; j++) { - if (Program.CurrentHost.Plugins[j].Train != null && Program.CurrentHost.Plugins[j].Train.CanLoadTrain(currentTrainFolder)) + if (Program.CurrentHost.Plugins[j].Train != null && Program.CurrentHost.Plugins[j].Train.CanLoadTrain(currentTrain)) { Control[] dummyControls = new Control[0]; TrainManager.Trains = new List { new TrainBase(TrainState.Available, TrainType.LocalPlayerTrain) }; AbstractTrain playerTrain = TrainManager.Trains[0]; - Program.CurrentHost.Plugins[j].Train.LoadTrain(Encoding.UTF8, currentTrainFolder, ref playerTrain, ref dummyControls); + Program.CurrentHost.Plugins[j].Train.LoadTrain(Encoding.UTF8, currentTrain, ref playerTrain, ref dummyControls); TrainManager.PlayerTrain = TrainManager.Trains[0]; canLoad = true; break; @@ -423,7 +427,7 @@ internal static void KeyDown(object sender, KeyboardKeyEventArgs e) { CheckFileExists = true, Multiselect = true, - Filter = @"All supported object files|*.csv;*.b3d;*.x;*.animated;extensions.cfg;*.l3dobj;*.l3dgrp;*.obj;*.s;train.xml|openBVE Objects|*.csv;*.b3d;*.x;*.animated;extensions.cfg;train.xml|LokSim 3D Objects|*.l3dobj;*.l3dgrp|Wavefront Objects|*.obj|Microsoft Train Simulator Objects|*.s|All files|*" + Filter = @"All supported object files|*.csv;*.b3d;*.x;*.animated;extensions.cfg;*.l3dobj;*.l3dgrp;*.obj;*.s;train.xml;*.con|openBVE Objects|*.csv;*.b3d;*.x;*.animated;extensions.cfg;train.xml|LokSim 3D Objects|*.l3dobj;*.l3dgrp|Wavefront Objects|*.obj|Microsoft Train Simulator Files|*.s;*.con|All files|*" }; if (Dialog.ShowDialog() == DialogResult.OK) { @@ -431,11 +435,11 @@ internal static void KeyDown(object sender, KeyboardKeyEventArgs e) string[] f = Dialog.FileNames; for (int i = 0; i < f.Length; i++) { - string currentTrainFolder = string.Empty; - if(f[i].EndsWith(".dat", StringComparison.InvariantCultureIgnoreCase) || f[i].EndsWith(".xml", StringComparison.InvariantCultureIgnoreCase) || f[i].EndsWith(".cfg", StringComparison.InvariantCultureIgnoreCase)) + string currentTrain = string.Empty; + if(f[i].EndsWith(".dat", StringComparison.InvariantCultureIgnoreCase) || f[i].EndsWith(".xml", StringComparison.InvariantCultureIgnoreCase) || f[i].EndsWith(".cfg", StringComparison.InvariantCultureIgnoreCase) || f[i].EndsWith(".con", StringComparison.InvariantCultureIgnoreCase)) { // only check to see if it's a train if this is a specified filetype, else we'll start loading the full train from an object in it's folder - currentTrainFolder = Path.GetDirectoryName(f[i]); + currentTrain = f[i].EndsWith(".con", StringComparison.InvariantCultureIgnoreCase) ? f[i] : Path.GetDirectoryName(f[i]); } for (int j = 0; j < Program.CurrentHost.Plugins.Length; j++) { @@ -453,7 +457,7 @@ internal static void KeyDown(object sender, KeyboardKeyEventArgs e) { Files.Add(f[i]); } - if (!string.IsNullOrEmpty(currentTrainFolder) && Program.CurrentHost.Plugins[j].Train != null && Program.CurrentHost.Plugins[j].Train.CanLoadTrain(currentTrainFolder)) + if (!string.IsNullOrEmpty(currentTrain) && Program.CurrentHost.Plugins[j].Train != null && Program.CurrentHost.Plugins[j].Train.CanLoadTrain(currentTrain)) { Files.Add(f[i]); } diff --git a/source/OpenBVE/System/Loading.cs b/source/OpenBVE/System/Loading.cs index e098eded14..78187f2a96 100644 --- a/source/OpenBVE/System/Loading.cs +++ b/source/OpenBVE/System/Loading.cs @@ -472,7 +472,7 @@ private static void LoadEverythingThreaded() { // load plugin for (int i = 0; i < Program.TrainManager.Trains.Count; i++) { if ( Program.TrainManager.Trains[i].State != TrainState.Bogus) { - if ( Program.TrainManager.Trains[i].IsPlayerTrain) { + if (Program.TrainManager.Trains[i].IsPlayerTrain && !string.IsNullOrEmpty(Program.TrainManager.Trains[i].TrainFolder)) { if (Program.TrainManager.Trains[i].Plugin == null && !Program.TrainManager.Trains[i].LoadCustomPlugin(Program.TrainManager.Trains[i].TrainFolder, CurrentTrainEncoding)) { Program.TrainManager.Trains[i].LoadDefaultPlugin(Program.TrainManager.Trains[i].TrainFolder); } diff --git a/source/OpenBVE/System/Options.cs b/source/OpenBVE/System/Options.cs index ff62461e1f..d166b7f47b 100644 --- a/source/OpenBVE/System/Options.cs +++ b/source/OpenBVE/System/Options.cs @@ -345,6 +345,7 @@ public override void Save(string fileName) Builder.AppendLine("[folders]"); Builder.AppendLine("route = " + RouteFolder); Builder.AppendLine("train = " + TrainFolder); + Builder.AppendLine("MSTSTrainset = " + Program.FileSystem.MSTSDirectory); Builder.AppendLine(); Builder.AppendLine("[recentlyUsedRoutes]"); for (int i = 0; i < RecentlyUsedRoutes.Length; i++) diff --git a/source/OpenBVE/UserInterface/formMain.Designer.cs b/source/OpenBVE/UserInterface/formMain.Designer.cs index 2b1ba07f03..1c762ab5c0 100644 --- a/source/OpenBVE/UserInterface/formMain.Designer.cs +++ b/source/OpenBVE/UserInterface/formMain.Designer.cs @@ -101,6 +101,53 @@ private void InitializeComponent() { this.panelOptions = new System.Windows.Forms.Panel(); this.buttonOptionsPrevious = new System.Windows.Forms.Button(); this.buttonOptionsNext = new System.Windows.Forms.Button(); + this.panelOptionsPage2 = new System.Windows.Forms.Panel(); + this.groupBoxInputDevice = new System.Windows.Forms.GroupBox(); + this.labelInputDevice = new System.Windows.Forms.Label(); + this.listviewInputDevice = new System.Windows.Forms.ListView(); + this.columnheaderInputDeviceName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnheaderInputDeviceStatus = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnheaderInputDeviceVersion = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnheaderInputDeviceProvider = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnheaderInputDeviceFileName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.checkBoxInputDeviceEnable = new System.Windows.Forms.CheckBox(); + this.buttonInputDeviceConfig = new System.Windows.Forms.Button(); + this.groupBoxObjectParser = new System.Windows.Forms.GroupBox(); + this.labelObjparser = new System.Windows.Forms.Label(); + this.comboBoxObjparser = new System.Windows.Forms.ComboBox(); + this.labelXparser = new System.Windows.Forms.Label(); + this.comboBoxXparser = new System.Windows.Forms.ComboBox(); + this.groupBoxKioskMode = new System.Windows.Forms.GroupBox(); + this.labelKioskTimeout = new System.Windows.Forms.Label(); + this.numericUpDownKioskTimeout = new System.Windows.Forms.NumericUpDown(); + this.checkBoxEnableKiosk = new System.Windows.Forms.CheckBox(); + this.groupBoxAdvancedOptions = new System.Windows.Forms.GroupBox(); + this.checkBoxPanel2Extended = new System.Windows.Forms.CheckBox(); + this.pictureboxCursor = new System.Windows.Forms.PictureBox(); + this.labelCursor = new System.Windows.Forms.Label(); + this.comboboxCursor = new System.Windows.Forms.ComboBox(); + this.checkBoxHacks = new System.Windows.Forms.CheckBox(); + this.checkBoxTransparencyFix = new System.Windows.Forms.CheckBox(); + this.checkBoxUnloadTextures = new System.Windows.Forms.CheckBox(); + this.labelTimeAcceleration = new System.Windows.Forms.Label(); + this.updownTimeAccelerationFactor = new System.Windows.Forms.NumericUpDown(); + this.checkBoxIsUseNewRenderer = new System.Windows.Forms.CheckBox(); + this.checkBoxLoadInAdvance = new System.Windows.Forms.CheckBox(); + this.groupBoxPackageOptions = new System.Windows.Forms.GroupBox(); + this.buttonMSTSTrainsetDirectory = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.textBoxMSTSTrainsetDirectory = new System.Windows.Forms.TextBox(); + this.comboBoxCompressionFormat = new System.Windows.Forms.ComboBox(); + this.labelPackageCompression = new System.Windows.Forms.Label(); + this.buttonOtherDirectory = new System.Windows.Forms.Button(); + this.labelOtherInstallDirectory = new System.Windows.Forms.Label(); + this.textBoxOtherDirectory = new System.Windows.Forms.TextBox(); + this.buttonTrainInstallationDirectory = new System.Windows.Forms.Button(); + this.labelTrainInstallDirectory = new System.Windows.Forms.Label(); + this.textBoxTrainDirectory = new System.Windows.Forms.TextBox(); + this.buttonSetRouteDirectory = new System.Windows.Forms.Button(); + this.labelRouteInstallDirectory = new System.Windows.Forms.Label(); + this.textBoxRouteDirectory = new System.Windows.Forms.TextBox(); this.panelOptionsLeft = new System.Windows.Forms.Panel(); this.groupboxDisplayMode = new System.Windows.Forms.GroupBox(); this.comboBoxFont = new System.Windows.Forms.ComboBox(); @@ -170,50 +217,6 @@ private void InitializeComponent() { this.groupboxSound = new System.Windows.Forms.GroupBox(); this.updownSoundNumber = new System.Windows.Forms.NumericUpDown(); this.labelSoundNumber = new System.Windows.Forms.Label(); - this.panelOptionsPage2 = new System.Windows.Forms.Panel(); - this.groupBoxInputDevice = new System.Windows.Forms.GroupBox(); - this.labelInputDevice = new System.Windows.Forms.Label(); - this.listviewInputDevice = new System.Windows.Forms.ListView(); - this.columnheaderInputDeviceName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); - this.columnheaderInputDeviceStatus = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); - this.columnheaderInputDeviceVersion = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); - this.columnheaderInputDeviceProvider = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); - this.columnheaderInputDeviceFileName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); - this.checkBoxInputDeviceEnable = new System.Windows.Forms.CheckBox(); - this.buttonInputDeviceConfig = new System.Windows.Forms.Button(); - this.groupBoxObjectParser = new System.Windows.Forms.GroupBox(); - this.labelObjparser = new System.Windows.Forms.Label(); - this.comboBoxObjparser = new System.Windows.Forms.ComboBox(); - this.labelXparser = new System.Windows.Forms.Label(); - this.comboBoxXparser = new System.Windows.Forms.ComboBox(); - this.groupBoxKioskMode = new System.Windows.Forms.GroupBox(); - this.labelKioskTimeout = new System.Windows.Forms.Label(); - this.numericUpDownKioskTimeout = new System.Windows.Forms.NumericUpDown(); - this.checkBoxEnableKiosk = new System.Windows.Forms.CheckBox(); - this.groupBoxAdvancedOptions = new System.Windows.Forms.GroupBox(); - this.checkBoxPanel2Extended = new System.Windows.Forms.CheckBox(); - this.pictureboxCursor = new System.Windows.Forms.PictureBox(); - this.labelCursor = new System.Windows.Forms.Label(); - this.comboboxCursor = new System.Windows.Forms.ComboBox(); - this.checkBoxHacks = new System.Windows.Forms.CheckBox(); - this.checkBoxTransparencyFix = new System.Windows.Forms.CheckBox(); - this.checkBoxUnloadTextures = new System.Windows.Forms.CheckBox(); - this.labelTimeAcceleration = new System.Windows.Forms.Label(); - this.updownTimeAccelerationFactor = new System.Windows.Forms.NumericUpDown(); - this.checkBoxIsUseNewRenderer = new System.Windows.Forms.CheckBox(); - this.checkBoxLoadInAdvance = new System.Windows.Forms.CheckBox(); - this.groupBoxPackageOptions = new System.Windows.Forms.GroupBox(); - this.comboBoxCompressionFormat = new System.Windows.Forms.ComboBox(); - this.labelPackageCompression = new System.Windows.Forms.Label(); - this.buttonOtherDirectory = new System.Windows.Forms.Button(); - this.labelOtherInstallDirectory = new System.Windows.Forms.Label(); - this.textBoxOtherDirectory = new System.Windows.Forms.TextBox(); - this.buttonTrainInstallationDirectory = new System.Windows.Forms.Button(); - this.labelTrainInstallDirectory = new System.Windows.Forms.Label(); - this.textBoxTrainDirectory = new System.Windows.Forms.TextBox(); - this.buttonSetRouteDirectory = new System.Windows.Forms.Button(); - this.labelRouteInstallDirectory = new System.Windows.Forms.Label(); - this.textBoxRouteDirectory = new System.Windows.Forms.TextBox(); this.pictureboxLanguage = new System.Windows.Forms.PictureBox(); this.comboboxLanguages = new System.Windows.Forms.ComboBox(); this.labelOptionsTitleSeparator = new System.Windows.Forms.Label(); @@ -490,6 +493,15 @@ private void InitializeComponent() { this.tabpageRouteSettings.SuspendLayout(); this.panelRouteEncoding.SuspendLayout(); this.panelOptions.SuspendLayout(); + this.panelOptionsPage2.SuspendLayout(); + this.groupBoxInputDevice.SuspendLayout(); + this.groupBoxObjectParser.SuspendLayout(); + this.groupBoxKioskMode.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownKioskTimeout)).BeginInit(); + this.groupBoxAdvancedOptions.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureboxCursor)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.updownTimeAccelerationFactor)).BeginInit(); + this.groupBoxPackageOptions.SuspendLayout(); this.panelOptionsLeft.SuspendLayout(); this.groupboxDisplayMode.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.trackBarHUDSize)).BeginInit(); @@ -514,15 +526,6 @@ private void InitializeComponent() { this.groupboxSimulation.SuspendLayout(); this.groupboxSound.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.updownSoundNumber)).BeginInit(); - this.panelOptionsPage2.SuspendLayout(); - this.groupBoxInputDevice.SuspendLayout(); - this.groupBoxObjectParser.SuspendLayout(); - this.groupBoxKioskMode.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.numericUpDownKioskTimeout)).BeginInit(); - this.groupBoxAdvancedOptions.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.pictureboxCursor)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.updownTimeAccelerationFactor)).BeginInit(); - this.groupBoxPackageOptions.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pictureboxLanguage)).BeginInit(); this.panelPanels.SuspendLayout(); this.panelReview.SuspendLayout(); @@ -593,7 +596,7 @@ private void InitializeComponent() { this.labelVerticalSeparator.BackColor = System.Drawing.Color.White; this.labelVerticalSeparator.Location = new System.Drawing.Point(158, 0); this.labelVerticalSeparator.Name = "labelVerticalSeparator"; - this.labelVerticalSeparator.Size = new System.Drawing.Size(2, 651); + this.labelVerticalSeparator.Size = new System.Drawing.Size(2, 681); this.labelVerticalSeparator.TabIndex = 9; // // buttonClose @@ -601,7 +604,7 @@ private void InitializeComponent() { this.buttonClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.buttonClose.AutoEllipsis = true; this.buttonClose.BackColor = System.Drawing.SystemColors.ButtonFace; - this.buttonClose.Location = new System.Drawing.Point(8, 599); + this.buttonClose.Location = new System.Drawing.Point(8, 629); this.buttonClose.Name = "buttonClose"; this.buttonClose.Size = new System.Drawing.Size(144, 26); this.buttonClose.TabIndex = 5; @@ -631,7 +634,7 @@ private void InitializeComponent() { this.panelStart.Controls.Add(this.labelStartTitleBackground); this.panelStart.Location = new System.Drawing.Point(160, 0); this.panelStart.Name = "panelStart"; - this.panelStart.Size = new System.Drawing.Size(699, 631); + this.panelStart.Size = new System.Drawing.Size(699, 661); this.panelStart.TabIndex = 7; // // comboboxMode @@ -639,7 +642,7 @@ private void InitializeComponent() { this.comboboxMode.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.comboboxMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboboxMode.FormattingEnabled = true; - this.comboboxMode.Location = new System.Drawing.Point(144, 599); + this.comboboxMode.Location = new System.Drawing.Point(144, 629); this.comboboxMode.Name = "comboboxMode"; this.comboboxMode.Size = new System.Drawing.Size(144, 21); this.comboboxMode.TabIndex = 11; @@ -649,7 +652,7 @@ private void InitializeComponent() { this.labelMode.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.labelMode.AutoEllipsis = true; this.labelMode.ForeColor = System.Drawing.Color.Black; - this.labelMode.Location = new System.Drawing.Point(16, 601); + this.labelMode.Location = new System.Drawing.Point(16, 631); this.labelMode.Name = "labelMode"; this.labelMode.Size = new System.Drawing.Size(128, 18); this.labelMode.TabIndex = 10; @@ -661,7 +664,7 @@ private void InitializeComponent() { this.groupboxTrainSelection.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.groupboxTrainSelection.Controls.Add(this.tabcontrolTrainSelection); this.groupboxTrainSelection.ForeColor = System.Drawing.Color.Black; - this.groupboxTrainSelection.Location = new System.Drawing.Point(8, 359); + this.groupboxTrainSelection.Location = new System.Drawing.Point(8, 389); this.groupboxTrainSelection.Name = "groupboxTrainSelection"; this.groupboxTrainSelection.Size = new System.Drawing.Size(365, 200); this.groupboxTrainSelection.TabIndex = 7; @@ -816,7 +819,7 @@ private void InitializeComponent() { this.groupboxTrainDetails.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.groupboxTrainDetails.Controls.Add(this.tabcontrolTrainDetails); this.groupboxTrainDetails.ForeColor = System.Drawing.Color.Black; - this.groupboxTrainDetails.Location = new System.Drawing.Point(375, 359); + this.groupboxTrainDetails.Location = new System.Drawing.Point(375, 389); this.groupboxTrainDetails.Name = "groupboxTrainDetails"; this.groupboxTrainDetails.Size = new System.Drawing.Size(319, 200); this.groupboxTrainDetails.TabIndex = 8; @@ -1016,7 +1019,7 @@ private void InitializeComponent() { this.buttonStart.AutoEllipsis = true; this.buttonStart.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonStart.Enabled = false; - this.buttonStart.Location = new System.Drawing.Point(571, 599); + this.buttonStart.Location = new System.Drawing.Point(571, 629); this.buttonStart.Name = "buttonStart"; this.buttonStart.Size = new System.Drawing.Size(120, 26); this.buttonStart.TabIndex = 12; @@ -1032,7 +1035,7 @@ private void InitializeComponent() { this.labelStart.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(107)))), ((int)(((byte)(130)))), ((int)(((byte)(153))))); this.labelStart.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelStart.ForeColor = System.Drawing.Color.White; - this.labelStart.Location = new System.Drawing.Point(7, 567); + this.labelStart.Location = new System.Drawing.Point(7, 597); this.labelStart.Name = "labelStart"; this.labelStart.Size = new System.Drawing.Size(684, 26); this.labelStart.TabIndex = 9; @@ -1047,7 +1050,7 @@ private void InitializeComponent() { this.labelTrain.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(107)))), ((int)(((byte)(130)))), ((int)(((byte)(153))))); this.labelTrain.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelTrain.ForeColor = System.Drawing.Color.White; - this.labelTrain.Location = new System.Drawing.Point(8, 327); + this.labelTrain.Location = new System.Drawing.Point(8, 357); this.labelTrain.Name = "labelTrain"; this.labelTrain.Size = new System.Drawing.Size(683, 24); this.labelTrain.TabIndex = 6; @@ -1062,7 +1065,7 @@ private void InitializeComponent() { this.groupboxRouteSelection.ForeColor = System.Drawing.Color.Black; this.groupboxRouteSelection.Location = new System.Drawing.Point(8, 72); this.groupboxRouteSelection.Name = "groupboxRouteSelection"; - this.groupboxRouteSelection.Size = new System.Drawing.Size(365, 247); + this.groupboxRouteSelection.Size = new System.Drawing.Size(365, 277); this.groupboxRouteSelection.TabIndex = 4; this.groupboxRouteSelection.TabStop = false; this.groupboxRouteSelection.Text = "Selection"; @@ -1078,7 +1081,7 @@ private void InitializeComponent() { this.tabcontrolRouteSelection.Location = new System.Drawing.Point(8, 16); this.tabcontrolRouteSelection.Name = "tabcontrolRouteSelection"; this.tabcontrolRouteSelection.SelectedIndex = 0; - this.tabcontrolRouteSelection.Size = new System.Drawing.Size(349, 223); + this.tabcontrolRouteSelection.Size = new System.Drawing.Size(349, 253); this.tabcontrolRouteSelection.TabIndex = 0; this.tabcontrolRouteSelection.SelectedIndexChanged += new System.EventHandler(this.tabcontrolRouteSelection_SelectedIndexChanged); // @@ -1089,7 +1092,7 @@ private void InitializeComponent() { this.tabpageRouteBrowse.Location = new System.Drawing.Point(4, 22); this.tabpageRouteBrowse.Name = "tabpageRouteBrowse"; this.tabpageRouteBrowse.Padding = new System.Windows.Forms.Padding(3); - this.tabpageRouteBrowse.Size = new System.Drawing.Size(341, 197); + this.tabpageRouteBrowse.Size = new System.Drawing.Size(341, 227); this.tabpageRouteBrowse.TabIndex = 0; this.tabpageRouteBrowse.Text = "File browser"; this.tabpageRouteBrowse.UseVisualStyleBackColor = true; @@ -1106,7 +1109,7 @@ private void InitializeComponent() { this.listviewRouteFiles.MultiSelect = false; this.listviewRouteFiles.Name = "listviewRouteFiles"; this.listviewRouteFiles.ShowGroups = false; - this.listviewRouteFiles.Size = new System.Drawing.Size(325, 159); + this.listviewRouteFiles.Size = new System.Drawing.Size(325, 189); this.listviewRouteFiles.TabIndex = 1; this.listviewRouteFiles.UseCompatibleStateImageBehavior = false; this.listviewRouteFiles.View = System.Windows.Forms.View.Details; @@ -1130,7 +1133,7 @@ private void InitializeComponent() { this.tabpageRouteRecently.Location = new System.Drawing.Point(4, 22); this.tabpageRouteRecently.Name = "tabpageRouteRecently"; this.tabpageRouteRecently.Padding = new System.Windows.Forms.Padding(3); - this.tabpageRouteRecently.Size = new System.Drawing.Size(341, 197); + this.tabpageRouteRecently.Size = new System.Drawing.Size(341, 227); this.tabpageRouteRecently.TabIndex = 1; this.tabpageRouteRecently.Text = "Recently used"; this.tabpageRouteRecently.UseVisualStyleBackColor = true; @@ -1158,7 +1161,7 @@ private void InitializeComponent() { this.tabPageRoutePackages.Controls.Add(this.listViewRoutePackages); this.tabPageRoutePackages.Location = new System.Drawing.Point(4, 22); this.tabPageRoutePackages.Name = "tabPageRoutePackages"; - this.tabPageRoutePackages.Size = new System.Drawing.Size(341, 197); + this.tabPageRoutePackages.Size = new System.Drawing.Size(341, 227); this.tabPageRoutePackages.TabIndex = 2; this.tabPageRoutePackages.Text = "Installed Packages"; this.tabPageRoutePackages.UseVisualStyleBackColor = true; @@ -1190,7 +1193,7 @@ private void InitializeComponent() { this.groupboxRouteDetails.ForeColor = System.Drawing.Color.Black; this.groupboxRouteDetails.Location = new System.Drawing.Point(375, 72); this.groupboxRouteDetails.Name = "groupboxRouteDetails"; - this.groupboxRouteDetails.Size = new System.Drawing.Size(319, 247); + this.groupboxRouteDetails.Size = new System.Drawing.Size(319, 277); this.groupboxRouteDetails.TabIndex = 5; this.groupboxRouteDetails.TabStop = false; this.groupboxRouteDetails.Text = "Details"; @@ -1208,7 +1211,7 @@ private void InitializeComponent() { this.tabcontrolRouteDetails.Location = new System.Drawing.Point(8, 16); this.tabcontrolRouteDetails.Name = "tabcontrolRouteDetails"; this.tabcontrolRouteDetails.SelectedIndex = 0; - this.tabcontrolRouteDetails.Size = new System.Drawing.Size(303, 223); + this.tabcontrolRouteDetails.Size = new System.Drawing.Size(303, 253); this.tabcontrolRouteDetails.TabIndex = 19; this.tabcontrolRouteDetails.SelectedIndexChanged += new System.EventHandler(this.tabcontrolRouteDetails_SelectedIndexChanged); // @@ -1219,7 +1222,7 @@ private void InitializeComponent() { this.tabpageRouteDescription.Location = new System.Drawing.Point(4, 22); this.tabpageRouteDescription.Name = "tabpageRouteDescription"; this.tabpageRouteDescription.Padding = new System.Windows.Forms.Padding(3); - this.tabpageRouteDescription.Size = new System.Drawing.Size(295, 197); + this.tabpageRouteDescription.Size = new System.Drawing.Size(295, 227); this.tabpageRouteDescription.TabIndex = 0; this.tabpageRouteDescription.Text = "Description"; this.tabpageRouteDescription.UseVisualStyleBackColor = true; @@ -1231,11 +1234,11 @@ private void InitializeComponent() { | System.Windows.Forms.AnchorStyles.Right))); this.textboxRouteDescription.BackColor = System.Drawing.SystemColors.Window; this.textboxRouteDescription.Location = new System.Drawing.Point(168, 8); - this.textboxRouteDescription.Multiline = true; this.textboxRouteDescription.Name = "textboxRouteDescription"; this.textboxRouteDescription.ReadOnly = true; - this.textboxRouteDescription.Size = new System.Drawing.Size(119, 183); + this.textboxRouteDescription.Size = new System.Drawing.Size(119, 213); this.textboxRouteDescription.TabIndex = 0; + this.textboxRouteDescription.Text = ""; // // pictureboxRouteImage // @@ -1244,7 +1247,7 @@ private void InitializeComponent() { this.pictureboxRouteImage.Cursor = System.Windows.Forms.Cursors.Hand; this.pictureboxRouteImage.Location = new System.Drawing.Point(8, 8); this.pictureboxRouteImage.Name = "pictureboxRouteImage"; - this.pictureboxRouteImage.Size = new System.Drawing.Size(152, 183); + this.pictureboxRouteImage.Size = new System.Drawing.Size(152, 213); this.pictureboxRouteImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; this.pictureboxRouteImage.TabIndex = 0; this.pictureboxRouteImage.TabStop = false; @@ -1256,7 +1259,7 @@ private void InitializeComponent() { this.tabpageRouteMap.Location = new System.Drawing.Point(4, 22); this.tabpageRouteMap.Name = "tabpageRouteMap"; this.tabpageRouteMap.Padding = new System.Windows.Forms.Padding(3); - this.tabpageRouteMap.Size = new System.Drawing.Size(295, 197); + this.tabpageRouteMap.Size = new System.Drawing.Size(295, 227); this.tabpageRouteMap.TabIndex = 1; this.tabpageRouteMap.Text = "Map"; this.tabpageRouteMap.UseVisualStyleBackColor = true; @@ -1279,12 +1282,12 @@ private void InitializeComponent() { this.pictureBoxContextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.toolStripExport}); this.pictureBoxContextMenu.Name = "pictureBoxRouteMapContextMenu"; - this.pictureBoxContextMenu.Size = new System.Drawing.Size(109, 26); + this.pictureBoxContextMenu.Size = new System.Drawing.Size(108, 26); // // toolStripExport // this.toolStripExport.Name = "toolStripExport"; - this.toolStripExport.Size = new System.Drawing.Size(108, 22); + this.toolStripExport.Size = new System.Drawing.Size(107, 22); this.toolStripExport.Text = "Export"; this.toolStripExport.Click += new System.EventHandler(this.toolStripExport_Click); // @@ -1294,7 +1297,7 @@ private void InitializeComponent() { this.tabpageRouteGradient.Location = new System.Drawing.Point(4, 22); this.tabpageRouteGradient.Name = "tabpageRouteGradient"; this.tabpageRouteGradient.Padding = new System.Windows.Forms.Padding(3); - this.tabpageRouteGradient.Size = new System.Drawing.Size(295, 197); + this.tabpageRouteGradient.Size = new System.Drawing.Size(295, 227); this.tabpageRouteGradient.TabIndex = 2; this.tabpageRouteGradient.Text = "Gradient profile"; this.tabpageRouteGradient.UseVisualStyleBackColor = true; @@ -1322,7 +1325,7 @@ private void InitializeComponent() { this.tabpageRouteSettings.Location = new System.Drawing.Point(4, 22); this.tabpageRouteSettings.Name = "tabpageRouteSettings"; this.tabpageRouteSettings.Padding = new System.Windows.Forms.Padding(3); - this.tabpageRouteSettings.Size = new System.Drawing.Size(295, 197); + this.tabpageRouteSettings.Size = new System.Drawing.Size(295, 227); this.tabpageRouteSettings.TabIndex = 3; this.tabpageRouteSettings.Text = "Settings"; this.tabpageRouteSettings.UseVisualStyleBackColor = true; @@ -1519,7 +1522,7 @@ private void InitializeComponent() { this.labelFillerTwo.BackColor = System.Drawing.Color.Silver; this.labelFillerTwo.Location = new System.Drawing.Point(0, 330); this.labelFillerTwo.Name = "labelFillerTwo"; - this.labelFillerTwo.Size = new System.Drawing.Size(160, 151); + this.labelFillerTwo.Size = new System.Drawing.Size(160, 181); this.labelFillerTwo.TabIndex = 2; // // panelOptions @@ -1530,9 +1533,9 @@ private void InitializeComponent() { this.panelOptions.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(243)))), ((int)(((byte)(255)))), ((int)(((byte)(243))))); this.panelOptions.Controls.Add(this.buttonOptionsPrevious); this.panelOptions.Controls.Add(this.buttonOptionsNext); + this.panelOptions.Controls.Add(this.panelOptionsPage2); this.panelOptions.Controls.Add(this.panelOptionsLeft); this.panelOptions.Controls.Add(this.panelOptionsRight); - this.panelOptions.Controls.Add(this.panelOptionsPage2); this.panelOptions.Controls.Add(this.pictureboxLanguage); this.panelOptions.Controls.Add(this.comboboxLanguages); this.panelOptions.Controls.Add(this.labelOptionsTitleSeparator); @@ -1540,7 +1543,7 @@ private void InitializeComponent() { this.panelOptions.Controls.Add(this.labelOptionsTitleBackground); this.panelOptions.Location = new System.Drawing.Point(160, 0); this.panelOptions.Name = "panelOptions"; - this.panelOptions.Size = new System.Drawing.Size(699, 631); + this.panelOptions.Size = new System.Drawing.Size(699, 661); this.panelOptions.TabIndex = 0; // // buttonOptionsPrevious @@ -1567,1412 +1570,1449 @@ private void InitializeComponent() { this.buttonOptionsNext.UseVisualStyleBackColor = true; this.buttonOptionsNext.Click += new System.EventHandler(this.buttonOptionsPrevious_Click); // - // panelOptionsLeft + // panelOptionsPage2 // - this.panelOptionsLeft.Controls.Add(this.groupboxDisplayMode); - this.panelOptionsLeft.Controls.Add(this.groupboxWindow); - this.panelOptionsLeft.Controls.Add(this.groupboxFullscreen); - this.panelOptionsLeft.Controls.Add(this.groupboxInterpolation); - this.panelOptionsLeft.Location = new System.Drawing.Point(8, 72); - this.panelOptionsLeft.Name = "panelOptionsLeft"; - this.panelOptionsLeft.Size = new System.Drawing.Size(316, 576); - this.panelOptionsLeft.TabIndex = 16; + this.panelOptionsPage2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.panelOptionsPage2.Controls.Add(this.groupBoxInputDevice); + this.panelOptionsPage2.Controls.Add(this.groupBoxObjectParser); + this.panelOptionsPage2.Controls.Add(this.groupBoxKioskMode); + this.panelOptionsPage2.Controls.Add(this.groupBoxAdvancedOptions); + this.panelOptionsPage2.Controls.Add(this.groupBoxPackageOptions); + this.panelOptionsPage2.Location = new System.Drawing.Point(0, 72); + this.panelOptionsPage2.Name = "panelOptionsPage2"; + this.panelOptionsPage2.Size = new System.Drawing.Size(683, 583); + this.panelOptionsPage2.TabIndex = 20; // - // groupboxDisplayMode + // groupBoxInputDevice // - this.groupboxDisplayMode.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + this.groupBoxInputDevice.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.groupboxDisplayMode.Controls.Add(this.comboBoxFont); - this.groupboxDisplayMode.Controls.Add(this.labelFontName); - this.groupboxDisplayMode.Controls.Add(this.labelHUDLarge); - this.groupboxDisplayMode.Controls.Add(this.labelHUDNormal); - this.groupboxDisplayMode.Controls.Add(this.labelHUDSmall); - this.groupboxDisplayMode.Controls.Add(this.trackBarHUDSize); - this.groupboxDisplayMode.Controls.Add(this.labelHUDScale); - this.groupboxDisplayMode.Controls.Add(this.comboboxVSync); - this.groupboxDisplayMode.Controls.Add(this.labelVSync); - this.groupboxDisplayMode.Controls.Add(this.radiobuttonFullscreen); - this.groupboxDisplayMode.Controls.Add(this.radiobuttonWindow); - this.groupboxDisplayMode.ForeColor = System.Drawing.Color.Black; - this.groupboxDisplayMode.Location = new System.Drawing.Point(0, 0); - this.groupboxDisplayMode.Name = "groupboxDisplayMode"; - this.groupboxDisplayMode.Size = new System.Drawing.Size(316, 189); - this.groupboxDisplayMode.TabIndex = 4; - this.groupboxDisplayMode.TabStop = false; - this.groupboxDisplayMode.Text = "Display mode"; + this.groupBoxInputDevice.Controls.Add(this.labelInputDevice); + this.groupBoxInputDevice.Controls.Add(this.listviewInputDevice); + this.groupBoxInputDevice.Controls.Add(this.checkBoxInputDeviceEnable); + this.groupBoxInputDevice.Controls.Add(this.buttonInputDeviceConfig); + this.groupBoxInputDevice.ForeColor = System.Drawing.Color.Black; + this.groupBoxInputDevice.Location = new System.Drawing.Point(6, 396); + this.groupBoxInputDevice.Name = "groupBoxInputDevice"; + this.groupBoxInputDevice.Size = new System.Drawing.Size(674, 181); + this.groupBoxInputDevice.TabIndex = 24; + this.groupBoxInputDevice.TabStop = false; + this.groupBoxInputDevice.Text = "Input Device Plugin"; // - // comboBoxFont + // labelInputDevice // - this.comboBoxFont.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.comboBoxFont.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.comboBoxFont.FormattingEnabled = true; - this.comboBoxFont.Location = new System.Drawing.Point(45, 155); - this.comboBoxFont.Name = "comboBoxFont"; - this.comboBoxFont.Size = new System.Drawing.Size(264, 21); - this.comboBoxFont.TabIndex = 14; - this.comboBoxFont.SelectionChangeCommitted += new System.EventHandler(this.comboBoxFont_SelectedIndexChanged); + this.labelInputDevice.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.labelInputDevice.Location = new System.Drawing.Point(8, 17); + this.labelInputDevice.Name = "labelInputDevice"; + this.labelInputDevice.Size = new System.Drawing.Size(658, 17); + this.labelInputDevice.TabIndex = 0; + this.labelInputDevice.Text = "WARNING: If you are turn on the Input Device Plugin(s), it may be happen the conf" + + "lict of input setting(s)."; // - // labelFontName + // listviewInputDevice // - this.labelFontName.Location = new System.Drawing.Point(8, 148); - this.labelFontName.Name = "labelFontName"; - this.labelFontName.Size = new System.Drawing.Size(50, 36); - this.labelFontName.TabIndex = 13; - this.labelFontName.Text = "Font:"; - this.labelFontName.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.listviewInputDevice.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.listviewInputDevice.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnheaderInputDeviceName, + this.columnheaderInputDeviceStatus, + this.columnheaderInputDeviceVersion, + this.columnheaderInputDeviceProvider, + this.columnheaderInputDeviceFileName}); + this.listviewInputDevice.FullRowSelect = true; + this.listviewInputDevice.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.listviewInputDevice.HideSelection = false; + this.listviewInputDevice.LabelWrap = false; + this.listviewInputDevice.Location = new System.Drawing.Point(8, 38); + this.listviewInputDevice.MultiSelect = false; + this.listviewInputDevice.Name = "listviewInputDevice"; + this.listviewInputDevice.ShowGroups = false; + this.listviewInputDevice.Size = new System.Drawing.Size(658, 103); + this.listviewInputDevice.TabIndex = 1; + this.listviewInputDevice.UseCompatibleStateImageBehavior = false; + this.listviewInputDevice.View = System.Windows.Forms.View.Details; + this.listviewInputDevice.SelectedIndexChanged += new System.EventHandler(this.listviewInputDevice_SelectedIndexChanged); + this.listviewInputDevice.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.listviewInputDevice_MouseDoubleClick); // - // labelHUDLarge + // columnheaderInputDeviceName // - this.labelHUDLarge.Location = new System.Drawing.Point(258, 125); - this.labelHUDLarge.Name = "labelHUDLarge"; - this.labelHUDLarge.Size = new System.Drawing.Size(72, 48); - this.labelHUDLarge.TabIndex = 12; - this.labelHUDLarge.Text = "Large"; - this.labelHUDLarge.TextAlign = System.Drawing.ContentAlignment.TopCenter; + this.columnheaderInputDeviceName.Text = "Name"; // - // labelHUDNormal + // columnheaderInputDeviceStatus // - this.labelHUDNormal.Location = new System.Drawing.Point(162, 125); - this.labelHUDNormal.Name = "labelHUDNormal"; - this.labelHUDNormal.Size = new System.Drawing.Size(70, 48); - this.labelHUDNormal.TabIndex = 11; - this.labelHUDNormal.Text = "Normal"; - this.labelHUDNormal.TextAlign = System.Drawing.ContentAlignment.TopCenter; + this.columnheaderInputDeviceStatus.Text = "Status"; // - // labelHUDSmall + // columnheaderInputDeviceVersion // - this.labelHUDSmall.Location = new System.Drawing.Point(66, 125); - this.labelHUDSmall.Name = "labelHUDSmall"; - this.labelHUDSmall.Size = new System.Drawing.Size(70, 48); - this.labelHUDSmall.TabIndex = 10; - this.labelHUDSmall.Text = "Small"; - this.labelHUDSmall.TextAlign = System.Drawing.ContentAlignment.TopCenter; + this.columnheaderInputDeviceVersion.Text = "Version"; // - // trackBarHUDSize + // columnheaderInputDeviceProvider // - this.trackBarHUDSize.LargeChange = 1; - this.trackBarHUDSize.Location = new System.Drawing.Point(88, 100); - this.trackBarHUDSize.Maximum = 2; - this.trackBarHUDSize.Name = "trackBarHUDSize"; - this.trackBarHUDSize.Size = new System.Drawing.Size(220, 45); - this.trackBarHUDSize.TabIndex = 9; - this.trackBarHUDSize.Value = 1; + this.columnheaderInputDeviceProvider.Text = "Provider"; // - // labelHUDScale + // columnheaderInputDeviceFileName // - this.labelHUDScale.AutoSize = true; - this.labelHUDScale.Location = new System.Drawing.Point(8, 106); - this.labelHUDScale.Name = "labelHUDScale"; - this.labelHUDScale.Size = new System.Drawing.Size(54, 13); - this.labelHUDScale.TabIndex = 8; - this.labelHUDScale.Text = "HUD Size"; + this.columnheaderInputDeviceFileName.Text = "File Name"; + this.columnheaderInputDeviceFileName.Width = 200; // - // comboboxVSync - // - this.comboboxVSync.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.comboboxVSync.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.comboboxVSync.FormattingEnabled = true; - this.comboboxVSync.Location = new System.Drawing.Point(156, 72); - this.comboboxVSync.Name = "comboboxVSync"; - this.comboboxVSync.Size = new System.Drawing.Size(152, 21); - this.comboboxVSync.TabIndex = 7; - // - // labelVSync - // - this.labelVSync.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.labelVSync.AutoEllipsis = true; - this.labelVSync.Location = new System.Drawing.Point(8, 72); - this.labelVSync.Name = "labelVSync"; - this.labelVSync.Size = new System.Drawing.Size(148, 18); - this.labelVSync.TabIndex = 2; - this.labelVSync.Text = "Vertical syncronization:"; - this.labelVSync.TextAlign = System.Drawing.ContentAlignment.TopRight; - // - // radiobuttonFullscreen - // - this.radiobuttonFullscreen.AutoSize = true; - this.radiobuttonFullscreen.Location = new System.Drawing.Point(8, 48); - this.radiobuttonFullscreen.Name = "radiobuttonFullscreen"; - this.radiobuttonFullscreen.Size = new System.Drawing.Size(102, 17); - this.radiobuttonFullscreen.TabIndex = 1; - this.radiobuttonFullscreen.TabStop = true; - this.radiobuttonFullscreen.Text = "Fullscreen mode"; - this.radiobuttonFullscreen.UseVisualStyleBackColor = true; - // - // radiobuttonWindow + // checkBoxInputDeviceEnable // - this.radiobuttonWindow.AutoSize = true; - this.radiobuttonWindow.Checked = true; - this.radiobuttonWindow.Location = new System.Drawing.Point(8, 24); - this.radiobuttonWindow.Name = "radiobuttonWindow"; - this.radiobuttonWindow.Size = new System.Drawing.Size(93, 17); - this.radiobuttonWindow.TabIndex = 0; - this.radiobuttonWindow.TabStop = true; - this.radiobuttonWindow.Text = "Window mode"; - this.radiobuttonWindow.UseVisualStyleBackColor = true; + this.checkBoxInputDeviceEnable.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.checkBoxInputDeviceEnable.Enabled = false; + this.checkBoxInputDeviceEnable.Location = new System.Drawing.Point(8, 144); + this.checkBoxInputDeviceEnable.Name = "checkBoxInputDeviceEnable"; + this.checkBoxInputDeviceEnable.Size = new System.Drawing.Size(230, 34); + this.checkBoxInputDeviceEnable.TabIndex = 2; + this.checkBoxInputDeviceEnable.Text = "Enable this Input Device Plugin"; + this.checkBoxInputDeviceEnable.UseVisualStyleBackColor = true; + this.checkBoxInputDeviceEnable.CheckedChanged += new System.EventHandler(this.checkBoxInputDeviceEnable_CheckedChanged); // - // groupboxWindow + // buttonInputDeviceConfig // - this.groupboxWindow.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.groupboxWindow.Controls.Add(this.updownWindowHeight); - this.groupboxWindow.Controls.Add(this.labelWindowHeight); - this.groupboxWindow.Controls.Add(this.updownWindowWidth); - this.groupboxWindow.Controls.Add(this.labelWindowWidth); - this.groupboxWindow.ForeColor = System.Drawing.Color.Black; - this.groupboxWindow.Location = new System.Drawing.Point(0, 192); - this.groupboxWindow.Name = "groupboxWindow"; - this.groupboxWindow.Size = new System.Drawing.Size(316, 80); - this.groupboxWindow.TabIndex = 5; - this.groupboxWindow.TabStop = false; - this.groupboxWindow.Text = "Window mode"; + this.buttonInputDeviceConfig.Anchor = System.Windows.Forms.AnchorStyles.Bottom; + this.buttonInputDeviceConfig.BackColor = System.Drawing.SystemColors.ButtonFace; + this.buttonInputDeviceConfig.Enabled = false; + this.buttonInputDeviceConfig.ForeColor = System.Drawing.SystemColors.ControlText; + this.buttonInputDeviceConfig.Location = new System.Drawing.Point(270, 148); + this.buttonInputDeviceConfig.MaximumSize = new System.Drawing.Size(106, 25); + this.buttonInputDeviceConfig.Name = "buttonInputDeviceConfig"; + this.buttonInputDeviceConfig.Size = new System.Drawing.Size(106, 25); + this.buttonInputDeviceConfig.TabIndex = 3; + this.buttonInputDeviceConfig.Text = "Config"; + this.buttonInputDeviceConfig.UseVisualStyleBackColor = true; + this.buttonInputDeviceConfig.Click += new System.EventHandler(this.buttonInputDeviceConfig_Click); // - // updownWindowHeight + // groupBoxObjectParser // - this.updownWindowHeight.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.updownWindowHeight.Location = new System.Drawing.Point(156, 48); - this.updownWindowHeight.Maximum = new decimal(new int[] { - 1048575, - 0, - 0, - 0}); - this.updownWindowHeight.Minimum = new decimal(new int[] { - 16, - 0, - 0, - 0}); - this.updownWindowHeight.Name = "updownWindowHeight"; - this.updownWindowHeight.Size = new System.Drawing.Size(152, 20); - this.updownWindowHeight.TabIndex = 3; - this.updownWindowHeight.Value = new decimal(new int[] { - 600, - 0, - 0, - 0}); + this.groupBoxObjectParser.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.groupBoxObjectParser.Controls.Add(this.labelObjparser); + this.groupBoxObjectParser.Controls.Add(this.comboBoxObjparser); + this.groupBoxObjectParser.Controls.Add(this.labelXparser); + this.groupBoxObjectParser.Controls.Add(this.comboBoxXparser); + this.groupBoxObjectParser.ForeColor = System.Drawing.Color.Black; + this.groupBoxObjectParser.Location = new System.Drawing.Point(377, 288); + this.groupBoxObjectParser.Name = "groupBoxObjectParser"; + this.groupBoxObjectParser.Size = new System.Drawing.Size(305, 110); + this.groupBoxObjectParser.TabIndex = 23; + this.groupBoxObjectParser.TabStop = false; + this.groupBoxObjectParser.Text = "Object Parser"; // - // labelWindowHeight + // labelObjparser // - this.labelWindowHeight.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.labelWindowHeight.AutoEllipsis = true; - this.labelWindowHeight.Location = new System.Drawing.Point(8, 50); - this.labelWindowHeight.Name = "labelWindowHeight"; - this.labelWindowHeight.Size = new System.Drawing.Size(148, 18); - this.labelWindowHeight.TabIndex = 2; - this.labelWindowHeight.Text = "Height:"; - this.labelWindowHeight.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.labelObjparser.Location = new System.Drawing.Point(7, 44); + this.labelObjparser.Name = "labelObjparser"; + this.labelObjparser.Size = new System.Drawing.Size(113, 26); + this.labelObjparser.TabIndex = 0; + this.labelObjparser.Text = "Obj Object Parser:"; + this.labelObjparser.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // - // updownWindowWidth + // comboBoxObjparser // - this.updownWindowWidth.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.updownWindowWidth.Location = new System.Drawing.Point(156, 24); - this.updownWindowWidth.Maximum = new decimal(new int[] { - 1048575, - 0, - 0, - 0}); - this.updownWindowWidth.Minimum = new decimal(new int[] { - 16, - 0, - 0, - 0}); - this.updownWindowWidth.Name = "updownWindowWidth"; - this.updownWindowWidth.Size = new System.Drawing.Size(152, 20); - this.updownWindowWidth.TabIndex = 1; - this.updownWindowWidth.Value = new decimal(new int[] { - 960, - 0, - 0, - 0}); + this.comboBoxObjparser.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBoxObjparser.FormattingEnabled = true; + this.comboBoxObjparser.Items.AddRange(new object[] { + "Original", + "Assimp"}); + this.comboBoxObjparser.Location = new System.Drawing.Point(127, 44); + this.comboBoxObjparser.Name = "comboBoxObjparser"; + this.comboBoxObjparser.Size = new System.Drawing.Size(170, 21); + this.comboBoxObjparser.TabIndex = 1; // - // labelWindowWidth + // labelXparser // - this.labelWindowWidth.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.labelWindowWidth.AutoEllipsis = true; - this.labelWindowWidth.Location = new System.Drawing.Point(8, 26); - this.labelWindowWidth.Name = "labelWindowWidth"; - this.labelWindowWidth.Size = new System.Drawing.Size(148, 18); - this.labelWindowWidth.TabIndex = 0; - this.labelWindowWidth.Text = "Width:"; - this.labelWindowWidth.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.labelXparser.Location = new System.Drawing.Point(7, 17); + this.labelXparser.Name = "labelXparser"; + this.labelXparser.Size = new System.Drawing.Size(113, 26); + this.labelXparser.TabIndex = 0; + this.labelXparser.Text = "X Object Parser:"; + this.labelXparser.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // - // groupboxFullscreen + // comboBoxXparser // - this.groupboxFullscreen.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.groupboxFullscreen.Controls.Add(this.comboboxFullscreenBits); - this.groupboxFullscreen.Controls.Add(this.labelFullscreenBits); - this.groupboxFullscreen.Controls.Add(this.updownFullscreenHeight); - this.groupboxFullscreen.Controls.Add(this.labelFullscreenHeight); - this.groupboxFullscreen.Controls.Add(this.updownFullscreenWidth); - this.groupboxFullscreen.Controls.Add(this.labelFullscreenWidth); - this.groupboxFullscreen.ForeColor = System.Drawing.Color.Black; - this.groupboxFullscreen.Location = new System.Drawing.Point(0, 275); - this.groupboxFullscreen.Name = "groupboxFullscreen"; - this.groupboxFullscreen.Size = new System.Drawing.Size(316, 104); - this.groupboxFullscreen.TabIndex = 6; - this.groupboxFullscreen.TabStop = false; - this.groupboxFullscreen.Text = "Fullscreen mode"; + this.comboBoxXparser.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBoxXparser.FormattingEnabled = true; + this.comboBoxXparser.Items.AddRange(new object[] { + "Original", + "NewXParser", + "Assimp"}); + this.comboBoxXparser.Location = new System.Drawing.Point(127, 21); + this.comboBoxXparser.Name = "comboBoxXparser"; + this.comboBoxXparser.Size = new System.Drawing.Size(170, 21); + this.comboBoxXparser.TabIndex = 1; // - // comboboxFullscreenBits + // groupBoxKioskMode // - this.comboboxFullscreenBits.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.comboboxFullscreenBits.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.comboboxFullscreenBits.FormattingEnabled = true; - this.comboboxFullscreenBits.Location = new System.Drawing.Point(156, 72); - this.comboboxFullscreenBits.Name = "comboboxFullscreenBits"; - this.comboboxFullscreenBits.Size = new System.Drawing.Size(152, 21); - this.comboboxFullscreenBits.TabIndex = 5; + this.groupBoxKioskMode.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.groupBoxKioskMode.Controls.Add(this.labelKioskTimeout); + this.groupBoxKioskMode.Controls.Add(this.numericUpDownKioskTimeout); + this.groupBoxKioskMode.Controls.Add(this.checkBoxEnableKiosk); + this.groupBoxKioskMode.ForeColor = System.Drawing.Color.Black; + this.groupBoxKioskMode.Location = new System.Drawing.Point(377, 190); + this.groupBoxKioskMode.Name = "groupBoxKioskMode"; + this.groupBoxKioskMode.Size = new System.Drawing.Size(305, 92); + this.groupBoxKioskMode.TabIndex = 22; + this.groupBoxKioskMode.TabStop = false; + this.groupBoxKioskMode.Text = "Kiosk Mode"; // - // labelFullscreenBits + // labelKioskTimeout // - this.labelFullscreenBits.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.labelFullscreenBits.AutoEllipsis = true; - this.labelFullscreenBits.Location = new System.Drawing.Point(8, 74); - this.labelFullscreenBits.Name = "labelFullscreenBits"; - this.labelFullscreenBits.Size = new System.Drawing.Size(148, 18); - this.labelFullscreenBits.TabIndex = 4; - this.labelFullscreenBits.Text = "Bits per pixel:"; - this.labelFullscreenBits.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.labelKioskTimeout.Location = new System.Drawing.Point(8, 37); + this.labelKioskTimeout.Name = "labelKioskTimeout"; + this.labelKioskTimeout.Size = new System.Drawing.Size(155, 30); + this.labelKioskTimeout.TabIndex = 2; + this.labelKioskTimeout.Text = "Control timeout (s)"; + this.labelKioskTimeout.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // - // updownFullscreenHeight + // numericUpDownKioskTimeout // - this.updownFullscreenHeight.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.updownFullscreenHeight.Location = new System.Drawing.Point(156, 48); - this.updownFullscreenHeight.Maximum = new decimal(new int[] { - 1048575, - 0, - 0, - 0}); - this.updownFullscreenHeight.Minimum = new decimal(new int[] { - 16, - 0, - 0, - 0}); - this.updownFullscreenHeight.Name = "updownFullscreenHeight"; - this.updownFullscreenHeight.Size = new System.Drawing.Size(152, 20); - this.updownFullscreenHeight.TabIndex = 3; - this.updownFullscreenHeight.Value = new decimal(new int[] { - 768, + this.numericUpDownKioskTimeout.DecimalPlaces = 2; + this.numericUpDownKioskTimeout.Location = new System.Drawing.Point(166, 41); + this.numericUpDownKioskTimeout.Maximum = new decimal(new int[] { + 10000, 0, 0, 0}); + this.numericUpDownKioskTimeout.Name = "numericUpDownKioskTimeout"; + this.numericUpDownKioskTimeout.Size = new System.Drawing.Size(131, 20); + this.numericUpDownKioskTimeout.TabIndex = 1; // - // labelFullscreenHeight + // checkBoxEnableKiosk // - this.labelFullscreenHeight.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.labelFullscreenHeight.AutoEllipsis = true; - this.labelFullscreenHeight.Location = new System.Drawing.Point(8, 50); - this.labelFullscreenHeight.Name = "labelFullscreenHeight"; - this.labelFullscreenHeight.Size = new System.Drawing.Size(148, 18); - this.labelFullscreenHeight.TabIndex = 2; - this.labelFullscreenHeight.Text = "Height:"; - this.labelFullscreenHeight.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.checkBoxEnableKiosk.AutoSize = true; + this.checkBoxEnableKiosk.Location = new System.Drawing.Point(9, 20); + this.checkBoxEnableKiosk.Name = "checkBoxEnableKiosk"; + this.checkBoxEnableKiosk.Size = new System.Drawing.Size(118, 17); + this.checkBoxEnableKiosk.TabIndex = 0; + this.checkBoxEnableKiosk.Text = "Enable Kiosk Mode"; + this.checkBoxEnableKiosk.UseVisualStyleBackColor = true; // - // updownFullscreenWidth + // groupBoxAdvancedOptions // - this.updownFullscreenWidth.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.updownFullscreenWidth.Location = new System.Drawing.Point(156, 24); - this.updownFullscreenWidth.Maximum = new decimal(new int[] { - 1048575, - 0, - 0, - 0}); - this.updownFullscreenWidth.Minimum = new decimal(new int[] { - 16, - 0, - 0, - 0}); - this.updownFullscreenWidth.Name = "updownFullscreenWidth"; - this.updownFullscreenWidth.Size = new System.Drawing.Size(152, 20); - this.updownFullscreenWidth.TabIndex = 1; - this.updownFullscreenWidth.Value = new decimal(new int[] { - 1024, - 0, - 0, - 0}); + this.groupBoxAdvancedOptions.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupBoxAdvancedOptions.Controls.Add(this.checkBoxPanel2Extended); + this.groupBoxAdvancedOptions.Controls.Add(this.pictureboxCursor); + this.groupBoxAdvancedOptions.Controls.Add(this.labelCursor); + this.groupBoxAdvancedOptions.Controls.Add(this.comboboxCursor); + this.groupBoxAdvancedOptions.Controls.Add(this.checkBoxHacks); + this.groupBoxAdvancedOptions.Controls.Add(this.checkBoxTransparencyFix); + this.groupBoxAdvancedOptions.Controls.Add(this.checkBoxUnloadTextures); + this.groupBoxAdvancedOptions.Controls.Add(this.labelTimeAcceleration); + this.groupBoxAdvancedOptions.Controls.Add(this.updownTimeAccelerationFactor); + this.groupBoxAdvancedOptions.Controls.Add(this.checkBoxIsUseNewRenderer); + this.groupBoxAdvancedOptions.Controls.Add(this.checkBoxLoadInAdvance); + this.groupBoxAdvancedOptions.ForeColor = System.Drawing.Color.Black; + this.groupBoxAdvancedOptions.Location = new System.Drawing.Point(8, 190); + this.groupBoxAdvancedOptions.Name = "groupBoxAdvancedOptions"; + this.groupBoxAdvancedOptions.Size = new System.Drawing.Size(358, 208); + this.groupBoxAdvancedOptions.TabIndex = 21; + this.groupBoxAdvancedOptions.TabStop = false; + this.groupBoxAdvancedOptions.Text = "Advanced Options"; // - // labelFullscreenWidth + // checkBoxPanel2Extended // - this.labelFullscreenWidth.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.labelFullscreenWidth.AutoEllipsis = true; - this.labelFullscreenWidth.Location = new System.Drawing.Point(8, 26); - this.labelFullscreenWidth.Name = "labelFullscreenWidth"; - this.labelFullscreenWidth.Size = new System.Drawing.Size(148, 18); - this.labelFullscreenWidth.TabIndex = 0; - this.labelFullscreenWidth.Text = "Width:"; - this.labelFullscreenWidth.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.checkBoxPanel2Extended.AutoSize = true; + this.checkBoxPanel2Extended.Location = new System.Drawing.Point(8, 183); + this.checkBoxPanel2Extended.Name = "checkBoxPanel2Extended"; + this.checkBoxPanel2Extended.Size = new System.Drawing.Size(159, 17); + this.checkBoxPanel2Extended.TabIndex = 20; + this.checkBoxPanel2Extended.Text = "Enable Panel2 extend mode"; + this.checkBoxPanel2Extended.UseVisualStyleBackColor = true; // - // groupboxInterpolation + // pictureboxCursor // - this.groupboxInterpolation.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.groupboxInterpolation.Controls.Add(this.updownAntiAliasing); - this.groupboxInterpolation.Controls.Add(this.labelAntiAliasing); - this.groupboxInterpolation.Controls.Add(this.labelTransparencyQuality); - this.groupboxInterpolation.Controls.Add(this.labelTransparencyPerformance); - this.groupboxInterpolation.Controls.Add(this.labelTransparency); - this.groupboxInterpolation.Controls.Add(this.updownAnisotropic); - this.groupboxInterpolation.Controls.Add(this.labelAnisotropic); - this.groupboxInterpolation.Controls.Add(this.comboboxInterpolation); - this.groupboxInterpolation.Controls.Add(this.labelInterpolation); - this.groupboxInterpolation.Controls.Add(this.trackbarTransparency); - this.groupboxInterpolation.ForeColor = System.Drawing.Color.Black; - this.groupboxInterpolation.Location = new System.Drawing.Point(0, 381); - this.groupboxInterpolation.Name = "groupboxInterpolation"; - this.groupboxInterpolation.Size = new System.Drawing.Size(316, 160); - this.groupboxInterpolation.TabIndex = 7; - this.groupboxInterpolation.TabStop = false; - this.groupboxInterpolation.Text = "Interpolation"; + this.pictureboxCursor.Location = new System.Drawing.Point(8, 145); + this.pictureboxCursor.Name = "pictureboxCursor"; + this.pictureboxCursor.Size = new System.Drawing.Size(32, 32); + this.pictureboxCursor.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; + this.pictureboxCursor.TabIndex = 18; + this.pictureboxCursor.TabStop = false; // - // updownAntiAliasing + // labelCursor // - this.updownAntiAliasing.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.updownAntiAliasing.Location = new System.Drawing.Point(156, 64); - this.updownAntiAliasing.Maximum = new decimal(new int[] { - 16, - 0, - 0, - 0}); - this.updownAntiAliasing.Name = "updownAntiAliasing"; - this.updownAntiAliasing.Size = new System.Drawing.Size(152, 20); - this.updownAntiAliasing.TabIndex = 5; + this.labelCursor.AutoSize = true; + this.labelCursor.Location = new System.Drawing.Point(48, 140); + this.labelCursor.Name = "labelCursor"; + this.labelCursor.Size = new System.Drawing.Size(37, 13); + this.labelCursor.TabIndex = 17; + this.labelCursor.Text = "Cursor"; // - // labelAntiAliasing + // comboboxCursor // - this.labelAntiAliasing.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.labelAntiAliasing.AutoEllipsis = true; - this.labelAntiAliasing.Location = new System.Drawing.Point(8, 66); - this.labelAntiAliasing.Name = "labelAntiAliasing"; - this.labelAntiAliasing.Size = new System.Drawing.Size(148, 18); - this.labelAntiAliasing.TabIndex = 4; - this.labelAntiAliasing.Text = "Level of anti-aliasing:"; - this.labelAntiAliasing.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.comboboxCursor.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboboxCursor.FormattingEnabled = true; + this.comboboxCursor.Location = new System.Drawing.Point(48, 158); + this.comboboxCursor.Name = "comboboxCursor"; + this.comboboxCursor.Size = new System.Drawing.Size(108, 21); + this.comboboxCursor.TabIndex = 19; + this.comboboxCursor.SelectedIndexChanged += new System.EventHandler(this.comboboxCursor_SelectedIndexChanged); // - // labelTransparencyQuality + // checkBoxHacks // - this.labelTransparencyQuality.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.labelTransparencyQuality.AutoEllipsis = true; - this.labelTransparencyQuality.Location = new System.Drawing.Point(230, 136); - this.labelTransparencyQuality.Name = "labelTransparencyQuality"; - this.labelTransparencyQuality.Size = new System.Drawing.Size(76, 16); - this.labelTransparencyQuality.TabIndex = 9; - this.labelTransparencyQuality.Text = "Smooth"; - this.labelTransparencyQuality.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.checkBoxHacks.AutoSize = true; + this.checkBoxHacks.Location = new System.Drawing.Point(8, 100); + this.checkBoxHacks.Name = "checkBoxHacks"; + this.checkBoxHacks.Size = new System.Drawing.Size(203, 17); + this.checkBoxHacks.TabIndex = 15; + this.checkBoxHacks.Text = "Enable hacks for buggy older content"; + this.checkBoxHacks.UseVisualStyleBackColor = true; // - // labelTransparencyPerformance + // checkBoxTransparencyFix // - this.labelTransparencyPerformance.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.labelTransparencyPerformance.AutoEllipsis = true; - this.labelTransparencyPerformance.Location = new System.Drawing.Point(130, 136); - this.labelTransparencyPerformance.Name = "labelTransparencyPerformance"; - this.labelTransparencyPerformance.Size = new System.Drawing.Size(76, 16); - this.labelTransparencyPerformance.TabIndex = 8; - this.labelTransparencyPerformance.Text = "Sharp"; - this.labelTransparencyPerformance.TextAlign = System.Drawing.ContentAlignment.TopCenter; + this.checkBoxTransparencyFix.AutoSize = true; + this.checkBoxTransparencyFix.Location = new System.Drawing.Point(8, 81); + this.checkBoxTransparencyFix.Name = "checkBoxTransparencyFix"; + this.checkBoxTransparencyFix.Size = new System.Drawing.Size(259, 17); + this.checkBoxTransparencyFix.TabIndex = 14; + this.checkBoxTransparencyFix.Text = "Attempt to fix transparency issues in older content"; + this.checkBoxTransparencyFix.UseVisualStyleBackColor = true; // - // labelTransparency + // checkBoxUnloadTextures // - this.labelTransparency.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.labelTransparency.AutoEllipsis = true; - this.labelTransparency.Location = new System.Drawing.Point(8, 100); - this.labelTransparency.Name = "labelTransparency"; - this.labelTransparency.Size = new System.Drawing.Size(148, 18); - this.labelTransparency.TabIndex = 6; - this.labelTransparency.Text = "Transparency:"; - this.labelTransparency.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.checkBoxUnloadTextures.AutoSize = true; + this.checkBoxUnloadTextures.Location = new System.Drawing.Point(8, 62); + this.checkBoxUnloadTextures.Name = "checkBoxUnloadTextures"; + this.checkBoxUnloadTextures.Size = new System.Drawing.Size(138, 17); + this.checkBoxUnloadTextures.TabIndex = 13; + this.checkBoxUnloadTextures.Text = "Unload unused textures"; + this.checkBoxUnloadTextures.UseVisualStyleBackColor = true; + this.checkBoxUnloadTextures.CheckedChanged += new System.EventHandler(this.checkBoxUnloadTextures_CheckedChanged); // - // updownAnisotropic + // labelTimeAcceleration // - this.updownAnisotropic.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.updownAnisotropic.Enabled = false; - this.updownAnisotropic.Location = new System.Drawing.Point(156, 40); - this.updownAnisotropic.Maximum = new decimal(new int[] { - 16, + this.labelTimeAcceleration.AutoSize = true; + this.labelTimeAcceleration.Location = new System.Drawing.Point(8, 123); + this.labelTimeAcceleration.Name = "labelTimeAcceleration"; + this.labelTimeAcceleration.Size = new System.Drawing.Size(126, 13); + this.labelTimeAcceleration.TabIndex = 10; + this.labelTimeAcceleration.Text = "Accelerated Time Factor:"; + // + // updownTimeAccelerationFactor + // + this.updownTimeAccelerationFactor.Location = new System.Drawing.Point(200, 122); + this.updownTimeAccelerationFactor.Maximum = new decimal(new int[] { + 5, 0, 0, 0}); - this.updownAnisotropic.Name = "updownAnisotropic"; - this.updownAnisotropic.Size = new System.Drawing.Size(152, 20); - this.updownAnisotropic.TabIndex = 3; + this.updownTimeAccelerationFactor.Name = "updownTimeAccelerationFactor"; + this.updownTimeAccelerationFactor.Size = new System.Drawing.Size(52, 20); + this.updownTimeAccelerationFactor.TabIndex = 16; + this.updownTimeAccelerationFactor.ValueChanged += new System.EventHandler(this.updownTimeAccelerationFactor_ValueChanged); // - // labelAnisotropic + // checkBoxIsUseNewRenderer // - this.labelAnisotropic.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.labelAnisotropic.AutoEllipsis = true; - this.labelAnisotropic.Enabled = false; - this.labelAnisotropic.Location = new System.Drawing.Point(8, 42); - this.labelAnisotropic.Name = "labelAnisotropic"; - this.labelAnisotropic.Size = new System.Drawing.Size(148, 18); - this.labelAnisotropic.TabIndex = 2; - this.labelAnisotropic.Text = "Level of anisotropic filtering:"; - this.labelAnisotropic.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.checkBoxIsUseNewRenderer.AutoSize = true; + this.checkBoxIsUseNewRenderer.Location = new System.Drawing.Point(8, 43); + this.checkBoxIsUseNewRenderer.Name = "checkBoxIsUseNewRenderer"; + this.checkBoxIsUseNewRenderer.Size = new System.Drawing.Size(159, 17); + this.checkBoxIsUseNewRenderer.TabIndex = 2; + this.checkBoxIsUseNewRenderer.Text = "Disable OpenGL display lists"; + this.checkBoxIsUseNewRenderer.UseVisualStyleBackColor = true; // - // comboboxInterpolation + // checkBoxLoadInAdvance // - this.comboboxInterpolation.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.comboboxInterpolation.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.comboboxInterpolation.FormattingEnabled = true; - this.comboboxInterpolation.Location = new System.Drawing.Point(156, 16); - this.comboboxInterpolation.Name = "comboboxInterpolation"; - this.comboboxInterpolation.Size = new System.Drawing.Size(152, 21); - this.comboboxInterpolation.TabIndex = 1; - this.comboboxInterpolation.SelectedIndexChanged += new System.EventHandler(this.comboboxInterpolation_SelectedIndexChanged); + this.checkBoxLoadInAdvance.AutoSize = true; + this.checkBoxLoadInAdvance.Location = new System.Drawing.Point(8, 24); + this.checkBoxLoadInAdvance.Name = "checkBoxLoadInAdvance"; + this.checkBoxLoadInAdvance.Size = new System.Drawing.Size(106, 17); + this.checkBoxLoadInAdvance.TabIndex = 1; + this.checkBoxLoadInAdvance.Text = "Load in advance"; + this.checkBoxLoadInAdvance.UseVisualStyleBackColor = true; + this.checkBoxLoadInAdvance.CheckedChanged += new System.EventHandler(this.checkBoxLoadInAdvance_CheckedChanged); // - // labelInterpolation + // groupBoxPackageOptions // - this.labelInterpolation.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + this.groupBoxPackageOptions.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.labelInterpolation.AutoEllipsis = true; - this.labelInterpolation.Location = new System.Drawing.Point(8, 18); - this.labelInterpolation.Name = "labelInterpolation"; - this.labelInterpolation.Size = new System.Drawing.Size(148, 18); - this.labelInterpolation.TabIndex = 0; - this.labelInterpolation.Text = "Mode:"; - this.labelInterpolation.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.groupBoxPackageOptions.Controls.Add(this.buttonMSTSTrainsetDirectory); + this.groupBoxPackageOptions.Controls.Add(this.label1); + this.groupBoxPackageOptions.Controls.Add(this.textBoxMSTSTrainsetDirectory); + this.groupBoxPackageOptions.Controls.Add(this.comboBoxCompressionFormat); + this.groupBoxPackageOptions.Controls.Add(this.labelPackageCompression); + this.groupBoxPackageOptions.Controls.Add(this.buttonOtherDirectory); + this.groupBoxPackageOptions.Controls.Add(this.labelOtherInstallDirectory); + this.groupBoxPackageOptions.Controls.Add(this.textBoxOtherDirectory); + this.groupBoxPackageOptions.Controls.Add(this.buttonTrainInstallationDirectory); + this.groupBoxPackageOptions.Controls.Add(this.labelTrainInstallDirectory); + this.groupBoxPackageOptions.Controls.Add(this.textBoxTrainDirectory); + this.groupBoxPackageOptions.Controls.Add(this.buttonSetRouteDirectory); + this.groupBoxPackageOptions.Controls.Add(this.labelRouteInstallDirectory); + this.groupBoxPackageOptions.Controls.Add(this.textBoxRouteDirectory); + this.groupBoxPackageOptions.ForeColor = System.Drawing.Color.Black; + this.groupBoxPackageOptions.Location = new System.Drawing.Point(6, 0); + this.groupBoxPackageOptions.Name = "groupBoxPackageOptions"; + this.groupBoxPackageOptions.Size = new System.Drawing.Size(674, 183); + this.groupBoxPackageOptions.TabIndex = 19; + this.groupBoxPackageOptions.TabStop = false; + this.groupBoxPackageOptions.Text = "Package Management"; // - // trackbarTransparency + // buttonMSTSTrainsetDirectory // - this.trackbarTransparency.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.trackbarTransparency.Location = new System.Drawing.Point(156, 88); - this.trackbarTransparency.Maximum = 2; - this.trackbarTransparency.Name = "trackbarTransparency"; - this.trackbarTransparency.Size = new System.Drawing.Size(152, 45); - this.trackbarTransparency.TabIndex = 7; - this.trackbarTransparency.TickStyle = System.Windows.Forms.TickStyle.Both; + this.buttonMSTSTrainsetDirectory.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.buttonMSTSTrainsetDirectory.BackColor = System.Drawing.SystemColors.Control; + this.buttonMSTSTrainsetDirectory.ForeColor = System.Drawing.SystemColors.ControlText; + this.buttonMSTSTrainsetDirectory.Location = new System.Drawing.Point(593, 114); + this.buttonMSTSTrainsetDirectory.Name = "buttonMSTSTrainsetDirectory"; + this.buttonMSTSTrainsetDirectory.Size = new System.Drawing.Size(75, 26); + this.buttonMSTSTrainsetDirectory.TabIndex = 13; + this.buttonMSTSTrainsetDirectory.Text = "Choose..."; + this.buttonMSTSTrainsetDirectory.UseVisualStyleBackColor = true; + this.buttonMSTSTrainsetDirectory.Click += new System.EventHandler(this.buttonMSTSTrainsetDirectory_Click); // - // panelOptionsRight + // label1 // - this.panelOptionsRight.Controls.Add(this.groupBoxOther); - this.panelOptionsRight.Controls.Add(this.groupBoxRailDriver); - this.panelOptionsRight.Controls.Add(this.groupboxDistance); - this.panelOptionsRight.Controls.Add(this.groupboxControls); - this.panelOptionsRight.Controls.Add(this.groupboxVerbosity); - this.panelOptionsRight.Controls.Add(this.groupboxSimulation); - this.panelOptionsRight.Controls.Add(this.groupboxSound); - this.panelOptionsRight.Location = new System.Drawing.Point(332, 72); - this.panelOptionsRight.Name = "panelOptionsRight"; - this.panelOptionsRight.Size = new System.Drawing.Size(316, 579); - this.panelOptionsRight.TabIndex = 17; + this.label1.Location = new System.Drawing.Point(5, 113); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(175, 30); + this.label1.TabIndex = 12; + this.label1.Text = "MSTS Directory:"; + this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // - // groupBoxOther + // textBoxMSTSTrainsetDirectory // - this.groupBoxOther.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + this.textBoxMSTSTrainsetDirectory.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.groupBoxOther.Controls.Add(this.comboBoxTimeTableDisplayMode); - this.groupBoxOther.Controls.Add(this.labelTimeTableDisplayMode); - this.groupBoxOther.ForeColor = System.Drawing.Color.Black; - this.groupBoxOther.Location = new System.Drawing.Point(0, 468); - this.groupBoxOther.Name = "groupBoxOther"; - this.groupBoxOther.Size = new System.Drawing.Size(316, 48); - this.groupBoxOther.TabIndex = 19; - this.groupBoxOther.TabStop = false; - this.groupBoxOther.Text = "Other"; + this.textBoxMSTSTrainsetDirectory.BackColor = System.Drawing.SystemColors.Control; + this.textBoxMSTSTrainsetDirectory.ForeColor = System.Drawing.SystemColors.ControlText; + this.textBoxMSTSTrainsetDirectory.Location = new System.Drawing.Point(199, 117); + this.textBoxMSTSTrainsetDirectory.Name = "textBoxMSTSTrainsetDirectory"; + this.textBoxMSTSTrainsetDirectory.ReadOnly = true; + this.textBoxMSTSTrainsetDirectory.Size = new System.Drawing.Size(387, 20); + this.textBoxMSTSTrainsetDirectory.TabIndex = 11; // - // comboBoxTimeTableDisplayMode + // comboBoxCompressionFormat // - this.comboBoxTimeTableDisplayMode.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.comboBoxTimeTableDisplayMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.comboBoxTimeTableDisplayMode.FormattingEnabled = true; - this.comboBoxTimeTableDisplayMode.Location = new System.Drawing.Point(156, 16); - this.comboBoxTimeTableDisplayMode.Name = "comboBoxTimeTableDisplayMode"; - this.comboBoxTimeTableDisplayMode.Size = new System.Drawing.Size(152, 21); - this.comboBoxTimeTableDisplayMode.TabIndex = 1; - this.comboBoxTimeTableDisplayMode.SelectedIndexChanged += new System.EventHandler(this.comboBoxTimeTableDisplayMode_SelectedIndexChanged); + this.comboBoxCompressionFormat.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBoxCompressionFormat.Items.AddRange(new object[] { + "LZMA ZIP ( .zip )", + "GZip ( .tgz )", + "BZip2 ( .bz2 )"}); + this.comboBoxCompressionFormat.Location = new System.Drawing.Point(202, 146); + this.comboBoxCompressionFormat.Name = "comboBoxCompressionFormat"; + this.comboBoxCompressionFormat.Size = new System.Drawing.Size(188, 21); + this.comboBoxCompressionFormat.TabIndex = 10; + this.comboBoxCompressionFormat.SelectedIndexChanged += new System.EventHandler(this.comboBoxCompressionFormat_SelectedIndexChanged); // - // labelTimeTableDisplayMode + // labelPackageCompression // - this.labelTimeTableDisplayMode.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + this.labelPackageCompression.AutoSize = true; + this.labelPackageCompression.Location = new System.Drawing.Point(8, 150); + this.labelPackageCompression.Name = "labelPackageCompression"; + this.labelPackageCompression.Size = new System.Drawing.Size(147, 13); + this.labelPackageCompression.TabIndex = 9; + this.labelPackageCompression.Text = "Package compression format:"; + // + // buttonOtherDirectory + // + this.buttonOtherDirectory.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.buttonOtherDirectory.BackColor = System.Drawing.SystemColors.Control; + this.buttonOtherDirectory.ForeColor = System.Drawing.SystemColors.ControlText; + this.buttonOtherDirectory.Location = new System.Drawing.Point(594, 81); + this.buttonOtherDirectory.Name = "buttonOtherDirectory"; + this.buttonOtherDirectory.Size = new System.Drawing.Size(75, 26); + this.buttonOtherDirectory.TabIndex = 8; + this.buttonOtherDirectory.Text = "Choose..."; + this.buttonOtherDirectory.UseVisualStyleBackColor = true; + this.buttonOtherDirectory.Click += new System.EventHandler(this.buttonOtherDirectory_Click); + // + // labelOtherInstallDirectory + // + this.labelOtherInstallDirectory.Location = new System.Drawing.Point(6, 80); + this.labelOtherInstallDirectory.Name = "labelOtherInstallDirectory"; + this.labelOtherInstallDirectory.Size = new System.Drawing.Size(175, 30); + this.labelOtherInstallDirectory.TabIndex = 7; + this.labelOtherInstallDirectory.Text = "Other items installation directory:"; + this.labelOtherInstallDirectory.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // textBoxOtherDirectory + // + this.textBoxOtherDirectory.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.labelTimeTableDisplayMode.AutoEllipsis = true; - this.labelTimeTableDisplayMode.Location = new System.Drawing.Point(3, 17); - this.labelTimeTableDisplayMode.Name = "labelTimeTableDisplayMode"; - this.labelTimeTableDisplayMode.Size = new System.Drawing.Size(153, 18); - this.labelTimeTableDisplayMode.TabIndex = 0; - this.labelTimeTableDisplayMode.Text = "Timetable Display Mode:"; - this.labelTimeTableDisplayMode.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + this.textBoxOtherDirectory.BackColor = System.Drawing.SystemColors.Control; + this.textBoxOtherDirectory.ForeColor = System.Drawing.SystemColors.ControlText; + this.textBoxOtherDirectory.Location = new System.Drawing.Point(200, 84); + this.textBoxOtherDirectory.Name = "textBoxOtherDirectory"; + this.textBoxOtherDirectory.ReadOnly = true; + this.textBoxOtherDirectory.Size = new System.Drawing.Size(387, 20); + this.textBoxOtherDirectory.TabIndex = 6; // - // groupBoxRailDriver + // buttonTrainInstallationDirectory // - this.groupBoxRailDriver.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.groupBoxRailDriver.Controls.Add(this.labelRailDriverCalibration); - this.groupBoxRailDriver.Controls.Add(this.buttonRailDriverCalibration); - this.groupBoxRailDriver.Controls.Add(this.comboBoxRailDriverUnits); - this.groupBoxRailDriver.Controls.Add(this.labelRailDriverSpeedUnits); - this.groupBoxRailDriver.ForeColor = System.Drawing.Color.Black; - this.groupBoxRailDriver.Location = new System.Drawing.Point(0, 230); - this.groupBoxRailDriver.Name = "groupBoxRailDriver"; - this.groupBoxRailDriver.Size = new System.Drawing.Size(316, 75); - this.groupBoxRailDriver.TabIndex = 21; - this.groupBoxRailDriver.TabStop = false; - this.groupBoxRailDriver.Text = "RailDriver"; + this.buttonTrainInstallationDirectory.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.buttonTrainInstallationDirectory.BackColor = System.Drawing.SystemColors.ButtonFace; + this.buttonTrainInstallationDirectory.ForeColor = System.Drawing.SystemColors.ControlText; + this.buttonTrainInstallationDirectory.Location = new System.Drawing.Point(594, 49); + this.buttonTrainInstallationDirectory.Name = "buttonTrainInstallationDirectory"; + this.buttonTrainInstallationDirectory.Size = new System.Drawing.Size(75, 26); + this.buttonTrainInstallationDirectory.TabIndex = 5; + this.buttonTrainInstallationDirectory.Text = "Choose..."; + this.buttonTrainInstallationDirectory.UseVisualStyleBackColor = true; + this.buttonTrainInstallationDirectory.Click += new System.EventHandler(this.buttonTrainInstallationDirectory_Click); // - // labelRailDriverCalibration + // labelTrainInstallDirectory // - this.labelRailDriverCalibration.AutoSize = true; - this.labelRailDriverCalibration.Location = new System.Drawing.Point(7, 46); - this.labelRailDriverCalibration.Name = "labelRailDriverCalibration"; - this.labelRailDriverCalibration.Size = new System.Drawing.Size(78, 13); - this.labelRailDriverCalibration.TabIndex = 5; - this.labelRailDriverCalibration.Text = "Set Calibration:"; + this.labelTrainInstallDirectory.AutoSize = true; + this.labelTrainInstallDirectory.Location = new System.Drawing.Point(6, 52); + this.labelTrainInstallDirectory.Name = "labelTrainInstallDirectory"; + this.labelTrainInstallDirectory.Size = new System.Drawing.Size(129, 13); + this.labelTrainInstallDirectory.TabIndex = 4; + this.labelTrainInstallDirectory.Text = "Train installation directory:"; // - // buttonRailDriverCalibration + // textBoxTrainDirectory // - this.buttonRailDriverCalibration.BackColor = System.Drawing.SystemColors.ButtonFace; - this.buttonRailDriverCalibration.ForeColor = System.Drawing.SystemColors.ControlText; - this.buttonRailDriverCalibration.Location = new System.Drawing.Point(230, 42); - this.buttonRailDriverCalibration.Name = "buttonRailDriverCalibration"; - this.buttonRailDriverCalibration.Size = new System.Drawing.Size(75, 26); - this.buttonRailDriverCalibration.TabIndex = 4; - this.buttonRailDriverCalibration.Text = "Launch..."; - this.buttonRailDriverCalibration.UseVisualStyleBackColor = true; - this.buttonRailDriverCalibration.Click += new System.EventHandler(this.buttonRailDriverCalibration_Click); + this.textBoxTrainDirectory.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textBoxTrainDirectory.BackColor = System.Drawing.SystemColors.Control; + this.textBoxTrainDirectory.ForeColor = System.Drawing.SystemColors.ControlText; + this.textBoxTrainDirectory.Location = new System.Drawing.Point(200, 51); + this.textBoxTrainDirectory.Name = "textBoxTrainDirectory"; + this.textBoxTrainDirectory.ReadOnly = true; + this.textBoxTrainDirectory.Size = new System.Drawing.Size(387, 20); + this.textBoxTrainDirectory.TabIndex = 3; // - // comboBoxRailDriverUnits + // buttonSetRouteDirectory // - this.comboBoxRailDriverUnits.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.comboBoxRailDriverUnits.FormattingEnabled = true; - this.comboBoxRailDriverUnits.Items.AddRange(new object[] { - "Miles per Hour (MPH)", - "Kilometers per Hour (KPH)"}); - this.comboBoxRailDriverUnits.Location = new System.Drawing.Point(140, 16); - this.comboBoxRailDriverUnits.Name = "comboBoxRailDriverUnits"; - this.comboBoxRailDriverUnits.Size = new System.Drawing.Size(165, 21); - this.comboBoxRailDriverUnits.TabIndex = 3; - this.comboBoxRailDriverUnits.SelectedIndexChanged += new System.EventHandler(this.comboBoxRailDriverUnits_SelectedIndexChanged); + this.buttonSetRouteDirectory.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.buttonSetRouteDirectory.BackColor = System.Drawing.SystemColors.ButtonFace; + this.buttonSetRouteDirectory.ForeColor = System.Drawing.SystemColors.ControlText; + this.buttonSetRouteDirectory.Location = new System.Drawing.Point(594, 18); + this.buttonSetRouteDirectory.Name = "buttonSetRouteDirectory"; + this.buttonSetRouteDirectory.Size = new System.Drawing.Size(75, 26); + this.buttonSetRouteDirectory.TabIndex = 2; + this.buttonSetRouteDirectory.Text = "Choose..."; + this.buttonSetRouteDirectory.UseVisualStyleBackColor = true; + this.buttonSetRouteDirectory.Click += new System.EventHandler(this.buttonSetRouteDirectory_Click); // - // labelRailDriverSpeedUnits + // labelRouteInstallDirectory // - this.labelRailDriverSpeedUnits.Location = new System.Drawing.Point(7, 14); - this.labelRailDriverSpeedUnits.Name = "labelRailDriverSpeedUnits"; - this.labelRailDriverSpeedUnits.Size = new System.Drawing.Size(130, 30); - this.labelRailDriverSpeedUnits.TabIndex = 2; - this.labelRailDriverSpeedUnits.Text = "LED Display speed units:"; - this.labelRailDriverSpeedUnits.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.labelRouteInstallDirectory.AutoSize = true; + this.labelRouteInstallDirectory.Location = new System.Drawing.Point(6, 21); + this.labelRouteInstallDirectory.Name = "labelRouteInstallDirectory"; + this.labelRouteInstallDirectory.Size = new System.Drawing.Size(134, 13); + this.labelRouteInstallDirectory.TabIndex = 1; + this.labelRouteInstallDirectory.Text = "Route installation directory:"; // - // groupboxDistance + // textBoxRouteDirectory // - this.groupboxDistance.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + this.textBoxRouteDirectory.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.groupboxDistance.Controls.Add(this.comboboxMotionBlur); - this.groupboxDistance.Controls.Add(this.labelMotionBlur); - this.groupboxDistance.Controls.Add(this.labelDistanceUnit); - this.groupboxDistance.Controls.Add(this.updownDistance); - this.groupboxDistance.Controls.Add(this.labelDistance); - this.groupboxDistance.ForeColor = System.Drawing.Color.Black; - this.groupboxDistance.Location = new System.Drawing.Point(0, 0); - this.groupboxDistance.Name = "groupboxDistance"; - this.groupboxDistance.Size = new System.Drawing.Size(316, 80); - this.groupboxDistance.TabIndex = 8; - this.groupboxDistance.TabStop = false; - this.groupboxDistance.Text = "Distance effects"; + this.textBoxRouteDirectory.BackColor = System.Drawing.SystemColors.Control; + this.textBoxRouteDirectory.ForeColor = System.Drawing.SystemColors.ControlText; + this.textBoxRouteDirectory.Location = new System.Drawing.Point(200, 20); + this.textBoxRouteDirectory.Name = "textBoxRouteDirectory"; + this.textBoxRouteDirectory.ReadOnly = true; + this.textBoxRouteDirectory.Size = new System.Drawing.Size(387, 20); + this.textBoxRouteDirectory.TabIndex = 0; // - // comboboxMotionBlur + // panelOptionsLeft // - this.comboboxMotionBlur.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.comboboxMotionBlur.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.comboboxMotionBlur.FormattingEnabled = true; - this.comboboxMotionBlur.Location = new System.Drawing.Point(144, 48); - this.comboboxMotionBlur.Name = "comboboxMotionBlur"; - this.comboboxMotionBlur.Size = new System.Drawing.Size(152, 21); - this.comboboxMotionBlur.TabIndex = 4; + this.panelOptionsLeft.Controls.Add(this.groupboxDisplayMode); + this.panelOptionsLeft.Controls.Add(this.groupboxWindow); + this.panelOptionsLeft.Controls.Add(this.groupboxFullscreen); + this.panelOptionsLeft.Controls.Add(this.groupboxInterpolation); + this.panelOptionsLeft.Location = new System.Drawing.Point(8, 72); + this.panelOptionsLeft.Name = "panelOptionsLeft"; + this.panelOptionsLeft.Size = new System.Drawing.Size(316, 576); + this.panelOptionsLeft.TabIndex = 16; // - // labelMotionBlur + // groupboxDisplayMode // - this.labelMotionBlur.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + this.groupboxDisplayMode.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.labelMotionBlur.AutoEllipsis = true; - this.labelMotionBlur.Location = new System.Drawing.Point(5, 51); - this.labelMotionBlur.Name = "labelMotionBlur"; - this.labelMotionBlur.Size = new System.Drawing.Size(140, 18); - this.labelMotionBlur.TabIndex = 3; - this.labelMotionBlur.Text = "Motion blur:"; - this.labelMotionBlur.TextAlign = System.Drawing.ContentAlignment.MiddleRight; - // - // labelDistanceUnit - // - this.labelDistanceUnit.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.labelDistanceUnit.AutoEllipsis = true; - this.labelDistanceUnit.Location = new System.Drawing.Point(272, 24); - this.labelDistanceUnit.Name = "labelDistanceUnit"; - this.labelDistanceUnit.Size = new System.Drawing.Size(24, 18); - this.labelDistanceUnit.TabIndex = 2; - this.labelDistanceUnit.Text = "m"; + this.groupboxDisplayMode.Controls.Add(this.comboBoxFont); + this.groupboxDisplayMode.Controls.Add(this.labelFontName); + this.groupboxDisplayMode.Controls.Add(this.labelHUDLarge); + this.groupboxDisplayMode.Controls.Add(this.labelHUDNormal); + this.groupboxDisplayMode.Controls.Add(this.labelHUDSmall); + this.groupboxDisplayMode.Controls.Add(this.trackBarHUDSize); + this.groupboxDisplayMode.Controls.Add(this.labelHUDScale); + this.groupboxDisplayMode.Controls.Add(this.comboboxVSync); + this.groupboxDisplayMode.Controls.Add(this.labelVSync); + this.groupboxDisplayMode.Controls.Add(this.radiobuttonFullscreen); + this.groupboxDisplayMode.Controls.Add(this.radiobuttonWindow); + this.groupboxDisplayMode.ForeColor = System.Drawing.Color.Black; + this.groupboxDisplayMode.Location = new System.Drawing.Point(0, 0); + this.groupboxDisplayMode.Name = "groupboxDisplayMode"; + this.groupboxDisplayMode.Size = new System.Drawing.Size(316, 189); + this.groupboxDisplayMode.TabIndex = 4; + this.groupboxDisplayMode.TabStop = false; + this.groupboxDisplayMode.Text = "Display mode"; // - // updownDistance + // comboBoxFont // - this.updownDistance.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.updownDistance.Location = new System.Drawing.Point(144, 24); - this.updownDistance.Maximum = new decimal(new int[] { - 100000, - 0, - 0, - 0}); - this.updownDistance.Minimum = new decimal(new int[] { - 100, - 0, - 0, - 0}); - this.updownDistance.Name = "updownDistance"; - this.updownDistance.Size = new System.Drawing.Size(128, 20); - this.updownDistance.TabIndex = 1; - this.updownDistance.Value = new decimal(new int[] { - 600, - 0, - 0, - 0}); + this.comboBoxFont.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.comboBoxFont.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBoxFont.FormattingEnabled = true; + this.comboBoxFont.Location = new System.Drawing.Point(45, 155); + this.comboBoxFont.Name = "comboBoxFont"; + this.comboBoxFont.Size = new System.Drawing.Size(264, 21); + this.comboBoxFont.TabIndex = 14; + this.comboBoxFont.SelectionChangeCommitted += new System.EventHandler(this.comboBoxFont_SelectedIndexChanged); // - // labelDistance + // labelFontName // - this.labelDistance.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.labelDistance.AutoEllipsis = true; - this.labelDistance.Location = new System.Drawing.Point(9, 26); - this.labelDistance.Name = "labelDistance"; - this.labelDistance.Size = new System.Drawing.Size(136, 18); - this.labelDistance.TabIndex = 0; - this.labelDistance.Text = "Viewing distance:"; - this.labelDistance.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.labelFontName.Location = new System.Drawing.Point(8, 148); + this.labelFontName.Name = "labelFontName"; + this.labelFontName.Size = new System.Drawing.Size(50, 36); + this.labelFontName.TabIndex = 13; + this.labelFontName.Text = "Font:"; + this.labelFontName.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // - // groupboxControls + // labelHUDLarge // - this.groupboxControls.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.groupboxControls.Controls.Add(this.checkBoxEBAxis); - this.groupboxControls.Controls.Add(this.trackbarJoystickAxisThreshold); - this.groupboxControls.Controls.Add(this.checkboxJoysticksUsed); - this.groupboxControls.Controls.Add(this.labelJoystickAxisThreshold); - this.groupboxControls.ForeColor = System.Drawing.Color.Black; - this.groupboxControls.Location = new System.Drawing.Point(0, 144); - this.groupboxControls.Name = "groupboxControls"; - this.groupboxControls.Size = new System.Drawing.Size(316, 80); - this.groupboxControls.TabIndex = 10; - this.groupboxControls.TabStop = false; - this.groupboxControls.Text = "Controls"; + this.labelHUDLarge.Location = new System.Drawing.Point(258, 125); + this.labelHUDLarge.Name = "labelHUDLarge"; + this.labelHUDLarge.Size = new System.Drawing.Size(72, 48); + this.labelHUDLarge.TabIndex = 12; + this.labelHUDLarge.Text = "Large"; + this.labelHUDLarge.TextAlign = System.Drawing.ContentAlignment.TopCenter; // - // checkBoxEBAxis + // labelHUDNormal // - this.checkBoxEBAxis.Checked = true; - this.checkBoxEBAxis.CheckState = System.Windows.Forms.CheckState.Checked; - this.checkBoxEBAxis.Location = new System.Drawing.Point(8, 41); - this.checkBoxEBAxis.Name = "checkBoxEBAxis"; - this.checkBoxEBAxis.Size = new System.Drawing.Size(190, 36); - this.checkBoxEBAxis.TabIndex = 18; - this.checkBoxEBAxis.Text = "Allow EB on brake axis"; - this.checkBoxEBAxis.UseVisualStyleBackColor = true; + this.labelHUDNormal.Location = new System.Drawing.Point(162, 125); + this.labelHUDNormal.Name = "labelHUDNormal"; + this.labelHUDNormal.Size = new System.Drawing.Size(70, 48); + this.labelHUDNormal.TabIndex = 11; + this.labelHUDNormal.Text = "Normal"; + this.labelHUDNormal.TextAlign = System.Drawing.ContentAlignment.TopCenter; // - // trackbarJoystickAxisThreshold + // labelHUDSmall // - this.trackbarJoystickAxisThreshold.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.trackbarJoystickAxisThreshold.LargeChange = 10; - this.trackbarJoystickAxisThreshold.Location = new System.Drawing.Point(200, 32); - this.trackbarJoystickAxisThreshold.Maximum = 100; - this.trackbarJoystickAxisThreshold.Name = "trackbarJoystickAxisThreshold"; - this.trackbarJoystickAxisThreshold.Size = new System.Drawing.Size(96, 45); - this.trackbarJoystickAxisThreshold.TabIndex = 2; - this.trackbarJoystickAxisThreshold.TickFrequency = 10; - this.trackbarJoystickAxisThreshold.TickStyle = System.Windows.Forms.TickStyle.Both; + this.labelHUDSmall.Location = new System.Drawing.Point(66, 125); + this.labelHUDSmall.Name = "labelHUDSmall"; + this.labelHUDSmall.Size = new System.Drawing.Size(70, 48); + this.labelHUDSmall.TabIndex = 10; + this.labelHUDSmall.Text = "Small"; + this.labelHUDSmall.TextAlign = System.Drawing.ContentAlignment.TopCenter; // - // checkboxJoysticksUsed + // trackBarHUDSize // - this.checkboxJoysticksUsed.AutoSize = true; - this.checkboxJoysticksUsed.Location = new System.Drawing.Point(8, 24); - this.checkboxJoysticksUsed.Name = "checkboxJoysticksUsed"; - this.checkboxJoysticksUsed.Size = new System.Drawing.Size(110, 17); - this.checkboxJoysticksUsed.TabIndex = 0; - this.checkboxJoysticksUsed.Text = "Joysticks enabled"; - this.checkboxJoysticksUsed.UseVisualStyleBackColor = true; - this.checkboxJoysticksUsed.CheckedChanged += new System.EventHandler(this.checkboxJoysticksUsed_CheckedChanged); + this.trackBarHUDSize.LargeChange = 1; + this.trackBarHUDSize.Location = new System.Drawing.Point(88, 100); + this.trackBarHUDSize.Maximum = 2; + this.trackBarHUDSize.Name = "trackBarHUDSize"; + this.trackBarHUDSize.Size = new System.Drawing.Size(220, 45); + this.trackBarHUDSize.TabIndex = 9; + this.trackBarHUDSize.Value = 1; // - // labelJoystickAxisThreshold + // labelHUDScale // - this.labelJoystickAxisThreshold.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.labelJoystickAxisThreshold.AutoEllipsis = true; - this.labelJoystickAxisThreshold.Location = new System.Drawing.Point(110, 10); - this.labelJoystickAxisThreshold.Name = "labelJoystickAxisThreshold"; - this.labelJoystickAxisThreshold.Size = new System.Drawing.Size(180, 18); - this.labelJoystickAxisThreshold.TabIndex = 1; - this.labelJoystickAxisThreshold.Text = "Joystick threshold:"; - this.labelJoystickAxisThreshold.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.labelHUDScale.AutoSize = true; + this.labelHUDScale.Location = new System.Drawing.Point(8, 106); + this.labelHUDScale.Name = "labelHUDScale"; + this.labelHUDScale.Size = new System.Drawing.Size(54, 13); + this.labelHUDScale.TabIndex = 8; + this.labelHUDScale.Text = "HUD Size"; // - // groupboxVerbosity + // comboboxVSync // - this.groupboxVerbosity.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.groupboxVerbosity.Controls.Add(this.checkBoxAccessibility); - this.groupboxVerbosity.Controls.Add(this.checkboxErrorMessages); - this.groupboxVerbosity.Controls.Add(this.checkboxWarningMessages); - this.groupboxVerbosity.ForeColor = System.Drawing.Color.Black; - this.groupboxVerbosity.Location = new System.Drawing.Point(0, 396); - this.groupboxVerbosity.Name = "groupboxVerbosity"; - this.groupboxVerbosity.Size = new System.Drawing.Size(316, 64); - this.groupboxVerbosity.TabIndex = 12; - this.groupboxVerbosity.TabStop = false; - this.groupboxVerbosity.Text = "Verbosity"; + this.comboboxVSync.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.comboboxVSync.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboboxVSync.FormattingEnabled = true; + this.comboboxVSync.Location = new System.Drawing.Point(156, 72); + this.comboboxVSync.Name = "comboboxVSync"; + this.comboboxVSync.Size = new System.Drawing.Size(152, 21); + this.comboboxVSync.TabIndex = 7; // - // checkBoxAccessibility + // labelVSync // - this.checkBoxAccessibility.AutoSize = true; - this.checkBoxAccessibility.Location = new System.Drawing.Point(176, 21); - this.checkBoxAccessibility.Name = "checkBoxAccessibility"; - this.checkBoxAccessibility.Size = new System.Drawing.Size(106, 17); - this.checkBoxAccessibility.TabIndex = 2; - this.checkBoxAccessibility.Text = "Accessibility Aids"; - this.checkBoxAccessibility.UseVisualStyleBackColor = true; + this.labelVSync.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.labelVSync.AutoEllipsis = true; + this.labelVSync.Location = new System.Drawing.Point(8, 72); + this.labelVSync.Name = "labelVSync"; + this.labelVSync.Size = new System.Drawing.Size(148, 18); + this.labelVSync.TabIndex = 2; + this.labelVSync.Text = "Vertical syncronization:"; + this.labelVSync.TextAlign = System.Drawing.ContentAlignment.TopRight; // - // checkboxErrorMessages + // radiobuttonFullscreen // - this.checkboxErrorMessages.AutoSize = true; - this.checkboxErrorMessages.Location = new System.Drawing.Point(8, 38); - this.checkboxErrorMessages.Name = "checkboxErrorMessages"; - this.checkboxErrorMessages.Size = new System.Drawing.Size(127, 17); - this.checkboxErrorMessages.TabIndex = 1; - this.checkboxErrorMessages.Text = "Show error messages"; - this.checkboxErrorMessages.UseVisualStyleBackColor = true; + this.radiobuttonFullscreen.AutoSize = true; + this.radiobuttonFullscreen.Location = new System.Drawing.Point(8, 48); + this.radiobuttonFullscreen.Name = "radiobuttonFullscreen"; + this.radiobuttonFullscreen.Size = new System.Drawing.Size(102, 17); + this.radiobuttonFullscreen.TabIndex = 1; + this.radiobuttonFullscreen.TabStop = true; + this.radiobuttonFullscreen.Text = "Fullscreen mode"; + this.radiobuttonFullscreen.UseVisualStyleBackColor = true; // - // checkboxWarningMessages + // radiobuttonWindow // - this.checkboxWarningMessages.AutoSize = true; - this.checkboxWarningMessages.Location = new System.Drawing.Point(8, 21); - this.checkboxWarningMessages.Name = "checkboxWarningMessages"; - this.checkboxWarningMessages.Size = new System.Drawing.Size(143, 17); - this.checkboxWarningMessages.TabIndex = 0; - this.checkboxWarningMessages.Text = "Show warning messages"; - this.checkboxWarningMessages.UseVisualStyleBackColor = true; + this.radiobuttonWindow.AutoSize = true; + this.radiobuttonWindow.Checked = true; + this.radiobuttonWindow.Location = new System.Drawing.Point(8, 24); + this.radiobuttonWindow.Name = "radiobuttonWindow"; + this.radiobuttonWindow.Size = new System.Drawing.Size(93, 17); + this.radiobuttonWindow.TabIndex = 0; + this.radiobuttonWindow.TabStop = true; + this.radiobuttonWindow.Text = "Window mode"; + this.radiobuttonWindow.UseVisualStyleBackColor = true; // - // groupboxSimulation + // groupboxWindow // - this.groupboxSimulation.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + this.groupboxWindow.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.groupboxSimulation.Controls.Add(this.checkBoxLoadingSway); - this.groupboxSimulation.Controls.Add(this.checkboxBlackBox); - this.groupboxSimulation.Controls.Add(this.checkboxDerailments); - this.groupboxSimulation.Controls.Add(this.checkboxCollisions); - this.groupboxSimulation.Controls.Add(this.checkboxToppling); - this.groupboxSimulation.ForeColor = System.Drawing.Color.Black; - this.groupboxSimulation.Location = new System.Drawing.Point(0, 310); - this.groupboxSimulation.Name = "groupboxSimulation"; - this.groupboxSimulation.Size = new System.Drawing.Size(316, 80); - this.groupboxSimulation.TabIndex = 11; - this.groupboxSimulation.TabStop = false; - this.groupboxSimulation.Text = "Detail of simulation"; + this.groupboxWindow.Controls.Add(this.updownWindowHeight); + this.groupboxWindow.Controls.Add(this.labelWindowHeight); + this.groupboxWindow.Controls.Add(this.updownWindowWidth); + this.groupboxWindow.Controls.Add(this.labelWindowWidth); + this.groupboxWindow.ForeColor = System.Drawing.Color.Black; + this.groupboxWindow.Location = new System.Drawing.Point(0, 192); + this.groupboxWindow.Name = "groupboxWindow"; + this.groupboxWindow.Size = new System.Drawing.Size(316, 80); + this.groupboxWindow.TabIndex = 5; + this.groupboxWindow.TabStop = false; + this.groupboxWindow.Text = "Window mode"; // - // checkBoxLoadingSway + // updownWindowHeight // - this.checkBoxLoadingSway.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.checkBoxLoadingSway.AutoSize = true; - this.checkBoxLoadingSway.Location = new System.Drawing.Point(176, 21); - this.checkBoxLoadingSway.Name = "checkBoxLoadingSway"; - this.checkBoxLoadingSway.Size = new System.Drawing.Size(123, 17); - this.checkBoxLoadingSway.TabIndex = 4; - this.checkBoxLoadingSway.Text = "Enable loading sway"; - this.checkBoxLoadingSway.UseVisualStyleBackColor = true; + this.updownWindowHeight.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.updownWindowHeight.Location = new System.Drawing.Point(156, 48); + this.updownWindowHeight.Maximum = new decimal(new int[] { + 1048575, + 0, + 0, + 0}); + this.updownWindowHeight.Minimum = new decimal(new int[] { + 16, + 0, + 0, + 0}); + this.updownWindowHeight.Name = "updownWindowHeight"; + this.updownWindowHeight.Size = new System.Drawing.Size(152, 20); + this.updownWindowHeight.TabIndex = 3; + this.updownWindowHeight.Value = new decimal(new int[] { + 600, + 0, + 0, + 0}); // - // checkboxBlackBox + // labelWindowHeight // - this.checkboxBlackBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) + this.labelWindowHeight.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.checkboxBlackBox.AutoSize = true; - this.checkboxBlackBox.Location = new System.Drawing.Point(176, 38); - this.checkboxBlackBox.Name = "checkboxBlackBox"; - this.checkboxBlackBox.Size = new System.Drawing.Size(108, 17); - this.checkboxBlackBox.TabIndex = 3; - this.checkboxBlackBox.Text = "Enable black box"; - this.checkboxBlackBox.UseVisualStyleBackColor = true; + this.labelWindowHeight.AutoEllipsis = true; + this.labelWindowHeight.Location = new System.Drawing.Point(8, 50); + this.labelWindowHeight.Name = "labelWindowHeight"; + this.labelWindowHeight.Size = new System.Drawing.Size(148, 18); + this.labelWindowHeight.TabIndex = 2; + this.labelWindowHeight.Text = "Height:"; + this.labelWindowHeight.TextAlign = System.Drawing.ContentAlignment.TopRight; // - // checkboxDerailments + // updownWindowWidth // - this.checkboxDerailments.AutoSize = true; - this.checkboxDerailments.Location = new System.Drawing.Point(8, 55); - this.checkboxDerailments.Name = "checkboxDerailments"; - this.checkboxDerailments.Size = new System.Drawing.Size(81, 17); - this.checkboxDerailments.TabIndex = 2; - this.checkboxDerailments.Text = "Derailments"; - this.checkboxDerailments.UseVisualStyleBackColor = true; + this.updownWindowWidth.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.updownWindowWidth.Location = new System.Drawing.Point(156, 24); + this.updownWindowWidth.Maximum = new decimal(new int[] { + 1048575, + 0, + 0, + 0}); + this.updownWindowWidth.Minimum = new decimal(new int[] { + 16, + 0, + 0, + 0}); + this.updownWindowWidth.Name = "updownWindowWidth"; + this.updownWindowWidth.Size = new System.Drawing.Size(152, 20); + this.updownWindowWidth.TabIndex = 1; + this.updownWindowWidth.Value = new decimal(new int[] { + 960, + 0, + 0, + 0}); // - // checkboxCollisions + // labelWindowWidth // - this.checkboxCollisions.AutoSize = true; - this.checkboxCollisions.Location = new System.Drawing.Point(8, 38); - this.checkboxCollisions.Name = "checkboxCollisions"; - this.checkboxCollisions.Size = new System.Drawing.Size(69, 17); - this.checkboxCollisions.TabIndex = 1; - this.checkboxCollisions.Text = "Collisions"; - this.checkboxCollisions.UseVisualStyleBackColor = true; + this.labelWindowWidth.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.labelWindowWidth.AutoEllipsis = true; + this.labelWindowWidth.Location = new System.Drawing.Point(8, 26); + this.labelWindowWidth.Name = "labelWindowWidth"; + this.labelWindowWidth.Size = new System.Drawing.Size(148, 18); + this.labelWindowWidth.TabIndex = 0; + this.labelWindowWidth.Text = "Width:"; + this.labelWindowWidth.TextAlign = System.Drawing.ContentAlignment.TopRight; // - // checkboxToppling + // groupboxFullscreen // - this.checkboxToppling.AutoSize = true; - this.checkboxToppling.Location = new System.Drawing.Point(8, 21); - this.checkboxToppling.Name = "checkboxToppling"; - this.checkboxToppling.Size = new System.Drawing.Size(67, 17); - this.checkboxToppling.TabIndex = 0; - this.checkboxToppling.Text = "Toppling"; - this.checkboxToppling.UseVisualStyleBackColor = true; + this.groupboxFullscreen.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupboxFullscreen.Controls.Add(this.comboboxFullscreenBits); + this.groupboxFullscreen.Controls.Add(this.labelFullscreenBits); + this.groupboxFullscreen.Controls.Add(this.updownFullscreenHeight); + this.groupboxFullscreen.Controls.Add(this.labelFullscreenHeight); + this.groupboxFullscreen.Controls.Add(this.updownFullscreenWidth); + this.groupboxFullscreen.Controls.Add(this.labelFullscreenWidth); + this.groupboxFullscreen.ForeColor = System.Drawing.Color.Black; + this.groupboxFullscreen.Location = new System.Drawing.Point(0, 275); + this.groupboxFullscreen.Name = "groupboxFullscreen"; + this.groupboxFullscreen.Size = new System.Drawing.Size(316, 104); + this.groupboxFullscreen.TabIndex = 6; + this.groupboxFullscreen.TabStop = false; + this.groupboxFullscreen.Text = "Fullscreen mode"; // - // groupboxSound + // comboboxFullscreenBits // - this.groupboxSound.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + this.comboboxFullscreenBits.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.comboboxFullscreenBits.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboboxFullscreenBits.FormattingEnabled = true; + this.comboboxFullscreenBits.Location = new System.Drawing.Point(156, 72); + this.comboboxFullscreenBits.Name = "comboboxFullscreenBits"; + this.comboboxFullscreenBits.Size = new System.Drawing.Size(152, 21); + this.comboboxFullscreenBits.TabIndex = 5; + // + // labelFullscreenBits + // + this.labelFullscreenBits.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.groupboxSound.Controls.Add(this.updownSoundNumber); - this.groupboxSound.Controls.Add(this.labelSoundNumber); - this.groupboxSound.ForeColor = System.Drawing.Color.Black; - this.groupboxSound.Location = new System.Drawing.Point(0, 88); - this.groupboxSound.Name = "groupboxSound"; - this.groupboxSound.Size = new System.Drawing.Size(316, 48); - this.groupboxSound.TabIndex = 9; - this.groupboxSound.TabStop = false; - this.groupboxSound.Text = "Sound"; + this.labelFullscreenBits.AutoEllipsis = true; + this.labelFullscreenBits.Location = new System.Drawing.Point(8, 74); + this.labelFullscreenBits.Name = "labelFullscreenBits"; + this.labelFullscreenBits.Size = new System.Drawing.Size(148, 18); + this.labelFullscreenBits.TabIndex = 4; + this.labelFullscreenBits.Text = "Bits per pixel:"; + this.labelFullscreenBits.TextAlign = System.Drawing.ContentAlignment.TopRight; // - // updownSoundNumber + // updownFullscreenHeight // - this.updownSoundNumber.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.updownSoundNumber.Location = new System.Drawing.Point(140, 16); - this.updownSoundNumber.Maximum = new decimal(new int[] { - 128, + this.updownFullscreenHeight.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.updownFullscreenHeight.Location = new System.Drawing.Point(156, 48); + this.updownFullscreenHeight.Maximum = new decimal(new int[] { + 1048575, 0, 0, 0}); - this.updownSoundNumber.Minimum = new decimal(new int[] { - 8, + this.updownFullscreenHeight.Minimum = new decimal(new int[] { + 16, 0, 0, 0}); - this.updownSoundNumber.Name = "updownSoundNumber"; - this.updownSoundNumber.Size = new System.Drawing.Size(152, 20); - this.updownSoundNumber.TabIndex = 3; - this.updownSoundNumber.Value = new decimal(new int[] { - 16, + this.updownFullscreenHeight.Name = "updownFullscreenHeight"; + this.updownFullscreenHeight.Size = new System.Drawing.Size(152, 20); + this.updownFullscreenHeight.TabIndex = 3; + this.updownFullscreenHeight.Value = new decimal(new int[] { + 768, 0, 0, 0}); // - // labelSoundNumber + // labelFullscreenHeight // - this.labelSoundNumber.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + this.labelFullscreenHeight.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.labelSoundNumber.Location = new System.Drawing.Point(5, 18); - this.labelSoundNumber.Name = "labelSoundNumber"; - this.labelSoundNumber.Size = new System.Drawing.Size(136, 18); - this.labelSoundNumber.TabIndex = 2; - this.labelSoundNumber.Text = "Number of allowed sounds:"; - this.labelSoundNumber.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.labelFullscreenHeight.AutoEllipsis = true; + this.labelFullscreenHeight.Location = new System.Drawing.Point(8, 50); + this.labelFullscreenHeight.Name = "labelFullscreenHeight"; + this.labelFullscreenHeight.Size = new System.Drawing.Size(148, 18); + this.labelFullscreenHeight.TabIndex = 2; + this.labelFullscreenHeight.Text = "Height:"; + this.labelFullscreenHeight.TextAlign = System.Drawing.ContentAlignment.TopRight; // - // panelOptionsPage2 + // updownFullscreenWidth // - this.panelOptionsPage2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.panelOptionsPage2.Controls.Add(this.groupBoxInputDevice); - this.panelOptionsPage2.Controls.Add(this.groupBoxObjectParser); - this.panelOptionsPage2.Controls.Add(this.groupBoxKioskMode); - this.panelOptionsPage2.Controls.Add(this.groupBoxAdvancedOptions); - this.panelOptionsPage2.Controls.Add(this.groupBoxPackageOptions); - this.panelOptionsPage2.Location = new System.Drawing.Point(0, 72); - this.panelOptionsPage2.Name = "panelOptionsPage2"; - this.panelOptionsPage2.Size = new System.Drawing.Size(683, 553); - this.panelOptionsPage2.TabIndex = 20; + this.updownFullscreenWidth.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.updownFullscreenWidth.Location = new System.Drawing.Point(156, 24); + this.updownFullscreenWidth.Maximum = new decimal(new int[] { + 1048575, + 0, + 0, + 0}); + this.updownFullscreenWidth.Minimum = new decimal(new int[] { + 16, + 0, + 0, + 0}); + this.updownFullscreenWidth.Name = "updownFullscreenWidth"; + this.updownFullscreenWidth.Size = new System.Drawing.Size(152, 20); + this.updownFullscreenWidth.TabIndex = 1; + this.updownFullscreenWidth.Value = new decimal(new int[] { + 1024, + 0, + 0, + 0}); // - // groupBoxInputDevice + // labelFullscreenWidth // - this.groupBoxInputDevice.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) + this.labelFullscreenWidth.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.groupBoxInputDevice.Controls.Add(this.labelInputDevice); - this.groupBoxInputDevice.Controls.Add(this.listviewInputDevice); - this.groupBoxInputDevice.Controls.Add(this.checkBoxInputDeviceEnable); - this.groupBoxInputDevice.Controls.Add(this.buttonInputDeviceConfig); - this.groupBoxInputDevice.ForeColor = System.Drawing.Color.Black; - this.groupBoxInputDevice.Location = new System.Drawing.Point(6, 374); - this.groupBoxInputDevice.Name = "groupBoxInputDevice"; - this.groupBoxInputDevice.Size = new System.Drawing.Size(674, 173); - this.groupBoxInputDevice.TabIndex = 24; - this.groupBoxInputDevice.TabStop = false; - this.groupBoxInputDevice.Text = "Input Device Plugin"; + this.labelFullscreenWidth.AutoEllipsis = true; + this.labelFullscreenWidth.Location = new System.Drawing.Point(8, 26); + this.labelFullscreenWidth.Name = "labelFullscreenWidth"; + this.labelFullscreenWidth.Size = new System.Drawing.Size(148, 18); + this.labelFullscreenWidth.TabIndex = 0; + this.labelFullscreenWidth.Text = "Width:"; + this.labelFullscreenWidth.TextAlign = System.Drawing.ContentAlignment.TopRight; // - // labelInputDevice + // groupboxInterpolation // - this.labelInputDevice.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + this.groupboxInterpolation.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.labelInputDevice.Location = new System.Drawing.Point(8, 17); - this.labelInputDevice.Name = "labelInputDevice"; - this.labelInputDevice.Size = new System.Drawing.Size(658, 17); - this.labelInputDevice.TabIndex = 0; - this.labelInputDevice.Text = "WARNING: If you are turn on the Input Device Plugin(s), it may be happen the conf" + - "lict of input setting(s)."; + this.groupboxInterpolation.Controls.Add(this.updownAntiAliasing); + this.groupboxInterpolation.Controls.Add(this.labelAntiAliasing); + this.groupboxInterpolation.Controls.Add(this.labelTransparencyQuality); + this.groupboxInterpolation.Controls.Add(this.labelTransparencyPerformance); + this.groupboxInterpolation.Controls.Add(this.labelTransparency); + this.groupboxInterpolation.Controls.Add(this.updownAnisotropic); + this.groupboxInterpolation.Controls.Add(this.labelAnisotropic); + this.groupboxInterpolation.Controls.Add(this.comboboxInterpolation); + this.groupboxInterpolation.Controls.Add(this.labelInterpolation); + this.groupboxInterpolation.Controls.Add(this.trackbarTransparency); + this.groupboxInterpolation.ForeColor = System.Drawing.Color.Black; + this.groupboxInterpolation.Location = new System.Drawing.Point(0, 381); + this.groupboxInterpolation.Name = "groupboxInterpolation"; + this.groupboxInterpolation.Size = new System.Drawing.Size(316, 160); + this.groupboxInterpolation.TabIndex = 7; + this.groupboxInterpolation.TabStop = false; + this.groupboxInterpolation.Text = "Interpolation"; // - // listviewInputDevice + // updownAntiAliasing // - this.listviewInputDevice.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.listviewInputDevice.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { - this.columnheaderInputDeviceName, - this.columnheaderInputDeviceStatus, - this.columnheaderInputDeviceVersion, - this.columnheaderInputDeviceProvider, - this.columnheaderInputDeviceFileName}); - this.listviewInputDevice.FullRowSelect = true; - this.listviewInputDevice.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; - this.listviewInputDevice.HideSelection = false; - this.listviewInputDevice.LabelWrap = false; - this.listviewInputDevice.Location = new System.Drawing.Point(8, 38); - this.listviewInputDevice.MultiSelect = false; - this.listviewInputDevice.Name = "listviewInputDevice"; - this.listviewInputDevice.ShowGroups = false; - this.listviewInputDevice.Size = new System.Drawing.Size(658, 95); - this.listviewInputDevice.TabIndex = 1; - this.listviewInputDevice.UseCompatibleStateImageBehavior = false; - this.listviewInputDevice.View = System.Windows.Forms.View.Details; - this.listviewInputDevice.SelectedIndexChanged += new System.EventHandler(this.listviewInputDevice_SelectedIndexChanged); - this.listviewInputDevice.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.listviewInputDevice_MouseDoubleClick); + this.updownAntiAliasing.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.updownAntiAliasing.Location = new System.Drawing.Point(156, 64); + this.updownAntiAliasing.Maximum = new decimal(new int[] { + 16, + 0, + 0, + 0}); + this.updownAntiAliasing.Name = "updownAntiAliasing"; + this.updownAntiAliasing.Size = new System.Drawing.Size(152, 20); + this.updownAntiAliasing.TabIndex = 5; // - // columnheaderInputDeviceName + // labelAntiAliasing // - this.columnheaderInputDeviceName.Text = "Name"; + this.labelAntiAliasing.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.labelAntiAliasing.AutoEllipsis = true; + this.labelAntiAliasing.Location = new System.Drawing.Point(8, 66); + this.labelAntiAliasing.Name = "labelAntiAliasing"; + this.labelAntiAliasing.Size = new System.Drawing.Size(148, 18); + this.labelAntiAliasing.TabIndex = 4; + this.labelAntiAliasing.Text = "Level of anti-aliasing:"; + this.labelAntiAliasing.TextAlign = System.Drawing.ContentAlignment.TopRight; // - // columnheaderInputDeviceStatus + // labelTransparencyQuality // - this.columnheaderInputDeviceStatus.Text = "Status"; + this.labelTransparencyQuality.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.labelTransparencyQuality.AutoEllipsis = true; + this.labelTransparencyQuality.Location = new System.Drawing.Point(230, 136); + this.labelTransparencyQuality.Name = "labelTransparencyQuality"; + this.labelTransparencyQuality.Size = new System.Drawing.Size(76, 16); + this.labelTransparencyQuality.TabIndex = 9; + this.labelTransparencyQuality.Text = "Smooth"; + this.labelTransparencyQuality.TextAlign = System.Drawing.ContentAlignment.TopRight; // - // columnheaderInputDeviceVersion + // labelTransparencyPerformance // - this.columnheaderInputDeviceVersion.Text = "Version"; + this.labelTransparencyPerformance.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.labelTransparencyPerformance.AutoEllipsis = true; + this.labelTransparencyPerformance.Location = new System.Drawing.Point(130, 136); + this.labelTransparencyPerformance.Name = "labelTransparencyPerformance"; + this.labelTransparencyPerformance.Size = new System.Drawing.Size(76, 16); + this.labelTransparencyPerformance.TabIndex = 8; + this.labelTransparencyPerformance.Text = "Sharp"; + this.labelTransparencyPerformance.TextAlign = System.Drawing.ContentAlignment.TopCenter; // - // columnheaderInputDeviceProvider + // labelTransparency // - this.columnheaderInputDeviceProvider.Text = "Provider"; + this.labelTransparency.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.labelTransparency.AutoEllipsis = true; + this.labelTransparency.Location = new System.Drawing.Point(8, 100); + this.labelTransparency.Name = "labelTransparency"; + this.labelTransparency.Size = new System.Drawing.Size(148, 18); + this.labelTransparency.TabIndex = 6; + this.labelTransparency.Text = "Transparency:"; + this.labelTransparency.TextAlign = System.Drawing.ContentAlignment.TopRight; // - // columnheaderInputDeviceFileName + // updownAnisotropic // - this.columnheaderInputDeviceFileName.Text = "File Name"; - this.columnheaderInputDeviceFileName.Width = 200; + this.updownAnisotropic.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.updownAnisotropic.Enabled = false; + this.updownAnisotropic.Location = new System.Drawing.Point(156, 40); + this.updownAnisotropic.Maximum = new decimal(new int[] { + 16, + 0, + 0, + 0}); + this.updownAnisotropic.Name = "updownAnisotropic"; + this.updownAnisotropic.Size = new System.Drawing.Size(152, 20); + this.updownAnisotropic.TabIndex = 3; // - // checkBoxInputDeviceEnable + // labelAnisotropic // - this.checkBoxInputDeviceEnable.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.checkBoxInputDeviceEnable.Enabled = false; - this.checkBoxInputDeviceEnable.Location = new System.Drawing.Point(8, 136); - this.checkBoxInputDeviceEnable.Name = "checkBoxInputDeviceEnable"; - this.checkBoxInputDeviceEnable.Size = new System.Drawing.Size(230, 34); - this.checkBoxInputDeviceEnable.TabIndex = 2; - this.checkBoxInputDeviceEnable.Text = "Enable this Input Device Plugin"; - this.checkBoxInputDeviceEnable.UseVisualStyleBackColor = true; - this.checkBoxInputDeviceEnable.CheckedChanged += new System.EventHandler(this.checkBoxInputDeviceEnable_CheckedChanged); + this.labelAnisotropic.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.labelAnisotropic.AutoEllipsis = true; + this.labelAnisotropic.Enabled = false; + this.labelAnisotropic.Location = new System.Drawing.Point(8, 42); + this.labelAnisotropic.Name = "labelAnisotropic"; + this.labelAnisotropic.Size = new System.Drawing.Size(148, 18); + this.labelAnisotropic.TabIndex = 2; + this.labelAnisotropic.Text = "Level of anisotropic filtering:"; + this.labelAnisotropic.TextAlign = System.Drawing.ContentAlignment.TopRight; // - // buttonInputDeviceConfig + // comboboxInterpolation // - this.buttonInputDeviceConfig.Anchor = System.Windows.Forms.AnchorStyles.Bottom; - this.buttonInputDeviceConfig.BackColor = System.Drawing.SystemColors.ButtonFace; - this.buttonInputDeviceConfig.Enabled = false; - this.buttonInputDeviceConfig.ForeColor = System.Drawing.SystemColors.ControlText; - this.buttonInputDeviceConfig.Location = new System.Drawing.Point(270, 140); - this.buttonInputDeviceConfig.MaximumSize = new System.Drawing.Size(106, 25); - this.buttonInputDeviceConfig.Name = "buttonInputDeviceConfig"; - this.buttonInputDeviceConfig.Size = new System.Drawing.Size(106, 25); - this.buttonInputDeviceConfig.TabIndex = 3; - this.buttonInputDeviceConfig.Text = "Config"; - this.buttonInputDeviceConfig.UseVisualStyleBackColor = true; - this.buttonInputDeviceConfig.Click += new System.EventHandler(this.buttonInputDeviceConfig_Click); + this.comboboxInterpolation.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.comboboxInterpolation.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboboxInterpolation.FormattingEnabled = true; + this.comboboxInterpolation.Location = new System.Drawing.Point(156, 16); + this.comboboxInterpolation.Name = "comboboxInterpolation"; + this.comboboxInterpolation.Size = new System.Drawing.Size(152, 21); + this.comboboxInterpolation.TabIndex = 1; + this.comboboxInterpolation.SelectedIndexChanged += new System.EventHandler(this.comboboxInterpolation_SelectedIndexChanged); // - // groupBoxObjectParser + // labelInterpolation // - this.groupBoxObjectParser.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.groupBoxObjectParser.Controls.Add(this.labelObjparser); - this.groupBoxObjectParser.Controls.Add(this.comboBoxObjparser); - this.groupBoxObjectParser.Controls.Add(this.labelXparser); - this.groupBoxObjectParser.Controls.Add(this.comboBoxXparser); - this.groupBoxObjectParser.ForeColor = System.Drawing.Color.Black; - this.groupBoxObjectParser.Location = new System.Drawing.Point(375, 258); - this.groupBoxObjectParser.Name = "groupBoxObjectParser"; - this.groupBoxObjectParser.Size = new System.Drawing.Size(305, 110); - this.groupBoxObjectParser.TabIndex = 23; - this.groupBoxObjectParser.TabStop = false; - this.groupBoxObjectParser.Text = "Object Parser"; + this.labelInterpolation.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.labelInterpolation.AutoEllipsis = true; + this.labelInterpolation.Location = new System.Drawing.Point(8, 18); + this.labelInterpolation.Name = "labelInterpolation"; + this.labelInterpolation.Size = new System.Drawing.Size(148, 18); + this.labelInterpolation.TabIndex = 0; + this.labelInterpolation.Text = "Mode:"; + this.labelInterpolation.TextAlign = System.Drawing.ContentAlignment.TopRight; // - // labelObjparser + // trackbarTransparency // - this.labelObjparser.Location = new System.Drawing.Point(7, 44); - this.labelObjparser.Name = "labelObjparser"; - this.labelObjparser.Size = new System.Drawing.Size(113, 26); - this.labelObjparser.TabIndex = 0; - this.labelObjparser.Text = "Obj Object Parser:"; - this.labelObjparser.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.trackbarTransparency.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.trackbarTransparency.Location = new System.Drawing.Point(156, 88); + this.trackbarTransparency.Maximum = 2; + this.trackbarTransparency.Name = "trackbarTransparency"; + this.trackbarTransparency.Size = new System.Drawing.Size(152, 45); + this.trackbarTransparency.TabIndex = 7; + this.trackbarTransparency.TickStyle = System.Windows.Forms.TickStyle.Both; // - // comboBoxObjparser + // panelOptionsRight // - this.comboBoxObjparser.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.comboBoxObjparser.FormattingEnabled = true; - this.comboBoxObjparser.Items.AddRange(new object[] { - "Original", - "Assimp"}); - this.comboBoxObjparser.Location = new System.Drawing.Point(127, 44); - this.comboBoxObjparser.Name = "comboBoxObjparser"; - this.comboBoxObjparser.Size = new System.Drawing.Size(170, 21); - this.comboBoxObjparser.TabIndex = 1; + this.panelOptionsRight.Controls.Add(this.groupBoxOther); + this.panelOptionsRight.Controls.Add(this.groupBoxRailDriver); + this.panelOptionsRight.Controls.Add(this.groupboxDistance); + this.panelOptionsRight.Controls.Add(this.groupboxControls); + this.panelOptionsRight.Controls.Add(this.groupboxVerbosity); + this.panelOptionsRight.Controls.Add(this.groupboxSimulation); + this.panelOptionsRight.Controls.Add(this.groupboxSound); + this.panelOptionsRight.Location = new System.Drawing.Point(332, 72); + this.panelOptionsRight.Name = "panelOptionsRight"; + this.panelOptionsRight.Size = new System.Drawing.Size(316, 579); + this.panelOptionsRight.TabIndex = 17; // - // labelXparser + // groupBoxOther // - this.labelXparser.Location = new System.Drawing.Point(7, 17); - this.labelXparser.Name = "labelXparser"; - this.labelXparser.Size = new System.Drawing.Size(113, 26); - this.labelXparser.TabIndex = 0; - this.labelXparser.Text = "X Object Parser:"; - this.labelXparser.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.groupBoxOther.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupBoxOther.Controls.Add(this.comboBoxTimeTableDisplayMode); + this.groupBoxOther.Controls.Add(this.labelTimeTableDisplayMode); + this.groupBoxOther.ForeColor = System.Drawing.Color.Black; + this.groupBoxOther.Location = new System.Drawing.Point(0, 468); + this.groupBoxOther.Name = "groupBoxOther"; + this.groupBoxOther.Size = new System.Drawing.Size(316, 48); + this.groupBoxOther.TabIndex = 19; + this.groupBoxOther.TabStop = false; + this.groupBoxOther.Text = "Other"; // - // comboBoxXparser + // comboBoxTimeTableDisplayMode // - this.comboBoxXparser.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.comboBoxXparser.FormattingEnabled = true; - this.comboBoxXparser.Items.AddRange(new object[] { - "Original", - "NewXParser", - "Assimp"}); - this.comboBoxXparser.Location = new System.Drawing.Point(127, 21); - this.comboBoxXparser.Name = "comboBoxXparser"; - this.comboBoxXparser.Size = new System.Drawing.Size(170, 21); - this.comboBoxXparser.TabIndex = 1; + this.comboBoxTimeTableDisplayMode.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.comboBoxTimeTableDisplayMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBoxTimeTableDisplayMode.FormattingEnabled = true; + this.comboBoxTimeTableDisplayMode.Location = new System.Drawing.Point(156, 16); + this.comboBoxTimeTableDisplayMode.Name = "comboBoxTimeTableDisplayMode"; + this.comboBoxTimeTableDisplayMode.Size = new System.Drawing.Size(152, 21); + this.comboBoxTimeTableDisplayMode.TabIndex = 1; + this.comboBoxTimeTableDisplayMode.SelectedIndexChanged += new System.EventHandler(this.comboBoxTimeTableDisplayMode_SelectedIndexChanged); // - // groupBoxKioskMode + // labelTimeTableDisplayMode // - this.groupBoxKioskMode.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.groupBoxKioskMode.Controls.Add(this.labelKioskTimeout); - this.groupBoxKioskMode.Controls.Add(this.numericUpDownKioskTimeout); - this.groupBoxKioskMode.Controls.Add(this.checkBoxEnableKiosk); - this.groupBoxKioskMode.ForeColor = System.Drawing.Color.Black; - this.groupBoxKioskMode.Location = new System.Drawing.Point(375, 160); - this.groupBoxKioskMode.Name = "groupBoxKioskMode"; - this.groupBoxKioskMode.Size = new System.Drawing.Size(305, 92); - this.groupBoxKioskMode.TabIndex = 22; - this.groupBoxKioskMode.TabStop = false; - this.groupBoxKioskMode.Text = "Kiosk Mode"; + this.labelTimeTableDisplayMode.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.labelTimeTableDisplayMode.AutoEllipsis = true; + this.labelTimeTableDisplayMode.Location = new System.Drawing.Point(3, 17); + this.labelTimeTableDisplayMode.Name = "labelTimeTableDisplayMode"; + this.labelTimeTableDisplayMode.Size = new System.Drawing.Size(153, 18); + this.labelTimeTableDisplayMode.TabIndex = 0; + this.labelTimeTableDisplayMode.Text = "Timetable Display Mode:"; + this.labelTimeTableDisplayMode.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // - // labelKioskTimeout + // groupBoxRailDriver // - this.labelKioskTimeout.Location = new System.Drawing.Point(8, 37); - this.labelKioskTimeout.Name = "labelKioskTimeout"; - this.labelKioskTimeout.Size = new System.Drawing.Size(155, 30); - this.labelKioskTimeout.TabIndex = 2; - this.labelKioskTimeout.Text = "Control timeout (s)"; - this.labelKioskTimeout.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.groupBoxRailDriver.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupBoxRailDriver.Controls.Add(this.labelRailDriverCalibration); + this.groupBoxRailDriver.Controls.Add(this.buttonRailDriverCalibration); + this.groupBoxRailDriver.Controls.Add(this.comboBoxRailDriverUnits); + this.groupBoxRailDriver.Controls.Add(this.labelRailDriverSpeedUnits); + this.groupBoxRailDriver.ForeColor = System.Drawing.Color.Black; + this.groupBoxRailDriver.Location = new System.Drawing.Point(0, 230); + this.groupBoxRailDriver.Name = "groupBoxRailDriver"; + this.groupBoxRailDriver.Size = new System.Drawing.Size(316, 75); + this.groupBoxRailDriver.TabIndex = 21; + this.groupBoxRailDriver.TabStop = false; + this.groupBoxRailDriver.Text = "RailDriver"; // - // numericUpDownKioskTimeout + // labelRailDriverCalibration // - this.numericUpDownKioskTimeout.DecimalPlaces = 2; - this.numericUpDownKioskTimeout.Location = new System.Drawing.Point(166, 41); - this.numericUpDownKioskTimeout.Maximum = new decimal(new int[] { - 10000, - 0, - 0, - 0}); - this.numericUpDownKioskTimeout.Name = "numericUpDownKioskTimeout"; - this.numericUpDownKioskTimeout.Size = new System.Drawing.Size(131, 20); - this.numericUpDownKioskTimeout.TabIndex = 1; + this.labelRailDriverCalibration.AutoSize = true; + this.labelRailDriverCalibration.Location = new System.Drawing.Point(7, 46); + this.labelRailDriverCalibration.Name = "labelRailDriverCalibration"; + this.labelRailDriverCalibration.Size = new System.Drawing.Size(78, 13); + this.labelRailDriverCalibration.TabIndex = 5; + this.labelRailDriverCalibration.Text = "Set Calibration:"; // - // checkBoxEnableKiosk + // buttonRailDriverCalibration // - this.checkBoxEnableKiosk.AutoSize = true; - this.checkBoxEnableKiosk.Location = new System.Drawing.Point(9, 20); - this.checkBoxEnableKiosk.Name = "checkBoxEnableKiosk"; - this.checkBoxEnableKiosk.Size = new System.Drawing.Size(118, 17); - this.checkBoxEnableKiosk.TabIndex = 0; - this.checkBoxEnableKiosk.Text = "Enable Kiosk Mode"; - this.checkBoxEnableKiosk.UseVisualStyleBackColor = true; + this.buttonRailDriverCalibration.BackColor = System.Drawing.SystemColors.ButtonFace; + this.buttonRailDriverCalibration.ForeColor = System.Drawing.SystemColors.ControlText; + this.buttonRailDriverCalibration.Location = new System.Drawing.Point(230, 42); + this.buttonRailDriverCalibration.Name = "buttonRailDriverCalibration"; + this.buttonRailDriverCalibration.Size = new System.Drawing.Size(75, 26); + this.buttonRailDriverCalibration.TabIndex = 4; + this.buttonRailDriverCalibration.Text = "Launch..."; + this.buttonRailDriverCalibration.UseVisualStyleBackColor = true; + this.buttonRailDriverCalibration.Click += new System.EventHandler(this.buttonRailDriverCalibration_Click); // - // groupBoxAdvancedOptions + // comboBoxRailDriverUnits // - this.groupBoxAdvancedOptions.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.groupBoxAdvancedOptions.Controls.Add(this.checkBoxPanel2Extended); - this.groupBoxAdvancedOptions.Controls.Add(this.pictureboxCursor); - this.groupBoxAdvancedOptions.Controls.Add(this.labelCursor); - this.groupBoxAdvancedOptions.Controls.Add(this.comboboxCursor); - this.groupBoxAdvancedOptions.Controls.Add(this.checkBoxHacks); - this.groupBoxAdvancedOptions.Controls.Add(this.checkBoxTransparencyFix); - this.groupBoxAdvancedOptions.Controls.Add(this.checkBoxUnloadTextures); - this.groupBoxAdvancedOptions.Controls.Add(this.labelTimeAcceleration); - this.groupBoxAdvancedOptions.Controls.Add(this.updownTimeAccelerationFactor); - this.groupBoxAdvancedOptions.Controls.Add(this.checkBoxIsUseNewRenderer); - this.groupBoxAdvancedOptions.Controls.Add(this.checkBoxLoadInAdvance); - this.groupBoxAdvancedOptions.ForeColor = System.Drawing.Color.Black; - this.groupBoxAdvancedOptions.Location = new System.Drawing.Point(6, 160); - this.groupBoxAdvancedOptions.Name = "groupBoxAdvancedOptions"; - this.groupBoxAdvancedOptions.Size = new System.Drawing.Size(358, 208); - this.groupBoxAdvancedOptions.TabIndex = 21; - this.groupBoxAdvancedOptions.TabStop = false; - this.groupBoxAdvancedOptions.Text = "Advanced Options"; + this.comboBoxRailDriverUnits.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBoxRailDriverUnits.FormattingEnabled = true; + this.comboBoxRailDriverUnits.Items.AddRange(new object[] { + "Miles per Hour (MPH)", + "Kilometers per Hour (KPH)"}); + this.comboBoxRailDriverUnits.Location = new System.Drawing.Point(140, 16); + this.comboBoxRailDriverUnits.Name = "comboBoxRailDriverUnits"; + this.comboBoxRailDriverUnits.Size = new System.Drawing.Size(165, 21); + this.comboBoxRailDriverUnits.TabIndex = 3; + this.comboBoxRailDriverUnits.SelectedIndexChanged += new System.EventHandler(this.comboBoxRailDriverUnits_SelectedIndexChanged); // - // checkBoxPanel2Extended + // labelRailDriverSpeedUnits // - this.checkBoxPanel2Extended.AutoSize = true; - this.checkBoxPanel2Extended.Location = new System.Drawing.Point(8, 183); - this.checkBoxPanel2Extended.Name = "checkBoxPanel2Extended"; - this.checkBoxPanel2Extended.Size = new System.Drawing.Size(159, 17); - this.checkBoxPanel2Extended.TabIndex = 20; - this.checkBoxPanel2Extended.Text = "Enable Panel2 extend mode"; - this.checkBoxPanel2Extended.UseVisualStyleBackColor = true; + this.labelRailDriverSpeedUnits.Location = new System.Drawing.Point(7, 14); + this.labelRailDriverSpeedUnits.Name = "labelRailDriverSpeedUnits"; + this.labelRailDriverSpeedUnits.Size = new System.Drawing.Size(130, 30); + this.labelRailDriverSpeedUnits.TabIndex = 2; + this.labelRailDriverSpeedUnits.Text = "LED Display speed units:"; + this.labelRailDriverSpeedUnits.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // - // pictureboxCursor + // groupboxDistance // - this.pictureboxCursor.Location = new System.Drawing.Point(8, 145); - this.pictureboxCursor.Name = "pictureboxCursor"; - this.pictureboxCursor.Size = new System.Drawing.Size(32, 32); - this.pictureboxCursor.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; - this.pictureboxCursor.TabIndex = 18; - this.pictureboxCursor.TabStop = false; + this.groupboxDistance.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupboxDistance.Controls.Add(this.comboboxMotionBlur); + this.groupboxDistance.Controls.Add(this.labelMotionBlur); + this.groupboxDistance.Controls.Add(this.labelDistanceUnit); + this.groupboxDistance.Controls.Add(this.updownDistance); + this.groupboxDistance.Controls.Add(this.labelDistance); + this.groupboxDistance.ForeColor = System.Drawing.Color.Black; + this.groupboxDistance.Location = new System.Drawing.Point(0, 0); + this.groupboxDistance.Name = "groupboxDistance"; + this.groupboxDistance.Size = new System.Drawing.Size(316, 80); + this.groupboxDistance.TabIndex = 8; + this.groupboxDistance.TabStop = false; + this.groupboxDistance.Text = "Distance effects"; // - // labelCursor + // comboboxMotionBlur // - this.labelCursor.AutoSize = true; - this.labelCursor.Location = new System.Drawing.Point(48, 140); - this.labelCursor.Name = "labelCursor"; - this.labelCursor.Size = new System.Drawing.Size(37, 13); - this.labelCursor.TabIndex = 17; - this.labelCursor.Text = "Cursor"; + this.comboboxMotionBlur.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.comboboxMotionBlur.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboboxMotionBlur.FormattingEnabled = true; + this.comboboxMotionBlur.Location = new System.Drawing.Point(144, 48); + this.comboboxMotionBlur.Name = "comboboxMotionBlur"; + this.comboboxMotionBlur.Size = new System.Drawing.Size(152, 21); + this.comboboxMotionBlur.TabIndex = 4; // - // comboboxCursor + // labelMotionBlur // - this.comboboxCursor.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.comboboxCursor.FormattingEnabled = true; - this.comboboxCursor.Location = new System.Drawing.Point(48, 158); - this.comboboxCursor.Name = "comboboxCursor"; - this.comboboxCursor.Size = new System.Drawing.Size(108, 21); - this.comboboxCursor.TabIndex = 19; - this.comboboxCursor.SelectedIndexChanged += new System.EventHandler(this.comboboxCursor_SelectedIndexChanged); + this.labelMotionBlur.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.labelMotionBlur.AutoEllipsis = true; + this.labelMotionBlur.Location = new System.Drawing.Point(5, 51); + this.labelMotionBlur.Name = "labelMotionBlur"; + this.labelMotionBlur.Size = new System.Drawing.Size(140, 18); + this.labelMotionBlur.TabIndex = 3; + this.labelMotionBlur.Text = "Motion blur:"; + this.labelMotionBlur.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // - // checkBoxHacks + // labelDistanceUnit // - this.checkBoxHacks.AutoSize = true; - this.checkBoxHacks.Location = new System.Drawing.Point(8, 100); - this.checkBoxHacks.Name = "checkBoxHacks"; - this.checkBoxHacks.Size = new System.Drawing.Size(203, 17); - this.checkBoxHacks.TabIndex = 15; - this.checkBoxHacks.Text = "Enable hacks for buggy older content"; - this.checkBoxHacks.UseVisualStyleBackColor = true; + this.labelDistanceUnit.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.labelDistanceUnit.AutoEllipsis = true; + this.labelDistanceUnit.Location = new System.Drawing.Point(272, 24); + this.labelDistanceUnit.Name = "labelDistanceUnit"; + this.labelDistanceUnit.Size = new System.Drawing.Size(24, 18); + this.labelDistanceUnit.TabIndex = 2; + this.labelDistanceUnit.Text = "m"; // - // checkBoxTransparencyFix + // updownDistance // - this.checkBoxTransparencyFix.AutoSize = true; - this.checkBoxTransparencyFix.Location = new System.Drawing.Point(8, 81); - this.checkBoxTransparencyFix.Name = "checkBoxTransparencyFix"; - this.checkBoxTransparencyFix.Size = new System.Drawing.Size(259, 17); - this.checkBoxTransparencyFix.TabIndex = 14; - this.checkBoxTransparencyFix.Text = "Attempt to fix transparency issues in older content"; - this.checkBoxTransparencyFix.UseVisualStyleBackColor = true; + this.updownDistance.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.updownDistance.Location = new System.Drawing.Point(144, 24); + this.updownDistance.Maximum = new decimal(new int[] { + 100000, + 0, + 0, + 0}); + this.updownDistance.Minimum = new decimal(new int[] { + 100, + 0, + 0, + 0}); + this.updownDistance.Name = "updownDistance"; + this.updownDistance.Size = new System.Drawing.Size(128, 20); + this.updownDistance.TabIndex = 1; + this.updownDistance.Value = new decimal(new int[] { + 600, + 0, + 0, + 0}); // - // checkBoxUnloadTextures + // labelDistance // - this.checkBoxUnloadTextures.AutoSize = true; - this.checkBoxUnloadTextures.Location = new System.Drawing.Point(8, 62); - this.checkBoxUnloadTextures.Name = "checkBoxUnloadTextures"; - this.checkBoxUnloadTextures.Size = new System.Drawing.Size(138, 17); - this.checkBoxUnloadTextures.TabIndex = 13; - this.checkBoxUnloadTextures.Text = "Unload unused textures"; - this.checkBoxUnloadTextures.UseVisualStyleBackColor = true; - this.checkBoxUnloadTextures.CheckedChanged += new System.EventHandler(this.checkBoxUnloadTextures_CheckedChanged); + this.labelDistance.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.labelDistance.AutoEllipsis = true; + this.labelDistance.Location = new System.Drawing.Point(9, 26); + this.labelDistance.Name = "labelDistance"; + this.labelDistance.Size = new System.Drawing.Size(136, 18); + this.labelDistance.TabIndex = 0; + this.labelDistance.Text = "Viewing distance:"; + this.labelDistance.TextAlign = System.Drawing.ContentAlignment.TopRight; // - // labelTimeAcceleration + // groupboxControls // - this.labelTimeAcceleration.AutoSize = true; - this.labelTimeAcceleration.Location = new System.Drawing.Point(8, 123); - this.labelTimeAcceleration.Name = "labelTimeAcceleration"; - this.labelTimeAcceleration.Size = new System.Drawing.Size(126, 13); - this.labelTimeAcceleration.TabIndex = 10; - this.labelTimeAcceleration.Text = "Accelerated Time Factor:"; + this.groupboxControls.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupboxControls.Controls.Add(this.checkBoxEBAxis); + this.groupboxControls.Controls.Add(this.trackbarJoystickAxisThreshold); + this.groupboxControls.Controls.Add(this.checkboxJoysticksUsed); + this.groupboxControls.Controls.Add(this.labelJoystickAxisThreshold); + this.groupboxControls.ForeColor = System.Drawing.Color.Black; + this.groupboxControls.Location = new System.Drawing.Point(0, 144); + this.groupboxControls.Name = "groupboxControls"; + this.groupboxControls.Size = new System.Drawing.Size(316, 80); + this.groupboxControls.TabIndex = 10; + this.groupboxControls.TabStop = false; + this.groupboxControls.Text = "Controls"; // - // updownTimeAccelerationFactor + // checkBoxEBAxis // - this.updownTimeAccelerationFactor.Location = new System.Drawing.Point(200, 122); - this.updownTimeAccelerationFactor.Maximum = new decimal(new int[] { - 5, - 0, - 0, - 0}); - this.updownTimeAccelerationFactor.Name = "updownTimeAccelerationFactor"; - this.updownTimeAccelerationFactor.Size = new System.Drawing.Size(52, 20); - this.updownTimeAccelerationFactor.TabIndex = 16; - this.updownTimeAccelerationFactor.ValueChanged += new System.EventHandler(this.updownTimeAccelerationFactor_ValueChanged); + this.checkBoxEBAxis.Checked = true; + this.checkBoxEBAxis.CheckState = System.Windows.Forms.CheckState.Checked; + this.checkBoxEBAxis.Location = new System.Drawing.Point(8, 41); + this.checkBoxEBAxis.Name = "checkBoxEBAxis"; + this.checkBoxEBAxis.Size = new System.Drawing.Size(190, 36); + this.checkBoxEBAxis.TabIndex = 18; + this.checkBoxEBAxis.Text = "Allow EB on brake axis"; + this.checkBoxEBAxis.UseVisualStyleBackColor = true; // - // checkBoxIsUseNewRenderer + // trackbarJoystickAxisThreshold // - this.checkBoxIsUseNewRenderer.AutoSize = true; - this.checkBoxIsUseNewRenderer.Location = new System.Drawing.Point(8, 43); - this.checkBoxIsUseNewRenderer.Name = "checkBoxIsUseNewRenderer"; - this.checkBoxIsUseNewRenderer.Size = new System.Drawing.Size(159, 17); - this.checkBoxIsUseNewRenderer.TabIndex = 2; - this.checkBoxIsUseNewRenderer.Text = "Disable OpenGL display lists"; - this.checkBoxIsUseNewRenderer.UseVisualStyleBackColor = true; + this.trackbarJoystickAxisThreshold.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.trackbarJoystickAxisThreshold.LargeChange = 10; + this.trackbarJoystickAxisThreshold.Location = new System.Drawing.Point(200, 32); + this.trackbarJoystickAxisThreshold.Maximum = 100; + this.trackbarJoystickAxisThreshold.Name = "trackbarJoystickAxisThreshold"; + this.trackbarJoystickAxisThreshold.Size = new System.Drawing.Size(96, 45); + this.trackbarJoystickAxisThreshold.TabIndex = 2; + this.trackbarJoystickAxisThreshold.TickFrequency = 10; + this.trackbarJoystickAxisThreshold.TickStyle = System.Windows.Forms.TickStyle.Both; // - // checkBoxLoadInAdvance + // checkboxJoysticksUsed // - this.checkBoxLoadInAdvance.AutoSize = true; - this.checkBoxLoadInAdvance.Location = new System.Drawing.Point(8, 24); - this.checkBoxLoadInAdvance.Name = "checkBoxLoadInAdvance"; - this.checkBoxLoadInAdvance.Size = new System.Drawing.Size(106, 17); - this.checkBoxLoadInAdvance.TabIndex = 1; - this.checkBoxLoadInAdvance.Text = "Load in advance"; - this.checkBoxLoadInAdvance.UseVisualStyleBackColor = true; - this.checkBoxLoadInAdvance.CheckedChanged += new System.EventHandler(this.checkBoxLoadInAdvance_CheckedChanged); + this.checkboxJoysticksUsed.AutoSize = true; + this.checkboxJoysticksUsed.Location = new System.Drawing.Point(8, 24); + this.checkboxJoysticksUsed.Name = "checkboxJoysticksUsed"; + this.checkboxJoysticksUsed.Size = new System.Drawing.Size(110, 17); + this.checkboxJoysticksUsed.TabIndex = 0; + this.checkboxJoysticksUsed.Text = "Joysticks enabled"; + this.checkboxJoysticksUsed.UseVisualStyleBackColor = true; + this.checkboxJoysticksUsed.CheckedChanged += new System.EventHandler(this.checkboxJoysticksUsed_CheckedChanged); // - // groupBoxPackageOptions + // labelJoystickAxisThreshold // - this.groupBoxPackageOptions.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + this.labelJoystickAxisThreshold.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.labelJoystickAxisThreshold.AutoEllipsis = true; + this.labelJoystickAxisThreshold.Location = new System.Drawing.Point(110, 10); + this.labelJoystickAxisThreshold.Name = "labelJoystickAxisThreshold"; + this.labelJoystickAxisThreshold.Size = new System.Drawing.Size(180, 18); + this.labelJoystickAxisThreshold.TabIndex = 1; + this.labelJoystickAxisThreshold.Text = "Joystick threshold:"; + this.labelJoystickAxisThreshold.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // groupboxVerbosity + // + this.groupboxVerbosity.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.groupBoxPackageOptions.Controls.Add(this.comboBoxCompressionFormat); - this.groupBoxPackageOptions.Controls.Add(this.labelPackageCompression); - this.groupBoxPackageOptions.Controls.Add(this.buttonOtherDirectory); - this.groupBoxPackageOptions.Controls.Add(this.labelOtherInstallDirectory); - this.groupBoxPackageOptions.Controls.Add(this.textBoxOtherDirectory); - this.groupBoxPackageOptions.Controls.Add(this.buttonTrainInstallationDirectory); - this.groupBoxPackageOptions.Controls.Add(this.labelTrainInstallDirectory); - this.groupBoxPackageOptions.Controls.Add(this.textBoxTrainDirectory); - this.groupBoxPackageOptions.Controls.Add(this.buttonSetRouteDirectory); - this.groupBoxPackageOptions.Controls.Add(this.labelRouteInstallDirectory); - this.groupBoxPackageOptions.Controls.Add(this.textBoxRouteDirectory); - this.groupBoxPackageOptions.ForeColor = System.Drawing.Color.Black; - this.groupBoxPackageOptions.Location = new System.Drawing.Point(6, 0); - this.groupBoxPackageOptions.Name = "groupBoxPackageOptions"; - this.groupBoxPackageOptions.Size = new System.Drawing.Size(674, 154); - this.groupBoxPackageOptions.TabIndex = 19; - this.groupBoxPackageOptions.TabStop = false; - this.groupBoxPackageOptions.Text = "Package Management"; + this.groupboxVerbosity.Controls.Add(this.checkBoxAccessibility); + this.groupboxVerbosity.Controls.Add(this.checkboxErrorMessages); + this.groupboxVerbosity.Controls.Add(this.checkboxWarningMessages); + this.groupboxVerbosity.ForeColor = System.Drawing.Color.Black; + this.groupboxVerbosity.Location = new System.Drawing.Point(0, 396); + this.groupboxVerbosity.Name = "groupboxVerbosity"; + this.groupboxVerbosity.Size = new System.Drawing.Size(316, 64); + this.groupboxVerbosity.TabIndex = 12; + this.groupboxVerbosity.TabStop = false; + this.groupboxVerbosity.Text = "Verbosity"; // - // comboBoxCompressionFormat + // checkBoxAccessibility // - this.comboBoxCompressionFormat.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.comboBoxCompressionFormat.Items.AddRange(new object[] { - "LZMA ZIP ( .zip )", - "GZip ( .tgz )", - "BZip2 ( .bz2 )"}); - this.comboBoxCompressionFormat.Location = new System.Drawing.Point(200, 117); - this.comboBoxCompressionFormat.Name = "comboBoxCompressionFormat"; - this.comboBoxCompressionFormat.Size = new System.Drawing.Size(188, 21); - this.comboBoxCompressionFormat.TabIndex = 10; - this.comboBoxCompressionFormat.SelectedIndexChanged += new System.EventHandler(this.comboBoxCompressionFormat_SelectedIndexChanged); + this.checkBoxAccessibility.AutoSize = true; + this.checkBoxAccessibility.Location = new System.Drawing.Point(176, 21); + this.checkBoxAccessibility.Name = "checkBoxAccessibility"; + this.checkBoxAccessibility.Size = new System.Drawing.Size(106, 17); + this.checkBoxAccessibility.TabIndex = 2; + this.checkBoxAccessibility.Text = "Accessibility Aids"; + this.checkBoxAccessibility.UseVisualStyleBackColor = true; // - // labelPackageCompression + // checkboxErrorMessages // - this.labelPackageCompression.AutoSize = true; - this.labelPackageCompression.Location = new System.Drawing.Point(6, 121); - this.labelPackageCompression.Name = "labelPackageCompression"; - this.labelPackageCompression.Size = new System.Drawing.Size(147, 13); - this.labelPackageCompression.TabIndex = 9; - this.labelPackageCompression.Text = "Package compression format:"; + this.checkboxErrorMessages.AutoSize = true; + this.checkboxErrorMessages.Location = new System.Drawing.Point(8, 38); + this.checkboxErrorMessages.Name = "checkboxErrorMessages"; + this.checkboxErrorMessages.Size = new System.Drawing.Size(127, 17); + this.checkboxErrorMessages.TabIndex = 1; + this.checkboxErrorMessages.Text = "Show error messages"; + this.checkboxErrorMessages.UseVisualStyleBackColor = true; // - // buttonOtherDirectory + // checkboxWarningMessages // - this.buttonOtherDirectory.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.buttonOtherDirectory.BackColor = System.Drawing.SystemColors.Control; - this.buttonOtherDirectory.ForeColor = System.Drawing.SystemColors.ControlText; - this.buttonOtherDirectory.Location = new System.Drawing.Point(594, 81); - this.buttonOtherDirectory.Name = "buttonOtherDirectory"; - this.buttonOtherDirectory.Size = new System.Drawing.Size(75, 26); - this.buttonOtherDirectory.TabIndex = 8; - this.buttonOtherDirectory.Text = "Choose..."; - this.buttonOtherDirectory.UseVisualStyleBackColor = true; - this.buttonOtherDirectory.Click += new System.EventHandler(this.buttonOtherDirectory_Click); + this.checkboxWarningMessages.AutoSize = true; + this.checkboxWarningMessages.Location = new System.Drawing.Point(8, 21); + this.checkboxWarningMessages.Name = "checkboxWarningMessages"; + this.checkboxWarningMessages.Size = new System.Drawing.Size(143, 17); + this.checkboxWarningMessages.TabIndex = 0; + this.checkboxWarningMessages.Text = "Show warning messages"; + this.checkboxWarningMessages.UseVisualStyleBackColor = true; // - // labelOtherInstallDirectory + // groupboxSimulation // - this.labelOtherInstallDirectory.Location = new System.Drawing.Point(6, 80); - this.labelOtherInstallDirectory.Name = "labelOtherInstallDirectory"; - this.labelOtherInstallDirectory.Size = new System.Drawing.Size(175, 30); - this.labelOtherInstallDirectory.TabIndex = 7; - this.labelOtherInstallDirectory.Text = "Other items installation directory:"; - this.labelOtherInstallDirectory.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.groupboxSimulation.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupboxSimulation.Controls.Add(this.checkBoxLoadingSway); + this.groupboxSimulation.Controls.Add(this.checkboxBlackBox); + this.groupboxSimulation.Controls.Add(this.checkboxDerailments); + this.groupboxSimulation.Controls.Add(this.checkboxCollisions); + this.groupboxSimulation.Controls.Add(this.checkboxToppling); + this.groupboxSimulation.ForeColor = System.Drawing.Color.Black; + this.groupboxSimulation.Location = new System.Drawing.Point(0, 310); + this.groupboxSimulation.Name = "groupboxSimulation"; + this.groupboxSimulation.Size = new System.Drawing.Size(316, 80); + this.groupboxSimulation.TabIndex = 11; + this.groupboxSimulation.TabStop = false; + this.groupboxSimulation.Text = "Detail of simulation"; // - // textBoxOtherDirectory + // checkBoxLoadingSway // - this.textBoxOtherDirectory.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + this.checkBoxLoadingSway.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.textBoxOtherDirectory.BackColor = System.Drawing.SystemColors.Control; - this.textBoxOtherDirectory.ForeColor = System.Drawing.SystemColors.ControlText; - this.textBoxOtherDirectory.Location = new System.Drawing.Point(200, 84); - this.textBoxOtherDirectory.Name = "textBoxOtherDirectory"; - this.textBoxOtherDirectory.ReadOnly = true; - this.textBoxOtherDirectory.Size = new System.Drawing.Size(387, 20); - this.textBoxOtherDirectory.TabIndex = 6; + this.checkBoxLoadingSway.AutoSize = true; + this.checkBoxLoadingSway.Location = new System.Drawing.Point(176, 21); + this.checkBoxLoadingSway.Name = "checkBoxLoadingSway"; + this.checkBoxLoadingSway.Size = new System.Drawing.Size(123, 17); + this.checkBoxLoadingSway.TabIndex = 4; + this.checkBoxLoadingSway.Text = "Enable loading sway"; + this.checkBoxLoadingSway.UseVisualStyleBackColor = true; // - // buttonTrainInstallationDirectory + // checkboxBlackBox // - this.buttonTrainInstallationDirectory.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.buttonTrainInstallationDirectory.BackColor = System.Drawing.SystemColors.ButtonFace; - this.buttonTrainInstallationDirectory.ForeColor = System.Drawing.SystemColors.ControlText; - this.buttonTrainInstallationDirectory.Location = new System.Drawing.Point(594, 49); - this.buttonTrainInstallationDirectory.Name = "buttonTrainInstallationDirectory"; - this.buttonTrainInstallationDirectory.Size = new System.Drawing.Size(75, 26); - this.buttonTrainInstallationDirectory.TabIndex = 5; - this.buttonTrainInstallationDirectory.Text = "Choose..."; - this.buttonTrainInstallationDirectory.UseVisualStyleBackColor = true; - this.buttonTrainInstallationDirectory.Click += new System.EventHandler(this.buttonTrainInstallationDirectory_Click); + this.checkboxBlackBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.checkboxBlackBox.AutoSize = true; + this.checkboxBlackBox.Location = new System.Drawing.Point(176, 38); + this.checkboxBlackBox.Name = "checkboxBlackBox"; + this.checkboxBlackBox.Size = new System.Drawing.Size(108, 17); + this.checkboxBlackBox.TabIndex = 3; + this.checkboxBlackBox.Text = "Enable black box"; + this.checkboxBlackBox.UseVisualStyleBackColor = true; // - // labelTrainInstallDirectory + // checkboxDerailments // - this.labelTrainInstallDirectory.AutoSize = true; - this.labelTrainInstallDirectory.Location = new System.Drawing.Point(6, 52); - this.labelTrainInstallDirectory.Name = "labelTrainInstallDirectory"; - this.labelTrainInstallDirectory.Size = new System.Drawing.Size(129, 13); - this.labelTrainInstallDirectory.TabIndex = 4; - this.labelTrainInstallDirectory.Text = "Train installation directory:"; + this.checkboxDerailments.AutoSize = true; + this.checkboxDerailments.Location = new System.Drawing.Point(8, 55); + this.checkboxDerailments.Name = "checkboxDerailments"; + this.checkboxDerailments.Size = new System.Drawing.Size(81, 17); + this.checkboxDerailments.TabIndex = 2; + this.checkboxDerailments.Text = "Derailments"; + this.checkboxDerailments.UseVisualStyleBackColor = true; // - // textBoxTrainDirectory + // checkboxCollisions // - this.textBoxTrainDirectory.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.textBoxTrainDirectory.BackColor = System.Drawing.SystemColors.Control; - this.textBoxTrainDirectory.ForeColor = System.Drawing.SystemColors.ControlText; - this.textBoxTrainDirectory.Location = new System.Drawing.Point(200, 51); - this.textBoxTrainDirectory.Name = "textBoxTrainDirectory"; - this.textBoxTrainDirectory.ReadOnly = true; - this.textBoxTrainDirectory.Size = new System.Drawing.Size(387, 20); - this.textBoxTrainDirectory.TabIndex = 3; + this.checkboxCollisions.AutoSize = true; + this.checkboxCollisions.Location = new System.Drawing.Point(8, 38); + this.checkboxCollisions.Name = "checkboxCollisions"; + this.checkboxCollisions.Size = new System.Drawing.Size(69, 17); + this.checkboxCollisions.TabIndex = 1; + this.checkboxCollisions.Text = "Collisions"; + this.checkboxCollisions.UseVisualStyleBackColor = true; // - // buttonSetRouteDirectory + // checkboxToppling // - this.buttonSetRouteDirectory.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.buttonSetRouteDirectory.BackColor = System.Drawing.SystemColors.ButtonFace; - this.buttonSetRouteDirectory.ForeColor = System.Drawing.SystemColors.ControlText; - this.buttonSetRouteDirectory.Location = new System.Drawing.Point(594, 18); - this.buttonSetRouteDirectory.Name = "buttonSetRouteDirectory"; - this.buttonSetRouteDirectory.Size = new System.Drawing.Size(75, 26); - this.buttonSetRouteDirectory.TabIndex = 2; - this.buttonSetRouteDirectory.Text = "Choose..."; - this.buttonSetRouteDirectory.UseVisualStyleBackColor = true; - this.buttonSetRouteDirectory.Click += new System.EventHandler(this.buttonSetRouteDirectory_Click); + this.checkboxToppling.AutoSize = true; + this.checkboxToppling.Location = new System.Drawing.Point(8, 21); + this.checkboxToppling.Name = "checkboxToppling"; + this.checkboxToppling.Size = new System.Drawing.Size(67, 17); + this.checkboxToppling.TabIndex = 0; + this.checkboxToppling.Text = "Toppling"; + this.checkboxToppling.UseVisualStyleBackColor = true; // - // labelRouteInstallDirectory + // groupboxSound // - this.labelRouteInstallDirectory.AutoSize = true; - this.labelRouteInstallDirectory.Location = new System.Drawing.Point(6, 21); - this.labelRouteInstallDirectory.Name = "labelRouteInstallDirectory"; - this.labelRouteInstallDirectory.Size = new System.Drawing.Size(134, 13); - this.labelRouteInstallDirectory.TabIndex = 1; - this.labelRouteInstallDirectory.Text = "Route installation directory:"; + this.groupboxSound.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupboxSound.Controls.Add(this.updownSoundNumber); + this.groupboxSound.Controls.Add(this.labelSoundNumber); + this.groupboxSound.ForeColor = System.Drawing.Color.Black; + this.groupboxSound.Location = new System.Drawing.Point(0, 88); + this.groupboxSound.Name = "groupboxSound"; + this.groupboxSound.Size = new System.Drawing.Size(316, 48); + this.groupboxSound.TabIndex = 9; + this.groupboxSound.TabStop = false; + this.groupboxSound.Text = "Sound"; // - // textBoxRouteDirectory + // updownSoundNumber // - this.textBoxRouteDirectory.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + this.updownSoundNumber.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.updownSoundNumber.Location = new System.Drawing.Point(140, 16); + this.updownSoundNumber.Maximum = new decimal(new int[] { + 128, + 0, + 0, + 0}); + this.updownSoundNumber.Minimum = new decimal(new int[] { + 8, + 0, + 0, + 0}); + this.updownSoundNumber.Name = "updownSoundNumber"; + this.updownSoundNumber.Size = new System.Drawing.Size(152, 20); + this.updownSoundNumber.TabIndex = 3; + this.updownSoundNumber.Value = new decimal(new int[] { + 16, + 0, + 0, + 0}); + // + // labelSoundNumber + // + this.labelSoundNumber.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.textBoxRouteDirectory.BackColor = System.Drawing.SystemColors.Control; - this.textBoxRouteDirectory.ForeColor = System.Drawing.SystemColors.ControlText; - this.textBoxRouteDirectory.Location = new System.Drawing.Point(200, 20); - this.textBoxRouteDirectory.Name = "textBoxRouteDirectory"; - this.textBoxRouteDirectory.ReadOnly = true; - this.textBoxRouteDirectory.Size = new System.Drawing.Size(387, 20); - this.textBoxRouteDirectory.TabIndex = 0; + this.labelSoundNumber.Location = new System.Drawing.Point(5, 18); + this.labelSoundNumber.Name = "labelSoundNumber"; + this.labelSoundNumber.Size = new System.Drawing.Size(136, 18); + this.labelSoundNumber.TabIndex = 2; + this.labelSoundNumber.Text = "Number of allowed sounds:"; + this.labelSoundNumber.TextAlign = System.Drawing.ContentAlignment.TopRight; // // pictureboxLanguage // @@ -3032,7 +3072,7 @@ private void InitializeComponent() { // this.labelFillerThree.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.labelFillerThree.BackColor = System.Drawing.Color.Silver; - this.labelFillerThree.Location = new System.Drawing.Point(0, 583); + this.labelFillerThree.Location = new System.Drawing.Point(0, 613); this.labelFillerThree.Name = "labelFillerThree"; this.labelFillerThree.Size = new System.Drawing.Size(160, 48); this.labelFillerThree.TabIndex = 4; @@ -3153,7 +3193,7 @@ private void InitializeComponent() { this.panelReview.Controls.Add(this.labelReviewTitleBackground); this.panelReview.Location = new System.Drawing.Point(160, 0); this.panelReview.Name = "panelReview"; - this.panelReview.Size = new System.Drawing.Size(699, 631); + this.panelReview.Size = new System.Drawing.Size(699, 661); this.panelReview.TabIndex = 10; // // comboboxBlackBoxFormat @@ -3161,7 +3201,7 @@ private void InitializeComponent() { this.comboboxBlackBoxFormat.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.comboboxBlackBoxFormat.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboboxBlackBoxFormat.FormattingEnabled = true; - this.comboboxBlackBoxFormat.Location = new System.Drawing.Point(104, 599); + this.comboboxBlackBoxFormat.Location = new System.Drawing.Point(104, 629); this.comboboxBlackBoxFormat.Name = "comboboxBlackBoxFormat"; this.comboboxBlackBoxFormat.Size = new System.Drawing.Size(144, 21); this.comboboxBlackBoxFormat.TabIndex = 12; @@ -3171,7 +3211,7 @@ private void InitializeComponent() { this.labelBlackBoxFormat.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.labelBlackBoxFormat.AutoEllipsis = true; this.labelBlackBoxFormat.ForeColor = System.Drawing.Color.Black; - this.labelBlackBoxFormat.Location = new System.Drawing.Point(8, 602); + this.labelBlackBoxFormat.Location = new System.Drawing.Point(8, 632); this.labelBlackBoxFormat.Name = "labelBlackBoxFormat"; this.labelBlackBoxFormat.Size = new System.Drawing.Size(96, 18); this.labelBlackBoxFormat.TabIndex = 11; @@ -3238,7 +3278,7 @@ private void InitializeComponent() { // this.buttonBlackBoxExport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.buttonBlackBoxExport.BackColor = System.Drawing.SystemColors.ButtonFace; - this.buttonBlackBoxExport.Location = new System.Drawing.Point(256, 597); + this.buttonBlackBoxExport.Location = new System.Drawing.Point(256, 627); this.buttonBlackBoxExport.Name = "buttonBlackBoxExport"; this.buttonBlackBoxExport.Size = new System.Drawing.Size(120, 26); this.buttonBlackBoxExport.TabIndex = 13; @@ -3254,7 +3294,7 @@ private void InitializeComponent() { this.labelBlackBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(153)))), ((int)(((byte)(107)))), ((int)(((byte)(107))))); this.labelBlackBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelBlackBox.ForeColor = System.Drawing.Color.White; - this.labelBlackBox.Location = new System.Drawing.Point(8, 567); + this.labelBlackBox.Location = new System.Drawing.Point(8, 597); this.labelBlackBox.Name = "labelBlackBox"; this.labelBlackBox.Size = new System.Drawing.Size(683, 24); this.labelBlackBox.TabIndex = 10; @@ -3272,7 +3312,7 @@ private void InitializeComponent() { this.groupboxScore.ForeColor = System.Drawing.Color.Black; this.groupboxScore.Location = new System.Drawing.Point(272, 176); this.groupboxScore.Name = "groupboxScore"; - this.groupboxScore.Size = new System.Drawing.Size(419, 383); + this.groupboxScore.Size = new System.Drawing.Size(419, 413); this.groupboxScore.TabIndex = 0; this.groupboxScore.TabStop = false; this.groupboxScore.Text = "Log"; @@ -3281,7 +3321,7 @@ private void InitializeComponent() { // this.checkboxScorePenalties.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.checkboxScorePenalties.AutoSize = true; - this.checkboxScorePenalties.Location = new System.Drawing.Point(8, 356); + this.checkboxScorePenalties.Location = new System.Drawing.Point(8, 386); this.checkboxScorePenalties.Name = "checkboxScorePenalties"; this.checkboxScorePenalties.Size = new System.Drawing.Size(120, 17); this.checkboxScorePenalties.TabIndex = 1; @@ -3294,7 +3334,7 @@ private void InitializeComponent() { this.buttonScoreExport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.buttonScoreExport.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonScoreExport.ForeColor = System.Drawing.SystemColors.ControlText; - this.buttonScoreExport.Location = new System.Drawing.Point(291, 350); + this.buttonScoreExport.Location = new System.Drawing.Point(291, 380); this.buttonScoreExport.Name = "buttonScoreExport"; this.buttonScoreExport.Size = new System.Drawing.Size(120, 26); this.buttonScoreExport.TabIndex = 2; @@ -3321,7 +3361,7 @@ private void InitializeComponent() { this.listviewScore.MultiSelect = false; this.listviewScore.Name = "listviewScore"; this.listviewScore.ShowGroups = false; - this.listviewScore.Size = new System.Drawing.Size(403, 327); + this.listviewScore.Size = new System.Drawing.Size(403, 357); this.listviewScore.TabIndex = 0; this.listviewScore.UseCompatibleStateImageBehavior = false; this.listviewScore.View = System.Windows.Forms.View.Details; @@ -3632,14 +3672,14 @@ private void InitializeComponent() { this.panelControls.Controls.Add(this.groupboxControl); this.panelControls.Location = new System.Drawing.Point(160, 0); this.panelControls.Name = "panelControls"; - this.panelControls.Size = new System.Drawing.Size(699, 631); + this.panelControls.Size = new System.Drawing.Size(699, 661); this.panelControls.TabIndex = 13; // // buttonControlReset // this.buttonControlReset.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.buttonControlReset.BackColor = System.Drawing.SystemColors.ButtonFace; - this.buttonControlReset.Location = new System.Drawing.Point(8, 293); + this.buttonControlReset.Location = new System.Drawing.Point(8, 323); this.buttonControlReset.Name = "buttonControlReset"; this.buttonControlReset.Size = new System.Drawing.Size(96, 26); this.buttonControlReset.TabIndex = 12; @@ -3651,7 +3691,7 @@ private void InitializeComponent() { // this.buttonControlsExport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.buttonControlsExport.BackColor = System.Drawing.SystemColors.ButtonFace; - this.buttonControlsExport.Location = new System.Drawing.Point(320, 262); + this.buttonControlsExport.Location = new System.Drawing.Point(320, 292); this.buttonControlsExport.Name = "buttonControlsExport"; this.buttonControlsExport.Size = new System.Drawing.Size(96, 26); this.buttonControlsExport.TabIndex = 7; @@ -3663,7 +3703,7 @@ private void InitializeComponent() { // this.buttonControlsImport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.buttonControlsImport.BackColor = System.Drawing.SystemColors.ButtonFace; - this.buttonControlsImport.Location = new System.Drawing.Point(216, 262); + this.buttonControlsImport.Location = new System.Drawing.Point(216, 292); this.buttonControlsImport.Name = "buttonControlsImport"; this.buttonControlsImport.Size = new System.Drawing.Size(96, 26); this.buttonControlsImport.TabIndex = 6; @@ -3675,7 +3715,7 @@ private void InitializeComponent() { // this.buttonControlDown.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.buttonControlDown.BackColor = System.Drawing.SystemColors.ButtonFace; - this.buttonControlDown.Location = new System.Drawing.Point(595, 262); + this.buttonControlDown.Location = new System.Drawing.Point(595, 292); this.buttonControlDown.Name = "buttonControlDown"; this.buttonControlDown.Size = new System.Drawing.Size(96, 26); this.buttonControlDown.TabIndex = 9; @@ -3687,7 +3727,7 @@ private void InitializeComponent() { // this.buttonControlUp.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.buttonControlUp.BackColor = System.Drawing.SystemColors.ButtonFace; - this.buttonControlUp.Location = new System.Drawing.Point(491, 262); + this.buttonControlUp.Location = new System.Drawing.Point(491, 292); this.buttonControlUp.Name = "buttonControlUp"; this.buttonControlUp.Size = new System.Drawing.Size(96, 26); this.buttonControlUp.TabIndex = 8; @@ -3699,7 +3739,7 @@ private void InitializeComponent() { // this.buttonControlRemove.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.buttonControlRemove.BackColor = System.Drawing.SystemColors.ButtonFace; - this.buttonControlRemove.Location = new System.Drawing.Point(112, 262); + this.buttonControlRemove.Location = new System.Drawing.Point(112, 292); this.buttonControlRemove.Name = "buttonControlRemove"; this.buttonControlRemove.Size = new System.Drawing.Size(96, 26); this.buttonControlRemove.TabIndex = 5; @@ -3711,7 +3751,7 @@ private void InitializeComponent() { // this.buttonControlAdd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.buttonControlAdd.BackColor = System.Drawing.SystemColors.ButtonFace; - this.buttonControlAdd.Location = new System.Drawing.Point(8, 262); + this.buttonControlAdd.Location = new System.Drawing.Point(8, 292); this.buttonControlAdd.Name = "buttonControlAdd"; this.buttonControlAdd.Size = new System.Drawing.Size(96, 26); this.buttonControlAdd.TabIndex = 4; @@ -3725,7 +3765,7 @@ private void InitializeComponent() { | System.Windows.Forms.AnchorStyles.Right))); this.groupboxJoysticks.Controls.Add(this.pictureboxJoysticks); this.groupboxJoysticks.ForeColor = System.Drawing.Color.Black; - this.groupboxJoysticks.Location = new System.Drawing.Point(8, 455); + this.groupboxJoysticks.Location = new System.Drawing.Point(8, 485); this.groupboxJoysticks.Name = "groupboxJoysticks"; this.groupboxJoysticks.Size = new System.Drawing.Size(683, 168); this.groupboxJoysticks.TabIndex = 11; @@ -3764,7 +3804,7 @@ private void InitializeComponent() { this.listviewControls.MultiSelect = false; this.listviewControls.Name = "listviewControls"; this.listviewControls.ShowGroups = false; - this.listviewControls.Size = new System.Drawing.Size(683, 213); + this.listviewControls.Size = new System.Drawing.Size(683, 243); this.listviewControls.TabIndex = 3; this.listviewControls.UseCompatibleStateImageBehavior = false; this.listviewControls.View = System.Windows.Forms.View.Details; @@ -3839,7 +3879,7 @@ private void InitializeComponent() { this.groupboxControl.Controls.Add(this.radiobuttonKeyboard); this.groupboxControl.Enabled = false; this.groupboxControl.ForeColor = System.Drawing.Color.Black; - this.groupboxControl.Location = new System.Drawing.Point(8, 319); + this.groupboxControl.Location = new System.Drawing.Point(8, 349); this.groupboxControl.Name = "groupboxControl"; this.groupboxControl.Size = new System.Drawing.Size(683, 128); this.groupboxControl.TabIndex = 10; @@ -4048,7 +4088,7 @@ private void InitializeComponent() { this.panelInfo.Controls.Add(this.labelVersion); this.panelInfo.Controls.Add(this.labelInfoBottom); this.panelInfo.Controls.Add(this.labelInfoTop); - this.panelInfo.Location = new System.Drawing.Point(0, 487); + this.panelInfo.Location = new System.Drawing.Point(0, 517); this.panelInfo.Name = "panelInfo"; this.panelInfo.Size = new System.Drawing.Size(160, 96); this.panelInfo.TabIndex = 3; @@ -4172,7 +4212,7 @@ private void InitializeComponent() { this.panelPackages.Controls.Add(this.panelCreatePackage); this.panelPackages.Location = new System.Drawing.Point(160, 0); this.panelPackages.Name = "panelPackages"; - this.panelPackages.Size = new System.Drawing.Size(699, 631); + this.panelPackages.Size = new System.Drawing.Size(699, 661); this.panelPackages.TabIndex = 14; // // labelPackagesTitleSeparator @@ -4234,7 +4274,7 @@ private void InitializeComponent() { this.panelPackageInstall.Controls.Add(this.pictureBoxPackageImage); this.panelPackageInstall.Location = new System.Drawing.Point(0, 34); this.panelPackageInstall.Name = "panelPackageInstall"; - this.panelPackageInstall.Size = new System.Drawing.Size(699, 597); + this.panelPackageInstall.Size = new System.Drawing.Size(699, 627); this.panelPackageInstall.TabIndex = 4; this.panelPackageInstall.DragDrop += new System.Windows.Forms.DragEventHandler(this.panelPackageInstall_DragDrop); this.panelPackageInstall.DragEnter += new System.Windows.Forms.DragEventHandler(this.panelPackageInstall_DragEnter); @@ -4245,7 +4285,7 @@ private void InitializeComponent() { this.buttonBack2.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonBack2.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.buttonBack2.ForeColor = System.Drawing.SystemColors.ControlText; - this.buttonBack2.Location = new System.Drawing.Point(445, 561); + this.buttonBack2.Location = new System.Drawing.Point(445, 591); this.buttonBack2.Name = "buttonBack2"; this.buttonBack2.Size = new System.Drawing.Size(120, 28); this.buttonBack2.TabIndex = 18; @@ -4260,7 +4300,7 @@ private void InitializeComponent() { this.buttonNext.Enabled = false; this.buttonNext.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.buttonNext.ForeColor = System.Drawing.SystemColors.ControlText; - this.buttonNext.Location = new System.Drawing.Point(571, 561); + this.buttonNext.Location = new System.Drawing.Point(571, 591); this.buttonNext.Name = "buttonNext"; this.buttonNext.Size = new System.Drawing.Size(120, 28); this.buttonNext.TabIndex = 17; @@ -4297,7 +4337,7 @@ private void InitializeComponent() { this.buttonSelectPackage.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonSelectPackage.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.buttonSelectPackage.ForeColor = System.Drawing.SystemColors.ControlText; - this.buttonSelectPackage.Location = new System.Drawing.Point(134, 535); + this.buttonSelectPackage.Location = new System.Drawing.Point(134, 565); this.buttonSelectPackage.Name = "buttonSelectPackage"; this.buttonSelectPackage.Size = new System.Drawing.Size(180, 28); this.buttonSelectPackage.TabIndex = 14; @@ -4311,7 +4351,7 @@ private void InitializeComponent() { | System.Windows.Forms.AnchorStyles.Right))); this.textBoxPackageDescription.BackColor = System.Drawing.SystemColors.Control; this.textBoxPackageDescription.ForeColor = System.Drawing.SystemColors.ControlText; - this.textBoxPackageDescription.Location = new System.Drawing.Point(134, 417); + this.textBoxPackageDescription.Location = new System.Drawing.Point(134, 447); this.textBoxPackageDescription.Multiline = true; this.textBoxPackageDescription.Name = "textBoxPackageDescription"; this.textBoxPackageDescription.ReadOnly = true; @@ -4324,7 +4364,7 @@ private void InitializeComponent() { // this.labelPackageDescription.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.labelPackageDescription.ForeColor = System.Drawing.Color.Black; - this.labelPackageDescription.Location = new System.Drawing.Point(8, 420); + this.labelPackageDescription.Location = new System.Drawing.Point(8, 450); this.labelPackageDescription.Name = "labelPackageDescription"; this.labelPackageDescription.Size = new System.Drawing.Size(120, 18); this.labelPackageDescription.TabIndex = 12; @@ -4335,7 +4375,7 @@ private void InitializeComponent() { // this.linkLabelPackageWebsite.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.linkLabelPackageWebsite.AutoSize = true; - this.linkLabelPackageWebsite.Location = new System.Drawing.Point(134, 397); + this.linkLabelPackageWebsite.Location = new System.Drawing.Point(134, 427); this.linkLabelPackageWebsite.Name = "linkLabelPackageWebsite"; this.linkLabelPackageWebsite.Size = new System.Drawing.Size(112, 13); this.linkLabelPackageWebsite.TabIndex = 11; @@ -4347,7 +4387,7 @@ private void InitializeComponent() { // this.labelPackageWebsite.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.labelPackageWebsite.ForeColor = System.Drawing.Color.Black; - this.labelPackageWebsite.Location = new System.Drawing.Point(8, 397); + this.labelPackageWebsite.Location = new System.Drawing.Point(8, 427); this.labelPackageWebsite.Name = "labelPackageWebsite"; this.labelPackageWebsite.Size = new System.Drawing.Size(120, 18); this.labelPackageWebsite.TabIndex = 10; @@ -4360,7 +4400,7 @@ private void InitializeComponent() { | System.Windows.Forms.AnchorStyles.Right))); this.textBoxPackageVersion.BackColor = System.Drawing.SystemColors.Control; this.textBoxPackageVersion.ForeColor = System.Drawing.SystemColors.ControlText; - this.textBoxPackageVersion.Location = new System.Drawing.Point(134, 369); + this.textBoxPackageVersion.Location = new System.Drawing.Point(134, 399); this.textBoxPackageVersion.Name = "textBoxPackageVersion"; this.textBoxPackageVersion.Size = new System.Drawing.Size(423, 20); this.textBoxPackageVersion.TabIndex = 9; @@ -4370,7 +4410,7 @@ private void InitializeComponent() { // this.labelPackageVersion.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.labelPackageVersion.ForeColor = System.Drawing.Color.Black; - this.labelPackageVersion.Location = new System.Drawing.Point(8, 372); + this.labelPackageVersion.Location = new System.Drawing.Point(8, 402); this.labelPackageVersion.Name = "labelPackageVersion"; this.labelPackageVersion.Size = new System.Drawing.Size(120, 18); this.labelPackageVersion.TabIndex = 8; @@ -4383,7 +4423,7 @@ private void InitializeComponent() { | System.Windows.Forms.AnchorStyles.Right))); this.textBoxPackageAuthor.BackColor = System.Drawing.SystemColors.Control; this.textBoxPackageAuthor.ForeColor = System.Drawing.SystemColors.ControlText; - this.textBoxPackageAuthor.Location = new System.Drawing.Point(134, 345); + this.textBoxPackageAuthor.Location = new System.Drawing.Point(134, 375); this.textBoxPackageAuthor.Name = "textBoxPackageAuthor"; this.textBoxPackageAuthor.Size = new System.Drawing.Size(423, 20); this.textBoxPackageAuthor.TabIndex = 7; @@ -4393,7 +4433,7 @@ private void InitializeComponent() { // this.labelPackageAuthor.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.labelPackageAuthor.ForeColor = System.Drawing.Color.Black; - this.labelPackageAuthor.Location = new System.Drawing.Point(8, 348); + this.labelPackageAuthor.Location = new System.Drawing.Point(8, 378); this.labelPackageAuthor.Name = "labelPackageAuthor"; this.labelPackageAuthor.Size = new System.Drawing.Size(120, 18); this.labelPackageAuthor.TabIndex = 6; @@ -4406,7 +4446,7 @@ private void InitializeComponent() { | System.Windows.Forms.AnchorStyles.Right))); this.textBoxPackageName.BackColor = System.Drawing.SystemColors.Control; this.textBoxPackageName.ForeColor = System.Drawing.SystemColors.ControlText; - this.textBoxPackageName.Location = new System.Drawing.Point(134, 321); + this.textBoxPackageName.Location = new System.Drawing.Point(134, 351); this.textBoxPackageName.Name = "textBoxPackageName"; this.textBoxPackageName.Size = new System.Drawing.Size(423, 20); this.textBoxPackageName.TabIndex = 5; @@ -4416,7 +4456,7 @@ private void InitializeComponent() { // this.labelPackageName.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.labelPackageName.ForeColor = System.Drawing.Color.Black; - this.labelPackageName.Location = new System.Drawing.Point(8, 324); + this.labelPackageName.Location = new System.Drawing.Point(8, 354); this.labelPackageName.Name = "labelPackageName"; this.labelPackageName.Size = new System.Drawing.Size(120, 18); this.labelPackageName.TabIndex = 4; @@ -4430,7 +4470,7 @@ private void InitializeComponent() { | System.Windows.Forms.AnchorStyles.Right))); this.pictureBoxPackageImage.Location = new System.Drawing.Point(8, 54); this.pictureBoxPackageImage.Name = "pictureBoxPackageImage"; - this.pictureBoxPackageImage.Size = new System.Drawing.Size(683, 241); + this.pictureBoxPackageImage.Size = new System.Drawing.Size(683, 271); this.pictureBoxPackageImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; this.pictureBoxPackageImage.TabIndex = 3; this.pictureBoxPackageImage.TabStop = false; @@ -4449,7 +4489,7 @@ private void InitializeComponent() { this.panelUninstallResult.Controls.Add(this.buttonUninstallFinish); this.panelUninstallResult.Location = new System.Drawing.Point(0, 34); this.panelUninstallResult.Name = "panelUninstallResult"; - this.panelUninstallResult.Size = new System.Drawing.Size(699, 597); + this.panelUninstallResult.Size = new System.Drawing.Size(699, 627); this.panelUninstallResult.TabIndex = 25; // // textBoxUninstallResult @@ -4464,7 +4504,7 @@ private void InitializeComponent() { this.textBoxUninstallResult.Name = "textBoxUninstallResult"; this.textBoxUninstallResult.ReadOnly = true; this.textBoxUninstallResult.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; - this.textBoxUninstallResult.Size = new System.Drawing.Size(683, 438); + this.textBoxUninstallResult.Size = new System.Drawing.Size(683, 468); this.textBoxUninstallResult.TabIndex = 19; // // labelUninstallLog @@ -4516,7 +4556,7 @@ private void InitializeComponent() { this.buttonUninstallFinish.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonUninstallFinish.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.buttonUninstallFinish.ForeColor = System.Drawing.SystemColors.ControlText; - this.buttonUninstallFinish.Location = new System.Drawing.Point(276, 543); + this.buttonUninstallFinish.Location = new System.Drawing.Point(276, 573); this.buttonUninstallFinish.Name = "buttonUninstallFinish"; this.buttonUninstallFinish.Size = new System.Drawing.Size(146, 40); this.buttonUninstallFinish.TabIndex = 14; @@ -4537,7 +4577,7 @@ private void InitializeComponent() { this.panelSuccess.Controls.Add(this.buttonInstallFinish); this.panelSuccess.Location = new System.Drawing.Point(0, 34); this.panelSuccess.Name = "panelSuccess"; - this.panelSuccess.Size = new System.Drawing.Size(699, 597); + this.panelSuccess.Size = new System.Drawing.Size(699, 627); this.panelSuccess.TabIndex = 23; // // textBoxFilesInstalled @@ -4552,7 +4592,7 @@ private void InitializeComponent() { this.textBoxFilesInstalled.Name = "textBoxFilesInstalled"; this.textBoxFilesInstalled.ReadOnly = true; this.textBoxFilesInstalled.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; - this.textBoxFilesInstalled.Size = new System.Drawing.Size(683, 438); + this.textBoxFilesInstalled.Size = new System.Drawing.Size(683, 468); this.textBoxFilesInstalled.TabIndex = 19; // // labelListFilesInstalled @@ -4604,7 +4644,7 @@ private void InitializeComponent() { this.buttonInstallFinish.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonInstallFinish.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.buttonInstallFinish.ForeColor = System.Drawing.SystemColors.ControlText; - this.buttonInstallFinish.Location = new System.Drawing.Point(276, 543); + this.buttonInstallFinish.Location = new System.Drawing.Point(276, 573); this.buttonInstallFinish.Name = "buttonInstallFinish"; this.buttonInstallFinish.Size = new System.Drawing.Size(146, 40); this.buttonInstallFinish.TabIndex = 14; @@ -4626,14 +4666,14 @@ private void InitializeComponent() { this.panelDependancyError.Controls.Add(this.buttonProceedAnyway); this.panelDependancyError.Location = new System.Drawing.Point(0, 34); this.panelDependancyError.Name = "panelDependancyError"; - this.panelDependancyError.Size = new System.Drawing.Size(699, 597); + this.panelDependancyError.Size = new System.Drawing.Size(699, 627); this.panelDependancyError.TabIndex = 5; // // buttonAbort // this.buttonAbort.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.buttonAbort.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.buttonAbort.Location = new System.Drawing.Point(445, 565); + this.buttonAbort.Location = new System.Drawing.Point(445, 595); this.buttonAbort.Name = "buttonAbort"; this.buttonAbort.Size = new System.Drawing.Size(120, 24); this.buttonAbort.TabIndex = 21; @@ -4666,7 +4706,7 @@ private void InitializeComponent() { this.dataGridViewDependancies.RowHeadersVisible = false; this.dataGridViewDependancies.RowHeadersWidth = 90; this.dataGridViewDependancies.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; - this.dataGridViewDependancies.Size = new System.Drawing.Size(683, 438); + this.dataGridViewDependancies.Size = new System.Drawing.Size(683, 468); this.dataGridViewDependancies.TabIndex = 20; this.dataGridViewDependancies.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridViewDependancies_CellContentClick); // @@ -4750,7 +4790,7 @@ private void InitializeComponent() { // this.buttonProceedAnyway.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.buttonProceedAnyway.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.buttonProceedAnyway.Location = new System.Drawing.Point(571, 565); + this.buttonProceedAnyway.Location = new System.Drawing.Point(571, 595); this.buttonProceedAnyway.Name = "buttonProceedAnyway"; this.buttonProceedAnyway.Size = new System.Drawing.Size(120, 24); this.buttonProceedAnyway.TabIndex = 14; @@ -4769,7 +4809,7 @@ private void InitializeComponent() { this.panelPleaseWait.Controls.Add(this.pictureBoxProcessing); this.panelPleaseWait.Location = new System.Drawing.Point(0, 34); this.panelPleaseWait.Name = "panelPleaseWait"; - this.panelPleaseWait.Size = new System.Drawing.Size(699, 597); + this.panelPleaseWait.Size = new System.Drawing.Size(699, 627); this.panelPleaseWait.TabIndex = 28; // // labelProgressFile @@ -4777,7 +4817,7 @@ private void InitializeComponent() { this.labelProgressFile.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); this.labelProgressFile.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelProgressFile.ForeColor = System.Drawing.Color.Black; - this.labelProgressFile.Location = new System.Drawing.Point(1, 431); + this.labelProgressFile.Location = new System.Drawing.Point(1, 446); this.labelProgressFile.Name = "labelProgressFile"; this.labelProgressFile.Size = new System.Drawing.Size(697, 30); this.labelProgressFile.TabIndex = 7; @@ -4789,7 +4829,7 @@ private void InitializeComponent() { this.labelProgressPercent.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); this.labelProgressPercent.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelProgressPercent.ForeColor = System.Drawing.Color.Black; - this.labelProgressPercent.Location = new System.Drawing.Point(290, 401); + this.labelProgressPercent.Location = new System.Drawing.Point(290, 416); this.labelProgressPercent.Name = "labelProgressPercent"; this.labelProgressPercent.Size = new System.Drawing.Size(118, 30); this.labelProgressPercent.TabIndex = 6; @@ -4801,7 +4841,7 @@ private void InitializeComponent() { this.labelPleaseWait.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); this.labelPleaseWait.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelPleaseWait.ForeColor = System.Drawing.Color.Black; - this.labelPleaseWait.Location = new System.Drawing.Point(1, 462); + this.labelPleaseWait.Location = new System.Drawing.Point(1, 477); this.labelPleaseWait.Name = "labelPleaseWait"; this.labelPleaseWait.Size = new System.Drawing.Size(697, 30); this.labelPleaseWait.TabIndex = 5; @@ -4811,7 +4851,7 @@ private void InitializeComponent() { // pictureBoxProcessing // this.pictureBoxProcessing.Anchor = System.Windows.Forms.AnchorStyles.None; - this.pictureBoxProcessing.Location = new System.Drawing.Point(174, 46); + this.pictureBoxProcessing.Location = new System.Drawing.Point(174, 61); this.pictureBoxProcessing.Name = "pictureBoxProcessing"; this.pictureBoxProcessing.Size = new System.Drawing.Size(350, 350); this.pictureBoxProcessing.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; @@ -4832,7 +4872,7 @@ private void InitializeComponent() { this.panelPackageDependsAdd.Controls.Add(this.splitContainerDependancies); this.panelPackageDependsAdd.Location = new System.Drawing.Point(0, 34); this.panelPackageDependsAdd.Name = "panelPackageDependsAdd"; - this.panelPackageDependsAdd.Size = new System.Drawing.Size(699, 597); + this.panelPackageDependsAdd.Size = new System.Drawing.Size(699, 627); this.panelPackageDependsAdd.TabIndex = 26; // // buttonBack @@ -4841,7 +4881,7 @@ private void InitializeComponent() { this.buttonBack.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonBack.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.buttonBack.ForeColor = System.Drawing.SystemColors.ControlText; - this.buttonBack.Location = new System.Drawing.Point(445, 561); + this.buttonBack.Location = new System.Drawing.Point(445, 591); this.buttonBack.Name = "buttonBack"; this.buttonBack.Size = new System.Drawing.Size(120, 28); this.buttonBack.TabIndex = 28; @@ -4855,7 +4895,7 @@ private void InitializeComponent() { this.buttonRemove.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonRemove.Enabled = false; this.buttonRemove.ForeColor = System.Drawing.SystemColors.ControlText; - this.buttonRemove.Location = new System.Drawing.Point(8, 525); + this.buttonRemove.Location = new System.Drawing.Point(8, 555); this.buttonRemove.Name = "buttonRemove"; this.buttonRemove.Size = new System.Drawing.Size(166, 26); this.buttonRemove.TabIndex = 25; @@ -4867,7 +4907,7 @@ private void InitializeComponent() { // this.labelNoDependencyReminder.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.labelNoDependencyReminder.ForeColor = System.Drawing.Color.Black; - this.labelNoDependencyReminder.Location = new System.Drawing.Point(8, 560); + this.labelNoDependencyReminder.Location = new System.Drawing.Point(8, 590); this.labelNoDependencyReminder.Name = "labelNoDependencyReminder"; this.labelNoDependencyReminder.Size = new System.Drawing.Size(400, 48); this.labelNoDependencyReminder.TabIndex = 24; @@ -4903,7 +4943,7 @@ private void InitializeComponent() { this.buttonCreatePackage.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonCreatePackage.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.buttonCreatePackage.ForeColor = System.Drawing.SystemColors.ControlText; - this.buttonCreatePackage.Location = new System.Drawing.Point(571, 561); + this.buttonCreatePackage.Location = new System.Drawing.Point(571, 591); this.buttonCreatePackage.Name = "buttonCreatePackage"; this.buttonCreatePackage.Size = new System.Drawing.Size(120, 28); this.buttonCreatePackage.TabIndex = 14; @@ -4933,8 +4973,8 @@ private void InitializeComponent() { this.splitContainerDependancies.Panel2.Controls.Add(this.buttonReccomends); this.splitContainerDependancies.Panel2.Controls.Add(this.labelSelectedDependencies); this.splitContainerDependancies.Panel2.Controls.Add(this.buttonDepends); - this.splitContainerDependancies.Size = new System.Drawing.Size(683, 469); - this.splitContainerDependancies.SplitterDistance = 244; + this.splitContainerDependancies.Size = new System.Drawing.Size(683, 499); + this.splitContainerDependancies.SplitterDistance = 259; this.splitContainerDependancies.TabIndex = 27; // // labelDependancyType @@ -4985,7 +5025,7 @@ private void InitializeComponent() { this.dataGridViewPackages2.RowHeadersVisible = false; this.dataGridViewPackages2.RowHeadersWidth = 90; this.dataGridViewPackages2.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; - this.dataGridViewPackages2.Size = new System.Drawing.Size(683, 162); + this.dataGridViewPackages2.Size = new System.Drawing.Size(683, 177); this.dataGridViewPackages2.TabIndex = 21; this.dataGridViewPackages2.SelectionChanged += new System.EventHandler(this.dataGridViewPackages2_SelectionChanged); // @@ -5053,7 +5093,7 @@ private void InitializeComponent() { this.dataGridViewPackages3.RowHeadersVisible = false; this.dataGridViewPackages3.RowHeadersWidth = 90; this.dataGridViewPackages3.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; - this.dataGridViewPackages3.Size = new System.Drawing.Size(683, 161); + this.dataGridViewPackages3.Size = new System.Drawing.Size(683, 176); this.dataGridViewPackages3.TabIndex = 22; this.dataGridViewPackages3.SelectionChanged += new System.EventHandler(this.dataGridViewPackages3_SelectionChanged); // @@ -5148,7 +5188,7 @@ private void InitializeComponent() { this.panelPackageList.Controls.Add(this.labelInstalledPackages); this.panelPackageList.Location = new System.Drawing.Point(0, 34); this.panelPackageList.Name = "panelPackageList"; - this.panelPackageList.Size = new System.Drawing.Size(699, 597); + this.panelPackageList.Size = new System.Drawing.Size(699, 627); this.panelPackageList.TabIndex = 3; // // comboBoxPackageType @@ -5180,7 +5220,7 @@ private void InitializeComponent() { this.createPackageButton.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.createPackageButton.BackColor = System.Drawing.SystemColors.ButtonFace; this.createPackageButton.ForeColor = System.Drawing.SystemColors.ControlText; - this.createPackageButton.Location = new System.Drawing.Point(281, 549); + this.createPackageButton.Location = new System.Drawing.Point(281, 579); this.createPackageButton.Name = "createPackageButton"; this.createPackageButton.Size = new System.Drawing.Size(136, 26); this.createPackageButton.TabIndex = 23; @@ -5193,7 +5233,7 @@ private void InitializeComponent() { this.buttonUninstallPackage.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.buttonUninstallPackage.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonUninstallPackage.ForeColor = System.Drawing.SystemColors.ControlText; - this.buttonUninstallPackage.Location = new System.Drawing.Point(555, 549); + this.buttonUninstallPackage.Location = new System.Drawing.Point(555, 579); this.buttonUninstallPackage.Name = "buttonUninstallPackage"; this.buttonUninstallPackage.Size = new System.Drawing.Size(136, 26); this.buttonUninstallPackage.TabIndex = 21; @@ -5206,7 +5246,7 @@ private void InitializeComponent() { this.buttonInstallPackage.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.buttonInstallPackage.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonInstallPackage.ForeColor = System.Drawing.SystemColors.ControlText; - this.buttonInstallPackage.Location = new System.Drawing.Point(8, 549); + this.buttonInstallPackage.Location = new System.Drawing.Point(8, 579); this.buttonInstallPackage.Name = "buttonInstallPackage"; this.buttonInstallPackage.Size = new System.Drawing.Size(136, 26); this.buttonInstallPackage.TabIndex = 20; @@ -5238,7 +5278,7 @@ private void InitializeComponent() { this.dataGridViewPackages.RowHeadersVisible = false; this.dataGridViewPackages.RowHeadersWidth = 90; this.dataGridViewPackages.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; - this.dataGridViewPackages.Size = new System.Drawing.Size(683, 454); + this.dataGridViewPackages.Size = new System.Drawing.Size(683, 484); this.dataGridViewPackages.TabIndex = 19; this.dataGridViewPackages.SelectionChanged += new System.EventHandler(this.dataGridViewPackages_SelectionChanged); // @@ -5300,7 +5340,7 @@ private void InitializeComponent() { this.panelVersionError.Controls.Add(this.labelVersionHeaderBackground); this.panelVersionError.Location = new System.Drawing.Point(0, 34); this.panelVersionError.Name = "panelVersionError"; - this.panelVersionError.Size = new System.Drawing.Size(699, 597); + this.panelVersionError.Size = new System.Drawing.Size(699, 627); this.panelVersionError.TabIndex = 24; // // buttonCancel @@ -5309,7 +5349,7 @@ private void InitializeComponent() { this.buttonCancel.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonCancel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.buttonCancel.ForeColor = System.Drawing.SystemColors.ControlText; - this.buttonCancel.Location = new System.Drawing.Point(445, 561); + this.buttonCancel.Location = new System.Drawing.Point(445, 591); this.buttonCancel.Name = "buttonCancel"; this.buttonCancel.Size = new System.Drawing.Size(120, 28); this.buttonCancel.TabIndex = 29; @@ -5341,7 +5381,7 @@ private void InitializeComponent() { // this.buttonProceedAnyway1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.buttonProceedAnyway1.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.buttonProceedAnyway1.Location = new System.Drawing.Point(571, 565); + this.buttonProceedAnyway1.Location = new System.Drawing.Point(571, 595); this.buttonProceedAnyway1.Name = "buttonProceedAnyway1"; this.buttonProceedAnyway1.Size = new System.Drawing.Size(120, 24); this.buttonProceedAnyway1.TabIndex = 14; @@ -5517,7 +5557,7 @@ private void InitializeComponent() { this.panelCreatePackage.Controls.Add(this.panelNewPackage); this.panelCreatePackage.Location = new System.Drawing.Point(0, 34); this.panelCreatePackage.Name = "panelCreatePackage"; - this.panelCreatePackage.Size = new System.Drawing.Size(699, 597); + this.panelCreatePackage.Size = new System.Drawing.Size(699, 627); this.panelCreatePackage.TabIndex = 27; // // buttonCancel2 @@ -5526,7 +5566,7 @@ private void InitializeComponent() { this.buttonCancel2.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonCancel2.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.buttonCancel2.ForeColor = System.Drawing.SystemColors.ControlText; - this.buttonCancel2.Location = new System.Drawing.Point(445, 561); + this.buttonCancel2.Location = new System.Drawing.Point(445, 591); this.buttonCancel2.Name = "buttonCancel2"; this.buttonCancel2.Size = new System.Drawing.Size(120, 28); this.buttonCancel2.TabIndex = 38; @@ -5539,7 +5579,7 @@ private void InitializeComponent() { this.SaveFileNameButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.SaveFileNameButton.BackColor = System.Drawing.SystemColors.ButtonFace; this.SaveFileNameButton.ForeColor = System.Drawing.SystemColors.ControlText; - this.SaveFileNameButton.Location = new System.Drawing.Point(571, 510); + this.SaveFileNameButton.Location = new System.Drawing.Point(571, 540); this.SaveFileNameButton.Name = "SaveFileNameButton"; this.SaveFileNameButton.Size = new System.Drawing.Size(120, 26); this.SaveFileNameButton.TabIndex = 37; @@ -5551,7 +5591,7 @@ private void InitializeComponent() { // this.textBoxPackageFileName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.textBoxPackageFileName.Location = new System.Drawing.Point(8, 511); + this.textBoxPackageFileName.Location = new System.Drawing.Point(8, 541); this.textBoxPackageFileName.Name = "textBoxPackageFileName"; this.textBoxPackageFileName.Size = new System.Drawing.Size(557, 20); this.textBoxPackageFileName.TabIndex = 36; @@ -5561,7 +5601,7 @@ private void InitializeComponent() { this.labelSaveAs.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.labelSaveAs.AutoSize = true; this.labelSaveAs.ForeColor = System.Drawing.Color.Black; - this.labelSaveAs.Location = new System.Drawing.Point(8, 495); + this.labelSaveAs.Location = new System.Drawing.Point(8, 525); this.labelSaveAs.Name = "labelSaveAs"; this.labelSaveAs.Size = new System.Drawing.Size(94, 13); this.labelSaveAs.TabIndex = 35; @@ -5572,7 +5612,7 @@ private void InitializeComponent() { this.labelDependanciesNextStep.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.labelDependanciesNextStep.AutoSize = true; this.labelDependanciesNextStep.ForeColor = System.Drawing.Color.Black; - this.labelDependanciesNextStep.Location = new System.Drawing.Point(8, 541); + this.labelDependanciesNextStep.Location = new System.Drawing.Point(8, 571); this.labelDependanciesNextStep.Name = "labelDependanciesNextStep"; this.labelDependanciesNextStep.Size = new System.Drawing.Size(360, 13); this.labelDependanciesNextStep.TabIndex = 34; @@ -5584,7 +5624,7 @@ private void InitializeComponent() { this.buttonCreateProceed.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonCreateProceed.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.buttonCreateProceed.ForeColor = System.Drawing.SystemColors.ControlText; - this.buttonCreateProceed.Location = new System.Drawing.Point(571, 561); + this.buttonCreateProceed.Location = new System.Drawing.Point(571, 591); this.buttonCreateProceed.Name = "buttonCreateProceed"; this.buttonCreateProceed.Size = new System.Drawing.Size(120, 28); this.buttonCreateProceed.TabIndex = 22; @@ -5734,14 +5774,14 @@ private void InitializeComponent() { this.panelReplacePackage.Controls.Add(this.dataGridViewReplacePackage); this.panelReplacePackage.Location = new System.Drawing.Point(0, 137); this.panelReplacePackage.Name = "panelReplacePackage"; - this.panelReplacePackage.Size = new System.Drawing.Size(699, 352); + this.panelReplacePackage.Size = new System.Drawing.Size(699, 382); this.panelReplacePackage.TabIndex = 20; // // replacePackageButton // this.replacePackageButton.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.replacePackageButton.Enabled = false; - this.replacePackageButton.Location = new System.Drawing.Point(270, 303); + this.replacePackageButton.Location = new System.Drawing.Point(270, 333); this.replacePackageButton.Name = "replacePackageButton"; this.replacePackageButton.Size = new System.Drawing.Size(159, 23); this.replacePackageButton.TabIndex = 25; @@ -5782,7 +5822,7 @@ private void InitializeComponent() { this.dataGridViewReplacePackage.RowHeadersVisible = false; this.dataGridViewReplacePackage.RowHeadersWidth = 90; this.dataGridViewReplacePackage.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; - this.dataGridViewReplacePackage.Size = new System.Drawing.Size(683, 274); + this.dataGridViewReplacePackage.Size = new System.Drawing.Size(683, 304); this.dataGridViewReplacePackage.TabIndex = 23; this.dataGridViewReplacePackage.SelectionChanged += new System.EventHandler(this.dataGridViewReplacePackage_SelectionChanged); // @@ -5828,7 +5868,7 @@ private void InitializeComponent() { this.panelNewPackage.Enabled = false; this.panelNewPackage.Location = new System.Drawing.Point(-2, 137); this.panelNewPackage.Name = "panelNewPackage"; - this.panelNewPackage.Size = new System.Drawing.Size(702, 355); + this.panelNewPackage.Size = new System.Drawing.Size(702, 385); this.panelNewPackage.TabIndex = 21; // // newPackageClearSelectionButton @@ -5836,7 +5876,7 @@ private void InitializeComponent() { this.newPackageClearSelectionButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.newPackageClearSelectionButton.BackColor = System.Drawing.SystemColors.ButtonFace; this.newPackageClearSelectionButton.ForeColor = System.Drawing.SystemColors.ControlText; - this.newPackageClearSelectionButton.Location = new System.Drawing.Point(572, 326); + this.newPackageClearSelectionButton.Location = new System.Drawing.Point(572, 356); this.newPackageClearSelectionButton.Name = "newPackageClearSelectionButton"; this.newPackageClearSelectionButton.Size = new System.Drawing.Size(121, 26); this.newPackageClearSelectionButton.TabIndex = 30; @@ -5854,7 +5894,7 @@ private void InitializeComponent() { this.filesToPackageBox.Multiline = true; this.filesToPackageBox.Name = "filesToPackageBox"; this.filesToPackageBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; - this.filesToPackageBox.Size = new System.Drawing.Size(685, 250); + this.filesToPackageBox.Size = new System.Drawing.Size(685, 280); this.filesToPackageBox.TabIndex = 29; // // addPackageItemsButton @@ -5862,7 +5902,7 @@ private void InitializeComponent() { this.addPackageItemsButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.addPackageItemsButton.BackColor = System.Drawing.SystemColors.ButtonFace; this.addPackageItemsButton.ForeColor = System.Drawing.SystemColors.ControlText; - this.addPackageItemsButton.Location = new System.Drawing.Point(8, 326); + this.addPackageItemsButton.Location = new System.Drawing.Point(8, 356); this.addPackageItemsButton.Name = "addPackageItemsButton"; this.addPackageItemsButton.Size = new System.Drawing.Size(121, 26); this.addPackageItemsButton.TabIndex = 28; @@ -5902,10 +5942,10 @@ private void InitializeComponent() { this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; this.BackColor = System.Drawing.Color.White; - this.ClientSize = new System.Drawing.Size(859, 631); + this.ClientSize = new System.Drawing.Size(859, 661); this.Controls.Add(this.labelVerticalSeparator); - this.Controls.Add(this.panelPackages); this.Controls.Add(this.panelOptions); + this.Controls.Add(this.panelPackages); this.Controls.Add(this.panelStart); this.Controls.Add(this.panelInfo); this.Controls.Add(this.panelPanels); @@ -5948,7 +5988,6 @@ private void InitializeComponent() { this.groupboxRouteDetails.ResumeLayout(false); this.tabcontrolRouteDetails.ResumeLayout(false); this.tabpageRouteDescription.ResumeLayout(false); - this.tabpageRouteDescription.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.pictureboxRouteImage)).EndInit(); this.tabpageRouteMap.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.pictureboxRouteMap)).EndInit(); @@ -5960,6 +5999,18 @@ private void InitializeComponent() { this.panelRouteEncoding.ResumeLayout(false); this.panelOptions.ResumeLayout(false); this.panelOptions.PerformLayout(); + this.panelOptionsPage2.ResumeLayout(false); + this.groupBoxInputDevice.ResumeLayout(false); + this.groupBoxObjectParser.ResumeLayout(false); + this.groupBoxKioskMode.ResumeLayout(false); + this.groupBoxKioskMode.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownKioskTimeout)).EndInit(); + this.groupBoxAdvancedOptions.ResumeLayout(false); + this.groupBoxAdvancedOptions.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureboxCursor)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.updownTimeAccelerationFactor)).EndInit(); + this.groupBoxPackageOptions.ResumeLayout(false); + this.groupBoxPackageOptions.PerformLayout(); this.panelOptionsLeft.ResumeLayout(false); this.groupboxDisplayMode.ResumeLayout(false); this.groupboxDisplayMode.PerformLayout(); @@ -5990,18 +6041,6 @@ private void InitializeComponent() { this.groupboxSimulation.PerformLayout(); this.groupboxSound.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.updownSoundNumber)).EndInit(); - this.panelOptionsPage2.ResumeLayout(false); - this.groupBoxInputDevice.ResumeLayout(false); - this.groupBoxObjectParser.ResumeLayout(false); - this.groupBoxKioskMode.ResumeLayout(false); - this.groupBoxKioskMode.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.numericUpDownKioskTimeout)).EndInit(); - this.groupBoxAdvancedOptions.ResumeLayout(false); - this.groupBoxAdvancedOptions.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.pictureboxCursor)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.updownTimeAccelerationFactor)).EndInit(); - this.groupBoxPackageOptions.ResumeLayout(false); - this.groupBoxPackageOptions.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.pictureboxLanguage)).EndInit(); this.panelPanels.ResumeLayout(false); this.panelPanels.PerformLayout(); @@ -6508,5 +6547,8 @@ private void InitializeComponent() { private System.Windows.Forms.ComboBox comboBoxFont; private System.Windows.Forms.Label labelFontName; private System.Windows.Forms.Label labelNoDependencyReminder; + private System.Windows.Forms.Button buttonMSTSTrainsetDirectory; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox textBoxMSTSTrainsetDirectory; } } diff --git a/source/OpenBVE/UserInterface/formMain.Start.cs b/source/OpenBVE/UserInterface/formMain.Start.cs index 04fc6d208d..5c0dcccfc4 100644 --- a/source/OpenBVE/UserInterface/formMain.Start.cs +++ b/source/OpenBVE/UserInterface/formMain.Start.cs @@ -15,6 +15,7 @@ using OpenBveApi.Interface; using OpenBveApi.Routes; using RouteManager2; +using TrainManager.SafetySystems; using Path = OpenBveApi.Path; namespace OpenBve @@ -660,6 +661,11 @@ private void OnTrainFolderChanged(object sender, EventArgs e) /// Whether this is a packaged content folder private void PopulateTrainList(string selectedFolder, ListView listView, bool packages) { + string error; //ignored in this case, background thread + if (Program.CurrentHost.Plugins == null && !Program.CurrentHost.LoadPlugins(Program.FileSystem, Interface.CurrentOptions, out error, Program.TrainManager, Program.Renderer)) + { + throw new Exception("Unable to load the required plugins- Please reinstall OpenBVE"); + } try { if (selectedFolder.Length == 0) @@ -717,7 +723,9 @@ private void PopulateTrainList(string selectedFolder, ListView listView, bool pa try { string[] Folders = Directory.GetDirectories(selectedFolder); + string[] Files = Directory.GetFiles(selectedFolder); Array.Sort(Folders); + Array.Sort(Files); for (int i = 0; i < Folders.Length; i++) { try @@ -744,6 +752,26 @@ private void PopulateTrainList(string selectedFolder, ListView listView, bool pa //Most likely permissions } } + + for (int i = 0; i < Files.Length; i++) + { + for (int j = 0; j < Program.CurrentHost.Plugins.Length; j++) + { + string fileName = Path.GetFileName(Files[i]); + if (fileName[0] == '#' && fileName.EndsWith(".con", StringComparison.InvariantCultureIgnoreCase)) + { + // MSTS / ORTS use a hash at the start of the filename to deliminate AI consists + // These generally have missing cabviews etc- Hide from visibility in the main menu + continue; + } + if (Program.CurrentHost.Plugins[j].Train != null && Program.CurrentHost.Plugins[j].Train.CanLoadTrain(Files[i])) + { + ListViewItem Item = listviewTrainFolders.Items.Add(System.IO.Path.GetFileName(Files[i])); + Item.ImageKey = @"msts"; + Item.Tag = Files[i]; + } + } + } } catch { @@ -776,7 +804,13 @@ private void listviewTrainFolders_SelectedIndexChanged(object sender, EventArgs return; } if (t != null) { - if (Directory.Exists(t)) { + if (t.EndsWith(".con", StringComparison.InvariantCultureIgnoreCase)) + { + Result.TrainFolder = t; + ShowTrain(false); + } + else if (Directory.Exists(t)) + { try { string File = Path.CombineFile(t, "train.dat"); @@ -1106,8 +1140,29 @@ private void buttonTrainEncodingBig5_Click(object sender, EventArgs e) { private readonly object StartGame = new Object(); private void buttonStart_Click(object sender, EventArgs e) { - if (Result.RouteFile != null & Result.TrainFolder != null) { - if (File.Exists(Result.RouteFile) & Directory.Exists(Result.TrainFolder)) { + if (Result.RouteFile != null & Result.TrainFolder != null) + { + bool canLoadRoute = false, canLoadTrain = false; + for (int i = 0; i < Program.CurrentHost.Plugins.Length; i++) + { + if (canLoadRoute == false) + { + if (Program.CurrentHost.Plugins[i].Route != null && Program.CurrentHost.Plugins[i].Route.CanLoadRoute(Result.RouteFile)) + { + canLoadRoute = true; + } + } + if (canLoadTrain == false) + { + if (Program.CurrentHost.Plugins[i].Train != null && Program.CurrentHost.Plugins[i].Train.CanLoadTrain(Result.TrainFolder)) + { + canLoadTrain = true; + } + } + } + + if (canLoadRoute && canLoadTrain) + { Result.Start = true; buttonClose_Click(StartGame, e); //HACK: Call Application.DoEvents() to force the message pump to process all pending messages when the form closes diff --git a/source/OpenBVE/UserInterface/formMain.cs b/source/OpenBVE/UserInterface/formMain.cs index 5b3fd77f99..336754e1f6 100644 --- a/source/OpenBVE/UserInterface/formMain.cs +++ b/source/OpenBVE/UserInterface/formMain.cs @@ -153,6 +153,7 @@ private void formMain_Load(object sender, EventArgs e) Image MechanikRouteIcon = LoadImage(MenuFolder, "icon_mechanik.png"); Image Bve5Icon = LoadImage(MenuFolder, "icon_bve5.png"); Image TrainIcon = LoadImage(MenuFolder, "icon_train.png"); + Image MSTSIcon = LoadImage(MenuFolder, "icon_msts.png"); Image KeyboardIcon = LoadImage(MenuFolder, "icon_keyboard.png"); Image MouseIcon = LoadImage(MenuFolder, "icon_mouse.png"); Image JoystickIcon = LoadImage(MenuFolder, "icon_joystick.png"); @@ -233,6 +234,7 @@ private void formMain_Load(object sender, EventArgs e) if (ParentIcon != null) listviewTrainFolders.SmallImageList.Images.Add("parent", ParentIcon); if (FolderIcon != null) listviewTrainFolders.SmallImageList.Images.Add("folder", FolderIcon); if (TrainIcon != null) listviewTrainFolders.SmallImageList.Images.Add("train", TrainIcon); + if (MSTSIcon != null) listviewTrainFolders.SmallImageList.Images.Add("msts", MSTSIcon); if (DiskIcon != null) listviewTrainFolders.SmallImageList.Images.Add("disk", DiskIcon); if (ParentIcon != null) listViewTrainPackages.SmallImageList.Images.Add("parent", ParentIcon); if (FolderIcon != null) listViewTrainPackages.SmallImageList.Images.Add("folder", FolderIcon); @@ -245,6 +247,7 @@ private void formMain_Load(object sender, EventArgs e) listviewTrainRecently.Columns.Add(""); listviewTrainRecently.SmallImageList = new ImageList { TransparentColor = Color.White }; if (TrainIcon != null) listviewTrainRecently.SmallImageList.Images.Add("train", TrainIcon); + if (MSTSIcon != null) listviewTrainRecently.SmallImageList.Images.Add("msts", MSTSIcon); for (int i = 0; i < Interface.CurrentOptions.RecentlyUsedTrains.Length; i++) { if (string.IsNullOrEmpty(Interface.CurrentOptions.RecentlyUsedTrains[i])) continue; @@ -255,7 +258,7 @@ private void formMain_Load(object sender, EventArgs e) continue; } ListViewItem Item = listviewTrainRecently.Items.Add(trainFileName); - Item.ImageKey = @"train"; + Item.ImageKey = trainFileName.EndsWith(".con", StringComparison.InvariantCultureIgnoreCase) ? @"msts" : @"train"; Item.Tag = Interface.CurrentOptions.RecentlyUsedTrains[i]; if (textboxTrainFolder.Items.Count == 0 || !textboxTrainFolder.Items.Contains(trainPath)) { @@ -739,9 +742,11 @@ private void ApplyLanguage() textBoxRouteDirectory.Text = Program.FileSystem.RouteInstallationDirectory; textBoxTrainDirectory.Text = Program.FileSystem.TrainInstallationDirectory; textBoxOtherDirectory.Text = Program.FileSystem.OtherInstallationDirectory; + textBoxMSTSTrainsetDirectory.Text = Program.FileSystem.MSTSDirectory; labelRouteInstallDirectory.Text = Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"options","package_route_directory"}); labelTrainInstallDirectory.Text = Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"options","package_train_directory"}); labelOtherInstallDirectory.Text = Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"options","package_other_directory"}); + labelOtherInstallDirectory.Text = Translations.GetInterfaceString(HostApplication.OpenBve, new[] { "options", "package_msts_directory" }); labelPackageCompression.Text = Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"options","package_compression"}); //Kiosk Mode groupBoxKioskMode.Text = Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"options","kiosk_mode"}); @@ -1198,7 +1203,7 @@ private void formMain_FormClosing() string[] a = new string[Interface.CurrentOptions.RecentlyUsedTrains.Length]; for (int i = 0; i < Interface.CurrentOptions.RecentlyUsedTrains.Length; i++) { - if (Directory.Exists(Interface.CurrentOptions.RecentlyUsedTrains[i])) + if ((Interface.CurrentOptions.RecentlyUsedTrains[i].EndsWith(".con", StringComparison.InvariantCultureIgnoreCase) && File.Exists(Interface.CurrentOptions.RecentlyUsedTrains[i])) || Directory.Exists(Interface.CurrentOptions.RecentlyUsedTrains[i])) { a[n] = Interface.CurrentOptions.RecentlyUsedTrains[i]; n++; @@ -2061,5 +2066,17 @@ private void textboxTrainDescription_LinkClicked(object sender, LinkClickedEvent { Process.Start(e.LinkText); } + + private void buttonMSTSTrainsetDirectory_Click(object sender, EventArgs e) + { + using (var folderSelectDialog = new FolderBrowserDialog()) + { + if (folderSelectDialog.ShowDialog() == DialogResult.OK) + { + Program.FileSystem.MSTSDirectory = folderSelectDialog.SelectedPath; + textBoxMSTSTrainsetDirectory.Text = folderSelectDialog.SelectedPath; + } + } + } } } diff --git a/source/OpenBVE/UserInterface/formMain.resx b/source/OpenBVE/UserInterface/formMain.resx index 77fa901946..2d26dd8152 100644 --- a/source/OpenBVE/UserInterface/formMain.resx +++ b/source/OpenBVE/UserInterface/formMain.resx @@ -120,10 +120,10 @@ 456, 18 - + True - + True diff --git a/source/OpenBveApi/Objects/ObjectInterface.cs b/source/OpenBveApi/Objects/ObjectInterface.cs index e3e6d96bb9..7d27418252 100644 --- a/source/OpenBveApi/Objects/ObjectInterface.cs +++ b/source/OpenBveApi/Objects/ObjectInterface.cs @@ -47,7 +47,7 @@ public virtual void SetObjectParser(object parserType) /// Receives the object. /// The encoding for the object /// Whether loading the object was successful. - public abstract bool LoadObject(string path, System.Text.Encoding Encoding, out UnifiedObject unifiedObject); + public abstract bool LoadObject(string path, System.Text.Encoding textEncoding, out UnifiedObject unifiedObject); /// Loads the specified object. /// The path to the file or folder that contains the object. diff --git a/source/OpenBveApi/Objects/ObjectTypes/AnimatedObject/AnimatedObject.cs b/source/OpenBveApi/Objects/ObjectTypes/AnimatedObject/AnimatedObject.cs index 760c52192b..c43b468bb6 100644 --- a/source/OpenBveApi/Objects/ObjectTypes/AnimatedObject/AnimatedObject.cs +++ b/source/OpenBveApi/Objects/ObjectTypes/AnimatedObject/AnimatedObject.cs @@ -729,7 +729,7 @@ public void Update(AbstractTrain Train, int CarIndex, double TrackPosition, Vect internalObject.Translation = Matrix4D.CreateTranslation(Position.X, Position.Y, -Position.Z); } - if (ColorFunction != null) + if (ColorFunction != null && Colors != null) { int color = (int)ColorFunction.LastResult; if (UpdateFunctions) diff --git a/source/OpenBveApi/Objects/ObjectTypes/UnifiedObject.cs b/source/OpenBveApi/Objects/ObjectTypes/UnifiedObject.cs index 08cfe798d2..510b3c1af3 100644 --- a/source/OpenBveApi/Objects/ObjectTypes/UnifiedObject.cs +++ b/source/OpenBveApi/Objects/ObjectTypes/UnifiedObject.cs @@ -87,6 +87,7 @@ public void CreateObject(Vector3 Position, Transformation WorldTransformation, T /// The X value /// The Y value /// The Z value + /// Controls whether the translation is added to the root matrix, or replaces it public abstract void ApplyTranslation(double x, double y, double z, bool absoluteTranslation = false); } diff --git a/source/OpenBveApi/OpenBveApi.csproj b/source/OpenBveApi/OpenBveApi.csproj index 75329db2e3..c630fb743d 100644 --- a/source/OpenBveApi/OpenBveApi.csproj +++ b/source/OpenBveApi/OpenBveApi.csproj @@ -265,6 +265,7 @@ + diff --git a/source/OpenBveApi/Runtime/System/CameraViewMode.cs b/source/OpenBveApi/Runtime/System/CameraViewMode.cs index 6f1715b063..70e5d25968 100644 --- a/source/OpenBveApi/Runtime/System/CameraViewMode.cs +++ b/source/OpenBveApi/Runtime/System/CameraViewMode.cs @@ -1,19 +1,26 @@ -namespace OpenBveApi.Runtime +using System; + +namespace OpenBveApi.Runtime { + /// Represents the available camera view modes. + [Flags] public enum CameraViewMode { + /// Unknown + /// Used for bitwise operations + NotDefined = 0, /// The interior of a 2D cab - Interior, + Interior = 2, /// The interior of a 3D cab - InteriorLookAhead, + InteriorLookAhead = 4, /// An exterior camera attached to a train - Exterior, + Exterior = 8, /// A camera attached to the track - Track, + Track = 16, /// A fly-by camera attached to a point on the track - FlyBy, + FlyBy = 32, /// A fly-by zooming camera attached to a point on the track - FlyByZooming + FlyByZooming = 64 } } diff --git a/source/OpenBveApi/System/FileSystem.cs b/source/OpenBveApi/System/FileSystem.cs index 24f12e2243..04fdefe87f 100644 --- a/source/OpenBveApi/System/FileSystem.cs +++ b/source/OpenBveApi/System/FileSystem.cs @@ -56,6 +56,9 @@ public class FileSystem { /// The Loksim3D data directory public string LoksimDataDirectory; + /// The MSTS trainset directory + public string MSTSDirectory; + /// Any lines loaded from the filesystem.cfg which were not understood internal string[] NotUnderstoodLines; @@ -158,7 +161,7 @@ public void SaveCurrentFileSystemConfiguration() { string file = Path.CombineFile(SettingsFolder, "FileSystem.cfg"); StringBuilder newLines = new StringBuilder(); - newLines.AppendLine("Version=1"); + newLines.AppendLine("Version=2"); try { if (File.Exists(file)) @@ -219,6 +222,11 @@ public void SaveCurrentFileSystemConfiguration() { newLines.AppendLine("LoksimPackageInstall=" + ReplacePath(LoksimPackageInstallationDirectory)); } + if (MSTSDirectory != null && + Directory.Exists(OtherInstallationDirectory)) + { + newLines.AppendLine("MSTSTrainset=" + ReplacePath(MSTSDirectory)); + } if (NotUnderstoodLines != null && NotUnderstoodLines.Length != 0) { for (int i = 0; i < NotUnderstoodLines.Length; i++) @@ -350,14 +358,14 @@ private static FileSystem FromConfigurationFile(string file, HostInterface Host) system.AppendToLogFile("WARNING: Invalid filesystem.cfg version detected."); } - if (v <= 1) + if (v <= 2) { //Silently upgrade to the current config version - system.Version = 1; + system.Version = 2; break; } - system.AppendToLogFile("WARNING: A newer filesystem.cfg version " + v + " was detected. The current version is 1."); + system.AppendToLogFile("WARNING: A newer filesystem.cfg version " + v + " was detected. The current version is 2."); system.Version = v; break; @@ -417,6 +425,9 @@ private static FileSystem FromConfigurationFile(string file, HostInterface Host) case "loksimpackageinstall": system.LoksimPackageInstallationDirectory = GetAbsolutePath(value, true); break; + case "mststrainset": + system.MSTSDirectory = GetAbsolutePath(value, true); + break; default: if (system.NotUnderstoodLines == null) { diff --git a/source/OpenBveApi/Train/Motor/EngineType.cs b/source/OpenBveApi/Train/Motor/EngineType.cs new file mode 100644 index 0000000000..10417acae7 --- /dev/null +++ b/source/OpenBveApi/Train/Motor/EngineType.cs @@ -0,0 +1,41 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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 OpenBveApi.Motor +{ + /// Describes the different types of engine + public enum EngineType + { + /// No engine + NoEngine = 0, + /// Diesel prime mover, electric transmission + Diesel, + /// Diesel prime mover, hydralic transmission + DieselHydraulic, + /// Steam engine + Steam, + /// Pure electric engine + Electric + } +} diff --git a/source/OpenBveApi/Train/Motor/PantographState.cs b/source/OpenBveApi/Train/Motor/PantographState.cs index f9ae1ac3c4..8df10e411f 100644 --- a/source/OpenBveApi/Train/Motor/PantographState.cs +++ b/source/OpenBveApi/Train/Motor/PantographState.cs @@ -27,10 +27,10 @@ namespace OpenBveApi.Motor /// The possible states of a pantograph public enum PantographState { - /// The pantograph is raised - Raised, /// The pantograph is lowered Lowered, + /// The pantograph is raised + Raised, /// The pantograph is raised, but no wire is present Dewired } diff --git a/source/OpenBveApi/World/UnitConversions/Torque.cs b/source/OpenBveApi/World/UnitConversions/Torque.cs index 913622a983..0d8223f99b 100644 --- a/source/OpenBveApi/World/UnitConversions/Torque.cs +++ b/source/OpenBveApi/World/UnitConversions/Torque.cs @@ -1,4 +1,4 @@ -//Simplified BSD License (BSD-2-Clause) +//Simplified BSD License (BSD-2-Clause) // //Copyright (c) 2025, Christopher Lees, The OpenBVE Project // @@ -31,6 +31,8 @@ public enum UnitOfTorque { /// Newton-meters per second NewtonMetersPerSecond, + /// Kilo-newton-meters per second + KiloNewtonMetersPerSecond, /// Foot pounds FootPound @@ -42,11 +44,12 @@ public class TorqueConverter : UnitConverter static TorqueConverter() { BaseUnit = UnitOfTorque.NewtonMetersPerSecond; - RegisterConversion(UnitOfTorque.FootPound, v => v * 0.7375621493, v => v / 1.3558179483); - KnownUnits = new Dictionary + RegisterConversion(UnitOfTorque.KiloNewtonMetersPerSecond, v => v / 1000, v => v * 1000); + RegisterConversion(UnitOfTorque.FootPound, v => v * 0.7375621493, v => v / 1.3558179483); + KnownUnits = new Dictionary { // n.b. assume that torque in plain newtons is actually newton meters - { "n/m/s", UnitOfTorque.NewtonMetersPerSecond }, { "nms", UnitOfTorque.NewtonMetersPerSecond }, { "newtonmeterspersecond", UnitOfTorque.NewtonMetersPerSecond }, { "n", UnitOfTorque.NewtonMetersPerSecond }, { "lb/ft", UnitOfTorque.FootPound } + {"n/m/s", UnitOfTorque.NewtonMetersPerSecond}, {"nms", UnitOfTorque.NewtonMetersPerSecond}, {"newtonmeterspersecond", UnitOfTorque.NewtonMetersPerSecond}, {"n", UnitOfTorque.NewtonMetersPerSecond}, {"knms", UnitOfTorque.KiloNewtonMetersPerSecond}, {"lb/ft", UnitOfTorque.FootPound} }; } diff --git a/source/Plugins/Train.MsTs/Effects/Exhaust.cs b/source/Plugins/Train.MsTs/Effects/Exhaust.cs new file mode 100644 index 0000000000..eb2b499acc --- /dev/null +++ b/source/Plugins/Train.MsTs/Effects/Exhaust.cs @@ -0,0 +1,45 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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 OpenBveApi.Math; + +namespace Train.MsTs +{ + /// Describes the properties of an exhaust animation for a MSTS model + internal struct Exhaust + { + /// The offset from the center of the model + internal Vector3 Offset; + /// The direction of the exhaust emissions + internal Vector3 Direction; + /// The size of the exhaust outlet (controls initial particle size) + internal double Size; + /// The maximum expanded size of a smoke particle + internal double SmokeMaxMagnitude; + /// The rate of particle emissions at idle + internal double SmokeInitialRate; + /// The rate of particle emissions at maximum power + internal double SmokeMaxRate; + } +} diff --git a/source/Plugins/Train.MsTs/Handles/Handle.cs b/source/Plugins/Train.MsTs/Handles/Handle.cs index 79cf20f144..7dc68553a6 100644 --- a/source/Plugins/Train.MsTs/Handles/Handle.cs +++ b/source/Plugins/Train.MsTs/Handles/Handle.cs @@ -1,4 +1,28 @@ -using System; +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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 OpenBve.Formats.MsTs; using TrainManager.Handles; using TrainManager.Trains; @@ -17,41 +41,56 @@ internal partial class WagonParser internal Tuple[] NotchDescriptions; - internal AbstractHandle ParseHandle(Block block, TrainBase train) + internal AbstractHandle ParseHandle(Block block, TrainBase train, bool isPower) { HandleMinimum = (int)(block.ReadSingle() * 100); HandleMaximum = (int)(block.ReadSingle() * 100); HandleStep = (int)(block.ReadSingle() * 100); HandleStartingPosition = (int)(block.ReadSingle() * 100); - - Block notchDescriptions = block.ReadSubBlock(KujuTokenID.NumNotches); - ParseNotchDescriptionBlock(notchDescriptions); - return new VariableHandle(train, NotchDescriptions); + // some handles seem to have extra numbers here, I believe ignored by the MSTS parser + Block notchDescriptions = block.GetSubBlock(KujuTokenID.NumNotches); + ParseNotchDescriptionBlock(notchDescriptions, isPower); + return new VariableHandle(train, isPower, NotchDescriptions); } - private void ParseNotchDescriptionBlock(Block block) + private void ParseNotchDescriptionBlock(Block block, bool isPower) { int numNotches = block.ReadInt32(); + if (numNotches < 2) + { + // percentage based notch + NotchDescriptions = null; + return; + } + NotchDescriptions = new Tuple[numNotches]; for (int i = 0; i < numNotches; i++) { Block descriptionBlock = block.ReadSubBlock(KujuTokenID.Notch); double notchPower = descriptionBlock.ReadSingle(); - descriptionBlock.ReadSingle(); // ?? - string notchDescription = descriptionBlock.ReadString(); - if (notchDescription.Equals("dummy", StringComparison.InvariantCultureIgnoreCase)) + try { - if (i == 0) + descriptionBlock.ReadSingle(); // ?? + string notchDescription = descriptionBlock.ReadString(); + if (notchDescription.Equals("dummy", StringComparison.InvariantCultureIgnoreCase)) { - notchDescription = "N"; - } - else - { - notchDescription = "P" + i; + if (i == 0) + { + notchDescription = "N"; + } + else + { + notchDescription = isPower ? "P" + i : "B" + i; + } + } - + NotchDescriptions[i] = new Tuple(notchPower, notchDescription); + } + catch + { + // ignore + NotchDescriptions[i] = new Tuple(notchPower, i.ToString()); } - NotchDescriptions[i] = new Tuple(notchPower, notchDescription); } } } diff --git a/source/Plugins/Train.MsTs/Misc/Units.cs b/source/Plugins/Train.MsTs/Misc/Units.cs new file mode 100644 index 0000000000..5900a81e8c --- /dev/null +++ b/source/Plugins/Train.MsTs/Misc/Units.cs @@ -0,0 +1,26 @@ +// ReSharper disable UnusedMember.Global +// ReSharper disable InconsistentNaming +namespace Train.MsTs +{ + /// Units encountered in a MSTS CVF + internal enum Units + { + Unknown = 0, + Milliamps, + Amps, + Volts, + Kilovolts, + Gallons, + Inches_Of_Mercury, + Kilometers_Per_Hour, + Km_Per_Hour = Kilometers_Per_Hour, + Miles_Per_Hour, + Miles_Hour_Min, + Meters_Sec, + PSI, + Kilo_Lbs, + Kilopascals, + Bar, + Kgs_Per_Square_Cm + } +} diff --git a/source/Plugins/Train.MsTs/Panel/CabComponent.cs b/source/Plugins/Train.MsTs/Panel/CabComponent.cs index 8d034ed5a5..2555c082af 100644 --- a/source/Plugins/Train.MsTs/Panel/CabComponent.cs +++ b/source/Plugins/Train.MsTs/Panel/CabComponent.cs @@ -1,4 +1,4 @@ -//Simplified BSD License (BSD-2-Clause) +//Simplified BSD License (BSD-2-Clause) // //Copyright (c) 2025, Christopher Lees, The OpenBVE Project // @@ -57,15 +57,17 @@ internal class CabComponent private int LeadingZeros; private FrameMapping[] FrameMappings = new FrameMapping[0]; private readonly Vector3 PanelPosition; - + private CabComponentStyle Style; private Tuple[] PositiveColors; private Tuple[] NegativeColors; + private Color24 ControlColor; + private int Accuracy; internal void Parse() { if (!Enum.TryParse(myBlock.Token.ToString(), true, out Type)) { - Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Unrecognised CabViewComponent type."); + Plugin.CurrentHost.AddMessage(MessageType.Error, false, "MSTS CVF Parser: Unrecognised CabViewComponent type."); return; } @@ -85,60 +87,65 @@ internal void Parse() } } - internal void Create(ref CarBase Car, int Layer) + internal void Create(ref CarBase currentCar, int componentLayer) { - if (File.Exists(TexturePath) || Type == CabComponentType.Digital) + if (!File.Exists(TexturePath) && Type != CabComponentType.Digital && Type != CabComponentType.DigitalClock) + { + return; + } + if (FrameMappings.Length < 2 && TotalFrames > 1) { - if (FrameMappings.Length == 0 && TotalFrames > 1) + // e.g. Acela power handle has 25 frames for total power value of 100% but no mappings specified + FrameMappings = new FrameMapping[TotalFrames]; + // frame 0 is always mapping value 0 + for (int i = 1; i < TotalFrames; i++) { - // e.g. Acela power handle has 25 frames for total power value of 100% but no mappings specified - FrameMappings = new FrameMapping[TotalFrames]; - // frame 0 is always mapping value 0 - for (int i = 1; i < TotalFrames; i++) - { - FrameMappings[i].MappingValue = (double)i / TotalFrames; - FrameMappings[i].FrameKey = i; - } - + FrameMappings[i].MappingValue = (double)i / TotalFrames; + FrameMappings[i].FrameKey = i; } - //Create element - double rW = 1024.0 / 640.0; - double rH = 768.0 / 480.0; - int wday, hday; - int j; - string f; - CultureInfo Culture = CultureInfo.InvariantCulture; - switch (Type) - { - case CabComponentType.Dial: - Plugin.CurrentHost.RegisterTexture(TexturePath, new TextureParameters(null, null), out Texture tday, true); - // correct angle position if appropriate - if (!DirIncrease && InitialAngle > LastAngle) - { - InitialAngle = -(365 - InitialAngle); - } + } - //Get final position from the 640px panel (Yuck...) - Position.X *= rW; - Position.Y *= rH; - Size.X *= rW; - Size.Y *= rH; - PivotPoint *= rH; - j = CabviewFileParser.CreateElement(ref Car.CarSections[CarSectionType.Interior].Groups[0], Position, Size, new Vector2((0.5 * Size.X) / (tday.Width * rW), PivotPoint / (tday.Height * rH)), Layer * CabviewFileParser.StackDistance, PanelPosition, tday, null, new Color32(255, 255, 255, 255)); - Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].RotateZDirection = new Vector3(0.0, 0.0, -1.0); - Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].RotateXDirection = DirIncrease ? new Vector3(1.0, 0.0, 0.0) : new Vector3(-1.0, 0.0, 0.0); - Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].RotateYDirection = Vector3.Cross(Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].RotateZDirection, Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].RotateXDirection); - f = CabviewFileParser.GetStackLanguageFromSubject(Car.baseTrain, panelSubject, Units); - InitialAngle = InitialAngle.ToRadians(); - LastAngle = LastAngle.ToRadians(); - double a0 = (InitialAngle * Maximum - LastAngle * Minimum) / (Maximum - Minimum); - double a1 = (LastAngle - InitialAngle) / (Maximum - Minimum); - f += " " + a1.ToString(Culture) + " * " + a0.ToString(Culture) + " +"; - Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].RotateZFunction = new FunctionScript(Plugin.CurrentHost, f, false); - break; - case CabComponentType.Lever: - /* + //Create element + const double rW = 1024.0 / 640.0; + const double rH = 768.0 / 480.0; + int wday, hday; + int elementIndex; + string f; + CultureInfo culture = CultureInfo.InvariantCulture; + switch (Type) + { + case CabComponentType.Dial: + Plugin.CurrentHost.RegisterTexture(TexturePath, new TextureParameters(null, null), out Texture tday, true); + // correct angle position if appropriate + if (!DirIncrease && InitialAngle > LastAngle) + { + InitialAngle = -(365 - InitialAngle); + } + + //Get final position from the 640px panel (Yuck...) + Position.X *= rW; + Position.Y *= rH; + Size.X *= rW; + Size.Y *= rH; + PivotPoint *= rH; + elementIndex = CabviewFileParser.CreateElement(ref currentCar.CarSections[CarSectionType.Interior].Groups[0], Position, Size, new Vector2((0.5 * Size.X) / (tday.Width * rW), PivotPoint / (tday.Height * rH)), componentLayer * CabviewFileParser.StackDistance, PanelPosition, tday, null, new Color32(255, 255, 255, 255)); + currentCar.CarSections[CarSectionType.Interior].Groups[0].Elements[elementIndex].RotateZDirection = new Vector3(0.0, 0.0, -1.0); + currentCar.CarSections[CarSectionType.Interior].Groups[0].Elements[elementIndex].RotateXDirection = DirIncrease ? new Vector3(1.0, 0.0, 0.0) : new Vector3(-1.0, 0.0, 0.0); + currentCar.CarSections[CarSectionType.Interior].Groups[0].Elements[elementIndex].RotateYDirection = Vector3.Cross(currentCar.CarSections[CarSectionType.Interior].Groups[0].Elements[elementIndex].RotateZDirection, currentCar.CarSections[CarSectionType.Interior].Groups[0].Elements[elementIndex].RotateXDirection); + f = CabviewFileParser.GetStackLanguageFromSubject(currentCar.baseTrain, panelSubject, Units); + InitialAngle = InitialAngle.ToRadians(); + LastAngle = LastAngle.ToRadians(); + double a0 = (InitialAngle * Maximum - LastAngle * Minimum) / (Maximum - Minimum); + double a1 = (LastAngle - InitialAngle) / (Maximum - Minimum); + f += " " + a1.ToString(culture) + " * " + a0.ToString(culture) + " +"; + currentCar.CarSections[CarSectionType.Interior].Groups[0].Elements[elementIndex].RotateZFunction = new FunctionScript(Plugin.CurrentHost, f, false); + // backstop by default e.g. ammeter when using dynamic brakes + currentCar.CarSections[CarSectionType.Interior].Groups[0].Elements[elementIndex].RotateZFunction.Minimum = InitialAngle; + currentCar.CarSections[CarSectionType.Interior].Groups[0].Elements[elementIndex].RotateZFunction.Maximum = LastAngle; + break; + case CabComponentType.Lever: + /* * TODO: * Need to revisit the actual position versus frame with MSTS content. * @@ -149,211 +156,246 @@ internal void Create(ref CarBase Car, int Layer) * Oddly, all frames appear to be distinct. Need to check OR + MSTS handling * Suspect there's a notch delay or something that should use these. */ - Position.X *= rW; - Position.Y *= rH; - Size.X *= rW; - Size.Y *= rH; - Plugin.CurrentHost.QueryTextureDimensions(TexturePath, out wday, out hday); - if (wday > 0 & hday > 0) + Position.X *= rW; + Position.Y *= rH; + Size.X *= rW; + Size.Y *= rH; + Plugin.CurrentHost.QueryTextureDimensions(TexturePath, out wday, out hday); + if (wday > 0 & hday > 0) + { + Texture[] textures = new Texture[TotalFrames]; + int row = 0; + int column = 0; + int frameWidth = wday / HorizontalFrames; + int frameHeight = hday / VerticalFrames; + for (int k = 0; k < TotalFrames; k++) { - Texture[] textures = new Texture[TotalFrames]; - int row = 0; - int column = 0; - int frameWidth = wday / HorizontalFrames; - int frameHeight = hday / VerticalFrames; - for (int k = 0; k < TotalFrames; k++) + Plugin.CurrentHost.RegisterTexture(TexturePath, new TextureParameters(new TextureClipRegion(column * frameWidth, row * frameHeight, frameWidth, frameHeight), null), out textures[k]); + if (column < HorizontalFrames - 1) { - Plugin.CurrentHost.RegisterTexture(TexturePath, new TextureParameters(new TextureClipRegion(column * frameWidth, row * frameHeight, frameWidth, frameHeight), null), out textures[k]); - if (column < HorizontalFrames - 1) - { - column++; - } - else - { - column = 0; - row++; - } + column++; } - - j = -1; - for (int k = 0; k < textures.Length; k++) + else { - - int l = CabviewFileParser.CreateElement(ref Car.CarSections[CarSectionType.Interior].Groups[0], Position, Size, new Vector2(0.5, 0.5), Layer * CabviewFileParser.StackDistance, PanelPosition, textures[k], null, new Color32(255, 255, 255, 255), k != 0); - if (k == 0) j = l; - } - - f = CabviewFileParser.GetStackLanguageFromSubject(Car.baseTrain, panelSubject, Units); - switch (panelSubject) - { - case PanelSubject.Throttle: - case PanelSubject.Train_Brake: - Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].StateFunction = new CvfAnimation(panelSubject, FrameMappings); - break; - default: - Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].StateFunction = new FunctionScript(Plugin.CurrentHost, f, false); - break; + column = 0; + row++; } + } + elementIndex = -1; + for (int k = 0; k < textures.Length; k++) + { + + int l = CabviewFileParser.CreateElement(ref currentCar.CarSections[CarSectionType.Interior].Groups[0], Position, Size, new Vector2(0.5, 0.5), componentLayer * CabviewFileParser.StackDistance, PanelPosition, textures[k], null, new Color32(255, 255, 255, 255), k != 0); + if (k == 0) elementIndex = l; } - break; - case CabComponentType.TriState: - case CabComponentType.TwoState: - case CabComponentType.MultiStateDisplay: - Position.X *= rW; - Position.Y *= rH; - Size.X *= rW; - Size.Y *= rH; - Plugin.CurrentHost.QueryTextureDimensions(TexturePath, out wday, out hday); - if (wday > 0 & hday > 0) + f = CabviewFileParser.GetStackLanguageFromSubject(currentCar.baseTrain, panelSubject, Units); + switch (panelSubject) { - Texture[] textures = new Texture[TotalFrames]; - int row = 0; - int column = 0; - int frameWidth = wday / HorizontalFrames; - int frameHeight = hday / VerticalFrames; - for (int k = 0; k < TotalFrames; k++) - { - Plugin.CurrentHost.RegisterTexture(TexturePath, new TextureParameters(new TextureClipRegion(column * frameWidth, row * frameHeight, frameWidth, frameHeight), null), out textures[k]); - if (column < HorizontalFrames - 1) - { - column++; - } - else - { - column = 0; - row++; - } - } + case PanelSubject.Engine_Brake: + case PanelSubject.Throttle: + case PanelSubject.Train_Brake: + case PanelSubject.Gears: + currentCar.CarSections[CarSectionType.Interior].Groups[0].Elements[elementIndex].StateFunction = new CvfAnimation(Plugin.CurrentHost, panelSubject, FrameMappings); + break; + default: + currentCar.CarSections[CarSectionType.Interior].Groups[0].Elements[elementIndex].StateFunction = new FunctionScript(Plugin.CurrentHost, f, false); + break; + } - j = -1; - for (int k = 0; k < textures.Length; k++) + } + + break; + case CabComponentType.TriState: + case CabComponentType.TwoState: + case CabComponentType.MultiStateDisplay: + case CabComponentType.CombinedControl: + Position.X *= rW; + Position.Y *= rH; + Size.X *= rW; + Size.Y *= rH; + Plugin.CurrentHost.QueryTextureDimensions(TexturePath, out wday, out hday); + if (wday > 0 & hday > 0) + { + Texture[] textures = new Texture[TotalFrames]; + int row = 0; + int column = 0; + int frameWidth = wday / HorizontalFrames; + int frameHeight = hday / VerticalFrames; + for (int k = 0; k < TotalFrames; k++) + { + Plugin.CurrentHost.RegisterTexture(TexturePath, new TextureParameters(new TextureClipRegion(column * frameWidth, row * frameHeight, frameWidth, frameHeight), null), out textures[k]); + if (column < HorizontalFrames - 1) { - int l = CabviewFileParser.CreateElement(ref Car.CarSections[CarSectionType.Interior].Groups[0], Position, Size, new Vector2(0.5, 0.5), Layer * CabviewFileParser.StackDistance, PanelPosition, textures[k], null, new Color32(255, 255, 255, 255), k != 0); - if (k == 0) j = l; + column++; } - - f = CabviewFileParser.GetStackLanguageFromSubject(Car.baseTrain, panelSubject, Units); - switch (panelSubject) + else { - case PanelSubject.Direction: - case PanelSubject.Direction_Display: - case PanelSubject.Overspeed: - Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].StateFunction = new CvfAnimation(panelSubject, FrameMappings); - break; - default: - Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].StateFunction = new FunctionScript(Plugin.CurrentHost, f, false); - break; + column = 0; + row++; } + } - + elementIndex = -1; + for (int k = 0; k < textures.Length; k++) + { + int l = CabviewFileParser.CreateElement(ref currentCar.CarSections[CarSectionType.Interior].Groups[0], Position, Size, new Vector2(0.5, 0.5), componentLayer * CabviewFileParser.StackDistance, PanelPosition, textures[k], null, new Color32(255, 255, 255, 255), k != 0); + if (k == 0) elementIndex = l; } - break; - case CabComponentType.Digital: - if (panelSubject != PanelSubject.Speedometer && panelSubject != PanelSubject.Speedlim_Display) + f = CabviewFileParser.GetStackLanguageFromSubject(currentCar.baseTrain, panelSubject, Units); + switch (panelSubject) { - break; + case PanelSubject.Direction: + case PanelSubject.Direction_Display: + case PanelSubject.Overspeed: + case PanelSubject.Sanders: + case PanelSubject.Sanding: + case PanelSubject.Wheelslip: + case PanelSubject.Alerter_Display: + case PanelSubject.Penalty_App: + case PanelSubject.Throttle_Display: + case PanelSubject.CP_Handle: + case PanelSubject.CPH_Display: + case PanelSubject.Friction_Braking: + currentCar.CarSections[CarSectionType.Interior].Groups[0].Elements[elementIndex].StateFunction = new CvfAnimation(Plugin.CurrentHost, panelSubject, FrameMappings); + break; + default: + currentCar.CarSections[CarSectionType.Interior].Groups[0].Elements[elementIndex].StateFunction = new FunctionScript(Plugin.CurrentHost, f, false); + break; } - Position.X *= rW; - Position.Y *= rH; - Color24 textColor = PositiveColors[0].Item2; + } + + break; + case CabComponentType.Digital: + Position.X *= rW; + Position.Y *= rH; + + Color24 textColor = PositiveColors[0].Item2; + + Texture[] frameTextures = new Texture[11]; + TexturePath = OpenBveApi.Path.CombineFile(OpenBveApi.Path.CombineDirectory(Plugin.FileSystem.DataFolder, "Compatibility"), "numbers.png"); // arial 9.5pt + Plugin.CurrentHost.QueryTextureDimensions(TexturePath, out wday, out hday); + + for (int i = 0; i < 10; i++) + { + Plugin.CurrentHost.RegisterTexture(TexturePath, new TextureParameters(new TextureClipRegion(0, i * 24, 16, 24), null), out frameTextures[i], true); + } + + Plugin.CurrentHost.RegisterTexture(TexturePath, new TextureParameters(new TextureClipRegion(0, 0, 16, 24), null), out frameTextures[10], true); // repeated zero [check vice MSTS] - Texture[] frameTextures = new Texture[11]; - TexturePath = OpenBveApi.Path.CombineFile(OpenBveApi.Path.CombineDirectory(Plugin.FileSystem.DataFolder, "Compatibility"), "numbers.png"); // arial 9.5pt - Plugin.CurrentHost.QueryTextureDimensions(TexturePath, out wday, out hday); + int numMaxDigits = (int)Math.Floor(Math.Log10(Maximum) + 1); + int numMinDigits = (int)Math.Floor(Math.Log10(Minimum) + 1); - for (int i = 0; i < 10; i++) + int totalDigits = Math.Max(numMinDigits, numMaxDigits) + LeadingZeros; + elementIndex = -1; + double digitWidth = Size.X / totalDigits; + for (int currentDigit = 0; currentDigit < totalDigits; currentDigit++) + { + for (int k = 0; k < frameTextures.Length; k++) { - Plugin.CurrentHost.RegisterTexture(TexturePath, new TextureParameters(new TextureClipRegion(0, i * 24, 16, 24), null), out frameTextures[i], true); + int l = CabviewFileParser.CreateElement(ref currentCar.CarSections[CarSectionType.Interior].Groups[0], new Vector2(Position.X + Size.X - (digitWidth * (currentDigit + 1)), Position.Y), new Vector2(digitWidth * rW, Size.Y * rH), new Vector2(0.5, 0.5), componentLayer * CabviewFileParser.StackDistance, PanelPosition, frameTextures[k], null, textColor, k != 0); + if (k == 0) elementIndex = l; } - Plugin.CurrentHost.RegisterTexture(TexturePath, new TextureParameters(new TextureClipRegion(0, 0, 16, 24), null), out frameTextures[10], true); // repeated zero [check vice MSTS] - - int numMaxDigits = (int)Math.Floor(Math.Log10(Maximum) + 1); - int numMinDigits = (int)Math.Floor(Math.Log10(Minimum) + 1); + // build color arrays and mappings + currentCar.CarSections[CarSectionType.Interior].Groups[0].Elements[elementIndex].Colors = new Color24[NegativeColors.Length + PositiveColors.Length]; + FrameMappings = new FrameMapping[PositiveColors.Length + NegativeColors.Length]; + for (int i = 0; i < NegativeColors.Length; i++) + { + FrameMappings[i].MappingValue = NegativeColors[i].Item1; + FrameMappings[i].FrameKey = i; + currentCar.CarSections[CarSectionType.Interior].Groups[0].Elements[elementIndex].Colors[i] = NegativeColors[i].Item2; + } - int totalDigits = Math.Max(numMinDigits, numMaxDigits) + LeadingZeros; - j = -1; - double digitWidth = Size.X / totalDigits; - for (int currentDigit = 0; currentDigit < totalDigits; currentDigit++) + for (int i = 0; i < PositiveColors.Length; i++) { - for (int k = 0; k < frameTextures.Length; k++) - { - int l = CabviewFileParser.CreateElement(ref Car.CarSections[CarSectionType.Interior].Groups[0], new Vector2(Position.X + Size.X - (digitWidth * (currentDigit + 1)), Position.Y), new Vector2(digitWidth * rW, Size.Y * rH), new Vector2(0.5, 0.5), Layer * CabviewFileParser.StackDistance, PanelPosition, frameTextures[k], null, textColor, k != 0); - if (k == 0) j = l; - } + FrameMappings[i + NegativeColors.Length].MappingValue = PositiveColors[i].Item1; + FrameMappings[i + NegativeColors.Length].FrameKey = i + NegativeColors.Length; + currentCar.CarSections[CarSectionType.Interior].Groups[0].Elements[elementIndex].Colors[i + NegativeColors.Length] = PositiveColors[i].Item2; + } - // build color arrays and mappings - Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].Colors = new Color24[NegativeColors.Length + PositiveColors.Length]; - FrameMappings = new FrameMapping[PositiveColors.Length + NegativeColors.Length]; - for (int i = 0; i < NegativeColors.Length; i++) + // create color and digit functions + currentCar.CarSections[CarSectionType.Interior].Groups[0].Elements[elementIndex].StateFunction = new CvfAnimation(Plugin.CurrentHost, panelSubject, Units, currentDigit); + currentCar.CarSections[CarSectionType.Interior].Groups[0].Elements[elementIndex].ColorFunction = new CvfAnimation(Plugin.CurrentHost, panelSubject, Units, FrameMappings); + } + + break; + case CabComponentType.CabSignalDisplay: + TotalFrames = 8; + HorizontalFrames = 4; + VerticalFrames = 2; + Position.X *= rW; + Position.Y *= rH; + Size.X *= rW; + Size.Y *= rH; + Plugin.CurrentHost.QueryTextureDimensions(TexturePath, out wday, out hday); + if (wday > 0 & hday > 0) + { + Texture[] textures = new Texture[8]; + // 4 h-frames, 2 v-frames + int row = 0; + int column = 0; + int frameWidth = wday / HorizontalFrames; + int frameHeight = hday / VerticalFrames; + for (int k = 0; k < TotalFrames; k++) + { + Plugin.CurrentHost.RegisterTexture(TexturePath, new TextureParameters(new TextureClipRegion(column * frameWidth, row * frameHeight, frameWidth, frameHeight), null), out textures[k]); + if (column < HorizontalFrames - 1) { - FrameMappings[i].MappingValue = NegativeColors[i].Item1; - FrameMappings[i].FrameKey = i; - Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].Colors[i] = NegativeColors[i].Item2; + column++; } - - for (int i = 0; i < PositiveColors.Length; i++) + else { - FrameMappings[i + NegativeColors.Length].MappingValue = PositiveColors[i].Item1; - FrameMappings[i + NegativeColors.Length].FrameKey = i + NegativeColors.Length; - Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].Colors[i + NegativeColors.Length] = PositiveColors[i].Item2; + column = 0; + row++; } - - // create color and digit functions - Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].StateFunction = new CvfAnimation(panelSubject, Units, currentDigit); - Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].ColorFunction = new CvfAnimation(panelSubject, Units, FrameMappings); } - break; - case CabComponentType.CabSignalDisplay: - TotalFrames = 8; - HorizontalFrames = 4; - VerticalFrames = 2; - Position.X *= rW; - Position.Y *= rH; - Size.X *= rW; - Size.Y *= rH; - Plugin.CurrentHost.QueryTextureDimensions(TexturePath, out wday, out hday); - if (wday > 0 & hday > 0) + elementIndex = -1; + for (int k = 0; k < textures.Length; k++) { - Texture[] textures = new Texture[8]; - // 4 h-frames, 2 v-frames - int row = 0; - int column = 0; - int frameWidth = wday / HorizontalFrames; - int frameHeight = hday / VerticalFrames; - for (int k = 0; k < TotalFrames; k++) - { - Plugin.CurrentHost.RegisterTexture(TexturePath, new TextureParameters(new TextureClipRegion(column * frameWidth, row * frameHeight, frameWidth, frameHeight), null), out textures[k]); - if (column < HorizontalFrames - 1) - { - column++; - } - else - { - column = 0; - row++; - } - } + int l = CabviewFileParser.CreateElement(ref currentCar.CarSections[CarSectionType.Interior].Groups[0], Position, Size, new Vector2(0.5, 0.5), componentLayer * CabviewFileParser.StackDistance, PanelPosition, textures[k], null, new Color32(255, 255, 255, 255), k != 0); + if (k == 0) elementIndex = l; + } - j = -1; - for (int k = 0; k < textures.Length; k++) - { - int l = CabviewFileParser.CreateElement(ref Car.CarSections[CarSectionType.Interior].Groups[0], Position, Size, new Vector2(0.5, 0.5), Layer * CabviewFileParser.StackDistance, PanelPosition, textures[k], null, new Color32(255, 255, 255, 255), k != 0); - if (k == 0) j = l; - } + currentCar.CarSections[CarSectionType.Interior].Groups[0].Elements[elementIndex].StateFunction = new CvfAnimation(Plugin.CurrentHost, panelSubject); - Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].StateFunction = new CvfAnimation(panelSubject); + } + break; + case CabComponentType.DigitalClock: + Position.X *= rW; + Position.Y *= rH; + totalDigits = Accuracy == 1 ? 8 : 5; // with or without secs + elementIndex = -1; + digitWidth = Size.X / totalDigits; + textColor = ControlColor; + + frameTextures = new Texture[12]; + TexturePath = OpenBveApi.Path.CombineFile(OpenBveApi.Path.CombineDirectory(Plugin.FileSystem.DataFolder, "Compatibility"), "numbers.png"); // arial 9.5pt + Plugin.CurrentHost.QueryTextureDimensions(TexturePath, out wday, out hday); + + for (int i = 0; i < 10; i++) + { + Plugin.CurrentHost.RegisterTexture(TexturePath, new TextureParameters(new TextureClipRegion(0, i * 24, 16, 24), null), out frameTextures[i], true); + } + Plugin.CurrentHost.RegisterTexture(TexturePath, new TextureParameters(new TextureClipRegion(0, 240, 16, 16), null), out frameTextures[11], true); + for (int currentDigit = 0; currentDigit < totalDigits; currentDigit++) + { + for (int k = 0; k < frameTextures.Length; k++) + { + int l = CabviewFileParser.CreateElement(ref currentCar.CarSections[CarSectionType.Interior].Groups[0], new Vector2(Position.X + Size.X - (digitWidth * (currentDigit + 1)), Position.Y), new Vector2(digitWidth * rW, Size.Y * rH), new Vector2(0.5, 0.5), componentLayer * CabviewFileParser.StackDistance, PanelPosition, frameTextures[k], null, textColor, k != 0); + if (k == 0) elementIndex = l; } - break; - } + // create digit functions + currentCar.CarSections[CarSectionType.Interior].Groups[0].Elements[elementIndex].StateFunction = new CvfAnimation(Plugin.CurrentHost, panelSubject, Style, Accuracy == 1 ? currentDigit : currentDigit + 3); + } + break; } } @@ -380,10 +422,27 @@ private void ReadSubBlock(Block block) TotalFrames = block.ReadInt16(); HorizontalFrames = block.ReadInt16(); VerticalFrames = block.ReadInt16(); + FrameMappings = new FrameMapping[TotalFrames]; + for (int i = 0; i < TotalFrames; i++) + { + FrameMappings[i].FrameKey = i; + var stateBlock = block.ReadSubBlock(KujuTokenID.State); + while (stateBlock.Length() - stateBlock.Position() > 3) + { + Block subBlock = stateBlock.ReadSubBlock(new[] { KujuTokenID.Style, KujuTokenID.SwitchVal }); + if (subBlock.Token == KujuTokenID.SwitchVal) + { + FrameMappings[i].MappingValue = subBlock.ReadSingle(); + break; + } + } + + + } break; case KujuTokenID.DirIncrease: // rotates Clockwise (0) or AntiClockwise (1) - DirIncrease = block.ReadInt16() == 1; + DirIncrease = block.ReadBool(); break; case KujuTokenID.Orientation: //Flip? @@ -416,14 +475,33 @@ private void ReadSubBlock(Block block) VerticalFrames = block.ReadInt16(); break; case KujuTokenID.MouseControl: - MouseControl = block.ReadInt16() == 1; + MouseControl = block.ReadBool(); break; case KujuTokenID.Style: - block.Skip((int)block.Length()); + Style = block.ReadEnumValue(default(CabComponentStyle)); break; case KujuTokenID.Graphic: string s = block.ReadString(); - TexturePath = OpenBveApi.Path.CombineFile(CabviewFileParser.CurrentFolder, s); + if (!string.IsNullOrEmpty(s)) + { + try + { + TexturePath = OpenBveApi.Path.CombineFile(CabviewFileParser.CurrentFolder, s); + } + catch + { + Plugin.CurrentHost.AddMessage(MessageType.Error, true, "MSTS CVF Parser: The texture path contains invalid characters in CabComponent " + Type); + } + + if (!File.Exists(TexturePath)) + { + Plugin.CurrentHost.AddMessage(MessageType.Error, true, "MSTS CVF Parser: The texture file " + s + " was not found in CabComponent " + Type); + } + } + else + { + Plugin.CurrentHost.AddMessage(MessageType.Error, true, "MSTS CVF Parser: A texture file was not specified in CabComponent " + Type); + } break; case KujuTokenID.Position: Position.X = block.ReadSingle(); @@ -501,7 +579,12 @@ private void ReadSubBlock(Block block) } } } - + break; + case KujuTokenID.ControlColour: + ControlColor = new Color24((byte)block.ReadInt16(), (byte)block.ReadInt16(), (byte)block.ReadInt16()); + break; + case KujuTokenID.Accuracy: + Accuracy = block.ReadInt16(); break; } } diff --git a/source/Plugins/Train.MsTs/Panel/CvfAnimation.cs b/source/Plugins/Train.MsTs/Panel/CvfAnimation.cs new file mode 100644 index 0000000000..84e80f143e --- /dev/null +++ b/source/Plugins/Train.MsTs/Panel/CvfAnimation.cs @@ -0,0 +1,491 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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 OpenBveApi.FunctionScripting; +using OpenBveApi.Math; +using OpenBveApi.Motor; +using OpenBveApi.Trains; +using System; +using System.Collections.Generic; +using OpenBveApi; +using TrainManager.Car.Systems; +using TrainManager.Motor; +using OpenBveApi.Hosts; +using TrainManager.SafetySystems; + +namespace Train.MsTs +{ + /// Animation class handling CVF elements with keyframe mappings + internal class CvfAnimation : AnimationScript + { + internal readonly HostInterface CurrentHost; + + internal readonly PanelSubject Subject; + + internal readonly FrameMapping[] FrameMapping; + + internal readonly int Digit; + + internal readonly double UnitConversionFactor; + + private double lastResult; + + internal CvfAnimation(HostInterface host, PanelSubject subject) + { + CurrentHost = host; + Subject = subject; + switch (subject) + { + case PanelSubject.Aspect_Display: + Minimum = 0; + Maximum = 7; + break; + default: + Minimum = 0; + Maximum = double.MaxValue; + break; + } + Digit = -1; + } + + internal CvfAnimation(HostInterface host, PanelSubject subject, FrameMapping[] frameMapping) + { + CurrentHost = host; + Subject = subject; + FrameMapping = frameMapping; + Minimum = 0; + Maximum = FrameMapping.Length; + Digit = -1; + } + + internal CvfAnimation(HostInterface host, PanelSubject subject, Units unit, int digit) + { + CurrentHost = host; + Subject = subject; + switch (unit) + { + case Units.Miles_Per_Hour: + UnitConversionFactor = 2.2369362920544; + break; + case Units.Kilometers_Per_Hour: + UnitConversionFactor = 3.6; + break; + case Units.PSI: + UnitConversionFactor = 0.000145038; + break; + } + Digit = digit; + } + + internal CvfAnimation(HostInterface host, PanelSubject subject, Units unit, FrameMapping[] frameMapping) + { + CurrentHost = host; + Subject = subject; + switch (unit) + { + case Units.Miles_Per_Hour: + UnitConversionFactor = 2.2369362920544; + break; + case Units.Kilometers_Per_Hour: + UnitConversionFactor = 3.6; + break; + } + + Digit = -1; + FrameMapping = frameMapping; + } + + internal CvfAnimation(HostInterface host, PanelSubject subject, CabComponentStyle style, int digit) + { + CurrentHost = host; + Subject = subject; + Digit = digit; + switch (style) + { + case CabComponentStyle.TwelveHour: + UnitConversionFactor = 1; + break; + case CabComponentStyle.TwentyFourHour: + UnitConversionFactor = 0; + break; + } + } + + public double ExecuteScript(AbstractTrain train, int carIndex, Vector3 position, double trackPosition, int sectionIndex, bool isPartOfTrain, double timeElapsed, int currentState) + { + dynamic dynamicTrain = train; + switch (Subject) + { + case PanelSubject.CP_Handle: + case PanelSubject.CPH_Display: + // NOTE: 0.5 mapping == N + double mapping = 0.5; + if (dynamicTrain.Handles.Brake.Actual > 0) + { + mapping += (double)dynamicTrain.Handles.Brake.Actual / dynamicTrain.Handles.Brake.MaximumNotch * 0.5; + } + else + { + mapping -= (double)dynamicTrain.Handles.Power.Actual / dynamicTrain.Handles.Power.MaximumNotch * 0.5; + } + MapResult(mapping); + break; + case PanelSubject.Throttle_Display: + case PanelSubject.Throttle: + MapResult((double)dynamicTrain.Handles.Power.Actual / dynamicTrain.Handles.Power.MaximumNotch); + break; + case PanelSubject.Train_Brake: + MapResult((double)dynamicTrain.Handles.Brake.Actual / dynamicTrain.Handles.Brake.MaximumNotch); + break; + case PanelSubject.Friction_Braking: + // NOTE: Assumed at the minute this goes out at speed zero + bool isBraking = Math.Abs(train.CurrentSpeed) > 0 && (dynamicTrain.Handles.Brake.Actual > 0 || (dynamicTrain.Handles.HasLocoBrake && dynamicTrain.Handles.LocoBrake.Actual > 0)); + MapResult(isBraking ? 1 : 0); + break; + case PanelSubject.Direction_Display: + case PanelSubject.Direction: + lastResult = (int)dynamicTrain.Handles.Reverser.Actual + 1; + break; + case PanelSubject.Speedlim_Display: + double speedLim = Math.Min(train.CurrentRouteLimit, train.CurrentSectionLimit) * UnitConversionFactor; + if (Digit == -1) + { + // color + for (int i = 0; i < FrameMapping.Length; i++) + { + if (FrameMapping[i].MappingValue <= speedLim) + { + lastResult = FrameMapping[i].FrameKey; + break; + } + } + } + else + { + // digit + if (speedLim == double.PositiveInfinity) + { + lastResult = -1; // cheat to hide + } + else + { + lastResult = (int)(speedLim / (int)Math.Pow(10, Digit) % 10); + } + } + break; + case PanelSubject.Speedometer: + double currentSpeed = Math.Abs(train.CurrentSpeed) * UnitConversionFactor; + MapDigitalResult(currentSpeed); + break; + case PanelSubject.Aspect_Display: + lastResult = train.CurrentSignalAspect; + break; + case PanelSubject.Overspeed: + double currentLimit = Math.Min(train.CurrentRouteLimit, train.CurrentSectionLimit); + lastResult = Math.Abs(train.CurrentSpeed) > currentLimit ? 1 : 0; + break; + case PanelSubject.Front_Hlight: + lastResult = dynamicTrain.SafetySystems.Headlights.CurrentState; + break; + case PanelSubject.Pantograph: + case PanelSubject.Panto_Display: + int pantographState = 0; + for (int k = 0; k < dynamicTrain.Cars.Length; k++) + { + if (dynamicTrain.Cars[k].TractionModel is ElectricEngine electricEngine && + electricEngine.Components.TryGetTypedValue(EngineComponent.Pantograph, out Pantograph pantograph)) + { + pantographState = (int)pantograph.State; + break; + } + } + lastResult = pantographState; + break; + case PanelSubject.Gears: + int gearState = 0; + for (int k = 0; k < dynamicTrain.Cars.Length; k++) + { + if (dynamicTrain.Cars[k].TractionModel is DieselEngine dieselEngine && + dieselEngine.Components.TryGetTypedValue(EngineComponent.Gearbox, out Gearbox gearbox)) + { + gearState = gearbox.CurrentGear; + break; + } + } + lastResult = gearState; + break; + case PanelSubject.Sanders: + // sanders button is pressed + int sandState = 0; + for (int k = 0; k < dynamicTrain.Cars.Length; k++) + { + if (dynamicTrain.Cars[k].ReAdhesionDevice is Sanders sanders) + { + sandState = sanders.State >= SandersState.Active ? 1 :0; + break; + } + } + lastResult = sandState; + break; + case PanelSubject.Sanding: + // sand is actually being dispensed / doing something + sandState = 0; + for (int k = 0; k < dynamicTrain.Cars.Length; k++) + { + if (dynamicTrain.Cars[k].ReAdhesionDevice is Sanders sanders && train.CurrentSpeed <= sanders.MaximumSpeed) + { + sandState = sanders.State == SandersState.Active ? 1 : 0; + break; + } + } + lastResult = sandState; + break; + case PanelSubject.Engine_Brake: + if (!dynamicTrain.Handles.HasLocoBrake) + { + lastResult = 0; + break; + } + for (int i = 0; i < FrameMapping.Length; i++) + { + if (FrameMapping[i].MappingValue >= (double)dynamicTrain.Handles.LocoBrake.Actual / dynamicTrain.Handles.LocoBrake.MaximumNotch) + { + lastResult = FrameMapping[i].FrameKey; + break; + } + } + break; + case PanelSubject.Wheelslip: + int wheelSlip = 0; + for (int k = 0; k < dynamicTrain.Cars.Length; k++) + { + if (dynamicTrain.Cars[k].FrontAxle.CurrentWheelSlip || dynamicTrain.Cars[k].RearAxle.CurrentWheelSlip) + { + wheelSlip = 1; + break; + } + } + lastResult = wheelSlip; + break; + case PanelSubject.Clock: + double hour = Math.Floor(CurrentHost.InGameTime / 3600.0); + hour %= 24; + if (UnitConversionFactor == 1) + { + if (hour > 12) + { + hour -= 12; + } + } + double min = Math.Floor(CurrentHost.InGameTime / 60 % 60); + double sec = CurrentHost.InGameTime % 60; + switch (Digit) + { + case 7: + // H + lastResult = hour >= 10 ? (int)(hour / 10) : 0; + break; + case 6: + // HH + lastResult = (int)(hour % 10); + break; + case 5: + // HH: + lastResult = 11; + break; + case 4: + // HH:M + lastResult = min >= 10 ? (int)(min / 10) : 0; + break; + case 3: + // HH:MM + lastResult = (int)(min % 10); + break; + case 2: + // HH:MM: + lastResult = 11; + break; + case 1: + // HH:MM:S + lastResult = sec >= 10 ? (int)(sec / 10) : 0; + break; + case 0: + // HH:MM:SS + lastResult = (int)(sec % 10); + break; + } + break; + case PanelSubject.Alerter_Display: + // can't use an extension on a dynamic directly, have to get to known type first + Dictionary safetySystems = dynamicTrain.Cars[dynamicTrain.DriverCar].SafetySystems; + if (safetySystems.TryGetTypedValue(SafetySystem.DriverSupervisionDevice, out DriverSupervisionDevice dsd) && dsd.CurrentState == SafetySystemState.Alarm) + { + lastResult = 1; + } + if (safetySystems.TryGetTypedValue(SafetySystem.OverspeedDevice, out OverspeedDevice osd) && osd.CurrentState == SafetySystemState.Alarm) + { + lastResult = 1; + } + break; + case PanelSubject.Penalty_App: + safetySystems = dynamicTrain.Cars[dynamicTrain.DriverCar].SafetySystems; + if (safetySystems.TryGetTypedValue(SafetySystem.DriverSupervisionDevice, out dsd) && dsd.CurrentState == SafetySystemState.Triggered) + { + lastResult = 1; + } + if (safetySystems.TryGetTypedValue(SafetySystem.OverspeedDevice, out osd) && osd.CurrentState == SafetySystemState.Triggered) + { + lastResult = 1; + } + break; + case PanelSubject.Load_Meter: + case PanelSubject.Ammeter: + case PanelSubject.Ammeter_Abs: + double amps = 0; + if (dynamicTrain != null) + { + int totalMotors = 0; + double ampsTotal = 0; + for (int k = 0; k < dynamicTrain.Cars.Length; k++) + { + if (dynamicTrain.Cars[k].TractionModel is DieselEngine dieselEngine) + { + if (dieselEngine.Components.TryGetTypedValue(EngineComponent.TractionMotor, out TractionMotor t)) + { + totalMotors++; + ampsTotal += t.CurrentAmps; + } + else if (dieselEngine.Components.TryGetTypedValue(EngineComponent.RegenerativeTractionMotor, out RegenerativeTractionMotor rt)) + { + totalMotors++; + ampsTotal += rt.CurrentAmps; + } + } + } + + if (totalMotors == 0) + { + amps = 0; + } + else + { + amps = ampsTotal / totalMotors; + if (Subject == PanelSubject.Ammeter_Abs) + { + amps = Math.Abs(amps); + } + } + } + else + { + amps = 0; + } + MapDigitalResult(amps); + break; + case PanelSubject.Brake_Pipe: + double bp = dynamicTrain.Cars[dynamicTrain.DriverCar].CarBrake.BrakePipe.CurrentPressure; + bp *= UnitConversionFactor; + MapDigitalResult(bp); + break; + case PanelSubject.Eq_Res: + double er = dynamicTrain.Cars[dynamicTrain.DriverCar].CarBrake.EqualizingReservoir.CurrentPressure; + er *= UnitConversionFactor; + MapDigitalResult(er); + break; + case PanelSubject.Brake_Cyl: + double bc = dynamicTrain.Cars[dynamicTrain.DriverCar].CarBrake.BrakeCylinder.CurrentPressure; + bc *= UnitConversionFactor; + MapDigitalResult(bc); + break; + case PanelSubject.Main_Res: + double mr = dynamicTrain.Cars[dynamicTrain.DriverCar].CarBrake.MainReservoir.CurrentPressure; + mr *= UnitConversionFactor; + MapDigitalResult(mr); + break; + } + return lastResult; + } + + private void MapResult(double val) + { + for (int i = 0; i < FrameMapping.Length; i++) + { + if (FrameMapping[i].MappingValue >= val) + { + lastResult = FrameMapping[i].FrameKey; + break; + } + } + } + + private void MapDigitalResult(double val) + { + if (Digit == -1) + { + // color + lastResult = FrameMapping[FrameMapping.Length - 1].FrameKey; + for (int i = 0; i < FrameMapping.Length; i++) + { + if (FrameMapping[i].MappingValue <= val) + { + if (i == FrameMapping.Length - 1 || FrameMapping[i + 1].MappingValue > val) + { + lastResult = FrameMapping[i].FrameKey; + } + break; + } + } + } + else + { + // digit + double absVal = Math.Abs(val); + lastResult = (int)(absVal / (int)Math.Pow(10, Digit) % 10); + } + } + + public AnimationScript Clone() + { + return new CvfAnimation(CurrentHost, Subject, FrameMapping); + } + + public double LastResult + { + get => lastResult; + set { } + } + + public double Maximum + { + get; + set; + } + + public double Minimum + { + get; + set; + } + } +} diff --git a/source/Plugins/Train.MsTs/Panel/CvfParser.cs b/source/Plugins/Train.MsTs/Panel/CvfParser.cs new file mode 100644 index 0000000000..398b52a458 --- /dev/null +++ b/source/Plugins/Train.MsTs/Panel/CvfParser.cs @@ -0,0 +1,576 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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 LibRender2.Trains; +using OpenBve.Formats.MsTs; +using OpenBveApi.Colors; +using OpenBveApi.Graphics; +using OpenBveApi.Interface; +using OpenBveApi.Math; +using OpenBveApi.Objects; +using OpenBveApi.Textures; +using SharpCompress.Compressors; +using SharpCompress.Compressors.Deflate; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using OpenBveApi.World; +using TrainManager.Car; +using TrainManager.Trains; + +namespace Train.MsTs +{ + internal class CabviewFileParser + { + // constants + internal const double StackDistance = 0.000001; + + /// EyeDistance is required to be 1.0 by UpdateCarSectionElement and by UpdateCameraRestriction, thus cannot be easily changed. + private const double eyeDistance = 1.0; + + internal static string CurrentFolder; + + internal static string FileName; + + private static readonly List cabComponents = new List(); + + // parse panel config + internal static bool ParseCabViewFile(string fileName, ref CarBase currentCar) + { + FileName = fileName; + cabComponents.Clear(); + CurrentFolder = Path.GetDirectoryName(fileName); + Stream fb = new FileStream(fileName, FileMode.Open, FileAccess.Read); + + byte[] buffer = new byte[34]; + fb.Read(buffer, 0, 2); + + bool unicode = buffer[0] == 0xFF && buffer[1] == 0xFE; + + string headerString; + if (unicode) + { + fb.Read(buffer, 0, 32); + headerString = Encoding.Unicode.GetString(buffer, 0, 16); + } + else + { + fb.Read(buffer, 2, 14); + headerString = Encoding.ASCII.GetString(buffer, 0, 8); + } + + // SIMISA@F means compressed + // SIMISA@@ means uncompressed + if (headerString.StartsWith("SIMISA@F")) + { + fb = new ZlibStream(fb, CompressionMode.Decompress); + } + else if (headerString.StartsWith("\r\nSIMISA")) + { + // ie us1rd2l1000r10d.s, we are going to allow this but warn + Console.Error.WriteLine("Improper header in " + fileName); + fb.Read(buffer, 0, 4); + } + else if (!headerString.StartsWith("SIMISA@@")) + { + Plugin.CurrentHost.AddMessage(MessageType.Error, false, "MSTS CVF Parser: Unrecognized cabview file header " + headerString + " in " + FileName); + return false; + } + + string subHeader; + if (unicode) + { + fb.Read(buffer, 0, 32); + subHeader = Encoding.Unicode.GetString(buffer, 0, 16); + } + else + { + fb.Read(buffer, 0, 16); + subHeader = Encoding.ASCII.GetString(buffer, 0, 8); + } + + if (subHeader[7] == 't') + { + using (BinaryReader reader = new BinaryReader(fb)) + { + byte[] newBytes = reader.ReadBytes((int) (fb.Length - fb.Position)); + string s = unicode ? Encoding.Unicode.GetString(newBytes) : Encoding.ASCII.GetString(newBytes); + TextualBlock block = new TextualBlock(s, KujuTokenID.Tr_CabViewFile); + ParseBlock(block); + } + + } + else if (subHeader[7] != 'b') + { + Plugin.CurrentHost.AddMessage(MessageType.Error, false, "MSTS CVF Parser: Unrecognized subHeader " + subHeader + " in " + FileName); + return false; + } + else + { + using (BinaryReader reader = new BinaryReader(fb)) + { + KujuTokenID currentToken = (KujuTokenID) reader.ReadUInt16(); + if (currentToken != KujuTokenID.Tr_CabViewFile) + { + throw new Exception(); //Shape definition + } + + reader.ReadUInt16(); + uint remainingBytes = reader.ReadUInt32(); + byte[] newBytes = reader.ReadBytes((int) remainingBytes); + BinaryBlock block = new BinaryBlock(newBytes, KujuTokenID.Tr_CabViewFile); + ParseBlock(block); + } + } + + //Create panel + //Create camera restriction + double worldWidth, worldHeight; + if (Plugin.Renderer.Screen.Width >= Plugin.Renderer.Screen.Height) + { + worldWidth = 2.0 * Math.Tan(0.5 * Plugin.Renderer.Camera.HorizontalViewingAngle) * eyeDistance; + worldHeight = worldWidth / Plugin.Renderer.Screen.AspectRatio; + } + else + { + worldHeight = 2.0 * Math.Tan(0.5 * Plugin.Renderer.Camera.VerticalViewingAngle) * eyeDistance / Plugin.Renderer.Screen.AspectRatio; + worldWidth = worldHeight * Plugin.Renderer.Screen.AspectRatio; + } + + double x0 = -panelCenter.X / panelResolution; + double x1 = (panelSize.X - panelCenter.X) / panelResolution; + double y0 = (panelCenter.Y - panelSize.Y) / panelResolution * Plugin.Renderer.Screen.AspectRatio; + double y1 = panelCenter.Y / panelResolution * Plugin.Renderer.Screen.AspectRatio; + currentCar.CameraRestriction.BottomLeft = new Vector3(x0 * worldWidth, y0 * worldHeight, eyeDistance); + currentCar.CameraRestriction.TopRight = new Vector3(x1 * worldWidth, y1 * worldHeight, eyeDistance); + currentCar.DriverYaw = Math.Atan((panelCenter.X - panelOrigin.X) * worldWidth / panelResolution); + currentCar.DriverPitch = Math.Atan((panelOrigin.Y - panelCenter.Y) * worldWidth / panelResolution); + + if(cabViews.Count == 0 || !File.Exists(cabViews[0].FileName)) + { + return false; + } + currentCar.CameraRestrictionMode = CameraRestrictionMode.On; + Plugin.Renderer.Camera.CurrentRestriction = CameraRestrictionMode.On; + currentCar.Driver = cabViews[0].Position; + for (int i = 0; i < cabViews.Count; i++) + { + + Plugin.CurrentHost.RegisterTexture(cabViews[i].FileName, new TextureParameters(null, null), out Texture tday, true); + switch (i) + { + case 0: + currentCar.CarSections.Add(CarSectionType.Interior, new CarSection(Plugin.CurrentHost, ObjectType.Overlay, true, currentCar)); + CreateElement(ref currentCar.CarSections[CarSectionType.Interior].Groups[0], Vector2.Null, panelSize, new Vector2(0.5, 0.5), 0.0, cabViews[0].Position, tday, null, new Color32(255, 255, 255, 255)); + currentCar.CarSections[CarSectionType.Interior].ViewDirection = new Transformation(cabViews[0].Direction.Y.ToRadians(), -cabViews[0].Direction.X.ToRadians(), -cabViews[0].Direction.Z.ToRadians()); + break; + case 1: + currentCar.CarSections.Add(CarSectionType.HeadOutLeft, new CarSection(Plugin.CurrentHost, ObjectType.Overlay, true, currentCar)); + CreateElement(ref currentCar.CarSections[CarSectionType.HeadOutLeft].Groups[0], Vector2.Null, panelSize, new Vector2(0.5, 0.5), 0.0, cabViews[1].Position, tday, null, new Color32(255, 255, 255, 255)); + currentCar.CarSections[CarSectionType.HeadOutLeft].ViewDirection = new Transformation(cabViews[1].Direction.Y.ToRadians(), -cabViews[1].Direction.X.ToRadians(), -cabViews[1].Direction.Z.ToRadians()); + break; + case 2: + currentCar.CarSections.Add(CarSectionType.HeadOutRight, new CarSection(Plugin.CurrentHost, ObjectType.Overlay, true, currentCar)); + CreateElement(ref currentCar.CarSections[CarSectionType.HeadOutRight].Groups[0], Vector2.Null, panelSize, new Vector2(0.5, 0.5), 0.0, cabViews[2].Position, tday, null, new Color32(255, 255, 255, 255)); + currentCar.CarSections[CarSectionType.HeadOutRight].ViewDirection = new Transformation(cabViews[2].Direction.Y.ToRadians(), -cabViews[2].Direction.X.ToRadians(), -cabViews[2].Direction.Z.ToRadians()); + break; + } + } + + int currentLayer = 1; + for (int i = 0; i < cabComponents.Count; i++) + { + cabComponents[i].Create(ref currentCar, currentLayer); + currentLayer++; // component layering stacks downwards directly through the cabview + } + + return true; + } + + private static CabView currentCabView; + + private static readonly List cabViews = new List(); + + private static void ParseBlock(Block block) + { + Block newBlock; + switch (block.Token) + { + case KujuTokenID.CabViewControls: + int count = block.ReadInt16(); + int controlCount = count; + while (block.Length() - block.Position() > 5) + { + newBlock = block.ReadSubBlock(true); + if (newBlock.Token == KujuTokenID.Skip) + { + continue; + } + CabComponent currentComponent = new CabComponent(newBlock, cabViews[0].Position); // cab components can only be applied to CabView #0, others are static views + currentComponent.Parse(); + cabComponents.Add(currentComponent); + controlCount--; + } + + if (controlCount != 0) + { + // control count was wrong... + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "MSTS CVF Parser: Expected " + count + " controls, but found " + (count - controlCount) + " in file " + FileName); + } + + break; + case KujuTokenID.Direction: + currentCabView.Direction.X = block.ReadSingle(); + currentCabView.Direction.Y = block.ReadSingle(); + currentCabView.Direction.Z = block.ReadSingle(); + break; + case KujuTokenID.Position: + currentCabView.Position.X = block.ReadSingle(); + currentCabView.Position.Y = block.ReadSingle(); + currentCabView.Position.Z = block.ReadSingle(); + break; + case KujuTokenID.CabViewWindow: + currentCabView.TopLeft.X = block.ReadInt16(); + currentCabView.TopLeft.Y = block.ReadInt16(); + currentCabView.PanelSize.X = block.ReadInt16(); + currentCabView.PanelSize.Y = block.ReadInt16(); + break; + case KujuTokenID.CabViewFile: + case KujuTokenID.CabViewWindowFile: + if (string.IsNullOrEmpty(currentCabView.FileName)) + { + currentCabView.SetCabView(CurrentFolder, block.ReadString()); + } + + break; + case KujuTokenID.CabViewType: + case KujuTokenID.EngineData: + block.Skip((int) block.Length()); + break; + case KujuTokenID.Tr_CabViewFile: + newBlock = block.ReadSubBlock(KujuTokenID.CabViewType); + ParseBlock(newBlock); + //The main front cabview + newBlock = block.ReadSubBlock(KujuTokenID.CabViewFile); + ParseBlock(newBlock); + newBlock = block.ReadSubBlock(KujuTokenID.CabViewWindow); + ParseBlock(newBlock); + newBlock = block.ReadSubBlock(KujuTokenID.CabViewWindowFile); //Appears to be a duplicate of CabViewFile, some are empty or v/v + ParseBlock(newBlock); + newBlock = block.ReadSubBlock(KujuTokenID.Position); //Position within loco X,Y,Z + ParseBlock(newBlock); + newBlock = block.ReadSubBlock(KujuTokenID.Direction); // ?? CAMERA DIRECTION ==> ROT Y, ROT X, ROT Z + ParseBlock(newBlock); + cabViews.Add(currentCabView); + currentCabView = new CabView(); + //View #2, normally L + newBlock = block.ReadSubBlock(KujuTokenID.CabViewFile); + ParseBlock(newBlock); + newBlock = block.ReadSubBlock(KujuTokenID.CabViewWindow); + ParseBlock(newBlock); + newBlock = block.ReadSubBlock(KujuTokenID.CabViewWindowFile); //Appears to be a duplicate of CabViewFile, some are empty or v/v + ParseBlock(newBlock); + newBlock = block.ReadSubBlock(KujuTokenID.Position); //Position within loco X,Y,Z + ParseBlock(newBlock); + newBlock = block.ReadSubBlock(KujuTokenID.Direction); // ?? CAMERA DIRECTION ==> ROT Y, ROT X, ROT Z + ParseBlock(newBlock); + cabViews.Add(currentCabView); + currentCabView = new CabView(); + //View #3, normally R + newBlock = block.ReadSubBlock(KujuTokenID.CabViewFile); + ParseBlock(newBlock); + newBlock = block.ReadSubBlock(KujuTokenID.CabViewWindow); + ParseBlock(newBlock); + newBlock = block.ReadSubBlock(KujuTokenID.CabViewWindowFile); //Appears to be a duplicate of CabViewFile, some are empty or v/v + ParseBlock(newBlock); + newBlock = block.ReadSubBlock(KujuTokenID.Position); //Position within loco X,Y,Z + ParseBlock(newBlock); + newBlock = block.ReadSubBlock(KujuTokenID.Direction); // ?? CAMERA DIRECTION ==> ROT Y, ROT X, ROT Z + ParseBlock(newBlock); + cabViews.Add(currentCabView); + newBlock = block.ReadSubBlock(KujuTokenID.EngineData); + ParseBlock(newBlock); + newBlock = block.ReadSubBlock(KujuTokenID.CabViewControls); + ParseBlock(newBlock); + break; + } + } + + private const double panelResolution = 1024.0; + private static readonly Vector2 panelSize = new Vector2(1024, 768); + private static readonly Vector2 panelCenter = new Vector2(0, 240); + private static readonly Vector2 panelOrigin = new Vector2(0, 240); + + // get stack language from subject + internal static string GetStackLanguageFromSubject(TrainBase train, PanelSubject subject, Units subjectUnits) + { + // transform subject + string Code = string.Empty; + switch (subject) + { + case PanelSubject.Load_Meter: + case PanelSubject.Ammeter: + Code = "amps"; + break; + case PanelSubject.Ammeter_Abs: + Code = "amps abs"; + break; + case PanelSubject.Brake_Cyl: + switch (subjectUnits) + { + case Units.PSI: + Code = "brakecylinder 0.000145038 *"; + break; + case Units.Inches_Of_Mercury: + Code = "brakecylinder 0.0002953 *"; + break; + case Units.Kilopascals: + Code = "brakecylinder 0.001 *"; + break; + case Units.Bar: + Code = "brakecylinder 0.00001 *"; + break; + case Units.Kgs_Per_Square_Cm: + Code = "brakecylinder 98066.5 *"; + break; + } + break; + case PanelSubject.Brake_Pipe: + switch (subjectUnits) + { + case Units.PSI: + Code = "brakepipe 0.000145038 *"; + break; + case Units.Inches_Of_Mercury: + Code = "brakepipe 0.0002953 *"; + break; + case Units.Kilopascals: + Code = "brakepipe 0.001 *"; + break; + case Units.Bar: + Code = "brakepipe 0.00001 *"; + break; + case Units.Kgs_Per_Square_Cm: + Code = "brakepipe 98066.5 *"; + break; + } + break; + case PanelSubject.Main_Res: + case PanelSubject.Vacuum_Reservoir_Pressure: + switch (subjectUnits) + { + case Units.PSI: + Code = "mainreservoir 0.000145038 *"; + break; + case Units.Inches_Of_Mercury: + Code = "mainreservoir 0.0002953 *"; + break; + case Units.Kilopascals: + Code = "mainreservoir 0.001 *"; + break; + case Units.Bar: + Code = "mainreservoir 0.00001 *"; + break; + case Units.Kgs_Per_Square_Cm: + Code = "mainreservoir 98066.5 *"; + break; + } + break; + case PanelSubject.Eq_Res: + switch (subjectUnits) + { + case Units.PSI: + Code = "equalizingreservoir 0.000145038 *"; + break; + case Units.Inches_Of_Mercury: + Code = "equalizingreservoir 0.0002953 *"; + break; + case Units.Kilopascals: + Code = "equalizingreservoir 0.001 *"; + break; + case Units.Bar: + Code = "equalizingreservoir 0.00001 *"; + break; + case Units.Kgs_Per_Square_Cm: + Code = "equalizingreservoir 98066.5 *"; + break; + } + break; + case PanelSubject.Direction: + Code = "reverserNotch ++"; + break; + case PanelSubject.Engine_Brake: + Code = "locoBrakeNotch"; + break; + case PanelSubject.Front_Hlight: + Code = "headlights"; + break; + case PanelSubject.Bell: + Code = "musichorn"; + break; + case PanelSubject.Whistle: + case PanelSubject.Horn: + Code = "horn"; + break; + case PanelSubject.Speedometer: + // use speed not speedometer at the minute as wheelslip isn't right + switch (subjectUnits) + { + case Units.Miles_Per_Hour: + Code = "speed abs 2.2369362920544 *"; + break; + case Units.Kilometers_Per_Hour: + Code = "speed abs 3.6 *"; + break; + } + break; + case PanelSubject.Throttle: + Code = "brakeNotchLinear 0 powerNotch ?"; + break; + case PanelSubject.Train_Brake: + Code = "brakeNotchLinear"; + break; + case PanelSubject.Wipers: + Code = "wiperstate"; + break; + case PanelSubject.Panto_Display: + case PanelSubject.Pantograph: + Code = "pantographstate"; + break; + case PanelSubject.Speedlim_Display: + switch (subjectUnits) + { + case Units.Miles_Per_Hour: + Code = "routelimit sectionlimit max 1 Minus == 1 Minus routelimit sectionlimit max 2.2369362920544 * ?"; + break; + case Units.Kilometers_Per_Hour: + Code = "routelimit sectionlimit max 1 Minus == 1 Minus routelimit sectionlimit max 3.6 * ?"; + break; + } + break; + case PanelSubject.Emergency_Brake: + Code = "emergencybrake"; + break; + default: + Code = "0"; + break; + } + return Code; + } + + internal static int CreateElement(ref ElementsGroup Group, Vector2 TopLeft, Vector2 Size, Vector2 RelativeRotationCenter, double Distance, Vector3 Driver, Texture DaytimeTexture, Texture NighttimeTexture, Color32 Color, bool AddStateToLastElement = false) + { + if (Size.X == 0 || Size.Y == 0) + { + Plugin.CurrentHost.AddMessage(MessageType.Error, false, "MSTS Cabview Parser: Attempted to create an invalid size element"); + } + + double worldWidth, worldHeight; + if (Plugin.Renderer.Screen.Width >= Plugin.Renderer.Screen.Height) + { + worldWidth = 2.0 * Math.Tan(0.5 * Plugin.Renderer.Camera.HorizontalViewingAngle) * eyeDistance; + worldHeight = worldWidth / Plugin.Renderer.Screen.AspectRatio; + } + else + { + worldHeight = 2.0 * Math.Tan(0.5 * Plugin.Renderer.Camera.VerticalViewingAngle) * eyeDistance / Plugin.Renderer.Screen.AspectRatio; + worldWidth = worldHeight * Plugin.Renderer.Screen.AspectRatio; + } + + double x0 = TopLeft.X / panelResolution; + double x1 = (TopLeft.X + Size.X) / panelResolution; + double y0 = (panelSize.Y - TopLeft.Y) / panelResolution * Plugin.Renderer.Screen.AspectRatio; + double y1 = (panelSize.Y - (TopLeft.Y + Size.Y)) / panelResolution * Plugin.Renderer.Screen.AspectRatio; + double xd = 0.5 - panelCenter.X / panelResolution; + x0 += xd; + x1 += xd; + double yt = panelSize.Y - panelResolution / Plugin.Renderer.Screen.AspectRatio; + double yd = (panelCenter.Y - yt) / (panelSize.Y - yt) - 0.5; + y0 += yd; + y1 += yd; + x0 = (x0 - 0.5) * worldWidth; + x1 = (x1 - 0.5) * worldWidth; + y0 = (y0 - 0.5) * worldHeight; + y1 = (y1 - 0.5) * worldHeight; + double xm = x0 * (1.0 - RelativeRotationCenter.X) + x1 * RelativeRotationCenter.X; + double ym = y0 * (1.0 - RelativeRotationCenter.Y) + y1 * RelativeRotationCenter.Y; + Vector3[] v = new Vector3[4]; + v[0] = new Vector3(x0 - xm, y1 - ym, 0); + v[1] = new Vector3(x0 - xm, y0 - ym, 0); + v[2] = new Vector3(x1 - xm, y0 - ym, 0); + v[3] = new Vector3(x1 - xm, y1 - ym, 0); + Vertex t0 = new Vertex(v[0], new Vector2(0.0f, 1.0f)); + Vertex t1 = new Vertex(v[1], new Vector2(0.0f, 0.0f)); + Vertex t2 = new Vertex(v[2], new Vector2(1.0f, 0.0f)); + Vertex t3 = new Vertex(v[3], new Vector2(1.0f, 1.0f)); + StaticObject staticObject = new StaticObject(Plugin.CurrentHost); + staticObject.Mesh.Vertices = new VertexTemplate[] {t0, t1, t2, t3}; + staticObject.Mesh.Faces = new[] {new MeshFace(new[] {0, 1, 2, 0, 2, 3}, FaceFlags.Triangles)}; //Must create as a single face like this to avoid Z-sort issues with overlapping bits + staticObject.Mesh.Materials = new MeshMaterial[1]; + staticObject.Mesh.Materials[0].Flags = new MaterialFlags(); + if (DaytimeTexture != null) + { + staticObject.Mesh.Materials[0].Flags |= MaterialFlags.TransparentColor; + } + + staticObject.Mesh.Materials[0].Color = Color; + staticObject.Mesh.Materials[0].TransparentColor = Color24.Blue; + staticObject.Mesh.Materials[0].DaytimeTexture = DaytimeTexture; + staticObject.Mesh.Materials[0].NighttimeTexture = NighttimeTexture; + staticObject.Dynamic = true; + // calculate offset + Vector3 o; + o.X = xm + Driver.X; + o.Y = ym + Driver.Y; + o.Z = eyeDistance - Distance + Driver.Z; + // add object + if (AddStateToLastElement) + { + int n = Group.Elements.Length - 1; + int j = Group.Elements[n].States.Length; + Array.Resize(ref Group.Elements[n].States, j + 1); + Group.Elements[n].States[j] = new ObjectState + { + Translation = Matrix4D.CreateTranslation(o.X, o.Y, -o.Z), + Prototype = staticObject + }; + return n; + } + else + { + int n = Group.Elements.Length; + Array.Resize(ref Group.Elements, n + 1); + Group.Elements[n] = new AnimatedObject(Plugin.CurrentHost); + Group.Elements[n].States = new[] {new ObjectState()}; + Group.Elements[n].States[0].Translation = Matrix4D.CreateTranslation(o.X, o.Y, -o.Z); + Group.Elements[n].States[0].Prototype = staticObject; + Group.Elements[n].CurrentState = 0; + Group.Elements[n].internalObject = new ObjectState {Prototype = staticObject}; + Plugin.CurrentHost.CreateDynamicObject(ref Group.Elements[n].internalObject); + return n; + } + } + } +} diff --git a/source/Plugins/Train.MsTs/Panel/Enums/CabComponentStyle.cs b/source/Plugins/Train.MsTs/Panel/Enums/CabComponentStyle.cs new file mode 100644 index 0000000000..ccc5ce07a0 --- /dev/null +++ b/source/Plugins/Train.MsTs/Panel/Enums/CabComponentStyle.cs @@ -0,0 +1,19 @@ +// ReSharper disable UnusedMember.Global +namespace Train.MsTs +{ + internal enum CabComponentStyle + { + None = 0, + Needle, + Pointer, + Solid, + Sprung, + Not_Sprung, + OnOff, + TwentyFourHour, + TwelveHour, + While_Pressed, + Pressed, + Liquid, + } +} diff --git a/source/Plugins/Train.MsTs/Panel/Enums/CabComponentType.cs b/source/Plugins/Train.MsTs/Panel/Enums/CabComponentType.cs new file mode 100644 index 0000000000..2f23f7b88f --- /dev/null +++ b/source/Plugins/Train.MsTs/Panel/Enums/CabComponentType.cs @@ -0,0 +1,31 @@ +// ReSharper disable UnusedMember.Global +namespace Train.MsTs +{ + internal enum CabComponentType + { + /// None + None = 0, + /// Dial based control + Dial = 1, + /// Lever based control + Lever = 2, + /// Dial based gauge + Gauge = 3, + /// Two-state based control + TwoState = 4, + /// Tri-state based control + TriState = 5, + /// A display capable of displaying N states + MultiStateDisplay = 6, + /// In cab signalling / safety system (e.g. AWS) + CabSignalDisplay = 7, + /// Steam locomotive firebox animation + Firebox = 8, + /// A digital display + Digital = 9, + /// A digital clock + DigitalClock = 10, + /// A combined power + brake controller + CombinedControl = 11 + } +} diff --git a/source/Plugins/Train.MsTs/Panel/Enums/PanelSubject.cs b/source/Plugins/Train.MsTs/Panel/Enums/PanelSubject.cs new file mode 100644 index 0000000000..51f0181bbd --- /dev/null +++ b/source/Plugins/Train.MsTs/Panel/Enums/PanelSubject.cs @@ -0,0 +1,86 @@ +// ReSharper disable InconsistentNaming +// ReSharper disable UnusedMember.Global +namespace Train.MsTs +{ + internal enum PanelSubject + { + Accelerometer, + Alerter_Display, + Ammeter, + Aspect_Display, + AWS, + Bell, + Blower, + Brake_Cyl, + Brake_Pipe, + Cab_Radio, + Clock, + CP_Handle, + CPH_Display, + Cutoff, + Cyl_Cocks, + Dampers_Back, + Dampers_Front, + Direction, + Direction_Display, + Dynamic_Brake, + Dynamic_Brake_Display, + Engine_Brake, + Engine_Braking_Button, + Emergency_Brake, + Eq_Res, + Firebox, + Firehole, + Friction_Braking, + Front_Hlight, + Fuel_Gauge, + Gears, + Horn, + Load_Meter, + Line_Voltage, + Main_Res, + Overspeed, + Pantograph, + Panto_Display, + Penalty_App, + Regulator, + Reset, + Reverser_Plate, + Sanders, + Sanding, + Small_Ejector, + Speedlim_Display, + Speedometer, + Steamchest_Pr, + Steamheat_Pressure, + Steam_Inj1, + Steam_Inj2, + Steam_Pr, + Tender_Water, + Boiler_Water, + Throttle, + Throttle_Display, + Traction_Braking, + Train_Brake, + Vacuum_Reservoir_Pressure, + Water_Injector1, + Water_Injector2, + Wheelslip, + Whistle, + Wipers, + + // From MSTSBin v1.7 documentation + Ammeter_Abs, + Doors_Display, + Pantograph2, // pantograph2 state [need to dig into this, but I think Pantograph2 was 'broken' until BIN patch, although appears in original GLOBAL folder defines and some default stock] + Pantographs_4c, // 4-state combined controller for pantograoh 1+2 + Pantographs_4, // with end position + Pantographs_5, // 5-state combined controller for pantograph 1+2 + RPM, + Speed_Projected, // projected speed in one minute + SpeedLimit, // signal limit, not track limit + + // probably MSTSBin + Dynamic_Brake_Force, + } +} diff --git a/source/Plugins/Train.MsTs/Panel/FrameMapping.cs b/source/Plugins/Train.MsTs/Panel/FrameMapping.cs new file mode 100644 index 0000000000..719558ed61 --- /dev/null +++ b/source/Plugins/Train.MsTs/Panel/FrameMapping.cs @@ -0,0 +1,35 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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 Train.MsTs +{ + /// Maps a CVF animation frame to the key value + internal struct FrameMapping + { + /// The frame key + internal int FrameKey; + /// The key value + internal double MappingValue; + } +} diff --git a/source/Plugins/Train.MsTs/Plugin.cs b/source/Plugins/Train.MsTs/Plugin.cs new file mode 100644 index 0000000000..e9759777e2 --- /dev/null +++ b/source/Plugins/Train.MsTs/Plugin.cs @@ -0,0 +1,105 @@ +using System.IO; +using System.Text; +using LibRender2; +using Microsoft.Win32; +using OpenBveApi; +using OpenBveApi.FileSystem; +using OpenBveApi.Hosts; +using OpenBveApi.Interface; +using OpenBveApi.Trains; +using TrainManager.Trains; + +namespace Train.MsTs +{ + public class Plugin : TrainInterface + { + internal static ConsistParser ConsistParser; + + internal static WagonParser WagonParser; + + internal static HostInterface CurrentHost; + + internal static BaseRenderer Renderer; + + internal static FileSystem FileSystem; + + internal static BaseOptions CurrentOptions; + + internal static bool PreviewOnly; + public Plugin() + { + ConsistParser = new ConsistParser(this); + WagonParser = new WagonParser(); + } + + public override void Load(HostInterface host, FileSystem fileSystem, BaseOptions options, object rendererReference) + { + CurrentHost = host; + FileSystem = fileSystem; + CurrentOptions = options; + Renderer = (BaseRenderer) rendererReference; + try + { + if (!string.IsNullOrEmpty(FileSystem.MSTSDirectory)) + { + return; + } + FileSystem.MSTSDirectory = (string)Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft Games\\Train Simulator\\1.0", "Path", string.Empty); + string OrTsPath = (string)Registry.GetValue("HKEY_CURRENT_USER\\Software\\OpenRails\\ORTS\\Folders", "Train Simulator", string.Empty); + if (!string.IsNullOrEmpty(OrTsPath)) + { + FileSystem.MSTSDirectory = OrTsPath; + } + } + catch + { + // ignored + } + } + + public override bool CanLoadTrain(string path) + { + return File.Exists(path) && path.ToLowerInvariant().EndsWith(".con"); + } + + public override bool LoadTrain(Encoding encoding, string trainPath, ref AbstractTrain train, ref Control[] currentControls) + { + PreviewOnly = false; + try + { + ConsistParser.ReadConsist(trainPath, ref train); + } + catch + { + return false; + } + + return true; + } + + public override string GetDescription(string trainPath, Encoding userSelectedEncoding = null) + { + PreviewOnly = true; + AbstractTrain train = new TrainBase(TrainState.Pending, TrainType.LocalPlayerTrain); + try + { + ConsistParser.ReadConsist(trainPath, ref train); + } + catch + { + return string.Empty; + } + + if(train is TrainBase trainBase && trainBase.Cars.Length != 0) + { + return trainBase.Cars[train.DriverCar].Description; + } + return string.Empty; + } + + public override string GetImage(string trainPath) + { + return string.Empty; + } + } +} diff --git a/source/Plugins/Train.MsTs/Properties/AssemblyInfo.cs b/source/Plugins/Train.MsTs/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..256878fa3a --- /dev/null +++ b/source/Plugins/Train.MsTs/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Train.MsTs")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Train.MsTs")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("a6e3d875-ddfa-446a-aaf5-bfaff3c9ef45")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/source/Plugins/Train.MsTs/Sound/SmsParser.cs b/source/Plugins/Train.MsTs/Sound/SmsParser.cs new file mode 100644 index 0000000000..f651092c55 --- /dev/null +++ b/source/Plugins/Train.MsTs/Sound/SmsParser.cs @@ -0,0 +1,706 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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 OpenBve.Formats.MsTs; +using OpenBveApi; +using OpenBveApi.Interface; +using OpenBveApi.Math; +using OpenBveApi.Motor; +using OpenBveApi.Runtime; +using OpenBveApi.World; +using SharpCompress.Compressors; +using SharpCompress.Compressors.Deflate; +using SoundManager; +using System; +using System.IO; +using System.Text; +using TrainManager.Car; +using TrainManager.Car.Systems; +using TrainManager.Motor; +using TrainManager.MsTsSounds; +using TrainManager.SafetySystems; + +namespace Train.MsTs +{ + internal class SoundModelSystemParser + { + private static string currentFolder; + + private static string currentFile; + + private static Tuple[] curvePoints; + + internal static bool ParseSoundFile(string fileName, ref CarBase currentCar) + { + currentFile = fileName; + currentFolder = System.IO.Path.GetDirectoryName(fileName); + Stream fb = new FileStream(fileName, FileMode.Open, FileAccess.Read); + + byte[] buffer = new byte[34]; + fb.Read(buffer, 0, 2); + + bool unicode = buffer[0] == 0xFF && buffer[1] == 0xFE; + + string headerString; + if (unicode) + { + fb.Read(buffer, 0, 32); + headerString = Encoding.Unicode.GetString(buffer, 0, 16); + } + else + { + fb.Read(buffer, 2, 14); + headerString = Encoding.ASCII.GetString(buffer, 0, 8); + } + + // SIMISA@F means compressed + // SIMISA@@ means uncompressed + if (headerString.StartsWith("SIMISA@F")) + { + fb = new ZlibStream(fb, CompressionMode.Decompress); + } + else if (headerString.StartsWith("\r\nSIMISA")) + { + // ie us1rd2l1000r10d.s, we are going to allow this but warn + Console.Error.WriteLine("Improper header in " + fileName); + fb.Read(buffer, 0, 4); + } + else if (!headerString.StartsWith("SIMISA@@")) + { + Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Unrecognized SMS file header " + headerString + " in " + fileName); + return false; + } + + string subHeader; + if (unicode) + { + fb.Read(buffer, 0, 32); + subHeader = Encoding.Unicode.GetString(buffer, 0, 16); + } + else + { + fb.Read(buffer, 0, 16); + subHeader = Encoding.ASCII.GetString(buffer, 0, 8); + } + SoundSet soundSet = new SoundSet(); + SoundStream soundStream = new SoundStream(currentCar, CameraViewMode.NotDefined, CameraViewMode.NotDefined); + + if (subHeader[7] == 't') + { + using (BinaryReader reader = new BinaryReader(fb)) + { + byte[] newBytes = reader.ReadBytes((int)(fb.Length - fb.Position)); + string s = unicode ? Encoding.Unicode.GetString(newBytes) : Encoding.ASCII.GetString(newBytes); + TextualBlock block = new TextualBlock(s, KujuTokenID.Tr_SMS); + ParseBlock(block, ref soundSet, ref soundStream, ref currentCar); + } + + } + else if (subHeader[7] != 'b') + { + Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Unrecognized subHeader " + subHeader + " in " + fileName); + } + else + { + using (BinaryReader reader = new BinaryReader(fb)) + { + KujuTokenID currentToken = (KujuTokenID)reader.ReadUInt16(); + if (currentToken != KujuTokenID.Tr_SMS) + { + return false; + } + + reader.ReadUInt16(); + uint remainingBytes = reader.ReadUInt32(); + byte[] newBytes = reader.ReadBytes((int)remainingBytes); + BinaryBlock block = new BinaryBlock(newBytes, KujuTokenID.Tr_SMS); + ParseBlock(block, ref soundSet, ref soundStream, ref currentCar); + } + } + + return false; + } + + + internal struct SoundSet + { + internal bool Activation; + internal double ActivationDistance; + internal double DeactivationDistance; + internal double Priority; + + internal SoundTrigger CurrentTrigger; + internal KujuTokenID CurrentSoundType; + internal KujuTokenID VariableTriggerType; + internal double VariableValue; + internal SoundBuffer[] SoundBuffers; + internal int CurrentBuffer; + internal KujuTokenID SelectionMethod; + internal CameraViewMode ActivationCameraModes; + internal CameraViewMode DeactivationCameraModes; + + internal void Create(CarBase car, SoundStream currentSoundStream) + { + switch (VariableTriggerType) + { + case KujuTokenID.Initial_Trigger: + currentSoundStream.Triggers.Add(new InitialTrigger(SoundBuffers, SelectionMethod, CurrentSoundType != KujuTokenID.PlayOneShot)); + break; + case KujuTokenID.Speed_Inc_Past: + currentSoundStream.Triggers.Add(new SpeedIncPast(SoundBuffers, SelectionMethod, VariableValue, CurrentSoundType != KujuTokenID.PlayOneShot)); + break; + case KujuTokenID.Speed_Dec_Past: + currentSoundStream.Triggers.Add(new SpeedDecPast(SoundBuffers, SelectionMethod, VariableValue, CurrentSoundType != KujuTokenID.PlayOneShot)); + break; + case KujuTokenID.Variable1_Inc_Past: + currentSoundStream.Triggers.Add(new Variable1IncPast(SoundBuffers, SelectionMethod, VariableValue, CurrentSoundType != KujuTokenID.PlayOneShot)); + break; + case KujuTokenID.Variable1_Dec_Past: + currentSoundStream.Triggers.Add(new Variable1DecPast(SoundBuffers, SelectionMethod, VariableValue, CurrentSoundType != KujuTokenID.PlayOneShot)); + break; + case KujuTokenID.Variable2_Inc_Past: + currentSoundStream.Triggers.Add(new Variable2IncPast(SoundBuffers, SelectionMethod, VariableValue, CurrentSoundType != KujuTokenID.PlayOneShot)); + break; + case KujuTokenID.Variable2_Dec_Past: + currentSoundStream.Triggers.Add(new Variable2DecPast(SoundBuffers, SelectionMethod, VariableValue, CurrentSoundType != KujuTokenID.PlayOneShot)); + break; + } + } + } + + + + private static void ParseBlock(Block block, ref SoundSet currentSoundSet, ref SoundStream currentSoundStream, ref CarBase car) + { + Block newBlock; + switch (block.Token) + { + case KujuTokenID.Tr_SMS: + // file root + while (block.Position() < block.Length() - 3) + { + try + { + newBlock = block.ReadSubBlock(true); + ParseBlock(newBlock, ref currentSoundSet, ref currentSoundStream, ref car); + } + catch + { + break; + } + } + break; + case KujuTokenID.ScalabiltyGroup: + // root container for sound groups + block.ReadSingle(); // number of groups + while (block.Position() < block.Length()) + { + try + { + newBlock = block.ReadSubBlock(true); + ParseBlock(newBlock, ref currentSoundSet, ref currentSoundStream, ref car); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + } + break; + case KujuTokenID.Activation: + // control the conditions under which the sounds in the group are activated + currentSoundSet.Activation = true; + while (block.Position() < block.Length() - 3) + { + newBlock = block.ReadSubBlock(true); + ParseBlock(newBlock, ref currentSoundSet, ref currentSoundStream, ref car); + } + break; + case KujuTokenID.Deactivation: + // control the conditions under which the sounds in the group are deactivated + currentSoundSet.Activation = false; + while (block.Position() < block.Length() - 3) + { + newBlock = block.ReadSubBlock(true); + ParseBlock(newBlock, ref currentSoundSet, ref currentSoundStream, ref car); + } + break; + case KujuTokenID.Distance: + // absolute distance, presumably to camera + if (currentSoundSet.Activation) + { + currentSoundSet.ActivationDistance = block.ReadSingle(); + } + else + { + currentSoundSet.DeactivationDistance = block.ReadSingle(); + } + break; + case KujuTokenID.ExternalCam: + if (currentSoundSet.Activation) + { + currentSoundSet.ActivationCameraModes |= CameraViewMode.Exterior; + currentSoundSet.ActivationCameraModes |= CameraViewMode.Track; + currentSoundSet.ActivationCameraModes |= CameraViewMode.FlyBy; + currentSoundSet.ActivationCameraModes |= CameraViewMode.FlyByZooming; + } + else + { + currentSoundSet.DeactivationCameraModes |= CameraViewMode.Exterior; + currentSoundSet.DeactivationCameraModes |= CameraViewMode.Track; + currentSoundSet.DeactivationCameraModes |= CameraViewMode.FlyBy; + currentSoundSet.DeactivationCameraModes |= CameraViewMode.FlyByZooming; + } + break; + case KujuTokenID.CabCam: + if (currentSoundSet.Activation) + { + currentSoundSet.ActivationCameraModes |= CameraViewMode.Interior; + } + else + { + currentSoundSet.DeactivationCameraModes |= CameraViewMode.Interior; + } + break; + case KujuTokenID.PassengerCam: + // FIXME: Passenger cam not currently distinguished from interior cam + break; + case KujuTokenID.Streams: + // each stream represents a unique sound + int numStreams = block.ReadInt32(); + for (int i = 0; i < numStreams; i++) + { + newBlock = block.ReadSubBlock(); + if (newBlock.Token != KujuTokenID.Stream) + { + i--; + if (newBlock.Token != KujuTokenID.Skip) + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Unexpected additional block " + newBlock.Token + " encounted within Stream block in SMS file " + currentFile); + } + if (block.Length() - block.Position() <= 3) + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Expected " + numStreams + ", but only found " + i + " in Stream block in SMS file " + currentFile); + break; + } + continue; + } + + ParseBlock(newBlock, ref currentSoundSet, ref currentSoundStream, ref car); + if (block.Length() - block.Position() <= 3) + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Expected " + numStreams + ", but only found " + i + " in Stream block in SMS file " + currentFile); + break; + } + } + break; + case KujuTokenID.Stream: + while (block.Position() < block.Length() - 3) + { + newBlock = block.ReadSubBlock(); + if (newBlock.Token == KujuTokenID.Stream) + { + // EBPHNWSE121.sms - Completely bugged, copy + paste error (?) + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Unexpected Stream found within a Stream block in SMS file " + currentFile); + continue; + } + ParseBlock(newBlock, ref currentSoundSet, ref currentSoundStream, ref car); + } + + if (currentSoundStream.Triggers.Count > 0) + { + car.Sounds.ControlledSounds.Add(currentSoundStream); + currentSoundStream = new SoundStream(car, currentSoundSet.ActivationCameraModes, currentSoundSet.DeactivationCameraModes); + } + break; + case KujuTokenID.Priority: + currentSoundSet.Priority = block.ReadSingle(); + break; + case KujuTokenID.Triggers: + int numTriggers = block.ReadInt32(); + for (int i = 0; i < numTriggers; i++) + { + // two triggers per sound set (start + stop) + newBlock = block.ReadSubBlock(new [] {KujuTokenID.Variable_Trigger, KujuTokenID.Initial_Trigger, KujuTokenID.Discrete_Trigger, KujuTokenID.Random_Trigger, KujuTokenID.Dist_Travelled_Trigger}); + ParseBlock(newBlock, ref currentSoundSet, ref currentSoundStream, ref car); + if (block.Length() - block.Position() <= 3) + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Expected " + numTriggers + ", but only found " + i + " in Triggers block in SMS file " + currentFile); + break; + } + } + break; + case KujuTokenID.Initial_Trigger: + // when initially appears, hence nothing other than StartLoop should be valid + currentSoundSet.VariableTriggerType = KujuTokenID.Initial_Trigger; + newBlock = block.ReadSubBlock(new[] { KujuTokenID.StartLoop, KujuTokenID.StartLoopRelease, KujuTokenID.ReleaseLoopRelease, KujuTokenID.EnableTrigger, KujuTokenID.DisableTrigger, KujuTokenID.PlayOneShot, KujuTokenID.SetStreamVolume }); + ParseBlock(newBlock, ref currentSoundSet, ref currentSoundStream, ref car); + break; + case KujuTokenID.StartLoopRelease: + case KujuTokenID.StartLoop: + /* StartLoopRelease - Loop stops when key is released + * StartLoop - Loop continues when key is released + * --------------------------------------------------- + * NOTE: Handle these on a per-sound trigger, as where possible + * map to existing subsystems + */ + currentSoundSet.CurrentSoundType = block.Token; + int numSounds = block.ReadInt16(); + currentSoundSet.SoundBuffers = new SoundBuffer[numSounds]; + currentSoundSet.SelectionMethod = KujuTokenID.SequentialSelection; + while (numSounds > 0 && block.Position() < block.Length() - 4) + { + newBlock = block.ReadSubBlock(); + if (newBlock.Token == KujuTokenID.File) + { + numSounds--; + } + ParseBlock(newBlock, ref currentSoundSet, ref currentSoundStream, ref car); + } + currentSoundSet.Create(car, currentSoundStream); + break; + case KujuTokenID.ReleaseLoopRelease: + // empty block expected + // paired with StartLoopRelease + currentSoundSet.Create(car, currentSoundStream); + break; + case KujuTokenID.File: + if (block.ReadPath(currentFolder, out string soundFile)) + { + // n.b. MSTS does not distinguish between increase / decrease sounds for handles etc. + // sound radii are also fudged based upon BVE values; most MSTS content just seems to use massive radii + switch (currentSoundSet.CurrentTrigger) + { + case SoundTrigger.VariableControlled: + // hack + Plugin.CurrentHost.RegisterSound(soundFile, currentSoundSet.ActivationDistance, out var soundHandle); + currentSoundSet.SoundBuffers[currentSoundSet.CurrentBuffer] = soundHandle as SoundBuffer; + break; + case SoundTrigger.ReverserToForwardBackward: + if (currentSoundSet.CurrentSoundType == KujuTokenID.PlayOneShot) + { + car.baseTrain.Handles.Reverser.EngageSound = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + } + break; + case SoundTrigger.ReverserToNeutral: + if (currentSoundSet.CurrentSoundType == KujuTokenID.PlayOneShot) + { + car.baseTrain.Handles.Reverser.ReleaseSound = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + } + break; + case SoundTrigger.ReverserChange: + if (currentSoundSet.CurrentSoundType == KujuTokenID.PlayOneShot) + { + // as we don't know the order these may be presented in, check the buffer + if (car.baseTrain.Handles.Reverser.EngageSound.Buffer == null) + { + car.baseTrain.Handles.Reverser.EngageSound = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + } + + if (car.baseTrain.Handles.Reverser.ReleaseSound.Buffer == null) + { + car.baseTrain.Handles.Reverser.ReleaseSound = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + } + } + break; + case SoundTrigger.ThrottleChange: + if (currentSoundSet.CurrentSoundType == KujuTokenID.PlayOneShot) + { + car.baseTrain.Handles.Power.Decrease = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + car.baseTrain.Handles.Power.DecreaseFast = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + car.baseTrain.Handles.Power.Increase = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + car.baseTrain.Handles.Power.IncreaseFast = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + car.baseTrain.Handles.Power.Min = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + car.baseTrain.Handles.Power.Max = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + } + break; + case SoundTrigger.TrainBrakeChange: + if (currentSoundSet.CurrentSoundType == KujuTokenID.PlayOneShot) + { + car.baseTrain.Handles.Brake.Decrease = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + car.baseTrain.Handles.Brake.DecreaseFast = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + car.baseTrain.Handles.Brake.Increase = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + car.baseTrain.Handles.Brake.IncreaseFast = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + car.baseTrain.Handles.Brake.Min = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + car.baseTrain.Handles.Brake.Max = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + } + break; + case SoundTrigger.EngineBrakeChange: + if (currentSoundSet.CurrentSoundType == KujuTokenID.PlayOneShot && car.baseTrain.Handles.LocoBrake != null) + { + car.baseTrain.Handles.LocoBrake.Decrease = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + car.baseTrain.Handles.LocoBrake.DecreaseFast = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + car.baseTrain.Handles.LocoBrake.Increase = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + car.baseTrain.Handles.LocoBrake.IncreaseFast = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + car.baseTrain.Handles.LocoBrake.Min = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + car.baseTrain.Handles.LocoBrake.Max = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + } + break; + case SoundTrigger.LightSwitchToggle: + if (currentSoundSet.CurrentSoundType == KujuTokenID.PlayOneShot && car.baseTrain.SafetySystems.Headlights != null) + { + Plugin.CurrentHost.RegisterSound(soundFile, 2.0, out soundHandle); + car.baseTrain.SafetySystems.Headlights.SwitchSoundBuffer = soundHandle as SoundBuffer; + } + break; + case SoundTrigger.HornOn: + if (currentSoundSet.CurrentSoundType == KujuTokenID.StartLoopRelease && car.Horns[0] != null) + { + Plugin.CurrentHost.RegisterSound(soundFile, 30.0, out soundHandle); + car.Horns[0].LoopSound = soundHandle as SoundBuffer; + } + break; + case SoundTrigger.BellOn: + if (currentSoundSet.CurrentSoundType == KujuTokenID.StartLoopRelease && car.Horns[2] != null) + { + Plugin.CurrentHost.RegisterSound(soundFile, 30.0, out soundHandle); + car.Horns[0].LoopSound = soundHandle as SoundBuffer; + } + break; + case SoundTrigger.Pantograph1Up: + case SoundTrigger.Pantograph1Down: + case SoundTrigger.Pantograph1Toggle: + if (car.TractionModel.Components.TryGetTypedValue(EngineComponent.Pantograph, out Pantograph pantograph)) + { + if (currentSoundSet.CurrentTrigger == SoundTrigger.Pantograph1Up) + { + pantograph.RaiseSound = new CarSound(Plugin.CurrentHost, soundFile, 100, Vector3.Zero); + } + else if(currentSoundSet.CurrentTrigger == SoundTrigger.Pantograph1Down) + { + pantograph.LowerSound = new CarSound(Plugin.CurrentHost, soundFile, 100, Vector3.Zero); + } + else + { + pantograph.SwitchToggle = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + } + } + else + { + // n.b. A WAG file may link to a model containing pantograph animations, or a SMS with pantograph sounds, but does not need to mention + // that it exists, so we may need to add it here. + Pantograph newPantograph = new Pantograph(car.TractionModel); + if (currentSoundSet.CurrentTrigger == SoundTrigger.Pantograph1Up) + { + newPantograph.RaiseSound = new CarSound(Plugin.CurrentHost, soundFile, 100, Vector3.Zero); + } + else if (currentSoundSet.CurrentTrigger == SoundTrigger.Pantograph1Down) + { + newPantograph.LowerSound = new CarSound(Plugin.CurrentHost, soundFile, 100, Vector3.Zero); + } + else + { + newPantograph.SwitchToggle = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + } + car.TractionModel.Components.Add(EngineComponent.Pantograph, newPantograph); + } + break; + case SoundTrigger.WiperOn: + case SoundTrigger.WiperOff: + car.Windscreen.Wipers.SwitchSound = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + break; + case SoundTrigger.SanderOn: + if (car.ReAdhesionDevice is Sanders sanders) + { + sanders.LoopSound = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + } + break; + case SoundTrigger.TrainBrakePressureDecrease: + if ((currentSoundStream.ActivationCameraModes & CameraViewMode.Interior) != 0) + { + car.CarBrake.AirZero = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + } + break; + case SoundTrigger.VigilanceAlarmOn: + // NOTE: appears to only apply to DSD, not overspeed + if (car.SafetySystems.TryGetTypedValue(SafetySystem.DriverSupervisionDevice, out DriverSupervisionDevice dsd)) + { + dsd.AlarmSound = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + dsd.AlertSound = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + } + break; + case SoundTrigger.GearUp: + case SoundTrigger.GearDown: + if (car.TractionModel.Components.TryGetTypedValue(EngineComponent.Gearbox, out Gearbox gearbox)) + { + if (currentSoundSet.CurrentTrigger == SoundTrigger.GearUp) + { + gearbox.GearUpSound = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + } + else + { + gearbox.GearDownSound = new CarSound(Plugin.CurrentHost, soundFile, 2.0, car.Driver); + } + } + break; + } + } + else + { + if (currentSoundSet.CurrentTrigger != SoundTrigger.Skip) + { + Plugin.CurrentHost.AddMessage(MessageType.Error, true, "MSTS Sound File " + soundFile + " was not found in SMS " + currentFile); + } + } + int checkDigit = block.ReadInt32(); + if (checkDigit != -1) + { + // Unknown purpose at the minute- set to -1 everywhere + throw new Exception(); + } + + break; + case KujuTokenID.SelectionMethod: + KujuTokenID token = block.ReadEnumValue(default(KujuTokenID)); + switch (token) + { + case KujuTokenID.SequentialSelection: + break; + case KujuTokenID.RandomSelection: + break; + } + break; + case KujuTokenID.Discrete_Trigger: + currentSoundSet.CurrentTrigger = (SoundTrigger)block.ReadInt32(); // stored as integer + newBlock = block.ReadSubBlock(new[] { KujuTokenID.PlayOneShot, KujuTokenID.StartLoop, KujuTokenID.StartLoopRelease, KujuTokenID.ReleaseLoopRelease, KujuTokenID.ReleaseLoopReleaseWithJump, KujuTokenID.SetStreamVolume, KujuTokenID.EnableTrigger, KujuTokenID.DisableTrigger }); + ParseBlock(newBlock, ref currentSoundSet, ref currentSoundStream, ref car); + break; + case KujuTokenID.Variable_Trigger: + currentSoundSet.VariableTriggerType = block.ReadEnumValue(default(KujuTokenID)); + switch (currentSoundSet.VariableTriggerType) + { + case KujuTokenID.StartLoop: + break; + case KujuTokenID.Speed_Inc_Past: + case KujuTokenID.Speed_Dec_Past: + currentSoundSet.VariableValue = block.ReadSingle(UnitOfVelocity.KilometersPerHour, UnitOfVelocity.MetersPerSecond); // speed in m/s + newBlock = block.ReadSubBlock(new[] { KujuTokenID.StartLoop, KujuTokenID.StartLoopRelease, KujuTokenID.ReleaseLoopRelease, KujuTokenID.ReleaseLoopReleaseWithJump, KujuTokenID.PlayOneShot, KujuTokenID.EnableTrigger, KujuTokenID.DisableTrigger, KujuTokenID.SetStreamVolume }); + ParseBlock(newBlock, ref currentSoundSet, ref currentSoundStream, ref car); + break; + case KujuTokenID.SpeedControlled: + break; + case KujuTokenID.DistanceControlled: + break; + case KujuTokenID.Distance_Inc_Past: + case KujuTokenID.Distance_Dec_Past: + break; + case KujuTokenID.Variable1Controlled: + break; + case KujuTokenID.Variable1_Inc_Past: + case KujuTokenID.Variable1_Dec_Past: + case KujuTokenID.Variable2_Inc_Past: + case KujuTokenID.Variable2_Dec_Past: + currentSoundSet.VariableValue = block.ReadSingle(); // variable value + newBlock = block.ReadSubBlock(new[] { KujuTokenID.StartLoop, KujuTokenID.StartLoopRelease, KujuTokenID.ReleaseLoopRelease, KujuTokenID.ReleaseLoopReleaseWithJump, KujuTokenID.EnableTrigger, KujuTokenID.DisableTrigger, KujuTokenID.PlayOneShot, KujuTokenID.SetStreamVolume }); + ParseBlock(newBlock, ref currentSoundSet, ref currentSoundStream, ref car); + break; + case KujuTokenID.Variable2Controlled: + break; + case KujuTokenID.Variable3_Inc_Past: + case KujuTokenID.Variable3_Dec_Past: + case KujuTokenID.Variable3Controlled: + break; + default: + throw new Exception("Unexpected enum value " + currentSoundSet.VariableTriggerType + " encounted in SMS file " + currentFile); + } + break; + case KujuTokenID.PlayOneShot: + currentSoundSet.CurrentSoundType = block.Token; + numSounds = block.ReadInt16(); + currentSoundSet.SoundBuffers = new SoundBuffer[numSounds]; + currentSoundSet.SelectionMethod = KujuTokenID.SequentialSelection; + while (numSounds > 0 && block.Position() < block.Length() - 4) + { + newBlock = block.ReadSubBlock(); + if (newBlock.Token == KujuTokenID.File) + { + numSounds--; + } + ParseBlock(newBlock, ref currentSoundSet, ref currentSoundStream, ref car); + } + currentSoundSet.Create(car, currentSoundStream); + break; + case KujuTokenID.Volume: + double volume = block.ReadSingle(); + break; + case KujuTokenID.VolumeCurve: + token = block.ReadEnumValue(default(KujuTokenID)); + switch (token) + { + case KujuTokenID.SpeedControlled: + case KujuTokenID.DistanceControlled: + case KujuTokenID.Variable1Controlled: + case KujuTokenID.Variable2Controlled: + case KujuTokenID.Variable3Controlled: + newBlock = block.ReadSubBlock(KujuTokenID.CurvePoints); + ParseBlock(newBlock, ref currentSoundSet, ref currentSoundStream, ref car); + break; + default: + throw new Exception("Unexpected enum value " + token + " encounted in SMS file " + currentFile); + } + + currentSoundStream.VolumeCurve = new MsTsVolumeCurve(car, token, curvePoints); + break; + case KujuTokenID.FrequencyCurve: + token = block.ReadEnumValue(default(KujuTokenID)); + switch (token) + { + case KujuTokenID.SpeedControlled: + case KujuTokenID.DistanceControlled: + case KujuTokenID.Variable1Controlled: + case KujuTokenID.Variable2Controlled: + case KujuTokenID.Variable3Controlled: + newBlock = block.ReadSubBlock(KujuTokenID.CurvePoints); + ParseBlock(newBlock, ref currentSoundSet, ref currentSoundStream, ref car); + break; + default: + throw new Exception("Unexpected enum value " + token + " encounted in SMS file " + currentFile); + } + + currentSoundStream.FrequencyCurve = new MsTsFrequencyCurve(car, token, curvePoints); + break; + case KujuTokenID.CurvePoints: + int numPoints = block.ReadInt32(); + curvePoints = new Tuple[numPoints]; + for (int i = 0; i < numPoints; i++) + { + // Normalise Variable2 values to be consistant across traction models + // MSTS yuck... + if (car.TractionModel is ElectricEngine) + { + curvePoints[i] = new Tuple(block.ReadSingle() / 100, block.ReadSingle()); + } + else + { + curvePoints[i] = new Tuple(block.ReadSingle(), block.ReadSingle()); + } + } + break; + case KujuTokenID.Granularity: + // presuming this is the step in km/h + break; + } + } + } +} diff --git a/source/Plugins/Train.MsTs/Sound/TriggerTypes.cs b/source/Plugins/Train.MsTs/Sound/TriggerTypes.cs new file mode 100644 index 0000000000..f4486ebf11 --- /dev/null +++ b/source/Plugins/Train.MsTs/Sound/TriggerTypes.cs @@ -0,0 +1,98 @@ +// ReSharper disable UnusedMember.Global +namespace Train.MsTs +{ + internal enum SoundTrigger + { + // -1 appears to be used to skip the sound + // unclear as to why it's not just left out, copy + paste or internal MSTS validation? + // some of the time, these exist, but a lot of the time the sound file is missing + Skip = -1, + VariableControlled = 0, + DynamicBrakeIncrease = 2, + DynamicBrakeOff = 3, + SanderOn = 4, + SanderOff = 5, + WiperOn = 6, + WiperOff = 7, + HornOn= 8, + HornOff = 9, + BellOn = 10, + BellOff = 11, + CompressorOn = 12, + CompressorOff = 13, + TrainBrakePressureIncrease = 14, + ReverserChange = 15, + ThrottleChange = 16, + TrainBrakeChange = 17, + EngineBrakeChange = 18, + // 19 not listed + DynamicBrakeChange = 20, + EngineBrakePressureIncrease = 21, + EngineBrakePressureDecrease = 22, + EnginePowerOn = 23, + EnginePowerOff = 24, + // 25 + 26 not listed + SteamEjector2On = 27, + SteamEjector2Off = 28, + //29 + 30 not listed + SteamEjector1On = 30, + SteamEjector1Off = 31, + DamperChange = 32, + BlowerChange = 33, + CylinderCocksToggle = 34, + // 35 not listed + FireboxDoorChange = 36, + LightSwitchToggle = 37, + WaterScoopDown = 38, + WaterScoopUp = 39, + // 40 not listed + FireboxDoorClose = 41, + SteamSafetyValveOn = 42, + SteamSafetyValveOff = 43, + SteamHeatChange = 44, + Pantograph1Up = 45, + Pantograph1Down = 46, + Pantograph1Toggle = 47, + VigilanceAlarmReset = 48, + // 49 - 53 not listed + + // + // acelaeng.sms has 53 commented as Brake Normal Apply and 54 as BrakeEmergencyApply + // + // OpenRails forum suggests that trigger 53 never actually worked + // + TrainBrakePressureDecrease = 54, + //55 not listed + VigilanceAlarmOn = 56, + VigilanceAlarmOff = 57, + Couple = 58, + CoupleB = 59, + CoupleC = 60, + Uncouple = 61, + UncoupleB = 62, + UncoupleC = 63, + // 64 + 65 not listed + Pantograph2Up = 66, + Pantograph2Down = 67, + /* + * NOTE: + * https://open-rails.readthedocs.io/en/latest/sound.html + * ORTS specific triggers + * + * Not looking to handle most of these at the minute. + */ + WaterPump1On = 90, + WaterPump1Off = 91, + WaterPump2On = 92, + WaterPump2Off = 93, + GearUp = 101, + GearDown = 102, + ReverserToForwardBackward = 103, + ReverserToNeutral = 104, + DoorOpen = 105, + DoorClose = 106, + MirrorOpen = 107, + MirrorClose = 108 + + } +} diff --git a/source/Plugins/Train.MsTs/Train.MsTs.csproj b/source/Plugins/Train.MsTs/Train.MsTs.csproj new file mode 100644 index 0000000000..e321a68373 --- /dev/null +++ b/source/Plugins/Train.MsTs/Train.MsTs.csproj @@ -0,0 +1,123 @@ + + + + + Debug + AnyCPU + {A6E3D875-DDFA-446A-AAF5-BFAFF3C9EF45} + Library + Properties + Train.MsTs + Train.MsTs + v4.6.1 + 512 + true + + + true + full + false + ..\..\..\bin_debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\..\bin_release\ + TRACE + prompt + 4 + + + + + ..\..\..\packages\SharpCompress.0.32.2\lib\net461\SharpCompress.dll + + + + ..\..\..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + + + ..\..\..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll + + + + ..\..\..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\..\..\packages\System.Text.Encoding.CodePages.6.0.0\lib\net461\System.Text.Encoding.CodePages.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {68215476-302C-49F2-9F7E-AAE20A2B6B12} + LibRender2 + + + {27134980-4415-4375-A564-40A9014DFA5F} + OpenBveApi + + + {90ABFA0C-ABCA-444E-ADEF-9A299AED6524} + SoundManager + + + {D0FCA2C5-FF75-42D8-AE80-310280A61FB1} + TrainManager + + + {e81b7bd8-a326-47d3-b7ee-e9c7d4d119fa} + Formats.Msts + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/Plugins/Train.MsTs/Train/Adhesion.cs b/source/Plugins/Train.MsTs/Train/Adhesion.cs new file mode 100644 index 0000000000..a3ca162941 --- /dev/null +++ b/source/Plugins/Train.MsTs/Train/Adhesion.cs @@ -0,0 +1,143 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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 OpenBve.Formats.MsTs; +using OpenBveApi.Interface; +using TrainManager.Car; +using TrainManager.Car.Systems; + +namespace Train.MsTs +{ + internal class Adhesion + { + /// Holds a reference to the base car + private readonly CarBase baseCar; + /// The value used in wheelslip conditions + private readonly double WheelSlip; + /// The value used in normal conditions + private readonly double Normal; + /// The value used in sanding conditions + private readonly double Sanding; + + internal Adhesion(CarBase car, bool isSteamEngine) + { + baseCar = car; + // Kuju suggested default values + // see Eng_and_wag_file_reference_guideV2.doc + if (isSteamEngine) + { + WheelSlip = 0.15; + Normal = 0.3; + } + else + { + WheelSlip = 0.2; + Normal = 0.4; + } + + Sanding = 2.0; + } + + internal Adhesion(Block block, CarBase car, bool isSteamEngine) + { + baseCar = car; + try + { + WheelSlip = block.ReadSingle(); + Normal = block.ReadSingle(); + Sanding = block.ReadSingle(); + + if (Sanding < 0.75 || Normal < 0.05 || WheelSlip < 0.05) + { + /* + * e.g. MT Class 47 + * If we don't apply at least 75 percent of rated power when *sanding* let alone normally + * the ENG file is clearly bugged + */ + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "MSTS Vehicle Parser: Discarding implausible Adheasion values"); + WheelSlip = 0.2; + Normal = 0.4; + Sanding = 2.0; + } + } + catch + { + // Kuju suggested default values + // see Eng_and_wag_file_reference_guideV2.doc + if (isSteamEngine) + { + WheelSlip = 0.15; + Normal = 0.3; + } + else + { + WheelSlip = 0.2; + Normal = 0.4; + } + + Sanding = 2.0; + } + } + + internal double GetWheelslipValue() + { + double multiplier; + // https://www.trainsim.com/forums/forum/general-discussion/traction/68459-msts-diesel-sanding-effective + // MSTS uses a per-axle drive force calculation, so our traction model's force is divided by the number of axles + // whereas BVE thinks in terms of the whole car, so for a *hacky* version, we don't need the NumWheels divisor or + // the mass, as we're not worrying about mass per axle yet... + // Unlikely to be perfect, but doing it this way resolves massive wheelslip issues + + if (baseCar.ReAdhesionDevice is Sanders sanders && sanders.State == SandersState.Active) + { + // Per-axle: + // multiplier = 0.95 * WheelSlip * Sanding * baseCar.CurrentMass / baseCar.DrivingWheels[0].TotalNumber / baseCar.CurrentMass; + multiplier = 0.95 * WheelSlip * Sanding; + } + else + { + if (!baseCar.FrontAxle.CurrentWheelSlip) + { + // Per-axle: + // multiplier = Normal * Sanding * baseCar.CurrentMass / baseCar.TrailingWheels[0].TotalNumber / baseCar.CurrentMass; + multiplier = Normal * Sanding; + } + else + { + // Per-axle: + // multiplier = Normal * Sanding * baseCar.CurrentMass / baseCar.DrivingWheels[0].TotalNumber / baseCar.CurrentMass; + multiplier = WheelSlip * Sanding; + } + } + + if (baseCar.TractionModel.MaximumPossibleAcceleration == 0) + { + // no possible acceleration, so can't wheelslip! + return double.MaxValue; + } + + return baseCar.TractionModel.MaximumPossibleAcceleration * multiplier; + } + } +} diff --git a/source/Plugins/Train.MsTs/Train/ConsistParser.cs b/source/Plugins/Train.MsTs/Train/ConsistParser.cs new file mode 100644 index 0000000000..53268f2a15 --- /dev/null +++ b/source/Plugins/Train.MsTs/Train/ConsistParser.cs @@ -0,0 +1,394 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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; +using System.IO; +using System.Text; +using LibRender2.Trains; +using OpenBve.Formats.MsTs; +using OpenBveApi.Interface; +using OpenBveApi.Routes; +using OpenBveApi.Trains; +using SharpCompress.Compressors; +using SharpCompress.Compressors.Deflate; +using SoundManager; +using TrainManager.BrakeSystems; +using TrainManager.Car; +using TrainManager.Handles; +using TrainManager.Motor; +using TrainManager.Power; +using TrainManager.Trains; + +namespace Train.MsTs +{ + internal class ConsistParser + { + internal readonly Plugin Plugin; + internal string TrainsetDirectory; + + internal ConsistParser(Plugin plugin) + { + Plugin = plugin; + } + + internal void ReadConsist(string fileName, ref AbstractTrain parsedTrain) + { + currentCarIndex = -1; + TrainBase train = parsedTrain as TrainBase; + if (train == null) + { + throw new Exception(); + } + train.Handles.Reverser = new ReverserHandle(train); + train.Handles.EmergencyBrake = new EmergencyHandle(train); + train.Handles.Power = new PowerHandle(8, 8, new double[] { }, new double[] { }, train); + train.Handles.Brake = new BrakeHandle(8, 8, train.Handles.EmergencyBrake, new double[] { }, new double[] { }, train); + train.Handles.LocoBrake = new LocoBrakeHandle(0, train.Handles.EmergencyBrake, new double[] {}, new double[] {}, train); + train.Handles.LocoBrakeType = LocoBrakeType.Independant; + train.Handles.HasLocoBrake = false; + train.Handles.HoldBrake = new HoldBrakeHandle(train); + train.Specs.AveragesPressureDistribution = true; + train.SafetySystems.Headlights = new LightSource(train, 2); + if(Directory.Exists(Plugin.FileSystem.MSTSDirectory)) + { + TrainsetDirectory = OpenBveApi.Path.CombineDirectory(Plugin.FileSystem.MSTSDirectory, "TRAINS\\trainset"); + } + else + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "The MSTS directory has not been set. Attempting to find the trainset directory."); + string currentFolder = Path.GetDirectoryName(fileName); + if (currentFolder == null) + { + throw new Exception("MSTS Consist Parser: Unable to determine the consist working directory."); + } + DirectoryInfo d = Directory.GetParent(currentFolder); + if (d == null || !d.Name.Equals("TRAINS", StringComparison.InvariantCultureIgnoreCase)) + { + //FIXME: Better finding of the trainset folder (set in options?) + throw new Exception("MSTS Consist Parser: Unable to find the MSTS TRAINS folder."); + } + TrainsetDirectory = OpenBveApi.Path.CombineDirectory(currentFolder, "trainset"); + } + + if (!Directory.Exists(TrainsetDirectory)) + { + throw new Exception("MSTS Consist Parser: Unable to find the MSTS trainset folder."); + } + + Stream fb = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); + + byte[] buffer = new byte[34]; + fb.Read(buffer, 0, 2); + + bool unicode = buffer[0] == 0xFF && buffer[1] == 0xFE; + + string headerString; + if (unicode) + { + fb.Read(buffer, 0, 32); + headerString = Encoding.Unicode.GetString(buffer, 0, 16); + } + else + { + fb.Read(buffer, 2, 14); + headerString = Encoding.ASCII.GetString(buffer, 0, 8); + } + + // SIMISA@F means compressed + // SIMISA@@ means uncompressed + if (headerString.StartsWith("SIMISA@F")) + { + fb = new ZlibStream(fb, CompressionMode.Decompress); + } + else if (headerString.StartsWith("\r\nSIMISA")) + { + // ie us1rd2l1000r10d.s, we are going to allow this but warn + Console.Error.WriteLine("Improper header in " + fileName); + fb.Read(buffer, 0, 4); + } + else if (!headerString.StartsWith("SIMISA@@")) + { + throw new Exception("Unrecognized shape file header " + headerString + " in " + fileName); + } + + string subHeader; + if (unicode) + { + fb.Read(buffer, 0, 32); + subHeader = Encoding.Unicode.GetString(buffer, 0, 16); + } + else + { + fb.Read(buffer, 0, 16); + subHeader = Encoding.ASCII.GetString(buffer, 0, 8); + } + if (subHeader[7] == 't') + { + using (BinaryReader reader = new BinaryReader(fb)) + { + byte[] newBytes = reader.ReadBytes((int)(fb.Length - fb.Position)); + string s = unicode ? Encoding.Unicode.GetString(newBytes) : Encoding.ASCII.GetString(newBytes); + TextualBlock block = new TextualBlock(s, KujuTokenID.Train); + ParseBlock(block, ref train); + } + + } + else if (subHeader[7] != 'b') + { + throw new Exception("Unrecognized subHeader \"" + subHeader + "\" in " + fileName); + } + else + { + using (BinaryReader reader = new BinaryReader(fb)) + { + KujuTokenID currentToken = (KujuTokenID) reader.ReadUInt16(); + if (currentToken != KujuTokenID.Train) + { + throw new Exception(); //Shape definition + } + reader.ReadUInt16(); + uint remainingBytes = reader.ReadUInt32(); + byte[] newBytes = reader.ReadBytes((int) remainingBytes); + BinaryBlock block = new BinaryBlock(newBytes, KujuTokenID.Train); + ParseBlock(block, ref train); + } + } + + if (train.Cars.Length == 0) + { + throw new InvalidDataException("Consist " + fileName + " appears to be invalid or malformed"); + } + + bool hasCabview = false; + //create couplers & other necessary properties for the thing to load + //TODO: Pull out MSTS properties + for (int i = 0; i < train.Cars.Length; i++) + { + train.Cars[i].Coupler = new Coupler(0.9 * 0.3, 1.1 * 0.3, train.Cars[i / 2], train.Cars.Length > 1 ? train.Cars[i / 2 + 1] : null); + train.Cars[i].CurrentCarSection = CarSectionType.NotVisible; + train.Cars[i].ChangeCarSection(CarSectionType.NotVisible); + train.Cars[i].FrontBogie.ChangeSection(-1); + train.Cars[i].RearBogie.ChangeSection(-1); + train.Cars[i].Coupler.ChangeSection(-1); + train.Cars[i].Specs.ExposedFrontalArea = 0.6 * train.Cars[i].Width * train.Cars[i].Height; + train.Cars[i].Specs.UnexposedFrontalArea = 0.2 * train.Cars[i].Width * train.Cars[i].Height; + train.Cars[i].Specs.CenterOfGravityHeight = 1.6; + train.Cars[i].Specs.CriticalTopplingAngle = 0.5 * Math.PI - Math.Atan(2 * train.Cars[i].Specs.CenterOfGravityHeight / train.Cars[i].Width); + if (train.Cars[i].HasInteriorView && hasCabview == false) + { + // For the minute at least, let's set our driver car to be the first car which has an interior view + hasCabview = true; + train.DriverCar = i; + } + + train.Cars[train.Cars.Length - 1].RearAxle.Follower.TriggerType = i == train.Cars.Length - 1 ? EventTriggerType.RearCarRearAxle : EventTriggerType.OtherCarRearAxle; + + if (train.Cars[i].TractionModel is TenderEngine) + { + bool hasTender = i > 0 && train.Cars[i - 1].TractionModel is Tender || i < train.Cars.Length - 2 && train.Cars[i + 1].TractionModel is Tender; + + if (hasTender == false) + { + // this is actually harmless at the minute + Plugin.CurrentHost.AddMessage(MessageType.Error, false, "MSTS Consist Parser: Steam Engine in car " + i + " requires a tender, but none is present."); + } + } + } + + train.Cars[train.Cars.Length - 1].RearAxle.Follower.TriggerType = EventTriggerType.RearCarRearAxle; + train.Cars[train.DriverCar].Windscreen = new Windscreen(256, 10.0, train.Cars[train.DriverCar]); + train.Cars[train.DriverCar].Windscreen.Wipers = new WindscreenWiper(train.Cars[parsedTrain.DriverCar].Windscreen, WiperPosition.Left, WiperPosition.Left, 1.0, 0.0, true); // hack: zero hold time so they act as fast with two states + train.Specs.AveragesPressureDistribution = false; + train.PlaceCars(0.0); + } + + private int currentCarIndex = -1; + private CarBase currentCar; + private bool reverseCurentCar; + private void ParseBlock(Block block, ref TrainBase currentTrain) + { + Block newBlock; + switch (block.Token) + { + default: + while (block.Length() - block.Position() > 2) + { + // YUCK: If valid wagon blocks are outside the TrainCfg block (incorrect terminator) + // MSTS actually adds them to the end of the train, e.g. see default oenoloco.con + newBlock = block.ReadSubBlock(true); + ParseBlock(newBlock, ref currentTrain); + } + break; + case KujuTokenID.Comment: + case KujuTokenID.Default: + // presumably used internally by MSTS, not useful + block.Skip((int)block.Length()); + break; + case KujuTokenID.Name: + string consistName = block.ReadString(); // displayed name for consist in-game + break; + case KujuTokenID.TrainCfg: + string trainName = block.ReadString(); // we're unlikely to want this, as this is just the MSTS internal dictionary key + while (block.Length() - block.Position() > 2) + { + try + { + newBlock = block.ReadSubBlock(true); + ParseBlock(newBlock, ref currentTrain); + } + catch + { + // ignore + } + } + break; + case KujuTokenID.Serial: + /* + * This identifies the revision number of the consist (e.g. how many times it's been edited using the built-in MSTS tools) + * OpenRails ignores it. + * Not really helpful for our use-case at the minute. + */ + break; + case KujuTokenID.MaxVelocity: + // Presumably max speed of the consist (derails over this or something?) + break; + case KujuTokenID.NextWagonUID: + /* + * This seems to identify the *next* wagon UID to be used when a train is coupled + * Again, OpenRails ignores it. + */ + break; + case KujuTokenID.Durability: + // Dunno- Presumably used for derailment / similar physics somewhere + break; + case KujuTokenID.Engine: + case KujuTokenID.Wagon: + Array.Resize(ref currentTrain.Cars, currentTrain.Cars.Length + 1); + currentCarIndex++; + while (block.Length() - block.Position() > 2) + { + newBlock = block.ReadSubBlock(new[] { KujuTokenID.EngineData, KujuTokenID.WagonData, KujuTokenID.UiD, KujuTokenID.Flip, KujuTokenID.EngineVariables }); + ParseBlock(newBlock, ref currentTrain); + } + currentCar.Doors = new[] + { + new Door(-1, 1000.0, 0), + new Door(1, 1000.0, 0) + }; + if (reverseCurentCar) + { + currentCar.Reverse(); + reverseCurentCar = false; + } + currentCar.Breaker = new Breaker(currentCar); + currentCar.Sounds.Plugin = new Dictionary(); + currentTrain.Cars[currentCarIndex] = currentCar; + /* + * FIXME: Needs removing or sorting when the car is created + */ + currentTrain.Cars[currentCarIndex].FrontAxle.Follower.TriggerType = currentCarIndex == 0 ? EventTriggerType.FrontCarFrontAxle : EventTriggerType.OtherCarFrontAxle; + currentTrain.Cars[currentCarIndex].BeaconReceiver.TriggerType = currentCarIndex == 0 ? EventTriggerType.TrainFront : EventTriggerType.None; + currentTrain.Cars[currentCarIndex].BeaconReceiverPosition = 0.5 * currentTrain.Cars[currentCarIndex].Length; + currentTrain.Cars[currentCarIndex].FrontAxle.Position = 0.4 * currentTrain.Cars[currentCarIndex].Length; + currentTrain.Cars[currentCarIndex].RearAxle.Position = -0.4 * currentTrain.Cars[currentCarIndex].Length; + break; + // Engine / wagon block + case KujuTokenID.UiD: + // Unique ID of engine / wagon within consist + // For the minute, let's just create a new car and advance our car number + break; + case KujuTokenID.WagonData: + case KujuTokenID.EngineData: + /* + * FIXME: All this needs to be pulled from the eng properties, or fixed so it doesn't matter + */ + currentCar = new CarBase(currentTrain, currentCarIndex); + currentCar.HoldBrake = new CarHoldBrake(currentCar); + //FIXME END + + /* + * Pull out the wagon path bits from the block next + * From the available documentation and experience this *appears* to be as follows: + * [0] - Name of the wagon to search for + * [1] - Search path relative to the TRAINS\trainset directory, if not found in DB + * + * If WagonName that is already in the database, but with a different folder is supplied + * then the original will be returned + * https://digital-rails.com/wordpress/2018/11/18/duplicate-wagons/ + * + * HOWEVER: + * http://www.elvastower.com/forums/index.php?/topic/34187-or-consist-format/ + * OpenRails seems to treat these as: + * [0] - WagonFileName => Must add approprite eng / wag extension + * [1] - Search path relative to the TRAINS\trainset directory + * + * Going to match MSTS for the minute, but possibly needs an OpenRails detection mechanism(?) + * Note that in all / most cases, both should be the same anyways. + */ + + string[] wagonFiles = block.ReadStringArray(); + switch (wagonFiles.Length) + { + case 0: + Plugin.CurrentHost.AddMessage(MessageType.Error, true, "MSTS Consist Parser: Unable to determine WagonFile to load."); + break; + case 1: + //Just a WagonName- This is likely invalid, but let's ignore + Plugin.WagonParser.Parse(TrainsetDirectory, wagonFiles[0], block.Token == KujuTokenID.EngineData, ref currentCar, ref currentTrain); + Plugin.CurrentHost.AddMessage(MessageType.Error, true, "MSTS Consist Parser: No WagonFolder supplied, searching entire trainset folder."); + break; + case 2: + string wagonDirectory = OpenBveApi.Path.CombineDirectory(TrainsetDirectory, wagonFiles[1]); + if (!Directory.Exists(wagonDirectory)) + { + Plugin.CurrentHost.AddMessage(MessageType.Error, true, "MSTS Consist Parser: WagonFolder " + wagonDirectory + " was not found."); + currentCar.Width = 2.6; + currentCar.Height = 3.6; + currentCar.Length = 25; + currentCar.EmptyMass = 1000; + currentCar.TractionModel = new BVETrailerCar(currentCar); + currentCar.CarBrake = new ThroughPiped(currentCar); // dummy + break; + } + Plugin.WagonParser.Parse(wagonDirectory, wagonFiles[0], block.Token == KujuTokenID.EngineData, ref currentCar, ref currentTrain); + break; + default: + Plugin.WagonParser.Parse(TrainsetDirectory, wagonFiles[1], block.Token == KujuTokenID.EngineData, ref currentCar, ref currentTrain); + Plugin.CurrentHost.AddMessage(MessageType.Error, true, "MSTS Consist Parser: Two parameters were expected- Check for correct escaping of strings."); + break; + } + break; + case KujuTokenID.Flip: + // Allows a car to be reversed within a consist + reverseCurentCar = true; + break; + case KujuTokenID.EngineVariables: + // Sets properties of the train when loaded, ignore for the minute + break; + } + + } + } +} diff --git a/source/Plugins/Train.MsTs/Train/Enums/BrakeEquipmentType.cs b/source/Plugins/Train.MsTs/Train/Enums/BrakeEquipmentType.cs new file mode 100644 index 0000000000..eb1603eecb --- /dev/null +++ b/source/Plugins/Train.MsTs/Train/Enums/BrakeEquipmentType.cs @@ -0,0 +1,55 @@ +// ReSharper disable InconsistentNaming +// ReSharper disable UnusedMember.Global +namespace Train.MsTs +{ + internal enum BrakeEquipmentType + { + /// Handbrake is fitted + Handbrake = 1, + /// Manual brake fitted. + Manual_Brake = 2, + /// 3 position retaining valve is fitted. + Retainer_3_Position = 3, + /// 4 position retaining valve is fitted + /// This is meant for freight wagons only + Retainer_4_Position = 4, + /// Twin-pipe vacuum brake is fitted. + Vacuum_Brake = 5, + Vaccum_Brake = 5, + Vacumn_Brake = 5, // typo + /// Single-pipe vacuum brake is fitted. + Vacuum_Single_Pipe = 6, + Vaccum_Single_Pipe = 6, + /// Standard triple valve is fitted. + Triple_Valve = 7, + /// Triple valve that permits partial releasing of the brakes. + Graduated_Release_Triple_Valve = 8, + Graduate_Release_Triple_valve = 8, // typo + /// Electrically controlled brake system is fitted. Release and application of the brakes are independently controlled. + EP_Brake = 9, + EP_Brakes = 9, + /// Same functionality as ep_brake + ECP_Brake = 10, + /// Air tank used for normal service brake applications. This is required for all brake systems. + Auxiliary_Reservoir = 11, + Auxilary_Reservoir = 11, // typo + /// Air tank used for emergency applications. + /// This is optional. + Emergency_Brake_Reservoir = 12, + /// Electronic or computer controller on the vehicle that can be set to independently control any parameter of the braking system. + Distributor = 13, + /// One pipe controls and supplies the air brakes. + Air_Single_Pipe = 14, + /// One pipe for control air, one pipe for supply air + Air_Twin_Pipe = 15, + Air_Brake = 15, + /// Graduated triple-release valve + Graduated_Triple_Release_Valve = 16, + /// Through piped for vacuum + Vacuum_Piped = 17, + /// Through piped for air + Air_Piped = 18, + /// Brake reservoir for emergency brakes only + Emergency_reservoir = 19 + } +} diff --git a/source/Plugins/Train.MsTs/Train/Enums/BrakeSystemType.cs b/source/Plugins/Train.MsTs/Train/Enums/BrakeSystemType.cs new file mode 100644 index 0000000000..d6bf893c61 --- /dev/null +++ b/source/Plugins/Train.MsTs/Train/Enums/BrakeSystemType.cs @@ -0,0 +1,34 @@ +// ReSharper disable InconsistentNaming +// ReSharper disable UnusedMember.Global +namespace Train.MsTs +{ + internal enum BrakeSystemType + { + /// One pipe controls and supplies the air brakes. + Air_single_pipe, + /// Two pipes are used. One to control the brakes, the other to charge the reserviors. + Air_twin_pipe, + /// The car uses a manual braking system. + Manual_Braking, + /// One pipe is used to supply and control the vacuum brakes. + Vacuum_single_pipe, + Vaccum_single_pipe = Vacuum_single_pipe, + Vacumn_single_pipe = Vaccum_single_pipe, + /// Two pipes are used. One controls the vacuum brakes, the other supply the vacuum reservior. + Vacuum_twin_pipe, + Vaccum_twin_pipe = Vacuum_twin_pipe, + /// The brakes are controlled by a computer or complex electrical control system. + ECP, + /// The brakes are a combination of standard air brakes and electrical control signals. + EP, + EP_Brake = EP, + /// The vehicle has no brakes + /// Air pipe pressure will be passed through this vehicle + Air_Piped, + /// The vehicle has no brakes + /// Vacuum pipe pressure will be passed through this vehicle + Vacuum_Piped, + /// The vehicle has a handbrake + Handbrake + } +} diff --git a/source/Plugins/Train.MsTs/Train/Enums/ControlType.cs b/source/Plugins/Train.MsTs/Train/Enums/ControlType.cs new file mode 100644 index 0000000000..763996fabd --- /dev/null +++ b/source/Plugins/Train.MsTs/Train/Enums/ControlType.cs @@ -0,0 +1,19 @@ +// ReSharper disable UnusedMember.Global +namespace Train.MsTs +{ + internal enum CombinedControlType + { + /// Unknown control type + Unknown = 0, + /// Throttle handle + Throttle = 1, + /// Dynamic brake handle + Dynamic = 2, + /// Independant brake handle + Independent = 3, + /// Independant brake handle + Independant = 3, + /// Train brake handle + Train = 4 + } +} diff --git a/source/Plugins/Train.MsTs/Train/Friction.cs b/source/Plugins/Train.MsTs/Train/Friction.cs new file mode 100644 index 0000000000..989d681921 --- /dev/null +++ b/source/Plugins/Train.MsTs/Train/Friction.cs @@ -0,0 +1,98 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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 OpenBve.Formats.MsTs; +using OpenBveApi.Interface; +using OpenBveApi.World; + +namespace Train.MsTs +{ + internal class Friction + { + /// The friction constant + internal double C1; + /// The friction exponent + internal double E1; + /// The second velocity segment start value + internal double V2; + /// The second friction constant + internal double C2; + /// The second friction exponent + internal double E2; + + internal Friction() + { + C1 = 100; + E1 = 1; + V2 = -1; + C2 = 0; + E2 = 1; + } + + internal Friction(Block block) + { + try + { + C1 = block.ReadSingle(UnitOfTorque.NewtonMetersPerSecond); + E1 = block.ReadSingle(); + V2 = block.ReadSingle(UnitOfVelocity.MetersPerSecond); + C2 = block.ReadSingle(UnitOfTorque.NewtonMetersPerSecond); + E2 = block.ReadSingle(); + + if (E1 <= 0 || E2 <= 0 || V2 < 0) + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "MSTS Vehicle Parser: The Friction properties contain nonsensical data."); + C1 = 100; + E1 = 1; + V2 = -1; + C2 = 0; + E2 = 1; + } + } + catch + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "MSTS Vehicle Parser: The Friction properties contain invalid data."); + C1 = 100; + E1 = 1; + V2 = -1; + C2 = 0; + E2 = 1; + } + + } + + internal double GetResistanceValue(double speed) + { + // see Eng_and_wag_file_reference_guideV2.doc + // Gives the result in newton-meters per second + if (V2 < 0 || speed <= V2) + { + return C1 * Math.Pow(speed, E1); + } + + return C1 + Math.Pow(V2, E1) + C2 * (V2 + Math.Pow(speed - V2, E2)); + } + } +} diff --git a/source/Plugins/Train.MsTs/Train/MSTSAxle.cs b/source/Plugins/Train.MsTs/Train/MSTSAxle.cs new file mode 100644 index 0000000000..b16e619608 --- /dev/null +++ b/source/Plugins/Train.MsTs/Train/MSTSAxle.cs @@ -0,0 +1,62 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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 OpenBveApi.Hosts; +using OpenBveApi.Trains; + +namespace Train.MsTs +{ + /// Derived class for axles on MSTS cars + public class MSTSAxle : AbstractAxle + { + /// The friction properties + private readonly Friction FrictionProperties; + /// The adhesion properties + private readonly Adhesion AdhesionProperties; + + internal MSTSAxle(HostInterface currentHost, AbstractTrain train, AbstractCar car, Friction friction, Adhesion adhesion) : base(currentHost, train, car) + { + FrictionProperties = friction; + AdhesionProperties = adhesion; + } + + public override double GetResistance(double Speed, double FrontalArea, double AirDensity, double AccelerationDueToGravity) + { + return FrictionProperties.GetResistanceValue(Speed) / Math.Max(1.0, baseCar.CurrentMass); + } + + public override double CriticalWheelSlipAccelerationForElectricMotor(double AccelerationDueToGravity) + { + return AdhesionProperties.GetWheelslipValue(); + } + + public override double CriticalWheelSlipAccelerationForFrictionBrake(double AccelerationDueToGravity) + { + // TODO: This is the BVE formula + double NormalForceAcceleration = Follower.WorldUp.Y * AccelerationDueToGravity; + return 0.35 * Follower.AdhesionMultiplier * NormalForceAcceleration; + } + } +} diff --git a/source/Plugins/Train.MsTs/Train/VehicleParser.cs b/source/Plugins/Train.MsTs/Train/VehicleParser.cs new file mode 100644 index 0000000000..d977feab33 --- /dev/null +++ b/source/Plugins/Train.MsTs/Train/VehicleParser.cs @@ -0,0 +1,1185 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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 LibRender2.Smoke; +using LibRender2.Trains; +using OpenBve.Formats.MsTs; +using OpenBveApi.Interface; +using OpenBveApi.Math; +using OpenBveApi.Motor; +using OpenBveApi.Objects; +using OpenBveApi.Trains; +using OpenBveApi.World; +using SharpCompress.Compressors; +using SharpCompress.Compressors.Deflate; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using TrainManager.BrakeSystems; +using TrainManager.Car; +using TrainManager.Car.Systems; +using TrainManager.Handles; +using TrainManager.Motor; +using TrainManager.Power; +using TrainManager.Trains; + +namespace Train.MsTs +{ + internal partial class WagonParser + { + private readonly Dictionary wagonCache; + private readonly Dictionary engineCache; + private readonly List soundFiles; + private string[] wagonFiles; + private double wheelRadius; + private bool exteriorLoaded = false; + private bool RequiresTender = false; + private bool EngineAlreadyParsed = false; + + internal WagonParser() + { + wagonCache = new Dictionary(); + engineCache = new Dictionary(); + soundFiles = new List(); + } + + internal void Parse(string trainSetDirectory, string wagonName, bool isEngine, ref CarBase currentCar, ref TrainBase train) + { + /* + * Reset to 'sensible' defaults for required parameters + * => International Union of Railways figure for 'standard' wheel size + * => Default BVE brake system parameters (we know these work correctly) + * => Reset gearbox parameters + * => Set max force figures to zero + */ + wheelRadius = 0.92; + mainReservoirMinimumPressure = 690000; + mainReservoirMaximumPressure = 780000; + brakeCylinderMaximumPressure = 440000; + compressionRate = 3500; + maxForce = 0; + maxBrakeForce = 0; + Gears = null; + exteriorLoaded = false; + wagonFiles = Directory.GetFiles(trainSetDirectory, isEngine ? "*.eng" : "*.wag", SearchOption.AllDirectories); + currentEngineType = EngineType.NoEngine; + /* + * MSTS maintains an internal database, as opposed to using full paths + * Unfortunately, this means we've got to do an approximation of the same thing! + * (TrainStore is / was an early MSTS attempt to deal with the same problem by moving + * excess eng, wag and con files out from the MSTS directory) + * + * Unclear at the minute as to whether an eng can refer to a *separate* wag file, but + * unless documentation specifically states otherwise, we'll assume it can + * + * So, the *first* thing we need to do is to read the engine (as this may + * refer to a different sub-wagon): + */ + if (isEngine) + { + if (engineCache.ContainsKey(wagonName)) + { + ReadWagonData(engineCache[wagonName], ref wagonName, true, ref currentCar, ref train); + } + else + { + for (int i = 0; i < wagonFiles.Length; i++) + { + if (ReadWagonData(wagonFiles[i], ref wagonName, true, ref currentCar, ref train)) + { + break; + } + } + } + } + else + { + currentCar.TractionModel = new BVETrailerCar(currentCar); + } + /* + * We've now found the engine properties- + * Now, we need to read the wagon properties to find the visual wagon to display + * (The Engine only holds the physics data) + */ + if (wagonCache.ContainsKey(wagonName)) + { + ReadWagonData(wagonCache[wagonName], ref wagonName, false, ref currentCar, ref train); + } + else + { + for (int i = 0; i < wagonFiles.Length; i++) + { + if (ReadWagonData(wagonFiles[i], ref wagonName, false, ref currentCar, ref train)) + { + break; + } + } + } + + // as properties may not be in order, set this stuff last + if (isEngine) + { + EngineAlreadyParsed = true; + // FIXME: Default BVE values + currentCar.Specs.JerkPowerUp = 10.0; + currentCar.Specs.JerkPowerDown = 10.0; + if (currentCar.DrivingWheels.Count == 0) + { + // An engine must implicity have at least one set of driving wheels + currentCar.DrivingWheels.Add(new Wheels(1, wheelRadius)); + } + // NOTE: Amps figure appears to need to be divided by the total wheels (to give a per-axle figure) + // see NWC_40138.eng for example- This is an issue where higher amps figures are in play + maxEngineAmps /= currentCar.DrivingWheels[0].TotalNumber; + maxBrakeAmps /= currentCar.DrivingWheels[0].TotalNumber; + + switch (currentEngineType) + { + case EngineType.Diesel: + currentCar.TractionModel = new DieselEngine(currentCar, new AccelerationCurve[] { new MSTSAccelerationCurve(currentCar, maxForce, maxContinuousForce, maxVelocity) }, dieselIdleRPM, dieselIdleRPM, dieselMaxRPM, dieselRPMChangeRate, dieselRPMChangeRate, dieselIdleUse, dieselMaxUse); + currentCar.TractionModel.FuelTank = new FuelTank(GetMaxDieselCapacity(currentCar.Index)); + currentCar.TractionModel.IsRunning = true; + + if (maxBrakeAmps > 0 && maxEngineAmps > 0) + { + currentCar.TractionModel.Components.Add(EngineComponent.RegenerativeTractionMotor, new RegenerativeTractionMotor(currentCar.TractionModel, maxEngineAmps, maxBrakeAmps)); + } + else if (maxEngineAmps > 0) + { + currentCar.TractionModel.Components.Add(EngineComponent.TractionMotor, new TractionMotor(currentCar.TractionModel, maxEngineAmps)); + } + break; + case EngineType.DieselHydraulic: + AccelerationCurve[] accelerationCurves = new AccelerationCurve[Gears.Length]; + for (int i = 0; i < Gears.Length; i++) + { + accelerationCurves[i] = new MSTSAccelerationCurve(currentCar, Gears[i].MaxTractiveForce, maxContinuousForce, Gears[i].MaximumSpeed); + } + + currentCar.TractionModel = new DieselEngine(currentCar, accelerationCurves, dieselIdleRPM, dieselIdleRPM, dieselMaxRPM, dieselRPMChangeRate, dieselRPMChangeRate, dieselIdleUse, dieselMaxUse); + currentCar.TractionModel.FuelTank = new FuelTank(GetMaxDieselCapacity(currentCar.Index)); + currentCar.TractionModel.IsRunning = true; + currentCar.TractionModel.Components.Add(EngineComponent.Gearbox, new Gearbox(currentCar.TractionModel, Gears, gearboxOperationMode)); + break; + case EngineType.Electric: + currentCar.TractionModel = new ElectricEngine(currentCar, new AccelerationCurve[] { new MSTSAccelerationCurve(currentCar, maxForce, maxContinuousForce, maxVelocity) }); + currentCar.TractionModel.Components.Add(EngineComponent.Pantograph, new Pantograph(currentCar.TractionModel)); + + if (maxBrakeAmps > 0 && maxEngineAmps > 0) + { + currentCar.TractionModel.Components.Add(EngineComponent.RegenerativeTractionMotor, new RegenerativeTractionMotor(currentCar.TractionModel, maxEngineAmps, maxBrakeAmps)); + } + else if (maxEngineAmps > 0) + { + currentCar.TractionModel.Components.Add(EngineComponent.TractionMotor, new TractionMotor(currentCar.TractionModel, maxEngineAmps)); + } + break; + case EngineType.Steam: + // NOT YET IMPLEMENTED FULLY + if (RequiresTender) + { + currentCar.TractionModel = new TenderEngine(currentCar, new AccelerationCurve[] { new MSTSAccelerationCurve(currentCar, maxForce, maxContinuousForce, maxVelocity) }); + if (currentCar.Index > 0) + { + CarBase previousCar = currentCar.baseTrain.Cars[currentCar.Index - 1]; + if (previousCar.TractionModel is Tender tender && tender.MaxWaterLevel == -1) + { + // recreate, as values are stored in the ENG + previousCar.TractionModel = new Tender(previousCar, MaxFuelLevel, MaxWaterLevel); + } + } + } + else + { + currentCar.TractionModel = new TankEngine(currentCar, new AccelerationCurve[] { new MSTSAccelerationCurve(currentCar, maxForce, maxContinuousForce, maxVelocity) }, MaxFuelLevel, MaxWaterLevel); + } + break; + case EngineType.NoEngine: + currentCar.TractionModel = new BVETrailerCar(currentCar); + break; + } + + if (currentCar.ReAdhesionDevice == null) + { + currentCar.ReAdhesionDevice = new BveReAdhesionDevice(currentCar, hasAntiSlipDevice ? ReadhesionDeviceType.TypeB : ReadhesionDeviceType.NotFitted); + } + + currentCar.Windscreen = new Windscreen(0, 0, currentCar); + currentCar.Windscreen.Wipers = new WindscreenWiper(currentCar.Windscreen, WiperPosition.Left, WiperPosition.Left, 1.0, 1.0); + + if (Exhaust.Size > 0) + { + Exhaust.Offset.Z -= 0.5 * currentCar.Length; + currentCar.ParticleSources.Add(new ParticleSource(Plugin.Renderer, currentCar, Exhaust.Offset, Exhaust.Size, Exhaust.SmokeMaxMagnitude, Exhaust.Direction)); + } + } + else + { + if (currentWagonType == WagonType.Tender) + { + currentCar.TractionModel = new Tender(currentCar, MaxFuelLevel, MaxWaterLevel); + } + + if (currentCar.TrailingWheels.Count == 0) + { + // non-engine must implicitly have at least one set of trailing wheels + currentCar.TrailingWheels.Add(new Wheels(1, wheelRadius)); + } + } + + if (brakeSystemTypes != null) + { + + // Add brakes last, as we need the acceleration values + if (brakeSystemTypes.Contains(BrakeSystemType.Vacuum_Piped) || brakeSystemTypes.Contains(BrakeSystemType.Air_Piped) || (brakeSystemTypes.Length == 1 && brakeSystemTypes[0] == BrakeSystemType.Handbrake)) + { + /* + * FIXME: Need to implement vac braked / air piped and vice-versa, but for the minute, we'll assume that if one or the other is present + * then the vehicle has no brakes + */ + currentCar.CarBrake = new ThroughPiped(currentCar); + } + else + { + if (brakeSystemTypes.Contains(BrakeSystemType.Air_single_pipe) || brakeSystemTypes.Contains(BrakeSystemType.Air_twin_pipe) || brakeSystemTypes.Contains(BrakeSystemType.EP) || brakeSystemTypes.Contains(BrakeSystemType.ECP)) + { + AirBrake airBrake; + // FIXME: Relationship of brake system values needs to be (close) to original proportions else the physics bug out + double bcVal = brakeCylinderMaximumPressure / 440000; + double emergencyVal = emergencyRate / 300000.0; + double releaseVal = releaseRate / 200000.0; + mainReservoirMinimumPressure = 690000.0 * bcVal; + mainReservoirMaximumPressure = 780000.0 * bcVal; + double operatingPressure = brakeCylinderMaximumPressure + 0.75 * (mainReservoirMinimumPressure - brakeCylinderMaximumPressure); + + if (brakeSystemTypes.Contains(BrakeSystemType.EP) || brakeSystemTypes.Contains(BrakeSystemType.ECP)) + { + // Combined air brakes and control signals + // Assume equivilant to ElectromagneticStraightAirBrake + airBrake = new ElectromagneticStraightAirBrake(EletropneumaticBrakeType.None, currentCar, 0, 0, 0, 0, new AccelerationCurve[] { new MSTSDecelerationCurve(train, maxBrakeForce == 0 ? maxForce : maxBrakeForce) }); + airBrake.BrakePipe = new BrakePipe(operatingPressure, 10000000.0, 1500000.0, 5000000.0, true); + } + else + { + // Assume equivilant to ElectromagneticStraightAirBrake + airBrake = new ElectromagneticStraightAirBrake(EletropneumaticBrakeType.None, currentCar, 0, 0, 0, 0, new AccelerationCurve[] { new MSTSDecelerationCurve(train, maxBrakeForce == 0 ? maxForce : maxBrakeForce) }); + airBrake.BrakePipe = new BrakePipe(operatingPressure, 10000000.0, 1500000.0, 5000000.0, false); + } + + airBrake.MainReservoir = new MainReservoir(mainReservoirMinimumPressure, mainReservoirMaximumPressure, 0.01, 0.075 / train.Cars.Length); + airBrake.Compressor = new Compressor(5000.0, airBrake.MainReservoir, currentCar); + airBrake.BrakeCylinder = new BrakeCylinder(brakeCylinderMaximumPressure, brakeCylinderMaximumPressure * 1.1, 90000.0, emergencyRate, releaseRate); + double r = 200000.0 / airBrake.BrakeCylinder.EmergencyMaximumPressure - 1.0; + if (r < 0.1) r = 0.1; + if (r > 1.0) r = 1.0; + airBrake.AuxiliaryReservoir = new AuxiliaryReservoir(0.975 * operatingPressure, 200000.0, 0.5, r); + airBrake.EqualizingReservoir = new EqualizingReservoir(50000.0, 250000.0, 200000.0); + airBrake.EqualizingReservoir.NormalPressure = 1.005 * operatingPressure; + airBrake.StraightAirPipe = new StraightAirPipe(300000.0, emergencyRate * emergencyVal, releaseRate * releaseVal); + + currentCar.CarBrake = airBrake; + } + + if (brakeSystemTypes.Contains(BrakeSystemType.Vaccum_single_pipe) || brakeSystemTypes.Contains(BrakeSystemType.Vacuum_twin_pipe)) + { + VaccumBrake vaccumBrake = new VaccumBrake(currentCar, new AccelerationCurve[] { new MSTSDecelerationCurve(train, maxBrakeForce == 0 ? maxForce : maxBrakeForce) }); + vaccumBrake.MainReservoir = new MainReservoir(71110, 84660, 0.01, 0.075 / train.Cars.Length); // ~21in/hg - ~25in/hg + vaccumBrake.BrakeCylinder = new BrakeCylinder(brakeCylinderMaximumPressure, brakeCylinderMaximumPressure * 1.1, 90000.0, 300000.0, 200000.0); + vaccumBrake.AuxiliaryReservoir = new AuxiliaryReservoir(0.975 * brakeCylinderMaximumPressure, 200000.0, 0.5, 1.0); + vaccumBrake.EqualizingReservoir = new EqualizingReservoir(50000.0, 250000.0, 200000.0); + vaccumBrake.EqualizingReservoir.NormalPressure = 1.005 * (vaccumBrake.BrakeCylinder.EmergencyMaximumPressure + 0.75 * (vaccumBrake.MainReservoir.MinimumPressure - vaccumBrake.BrakeCylinder.EmergencyMaximumPressure)); + vaccumBrake.BrakePipe = new BrakePipe(brakeCylinderMaximumPressure, 10000000.0, 1500000.0, 5000000.0, false); + currentCar.CarBrake = vaccumBrake; + } + + currentCar.CarBrake.BrakeType = currentCar.Index == train.DriverCar || isEngine ? BrakeType.Main : BrakeType.Auxiliary; + currentCar.CarBrake.JerkUp = 10; + currentCar.CarBrake.JerkDown = 10; + } + + currentCar.CarBrake.Initialize(TrainStartMode.ServiceBrakesAts); + } + else + { + currentCar.CarBrake = new ThroughPiped(currentCar); + } + + currentCar.FrontAxle = new MSTSAxle(Plugin.CurrentHost, train, currentCar, friction ?? new Friction(), adhesion ?? new Adhesion(currentCar, currentEngineType == EngineType.Steam)); + currentCar.RearAxle = new MSTSAxle(Plugin.CurrentHost, train, currentCar, friction ?? new Friction(), adhesion ?? new Adhesion(currentCar, currentEngineType == EngineType.Steam)); + + if (soundFiles.Count > 0) + { + for (int i = 0; i < soundFiles.Count; i++) + { + SoundModelSystemParser.ParseSoundFile(soundFiles[i], ref currentCar); + } + soundFiles.Clear(); + } + } + + internal bool ReadWagonData(string fileName, ref string wagonName, bool isEngine, ref CarBase car, ref TrainBase train) + { + Stream fb = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); + vigilanceDevices = new List(); + byte[] buffer = new byte[34]; + fb.Read(buffer, 0, 2); + + bool unicode = buffer[0] == 0xFF && buffer[1] == 0xFE; + + string headerString; + if (unicode) + { + fb.Read(buffer, 0, 32); + headerString = Encoding.Unicode.GetString(buffer, 0, 16); + } + else + { + fb.Read(buffer, 2, 14); + headerString = Encoding.ASCII.GetString(buffer, 0, 8); + } + + // SIMISA@F means compressed + // SIMISA@@ means uncompressed + if (headerString.StartsWith("SIMISA@F")) + { + fb = new ZlibStream(fb, CompressionMode.Decompress); + } + else if (headerString.StartsWith("\r\nSIMISA")) + { + // ie us1rd2l1000r10d.s, we are going to allow this but warn + Console.Error.WriteLine("Improper header in " + fileName); + fb.Read(buffer, 0, 4); + } + else if (!headerString.StartsWith("SIMISA@@")) + { + throw new Exception("MSTS Vehicle Parser: Unrecognized vehicle file header " + headerString + " in " + fileName); + } + + string subHeader; + if (unicode) + { + fb.Read(buffer, 0, 32); + subHeader = Encoding.Unicode.GetString(buffer, 0, 16); + } + else + { + fb.Read(buffer, 0, 16); + subHeader = Encoding.ASCII.GetString(buffer, 0, 8); + } + if (subHeader[7] == 't') + { + using (BinaryReader reader = new BinaryReader(fb)) + { + byte[] newBytes = reader.ReadBytes((int)(fb.Length - fb.Position)); + string s = unicode ? Encoding.Unicode.GetString(newBytes) : Encoding.ASCII.GetString(newBytes); + + /* + * Engine files contain two blocks, not in an enclosing block + * Assume that these can be of arbritrary order, so read using a dictionary + */ + List blocks = TextualBlock.ReadBlocks(s); + + List wagonBlocks = blocks.Where(b => b.Token == KujuTokenID.Wagon).ToList(); + List engineBlocks = blocks.Where(b => b.Token == KujuTokenID.Engine).ToList(); + + if (wagonBlocks.Count == 0) + { + //Not found any wagon data in this file + return false; + } + if (isEngine && engineBlocks.Count > 0) + { + if (engineBlocks.Count > 1) + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "MSTS Vehicle Parser: Multiple engine blocks encounted in MSTS ENG file "+ fileName); + } + return ParseBlock(engineBlocks[0], fileName, ref wagonName, true, ref car, ref train); + } + if (!isEngine && wagonBlocks.Count > 0) + { + if (wagonBlocks.Count > 1) + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "MSTS Vehicle Parser: Multiple wagon blocks encounted in MSTS WAG file " + fileName); + } + return ParseBlock(wagonBlocks[0], fileName, ref wagonName, false, ref car, ref train); + } + return false; + } + + } + if (subHeader[7] != 'b') + { + throw new Exception("Unrecognized subHeader \"" + subHeader + "\" in " + fileName); + } + + using (BinaryReader reader = new BinaryReader(fb)) + { + KujuTokenID currentToken = (KujuTokenID) reader.ReadUInt16(); + if (currentToken != KujuTokenID.Wagon) + { + throw new Exception(); //Shape definition + } + reader.ReadUInt16(); + uint remainingBytes = reader.ReadUInt32(); + byte[] newBytes = reader.ReadBytes((int) remainingBytes); + BinaryBlock block = new BinaryBlock(newBytes, KujuTokenID.Wagon); + try + { + ParseBlock(block, fileName, ref wagonName, isEngine, ref car, ref train); + } + catch (InvalidDataException) + { + return false; + } + + } + return true; + } + + private double maxForce = 0; + private double maxContinuousForce = 0; + private double maxBrakeForce = 0; + private BrakeSystemType[] brakeSystemTypes; + private EngineType currentEngineType; + private WagonType currentWagonType; + private double dieselIdleRPM; + private double dieselMaxRPM; + private double dieselRPMChangeRate; + private double dieselIdleUse; + private double dieselMaxUse; + private double dieselCapacity; + private double dieselMaxTractiveEffortSpeed; + private double maxEngineAmps; + private double maxBrakeAmps; + private double mainReservoirMinimumPressure = 690000.0; + private double mainReservoirMaximumPressure = 780000.0; + private double brakeCylinderMaximumPressure = 440000.0; + private double emergencyRate; + private double releaseRate; + private double compressionRate = 3500; + private double maxVelocity; + private bool hasAntiSlipDevice; + private List vigilanceDevices; + private Exhaust Exhaust; + private Gear[] Gears; + private GearboxOperation gearboxOperationMode = GearboxOperation.Manual; + private double maxSandingSpeed; + private CouplingType couplingType; + private Friction friction; + private Adhesion adhesion; + private UnitOfPressure brakeSystemDefaultUnits = UnitOfPressure.PoundsPerSquareInch; + private double MaxWaterLevel = -1; + private double MaxFuelLevel = -1; + + private double GetMaxDieselCapacity(int carIndex) + { + if (dieselCapacity <= 0 && MaxFuelLevel > 0) + { + return MaxFuelLevel; // if zero capacity, try the figure from EngineVariables + } + + if (dieselCapacity == 0) + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "MSTS Vehicle Parser: Diesel locomotive for car " + carIndex + " appears to have zero fuel capacity."); + } + return dieselCapacity; + } + + private bool ParseBlock(Block block, string fileName, ref string wagonName, bool isEngine, ref CarBase car, ref TrainBase train) + { + Block newBlock; + switch (block.Token) + { + case KujuTokenID.Wagon: + string name = block.ReadString().Trim(); + if (isEngine) + { + // Within an Engine block, the Wagon block defines the visual wagon to display + wagonName = name; + } + else + { + if (!name.Equals(wagonName, StringComparison.InvariantCultureIgnoreCase)) + { + if (!wagonCache.ContainsKey(name)) + { + // CHECK: How do MSTS / OR mediate between files with the same key + wagonCache.Add(name, fileName); + } + return false; + } + while (block.Length() - block.Position() > 2) + { + try + { + newBlock = block.ReadSubBlock(true); + ParseBlock(newBlock, fileName, ref wagonName, false, ref car, ref train); + } + catch + { + //ignore + } + } + } + break; + case KujuTokenID.Engine: + name = block.ReadString().Trim(); + if (!name.Equals(wagonName, StringComparison.InvariantCultureIgnoreCase)) + { + if (!engineCache.ContainsKey(name)) + { + // CHECK: How do MSTS / OR mediate between files with the same key + engineCache.Add(name, fileName); + } + return false; + } + while (block.Length() - block.Position() > 2) + { + try + { + newBlock = block.ReadSubBlock(true); + ParseBlock(newBlock, fileName, ref wagonName, isEngine, ref car, ref train); + } + catch + { + //ignore + } + } + break; + case KujuTokenID.Type: + switch (block.ParentBlock.Token) + { + case KujuTokenID.Engine: + case KujuTokenID.Wagon: + if (isEngine) + { + currentEngineType = block.ReadEnumValue(default(EngineType)); + } + else + { + try + { + currentWagonType = block.ReadEnumValue(default(WagonType)); + } + catch + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "MSTS Vehicle Parser: Invalid vehicle type specified."); + } + } + break; + case KujuTokenID.Coupling: + couplingType = block.ReadEnumValue(default(CouplingType)); + break; + } + break; + case KujuTokenID.DieselEngineType: + if (currentEngineType == EngineType.Diesel) + { + string type = block.ReadString(); + switch (type.ToLowerInvariant()) + { + case "hydraulic": + currentEngineType = EngineType.DieselHydraulic; + break; + } + } + else + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "MSTS Vehicle Parser: Invalid vehicle type specified."); + } + break; + case KujuTokenID.WagonShape: + if(Plugin.PreviewOnly) + { + break; + } + // Loads exterior object + string objectFile = OpenBveApi.Path.CombineFile(Path.GetDirectoryName(fileName), block.ReadString()); + if (!File.Exists(objectFile)) + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "MSTS Vehicle Parser: Vehicle object file " + objectFile + " was not found"); + return true; + } + + for (int i = 0; i < Plugin.CurrentHost.Plugins.Length; i++) + { + + if (Plugin.CurrentHost.Plugins[i].Object != null && Plugin.CurrentHost.Plugins[i].Object.CanLoadObject(objectFile)) + { + Plugin.CurrentHost.Plugins[i].Object.LoadObject(objectFile, Path.GetDirectoryName(fileName), Encoding.Default, out UnifiedObject carObject); + if (exteriorLoaded) + { + CarSection exteriorCarSection = car.CarSections[CarSectionType.Exterior]; + exteriorCarSection.AppendObject(Plugin.CurrentHost, Vector3.Zero, car, carObject); + car.CarSections[CarSectionType.Exterior] = exteriorCarSection; + } + else + { + car.CarSections.Add(CarSectionType.Exterior, new CarSection(Plugin.CurrentHost, ObjectType.Dynamic, false, car, carObject)); + } + break; + } + } + + exteriorLoaded = true; + break; + case KujuTokenID.Size: + // Physical size of the car + car.Width = block.ReadSingle(UnitOfLength.Meter); + if (car.Width <= 0.1) // see for example LU1938TS - typo makes the car 2.26mm high + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "MSTS Vehicle Parser: Vehicle width is invalid."); + car.Width = 2; + } + car.Height = block.ReadSingle(UnitOfLength.Meter); + if (car.Height <= 0.1) + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "MSTS Vehicle Parser: Vehicle height is invalid."); + car.Height = 2; + } + car.Length = block.ReadSingle(UnitOfLength.Meter); + if (car.Length <= 0.5) + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "MSTS Vehicle Parser: Vehicle length is invalid."); + car.Length = 25; + } + break; + case KujuTokenID.Mass: + // Sets the empty mass of the car + car.EmptyMass = block.ReadSingle(UnitOfWeight.Kilograms); + break; + case KujuTokenID.BrakeEquipmentType: + // Determines the brake equipment types available + BrakeEquipmentType[] brakeEquipmentTypes = block.ReadEnumArray(default(BrakeEquipmentType)); + break; + case KujuTokenID.BrakeSystemType: + // Determines the brake system types available + brakeSystemTypes = block.ReadEnumArray(default(BrakeSystemType)); + // WARNING: If vehicle only has vac brakes, default parameters for brake system are in/hg + // otherwise, default parameters are psi + bool hasAirBrakes = false; + for (int i = 0; i < brakeSystemTypes.Length; i++) + { + switch (brakeSystemTypes[i]) + { + case BrakeSystemType.Air_single_pipe: + case BrakeSystemType.Air_twin_pipe: + case BrakeSystemType.EP: + case BrakeSystemType.ECP: + hasAirBrakes = true; + break; + } + } + + if (!hasAirBrakes) + { + brakeSystemDefaultUnits = UnitOfPressure.InchesOfMercury; + } + break; + case KujuTokenID.CabView: + // Loads cab view file + if (Plugin.PreviewOnly) + { + break; + } + string cabViewFile = OpenBveApi.Path.CombineFile(OpenBveApi.Path.CombineDirectory(Path.GetDirectoryName(fileName), "CABVIEW"), block.ReadString()); + if (!File.Exists(cabViewFile)) + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "MSTS Vehicle Parser: Cab view file " + cabViewFile + " was not found"); + return true; + } + + CabviewFileParser.ParseCabViewFile(cabViewFile, ref car); + car.HasInteriorView = true; + break; + case KujuTokenID.Description: + /* + * Only I believe valid in ENG files + * NOTE: For some reason, the array appears to be as lines, however it also contains the newline character + * Binary format?? + */ + string[] strings = block.ReadStringArray(); + car.Description = string.Join("", strings).Replace(@"\n", Environment.NewLine); + break; + case KujuTokenID.Comment: + if(car.Description == string.Empty) + { + // WAG files often have a comment block with a basic description + strings = block.ReadStringArray(); + car.Description = string.Join("", strings); + } + break; + case KujuTokenID.MaxPower: + // maximum continous power at the rails provided to the wheels + break; + case KujuTokenID.MaxForce: + // maximum force applied when starting + if (!isEngine) + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "MSTS Vehicle Parser: MaxForce is not expected to be present in a wagon block."); + break; + } + maxForce = block.ReadSingle(UnitOfForce.Newton); + break; + case KujuTokenID.MaxVelocity: + maxVelocity = block.ReadSingle(UnitOfVelocity.MetersPerSecond); + break; + case KujuTokenID.MaxBrakeForce: + maxBrakeForce = block.ReadSingle(UnitOfForce.Newton); + break; + case KujuTokenID.MaxContinuousForce: + // Maximum continuous force + if (!isEngine) + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "MSTS Vehicle Parser: MaxContinuousForce is not expected to be present in a wagon block."); + break; + } + maxContinuousForce = block.ReadSingle(UnitOfForce.Newton); + break; + case KujuTokenID.RunUpTimeToMaxForce: + // + break; + case KujuTokenID.WheelRadius: + wheelRadius = block.ReadSingle(UnitOfLength.Meter); + break; + case KujuTokenID.NumWheels: + int numWheels = block.ReadInt32(); + if (numWheels < 2) + { + // NumWheels *should* be divisible by two (to get axles), but some content uses a single wheel, e.g. stock Class 50 + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "MSTS Vehicle Parser: Invalid number of wheels."); + numWheels = 1; + } + else + { + numWheels /= 2; + } + + if (block.ParentBlock.Token == KujuTokenID.Engine) + { + car.DrivingWheels.Add(new Wheels(numWheels == 1 ? 2 : numWheels, wheelRadius)); + } + else + { + car.TrailingWheels.Add(new Wheels(numWheels == 1 ? 2 : numWheels, wheelRadius)); + } + break; + case KujuTokenID.Sound: + // parse the sounds *after* we've loaded the traction model though + string sF = block.ReadString(); + string soundFile = OpenBveApi.Path.CombineFile(OpenBveApi.Path.CombineDirectory(Path.GetDirectoryName(fileName), "SOUND"), sF); + if (!File.Exists(soundFile)) + { + if (Directory.Exists(Plugin.FileSystem.MSTSDirectory)) + { + // If sound file is not relative to the ENG / WAG, try in the MSTS common sound directory (most generic wagons + coaches) + soundFile = OpenBveApi.Path.CombineFile(OpenBveApi.Path.CombineDirectory(Plugin.FileSystem.MSTSDirectory, "SOUND"), sF); + } + if (!File.Exists(soundFile)) + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "MSTS Vehicle Parser: SMS file " + soundFile + " was not found."); + } + break; + } + soundFiles.Add(soundFile); + break; + case KujuTokenID.EngineControllers: + if (!isEngine) + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "MSTS Vehicle Parser: An EngineControllers block is not valid in a wagon block."); + break; + } + + while (block.Position() < block.Length() - 2) + { + // large number of potential controls when including diesel + steam, so allow *any* block here + newBlock = block.ReadSubBlock(true); + ParseBlock(newBlock, fileName, ref wagonName, true, ref car, ref train); + } + break; + case KujuTokenID.Throttle: + if (EngineAlreadyParsed) + { + // NOTE: Per-car handles not yet supported, so take the handles from the first engine in the train + // otherwise, if we've got a VariableHandle (0-100) followed by notched, we'll be stuck at basically zero power + break; + } + if (currentEngineType == EngineType.Steam) + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "MSTS Vehicle Parser: A throttle is not valid for a Steam Locomotive."); + break; + } + train.Handles.Power = ParseHandle(block, train, true); + break; + case KujuTokenID.Brake_Train: + if (EngineAlreadyParsed) + { + break; + } + train.Handles.Brake = ParseHandle(block, train, false); + break; + case KujuTokenID.Brake_Engine: + if (EngineAlreadyParsed) + { + break; + } + train.Handles.HasLocoBrake = true; + train.Handles.LocoBrake = ParseHandle(block, train, false); + break; + case KujuTokenID.Combined_Control: + if (EngineAlreadyParsed) + { + break; + } + block.ReadSingle(); + block.ReadSingle(); + block.ReadSingle(); + block.ReadSingle(); + + CombinedControlType firstCombinedControl = block.ReadEnumValue(default(CombinedControlType)); + CombinedControlType secondCombinedControl = block.ReadEnumValue(default(CombinedControlType)); + + if (firstCombinedControl == CombinedControlType.Throttle && secondCombinedControl == CombinedControlType.Dynamic) + { + train.Handles.HandleType = HandleType.SingleHandle; + } + break; + case KujuTokenID.Sanding: + switch (block.ParentBlock.Token) + { + case KujuTokenID.Engine: + maxSandingSpeed = block.ReadSingle(UnitOfVelocity.MetersPerSecond, UnitOfVelocity.MilesPerHour); + break; + case KujuTokenID.EngineControllers: + int p1 = block.ReadInt16(); + int p2 = block.ReadInt16(); + int p3 = block.ReadInt16(); + if (p1 == 0 && p2 == 1 && p3 == 0) + { + car.ReAdhesionDevice = new Sanders(car, SandersType.PressAndHold, maxSandingSpeed); + } + break; + } + break; + case KujuTokenID.DieselEngineSpeedOfMaxTractiveEffort: + dieselMaxTractiveEffortSpeed = block.ReadSingle(UnitOfVelocity.MetersPerSecond); + break; + case KujuTokenID.DieselEngineIdleRPM: + dieselIdleRPM = block.ReadSingle(); + break; + case KujuTokenID.DieselEngineMaxRPM: + dieselMaxRPM = block.ReadSingle(); + break; + case KujuTokenID.DieselEngineMaxRPMChangeRate: + dieselRPMChangeRate = block.ReadSingle(); + break; + case KujuTokenID.DieselUsedPerHourAtIdle: + dieselIdleUse = block.ReadSingle(UnitOfVolume.Litres); + dieselIdleUse /= 3600; + break; + case KujuTokenID.DieselUsedPerHourAtMaxPower: + dieselMaxUse = block.ReadSingle(UnitOfVolume.Litres); + dieselMaxUse /= 3600; + break; + case KujuTokenID.MaxDieselLevel: + dieselCapacity = block.ReadSingle(UnitOfVolume.Litres); + break; + case KujuTokenID.MaxCurrent: + maxEngineAmps = block.ReadSingle(UnitOfCurrent.Amps); + break; + case KujuTokenID.DynamicBrakesResistorCurrentLimit: + maxBrakeAmps = block.ReadSingle(UnitOfCurrent.Amps); + break; + case KujuTokenID.AirBrakesMainMinResAirPressure: + mainReservoirMinimumPressure = block.ReadSingle(UnitOfPressure.Pascal, UnitOfPressure.PoundsPerSquareInch); + break; + case KujuTokenID.AirBrakesMainMaxAirPressure: + mainReservoirMaximumPressure = block.ReadSingle(UnitOfPressure.Pascal, UnitOfPressure.PoundsPerSquareInch); + break; + case KujuTokenID.BrakeCylinderPressureForMaxBrakeBrakeForce: + brakeCylinderMaximumPressure = block.ReadSingle(UnitOfPressure.Pascal, brakeSystemDefaultUnits); + break; + case KujuTokenID.TrainBrakesControllerEmergencyApplicationRate: + emergencyRate = block.ReadSingle(UnitOfPressure.Pascal, brakeSystemDefaultUnits); + break; + case KujuTokenID.AirBrakesAirCompressorPowerRating: + compressionRate = block.ReadSingle(UnitOfPressure.Pascal, UnitOfPressure.PoundsPerSquareInch); + if (compressionRate < 3500 || compressionRate > 34475) // assume valid range to be 0.5psi/s to 5psi/s + { + compressionRate = 3500; + } + break; + case KujuTokenID.TrainBrakesControllerMaxReleaseRate: + releaseRate = block.ReadSingle(UnitOfPressure.Pascal, brakeSystemDefaultUnits); + break; + case KujuTokenID.AntiSlip: + // if any value in this block, car has wheelslip detection control + hasAntiSlipDevice = true; + break; + case KujuTokenID.AWSMonitor: + case KujuTokenID.EmergencyStopMonitor: + case KujuTokenID.OverspeedMonitor: + case KujuTokenID.VigilanceMonitor: + // MSTS safety systems + VigilanceDevice device = VigilanceDevice.CreateVigilanceDevice(block.Token); + while (block.Position() < block.Length() - 2) + { + newBlock = block.ReadSubBlock(true); + device.ParseBlock(newBlock); + } + device.Create(car); + vigilanceDevices.Add(device); + break; + case KujuTokenID.FreightAnim: + if (Plugin.PreviewOnly) + { + break; + } + objectFile = OpenBveApi.Path.CombineFile(Path.GetDirectoryName(fileName), block.ReadString()); + + if (!File.Exists(objectFile)) + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "MSTS Vehicle Parser: FreightAnim object file " + objectFile + " was not found"); + break; + } + + /* + * + * https://tsforum.forumotion.net/t655-freight-animation + * FreightAnim are a total mess... + * Whilst it appears they were intended to work with all cars, the positioning logic only works correctly + * with tenders. + * + * TENDERS: + * -------- + * First value: Starting Y position (full) + * Second value: Ending Y position (empty) + * Third value: Must be positive or omitted for animation to work. If negative, animation stays at full. + * + * OTHER CARS: + * ---------- + * First value: Ignored + * Second value: Must be any positive number. + * + * However, this is actually done by directly replacing the Y translation component of Matrix[0] within the shape + * This means that if our shape actually has a value here, things can get really messy. + * + * The UKTS RCH wagon loads contain a Y value of approx 2.53, which when loaded in this way actually gets discarded + * + */ + + double loadPosition = 0; + double emptyPosition = 0; + try + { + loadPosition = block.ReadSingle(); + emptyPosition = block.ReadSingle(); + // may also be one more number, but this appears unused + } + catch + { + // ignore + } + + if (isEngine == false && currentWagonType != WagonType.Tender) + { + if (emptyPosition == 0) + { + break; + } + loadPosition = 0; + } + + for (int i = 0; i < Plugin.CurrentHost.Plugins.Length; i++) + { + if (Plugin.CurrentHost.Plugins[i].Object == null || !Plugin.CurrentHost.Plugins[i].Object.CanLoadObject(objectFile)) + { + continue; + } + Plugin.CurrentHost.Plugins[i].Object.LoadObject(objectFile, Path.GetDirectoryName(fileName), Encoding.Default, out UnifiedObject freightObject); + if (exteriorLoaded) + { + + CarSection exteriorCarSection = car.CarSections[CarSectionType.Exterior]; + exteriorCarSection.AppendObject(Plugin.CurrentHost, new Vector3(0, loadPosition, 0), car, freightObject); + car.CarSections[CarSectionType.Exterior] = exteriorCarSection; + } + else + { + car.CarSections.Add(CarSectionType.Exterior, new CarSection(Plugin.CurrentHost, ObjectType.Dynamic, false, car, freightObject)); + } + break; + } + break; + case KujuTokenID.Effects: + while (block.Position() < block.Length() - 2) + { + newBlock = block.ReadSubBlock(true); + ParseBlock(newBlock, fileName, ref wagonName, isEngine, ref car, ref train); + } + break; + case KujuTokenID.DieselSpecialEffects: + while (block.Position() < block.Length() - 2) + { + newBlock = block.ReadSubBlock(true); + ParseBlock(newBlock, fileName, ref wagonName, isEngine, ref car, ref train); + } + break; + case KujuTokenID.Exhaust1: + Exhaust.Offset = new Vector3(block.ReadSingle(), block.ReadSingle(), block.ReadSingle()); + Exhaust.Direction = new Vector3(block.ReadSingle(), block.ReadSingle(), block.ReadSingle()); + Exhaust.Size = block.ReadSingle(); + break; + case KujuTokenID.DieselSmokeEffectMaxMagnitude: + Exhaust.SmokeMaxMagnitude = block.ReadSingle(); + break; + case KujuTokenID.DieselSmokeEffectInitialSmokeRate: + Exhaust.SmokeInitialRate = block.ReadSingle(); + break; + case KujuTokenID.DieselSmokeEffectMaxSmokeRate: + Exhaust.SmokeMaxRate = block.ReadSingle(); + break; + case KujuTokenID.GearBoxNumberOfGears: + int numGears = block.ReadInt16(); + Gears = new Gear[numGears]; + break; + case KujuTokenID.GearBoxMaxSpeedForGears: + if (Gears == null) + { + Plugin.CurrentHost.AddMessage(MessageType.Error, false, "MSTS Vehicle Parser: Gears must be specified when using GearBoxMaxSpeedForGears."); + break; + } + + for (int i = 0; i < Gears.Length; i++) + { + Gears[i].MaximumSpeed = block.ReadSingle(UnitOfVelocity.MetersPerSecond, UnitOfVelocity.MilesPerHour); + } + break; + case KujuTokenID.GearBoxMaxTractiveForceForGears: + if (Gears == null) + { + Plugin.CurrentHost.AddMessage(MessageType.Error, false, "MSTS Vehicle Parser: Gears must be specified when using GearBoxMaxTractiveForceForGears."); + break; + } + + for (int i = 0; i < Gears.Length; i++) + { + Gears[i].MaxTractiveForce = block.ReadSingle(UnitOfForce.Newton); + } + break; + case KujuTokenID.GearBoxOverspeedPercentageForFailure: + if (Gears == null) + { + Plugin.CurrentHost.AddMessage(MessageType.Error, false, "MSTS Vehicle Parser: Gears must be specified when using GearBoxOVerspeedPercentageForFailure."); + break; + } + + double perc = block.ReadSingle() / 100; + for (int i = 0; i < Gears.Length; i++) + { + Gears[i].OverspeedFailure = Gears[i].MaximumSpeed * perc; + } + break; + case KujuTokenID.GearBoxOperation: + gearboxOperationMode = block.ReadEnumValue(default(GearboxOperation)); + break; + case KujuTokenID.Friction: + friction = new Friction(block); + break; + case KujuTokenID.Adheasion: + adhesion = new Adhesion(block, car, currentEngineType == EngineType.Steam); + break; + case KujuTokenID.Coupling: + while (block.Position() < block.Length() - 2) + { + newBlock = block.ReadSubBlock(true); + ParseBlock(newBlock, fileName, ref wagonName, isEngine, ref car, ref train); + } + break; + case KujuTokenID.Spring: + while (block.Position() < block.Length() - 2) + { + newBlock = block.ReadSubBlock(true); + ParseBlock(newBlock, fileName, ref wagonName, isEngine, ref car, ref train); + } + break; + case KujuTokenID.r0: + try + { + car.Coupler.MinimumDistanceBetweenCars = block.ReadSingle(UnitOfLength.Meter); + car.Coupler.MaximumDistanceBetweenCars = couplingType != CouplingType.Bar ? block.ReadSingle(UnitOfLength.Meter) : car.Coupler.MinimumDistanceBetweenCars; + } + catch + { + // ignored + } + + if (car.Coupler.MaximumDistanceBetweenCars > 2) + { + // some automatic / bar couplers seem to have absurd maximum distances + // so let's assume they're no good + car.Coupler.MaximumDistanceBetweenCars = car.Coupler.MinimumDistanceBetweenCars; + } + break; + case KujuTokenID.IsTenderRequired: + RequiresTender = block.ReadBool(); + break; + case KujuTokenID.EngineVariables: + switch (currentEngineType) + { + case EngineType.DieselHydraulic: + case EngineType.Diesel: + // Fuel capacity (L) + // NOTE: Max fuel is set by MaxDieselLevel + MaxFuelLevel = block.ReadSingle(UnitOfVolume.Litres); + break; + case EngineType.Steam: + // https://tsforum.forumotion.net/t120-msts-helpful-facts-and-links-part-14-enginevariables-for-steam-locomotives-by-slipperman12 + // Fire temp (deg c) + // Fire mass (lbs) + // Water mass in boiler (lbs) + // Boiler pressure (psi) + // Tender water mass (If switched to in ACT mode, so ignore) + // Tender coal mass (If switched to in ACT mode, so ignore) + // Smoke quantity multiplier + // Fire condition + // Coal quality + // NOTE: Max fuel / water levels are set by MaxTenderCoalMass and MaxTenderWaterMass + break; + } + break; + case KujuTokenID.MaxTenderCoalMass: + MaxFuelLevel = block.ReadSingle(UnitOfWeight.Kilograms, UnitOfWeight.Pounds); + break; + case KujuTokenID.MaxTenderWaterMass: + // at atmospheric pressure, 1kg of water = 1L + MaxWaterLevel = block.ReadSingle(UnitOfWeight.Kilograms, UnitOfWeight.Pounds); + break; + } + return true; + } + } +} diff --git a/source/Plugins/Train.MsTs/Vigilance/AWSMonitor.cs b/source/Plugins/Train.MsTs/Vigilance/AWSMonitor.cs new file mode 100644 index 0000000000..a8cd6dc6a5 --- /dev/null +++ b/source/Plugins/Train.MsTs/Vigilance/AWSMonitor.cs @@ -0,0 +1,17 @@ +namespace Train.MsTs +{ + internal class AWSMonitor : VigilanceDevice + { + /* + * NOT IMPLEMENTED + * + * NOTE: OpenRails does not implement the AWSMonitor + * + * Need to look further into whether it ever actually worked, + * or was implemented in any real-world content. + * + * No use of it in a relatively large UK content collection + * (some AWS displays appear to be simulated via CAB_SIGNAL_DISPLAY) + */ + } +} diff --git a/source/Plugins/Train.MsTs/Vigilance/AbstractVigilanceDevice.cs b/source/Plugins/Train.MsTs/Vigilance/AbstractVigilanceDevice.cs new file mode 100644 index 0000000000..c0231fea81 --- /dev/null +++ b/source/Plugins/Train.MsTs/Vigilance/AbstractVigilanceDevice.cs @@ -0,0 +1,182 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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.IO; +using OpenBve.Formats.MsTs; +using OpenBveApi.World; +using TrainManager.Car; + +namespace Train.MsTs +{ + internal abstract class VigilanceDevice + { + /// The time limit before this device alarms + /// Set to -1 for triggered devices + internal double TimeLimit; + /// The time limit before the device intervenes in alarm state + internal double AlarmTimeLimit; + /// The time limit before the device applies a penalty + internal double PenaltyTimeLimit; + /// The critical level of the trigger + internal double CriticalLevel; + /// The reset level of the trigger + internal double ResetLevel; + /// Whether full brake is applied on intervention + internal bool AppliesFullBrake; + /// Whether emergency brake is applied on intervention + internal bool AppliesEmergencyBrake; + /// Whether power is cut on invervention + internal bool CutsPower; + /// Whether the engine is shut down on intervention + internal bool ShutsDownEngine; + /// The reset conditions + internal ResetCondition ResetConditions; + + internal static VigilanceDevice CreateVigilanceDevice(KujuTokenID token) + { + switch (token) + { + case KujuTokenID.AWSMonitor: + return new AWSMonitor(); + case KujuTokenID.EmergencyStopMonitor: + return new EmergencyStopMonitor(); + case KujuTokenID.OverspeedMonitor: + return new OverspeedMonitor(); + case KujuTokenID.VigilanceMonitor: + return new VigilanceMonitor(); + default: + throw new InvalidDataException("Not a valid vigilance device"); + } + } + + internal virtual void Create(CarBase car) + { + + } + + internal void ParseBlock(Block block) + { + switch (block.Token) + { + case KujuTokenID.MonitoringDeviceMonitorTimeLimit: + TimeLimit = block.ReadSingle(); + break; + case KujuTokenID.MonitoringDeviceAlarmTimeLimit: + AlarmTimeLimit = block.ReadSingle(); + break; + case KujuTokenID.MonitoringDevicePenaltyTimeLimit: + PenaltyTimeLimit = block.ReadSingle(); + break; + case KujuTokenID.MonitoringDeviceCriticalLevel: + if (block.ParentBlock.Token == KujuTokenID.OverspeedMonitor) + { + // Check exact behaviour + CriticalLevel = block.ReadSingle(UnitOfVelocity.MetersPerSecond, UnitOfVelocity.MilesPerHour); + } + else + { + CriticalLevel = block.ReadSingle(); + } + break; + case KujuTokenID.MonitoringDeviceResetLevel: + if (block.ParentBlock.Token == KujuTokenID.OverspeedMonitor) + { + // Check exact behaviour + ResetLevel = block.ReadSingle(UnitOfVelocity.MetersPerSecond, UnitOfVelocity.MilesPerHour); + } + else + { + ResetLevel = block.ReadSingle(); + } + break; + case KujuTokenID.MonitoringDeviceAppliesFullBrake: + AppliesFullBrake = block.ReadInt32() == 1; + break; + case KujuTokenID.MonitoringDeviceAppliesEmergencyBrake: + AppliesEmergencyBrake = block.ReadInt32() == 1; + break; + case KujuTokenID.MonitoringDeviceAppliesCutsPower: + CutsPower = block.ReadInt32() == 1; + break; + case KujuTokenID.MonitoringDeviceAppliesShutsDownEngine: + ShutsDownEngine = block.ReadInt32() == 1; + break; + case KujuTokenID.MonitoringDeviceResetOnZeroSpeed: + if (block.ReadInt32() == 1) + { + ResetConditions |= ResetCondition.ZeroSpeed; + } + break; + case KujuTokenID.MonitoringDeviceResetOnZeroThrottle: + if (block.ReadInt32() == 1) + { + ResetConditions |= ResetCondition.ZeroThrottle; + } + break; + case KujuTokenID.MonitoringDeviceResetOnDirectionNeutral: + if (block.ReadInt32() == 1) + { + ResetConditions |= ResetCondition.DirectionNeutral; + } + break; + case KujuTokenID.MonitoringDeviceResetOnEngineAtIdle: + if (block.ReadInt32() == 1) + { + ResetConditions |= ResetCondition.EngineAtIdle; + } + break; + case KujuTokenID.MonitoringDeviceResetOnBrakeOff: + if (block.ReadInt32() == 1) + { + ResetConditions |= ResetCondition.BrakeOff; + } + break; + case KujuTokenID.MonitoringDeviceResetOnBrakeFullyOn: + if (block.ReadInt32() == 1) + { + ResetConditions |= ResetCondition.BrakeFullyOn; + } + break; + case KujuTokenID.MonitoringDeviceResetOnDynamicBrakeOff: + if (block.ReadInt32() == 1) + { + ResetConditions |= ResetCondition.DynamicBrakeOff; + } + break; + case KujuTokenID.MonitoringDeviceResetOnDynamicBrakeOn: + if (block.ReadInt32() == 1) + { + ResetConditions |= ResetCondition.DynamicBrakeOn; + } + break; + case KujuTokenID.MonitoringDeviceResetOnResetButton: + if (block.ReadInt32() == 1) + { + ResetConditions |= ResetCondition.ResetButton; + } + break; + } + } + } +} diff --git a/source/Plugins/Train.MsTs/Vigilance/EmergencyStopMonitor.cs b/source/Plugins/Train.MsTs/Vigilance/EmergencyStopMonitor.cs new file mode 100644 index 0000000000..81229e7dcc --- /dev/null +++ b/source/Plugins/Train.MsTs/Vigilance/EmergencyStopMonitor.cs @@ -0,0 +1,7 @@ +namespace Train.MsTs +{ + internal class EmergencyStopMonitor : VigilanceDevice + { + // NOT YET IMPLEMENTED + } +} diff --git a/source/Plugins/Train.MsTs/Vigilance/OverspeedMonitor.cs b/source/Plugins/Train.MsTs/Vigilance/OverspeedMonitor.cs new file mode 100644 index 0000000000..a26391d746 --- /dev/null +++ b/source/Plugins/Train.MsTs/Vigilance/OverspeedMonitor.cs @@ -0,0 +1,52 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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 TrainManager.Car; +using TrainManager.SafetySystems; + +namespace Train.MsTs +{ + internal class OverspeedMonitor : VigilanceDevice + { + internal override void Create(CarBase car) + { + SafetySystemType deviceType = SafetySystemType.None; + + if (CutsPower) + { + deviceType = SafetySystemType.CutsPower; + } + if (AppliesFullBrake) + { + deviceType = SafetySystemType.ApplyBrake; + } + if (AppliesEmergencyBrake) + { + deviceType = SafetySystemType.ApplyEmergencyBrake; + } + + car.SafetySystems.Add(SafetySystem.OverspeedDevice, new OverspeedDevice(car, deviceType, CriticalLevel, ResetLevel, TimeLimit, PenaltyTimeLimit, 0)); + } + } +} diff --git a/source/Plugins/Train.MsTs/Vigilance/ResetCondition.cs b/source/Plugins/Train.MsTs/Vigilance/ResetCondition.cs new file mode 100644 index 0000000000..578aa040cf --- /dev/null +++ b/source/Plugins/Train.MsTs/Vigilance/ResetCondition.cs @@ -0,0 +1,19 @@ +using System; + +namespace Train.MsTs +{ + [Flags] + public enum ResetCondition + { + None = 0, + ZeroSpeed = 1, + ZeroThrottle= 2, + DirectionNeutral = 4, + EngineAtIdle = 8, + BrakeOff = 16, + BrakeFullyOn = 32, + DynamicBrakeOff = 64, + DynamicBrakeOn = 128, + ResetButton = 256, + } +} diff --git a/source/Plugins/Train.MsTs/Vigilance/VigilanceMonitor.cs b/source/Plugins/Train.MsTs/Vigilance/VigilanceMonitor.cs new file mode 100644 index 0000000000..486facdd69 --- /dev/null +++ b/source/Plugins/Train.MsTs/Vigilance/VigilanceMonitor.cs @@ -0,0 +1,52 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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 TrainManager.Car; +using TrainManager.SafetySystems; + +namespace Train.MsTs +{ + internal class VigilanceMonitor : VigilanceDevice + { + internal override void Create(CarBase car) + { + SafetySystemType deviceType = SafetySystemType.None; + + if (CutsPower) + { + deviceType = SafetySystemType.CutsPower; + } + if (AppliesFullBrake) + { + deviceType = SafetySystemType.ApplyBrake; + } + if (AppliesEmergencyBrake) + { + deviceType = SafetySystemType.ApplyEmergencyBrake; + } + + car.SafetySystems.Add(SafetySystem.DriverSupervisionDevice, new DriverSupervisionDevice(car, deviceType, DriverSupervisionDeviceMode.AnyHandle, SafetySystemTriggerMode.TrainMoving, AlarmTimeLimit, TimeLimit, PenaltyTimeLimit)); + } + } +} diff --git a/source/Plugins/Train.MsTs/app.config b/source/Plugins/Train.MsTs/app.config new file mode 100644 index 0000000000..a1e2bff25d --- /dev/null +++ b/source/Plugins/Train.MsTs/app.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/Plugins/Train.MsTs/packages.config b/source/Plugins/Train.MsTs/packages.config new file mode 100644 index 0000000000..5d2acb5f31 --- /dev/null +++ b/source/Plugins/Train.MsTs/packages.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/source/SoundManager/SoundManager.csproj b/source/SoundManager/SoundManager.csproj index 86e28e3270..857bdfaad0 100644 --- a/source/SoundManager/SoundManager.csproj +++ b/source/SoundManager/SoundManager.csproj @@ -47,6 +47,7 @@ prompt + ..\..\packages\OpenTK-OpenBVE.1.0.4\lib\net20\OpenTK.dll @@ -78,5 +79,6 @@ + \ No newline at end of file diff --git a/source/SoundManager/Sounds.CarSound.cs b/source/SoundManager/Sounds.CarSound.cs index c28c2c2bc9..f6f9e63b51 100644 --- a/source/SoundManager/Sounds.CarSound.cs +++ b/source/SoundManager/Sounds.CarSound.cs @@ -3,8 +3,9 @@ using OpenBveApi.Hosts; using OpenBveApi.Interface; using OpenBveApi.Math; -using OpenBveApi.Sounds; +using OpenBveApi.Runtime; using OpenBveApi.Trains; +using SoundHandle = OpenBveApi.Sounds.SoundHandle; namespace SoundManager { @@ -21,6 +22,8 @@ public class CarSound /// Used when crossfading between multiple sounds of the same type public double TargetVolume; + public CameraViewMode ViewModes; + public CarSound(HostInterface currentHost, string trainFolder, string soundFile, double radius, Vector3 position) : this(currentHost, trainFolder, string.Empty, -1, soundFile, radius, position) { } diff --git a/source/TrainManager/Brake/AirBrake/ThroughPiped.cs b/source/TrainManager/Brake/AirBrake/ThroughPiped.cs index e728eae1b7..ecbf67d7ad 100644 --- a/source/TrainManager/Brake/AirBrake/ThroughPiped.cs +++ b/source/TrainManager/Brake/AirBrake/ThroughPiped.cs @@ -33,7 +33,6 @@ public class ThroughPiped : CarBrake { public ThroughPiped(CarBase car) : base(car, new AccelerationCurve[] {}) { - DecelerationCurves = new AccelerationCurve[] { }; BrakeType = BrakeType.None; BrakePipe = new BrakePipe(0); } diff --git a/source/TrainManager/Brake/CarBrake.cs b/source/TrainManager/Brake/CarBrake.cs index 78d2ba7056..78409ebbd5 100644 --- a/source/TrainManager/Brake/CarBrake.cs +++ b/source/TrainManager/Brake/CarBrake.cs @@ -101,6 +101,11 @@ public double DecelerationAtServiceMaximumPressure(int Notch, double currentSpee { return 0; } + + if (DecelerationCurves[0] is MSTSDecelerationCurve dec) + { + return dec.MaximumAcceleration; + } if (Notch == 0) { return this.DecelerationCurves[0].GetAccelerationOutput(currentSpeed); diff --git a/source/TrainManager/Car/CarSounds.cs b/source/TrainManager/Car/CarSounds.cs index a36b226a59..e14b1464f5 100644 --- a/source/TrainManager/Car/CarSounds.cs +++ b/source/TrainManager/Car/CarSounds.cs @@ -1,5 +1,7 @@ -using System.Collections.Generic; +using System.Collections.Generic; using SoundManager; +using TrainManager.Motor; +using TrainManager.MsTsSounds; namespace TrainManager.Car { @@ -8,6 +10,8 @@ public class CarSounds { /// The loop sound public CarSound Loop; + + public List ControlledSounds = new List(); /// The sounds triggered by the train's plugin public Dictionary Plugin = new Dictionary(); /// The sounds triggered by a request stop diff --git a/source/TrainManager/Motor/DieselEngine/Gearbox.cs b/source/TrainManager/Motor/DieselEngine/Gearbox.cs index 85059c54b6..6fcb3eeb3a 100644 --- a/source/TrainManager/Motor/DieselEngine/Gearbox.cs +++ b/source/TrainManager/Motor/DieselEngine/Gearbox.cs @@ -11,9 +11,9 @@ public class Gearbox : AbstractComponent /// The current gear public int CurrentGear; /// The sound played when the gear is increased - internal CarSound GearUpSound; + public CarSound GearUpSound; /// The sound played when the gear is decreased - internal CarSound GearDownSound; + public CarSound GearDownSound; /// The sound played when the gearbox returns to neutral internal CarSound NeutralSound; /// The maximum speed attainable in the current gear diff --git a/source/TrainManager/Motor/ElectricEngine/ElectricEngine.cs b/source/TrainManager/Motor/ElectricEngine/ElectricEngine.cs index 0353b04617..48805a9e55 100644 --- a/source/TrainManager/Motor/ElectricEngine/ElectricEngine.cs +++ b/source/TrainManager/Motor/ElectricEngine/ElectricEngine.cs @@ -1,4 +1,4 @@ -//Simplified BSD License (BSD-2-Clause) +//Simplified BSD License (BSD-2-Clause) // //Copyright (c) 2025, Christopher Lees, The OpenBVE Project // @@ -32,10 +32,6 @@ namespace TrainManager.Motor { public class ElectricEngine : TractionModel { - public ElectricEngine(CarBase car) : base(car) - { - } - public ElectricEngine(CarBase car, AccelerationCurve[] accelerationCurves) : base(car, accelerationCurves, true) { } diff --git a/source/TrainManager/Motor/Fuel/FuelTank.cs b/source/TrainManager/Motor/Fuel/FuelTank.cs index 8f08364928..0c4d6e7dce 100644 --- a/source/TrainManager/Motor/Fuel/FuelTank.cs +++ b/source/TrainManager/Motor/Fuel/FuelTank.cs @@ -25,5 +25,12 @@ public FuelTank(double maxLevel, double minLevel, double currentLevel) MinLevel = minLevel; CurrentLevel = currentLevel; } + + public FuelTank(double currentLevel) + { + MaxLevel = currentLevel; + MinLevel = 0; + CurrentLevel = currentLevel; + } } } diff --git a/source/TrainManager/Motor/SteamEngine/TankEngine.cs b/source/TrainManager/Motor/SteamEngine/TankEngine.cs new file mode 100644 index 0000000000..a2d1275c04 --- /dev/null +++ b/source/TrainManager/Motor/SteamEngine/TankEngine.cs @@ -0,0 +1,61 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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 TrainManager.Car; +using TrainManager.Power; + +namespace TrainManager.Motor +{ + public class TankEngine : TractionModel + { + /// The maximum water level + public readonly double TankMaxWaterLevel; + /// The current water level + public double TankWaterLevel; + + public TankEngine(CarBase baseCar, AccelerationCurve[] accelerationCurves, double maxFuelLevel, double maxWaterLevel) : base(baseCar, accelerationCurves, true) + { + FuelTank = new FuelTank(maxFuelLevel, 0, maxFuelLevel); + TankMaxWaterLevel = maxWaterLevel; + TankWaterLevel = maxWaterLevel; + } + + public TankEngine(CarBase baseCar, AccelerationCurve[] accelerationCurves, double maxFuelLevel, double fuelLevel, double maxWaterLevel, double waterLevel) : base(baseCar, accelerationCurves, true) + { + FuelTank = new FuelTank(maxFuelLevel, 0, fuelLevel); + TankMaxWaterLevel = maxWaterLevel; + TankWaterLevel = waterLevel; + } + + public override void Update(double timeElapsed) + { + } + + // TODO: PLACEHOLDER VALUES + + public override double CurrentPower => 1.0; + + public override double TargetAcceleration => AccelerationCurves[0].GetAccelerationOutput(BaseCar.CurrentSpeed); + } +} diff --git a/source/TrainManager/Motor/SteamEngine/Tender.cs b/source/TrainManager/Motor/SteamEngine/Tender.cs new file mode 100644 index 0000000000..be2f1a37f1 --- /dev/null +++ b/source/TrainManager/Motor/SteamEngine/Tender.cs @@ -0,0 +1,55 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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 TrainManager.Car; + +namespace TrainManager.Motor +{ + public class Tender : TractionModel + { + /// The maximum water level + public readonly double MaxWaterLevel; + /// The current water level + public double WaterLevel; + + public Tender(CarBase car, double maxFuelLevel, double maxWaterLevel) : base(car, null, false) + { + FuelTank = new FuelTank(maxFuelLevel, 0, maxFuelLevel); + MaxWaterLevel = maxWaterLevel; + WaterLevel = maxWaterLevel; + } + + public Tender(CarBase car, double maxFuelLevel, double fuelLevel, double maxWaterLevel, double waterLevel) : base(car, null, false) + { + FuelTank = new FuelTank(maxFuelLevel, 0, fuelLevel); + MaxWaterLevel = maxWaterLevel; + WaterLevel = waterLevel; + } + + public override void Update(double timeElapsed) + { + + } + } +} diff --git a/source/TrainManager/Motor/SteamEngine/TenderEngine.cs b/source/TrainManager/Motor/SteamEngine/TenderEngine.cs new file mode 100644 index 0000000000..ee158b85ae --- /dev/null +++ b/source/TrainManager/Motor/SteamEngine/TenderEngine.cs @@ -0,0 +1,49 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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 TrainManager.Car; +using TrainManager.Power; + +namespace TrainManager.Motor +{ + public class TenderEngine : TractionModel + { + /// Holds a reference to the connected tender, or null if no tender connected + public Tender Tender; + + public TenderEngine(CarBase baseCar, AccelerationCurve[] accelerationCurves) : base(baseCar, accelerationCurves, true) + { + } + + public override void Update(double timeElapsed) + { + } + + // TODO: PLACEHOLDER VALUES + + public override double CurrentPower => 1.0; + + public override double TargetAcceleration => AccelerationCurves[0].GetAccelerationOutput(BaseCar.CurrentSpeed); + } +} diff --git a/source/TrainManager/Power/AccelerationCurve.cs b/source/TrainManager/Power/AccelerationCurve.cs index 3545fa5f9f..838fd02ac6 100644 --- a/source/TrainManager/Power/AccelerationCurve.cs +++ b/source/TrainManager/Power/AccelerationCurve.cs @@ -5,7 +5,7 @@ public abstract class AccelerationCurve { /// Gets the acceleration output for this curve /// The current speed - /// The acceleration output + /// The acceleration output in km/h/s public abstract double GetAccelerationOutput(double Speed); /// Gets the maximum possible acceleration output for this curve diff --git a/source/TrainManager/Power/MSTS/MSTSAccelerationCurve.cs b/source/TrainManager/Power/MSTS/MSTSAccelerationCurve.cs new file mode 100644 index 0000000000..4f776bf61e --- /dev/null +++ b/source/TrainManager/Power/MSTS/MSTSAccelerationCurve.cs @@ -0,0 +1,184 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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. + +// ReSharper disable InconsistentNaming + +using TrainManager.Car; +using TrainManager.Handles; +using TrainManager.Motor; +using TrainManager.Trains; + +namespace TrainManager.Power +{ + /// Represents a MSTS acceleration curve + public class MSTSAccelerationCurve : AccelerationCurve + { + /// Holds a reference to the car + /// Can't store the train reference directly as we may couple to another + private readonly CarBase baseCar; + /// The maximum force supplied by the engine + private readonly double MaxForce; + /// The maximum velocity attainable + private readonly double MaxVelocity; + /// The maximum continuous force supplied by the engine + /// Used by electric model + private readonly double MaxContinuousForce; + + public MSTSAccelerationCurve(CarBase car, double maxForce, double maxContinuousForce, double maxVelocity) + { + baseCar = car; + MaxForce = maxForce; + MaxContinuousForce = maxContinuousForce; + MaxVelocity = maxVelocity; + } + + public override double GetAccelerationOutput(double Speed) + { + if (baseCar.Specs.PerceivedSpeed > MaxVelocity) + { + return 0; + } + + /* + * According to Newton's second law, Acceleration = Force / Mass + * For the minute at least, we'll assume that the Force value specified in an + * ENG file is proportionate across power notches, and remains constant throughout + * the speed range. In practice, MSTS will actually have internally simulated this + * via engine RPM, boiler pressure etc. etc. but at present we obviously don't + * have these available. + * + * We don't in this case need to take the speed or loading values into account, but + * retain them as legacy + */ + double totalMass = 0; + + TrainBase baseTrain = baseCar.baseTrain; + for (int i = 0; i < baseTrain.Cars.Length; i++) + { + totalMass += baseTrain.Cars[i].CurrentMass; + } + + if (baseTrain.Handles.EmergencyBrake.Actual) + { + return totalMass / MaxForce / 3.6; + } + + if (baseTrain.Handles.Brake.Actual > 0) + { + return ((baseTrain.Handles.Brake.Actual / (double)baseTrain.Handles.Brake.MaximumNotch) * (totalMass / MaxForce)); + } + + if (baseCar.TractionModel is DieselEngine dieselEngine) + { + // diesel engine uses simulated power level of MaxForce + return dieselEngine.CurrentPower * (totalMass / MaxForce); + } + + if (baseCar.TractionModel is ElectricEngine electricEngine) + { + if (baseCar.Specs.PerceivedSpeed < 3.61111 || baseCar.Specs.PerceivedSpeed > 7.22222) + { + // for electrics, MaxContinuousForce is used between 13km/h and 26km/h as per original Kuju physics model + return electricEngine.CurrentPower * (totalMass / MaxContinuousForce); + } + else + { + return electricEngine.CurrentPower * (totalMass / MaxForce); + } + } + + return ((baseTrain.Handles.Power.Actual / (double)baseTrain.Handles.Power.MaximumNotch) * (totalMass / MaxForce)); + } + + public override double MaximumAcceleration + { + get + { + double totalMass = 0; + TrainBase baseTrain = baseCar.baseTrain; + for (int i = 0; i < baseTrain.Cars.Length; i++) + { + totalMass += baseTrain.Cars[i].CurrentMass; + } + + return totalMass / MaxForce; + } + } + + } + + public class MSTSDecelerationCurve : AccelerationCurve + { + private readonly TrainBase Train; + /// The maximum force supplied by the engine + private readonly double MaxForce; + + public MSTSDecelerationCurve(TrainBase train, double maxForce) + { + Train = train; + MaxForce = maxForce; + } + public override double GetAccelerationOutput(double Speed) + { + /* + * According to Newton's second law, Acceleration = Force / Mass + * For the minute at least, we'll assume that the Force value specified in an + * ENG file is proportionate across power notches, and remains constant throughout + * the speed range. In practice, MSTS will actually have internally simulated this + * via engine RPM, boiler pressure etc. etc. but at present we obviously don't + * have these available. + * + * We don't in this case need to take the speed or loading values into account, but + * retain them as legacy + * REFACTOR: Store the train reference in BVE acceleration curves??? + */ + double totalMass = 0; + for (int i = 0; i < Train.Cars.Length; i++) + { + totalMass += Train.Cars[i].CurrentMass; + } + + double maxBrakeForce = totalMass / MaxForce / 3.6; + if (Train.Handles.Brake is VariableHandle variableHandle) + { + maxBrakeForce *= variableHandle.GetPowerModifier; + } + return maxBrakeForce; + } + + public override double MaximumAcceleration + { + get + { + double totalMass = 0; + for (int i = 0; i < Train.Cars.Length; i++) + { + totalMass += Train.Cars[i].CurrentMass; + } + + return totalMass / MaxForce / 3.6; + } + } + } +} diff --git a/source/TrainManager/Sounds/MSTS/FrequencyCurve.cs b/source/TrainManager/Sounds/MSTS/FrequencyCurve.cs new file mode 100644 index 0000000000..eeca176bfc --- /dev/null +++ b/source/TrainManager/Sounds/MSTS/FrequencyCurve.cs @@ -0,0 +1,79 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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 OpenBve.Formats.MsTs; +using TrainManager.Car; + +namespace TrainManager.MsTsSounds +{ + public class MsTsFrequencyCurve + { + private readonly CarBase car; + private readonly Tuple[] frequencyPoints; + + private readonly KujuTokenID controller; + + public MsTsFrequencyCurve(CarBase car, KujuTokenID controller, Tuple[] points) + { + this.car = car; + this.controller = controller; + frequencyPoints = new Tuple[points.Length]; + for (int i = 0; i < frequencyPoints.Length; i++) + { + frequencyPoints[i] = new Tuple(points[i].Item1, points[i].Item2 / 11025); // MSTS base sound frequency is 11025hz, convert to percentage + } + } + + public double Pitch + { + get + { + switch (controller) + { + case KujuTokenID.SpeedControlled: + for (int i = frequencyPoints.Length - 1; i >= 0; i--) + { + if (Math.Abs(car.CurrentSpeed) >= frequencyPoints[i].Item1) + { + return frequencyPoints[i].Item2; + } + } + break; + case KujuTokenID.Variable2Controlled: + for (int i = frequencyPoints.Length - 1; i >= 0; i--) + { + if (car.TractionModel.CurrentPower >= frequencyPoints[i].Item1) + { + return frequencyPoints[i].Item2; + } + } + break; + + } + return 0; + } + } + } +} diff --git a/source/TrainManager/Sounds/MSTS/SoundStream.cs b/source/TrainManager/Sounds/MSTS/SoundStream.cs new file mode 100644 index 0000000000..99d5640f4a --- /dev/null +++ b/source/TrainManager/Sounds/MSTS/SoundStream.cs @@ -0,0 +1,130 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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 OpenBveApi.Runtime; +using System.Collections.Generic; +using OpenBveApi.Math; +using SoundManager; +using TrainManager.Car; + +namespace TrainManager.MsTsSounds +{ + public class SoundStream + { + /// The list of sound triggers within the sound stream + public List Triggers; + /// The volume curve for the sound stream + public MsTsVolumeCurve VolumeCurve; + /// The frequency curve for the sound stream + public MsTsFrequencyCurve FrequencyCurve; + /// The camera modes in which this sound stream is active + public readonly CameraViewMode ActivationCameraModes; + /// The modes in which this sound stream is not active + public readonly CameraViewMode DeactivationCameraModes; + + private SoundSource soundSource; + + private CarBase car; + + public SoundStream(CarBase baseCar, CameraViewMode activationCameraModes, CameraViewMode deactivationCameraModes) + { + Triggers = new List(); + ActivationCameraModes = activationCameraModes; + DeactivationCameraModes = deactivationCameraModes; + car = baseCar; + } + + public void Update(double timeElapsed) + { + double volume = 1.0, pitch = 1.0; + if (VolumeCurve != null) + { + volume = VolumeCurve.Volume; + } + + if (FrequencyCurve != null) + { + pitch = FrequencyCurve.Pitch; + } + + bool canActivate = true; + if (ActivationCameraModes != CameraViewMode.NotDefined) + { + canActivate = (ActivationCameraModes & TrainManagerBase.Renderer.Camera.CurrentMode) != 0; + } + + if (DeactivationCameraModes != CameraViewMode.NotDefined) + { + if (canActivate) + { + canActivate = (DeactivationCameraModes & TrainManagerBase.Renderer.Camera.CurrentMode) == 0; + } + } + + /* + * To get the playing buffer and loop state, we itinerate through all sound triggers in a stream + * in order. If the conditions for a trigger are met, the buffer ref and loop status are updated + * + * At the end of our loop, if the buffer is not null, either adjust the pitch / gain (if currently + * playing) or replace the playing buffer with it + * Otherwise, stop the playing buffer. + */ + SoundBuffer soundBuffer = null; + if (soundSource != null) + { + soundBuffer = soundSource.Buffer; + } + bool loops = false; + + for (int i = 0; i < Triggers.Count; i++) + { + if (canActivate) + { + Triggers[i].Update(timeElapsed, car, ref soundBuffer, ref loops); + } + } + + if (soundBuffer != null) + { + if (soundSource != null && soundSource.Buffer == soundBuffer) + { + soundSource.Volume = volume; + soundSource.Pitch = pitch; + } + else + { + soundSource?.Stop(); + soundSource = (SoundSource)TrainManagerBase.currentHost.PlaySound(soundBuffer, pitch, volume, Vector3.Zero, car, loops); + } + } + else + { + if (soundSource != null && soundSource.IsPlaying()) + { + soundSource.Stop(); + } + } + } + } +} diff --git a/source/TrainManager/Sounds/MSTS/SoundTrigger.Initial.cs b/source/TrainManager/Sounds/MSTS/SoundTrigger.Initial.cs new file mode 100644 index 0000000000..bddc012cfc --- /dev/null +++ b/source/TrainManager/Sounds/MSTS/SoundTrigger.Initial.cs @@ -0,0 +1,53 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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 OpenBve.Formats.MsTs; +using SoundManager; +using TrainManager.Car; + +namespace TrainManager.MsTsSounds +{ + /// A trigger which is activated when the car is introduced + public class InitialTrigger: SoundTrigger + { + public InitialTrigger(SoundBuffer buffer, bool soundLoops) : base(buffer, soundLoops) + { + } + + public InitialTrigger(SoundBuffer[] buffers, KujuTokenID selectionMethod, bool soundLoops) : base(buffers, selectionMethod, soundLoops) + { + } + + + public override void Update(double timeElapsed, CarBase car, ref SoundBuffer soundBuffer, ref bool soundLoops) + { + if (Triggered == false) + { + soundBuffer = Buffer; + soundLoops = SoundLoops; + Triggered = true; + } + } + } +} diff --git a/source/TrainManager/Sounds/MSTS/SoundTrigger.Speed.cs b/source/TrainManager/Sounds/MSTS/SoundTrigger.Speed.cs new file mode 100644 index 0000000000..28b60addc4 --- /dev/null +++ b/source/TrainManager/Sounds/MSTS/SoundTrigger.Speed.cs @@ -0,0 +1,96 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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 OpenBve.Formats.MsTs; +using SoundManager; +using TrainManager.Car; + +namespace TrainManager.MsTsSounds +{ + /// A sound trigger that activates when the train's speed increases past the set value + public class SpeedIncPast : SoundTrigger + { + private readonly double speedValue; + + + public SpeedIncPast(SoundBuffer[] buffers, KujuTokenID selectionMethod, double speedValue, bool soundLoops) : base(buffers, selectionMethod, soundLoops) + { + this.speedValue = speedValue; + } + + public SpeedIncPast(CarBase car, SoundBuffer buffer, double speedValue, bool soundLoops) : base(buffer, soundLoops) + { + this.speedValue = speedValue; + } + + public override void Update(double timeElapsed, CarBase car, ref SoundBuffer soundBuffer, ref bool soundLoops) + { + double speed = Math.Abs(car.CurrentSpeed); + if (speed >= speedValue) + { + soundBuffer = Buffer; + soundLoops = SoundLoops; + Triggered = true; + } + + if (speed < speedValue) + { + Triggered = false; + } + } + } + + /// A sound trigger that activates when the train's speed decreases past the set value + public class SpeedDecPast : SoundTrigger + { + private readonly double speedValue; + + public SpeedDecPast(SoundBuffer[] buffers, KujuTokenID selectionMethod, double speedValue, bool soundLoops) : base(buffers, selectionMethod, soundLoops) + { + this.speedValue = speedValue; + } + + public SpeedDecPast(SoundBuffer buffer, double speedValue, bool soundLoops) : base(buffer, soundLoops) + { + this.speedValue = speedValue; + } + + public override void Update(double timeElapsed, CarBase car, ref SoundBuffer soundBuffer, ref bool soundLoops) + { + double speed = Math.Abs(car.CurrentSpeed); + if (speed <= speedValue && Triggered == false) + { + soundBuffer = Buffer; + soundLoops = SoundLoops; + Triggered = true; + } + + if (speed > speedValue) + { + Triggered = false; + } + } + } +} diff --git a/source/TrainManager/Sounds/MSTS/SoundTrigger.VariableControlled.cs b/source/TrainManager/Sounds/MSTS/SoundTrigger.VariableControlled.cs new file mode 100644 index 0000000000..c90072a984 --- /dev/null +++ b/source/TrainManager/Sounds/MSTS/SoundTrigger.VariableControlled.cs @@ -0,0 +1,169 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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 OpenBve.Formats.MsTs; +using SoundManager; +using TrainManager.Car; + +namespace TrainManager.MsTsSounds +{ + /// A sound trigger controlled by Variable1 increasing past the setpoint + /// Variable1 represents the current wheel rotation speed + /// Diesel- EngineRPM + /// Electric- TractiveForce + /// Steam- CylinderPressure + public class Variable1IncPast : SoundTrigger + { + private readonly double variableValue; + + public Variable1IncPast(SoundBuffer[] buffers, KujuTokenID selectionMethod, double variableValue, bool soundLoops) : base(buffers, selectionMethod, soundLoops) + { + this.variableValue = variableValue; + } + + public Variable1IncPast(SoundBuffer buffer, double variableValue, bool soundLoops) : base(buffer, soundLoops) + { + this.variableValue = variableValue; + } + + public override void Update(double timeElapsed, CarBase car, ref SoundBuffer soundBuffer, ref bool soundLoops) + { + if (car.baseTrain.Handles.Power.Actual > 0 && car.CurrentSpeed / 1000 / car.DrivingWheels[0].Radius / System.Math.PI * 5 > variableValue) + { + soundBuffer = Buffer; + soundLoops = SoundLoops; + Triggered = true; + } + + if (car.baseTrain.Handles.Power.Actual == 0 || car.TractionModel.CurrentPower < variableValue) + { + Triggered = false; + } + } + } + + /// A sound trigger controlled by Variable1 increasing past the setpoint + /// Variable1 represents the current wheel rotation speed + /// Diesel- EngineRPM + /// Electric- TractiveForce + /// Steam- CylinderPressure + public class Variable1DecPast : SoundTrigger + { + private readonly double variableValue; + + public Variable1DecPast(SoundBuffer[] buffers, KujuTokenID selectionMethod, double variableValue, bool soundLoops) : base(buffers, selectionMethod, soundLoops) + { + this.variableValue = variableValue; + } + + public Variable1DecPast(SoundBuffer buffer, double variableValue, bool soundLoops) : base(buffer, soundLoops) + { + this.variableValue = variableValue; + } + + public override void Update(double timeElapsed, CarBase car, ref SoundBuffer soundBuffer, ref bool soundLoops) + { + if (car.baseTrain.Handles.Power.Actual > 0 && car.CurrentSpeed / 1000 / car.DrivingWheels[0].Radius / System.Math.PI * 5 < variableValue) + { + soundBuffer = Buffer; + soundLoops = SoundLoops; + Triggered = true; + } + + if (car.baseTrain.Handles.Power.Actual == 0 || car.TractionModel.CurrentPower > variableValue) + { + Triggered = false; + } + } + } + + /// A sound trigger controlled by Variable2 increasing past the setpoint + /// Variable2 represents the proportion of power the car's TractionModel is currently generating + /// Diesel- EngineRPM + /// Electric- TractiveForce + /// Steam- CylinderPressure + public class Variable2IncPast : SoundTrigger + { + private readonly double variableValue; + + public Variable2IncPast(SoundBuffer[] buffers, KujuTokenID selectionMethod, double variableValue, bool soundLoops) : base(buffers, selectionMethod, soundLoops) + { + this.variableValue = variableValue; + } + + public Variable2IncPast(SoundBuffer buffer, double variableValue, bool soundLoops) : base(buffer, soundLoops) + { + this.variableValue = variableValue; + } + + public override void Update(double timeElapsed, CarBase car, ref SoundBuffer soundBuffer, ref bool soundLoops) + { + if (car.TractionModel.CurrentPower >= variableValue && Triggered == false) + { + soundBuffer = Buffer; + soundLoops = SoundLoops; + Triggered = true; + } + + if (car.TractionModel.CurrentPower < variableValue) + { + Triggered = false; + } + } + } + + /// A sound trigger controlled by Variable2 decreasing past the setpoint + /// Variable2 represents the proportion of power the car's TractionModel is currently generating + /// Diesel- EngineRPM + /// Electric- TractiveForce + /// Steam- CylinderPressure + public class Variable2DecPast : SoundTrigger + { + private readonly double variableValue; + + public Variable2DecPast(SoundBuffer[] buffers, KujuTokenID selectionMethod, double variableValue, bool soundLoops) : base(buffers, selectionMethod, soundLoops) + { + this.variableValue = variableValue; + } + public Variable2DecPast(SoundBuffer buffer, double variableValue, bool soundLoops) : base(buffer, soundLoops) + { + this.variableValue = variableValue; + } + + public override void Update(double timeElapsed, CarBase car, ref SoundBuffer soundBuffer, ref bool soundLoops) + { + if (car.TractionModel.CurrentPower <= variableValue && Triggered == false) + { + soundBuffer = Buffer; + soundLoops = SoundLoops; + Triggered = true; + } + + if (car.TractionModel.CurrentPower > variableValue) + { + Triggered = false; + } + } + } +} diff --git a/source/TrainManager/Sounds/MSTS/SoundTrigger.cs b/source/TrainManager/Sounds/MSTS/SoundTrigger.cs new file mode 100644 index 0000000000..df15f82caf --- /dev/null +++ b/source/TrainManager/Sounds/MSTS/SoundTrigger.cs @@ -0,0 +1,85 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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 OpenBve.Formats.MsTs; +using SoundManager; +using TrainManager.Car; + +namespace TrainManager.MsTsSounds +{ + public abstract class SoundTrigger + { + /// Holds the list of possible sound buffers to be selected from + internal readonly SoundBuffer[] Buffers; + /// The selection method used to determine the buffer to play + private readonly KujuTokenID bufferSelectionMethod; + /// Whether the sound loops + internal readonly bool SoundLoops; + /// Timer used in derived updates + internal double Timer; + /// The previously selected buffer index + internal int BufferIndex; + /// Stores whether the trigger is currently active + internal bool Triggered; + /// Gets the actual sound buffer to be played + internal SoundBuffer Buffer + { + get + { + switch (bufferSelectionMethod) + { + case KujuTokenID.SequentialSelection: + BufferIndex++; + if (BufferIndex > Buffers.Length - 1) + { + BufferIndex = 0; + } + break; + case KujuTokenID.RandomSelection: + BufferIndex = TrainManagerBase.RandomNumberGenerator.Next(0, Buffers.Length - 1); + break; + } + return Buffers[BufferIndex]; + } + } + + internal SoundTrigger(SoundBuffer buffer, bool soundLoops) + { + Buffers = new[] { buffer }; + SoundLoops = soundLoops; + } + + internal SoundTrigger(SoundBuffer[] buffers, KujuTokenID selectionMethod, bool soundLoops) + { + Buffers = buffers; + bufferSelectionMethod = selectionMethod; + SoundLoops = soundLoops; + } + + public virtual void Update(double timeElapsed, CarBase car, ref SoundBuffer soundBuffer, ref bool soundLoops) + { + + } + } +} diff --git a/source/TrainManager/Sounds/MSTS/VolumeCurve.cs b/source/TrainManager/Sounds/MSTS/VolumeCurve.cs new file mode 100644 index 0000000000..ca881a5a1e --- /dev/null +++ b/source/TrainManager/Sounds/MSTS/VolumeCurve.cs @@ -0,0 +1,74 @@ +//Simplified BSD License (BSD-2-Clause) +// +//Copyright (c) 2025, Christopher Lees, The OpenBVE Project +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//1. Redistributions of source code must retain the above copyright notice, 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 materials provided with the distribution. +// +//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 OpenBve.Formats.MsTs; +using System; +using TrainManager.Car; + +namespace TrainManager.MsTsSounds +{ + public class MsTsVolumeCurve + { + private readonly CarBase car; + private readonly Tuple[] volumePoints; + + private readonly KujuTokenID controller; + + public MsTsVolumeCurve(CarBase car, KujuTokenID controller, Tuple[] points) + { + this.car = car; + this.controller = controller; + volumePoints = points; + } + + public double Volume + { + get + { + switch (controller) + { + case KujuTokenID.SpeedControlled: + for (int i = volumePoints.Length - 1; i >= 0; i--) + { + if (Math.Abs(car.CurrentSpeed) >= volumePoints[i].Item1) + { + return volumePoints[i].Item2; + } + } + break; + case KujuTokenID.Variable2Controlled: + for (int i = volumePoints.Length - 1; i >= 0; i--) + { + if (car.TractionModel.CurrentPower >= volumePoints[i].Item1) + { + return volumePoints[i].Item2; + } + } + break; + } + return 0; + } + } + } +} diff --git a/source/TrainManager/Train/Station.cs b/source/TrainManager/Train/Station.cs index 3cad38dcba..625141ce68 100644 --- a/source/TrainManager/Train/Station.cs +++ b/source/TrainManager/Train/Station.cs @@ -1,4 +1,4 @@ -using System; +using System; using OpenBveApi; using OpenBveApi.Colors; using OpenBveApi.Hosts; diff --git a/source/TrainManager/Train/TrainBase.cs b/source/TrainManager/Train/TrainBase.cs index 6e6a99bdff..296a58236d 100644 --- a/source/TrainManager/Train/TrainBase.cs +++ b/source/TrainManager/Train/TrainBase.cs @@ -523,6 +523,10 @@ private void UpdatePhysicsAndControls(double timeElapsed) for (int i = 0; i < Cars.Length; i++) { Cars[i].Run.Update(timeElapsed); + for (int j = 0; j < Cars[i].Sounds.ControlledSounds.Count; j++) + { + Cars[i].Sounds.ControlledSounds[j].Update(timeElapsed); + } } // safety system @@ -601,7 +605,7 @@ private void UpdateSpeeds(double timeElapsed) CenterOfCarPositions[i] = 0.5 * (pr + pf); CenterOfMassPosition += CenterOfCarPositions[i] * Cars[i].CurrentMass; TrainMass += Cars[i].CurrentMass; - // update engine + // update engine etc. if (Cars[i].TractionModel.ProvidesPower && Cars[i].TractionModel != null) { Cars[i].TractionModel.Update(timeElapsed); diff --git a/source/TrainManager/TrainManager.csproj b/source/TrainManager/TrainManager.csproj index 256b2791de..d23302adb6 100644 --- a/source/TrainManager/TrainManager.csproj +++ b/source/TrainManager/TrainManager.csproj @@ -136,9 +136,13 @@ + + + + @@ -168,6 +172,13 @@ + + + + + + + @@ -194,6 +205,10 @@ {27134980-4415-4375-a564-40a9014dfa5f} OpenBveApi + + {e81b7bd8-a326-47d3-b7ee-e9c7d4d119fa} + Formats.Msts + {4ef680d7-df17-4978-9872-133edc169b4b} RouteManager2