| 329 | } |
| 330 | |
| 331 | bool InstallUserCapture() { |
| 332 | if (!g_writerStart) return false; |
| 333 | bool expected = false; |
| 334 | if (!g_captureInstalled.compare_exchange_strong(expected, true)) return true; |
| 335 | |
| 336 | g_hookPoint = g_writerStart + WRITER_PROLOGUE; |
| 337 | if (!BuildTrampoline(g_hookPoint)) { |
| 338 | g_captureInstalled.store(false); |
| 339 | return false; |
| 340 | } |
| 341 | g_savedLen = WRITER_STOLEN; |
| 342 | memcpy(g_savedBytes, g_hookPoint, g_savedLen); |
| 343 | if (!MakeWritable(g_hookPoint, g_savedLen)) { |
| 344 | g_captureInstalled.store(false); |
| 345 | return false; |
| 346 | } |
| 347 | int32_t rel = (int32_t)((uintptr_t)g_trampoline - ((uintptr_t)g_hookPoint + 5)); |
| 348 | g_hookPoint[0] = 0xE9; |
| 349 | memcpy(g_hookPoint + 1, &rel, 4); |
| 350 | for (size_t i = 5; i < g_savedLen; ++i) g_hookPoint[i] = 0x90; // nop pad |
| 351 | __builtin___clear_cache((char*)g_hookPoint, (char*)g_hookPoint + g_savedLen); |
| 352 | LOG("[Stats] LivePlaytime CUser-capture detour installed at %p", g_hookPoint); |
| 353 | return true; |
| 354 | } |
| 355 | |
| 356 | void RemoveUserCapture() { |
| 357 | if (!g_captureInstalled.load(std::memory_order_acquire)) return; |
no test coverage detected