Thanks Ken!
I don't think a missing library is the problem: the game stores all its texture resources in a file called bitmap16.flx and the code paths under Wine for reading those bitmaps and making textures out of them seems to work just fine given that many of them are visible in the final running game.
The offset of the cursor resource within bitmap16.flx is 0x1020eb4 and the '.flx item' (as files within this proprietary archive are called) itself is about 400,000 bytes (that's for 121 separate cursors all read at once).
Looking through +file,+tid,+relay recommended by Ken, I found:
0009:trace:file:CreateFileW L"C:\GOG Games\Ultima IX - Ascension\static\bitmap16.flx" GENERIC_READ creation 4 attributes 0x80 0009:trace:file:RtlDosPathNameToNtPathName_U (L"C:\GOG Games\Ultima IX - Ascension\static\bitmap16.flx",0x32ef74,(nil),(nil)) 0009:trace:file:RtlGetFullPathName_U (L"C:\GOG Games\Ultima IX - Ascension\static\bitmap16.flx" 520 0x32ecc4 (nil)) 0009:trace:file:wine_nt_to_unix_file_name L"\??\C:\GOG Games\Ultima IX - Ascension\static\bitmap16.flx" -> "/home/cthielen/.wine/dosdevices/c:/GOG Games/Ultima IX - Ascension/static/bitmap16.flx" 0009:trace:file:CreateFileW returning 0xac
which I believe implies bitmap16.flx is being given the file handle 0xac. Searching for '0xac' I later find:
0009:Call KERNEL32.SetFilePointer(000000ac,01020eb4,00000000,00000000) ret=005de4c9 0009:Ret KERNEL32.SetFilePointer() retval=01020eb4 ret=005de4c9 0009:Call KERNEL32.ReadFile(000000ac,02539110,00061b8c,0032edfc,00000000) ret=005de512 0009:trace:file:ReadFile 0xac 0x2539110 400268 0x32edfc (nil) 0009:Ret KERNEL32.ReadFile() retval=00000001 ret=005de512 0009:Call KERNEL32.GetLastError() ret=005de518 0009:Ret KERNEL32.GetLastError() retval=00000002 ret=005de518 0009:Call ntdll.RtlAllocateHeap(01450000,00000000,00001700) ret=00714f22 0009:Ret ntdll.RtlAllocateHeap() retval=0259aca8 ret=00714f22 0009:Call ntdll.RtlAllocateHeap(01450000,00000000,00000200) ret=00714f22 0009:Ret ntdll.RtlAllocateHeap() retval=0259c3b0 ret=00714f22 0009:Call user32.LoadCursorA(00000000,00007f00) ret=005d5743 0009:Ret user32.LoadCursorA() retval=00010038 ret=005d5743 0009:Call user32.SetCursor(00010038) ret=005dbf5f 0009:Call winex11.drv.SetCursor(00010038) ret=7eb0f777 0009:Ret winex11.drv.SetCursor() retval=00000000 ret=7eb0f777 0009:Ret user32.SetCursor() retval=00000000 ret=005dbf5f 0009:Call user32.CreateWindowExA(00000000,0032f5c0 "OFCBlackWindowClass",00787774 "Ultima IX",80030000,80000000,80000000,80000000,80000000,00000000,00000000,00400000,00000000) ret=005d579c 0009:Call KERNEL32.LZOpenFileW(0032ee74 L"C:\GOG Games\Ultima IX - Ascension\u9.exe",0032ed64,00000000) ret=b7788b42 0009:trace:file:LZOpenFileA (C:\GOG Games\Ultima IX - Ascension\u9.exe,0x32ed64,0)
which appears to be reading the exact offset I know the cursor to be in within bitmap16.flx (file handle 0xac presumably). It also seems to request about 400,000 bytes at that offset, which also lines up with what I know about the missing cursor resource.
What it then seems to do is call LoadCursor and SetCursor which may be curious because I thought we had ruled out user32 cursors as having been used. Based on cross-compiling d3d7 and running the game on Windows XP with Wine's d3d7 however, we know the problem is not in Wine's D3D implementation, so maybe it's logical to suspect cursor after all.
However that doesn't make much sense because LoadCursor 7f00 is the standard system cursor.
But what about the GetLastError() call returning 2 (which I believe is ERROR_FILE_NOT_FOUND)? Does that seem normal right after a successful (non-zero) file read?
Here's a wild theory knowing little about this stuff: ReadFile is maybe supposed to clear out that thread's GetLastError value and u9.exe is calling GetLastError() to see if errors exist, instead of checking only after a zero return value from ReadFile like it should (it is considered a poorly written game). If Wine doesn't clear out GetLastError correctly, it could cause an alternate code path to run under Wine in which the cursors are believed not to be loaded properly.
Notably, that same file handle seems to be used correctly later on:
(0xac is bitmap16.flx)
0009:Call KERNEL32.SetFilePointer(000000ac,01e3dcc0,00000000,00000000) ret=005de4c9 0009:Ret KERNEL32.SetFilePointer() retval=01e3dcc0 ret=005de4c9 0009:Call KERNEL32.ReadFile(000000ac,0032f3f4,00000008,0032f3c4,00000000) ret=005de512 0009:trace:file:ReadFile 0xac 0x32f3f4 8 0x32f3c4 (nil) 0009:Ret KERNEL32.ReadFile() retval=00000001 ret=005de512 0009:Call KERNEL32.GetLastError() ret=005de518 0009:Ret KERNEL32.GetLastError() retval=00000000 ret=005de518
Or is that am incorrect assumption?
Any ideas?
On 03/24/2014 06:27 PM, Ken Thomases wrote:
On Mar 24, 2014, at 7:48 PM, Christopher Thielen wrote:
Using WINEDEBUG=+all, the resource in question (static/bitmap16.flx) is loaded via kernel32's CreateFile, ReadFile.
Is there a way to trace only kernel32 calls dealing with a specific file handle? I'm curious if there are any failed reads. Does that seem like a reasonable next step?
Not really. You'll want to use a +file log and follow the handle as it's passed to subsequent calls. It might also be necessary to use +tid,+relay to see all uses if Wine isn't logging the handle in all relevant functions.
Certainly, look for failed reads, but I wouldn't expect that unless you think the file is damaged vs. what's installed on Windows. More likely, the game requires external help interpreting the data that is has read, like a compression or image library. If that's not present or is incomplete in Wine, then that would explain the problem. The +tid,+relay log may help to identify what the program does next after it has read the data in.
-Ken