https://bugs.winehq.org/show_bug.cgi?id=46030
Bug ID: 46030 Summary: Trackmania Unlimiter crashes at startup Product: Wine Version: 3.18 Hardware: x86-64 OS: Linux Status: UNCONFIRMED Severity: normal Priority: P2 Component: -unknown Assignee: wine-bugs@winehq.org Reporter: bernhard.gebetsberger@gmx.at Distribution: ---
Created attachment 62606 --> https://bugs.winehq.org/attachment.cgi?id=62606 Crashlog
Trackmania Unlimiter(1.3) crashes immediately after selecting an account at the account selection screen.
https://bugs.winehq.org/show_bug.cgi?id=46030
Bernhard Gebetsberger bernhard.gebetsberger@gmx.at changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |bernhard.gebetsberger@gmx.a | |t Distribution|--- |Other
https://bugs.winehq.org/show_bug.cgi?id=46030
Anastasius Focht focht@gmx.net changed:
What |Removed |Added ---------------------------------------------------------------------------- Keywords| |download, obfuscation Summary|Trackmania Unlimiter |Trackmania Unlimiter 1.3.x |crashes at startup |for TrackMania United | |Forever 2.11.26 crashes at | |account selection screen | |(heap manager differences, | |incorrect assumptions of | |mod on internal game engine | |data structures) Ever confirmed|0 |1 URL| |http://www.kemot0055.boo.pl | |/tm/unlimiter/TMInfinity_1. | |3.0.1.zip Status|UNCONFIRMED |NEW CC| |focht@gmx.net
--- Comment #1 from Anastasius Focht focht@gmx.net --- Hello folks,
it seems OP is talking about an extension/mod to the "TrackMania United Forever" game. There are multiple titles:
https://appdb.winehq.org/objectManager.php?sClass=application&iId=7064
* TMN TrackMania Nations * TMNF TrackMania Nations Forever * TMO TrackMania Original * TMS TrackMania Sunrise * TMU TrackMania United * TMUF TrackMania United Forever
The game name/version info is buried in the attached log file.
Game download is mentioned here:
http://www.tm-forum.com/viewtopic.php?t=30755
Download: http://files.trackmaniaforever.com/tmunitedforever_setup.exe
The actual mod "Trackmania Unlimiter 1.3.x" is mentioned here:
http://tm.maniazones.com/forum/index.php?topic=43895.0
Download: http://www.kemot0055.boo.pl/tm/unlimiter/TMInfinity_1.3.0.1.zip
It seems the mod uses 'TMInfinity.exe' launcher process which injects 'TMInfinity.dll' into the main game process using 'CreateRemoteThread'/'LoadLibrary' mechanism (also making assumptions about fixed load addresses for core libs) .
--- snip --- Base Module Party Path
00400000 tmforever.exe User C:\Program Files (x86)\TmUnitedForever\TmForever.exe 00E30000 d3dx9_30.dll System C:\windows\syswow64\d3dx9_30.dll 01710000 tminfinity.dll User C:\Program Files (x86)\TmUnitedForever\TMInfinity.dll 026F0000 wrap_oal.dll User C:\Program Files (x86)\TmUnitedForever\wrap_oal.dll 10000000 openal32.dll User C:\Program Files (x86)\TmUnitedForever\OpenAL32.dll 18000000 binkw32.dll User C:\Program Files (x86)\TmUnitedForever\binkw32.dll 799E0000 dsound.dll System C:\windows\system32\dsound.dll 79BE0000 msvcr100.dll System C:\windows\system32\msvcr100.dll 79CD0000 msvcp100.dll System C:\windows\system32\msvcp100.dll 7A820000 opengl32.dll System C:\windows\system32\opengl32.dll 7ABB0000 winepulse.drv System C:\windows\system32\winepulse.drv ... --- snip ---
--- snip --- $ pwd /home/focht/.wine/drive_c/Program Files (x86)/TmUnitedForever ... $ WINEDEBUG=+seh,+relay wine ./TMInfinity.exe >>log.txt 2>&1 ... 002a:Call KERNEL32.CreateProcessA(00402108 "TmForever.exe",00000000,00000000,00000000,00000000,00000000,00000000,00000000,0033fdd8,0033fe1c) ret=0040109a ... 002c:Call KERNEL32.__wine_kernel_init() ret=7bc59f9d 002a:Ret KERNEL32.CreateProcessA() retval=00000001 ret=0040109a 002a:Call user32.WaitForInputIdle(00000054,ffffffff) ret=004010af ... 002a:Call KERNEL32.OpenProcess(001fffff,00000000,0000002b) ret=004010c0 002a:Ret KERNEL32.OpenProcess() retval=0000005c ret=004010c0 ... 002a:Call KERNEL32.VirtualAllocEx(0000005c,00000000,0000000e,00003000,00000004) ret=004010d4 ... 002a:Ret KERNEL32.VirtualAllocEx() retval=01700000 ret=004010d4 ... 002a:Call KERNEL32.WriteProcessMemory(0000005c,01700000,0040218c,0000000e,00000000) ret=004010e7 ... 002a:Ret KERNEL32.WriteProcessMemory() retval=00000001 ret=004010e7 002a:Call KERNEL32.GetModuleHandleA(004021f8 "kernel32.dll") ret=004010f7 002a:Ret KERNEL32.GetModuleHandleA() retval=7b420000 ret=004010f7 002a:Call KERNEL32.GetProcAddress(7b420000,004021e8 "LoadLibraryA") ret=004010fe 002a:Ret KERNEL32.GetProcAddress() retval=7b426c5c ret=004010fe 002a:Call KERNEL32.CreateRemoteThread(0000005c,00000000,00000000,7b426c5c,01700000,00000000,00000000) ret=0040110f ... 002a:Ret KERNEL32.CreateRemoteThread() retval=00000064 ret=0040110f ... 0030:Starting thread proc 0x7b426c5c (arg=0x1700000) 0030:Call KERNEL32.LoadLibraryA(01700000 "TMInfinity.dll") ret=7bc81644 ... 0030:Call KERNEL32.VirtualProtect(00401000,00727000,00000020,024bfb1c) ret=01739024 0030:Ret KERNEL32.VirtualProtect() retval=00000001 ret=01739024 0030:Ret PE DLL (proc=0x173aafe,module=0x1710000 L"TMInfinity.dll",reason=PROCESS_ATTACH,res=(nil)) retval=1 0030:Ret KERNEL32.LoadLibraryA() retval=01710000 ret=7bc81644 ... --- snip ---
The mod hooks very deeply into the engine and makes assumptions about game engine internal data structures (in-memory layout). Tidbit: It patches internal functions directly at fixed offsets (not at entry) which is very dangerous as any game update will break the mod unless it uses dynamic disassembler/opcode pattern search/matching techniques.
--- snip --- tmforever.sub_69AF30: | ; internal function, no export 0069AF30 | push FFFFFFFF | 0069AF32 | push <tmforever.sub_AB1D20> | 0069AF37 | mov eax,dword ptr fs:[0] | 0069AF3D | push eax | 0069AF3E | sub esp,10 | 0069AF41 | push esi | 0069AF42 | mov eax,dword ptr ds:[CCC150] | 0069AF47 | xor eax,esp | 0069AF49 | push eax | 0069AF4A | lea eax,dword ptr ss:[esp+18] | 0069AF4E | mov dword ptr fs:[0],eax | 0069AF54 | lea esi,dword ptr ds:[ecx+6C] | 0069AF57 | push 0 | 0069AF59 | mov ecx,esi | 0069AF5B | mov dword ptr ss:[esp+24],0 | 0069AF63 | call <tmforever.sub_7EC070> | 0069AF68 | jmp tminfinity.17257F0 | ; patched by mod for redirection 0069AF6D | lea ecx,dword ptr ss:[esp+C] | ; return from mod trampoline 0069AF71 | call <tmforever.sub_428A00> | 0069AF76 | push 1 | 0069AF78 | push 2F | 0069AF7A | lea ecx,dword ptr ss:[esp+10] | 0069AF7E | mov byte ptr ss:[esp+28],1 | 0069AF83 | call <tmforever.sub_900190> | 0069AF88 | lea ecx,dword ptr ss:[esp+8] | 0069AF8C | push ecx | 0069AF8D | mov ecx,esi | 0069AF8F | call <tmforever.sub_420E20> | 0069AF94 | push 1 | 0069AF96 | push 2F | 0069AF98 | lea ecx,dword ptr ss:[esp+30] | 0069AF9C | call <tmforever.sub_900210> | 0069AFA1 | test eax,eax | 0069AFA3 | je tmforever.69AFF8 | 0069AFA5 | jmp tmforever.69AFB0 | 0069AFA7 | lea esp,dword ptr ss:[esp] | 0069AFAE | mov edi,edi | 0069AFB0 | mov edx,dword ptr ss:[esp+2C] | 0069AFB4 | mov eax,dword ptr ss:[esp+28] | 0069AFB8 | lea ecx,dword ptr ss:[esp+10] | 0069AFBC | push ecx | 0069AFBD | lea ecx,dword ptr ss:[esp+C] | 0069AFC1 | mov dword ptr ss:[esp+14],edx | 0069AFC5 | mov dword ptr ss:[esp+18],eax | 0069AFC9 | call <tmforever.sub_8FF6D0> | 0069AFCE | push 1 | 0069AFD0 | push 2F | 0069AFD2 | lea ecx,dword ptr ss:[esp+10] | 0069AFD6 | call <tmforever.sub_900190> | 0069AFDB | lea edx,dword ptr ss:[esp+8] | 0069AFDF | push edx | 0069AFE0 | mov ecx,esi | 0069AFE2 | call <tmforever.sub_420E20> | 0069AFE7 | push 1 | 0069AFE9 | push 2F | 0069AFEB | lea ecx,dword ptr ss:[esp+30] | 0069AFEF | call <tmforever.sub_900210> | 0069AFF4 | test eax,eax | 0069AFF6 | jne tmforever.69AFB0 | 0069AFF8 | mov ecx,dword ptr ss:[esp+C] | 0069AFFC | mov eax,dword ptr ds:[BBF7B8] | 0069B001 | cmp ecx,eax | 0069B003 | je tmforever.69B02B | 0069B005 | test byte ptr ds:[ecx-1],80 | 0069B009 | lea eax,dword ptr ds:[ecx-1] | 0069B00C | je tmforever.69B011 | 0069B00E | lea eax,dword ptr ds:[ecx-4] | 0069B011 | push eax | 0069B012 | call <tmforever.sub_402F7B> | 0069B017 | mov eax,dword ptr ds:[BBF7B8] | 0069B01C | add esp,4 | 0069B01F | mov dword ptr ss:[esp+8],0 | 0069B027 | mov dword ptr ss:[esp+C],eax | 0069B02B | mov ecx,dword ptr ss:[esp+2C] | 0069B02F | cmp ecx,eax | 0069B031 | je tmforever.69B048 | 0069B033 | test byte ptr ds:[ecx-1],80 | 0069B037 | lea eax,dword ptr ds:[ecx-1] | 0069B03A | je tmforever.69B03F | 0069B03C | lea eax,dword ptr ds:[ecx-4] | 0069B03F | push eax | 0069B040 | call <tmforever.sub_402F7B> | 0069B045 | add esp,4 | 0069B048 | mov ecx,dword ptr ss:[esp+18] | 0069B04C | mov dword ptr fs:[0],ecx | 0069B053 | pop ecx | 0069B054 | pop esi | 0069B055 | add esp,1C | 0069B058 | ret 8 | ... 007EC070 | mov eax,dword ptr ss:[esp+4] | 007EC074 | mov dword ptr ds:[ecx],eax | 007EC076 | ret 4 | --- snip ---
Mod trampoline:
--- snip --- 017257F0 | mov eax,esi | 017257F2 | sub eax,6C | ; offset data struct start 017257F5 | push eax | 017257F6 | call tminfinity.1725810 | ; modify/inject engine data 017257FB | add esp,4 | 017257FE | lea eax,dword ptr ss:[esp+28] | 01725802 | push eax | 01725803 | jmp dword ptr ds:[17501F4] | ; retpol: tmforever.0069AF6D --- snip ---
Data struct:
--- snip --- $-8 0A859E50 000000A0 ; block size (round) $-4 0A859E54 04455355 ; heap in-use magic 'USE' $ ==> 0A859E58 00B7DB1C $+4 0A859E5C 00000000 $+8 0A859E60 00000000 $+C 0A859E64 00000000 $+10 0A859E68 00000000 $+14 0A859E6C 04F54F38 $+18 0A859E70 40000733 $+1C 0A859E74 40002683 $+20 0A859E78 40000A10 $+24 0A859E7C 00000000 $+28 0A859E80 40000733 $+2C 0A859E84 40002683 $+30 0A859E88 40000A10 $+34 0A859E8C 0000000D $+38 0A859E90 0A8599F1 ; "Decors/Signs/" $+3C 0A859E94 00000005 $+40 0A859E98 040000A5 $+44 0A859E9C 00000050 $+48 0A859EA0 04F5000A $+4C 0A859EA4 FFFFFFFF $+50 0A859EA8 FFFFFFFF $+54 0A859EAC 00000000 $+58 0A859EB0 00000000 $+5C 0A859EB4 00000000 $+60 0A859EB8 00000000 $+64 0A859EBC 0000000D $+68 0A859EC0 0A859641 ; "Decors/Signs/" $+6C 0A859EC4 00000000 ; base offset from engine, prev: 0, hooker: 0x1 $+70 0A859EC8 00000000 ; prev: 0, hooker: 0x0A859A20 -> see follow dump $+74 0A859ECC 00000000 ; prev: 0, hooker: 0x1 $+78 0A859ED0 04F52CE0 $+7C 0A859ED4 11B5542F $+80 0A859ED8 11B5542E $+84 0A859EDC 00000001 $+88 0A859EE0 00000001 $+8C 0A859EE4 FFFFFFFF $+90 0A859EE8 00000000 $+94 0A859EEC 00000000 $+98 0A859EF0 00000000 $+9C 0A859EF4 04F52170 $+A0 0A859EF8 000004C1 $+A4 0A859EFC 45455246 ; heap-free magic --- snip ---
Data block 0x0A859A20 allocated and inserted by mod:
--- snip --- $-8 0A859A18 00000012 ; block len (rounded) $-4 0A859A1C 08455355 ; heap in-use magic 'USE' $ ==> 0A859A20 00000006 ; data length $+4 0A859A24 0A859A39 ; "Alpine" (data) $+8 0A859A28 04F53FE8 $+C 0A859A2C 04F53EE0 $+10 0A859A30 00000028 $+14 0A859A34 20455355 ; next block --- snip ---
Example data block allocated and inserted when mod is not active:
--- snip --- $-8 00000010 ; block len (rounded) $-4 04455355 ; heap in-use magic 'USE' $ ==> 00000001 ; flag: will be later accessed $+4 00000006 ; data length $+8 0A859F19 ; "Decors" $+C 04F54E50 $+10 00000010 $+14 08455355 ; next block --- snip ---
For whatever reason the mod allocates/uses an incorrect struct layout which has one member missing. Wine's heap-in-use magic data is mistakenly used as first member data by the engine, causing some erroneous calculations, leading to the crash.
--- snip --- ... 002c:Call ntdll.RtlAllocateHeap(013c0000,00000000,00000008) ret=00407a97 002c:Ret ntdll.RtlAllocateHeap() retval=04f174b8 ret=00407a97 002c:Call ntdll.RtlAllocateHeap(013c0000,00000000,00000008) ret=00407a97 002c:Ret ntdll.RtlAllocateHeap() retval=04f174d0 ret=00407a97 002c:Call msvcr100.memcpy(04f174d1,0a709c69,00000006) ret=0173a22b 002c:Ret msvcr100.memcpy() retval=04f174d1 ret=0173a22b 002c:Call ntdll.RtlFreeHeap(013c0000,00000000,0a709c68) ret=00405bd4 002c:Ret ntdll.RtlFreeHeap() retval=00000001 ret=00405bd4 002c:Call ntdll.RtlAllocateHeap(013c0000,00000000,0000000f) ret=00407a97 002c:Ret ntdll.RtlAllocateHeap() retval=0a709c68 ret=00407a97 002c:Call ntdll.RtlAllocateHeap(013c0000,00000000,00000014) ret=00407a97 002c:Ret ntdll.RtlAllocateHeap() retval=04f174e8 ret=00407a97 002c:Call ntdll.RtlAllocateHeap(013c0000,00000000,00000008) ret=00407a97 002c:Ret ntdll.RtlAllocateHeap() retval=04f17508 ret=00407a97 002c:trace:seh:raise_exception code=c0000005 flags=0 addr=0x4683e3 ip=004683e3 tid=002c 002c:trace:seh:raise_exception info[0]=00000000 002c:trace:seh:raise_exception info[1]=471c0f5c 002c:trace:seh:raise_exception eax=422a9aa8 ebx=04f174ec ecx=471c0f58 edx=00000003 esi=471c0f58 edi=0a709d8c 002c:trace:seh:raise_exception ebp=0033f600 esp=0033f5cc cs=0023 ds=002b es=002b fs=0063 gs=006b flags=00210212 002c:trace:seh:call_stack_handlers calling handler at 0x407bf0 code=c0000005 flags=0 002c:trace:seh:call_stack_handlers handler at 0x407bf0 returned 1 002c:trace:seh:call_stack_handlers calling handler at 0xa812eb code=c0000005 flags=0 --- snip ---
--- snip --- tmforever.sub_403151: | internal function 00403151 | push C | 00403153 | push tmforever.C68148 | 00403158 | call <tmforever.sub_407B88> | ; stack canary check 0040315D | and dword ptr ss:[ebp-1C],0 | 00403161 | mov esi,dword ptr ss:[ebp+C] | ; esi = 0x8 00403164 | mov eax,esi | 00403166 | imul eax,dword ptr ss:[ebp+10] | ; invld 08455355 (magic) * 8 0040316A | add dword ptr ss:[ebp+8],eax | ; 0A859A10 + 422A9AA8 = 4CB034B8 0040316D | and dword ptr ss:[ebp-4],0 | 00403171 | dec dword ptr ss:[ebp+10] | ; invld 08455354 (08455355-1) 00403174 | js tmforever.403181 | ; sign bit not trigged in Wine 00403176 | sub dword ptr ss:[ebp+8],esi | 00403179 | mov ecx,dword ptr ss:[ebp+8] | ; invld address 0040317C | call dword ptr ss:[ebp+14] | ; 004683E0 0040317F | jmp tmforever.403171 | 00403181 | mov dword ptr ss:[ebp-1C],1 | 00403188 | mov dword ptr ss:[ebp-4],FFFFFFFE| 0040318F | call <tmforever.sub_40319C> | 00403194 | call <tmforever.sub_407BCD> | 00403199 | ret 10 | --- snip ---
Why does this work on Windows? No idea. It most likely works just by chance if the last DWORD from the preceding memory block has a value range that triggers a specific code path (00403171: dec, sign bit -> 8xxxxxxx -> jmp). It's still incorrect but it doesn't lead to a crash.
I made a very ugly hack as further proof: cutting the most significant bit from Wine's heap manager 'ARENA_INUSE' struct 'unused_bytes' member (8 bit) and "repurposed" it to trigger the other code path via sign jump (bit set to 1). The mod/game works after that. Profile selection -> offline game -> create new game.
The game/mod wouldn't survive a millisecond on Windows when run with heap validation active (pageheap/gflags). As a sad fact, gazillion of other apps/games wouldn't survive either ;-)
This is what I came up with after some hours of debugging. IMHO INVALID/WONTFIX. I'm open for alternative analysis/conclusions though, bring it on ;-)
$ sha1sum tmunitedforever_setup.exe c5e45181d9bfd96254616ec3c8c896dbde7d3b23 tmunitedforever_setup.exe
$ du -sh tmunitedforever_setup.exe 1.2G tmunitedforever_setup.exe
$ sha1sum TMInfinity_1.3.0.1.zip 7498369dc5227d45791f7325c5fff8a00b26c5da TMInfinity_1.3.0.1.zip
$ du -sh TMInfinity_1.3.0.1.zip 19M TMInfinity_1.3.0.1.zip
$ wine --version wine-3.18-118-g377243b411
Regards
https://bugs.winehq.org/show_bug.cgi?id=46030
Qwerty Chouskie asdfghrbljzmkd@outlook.com changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |asdfghrbljzmkd@outlook.com
--- Comment #2 from Qwerty Chouskie asdfghrbljzmkd@outlook.com --- This works fine in modern versions of Wine, as seen in https://bugs.winehq.org/show_bug.cgi?id=53814
This can be closed as fixed.
https://bugs.winehq.org/show_bug.cgi?id=46030
Zeb Figura z.figura12@gmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |z.figura12@gmail.com Status|NEW |RESOLVED Resolution|--- |FIXED
--- Comment #3 from Zeb Figura z.figura12@gmail.com --- (In reply to Qwerty Chouskie from comment #2)
This works fine in modern versions of Wine, as seen in https://bugs.winehq.org/show_bug.cgi?id=53814
This can be closed as fixed.
Resolving fixed; please reopen if this can be reproduced again.
https://bugs.winehq.org/show_bug.cgi?id=46030
Anastasius Focht focht@gmx.net changed:
What |Removed |Added ---------------------------------------------------------------------------- Component|-unknown |ntdll URL|http://www.kemot0055.boo.pl |https://archive.org/details |/tm/unlimiter/TMInfinity_1. |/tminfinity-1.3.0.1 |3.0.1.zip | Fixed by SHA1| |059094c1c18ddc33b04eac53a72 | |fd0eb7510be94
--- Comment #4 from Anastasius Focht focht@gmx.net --- Hello folks,
revisiting. Since I spent a considerable amount of time on analysis I was curious what fixed it.
Turns out https://source.winehq.org/git/wine.git/commitdiff/059094c1c18ddc33b04eac53a7... ("ntdll: Define heap block's BLOCK_FLAG_LFH as 0x80."), part of Wine 8.19 release helped here.
Before that, Wine's heap management was overhauled in a series of commits: https://source.winehq.org/git/wine.git/history/059094c1c18ddc33b04eac53a72fd...
But as on Windows it just works by chance.
--- snip --- $-8 0BB6F330 000C0016 .... $-4 0BB6F334 80750001 ..u. ; block flags (0x80 = BLOCK_FLAG_LFH) $ ==> 0BB6F338 00B7DB1C .Û·. $+4 0BB6F33C 00000000 .... $+8 0BB6F340 00000000 .... $+C 0BB6F344 00000000 .... $+10 0BB6F348 00000000 .... $+14 0BB6F34C 06988648 H... $+18 0BB6F350 40000733 3..@ $+1C 0BB6F354 40002683 .&.@ $+20 0BB6F358 40000A10 ...@ $+24 0BB6F35C 00000000 .... $+28 0BB6F360 40000733 3..@ $+2C 0BB6F364 40002683 .&.@ $+30 0BB6F368 40000A10 ...@ $+34 0BB6F36C 0000000D .... $+38 0BB6F370 0BC02F11 ./À. "Decors/Signs/"
00403164 | mov eax,esi ; 00000008 00403166 | imul eax,dword ptr ss:[ebp+10] ; 03A80020 0040316A | add dword ptr ss:[ebp+8],eax ; 0F682FB0 0040316D | and dword ptr ss:[ebp-4],0 00403171 | dec dword ptr ss:[ebp+10] ; 80750004-1 = 80750003 00403174 | js tmforever.403181 ; sign bit set --- snip ---
Interesting tidbit: The mod got an update two years later: 1.3.0.1 -> 1.3.0.2 which also works with the old Wine versions this bug was reported against. Apparently the mod developers fixed the incorrect struct layout in the mod 1.3.0.2 update.
From 1.3.0.2 README.TXT:
--- quote --- C) CHANGELOG:
1.3.0.2: - Improved custom block memory management - blocks are now loaded when game needs it. - Fixed mood change handling while loading a tracks - Updated version check which points to new server
1.3.0.1: - Fixed crash while game loading - Fixed puzzle block placing bug - Fixed hidden hills folder when building in snow environment - Added new shortcuts: * Shift + Tab : Resets manipulation values for moving/rotation/scale * Shift + A : Switches between move/rotation/scale manipulation mode * Shift + Enter : Resets strength settings to default * Shift + E : Toggles updating nearby blocks * Shift + Num+ : Increases strength 2x * Shift + Num- : Decreases strength 2x --- quote ---
Wine 3.19:
* mod 1.3.0.1, crashes at account selection (this bug) * mod 1.3.0.2, account selection works, offline game works (played one round)
Wine 8.19, Wine 9.5:
* mod 1.3.0.1 and 1.3.0.2 works with account selection and offline gameplay
Stable links from Internet Archive for reference:
https://web.archive.org/web/20200901000000*/http://files.trackmaniaforever.c...
https://web.archive.org/web/20220910194411/https://unlimiter.net/download (TMUnlimiter "TMInfinity" 1.3.0.2)
https://archive.org/details/tminfinity-1.3.0.1 (I've uploaded from my own archive since the original site had no snapshots saved for it).
Regards
https://bugs.winehq.org/show_bug.cgi?id=46030
Alexandre Julliard julliard@winehq.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Status|RESOLVED |CLOSED
--- Comment #5 from Alexandre Julliard julliard@winehq.org --- Closing bugs fixed in 9.6.