├── ddu.png ├── inputs.png ├── OverlayDDU.h ├── relative.png ├── standings.png ├── OverlayStandings.h ├── .gitignore ├── iron.sln ├── LICENSE ├── OverlayCover.h ├── 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 ├── Overlay.h ├── OverlayDebug.cpp ├── README.md ├── Config.cpp ├── OverlayInputs.h ├── iron.vcxproj ├── main.cpp ├── util.h ├── Overlay.cpp ├── OverlayRelative.h └── iracing.h /ddu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lespalt/iRon/HEAD/ddu.png -------------------------------------------------------------------------------- /inputs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lespalt/iRon/HEAD/inputs.png -------------------------------------------------------------------------------- /OverlayDDU.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lespalt/iRon/HEAD/OverlayDDU.h -------------------------------------------------------------------------------- /relative.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lespalt/iRon/HEAD/relative.png -------------------------------------------------------------------------------- /standings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lespalt/iRon/HEAD/standings.png -------------------------------------------------------------------------------- /OverlayStandings.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lespalt/iRon/HEAD/OverlayStandings.h -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | iron.vcxproj.user 2 | .vs/ 3 | x64/ 4 | Debug/ 5 | Release/ 6 | sessionYaml.txt 7 | config.json 8 | 9 | -------------------------------------------------------------------------------- /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|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {74CC4F41-58B4-4B1C-8F5D-19ABADCE940B}.Debug|x64.ActiveCfg = Debug|x64 15 | {74CC4F41-58B4-4B1C-8F5D-19ABADCE940B}.Debug|x64.Build.0 = Debug|x64 16 | {74CC4F41-58B4-4B1C-8F5D-19ABADCE940B}.Release|x64.ActiveCfg = Release|x64 17 | {74CC4F41-58B4-4B1C-8F5D-19ABADCE940B}.Release|x64.Build.0 = Release|x64 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {17DBA48D-5073-43E7-9ACB-502EAE4495FD} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /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() 34 | : Overlay("OverlayCover") 35 | {} 36 | }; 37 | -------------------------------------------------------------------------------- /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(); 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | class Overlay 38 | { 39 | public: 40 | 41 | Overlay( const std::string name ); 42 | virtual ~Overlay(); 43 | 44 | std::string getName() const; 45 | virtual bool canEnableWhileNotDriving() const; 46 | virtual bool canEnableWhileDisconnected() const; 47 | 48 | void enable( bool on ); 49 | bool isEnabled() const; 50 | 51 | void enableUiEdit( bool on ); 52 | bool isUiEditEnabled() const; 53 | 54 | void configChanged(); 55 | void sessionChanged(); 56 | 57 | void update(); 58 | 59 | void setWindowPosAndSize( int x, int y, int w, int h, bool callSetWindowPos=true ); 60 | void saveWindowPosAndSize(); 61 | 62 | protected: 63 | 64 | virtual void onEnable(); 65 | virtual void onDisable(); 66 | virtual void onUpdate(); 67 | virtual void onConfigChanged(); 68 | virtual void onSessionChanged(); 69 | virtual float2 getDefaultSize(); 70 | virtual bool hasCustomBackground(); 71 | 72 | std::string m_name; 73 | HWND m_hwnd = 0; 74 | bool m_enabled = false; 75 | bool m_uiEditEnabled = false; 76 | int m_xpos = 0; 77 | int m_ypos = 0; 78 | int m_width = 0; 79 | int m_height = 0; 80 | 81 | Microsoft::WRL::ComPtr m_d3dDevice; 82 | Microsoft::WRL::ComPtr m_swapChain; 83 | Microsoft::WRL::ComPtr m_d2dFactory; 84 | Microsoft::WRL::ComPtr m_renderTarget; 85 | Microsoft::WRL::ComPtr m_compositionDevice; 86 | Microsoft::WRL::ComPtr m_compositionTarget; 87 | Microsoft::WRL::ComPtr m_compositionVisual; 88 | Microsoft::WRL::ComPtr m_dwriteFactory; 89 | Microsoft::WRL::ComPtr m_brush; 90 | }; 91 | -------------------------------------------------------------------------------- /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 | 40 | void dbg( const float4& color, const char* fmt, ... ) 41 | { 42 | #ifndef _DEBUG 43 | return; 44 | #endif 45 | 46 | va_list args; 47 | va_start( args, fmt ); 48 | 49 | char s[2048]; 50 | vsnprintf( s, sizeof(s), fmt, args ); 51 | 52 | DbgLine line; 53 | line.s = s; 54 | line.col = color; 55 | g_dbgLines.emplace_back( line ); 56 | 57 | va_end( args ); 58 | } 59 | 60 | void dbg( const char* fmt, ... ) 61 | { 62 | #ifndef _DEBUG 63 | return; 64 | #endif 65 | 66 | va_list args; 67 | va_start( args, fmt ); 68 | 69 | char s[2048]; 70 | vsnprintf( s, sizeof(s), fmt, args ); 71 | 72 | DbgLine line; 73 | line.s = s; 74 | line.col = float4(1,1,1,0.9f); 75 | g_dbgLines.emplace_back( line ); 76 | 77 | va_end( args ); 78 | } 79 | 80 | 81 | OverlayDebug::OverlayDebug() 82 | : Overlay("OverlayDebug") 83 | {} 84 | 85 | void OverlayDebug::onEnable() 86 | { 87 | onConfigChanged(); // trigger font load 88 | } 89 | 90 | void OverlayDebug::onConfigChanged() 91 | { 92 | 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 )); 93 | m_textFormat->SetParagraphAlignment( DWRITE_PARAGRAPH_ALIGNMENT_CENTER ); 94 | m_textFormat->SetWordWrapping( DWRITE_WORD_WRAPPING_NO_WRAP ); 95 | } 96 | 97 | void OverlayDebug::onUpdate() 98 | { 99 | const float lineHeight = 20; 100 | 101 | m_renderTarget->BeginDraw(); 102 | 103 | for( int i=0; i<(int)g_dbgLines.size(); ++i ) 104 | { 105 | const DbgLine& line = g_dbgLines[i]; 106 | 107 | const float y = 10 + lineHeight/2 + i*lineHeight; 108 | 109 | m_brush->SetColor( line.col ); 110 | D2D1_RECT_F r = { 10, y-lineHeight/2, (float)m_width-10, y+lineHeight/2 }; 111 | auto wstr = toWide( line.s ); 112 | m_renderTarget->DrawTextA( wstr.c_str(), (int)wstr.size(), m_textFormat.Get(), &r, m_brush.Get(), D2D1_DRAW_TEXT_OPTIONS_CLIP ); 113 | } 114 | 115 | m_renderTarget->EndDraw(); 116 | 117 | g_dbgLines.clear(); 118 | } 119 | 120 | bool OverlayDebug::canEnableWhileNotDriving() const 121 | { 122 | return true; 123 | } 124 | 125 | bool OverlayDebug::canEnableWhileDisconnected() const 126 | { 127 | return true; 128 | } 129 | -------------------------------------------------------------------------------- /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](https://github.com/lespalt/iRon/blob/main/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 5% safety margin is added. These parameters can be customized. 46 | 47 | ![ddu](https://github.com/lespalt/iRon/blob/main/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](https://github.com/lespalt/iRon/blob/main/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](https://github.com/lespalt/iRon/blob/main/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 | --- 88 | 89 | ## Building from source 90 | 91 | This app is built with Visual Studio 2022. The free version should suffice, though I haven't verified it. 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. 92 | 93 | --- 94 | 95 | ## Dependencies 96 | 97 | 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. 98 | 99 | Build dependencies (most notably the iRacing SDK and picojson) are kept to a minimum and are included in the repository. 100 | 101 | --- 102 | 103 | ## Bug reports and feature requests 104 | 105 | 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. 106 | 107 | 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. 108 | 109 | --- 110 | 111 | ## Donations 112 | 113 | 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**. 114 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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() 36 | : Overlay("OverlayInputs") 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_steerVtx.resize( m_width ); 52 | for( int i=0; ifloat2 { 90 | return float2( v.x+0.5f, h-0.5f*thickness - v.y*(h-thickness) ); 91 | }; 92 | 93 | // Throttle (fill) 94 | Microsoft::WRL::ComPtr throttleFillPath; 95 | Microsoft::WRL::ComPtr throttleFillSink; 96 | m_d2dFactory->CreatePathGeometry( &throttleFillPath ); 97 | throttleFillPath->Open( &throttleFillSink ); 98 | throttleFillSink->BeginFigure( float2(0,h), D2D1_FIGURE_BEGIN_FILLED ); 99 | for( int i=0; i<(int)m_throttleVtx.size(); ++i ) 100 | throttleFillSink->AddLine( vtx2coord(m_throttleVtx[i]) ); 101 | throttleFillSink->AddLine( float2(m_throttleVtx[m_throttleVtx.size()-1].x+0.5f,h) ); 102 | throttleFillSink->EndFigure( D2D1_FIGURE_END_OPEN ); 103 | throttleFillSink->Close(); 104 | 105 | // Brake (fill) 106 | Microsoft::WRL::ComPtr brakeFillPath; 107 | Microsoft::WRL::ComPtr brakeFillSink; 108 | m_d2dFactory->CreatePathGeometry( &brakeFillPath ); 109 | brakeFillPath->Open( &brakeFillSink ); 110 | brakeFillSink->BeginFigure( float2(0,h), D2D1_FIGURE_BEGIN_FILLED ); 111 | for( int i=0; i<(int)m_brakeVtx.size(); ++i ) 112 | brakeFillSink->AddLine( vtx2coord(m_brakeVtx[i]) ); 113 | brakeFillSink->AddLine( float2(m_brakeVtx[m_brakeVtx.size()-1].x+0.5f,h) ); 114 | brakeFillSink->EndFigure( D2D1_FIGURE_END_OPEN ); 115 | brakeFillSink->Close(); 116 | 117 | // Throttle (line) 118 | Microsoft::WRL::ComPtr throttleLinePath; 119 | Microsoft::WRL::ComPtr throttleLineSink; 120 | m_d2dFactory->CreatePathGeometry( &throttleLinePath ); 121 | throttleLinePath->Open( &throttleLineSink ); 122 | throttleLineSink->BeginFigure( vtx2coord(m_throttleVtx[0]), D2D1_FIGURE_BEGIN_HOLLOW ); 123 | for( int i=1; i<(int)m_throttleVtx.size(); ++i ) 124 | throttleLineSink->AddLine( vtx2coord(m_throttleVtx[i]) ); 125 | throttleLineSink->EndFigure( D2D1_FIGURE_END_OPEN ); 126 | throttleLineSink->Close(); 127 | 128 | // Brake (line) 129 | Microsoft::WRL::ComPtr brakeLinePath; 130 | Microsoft::WRL::ComPtr brakeLineSink; 131 | m_d2dFactory->CreatePathGeometry( &brakeLinePath ); 132 | brakeLinePath->Open( &brakeLineSink ); 133 | brakeLineSink->BeginFigure( vtx2coord(m_brakeVtx[0]), D2D1_FIGURE_BEGIN_HOLLOW ); 134 | for( int i=1; i<(int)m_brakeVtx.size(); ++i ) 135 | brakeLineSink->AddLine( vtx2coord(m_brakeVtx[i]) ); 136 | brakeLineSink->EndFigure( D2D1_FIGURE_END_OPEN ); 137 | brakeLineSink->Close(); 138 | 139 | // Steering 140 | Microsoft::WRL::ComPtr steeringLinePath; 141 | Microsoft::WRL::ComPtr steeringLineSink; 142 | m_d2dFactory->CreatePathGeometry( &steeringLinePath ); 143 | steeringLinePath->Open( &steeringLineSink ); 144 | steeringLineSink->BeginFigure( vtx2coord(m_steerVtx[0]), D2D1_FIGURE_BEGIN_HOLLOW ); 145 | for( int i=1; i<(int)m_steerVtx.size(); ++i ) 146 | steeringLineSink->AddLine( vtx2coord(m_steerVtx[i]) ); 147 | steeringLineSink->EndFigure( D2D1_FIGURE_END_OPEN ); 148 | steeringLineSink->Close(); 149 | 150 | m_renderTarget->BeginDraw(); 151 | m_brush->SetColor( g_cfg.getFloat4( m_name, "throttle_fill_col", float4(0.2f,0.45f,0.15f,0.6f) ) ); 152 | m_renderTarget->FillGeometry( throttleFillPath.Get(), m_brush.Get() ); 153 | m_brush->SetColor( g_cfg.getFloat4( m_name, "brake_fill_col", float4(0.46f,0.01f,0.06f,0.6f) ) ); 154 | m_renderTarget->FillGeometry( brakeFillPath.Get(), m_brush.Get() ); 155 | m_brush->SetColor( g_cfg.getFloat4( m_name, "throttle_col", float4(0.38f,0.91f,0.31f,0.8f) ) ); 156 | m_renderTarget->DrawGeometry( throttleLinePath.Get(), m_brush.Get(), thickness ); 157 | m_brush->SetColor( g_cfg.getFloat4( m_name, "brake_col", float4(0.93f,0.03f,0.13f,0.8f) ) ); 158 | m_renderTarget->DrawGeometry( brakeLinePath.Get(), m_brush.Get(), thickness ); 159 | m_brush->SetColor( g_cfg.getFloat4( m_name, "steering_col", float4(1,1,1,0.3f) ) ); 160 | m_renderTarget->DrawGeometry( steeringLinePath.Get(), m_brush.Get(), thickness ); 161 | m_renderTarget->EndDraw(); 162 | } 163 | 164 | protected: 165 | 166 | std::vector m_throttleVtx; 167 | std::vector m_brakeVtx; 168 | std::vector m_steerVtx; 169 | }; 170 | -------------------------------------------------------------------------------- /iron.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {74cc4f41-58b4-4b1c-8f5d-19abadce940b} 25 | iron 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v143 33 | MultiByte 34 | 35 | 36 | Application 37 | false 38 | v143 39 | true 40 | MultiByte 41 | 42 | 43 | Application 44 | true 45 | v143 46 | MultiByte 47 | 48 | 49 | Application 50 | false 51 | v143 52 | true 53 | MultiByte 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | true 89 | NOMINMAX;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 90 | true 91 | 92 | 93 | Console 94 | true 95 | 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) 96 | 97 | 98 | 99 | 100 | Level3 101 | true 102 | true 103 | true 104 | NOMINMAX;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 105 | true 106 | 107 | 108 | Console 109 | true 110 | true 111 | true 112 | 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) 113 | true 114 | 115 | 116 | 117 | 118 | Level3 119 | true 120 | PICOJSON_USE_RVALUE_REFERENCE=0;NOMINMAX;_CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 121 | true 122 | MultiThreadedDebug 123 | 124 | 125 | Console 126 | true 127 | 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) 128 | 129 | 130 | 131 | 132 | Level3 133 | true 134 | true 135 | true 136 | PICOJSON_USE_RVALUE_REFERENCE=0;NOMINMAX;_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 137 | true 138 | MultiThreaded 139 | 140 | 141 | Console 142 | true 143 | true 144 | true 145 | 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) 146 | true 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 "iracing.h" 38 | #include "Config.h" 39 | #include "OverlayCover.h" 40 | #include "OverlayRelative.h" 41 | #include "OverlayInputs.h" 42 | #include "OverlayStandings.h" 43 | #include "OverlayDebug.h" 44 | #include "OverlayDDU.h" 45 | 46 | enum class Hotkey 47 | { 48 | UiEdit, 49 | Standings, 50 | DDU, 51 | Inputs, 52 | Relative, 53 | Cover 54 | }; 55 | 56 | static void registerHotkeys() 57 | { 58 | UnregisterHotKey( NULL, (int)Hotkey::UiEdit ); 59 | UnregisterHotKey( NULL, (int)Hotkey::Standings ); 60 | UnregisterHotKey( NULL, (int)Hotkey::DDU ); 61 | UnregisterHotKey( NULL, (int)Hotkey::Inputs ); 62 | UnregisterHotKey( NULL, (int)Hotkey::Relative ); 63 | UnregisterHotKey( NULL, (int)Hotkey::Cover ); 64 | 65 | UINT vk, mod; 66 | 67 | if( parseHotkey( g_cfg.getString("General","ui_edit_hotkey","alt-j"),&mod,&vk) ) 68 | RegisterHotKey( NULL, (int)Hotkey::UiEdit, mod, vk ); 69 | 70 | if( parseHotkey( g_cfg.getString("OverlayStandings","toggle_hotkey","ctrl-space"),&mod,&vk) ) 71 | RegisterHotKey( NULL, (int)Hotkey::Standings, mod, vk ); 72 | 73 | if( parseHotkey( g_cfg.getString("OverlayDDU","toggle_hotkey","ctrl-1"),&mod,&vk) ) 74 | RegisterHotKey( NULL, (int)Hotkey::DDU, mod, vk ); 75 | 76 | if( parseHotkey( g_cfg.getString("OverlayInputs","toggle_hotkey","ctrl-2"),&mod,&vk) ) 77 | RegisterHotKey( NULL, (int)Hotkey::Inputs, mod, vk ); 78 | 79 | if( parseHotkey( g_cfg.getString("OverlayRelative","toggle_hotkey","ctrl-3"),&mod,&vk) ) 80 | RegisterHotKey( NULL, (int)Hotkey::Relative, mod, vk ); 81 | 82 | if( parseHotkey( g_cfg.getString("OverlayCover","toggle_hotkey","ctrl-4"),&mod,&vk) ) 83 | RegisterHotKey( NULL, (int)Hotkey::Cover, mod, vk ); 84 | } 85 | 86 | static void handleConfigChange( std::vector overlays, ConnectionStatus status ) 87 | { 88 | registerHotkeys(); 89 | 90 | ir_handleConfigChange(); 91 | 92 | for( Overlay* o : overlays ) 93 | { 94 | o->enable( g_cfg.getBool(o->getName(),"enabled",true) && ( 95 | status == ConnectionStatus::DRIVING || 96 | status == ConnectionStatus::CONNECTED && o->canEnableWhileNotDriving() || 97 | status == ConnectionStatus::DISCONNECTED && o->canEnableWhileDisconnected() 98 | )); 99 | o->configChanged(); 100 | } 101 | } 102 | 103 | static void giveFocusToIracing() 104 | { 105 | HWND hwnd = FindWindow( "SimWinClass", NULL ); 106 | if( hwnd ) 107 | SetForegroundWindow( hwnd ); 108 | } 109 | 110 | int main() 111 | { 112 | // Bump priority up so we get time from the sim 113 | SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); 114 | 115 | // Load the config and watch it for changes 116 | g_cfg.load(); 117 | g_cfg.watchForChanges(); 118 | 119 | // Register global hotkeys 120 | registerHotkeys(); 121 | 122 | printf("\n====================================================================================\n"); 123 | printf("Welcome to iRon! This app provides a few simple overlays for iRacing.\n\n"); 124 | printf("NOTE: Most overlays are only active when iRacing is running and the car is on track.\n\n"); 125 | printf("Current hotkeys:\n"); 126 | printf(" Move and resize overlays: %s\n", g_cfg.getString("General","ui_edit_hotkey","").c_str() ); 127 | printf(" Toggle standings overlay: %s\n", g_cfg.getString("OverlayStandings","toggle_hotkey","").c_str() ); 128 | printf(" Toggle DDU overlay: %s\n", g_cfg.getString("OverlayDDU","toggle_hotkey","").c_str() ); 129 | printf(" Toggle inputs overlay: %s\n", g_cfg.getString("OverlayInputs","toggle_hotkey","").c_str() ); 130 | printf(" Toggle relative overlay: %s\n", g_cfg.getString("OverlayRelative","toggle_hotkey","").c_str() ); 131 | printf(" Toggle cover overlay: %s\n", g_cfg.getString("OverlayCover","toggle_hotkey","").c_str() ); 132 | printf("\niRon will generate a file called \'config.json\' in its current directory. This file\n"\ 133 | "stores your settings. You can edit the file at any time, even while iRon is running,\n"\ 134 | "to customize your overlays and hotkeys.\n\n"); 135 | printf("To exit iRon, simply close this console window.\n\n"); 136 | printf("For the latest version or to submit bug reports, go to:\n\n https://github.com/lespalt/iRon\n\n"); 137 | printf("\nHappy Racing!\n"); 138 | printf("====================================================================================\n\n"); 139 | 140 | // Create overlays 141 | std::vector overlays; 142 | overlays.push_back( new OverlayCover() ); 143 | overlays.push_back( new OverlayRelative() ); 144 | overlays.push_back( new OverlayInputs() ); 145 | overlays.push_back( new OverlayStandings() ); 146 | overlays.push_back( new OverlayDDU() ); 147 | #ifdef _DEBUG 148 | overlays.push_back( new OverlayDebug() ); 149 | #endif 150 | 151 | ConnectionStatus status = ConnectionStatus::UNKNOWN; 152 | bool uiEdit = false; 153 | unsigned frameCnt = 0; 154 | 155 | while( true ) 156 | { 157 | ConnectionStatus prevStatus = status; 158 | SessionType prevSessionType = ir_session.sessionType; 159 | 160 | // Refresh connection and session info 161 | status = ir_tick(); 162 | if( status != prevStatus ) 163 | { 164 | if( status == ConnectionStatus::DISCONNECTED ) 165 | printf("Waiting for iRacing connection...\n"); 166 | else 167 | printf("iRacing connected (%s)\n", ConnectionStatusStr[(int)status]); 168 | 169 | // Enable user-selected overlays, but only if we're driving 170 | handleConfigChange( overlays, status ); 171 | } 172 | 173 | if( ir_session.sessionType != prevSessionType ) 174 | { 175 | for( Overlay* o : overlays ) 176 | o->sessionChanged(); 177 | } 178 | 179 | 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() ); 180 | 181 | // Update/render overlays 182 | { 183 | if( !g_cfg.getBool("General", "performance_mode_30hz", false) ) 184 | { 185 | // Update everything every frame, roughly every 16ms (~60Hz) 186 | for( Overlay* o : overlays ) 187 | o->update(); 188 | } 189 | else 190 | { 191 | // To save perf, update half of the (enabled) overlays on even frames and the other half on odd, for ~30Hz overall 192 | int cnt = 0; 193 | for( Overlay* o : overlays ) 194 | { 195 | if( o->isEnabled() ) 196 | cnt++; 197 | 198 | if( (cnt & 1) == (frameCnt & 1) ) 199 | o->update(); 200 | } 201 | } 202 | } 203 | 204 | // Watch for config change signal 205 | if( g_cfg.hasChanged() ) 206 | { 207 | g_cfg.load(); 208 | handleConfigChange( overlays, status ); 209 | } 210 | 211 | // Message pump 212 | MSG msg = {}; 213 | while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 214 | { 215 | // Handle hotkeys 216 | if( msg.message == WM_HOTKEY ) 217 | { 218 | if( msg.wParam == (int)Hotkey::UiEdit ) 219 | { 220 | uiEdit = !uiEdit; 221 | for( Overlay* o : overlays ) 222 | o->enableUiEdit( uiEdit ); 223 | 224 | // When we're exiting edit mode, attempt to make iRacing the foreground window again for best perf 225 | // without the user having to manually click into iRacing. 226 | if( !uiEdit ) 227 | giveFocusToIracing(); 228 | } 229 | else 230 | { 231 | switch( msg.wParam ) 232 | { 233 | case (int)Hotkey::Standings: 234 | g_cfg.setBool( "OverlayStandings", "enabled", !g_cfg.getBool("OverlayStandings","enabled",true) ); 235 | break; 236 | case (int)Hotkey::DDU: 237 | g_cfg.setBool( "OverlayDDU", "enabled", !g_cfg.getBool("OverlayDDU","enabled",true) ); 238 | break; 239 | case (int)Hotkey::Inputs: 240 | g_cfg.setBool( "OverlayInputs", "enabled", !g_cfg.getBool("OverlayInputs","enabled",true) ); 241 | break; 242 | case (int)Hotkey::Relative: 243 | g_cfg.setBool( "OverlayRelative", "enabled", !g_cfg.getBool("OverlayRelative","enabled",true) ); 244 | break; 245 | case (int)Hotkey::Cover: 246 | g_cfg.setBool( "OverlayCover", "enabled", !g_cfg.getBool("OverlayCover","enabled",true) ); 247 | break; 248 | } 249 | 250 | g_cfg.save(); 251 | handleConfigChange( overlays, status ); 252 | } 253 | } 254 | 255 | TranslateMessage(&msg); 256 | DispatchMessage(&msg); 257 | } 258 | 259 | frameCnt++; 260 | } 261 | 262 | for( Overlay* o : overlays ) 263 | delete o; 264 | } 265 | -------------------------------------------------------------------------------- /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 true; 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 | -------------------------------------------------------------------------------- /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 | 38 | #define HRCHECK( x_ ) do{ \ 39 | HRESULT hr_ = x_; \ 40 | if( FAILED(hr_) ) { \ 41 | printf("ERROR: failed call to %s (%s:%d), hr=0x%x\n", #x_, __FILE__, __LINE__,hr_); \ 42 | exit(1); \ 43 | } } while(0) 44 | 45 | struct float2 46 | { 47 | union { float r; float x; }; 48 | union { float g; float y; }; 49 | float2() = default; 50 | float2( float _x, float _y ) : x(_x), y(_y) {} 51 | float2( const D2D1_POINT_2F& p ) : x(p.x), y(p.y) {} 52 | operator D2D1_POINT_2F() const { return {x,y}; } 53 | float* operator&() { return &x; } 54 | const float* operator&() const { return &x; } 55 | }; 56 | 57 | struct float4 58 | { 59 | union { float r; float x; }; 60 | union { float g; float y; }; 61 | union { float b; float z; }; 62 | union { float a; float w; }; 63 | float4() = default; 64 | float4( float _x, float _y, float _z, float _w ) : x(_x), y(_y), z(_z), w(_w) {} 65 | float4( const D2D1_COLOR_F& c ) : r(c.r), g(c.g), b(c.b), a(c.a) {} 66 | operator D2D1_COLOR_F() const { return {r,g,b,a}; } 67 | float* operator&() { return &x; } 68 | const float* operator&() const { return &x; } 69 | }; 70 | 71 | inline bool loadFile( const std::string& fname, std::string& output ) 72 | { 73 | FILE* fp = fopen( fname.c_str(), "rb" ); 74 | if( !fp ) 75 | return false; 76 | 77 | fseek( fp, 0, SEEK_END ); 78 | const long sz = ftell( fp ); 79 | fseek( fp, 0, SEEK_SET ); 80 | 81 | char* buf = new char[sz]; 82 | 83 | fread( buf, 1, sz, fp ); 84 | fclose( fp ); 85 | output = std::string( buf, sz ); 86 | 87 | delete[] buf; 88 | return true; 89 | } 90 | 91 | inline bool saveFile( const std::string& fname, const std::string& s ) 92 | { 93 | FILE* fp = fopen( fname.c_str(), "wb" ); 94 | if( !fp ) 95 | return false; 96 | 97 | fwrite( s.data(), 1, s.length(), fp ); 98 | 99 | fclose( fp ); 100 | return true; 101 | } 102 | 103 | inline std::wstring toWide( const std::string& narrow ) 104 | { 105 | return std::wstring(narrow.begin(),narrow.end()); 106 | } 107 | 108 | inline std::string formatLaptime( float secs ) 109 | { 110 | char s[32]; 111 | const int mins = int(secs/60.0f); 112 | if( mins ) 113 | sprintf( s, "%d:%06.3f", mins, fmodf(secs,60.0f) ); 114 | else 115 | sprintf( s, "%.03f", secs ); 116 | return std::string( s ); 117 | } 118 | 119 | class ColumnLayout 120 | { 121 | public: 122 | 123 | struct Column 124 | { 125 | int id = 0; 126 | float textWidth = 0; 127 | float borderL = 0; 128 | float borderR = 0; 129 | float textL = 0; 130 | float textR = 0; 131 | bool autoWidth = false; 132 | }; 133 | 134 | void reset() 135 | { 136 | m_columns.clear(); 137 | } 138 | 139 | // Pass in zero width for auto-scale 140 | void add( int id, float textWidth, float borderL, float borderR ) 141 | { 142 | Column clm; 143 | clm.id = id; 144 | clm.textWidth = textWidth; 145 | clm.borderL = borderL; 146 | clm.borderR = borderR; 147 | clm.autoWidth = textWidth == 0; 148 | m_columns.emplace_back( clm ); 149 | } 150 | void add( int id, float textWidth, float border ) 151 | { 152 | add( id, textWidth, border, border ); 153 | } 154 | 155 | void layout( float totalWidth ) 156 | { 157 | int autoWidthCnt = 0; 158 | float fixedWidth = 0; 159 | for( const Column& clm : m_columns ) 160 | { 161 | if( clm.autoWidth ) 162 | { 163 | autoWidthCnt++; 164 | fixedWidth += clm.borderL + clm.borderR; 165 | } 166 | else 167 | { 168 | fixedWidth += clm.textWidth + clm.borderL + clm.borderR; 169 | } 170 | } 171 | 172 | const float autoTextWidth = std::max( 0.0f, (totalWidth - fixedWidth) / autoWidthCnt ); 173 | 174 | float x = 0; 175 | for( Column& clm : m_columns ) 176 | { 177 | if( clm.autoWidth ) 178 | clm.textWidth = autoTextWidth; 179 | 180 | clm.textL = x + clm.borderL; 181 | clm.textR = clm.textL + clm.textWidth; 182 | 183 | x = clm.textR + clm.borderR; 184 | } 185 | } 186 | 187 | const Column* get( int id ) const 188 | { 189 | for( int i=0; i<(int)m_columns.size(); ++i ) 190 | { 191 | if( m_columns[i].id == id ) 192 | return &m_columns[i]; 193 | } 194 | return nullptr; 195 | } 196 | 197 | private: 198 | 199 | std::vector m_columns; 200 | }; 201 | 202 | //----------------------------------------------------------------------------- 203 | // MurmurHash2, by Austin Appleby 204 | 205 | // Note - This code makes a few assumptions about how your machine behaves - 206 | 207 | // 1. We can read a 4-byte value from any address without crashing 208 | // 2. sizeof(int) == 4 209 | 210 | // And it has a few limitations - 211 | 212 | // 1. It will not work incrementally. 213 | // 2. It will not produce the same results on little-endian and big-endian 214 | // machines. 215 | 216 | inline unsigned int MurmurHash2 ( const void * key, int len, unsigned int seed ) 217 | { 218 | // 'm' and 'r' are mixing constants generated offline. 219 | // They're not really 'magic', they just happen to work well. 220 | 221 | const unsigned int m = 0x5bd1e995; 222 | const int r = 24; 223 | 224 | // Initialize the hash to a 'random' value 225 | 226 | unsigned int h = seed ^ len; 227 | 228 | // Mix 4 bytes at a time into the hash 229 | 230 | const unsigned char * data = (const unsigned char *)key; 231 | 232 | while(len >= 4) 233 | { 234 | unsigned int k = *(unsigned int *)data; 235 | 236 | k *= m; 237 | k ^= k >> r; 238 | k *= m; 239 | 240 | h *= m; 241 | h ^= k; 242 | 243 | data += 4; 244 | len -= 4; 245 | } 246 | 247 | // Handle the last few bytes of the input array 248 | 249 | switch(len) 250 | { 251 | case 3: h ^= data[2] << 16; 252 | case 2: h ^= data[1] << 8; 253 | case 1: h ^= data[0]; 254 | h *= m; 255 | }; 256 | 257 | // Do a few final mixes of the hash to ensure the last few 258 | // bytes are well-incorporated. 259 | 260 | h ^= h >> 13; 261 | h *= m; 262 | h ^= h >> 15; 263 | 264 | return h; 265 | } 266 | // End MurmurHash2 267 | //----------------------------------------------------------------------------- 268 | 269 | class TextCache 270 | { 271 | public: 272 | 273 | ~TextCache() 274 | { 275 | reset(); 276 | } 277 | 278 | void reset( IDWriteFactory* factory=nullptr ) 279 | { 280 | for( auto& it : m_cache ) 281 | it.second->Release(); 282 | 283 | m_cache.clear(); 284 | m_factory = factory; 285 | } 286 | 287 | // 288 | // Render some text, using a cached TextLayout if possible. 289 | // This works around spending ungodly amount of CPU cycles on ID2D1RenderTarget::DrawText. 290 | // 291 | // Assumption: all values stored in 'textFormat' are invariant between calls to this function, except horizontal alignment. 292 | // Which is why we're including alignment in the hash explicitly, and otherwise just include the text format pointer. 293 | // 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 294 | // patterns, recreating text formats always implies nuking this cache anyway, so don't bother with a more complicated design. 295 | // 296 | // Assumption: textFormat is set to DWRITE_PARAGRAPH_ALIGNMENT_CENTER, so ycenter +/- fontSize is enough vertical room in all 297 | // cases. I.e. we only care about rendering single-line text. 298 | // 299 | void render( ID2D1RenderTarget* renderTarget, const wchar_t* str, IDWriteTextFormat* textFormat, float xmin, float xmax, float ycenter, ID2D1SolidColorBrush* brush, DWRITE_TEXT_ALIGNMENT align ) 300 | { 301 | IDWriteTextLayout* textLayout = getOrCreateTextLayout( str, textFormat, xmin, xmax, align ); 302 | if( !textLayout ) 303 | return; 304 | 305 | const float fontSize = textFormat->GetFontSize(); 306 | 307 | const D2D1_RECT_F r = { xmin, ycenter-fontSize, xmax, ycenter+fontSize }; 308 | renderTarget->DrawTextLayout( float2(xmin,ycenter-fontSize), textLayout, brush, D2D1_DRAW_TEXT_OPTIONS_CLIP ); 309 | } 310 | 311 | // 312 | // Same assumptions as render(). 313 | // 314 | float2 getExtent( const wchar_t* str, IDWriteTextFormat* textFormat, float xmin, float xmax, DWRITE_TEXT_ALIGNMENT align ) 315 | { 316 | IDWriteTextLayout* textLayout = getOrCreateTextLayout( str, textFormat, xmin, xmax, align ); 317 | if( !textLayout ) 318 | return float2(0,0); 319 | 320 | DWRITE_TEXT_METRICS m = {}; 321 | textLayout->GetMetrics( &m ); 322 | 323 | return float2( m.width, m.height ); 324 | } 325 | 326 | private: 327 | 328 | IDWriteTextLayout* getOrCreateTextLayout( const wchar_t* str, IDWriteTextFormat* textFormat, float xmin, float xmax, DWRITE_TEXT_ALIGNMENT align ) 329 | { 330 | if( xmax < xmin ) 331 | return nullptr; 332 | 333 | const float fontSize = textFormat->GetFontSize(); 334 | const float width = xmax - xmin; 335 | 336 | textFormat->SetTextAlignment( align ); 337 | 338 | const int len = (int)wcslen( str ); 339 | unsigned hash = MurmurHash2( str, len*sizeof(wchar_t), 0x12341234 ); 340 | hash ^= (unsigned)(uint64_t(textFormat) & 0xffffffff); 341 | hash ^= (unsigned)(uint64_t(textFormat) >> 32); 342 | hash ^= *((unsigned*)&width); 343 | hash ^= (unsigned)align; 344 | 345 | IDWriteTextLayout* textLayout = nullptr; 346 | 347 | auto it = m_cache.find( hash ); 348 | 349 | if( it == m_cache.end() ) 350 | { 351 | m_factory->CreateTextLayout( str, len, textFormat, width, fontSize*2, &textLayout ); 352 | m_cache.insert( std::make_pair(hash, textLayout) ); 353 | } 354 | else 355 | { 356 | textLayout = it->second; 357 | } 358 | 359 | return textLayout; 360 | } 361 | 362 | std::unordered_map m_cache; 363 | IDWriteFactory* m_factory = nullptr; 364 | }; 365 | 366 | inline float2 computeTextExtent( const wchar_t* str, IDWriteFactory* factory, IDWriteTextFormat* textFormat ) 367 | { 368 | IDWriteTextLayout* textLayout = nullptr; 369 | 370 | factory->CreateTextLayout( str, (int)wcslen(str), textFormat, 99999, 99999, &textLayout ); 371 | DWRITE_TEXT_METRICS m = {}; 372 | textLayout->GetMetrics( &m ); 373 | 374 | textLayout->Release(); 375 | 376 | return float2( m.width, m.height ); 377 | } 378 | 379 | inline float celsiusToFahrenheit( float c ) 380 | { 381 | return c * (9.0f / 5.0f) + 32.0f; 382 | } 383 | 384 | inline bool parseHotkey( const std::string& desc, UINT* mod, UINT* vk ) 385 | { 386 | // Dumb but good-enough way to turn strings like "Ctrl-Shift-F1" into values understood by RegisterHotkey. 387 | 388 | std::string s = desc; 389 | for( char& c : s ) 390 | c = (char)toupper( (unsigned char)c ); 391 | 392 | // Need at least one modifier 393 | size_t pos = s.find_last_of("+- "); 394 | if( pos == std::string::npos ) 395 | return false; 396 | 397 | // "Parse" modifier 398 | *mod = 0; 399 | if( strstr(s.c_str(),"CTRL") || strstr(s.c_str(),"CONTROL")) 400 | *mod |= MOD_CONTROL; 401 | if( strstr(s.c_str(),"ALT") ) 402 | *mod |= MOD_ALT; 403 | if( strstr(s.c_str(),"SHIFT") ) 404 | *mod |= MOD_SHIFT; 405 | 406 | // Parse key 407 | const std::string key = s.substr( pos+1 ); 408 | 409 | for( int i=1; i<=24; ++i ) 410 | { 411 | const std::string fkey = "F" + std::to_string(i); 412 | if( key == fkey ) { 413 | *vk = VK_F1 + (i-1); 414 | return true; 415 | } 416 | } 417 | 418 | if( key == "ENTER" || key == "RETURN" ) 419 | { 420 | *vk = VK_RETURN; 421 | return true; 422 | } 423 | 424 | if( key == "SPACE" ) 425 | { 426 | *vk = VK_SPACE; 427 | return true; 428 | } 429 | 430 | if( key.length() == 1 ) 431 | { 432 | *vk = key[0]; 433 | return true; 434 | } 435 | 436 | return false; 437 | } 438 | -------------------------------------------------------------------------------- /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 | using namespace Microsoft::WRL; 32 | 33 | static const int ResizeBorderWidth = 25; 34 | 35 | static LRESULT CALLBACK windowProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) 36 | { 37 | Overlay* o = (Overlay*)GetWindowLongPtr( hwnd, GWLP_USERDATA ); 38 | 39 | if( !o || !o->isUiEditEnabled() ) 40 | return DefWindowProc( hwnd, msg, wparam, lparam ); 41 | 42 | switch( msg ) 43 | { 44 | // handle moving/resizing 45 | case WM_NCHITTEST: 46 | { 47 | LRESULT hit = DefWindowProc( hwnd, msg, wparam, lparam ); 48 | if( hit == HTCLIENT ) 49 | { 50 | // check if we hit the corner area of the window to allow resizing despite having no border 51 | RECT r; 52 | GetWindowRect( hwnd, &r ); 53 | const int cur_x = GET_X_LPARAM( lparam ) - r.left; 54 | const int cur_y = GET_Y_LPARAM( lparam ) - r.top; 55 | const int w = r.right - r.left; 56 | const int h = r.bottom - r.top; 57 | const int border = ResizeBorderWidth; 58 | /* Disabled these corners because using them doesn't let us update the window contents 59 | fast enough for some reason, leading to a weird window-wobble appearance when resizing. 60 | if( cur_x < border && cur_y < border ) 61 | return HTTOPLEFT; 62 | if( cur_x > w-border && cur_y < border ) 63 | return HTTOPRIGHT; 64 | if( cur_x < border && cur_y > h-border ) 65 | return HTBOTTOMLEFT; 66 | */ 67 | if( cur_x > w-border && cur_y > h-border ) 68 | return HTBOTTOMRIGHT; 69 | 70 | // say we hit the caption to allow dragging the window from the client area 71 | hit = HTCAPTION; 72 | } 73 | return hit; 74 | } 75 | case WM_MOVING: 76 | case WM_SIZE: 77 | { 78 | if( o ) 79 | { 80 | RECT r; 81 | GetWindowRect( hwnd, &r ); 82 | const int x = r.left; 83 | const int y = r.top; 84 | const int w = r.right - r.left; 85 | const int h = r.bottom - r.top; 86 | o->setWindowPosAndSize( x, y, w, h, false ); 87 | o->saveWindowPosAndSize(); 88 | o->update(); // draw window content while moving/resizing 89 | } 90 | break; 91 | } 92 | } 93 | return DefWindowProc( hwnd, msg, wparam, lparam ); 94 | } 95 | 96 | 97 | // 98 | // Overlay 99 | // 100 | 101 | Overlay::Overlay( const std::string name ) 102 | : m_name( name ) 103 | {} 104 | 105 | Overlay::~Overlay() 106 | { 107 | enable( false ); 108 | } 109 | 110 | std::string Overlay::getName() const 111 | { 112 | return m_name; 113 | } 114 | 115 | void Overlay::enable( bool on ) 116 | { 117 | if( on && !m_hwnd ) // enable 118 | { 119 | // 120 | // Create window 121 | // 122 | 123 | const char* const wndclassName = "overlay"; 124 | WNDCLASSEX wndclass = {}; 125 | if( !GetClassInfoEx( 0, wndclassName, &wndclass ) ) // only the first overlay we open registers the window class 126 | { 127 | wndclass.cbSize = sizeof(WNDCLASSEX); 128 | wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; 129 | wndclass.lpfnWndProc = windowProc; 130 | wndclass.lpszClassName = wndclassName; 131 | wndclass.hbrBackground = CreateSolidBrush(0); 132 | RegisterClassEx(&wndclass); 133 | } 134 | 135 | m_hwnd = CreateWindowEx( WS_EX_TOPMOST|WS_EX_TOOLWINDOW|WS_EX_NOREDIRECTIONBITMAP, wndclassName, m_name.c_str(), WS_POPUP|WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 500, 400, NULL, NULL, NULL, NULL ); 136 | SetWindowLongPtr( m_hwnd, GWLP_USERDATA, (LONG_PTR)this ); 137 | 138 | RECT r; 139 | GetWindowRect( m_hwnd, &r ); 140 | const int width = r.right - r.left; 141 | const int height = r.bottom - r.top; 142 | 143 | // 144 | // Create the unsettling amount of stuff that's needed to get a window to 145 | // properly alpha-blend our Direct2D rendering into the desktop. 146 | // See: https://docs.microsoft.com/en-us/archive/msdn-magazine/2014/june/windows-with-c-high-performance-window-layering-using-the-windows-composition-engine 147 | // 148 | 149 | #ifdef _DEBUG 150 | const bool isdebug = true; 151 | #else 152 | const bool isdebug = false; 153 | #endif 154 | 155 | // D3D11 device 156 | 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 )); 157 | 158 | // DXGI device 159 | ComPtr dxgiDevice; 160 | HRCHECK(m_d3dDevice.As(&dxgiDevice)); 161 | 162 | // DXGI factory 163 | ComPtr dxgiFactory; 164 | HRCHECK(CreateDXGIFactory2( isdebug ? DXGI_CREATE_FACTORY_DEBUG : 0, IID_PPV_ARGS(&dxgiFactory) )); 165 | 166 | // DXGI Swap chain 167 | DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; 168 | swapChainDesc.Width = width; 169 | swapChainDesc.Height = height; 170 | swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; 171 | swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; 172 | swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; 173 | swapChainDesc.BufferCount = 2; 174 | swapChainDesc.SampleDesc.Count = 1; 175 | swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED; 176 | HRCHECK(dxgiFactory->CreateSwapChainForComposition( dxgiDevice.Get(), &swapChainDesc, NULL, &m_swapChain )); 177 | HRCHECK(dxgiFactory->MakeWindowAssociation( m_hwnd, DXGI_MWA_NO_ALT_ENTER )); 178 | 179 | // DXGI surface 180 | ComPtr dxgiSurface; 181 | HRCHECK(m_swapChain->GetBuffer( 0, IID_PPV_ARGS(&dxgiSurface) )); 182 | 183 | // D2D factory 184 | D2D1_FACTORY_OPTIONS factoryOptions = {}; 185 | factoryOptions.debugLevel = isdebug ? D2D1_DEBUG_LEVEL_INFORMATION : D2D1_DEBUG_LEVEL_NONE; 186 | HRCHECK(D2D1CreateFactory( D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(m_d2dFactory), &factoryOptions, &m_d2dFactory )); 187 | 188 | // D2D render target 189 | D2D1_RENDER_TARGET_PROPERTIES targetProperties = {}; 190 | targetProperties.type = D2D1_RENDER_TARGET_TYPE_DEFAULT; 191 | targetProperties.pixelFormat.format = DXGI_FORMAT_UNKNOWN; 192 | targetProperties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; 193 | HRCHECK(m_d2dFactory->CreateDxgiSurfaceRenderTarget( dxgiSurface.Get(), &targetProperties, &m_renderTarget )); 194 | 195 | // Composition stuff 196 | HRCHECK(DCompositionCreateDevice( dxgiDevice.Get(), IID_PPV_ARGS(&m_compositionDevice) )); 197 | HRCHECK(m_compositionDevice->CreateTargetForHwnd( m_hwnd, true, &m_compositionTarget )); 198 | HRCHECK(m_compositionDevice->CreateVisual( &m_compositionVisual )); 199 | HRCHECK(m_compositionVisual->SetContent(m_swapChain.Get())); 200 | HRCHECK(m_compositionTarget->SetRoot(m_compositionVisual.Get())); 201 | HRCHECK(m_compositionDevice->Commit()); 202 | 203 | // DirectWrite factory 204 | HRCHECK(DWriteCreateFactory( DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast(m_dwriteFactory.GetAddressOf()) )); 205 | 206 | // Default brush 207 | HRCHECK(m_renderTarget->CreateSolidColorBrush( float4(0,0,0,1), &m_brush )); 208 | 209 | // 210 | // Finalize enable 211 | // 212 | 213 | m_enabled = true; 214 | onEnable(); 215 | } 216 | else if( !on && m_hwnd ) // disable 217 | { 218 | onDisable(); 219 | 220 | m_dwriteFactory.Reset(); 221 | m_compositionVisual.Reset(); 222 | m_compositionTarget.Reset(); 223 | m_compositionDevice.Reset(); 224 | m_renderTarget.Reset(); 225 | m_d2dFactory.Reset(); 226 | m_swapChain.Reset(); 227 | m_d3dDevice.Reset(); 228 | 229 | DestroyWindow( m_hwnd ); 230 | m_hwnd = 0; 231 | m_enabled = false; 232 | } 233 | } 234 | 235 | bool Overlay::isEnabled() const 236 | { 237 | return m_enabled; 238 | } 239 | 240 | void Overlay::enableUiEdit( bool on ) 241 | { 242 | m_uiEditEnabled = on; 243 | update(); 244 | } 245 | 246 | bool Overlay::isUiEditEnabled() const 247 | { 248 | return m_uiEditEnabled; 249 | } 250 | 251 | void Overlay::configChanged() 252 | { 253 | if( !m_enabled ) 254 | return; 255 | 256 | // Somewhat silly way to ensure the default positions of the overlays aren't all on top of each other. 257 | const unsigned hash = MurmurHash2(m_name.c_str(),(int)m_name.length(),0x1234); 258 | const int defaultX = (hash % 100) * 15; 259 | const int defaultY = (hash % 80) * 10; 260 | 261 | const float2 defaultSize = getDefaultSize(); 262 | 263 | // Position/dimensions might have changed 264 | const int x = g_cfg.getInt(m_name,"window_pos_x", defaultX); 265 | const int y = g_cfg.getInt(m_name,"window_pos_y", defaultY); 266 | const int w = g_cfg.getInt(m_name,"window_size_x", (int)defaultSize.x); 267 | const int h = g_cfg.getInt(m_name,"window_size_y", (int)defaultSize.y); 268 | setWindowPosAndSize( x, y, w, h ); 269 | 270 | onConfigChanged(); 271 | } 272 | 273 | void Overlay::sessionChanged() 274 | { 275 | onSessionChanged(); 276 | } 277 | 278 | void Overlay::update() 279 | { 280 | if( !m_enabled ) 281 | return; 282 | 283 | const float w = (float)m_width; 284 | const float h = (float)m_height; 285 | const float cornerRadius = g_cfg.getFloat( m_name, "corner_radius", m_name=="OverlayInputs"?2.0f:6.0f ); 286 | 287 | // Clear/draw background 288 | if( !hasCustomBackground() ) 289 | { 290 | m_renderTarget->BeginDraw(); 291 | m_renderTarget->Clear( float4(0,0,0,0) ); 292 | D2D1_ROUNDED_RECT rr; 293 | rr.rect = { 0.5f, 0.5f, w-0.5f, h-0.5f }; 294 | rr.radiusX = cornerRadius; 295 | rr.radiusY = cornerRadius; 296 | m_brush->SetColor( g_cfg.getFloat4( m_name, "background_col", float4(0,0,0,0.7f) ) ); 297 | m_renderTarget->FillRoundedRectangle( &rr, m_brush.Get() ); 298 | m_renderTarget->EndDraw(); 299 | } 300 | 301 | // Overlay-specific logic and rendering 302 | onUpdate(); 303 | 304 | if( m_uiEditEnabled ) 305 | { 306 | // Draw highlight frame and resize corner indicators 307 | m_renderTarget->BeginDraw(); 308 | D2D1_ROUNDED_RECT rr; 309 | rr.rect = { 0.5f, 0.5f, w-0.5f, h-0.5f }; 310 | rr.radiusX = cornerRadius; 311 | rr.radiusY = cornerRadius; 312 | m_brush->SetColor( float4(1,1,1,0.7f) ); 313 | m_renderTarget->DrawRoundedRectangle( &rr, m_brush.Get(), 2 ); 314 | m_renderTarget->DrawLine( float2(w-0.5f,h-0.5f-ResizeBorderWidth), float2(w-0.5f-ResizeBorderWidth,h-0.5f-ResizeBorderWidth), m_brush.Get(), 2 ); 315 | m_renderTarget->DrawLine( float2(w-0.5f-ResizeBorderWidth,h-0.5f), float2(w-0.5f-ResizeBorderWidth,h-0.5f-ResizeBorderWidth), m_brush.Get(), 2 ); 316 | m_renderTarget->EndDraw(); 317 | } 318 | 319 | HRCHECK(m_swapChain->Present( 1, 0 )); 320 | } 321 | 322 | void Overlay::setWindowPosAndSize( int x, int y, int w, int h, bool callSetWindowPos ) 323 | { 324 | w = std::max( w, 30 ); 325 | h = std::max( h, 30 ); 326 | 327 | if( callSetWindowPos ) 328 | SetWindowPos( m_hwnd, HWND_TOPMOST, x, y, w, h, SWP_NOACTIVATE|SWP_SHOWWINDOW ); 329 | 330 | m_xpos = x; 331 | m_ypos = y; 332 | m_width = w; 333 | m_height = h; 334 | 335 | m_renderTarget.Reset(); // need to release all references to swap chain's back buffers before calling ResizeBuffers 336 | 337 | HRCHECK(m_swapChain->ResizeBuffers( 0, w, h, DXGI_FORMAT_UNKNOWN, 0 )); 338 | 339 | // Recreate render target 340 | ComPtr dxgiSurface; 341 | HRCHECK(m_swapChain->GetBuffer( 0, IID_PPV_ARGS(&dxgiSurface) )); 342 | D2D1_RENDER_TARGET_PROPERTIES targetProperties = {}; 343 | targetProperties.type = D2D1_RENDER_TARGET_TYPE_DEFAULT; 344 | targetProperties.pixelFormat.format = DXGI_FORMAT_UNKNOWN; 345 | targetProperties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; 346 | HRCHECK(m_d2dFactory->CreateDxgiSurfaceRenderTarget( dxgiSurface.Get(), &targetProperties, &m_renderTarget )); 347 | } 348 | 349 | void Overlay::saveWindowPosAndSize() 350 | { 351 | g_cfg.setInt( m_name, "window_pos_x", m_xpos ); 352 | g_cfg.setInt( m_name, "window_pos_y", m_ypos ); 353 | g_cfg.setInt( m_name, "window_size_x", m_width ); 354 | g_cfg.setInt( m_name, "window_size_y", m_height ); 355 | 356 | g_cfg.save(); 357 | } 358 | 359 | bool Overlay::canEnableWhileNotDriving() const 360 | { 361 | return false; 362 | } 363 | 364 | bool Overlay::canEnableWhileDisconnected() const 365 | { 366 | return false; 367 | } 368 | 369 | void Overlay::onEnable() {} 370 | void Overlay::onDisable() {} 371 | void Overlay::onUpdate() {} 372 | void Overlay::onConfigChanged() {} 373 | void Overlay::onSessionChanged() {} 374 | float2 Overlay::getDefaultSize() { return float2(400,300); } 375 | bool Overlay::hasCustomBackground() { return false; } 376 | 377 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /OverlayRelative.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 "Overlay.h" 30 | #include "iracing.h" 31 | #include "Config.h" 32 | 33 | class OverlayRelative : public Overlay 34 | { 35 | public: 36 | 37 | const float DefaultFontSize = 15.3f; 38 | 39 | OverlayRelative() 40 | : Overlay("OverlayRelative") 41 | {} 42 | 43 | protected: 44 | 45 | enum class Columns { POSITION, CAR_NUMBER, NAME, DELTA, LICENSE, SAFETY_RATING, IRATING, PIT }; 46 | 47 | virtual void onEnable() 48 | { 49 | onConfigChanged(); // trigger font load 50 | } 51 | 52 | virtual void onDisable() 53 | { 54 | m_text.reset(); 55 | } 56 | 57 | virtual void onConfigChanged() 58 | { 59 | m_text.reset( m_dwriteFactory.Get() ); 60 | 61 | const std::string font = g_cfg.getString( m_name, "font", "Microsoft YaHei UI" ); 62 | const float fontSize = g_cfg.getFloat( m_name, "font_size", DefaultFontSize ); 63 | const int fontWeight = g_cfg.getInt( m_name, "font_weight", 500 ); 64 | HRCHECK(m_dwriteFactory->CreateTextFormat( toWide(font).c_str(), NULL, (DWRITE_FONT_WEIGHT)fontWeight, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, fontSize, L"en-us", &m_textFormat )); 65 | m_textFormat->SetParagraphAlignment( DWRITE_PARAGRAPH_ALIGNMENT_CENTER ); 66 | m_textFormat->SetWordWrapping( DWRITE_WORD_WRAPPING_NO_WRAP ); 67 | 68 | HRCHECK(m_dwriteFactory->CreateTextFormat( toWide(font).c_str(), NULL, (DWRITE_FONT_WEIGHT)fontWeight, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, fontSize*0.8f, L"en-us", &m_textFormatSmall )); 69 | m_textFormatSmall->SetParagraphAlignment( DWRITE_PARAGRAPH_ALIGNMENT_CENTER ); 70 | m_textFormatSmall->SetWordWrapping( DWRITE_WORD_WRAPPING_NO_WRAP ); 71 | 72 | // Determine widths of text columns 73 | m_columns.reset(); 74 | m_columns.add( (int)Columns::POSITION, computeTextExtent( L"P99", m_dwriteFactory.Get(), m_textFormat.Get() ).x, fontSize/2 ); 75 | m_columns.add( (int)Columns::CAR_NUMBER, computeTextExtent( L"#999", m_dwriteFactory.Get(), m_textFormat.Get() ).x, fontSize/2 ); 76 | m_columns.add( (int)Columns::NAME, 0, fontSize/2 ); 77 | m_columns.add( (int)Columns::DELTA, computeTextExtent( L"+99L -99.9", m_dwriteFactory.Get(), m_textFormat.Get() ).x, 1, fontSize/2 ); 78 | 79 | if( g_cfg.getBool(m_name,"show_pit_age",true) ) 80 | m_columns.add( (int)Columns::PIT, computeTextExtent( L"999", m_dwriteFactory.Get(), m_textFormatSmall.Get() ).x, fontSize/4 ); 81 | if( g_cfg.getBool(m_name,"show_license",true) && !g_cfg.getBool(m_name,"show_sr",false) ) 82 | m_columns.add( (int)Columns::LICENSE, computeTextExtent( L" A ", m_dwriteFactory.Get(), m_textFormatSmall.Get() ).x*1.6f, fontSize/10 ); 83 | if( g_cfg.getBool(m_name,"show_sr",false) ) 84 | m_columns.add( (int)Columns::SAFETY_RATING, computeTextExtent( L"A 4.44", m_dwriteFactory.Get(), m_textFormatSmall.Get() ).x, fontSize/8 ); 85 | if( g_cfg.getBool(m_name,"show_irating",true) ) 86 | m_columns.add( (int)Columns::IRATING, computeTextExtent( L"999.9k", m_dwriteFactory.Get(), m_textFormatSmall.Get() ).x, fontSize/8 ); 87 | } 88 | 89 | virtual void onUpdate() 90 | { 91 | struct CarInfo { 92 | int carIdx = 0; 93 | float delta = 0; 94 | int lapDelta = 0; 95 | int pitAge = 0; 96 | }; 97 | std::vector relatives; 98 | relatives.reserve( IR_MAX_CARS ); 99 | 100 | // Populate cars with the ones for which a relative/delta comparison is valid 101 | for( int i=0; i= 0 && !car.isSpectator && car.carNumber>=0 ) 109 | { 110 | // Add the pace car only under yellow or initial pace lap 111 | if( car.isPaceCar && !(ir_SessionFlags.getInt() & (irsdk_caution|irsdk_cautionWaving)) && !ir_isPreStart() ) 112 | continue; 113 | 114 | // If the other car is up to half a lap in front, we consider the delta 'ahead', otherwise 'behind'. 115 | 116 | float delta = 0; 117 | int lapDelta = lapcountC - lapcountS; 118 | 119 | const float L = ir_estimateLaptime(); 120 | const float C = ir_CarIdxEstTime.getFloat(i); 121 | const float S = ir_CarIdxEstTime.getFloat(ir_session.driverCarIdx); 122 | 123 | // Does the delta between us and the other car span across the start/finish line? 124 | const bool wrap = fabsf(ir_CarIdxLapDistPct.getFloat(i) - ir_CarIdxLapDistPct.getFloat(ir_session.driverCarIdx)) > 0.5f; 125 | 126 | if( wrap ) 127 | { 128 | delta = S > C ? (C-S)+L : (C-S)-L; 129 | lapDelta += S > C ? -1 : 1; 130 | } 131 | else 132 | { 133 | delta = C - S; 134 | } 135 | 136 | // Assume no lap delta when not in a race, because we don't want to show drivers as lapped/lapping there. 137 | // Also reset it during initial pacing, since iRacing for some reason starts counting 138 | // during the pace lap but then resets the counter a couple seconds in, confusing the logic. 139 | // And consider the pace car in the same lap as us, too. 140 | if( ir_session.sessionType!=SessionType::RACE || ir_isPreStart() || car.isPaceCar ) 141 | { 142 | lapDelta = 0; 143 | } 144 | 145 | CarInfo ci; 146 | ci.carIdx = i; 147 | ci.delta = delta; 148 | ci.lapDelta = lapDelta; 149 | ci.pitAge = ir_CarIdxLap.getInt(i) - car.lastLapInPits; 150 | relatives.push_back( ci ); 151 | } 152 | } 153 | 154 | // Sort by delta 155 | std::sort( relatives.begin(), relatives.end(), 156 | []( const CarInfo& a, const CarInfo&b ) { return a.delta > b.delta; } ); 157 | 158 | // Locate our driver's index in the new array 159 | int selfCarInfoIdx = -1; 160 | for( int i=0; i<(int)relatives.size(); ++i ) 161 | { 162 | if( relatives[i].carIdx == ir_session.driverCarIdx ) { 163 | selfCarInfoIdx = i; 164 | break; 165 | } 166 | } 167 | 168 | // Something's wrong if we didn't find our driver. Bail. 169 | if( selfCarInfoIdx < 0 ) 170 | return; 171 | 172 | // Display such that our driver is in the vertical center of the area where we're listing cars 173 | 174 | const float fontSize = g_cfg.getFloat( m_name, "font_size", DefaultFontSize ); 175 | const float lineSpacing = g_cfg.getFloat( m_name, "line_spacing", 6 ); 176 | const float lineHeight = fontSize + lineSpacing; 177 | const float4 selfCol = g_cfg.getFloat4( m_name, "self_col", float4(0.94f,0.67f,0.13f,1) ); 178 | const float4 sameLapCol = g_cfg.getFloat4( m_name, "same_lap_col", float4(1,1,1,1) ); 179 | const float4 lapAheadCol = g_cfg.getFloat4( m_name, "lap_ahead_col", float4(0.9f,0.17f,0.17f,1) ); 180 | const float4 lapBehindCol = g_cfg.getFloat4( m_name, "lap_behind_col", float4(0,0.71f,0.95f,1) ); 181 | const float4 iratingTextCol = g_cfg.getFloat4( m_name, "irating_text_col", float4(0,0,0,0.9f) ); 182 | const float4 iratingBgCol = g_cfg.getFloat4( m_name, "irating_background_col", float4(1,1,1,0.85f) ); 183 | const float4 licenseTextCol = g_cfg.getFloat4( m_name, "license_text_col", float4(1,1,1,0.9f) ); 184 | const float licenseBgAlpha = g_cfg.getFloat( m_name, "license_background_alpha", 0.8f ); 185 | const float4 alternateLineBgCol = g_cfg.getFloat4( m_name, "alternate_line_background_col", float4(0.5f,0.5f,0.5f,0) ); 186 | const float4 buddyCol = g_cfg.getFloat4( m_name, "buddy_col", float4(0.2f,0.75f,0,1) ); 187 | const float4 flaggedCol = g_cfg.getFloat4( m_name, "flagged_col", float4(0.6f,0.35f,0.2f,1) ); 188 | const float4 carNumberBgCol = g_cfg.getFloat4( m_name, "car_number_background_col", float4(1,1,1,0.9f) ); 189 | const float4 carNumberTextCol = g_cfg.getFloat4( m_name, "car_number_text_col", float4(0,0,0,0.9f) ); 190 | const float4 pitCol = g_cfg.getFloat4( m_name, "pit_col", float4(0.94f,0.8f,0.13f,1) ); 191 | const bool minimapEnabled = g_cfg.getBool( m_name, "minimap_enabled", true ); 192 | const bool minimapIsRelative = g_cfg.getBool( m_name, "minimap_is_relative", true ); 193 | const float4 minimapBgCol = g_cfg.getFloat4( m_name, "minimap_background_col", float4(0,0,0,0.13f) ); 194 | const float listingAreaTop = minimapEnabled ? 30 : 10.0f; 195 | const float listingAreaBot = m_height - 10.0f; 196 | const float yself = listingAreaTop + (listingAreaBot-listingAreaTop) / 2.0f; 197 | const int entriesAbove = int( (yself - lineHeight/2 - listingAreaTop) / lineHeight ); 198 | 199 | float y = yself - entriesAbove * lineHeight; 200 | 201 | const float xoff = 10.0f; 202 | m_columns.layout( (float)m_width - 20 ); 203 | 204 | m_renderTarget->BeginDraw(); 205 | for( int cnt=0, i=selfCarInfoIdx-entriesAbove; i<(int)relatives.size() && y<=listingAreaBot-lineHeight/2; ++i, y+=lineHeight, ++cnt ) 206 | { 207 | // Alternating line backgrounds 208 | if( cnt & 1 && alternateLineBgCol.a > 0 ) 209 | { 210 | D2D1_RECT_F r = { 0, y-lineHeight/2, (float)m_width, y+lineHeight/2 }; 211 | m_brush->SetColor( alternateLineBgCol ); 212 | m_renderTarget->FillRectangle( &r, m_brush.Get() ); 213 | } 214 | 215 | // Skip if we don't have a car to list for this line 216 | if( i < 0 ) 217 | continue; 218 | 219 | const CarInfo& ci = relatives[i]; 220 | const Car& car = ir_session.cars[ci.carIdx]; 221 | 222 | // Determine text color 223 | float4 col = sameLapCol; 224 | if( ci.lapDelta > 0 ) 225 | col = lapAheadCol; 226 | if( ci.lapDelta < 0 ) 227 | col = lapBehindCol; 228 | 229 | if( car.isSelf ) 230 | col = selfCol; 231 | else if( ir_CarIdxOnPitRoad.getBool(ci.carIdx) ) 232 | col.a *= 0.5f; 233 | 234 | wchar_t s[512]; 235 | D2D1_RECT_F r = {}; 236 | D2D1_ROUNDED_RECT rr = {}; 237 | const ColumnLayout::Column* clm = nullptr; 238 | 239 | // Position 240 | if( ir_getPosition(ci.carIdx) > 0 ) 241 | { 242 | clm = m_columns.get( (int)Columns::POSITION ); 243 | m_brush->SetColor( col ); 244 | swprintf( s, _countof(s), L"P%d", ir_getPosition(ci.carIdx) ); 245 | m_textFormat->SetTextAlignment( DWRITE_TEXT_ALIGNMENT_TRAILING ); 246 | m_text.render( m_renderTarget.Get(), s, m_textFormat.Get(), xoff+clm->textL, xoff+clm->textR, y, m_brush.Get(), DWRITE_TEXT_ALIGNMENT_TRAILING ); 247 | } 248 | 249 | // Car number 250 | { 251 | clm = m_columns.get( (int)Columns::CAR_NUMBER ); 252 | swprintf( s, _countof(s), L"#%S", car.carNumberStr.c_str() ); 253 | r = { xoff+clm->textL, y-lineHeight/2, xoff+clm->textR, y+lineHeight/2 }; 254 | rr.rect = { r.left-2, r.top+1, r.right+2, r.bottom-1 }; 255 | rr.radiusX = 3; 256 | rr.radiusY = 3; 257 | m_brush->SetColor( car.isSelf ? selfCol : (car.isBuddy ? buddyCol : (car.isFlagged?flaggedCol:carNumberBgCol)) ); 258 | m_renderTarget->FillRoundedRectangle( &rr, m_brush.Get() ); 259 | m_brush->SetColor( carNumberTextCol ); 260 | m_text.render( m_renderTarget.Get(), s, m_textFormat.Get(), xoff+clm->textL, xoff+clm->textR, y, m_brush.Get(), DWRITE_TEXT_ALIGNMENT_CENTER ); 261 | } 262 | 263 | // Name 264 | { 265 | clm = m_columns.get( (int)Columns::NAME ); 266 | swprintf( s, _countof(s), L"%S", car.userName.c_str() ); 267 | m_brush->SetColor( col ); 268 | m_text.render( m_renderTarget.Get(), s, m_textFormat.Get(), xoff+clm->textL, xoff+clm->textR, y, m_brush.Get(), DWRITE_TEXT_ALIGNMENT_LEADING ); 269 | } 270 | 271 | // Delta 272 | { 273 | clm = m_columns.get( (int)Columns::DELTA ); 274 | if( ci.lapDelta ) 275 | swprintf( s, _countof(s), L"%+dL %.1f", ci.lapDelta, ci.delta ); 276 | else 277 | swprintf( s, _countof(s), L"%.1f", ci.delta ); 278 | m_brush->SetColor( col ); 279 | m_text.render( m_renderTarget.Get(), s, m_textFormat.Get(), xoff+clm->textL, xoff+clm->textR, y, m_brush.Get(), DWRITE_TEXT_ALIGNMENT_TRAILING ); 280 | } 281 | 282 | // Pit age 283 | if( (clm = m_columns.get((int)Columns::PIT)) && !ir_isPreStart() && (ci.pitAge>=0||ir_CarIdxOnPitRoad.getBool(ci.carIdx)) ) 284 | { 285 | r = { xoff+clm->textL, y-lineHeight/2+2, xoff+clm->textR, y+lineHeight/2-2 }; 286 | m_brush->SetColor( pitCol ); 287 | m_renderTarget->DrawRectangle( &r, m_brush.Get() ); 288 | if( ir_CarIdxOnPitRoad.getBool(ci.carIdx) ) { 289 | swprintf( s, _countof(s), L"PIT" ); 290 | m_renderTarget->FillRectangle( &r, m_brush.Get() ); 291 | m_brush->SetColor( float4(0,0,0,1) ); 292 | } 293 | else { 294 | swprintf( s, _countof(s), L"%d", ci.pitAge ); 295 | m_renderTarget->DrawRectangle( &r, m_brush.Get() ); 296 | } 297 | m_text.render( m_renderTarget.Get(), s, m_textFormatSmall.Get(), xoff+clm->textL, xoff+clm->textR, y, m_brush.Get(), DWRITE_TEXT_ALIGNMENT_CENTER ); 298 | } 299 | 300 | // License without SR 301 | if( clm = m_columns.get( (int)Columns::LICENSE ) ) 302 | { 303 | swprintf( s, _countof(s), L"%C", car.licenseChar ); 304 | r = { xoff+clm->textL, y-lineHeight/2, xoff+clm->textR, y+lineHeight/2 }; 305 | rr.rect = { r.left+1, r.top+1, r.right-1, r.bottom-1 }; 306 | rr.radiusX = 3; 307 | rr.radiusY = 3; 308 | float4 c = car.licenseCol; 309 | c.a = licenseBgAlpha; 310 | m_brush->SetColor( c ); 311 | m_renderTarget->FillRoundedRectangle( &rr, m_brush.Get() ); 312 | m_brush->SetColor( licenseTextCol ); 313 | m_text.render( m_renderTarget.Get(), s, m_textFormatSmall.Get(), xoff+clm->textL, xoff+clm->textR, y, m_brush.Get(), DWRITE_TEXT_ALIGNMENT_CENTER ); 314 | } 315 | 316 | // License with SR 317 | if( clm = m_columns.get( (int)Columns::SAFETY_RATING ) ) 318 | { 319 | swprintf( s, _countof(s), L"%C %.1f", car.licenseChar, car.licenseSR ); 320 | r = { xoff+clm->textL, y-lineHeight/2, xoff+clm->textR, y+lineHeight/2 }; 321 | rr.rect = { r.left+1, r.top+1, r.right-1, r.bottom-1 }; 322 | rr.radiusX = 3; 323 | rr.radiusY = 3; 324 | float4 c = car.licenseCol; 325 | c.a = licenseBgAlpha; 326 | m_brush->SetColor( c ); 327 | m_renderTarget->FillRoundedRectangle( &rr, m_brush.Get() ); 328 | m_brush->SetColor( licenseTextCol ); 329 | m_text.render( m_renderTarget.Get(), s, m_textFormatSmall.Get(), xoff+clm->textL, xoff+clm->textR, y, m_brush.Get(), DWRITE_TEXT_ALIGNMENT_CENTER ); 330 | } 331 | 332 | // Irating 333 | if( clm = m_columns.get( (int)Columns::IRATING ) ) 334 | { 335 | swprintf( s, _countof(s), L"%.1fk", (float)car.irating/1000.0f ); 336 | r = { xoff+clm->textL, y-lineHeight/2, xoff+clm->textR, y+lineHeight/2 }; 337 | rr.rect = { r.left+1, r.top+1, r.right-1, r.bottom-1 }; 338 | rr.radiusX = 3; 339 | rr.radiusY = 3; 340 | m_brush->SetColor( iratingBgCol ); 341 | m_renderTarget->FillRoundedRectangle( &rr, m_brush.Get() ); 342 | m_brush->SetColor( iratingTextCol ); 343 | m_text.render( m_renderTarget.Get(), s, m_textFormatSmall.Get(), xoff+clm->textL, xoff+clm->textR, y, m_brush.Get(), DWRITE_TEXT_ALIGNMENT_CENTER ); 344 | } 345 | } 346 | 347 | // Minimap 348 | if( minimapEnabled ) 349 | { 350 | const float y = 10; 351 | const float x = 10; 352 | const float h = 15; 353 | const float w = (float)m_width - 2*x; 354 | D2D1_RECT_F r = { x, y, x+w, y+h }; 355 | m_brush->SetColor( minimapBgCol ); 356 | m_renderTarget->FillRectangle( &r, m_brush.Get() ); 357 | 358 | // phases: lap down, same lap, lap ahead, buddies, pacecar, self 359 | for( int phase=0; phase<6; ++phase ) 360 | { 361 | float4 baseCol = float4(0,0,0,0); 362 | switch(phase) 363 | { 364 | case 0: baseCol = lapBehindCol; break; 365 | case 1: baseCol = sameLapCol; break; 366 | case 2: baseCol = lapAheadCol; break; 367 | case 3: baseCol = buddyCol; break; 368 | case 4: baseCol = float4(1,1,1,1); break; 369 | case 5: baseCol = selfCol; break; 370 | default: break; 371 | } 372 | 373 | for( int i=0; i<(int)relatives.size(); ++i ) 374 | { 375 | const CarInfo& ci = relatives[i]; 376 | const Car& car = ir_session.cars[ci.carIdx]; 377 | 378 | if( phase == 0 && ci.lapDelta >= 0 ) 379 | continue; 380 | if( phase == 1 && ci.lapDelta != 0 ) 381 | continue; 382 | if( phase == 2 && ci.lapDelta <= 0 ) 383 | continue; 384 | if( phase == 3 && !car.isBuddy ) 385 | continue; 386 | if( phase == 4 && !car.isPaceCar ) 387 | continue; 388 | if( phase == 5 && !car.isSelf ) 389 | continue; 390 | 391 | float e = ir_CarIdxLapDistPct.getFloat(ci.carIdx); 392 | 393 | const float eself = ir_CarIdxLapDistPct.getFloat(ir_session.driverCarIdx); 394 | 395 | if( minimapIsRelative ) 396 | { 397 | e = e - eself + 0.5f; 398 | if( e > 1 ) 399 | e -= 1; 400 | if( e < 0 ) 401 | e += 1; 402 | } 403 | e = e * w + x; 404 | 405 | float4 col = baseCol; 406 | if( !car.isSelf && ir_CarIdxOnPitRoad.getBool(ci.carIdx) ) 407 | col.a *= 0.5f; 408 | 409 | const float dx = 2; 410 | const float dy = car.isSelf || car.isPaceCar ? 4.0f : 0.0f; 411 | r = {e-dx, y+2-dy, e+dx, y+h-2+dy}; 412 | m_brush->SetColor( col ); 413 | m_renderTarget->FillRectangle( &r, m_brush.Get() ); 414 | } 415 | } 416 | } 417 | m_renderTarget->EndDraw(); 418 | } 419 | 420 | protected: 421 | 422 | Microsoft::WRL::ComPtr m_textFormat; 423 | Microsoft::WRL::ComPtr m_textFormatSmall; 424 | 425 | ColumnLayout m_columns; 426 | TextCache m_text; 427 | }; 428 | -------------------------------------------------------------------------------- /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 | enum class ConnectionStatus 36 | { 37 | UNKNOWN = 0, 38 | DISCONNECTED, 39 | CONNECTED, 40 | DRIVING 41 | }; 42 | static const char* const ConnectionStatusStr[] = {"UNKNOWN","DISCONNECTED","CONNECTED","DRIVING"}; 43 | 44 | enum class SessionType 45 | { 46 | UNKNOWN = 0, 47 | PRACTICE, 48 | QUALIFY, 49 | RACE 50 | }; 51 | static const char* const SessionTypeStr[] = {"UNKNOWN","PRACTICE","QUALIFY","RACE"}; 52 | 53 | struct Car 54 | { 55 | std::string userName; 56 | int carNumber = 0; 57 | std::string carNumberStr; 58 | std::string licenseStr; 59 | char licenseChar = 'R'; 60 | float licenseSR = 0; 61 | std::string licenseColStr; 62 | float4 licenseCol = float4(0,0,0,1); 63 | int irating = 0; 64 | int isSelf = 0; 65 | int isPaceCar = 0; 66 | int isSpectator = 0; 67 | int isBuddy = 0; 68 | int isFlagged = 0; 69 | int incidentCount = 0; 70 | float carClassEstLapTime = 0; 71 | int practicePosition = 0; 72 | int qualPosition = 0; 73 | float qualTime = 0; 74 | int racePosition = 0; 75 | int lastLapInPits = 0; 76 | }; 77 | 78 | struct Session 79 | { 80 | SessionType sessionType = SessionType::UNKNOWN; 81 | Car cars[IR_MAX_CARS]; 82 | int driverCarIdx = -1; 83 | int sof = 0; 84 | int subsessionId = 0; 85 | int isFixedSetup = 0; 86 | int isUnlimitedTime = 0; 87 | int isUnlimitedLaps = 0; 88 | float fuelMaxLtr = 0; 89 | float rpmIdle = 0; 90 | float rpmRedline = 0; 91 | float rpmSLFirst = 0; 92 | float rpmSLShift = 0; 93 | float rpmSLLast = 0; 94 | float rpmSLBlink = 0; 95 | }; 96 | 97 | extern irsdkCVar ir_SessionTime; // double[1] Seconds since session start (s) 98 | extern irsdkCVar ir_SessionTick; // int[1] Current update number () 99 | extern irsdkCVar ir_SessionNum; // int[1] Session number () 100 | extern irsdkCVar ir_SessionState; // int[1] Session state (irsdk_SessionState) 101 | extern irsdkCVar ir_SessionUniqueID; // int[1] Session ID () 102 | extern irsdkCVar ir_SessionFlags; // bitfield[1] Session flags (irsdk_Flags) 103 | extern irsdkCVar ir_SessionTimeRemain; // double[1] Seconds left till session ends (s) 104 | extern irsdkCVar ir_SessionLapsRemain; // int[1] Old laps left till session ends use SessionLapsRemainEx () 105 | extern irsdkCVar ir_SessionLapsRemainEx; // int[1] New improved laps left till session ends () 106 | extern irsdkCVar ir_SessionTimeTotal; // double[1] Total number of seconds in session (s) 107 | extern irsdkCVar ir_SessionLapsTotal; // int[1] Total number of laps in session () 108 | extern irsdkCVar ir_SessionTimeOfDay; // float[1] Time of day in seconds (s) 109 | extern irsdkCVar ir_RadioTransmitCarIdx; // int[1] The car index of the current person speaking on the radio () 110 | extern irsdkCVar ir_RadioTransmitRadioIdx; // int[1] The radio index of the current person speaking on the radio () 111 | extern irsdkCVar ir_RadioTransmitFrequencyIdx; // int[1] The frequency index of the current person speaking on the radio () 112 | extern irsdkCVar ir_DisplayUnits; // int[1] Default units for the user interface 0 = english 1 = metric () 113 | extern irsdkCVar ir_DriverMarker; // bool[1] Driver activated flag () 114 | extern irsdkCVar ir_PushToPass; // bool[1] Push to pass button state () 115 | extern irsdkCVar ir_ManualBoost; // bool[1] Hybrid manual boost state () 116 | extern irsdkCVar ir_ManualNoBoost; // bool[1] Hybrid manual no boost state () 117 | extern irsdkCVar ir_IsOnTrack; // bool[1] 1=Car on track physics running with player in car () 118 | extern irsdkCVar ir_IsReplayPlaying; // bool[1] 0=replay not playing 1=replay playing () 119 | extern irsdkCVar ir_ReplayFrameNum; // int[1] Integer replay frame number (60 per second) () 120 | extern irsdkCVar ir_ReplayFrameNumEnd; // int[1] Integer replay frame number from end of tape () 121 | extern irsdkCVar ir_IsDiskLoggingEnabled; // bool[1] 0=disk based telemetry turned off 1=turned on () 122 | extern irsdkCVar ir_IsDiskLoggingActive; // bool[1] 0=disk based telemetry file not being written 1=being written () 123 | extern irsdkCVar ir_FrameRate; // float[1] Average frames per second (fps) 124 | extern irsdkCVar ir_CpuUsageFG; // float[1] Percent of available tim fg thread took with a 1 sec avg (%) 125 | extern irsdkCVar ir_GpuUsage; // float[1] Percent of available tim gpu took with a 1 sec avg (%) 126 | extern irsdkCVar ir_ChanAvgLatency; // float[1] Communications average latency (s) 127 | extern irsdkCVar ir_ChanLatency; // float[1] Communications latency (s) 128 | extern irsdkCVar ir_ChanQuality; // float[1] Communications quality (%) 129 | extern irsdkCVar ir_ChanPartnerQuality; // float[1] Partner communications quality (%) 130 | extern irsdkCVar ir_CpuUsageBG; // float[1] Percent of available tim bg thread took with a 1 sec avg (%) 131 | extern irsdkCVar ir_ChanClockSkew; // float[1] Communications server clock skew (s) 132 | extern irsdkCVar ir_MemPageFaultSec; // float[1] Memory page faults per second () 133 | extern irsdkCVar ir_PlayerCarPosition; // int[1] Players position in race () 134 | extern irsdkCVar ir_PlayerCarClassPosition; // int[1] Players class position in race () 135 | extern irsdkCVar ir_PlayerCarClass; // int[1] Player car class id () 136 | extern irsdkCVar ir_PlayerTrackSurface; // int[1] Players car track surface type (irsdk_TrkLoc) 137 | extern irsdkCVar ir_PlayerTrackSurfaceMaterial; // int[1] Players car track surface material type (irsdk_TrkSurf) 138 | extern irsdkCVar ir_PlayerCarIdx; // int[1] Players carIdx () 139 | extern irsdkCVar ir_PlayerCarTeamIncidentCount; // int[1] Players team incident count for this session () 140 | extern irsdkCVar ir_PlayerCarMyIncidentCount; // int[1] Players own incident count for this session () 141 | extern irsdkCVar ir_PlayerCarDriverIncidentCount; // int[1] Teams current drivers incident count for this session () 142 | extern irsdkCVar ir_PlayerCarWeightPenalty; // float[1] Players weight penalty (kg) 143 | extern irsdkCVar ir_PlayerCarPowerAdjust; // float[1] Players power adjust (%) 144 | extern irsdkCVar ir_PlayerCarDryTireSetLimit; // int[1] Players dry tire set limit () 145 | extern irsdkCVar ir_PlayerCarTowTime; // float[1] Players car is being towed if time is greater than zero (s) 146 | extern irsdkCVar ir_PlayerCarInPitStall; // bool[1] Players car is properly in there pitstall () 147 | extern irsdkCVar ir_PlayerCarPitSvStatus; // int[1] Players car pit service status bits (irsdk_PitSvStatus) 148 | extern irsdkCVar ir_PlayerTireCompound; // int[1] Players car current tire compound () 149 | extern irsdkCVar ir_PlayerFastRepairsUsed; // int[1] Players car number of fast repairs used () 150 | extern irsdkCVar ir_CarIdxLap; // int[64] Laps started by car index () 151 | extern irsdkCVar ir_CarIdxLapCompleted; // int[64] Laps completed by car index () 152 | extern irsdkCVar ir_CarIdxLapDistPct; // float[64] Percentage distance around lap by car index (%) 153 | extern irsdkCVar ir_CarIdxTrackSurface; // int[64] Track surface type by car index (irsdk_TrkLoc) 154 | extern irsdkCVar ir_CarIdxTrackSurfaceMaterial; // int[64] Track surface material type by car index (irsdk_TrkSurf) 155 | extern irsdkCVar ir_CarIdxOnPitRoad; // bool[64] On pit road between the cones by car index () 156 | extern irsdkCVar ir_CarIdxPosition; // int[64] Cars position in race by car index () 157 | extern irsdkCVar ir_CarIdxClassPosition; // int[64] Cars class position in race by car index () 158 | extern irsdkCVar ir_CarIdxClass; // int[64] Cars class id by car index () 159 | extern irsdkCVar ir_CarIdxF2Time; // float[64] Race time behind leader or fastest lap time otherwise (s) 160 | extern irsdkCVar ir_CarIdxEstTime; // float[64] Estimated time to reach current location on track (s) 161 | extern irsdkCVar ir_CarIdxLastLapTime; // float[64] Cars last lap time (s) 162 | extern irsdkCVar ir_CarIdxBestLapTime; // float[64] Cars best lap time (s) 163 | extern irsdkCVar ir_CarIdxBestLapNum; // int[64] Cars best lap number () 164 | extern irsdkCVar ir_CarIdxTireCompound; // int[64] Cars current tire compound () 165 | extern irsdkCVar ir_CarIdxQualTireCompound; // int[64] Cars Qual tire compound () 166 | extern irsdkCVar ir_CarIdxQualTireCompoundLocked; // bool[64] Cars Qual tire compound is locked-in () 167 | extern irsdkCVar ir_CarIdxFastRepairsUsed; // int[64] How many fast repairs each car has used () 168 | extern irsdkCVar ir_PaceMode; // int[1] Are we pacing or not (irsdk_PaceMode) 169 | extern irsdkCVar ir_CarIdxPaceLine; // int[64] What line cars are pacing in or -1 if not pacing () 170 | extern irsdkCVar ir_CarIdxPaceRow; // int[64] What row cars are pacing in or -1 if not pacing () 171 | extern irsdkCVar ir_CarIdxPaceFlags; // int[64] Pacing status flags for each car (irsdk_PaceFlags) 172 | extern irsdkCVar ir_OnPitRoad; // bool[1] Is the player car on pit road between the cones () 173 | extern irsdkCVar ir_CarIdxSteer; // float[64] Steering wheel angle by car index (rad) 174 | extern irsdkCVar ir_CarIdxRPM; // float[64] Engine rpm by car index (revs/min) 175 | extern irsdkCVar ir_CarIdxGear; // int[64] -1=reverse 0=neutral 1..n=current gear by car index () 176 | extern irsdkCVar ir_SteeringWheelAngle; // float[1] Steering wheel angle (rad) 177 | extern irsdkCVar ir_Throttle; // float[1] 0=off throttle to 1=full throttle (%) 178 | extern irsdkCVar ir_Brake; // float[1] 0=brake released to 1=max pedal force (%) 179 | extern irsdkCVar ir_Clutch; // float[1] 0=disengaged to 1=fully engaged (%) 180 | extern irsdkCVar ir_Gear; // int[1] -1=reverse 0=neutral 1..n=current gear () 181 | extern irsdkCVar ir_RPM; // float[1] Engine rpm (revs/min) 182 | extern irsdkCVar ir_Lap; // int[1] Laps started count () 183 | extern irsdkCVar ir_LapCompleted; // int[1] Laps completed count () 184 | extern irsdkCVar ir_LapDist; // float[1] Meters traveled from S/F this lap (m) 185 | extern irsdkCVar ir_LapDistPct; // float[1] Percentage distance around lap (%) 186 | extern irsdkCVar ir_RaceLaps; // int[1] Laps completed in race () 187 | extern irsdkCVar ir_LapBestLap; // int[1] Players best lap number () 188 | extern irsdkCVar ir_LapBestLapTime; // float[1] Players best lap time (s) 189 | extern irsdkCVar ir_LapLastLapTime; // float[1] Players last lap time (s) 190 | extern irsdkCVar ir_LapCurrentLapTime; // float[1] Estimate of players current lap time as shown in F3 box (s) 191 | extern irsdkCVar ir_LapLasNLapSeq; // int[1] Player num consecutive clean laps completed for N average () 192 | extern irsdkCVar ir_LapLastNLapTime; // float[1] Player last N average lap time (s) 193 | extern irsdkCVar ir_LapBestNLapLap; // int[1] Player last lap in best N average lap time () 194 | extern irsdkCVar ir_LapBestNLapTime; // float[1] Player best N average lap time (s) 195 | extern irsdkCVar ir_LapDeltaToBestLap; // float[1] Delta time for best lap (s) 196 | extern irsdkCVar ir_LapDeltaToBestLap_DD; // float[1] Rate of change of delta time for best lap (s/s) 197 | extern irsdkCVar ir_LapDeltaToBestLap_OK; // bool[1] Delta time for best lap is valid () 198 | extern irsdkCVar ir_LapDeltaToOptimalLap; // float[1] Delta time for optimal lap (s) 199 | extern irsdkCVar ir_LapDeltaToOptimalLap_DD; // float[1] Rate of change of delta time for optimal lap (s/s) 200 | extern irsdkCVar ir_LapDeltaToOptimalLap_OK; // bool[1] Delta time for optimal lap is valid () 201 | extern irsdkCVar ir_LapDeltaToSessionBestLap; // float[1] Delta time for session best lap (s) 202 | extern irsdkCVar ir_LapDeltaToSessionBestLap_DD; // float[1] Rate of change of delta time for session best lap (s/s) 203 | extern irsdkCVar ir_LapDeltaToSessionBestLap_OK; // bool[1] Delta time for session best lap is valid () 204 | extern irsdkCVar ir_LapDeltaToSessionOptimalLap; // float[1] Delta time for session optimal lap (s) 205 | extern irsdkCVar ir_LapDeltaToSessionOptimalLap_DD; // float[1] Rate of change of delta time for session optimal lap (s/s) 206 | extern irsdkCVar ir_LapDeltaToSessionOptimalLap_OK; // bool[1] Delta time for session optimal lap is valid () 207 | extern irsdkCVar ir_LapDeltaToSessionLastlLap; // float[1] Delta time for session last lap (s) 208 | extern irsdkCVar ir_LapDeltaToSessionLastlLap_DD; // float[1] Rate of change of delta time for session last lap (s/s) 209 | extern irsdkCVar ir_LapDeltaToSessionLastlLap_OK; // bool[1] Delta time for session last lap is valid () 210 | extern irsdkCVar ir_Speed; // float[1] GPS vehicle speed (m/s) 211 | extern irsdkCVar ir_Yaw; // float[1] Yaw orientation (rad) 212 | extern irsdkCVar ir_YawNorth; // float[1] Yaw orientation relative to north (rad) 213 | extern irsdkCVar ir_Pitch; // float[1] Pitch orientation (rad) 214 | extern irsdkCVar ir_Roll; // float[1] Roll orientation (rad) 215 | extern irsdkCVar ir_EnterExitReset; // int[1] Indicate action the reset key will take 0 enter 1 exit 2 reset () 216 | extern irsdkCVar ir_TrackTemp; // float[1] Deprecated set to TrackTempCrew (C) 217 | extern irsdkCVar ir_TrackTempCrew; // float[1] Temperature of track measured by crew around track (C) 218 | extern irsdkCVar ir_AirTemp; // float[1] Temperature of air at start/finish line (C) 219 | extern irsdkCVar ir_WeatherType; // int[1] Weather type (0=constant 1=dynamic) () 220 | extern irsdkCVar ir_Skies; // int[1] Skies (0=clear/1=p cloudy/2=m cloudy/3=overcast) () 221 | extern irsdkCVar ir_AirDensity; // float[1] Density of air at start/finish line (kg/m^3) 222 | extern irsdkCVar ir_AirPressure; // float[1] Pressure of air at start/finish line (Hg) 223 | extern irsdkCVar ir_WindVel; // float[1] Wind velocity at start/finish line (m/s) 224 | extern irsdkCVar ir_WindDir; // float[1] Wind direction at start/finish line (rad) 225 | extern irsdkCVar ir_RelativeHumidity; // float[1] Relative Humidity (%) 226 | extern irsdkCVar ir_FogLevel; // float[1] Fog level (%) 227 | extern irsdkCVar ir_DCLapStatus; // int[1] Status of driver change lap requirements () 228 | extern irsdkCVar ir_DCDriversSoFar; // int[1] Number of team drivers who have run a stint () 229 | extern irsdkCVar ir_OkToReloadTextures; // bool[1] True if it is ok to reload car textures at this time () 230 | extern irsdkCVar ir_LoadNumTextures; // bool[1] True if the car_num texture will be loaded () 231 | extern irsdkCVar ir_CarLeftRight; // bitfield[1] Notify if car is to the left or right of driver (irsdk_CarLeftRight) 232 | extern irsdkCVar ir_PitsOpen; // bool[1] True if pit stop is allowed for the current player () 233 | extern irsdkCVar ir_VidCapEnabled; // bool[1] True if video capture system is enabled () 234 | extern irsdkCVar ir_VidCapActive; // bool[1] True if video currently being captured () 235 | extern irsdkCVar ir_PitRepairLeft; // float[1] Time left for mandatory pit repairs if repairs are active (s) 236 | extern irsdkCVar ir_PitOptRepairLeft; // float[1] Time left for optional repairs if repairs are active (s) 237 | extern irsdkCVar ir_PitstopActive; // bool[1] Is the player getting pit stop service () 238 | extern irsdkCVar ir_FastRepairUsed; // int[1] How many fast repairs used so far () 239 | extern irsdkCVar ir_FastRepairAvailable; // int[1] How many fast repairs left 255 is unlimited () 240 | extern irsdkCVar ir_LFTiresUsed; // int[1] How many left front tires used so far () 241 | extern irsdkCVar ir_RFTiresUsed; // int[1] How many right front tires used so far () 242 | extern irsdkCVar ir_LRTiresUsed; // int[1] How many left rear tires used so far () 243 | extern irsdkCVar ir_RRTiresUsed; // int[1] How many right rear tires used so far () 244 | extern irsdkCVar ir_LeftTireSetsUsed; // int[1] How many left tire sets used so far () 245 | extern irsdkCVar ir_RightTireSetsUsed; // int[1] How many right tire sets used so far () 246 | extern irsdkCVar ir_FrontTireSetsUsed; // int[1] How many front tire sets used so far () 247 | extern irsdkCVar ir_RearTireSetsUsed; // int[1] How many rear tire sets used so far () 248 | extern irsdkCVar ir_TireSetsUsed; // int[1] How many tire sets used so far () 249 | extern irsdkCVar ir_LFTiresAvailable; // int[1] How many left front tires are remaining 255 is unlimited () 250 | extern irsdkCVar ir_RFTiresAvailable; // int[1] How many right front tires are remaining 255 is unlimited () 251 | extern irsdkCVar ir_LRTiresAvailable; // int[1] How many left rear tires are remaining 255 is unlimited () 252 | extern irsdkCVar ir_RRTiresAvailable; // int[1] How many right rear tires are remaining 255 is unlimited () 253 | extern irsdkCVar ir_LeftTireSetsAvailable; // int[1] How many left tire sets are remaining 255 is unlimited () 254 | extern irsdkCVar ir_RightTireSetsAvailable; // int[1] How many right tire sets are remaining 255 is unlimited () 255 | extern irsdkCVar ir_FrontTireSetsAvailable; // int[1] How many front tire sets are remaining 255 is unlimited () 256 | extern irsdkCVar ir_RearTireSetsAvailable; // int[1] How many rear tire sets are remaining 255 is unlimited () 257 | extern irsdkCVar ir_TireSetsAvailable; // int[1] How many tire sets are remaining 255 is unlimited () 258 | extern irsdkCVar ir_CamCarIdx; // int[1] Active camera's focus car index () 259 | extern irsdkCVar ir_CamCameraNumber; // int[1] Active camera number () 260 | extern irsdkCVar ir_CamGroupNumber; // int[1] Active camera group number () 261 | extern irsdkCVar ir_CamCameraState; // bitfield[1] State of camera system (irsdk_CameraState) 262 | extern irsdkCVar ir_IsOnTrackCar; // bool[1] 1=Car on track physics running () 263 | extern irsdkCVar ir_IsInGarage; // bool[1] 1=Car in garage physics running () 264 | extern irsdkCVar ir_SteeringWheelPctTorque; // float[1] Force feedback % max torque on steering shaft unsigned (%) 265 | extern irsdkCVar ir_SteeringWheelPctTorqueSign; // float[1] Force feedback % max torque on steering shaft signed (%) 266 | extern irsdkCVar ir_SteeringWheelPctTorqueSignStops; // float[1] Force feedback % max torque on steering shaft signed stops (%) 267 | extern irsdkCVar ir_SteeringWheelPctDamper; // float[1] Force feedback % max damping (%) 268 | extern irsdkCVar ir_SteeringWheelAngleMax; // float[1] Steering wheel max angle (rad) 269 | extern irsdkCVar ir_SteeringWheelLimiter; // float[1] Force feedback limiter strength limits impacts and oscillation (%) 270 | extern irsdkCVar ir_ShiftIndicatorPct; // float[1] DEPRECATED use DriverCarSLBlinkRPM instead (%) 271 | extern irsdkCVar ir_ShiftPowerPct; // float[1] Friction torque applied to gears when shifting or grinding (%) 272 | extern irsdkCVar ir_ShiftGrindRPM; // float[1] RPM of shifter grinding noise (RPM) 273 | extern irsdkCVar ir_ThrottleRaw; // float[1] Raw throttle input 0=off throttle to 1=full throttle (%) 274 | extern irsdkCVar ir_BrakeRaw; // float[1] Raw brake input 0=brake released to 1=max pedal force (%) 275 | extern irsdkCVar ir_HandbrakeRaw; // float[1] Raw handbrake input 0=handbrake released to 1=max force (%) 276 | extern irsdkCVar ir_SteeringWheelPeakForceNm; // float[1] Peak torque mapping to direct input units for FFB (N*m) 277 | extern irsdkCVar ir_SteeringWheelMaxForceNm; // float[1] Value of strength or max force slider in Nm for FFB (N*m) 278 | extern irsdkCVar ir_SteeringWheelUseLinear; // bool[1] True if steering wheel force is using linear mode () 279 | extern irsdkCVar ir_BrakeABSactive; // bool[1] true if abs is currently reducing brake force pressure () 280 | extern irsdkCVar ir_EngineWarnings; // bitfield[1] Bitfield for warning lights (irsdk_EngineWarnings) 281 | extern irsdkCVar ir_FuelLevel; // float[1] Liters of fuel remaining (l) 282 | extern irsdkCVar ir_FuelLevelPct; // float[1] Percent fuel remaining (%) 283 | extern irsdkCVar ir_PitSvFlags; // bitfield[1] Bitfield of pit service checkboxes (irsdk_PitSvFlags) 284 | extern irsdkCVar ir_PitSvLFP; // float[1] Pit service left front tire pressure (kPa) 285 | extern irsdkCVar ir_PitSvRFP; // float[1] Pit service right front tire pressure (kPa) 286 | extern irsdkCVar ir_PitSvLRP; // float[1] Pit service left rear tire pressure (kPa) 287 | extern irsdkCVar ir_PitSvRRP; // float[1] Pit service right rear tire pressure (kPa) 288 | extern irsdkCVar ir_PitSvFuel; // float[1] Pit service fuel add amount (l) 289 | extern irsdkCVar ir_PitSvTireCompound; // int[1] Pit service pending tire compound () 290 | extern irsdkCVar ir_CarIdxP2P_Status; // bool[64] Push2Pass active or not () 291 | extern irsdkCVar ir_CarIdxP2P_Count; // int[64] Push2Pass count of usage (or remaining in Race) () 292 | extern irsdkCVar ir_ReplayPlaySpeed; // int[1] Replay playback speed () 293 | extern irsdkCVar ir_ReplayPlaySlowMotion; // bool[1] 0=not slow motion 1=replay is in slow motion () 294 | extern irsdkCVar ir_ReplaySessionTime; // double[1] Seconds since replay session start (s) 295 | extern irsdkCVar ir_ReplaySessionNum; // int[1] Replay session number () 296 | extern irsdkCVar ir_TireLF_RumblePitch; // float[1] Players LF Tire Sound rumblestrip pitch (Hz) 297 | extern irsdkCVar ir_TireRF_RumblePitch; // float[1] Players RF Tire Sound rumblestrip pitch (Hz) 298 | extern irsdkCVar ir_TireLR_RumblePitch; // float[1] Players LR Tire Sound rumblestrip pitch (Hz) 299 | extern irsdkCVar ir_TireRR_RumblePitch; // float[1] Players RR Tire Sound rumblestrip pitch (Hz) 300 | extern irsdkCVar ir_SteeringWheelTorque_ST; // float[6] Output torque on steering shaft at 360 Hz (N*m) 301 | extern irsdkCVar ir_SteeringWheelTorque; // float[1] Output torque on steering shaft (N*m) 302 | extern irsdkCVar ir_VelocityZ_ST; // float[6] Z velocity (m/s at 360 Hz) 303 | extern irsdkCVar ir_VelocityY_ST; // float[6] Y velocity (m/s at 360 Hz) 304 | extern irsdkCVar ir_VelocityX_ST; // float[6] X velocity (m/s at 360 Hz) 305 | extern irsdkCVar ir_VelocityZ; // float[1] Z velocity (m/s) 306 | extern irsdkCVar ir_VelocityY; // float[1] Y velocity (m/s) 307 | extern irsdkCVar ir_VelocityX; // float[1] X velocity (m/s) 308 | extern irsdkCVar ir_YawRate_ST; // float[6] Yaw rate at 360 Hz (rad/s) 309 | extern irsdkCVar ir_PitchRate_ST; // float[6] Pitch rate at 360 Hz (rad/s) 310 | extern irsdkCVar ir_RollRate_ST; // float[6] Roll rate at 360 Hz (rad/s) 311 | extern irsdkCVar ir_YawRate; // float[1] Yaw rate (rad/s) 312 | extern irsdkCVar ir_PitchRate; // float[1] Pitch rate (rad/s) 313 | extern irsdkCVar ir_RollRate; // float[1] Roll rate (rad/s) 314 | extern irsdkCVar ir_VertAccel_ST; // float[6] Vertical acceleration (including gravity) at 360 Hz (m/s^2) 315 | extern irsdkCVar ir_LatAccel_ST; // float[6] Lateral acceleration (including gravity) at 360 Hz (m/s^2) 316 | extern irsdkCVar ir_LongAccel_ST; // float[6] Longitudinal acceleration (including gravity) at 360 Hz (m/s^2) 317 | extern irsdkCVar ir_VertAccel; // float[1] Vertical acceleration (including gravity) (m/s^2) 318 | extern irsdkCVar ir_LatAccel; // float[1] Lateral acceleration (including gravity) (m/s^2) 319 | extern irsdkCVar ir_LongAccel; // float[1] Longitudinal acceleration (including gravity) (m/s^2) 320 | extern irsdkCVar ir_dcStarter; // bool[1] In car trigger car starter () 321 | extern irsdkCVar ir_dpRTireChange; // float[1] Pitstop right tire change request () 322 | extern irsdkCVar ir_dpLTireChange; // float[1] Pitstop left tire change request () 323 | extern irsdkCVar ir_dpFuelFill; // float[1] Pitstop fuel fill flag () 324 | extern irsdkCVar ir_dpWindshieldTearoff; // float[1] Pitstop windshield tearoff () 325 | extern irsdkCVar ir_dpFuelAddKg; // float[1] Pitstop fuel add ammount (kg) 326 | extern irsdkCVar ir_dpFastRepair; // float[1] Pitstop fast repair set () 327 | extern irsdkCVar ir_dcBrakeBias; // float[1] In car brake bias adjustment () 328 | extern irsdkCVar ir_dpLFTireColdPress; // float[1] Pitstop lf tire cold pressure adjustment (Pa) 329 | extern irsdkCVar ir_dpRFTireColdPress; // float[1] Pitstop rf cold tire pressure adjustment (Pa) 330 | extern irsdkCVar ir_dpLRTireColdPress; // float[1] Pitstop lr tire cold pressure adjustment (Pa) 331 | extern irsdkCVar ir_dpRRTireColdPress; // float[1] Pitstop rr cold tire pressure adjustment (Pa) 332 | extern irsdkCVar ir_dpWeightJackerLeft; // float[1] Pitstop left wedge/weight jacker adjustment () 333 | extern irsdkCVar ir_dpWeightJackerRight; // float[1] Pitstop right wedge/weight jacker adjustment () 334 | extern irsdkCVar ir_WaterTemp; // float[1] Engine coolant temp (C) 335 | extern irsdkCVar ir_WaterLevel; // float[1] Engine coolant level (l) 336 | extern irsdkCVar ir_FuelPress; // float[1] Engine fuel pressure (bar) 337 | extern irsdkCVar ir_FuelUsePerHour; // float[1] Engine fuel used instantaneous (kg/h) 338 | extern irsdkCVar ir_OilTemp; // float[1] Engine oil temperature (C) 339 | extern irsdkCVar ir_OilPress; // float[1] Engine oil pressure (bar) 340 | extern irsdkCVar ir_OilLevel; // float[1] Engine oil level (l) 341 | extern irsdkCVar ir_Voltage; // float[1] Engine voltage (V) 342 | extern irsdkCVar ir_ManifoldPress; // float[1] Engine manifold pressure (bar) 343 | extern irsdkCVar ir_RFcoldPressure; // float[1] RF tire cold pressure as set in the garage (kPa) 344 | extern irsdkCVar ir_RFtempCL; // float[1] RF tire left carcass temperature (C) 345 | extern irsdkCVar ir_RFtempCM; // float[1] RF tire middle carcass temperature (C) 346 | extern irsdkCVar ir_RFtempCR; // float[1] RF tire right carcass temperature (C) 347 | extern irsdkCVar ir_RFwearL; // float[1] RF tire left percent tread remaining (%) 348 | extern irsdkCVar ir_RFwearM; // float[1] RF tire middle percent tread remaining (%) 349 | extern irsdkCVar ir_RFwearR; // float[1] RF tire right percent tread remaining (%) 350 | extern irsdkCVar ir_LFcoldPressure; // float[1] LF tire cold pressure as set in the garage (kPa) 351 | extern irsdkCVar ir_LFtempCL; // float[1] LF tire left carcass temperature (C) 352 | extern irsdkCVar ir_LFtempCM; // float[1] LF tire middle carcass temperature (C) 353 | extern irsdkCVar ir_LFtempCR; // float[1] LF tire right carcass temperature (C) 354 | extern irsdkCVar ir_LFwearL; // float[1] LF tire left percent tread remaining (%) 355 | extern irsdkCVar ir_LFwearM; // float[1] LF tire middle percent tread remaining (%) 356 | extern irsdkCVar ir_LFwearR; // float[1] LF tire right percent tread remaining (%) 357 | extern irsdkCVar ir_RRcoldPressure; // float[1] RR tire cold pressure as set in the garage (kPa) 358 | extern irsdkCVar ir_RRtempCL; // float[1] RR tire left carcass temperature (C) 359 | extern irsdkCVar ir_RRtempCM; // float[1] RR tire middle carcass temperature (C) 360 | extern irsdkCVar ir_RRtempCR; // float[1] RR tire right carcass temperature (C) 361 | extern irsdkCVar ir_RRwearL; // float[1] RR tire left percent tread remaining (%) 362 | extern irsdkCVar ir_RRwearM; // float[1] RR tire middle percent tread remaining (%) 363 | extern irsdkCVar ir_RRwearR; // float[1] RR tire right percent tread remaining (%) 364 | extern irsdkCVar ir_LRcoldPressure; // float[1] LR tire cold pressure as set in the garage (kPa) 365 | extern irsdkCVar ir_LRtempCL; // float[1] LR tire left carcass temperature (C) 366 | extern irsdkCVar ir_LRtempCM; // float[1] LR tire middle carcass temperature (C) 367 | extern irsdkCVar ir_LRtempCR; // float[1] LR tire right carcass temperature (C) 368 | extern irsdkCVar ir_LRwearL; // float[1] LR tire left percent tread remaining (%) 369 | extern irsdkCVar ir_LRwearM; // float[1] LR tire middle percent tread remaining (%) 370 | extern irsdkCVar ir_LRwearR; // float[1] LR tire right percent tread remaining (%) 371 | extern irsdkCVar ir_RRSHshockDefl; // float[1] RRSH shock deflection (m) 372 | extern irsdkCVar ir_RRSHshockDefl_ST; // float[6] RRSH shock deflection at 360 Hz (m) 373 | extern irsdkCVar ir_RRSHshockVel; // float[1] RRSH shock velocity (m/s) 374 | extern irsdkCVar ir_RRSHshockVel_ST; // float[6] RRSH shock velocity at 360 Hz (m/s) 375 | extern irsdkCVar ir_LRSHshockDefl; // float[1] LRSH shock deflection (m) 376 | extern irsdkCVar ir_LRSHshockDefl_ST; // float[6] LRSH shock deflection at 360 Hz (m) 377 | extern irsdkCVar ir_LRSHshockVel; // float[1] LRSH shock velocity (m/s) 378 | extern irsdkCVar ir_LRSHshockVel_ST; // float[6] LRSH shock velocity at 360 Hz (m/s) 379 | extern irsdkCVar ir_RFSHshockDefl; // float[1] RFSH shock deflection (m) 380 | extern irsdkCVar ir_RFSHshockDefl_ST; // float[6] RFSH shock deflection at 360 Hz (m) 381 | extern irsdkCVar ir_RFSHshockVel; // float[1] RFSH shock velocity (m/s) 382 | extern irsdkCVar ir_RFSHshockVel_ST; // float[6] RFSH shock velocity at 360 Hz (m/s) 383 | extern irsdkCVar ir_LFSHshockDefl; // float[1] LFSH shock deflection (m) 384 | extern irsdkCVar ir_LFSHshockDefl_ST; // float[6] LFSH shock deflection at 360 Hz (m) 385 | extern irsdkCVar ir_LFSHshockVel; // float[1] LFSH shock velocity (m/s) 386 | extern irsdkCVar ir_LFSHshockVel_ST; // float[6] LFSH shock velocity at 360 Hz (m/s) 387 | 388 | extern Session ir_session; 389 | 390 | // Keep the session data updated. 391 | // Will block for around 16 milliseconds. 392 | ConnectionStatus ir_tick(); 393 | 394 | // Let the session data tracking know that the config has changed. 395 | void ir_handleConfigChange(); 396 | 397 | // Return whether we're in the process of getting in the car, waiting for others 398 | // to grid, or doing pace laps before the actual race start. 399 | bool ir_isPreStart(); 400 | 401 | // Estimate time for a full lap. 402 | float ir_estimateLaptime(); 403 | 404 | // Get the best known position, from the latest session we can find. 405 | int ir_getPosition( int carIdx ); 406 | 407 | // Get lap delta to P0 car if available. 408 | int ir_getLapDeltaToLeader( int carIdx, int ldrIdx ); 409 | 410 | // Print all the variables the sim supports. 411 | void ir_printVariables(); 412 | --------------------------------------------------------------------------------