I am an ardent fan of the game "Truckers-2", but unfortunately, I could not run them under Linux. I tried to make the game work for several years (this does not mean that I was sitting all day long over it) with varying success. I tried different versions of wine, picked the settings, but the game did not yield. Once I had some free time, and I decided to figure out why the Truckers didn’t run under Wine, although they worked fine under Cedega. Next, I will tell you how I managed it.
Attempt number 0. We tighten the config
At first I took the path of least resistance: try setting up the game itself. I partially succeeded. To start, you need to edit the TRUCK.INI file, namely the
DRV section, and set the correct settings for the video adapter in it. I once managed to find the TRUCK.INI file with the correct settings and the game started, but when I sat down to write this article, I could not reproduce the launch of the game with the "correct" TRUCK.INI on unpatched wine. We consider this method unreliable.
Attempt number 1. Choose a version of wine
I tried several versions that I had on hand (including the latest from git). None of them started the game on the “clean settings”. Therefore, it was decided to take the source code of the latest stable version. At the time of this writing, it was a version of
wine-1.2.3 . Then I conducted all the experiments on this version of wine.
')
Game log analysis
When I start the game on a "pure" wine, it shows me the starting splash-screen, after which it opens the window several times and turns off without any message. After such a launch, I looked into the folder with the game and saw the
warn.log file with the following contents:
warning : fail in SetCoop... One or more of the parameters passed to the method are incorrect. (unknown) 0
warning : DirectDraw4/7 not found 0
warning :
(09-03-12 11:09)
,
(fail in SetCoop... One or more of the parameters passed to the method are incorrect. (unknown)(null))
0
This log gave me the first hint: the DirectDraw subsystem is incorrectly initialized. I hadn’t dealt with directdraw before, so this record helped me a little, however it was the first clue. Google on the query "directdraw SetCoop" gave me information about the function SetCooperativeLevel. I did not go into the description of this function, instead I decided to go a different way (but I’ll return to this function).
We go deep into the theory. Wine logging features
I went to the office. wine site and started looking for information on how to make wine give me a message about all the errors that occur when you start the application. All information is located at
www.winehq.org/developer-cheatsheet . I would like to say that the logging capabilities of wine are very rich, and allow you to receive various information about the program being started (you can even trace the chain of calls to all WinAPI functions with all input and output parameters).
So, run the wine and remove the logs:
At first I launched without parameters:
wine king > wine.dbg 2>&1
. Received at the exit
fixme:win:EnumDisplayDevicesW ((null),0,0x32f110,0x00000000), stub!
fixme:win:EnumDisplayDevicesW ((null),0,0x32f424,0x00000000), stub!
There is nothing interesting here.
A small digression: description of the logging subsystem in wine
- In wine, there are several classes (although I’m used to the word level) logging is FIXME, ERR, WARN, TRACE, MESSAGE. They are responsible for the importance of the message and its type.
- Also in wine there are debugging channels, they are responsible for one or another subsystem within wine. For example, reg is responsible for the registry subsystem, etc.
- Logging is controlled by the environment variable WINEDEBUG. For example, I ran with the following arguments: WINEDEBUG = warn + all, trace + all
So, back to our task. After exploring the possibilities of logging, I started to run the game with different logging parameters, stopped at the above.
After launch, I received a 40 megabyte log. It is clear that manually find something in it is unrealistic. At first, I filtered it by
ERR class, but I didn’t find anything interesting there. Then I decided to return to the message from warn.log, and filtered it by
SetCoop :
cat wine.dbg | grep SetCoop
0009:trace:ddraw:IDirectDrawImpl_SetCooperativeLevel (0x15ae78)->(0x40066,0000105b)
0009:trace:ddraw:IDirectDrawImpl_SetCooperativeLevel (0x15ae78) DDSCL_NORMAL is not compative with DDSCL_FULLSCREEN or DDSCL_EXCLUSIVE
0009:trace:ddraw:IDirectDrawImpl_SetCooperativeLevel (0x15ae78)->(0x40066,00000008)
0009:trace:ddraw:IDirectDrawImpl_SetCooperativeLevel SetCooperativeLevel retuning DD_OK
0009:trace:ddraw:IDirectDrawImpl_SetCooperativeLevel (0x15ae78)->(0x40066,00000008)
0009:trace:ddraw:IDirectDrawImpl_SetCooperativeLevel SetCooperativeLevel retuning DD_OK
0009:trace:ddraw:IDirectDrawImpl_SetCooperativeLevel (0x15ae78)->((nil),00000008)
0009:trace:ddraw:IDirectDrawImpl_SetCooperativeLevel SetCooperativeLevel retuning DD_OK
0009:trace:ddraw:IDirectDrawImpl_SetCooperativeLevel (0x160420)->(0x40066,0000105b)
0009:trace:ddraw:IDirectDrawImpl_SetCooperativeLevel (0x160420) DDSCL_NORMAL is not compative with DDSCL_FULLSCREEN or DDSCL_EXCLUSIVE
0009:trace:ddraw:IDirectDrawImpl_SetCooperativeLevel (0x160420)->(0x40066,00000008)
0009:trace:ddraw:IDirectDrawImpl_SetCooperativeLevel SetCooperativeLevel retuning DD_OK
0009:trace:ddraw:IDirectDrawImpl_SetCooperativeLevel (0x160420)->((nil),00000008)
0009:trace:ddraw:IDirectDrawImpl_SetCooperativeLevel SetCooperativeLevel retuning DD_OK
Here are more interesting data. We see that incompatible parameters were passed to the SetCooperativeLevel function, this is already something. If we look at the previous lines in the log, we will see all the parameters that are passed to the function:
0009:trace:ddraw:DDRAW_dump_cooperativelevel - DDSCL_FULLSCREEN DDSCL_ALLOWREBOOT DDSCL_NORMAL DDSCL_ALLOWMODEX DDSCL_EXCLUSIVE
0009:trace:ddraw:DDRAW_dump_cooperativelevel - DDSCL_FULLSCREEN DDSCL_ALLOWREBOOT DDSCL_NORMAL DDSCL_ALLOWMODEX DDSCL_EXCLUSIVE
Description of all parameters can be found in MSDN
Source Code Analysis
Next, I moved on to analyzing the source code and documentation for the SetCooperativeLevel function.
Finding the desired function in wine is easy; it is in% SRC_ROOT% / dlls / ddraw / ddraw.c. The function is called
IDirectDrawImpl_SetCooperativeLevel .
Open MSDN and find the description on the function
SetCooperativeLevel and see:
DDSCL_NORMAL
The application functions as a typical Windows application. This flag cannot be used with the DDSCL_ALLOWMODEX, DDSCL_EXCLUSIVE, or DDSCL_FULLSCREEN flags
That is, this means that these flags can not be together.
It should be noted that the implementation of wine fully complies with the documentation and gives the error code DDERR_INVALIDPARAMS in this case. I decided for the test to try to remove the check for a specific combination of flags:
if(cooplevel & DDSCL_NORMAL) {
Compile and install wine, run Truckers and ...
Loaded familiar game menu, victory! We try to create a game, the game is created and I can ride on my favorite truck Zil!
Improvements in the study
Having done so much on the virtual map, I decided that the game works quite stably and that means it's time to issue our results in the form of a patch and create an application in the wine bug tracker.
In order to fully understand this function, I decided to write a test program for win32 that would give an error code depending on the combination of the DDSCL_ * flags and I saw that the SetCooperativeLevel function returns a specific combination of flags (which is used by the “Long-haul truckers”), then there is success.
It is also worth noting that this function returns DD_OK much more often than described in MSDN . It is possible that the game developers did not take into account the difference in the description and in the implementation of this function and used flags that are prohibited in the documentation.
I created an
application , attached a log and a patch to it.
In fact, the patch is very simple, I just commented on checking for prohibited flags (as in the example above), but this function is used in many applications, so it is necessary to conduct a more detailed analysis. To do this, I attached to the application program for win32, which goes through all possible combinations of flags and gives the appropriate error codes.
PS Checked on the version of wine-1.4 everything works fine.
In this version of SetCooperativeLevel has been significantly reworked, and now the behavior of this function is close to the actual implementation in win32. I closed the corresponding application in wine. Thank you all for the comments.