Rearrange winver detection code and cache the winver value we
[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 "winbase.h"
13 #include "winuser.h"
14 #include "wine/winbase16.h"
15 #include "process.h"
16 #include "options.h"
17 #include "debug.h"
18 #include "neexe.h"
19 #include "winversion.h"
20
21 DEFAULT_DEBUG_CHANNEL(ver)
22
23 typedef struct
24 {
25     LONG             getVersion16; 
26     LONG             getVersion32;
27     OSVERSIONINFOA getVersionEx;
28 } VERSION_DATA;
29
30
31 /* FIXME: compare values below with original and fix */
32 static VERSION_DATA VersionData[NB_WINDOWS_VERSIONS] =
33 {
34     /* WIN31 */
35     {
36         MAKELONG( 0x0a03, 0x0616 ), /* DOS 6.22 */
37         MAKELONG( 0x0a03, 0x8000 ),
38         {
39             sizeof(OSVERSIONINFOA), 3, 10, 0,
40             VER_PLATFORM_WIN32s, "Win32s 1.3" 
41         }
42     },
43     /* WIN95 */
44     {
45         0x07005F03,
46         0xC0000004,
47         {
48             sizeof(OSVERSIONINFOA), 4, 0, 0x40003B6,
49             VER_PLATFORM_WIN32_WINDOWS, "Win95"
50         }
51     },
52     /* NT351 */
53     {
54         0x05000A03,
55         0x04213303,
56         {
57             sizeof(OSVERSIONINFOA), 3, 51, 0x421,
58             VER_PLATFORM_WIN32_NT, "Service Pack 2"
59         }
60     },
61     /* NT40 */
62     {
63         0x05000A03,
64         0x05650004,
65         {
66             sizeof(OSVERSIONINFOA), 4, 0, 0x565,
67             VER_PLATFORM_WIN32_NT, "Service Pack 3"
68         }
69     }
70 };
71
72 static const char *WinVersionNames[NB_WINDOWS_VERSIONS] =
73 {
74     "win31",
75     "win95",
76     "nt351",
77     "nt40"
78 };
79
80 /* the current version has not been autodetected but forced via cmdline */
81 static BOOL versionForced = FALSE;
82 static WINDOWS_VERSION defaultWinVersion = WIN31;
83
84
85 /**********************************************************************
86  *         VERSION_ParseWinVersion
87  */
88 void VERSION_ParseWinVersion( const char *arg )
89 {
90     int i;
91     for (i = 0; i < NB_WINDOWS_VERSIONS; i++)
92     {
93         if (!strcmp( WinVersionNames[i], arg ))
94         {
95             defaultWinVersion = (WINDOWS_VERSION)i;
96             versionForced = TRUE;
97             return;
98         }
99     }
100     MSG("Invalid winver value '%s' specified.\n", arg );
101     MSG("Valid versions are:" );
102     for (i = 0; i < NB_WINDOWS_VERSIONS; i++)
103         MSG(" '%s'%c", WinVersionNames[i],
104             (i == NB_WINDOWS_VERSIONS - 1) ? '\n' : ',' );
105 }
106
107
108 /**********************************************************************
109  *         VERSION_ParseDosVersion
110  */
111 void VERSION_ParseDosVersion( const char *arg )
112 {
113     int hi, lo;
114     if (sscanf( arg, "%d.%d", &hi, &lo ) == 2)
115     {
116         VersionData[WIN31].getVersion16 =
117             MAKELONG(LOWORD(VersionData[WIN31].getVersion16),
118                      (hi<<8) + lo);
119     }
120     else
121         fprintf( stderr, "-dosver: Wrong version format. Use \"-dosver x.xx\"\n");
122 }
123
124
125 WINDOWS_VERSION VERSION_GetImageVersion(PDB *pdb)
126 {
127     PIMAGE_NT_HEADERS peheader;
128
129         if (!pdb->exe_modref)
130         {
131                 /* HACK: if we have loaded a PE image into this address space,
132                  * we are probably using thunks, so Win95 is our best bet
133                  */
134                 if (pdb->modref_list)
135                         return WIN95;
136                 /* FIXME: hmm, do anything else ?
137                    TDB.version doesn't help here
138                    as it always holds version 3.10 */
139                         return WIN31;
140         }
141         else
142         {
143                 peheader = PE_HEADER(pdb->exe_modref->module);
144 #define OPTHD peheader->OptionalHeader
145
146                 TRACE(ver, "%02x.%02x/%02x.%02x/%02x.%02x/%02x.%02x\n",
147                           OPTHD.MajorLinkerVersion,
148                           OPTHD.MinorLinkerVersion,
149                           OPTHD.MajorOperatingSystemVersion,
150                           OPTHD.MinorOperatingSystemVersion,
151                           OPTHD.MajorImageVersion,
152                           OPTHD.MinorImageVersion,
153                           OPTHD.MajorSubsystemVersion,
154                           OPTHD.MinorSubsystemVersion);
155
156         switch (OPTHD.MajorSubsystemVersion)
157                 {
158                         case 4:
159                                 switch (OPTHD.MajorOperatingSystemVersion)
160                                 {
161                                         case 5:
162                                                 return NT40; /* FIXME: this is NT 5, isn't it ? */
163                                         case 4:
164                                                 if ((OPTHD.MajorImageVersion == 0) &&
165                                                         (OPTHD.SectionAlignment == 4096))
166                                                         return WIN95;
167                                                 else
168                                                         return NT40;
169                                         case 1:
170                                                 return WIN95;
171                                         default:
172                                                 return WIN95; /* FIXME ? */
173                                 }
174                         case 3:
175                                 /* 3.1x versions */
176                                 if (OPTHD.MinorSubsystemVersion <= 11)
177                                 {
178                                         if (OPTHD.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI)
179                                                 return NT351; /* FIXME: NT 3.1 */
180                                         else
181                                                 return WIN31;
182                                 }
183                                 else
184                                 {
185                                         /* NT 3.51 */
186                                         if (OPTHD.MinorSubsystemVersion == 50)
187                                                 return NT351;
188                                         else
189                                         if (OPTHD.MinorSubsystemVersion == 51)
190                                                 return NT351;
191                                 }
192                         default:
193                                 ERR(ver,"unknown subsystem version: %04x.%04x, please report.\n",
194                                         OPTHD.MajorSubsystemVersion,
195                                         OPTHD.MinorSubsystemVersion );
196                                 return defaultWinVersion;
197                 }
198 #undef OPTHD
199         }
200 }
201
202
203 /**********************************************************************
204  *
205  * Check the version of COMDLG32, SHELL32, COMCTL32 and CTL3D32.
206  * Not functional yet.
207  */
208 DWORD VERSION_GetLinkedDllVersion(PDB *pdb)
209 {
210         WINE_MODREF *wm;
211     WORD VersionCounter[NB_WINDOWS_VERSIONS];
212
213     memset(VersionCounter, 0, sizeof(VersionCounter));
214
215         VersionCounter[WIN95] = 1;
216         for ( wm = PROCESS_Current()->modref_list; wm; wm=wm->next ) {
217             if (!(lstrncmpiA(wm->modname, "CTL3D32", 7)))
218                         VersionCounter[WIN95]++;
219         }
220
221         return MAKELONG(WIN95, VersionCounter[WIN95]);
222 }
223
224
225 /**********************************************************************
226  *         VERSION_GetVersion
227  *
228  * Some version data:
229  * linker/OS/image/subsys       Name                    Intended for
230  *
231  * 2.39/1.00/0.00/3.10          freecell.exe    Win32s (any version)
232  * 2.55/1.00/0.00/4.00          acrord32.exe    Win32s, Win95 supported (?)
233  * 
234  * 2.50/1.00/4.00/4.00          winhlp32.exe    Win32s 1.30
235  * 4.20/4.00/1.00/4.00          Asuslm.exe              Win95 (Aaargh !)
236  * 5.12/4.00/1.07/4.00      clikfixi.exe    NT 4 (service pack files)
237  * 3.10/4.00/4.00/4.00          PLUMBING.EXE    NT
238  * ?.??/4.00/97.01/4.00         sse.exe                 huh ?? (damn crackerz ;)
239  * 5.12/5.00/5.00/4.00          comctl32.dll    NT4 / IE 5.0
240  * 6.00/5.00/5.00/4.00                                          NT 4 driver update (strange numbers)
241  * 
242  * Common versions:
243  * x.xx/1.00/0.00/3.10                                          Win32s (any version ?)
244  * 2.xx/1.00/0.00/4.00                                          Win95 (Microsoft/system files) 
245  * x.xx/4.00/0.00/4.00                                          Win95 (most applications !)
246  * x.xx/4.00/4.00/4.00                                          NT 4 (most apps)
247  * x.xx/5.00/5.00/4.00                                          NT 4 newer files / NT 5 ??
248  */
249 WINDOWS_VERSION VERSION_GetVersion(void)
250 {
251     PDB *pdb = PROCESS_Current();
252     DWORD DllVer;
253
254     if (versionForced) /* user has overridden any sensible checks */
255         return defaultWinVersion;
256
257     if (pdb->winver == 0xffff) /* to be determined */
258         {
259                 DllVer = 0/*VERSION_GetLinkedDllVersion(pdb)*/;
260                 if (HIWORD(DllVer) > 1)
261                         pdb->winver = LOWORD(DllVer);
262             else
263                         pdb->winver = VERSION_GetImageVersion(pdb);
264         }
265
266         return pdb->winver;
267 }
268
269
270 /**********************************************************************
271  *         VERSION_GetVersionName
272  */
273 char *VERSION_GetVersionName()
274 {
275   WINDOWS_VERSION ver = VERSION_GetVersion();
276   switch(ver)
277     {
278     case WIN31:
279       return "Windows 3.1";
280     case WIN95:  
281       return "Windows 95";
282     case NT351:
283       return "Windows NT 3.51";
284     case NT40:
285       return "Windows NT 4.0";
286     default:
287       FIXME(ver,"Windows version %d not named",ver);
288       return "Windows <Unknown>";
289     }
290 }
291
292 /***********************************************************************
293  *         GetVersion16   (KERNEL.3)
294  */
295 LONG WINAPI GetVersion16(void)
296 {
297     WINDOWS_VERSION ver = VERSION_GetVersion();
298     return VersionData[ver].getVersion16;
299 }
300
301
302 /***********************************************************************
303  *         GetVersion32   (KERNEL32.427)
304  */
305 LONG WINAPI GetVersion(void)
306 {
307     WINDOWS_VERSION ver = VERSION_GetVersion();
308     return VersionData[ver].getVersion32;
309 }
310
311
312 /***********************************************************************
313  *         GetVersionEx16   (KERNEL.149)
314  */
315 BOOL16 WINAPI GetVersionEx16(OSVERSIONINFO16 *v)
316 {
317     WINDOWS_VERSION ver = VERSION_GetVersion();
318     if (v->dwOSVersionInfoSize != sizeof(OSVERSIONINFO16))
319     {
320         WARN(ver,"wrong OSVERSIONINFO size from app");
321         return FALSE;
322     }
323     v->dwMajorVersion = VersionData[ver].getVersionEx.dwMajorVersion;
324     v->dwMinorVersion = VersionData[ver].getVersionEx.dwMinorVersion;
325     v->dwBuildNumber  = VersionData[ver].getVersionEx.dwBuildNumber;
326     v->dwPlatformId   = VersionData[ver].getVersionEx.dwPlatformId;
327     strcpy( v->szCSDVersion, VersionData[ver].getVersionEx.szCSDVersion );
328     return TRUE;
329 }
330
331
332 /***********************************************************************
333  *         GetVersionEx32A   (KERNEL32.428)
334  */
335 BOOL WINAPI GetVersionExA(OSVERSIONINFOA *v)
336 {
337     WINDOWS_VERSION ver = VERSION_GetVersion();
338     if (v->dwOSVersionInfoSize != sizeof(OSVERSIONINFOA))
339     {
340         WARN(ver,"wrong OSVERSIONINFO size from app");
341         return FALSE;
342     }
343     v->dwMajorVersion = VersionData[ver].getVersionEx.dwMajorVersion;
344     v->dwMinorVersion = VersionData[ver].getVersionEx.dwMinorVersion;
345     v->dwBuildNumber  = VersionData[ver].getVersionEx.dwBuildNumber;
346     v->dwPlatformId   = VersionData[ver].getVersionEx.dwPlatformId;
347     strcpy( v->szCSDVersion, VersionData[ver].getVersionEx.szCSDVersion );
348     return TRUE;
349 }
350
351
352 /***********************************************************************
353  *         GetVersionEx32W   (KERNEL32.429)
354  */
355 BOOL WINAPI GetVersionExW(OSVERSIONINFOW *v)
356 {
357     WINDOWS_VERSION ver = VERSION_GetVersion();
358
359     if (v->dwOSVersionInfoSize!=sizeof(OSVERSIONINFOW))
360     {
361         WARN(ver,"wrong OSVERSIONINFO size from app");
362         return FALSE;
363     }
364     v->dwMajorVersion = VersionData[ver].getVersionEx.dwMajorVersion;
365     v->dwMinorVersion = VersionData[ver].getVersionEx.dwMinorVersion;
366     v->dwBuildNumber  = VersionData[ver].getVersionEx.dwBuildNumber;
367     v->dwPlatformId   = VersionData[ver].getVersionEx.dwPlatformId;
368     lstrcpyAtoW( v->szCSDVersion, VersionData[ver].getVersionEx.szCSDVersion );
369     return TRUE;
370 }
371
372
373 /***********************************************************************
374  *          GetWinFlags   (KERNEL.132)
375  */
376 DWORD WINAPI GetWinFlags16(void)
377 {
378   static const long cpuflags[5] =
379     { WF_CPU086, WF_CPU186, WF_CPU286, WF_CPU386, WF_CPU486 };
380   SYSTEM_INFO si;
381   OSVERSIONINFOA ovi;
382   DWORD result;
383
384   GetSystemInfo(&si);
385
386   /* There doesn't seem to be any Pentium flag.  */
387   result = cpuflags[MIN (si.wProcessorLevel, 4)];
388
389   switch(Options.mode)
390   {
391   case MODE_STANDARD:
392       result |= WF_STANDARD | WF_PMODE | WF_80x87;
393       break;
394
395   case MODE_ENHANCED:
396       result |= WF_ENHANCED | WF_PMODE | WF_80x87 | WF_PAGING;
397       break;
398
399   default:
400       ERR(ver, "Unknown mode set? This shouldn't happen. Check GetWinFlags()!\n");
401       break;
402   }
403   if (si.wProcessorLevel >= 4) result |= WF_HASCPUID;
404   ovi.dwOSVersionInfoSize = sizeof(ovi);
405   GetVersionExA(&ovi);
406   if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT)
407       result |= WF_WIN32WOW; /* undocumented WF_WINNT */
408   return result;
409 }
410
411
412 /***********************************************************************
413  *          GetWinDebugInfo   (KERNEL.355)
414  */
415 BOOL16 WINAPI GetWinDebugInfo16(WINDEBUGINFO *lpwdi, UINT16 flags)
416 {
417     FIXME(ver, "(%8lx,%d): stub returning 0\n",
418           (unsigned long)lpwdi, flags);
419     /* 0 means not in debugging mode/version */
420     /* Can this type of debugging be used in wine ? */
421     /* Constants: WDI_OPTIONS WDI_FILTER WDI_ALLOCBREAK */
422     return 0;
423 }
424
425
426 /***********************************************************************
427  *          SetWinDebugInfo   (KERNEL.356)
428  */
429 BOOL16 WINAPI SetWinDebugInfo16(WINDEBUGINFO *lpwdi)
430 {
431     FIXME(ver, "(%8lx): stub returning 0\n", (unsigned long)lpwdi);
432     /* 0 means not in debugging mode/version */
433     /* Can this type of debugging be used in wine ? */
434     /* Constants: WDI_OPTIONS WDI_FILTER WDI_ALLOCBREAK */
435     return 0;
436 }
437
438
439 /***********************************************************************
440  *           DebugFillBuffer                    (KERNEL.329)
441  *
442  * TODO:
443  * Should fill lpBuffer only if DBO_BUFFERFILL has been set by SetWinDebugInfo()
444  */
445 void WINAPI DebugFillBuffer(LPSTR lpBuffer, WORD wBytes)
446 {
447         memset(lpBuffer, DBGFILL_BUFFER, wBytes);
448 }
449
450 /***********************************************************************
451  *           DiagQuery                          (KERNEL.339)
452  *
453  * returns TRUE if Win called with "/b" (bootlog.txt)
454  */
455 BOOL16 WINAPI DiagQuery16()
456 {
457         /* perhaps implement a Wine "/b" command line flag sometime ? */
458         return FALSE;
459 }
460
461 /***********************************************************************
462  *           DiagOutput                         (KERNEL.340)
463  *
464  * writes a debug string into <windir>\bootlog.txt
465  */
466 void WINAPI DiagOutput16(LPCSTR str)
467 {
468         /* FIXME */
469         DPRINTF("DIAGOUTPUT:%s\n", debugstr_a(str));
470 }
471
472 /***********************************************************************
473  *           OaBuildVersion           [OLEAUT32.170]
474  */
475 UINT WINAPI OaBuildVersion()
476 {
477     WINDOWS_VERSION ver = VERSION_GetVersion();
478
479     FIXME(ver, "Please report to a.mohr@mailto.de if you get version error messages !\n");
480     switch(VersionData[ver].getVersion32)
481     {
482         case 0x80000a03: /* Win 3.1 */
483                 return 0x140fd1; /* from Win32s 1.1e */
484         case 0xc0000004: /* Win 95 */
485                 return 0x1e10a9; /* some older version: 0x0a0bd3 */
486         case 0x04213303: /* NT 3.51 */
487                 FIXME(ver, "NT 3.51 version value unknown !\n");
488                 return 0x1e10a9; /* value borrowed from Win95 */
489         case 0x05650004: /* NT 4.0 */
490                 return 0x141016;
491         default:
492                 return 0x0;
493     }
494 }
495 /***********************************************************************
496  *        VERSION_OsIsUnicode   [internal]
497  *
498  * NOTES
499  *   some functions getting sometimes LPSTR sometimes LPWSTR...
500  *
501  */
502 BOOL VERSION_OsIsUnicode(void)
503 {
504     switch(VERSION_GetVersion())
505     {
506     case NT351:
507     case NT40:
508         return TRUE;
509     default:
510         return FALSE;
511     }
512 }