@@ -358,11 +358,68 @@ static void _declspec(naked) HOOK_CTimer_Update()
358358 _asm
359359 {
360360 popad
361- mov ecx,dword ptr ds:[0B7CB28h]
361+ // Original code: mov ecx, ds:_timerFunction
362+ // GTA has its own NULL check at 0x561B19, so we just execute the original instruction
363+ mov ecx, dword ptr ds:[0B7CB28h]
362364 jmp CONTINUE_CTimer_Update
363365 }
364366}
365367
368+ // ////////////////////////////////////////////////////////////////////////////////////////
369+ //
370+ // CTimer::Suspend / CTimer::Resume
371+ //
372+ // Prevent crashes if _timerFunction is NULL during init
373+ //
374+ // ////////////////////////////////////////////////////////////////////////////////////////
375+ #define HOOKPOS_CTimer_Suspend 0x5619E9
376+ #define HOOKSIZE_CTimer_Suspend 6
377+ static const DWORD CONTINUE_CTimer_Suspend = 0x5619EF ;
378+ static void _declspec (naked) HOOK_CTimer_Suspend()
379+ {
380+ _asm
381+ {
382+ // Check if _timerFunction is NULL
383+ mov eax, dword ptr ds:[0B7CB28h]
384+ test eax, eax
385+ jz skip_suspend
386+
387+ // Original code: call [_timerFunction]
388+ call eax
389+ jmp CONTINUE_CTimer_Suspend
390+
391+ skip_suspend:
392+ // If NULL, return zero timestamp (EAX:EDX = 0) to avoid corrupting pause time
393+ xor eax, eax
394+ xor edx, edx
395+ jmp CONTINUE_CTimer_Suspend
396+ }
397+ }
398+
399+ #define HOOKPOS_CTimer_Resume 0x561A11
400+ #define HOOKSIZE_CTimer_Resume 6
401+ static const DWORD CONTINUE_CTimer_Resume = 0x561A17 ;
402+ static void _declspec (naked) HOOK_CTimer_Resume()
403+ {
404+ _asm
405+ {
406+ // Check if _timerFunction is NULL
407+ mov eax, dword ptr ds:[0B7CB28h]
408+ test eax, eax
409+ jz skip_resume
410+
411+ // Original code: call [_timerFunction]
412+ call eax
413+ jmp CONTINUE_CTimer_Resume
414+
415+ skip_resume:
416+ // If NULL, return zero timestamp (EAX:EDX = 0) to avoid corrupting resume time calculations
417+ xor eax, eax
418+ xor edx, edx
419+ jmp CONTINUE_CTimer_Resume
420+ }
421+ }
422+
366423// ////////////////////////////////////////////////////////////////////////////////////////
367424//
368425// Photograph screen grab in windowed mode
@@ -758,6 +815,8 @@ void CMultiplayerSA::InitHooks_Rendering()
758815 EZHookInstall (Check_NoOfVisibleEntities);
759816 EZHookInstall (WinLoop);
760817 EZHookInstall (CTimer_Update);
818+ EZHookInstall (CTimer_Suspend);
819+ EZHookInstall (CTimer_Resume);
761820 EZHookInstall (psGrabScreen);
762821 EZHookInstallChecked (CClouds_RenderSkyPolys);
763822 EZHookInstallChecked (RwCameraSetNearClipPlane);
0 commit comments