├── ddu.png ├── inputs.png ├── iracing.cpp ├── OverlayDDU.h ├── relative.png ├── standings.png ├── carIcons ├── BMW.png ├── DS.png ├── Kia.png ├── VW.png ├── Audi.png ├── Dacia.png ├── FIAT.png ├── Ford.png ├── Hudson.png ├── Jaguar.png ├── Jeep.png ├── Lexus.png ├── Ligier.png ├── Lotus.png ├── Mazda.png ├── Mini.png ├── Opel.png ├── Sauber.png ├── Scoda.png ├── Seat.png ├── Subaru.png ├── Suzuki.png ├── Tesla.png ├── Toyota.png ├── Volvo.png ├── 00Error.png ├── Bugatti.png ├── Cadillac.png ├── Citroen.png ├── Corvette.png ├── Crysler.png ├── Dallara.png ├── Ferrari.png ├── Hyundai.png ├── Infiniti.png ├── Maserati.png ├── McLaren.png ├── Mercedes.png ├── Mustang.png ├── Nismo-1.png ├── Peugeot.png ├── Pontiac.png ├── Porsche.png ├── Radical.png ├── Renault.png ├── Alpha_Romeo.png ├── Chevrolet.png ├── Lamborghini.png ├── Land_Rover.png ├── Mitsubishi.png ├── Rolce_Royce.png ├── Alpine_modern.png └── Aston_Martin.png ├── OverlayRelative.h ├── .gitignore ├── LICENSE ├── OverlayCover.h ├── iron.sln ├── OverlayDebug.h ├── irsdk ├── yaml_parser.h ├── yaml_parser.cpp ├── irsdk_client.h ├── irsdk_utils.cpp ├── irsdk_client.cpp └── irsdk_defines.h ├── iron.vcxproj.filters ├── Config.h ├── OverlayDebug.cpp ├── Overlay.h ├── README.md ├── Config.cpp ├── OverlayInputs.h ├── OverlayRadar.h ├── iron.vcxproj ├── Overlay.cpp ├── util.h ├── main.cpp └── iracing.h /ddu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/ddu.png -------------------------------------------------------------------------------- /inputs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/inputs.png -------------------------------------------------------------------------------- /iracing.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/iracing.cpp -------------------------------------------------------------------------------- /OverlayDDU.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/OverlayDDU.h -------------------------------------------------------------------------------- /relative.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/relative.png -------------------------------------------------------------------------------- /standings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/standings.png -------------------------------------------------------------------------------- /carIcons/BMW.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/BMW.png -------------------------------------------------------------------------------- /carIcons/DS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/DS.png -------------------------------------------------------------------------------- /carIcons/Kia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Kia.png -------------------------------------------------------------------------------- /carIcons/VW.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/VW.png -------------------------------------------------------------------------------- /OverlayRelative.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/OverlayRelative.h -------------------------------------------------------------------------------- /carIcons/Audi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Audi.png -------------------------------------------------------------------------------- /carIcons/Dacia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Dacia.png -------------------------------------------------------------------------------- /carIcons/FIAT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/FIAT.png -------------------------------------------------------------------------------- /carIcons/Ford.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Ford.png -------------------------------------------------------------------------------- /carIcons/Hudson.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Hudson.png -------------------------------------------------------------------------------- /carIcons/Jaguar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Jaguar.png -------------------------------------------------------------------------------- /carIcons/Jeep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Jeep.png -------------------------------------------------------------------------------- /carIcons/Lexus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Lexus.png -------------------------------------------------------------------------------- /carIcons/Ligier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Ligier.png -------------------------------------------------------------------------------- /carIcons/Lotus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Lotus.png -------------------------------------------------------------------------------- /carIcons/Mazda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Mazda.png -------------------------------------------------------------------------------- /carIcons/Mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Mini.png -------------------------------------------------------------------------------- /carIcons/Opel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Opel.png -------------------------------------------------------------------------------- /carIcons/Sauber.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Sauber.png -------------------------------------------------------------------------------- /carIcons/Scoda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Scoda.png -------------------------------------------------------------------------------- /carIcons/Seat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Seat.png -------------------------------------------------------------------------------- /carIcons/Subaru.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Subaru.png -------------------------------------------------------------------------------- /carIcons/Suzuki.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Suzuki.png -------------------------------------------------------------------------------- /carIcons/Tesla.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Tesla.png -------------------------------------------------------------------------------- /carIcons/Toyota.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Toyota.png -------------------------------------------------------------------------------- /carIcons/Volvo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Volvo.png -------------------------------------------------------------------------------- /carIcons/00Error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/00Error.png -------------------------------------------------------------------------------- /carIcons/Bugatti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Bugatti.png -------------------------------------------------------------------------------- /carIcons/Cadillac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Cadillac.png -------------------------------------------------------------------------------- /carIcons/Citroen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Citroen.png -------------------------------------------------------------------------------- /carIcons/Corvette.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Corvette.png -------------------------------------------------------------------------------- /carIcons/Crysler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Crysler.png -------------------------------------------------------------------------------- /carIcons/Dallara.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Dallara.png -------------------------------------------------------------------------------- /carIcons/Ferrari.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Ferrari.png -------------------------------------------------------------------------------- /carIcons/Hyundai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Hyundai.png -------------------------------------------------------------------------------- /carIcons/Infiniti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Infiniti.png -------------------------------------------------------------------------------- /carIcons/Maserati.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Maserati.png -------------------------------------------------------------------------------- /carIcons/McLaren.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/McLaren.png -------------------------------------------------------------------------------- /carIcons/Mercedes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Mercedes.png -------------------------------------------------------------------------------- /carIcons/Mustang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Mustang.png -------------------------------------------------------------------------------- /carIcons/Nismo-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Nismo-1.png -------------------------------------------------------------------------------- /carIcons/Peugeot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Peugeot.png -------------------------------------------------------------------------------- /carIcons/Pontiac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Pontiac.png -------------------------------------------------------------------------------- /carIcons/Porsche.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Porsche.png -------------------------------------------------------------------------------- /carIcons/Radical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Radical.png -------------------------------------------------------------------------------- /carIcons/Renault.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Renault.png -------------------------------------------------------------------------------- /carIcons/Alpha_Romeo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Alpha_Romeo.png -------------------------------------------------------------------------------- /carIcons/Chevrolet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Chevrolet.png -------------------------------------------------------------------------------- /carIcons/Lamborghini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Lamborghini.png -------------------------------------------------------------------------------- /carIcons/Land_Rover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Land_Rover.png -------------------------------------------------------------------------------- /carIcons/Mitsubishi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Mitsubishi.png -------------------------------------------------------------------------------- /carIcons/Rolce_Royce.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Rolce_Royce.png -------------------------------------------------------------------------------- /carIcons/Alpine_modern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Alpine_modern.png -------------------------------------------------------------------------------- /carIcons/Aston_Martin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegoschneider/iRon/HEAD/carIcons/Aston_Martin.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | iron.vcxproj.user 2 | .vs/ 3 | x64/ 4 | Debug/ 5 | Release/ 6 | sessionYaml.txt 7 | config.json 8 | 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-2022 L. E. Spalt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /OverlayCover.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021-2022 L. E. Spalt 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #pragma once 26 | 27 | #include "Overlay.h" 28 | 29 | class OverlayCover : public Overlay 30 | { 31 | public: 32 | 33 | OverlayCover(Microsoft::WRL::ComPtr d3dDevice) 34 | : Overlay("OverlayCover", d3dDevice) 35 | {} 36 | }; 37 | -------------------------------------------------------------------------------- /iron.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.32014.148 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "iron", "iron.vcxproj", "{74CC4F41-58B4-4B1C-8F5D-19ABADCE940B}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Release - DebugLoopPerformance|x64 = Release - DebugLoopPerformance|x64 12 | Release|x64 = Release|x64 13 | EndGlobalSection 14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 15 | {74CC4F41-58B4-4B1C-8F5D-19ABADCE940B}.Debug|x64.ActiveCfg = Debug|x64 16 | {74CC4F41-58B4-4B1C-8F5D-19ABADCE940B}.Debug|x64.Build.0 = Debug|x64 17 | {74CC4F41-58B4-4B1C-8F5D-19ABADCE940B}.Release - DebugLoopPerformance|x64.ActiveCfg = Release - DebugLoopPerformance|x64 18 | {74CC4F41-58B4-4B1C-8F5D-19ABADCE940B}.Release - DebugLoopPerformance|x64.Build.0 = Release - DebugLoopPerformance|x64 19 | {74CC4F41-58B4-4B1C-8F5D-19ABADCE940B}.Release|x64.ActiveCfg = Release|x64 20 | {74CC4F41-58B4-4B1C-8F5D-19ABADCE940B}.Release|x64.Build.0 = Release|x64 21 | EndGlobalSection 22 | GlobalSection(SolutionProperties) = preSolution 23 | HideSolutionNode = FALSE 24 | EndGlobalSection 25 | GlobalSection(ExtensibilityGlobals) = postSolution 26 | SolutionGuid = {17DBA48D-5073-43E7-9ACB-502EAE4495FD} 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /OverlayDebug.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021-2022 L. E. Spalt 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #pragma once 26 | 27 | #include "Overlay.h" 28 | #include "util.h" 29 | 30 | 31 | void dbg( const float4& color, const char* fmt, ... ); 32 | void dbg( const char* fmt, ... ); 33 | 34 | class OverlayDebug : public Overlay 35 | { 36 | public: 37 | 38 | OverlayDebug(Microsoft::WRL::ComPtr d3dDevice); 39 | virtual void onEnable(); 40 | virtual void onConfigChanged(); 41 | virtual void onUpdate(); 42 | virtual bool canEnableWhileNotDriving() const; 43 | virtual bool canEnableWhileDisconnected() const; 44 | 45 | protected: 46 | 47 | Microsoft::WRL::ComPtr m_textFormat; 48 | 49 | }; 50 | -------------------------------------------------------------------------------- /irsdk/yaml_parser.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013, iRacing.com Motorsport Simulations, LLC. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of iRacing.com Motorsport Simulations nor the 13 | names of its contributors may be used to endorse or promote products 14 | derived from this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef YAML_PARSER_H 29 | #define YAML_PARSER_H 30 | 31 | // super simple YAML parser 32 | bool parseYaml(const char *data, const char* path, const char **val, int *len); 33 | 34 | #endif //YAML_PARSER_H 35 | -------------------------------------------------------------------------------- /iron.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | irsdk 7 | 8 | 9 | irsdk 10 | 11 | 12 | irsdk 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | {a67f493c-b77c-46e5-be0b-98bb42ae3d17} 22 | 23 | 24 | 25 | 26 | irsdk 27 | 28 | 29 | irsdk 30 | 31 | 32 | irsdk 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /Config.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021-2022 L. E. Spalt 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #pragma once 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include "picojson.h" 32 | #include "util.h" 33 | 34 | class Config 35 | { 36 | public: 37 | 38 | bool load(); 39 | bool save(); 40 | 41 | void watchForChanges(); 42 | bool hasChanged(); 43 | 44 | bool getBool( const std::string& component, const std::string& key, bool defaultVal ); 45 | int getInt( const std::string& component, const std::string& key, int defaultVal ); 46 | float getFloat( const std::string& component, const std::string& key, float defaultVal ); 47 | float4 getFloat4( const std::string& component, const std::string& key, const float4& defaultVal ); 48 | std::string getString( const std::string& component, const std::string& key, const std::string& defaultVal ); 49 | std::vector getStringVec( const std::string& component, const std::string& key, const std::vector& defaultVal ); 50 | 51 | void setInt( const std::string& component, const std::string& key, int v ); 52 | void setBool( const std::string& component, const std::string& key, bool v ); 53 | 54 | private: 55 | 56 | picojson::object& getOrInsertComponent( const std::string& component, bool* existed=nullptr ); 57 | picojson::value& getOrInsertValue( const std::string& component, const std::string& key, bool* existed=nullptr ); 58 | 59 | picojson::object m_pj; 60 | std::atomic m_hasChanged = false; 61 | std::thread m_configWatchThread; 62 | std::string m_filename = "config.json"; 63 | }; 64 | 65 | extern Config g_cfg; 66 | 67 | -------------------------------------------------------------------------------- /OverlayDebug.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021-2022 L. E. Spalt 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #pragma once 26 | 27 | #include "OverlayDebug.h" 28 | #include 29 | #include 30 | #include 31 | 32 | struct DbgLine 33 | { 34 | std::string s; 35 | float4 col; 36 | }; 37 | 38 | static std::vector g_dbgLines; 39 | extern bool g_dbgOverlayEnabled; 40 | 41 | void dbg( const float4& color, const char* fmt, ... ) 42 | { 43 | #ifndef _DEBUG 44 | return; 45 | #endif 46 | if (!g_dbgOverlayEnabled) return; 47 | 48 | va_list args; 49 | va_start( args, fmt ); 50 | 51 | char s[2048]; 52 | vsnprintf( s, sizeof(s), fmt, args ); 53 | 54 | DbgLine line; 55 | line.s = s; 56 | line.col = color; 57 | g_dbgLines.emplace_back( line ); 58 | 59 | va_end( args ); 60 | } 61 | 62 | void dbg( const char* fmt, ... ) 63 | { 64 | #ifndef _DEBUG 65 | return; 66 | #endif 67 | if (!g_dbgOverlayEnabled) return; 68 | 69 | va_list args; 70 | va_start( args, fmt ); 71 | 72 | char s[2048]; 73 | vsnprintf( s, sizeof(s), fmt, args ); 74 | 75 | DbgLine line; 76 | line.s = s; 77 | line.col = float4(1,1,1,0.9f); 78 | g_dbgLines.emplace_back( line ); 79 | 80 | va_end( args ); 81 | } 82 | 83 | 84 | OverlayDebug::OverlayDebug(Microsoft::WRL::ComPtr d3dDevice) 85 | : Overlay("OverlayDebug", d3dDevice) 86 | {} 87 | 88 | void OverlayDebug::onEnable() 89 | { 90 | onConfigChanged(); // trigger font load 91 | } 92 | 93 | void OverlayDebug::onConfigChanged() 94 | { 95 | HRCHECK(m_dwriteFactory->CreateTextFormat( L"Consolas", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 15, L"en-us", &m_textFormat )); 96 | m_textFormat->SetParagraphAlignment( DWRITE_PARAGRAPH_ALIGNMENT_CENTER ); 97 | m_textFormat->SetWordWrapping( DWRITE_WORD_WRAPPING_NO_WRAP ); 98 | } 99 | 100 | void OverlayDebug::onUpdate() 101 | { 102 | const float lineHeight = 20; 103 | 104 | m_renderTarget->BeginDraw(); 105 | 106 | for( int i=0; i<(int)g_dbgLines.size(); ++i ) 107 | { 108 | const DbgLine& line = g_dbgLines[i]; 109 | 110 | const float y = 10 + lineHeight/2 + i*lineHeight; 111 | 112 | m_brush->SetColor( line.col ); 113 | D2D1_RECT_F r = { 10, y-lineHeight/2, (float)m_width-10, y+lineHeight/2 }; 114 | auto wstr = toWide( line.s ); 115 | m_renderTarget->DrawTextA( wstr.c_str(), (int)wstr.size(), m_textFormat.Get(), &r, m_brush.Get(), D2D1_DRAW_TEXT_OPTIONS_CLIP ); 116 | } 117 | 118 | m_renderTarget->EndDraw(); 119 | 120 | g_dbgLines.clear(); 121 | } 122 | 123 | bool OverlayDebug::canEnableWhileNotDriving() const 124 | { 125 | return true; 126 | } 127 | 128 | bool OverlayDebug::canEnableWhileDisconnected() const 129 | { 130 | return true; 131 | } 132 | -------------------------------------------------------------------------------- /Overlay.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021-2022 L. E. Spalt 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #pragma once 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include "util.h" 36 | 37 | #if defined(_DEBUG) or defined(DEBUG_OVERLAY_TIME) 38 | #include 39 | #endif 40 | 41 | class Overlay 42 | { 43 | public: 44 | 45 | Overlay( const std::string name, Microsoft::WRL::ComPtr d3dDevice); 46 | virtual ~Overlay(); 47 | 48 | std::string getName() const; 49 | virtual bool canEnableWhileNotDriving() const; 50 | virtual bool canEnableWhileDisconnected() const; 51 | 52 | void enable( bool on ); 53 | bool isEnabled() const; 54 | 55 | void enableUiEdit( bool on ); 56 | bool isUiEditEnabled() const; 57 | 58 | void configChanged(); 59 | void sessionChanged(); 60 | 61 | void update(); 62 | 63 | void setWindowPosAndSize( int x, int y, int w, int h, bool callSetWindowPos=true ); 64 | void saveWindowPosAndSize(); 65 | 66 | protected: 67 | 68 | virtual void onEnable(); 69 | virtual void onDisable(); 70 | virtual void onUpdate(); 71 | virtual void onConfigChanged(); 72 | virtual void onSessionChanged(); 73 | virtual float2 getDefaultSize(); 74 | virtual bool hasCustomBackground(); 75 | 76 | std::string m_name; 77 | HWND m_hwnd = 0; 78 | bool m_enabled = false; 79 | bool m_uiEditEnabled = false; 80 | int m_xpos = 0; 81 | int m_ypos = 0; 82 | int m_width = 0; 83 | int m_height = 0; 84 | #if defined(_DEBUG) or defined(DEBUG_OVERLAY_TIME) 85 | std::chrono::steady_clock::time_point debugTimeStart = std::chrono::high_resolution_clock::now(); 86 | std::chrono::steady_clock::time_point debugTimeEnd = debugTimeStart; 87 | long long debugTimeDiff = 0; 88 | float debugTimeAvg = 0.0f; 89 | #endif 90 | 91 | Microsoft::WRL::ComPtr m_d3dDevice; 92 | Microsoft::WRL::ComPtr m_swapChain; 93 | Microsoft::WRL::ComPtr m_d2dFactory; 94 | Microsoft::WRL::ComPtr m_renderTarget; 95 | Microsoft::WRL::ComPtr m_compositionDevice; 96 | Microsoft::WRL::ComPtr m_compositionTarget; 97 | Microsoft::WRL::ComPtr m_compositionVisual; 98 | Microsoft::WRL::ComPtr m_dwriteFactory; 99 | Microsoft::WRL::ComPtr m_brush; 100 | }; 101 | -------------------------------------------------------------------------------- /irsdk/yaml_parser.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013, iRacing.com Motorsport Simulations, LLC. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of iRacing.com Motorsport Simulations nor the 13 | names of its contributors may be used to endorse or promote products 14 | derived from this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | 31 | enum yaml_state { 32 | space, 33 | key, 34 | keysep, 35 | value, 36 | newline 37 | }; 38 | 39 | // super simple YAML parser 40 | bool parseYaml(const char *data, const char* path, const char **val, int *len) 41 | { 42 | if(data && path && val && len) 43 | { 44 | // make sure we set this to something 45 | *val = NULL; 46 | *len = 0; 47 | 48 | int depth = 0; 49 | yaml_state state = space; 50 | 51 | const char *keystr = NULL; 52 | int keylen = 0; 53 | 54 | const char *valuestr = NULL; 55 | int valuelen = 0; 56 | 57 | const char *pathptr = path; 58 | int pathdepth = 0; 59 | 60 | while(*data) 61 | { 62 | switch(*data) 63 | { 64 | case ' ': 65 | if(state == newline) 66 | state = space; 67 | if(state == space) 68 | depth++; 69 | else if(state == key) 70 | keylen++; 71 | else if(state == value) 72 | valuelen++; 73 | break; 74 | case '-': 75 | if(state == newline) 76 | state = space; 77 | if(state == space) 78 | depth++; 79 | else if(state == key) 80 | keylen++; 81 | else if(state == value) 82 | valuelen++; 83 | else if(state == keysep) 84 | { 85 | state = value; 86 | valuestr = data; 87 | valuelen = 1; 88 | } 89 | break; 90 | case ':': 91 | if(state == key) 92 | { 93 | state = keysep; 94 | keylen++; 95 | } 96 | else if(state == keysep) 97 | { 98 | state = value; 99 | valuestr = data; 100 | } 101 | else if(state == value) 102 | valuelen++; 103 | break; 104 | case '\n': 105 | case '\r': 106 | if(state != newline) 107 | { 108 | if(depth < pathdepth) 109 | { 110 | return false; 111 | } 112 | else if(keylen && 0 == strncmp(keystr, pathptr, keylen)) 113 | { 114 | bool found = true; 115 | //do we need to test the value? 116 | if(*(pathptr+keylen) == '{') 117 | { 118 | //search for closing brace 119 | int pathvaluelen = keylen + 1; 120 | while(*(pathptr+pathvaluelen) && *(pathptr+pathvaluelen) != '}') 121 | pathvaluelen++; 122 | 123 | if(valuelen == pathvaluelen - (keylen+1) && 0 == strncmp(valuestr, (pathptr+keylen+1), valuelen)) 124 | pathptr += valuelen + 2; 125 | else 126 | found = false; 127 | } 128 | 129 | if(found) 130 | { 131 | pathptr += keylen; 132 | pathdepth = depth; 133 | 134 | if(*pathptr == '\0') 135 | { 136 | *val = valuestr; 137 | *len = valuelen; 138 | return true; 139 | } 140 | } 141 | } 142 | 143 | depth = 0; 144 | keylen = 0; 145 | valuelen = 0; 146 | } 147 | state = newline; 148 | break; 149 | default: 150 | if(state == space || state == newline) 151 | { 152 | state = key; 153 | keystr = data; 154 | keylen = 0; //redundant? 155 | } 156 | else if(state == keysep) 157 | { 158 | state = value; 159 | valuestr = data; 160 | valuelen = 0; //redundant? 161 | } 162 | if(state == key) 163 | keylen++; 164 | if(state == value) 165 | valuelen++; 166 | break; 167 | } 168 | 169 | // important, increment our pointer 170 | data++; 171 | } 172 | 173 | } 174 | return false; 175 | } 176 | 177 | -------------------------------------------------------------------------------- /irsdk/irsdk_client.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013, iRacing.com Motorsport Simulations, LLC. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of iRacing.com Motorsport Simulations nor the 13 | names of its contributors may be used to endorse or promote products 14 | derived from this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef IRSDKCLIENT_H 29 | #define IRSDKCLIENT_H 30 | 31 | // A C++ wrapper around the irsdk calls that takes care of the details of maintaining a connection. 32 | // reads out the data into a cache so you don't have to worry about timming 33 | class irsdkClient 34 | { 35 | public: 36 | // singleton 37 | static irsdkClient& instance(); 38 | 39 | // wait for live data, or if a .ibt file is open 40 | // then read the next line from the file. 41 | bool waitForData(int timeoutMS = 16); 42 | 43 | bool isConnected(); 44 | int getStatusID() { return m_statusID; } 45 | 46 | int getVarIdx(const char*name); 47 | 48 | // what is the base type of the data 49 | // returns irsdk_VarType as int so we don't depend on irsdk_defines.h 50 | int getVarType(int idx); 51 | int getVarType(const char *name) { return getVarType(getVarIdx(name)); } 52 | 53 | // how many elements in array, or 1 if not an array 54 | int getVarCount(int idx); 55 | int getVarCount(const char *name) { return getVarCount(getVarIdx(name)); } 56 | 57 | // idx is the variables index, entry is the array offset, or 0 if not an array element 58 | // will convert data to requested type 59 | bool getVarBool(int idx, int entry = 0); 60 | bool getVarBool(const char *name, int entry = 0) { return getVarBool(getVarIdx(name), entry); } 61 | 62 | int getVarInt(int idx, int entry = 0); 63 | int getVarInt(const char *name, int entry = 0) { return getVarInt(getVarIdx(name), entry); } 64 | 65 | float getVarFloat(int idx, int entry = 0); 66 | float getVarFloat(const char *name, int entry = 0) { return getVarFloat(getVarIdx(name), entry); } 67 | 68 | double getVarDouble(int idx, int entry = 0); 69 | double getVarDouble(const char *name, int entry = 0) { return getVarDouble(getVarIdx(name), entry); } 70 | 71 | //--- 72 | 73 | // value that increments with each update to string 74 | int getSessionCt() { return irsdk_getSessionInfoStrUpdate(); } 75 | 76 | // has string changed since we last read any values from it 77 | bool wasSessionStrUpdated() { return m_lastSessionCt != getSessionCt(); } 78 | 79 | // pars string for individual value, 1 success, 0 failure, -n minimum buffer size 80 | //****Note, this is a linear parser, so it is slow! 81 | int getSessionStrVal(const char *path, char *val, int valLen); 82 | 83 | // get the whole string 84 | const char *getSessionStr(); 85 | 86 | protected: 87 | 88 | irsdkClient() 89 | : m_data(NULL) 90 | , m_nData(0) 91 | , m_statusID(0) 92 | , m_lastSessionCt(-1) 93 | { } 94 | 95 | ~irsdkClient() { shutdown(); } 96 | 97 | void shutdown(); 98 | 99 | char *m_data; 100 | int m_nData; 101 | int m_statusID; 102 | 103 | int m_lastSessionCt; 104 | 105 | static irsdkClient *m_instance; 106 | }; 107 | 108 | 109 | // helper class to keep track of our variables index 110 | // Create a global instance of this and it will take care of the details for you. 111 | class irsdkCVar 112 | { 113 | public: 114 | irsdkCVar(); 115 | irsdkCVar(const char *name); 116 | 117 | void setVarName(const char *name); 118 | 119 | // returns irsdk_VarType as int so we don't depend on irsdk_defines.h 120 | int getType(); 121 | int getCount(); 122 | bool isValid(); 123 | 124 | // entry is the array offset, or 0 if not an array element 125 | bool getBool(int entry = 0); 126 | int getInt(int entry = 0); 127 | float getFloat(int entry = 0); 128 | double getDouble(int entry = 0); 129 | 130 | protected: 131 | bool checkIdx(); 132 | 133 | static const int max_string = 32; //IRSDK_MAX_STRING 134 | char m_name[max_string]; 135 | int m_idx; 136 | int m_statusID; 137 | }; 138 | 139 | #endif // IRSDKCLIENT_H 140 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iRon - lightweight overlays for iRacing 2 | 3 | This project provides a few lightweight overlays for iRacing. Included are: a relative display with optional minimap, a dashboard with fuel calculator, a throttle/brake input graph, and a standings display. 4 | 5 | I created iRon for my own personal use. As such, its feature set is limited to what I considered sensible in practice given the way I use iRacing. I don't currently plan to extend it further. That said, I'm making it available in the hope it might be useful to others in the iRacing community, either for direct use or as a starting point for other homebrew overlays. 6 | 7 | The project's code base aims to be small, easy to modify, and free of external dependencies. 8 | 9 | # Contents 10 | 11 | - [Where to Download](#where-to-download) 12 | - [Overlays](#overlays) 13 | - [*Relative*](#relative) 14 | - [*DDU*](#ddu) 15 | - [*Inputs*](#inputs) 16 | - [*Standings*](#standings) 17 | - [*Cover*](#cover) 18 | - [Installing & Running](#installing--running) 19 | - [Configuration](#configuration) 20 | - [Building from source](#building-from-source) 21 | - [Dependencies](#dependencies) 22 | - [Bug reports and feature requests](#bug-reports-and-feature-requests) 23 | - [Donations](#donations) 24 | 25 | --- 26 | 27 | ## Where to Download 28 | 29 | The latest binary release can be found [here](https://github.com/lespalt/iRon/releases/latest). 30 | 31 | ## Overlays 32 | 33 | ### *Relative* 34 | 35 | Like the *Relative* box in iRacing, but with additional information such as license, iRating, and laps driven since the last pit stop. You can also highlight your friends by adding their names to a buddy list. 36 | 37 | At the top is an optional minimap. It can be set to either relative mode (own car fixed in the center) or absolute mode (start/finish line fixed in the center). 38 | 39 | ![relative](relative.png?raw=true) 40 | 41 | ### *DDU* 42 | 43 | A dashboard that concentrates important pieces of information for which you would otherwise have to flip through various boxes in iRacing. 44 | 45 | The fuel calculator shows the estimated remaining laps, remaining amount of fuel, estimated fuel used per lap, estimated _additional_ fuel required to finish the race, and the fuel amount that is scheduled to be added on the next pit stop. To compute the estimated fuel consumption, the last 4 laps under green and without pit stops are taken into account, and a configurable safety margin is added. These parameters can be customized. 46 | 47 | ![ddu](ddu.png?raw=true) 48 | 49 | ### *Inputs* 50 | 51 | Shows throttle/brake/steering in a moving graph. I find it useful to practice consistent braking. 52 | 53 | ![inputs](inputs.png?raw=true) 54 | 55 | ### *Standings* 56 | 57 | Shows the standings of the entire field, including safety rating, iRating, and number of laps since the last pit stop ("pit age"). I usually leave this off by default and switch it on during cautions. Or glimpse at it pre-race to get a sense of the competition level. 58 | 59 | Like the "Relative" overlay, this will highlight buddies in green (Dale Jr. in the example below). 60 | 61 | ![standings](standings.png?raw=true) 62 | 63 | ### *Cover* 64 | 65 | No screenshot for this one, because all it is is a blank rectangle. Can be useful to cover up distracting in-game dashboards, like the one in the next-gen NASCAR. 66 | 67 | --- 68 | 69 | ## Installing & Running 70 | 71 | The app does not require installation. Just copy the executable to a folder of your choice. Make sure the folder is not write protected, as iRon will attempt to save its configuration file in the working directory. 72 | 73 | To use it, simply run the executable. It doesn't matter whether you do this before or after launching iRacing. A console window will pop up, indicating that iRon is running. Once you're in the car in iRacing, the overlays should show up, and you can configure things to your liking. I recommend running iRacing in borderless window mode. Overlays *might* work in other modes as well, but I haven't tested it. 74 | 75 | --- 76 | 77 | ## Configuration 78 | 79 | To place and resize the overlays, press ALT-j. This will enter a mode in which you can move overlays around with the mouse and resize them by dragging their bottom-right corner. Press ALT-j again to go back to normal mode. 80 | 81 | Overlays can be switched on and off at runtime using the hotkeys displayed during startup. All hotkeys are configurable. 82 | 83 | Certain aspects of the overlays, such as colors, font types, sizes etc. can be customized. To do that, open the file **config.json** that iRon created and experiment by editing the (hopefully mostly self-explanatory) parameters. You can do that while the app is running -- the changes will take effect immediately whenever the file is saved. 84 | 85 | _Note that currently, the config file will be created only after the overlays have been "touched" for the first time, usually by dragging or resizing them._ 86 | 87 | ##### Fuel calculator 88 | Fuel in laps = ( Remaining_Fuel - Reserve Fuel ) / (Average_Use x Fuel_Estimate_Factor) 89 | 90 | Vars: 91 | fuel_estimate_avg_green_laps: Amount of laps to average in the lap calculation 92 | fuel_estimate_factor: Amount to multiply the fuel use for (coarse safety margin) 93 | fuel_reserve_margin: Amount of fuel to reserve in the calculations 94 | 95 | --- 96 | 97 | ## Building from source 98 | 99 | This app is built with Visual Studio 2022 Community version. The project/solution files should work out of the box. Depending on your Visual Studio setup, you may need to install additional prerequisites (static libs) needed to build DirectX applications. 100 | 101 | --- 102 | 103 | ## Dependencies 104 | 105 | There are no runtime dependencies other than standard Windows components like DirectX. Those should already be present on most if not all systems that can run iRacing. 106 | 107 | Build dependencies (most notably the iRacing SDK and picojson) are kept to a minimum and are included in the repository. 108 | 109 | --- 110 | 111 | ## Bug reports and feature requests 112 | 113 | If you encounter a problem, please file a github issue and I'll do my best to address it. Pull requests with fixes are welcome too, of course. 114 | 115 | If you'd like to see a specific feature added, feel free to file a github issue as well. If it's something small, I may actually get to it :-) No promises though, as unfortunately the time I can spend on this project is quite limited. 116 | 117 | --- 118 | 119 | ## Donations 120 | 121 | If you like this project enough to wonder whether you can contribute financially: first of all, thank you! I'm not looking for donations, but **please consider giving to Ukraine-related charities instead**. 122 | 123 | -------------------------------------------------------------------------------- /Config.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021-2022 L. E. Spalt 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | 26 | #include 27 | #include "Config.h" 28 | 29 | Config g_cfg; 30 | 31 | static void configWatcher( std::atomic* m_hasChanged ) 32 | { 33 | HANDLE dir = CreateFile( ".", FILE_LIST_DIRECTORY, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL ); 34 | if( dir == INVALID_HANDLE_VALUE ) 35 | { 36 | printf( "Could not start config watch thread.\n" ); 37 | return; 38 | } 39 | 40 | std::vector buf( 1024*1024 ); 41 | DWORD bytesReturned = 0; 42 | 43 | while( true ) 44 | { 45 | if( ReadDirectoryChangesW( dir, buf.data(), (DWORD)buf.size()/sizeof(DWORD), TRUE, FILE_NOTIFY_CHANGE_LAST_WRITE, &bytesReturned, NULL, NULL ) ) 46 | { 47 | Sleep( 100 ); // wait a bit to make sure changes are actually picked up when we reload 48 | *m_hasChanged = true; 49 | } 50 | } 51 | } 52 | 53 | bool Config::load() 54 | { 55 | std::string json; 56 | if( !loadFile(m_filename, json) ) 57 | { 58 | //printf("Could not load config file\n"); 59 | return false; 60 | } 61 | 62 | picojson::value pjval; 63 | std::string parseError = picojson::parse( pjval, json ); 64 | if( !parseError.empty() ) 65 | { 66 | printf("Config file is not valid JSON!\n%s\n", parseError.c_str() ); 67 | return false; 68 | } 69 | 70 | m_pj = pjval.get(); 71 | m_hasChanged = false; 72 | return true; 73 | } 74 | 75 | bool Config::save() 76 | { 77 | const picojson::value value = picojson::value( m_pj ); 78 | const std::string json = value.serialize(true); 79 | const bool ok = saveFile( m_filename, json ); 80 | if( !ok ) { 81 | char s[1024]; 82 | GetCurrentDirectory( sizeof(s), s ); 83 | printf("Could not save config file! Please make sure iRon is started from a directory for which it has write permissions. The current directory is: %s.\n", s); 84 | } 85 | return ok; 86 | } 87 | 88 | void Config::watchForChanges() 89 | { 90 | m_configWatchThread = std::thread( configWatcher, &m_hasChanged ); 91 | m_configWatchThread.detach(); 92 | } 93 | 94 | bool Config::hasChanged() 95 | { 96 | return m_hasChanged; 97 | } 98 | 99 | bool Config::getBool( const std::string& component, const std::string& key, bool defaultVal ) 100 | { 101 | bool existed = false; 102 | picojson::value& value = getOrInsertValue( component, key, &existed ); 103 | 104 | if( !existed ) 105 | value.set( defaultVal ); 106 | 107 | return value.get(); 108 | } 109 | 110 | int Config::getInt( const std::string& component, const std::string& key, int defaultVal ) 111 | { 112 | bool existed = false; 113 | picojson::value& value = getOrInsertValue( component, key, &existed ); 114 | 115 | if( !existed ) 116 | value.set( defaultVal ); 117 | 118 | return (int)value.get(); 119 | } 120 | 121 | float Config::getFloat( const std::string& component, const std::string& key, float defaultVal ) 122 | { 123 | bool existed = false; 124 | picojson::value& value = getOrInsertValue( component, key, &existed ); 125 | 126 | if( !existed ) 127 | value.set( defaultVal ); 128 | 129 | return (float)value.get(); 130 | } 131 | 132 | float4 Config::getFloat4( const std::string& component, const std::string& key, const float4& defaultVal ) 133 | { 134 | bool existed = false; 135 | picojson::value& value = getOrInsertValue( component, key, &existed ); 136 | 137 | if( !existed ) 138 | { 139 | picojson::array arr( 4 ); 140 | arr[0].set( defaultVal.x ); 141 | arr[1].set( defaultVal.y ); 142 | arr[2].set( defaultVal.z ); 143 | arr[3].set( defaultVal.w ); 144 | value.set( arr ); 145 | } 146 | 147 | picojson::array& arr = value.get(); 148 | float4 ret; 149 | ret.x = (float)arr[0].get(); 150 | ret.y = (float)arr[1].get(); 151 | ret.z = (float)arr[2].get(); 152 | ret.w = (float)arr[3].get(); 153 | return ret; 154 | } 155 | 156 | std::string Config::getString( const std::string& component, const std::string& key, const std::string& defaultVal ) 157 | { 158 | bool existed = false; 159 | picojson::value& value = getOrInsertValue( component, key, &existed ); 160 | 161 | if( !existed ) 162 | value.set( defaultVal ); 163 | 164 | return value.get(); 165 | } 166 | 167 | std::vector Config::getStringVec( const std::string& component, const std::string& key, const std::vector& defaultVal ) 168 | { 169 | bool existed = false; 170 | picojson::value& value = getOrInsertValue( component, key, &existed ); 171 | 172 | if( !existed ) 173 | { 174 | picojson::array arr( defaultVal.size() ); 175 | for( int i=0; i<(int)defaultVal.size(); ++i ) 176 | arr[i].set( defaultVal[i] ); 177 | value.set( arr ); 178 | } 179 | 180 | picojson::array& arr = value.get(); 181 | std::vector ret( arr.size() ); 182 | for( picojson::value& entry : arr ) 183 | ret.push_back( entry.get() ); 184 | return ret; 185 | } 186 | 187 | void Config::setInt( const std::string& component, const std::string& key, int v ) 188 | { 189 | picojson::object& pjcomp = m_pj[component].get(); 190 | double d = double(v); 191 | pjcomp[key].set( d ); 192 | } 193 | 194 | void Config::setBool( const std::string& component, const std::string& key, bool v ) 195 | { 196 | picojson::object& pjcomp = m_pj[component].get(); 197 | pjcomp[key].set( v ); 198 | } 199 | 200 | picojson::object& Config::getOrInsertComponent( const std::string& component, bool* existed ) 201 | { 202 | auto it = m_pj.insert(std::make_pair(component,picojson::object())); 203 | 204 | if( existed ) 205 | *existed = !it.second; 206 | 207 | return it.first->second.get(); 208 | } 209 | 210 | picojson::value& Config::getOrInsertValue( const std::string& component, const std::string& key, bool* existed ) 211 | { 212 | picojson::object& comp = getOrInsertComponent( component ); 213 | 214 | auto it = comp.insert(std::make_pair(key,picojson::value())); 215 | 216 | if( existed ) 217 | *existed = !it.second; 218 | 219 | return it.first->second; 220 | } 221 | -------------------------------------------------------------------------------- /irsdk/irsdk_utils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013, iRacing.com Motorsport Simulations, LLC. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of iRacing.com Motorsport Simulations nor the 13 | names of its contributors may be used to endorse or promote products 14 | derived from this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #define MIN_WIN_VER 0x0501 29 | 30 | #ifndef WINVER 31 | # define WINVER MIN_WIN_VER 32 | #endif 33 | 34 | #ifndef _WIN32_WINNT 35 | # define _WIN32_WINNT MIN_WIN_VER 36 | #endif 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #ifdef _MSC_VER 44 | #include 45 | #endif 46 | 47 | #include "irsdk_defines.h" 48 | 49 | // for timeBeginPeriod() 50 | #pragma comment(lib, "Winmm") 51 | // for RegisterWindowMessage() and SendMessage() 52 | #pragma comment(lib, "User32") 53 | 54 | // Local memory 55 | 56 | static HANDLE hDataValidEvent = NULL; 57 | static HANDLE hMemMapFile = NULL; 58 | 59 | static const char *pSharedMem = NULL; 60 | static const irsdk_header *pHeader = NULL; 61 | 62 | static int lastTickCount = INT_MAX; 63 | static bool isInitialized = false; 64 | 65 | static const double timeout = 30.0; // timeout after 30 seconds with no communication 66 | static time_t lastValidTime = 0; 67 | 68 | // Function Implementations 69 | 70 | bool irsdk_startup() 71 | { 72 | if(!hMemMapFile) 73 | { 74 | hMemMapFile = OpenFileMapping( FILE_MAP_READ, FALSE, IRSDK_MEMMAPFILENAME); 75 | lastTickCount = INT_MAX; 76 | } 77 | 78 | if(hMemMapFile) 79 | { 80 | if(!pSharedMem) 81 | { 82 | pSharedMem = (const char *)MapViewOfFile(hMemMapFile, FILE_MAP_READ, 0, 0, 0); 83 | pHeader = (irsdk_header *)pSharedMem; 84 | lastTickCount = INT_MAX; 85 | } 86 | 87 | if(pSharedMem) 88 | { 89 | if(!hDataValidEvent) 90 | { 91 | hDataValidEvent = OpenEvent(SYNCHRONIZE, false, IRSDK_DATAVALIDEVENTNAME); 92 | lastTickCount = INT_MAX; 93 | } 94 | 95 | if(hDataValidEvent) 96 | { 97 | isInitialized = true; 98 | return isInitialized; 99 | } 100 | //else printf("Error opening event: %d\n", GetLastError()); 101 | } 102 | //else printf("Error mapping file: %d\n", GetLastError()); 103 | } 104 | //else printf("Error opening file: %d\n", GetLastError()); 105 | 106 | isInitialized = false; 107 | return isInitialized; 108 | } 109 | 110 | void irsdk_shutdown() 111 | { 112 | if(hDataValidEvent) 113 | CloseHandle(hDataValidEvent); 114 | 115 | if(pSharedMem) 116 | UnmapViewOfFile(pSharedMem); 117 | 118 | if(hMemMapFile) 119 | CloseHandle(hMemMapFile); 120 | 121 | hDataValidEvent = NULL; 122 | pSharedMem = NULL; 123 | pHeader = NULL; 124 | hMemMapFile = NULL; 125 | 126 | isInitialized = false; 127 | lastTickCount = INT_MAX; 128 | } 129 | 130 | bool irsdk_getNewData(char *data) 131 | { 132 | if(isInitialized || irsdk_startup()) 133 | { 134 | #ifdef _MSC_VER 135 | _ASSERTE(NULL != pHeader); 136 | #endif 137 | 138 | // if sim is not active, then no new data 139 | if(!(pHeader->status & irsdk_stConnected)) 140 | { 141 | lastTickCount = INT_MAX; 142 | return false; 143 | } 144 | 145 | int latest = 0; 146 | for(int i=1; inumBuf; i++) 147 | if(pHeader->varBuf[latest].tickCount < pHeader->varBuf[i].tickCount) 148 | latest = i; 149 | 150 | // if newer than last recieved, than report new data 151 | if(lastTickCount < pHeader->varBuf[latest].tickCount) 152 | { 153 | // if asked to retrieve the data 154 | if(data) 155 | { 156 | // try twice to get the data out 157 | for(int count = 0; count < 2; count++) 158 | { 159 | int curTickCount = pHeader->varBuf[latest].tickCount; 160 | memcpy(data, pSharedMem + pHeader->varBuf[latest].bufOffset, pHeader->bufLen); 161 | if(curTickCount == pHeader->varBuf[latest].tickCount) 162 | { 163 | lastTickCount = curTickCount; 164 | lastValidTime = time(NULL); 165 | return true; 166 | } 167 | } 168 | // if here, the data changed out from under us. 169 | return false; 170 | } 171 | else 172 | { 173 | lastTickCount = pHeader->varBuf[latest].tickCount; 174 | lastValidTime = time(NULL); 175 | return true; 176 | } 177 | } 178 | // if older than last recieved, than reset, we probably disconnected 179 | else if(lastTickCount > pHeader->varBuf[latest].tickCount) 180 | { 181 | lastTickCount = pHeader->varBuf[latest].tickCount; 182 | return false; 183 | } 184 | // else the same, and nothing changed this tick 185 | } 186 | 187 | return false; 188 | } 189 | 190 | 191 | bool irsdk_waitForDataReady(int timeOut, char *data) 192 | { 193 | #ifdef _MSC_VER 194 | _ASSERTE(timeOut >= 0); 195 | #endif 196 | 197 | if(isInitialized || irsdk_startup()) 198 | { 199 | // just to be sure, check before we sleep 200 | if(irsdk_getNewData(data)) 201 | return true; 202 | 203 | // sleep till signaled 204 | WaitForSingleObject(hDataValidEvent, timeOut); 205 | 206 | // we woke up, so check for data 207 | if(irsdk_getNewData(data)) 208 | return true; 209 | else 210 | return false; 211 | } 212 | 213 | // sleep if error 214 | if(timeOut > 0) 215 | Sleep(timeOut); 216 | 217 | return false; 218 | } 219 | 220 | bool irsdk_isConnected() 221 | { 222 | if(isInitialized) 223 | { 224 | int elapsed = (int)difftime(time(NULL), lastValidTime); 225 | return (pHeader->status & irsdk_stConnected) > 0 && elapsed < timeout; 226 | } 227 | 228 | return false; 229 | } 230 | 231 | const irsdk_header *irsdk_getHeader() 232 | { 233 | if(isInitialized) 234 | { 235 | return pHeader; 236 | } 237 | 238 | return NULL; 239 | } 240 | 241 | // direct access to the data buffer 242 | // Warnign! This buffer is volitile so read it out fast! 243 | // Use the cached copy from irsdk_waitForDataReady() or irsdk_getNewData() instead 244 | const char *irsdk_getData(int index) 245 | { 246 | if(isInitialized) 247 | { 248 | return pSharedMem + pHeader->varBuf[index].bufOffset; 249 | } 250 | 251 | return NULL; 252 | } 253 | 254 | const char *irsdk_getSessionInfoStr() 255 | { 256 | if(isInitialized) 257 | { 258 | return pSharedMem + pHeader->sessionInfoOffset; 259 | } 260 | return NULL; 261 | } 262 | 263 | int irsdk_getSessionInfoStrUpdate() 264 | { 265 | if(isInitialized) 266 | { 267 | return pHeader->sessionInfoUpdate; 268 | } 269 | return -1; 270 | } 271 | 272 | const irsdk_varHeader *irsdk_getVarHeaderPtr() 273 | { 274 | if(isInitialized) 275 | { 276 | return ((irsdk_varHeader*)(pSharedMem + pHeader->varHeaderOffset)); 277 | } 278 | return NULL; 279 | } 280 | 281 | const irsdk_varHeader *irsdk_getVarHeaderEntry(int index) 282 | { 283 | if(isInitialized) 284 | { 285 | if(index >= 0 && index < pHeader->numVars) 286 | { 287 | return &((irsdk_varHeader*)(pSharedMem + pHeader->varHeaderOffset))[index]; 288 | } 289 | } 290 | return NULL; 291 | } 292 | 293 | // Note: this is a linear search, so cache the results 294 | int irsdk_varNameToIndex(const char *name) 295 | { 296 | const irsdk_varHeader *pVar; 297 | 298 | if(name) 299 | { 300 | for(int index=0; indexnumVars; index++) 301 | { 302 | pVar = irsdk_getVarHeaderEntry(index); 303 | if(pVar && 0 == strncmp(name, pVar->name, IRSDK_MAX_STRING)) 304 | { 305 | return index; 306 | } 307 | } 308 | } 309 | 310 | return -1; 311 | } 312 | 313 | int irsdk_varNameToOffset(const char *name) 314 | { 315 | const irsdk_varHeader *pVar; 316 | 317 | if(name) 318 | { 319 | for(int index=0; indexnumVars; index++) 320 | { 321 | pVar = irsdk_getVarHeaderEntry(index); 322 | if(pVar && 0 == strncmp(name, pVar->name, IRSDK_MAX_STRING)) 323 | { 324 | return pVar->offset; 325 | } 326 | } 327 | } 328 | 329 | return -1; 330 | } 331 | 332 | unsigned int irsdk_getBroadcastMsgID() 333 | { 334 | static unsigned int msgId = RegisterWindowMessage(IRSDK_BROADCASTMSGNAME); 335 | 336 | return msgId; 337 | } 338 | 339 | void irsdk_broadcastMsg(irsdk_BroadcastMsg msg, int var1, int var2, int var3) 340 | { 341 | irsdk_broadcastMsg(msg, var1, (int)MAKELONG(var2, var3)); 342 | } 343 | 344 | void irsdk_broadcastMsg(irsdk_BroadcastMsg msg, int var1, float var2) 345 | { 346 | // multiply by 2^16-1 to move fractional part to the integer part 347 | int real = (int)(var2 * 65536.0f); 348 | 349 | irsdk_broadcastMsg(msg, var1, real); 350 | } 351 | 352 | void irsdk_broadcastMsg(irsdk_BroadcastMsg msg, int var1, int var2) 353 | { 354 | static unsigned int msgId = irsdk_getBroadcastMsgID(); 355 | 356 | if(msgId && msg >= 0 && msg < irsdk_BroadcastLast) 357 | { 358 | SendNotifyMessage(HWND_BROADCAST, msgId, MAKELONG(msg, var1), var2); 359 | } 360 | } 361 | 362 | int irsdk_padCarNum(int num, int zero) 363 | { 364 | int retVal = num; 365 | int numPlace = 1; 366 | if(num > 99) 367 | numPlace = 3; 368 | else if(num > 9) 369 | numPlace = 2; 370 | if(zero) 371 | { 372 | numPlace += zero; 373 | retVal = num + 1000*numPlace; 374 | } 375 | 376 | return retVal; 377 | } 378 | -------------------------------------------------------------------------------- /OverlayInputs.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021-2022 L. E. Spalt 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #pragma once 26 | 27 | #include "Overlay.h" 28 | #include "Config.h" 29 | #include "OverlayDebug.h" 30 | 31 | class OverlayInputs : public Overlay 32 | { 33 | public: 34 | 35 | OverlayInputs(Microsoft::WRL::ComPtr d3dDevice) 36 | : Overlay("OverlayInputs", d3dDevice) 37 | {} 38 | 39 | protected: 40 | 41 | virtual float2 getDefaultSize() 42 | { 43 | return float2(400,100); 44 | } 45 | 46 | virtual void onConfigChanged() 47 | { 48 | // Width might have changed, reset tracker values 49 | m_throttleVtx.resize( m_width ); 50 | m_brakeVtx.resize( m_width ); 51 | m_clutchVtx.resize( m_width ); 52 | m_steerVtx.resize( m_width ); 53 | for( int i=0; ifloat2 { 99 | return float2( v.x+0.5f, h-0.5f*thickness - v.y*(h-thickness) ); 100 | }; 101 | 102 | // Throttle (fill) 103 | Microsoft::WRL::ComPtr throttleFillPath; 104 | Microsoft::WRL::ComPtr throttleFillSink; 105 | m_d2dFactory->CreatePathGeometry( &throttleFillPath ); 106 | throttleFillPath->Open( &throttleFillSink ); 107 | throttleFillSink->BeginFigure( float2(0,h), D2D1_FIGURE_BEGIN_FILLED ); 108 | for( int i=0; i<(int)m_throttleVtx.size(); ++i ) 109 | throttleFillSink->AddLine( vtx2coord(m_throttleVtx[i]) ); 110 | throttleFillSink->AddLine( float2(m_throttleVtx[m_throttleVtx.size()-1].x+0.5f,h) ); 111 | throttleFillSink->EndFigure( D2D1_FIGURE_END_OPEN ); 112 | throttleFillSink->Close(); 113 | 114 | // Brake (fill) 115 | Microsoft::WRL::ComPtr brakeFillPath; 116 | Microsoft::WRL::ComPtr brakeFillSink; 117 | m_d2dFactory->CreatePathGeometry( &brakeFillPath ); 118 | brakeFillPath->Open( &brakeFillSink ); 119 | brakeFillSink->BeginFigure( float2(0,h), D2D1_FIGURE_BEGIN_FILLED ); 120 | for( int i=0; i<(int)m_brakeVtx.size(); ++i ) 121 | brakeFillSink->AddLine( vtx2coord(m_brakeVtx[i]) ); 122 | brakeFillSink->AddLine( float2(m_brakeVtx[m_brakeVtx.size()-1].x+0.5f,h) ); 123 | brakeFillSink->EndFigure( D2D1_FIGURE_END_OPEN ); 124 | brakeFillSink->Close(); 125 | 126 | // Clutch (fill) 127 | Microsoft::WRL::ComPtr clutchFillPath; 128 | Microsoft::WRL::ComPtr clutchFillSink; 129 | m_d2dFactory->CreatePathGeometry( &clutchFillPath ); 130 | clutchFillPath->Open( &clutchFillSink ); 131 | clutchFillSink->BeginFigure( float2(0,h), D2D1_FIGURE_BEGIN_FILLED ); 132 | for( int i=0; i<(int)m_clutchVtx.size(); ++i ) 133 | clutchFillSink->AddLine( vtx2coord(m_clutchVtx[i]) ); 134 | clutchFillSink->AddLine( float2(m_clutchVtx[m_clutchVtx.size()-1].x+0.5f,h) ); 135 | clutchFillSink->EndFigure( D2D1_FIGURE_END_OPEN ); 136 | clutchFillSink->Close(); 137 | 138 | // Throttle (line) 139 | Microsoft::WRL::ComPtr throttleLinePath; 140 | Microsoft::WRL::ComPtr throttleLineSink; 141 | m_d2dFactory->CreatePathGeometry( &throttleLinePath ); 142 | throttleLinePath->Open( &throttleLineSink ); 143 | throttleLineSink->BeginFigure( vtx2coord(m_throttleVtx[0]), D2D1_FIGURE_BEGIN_HOLLOW ); 144 | for( int i=1; i<(int)m_throttleVtx.size(); ++i ) 145 | throttleLineSink->AddLine( vtx2coord(m_throttleVtx[i]) ); 146 | throttleLineSink->EndFigure( D2D1_FIGURE_END_OPEN ); 147 | throttleLineSink->Close(); 148 | 149 | // Brake (line) 150 | Microsoft::WRL::ComPtr brakeLinePath; 151 | Microsoft::WRL::ComPtr brakeLineSink; 152 | m_d2dFactory->CreatePathGeometry( &brakeLinePath ); 153 | brakeLinePath->Open( &brakeLineSink ); 154 | brakeLineSink->BeginFigure( vtx2coord(m_brakeVtx[0]), D2D1_FIGURE_BEGIN_HOLLOW ); 155 | for( int i=1; i<(int)m_brakeVtx.size(); ++i ) 156 | brakeLineSink->AddLine( vtx2coord(m_brakeVtx[i]) ); 157 | brakeLineSink->EndFigure( D2D1_FIGURE_END_OPEN ); 158 | brakeLineSink->Close(); 159 | 160 | // Clutch (line) 161 | Microsoft::WRL::ComPtr clutchLinePath; 162 | Microsoft::WRL::ComPtr clutchLineSink; 163 | m_d2dFactory->CreatePathGeometry( &clutchLinePath ); 164 | clutchLinePath->Open( &clutchLineSink ); 165 | clutchLineSink->BeginFigure( vtx2coord(m_clutchVtx[0]), D2D1_FIGURE_BEGIN_HOLLOW ); 166 | for( int i=1; i<(int)m_clutchVtx.size(); ++i ) 167 | clutchLineSink->AddLine( vtx2coord(m_clutchVtx[i]) ); 168 | clutchLineSink->EndFigure( D2D1_FIGURE_END_OPEN ); 169 | clutchLineSink->Close(); 170 | 171 | // Steering 172 | Microsoft::WRL::ComPtr steeringLinePath; 173 | Microsoft::WRL::ComPtr steeringLineSink; 174 | m_d2dFactory->CreatePathGeometry( &steeringLinePath ); 175 | steeringLinePath->Open( &steeringLineSink ); 176 | steeringLineSink->BeginFigure( vtx2coord(m_steerVtx[0]), D2D1_FIGURE_BEGIN_HOLLOW ); 177 | for( int i=1; i<(int)m_steerVtx.size(); ++i ) 178 | steeringLineSink->AddLine( vtx2coord(m_steerVtx[i]) ); 179 | steeringLineSink->EndFigure( D2D1_FIGURE_END_OPEN ); 180 | steeringLineSink->Close(); 181 | 182 | m_renderTarget->BeginDraw(); 183 | m_brush->SetColor( g_cfg.getFloat4( m_name, "throttle_fill_col", float4(0.2f,0.45f,0.15f,0.6f) ) ); 184 | m_renderTarget->FillGeometry( throttleFillPath.Get(), m_brush.Get() ); 185 | m_brush->SetColor( g_cfg.getFloat4( m_name, "brake_fill_col", float4(0.46f,0.01f,0.06f,0.6f) ) ); 186 | m_renderTarget->FillGeometry( brakeFillPath.Get(), m_brush.Get() ); 187 | m_brush->SetColor( g_cfg.getFloat4( m_name, "clutch_fill_col", float4(0.0f,0.01f,0.46f,0.6f) ) ); 188 | m_renderTarget->FillGeometry( clutchFillPath.Get(), m_brush.Get() ); 189 | m_brush->SetColor( g_cfg.getFloat4( m_name, "throttle_col", float4(0.38f,0.91f,0.31f,0.8f) ) ); 190 | m_renderTarget->DrawGeometry( throttleLinePath.Get(), m_brush.Get(), thickness ); 191 | m_brush->SetColor( g_cfg.getFloat4( m_name, "brake_col", float4(0.93f,0.03f,0.13f,0.8f) ) ); 192 | m_renderTarget->DrawGeometry( brakeLinePath.Get(), m_brush.Get(), thickness ); 193 | m_brush->SetColor( g_cfg.getFloat4( m_name, "clutch_col", float4(0.0f,0.03f,0.93f,0.8f) ) ); 194 | m_renderTarget->DrawGeometry( clutchLinePath.Get(), m_brush.Get(), thickness ); 195 | m_brush->SetColor( g_cfg.getFloat4( m_name, "steering_col", float4(1,1,1,0.3f) ) ); 196 | m_renderTarget->DrawGeometry( steeringLinePath.Get(), m_brush.Get(), thickness ); 197 | m_renderTarget->EndDraw(); 198 | } 199 | 200 | virtual bool canEnableWhileNotDriving() const 201 | { 202 | // Show only while watching a replay (Session type is unknown while in a replay. Maybe there is a better way to detect it) 203 | return ir_session.isReplay; 204 | } 205 | 206 | protected: 207 | 208 | std::vector m_throttleVtx; 209 | std::vector m_brakeVtx; 210 | std::vector m_clutchVtx; 211 | std::vector m_steerVtx; 212 | bool doUpdate = true; 213 | }; 214 | -------------------------------------------------------------------------------- /irsdk/irsdk_client.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013, iRacing.com Motorsport Simulations, LLC. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of iRacing.com Motorsport Simulations nor the 13 | names of its contributors may be used to endorse or promote products 14 | derived from this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | 31 | #include 32 | #include "irsdk_defines.h" 33 | #include "yaml_parser.h" 34 | #include "irsdk_client.h" 35 | 36 | #pragma warning(disable:4996) 37 | 38 | irsdkClient& irsdkClient::instance() 39 | { 40 | static irsdkClient INSTANCE; 41 | return INSTANCE; 42 | } 43 | 44 | bool irsdkClient::waitForData(int timeoutMS) 45 | { 46 | // wait for start of session or new data 47 | if(irsdk_waitForDataReady(timeoutMS, m_data) && irsdk_getHeader()) 48 | { 49 | // if new connection, or data changed lenght then init 50 | if(!m_data || m_nData != irsdk_getHeader()->bufLen) 51 | { 52 | // allocate memory to hold incoming data from sim 53 | if(m_data) delete [] m_data; 54 | m_nData = irsdk_getHeader()->bufLen; 55 | m_data = new char[m_nData]; 56 | 57 | // indicate a new connection 58 | m_statusID++; 59 | 60 | // reset session info str status 61 | m_lastSessionCt = -1; 62 | 63 | // and try to fill in the data 64 | if(irsdk_getNewData(m_data)) 65 | return true; 66 | } 67 | else if(m_data) 68 | { 69 | // else we are allready initialized, and data is ready for processing 70 | return true; 71 | } 72 | } 73 | else if(!isConnected()) 74 | { 75 | // else session ended 76 | if(m_data) 77 | delete[] m_data; 78 | m_data = NULL; 79 | 80 | // reset session info str status 81 | m_lastSessionCt = -1; 82 | } 83 | 84 | return false; 85 | } 86 | 87 | void irsdkClient::shutdown() 88 | { 89 | irsdk_shutdown(); 90 | if(m_data) 91 | delete[] m_data; 92 | m_data = NULL; 93 | 94 | // reset session info str status 95 | m_lastSessionCt = -1; 96 | } 97 | 98 | bool irsdkClient::isConnected() 99 | { 100 | return m_data != NULL && irsdk_isConnected(); 101 | } 102 | 103 | int irsdkClient::getVarIdx(const char*name) 104 | { 105 | if(isConnected()) 106 | { 107 | return irsdk_varNameToIndex(name); 108 | } 109 | 110 | return -1; 111 | } 112 | 113 | int /*irsdk_VarType*/ irsdkClient::getVarType(int idx) 114 | { 115 | if(isConnected()) 116 | { 117 | const irsdk_varHeader *vh = irsdk_getVarHeaderEntry(idx); 118 | if(vh) 119 | { 120 | return vh->type; 121 | } 122 | else 123 | { 124 | //invalid variable index 125 | assert(false); 126 | } 127 | } 128 | 129 | return irsdk_char; 130 | } 131 | 132 | int irsdkClient::getVarCount(int idx) 133 | { 134 | if(isConnected()) 135 | { 136 | const irsdk_varHeader *vh = irsdk_getVarHeaderEntry(idx); 137 | if(vh) 138 | { 139 | return vh->count; 140 | } 141 | else 142 | { 143 | //invalid variable index 144 | assert(false); 145 | } 146 | } 147 | 148 | return 0; 149 | } 150 | 151 | bool irsdkClient::getVarBool(int idx, int entry) 152 | { 153 | if(isConnected()) 154 | { 155 | const irsdk_varHeader *vh = irsdk_getVarHeaderEntry(idx); 156 | if(vh) 157 | { 158 | if(entry >= 0 && entry < vh->count) 159 | { 160 | const char * data = m_data + vh->offset; 161 | switch(vh->type) 162 | { 163 | // 1 byte 164 | case irsdk_char: 165 | case irsdk_bool: 166 | return (((const char*)data)[entry]) != 0; 167 | break; 168 | 169 | // 4 bytes 170 | case irsdk_int: 171 | case irsdk_bitField: 172 | return (((const int*)data)[entry]) != 0; 173 | break; 174 | 175 | // test float/double for greater than 1.0 so that 176 | // we have a chance of this being usefull 177 | // technically there is no right conversion... 178 | case irsdk_float: 179 | return (((const float*)data)[entry]) >= 1.0f; 180 | break; 181 | 182 | // 8 bytes 183 | case irsdk_double: 184 | return (((const double*)data)[entry]) >= 1.0; 185 | break; 186 | } 187 | } 188 | else 189 | { 190 | // invalid offset 191 | assert(false); 192 | } 193 | } 194 | else 195 | { 196 | //invalid variable index 197 | assert(false); 198 | } 199 | } 200 | 201 | return false; 202 | } 203 | 204 | int irsdkClient::getVarInt(int idx, int entry) 205 | { 206 | if(isConnected()) 207 | { 208 | const irsdk_varHeader *vh = irsdk_getVarHeaderEntry(idx); 209 | if(vh) 210 | { 211 | if(entry >= 0 && entry < vh->count) 212 | { 213 | const char * data = m_data + vh->offset; 214 | switch(vh->type) 215 | { 216 | // 1 byte 217 | case irsdk_char: 218 | case irsdk_bool: 219 | return (int)(((const char*)data)[entry]); 220 | break; 221 | 222 | // 4 bytes 223 | case irsdk_int: 224 | case irsdk_bitField: 225 | return (int)(((const int*)data)[entry]); 226 | break; 227 | 228 | case irsdk_float: 229 | return (int)(((const float*)data)[entry]); 230 | break; 231 | 232 | // 8 bytes 233 | case irsdk_double: 234 | return (int)(((const double*)data)[entry]); 235 | break; 236 | } 237 | } 238 | else 239 | { 240 | // invalid offset 241 | assert(false); 242 | } 243 | } 244 | else 245 | { 246 | //invalid variable index 247 | assert(false); 248 | } 249 | } 250 | 251 | return 0; 252 | } 253 | 254 | float irsdkClient::getVarFloat(int idx, int entry) 255 | { 256 | if(isConnected()) 257 | { 258 | const irsdk_varHeader *vh = irsdk_getVarHeaderEntry(idx); 259 | if(vh) 260 | { 261 | if(entry >= 0 && entry < vh->count) 262 | { 263 | const char * data = m_data + vh->offset; 264 | switch(vh->type) 265 | { 266 | // 1 byte 267 | case irsdk_char: 268 | case irsdk_bool: 269 | return (float)(((const char*)data)[entry]); 270 | break; 271 | 272 | // 4 bytes 273 | case irsdk_int: 274 | case irsdk_bitField: 275 | return (float)(((const int*)data)[entry]); 276 | break; 277 | 278 | case irsdk_float: 279 | return (float)(((const float*)data)[entry]); 280 | break; 281 | 282 | // 8 bytes 283 | case irsdk_double: 284 | return (float)(((const double*)data)[entry]); 285 | break; 286 | } 287 | } 288 | else 289 | { 290 | // invalid offset 291 | assert(false); 292 | } 293 | } 294 | else 295 | { 296 | //invalid variable index 297 | assert(false); 298 | } 299 | } 300 | 301 | return 0.0f; 302 | } 303 | 304 | double irsdkClient::getVarDouble(int idx, int entry) 305 | { 306 | if(isConnected()) 307 | { 308 | const irsdk_varHeader *vh = irsdk_getVarHeaderEntry(idx); 309 | if(vh) 310 | { 311 | if(entry >= 0 && entry < vh->count) 312 | { 313 | const char * data = m_data + vh->offset; 314 | switch(vh->type) 315 | { 316 | // 1 byte 317 | case irsdk_char: 318 | case irsdk_bool: 319 | return (double)(((const char*)data)[entry]); 320 | break; 321 | 322 | // 4 bytes 323 | case irsdk_int: 324 | case irsdk_bitField: 325 | return (double)(((const int*)data)[entry]); 326 | break; 327 | 328 | case irsdk_float: 329 | return (double)(((const float*)data)[entry]); 330 | break; 331 | 332 | // 8 bytes 333 | case irsdk_double: 334 | return (double)(((const double*)data)[entry]); 335 | break; 336 | } 337 | } 338 | else 339 | { 340 | // invalid offset 341 | assert(false); 342 | } 343 | } 344 | else 345 | { 346 | //invalid variable index 347 | assert(false); 348 | } 349 | } 350 | 351 | return 0.0; 352 | } 353 | 354 | //path is in the form of "DriverInfo:Drivers:CarIdx:{%d}UserName:" 355 | int irsdkClient::getSessionStrVal(const char *path, char *val, int valLen) 356 | { 357 | if(isConnected() && path && val && valLen > 0) 358 | { 359 | // track changes in string 360 | m_lastSessionCt = getSessionCt(); 361 | 362 | const char *tVal = NULL; 363 | int tValLen = 0; 364 | if(parseYaml(irsdk_getSessionInfoStr(), path, &tVal, &tValLen)) 365 | { 366 | // dont overflow out buffer 367 | int len = tValLen; 368 | if(len > valLen) 369 | len = valLen; 370 | 371 | // copy what we can, even if buffer too small 372 | memcpy(val, tVal, len); 373 | val[len] = '\0'; // origional string has no null termination... 374 | 375 | // if buffer was big enough, return success 376 | if(valLen >= tValLen) 377 | return 1; 378 | else // return size of buffer needed 379 | return -tValLen; 380 | } 381 | } 382 | 383 | return 0; 384 | } 385 | 386 | // get the whole string 387 | const char* irsdkClient::getSessionStr() 388 | { 389 | if(isConnected()) 390 | { 391 | m_lastSessionCt = getSessionCt(); 392 | return irsdk_getSessionInfoStr(); 393 | } 394 | 395 | return NULL; 396 | } 397 | 398 | 399 | //---------------------------------- 400 | 401 | irsdkCVar::irsdkCVar() 402 | : m_idx(-1) 403 | , m_statusID(-1) 404 | { 405 | m_name[0] = '\0'; 406 | } 407 | 408 | irsdkCVar::irsdkCVar(const char *name) 409 | { 410 | m_name[0] = '\0'; 411 | setVarName(name); 412 | } 413 | 414 | void irsdkCVar::setVarName(const char *name) 415 | { 416 | if(!name || 0 != strncmp(name, m_name, sizeof(m_name))) 417 | { 418 | m_idx = -1; 419 | m_statusID = -1; 420 | 421 | if(name) 422 | { 423 | strncpy(m_name, name, max_string); 424 | m_name[max_string-1] = '\0'; 425 | } 426 | else 427 | m_name[0] = '\0'; 428 | } 429 | } 430 | 431 | bool irsdkCVar::checkIdx() 432 | { 433 | if(irsdkClient::instance().isConnected()) 434 | { 435 | if(m_statusID != irsdkClient::instance().getStatusID()) 436 | { 437 | m_statusID = irsdkClient::instance().getStatusID(); 438 | m_idx = irsdkClient::instance().getVarIdx(m_name); 439 | } 440 | 441 | return (m_idx != -1); 442 | } 443 | 444 | return false; 445 | } 446 | 447 | int /*irsdk_VarType*/ irsdkCVar::getType() 448 | { 449 | if(checkIdx()) 450 | return irsdkClient::instance().getVarType(m_idx); 451 | return 0; 452 | } 453 | 454 | int irsdkCVar::getCount() 455 | { 456 | if(checkIdx()) 457 | return irsdkClient::instance().getVarCount(m_idx); 458 | return 0; 459 | } 460 | 461 | bool irsdkCVar::isValid() 462 | { 463 | checkIdx(); 464 | return (m_idx > -1); 465 | } 466 | 467 | 468 | bool irsdkCVar::getBool(int entry) 469 | { 470 | if(checkIdx()) 471 | return irsdkClient::instance().getVarBool(m_idx, entry); 472 | return false; 473 | } 474 | 475 | int irsdkCVar::getInt(int entry) 476 | { 477 | if(checkIdx()) 478 | return irsdkClient::instance().getVarInt(m_idx, entry); 479 | return 0; 480 | } 481 | 482 | float irsdkCVar::getFloat(int entry) 483 | { 484 | if(checkIdx()) 485 | return irsdkClient::instance().getVarFloat(m_idx, entry); 486 | return 0.0f; 487 | } 488 | 489 | double irsdkCVar::getDouble(int entry) 490 | { 491 | if(checkIdx()) 492 | return irsdkClient::instance().getVarDouble(m_idx, entry); 493 | return 0.0; 494 | } 495 | 496 | -------------------------------------------------------------------------------- /OverlayRadar.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021-2022 L. E. Spalt, Diego Schneider 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to w5m the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #pragma once 26 | 27 | #include 28 | #include "Overlay.h" 29 | #include "Config.h" 30 | #include "OverlayDebug.h" 31 | 32 | static struct CarInfo { 33 | int carIdx = 0; 34 | float deltaMts = 0; 35 | int carID = 0; 36 | }; 37 | 38 | class OverlayRadar : public Overlay 39 | { 40 | public: 41 | 42 | OverlayRadar(Microsoft::WRL::ComPtr d3dDevice) 43 | : Overlay("OverlayRadar", d3dDevice) 44 | {} 45 | 46 | protected: 47 | 48 | virtual float2 getDefaultSize() 49 | { 50 | return float2(400, 400); 51 | } 52 | 53 | virtual void onConfigChanged() 54 | { 55 | 56 | } 57 | 58 | virtual void onUpdate() 59 | { 60 | const float w = (float)m_width; 61 | const float h = (float)m_height; 62 | 63 | const float cornerRadius = g_cfg.getFloat(m_name, "corner_radius", 2.0f); 64 | const float markerWidth = g_cfg.getFloat(m_name, "marker_width", 20.0f); 65 | 66 | std::vector radarInfo; 67 | radarInfo.reserve(IR_MAX_CARS); 68 | const float selfLapDistPct = ir_LapDistPct.getFloat(); 69 | const float trackLength = ir_LapDist.getFloat() / selfLapDistPct; 70 | const float maxDist = g_cfg.getFloat(m_name, "max_distance", 7.0f); 71 | 72 | // Populate RadarInfo 73 | for (int i = 0; i < IR_MAX_CARS; ++i) 74 | { 75 | const Car& car = ir_session.cars[i]; 76 | const int lapcountCar = ir_CarIdxLap.getInt(i); 77 | 78 | // Ignore pace car and cars in pits 79 | if (lapcountCar >= 0 && !car.isSpectator && car.carNumber >= 0 && !car.isPaceCar && !ir_CarIdxOnPitRoad.getBool(i)) 80 | { 81 | const float carLapDistPct = ir_CarIdxLapDistPct.getFloat(i); 82 | const bool wrap = fabsf(selfLapDistPct - carLapDistPct) > 0.5f; 83 | float lapDistPctDelta = selfLapDistPct - carLapDistPct; 84 | 85 | // Account for start/finish line 86 | if (wrap) { 87 | if (selfLapDistPct > carLapDistPct) { 88 | lapDistPctDelta -= 1; 89 | } 90 | else { 91 | lapDistPctDelta += 1; 92 | } 93 | } 94 | const float deltaMts = lapDistPctDelta * trackLength; 95 | 96 | // Filter the list, we dont care about far away cars 97 | // TODO: 5.0f? Shouldn't be by largest car length or something like that? 98 | if (fabs(deltaMts) < maxDist + 5.0f) 99 | radarInfo.emplace_back(i, deltaMts, car.carID); 100 | 101 | } 102 | } 103 | 104 | std::sort(radarInfo.begin(), radarInfo.end(), 105 | [](const CarInfo& a, const CarInfo& b) {return a.deltaMts > b.deltaMts;}); 106 | 107 | // Locate our driver's index in the new array, and nearest Ahead/Behind 108 | int selfRadarInfoIdx = -1, nearAhead = -1, nearBehind = -1; 109 | 110 | for (int i = 0; i < (int)radarInfo.size(); ++i) 111 | { 112 | const CarInfo ci = radarInfo[i]; 113 | if (ci.carIdx == ir_session.driverCarIdx) 114 | { 115 | selfRadarInfoIdx = i; 116 | 117 | if (i > 0) { 118 | nearBehind = i - 1; 119 | } 120 | if (i + 1 < (int)radarInfo.size()) { 121 | nearAhead = i + 1; 122 | } 123 | } 124 | } 125 | 126 | float nearAheadDeltaMts = 0; 127 | if (nearAhead != -1 ) { 128 | nearAheadDeltaMts = radarInfo[nearAhead].deltaMts; 129 | } 130 | dbg("Nearahead: %d - %f ", nearAhead, nearAheadDeltaMts); 131 | 132 | // Something's wrong if we didn't find our driver. Bail. 133 | if (selfRadarInfoIdx < 0) 134 | return; 135 | 136 | // Update car lengths if just cleared 137 | check_update_car_lengths(radarInfo, selfRadarInfoIdx, nearAhead, nearBehind); 138 | 139 | std::string s = "CarLen: "; 140 | s.reserve(256); 141 | for (const auto carLenData : m_carLength) { 142 | char t[32]; 143 | snprintf(t, sizeof(t), "%d: %f - ", carLenData.first, carLenData.second); 144 | s += t; 145 | } 146 | dbg(s.c_str()); 147 | 148 | D2D1_ROUNDED_RECT lRect, rRect; 149 | int carsNear = 0; 150 | const int carLeftRight = ir_CarLeftRight.getInt(); 151 | 152 | m_renderTarget->BeginDraw(); 153 | for (const CarInfo ci : radarInfo) { 154 | 155 | const float carLength = max(m_carLength[ci.carID], 4.0f); 156 | 157 | if (fabsf(ci.deltaMts) > maxDist+carLength || ci.deltaMts == 0) 158 | continue; 159 | 160 | carsNear++; 161 | 162 | const float rect_top = calculate_radar_Y(ci.deltaMts, maxDist); 163 | const float rect_bot = calculate_radar_Y(ci.deltaMts+carLength, maxDist); 164 | dbg("Deltamts: %f", ci.deltaMts); 165 | 166 | // Left side 167 | lRect.rect = { 0, h * rect_top, markerWidth, h * rect_bot }; 168 | lRect.radiusX = cornerRadius; 169 | lRect.radiusY = cornerRadius; 170 | 171 | // Right side 172 | rRect.rect = { w - markerWidth, h * rect_top, w, h * rect_bot }; 173 | rRect.radiusX = cornerRadius; 174 | rRect.radiusY = cornerRadius; 175 | 176 | m_brush->SetColor(g_cfg.getFloat4(m_name, "car_near_fill_col", float4(1.0f, 0.2f, 0.0f, 0.5f))); 177 | 178 | // Paint left 179 | switch (carLeftRight) { 180 | case irsdk_LRCarLeft: 181 | case irsdk_LR2CarsLeft: 182 | case irsdk_LRCarLeftRight: 183 | m_brush->SetColor(g_cfg.getFloat4(m_name, "car_near_fill_col", float4(1.0f, 0.2f, 0.0f, 0.5f))); 184 | break; 185 | default: 186 | m_brush->SetColor(g_cfg.getFloat4(m_name, "car_far_fill_col", float4(1.0f, 1.0f, 0.0f, 0.5f))); 187 | } 188 | m_renderTarget->FillRoundedRectangle(&lRect, m_brush.Get()); 189 | 190 | switch (carLeftRight) { 191 | case irsdk_LRCarRight: 192 | case irsdk_LR2CarsRight: 193 | case irsdk_LRCarLeftRight: 194 | m_brush->SetColor(g_cfg.getFloat4(m_name, "car_near_fill_col", float4(1.0f, 0.2f, 0.0f, 0.5f))); 195 | break; 196 | default: 197 | m_brush->SetColor(g_cfg.getFloat4(m_name, "car_far_fill_col", float4(1.0f, 1.0f, 0.0f, 0.5f))); 198 | } 199 | m_renderTarget->FillRoundedRectangle(&rRect, m_brush.Get()); 200 | } 201 | 202 | if (carsNear > 0 || true) { 203 | D2D1_RECT_F lRect, rRect; 204 | 205 | // Car limits Marks 206 | float carLimitsMarkLen = g_cfg.getFloat(m_name, "car_limits_mark_len", 10.0f); 207 | float rect_top = calculate_radar_Y(0+carLimitsMarkLen, maxDist); 208 | float rect_bot = calculate_radar_Y(0, maxDist); 209 | m_brush->SetColor(g_cfg.getFloat4(m_name, "car_limits_fill_col", float4(0.2f, 0.2f, 0.2f, 0.8f))); 210 | 211 | // Left side 212 | lRect = D2D1::RectF(0, h * rect_top, markerWidth, h * rect_bot); 213 | // Right side 214 | rRect = D2D1::RectF(w - markerWidth, h * rect_top, w, h * rect_bot); 215 | 216 | m_renderTarget->FillRectangle(&lRect, m_brush.Get()); 217 | m_renderTarget->FillRectangle(&rRect, m_brush.Get()); 218 | 219 | 220 | const float selfLen = m_carLength[radarInfo[selfRadarInfoIdx].carID]; 221 | rect_top = calculate_radar_Y( selfLen, maxDist); 222 | rect_bot = calculate_radar_Y(selfLen+carLimitsMarkLen, maxDist); 223 | // Left side 224 | lRect = D2D1::RectF(0, h * rect_top, markerWidth, h * rect_bot); 225 | // Right side 226 | rRect = D2D1::RectF(w - markerWidth, h * rect_top, w, h * rect_bot); 227 | 228 | m_renderTarget->FillRectangle(&lRect, m_brush.Get()); 229 | m_renderTarget->FillRectangle(&rRect, m_brush.Get()); 230 | } 231 | 232 | m_renderTarget->EndDraw(); 233 | } 234 | 235 | // Uhm... isn't the deque ordered now? 236 | float calculate_median_from_deque(const std::deque& deque) { 237 | 238 | std::vector tmpVec; 239 | tmpVec.reserve((int)deque.size()); 240 | for (float v : deque) { 241 | tmpVec.push_back(v); 242 | } 243 | 244 | auto m = tmpVec.begin() + tmpVec.size() / 2; 245 | std::nth_element(tmpVec.begin(), m, tmpVec.end()); 246 | return tmpVec[tmpVec.size() / 2]; 247 | } 248 | 249 | // This uses -value as the coords are top to bottom! 250 | float calculate_radar_Y(float value, float maxDist) { 251 | const float carOffset = g_cfg.getFloat(m_name, "car_offset", 2.0f); 252 | const float clamp_max = maxDist - carOffset; 253 | const float clamp_min = -maxDist - carOffset; 254 | return min(max(0.0f + (1.0f / (clamp_max - clamp_min)) * (value - clamp_min), 0.0f), 1.0f); 255 | //output = output_start + ((output_end - output_start) / (input_end - input_start)) * (input - input_start) 256 | 257 | //return min(max(1.0f + (-1.0f / (2 * clamp)) * (-value + clamp), 0.0f), 1.0f); 258 | } 259 | 260 | void update_car_length(int carID, float deltaMts) { 261 | 262 | const int nearestClearQueueSize = g_cfg.getInt(m_name, "nearest_clear_queue_size", 5); 263 | if (deltaMts > 1.5f && deltaMts < 5.5f) { 264 | std::cout << "Updating! "; 265 | m_carLengthCalculationData[carID].push_back(deltaMts); 266 | std::sort(m_carLengthCalculationData[carID].begin(), m_carLengthCalculationData[carID].end(), 267 | [](const float a, const float b) {return a > b;}); 268 | float carLen = m_carLength[carID]; 269 | 270 | while (m_carLengthCalculationData[carID].size() > nearestClearQueueSize) { 271 | printf("Dropping: F:%f B:%f ", m_carLengthCalculationData[carID].front(), m_carLengthCalculationData[carID].back()); 272 | m_carLengthCalculationData[carID].pop_front(); 273 | m_carLengthCalculationData[carID].pop_back(); 274 | } 275 | 276 | 277 | carLen = calculate_median_from_deque(m_carLengthCalculationData[carID]); 278 | m_carLength[carID] = carLen; 279 | printf(" new carLength: %f", carLen); 280 | } 281 | } 282 | 283 | void check_update_car_lengths(const std::vector radarInfo, int selfIdx, int aheadIdx, int behindIdx) { 284 | 285 | const int selfCarID = radarInfo[selfIdx].carID; 286 | const int carLeftRight = ir_CarLeftRight.getInt(); 287 | 288 | if (carLeftRight == irsdk_LRClear) { 289 | if (!m_areWeClear) { 290 | m_areWeClear = true; 291 | // Just got cleared. Calc!! 292 | 293 | // We got a car ahead 294 | if (aheadIdx != -1) { 295 | 296 | std::cout << "Checking len ahead... "; 297 | 298 | // ahead == negative numbers // skip too low values, maybe a spinout 299 | const float deltaMts = -radarInfo[aheadIdx].deltaMts; 300 | const int carID = radarInfo[aheadIdx].carID; 301 | printf("%d: %f - ", carID, deltaMts); 302 | update_car_length(carID, deltaMts); 303 | std::cout << std::endl; 304 | } 305 | 306 | // We got a car behind, this updates OUR car! 307 | if (behindIdx != -1) { 308 | std::cout << "Checking len behind..."; 309 | // behind == positive numbers // skip too low values, maybe a spinout 310 | const float deltaMts = radarInfo[behindIdx].deltaMts; 311 | printf("%d: %f - ", selfCarID,deltaMts); 312 | update_car_length(selfCarID, deltaMts); 313 | std::cout << std::endl; 314 | } 315 | 316 | } 317 | } 318 | else { 319 | // Should we track cars here, such that we can pre-empt if we are clearing them from ahead/behind? 320 | // That would be, at least, cool. 321 | // And would let us get a good first read on car lengths (if we don't hardcode the car lengths eventually) 322 | 323 | m_areWeClear = false; 324 | } 325 | 326 | 327 | } 328 | 329 | protected: 330 | 331 | bool m_areWeClear = true; 332 | std::map > m_carLengthCalculationData; 333 | std::map m_carLength; 334 | #pragma message ("WARNING: We never free the m_carLength and m_carLengthCalculationData std::map's") 335 | }; 336 | -------------------------------------------------------------------------------- /iron.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release - DebugLoopPerformance 10 | Win32 11 | 12 | 13 | Release - DebugLoopPerformance 14 | x64 15 | 16 | 17 | Release 18 | Win32 19 | 20 | 21 | Debug 22 | x64 23 | 24 | 25 | Release 26 | x64 27 | 28 | 29 | 30 | 16.0 31 | Win32Proj 32 | {74cc4f41-58b4-4b1c-8f5d-19abadce940b} 33 | iron 34 | 10.0 35 | 36 | 37 | 38 | Application 39 | true 40 | v143 41 | MultiByte 42 | 43 | 44 | Application 45 | false 46 | v143 47 | true 48 | MultiByte 49 | 50 | 51 | Application 52 | false 53 | v143 54 | true 55 | MultiByte 56 | 57 | 58 | Application 59 | true 60 | v143 61 | MultiByte 62 | 63 | 64 | Application 65 | false 66 | v143 67 | true 68 | MultiByte 69 | 70 | 71 | Application 72 | false 73 | v143 74 | true 75 | MultiByte 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | true 103 | 104 | 105 | false 106 | 107 | 108 | false 109 | 110 | 111 | true 112 | 113 | 114 | false 115 | 116 | 117 | false 118 | 119 | 120 | 121 | Level3 122 | true 123 | NOMINMAX;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 124 | true 125 | 126 | 127 | Console 128 | true 129 | dwmapi.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 130 | 131 | 132 | 133 | 134 | Level3 135 | true 136 | true 137 | true 138 | NOMINMAX;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 139 | true 140 | 141 | 142 | Console 143 | true 144 | true 145 | true 146 | dwmapi.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 147 | true 148 | 149 | 150 | 151 | 152 | Level3 153 | true 154 | true 155 | true 156 | NOMINMAX;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 157 | true 158 | 159 | 160 | Console 161 | true 162 | true 163 | true 164 | dwmapi.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 165 | true 166 | 167 | 168 | 169 | 170 | Level3 171 | true 172 | PICOJSON_USE_RVALUE_REFERENCE=0;NOMINMAX;_CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 173 | true 174 | MultiThreadedDebug 175 | stdcpp20 176 | 177 | 178 | Console 179 | true 180 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 181 | 182 | 183 | 184 | 185 | Level3 186 | true 187 | true 188 | true 189 | PICOJSON_USE_RVALUE_REFERENCE=0;NOMINMAX;_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 190 | true 191 | MultiThreaded 192 | stdcpp20 193 | 194 | 195 | Console 196 | true 197 | true 198 | true 199 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 200 | true 201 | 202 | 203 | 204 | 205 | Level3 206 | true 207 | true 208 | true 209 | PICOJSON_USE_RVALUE_REFERENCE=0;NOMINMAX;_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;DEBUG_OVERLAY_TIME;%(PreprocessorDefinitions) 210 | true 211 | MultiThreaded 212 | stdcpp20 213 | 214 | 215 | Console 216 | true 217 | true 218 | true 219 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 220 | true 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | -------------------------------------------------------------------------------- /Overlay.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021-2022 L. E. Spalt 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | 26 | #include 27 | #include 28 | #include "Overlay.h" 29 | #include "Config.h" 30 | 31 | #if defined(_DEBUG) or defined(DEBUG_OVERLAY_TIME) 32 | #include "OverlayDebug.h" 33 | #endif 34 | using namespace Microsoft::WRL; 35 | 36 | static const int ResizeBorderWidth = 25; 37 | static const int EditStyle = WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_NOREDIRECTIONBITMAP; 38 | static const int DefaultStyle = EditStyle | WS_EX_LAYERED | WS_EX_TRANSPARENT; 39 | 40 | static LRESULT CALLBACK windowProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) 41 | { 42 | Overlay* o = (Overlay*)GetWindowLongPtr( hwnd, GWLP_USERDATA ); 43 | 44 | if( !o || !o->isUiEditEnabled() ) 45 | return DefWindowProc( hwnd, msg, wparam, lparam ); 46 | 47 | switch( msg ) 48 | { 49 | // handle moving/resizing 50 | case WM_NCHITTEST: 51 | { 52 | LRESULT hit = DefWindowProc( hwnd, msg, wparam, lparam ); 53 | if( hit == HTCLIENT ) 54 | { 55 | // check if we hit the corner area of the window to allow resizing despite having no border 56 | RECT r; 57 | GetWindowRect( hwnd, &r ); 58 | const int cur_x = GET_X_LPARAM( lparam ) - r.left; 59 | const int cur_y = GET_Y_LPARAM( lparam ) - r.top; 60 | const int w = r.right - r.left; 61 | const int h = r.bottom - r.top; 62 | const int border = ResizeBorderWidth; 63 | /* Disabled these corners because using them doesn't let us update the window contents 64 | fast enough for some reason, leading to a weird window-wobble appearance when resizing. 65 | if( cur_x < border && cur_y < border ) 66 | return HTTOPLEFT; 67 | if( cur_x > w-border && cur_y < border ) 68 | return HTTOPRIGHT; 69 | if( cur_x < border && cur_y > h-border ) 70 | return HTBOTTOMLEFT; 71 | */ 72 | if( cur_x > w-border && cur_y > h-border ) 73 | return HTBOTTOMRIGHT; 74 | 75 | // say we hit the caption to allow dragging the window from the client area 76 | hit = HTCAPTION; 77 | } 78 | return hit; 79 | } 80 | case WM_MOVING: 81 | case WM_SIZE: 82 | { 83 | if( o ) 84 | { 85 | RECT r; 86 | GetWindowRect( hwnd, &r ); 87 | const int x = r.left; 88 | const int y = r.top; 89 | const int w = r.right - r.left; 90 | const int h = r.bottom - r.top; 91 | o->setWindowPosAndSize( x, y, w, h, false ); 92 | o->saveWindowPosAndSize(); 93 | o->update(); // draw window content while moving/resizing 94 | } 95 | break; 96 | } 97 | } 98 | return DefWindowProc( hwnd, msg, wparam, lparam ); 99 | } 100 | 101 | 102 | // 103 | // Overlay 104 | // 105 | 106 | Overlay::Overlay( const std::string name, Microsoft::WRL::ComPtr d3dDevice) 107 | : m_name( name ), m_d3dDevice( d3dDevice ) 108 | {} 109 | 110 | Overlay::~Overlay() 111 | { 112 | enable( false ); 113 | } 114 | 115 | std::string Overlay::getName() const 116 | { 117 | return m_name; 118 | } 119 | 120 | void Overlay::enable( bool on ) 121 | { 122 | if( on && !m_hwnd ) // enable 123 | { 124 | // 125 | // Create window 126 | // 127 | 128 | const char* const wndclassName = "overlay"; 129 | WNDCLASSEX wndclass = {}; 130 | if( !GetClassInfoEx( 0, wndclassName, &wndclass ) ) // only the first overlay we open registers the window class 131 | { 132 | wndclass.cbSize = sizeof(WNDCLASSEX); 133 | wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; 134 | wndclass.lpfnWndProc = windowProc; 135 | wndclass.lpszClassName = wndclassName; 136 | wndclass.hbrBackground = CreateSolidBrush(0); 137 | RegisterClassEx(&wndclass); 138 | } 139 | 140 | m_hwnd = CreateWindowEx( DefaultStyle , wndclassName, m_name.c_str(), WS_POPUP|WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 500, 400, NULL, NULL, NULL, NULL ); 141 | SetWindowLongPtr( m_hwnd, GWLP_USERDATA, (LONG_PTR)this ); 142 | 143 | RECT r; 144 | GetWindowRect( m_hwnd, &r ); 145 | const int width = r.right - r.left; 146 | const int height = r.bottom - r.top; 147 | 148 | // 149 | // Create the unsettling amount of stuff that's needed to get a window to 150 | // properly alpha-blend our Direct2D rendering into the desktop. 151 | // See: https://docs.microsoft.com/en-us/archive/msdn-magazine/2014/june/windows-with-c-high-performance-window-layering-using-the-windows-composition-engine 152 | // 153 | 154 | #ifdef _DEBUG 155 | const bool isdebug = true; 156 | #else 157 | const bool isdebug = false; 158 | #endif 159 | 160 | // DXGI device 161 | ComPtr dxgiDevice; 162 | HRCHECK(m_d3dDevice.As(&dxgiDevice)); 163 | 164 | // DXGI factory 165 | ComPtr dxgiFactory; 166 | HRCHECK(CreateDXGIFactory2( isdebug ? DXGI_CREATE_FACTORY_DEBUG : 0, IID_PPV_ARGS(&dxgiFactory) )); 167 | 168 | // DXGI Swap chain 169 | DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; 170 | swapChainDesc.Width = width; 171 | swapChainDesc.Height = height; 172 | swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; 173 | swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; 174 | swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; 175 | swapChainDesc.BufferCount = 2; 176 | swapChainDesc.SampleDesc.Count = 1; 177 | swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED; 178 | HRCHECK(dxgiFactory->CreateSwapChainForComposition( dxgiDevice.Get(), &swapChainDesc, NULL, &m_swapChain )); 179 | HRCHECK(dxgiFactory->MakeWindowAssociation( m_hwnd, DXGI_MWA_NO_ALT_ENTER )); 180 | 181 | // DXGI surface 182 | ComPtr dxgiSurface; 183 | HRCHECK(m_swapChain->GetBuffer( 0, IID_PPV_ARGS(&dxgiSurface) )); 184 | 185 | // D2D factory 186 | D2D1_FACTORY_OPTIONS factoryOptions = {}; 187 | factoryOptions.debugLevel = isdebug ? D2D1_DEBUG_LEVEL_INFORMATION : D2D1_DEBUG_LEVEL_NONE; 188 | HRCHECK(D2D1CreateFactory( D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(m_d2dFactory), &factoryOptions, &m_d2dFactory )); 189 | 190 | // D2D render target 191 | D2D1_RENDER_TARGET_PROPERTIES targetProperties = {}; 192 | targetProperties.type = D2D1_RENDER_TARGET_TYPE_DEFAULT; 193 | targetProperties.pixelFormat.format = DXGI_FORMAT_UNKNOWN; 194 | targetProperties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; 195 | HRCHECK(m_d2dFactory->CreateDxgiSurfaceRenderTarget( dxgiSurface.Get(), &targetProperties, &m_renderTarget )); 196 | 197 | // Composition stuff 198 | HRCHECK(DCompositionCreateDevice( dxgiDevice.Get(), IID_PPV_ARGS(&m_compositionDevice) )); 199 | HRCHECK(m_compositionDevice->CreateTargetForHwnd( m_hwnd, true, &m_compositionTarget )); 200 | HRCHECK(m_compositionDevice->CreateVisual( &m_compositionVisual )); 201 | HRCHECK(m_compositionVisual->SetContent(m_swapChain.Get())); 202 | HRCHECK(m_compositionTarget->SetRoot(m_compositionVisual.Get())); 203 | HRCHECK(m_compositionDevice->Commit()); 204 | 205 | // DirectWrite factory 206 | HRCHECK(DWriteCreateFactory( DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast(m_dwriteFactory.GetAddressOf()) )); 207 | 208 | // Default brush 209 | HRCHECK(m_renderTarget->CreateSolidColorBrush( float4(0,0,0,1), &m_brush )); 210 | 211 | // 212 | // Finalize enable 213 | // 214 | 215 | m_enabled = true; 216 | onEnable(); 217 | } 218 | else if( !on && m_hwnd ) // disable 219 | { 220 | onDisable(); 221 | 222 | m_dwriteFactory.Reset(); 223 | m_compositionVisual.Reset(); 224 | m_compositionTarget.Reset(); 225 | m_compositionDevice.Reset(); 226 | m_renderTarget.Reset(); 227 | m_d2dFactory.Reset(); 228 | m_swapChain.Reset(); 229 | 230 | DestroyWindow( m_hwnd ); 231 | m_hwnd = 0; 232 | m_enabled = false; 233 | } 234 | } 235 | 236 | bool Overlay::isEnabled() const 237 | { 238 | return m_enabled; 239 | } 240 | 241 | void Overlay::enableUiEdit( bool on ) 242 | { 243 | m_uiEditEnabled = on; 244 | if (on) 245 | SetWindowLongPtr(m_hwnd, GWL_EXSTYLE, EditStyle); 246 | else 247 | SetWindowLongPtr(m_hwnd, GWL_EXSTYLE, DefaultStyle); 248 | 249 | update(); 250 | } 251 | 252 | bool Overlay::isUiEditEnabled() const 253 | { 254 | return m_uiEditEnabled; 255 | } 256 | 257 | void Overlay::configChanged() 258 | { 259 | if( !m_enabled ) 260 | return; 261 | 262 | // Somewhat silly way to ensure the default positions of the overlays aren't all on top of each other. 263 | const unsigned hash = MurmurHash2(m_name.c_str(),(int)m_name.length(),0x1234); 264 | const int defaultX = (hash % 100) * 15; 265 | const int defaultY = (hash % 80) * 10; 266 | 267 | const float2 defaultSize = getDefaultSize(); 268 | 269 | // Position/dimensions might have changed 270 | const int x = g_cfg.getInt(m_name,"window_pos_x", defaultX); 271 | const int y = g_cfg.getInt(m_name,"window_pos_y", defaultY); 272 | const int w = g_cfg.getInt(m_name,"window_size_x", (int)defaultSize.x); 273 | const int h = g_cfg.getInt(m_name,"window_size_y", (int)defaultSize.y); 274 | setWindowPosAndSize( x, y, w, h ); 275 | 276 | onConfigChanged(); 277 | } 278 | 279 | void Overlay::sessionChanged() 280 | { 281 | onSessionChanged(); 282 | } 283 | 284 | void Overlay::update() 285 | { 286 | if( !m_enabled ) 287 | return; 288 | 289 | const float w = (float)m_width; 290 | const float h = (float)m_height; 291 | const float cornerRadius = g_cfg.getFloat( m_name, "corner_radius", m_name=="OverlayInputs"?2.0f:6.0f ); 292 | 293 | #if defined(_DEBUG) or defined(DEBUG_OVERLAY_TIME) 294 | debugTimeStart = std::chrono::high_resolution_clock::now(); 295 | #endif 296 | // Clear/draw background 297 | if( !hasCustomBackground() ) 298 | { 299 | m_renderTarget->BeginDraw(); 300 | m_renderTarget->Clear( float4(0,0,0,0) ); 301 | D2D1_ROUNDED_RECT rr; 302 | rr.rect = { 0.5f, 0.5f, w-0.5f, h-0.5f }; 303 | rr.radiusX = cornerRadius; 304 | rr.radiusY = cornerRadius; 305 | m_brush->SetColor( g_cfg.getFloat4( m_name, "background_col", float4(0,0,0,0.7f) ) ); 306 | m_renderTarget->FillRoundedRectangle( &rr, m_brush.Get() ); 307 | m_renderTarget->EndDraw(); 308 | } 309 | 310 | // Overlay-specific logic and rendering 311 | onUpdate(); 312 | 313 | if( m_uiEditEnabled ) 314 | { 315 | // Draw highlight frame and resize corner indicators 316 | m_renderTarget->BeginDraw(); 317 | D2D1_ROUNDED_RECT rr; 318 | rr.rect = { 0.5f, 0.5f, w-0.5f, h-0.5f }; 319 | rr.radiusX = cornerRadius; 320 | rr.radiusY = cornerRadius; 321 | m_brush->SetColor( float4(1,1,1,0.7f) ); 322 | m_renderTarget->DrawRoundedRectangle( &rr, m_brush.Get(), 2 ); 323 | m_renderTarget->DrawLine( float2(w-0.5f,h-0.5f-ResizeBorderWidth), float2(w-0.5f-ResizeBorderWidth,h-0.5f-ResizeBorderWidth), m_brush.Get(), 2 ); 324 | m_renderTarget->DrawLine( float2(w-0.5f-ResizeBorderWidth,h-0.5f), float2(w-0.5f-ResizeBorderWidth,h-0.5f-ResizeBorderWidth), m_brush.Get(), 2 ); 325 | m_renderTarget->EndDraw(); 326 | } 327 | 328 | HRCHECK(m_swapChain->Present( 1, 0 )); 329 | 330 | #if defined(_DEBUG) or defined(DEBUG_OVERLAY_TIME) 331 | using micro = std::chrono::microseconds; 332 | debugTimeEnd = std::chrono::high_resolution_clock::now(); 333 | debugTimeDiff = std::chrono::duration_cast(debugTimeEnd - debugTimeStart).count(); 334 | debugTimeAvg = (debugTimeAvg / 10) * 9 + (float)(debugTimeDiff) / 10; 335 | dbg("%s loop took %.4f (%i) microseconds", m_name.c_str(), debugTimeAvg, debugTimeDiff); 336 | # if defined(DEBUG_OVERLAY_TIME) 337 | std::cout << std::format("{} loop took {:.4f} ({}) microseconds", m_name.c_str(), debugTimeAvg, debugTimeDiff) << std::endl; 338 | # endif 339 | debugTimeStart = std::chrono::high_resolution_clock::now(); 340 | #endif 341 | } 342 | 343 | void Overlay::setWindowPosAndSize( int x, int y, int w, int h, bool callSetWindowPos ) 344 | { 345 | w = std::max( w, 30 ); 346 | h = std::max( h, 30 ); 347 | 348 | if( callSetWindowPos ) 349 | SetWindowPos( m_hwnd, HWND_TOPMOST, x, y, w, h, SWP_NOACTIVATE|SWP_SHOWWINDOW ); 350 | 351 | m_xpos = x; 352 | m_ypos = y; 353 | m_width = w; 354 | m_height = h; 355 | 356 | m_renderTarget.Reset(); // need to release all references to swap chain's back buffers before calling ResizeBuffers 357 | 358 | HRCHECK(m_swapChain->ResizeBuffers( 0, w, h, DXGI_FORMAT_UNKNOWN, 0 )); 359 | 360 | // Recreate render target 361 | ComPtr dxgiSurface; 362 | HRCHECK(m_swapChain->GetBuffer( 0, IID_PPV_ARGS(&dxgiSurface) )); 363 | D2D1_RENDER_TARGET_PROPERTIES targetProperties = {}; 364 | targetProperties.type = D2D1_RENDER_TARGET_TYPE_DEFAULT; 365 | targetProperties.pixelFormat.format = DXGI_FORMAT_UNKNOWN; 366 | targetProperties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; 367 | HRCHECK(m_d2dFactory->CreateDxgiSurfaceRenderTarget( dxgiSurface.Get(), &targetProperties, &m_renderTarget )); 368 | } 369 | 370 | void Overlay::saveWindowPosAndSize() 371 | { 372 | g_cfg.setInt( m_name, "window_pos_x", m_xpos ); 373 | g_cfg.setInt( m_name, "window_pos_y", m_ypos ); 374 | g_cfg.setInt( m_name, "window_size_x", m_width ); 375 | g_cfg.setInt( m_name, "window_size_y", m_height ); 376 | 377 | g_cfg.save(); 378 | } 379 | 380 | bool Overlay::canEnableWhileNotDriving() const 381 | { 382 | return false; 383 | } 384 | 385 | bool Overlay::canEnableWhileDisconnected() const 386 | { 387 | return false; 388 | } 389 | 390 | void Overlay::onEnable() {} 391 | void Overlay::onDisable() {} 392 | void Overlay::onUpdate() {} 393 | void Overlay::onConfigChanged() {} 394 | void Overlay::onSessionChanged() {} 395 | float2 Overlay::getDefaultSize() { return float2(400,300); } 396 | bool Overlay::hasCustomBackground() { return false; } 397 | 398 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021-2022 L. E. Spalt 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | 26 | #pragma once 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #define HRCHECK( x_ ) do{ \ 42 | HRESULT hr_ = x_; \ 43 | if( FAILED(hr_) ) { \ 44 | printf("ERROR: failed call to %s (%s:%d), hr=0x%x\n", #x_, __FILE__, __LINE__,hr_); \ 45 | exit(1); \ 46 | } } while(0) 47 | 48 | struct float2 49 | { 50 | union { float r; float x; }; 51 | union { float g; float y; }; 52 | float2() = default; 53 | float2( float _x, float _y ) : x(_x), y(_y) {} 54 | float2( const D2D1_POINT_2F& p ) : x(p.x), y(p.y) {} 55 | operator D2D1_POINT_2F() const { return {x,y}; } 56 | float* operator&() { return &x; } 57 | const float* operator&() const { return &x; } 58 | }; 59 | 60 | struct float4 61 | { 62 | union { float r; float x; }; 63 | union { float g; float y; }; 64 | union { float b; float z; }; 65 | union { float a; float w; }; 66 | float4() = default; 67 | float4( float _x, float _y, float _z, float _w ) : x(_x), y(_y), z(_z), w(_w) {} 68 | float4( const D2D1_COLOR_F& c ) : r(c.r), g(c.g), b(c.b), a(c.a) {} 69 | operator D2D1_COLOR_F() const { return {r,g,b,a}; } 70 | float* operator&() { return &x; } 71 | const float* operator&() const { return &x; } 72 | }; 73 | 74 | inline bool loadFile( const std::string& fname, std::string& output ) 75 | { 76 | FILE* fp = fopen( fname.c_str(), "rb" ); 77 | if( !fp ) 78 | return false; 79 | 80 | fseek( fp, 0, SEEK_END ); 81 | const long sz = ftell( fp ); 82 | fseek( fp, 0, SEEK_SET ); 83 | 84 | char* buf = new char[sz]; 85 | 86 | fread( buf, 1, sz, fp ); 87 | fclose( fp ); 88 | output = std::string( buf, sz ); 89 | 90 | delete[] buf; 91 | return true; 92 | } 93 | 94 | inline bool saveFile( const std::string& fname, const std::string& s ) 95 | { 96 | FILE* fp = fopen( fname.c_str(), "wb" ); 97 | if( !fp ) 98 | return false; 99 | 100 | fwrite( s.data(), 1, s.length(), fp ); 101 | 102 | fclose( fp ); 103 | return true; 104 | } 105 | 106 | inline std::wstring toWide( const std::string& narrow ) 107 | { 108 | return std::wstring(narrow.begin(),narrow.end()); 109 | } 110 | 111 | inline std::string formatLaptime( float secs ) 112 | { 113 | char s[32]; 114 | const int mins = int(secs/60.0f); 115 | if( mins ) 116 | sprintf( s, "%d:%06.3f", mins, fmodf(secs,60.0f) ); 117 | else 118 | sprintf( s, "%.03f", secs ); 119 | return std::string( s ); 120 | } 121 | 122 | class ColumnLayout 123 | { 124 | public: 125 | 126 | struct Column 127 | { 128 | int id = 0; 129 | float textWidth = 0; 130 | float borderL = 0; 131 | float borderR = 0; 132 | float textL = 0; 133 | float textR = 0; 134 | bool autoWidth = false; 135 | }; 136 | 137 | void reset() 138 | { 139 | m_columns.clear(); 140 | } 141 | 142 | // Pass in zero width for auto-scale 143 | void add( int id, float textWidth, float borderL, float borderR ) 144 | { 145 | Column clm; 146 | clm.id = id; 147 | clm.textWidth = textWidth; 148 | clm.borderL = borderL; 149 | clm.borderR = borderR; 150 | clm.autoWidth = textWidth == 0; 151 | m_columns.emplace_back( clm ); 152 | } 153 | void add( int id, float textWidth, float border ) 154 | { 155 | add( id, textWidth, border, border ); 156 | } 157 | 158 | void layout( float totalWidth ) 159 | { 160 | int autoWidthCnt = 0; 161 | float fixedWidth = 0; 162 | for( const Column& clm : m_columns ) 163 | { 164 | if( clm.autoWidth ) 165 | { 166 | autoWidthCnt++; 167 | fixedWidth += clm.borderL + clm.borderR; 168 | } 169 | else 170 | { 171 | fixedWidth += clm.textWidth + clm.borderL + clm.borderR; 172 | } 173 | } 174 | 175 | const float autoTextWidth = std::max( 0.0f, (totalWidth - fixedWidth) / autoWidthCnt ); 176 | 177 | float x = 0; 178 | for( Column& clm : m_columns ) 179 | { 180 | if( clm.autoWidth ) 181 | clm.textWidth = autoTextWidth; 182 | 183 | clm.textL = x + clm.borderL; 184 | clm.textR = clm.textL + clm.textWidth; 185 | 186 | x = clm.textR + clm.borderR; 187 | } 188 | } 189 | 190 | const Column* get( int id ) const 191 | { 192 | for( int i=0; i<(int)m_columns.size(); ++i ) 193 | { 194 | if( m_columns[i].id == id ) 195 | return &m_columns[i]; 196 | } 197 | return nullptr; 198 | } 199 | 200 | private: 201 | 202 | std::vector m_columns; 203 | }; 204 | 205 | //----------------------------------------------------------------------------- 206 | // MurmurHash2, by Austin Appleby 207 | 208 | // Note - This code makes a few assumptions about how your machine behaves - 209 | 210 | // 1. We can read a 4-byte value from any address without crashing 211 | // 2. sizeof(int) == 4 212 | 213 | // And it has a few limitations - 214 | 215 | // 1. It will not work incrementally. 216 | // 2. It will not produce the same results on little-endian and big-endian 217 | // machines. 218 | 219 | inline unsigned int MurmurHash2 ( const void * key, int len, unsigned int seed ) 220 | { 221 | // 'm' and 'r' are mixing constants generated offline. 222 | // They're not really 'magic', they just happen to work well. 223 | 224 | const unsigned int m = 0x5bd1e995; 225 | const int r = 24; 226 | 227 | // Initialize the hash to a 'random' value 228 | 229 | unsigned int h = seed ^ len; 230 | 231 | // Mix 4 bytes at a time into the hash 232 | 233 | const unsigned char * data = (const unsigned char *)key; 234 | 235 | while(len >= 4) 236 | { 237 | unsigned int k = *(unsigned int *)data; 238 | 239 | k *= m; 240 | k ^= k >> r; 241 | k *= m; 242 | 243 | h *= m; 244 | h ^= k; 245 | 246 | data += 4; 247 | len -= 4; 248 | } 249 | 250 | // Handle the last few bytes of the input array 251 | 252 | switch(len) 253 | { 254 | case 3: h ^= data[2] << 16; 255 | case 2: h ^= data[1] << 8; 256 | case 1: h ^= data[0]; 257 | h *= m; 258 | }; 259 | 260 | // Do a few final mixes of the hash to ensure the last few 261 | // bytes are well-incorporated. 262 | 263 | h ^= h >> 13; 264 | h *= m; 265 | h ^= h >> 15; 266 | 267 | return h; 268 | } 269 | // End MurmurHash2 270 | //----------------------------------------------------------------------------- 271 | 272 | class TextCache 273 | { 274 | public: 275 | 276 | ~TextCache() 277 | { 278 | reset(); 279 | } 280 | 281 | void reset( IDWriteFactory* factory=nullptr ) 282 | { 283 | for( auto& it : m_cache ) 284 | it.second->Release(); 285 | 286 | m_cache.clear(); 287 | m_factory = factory; 288 | } 289 | 290 | // 291 | // Render some text, using a cached TextLayout if possible. 292 | // This works around spending ungodly amount of CPU cycles on ID2D1RenderTarget::DrawText. 293 | // 294 | // To disable cache creation, a bool is added as a last parameter. 295 | // This should be used for texts that are not likely to repeat, or could have too many different values, to avoid OOMs 296 | // For example, the DDU vsBest delta if the car is stopped on track 297 | // 298 | // Assumption: all values stored in 'textFormat' are invariant between calls to this function, except horizontal alignment. 299 | // Which is why we're including alignment in the hash explicitly, and otherwise just include the text format pointer. 300 | // This isn't bullet proof, since a user could get the same address again for a newly (re-)created text format. But in our usage 301 | // patterns, recreating text formats always implies nuking this cache anyway, so don't bother with a more complicated design. 302 | // 303 | // Assumption: textFormat is set to DWRITE_PARAGRAPH_ALIGNMENT_CENTER, so ycenter +/- fontSize is enough vertical room in all 304 | // cases. I.e. we only care about rendering single-line text. 305 | // 306 | void render( ID2D1RenderTarget* renderTarget, const wchar_t* str, IDWriteTextFormat* textFormat, float xmin, float xmax, float ycenter, ID2D1SolidColorBrush* brush, DWRITE_TEXT_ALIGNMENT align, bool noCache = false) 307 | { 308 | IDWriteTextLayout* textLayout = getOrCreateTextLayout( str, textFormat, xmin, xmax, align, noCache); 309 | if( !textLayout ) 310 | return; 311 | 312 | const float fontSize = textFormat->GetFontSize(); 313 | 314 | const D2D1_RECT_F r = { xmin, ycenter-fontSize, xmax, ycenter+fontSize }; 315 | renderTarget->DrawTextLayout( float2(xmin,ycenter-fontSize), textLayout, brush, D2D1_DRAW_TEXT_OPTIONS_CLIP ); 316 | if (noCache) { 317 | textLayout->Release(); 318 | } 319 | } 320 | 321 | // 322 | // Same assumptions as render(). 323 | // 324 | float2 getExtent( const wchar_t* str, IDWriteTextFormat* textFormat, float xmin, float xmax, DWRITE_TEXT_ALIGNMENT align ) 325 | { 326 | IDWriteTextLayout* textLayout = getOrCreateTextLayout( str, textFormat, xmin, xmax, align ); 327 | if( !textLayout ) 328 | return float2(0,0); 329 | 330 | DWRITE_TEXT_METRICS m = {}; 331 | textLayout->GetMetrics( &m ); 332 | 333 | return float2( m.width, m.height ); 334 | } 335 | 336 | private: 337 | 338 | IDWriteTextLayout* getOrCreateTextLayout( const wchar_t* str, IDWriteTextFormat* textFormat, float xmin, float xmax, DWRITE_TEXT_ALIGNMENT align, bool noCache = false) 339 | { 340 | if( xmax < xmin ) 341 | return nullptr; 342 | 343 | const float fontSize = textFormat->GetFontSize(); 344 | const float width = xmax - xmin; 345 | 346 | textFormat->SetTextAlignment( align ); 347 | 348 | const int len = (int)wcslen( str ); 349 | unsigned hash = MurmurHash2( str, len*sizeof(wchar_t), 0x12341234 ); 350 | hash ^= (unsigned)(uint64_t(textFormat) & 0xffffffff); 351 | hash ^= (unsigned)(uint64_t(textFormat) >> 32); 352 | hash ^= *((unsigned*)&width); 353 | hash ^= (unsigned)align; 354 | 355 | IDWriteTextLayout* textLayout = nullptr; 356 | 357 | auto it = m_cache.find( hash ); 358 | 359 | if( it == m_cache.end() ) 360 | { 361 | //printf("Not cached: %ls\n", str); 362 | m_factory->CreateTextLayout( str, len, textFormat, width, fontSize*2, &textLayout ); 363 | if (!noCache) { 364 | m_cache.insert(std::make_pair(hash, textLayout)); 365 | //printf("Caching!\n"); 366 | } 367 | } 368 | else 369 | { 370 | //printf("Already cached: %ls\n", str); 371 | textLayout = it->second; 372 | } 373 | 374 | return textLayout; 375 | } 376 | 377 | std::unordered_map m_cache; 378 | IDWriteFactory* m_factory = nullptr; 379 | }; 380 | 381 | inline float2 computeTextExtent( const wchar_t* str, IDWriteFactory* factory, IDWriteTextFormat* textFormat ) 382 | { 383 | IDWriteTextLayout* textLayout = nullptr; 384 | 385 | factory->CreateTextLayout( str, (int)wcslen(str), textFormat, 99999, 99999, &textLayout ); 386 | DWRITE_TEXT_METRICS m = {}; 387 | textLayout->GetMetrics( &m ); 388 | 389 | textLayout->Release(); 390 | 391 | return float2( m.width, m.height ); 392 | } 393 | 394 | inline float celsiusToFahrenheit( float c ) 395 | { 396 | return c * (9.0f / 5.0f) + 32.0f; 397 | } 398 | 399 | inline bool parseHotkey( const std::string& desc, UINT* mod, UINT* vk ) 400 | { 401 | // Dumb but good-enough way to turn strings like "Ctrl-Shift-F1" into values understood by RegisterHotkey. 402 | 403 | std::string s = desc; 404 | for( char& c : s ) 405 | c = (char)toupper( (unsigned char)c ); 406 | 407 | // Need at least one modifier 408 | size_t pos = s.find_last_of("+- "); 409 | if( pos == std::string::npos ) 410 | return false; 411 | 412 | // "Parse" modifier 413 | *mod = 0; 414 | if( strstr(s.c_str(),"CTRL") || strstr(s.c_str(),"CONTROL")) 415 | *mod |= MOD_CONTROL; 416 | if( strstr(s.c_str(),"ALT") ) 417 | *mod |= MOD_ALT; 418 | if( strstr(s.c_str(),"SHIFT") ) 419 | *mod |= MOD_SHIFT; 420 | 421 | // Parse key 422 | const std::string key = s.substr( pos+1 ); 423 | 424 | for( int i=1; i<=24; ++i ) 425 | { 426 | const std::string fkey = "F" + std::to_string(i); 427 | if( key == fkey ) { 428 | *vk = VK_F1 + (i-1); 429 | return true; 430 | } 431 | } 432 | 433 | if( key == "ENTER" || key == "RETURN" ) 434 | { 435 | *vk = VK_RETURN; 436 | return true; 437 | } 438 | 439 | if( key == "SPACE" ) 440 | { 441 | *vk = VK_SPACE; 442 | return true; 443 | } 444 | 445 | if( key.length() == 1 ) 446 | { 447 | // if A-Z 448 | if(key[0] >= 0x41 && key[0] <= 0x5A) { 449 | *vk = key[0]; 450 | return true; 451 | } 452 | 453 | // TODO: This is dumb. Should check for the keyboard distribution, right? 454 | if (key[0] == '+') { 455 | *vk = VK_OEM_PLUS; 456 | return true; 457 | } 458 | if (key[0] == ',') { 459 | *vk = VK_OEM_COMMA; 460 | return true; 461 | } 462 | if (key[0] == '-') { 463 | *vk = VK_OEM_MINUS; 464 | return true; 465 | } 466 | if (key[0] == '.') { 467 | *vk = VK_OEM_PERIOD; 468 | return true; 469 | } 470 | 471 | *vk = key[0]; 472 | return true; 473 | } 474 | 475 | return false; 476 | } 477 | 478 | 479 | inline std::string toLowerCase(std::string str) { 480 | std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { 481 | return std::tolower(c); 482 | }); 483 | return str; 484 | }; 485 | 486 | 487 | inline IWICFormatConverter* findCarBrandIcon(const std::string& carName, std::map& carBrandsMap) 488 | { 489 | std::string cocheLowerCase = toLowerCase(carName); 490 | 491 | IWICFormatConverter* valor = nullptr; // Valor por defecto si no se encuentra el coche en el mapa 492 | 493 | // Buscar el coche en el mapa 494 | for (const auto& pair : carBrandsMap) 495 | { 496 | if (cocheLowerCase.find(pair.first) != std::string::npos) 497 | { 498 | valor = pair.second; 499 | break; 500 | } 501 | } 502 | 503 | if (valor == nullptr) valor = carBrandsMap["00error"]; 504 | 505 | return valor; 506 | } 507 | 508 | 509 | -------------------------------------------------------------------------------- /irsdk/irsdk_defines.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013, iRacing.com Motorsport Simulations, LLC. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of iRacing.com Motorsport Simulations nor the 13 | names of its contributors may be used to endorse or promote products 14 | derived from this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef IRSDK_DEFINES_H 29 | #define IRSDK_DEFINES_H 30 | 31 | /* 32 | The IRSDK is a simple api that lets clients access telemetry data from the 33 | iRacing simulator. It is broken down into several parts: 34 | 35 | - Live data 36 | Live data is output from the sim into a shared memory mapped file. Any 37 | application can open this memory mapped file and read the telemetry data 38 | out. The format of this data was laid out in such a way that it should be 39 | possible to access from any language that can open a windows memory mapped 40 | file, without needing an external api. 41 | 42 | There are two different types of data that the telemetry outputs, 43 | sessionInfo and variables: 44 | 45 | Session info is for data that only needs to be updated every once in a 46 | while. This data is output as a YAML formatted string. 47 | 48 | Variables, on the other hand, are output at a rate of 60 times a second. 49 | The varHeader struct defines each variable that the sim will output, while 50 | the varData struct gives details about the current line buffer that the vars 51 | are being written into. Each variable is packed into a binary array with 52 | an offset and length stored in the varHeader. The number of variables 53 | available can change depending on the car or session loaded. But once the 54 | sim is running the variable list is locked down and will not change during a 55 | session. 56 | 57 | The sim writes a new line of variables every 16 ms, and then signals any 58 | listeners in order to wake them up to read the data. Because the sim has no 59 | way of knowing when a listener is done reading the data, we triple buffer 60 | it in order to give all the clients enough time to read the data out. This 61 | gives you a minimum of 16 ms to read the data out and process it. So it is 62 | best to copy the data out before processing it. You can use the function 63 | irsdk_waitForDataReady() to both wait for new data and copy the data to a 64 | local buffer. 65 | 66 | - Logged data 67 | Detailed information about the local drivers car can be logged to disk in 68 | the form of an ibt binary file. This logging is enabled in the sim by 69 | typing alt-L at any time. The ibt file format directly mirrors the format 70 | of the live data. 71 | 72 | It is stored as an irsdk_header followed immediately by an irsdk_diskSubHeader. 73 | After that the offsets in the irsdk_header point to the sessionInfo string, 74 | the varHeader, and the varBuffer. 75 | 76 | - Remote Conrol 77 | You can control the camera selections and playback of a replay tape, from 78 | any external application by sending a windows message with the 79 | irsdk_broadcastMsg() function. 80 | */ 81 | 82 | // Constant Definitions 83 | 84 | #include 85 | 86 | static const _TCHAR IRSDK_DATAVALIDEVENTNAME[] = _T("Local\\IRSDKDataValidEvent"); 87 | static const _TCHAR IRSDK_MEMMAPFILENAME[] = _T("Local\\IRSDKMemMapFileName"); 88 | static const _TCHAR IRSDK_BROADCASTMSGNAME[] = _T("IRSDK_BROADCASTMSG"); 89 | 90 | static const int IRSDK_MAX_BUFS = 4; 91 | static const int IRSDK_MAX_STRING = 32; 92 | // descriptions can be longer than max_string! 93 | static const int IRSDK_MAX_DESC = 64; 94 | 95 | // define markers for unlimited session lap and time 96 | static const int IRSDK_UNLIMITED_LAPS = 32767; 97 | static const float IRSDK_UNLIMITED_TIME = 604800.0f; 98 | 99 | // latest version of our telemetry headers 100 | static const int IRSDK_VER = 2; 101 | 102 | enum irsdk_StatusField 103 | { 104 | irsdk_stConnected = 1 105 | }; 106 | 107 | enum irsdk_VarType 108 | { 109 | // 1 byte 110 | irsdk_char = 0, 111 | irsdk_bool, 112 | 113 | // 4 bytes 114 | irsdk_int, 115 | irsdk_bitField, 116 | irsdk_float, 117 | 118 | // 8 bytes 119 | irsdk_double, 120 | 121 | //index, don't use 122 | irsdk_ETCount 123 | }; 124 | 125 | static const int irsdk_VarTypeBytes[irsdk_ETCount] = 126 | { 127 | 1, // irsdk_char 128 | 1, // irsdk_bool 129 | 130 | 4, // irsdk_int 131 | 4, // irsdk_bitField 132 | 4, // irsdk_float 133 | 134 | 8 // irsdk_double 135 | }; 136 | 137 | // bit fields 138 | enum irsdk_EngineWarnings 139 | { 140 | irsdk_waterTempWarning = 0x01, 141 | irsdk_fuelPressureWarning = 0x02, 142 | irsdk_oilPressureWarning = 0x04, 143 | irsdk_engineStalled = 0x08, 144 | irsdk_pitSpeedLimiter = 0x10, 145 | irsdk_revLimiterActive = 0x20, 146 | irsdk_oilTempWarning = 0x40, 147 | }; 148 | 149 | // global flags 150 | enum irsdk_Flags 151 | { 152 | // global flags 153 | irsdk_checkered = 0x00000001, 154 | irsdk_white = 0x00000002, 155 | irsdk_green = 0x00000004, 156 | irsdk_yellow = 0x00000008, 157 | irsdk_red = 0x00000010, 158 | irsdk_blue = 0x00000020, 159 | irsdk_debris = 0x00000040, 160 | irsdk_crossed = 0x00000080, 161 | irsdk_yellowWaving = 0x00000100, 162 | irsdk_oneLapToGreen = 0x00000200, 163 | irsdk_greenHeld = 0x00000400, 164 | irsdk_tenToGo = 0x00000800, 165 | irsdk_fiveToGo = 0x00001000, 166 | irsdk_randomWaving = 0x00002000, 167 | irsdk_caution = 0x00004000, 168 | irsdk_cautionWaving = 0x00008000, 169 | 170 | // drivers black flags 171 | irsdk_black = 0x00010000, 172 | irsdk_disqualify = 0x00020000, 173 | irsdk_servicible = 0x00040000, // car is allowed service (not a flag) 174 | irsdk_furled = 0x00080000, 175 | irsdk_repair = 0x00100000, 176 | 177 | // start lights 178 | irsdk_startHidden = 0x10000000, 179 | irsdk_startReady = 0x20000000, 180 | irsdk_startSet = 0x40000000, 181 | irsdk_startGo = 0x80000000, 182 | }; 183 | 184 | 185 | // status 186 | enum irsdk_TrkLoc 187 | { 188 | irsdk_NotInWorld = -1, 189 | irsdk_OffTrack, 190 | irsdk_InPitStall, 191 | irsdk_AproachingPits, 192 | irsdk_OnTrack 193 | }; 194 | 195 | enum irsdk_TrkSurf 196 | { 197 | irsdk_SurfaceNotInWorld = -1, 198 | irsdk_UndefinedMaterial = 0, 199 | 200 | irsdk_Asphalt1Material, 201 | irsdk_Asphalt2Material, 202 | irsdk_Asphalt3Material, 203 | irsdk_Asphalt4Material, 204 | irsdk_Concrete1Material, 205 | irsdk_Concrete2Material, 206 | irsdk_RacingDirt1Material, 207 | irsdk_RacingDirt2Material, 208 | irsdk_Paint1Material, 209 | irsdk_Paint2Material, 210 | irsdk_Rumble1Material, 211 | irsdk_Rumble2Material, 212 | irsdk_Rumble3Material, 213 | irsdk_Rumble4Material, 214 | 215 | irsdk_Grass1Material, 216 | irsdk_Grass2Material, 217 | irsdk_Grass3Material, 218 | irsdk_Grass4Material, 219 | irsdk_Dirt1Material, 220 | irsdk_Dirt2Material, 221 | irsdk_Dirt3Material, 222 | irsdk_Dirt4Material, 223 | irsdk_SandMaterial, 224 | irsdk_Gravel1Material, 225 | irsdk_Gravel2Material, 226 | irsdk_GrasscreteMaterial, 227 | irsdk_AstroturfMaterial, 228 | }; 229 | 230 | enum irsdk_SessionState 231 | { 232 | irsdk_StateInvalid, 233 | irsdk_StateGetInCar, 234 | irsdk_StateWarmup, 235 | irsdk_StateParadeLaps, 236 | irsdk_StateRacing, 237 | irsdk_StateCheckered, 238 | irsdk_StateCoolDown 239 | }; 240 | 241 | enum irsdk_CarLeftRight 242 | { 243 | irsdk_LROff, 244 | irsdk_LRClear, // no cars around us. 245 | irsdk_LRCarLeft, // there is a car to our left. 246 | irsdk_LRCarRight, // there is a car to our right. 247 | irsdk_LRCarLeftRight, // there are cars on each side. 248 | irsdk_LR2CarsLeft, // there are two cars to our left. 249 | irsdk_LR2CarsRight // there are two cars to our right. 250 | }; 251 | 252 | enum irsdk_CameraState 253 | { 254 | irsdk_IsSessionScreen = 0x0001, // the camera tool can only be activated if viewing the session screen (out of car) 255 | irsdk_IsScenicActive = 0x0002, // the scenic camera is active (no focus car) 256 | 257 | //these can be changed with a broadcast message 258 | irsdk_CamToolActive = 0x0004, 259 | irsdk_UIHidden = 0x0008, 260 | irsdk_UseAutoShotSelection = 0x0010, 261 | irsdk_UseTemporaryEdits = 0x0020, 262 | irsdk_UseKeyAcceleration = 0x0040, 263 | irsdk_UseKey10xAcceleration = 0x0080, 264 | irsdk_UseMouseAimMode = 0x0100 265 | }; 266 | 267 | enum irsdk_PitSvFlags 268 | { 269 | irsdk_LFTireChange = 0x0001, 270 | irsdk_RFTireChange = 0x0002, 271 | irsdk_LRTireChange = 0x0004, 272 | irsdk_RRTireChange = 0x0008, 273 | 274 | irsdk_FuelFill = 0x0010, 275 | irsdk_WindshieldTearoff = 0x0020, 276 | irsdk_FastRepair = 0x0040 277 | }; 278 | 279 | enum irsdk_PitSvStatus 280 | { 281 | // status 282 | irsdk_PitSvNone = 0, 283 | irsdk_PitSvInProgress, 284 | irsdk_PitSvComplete, 285 | 286 | // errors 287 | irsdk_PitSvTooFarLeft = 100, 288 | irsdk_PitSvTooFarRight, 289 | irsdk_PitSvTooFarForward, 290 | irsdk_PitSvTooFarBack, 291 | irsdk_PitSvBadAngle, 292 | irsdk_PitSvCantFixThat, 293 | }; 294 | 295 | enum irsdk_PaceMode 296 | { 297 | irsdk_PaceModeSingleFileStart = 0, 298 | irsdk_PaceModeDoubleFileStart, 299 | irsdk_PaceModeSingleFileRestart, 300 | irsdk_PaceModeDoubleFileRestart, 301 | irsdk_PaceModeNotPacing, 302 | }; 303 | 304 | enum irsdk_PaceFlags 305 | { 306 | irsdk_PaceFlagsEndOfLine = 0x01, 307 | irsdk_PaceFlagsFreePass = 0x02, 308 | irsdk_PaceFlagsWavedAround = 0x04, 309 | }; 310 | 311 | //---- 312 | // 313 | 314 | struct irsdk_varHeader 315 | { 316 | int type; // irsdk_VarType 317 | int offset; // offset fron start of buffer row 318 | int count; // number of entrys (array) 319 | // so length in bytes would be irsdk_VarTypeBytes[type] * count 320 | bool countAsTime; 321 | char pad[3]; // (16 byte align) 322 | 323 | char name[IRSDK_MAX_STRING]; 324 | char desc[IRSDK_MAX_DESC]; 325 | char unit[IRSDK_MAX_STRING]; // something like "kg/m^2" 326 | 327 | void clear() 328 | { 329 | type = 0; 330 | offset = 0; 331 | count = 0; 332 | countAsTime = false; 333 | memset(name, 0, sizeof(name)); 334 | memset(desc, 0, sizeof(name)); 335 | memset(unit, 0, sizeof(name)); 336 | } 337 | }; 338 | 339 | struct irsdk_varBuf 340 | { 341 | int tickCount; // used to detect changes in data 342 | int bufOffset; // offset from header 343 | int pad[2]; // (16 byte align) 344 | }; 345 | 346 | struct irsdk_header 347 | { 348 | int ver; // this api header version, see IRSDK_VER 349 | int status; // bitfield using irsdk_StatusField 350 | int tickRate; // ticks per second (60 or 360 etc) 351 | 352 | // session information, updated periodicaly 353 | int sessionInfoUpdate; // Incremented when session info changes 354 | int sessionInfoLen; // Length in bytes of session info string 355 | int sessionInfoOffset; // Session info, encoded in YAML format 356 | 357 | // State data, output at tickRate 358 | 359 | int numVars; // length of arra pointed to by varHeaderOffset 360 | int varHeaderOffset; // offset to irsdk_varHeader[numVars] array, Describes the variables received in varBuf 361 | 362 | int numBuf; // <= IRSDK_MAX_BUFS (3 for now) 363 | int bufLen; // length in bytes for one line 364 | int pad1[2]; // (16 byte align) 365 | irsdk_varBuf varBuf[IRSDK_MAX_BUFS]; // buffers of data being written to 366 | }; 367 | 368 | // sub header used when writing telemetry to disk 369 | struct irsdk_diskSubHeader 370 | { 371 | time_t sessionStartDate; 372 | double sessionStartTime; 373 | double sessionEndTime; 374 | int sessionLapCount; 375 | int sessionRecordCount; 376 | }; 377 | 378 | //---- 379 | // Client function definitions 380 | 381 | bool irsdk_startup(); 382 | void irsdk_shutdown(); 383 | 384 | bool irsdk_getNewData(char *data); 385 | bool irsdk_waitForDataReady(int timeOut, char *data); 386 | bool irsdk_isConnected(); 387 | 388 | const irsdk_header *irsdk_getHeader(); 389 | const char *irsdk_getData(int index); 390 | const char *irsdk_getSessionInfoStr(); 391 | int irsdk_getSessionInfoStrUpdate(); // incrementing index that indicates new session info string 392 | 393 | const irsdk_varHeader *irsdk_getVarHeaderPtr(); 394 | const irsdk_varHeader *irsdk_getVarHeaderEntry(int index); 395 | 396 | int irsdk_varNameToIndex(const char *name); 397 | int irsdk_varNameToOffset(const char *name); 398 | 399 | //---- 400 | // Remote controll the sim by sending these windows messages 401 | // camera and replay commands only work when you are out of your car, 402 | // pit commands only work when in your car 403 | enum irsdk_BroadcastMsg 404 | { 405 | irsdk_BroadcastCamSwitchPos = 0, // car position, group, camera 406 | irsdk_BroadcastCamSwitchNum, // driver #, group, camera 407 | irsdk_BroadcastCamSetState, // irsdk_CameraState, unused, unused 408 | irsdk_BroadcastReplaySetPlaySpeed, // speed, slowMotion, unused 409 | irskd_BroadcastReplaySetPlayPosition, // irsdk_RpyPosMode, Frame Number (high, low) 410 | irsdk_BroadcastReplaySearch, // irsdk_RpySrchMode, unused, unused 411 | irsdk_BroadcastReplaySetState, // irsdk_RpyStateMode, unused, unused 412 | irsdk_BroadcastReloadTextures, // irsdk_ReloadTexturesMode, carIdx, unused 413 | irsdk_BroadcastChatComand, // irsdk_ChatCommandMode, subCommand, unused 414 | irsdk_BroadcastPitCommand, // irsdk_PitCommandMode, parameter 415 | irsdk_BroadcastTelemCommand, // irsdk_TelemCommandMode, unused, unused 416 | irsdk_BroadcastFFBCommand, // irsdk_FFBCommandMode, value (float, high, low) 417 | irsdk_BroadcastReplaySearchSessionTime, // sessionNum, sessionTimeMS (high, low) 418 | irsdk_BroadcastVideoCapture, // irsdk_VideoCaptureMode, unused, unused 419 | irsdk_BroadcastLast // unused placeholder 420 | }; 421 | 422 | enum irsdk_ChatCommandMode 423 | { 424 | irsdk_ChatCommand_Macro = 0, // pass in a number from 1-15 representing the chat macro to launch 425 | irsdk_ChatCommand_BeginChat, // Open up a new chat window 426 | irsdk_ChatCommand_Reply, // Reply to last private chat 427 | irsdk_ChatCommand_Cancel // Close chat window 428 | }; 429 | 430 | enum irsdk_PitCommandMode // this only works when the driver is in the car 431 | { 432 | irsdk_PitCommand_Clear = 0, // Clear all pit checkboxes 433 | irsdk_PitCommand_WS, // Clean the winshield, using one tear off 434 | irsdk_PitCommand_Fuel, // Add fuel, optionally specify the amount to add in liters or pass '0' to use existing amount 435 | irsdk_PitCommand_LF, // Change the left front tire, optionally specifying the pressure in KPa or pass '0' to use existing pressure 436 | irsdk_PitCommand_RF, // right front 437 | irsdk_PitCommand_LR, // left rear 438 | irsdk_PitCommand_RR, // right rear 439 | irsdk_PitCommand_ClearTires, // Clear tire pit checkboxes 440 | irsdk_PitCommand_FR, // Request a fast repair 441 | irsdk_PitCommand_ClearWS, // Uncheck Clean the winshield checkbox 442 | irsdk_PitCommand_ClearFR, // Uncheck request a fast repair 443 | irsdk_PitCommand_ClearFuel, // Uncheck add fuel 444 | }; 445 | 446 | enum irsdk_TelemCommandMode // You can call this any time, but telemtry only records when driver is in there car 447 | { 448 | irsdk_TelemCommand_Stop = 0, // Turn telemetry recording off 449 | irsdk_TelemCommand_Start, // Turn telemetry recording on 450 | irsdk_TelemCommand_Restart, // Write current file to disk and start a new one 451 | }; 452 | 453 | enum irsdk_RpyStateMode 454 | { 455 | irsdk_RpyState_EraseTape = 0, // clear any data in the replay tape 456 | irsdk_RpyState_Last // unused place holder 457 | }; 458 | 459 | enum irsdk_ReloadTexturesMode 460 | { 461 | irsdk_ReloadTextures_All = 0, // reload all textuers 462 | irsdk_ReloadTextures_CarIdx // reload only textures for the specific carIdx 463 | }; 464 | 465 | // Search replay tape for events 466 | enum irsdk_RpySrchMode 467 | { 468 | irsdk_RpySrch_ToStart = 0, 469 | irsdk_RpySrch_ToEnd, 470 | irsdk_RpySrch_PrevSession, 471 | irsdk_RpySrch_NextSession, 472 | irsdk_RpySrch_PrevLap, 473 | irsdk_RpySrch_NextLap, 474 | irsdk_RpySrch_PrevFrame, 475 | irsdk_RpySrch_NextFrame, 476 | irsdk_RpySrch_PrevIncident, 477 | irsdk_RpySrch_NextIncident, 478 | irsdk_RpySrch_Last // unused placeholder 479 | }; 480 | 481 | enum irsdk_RpyPosMode 482 | { 483 | irsdk_RpyPos_Begin = 0, 484 | irsdk_RpyPos_Current, 485 | irsdk_RpyPos_End, 486 | irsdk_RpyPos_Last // unused placeholder 487 | }; 488 | 489 | enum irsdk_FFBCommandMode // You can call this any time 490 | { 491 | irsdk_FFBCommand_MaxForce = 0, // Set the maximum force when mapping steering torque force to direct input units (float in Nm) 492 | irsdk_FFBCommand_Last // unused placeholder 493 | }; 494 | 495 | // irsdk_BroadcastCamSwitchPos or irsdk_BroadcastCamSwitchNum camera focus defines 496 | // pass these in for the first parameter to select the 'focus at' types in the camera system. 497 | enum irsdk_csMode 498 | { 499 | irsdk_csFocusAtIncident = -3, 500 | irsdk_csFocusAtLeader = -2, 501 | irsdk_csFocusAtExiting = -1, 502 | // ctFocusAtDriver + car number... 503 | irsdk_csFocusAtDriver = 0 504 | }; 505 | 506 | enum irsdk_VideoCaptureMode 507 | { 508 | irsdk_VideoCapture_TriggerScreenShot = 0, // save a screenshot to disk 509 | irsdk_VideoCaptuer_StartVideoCapture, // start capturing video 510 | irsdk_VideoCaptuer_EndVideoCapture, // stop capturing video 511 | irsdk_VideoCaptuer_ToggleVideoCapture, // toggle video capture on/off 512 | irsdk_VideoCaptuer_ShowVideoTimer, // show video timer in upper left corner of display 513 | irsdk_VideoCaptuer_HideVideoTimer, // hide video timer 514 | }; 515 | 516 | //send a remote controll message to the sim 517 | // var1, var2, and var3 are all 16 bits signed 518 | void irsdk_broadcastMsg(irsdk_BroadcastMsg msg, int var1, int var2, int var3); 519 | // var2 can be a full 32 bits 520 | void irsdk_broadcastMsg(irsdk_BroadcastMsg msg, int var1, int var2); 521 | // var2 can be a full 32 bit float 522 | void irsdk_broadcastMsg(irsdk_BroadcastMsg msg, int var1, float var2); 523 | 524 | // add a leading zero (or zeros) to a car number 525 | // to encode car #001 call padCarNum(1,2) 526 | int irsdk_padCarNum(int num, int zero); 527 | 528 | #endif //IRSDK_DEFINES_H 529 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021-2022 L. E. Spalt 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #pragma comment(lib,"dxgi.lib") 26 | #pragma comment(lib,"d3d11.lib") 27 | #pragma comment(lib,"d2d1.lib") 28 | #pragma comment(lib,"dcomp.lib") 29 | #pragma comment(lib,"dwrite.lib") 30 | 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include "iracing.h" 43 | #include "Config.h" 44 | #include "OverlayCover.h" 45 | #include "OverlayRelative.h" 46 | #include "OverlayInputs.h" 47 | #include "OverlayStandings.h" 48 | #include "OverlayDebug.h" 49 | #include "OverlayDDU.h" 50 | #include "OverlayRadar.h" 51 | #include "util.h" 52 | 53 | // Global var 54 | bool g_dbgOverlayEnabled; 55 | #if defined(_DEBUG) or defined(DEBUG_OVERLAY_TIME) 56 | #include 57 | 58 | //#define DEBUG_DUMP_VARS 59 | 60 | #endif 61 | using namespace Microsoft::WRL; 62 | using namespace std; 63 | 64 | enum class Hotkey 65 | { 66 | Debug, 67 | UiEdit, 68 | Standings, 69 | DDU, 70 | TargetLapUp, 71 | TargetLapDown, 72 | Inputs, 73 | Relative, 74 | Cover, 75 | Radar 76 | }; 77 | 78 | static void registerHotkeys() 79 | { 80 | UnregisterHotKey( NULL, (int)Hotkey::Debug ); 81 | UnregisterHotKey( NULL, (int)Hotkey::UiEdit ); 82 | UnregisterHotKey( NULL, (int)Hotkey::Standings ); 83 | UnregisterHotKey( NULL, (int)Hotkey::DDU ); 84 | UnregisterHotKey(NULL, (int)Hotkey::TargetLapUp); 85 | UnregisterHotKey(NULL, (int)Hotkey::TargetLapDown); 86 | UnregisterHotKey( NULL, (int)Hotkey::Inputs ); 87 | UnregisterHotKey( NULL, (int)Hotkey::Relative ); 88 | UnregisterHotKey( NULL, (int)Hotkey::Cover ); 89 | UnregisterHotKey(NULL, (int)Hotkey::Radar ); 90 | 91 | UINT vk, mod; 92 | 93 | if (parseHotkey("ctrl+9", &mod, &vk)) 94 | RegisterHotKey(NULL, (int)Hotkey::Debug, mod, vk); 95 | 96 | if( parseHotkey( g_cfg.getString("General","ui_edit_hotkey","alt-j"),&mod,&vk) ) 97 | RegisterHotKey( NULL, (int)Hotkey::UiEdit, mod, vk ); 98 | 99 | if( parseHotkey( g_cfg.getString("OverlayStandings","toggle_hotkey","ctrl-space"),&mod,&vk) ) 100 | RegisterHotKey( NULL, (int)Hotkey::Standings, mod, vk ); 101 | 102 | if( parseHotkey( g_cfg.getString("OverlayDDU","toggle_hotkey","ctrl-1"),&mod,&vk) ) 103 | RegisterHotKey( NULL, (int)Hotkey::DDU, mod, vk ); 104 | 105 | if (parseHotkey(g_cfg.getString("OverlayDDU", "target_lap_up", "ctrl-."), &mod, &vk)) 106 | RegisterHotKey(NULL, (int)Hotkey::TargetLapUp, mod, vk); 107 | if (parseHotkey(g_cfg.getString("OverlayDDU", "target_lap_down", "ctrl-,"), &mod, &vk)) 108 | RegisterHotKey(NULL, (int)Hotkey::TargetLapDown, mod, vk); 109 | 110 | if( parseHotkey( g_cfg.getString("OverlayInputs","toggle_hotkey","ctrl-2"),&mod,&vk) ) 111 | RegisterHotKey( NULL, (int)Hotkey::Inputs, mod, vk ); 112 | 113 | if( parseHotkey( g_cfg.getString("OverlayRelative","toggle_hotkey","ctrl-3"),&mod,&vk) ) 114 | RegisterHotKey( NULL, (int)Hotkey::Relative, mod, vk ); 115 | 116 | if( parseHotkey( g_cfg.getString("OverlayCover","toggle_hotkey","ctrl-4"),&mod,&vk) ) 117 | RegisterHotKey( NULL, (int)Hotkey::Cover, mod, vk ); 118 | 119 | if (parseHotkey(g_cfg.getString("OverlayRadar", "toggle_hotkey", "ctrl-5"), &mod, &vk)) 120 | RegisterHotKey(NULL, (int)Hotkey::Radar, mod, vk); 121 | } 122 | 123 | static void handleConfigChange( vector overlays, ConnectionStatus status ) 124 | { 125 | registerHotkeys(); 126 | 127 | ir_handleConfigChange(); 128 | 129 | for( Overlay* o : overlays ) 130 | { 131 | o->enable( g_cfg.getBool(o->getName(),"enabled",true) && ( 132 | status == ConnectionStatus::DRIVING || 133 | status == ConnectionStatus::CONNECTED && o->canEnableWhileNotDriving() || 134 | status == ConnectionStatus::DISCONNECTED && o->canEnableWhileDisconnected() 135 | )); 136 | o->configChanged(); 137 | } 138 | } 139 | 140 | static void giveFocusToIracing() 141 | { 142 | HWND hwnd = FindWindow( "SimWinClass", NULL ); 143 | if( hwnd ) 144 | SetForegroundWindow( hwnd ); 145 | } 146 | 147 | // Cargar una imagen .png utilizando WIC 148 | void LoadPNGImage(const wchar_t* filePath, ComPtr& wicFactory, ComPtr& decoder, ComPtr& frame, ComPtr& formatConverter) { 149 | 150 | // Carga el archivo PNG utilizando el decodificador de mapas de bits WIC 151 | wicFactory->CreateDecoderFromFilename(filePath, nullptr, GENERIC_READ, WICDecodeMetadataCacheOnDemand, decoder.GetAddressOf()); 152 | 153 | // Obtiene el primer fotograma del archivo PNG 154 | decoder->GetFrame(0, frame.GetAddressOf()); 155 | 156 | // Convierte el formato del fotograma a 32 bpp ARGB 157 | wicFactory->CreateFormatConverter(formatConverter.GetAddressOf()); 158 | formatConverter->Initialize(frame.Get(), GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, nullptr, 0.0f, WICBitmapPaletteTypeCustom); 159 | 160 | /*if (FAILED(hr)) 161 | { 162 | // Error al crear el decodificador 163 | return hr; 164 | }*/ 165 | 166 | } 167 | 168 | static bool LoadCarIcons(map& carBrandIconsMap) { 169 | const wchar_t* directory = L"./carIcons"; 170 | 171 | CoInitialize(nullptr); 172 | 173 | ComPtr wicFactory; 174 | ComPtr decoder; 175 | ComPtr frame; 176 | ComPtr formatConverter; 177 | 178 | CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(wicFactory.GetAddressOf())); 179 | 180 | if (filesystem::exists(directory) && filesystem::is_directory(directory)) { 181 | for (const auto& iconFilename : filesystem::directory_iterator(directory)) { 182 | if (filesystem::is_regular_file(iconFilename)) { 183 | std::string ruta = iconFilename.path().string(); 184 | 185 | int length = MultiByteToWideChar(CP_UTF8, 0, ruta.c_str(), -1, NULL, 0); 186 | wchar_t* path_wchar = new wchar_t[length]; 187 | MultiByteToWideChar(CP_UTF8, 0, ruta.c_str(), -1, path_wchar, length); 188 | 189 | LoadPNGImage(path_wchar, wicFactory, decoder, frame, formatConverter); 190 | 191 | std::regex pattern("\\.\\w+$"); 192 | string brandName = iconFilename.path().filename().string(); 193 | brandName = regex_replace(brandName, pattern, ""); 194 | brandName = toLowerCase(brandName); 195 | carBrandIconsMap[brandName] = formatConverter.Get(); 196 | } 197 | } 198 | return true; 199 | } 200 | else { 201 | cout << "#### Cars icons folder not found! ####" << endl; 202 | return false; 203 | } 204 | 205 | } 206 | 207 | int main() 208 | { 209 | #if defined(_DEBUG) or defined(DEBUG_OVERLAY_TIME) 210 | auto debugtime_start = std::chrono::high_resolution_clock::now(); 211 | auto debugtime_finish = debugtime_start; 212 | bool debugtimer_started = false; 213 | float debugtimeavg = 0.0f; 214 | long long debugtimediff; 215 | #endif 216 | // Bump priority up so we get time from the sim 217 | SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); 218 | 219 | // Disable QuickEdit Mode, so we don't hang the program accidentally 220 | DWORD prev_mode; 221 | HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); 222 | GetConsoleMode(hStdin, &prev_mode); 223 | SetConsoleMode(hStdin, ENABLE_EXTENDED_FLAGS | 224 | (prev_mode & ~ENABLE_QUICK_EDIT_MODE)); 225 | 226 | setlocale(LC_ALL, ""); 227 | 228 | // Load the config and watch it for changes 229 | g_cfg.load(); 230 | g_cfg.watchForChanges(); 231 | 232 | // Load car brand icons 233 | bool carBrandIconsLoaded = false; 234 | map carBrandIconsMap; 235 | if (g_cfg.getBool("OverlayStandings", "show_car_brand", true)) { 236 | carBrandIconsLoaded = LoadCarIcons(carBrandIconsMap); 237 | } 238 | 239 | // Register global hotkeys 240 | registerHotkeys(); 241 | 242 | printf("\n====================================================================================\n"); 243 | printf("Welcome to iRon! This app provides a few simple overlays for iRacing.\n\n"); 244 | printf("NOTE: Most overlays are only active when iRacing is running and the car is on track.\n\n"); 245 | printf("Current hotkeys:\n"); 246 | printf(" Move and resize overlays: %s\n", g_cfg.getString("General","ui_edit_hotkey","").c_str() ); 247 | printf(" Toggle standings overlay: %s\n", g_cfg.getString("OverlayStandings","toggle_hotkey","").c_str() ); 248 | printf(" Toggle DDU overlay: %s\n", g_cfg.getString("OverlayDDU","toggle_hotkey","").c_str() ); 249 | printf(" DDU Target lap up %s\n", g_cfg.getString("OverlayDDU", "target_lap_up", "").c_str()); 250 | printf(" DDU Target lap down %s\n", g_cfg.getString("OverlayDDU", "target_lap_down", "").c_str()); 251 | printf(" Toggle inputs overlay: %s\n", g_cfg.getString("OverlayInputs","toggle_hotkey","").c_str() ); 252 | printf(" Toggle relative overlay: %s\n", g_cfg.getString("OverlayRelative","toggle_hotkey","").c_str() ); 253 | printf(" Toggle cover overlay: %s\n", g_cfg.getString("OverlayCover","toggle_hotkey","").c_str() ); 254 | printf(" Toggle radar overlay: %s\n", g_cfg.getString("OverlayRadar", "toggle_hotkey", "").c_str()); 255 | printf("\niRon will generate a file called \'config.json\' in its current directory. This file\n"\ 256 | "stores your settings. You can edit the file at any time, even while iRon is running,\n"\ 257 | "to customize your overlays and hotkeys.\n\n"); 258 | printf("To exit iRon, simply close this console window.\n\n"); 259 | printf("For the latest version ***of this fork*** or to submit bug reports, go to:\n\n https://github.com/diegoschneider/iRon\n\n"); 260 | printf("Many thanks to lespalt (https://github.com/lespalt/iRon) and frmjar (https://github.com/frmjar/iRon) for their work!\n\n"); 261 | printf("Happy Racing!\n"); 262 | printf("====================================================================================\n\n"); 263 | 264 | #ifdef _DEBUG 265 | printf(" ################################################\n"); 266 | printf(" # DEBUG BUILD! PERFORMANCE WILL SUFFER! #\n"); 267 | printf(" # DEBUG BUILD! PERFORMANCE WILL SUFFER! #\n"); 268 | printf(" # DEBUG BUILD! PERFORMANCE WILL SUFFER! #\n"); 269 | printf(" # DEBUG BUILD! PERFORMANCE WILL SUFFER! #\n"); 270 | printf(" ################################################\n"); 271 | printf(" Toggle debug overlay: ctrl+9\n"); 272 | #endif 273 | 274 | // Create D3D Device 275 | Microsoft::WRL::ComPtr m_d3dDevice; 276 | // D3D11 device 277 | HRCHECK(D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, D3D11_CREATE_DEVICE_SINGLETHREADED | D3D11_CREATE_DEVICE_BGRA_SUPPORT, NULL, 0, D3D11_SDK_VERSION, &m_d3dDevice, NULL, NULL)); 278 | 279 | // Create overlays 280 | vector overlays; 281 | overlays.push_back( new OverlayCover(m_d3dDevice) ); 282 | overlays.push_back( new OverlayRelative(m_d3dDevice) ); 283 | overlays.push_back( new OverlayInputs(m_d3dDevice) ); 284 | overlays.push_back( new OverlayStandings(m_d3dDevice, carBrandIconsMap, carBrandIconsLoaded) ); 285 | overlays.push_back( new OverlayDDU(m_d3dDevice) ); 286 | overlays.push_back(new OverlayRadar(m_d3dDevice) ); 287 | 288 | #ifdef _DEBUG 289 | overlays.push_back( new OverlayDebug(m_d3dDevice) ); 290 | g_dbgOverlayEnabled = g_cfg.getBool("OverlayDebug", "enabled", true); 291 | #endif 292 | 293 | ConnectionStatus status = ConnectionStatus::UNKNOWN; 294 | bool uiEdit = false; 295 | unsigned frameCnt = 0; 296 | #if defined(_DEBUG) or defined(DEBUG_OVERLAY_TIME) 297 | // Added in debug only for now 298 | std::chrono::steady_clock::time_point loopTimeStart, loopTimeEnd; 299 | long long loopTimeDiff; 300 | #endif 301 | 302 | while( true ) 303 | { 304 | ConnectionStatus prevStatus = status; 305 | SessionType prevSessionType = ir_session.sessionType; 306 | 307 | // Refresh connection and session info 308 | status = ir_tick(); 309 | #if defined(_DEBUG) or defined(DEBUG_OVERLAY_TIME) 310 | // Added in debug only for now 311 | loopTimeStart = std::chrono::high_resolution_clock::now(); 312 | #endif 313 | if( status != prevStatus ) 314 | { 315 | if( status == ConnectionStatus::DISCONNECTED ) 316 | printf("Waiting for iRacing connection...\n"); 317 | else 318 | printf("iRacing connected (%s)\n", ConnectionStatusStr[(int)status]); 319 | 320 | // Enable user-selected overlays, but only if we're driving 321 | handleConfigChange( overlays, status ); 322 | 323 | #if defined(_DEBUG) and defined(DEBUG_DUMP_VARS) 324 | ir_printVariables(); 325 | #endif 326 | } 327 | 328 | if( ir_session.sessionType != prevSessionType ) 329 | { 330 | for( Overlay* o : overlays ) 331 | o->sessionChanged(); 332 | } 333 | 334 | dbg( "connection status: %s, session type: %s, session state: %d, pace mode: %d, on track: %d, flags: 0x%X", ConnectionStatusStr[(int)status], SessionTypeStr[(int)ir_session.sessionType], ir_SessionState.getInt(), ir_PaceMode.getInt(), (int)ir_IsOnTrackCar.getBool(), ir_SessionFlags.getInt() ); 335 | 336 | // Update/render overlays 337 | { 338 | if( !g_cfg.getBool("General", "performance_mode_30hz", false) ) 339 | { 340 | // Update everything every frame, roughly every 16ms (~60Hz) 341 | for( Overlay* o : overlays ) 342 | o->update(); 343 | } 344 | else 345 | { 346 | // To save perf, update half of the (enabled) overlays on even frames and the other half on odd, for ~30Hz overall 347 | int cnt = 0; 348 | for( Overlay* o : overlays ) 349 | { 350 | if( o->isEnabled() ) 351 | cnt++; 352 | 353 | if( (cnt & 1) == (frameCnt & 1) ) 354 | o->update(); 355 | } 356 | } 357 | } 358 | 359 | // Watch for config change signal 360 | if( g_cfg.hasChanged() ) 361 | { 362 | g_cfg.load(); 363 | handleConfigChange( overlays, status ); 364 | } 365 | 366 | // Message pump 367 | MSG msg = {}; 368 | while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 369 | { 370 | // Handle hotkeys 371 | if( msg.message == WM_HOTKEY ) 372 | { 373 | if( msg.wParam == (int)Hotkey::UiEdit ) 374 | { 375 | uiEdit = !uiEdit; 376 | for( Overlay* o : overlays ) 377 | o->enableUiEdit( uiEdit ); 378 | 379 | // When we're exiting edit mode, attempt to make iRacing the foreground window again for best perf 380 | // without the user having to manually click into iRacing. 381 | if( !uiEdit ) 382 | giveFocusToIracing(); 383 | } 384 | else 385 | { 386 | switch( msg.wParam ) 387 | { 388 | case (int)Hotkey::Standings: 389 | g_cfg.setBool( "OverlayStandings", "enabled", !g_cfg.getBool("OverlayStandings","enabled",true) ); 390 | break; 391 | case (int)Hotkey::DDU: 392 | g_cfg.setBool( "OverlayDDU", "enabled", !g_cfg.getBool("OverlayDDU","enabled",true) ); 393 | break; 394 | case (int)Hotkey::Inputs: 395 | g_cfg.setBool( "OverlayInputs", "enabled", !g_cfg.getBool("OverlayInputs","enabled",true) ); 396 | break; 397 | case (int)Hotkey::Relative: 398 | g_cfg.setBool( "OverlayRelative", "enabled", !g_cfg.getBool("OverlayRelative","enabled",true) ); 399 | break; 400 | case (int)Hotkey::Cover: 401 | g_cfg.setBool( "OverlayCover", "enabled", !g_cfg.getBool("OverlayCover","enabled",true) ); 402 | break; 403 | case (int)Hotkey::Radar: 404 | g_cfg.setBool("OverlayRadar", "enabled", !g_cfg.getBool("OverlayRadar", "enabled", true)); 405 | break; 406 | 407 | case (int)Hotkey::TargetLapUp: 408 | g_cfg.setInt("OverlayDDU", "fuel_target_lap", g_cfg.getInt("OverlayDDU", "fuel_target_lap", 0) + 1); 409 | break; 410 | case (int)Hotkey::TargetLapDown: 411 | g_cfg.setInt("OverlayDDU", "fuel_target_lap", std::max( g_cfg.getInt("OverlayDDU", "fuel_target_lap", 0) - 1, 0) ); 412 | break; 413 | 414 | case (int)Hotkey::Debug: 415 | const bool newDebugStatus = !g_cfg.getBool("OverlayDebug", "enabled", true); 416 | g_cfg.setBool( "OverlayDebug", "enabled", newDebugStatus); 417 | g_dbgOverlayEnabled = newDebugStatus; 418 | break; 419 | } 420 | 421 | g_cfg.save(); 422 | handleConfigChange( overlays, status ); 423 | } 424 | } 425 | 426 | TranslateMessage(&msg); 427 | DispatchMessage(&msg); 428 | } 429 | 430 | frameCnt++; 431 | 432 | #if defined(_DEBUG) or defined(DEBUG_OVERLAY_TIME) 433 | // Added in debug for now 434 | using micro = std::chrono::microseconds; 435 | loopTimeEnd = std::chrono::high_resolution_clock::now(); 436 | loopTimeDiff = std::chrono::duration_cast(loopTimeEnd - loopTimeStart).count(); 437 | 438 | // This should remain in debug - START 439 | if (!debugtimer_started) { 440 | debugtimediff = std::chrono::duration_cast(loopTimeEnd - debugtime_start).count(); 441 | std::cout << "First run took " << debugtimediff << " microseconds\n"; 442 | debugtimer_started = true; 443 | } 444 | debugtimeavg = (debugtimeavg / 10) * 9 + (float)(loopTimeDiff) / 10; 445 | dbg("Main loop took %.4f (%i) microseconds", debugtimeavg, loopTimeDiff); 446 | # if defined(DEBUG_OVERLAY_TIME) 447 | std::cout << std::format("Main loop took {:.4f} ({}) microseconds", debugtimeavg, loopTimeDiff) << std::endl; 448 | # endif 449 | debugtime_start = std::chrono::high_resolution_clock::now(); 450 | // This should remain in debug - END 451 | #endif 452 | } 453 | 454 | for( Overlay* o : overlays ) 455 | delete o; 456 | 457 | // Libera los recursos de COM 458 | CoUninitialize(); 459 | } 460 | -------------------------------------------------------------------------------- /iracing.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021-2022 L. E. Spalt 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #pragma once 26 | 27 | #include "irsdk/irsdk_defines.h" 28 | #include "irsdk/irsdk_client.h" 29 | #include "irsdk/yaml_parser.h" 30 | #include 31 | #include "util.h" 32 | 33 | #define IR_MAX_CARS 64 34 | 35 | using namespace std; 36 | 37 | enum class ConnectionStatus 38 | { 39 | UNKNOWN = 0, 40 | DISCONNECTED, 41 | CONNECTED, 42 | DRIVING 43 | }; 44 | static const char* const ConnectionStatusStr[] = {"UNKNOWN","DISCONNECTED","CONNECTED","DRIVING"}; 45 | 46 | enum class SessionType 47 | { 48 | UNKNOWN = 0, 49 | PRACTICE, 50 | QUALIFY, 51 | RACE 52 | }; 53 | static const char* const SessionTypeStr[] = {"UNKNOWN","PRACTICE","QUALIFY","RACE"}; 54 | 55 | struct SessionPosTimes 56 | { 57 | float lastTime = 0; 58 | float fastestTime = 0; 59 | int position = 0; 60 | }; 61 | 62 | struct Car 63 | { 64 | string userName; 65 | string teamName; 66 | int carNumber = 0; 67 | string carNumberStr; 68 | string carName; 69 | int carID; 70 | string licenseStr; 71 | char licenseChar = 'R'; 72 | float licenseSR = 0; 73 | string licenseColStr; 74 | float4 licenseCol = float4(0,0,0,1); 75 | string classColStr; 76 | float4 classCol = float4(0, 0, 0, 1); 77 | int classId = 0; 78 | int irating = 0; 79 | int isSelf = 0; 80 | int isPaceCar = 0; 81 | int isSpectator = 0; 82 | int isBuddy = 0; 83 | int isFlagged = 0; 84 | int incidentCount = 0; 85 | float carClassEstLapTime = 0; 86 | int lastLapInPits = 0; 87 | SessionPosTimes practice; 88 | SessionPosTimes qualy; 89 | SessionPosTimes race; 90 | }; 91 | 92 | struct Session 93 | { 94 | SessionType sessionType = SessionType::UNKNOWN; 95 | bool isReplay; 96 | Car cars[IR_MAX_CARS]; 97 | int numCarClasses = -1; 98 | int driverCarIdx = -1; 99 | int sof = 0; 100 | int subsessionId = 0; 101 | int isFixedSetup = 0; 102 | int isUnlimitedTime = 0; 103 | int isUnlimitedLaps = 0; 104 | float fuelMaxLtr = 0; 105 | float rpmIdle = 0; 106 | float rpmRedline = 0; 107 | float rpmSLFirst = 0; 108 | float rpmSLShift = 0; 109 | float rpmSLLast = 0; 110 | float rpmSLBlink = 0; 111 | }; 112 | 113 | extern irsdkCVar ir_SessionTime; // double[1] Seconds since session start (s) 114 | extern irsdkCVar ir_SessionTick; // int[1] Current update number () 115 | extern irsdkCVar ir_SessionNum; // int[1] Session number () 116 | extern irsdkCVar ir_SessionState; // int[1] Session state (irsdk_SessionState) 117 | extern irsdkCVar ir_SessionUniqueID; // int[1] Session ID () 118 | extern irsdkCVar ir_SessionFlags; // bitfield[1] Session flags (irsdk_Flags) 119 | extern irsdkCVar ir_SessionTimeRemain; // double[1] Seconds left till session ends (s) 120 | extern irsdkCVar ir_SessionLapsRemain; // int[1] Old laps left till session ends use SessionLapsRemainEx () 121 | extern irsdkCVar ir_SessionLapsRemainEx; // int[1] New improved laps left till session ends () 122 | extern irsdkCVar ir_SessionTimeTotal; // double[1] Total number of seconds in session (s) 123 | extern irsdkCVar ir_SessionLapsTotal; // int[1] Total number of laps in session () 124 | extern irsdkCVar ir_SessionTimeOfDay; // float[1] Time of day in seconds (s) 125 | extern irsdkCVar ir_RadioTransmitCarIdx; // int[1] The car index of the current person speaking on the radio () 126 | extern irsdkCVar ir_RadioTransmitRadioIdx; // int[1] The radio index of the current person speaking on the radio () 127 | extern irsdkCVar ir_RadioTransmitFrequencyIdx; // int[1] The frequency index of the current person speaking on the radio () 128 | extern irsdkCVar ir_DisplayUnits; // int[1] Default units for the user interface 0 = english 1 = metric () 129 | extern irsdkCVar ir_DriverMarker; // bool[1] Driver activated flag () 130 | extern irsdkCVar ir_PushToPass; // bool[1] Push to pass button state () 131 | extern irsdkCVar ir_ManualBoost; // bool[1] Hybrid manual boost state () 132 | extern irsdkCVar ir_ManualNoBoost; // bool[1] Hybrid manual no boost state () 133 | extern irsdkCVar ir_IsOnTrack; // bool[1] 1=Car on track physics running with player in car () 134 | extern irsdkCVar ir_IsReplayPlaying; // bool[1] 0=replay not playing 1=replay playing () 135 | extern irsdkCVar ir_ReplayFrameNum; // int[1] Integer replay frame number (60 per second) () 136 | extern irsdkCVar ir_ReplayFrameNumEnd; // int[1] Integer replay frame number from end of tape () 137 | extern irsdkCVar ir_IsDiskLoggingEnabled; // bool[1] 0=disk based telemetry turned off 1=turned on () 138 | extern irsdkCVar ir_IsDiskLoggingActive; // bool[1] 0=disk based telemetry file not being written 1=being written () 139 | extern irsdkCVar ir_FrameRate; // float[1] Average frames per second (fps) 140 | extern irsdkCVar ir_CpuUsageFG; // float[1] Percent of available tim fg thread took with a 1 sec avg (%) 141 | extern irsdkCVar ir_GpuUsage; // float[1] Percent of available tim gpu took with a 1 sec avg (%) 142 | extern irsdkCVar ir_ChanAvgLatency; // float[1] Communications average latency (s) 143 | extern irsdkCVar ir_ChanLatency; // float[1] Communications latency (s) 144 | extern irsdkCVar ir_ChanQuality; // float[1] Communications quality (%) 145 | extern irsdkCVar ir_ChanPartnerQuality; // float[1] Partner communications quality (%) 146 | extern irsdkCVar ir_CpuUsageBG; // float[1] Percent of available tim bg thread took with a 1 sec avg (%) 147 | extern irsdkCVar ir_ChanClockSkew; // float[1] Communications server clock skew (s) 148 | extern irsdkCVar ir_MemPageFaultSec; // float[1] Memory page faults per second () 149 | extern irsdkCVar ir_PlayerCarPosition; // int[1] Players position in race () 150 | extern irsdkCVar ir_PlayerCarClassPosition; // int[1] Players class position in race () 151 | extern irsdkCVar ir_PlayerCarClass; // int[1] Player car class id () 152 | extern irsdkCVar ir_PlayerTrackSurface; // int[1] Players car track surface type (irsdk_TrkLoc) 153 | extern irsdkCVar ir_PlayerTrackSurfaceMaterial; // int[1] Players car track surface material type (irsdk_TrkSurf) 154 | extern irsdkCVar ir_PlayerCarIdx; // int[1] Players carIdx () 155 | extern irsdkCVar ir_PlayerCarTeamIncidentCount; // int[1] Players team incident count for this session () 156 | extern irsdkCVar ir_PlayerCarMyIncidentCount; // int[1] Players own incident count for this session () 157 | extern irsdkCVar ir_PlayerCarDriverIncidentCount; // int[1] Teams current drivers incident count for this session () 158 | extern irsdkCVar ir_PlayerCarWeightPenalty; // float[1] Players weight penalty (kg) 159 | extern irsdkCVar ir_PlayerCarPowerAdjust; // float[1] Players power adjust (%) 160 | extern irsdkCVar ir_PlayerCarDryTireSetLimit; // int[1] Players dry tire set limit () 161 | extern irsdkCVar ir_PlayerCarTowTime; // float[1] Players car is being towed if time is greater than zero (s) 162 | extern irsdkCVar ir_PlayerCarInPitStall; // bool[1] Players car is properly in there pitstall () 163 | extern irsdkCVar ir_PlayerCarPitSvStatus; // int[1] Players car pit service status bits (irsdk_PitSvStatus) 164 | extern irsdkCVar ir_PlayerTireCompound; // int[1] Players car current tire compound () 165 | extern irsdkCVar ir_PlayerFastRepairsUsed; // int[1] Players car number of fast repairs used () 166 | extern irsdkCVar ir_CarIdxLap; // int[64] Laps started by car index () 167 | extern irsdkCVar ir_CarIdxLapCompleted; // int[64] Laps completed by car index () 168 | extern irsdkCVar ir_CarIdxLapDistPct; // float[64] Percentage distance around lap by car index (%) 169 | extern irsdkCVar ir_CarIdxTrackSurface; // int[64] Track surface type by car index (irsdk_TrkLoc) 170 | extern irsdkCVar ir_CarIdxTrackSurfaceMaterial; // int[64] Track surface material type by car index (irsdk_TrkSurf) 171 | extern irsdkCVar ir_CarIdxOnPitRoad; // bool[64] On pit road between the cones by car index () 172 | extern irsdkCVar ir_CarIdxPosition; // int[64] Cars position in race by car index () 173 | extern irsdkCVar ir_CarIdxClassPosition; // int[64] Cars class position in race by car index () 174 | extern irsdkCVar ir_CarIdxClass; // int[64] Cars class id by car index () 175 | extern irsdkCVar ir_CarIdxF2Time; // float[64] Race time behind leader or fastest lap time otherwise (s) 176 | extern irsdkCVar ir_CarIdxEstTime; // float[64] Estimated time to reach current location on track (s) 177 | extern irsdkCVar ir_CarIdxLastLapTime; // float[64] Cars last lap time (s) 178 | extern irsdkCVar ir_CarIdxBestLapTime; // float[64] Cars best lap time (s) 179 | extern irsdkCVar ir_CarIdxBestLapNum; // int[64] Cars best lap number () 180 | extern irsdkCVar ir_CarIdxTireCompound; // int[64] Cars current tire compound () 181 | extern irsdkCVar ir_CarIdxQualTireCompound; // int[64] Cars Qual tire compound () 182 | extern irsdkCVar ir_CarIdxQualTireCompoundLocked; // bool[64] Cars Qual tire compound is locked-in () 183 | extern irsdkCVar ir_CarIdxFastRepairsUsed; // int[64] How many fast repairs each car has used () 184 | extern irsdkCVar ir_PaceMode; // int[1] Are we pacing or not (irsdk_PaceMode) 185 | extern irsdkCVar ir_CarIdxPaceLine; // int[64] What line cars are pacing in or -1 if not pacing () 186 | extern irsdkCVar ir_CarIdxPaceRow; // int[64] What row cars are pacing in or -1 if not pacing () 187 | extern irsdkCVar ir_CarIdxPaceFlags; // int[64] Pacing status flags for each car (irsdk_PaceFlags) 188 | extern irsdkCVar ir_OnPitRoad; // bool[1] Is the player car on pit road between the cones () 189 | extern irsdkCVar ir_CarIdxSteer; // float[64] Steering wheel angle by car index (rad) 190 | extern irsdkCVar ir_CarIdxRPM; // float[64] Engine rpm by car index (revs/min) 191 | extern irsdkCVar ir_CarIdxGear; // int[64] -1=reverse 0=neutral 1..n=current gear by car index () 192 | extern irsdkCVar ir_SteeringWheelAngle; // float[1] Steering wheel angle (rad) 193 | extern irsdkCVar ir_Throttle; // float[1] 0=off throttle to 1=full throttle (%) 194 | extern irsdkCVar ir_Brake; // float[1] 0=brake released to 1=max pedal force (%) 195 | extern irsdkCVar ir_Clutch; // float[1] 0=disengaged to 1=fully engaged (%) 196 | extern irsdkCVar ir_Gear; // int[1] -1=reverse 0=neutral 1..n=current gear () 197 | extern irsdkCVar ir_RPM; // float[1] Engine rpm (revs/min) 198 | extern irsdkCVar ir_Lap; // int[1] Laps started count () 199 | extern irsdkCVar ir_LapCompleted; // int[1] Laps completed count () 200 | extern irsdkCVar ir_LapDist; // float[1] Meters traveled from S/F this lap (m) 201 | extern irsdkCVar ir_LapDistPct; // float[1] Percentage distance around lap (%) 202 | extern irsdkCVar ir_RaceLaps; // int[1] Laps completed in race () 203 | extern irsdkCVar ir_LapBestLap; // int[1] Players best lap number () 204 | extern irsdkCVar ir_LapBestLapTime; // float[1] Players best lap time (s) 205 | extern irsdkCVar ir_LapLastLapTime; // float[1] Players last lap time (s) 206 | extern irsdkCVar ir_LapCurrentLapTime; // float[1] Estimate of players current lap time as shown in F3 box (s) 207 | extern irsdkCVar ir_LapLasNLapSeq; // int[1] Player num consecutive clean laps completed for N average () 208 | extern irsdkCVar ir_LapLastNLapTime; // float[1] Player last N average lap time (s) 209 | extern irsdkCVar ir_LapBestNLapLap; // int[1] Player last lap in best N average lap time () 210 | extern irsdkCVar ir_LapBestNLapTime; // float[1] Player best N average lap time (s) 211 | extern irsdkCVar ir_LapDeltaToBestLap; // float[1] Delta time for best lap (s) 212 | extern irsdkCVar ir_LapDeltaToBestLap_DD; // float[1] Rate of change of delta time for best lap (s/s) 213 | extern irsdkCVar ir_LapDeltaToBestLap_OK; // bool[1] Delta time for best lap is valid () 214 | extern irsdkCVar ir_LapDeltaToOptimalLap; // float[1] Delta time for optimal lap (s) 215 | extern irsdkCVar ir_LapDeltaToOptimalLap_DD; // float[1] Rate of change of delta time for optimal lap (s/s) 216 | extern irsdkCVar ir_LapDeltaToOptimalLap_OK; // bool[1] Delta time for optimal lap is valid () 217 | extern irsdkCVar ir_LapDeltaToSessionBestLap; // float[1] Delta time for session best lap (s) 218 | extern irsdkCVar ir_LapDeltaToSessionBestLap_DD; // float[1] Rate of change of delta time for session best lap (s/s) 219 | extern irsdkCVar ir_LapDeltaToSessionBestLap_OK; // bool[1] Delta time for session best lap is valid () 220 | extern irsdkCVar ir_LapDeltaToSessionOptimalLap; // float[1] Delta time for session optimal lap (s) 221 | extern irsdkCVar ir_LapDeltaToSessionOptimalLap_DD; // float[1] Rate of change of delta time for session optimal lap (s/s) 222 | extern irsdkCVar ir_LapDeltaToSessionOptimalLap_OK; // bool[1] Delta time for session optimal lap is valid () 223 | extern irsdkCVar ir_LapDeltaToSessionLastlLap; // float[1] Delta time for session last lap (s) 224 | extern irsdkCVar ir_LapDeltaToSessionLastlLap_DD; // float[1] Rate of change of delta time for session last lap (s/s) 225 | extern irsdkCVar ir_LapDeltaToSessionLastlLap_OK; // bool[1] Delta time for session last lap is valid () 226 | extern irsdkCVar ir_Speed; // float[1] GPS vehicle speed (m/s) 227 | extern irsdkCVar ir_Yaw; // float[1] Yaw orientation (rad) 228 | extern irsdkCVar ir_YawNorth; // float[1] Yaw orientation relative to north (rad) 229 | extern irsdkCVar ir_Pitch; // float[1] Pitch orientation (rad) 230 | extern irsdkCVar ir_Roll; // float[1] Roll orientation (rad) 231 | extern irsdkCVar ir_EnterExitReset; // int[1] Indicate action the reset key will take 0 enter 1 exit 2 reset () 232 | extern irsdkCVar ir_TrackTemp; // float[1] Deprecated set to TrackTempCrew (C) 233 | extern irsdkCVar ir_TrackTempCrew; // float[1] Temperature of track measured by crew around track (C) 234 | extern irsdkCVar ir_AirTemp; // float[1] Temperature of air at start/finish line (C) 235 | extern irsdkCVar ir_WeatherType; // int[1] Weather type (0=constant 1=dynamic) () 236 | extern irsdkCVar ir_Skies; // int[1] Skies (0=clear/1=p cloudy/2=m cloudy/3=overcast) () 237 | extern irsdkCVar ir_AirDensity; // float[1] Density of air at start/finish line (kg/m^3) 238 | extern irsdkCVar ir_AirPressure; // float[1] Pressure of air at start/finish line (Hg) 239 | extern irsdkCVar ir_WindVel; // float[1] Wind velocity at start/finish line (m/s) 240 | extern irsdkCVar ir_WindDir; // float[1] Wind direction at start/finish line (rad) 241 | extern irsdkCVar ir_RelativeHumidity; // float[1] Relative Humidity (%) 242 | extern irsdkCVar ir_FogLevel; // float[1] Fog level (%) 243 | extern irsdkCVar ir_DCLapStatus; // int[1] Status of driver change lap requirements () 244 | extern irsdkCVar ir_DCDriversSoFar; // int[1] Number of team drivers who have run a stint () 245 | extern irsdkCVar ir_OkToReloadTextures; // bool[1] True if it is ok to reload car textures at this time () 246 | extern irsdkCVar ir_LoadNumTextures; // bool[1] True if the car_num texture will be loaded () 247 | extern irsdkCVar ir_CarLeftRight; // bitfield[1] Notify if car is to the left or right of driver (irsdk_CarLeftRight) 248 | extern irsdkCVar ir_PitsOpen; // bool[1] True if pit stop is allowed for the current player () 249 | extern irsdkCVar ir_VidCapEnabled; // bool[1] True if video capture system is enabled () 250 | extern irsdkCVar ir_VidCapActive; // bool[1] True if video currently being captured () 251 | extern irsdkCVar ir_PitRepairLeft; // float[1] Time left for mandatory pit repairs if repairs are active (s) 252 | extern irsdkCVar ir_PitOptRepairLeft; // float[1] Time left for optional repairs if repairs are active (s) 253 | extern irsdkCVar ir_PitstopActive; // bool[1] Is the player getting pit stop service () 254 | extern irsdkCVar ir_FastRepairUsed; // int[1] How many fast repairs used so far () 255 | extern irsdkCVar ir_FastRepairAvailable; // int[1] How many fast repairs left 255 is unlimited () 256 | extern irsdkCVar ir_LFTiresUsed; // int[1] How many left front tires used so far () 257 | extern irsdkCVar ir_RFTiresUsed; // int[1] How many right front tires used so far () 258 | extern irsdkCVar ir_LRTiresUsed; // int[1] How many left rear tires used so far () 259 | extern irsdkCVar ir_RRTiresUsed; // int[1] How many right rear tires used so far () 260 | extern irsdkCVar ir_LeftTireSetsUsed; // int[1] How many left tire sets used so far () 261 | extern irsdkCVar ir_RightTireSetsUsed; // int[1] How many right tire sets used so far () 262 | extern irsdkCVar ir_FrontTireSetsUsed; // int[1] How many front tire sets used so far () 263 | extern irsdkCVar ir_RearTireSetsUsed; // int[1] How many rear tire sets used so far () 264 | extern irsdkCVar ir_TireSetsUsed; // int[1] How many tire sets used so far () 265 | extern irsdkCVar ir_LFTiresAvailable; // int[1] How many left front tires are remaining 255 is unlimited () 266 | extern irsdkCVar ir_RFTiresAvailable; // int[1] How many right front tires are remaining 255 is unlimited () 267 | extern irsdkCVar ir_LRTiresAvailable; // int[1] How many left rear tires are remaining 255 is unlimited () 268 | extern irsdkCVar ir_RRTiresAvailable; // int[1] How many right rear tires are remaining 255 is unlimited () 269 | extern irsdkCVar ir_LeftTireSetsAvailable; // int[1] How many left tire sets are remaining 255 is unlimited () 270 | extern irsdkCVar ir_RightTireSetsAvailable; // int[1] How many right tire sets are remaining 255 is unlimited () 271 | extern irsdkCVar ir_FrontTireSetsAvailable; // int[1] How many front tire sets are remaining 255 is unlimited () 272 | extern irsdkCVar ir_RearTireSetsAvailable; // int[1] How many rear tire sets are remaining 255 is unlimited () 273 | extern irsdkCVar ir_TireSetsAvailable; // int[1] How many tire sets are remaining 255 is unlimited () 274 | extern irsdkCVar ir_CamCarIdx; // int[1] Active camera's focus car index () 275 | extern irsdkCVar ir_CamCameraNumber; // int[1] Active camera number () 276 | extern irsdkCVar ir_CamGroupNumber; // int[1] Active camera group number () 277 | extern irsdkCVar ir_CamCameraState; // bitfield[1] State of camera system (irsdk_CameraState) 278 | extern irsdkCVar ir_IsOnTrackCar; // bool[1] 1=Car on track physics running () 279 | extern irsdkCVar ir_IsInGarage; // bool[1] 1=Car in garage physics running () 280 | extern irsdkCVar ir_SteeringWheelPctTorque; // float[1] Force feedback % max torque on steering shaft unsigned (%) 281 | extern irsdkCVar ir_SteeringWheelPctTorqueSign; // float[1] Force feedback % max torque on steering shaft signed (%) 282 | extern irsdkCVar ir_SteeringWheelPctTorqueSignStops; // float[1] Force feedback % max torque on steering shaft signed stops (%) 283 | extern irsdkCVar ir_SteeringWheelPctDamper; // float[1] Force feedback % max damping (%) 284 | extern irsdkCVar ir_SteeringWheelAngleMax; // float[1] Steering wheel max angle (rad) 285 | extern irsdkCVar ir_SteeringWheelLimiter; // float[1] Force feedback limiter strength limits impacts and oscillation (%) 286 | extern irsdkCVar ir_ShiftIndicatorPct; // float[1] DEPRECATED use DriverCarSLBlinkRPM instead (%) 287 | extern irsdkCVar ir_ShiftPowerPct; // float[1] Friction torque applied to gears when shifting or grinding (%) 288 | extern irsdkCVar ir_ShiftGrindRPM; // float[1] RPM of shifter grinding noise (RPM) 289 | extern irsdkCVar ir_ThrottleRaw; // float[1] Raw throttle input 0=off throttle to 1=full throttle (%) 290 | extern irsdkCVar ir_BrakeRaw; // float[1] Raw brake input 0=brake released to 1=max pedal force (%) 291 | extern irsdkCVar ir_HandbrakeRaw; // float[1] Raw handbrake input 0=handbrake released to 1=max force (%) 292 | extern irsdkCVar ir_SteeringWheelPeakForceNm; // float[1] Peak torque mapping to direct input units for FFB (N*m) 293 | extern irsdkCVar ir_SteeringWheelMaxForceNm; // float[1] Value of strength or max force slider in Nm for FFB (N*m) 294 | extern irsdkCVar ir_SteeringWheelUseLinear; // bool[1] True if steering wheel force is using linear mode () 295 | extern irsdkCVar ir_BrakeABSactive; // bool[1] true if abs is currently reducing brake force pressure () 296 | extern irsdkCVar ir_EngineWarnings; // bitfield[1] Bitfield for warning lights (irsdk_EngineWarnings) 297 | extern irsdkCVar ir_FuelLevel; // float[1] Liters of fuel remaining (l) 298 | extern irsdkCVar ir_FuelLevelPct; // float[1] Percent fuel remaining (%) 299 | extern irsdkCVar ir_PitSvFlags; // bitfield[1] Bitfield of pit service checkboxes (irsdk_PitSvFlags) 300 | extern irsdkCVar ir_PitSvLFP; // float[1] Pit service left front tire pressure (kPa) 301 | extern irsdkCVar ir_PitSvRFP; // float[1] Pit service right front tire pressure (kPa) 302 | extern irsdkCVar ir_PitSvLRP; // float[1] Pit service left rear tire pressure (kPa) 303 | extern irsdkCVar ir_PitSvRRP; // float[1] Pit service right rear tire pressure (kPa) 304 | extern irsdkCVar ir_PitSvFuel; // float[1] Pit service fuel add amount (l) 305 | extern irsdkCVar ir_PitSvTireCompound; // int[1] Pit service pending tire compound () 306 | extern irsdkCVar ir_CarIdxP2P_Status; // bool[64] Push2Pass active or not () 307 | extern irsdkCVar ir_CarIdxP2P_Count; // int[64] Push2Pass count of usage (or remaining in Race) () 308 | extern irsdkCVar ir_ReplayPlaySpeed; // int[1] Replay playback speed () 309 | extern irsdkCVar ir_ReplayPlaySlowMotion; // bool[1] 0=not slow motion 1=replay is in slow motion () 310 | extern irsdkCVar ir_ReplaySessionTime; // double[1] Seconds since replay session start (s) 311 | extern irsdkCVar ir_ReplaySessionNum; // int[1] Replay session number () 312 | extern irsdkCVar ir_TireLF_RumblePitch; // float[1] Players LF Tire Sound rumblestrip pitch (Hz) 313 | extern irsdkCVar ir_TireRF_RumblePitch; // float[1] Players RF Tire Sound rumblestrip pitch (Hz) 314 | extern irsdkCVar ir_TireLR_RumblePitch; // float[1] Players LR Tire Sound rumblestrip pitch (Hz) 315 | extern irsdkCVar ir_TireRR_RumblePitch; // float[1] Players RR Tire Sound rumblestrip pitch (Hz) 316 | extern irsdkCVar ir_SteeringWheelTorque_ST; // float[6] Output torque on steering shaft at 360 Hz (N*m) 317 | extern irsdkCVar ir_SteeringWheelTorque; // float[1] Output torque on steering shaft (N*m) 318 | extern irsdkCVar ir_VelocityZ_ST; // float[6] Z velocity (m/s at 360 Hz) 319 | extern irsdkCVar ir_VelocityY_ST; // float[6] Y velocity (m/s at 360 Hz) 320 | extern irsdkCVar ir_VelocityX_ST; // float[6] X velocity (m/s at 360 Hz) 321 | extern irsdkCVar ir_VelocityZ; // float[1] Z velocity (m/s) 322 | extern irsdkCVar ir_VelocityY; // float[1] Y velocity (m/s) 323 | extern irsdkCVar ir_VelocityX; // float[1] X velocity (m/s) 324 | extern irsdkCVar ir_YawRate_ST; // float[6] Yaw rate at 360 Hz (rad/s) 325 | extern irsdkCVar ir_PitchRate_ST; // float[6] Pitch rate at 360 Hz (rad/s) 326 | extern irsdkCVar ir_RollRate_ST; // float[6] Roll rate at 360 Hz (rad/s) 327 | extern irsdkCVar ir_YawRate; // float[1] Yaw rate (rad/s) 328 | extern irsdkCVar ir_PitchRate; // float[1] Pitch rate (rad/s) 329 | extern irsdkCVar ir_RollRate; // float[1] Roll rate (rad/s) 330 | extern irsdkCVar ir_VertAccel_ST; // float[6] Vertical acceleration (including gravity) at 360 Hz (m/s^2) 331 | extern irsdkCVar ir_LatAccel_ST; // float[6] Lateral acceleration (including gravity) at 360 Hz (m/s^2) 332 | extern irsdkCVar ir_LongAccel_ST; // float[6] Longitudinal acceleration (including gravity) at 360 Hz (m/s^2) 333 | extern irsdkCVar ir_VertAccel; // float[1] Vertical acceleration (including gravity) (m/s^2) 334 | extern irsdkCVar ir_LatAccel; // float[1] Lateral acceleration (including gravity) (m/s^2) 335 | extern irsdkCVar ir_LongAccel; // float[1] Longitudinal acceleration (including gravity) (m/s^2) 336 | extern irsdkCVar ir_dcStarter; // bool[1] In car trigger car starter () 337 | extern irsdkCVar ir_dpRFTireChange; // float[1] Pitstop right front tire change request () 338 | extern irsdkCVar ir_dpLFTireChange; // float[1] Pitstop left front tire change request () 339 | extern irsdkCVar ir_dpRRTireChange; // float[1] Pitstop right rear tire change request () 340 | extern irsdkCVar ir_dpLRTireChange; // float[1] Pitstop left rear tire change request () 341 | extern irsdkCVar ir_dpTireChange; // float[1] Pitstop all tire change request () 342 | extern irsdkCVar ir_dpLTireChange; // float[1] Pitstop left tire change request () 343 | extern irsdkCVar ir_dpRTireChange; // float[1] Pitstop right tire change request () 344 | extern irsdkCVar ir_dpFuelFill; // float[1] Pitstop fuel fill flag () 345 | extern irsdkCVar ir_dpWindshieldTearoff; // float[1] Pitstop windshield tearoff () 346 | extern irsdkCVar ir_dpFuelAddKg; // float[1] Pitstop fuel add ammount (kg) 347 | extern irsdkCVar ir_dpFastRepair; // float[1] Pitstop fast repair set () 348 | extern irsdkCVar ir_dcBrakeBias; // float[1] In car brake bias adjustment () 349 | extern irsdkCVar ir_dpLFTireColdPress; // float[1] Pitstop lf tire cold pressure adjustment (Pa) 350 | extern irsdkCVar ir_dpRFTireColdPress; // float[1] Pitstop rf cold tire pressure adjustment (Pa) 351 | extern irsdkCVar ir_dpLRTireColdPress; // float[1] Pitstop lr tire cold pressure adjustment (Pa) 352 | extern irsdkCVar ir_dpRRTireColdPress; // float[1] Pitstop rr cold tire pressure adjustment (Pa) 353 | extern irsdkCVar ir_dpWeightJackerLeft; // float[1] Pitstop left wedge/weight jacker adjustment () 354 | extern irsdkCVar ir_dpWeightJackerRight; // float[1] Pitstop right wedge/weight jacker adjustment () 355 | extern irsdkCVar ir_WaterTemp; // float[1] Engine coolant temp (C) 356 | extern irsdkCVar ir_WaterLevel; // float[1] Engine coolant level (l) 357 | extern irsdkCVar ir_FuelPress; // float[1] Engine fuel pressure (bar) 358 | extern irsdkCVar ir_FuelUsePerHour; // float[1] Engine fuel used instantaneous (kg/h) 359 | extern irsdkCVar ir_OilTemp; // float[1] Engine oil temperature (C) 360 | extern irsdkCVar ir_OilPress; // float[1] Engine oil pressure (bar) 361 | extern irsdkCVar ir_OilLevel; // float[1] Engine oil level (l) 362 | extern irsdkCVar ir_Voltage; // float[1] Engine voltage (V) 363 | extern irsdkCVar ir_ManifoldPress; // float[1] Engine manifold pressure (bar) 364 | extern irsdkCVar ir_RFcoldPressure; // float[1] RF tire cold pressure as set in the garage (kPa) 365 | extern irsdkCVar ir_RFtempCL; // float[1] RF tire left carcass temperature (C) 366 | extern irsdkCVar ir_RFtempCM; // float[1] RF tire middle carcass temperature (C) 367 | extern irsdkCVar ir_RFtempCR; // float[1] RF tire right carcass temperature (C) 368 | extern irsdkCVar ir_RFwearL; // float[1] RF tire left percent tread remaining (%) 369 | extern irsdkCVar ir_RFwearM; // float[1] RF tire middle percent tread remaining (%) 370 | extern irsdkCVar ir_RFwearR; // float[1] RF tire right percent tread remaining (%) 371 | extern irsdkCVar ir_LFcoldPressure; // float[1] LF tire cold pressure as set in the garage (kPa) 372 | extern irsdkCVar ir_LFtempCL; // float[1] LF tire left carcass temperature (C) 373 | extern irsdkCVar ir_LFtempCM; // float[1] LF tire middle carcass temperature (C) 374 | extern irsdkCVar ir_LFtempCR; // float[1] LF tire right carcass temperature (C) 375 | extern irsdkCVar ir_LFwearL; // float[1] LF tire left percent tread remaining (%) 376 | extern irsdkCVar ir_LFwearM; // float[1] LF tire middle percent tread remaining (%) 377 | extern irsdkCVar ir_LFwearR; // float[1] LF tire right percent tread remaining (%) 378 | extern irsdkCVar ir_RRcoldPressure; // float[1] RR tire cold pressure as set in the garage (kPa) 379 | extern irsdkCVar ir_RRtempCL; // float[1] RR tire left carcass temperature (C) 380 | extern irsdkCVar ir_RRtempCM; // float[1] RR tire middle carcass temperature (C) 381 | extern irsdkCVar ir_RRtempCR; // float[1] RR tire right carcass temperature (C) 382 | extern irsdkCVar ir_RRwearL; // float[1] RR tire left percent tread remaining (%) 383 | extern irsdkCVar ir_RRwearM; // float[1] RR tire middle percent tread remaining (%) 384 | extern irsdkCVar ir_RRwearR; // float[1] RR tire right percent tread remaining (%) 385 | extern irsdkCVar ir_LRcoldPressure; // float[1] LR tire cold pressure as set in the garage (kPa) 386 | extern irsdkCVar ir_LRtempCL; // float[1] LR tire left carcass temperature (C) 387 | extern irsdkCVar ir_LRtempCM; // float[1] LR tire middle carcass temperature (C) 388 | extern irsdkCVar ir_LRtempCR; // float[1] LR tire right carcass temperature (C) 389 | extern irsdkCVar ir_LRwearL; // float[1] LR tire left percent tread remaining (%) 390 | extern irsdkCVar ir_LRwearM; // float[1] LR tire middle percent tread remaining (%) 391 | extern irsdkCVar ir_LRwearR; // float[1] LR tire right percent tread remaining (%) 392 | extern irsdkCVar ir_RRSHshockDefl; // float[1] RRSH shock deflection (m) 393 | extern irsdkCVar ir_RRSHshockDefl_ST; // float[6] RRSH shock deflection at 360 Hz (m) 394 | extern irsdkCVar ir_RRSHshockVel; // float[1] RRSH shock velocity (m/s) 395 | extern irsdkCVar ir_RRSHshockVel_ST; // float[6] RRSH shock velocity at 360 Hz (m/s) 396 | extern irsdkCVar ir_LRSHshockDefl; // float[1] LRSH shock deflection (m) 397 | extern irsdkCVar ir_LRSHshockDefl_ST; // float[6] LRSH shock deflection at 360 Hz (m) 398 | extern irsdkCVar ir_LRSHshockVel; // float[1] LRSH shock velocity (m/s) 399 | extern irsdkCVar ir_LRSHshockVel_ST; // float[6] LRSH shock velocity at 360 Hz (m/s) 400 | extern irsdkCVar ir_RFSHshockDefl; // float[1] RFSH shock deflection (m) 401 | extern irsdkCVar ir_RFSHshockDefl_ST; // float[6] RFSH shock deflection at 360 Hz (m) 402 | extern irsdkCVar ir_RFSHshockVel; // float[1] RFSH shock velocity (m/s) 403 | extern irsdkCVar ir_RFSHshockVel_ST; // float[6] RFSH shock velocity at 360 Hz (m/s) 404 | extern irsdkCVar ir_LFSHshockDefl; // float[1] LFSH shock deflection (m) 405 | extern irsdkCVar ir_LFSHshockDefl_ST; // float[6] LFSH shock deflection at 360 Hz (m) 406 | extern irsdkCVar ir_LFSHshockVel; // float[1] LFSH shock velocity (m/s) 407 | extern irsdkCVar ir_LFSHshockVel_ST; // float[6] LFSH shock velocity at 360 Hz (m/s) 408 | 409 | extern Session ir_session; 410 | 411 | // Keep the session data updated. 412 | // Will block for around 16 milliseconds. 413 | ConnectionStatus ir_tick(); 414 | 415 | // Let the session data tracking know that the config has changed. 416 | void ir_handleConfigChange(); 417 | 418 | // Return whether we're in the process of getting in the car, waiting for others 419 | // to grid, or doing pace laps before the actual race start. 420 | bool ir_isPreStart(); 421 | 422 | // Estimate time for a full lap. 423 | float ir_estimateLaptime(); 424 | 425 | // Get the best known position, from the latest session we can find. 426 | int ir_getPosition( int carIdx ); 427 | 428 | // Get gained positions. 429 | int ir_getPositionsChanged(int carIdx); 430 | 431 | // Get lap gap to P0 car if available. 432 | int ir_getLapDeltaToLeader( int carIdx, int ldrIdx ); 433 | 434 | // Get lap delta if available. 435 | float ir_getDeltaTime(int carIdx, int selfIdx); 436 | 437 | // Get laps remaining for sesion 438 | int ir_getLapsRemaining(); 439 | 440 | // Get session time remaining 441 | void ir_getSessionTimeRemaining(int& hours, int& mins, int& secs); 442 | 443 | // Get car class id 444 | int ir_getClassId(int carIdx); 445 | 446 | // Print all the variables the sim supports. 447 | void ir_printVariables(); 448 | --------------------------------------------------------------------------------