Number 3:
A structure pointed to at 0x926290 (0xDD83B8) contains a dword value of a timer that traditionally stores the timer in seconds that is incremented by a delta of two timegettime function calls (which return the system time in milliseconds). This is accomplished by storing timegettime at the beginning in this structure then storing timegettime in another section. Bypassing all this is easily possible.
.text:0072D0E6 call ds:timeGetTime
.text:0072D0EC mov ecx, off_926290
.text:0072D0F2 mov [ecx+60h], eax
.text:0072D0F5 mov edx, off_926290
.text:0072D0FB cmp dword ptr [edx+64h], 0
.text:0072D0FF jnz short loc_72D112
.text:0072D101 mov eax, off_926290
.text:0072D106 mov ecx, off_926290
.text:0072D10C mov edx, [ecx+60h]
.text:0072D10F mov [eax+64h], edx
.text:0072D112
.text:0072D112 loc_72D112: ; CODE XREF: sub_72D0C0+3Fj
.text:0072D112 mov eax, off_926290
.text:0072D117 mov ecx, off_926290
.text:0072D11D mov edx, [eax+60h]
.text:0072D120 sub edx, [ecx+64h]
.text:0072D123 mov eax, off_926290
.text:0072D128 mov ecx, [eax+10h]
.text:0072D12B add ecx, edx
.text:0072D12D mov edx, off_926290
.text:0072D133 mov [edx+10h], ecx
.text:0072D136 mov eax, off_926290
.text:0072D13B cmp dword ptr [eax+10h], 5999999
.text:0072D142 jbe short loc_72D151
.text:0072D144 mov ecx, off_926290
.text:0072D14A mov dword ptr [ecx+10h], 5999999
.text:0072D151
.text:0072D151 loc_72D151: ; CODE XREF: sub_72D0C0+82j
.text:0072D151 mov edx, off_926290
.text:0072D157 mov eax, off_926290
.text:0072D15C mov ecx, [eax+60h]
.text:0072D15F mov [edx+64h], ecx
That's the code it traditionally uses. Reducing it to use only frames as its counter it becomes:
.text:0072D0E6 mov eax, off_926290
.text:0072D128 mov ecx, [eax+10h]
.text:0072D12B inc ecx
.text:0072D12D mov edx, off_926290
.text:0072D133 mov [edx+10h], ecx
.text:0072D136 mov eax, off_926290
.text:0072D13B cmp dword ptr [eax+10h], 99999
.text:0072D142 jbe short loc_72D162
.text:0072D144 mov ecx, off_926290
.text:0072D14A mov dword ptr [ecx+10h], 99999
Tada! Now it will count per frame with an upper limit of 99999 frames (max time of 27:46.65. If you take this long you're doing something HORRIBLY wrong). That doesn't solve everything, however as now we have to change the display. It's located at 0x72D485 which is a long function that I'm about to overwrite. This function is used in four places so it's probably holding times for the other mini-games as well. Specifically the highway chase and submarine games.
.text:0072D485 ; int __stdcall formatTimeString(int TimerValue, int DestinationMemory)
.text:0072D485 formatTimeString proc near ; CODE XREF: sub_72994E+4Ep
.text:0072D485 ; sub_72D207+2Ep ...
.text:0072D485
.text:0072D485 var_10 = dword ptr -10h
.text:0072D485 var_C = dword ptr -0Ch
.text:0072D485 var_8 = dword ptr -8
.text:0072D485 var_4 = dword ptr -4
.text:0072D485 TimerValue = dword ptr 8
.text:0072D485 DestinationMemory= dword ptr 0Ch
.text:0072D485
.text:0072D485 push ebp
.text:0072D486 mov ebp, esp
.text:0072D488 sub esp, 10h
.text:0072D48B mov [ebp+var_10], ecx ;This is a pointless? line
.text:0072D48E mov eax, [ebp+TimerValue]
.text:0072D493 xor edx, edx
.text:0072D495 mov ecx, 10
.text:0072D49A div ecx
.text:0072D4B0 mov ecx, [ebp+DestinationMemory]
.text:0072D4B3 mov [ecx+10h], edx ;TimerValue % 10
.text:0072D49C mov [ebp+Result], eax ;now a tenth of what it was before
.text:0072D49F mov eax, [ebp+Result] ;This seems a little ridiculous too, but it's actually not
.text:0072D493 xor edx, edx
.text:0072D495 mov ecx, 10
.text:0072D49A div ecx
.text:0072D4B0 mov ecx, [ebp+DestinationMemory]
.text:0072D4B3 mov [ecx+0Ch], edx ;(TimerValue / 10) % 10
.text:0072D49C mov [ebp+Result], eax ;now a tenth of what it was before
.text:0072D49F mov eax, [ebp+Result]
.text:0072D493 xor edx, edx
.text:0072D495 mov ecx, 10
.text:0072D49A div ecx
.text:0072D4B0 mov ecx, [ebp+DestinationMemory]
.text:0072D4B3 mov [ecx+8h], edx ;(TimerValue / 100) % 10
.text:0072D49C mov [ebp+Result], eax ;now a tenth of what it was before
.text:0072D49F mov eax, [ebp+Result]
.text:0072D493 xor edx, edx
.text:0072D495 mov ecx, 10
.text:0072D49A div ecx
.text:0072D4B0 mov ecx, [ebp+DestinationMemory]
.text:0072D4B3 mov [ecx+4h], edx ;(TimerValue / 1000) % 10
.text:0072D49C mov [ebp+Result], eax ;now a tenth of what it was before
.text:0072D49F mov eax, [ebp+Result]
.text:0072D493 xor edx, edx
.text:0072D495 mov ecx, 10
.text:0072D49A div ecx
.text:0072D4B0 mov ecx, [ebp+DestinationMemory]
.text:0072D4B3 mov [ecx], edx ;(TimerValue / 10000) % 10
.text:0072D577 mov esp, ebp
.text:0072D579 pop ebp
.text:0072D57A retn 8
.text:0072D57A sub_72D485 endp
Because this is accessed in other places, it'd be safest to write this to some unused memory block. Now we have the digits stored in order in the DestinationMemory block in big-endian decimal. So we need to force this into the display string:
.text:0072D3EA ; int __stdcall sub_72D3EA(int DestinationString, int FormattedTimeValue, char FF7TextOffset)
.text:0072D3EA sub_72D3EA proc near ; CODE XREF: sub_72D207+7Bp
.text:0072D3EA ; sub_72D333+60p ...
.text:0072D3EA
.text:0072D3EA var_4 = dword ptr -4
.text:0072D3EA DestinationString= dword ptr 8
.text:0072D3EA FormattedTimeValue= dword ptr 0Ch
.text:0072D3EA FF7TextOffset = byte ptr 10h
.text:0072D3EA
.text:0072D3EA push ebp
.text:0072D3EB mov ebp, esp
.text:0072D3ED push ecx
.text:0072D3EE mov [ebp+var_4], ecx ;I have no idea what purpose this serves
.text:0072D3F1 movsx eax, [ebp+FF7TextOffset]
.text:0072D3F5 mov ecx, [ebp+FormattedTimeValue]
.text:0072D3F8 mov edx, [ecx]
.text:0072D3FA add edx, eax
.text:0072D3FC mov eax, [ebp+DestinationString]
.text:0072D3FF mov [eax], dl
.text:0072D401 movsx ecx, [ebp+FF7TextOffset]
.text:0072D405 mov edx, [ebp+FormattedTimeValue]
.text:0072D408 mov eax, [edx+4]
.text:0072D40B add eax, ecx
.text:0072D40D mov ecx, [ebp+DestinationString]
.text:0072D410 mov [ecx+1], al
.text:0072D413 movsx edx, [ebp+FF7TextOffset]
.text:0072D417 mov eax, [ebp+FormattedTimeValue]
.text:0072D41A mov ecx, [eax+8]
.text:0072D41D add ecx, edx
.text:0072D41F mov edx, [ebp+DestinationString]
.text:0072D422 mov [edx+2], cl
.text:0072D425 movsx eax, [ebp+FF7TextOffset]
.text:0072D429 mov ecx, [ebp+FormattedTimeValue]
.text:0072D42C mov edx, [ecx+0Ch]
.text:0072D42F add edx, eax
.text:0072D431 mov eax, [ebp+DestinationString]
.text:0072D434 mov [eax+3], dl
.text:0072D437 movsx ecx, [ebp+FF7TextOffset]
.text:0072D43B mov edx, [ebp+FormattedTimeValue]
.text:0072D43E mov eax, [edx+10h]
.text:0072D441 add eax, ecx
.text:0072D443 mov ecx, [ebp+DestinationString]
.text:0072D446 mov [ecx+4], al
.text:0072D449 movsx edx, [ebp+FF7TextOffset]
.text:0072D44D mov eax, [ebp+FormattedTimeValue]
.text:0072D450 mov ecx, [eax+14h]
.text:0072D453 add ecx, edx
.text:0072D455 mov edx, [ebp+DestinationString]
.text:0072D458 mov [edx+5], cl
.------------------------------------------------------------ chunk erased
.text:0072D47F mov esp, ebp
.text:0072D481 pop ebp
.text:0072D482 retn 0Ch
.text:0072D482 sub_72D3EA endp
FF7TextOffset in this case should be 10h because that's where the '0' character is in its text map.
This function, again, is referenced in other locations so it might be best to write this somewhere else too.
So the formatting string needs to be changed to:
.data:009568A0 TimerText db '00000', 00 ; DATA XREF: sub_72D207+73o
Just a null-terminated string of 5 zeroes. This forces the display of the time to have leading zeroes, but that's good for the leaderboard for all the text to be aligned.