Removed winver and service_table from the PDB and made them static
[wine] / misc / version.c
1 /*
2  * Windows and DOS version functions
3  *
4  * Copyright 1997 Alexandre Julliard
5  * Copyright 1997 Marcus Meissner
6  * Copyright 1998 Patrik Stridvall
7  * Copyright 1998 Andreas Mohr
8  */
9
10 #include <string.h>
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include "windef.h"
14 #include "winbase.h"
15 #include "wingdi.h"
16 #include "winuser.h"
17 #include "wine/winbase16.h"
18 #include "process.h"
19 #include "options.h"
20 #include "debugtools.h"
21 #include "neexe.h"
22 #include "winversion.h"
23 #include "winerror.h"
24
25 DEFAULT_DEBUG_CHANNEL(ver)
26
27 typedef struct
28 {
29     LONG             getVersion16; 
30     LONG             getVersion32;
31     OSVERSIONINFOA getVersionEx;
32 } VERSION_DATA;
33
34
35 /* FIXME: compare values below with original and fix */
36 static VERSION_DATA VersionData[NB_WINDOWS_VERSIONS] =
37 {
38     /* WIN31 */
39     {
40         MAKELONG( 0x0a03, 0x0616 ), /* DOS 6.22 */
41         MAKELONG( 0x0a03, 0x8000 ),
42         {
43             sizeof(OSVERSIONINFOA), 3, 10, 0,
44             VER_PLATFORM_WIN32s, "Win32s 1.3" 
45         }
46     },
47     /* WIN95 */
48     {
49         0x07005F03,
50         0xC0000004,
51         {
52             sizeof(OSVERSIONINFOA), 4, 0, 0x40003B6,
53             VER_PLATFORM_WIN32_WINDOWS, "Win95"
54         }
55     },
56     /* WIN98 */
57     {
58         0x07005F03,     /* FIXME: need DOS value from real Win98 */
59         0xC0000A04,
60         {
61             sizeof(OSVERSIONINFOA), 4, 10, 0x40A07CE,
62             VER_PLATFORM_WIN32_WINDOWS, "Win98"
63         }
64     },
65     /* NT351 */
66     {
67         0x05000A03,
68         0x04213303,
69         {
70             sizeof(OSVERSIONINFOA), 3, 51, 0x421,
71             VER_PLATFORM_WIN32_NT, "Service Pack 2"
72         }
73     },
74     /* NT40 */
75     {
76         0x05000A03,
77         0x05650004,
78         {
79             sizeof(OSVERSIONINFOA), 4, 0, 0x565,
80             VER_PLATFORM_WIN32_NT, "Service Pack 3"
81         }
82     }
83 };
84
85 static const char *WinVersionNames[NB_WINDOWS_VERSIONS] =
86 {
87     "win31",
88     "win95",
89     "win98",
90     "nt351",
91     "nt40"
92 };
93
94 /* if one of the following dlls is importing ntdll the windows
95 version autodetection switches wine to unicode (nt 3.51 or 4.0) */
96 static char * special_dlls[] =
97 {
98         "COMDLG32",
99         "COMCTL32",
100         "SHELL32",
101         "OLE32",
102         "RPCRT4",
103         NULL
104 };
105
106 /* the current version has not been autodetected but forced via cmdline */
107 static BOOL versionForced = FALSE;
108 static WINDOWS_VERSION defaultWinVersion = WIN31;
109
110 /**********************************************************************
111  *         VERSION_ParseWinVersion
112  */
113 void VERSION_ParseWinVersion( const char *arg )
114 {
115     int i;
116     for (i = 0; i < NB_WINDOWS_VERSIONS; i++)
117     {
118         if (!strcmp( WinVersionNames[i], arg ))
119         {
120             defaultWinVersion = (WINDOWS_VERSION)i;
121             versionForced = TRUE;
122             return;
123         }
124     }
125     MESSAGE("Invalid winver value '%s' specified.\n", arg );
126     MESSAGE("Valid versions are:" );
127     for (i = 0; i < NB_WINDOWS_VERSIONS; i++)
128         MESSAGE(" '%s'%c", WinVersionNames[i],
129             (i == NB_WINDOWS_VERSIONS - 1) ? '\n' : ',' );
130     ExitProcess(1);
131 }
132
133
134 /**********************************************************************
135  *         VERSION_ParseDosVersion
136  */
137 void VERSION_ParseDosVersion( const char *arg )
138 {
139     int hi, lo;
140     if (sscanf( arg, "%d.%d", &hi, &lo ) == 2)
141     {
142         VersionData[WIN31].getVersion16 =
143             MAKELONG(LOWORD(VersionData[WIN31].getVersion16),
144                      (hi<<8) + lo);
145     }
146     else
147     {
148         MESSAGE("--dosver: Wrong version format. Use \"--dosver x.xx\"\n");
149         ExitProcess(1);
150     }
151 }
152
153 /**********************************************************************
154  *      VERSION_GetSystemDLLVersion
155  *
156  * This function tryes to figure out if a given (native) dll comes from
157  * win95/98 or winnt. Since all values in the OptionalHeader are not a 
158  * usable hint, we test if a dll imports the ntdll.
159  * This is at least working for all system-dlls like comctl32, comdlg32 and
160  * shell32.
161  * If you have a better idea to figure this out...
162  */
163 static DWORD VERSION_GetSystemDLLVersion( HMODULE hmod )
164 {
165     IMAGE_DATA_DIRECTORY *dir = PE_HEADER(hmod)->OptionalHeader.DataDirectory
166                                 + IMAGE_DIRECTORY_ENTRY_IMPORT;
167     if (dir->Size && dir->VirtualAddress)
168     {
169         IMAGE_IMPORT_DESCRIPTOR *pe_imp = (IMAGE_IMPORT_DESCRIPTOR *)((char *)hmod + dir->VirtualAddress);
170         for ( ; pe_imp->Name; pe_imp++)
171         {
172             char * name = (char *)hmod + (unsigned int)pe_imp->Name;
173             TRACE("%s\n", name);
174             
175             if (!strncasecmp(name, "ntdll", 5))
176             {
177               if (3 == PE_HEADER(hmod)->OptionalHeader.MajorOperatingSystemVersion)
178                 return NT351;
179               else
180                 return NT40;
181             }
182         }
183     }
184     return WIN95;
185 }
186 /**********************************************************************
187  *      VERSION_GetLinkedDllVersion
188  *
189  * Some version data (not reliable!):
190  * linker/OS/image/subsys
191  *
192  * x.xx/1.00/0.00/3.10  Win32s          (any version ?)
193  * 2.39/1.00/0.00/3.10  Win32s          freecell.exe (any version)
194  * 2.50/1.00/4.00/4.00  Win32s 1.30     winhlp32.exe    
195  * 2.60/3.51/3.51/3.51  NT351SP5        system dlls 
196  * 2.60/3.51/3.51/4.00  NT351SP5        comctl32 dll
197  * 2.xx/1.00/0.00/4.00  Win95           system files
198  * x.xx/4.00/0.00/4.00  Win95           most applications
199  * 3.10/4.00/0.00/4.00  Win98           notepad
200  * x.xx/5.00/5.00/4.00  Win98           system dlls
201  * x.xx/4.00/4.00/4.00  NT 4            most apps
202  * 5.12/5.00/5.00/4.00  NT4+IE5         comctl32.dll
203  * 5.12/5.00/5.00/4.00  Win98           calc
204  * x.xx/5.00/5.00/4.00  win95/win98/NT4 IE5 files
205  */
206 DWORD VERSION_GetLinkedDllVersion(PDB *pdb)
207 {
208         WINE_MODREF *wm;
209         DWORD WinVersion = NB_WINDOWS_VERSIONS;
210         PIMAGE_OPTIONAL_HEADER ophd;
211
212         if (!pdb->exe_modref)
213         {
214           if (!pdb->modref_list)
215             return WIN31;
216
217           /* FIXME: The above condition will never trigger, since all our
218            * standard dlls load their win32 equivalents. We have usually at
219            * this point: kernel32.dll and ntdll.dll.
220            */
221           return WIN95;
222         }
223         /* First check the native dlls provided. These have to be
224         from one windows version */
225         for ( wm = pdb->modref_list; wm; wm=wm->next )
226         {
227           ophd = &(PE_HEADER(wm->module)->OptionalHeader);
228
229           TRACE("%s: %02x.%02x/%02x.%02x/%02x.%02x/%02x.%02x\n",
230             wm->modname,
231             ophd->MajorLinkerVersion, ophd->MinorLinkerVersion,
232             ophd->MajorOperatingSystemVersion, ophd->MinorOperatingSystemVersion,
233             ophd->MajorImageVersion, ophd->MinorImageVersion,
234             ophd->MajorSubsystemVersion, ophd->MinorSubsystemVersion);
235
236           /* test if it is a external (native) dll */
237           if (!(wm->flags & WINE_MODREF_INTERNAL))
238           {
239             int i;
240             for (i = 0; special_dlls[i]; i++)
241             {
242               /* test if it a special dll */
243               if (!strncasecmp(wm->modname, special_dlls[i], strlen(special_dlls[i]) ))
244               {
245                 DWORD DllVersion = VERSION_GetSystemDLLVersion(wm->module);
246                 if (WinVersion == NB_WINDOWS_VERSIONS) 
247                   WinVersion = DllVersion;
248                 else {
249                   if (WinVersion != DllVersion) {
250                     ERR("You mixed system dlls from different windows versions! Expect a crash! (%s: expected version '%s', but is '%s')\n",
251                         wm->modname,
252                         VersionData[WinVersion].getVersionEx.szCSDVersion,
253                         VersionData[DllVersion].getVersionEx.szCSDVersion);
254                     return WIN31; /* this may let the exe exiting */
255                   }
256                 }
257                 break;
258               }
259             }
260           }
261         }
262         
263         if(WinVersion != NB_WINDOWS_VERSIONS) return WinVersion;
264         
265         /* we are using no external system dlls, look at the exe */
266         ophd = &(PE_HEADER(pdb->exe_modref->module)->OptionalHeader);
267         
268         TRACE("-%s: %02x.%02x/%02x.%02x/%02x.%02x/%02x.%02x\n",
269             pdb->exe_modref->modname,
270             ophd->MajorLinkerVersion, ophd->MinorLinkerVersion,
271             ophd->MajorOperatingSystemVersion, ophd->MinorOperatingSystemVersion,
272             ophd->MajorImageVersion, ophd->MinorImageVersion,
273             ophd->MajorSubsystemVersion, ophd->MinorSubsystemVersion);
274
275         /* special nt 3.51 */
276         if (3 == ophd->MajorOperatingSystemVersion && 51 == ophd->MinorOperatingSystemVersion)
277         {
278             return NT351;
279         }
280
281         /* the MajorSubsystemVersion is the only usable singn */
282         if (ophd->MajorSubsystemVersion < 4)
283         {
284           if ( ophd->MajorOperatingSystemVersion == 1 
285             && ophd->MinorOperatingSystemVersion == 0)
286           {
287             return WIN31; /* win32s */
288           }
289           
290           if (ophd->Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI)
291             return NT351; /* FIXME: NT 3.1, not tested */
292           else
293             return WIN95;
294         }       
295
296         return WIN95;
297 }
298
299 /**********************************************************************
300  *         VERSION_GetVersion
301  *
302  * WARNING !!!
303  * Don't call this function too early during the Wine init,
304  * as pdb->exe_modref (required by VERSION_GetImageVersion()) might still
305  * be NULL in such cases, which causes the winver to ALWAYS be detected
306  * as WIN31.
307  * And as we cache the winver once it has been determined, this is bad.
308  * This can happen much easier than you might think, as this function
309  * is called by EVERY GetVersion*() API !
310  *
311  */
312 WINDOWS_VERSION VERSION_GetVersion(void)
313 {
314         static WORD winver = 0xffff;
315
316         if (versionForced) /* user has overridden any sensible checks */
317           return defaultWinVersion;
318
319         if (winver == 0xffff) /* to be determined */ {
320           WINDOWS_VERSION retver = VERSION_GetLinkedDllVersion( PROCESS_Current() );
321
322           if (retver != WIN31) winver = retver;
323           return retver;
324         }
325         return winver;
326 }
327
328 /**********************************************************************
329  *         VERSION_AppWinVer
330  * Returns the window version in case Wine emulates a later version
331  * of windows then the application expects.
332  * 
333  * In a number of cases when windows runs an application that was
334  * designed for an earlier windows version, windows reverts
335  * to "old" behaviour of that earlier version.
336  * 
337  * An example is a disabled  edit control that needs to be painted. 
338  * Old style behaviour is to send a WM_CTLCOLOREDIT message. This was 
339  * changed in Win95, NT4.0 by a WM_CTLCOLORSTATIC message _only_ for 
340  * applications with an expected version 0f 4.0 or higher.
341  * 
342  */
343 DWORD VERSION_AppWinVer(void)
344 {
345     WINDOWS_VERSION ver = VERSION_GetVersion();
346     DWORD dwEmulatedVersion=MAKELONG( VersionData[ver].getVersionEx.dwMinorVersion, 
347                     VersionData[ver].getVersionEx.dwMajorVersion);
348     /* fixme: this may not be 100% correct; see discussion on the
349      * wine developer list in Nov 1999 */
350     DWORD dwProcVersion = GetProcessVersion(0);
351     return dwProcVersion < dwEmulatedVersion ? dwProcVersion : dwEmulatedVersion; 
352 }
353
354
355 /**********************************************************************
356  *         VERSION_GetVersionName
357  */
358 char *VERSION_GetVersionName()
359 {
360   WINDOWS_VERSION ver = VERSION_GetVersion();
361   switch(ver)
362     {
363     case WIN31:
364       return "Windows 3.1";
365     case WIN95:  
366       return "Windows 95";
367     case WIN98:
368       return "Windows 98";
369     case NT351:
370       return "Windows NT 3.51";
371     case NT40:
372       return "Windows NT 4.0";
373     default:
374       FIXME("Windows version %d not named",ver);
375       return "Windows <Unknown>";
376     }
377 }
378
379 /***********************************************************************
380  *         GetVersion16   (KERNEL.3)
381  */
382 LONG WINAPI GetVersion16(void)
383 {
384     WINDOWS_VERSION ver = VERSION_GetVersion();
385     return VersionData[ver].getVersion16;
386 }
387
388
389 /***********************************************************************
390  *         GetVersion   (KERNEL32.427)
391  */
392 LONG WINAPI GetVersion(void)
393 {
394     WINDOWS_VERSION ver = VERSION_GetVersion();
395     return VersionData[ver].getVersion32;
396 }
397
398
399 /***********************************************************************
400  *         GetVersionEx16   (KERNEL.149)
401  */
402 BOOL16 WINAPI GetVersionEx16(OSVERSIONINFO16 *v)
403 {
404     WINDOWS_VERSION ver = VERSION_GetVersion();
405     if (v->dwOSVersionInfoSize != sizeof(OSVERSIONINFO16))
406     {
407         WARN("wrong OSVERSIONINFO size from app");
408         SetLastError(ERROR_INSUFFICIENT_BUFFER);
409         return FALSE;
410     }
411     v->dwMajorVersion = VersionData[ver].getVersionEx.dwMajorVersion;
412     v->dwMinorVersion = VersionData[ver].getVersionEx.dwMinorVersion;
413     v->dwBuildNumber  = VersionData[ver].getVersionEx.dwBuildNumber;
414     v->dwPlatformId   = VersionData[ver].getVersionEx.dwPlatformId;
415     strcpy( v->szCSDVersion, VersionData[ver].getVersionEx.szCSDVersion );
416     return TRUE;
417 }
418
419
420 /***********************************************************************
421  *         GetVersionExA   (KERNEL32.428)
422  */
423 BOOL WINAPI GetVersionExA(OSVERSIONINFOA *v)
424 {
425     WINDOWS_VERSION ver = VERSION_GetVersion();
426     if (v->dwOSVersionInfoSize != sizeof(OSVERSIONINFOA))
427     {
428         WARN("wrong OSVERSIONINFO size from app (got: %ld, expected: %d)",
429                         v->dwOSVersionInfoSize, sizeof(OSVERSIONINFOA));
430         SetLastError(ERROR_INSUFFICIENT_BUFFER);
431         return FALSE;
432     }
433     v->dwMajorVersion = VersionData[ver].getVersionEx.dwMajorVersion;
434     v->dwMinorVersion = VersionData[ver].getVersionEx.dwMinorVersion;
435     v->dwBuildNumber  = VersionData[ver].getVersionEx.dwBuildNumber;
436     v->dwPlatformId   = VersionData[ver].getVersionEx.dwPlatformId;
437     strcpy( v->szCSDVersion, VersionData[ver].getVersionEx.szCSDVersion );
438     return TRUE;
439 }
440
441
442 /***********************************************************************
443  *         GetVersionExW   (KERNEL32.429)
444  */
445 BOOL WINAPI GetVersionExW(OSVERSIONINFOW *v)
446 {
447     WINDOWS_VERSION ver = VERSION_GetVersion();
448
449     if (v->dwOSVersionInfoSize!=sizeof(OSVERSIONINFOW))
450     {
451         WARN("wrong OSVERSIONINFO size from app (got: %ld, expected: %d)",
452                         v->dwOSVersionInfoSize, sizeof(OSVERSIONINFOW));
453         SetLastError(ERROR_INSUFFICIENT_BUFFER);
454         return FALSE;
455     }
456     v->dwMajorVersion = VersionData[ver].getVersionEx.dwMajorVersion;
457     v->dwMinorVersion = VersionData[ver].getVersionEx.dwMinorVersion;
458     v->dwBuildNumber  = VersionData[ver].getVersionEx.dwBuildNumber;
459     v->dwPlatformId   = VersionData[ver].getVersionEx.dwPlatformId;
460     lstrcpyAtoW( v->szCSDVersion, VersionData[ver].getVersionEx.szCSDVersion );
461     return TRUE;
462 }
463
464
465 /***********************************************************************
466  *          GetWinFlags   (KERNEL.132)
467  */
468 DWORD WINAPI GetWinFlags16(void)
469 {
470   static const long cpuflags[5] =
471     { WF_CPU086, WF_CPU186, WF_CPU286, WF_CPU386, WF_CPU486 };
472   SYSTEM_INFO si;
473   OSVERSIONINFOA ovi;
474   DWORD result;
475
476   GetSystemInfo(&si);
477
478   /* There doesn't seem to be any Pentium flag.  */
479   result = cpuflags[min(si.wProcessorLevel, 4)] | WF_ENHANCED | WF_PMODE | WF_80x87 | WF_PAGING;
480   if (si.wProcessorLevel >= 4) result |= WF_HASCPUID;
481   ovi.dwOSVersionInfoSize = sizeof(ovi);
482   GetVersionExA(&ovi);
483   if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT)
484       result |= WF_WIN32WOW; /* undocumented WF_WINNT */
485   return result;
486 }
487
488
489 /***********************************************************************
490  *          GetWinDebugInfo   (KERNEL.355)
491  */
492 BOOL16 WINAPI GetWinDebugInfo16(WINDEBUGINFO *lpwdi, UINT16 flags)
493 {
494     FIXME("(%8lx,%d): stub returning 0\n",
495           (unsigned long)lpwdi, flags);
496     /* 0 means not in debugging mode/version */
497     /* Can this type of debugging be used in wine ? */
498     /* Constants: WDI_OPTIONS WDI_FILTER WDI_ALLOCBREAK */
499     return 0;
500 }
501
502
503 /***********************************************************************
504  *          SetWinDebugInfo   (KERNEL.356)
505  */
506 BOOL16 WINAPI SetWinDebugInfo16(WINDEBUGINFO *lpwdi)
507 {
508     FIXME("(%8lx): stub returning 0\n", (unsigned long)lpwdi);
509     /* 0 means not in debugging mode/version */
510     /* Can this type of debugging be used in wine ? */
511     /* Constants: WDI_OPTIONS WDI_FILTER WDI_ALLOCBREAK */
512     return 0;
513 }
514
515
516 /***********************************************************************
517  *           DebugFillBuffer                    (KERNEL.329)
518  *
519  * TODO:
520  * Should fill lpBuffer only if DBO_BUFFERFILL has been set by SetWinDebugInfo()
521  */
522 void WINAPI DebugFillBuffer(LPSTR lpBuffer, WORD wBytes)
523 {
524         memset(lpBuffer, DBGFILL_BUFFER, wBytes);
525 }
526
527 /***********************************************************************
528  *           DiagQuery                          (KERNEL.339)
529  *
530  * returns TRUE if Win called with "/b" (bootlog.txt)
531  */
532 BOOL16 WINAPI DiagQuery16()
533 {
534         /* perhaps implement a Wine "/b" command line flag sometime ? */
535         return FALSE;
536 }
537
538 /***********************************************************************
539  *           DiagOutput                         (KERNEL.340)
540  *
541  * writes a debug string into <windir>\bootlog.txt
542  */
543 void WINAPI DiagOutput16(LPCSTR str)
544 {
545         /* FIXME */
546         DPRINTF("DIAGOUTPUT:%s\n", debugstr_a(str));
547 }
548
549 /***********************************************************************
550  *        VERSION_OsIsUnicode   [internal]
551  *
552  * NOTES
553  *   some functions getting sometimes LPSTR sometimes LPWSTR...
554  *
555  */
556 BOOL VERSION_OsIsUnicode(void)
557 {
558     switch(VERSION_GetVersion())
559     {
560     case NT351:
561     case NT40:
562         return TRUE;
563     default:
564         return FALSE;
565     }
566 }