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 : }
|