@@ -376,11 +376,68 @@ static void __declspec(naked) HOOK_CTimer_Update()
376376 __asm
377377 {
378378 popad
379- mov ecx,dword ptr ds:[0B7CB28h]
379+ // Original code: mov ecx, ds:_timerFunction
380+ // GTA has its own NULL check at 0x561B19, so we just execute the original instruction
381+ mov ecx, dword ptr ds:[0B7CB28h]
380382 jmp CONTINUE_CTimer_Update
381383 }
382384}
383385
386+ // ////////////////////////////////////////////////////////////////////////////////////////
387+ //
388+ // CTimer::Suspend / CTimer::Resume
389+ //
390+ // Prevent crashes if _timerFunction is NULL during init
391+ //
392+ // ////////////////////////////////////////////////////////////////////////////////////////
393+ #define HOOKPOS_CTimer_Suspend 0x5619E9
394+ #define HOOKSIZE_CTimer_Suspend 6
395+ static const DWORD CONTINUE_CTimer_Suspend = 0x5619EF ;
396+ static void _declspec (naked) HOOK_CTimer_Suspend()
397+ {
398+ _asm
399+ {
400+ // Check if _timerFunction is NULL
401+ mov eax, dword ptr ds:[0B7CB28h]
402+ test eax, eax
403+ jz skip_suspend
404+
405+ // Original code: call [_timerFunction]
406+ call eax
407+ jmp CONTINUE_CTimer_Suspend
408+
409+ skip_suspend:
410+ // If NULL, return zero timestamp (EAX:EDX = 0) to avoid corrupting pause time
411+ xor eax, eax
412+ xor edx, edx
413+ jmp CONTINUE_CTimer_Suspend
414+ }
415+ }
416+
417+ #define HOOKPOS_CTimer_Resume 0x561A11
418+ #define HOOKSIZE_CTimer_Resume 6
419+ static const DWORD CONTINUE_CTimer_Resume = 0x561A17 ;
420+ static void _declspec (naked) HOOK_CTimer_Resume()
421+ {
422+ _asm
423+ {
424+ // Check if _timerFunction is NULL
425+ mov eax, dword ptr ds:[0B7CB28h]
426+ test eax, eax
427+ jz skip_resume
428+
429+ // Original code: call [_timerFunction]
430+ call eax
431+ jmp CONTINUE_CTimer_Resume
432+
433+ skip_resume:
434+ // If NULL, return zero timestamp (EAX:EDX = 0) to avoid corrupting resume time calculations
435+ xor eax, eax
436+ xor edx, edx
437+ jmp CONTINUE_CTimer_Resume
438+ }
439+ }
440+
384441// ////////////////////////////////////////////////////////////////////////////////////////
385442//
386443// Photograph screen grab in windowed mode
@@ -788,6 +845,8 @@ void CMultiplayerSA::InitHooks_Rendering()
788845 EZHookInstall (Check_NoOfVisibleEntities);
789846 EZHookInstall (WinLoop);
790847 EZHookInstall (CTimer_Update);
848+ EZHookInstall (CTimer_Suspend);
849+ EZHookInstall (CTimer_Resume);
791850 EZHookInstall (psGrabScreen);
792851 EZHookInstallChecked (CClouds_RenderSkyPolys);
793852 EZHookInstallChecked (RwCameraSetNearClipPlane);
0 commit comments