LCOV - code coverage report
Current view: top level - src/lmu_sm_interface - SharedMemoryInterface.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 90.7 % 75 68
Test Date: 2026-03-01 21:30:38 Functions: 100.0 % 9 9
Branches: 86.8 % 38 33

             Branch data     Line data    Source code
       1                 :             : #pragma once
       2                 :             : #include "InternalsPlugin.hpp"
       3                 :             : 
       4                 :             : /*
       5                 :             : * Usage example:
       6                 :             : 
       7                 :             : int main(int argc, char* argv[])
       8                 :             : {
       9                 :             :     int retVal = 0;
      10                 :             :     if (argc < 2) {
      11                 :             :         std::cerr << "Usage: child.exe <LMU-pid>\n";
      12                 :             :         return 1;
      13                 :             :     }
      14                 :             :     // Get the LMU Handle
      15                 :             :     DWORD parentPid = 0;
      16                 :             :     try {
      17                 :             :         parentPid = static_cast<DWORD>(std::stoul(argv[1]));
      18                 :             :     }
      19                 :             :     catch (...) {
      20                 :             :         std::cerr << "Invalid parent PID argument.\n";
      21                 :             :         return 1;
      22                 :             :     }
      23                 :             :     auto smLock = SharedMemoryLock::MakeSharedMemoryLock();
      24                 :             :     if (!smLock.has_value()) {
      25                 :             :         std::cerr << "Cannot initialize SharedMemoryLock.\n";
      26                 :             :         return 1;
      27                 :             :     }
      28                 :             :     static SharedMemoryObjectOut copiedMem;
      29                 :             :     // Try to open a handle to the parent process with SYNCHRONIZE right.
      30                 :             :     // SYNCHRONIZE is enough to wait on the process handle for exit.
      31                 :             :     HANDLE hParent = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION, FALSE, parentPid);
      32                 :             :     HANDLE hEvent = OpenEventA(SYNCHRONIZE, FALSE, "LMU_Data_Event");
      33                 :             :     HANDLE hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, L"LMU_Data");
      34                 :             :     if (hParent && hEvent && hMapFile) {
      35                 :             :         if (SharedMemoryLayout* pBuf = (SharedMemoryLayout*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(SharedMemoryLayout))) {
      36                 :             :             HANDLE objectHandlesArray[2] = { hParent, hEvent };
      37                 :             :             for (DWORD waitObject = WaitForMultipleObjects(2, objectHandlesArray, FALSE, INFINITE); waitObject != WAIT_OBJECT_0; waitObject = WaitForMultipleObjects(2, objectHandlesArray, FALSE, INFINITE)) {
      38                 :             :                 if (waitObject == WAIT_OBJECT_0 + 1) {
      39                 :             :                     smLock->Lock();
      40                 :             :                     CopySharedMemoryObj(copiedMem, pBuf->data);
      41                 :             :                     smLock->Unlock();
      42                 :             :                     // >>>>> ProcessSharedMemory(copiedMem); <<<<<<
      43                 :             :                 }
      44                 :             :                 else {
      45                 :             :                     std::cerr << "Wait failed: " << GetLastError() << "\n";
      46                 :             :                     break;
      47                 :             :                 }
      48                 :             :             }
      49                 :             :             UnmapViewOfFile(pBuf);
      50                 :             :         }
      51                 :             :         else {
      52                 :             :             std::cerr << "Could not map view of file. Error: " << GetLastError() << std::endl;
      53                 :             :             retVal = 1;
      54                 :             :         }
      55                 :             :     }
      56                 :             :     else {
      57                 :             :         std::cerr << "Something went wrong durin initialization. Error: " << GetLastError() << std::endl;
      58                 :             :         retVal = 1;
      59                 :             :     }
      60                 :             :     if (hMapFile)
      61                 :             :         CloseHandle(hMapFile);
      62                 :             :     if (hEvent)
      63                 :             :         CloseHandle(hEvent);
      64                 :             :     if (hParent)
      65                 :             :         CloseHandle(hParent);
      66                 :             : 
      67                 :             :     return retVal;
      68                 :             : }
      69                 :             : 
      70                 :             : */
      71                 :             : 
      72                 :             : #define LMU_SHARED_MEMORY_FILE "LMU_Data"
      73                 :             : #define LMU_SHARED_MEMORY_EVENT "LMU_Data_Event"
      74                 :             : enum SharedMemoryEvent : uint32_t {
      75                 :             :     SME_ENTER,
      76                 :             :     SME_EXIT,
      77                 :             :     SME_STARTUP,
      78                 :             :     SME_SHUTDOWN,
      79                 :             :     SME_LOAD,
      80                 :             :     SME_UNLOAD,
      81                 :             :     SME_START_SESSION,
      82                 :             :     SME_END_SESSION,
      83                 :             :     SME_ENTER_REALTIME,
      84                 :             :     SME_EXIT_REALTIME,
      85                 :             :     SME_UPDATE_SCORING,
      86                 :             :     SME_UPDATE_TELEMETRY,
      87                 :             :     SME_INIT_APPLICATION,
      88                 :             :     SME_UNINIT_APPLICATION,
      89                 :             :     SME_SET_ENVIRONMENT,
      90                 :             :     SME_FFB,
      91                 :             :     SME_MAX
      92                 :             : };
      93                 :             : 
      94                 :             : class SharedMemoryLock {
      95                 :             : public:
      96                 :       12423 :     static std::optional<SharedMemoryLock> MakeSharedMemoryLock() {
      97                 :       12423 :         SharedMemoryLock memoryLock;
      98   [ +  -  +  + ]:       12423 :         if (memoryLock.Init()) {
      99         [ +  - ]:       12420 :             return std::move(memoryLock);
     100                 :             :         }
     101                 :           3 :         return std::nullopt;
     102                 :       12423 :     }
     103                 :        9928 :     bool Lock(DWORD dwMilliseconds = INFINITE) {
     104                 :        9928 :         int MAX_SPINS = 4000;
     105         [ +  + ]:       25928 :         for (int spins = 0; spins < MAX_SPINS; ++spins) {
     106         [ +  + ]:       25924 :             if (InterlockedCompareExchange(&mDataPtr->busy, 1, 0) == 0)
     107                 :        9924 :                 return true;
     108                 :       16000 :             YieldProcessor(); // CPU pause hint
     109                 :             :         }
     110                 :           4 :         InterlockedIncrement(&mDataPtr->waiters);
     111                 :             :         while (true) {
     112         [ -  + ]:           4 :             if (InterlockedCompareExchange(&mDataPtr->busy, 1, 0) == 0) {
     113                 :           0 :                 InterlockedDecrement(&mDataPtr->waiters);
     114                 :           0 :                 return true;
     115                 :             :             }
     116                 :           4 :             return WaitForSingleObject(mWaitEventHandle, dwMilliseconds) == WAIT_OBJECT_0;
     117                 :             :         }
     118                 :             :     }
     119                 :        9926 :     void Unlock() {
     120                 :        9926 :         InterlockedExchange(&mDataPtr->busy, 0);
     121         [ +  + ]:        9926 :         if (mDataPtr->waiters > 0) {
     122                 :        4769 :             SetEvent(mWaitEventHandle);
     123                 :             :         }
     124                 :        9926 :     }
     125                 :           2 :     void Reset() { // Call this function only from the core application.
     126                 :           2 :         mDataPtr->waiters = 0;
     127                 :           2 :         mDataPtr->busy = 0;
     128                 :           2 :     }
     129                 :       62076 :     ~SharedMemoryLock() {
     130         [ +  + ]:       62076 :         if (mWaitEventHandle)
     131                 :       12420 :             CloseHandle(mWaitEventHandle);
     132         [ +  + ]:       62076 :         if (mMapHandle)
     133                 :       12420 :             CloseHandle(mMapHandle);
     134         [ +  + ]:       62076 :         if (mDataPtr)
     135                 :       12420 :             UnmapViewOfFile(mDataPtr);
     136                 :       62076 :     }
     137                 :       49653 :     SharedMemoryLock(SharedMemoryLock&& other) : mMapHandle(std::exchange(other.mMapHandle, nullptr)), mWaitEventHandle(std::exchange(other.mWaitEventHandle, nullptr)) ,
     138                 :       49653 :         mDataPtr(std::exchange(other.mDataPtr, nullptr)) {}
     139                 :           3 :     SharedMemoryLock& operator=(SharedMemoryLock&& other) {
     140                 :           3 :         std::swap(mMapHandle, other.mMapHandle);
     141                 :           3 :         std::swap(mWaitEventHandle, other.mWaitEventHandle);
     142                 :           3 :         std::swap(mDataPtr, other.mDataPtr);
     143                 :           3 :         return *this;
     144                 :             :     }
     145                 :             : private:
     146                 :             :     struct LockData {
     147                 :             :         volatile LONG waiters;
     148                 :             :         volatile LONG busy;
     149                 :             :     };
     150                 :             :     SharedMemoryLock() = default;
     151                 :       12423 :     bool Init() {
     152                 :       12423 :         mMapHandle = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (DWORD)sizeof(LockData), "LMU_SharedMemoryLockData");
     153         [ +  + ]:       12423 :         if (!mMapHandle) {
     154                 :           3 :             return false;
     155                 :             :         }
     156                 :       12420 :         mDataPtr = (LockData*)MapViewOfFile(mMapHandle, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(LockData));
     157         [ -  + ]:       12420 :         if (!mDataPtr) {
     158                 :           0 :             CloseHandle(mMapHandle);
     159                 :           0 :             return false;
     160                 :             :         }
     161         [ +  + ]:       12420 :         if (GetLastError() != ERROR_ALREADY_EXISTS) {
     162                 :           1 :             Reset();
     163                 :             :         }
     164                 :       12420 :         mWaitEventHandle = CreateEventA(NULL, FALSE, FALSE, "LMU_SharedMemoryLockEvent");
     165         [ -  + ]:       12420 :         if (!mWaitEventHandle) {
     166                 :           0 :             UnmapViewOfFile(mDataPtr);
     167                 :           0 :             CloseHandle(mMapHandle);
     168                 :           0 :             return false;
     169                 :             :         }
     170                 :       12420 :         return true;
     171                 :             :     }
     172                 :             :     HANDLE mMapHandle = NULL;
     173                 :             :     HANDLE mWaitEventHandle = NULL;
     174                 :             :     LockData* mDataPtr = nullptr;
     175                 :             : };
     176                 :             : 
     177                 :             : struct SharedMemoryScoringData { // Remember to check CopySharedMemoryObj still works properly when updating this struct
     178                 :             :     ScoringInfoV01 scoringInfo;
     179                 :             :     size_t scoringStreamSize;
     180                 :             :     VehicleScoringInfoV01 vehScoringInfo[104]; // MUST NOT BE MOVED!
     181                 :             :     char scoringStream[65536];
     182                 :             : };
     183                 :             : 
     184                 :             : struct SharedMemoryTelemtryData { // Remember to check CopySharedMemoryObj still works properly when updating this struct
     185                 :             :     uint8_t activeVehicles;
     186                 :             :     uint8_t playerVehicleIdx;
     187                 :             :     bool playerHasVehicle;
     188                 :             :     TelemInfoV01 telemInfo[104];
     189                 :             : };
     190                 :             : 
     191                 :             : struct SharedMemoryPathData {
     192                 :             :     char userData[MAX_PATH];
     193                 :             :     char customVariables[MAX_PATH];
     194                 :             :     char stewardResults[MAX_PATH];
     195                 :             :     char playerProfile[MAX_PATH];
     196                 :             :     char pluginsFolder[MAX_PATH];
     197                 :             : };
     198                 :             : 
     199                 :             : struct SharedMemoryGeneric {
     200                 :             :     SharedMemoryEvent events[SharedMemoryEvent::SME_MAX];
     201                 :             :     long gameVersion;
     202                 :             :     float FFBTorque;
     203                 :             :     ApplicationStateV01 appInfo;
     204                 :             : };
     205                 :             : 
     206                 :             : struct SharedMemoryObjectOut { // Remember to check CopySharedMemoryObj still works properly when updating this struct
     207                 :             :     SharedMemoryGeneric generic;
     208                 :             :     SharedMemoryPathData paths;
     209                 :             :     SharedMemoryScoringData scoring;
     210                 :             :     SharedMemoryTelemtryData telemetry;
     211                 :             : };
     212                 :             : 
     213                 :             : struct SharedMemoryLayout {
     214                 :             :     SharedMemoryObjectOut data;
     215                 :             : };
     216                 :             : 
     217                 :        9928 : static void CopySharedMemoryObj(SharedMemoryObjectOut& dst, SharedMemoryObjectOut& src) {
     218                 :        9928 :     memcpy(&dst.generic, &src.generic, sizeof(SharedMemoryGeneric));
     219         [ +  + ]:        9928 :     if (src.generic.events[SME_UPDATE_SCORING]) {
     220                 :           5 :         memcpy(&dst.scoring.scoringInfo, &src.scoring.scoringInfo, sizeof(ScoringInfoV01));
     221                 :           5 :         memcpy(&dst.scoring.vehScoringInfo, &src.scoring.vehScoringInfo, src.scoring.scoringInfo.mNumVehicles * sizeof(VehicleScoringInfoV01));
     222                 :           5 :         memcpy(&dst.scoring.scoringStream, &src.scoring.scoringStream, src.scoring.scoringStreamSize);
     223                 :           5 :         dst.scoring.scoringStreamSize = src.scoring.scoringStreamSize;
     224                 :           5 :         dst.scoring.scoringStream[dst.scoring.scoringStreamSize] = '\0';
     225                 :           5 :         dst.scoring.scoringInfo.mVehicle = &dst.scoring.vehScoringInfo[0];
     226                 :           5 :         dst.scoring.scoringInfo.mResultsStream = &dst.scoring.scoringStream[0];
     227                 :             :     }
     228         [ +  + ]:        9928 :     if (src.generic.events[SME_UPDATE_TELEMETRY]) {
     229                 :           8 :         dst.telemetry.activeVehicles = src.telemetry.activeVehicles;
     230                 :           8 :         dst.telemetry.playerHasVehicle = src.telemetry.playerHasVehicle;
     231                 :           8 :         dst.telemetry.playerVehicleIdx = src.telemetry.playerVehicleIdx;
     232                 :           8 :         memcpy(&dst.telemetry.telemInfo, &src.telemetry.telemInfo, src.telemetry.activeVehicles * sizeof(TelemInfoV01));
     233                 :             :     }
     234   [ +  +  +  +  :        9928 :     if (src.generic.events[SME_ENTER] || src.generic.events[SME_EXIT] || src.generic.events[SME_SET_ENVIRONMENT]) {
                   +  + ]
     235                 :           5 :         memcpy(&dst.paths, &src.paths, sizeof(SharedMemoryPathData));
     236                 :             :     }
     237                 :        9928 : }
        

Generated by: LCOV version 2.0-1