Fix the case of product and company names.
[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,2003 Andreas Mohr
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "config.h"
25 #include "wine/port.h"
26
27 #include <string.h>
28 #include <stdlib.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include "ntstatus.h"
32 #include "windef.h"
33 #include "winbase.h"
34 #include "wingdi.h"
35 #include "winuser.h"
36 #include "winreg.h"
37 #include "winternl.h"
38 #include "winerror.h"
39 #include "wine/winbase16.h"
40 #include "module.h"
41 #include "wine/unicode.h"
42 #include "wine/debug.h"
43 #include "ntdll_misc.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(ver);
46
47 typedef enum
48 {
49     WIN20,   /* Windows 2.0 */
50     WIN30,   /* Windows 3.0 */
51     WIN31,   /* Windows 3.1 */
52     WIN95,   /* Windows 95 */
53     WIN98,   /* Windows 98 */
54     WINME,   /* Windows Me */
55     NT351,   /* Windows NT 3.51 */
56     NT40,    /* Windows NT 4.0 */
57     NT2K,    /* Windows 2000 */
58     WINXP,   /* Windows XP */
59     WIN2K3,  /* Windows 2003 */
60     NB_WINDOWS_VERSIONS
61 } WINDOWS_VERSION;
62
63 typedef struct
64 {
65     char             human_readable[32];
66     LONG             getVersion16;
67     LONG             getVersion32;
68     OSVERSIONINFOEXA getVersionEx;
69 } VERSION_DATA;
70
71 /* FIXME: compare values below with original and fix.
72  * An *excellent* win9x version page (ALL versions !)
73  * can be found at members.aol.com/axcel216/ver.htm */
74 static VERSION_DATA VersionData[NB_WINDOWS_VERSIONS] =
75 {
76     /* WIN20 FIXME: verify values */
77     {
78         "Windows 2.0",
79         MAKELONG( 0x0002, 0x0303 ), /* assume DOS 3.3 */
80         MAKELONG( 0x0002, 0x8000 ),
81         {
82             /* yes, sizeof(OSVERSIONINFOA) is correct here
83              * (in case of OSVERSIONINFOEXA application request,
84              * we adapt it dynamically). */
85             sizeof(OSVERSIONINFOA), 2, 0, 0,
86             VER_PLATFORM_WIN32s, "Win32s 1.3",
87             0, 0, 0, 0, 0
88         }
89     },
90     /* WIN30 FIXME: verify values */
91     {
92         "Windows 3.0",
93         MAKELONG( 0x0003, 0x0500 ), /* assume DOS 5.00 */
94         MAKELONG( 0x0003, 0x8000 ),
95         {
96             sizeof(OSVERSIONINFOA), 3, 0, 0,
97             VER_PLATFORM_WIN32s, "Win32s 1.3",
98             0, 0, 0, 0, 0
99         }
100     },
101     /* WIN31 */
102     {
103         "Windows 3.1",
104         MAKELONG( 0x0a03, 0x0616 ), /* DOS 6.22 */
105         MAKELONG( 0x0a03, 0x8000 ),
106         {
107             sizeof(OSVERSIONINFOA), 3, 10, 0,
108             VER_PLATFORM_WIN32s, "Win32s 1.3",
109             0, 0, 0, 0, 0
110         }
111     },
112     /* WIN95 */
113     {
114         "Windows 95",
115         0x07005F03,
116         0xC0000004,
117         {
118             /* Win95:       4, 0, 0x40003B6, ""
119              * Win95sp1:    4, 0, 0x40003B6, " A " (according to doc)
120              * Win95osr2:   4, 0, 0x4000457, " B " (according to doc)
121              * Win95osr2.1: 4, 3, 0x40304BC, " B " (according to doc)
122              * Win95osr2.5: 4, 3, 0x40304BE, " C " (according to doc)
123              * Win95a/b can be discerned via regkey SubVersionNumber
124              * See also:
125              * http://support.microsoft.com/support/kb/articles/q158/2/38.asp
126              */
127             sizeof(OSVERSIONINFOA), 4, 0, 0x40003B6,
128             VER_PLATFORM_WIN32_WINDOWS, "",
129             0, 0, 0, 0, 0
130         }
131     },
132     /* WIN98 (second edition) */
133     {
134         "Windows 98 SE",
135         0x070A5F03,
136         0xC0000A04,
137         {
138             /* Win98:   4, 10, 0x40A07CE, " "   4.10.1998
139              * Win98SE: 4, 10, 0x40A08AE, " A " 4.10.2222
140              */
141             sizeof(OSVERSIONINFOA), 4, 10, 0x40A08AE,
142             VER_PLATFORM_WIN32_WINDOWS, " A ",
143             0, 0, 0, 0, 0
144         }
145     },
146     /* WINME */
147     {
148         "Windows ME",
149         0x08005F03,
150         0xC0005A04,
151         {
152             sizeof(OSVERSIONINFOA), 4, 90, 0x45A0BB8,
153             VER_PLATFORM_WIN32_WINDOWS, " ",
154             0, 0, 0, 0, 0
155         }
156     },
157     /* NT351 */
158     {
159         "Windows NT 3.51",
160         0x05000A03,
161         0x04213303,
162         {
163             sizeof(OSVERSIONINFOA), 3, 51, 0x421,
164             VER_PLATFORM_WIN32_NT, "Service Pack 2",
165             0, 0, 0, 0, 0
166         }
167     },
168     /* NT40 */
169     {
170         "Windows NT 4.0",
171         0x05000A03,
172         0x05650004,
173         {
174             sizeof(OSVERSIONINFOA), 4, 0, 0x565,
175             VER_PLATFORM_WIN32_NT, "Service Pack 6",
176             6, 0, 0, VER_NT_WORKSTATION, 0
177         }
178     },
179     /* NT2K */
180     {
181         "Windows 2000",
182         0x05005F03,
183         0x08930005,
184         {
185             sizeof(OSVERSIONINFOA), 5, 0, 0x893,
186             VER_PLATFORM_WIN32_NT, "Service Pack 3",
187             3, 0, 0, VER_NT_WORKSTATION, 30 /* FIXME: Great, a reserved field with a value! */
188         }
189     },
190     /* WINXP */
191     {
192         "Windows XP",
193         0x05005F03, /* Assuming DOS 5 like the other NT */
194         0x0A280105,
195         {
196             sizeof(OSVERSIONINFOA), 5, 1, 0xA28,
197             VER_PLATFORM_WIN32_NT, "Service Pack 1",
198             1, 0, VER_SUITE_SINGLEUSERTS, VER_NT_WORKSTATION, 30 /* FIXME: Great, a reserved field with a value! */
199         }
200     },
201     /* WIN2K3 */
202     {
203         "Windows 2003",
204         0x05005F03, /* Assuming DOS 5 like the other NT */
205         0x0ECE0205,
206         {
207             sizeof(OSVERSIONINFOA), 5, 2, 0xECE,
208             VER_PLATFORM_WIN32_NT, "",
209             0, 0, VER_SUITE_SINGLEUSERTS, VER_NT_SERVER, 0
210         }
211     }
212 };
213
214 static const char *WinVersionNames[NB_WINDOWS_VERSIONS] =
215 { /* no spaces in here ! */
216     "win20",
217     "win30",
218     "win31",
219     "win95",
220     "win98",
221     "winme",
222     "nt351",
223     "nt40",
224     "win2000,win2k,nt2k,nt2000",
225     "winxp",
226     "win2003,win2k3"
227 };
228
229 /* if one of the following dlls is importing ntdll the windows
230 version autodetection switches wine to unicode (nt 3.51 or 4.0) */
231 static char * special_dlls[] =
232 {
233         "comdlg32.dll",
234         "comctl32.dll",
235         "shell32.dll",
236         "ole32.dll",
237         "rpcrt4.dll",
238         NULL
239 };
240
241 /* the current version has not been autodetected but forced via cmdline */
242 static BOOL versionForced = FALSE;
243 static WINDOWS_VERSION forcedWinVersion = WIN31; /* init value irrelevant */
244
245 /**********************************************************************
246  *         VERSION_ParseWinVersion
247  */
248 static void VERSION_ParseWinVersion( const char *arg )
249 {
250     int i, len;
251     const char *pCurr, *p;
252     for (i = 0; i < NB_WINDOWS_VERSIONS; i++)
253     {
254         pCurr = WinVersionNames[i];
255         /* iterate through all winver aliases separated by comma */
256         do {
257             p = strchr(pCurr, ',');
258             len = p ? (int)p - (int)pCurr : strlen(pCurr);
259             if ( (!strncmp( pCurr, arg, len )) && (arg[len] == '\0') )
260             {
261                 forcedWinVersion = (WINDOWS_VERSION)i;
262                 versionForced = TRUE;
263                 return;
264             }
265             pCurr = p+1;
266         } while (p);
267     }
268     MESSAGE("Invalid Windows version value '%s' specified in config file.\n", arg );
269     MESSAGE("Valid versions are:" );
270     for (i = 0; i < NB_WINDOWS_VERSIONS; i++)
271     {
272         /* only list the first, "official" alias in case of aliases */
273         pCurr = WinVersionNames[i];
274         p = strchr(pCurr, ',');
275         len = (p) ? (int)p - (int)pCurr : strlen(pCurr);
276
277         MESSAGE(" '%.*s'%c", len, pCurr,
278                 (i == NB_WINDOWS_VERSIONS - 1) ? '\n' : ',' );
279     }
280     ExitProcess(1);
281 }
282
283
284 /**********************************************************************
285  *         VERSION_ParseDosVersion
286  */
287 static void VERSION_ParseDosVersion( const char *arg )
288 {
289     int hi, lo;
290     if (sscanf( arg, "%d.%d", &hi, &lo ) == 2)
291     {
292         VersionData[WIN31].getVersion16 =
293             MAKELONG(LOWORD(VersionData[WIN31].getVersion16),
294                      (hi<<8) + lo);
295     }
296     else
297     {
298         MESSAGE("Wrong format for DOS version in config file. Use \"x.xx\"\n");
299         ExitProcess(1);
300     }
301 }
302
303
304 /**********************************************************************
305  *         VERSION_ParseVersion
306  *
307  * Parse the contents of the Version key.
308  */
309 static void VERSION_ParseVersion( HKEY hkey, BOOL *got_win_ver, BOOL *got_dos_ver )
310 {
311     static const WCHAR WindowsW[] = {'W','i','n','d','o','w','s',0};
312     static const WCHAR DosW[] = {'D','O','S',0};
313
314     UNICODE_STRING valueW;
315     char tmp[64], buffer[50];
316     KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)tmp;
317     DWORD count, len;
318
319     if (!*got_win_ver)
320     {
321         RtlInitUnicodeString( &valueW, WindowsW );
322         if (!NtQueryValueKey( hkey, &valueW, KeyValuePartialInformation, tmp, sizeof(tmp), &count ))
323         {
324             RtlUnicodeToMultiByteN( buffer, sizeof(buffer)-1, &len,
325                                     (WCHAR *)info->Data, info->DataLength );
326             buffer[len] = 0;
327             VERSION_ParseWinVersion( buffer );
328             TRACE( "got win version %s\n", WinVersionNames[forcedWinVersion] );
329             *got_win_ver = TRUE;
330         }
331     }
332     if (!*got_dos_ver)
333     {
334         RtlInitUnicodeString( &valueW, DosW );
335         if (!NtQueryValueKey( hkey, &valueW, KeyValuePartialInformation, tmp, sizeof(tmp), &count ))
336         {
337             RtlUnicodeToMultiByteN( buffer, sizeof(buffer)-1, &len,
338                                     (WCHAR *)info->Data, info->DataLength );
339             buffer[len] = 0;
340             VERSION_ParseDosVersion( buffer );
341             TRACE( "got dos version %lx\n", VersionData[WIN31].getVersion16 );
342             *got_dos_ver = TRUE;
343         }
344     }
345 }
346
347
348 /**********************************************************************
349  *         VERSION_Init
350  */
351 void VERSION_Init( const char *appname )
352 {
353     OBJECT_ATTRIBUTES attr;
354     UNICODE_STRING nameW;
355     HKEY hkey, config_key;
356     BOOL got_win_ver = FALSE, got_dos_ver = FALSE;
357     static const WCHAR configW[] = {'M','a','c','h','i','n','e','\\',
358                                     'S','o','f','t','w','a','r','e','\\',
359                                     'W','i','n','e','\\',
360                                     'W','i','n','e','\\',
361                                     'C','o','n','f','i','g',0};
362     static const WCHAR appdefaultsW[] = {'A','p','p','D','e','f','a','u','l','t','s','\\',0};
363     static const WCHAR versionW[] = {'\\','V','e','r','s','i','o','n',0};
364
365     attr.Length = sizeof(attr);
366     attr.RootDirectory = 0;
367     attr.ObjectName = &nameW;
368     attr.Attributes = 0;
369     attr.SecurityDescriptor = NULL;
370     attr.SecurityQualityOfService = NULL;
371     RtlInitUnicodeString( &nameW, configW );
372
373     if (NtOpenKey( &config_key, KEY_ALL_ACCESS, &attr )) return;
374     attr.RootDirectory = config_key;
375
376     /* open AppDefaults\\appname\\Version key */
377     if (appname && *appname)
378     {
379         const char *p;
380         DWORD len;
381         WCHAR appversion[MAX_PATH+20];
382
383         if ((p = strrchr( appname, '/' ))) appname = p + 1;
384         if ((p = strrchr( appname, '\\' ))) appname = p + 1;
385
386         strcpyW( appversion, appdefaultsW );
387         len = strlenW(appversion);
388         RtlMultiByteToUnicodeN( appversion + len, sizeof(appversion) - len*sizeof(WCHAR),
389                                 &len, appname, strlen(appname)+1 );
390         strcatW( appversion, versionW );
391         TRACE( "getting version from %s\n", debugstr_w(appversion) );
392         RtlInitUnicodeString( &nameW, appversion );
393
394         if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
395         {
396             VERSION_ParseVersion( hkey, &got_win_ver, &got_dos_ver );
397             NtClose( hkey );
398         }
399         if (got_win_ver && got_dos_ver) goto done;
400     }
401
402     TRACE( "getting default version\n" );
403     RtlInitUnicodeString( &nameW, versionW + 1 );
404     if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
405     {
406         VERSION_ParseVersion( hkey, &got_win_ver, &got_dos_ver );
407         NtClose( hkey );
408     }
409
410  done:
411     NtClose( config_key );
412 }
413
414
415 /**********************************************************************
416  *      VERSION_GetSystemDLLVersion
417  *
418  * This function tries to figure out if a given (native) dll comes from
419  * win95/98 or winnt. Since all values in the OptionalHeader are not a
420  * usable hint, we test if a dll imports the ntdll.
421  * This is at least working for all system dlls like comctl32, comdlg32 and
422  * shell32.
423  * If you have a better idea to figure this out...
424  */
425 static DWORD VERSION_GetSystemDLLVersion( HMODULE hmod )
426 {
427     DWORD size;
428     IMAGE_IMPORT_DESCRIPTOR *pe_imp;
429
430     if ((pe_imp = RtlImageDirectoryEntryToData( hmod, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size )))
431     {
432         for ( ; pe_imp->Name; pe_imp++)
433         {
434             char * name = (char *)hmod + (unsigned int)pe_imp->Name;
435             TRACE("%s\n", name);
436
437             if (!strncasecmp(name, "ntdll", 5))
438             {
439               switch(RtlImageNtHeader(hmod)->OptionalHeader.MajorOperatingSystemVersion) {
440                   case 3:
441                           MESSAGE("WARNING: very old native DLL (NT 3.x) used, might cause instability.\n");
442                           return NT351;
443                   case 4: return NT40;
444                   case 5: return NT2K;
445                   case 6: return WINXP;
446                   case 7: return WIN2K3; /* FIXME: Not sure, should be verified with a Win2K3 dll */
447                   default:
448                           FIXME("Unknown DLL OS version, please report !!\n");
449                           return WIN2K3;
450               }
451             }
452         }
453     }
454     return WIN95;
455 }
456 /**********************************************************************
457  *      VERSION_GetLinkedDllVersion
458  *
459  * Some version data (not reliable!):
460  * linker/OS/image/subsys
461  *
462  * x.xx/1.00/0.00/3.10  Win32s          (any version ?)
463  * 2.39/1.00/0.00/3.10  Win32s          freecell.exe (any version)
464  * 2.50/1.00/4.00/4.00  Win32s 1.30     winhlp32.exe
465  * 2.60/3.51/3.51/3.51  NT351SP5        system dlls
466  * 2.60/3.51/3.51/4.00  NT351SP5        comctl32 dll
467  * 2.xx/1.00/0.00/4.00  Win95           system files
468  * x.xx/4.00/0.00/4.00  Win95           most applications
469  * 3.10/4.00/0.00/4.00  Win98           notepad
470  * x.xx/5.00/5.00/4.00  Win98           system dlls (e.g. comctl32.dll)
471  * x.xx/4.00/4.00/4.00  NT 4            most apps
472  * 5.12/5.00/5.00/4.00  NT4+IE5         comctl32.dll
473  * 5.12/5.00/5.00/4.00  Win98           calc
474  * x.xx/5.00/5.00/4.00  win95/win98/NT4 IE5 files
475  */
476 static DWORD VERSION_GetLinkedDllVersion(void)
477 {
478         DWORD WinVersion = NB_WINDOWS_VERSIONS;
479         PIMAGE_OPTIONAL_HEADER ophd;
480         IMAGE_NT_HEADERS *nt;
481         ULONG       count, required;
482         SYSTEM_MODULE_INFORMATION*  smi;
483
484         /* First check the native dlls provided. These have to be
485         from one windows version */
486         smi = (SYSTEM_MODULE_INFORMATION*)&count;
487         LdrQueryProcessModuleInformation(smi, sizeof(count), &required);
488         smi = RtlAllocateHeap(ntdll_get_process_heap(), 0, required);
489         if (smi)
490         {
491             if (LdrQueryProcessModuleInformation(smi, required, NULL) == STATUS_SUCCESS)
492             {
493                 int i, k;
494                 for (k = 0; k < smi->ModulesCount; k++)
495                 {
496                     nt = RtlImageNtHeader(smi->Modules[k].ImageBaseAddress);
497                     ophd = &nt->OptionalHeader;
498
499                     TRACE("%s: %02x.%02x/%02x.%02x/%02x.%02x/%02x.%02x\n",
500                           &smi->Modules[k].Name[smi->Modules[k].NameOffset],
501                           ophd->MajorLinkerVersion, ophd->MinorLinkerVersion,
502                           ophd->MajorOperatingSystemVersion, ophd->MinorOperatingSystemVersion,
503                           ophd->MajorImageVersion, ophd->MinorImageVersion,
504                           ophd->MajorSubsystemVersion, ophd->MinorSubsystemVersion);
505
506                     /* test if it is an external (native) dll */
507                     if (smi->Modules[k].Flags & LDR_WINE_INTERNAL) continue;
508
509                     for (i = 0; special_dlls[i]; i++)
510                     {
511                         /* test if it is a special dll */
512                         if (!strcasecmp(&smi->Modules[k].Name[smi->Modules[k].NameOffset], special_dlls[i]))
513                         {
514                             DWORD DllVersion = VERSION_GetSystemDLLVersion(smi->Modules[k].ImageBaseAddress);
515                             if (WinVersion == NB_WINDOWS_VERSIONS)
516                                 WinVersion = DllVersion;
517                             else 
518                             {
519                                 if (WinVersion != DllVersion) {
520                                     ERR("You mixed system DLLs from different windows versions! Expect a crash! (%s: expected version '%s', but is '%s')\n",
521                                         &smi->Modules[k].Name[smi->Modules[k].NameOffset],
522                                         VersionData[WinVersion].getVersionEx.szCSDVersion,
523                                         VersionData[DllVersion].getVersionEx.szCSDVersion);
524                                     return WIN20; /* this may let the exe exiting */
525                                 }
526                             }
527                             break;
528                         }
529                     }
530                 }
531             }
532             RtlFreeHeap(ntdll_get_process_heap(), 0, smi);
533         }
534
535         if(WinVersion != NB_WINDOWS_VERSIONS) return WinVersion;
536
537         /* we are using no external system dlls, look at the exe */
538         nt = RtlImageNtHeader(GetModuleHandleA(NULL));
539         ophd = &nt->OptionalHeader;
540
541         TRACE("%02x.%02x/%02x.%02x/%02x.%02x/%02x.%02x\n",
542             ophd->MajorLinkerVersion, ophd->MinorLinkerVersion,
543             ophd->MajorOperatingSystemVersion, ophd->MinorOperatingSystemVersion,
544             ophd->MajorImageVersion, ophd->MinorImageVersion,
545             ophd->MajorSubsystemVersion, ophd->MinorSubsystemVersion);
546
547         /* special nt 3.51 */
548         if (3 == ophd->MajorOperatingSystemVersion && 51 == ophd->MinorOperatingSystemVersion)
549         {
550             return NT351;
551         }
552
553         /* the MajorSubsystemVersion is the only usable sign */
554         if (ophd->MajorSubsystemVersion < 4)
555         {
556           if ( ophd->MajorOperatingSystemVersion == 1
557             && ophd->MinorOperatingSystemVersion == 0)
558           {
559             return WIN31; /* win32s */
560           }
561
562           if (ophd->Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI)
563             return NT351; /* FIXME: NT 3.1, not tested */
564           else
565             return WIN95;
566         }
567
568         return WIN95;
569 }
570
571 /**********************************************************************
572  *         VERSION_GetVersion
573  *
574  * WARNING !!!
575  * Don't call this function too early during the Wine init,
576  * as pdb->exe_modref (required by VERSION_GetImageVersion()) might still
577  * be NULL in such cases, which causes the winver to ALWAYS be detected
578  * as WIN31.
579  * And as we cache the winver once it has been determined, this is bad.
580  * This can happen much easier than you might think, as this function
581  * is called by EVERY GetVersion*() API !
582  *
583  */
584 static WINDOWS_VERSION VERSION_GetVersion(void)
585 {
586     static WORD winver = 0xffff;
587
588     if (versionForced) return forcedWinVersion;  /* user has overridden any sensible checks */
589
590     if (winver == 0xffff) /* to be determined */
591     {
592         WINDOWS_VERSION retver = VERSION_GetLinkedDllVersion();
593
594         /* cache determined value, but do not store in case of WIN31 */
595         if (retver != WIN31) winver = retver;
596         return retver;
597     }
598     return winver;
599 }
600
601
602 /***********************************************************************
603  *         GetVersion   (KERNEL.3)
604  */
605 LONG WINAPI GetVersion16(void)
606 {
607     WINDOWS_VERSION ver = VERSION_GetVersion();
608     TRACE("<-- %s (%s)\n", VersionData[ver].human_readable, VersionData[ver].getVersionEx.szCSDVersion);
609     return VersionData[ver].getVersion16;
610 }
611
612
613 /***********************************************************************
614  *         GetVersion   (KERNEL32.@)
615  */
616 LONG WINAPI GetVersion(void)
617 {
618     WINDOWS_VERSION ver = VERSION_GetVersion();
619     TRACE("<-- %s (%s)\n", VersionData[ver].human_readable, VersionData[ver].getVersionEx.szCSDVersion);
620     return VersionData[ver].getVersion32;
621 }
622
623
624 /***********************************************************************
625  *         GetVersionEx   (KERNEL.149)
626  */
627 BOOL16 WINAPI GetVersionEx16(OSVERSIONINFO16 *v)
628 {
629     WINDOWS_VERSION ver = VERSION_GetVersion();
630     if (v->dwOSVersionInfoSize < sizeof(OSVERSIONINFO16))
631     {
632         WARN("wrong OSVERSIONINFO size from app\n");
633         SetLastError(ERROR_INSUFFICIENT_BUFFER);
634         return FALSE;
635     }
636     v->dwMajorVersion = VersionData[ver].getVersionEx.dwMajorVersion;
637     v->dwMinorVersion = VersionData[ver].getVersionEx.dwMinorVersion;
638     v->dwBuildNumber  = VersionData[ver].getVersionEx.dwBuildNumber;
639     v->dwPlatformId   = VersionData[ver].getVersionEx.dwPlatformId;
640     strcpy( v->szCSDVersion, VersionData[ver].getVersionEx.szCSDVersion );
641     TRACE("<-- %s (%s)\n", VersionData[ver].human_readable, VersionData[ver].getVersionEx.szCSDVersion);
642     return TRUE;
643 }
644
645
646 /***********************************************************************
647  *         GetVersionExA   (KERNEL32.@)
648  */
649 BOOL WINAPI GetVersionExA(OSVERSIONINFOA *v)
650 {
651     WINDOWS_VERSION ver = VERSION_GetVersion();
652     LPOSVERSIONINFOEXA vex;
653
654     if (v->dwOSVersionInfoSize != sizeof(OSVERSIONINFOA) &&
655         v->dwOSVersionInfoSize != sizeof(OSVERSIONINFOEXA))
656     {
657         WARN("wrong OSVERSIONINFO size from app (got: %ld, expected: %d or %d)\n",
658                         v->dwOSVersionInfoSize, sizeof(OSVERSIONINFOA),
659                         sizeof(OSVERSIONINFOEXA));
660         SetLastError(ERROR_INSUFFICIENT_BUFFER);
661         return FALSE;
662     }
663     v->dwMajorVersion = VersionData[ver].getVersionEx.dwMajorVersion;
664     v->dwMinorVersion = VersionData[ver].getVersionEx.dwMinorVersion;
665     v->dwBuildNumber  = VersionData[ver].getVersionEx.dwBuildNumber;
666     v->dwPlatformId   = VersionData[ver].getVersionEx.dwPlatformId;
667     strcpy( v->szCSDVersion, VersionData[ver].getVersionEx.szCSDVersion );
668     if(v->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXA)) {
669         vex = (LPOSVERSIONINFOEXA) v;
670         vex->wServicePackMajor = VersionData[ver].getVersionEx.wServicePackMajor;
671         vex->wServicePackMinor = VersionData[ver].getVersionEx.wServicePackMinor;
672         vex->wSuiteMask = VersionData[ver].getVersionEx.wSuiteMask;
673         vex->wProductType = VersionData[ver].getVersionEx.wProductType;
674     }
675     TRACE("<-- %s (%s)\n", VersionData[ver].human_readable, VersionData[ver].getVersionEx.szCSDVersion);
676     return TRUE;
677 }
678
679
680 /***********************************************************************
681  *         GetVersionExW   (KERNEL32.@)
682  */
683 BOOL WINAPI GetVersionExW(OSVERSIONINFOW *v)
684 {
685     WINDOWS_VERSION ver = VERSION_GetVersion();
686     LPOSVERSIONINFOEXW vex;
687
688     if (v->dwOSVersionInfoSize != sizeof(OSVERSIONINFOW) &&
689         v->dwOSVersionInfoSize != sizeof(OSVERSIONINFOEXW))
690     {
691         WARN("wrong OSVERSIONINFO size from app (got: %ld, expected: %d or %d)\n",
692                         v->dwOSVersionInfoSize, sizeof(OSVERSIONINFOW),
693                         sizeof(OSVERSIONINFOEXW));
694         SetLastError(ERROR_INSUFFICIENT_BUFFER);
695         return FALSE;
696     }
697     v->dwMajorVersion = VersionData[ver].getVersionEx.dwMajorVersion;
698     v->dwMinorVersion = VersionData[ver].getVersionEx.dwMinorVersion;
699     v->dwBuildNumber  = VersionData[ver].getVersionEx.dwBuildNumber;
700     v->dwPlatformId   = VersionData[ver].getVersionEx.dwPlatformId;
701     MultiByteToWideChar( CP_ACP, 0, VersionData[ver].getVersionEx.szCSDVersion, -1,
702                          v->szCSDVersion, sizeof(v->szCSDVersion)/sizeof(WCHAR) );
703     if(v->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXW)) {
704         vex = (LPOSVERSIONINFOEXW) v;
705         vex->wServicePackMajor = VersionData[ver].getVersionEx.wServicePackMajor;
706         vex->wServicePackMinor = VersionData[ver].getVersionEx.wServicePackMinor;
707         vex->wSuiteMask = VersionData[ver].getVersionEx.wSuiteMask;
708         vex->wProductType = VersionData[ver].getVersionEx.wProductType;
709     }
710     TRACE("<-- %s (%s)\n", VersionData[ver].human_readable, VersionData[ver].getVersionEx.szCSDVersion);
711     return TRUE;
712 }
713
714
715 /******************************************************************************
716  *        VerifyVersionInfoA   (KERNEL32.@)
717  */
718 BOOL WINAPI VerifyVersionInfoA( LPOSVERSIONINFOEXA lpVersionInfo, DWORD dwTypeMask,
719                                 DWORDLONG dwlConditionMask)
720 {
721     OSVERSIONINFOEXW verW;
722
723     verW.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
724     verW.dwMajorVersion = lpVersionInfo->dwMajorVersion;
725     verW.dwMinorVersion = lpVersionInfo->dwMinorVersion;
726     verW.dwBuildNumber = lpVersionInfo->dwBuildNumber;
727     verW.dwPlatformId = lpVersionInfo->dwPlatformId;
728     verW.wServicePackMajor = lpVersionInfo->wServicePackMajor;
729     verW.wServicePackMinor = lpVersionInfo->wServicePackMinor;
730     verW.wSuiteMask = lpVersionInfo->wSuiteMask;
731     verW.wProductType = lpVersionInfo->wProductType;
732     verW.wReserved = lpVersionInfo->wReserved;
733
734     return VerifyVersionInfoW(&verW, dwTypeMask, dwlConditionMask);
735 }
736
737
738 /******************************************************************************
739  *        VerifyVersionInfoW   (KERNEL32.@)
740  */
741 BOOL WINAPI VerifyVersionInfoW( LPOSVERSIONINFOEXW lpVersionInfo, DWORD dwTypeMask,
742                                 DWORDLONG dwlConditionMask)
743 {
744     OSVERSIONINFOEXW ver;
745     BOOL res, error_set;
746
747     FIXME("(%p,%lu,%llx): Not all cases correctly implemented yet\n", lpVersionInfo, dwTypeMask, dwlConditionMask);
748     /* FIXME:
749         - Check the following special case on Windows (various versions):
750           o lp->wSuiteMask == 0 and ver.wSuiteMask != 0 and VER_AND/VER_OR
751           o lp->dwOSVersionInfoSize != sizeof(OSVERSIONINFOEXW)
752         - MSDN talks about some tests being impossible. Check what really happens.
753      */
754
755     ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
756     if(!GetVersionExW((LPOSVERSIONINFOW) &ver))
757         return FALSE;
758
759     res = TRUE;
760     error_set = FALSE;
761     if(!(dwTypeMask && dwlConditionMask)) {
762         res = FALSE;
763         SetLastError(ERROR_BAD_ARGUMENTS);
764         error_set = TRUE;
765     }
766     if(dwTypeMask & VER_PRODUCT_TYPE)
767         switch(dwlConditionMask >> 7*3 & 0x07) {
768             case VER_EQUAL:
769                 if(ver.wProductType != lpVersionInfo->wProductType)
770                     res = FALSE;
771                 break;
772             case VER_GREATER:
773                 if(ver.wProductType <= lpVersionInfo->wProductType)
774                     res = FALSE;
775                 break;
776             case VER_GREATER_EQUAL:
777                 if(ver.wProductType < lpVersionInfo->wProductType)
778                     res = FALSE;
779                 break;
780             case VER_LESS:
781                 if(ver.wProductType >= lpVersionInfo->wProductType)
782                     res = FALSE;
783                 break;
784             case VER_LESS_EQUAL:
785                 if(ver.wProductType > lpVersionInfo->wProductType)
786                     res = FALSE;
787                 break;
788             default:
789                 res = FALSE;
790                 SetLastError(ERROR_BAD_ARGUMENTS);
791                 error_set = TRUE;
792         }
793     if(dwTypeMask & VER_SUITENAME && res)
794         switch(dwlConditionMask >> 6*3 & 0x07) {
795             case VER_AND:
796                 if((lpVersionInfo->wSuiteMask & ver.wSuiteMask) != lpVersionInfo->wSuiteMask)
797                     res = FALSE;
798                 break;
799             case VER_OR:
800                 if(!(lpVersionInfo->wSuiteMask & ver.wSuiteMask) && lpVersionInfo->wSuiteMask)
801                     res = FALSE;
802                 break;
803             default:
804                 res = FALSE;
805                 SetLastError(ERROR_BAD_ARGUMENTS);
806                 error_set = TRUE;
807         }
808     if(dwTypeMask & VER_PLATFORMID && res)
809         switch(dwlConditionMask >> 3*3 & 0x07) {
810             case VER_EQUAL:
811                 if(ver.dwPlatformId != lpVersionInfo->dwPlatformId)
812                     res = FALSE;
813                 break;
814             case VER_GREATER:
815                 if(ver.dwPlatformId <= lpVersionInfo->dwPlatformId)
816                     res = FALSE;
817                 break;
818             case VER_GREATER_EQUAL:
819                 if(ver.dwPlatformId < lpVersionInfo->dwPlatformId)
820                     res = FALSE;
821                 break;
822             case VER_LESS:
823                 if(ver.dwPlatformId >= lpVersionInfo->dwPlatformId)
824                     res = FALSE;
825                 break;
826             case VER_LESS_EQUAL:
827                 if(ver.dwPlatformId > lpVersionInfo->dwPlatformId)
828                     res = FALSE;
829                 break;
830             default:
831                 res = FALSE;
832                 SetLastError(ERROR_BAD_ARGUMENTS);
833                 error_set = TRUE;
834         }
835     if(dwTypeMask & VER_BUILDNUMBER && res)
836         switch(dwlConditionMask >> 2*3 & 0x07) {
837             case VER_EQUAL:
838                 if(ver.dwBuildNumber != lpVersionInfo->dwBuildNumber)
839                     res = FALSE;
840                 break;
841             case VER_GREATER:
842                 if(ver.dwBuildNumber <= lpVersionInfo->dwBuildNumber)
843                     res = FALSE;
844                 break;
845             case VER_GREATER_EQUAL:
846                 if(ver.dwBuildNumber < lpVersionInfo->dwBuildNumber)
847                     res = FALSE;
848                 break;
849             case VER_LESS:
850                 if(ver.dwBuildNumber >= lpVersionInfo->dwBuildNumber)
851                     res = FALSE;
852                 break;
853             case VER_LESS_EQUAL:
854                 if(ver.dwBuildNumber > lpVersionInfo->dwBuildNumber)
855                     res = FALSE;
856                 break;
857             default:
858                 res = FALSE;
859                 SetLastError(ERROR_BAD_ARGUMENTS);
860                 error_set = TRUE;
861         }
862     if(dwTypeMask & VER_MAJORVERSION && res)
863         switch(dwlConditionMask >> 1*3 & 0x07) {
864             case VER_EQUAL:
865                 if(ver.dwMajorVersion != lpVersionInfo->dwMajorVersion)
866                     res = FALSE;
867                 break;
868             case VER_GREATER:
869                 if(ver.dwMajorVersion <= lpVersionInfo->dwMajorVersion)
870                     res = FALSE;
871                 break;
872             case VER_GREATER_EQUAL:
873                 if(ver.dwMajorVersion < lpVersionInfo->dwMajorVersion)
874                     res = FALSE;
875                 break;
876             case VER_LESS:
877                 if(ver.dwMajorVersion >= lpVersionInfo->dwMajorVersion)
878                     res = FALSE;
879                 break;
880             case VER_LESS_EQUAL:
881                 if(ver.dwMajorVersion > lpVersionInfo->dwMajorVersion)
882                     res = FALSE;
883                 break;
884             default:
885                 res = FALSE;
886                 SetLastError(ERROR_BAD_ARGUMENTS);
887                 error_set = TRUE;
888         }
889     if(dwTypeMask & VER_MINORVERSION && res)
890         switch(dwlConditionMask >> 0*3 & 0x07) {
891             case VER_EQUAL:
892                 if(ver.dwMinorVersion != lpVersionInfo->dwMinorVersion)
893                     res = FALSE;
894                 break;
895             case VER_GREATER:
896                 if(ver.dwMinorVersion <= lpVersionInfo->dwMinorVersion)
897                     res = FALSE;
898                 break;
899             case VER_GREATER_EQUAL:
900                 if(ver.dwMinorVersion < lpVersionInfo->dwMinorVersion)
901                     res = FALSE;
902                 break;
903             case VER_LESS:
904                 if(ver.dwMinorVersion >= lpVersionInfo->dwMinorVersion)
905                     res = FALSE;
906                 break;
907             case VER_LESS_EQUAL:
908                 if(ver.dwMinorVersion > lpVersionInfo->dwMinorVersion)
909                     res = FALSE;
910                 break;
911             default:
912                 res = FALSE;
913                 SetLastError(ERROR_BAD_ARGUMENTS);
914                 error_set = TRUE;
915         }
916     if(dwTypeMask & VER_SERVICEPACKMAJOR && res)
917         switch(dwlConditionMask >> 5*3 & 0x07) {
918             case VER_EQUAL:
919                 if(ver.wServicePackMajor != lpVersionInfo->wServicePackMajor)
920                     res = FALSE;
921                 break;
922             case VER_GREATER:
923                 if(ver.wServicePackMajor <= lpVersionInfo->wServicePackMajor)
924                     res = FALSE;
925                 break;
926             case VER_GREATER_EQUAL:
927                 if(ver.wServicePackMajor < lpVersionInfo->wServicePackMajor)
928                     res = FALSE;
929                 break;
930             case VER_LESS:
931                 if(ver.wServicePackMajor >= lpVersionInfo->wServicePackMajor)
932                     res = FALSE;
933                 break;
934             case VER_LESS_EQUAL:
935                 if(ver.wServicePackMajor > lpVersionInfo->wServicePackMajor)
936                     res = FALSE;
937                 break;
938             default:
939                 res = FALSE;
940                 SetLastError(ERROR_BAD_ARGUMENTS);
941                 error_set = TRUE;
942         }
943     if(dwTypeMask & VER_SERVICEPACKMINOR && res)
944         switch(dwlConditionMask >> 4*3 & 0x07) {
945             case VER_EQUAL:
946                 if(ver.wServicePackMinor != lpVersionInfo->wServicePackMinor)
947                     res = FALSE;
948                 break;
949             case VER_GREATER:
950                 if(ver.wServicePackMinor <= lpVersionInfo->wServicePackMinor)
951                     res = FALSE;
952                 break;
953             case VER_GREATER_EQUAL:
954                 if(ver.wServicePackMinor < lpVersionInfo->wServicePackMinor)
955                     res = FALSE;
956                 break;
957             case VER_LESS:
958                 if(ver.wServicePackMinor >= lpVersionInfo->wServicePackMinor)
959                     res = FALSE;
960                 break;
961             case VER_LESS_EQUAL:
962                 if(ver.wServicePackMinor > lpVersionInfo->wServicePackMinor)
963                     res = FALSE;
964                 break;
965             default:
966                 res = FALSE;
967                 SetLastError(ERROR_BAD_ARGUMENTS);
968                 error_set = TRUE;
969         }
970
971     if(!(res || error_set))
972         SetLastError(ERROR_OLD_WIN_VERSION);
973     return res;
974 }
975
976
977 /***********************************************************************
978  *          GetWinFlags   (KERNEL.132)
979  */
980 DWORD WINAPI GetWinFlags16(void)
981 {
982   static const long cpuflags[5] =
983     { WF_CPU086, WF_CPU186, WF_CPU286, WF_CPU386, WF_CPU486 };
984   SYSTEM_INFO si;
985   OSVERSIONINFOA ovi;
986   DWORD result;
987
988   GetSystemInfo(&si);
989
990   /* There doesn't seem to be any Pentium flag.  */
991   result = cpuflags[min(si.wProcessorLevel, 4)] | WF_ENHANCED | WF_PMODE | WF_80x87 | WF_PAGING;
992   if (si.wProcessorLevel >= 4) result |= WF_HASCPUID;
993   ovi.dwOSVersionInfoSize = sizeof(ovi);
994   GetVersionExA(&ovi);
995   if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT)
996       result |= WF_WIN32WOW; /* undocumented WF_WINNT */
997   return result;
998 }
999
1000
1001 #if 0
1002 /* Not used at this time. This is here for documentation only */
1003
1004 /* WINDEBUGINFO flags values */
1005 #define WDI_OPTIONS         0x0001
1006 #define WDI_FILTER          0x0002
1007 #define WDI_ALLOCBREAK      0x0004
1008
1009 /* dwOptions values */
1010 #define DBO_CHECKHEAP       0x0001
1011 #define DBO_BUFFERFILL      0x0004
1012 #define DBO_DISABLEGPTRAPPING 0x0010
1013 #define DBO_CHECKFREE       0x0020
1014
1015 #define DBO_SILENT          0x8000
1016
1017 #define DBO_TRACEBREAK      0x2000
1018 #define DBO_WARNINGBREAK    0x1000
1019 #define DBO_NOERRORBREAK    0x0800
1020 #define DBO_NOFATALBREAK    0x0400
1021 #define DBO_INT3BREAK       0x0100
1022
1023 /* DebugOutput flags values */
1024 #define DBF_TRACE           0x0000
1025 #define DBF_WARNING         0x4000
1026 #define DBF_ERROR           0x8000
1027 #define DBF_FATAL           0xc000
1028
1029 /* dwFilter values */
1030 #define DBF_KERNEL          0x1000
1031 #define DBF_KRN_MEMMAN      0x0001
1032 #define DBF_KRN_LOADMODULE  0x0002
1033 #define DBF_KRN_SEGMENTLOAD 0x0004
1034 #define DBF_USER            0x0800
1035 #define DBF_GDI             0x0400
1036 #define DBF_MMSYSTEM        0x0040
1037 #define DBF_PENWIN          0x0020
1038 #define DBF_APPLICATION     0x0008
1039 #define DBF_DRIVER          0x0010
1040
1041 #endif /* NOLOGERROR */
1042
1043
1044 /***********************************************************************
1045  *          GetWinDebugInfo   (KERNEL.355)
1046  */
1047 BOOL16 WINAPI GetWinDebugInfo16(WINDEBUGINFO16 *lpwdi, UINT16 flags)
1048 {
1049     FIXME("(%8lx,%d): stub returning 0\n",
1050           (unsigned long)lpwdi, flags);
1051     /* 0 means not in debugging mode/version */
1052     /* Can this type of debugging be used in wine ? */
1053     /* Constants: WDI_OPTIONS WDI_FILTER WDI_ALLOCBREAK */
1054     return 0;
1055 }
1056
1057
1058 /***********************************************************************
1059  *          SetWinDebugInfo   (KERNEL.356)
1060  */
1061 BOOL16 WINAPI SetWinDebugInfo16(WINDEBUGINFO16 *lpwdi)
1062 {
1063     FIXME("(%8lx): stub returning 0\n", (unsigned long)lpwdi);
1064     /* 0 means not in debugging mode/version */
1065     /* Can this type of debugging be used in wine ? */
1066     /* Constants: WDI_OPTIONS WDI_FILTER WDI_ALLOCBREAK */
1067     return 0;
1068 }
1069
1070
1071 /***********************************************************************
1072  *           K329                    (KERNEL.329)
1073  *
1074  * TODO:
1075  * Should fill lpBuffer only if DBO_BUFFERFILL has been set by SetWinDebugInfo()
1076  */
1077 void WINAPI DebugFillBuffer(LPSTR lpBuffer, WORD wBytes)
1078 {
1079         memset(lpBuffer, DBGFILL_BUFFER, wBytes);
1080 }
1081
1082 /***********************************************************************
1083  *           DiagQuery                          (KERNEL.339)
1084  *
1085  * returns TRUE if Win called with "/b" (bootlog.txt)
1086  */
1087 BOOL16 WINAPI DiagQuery16(void)
1088 {
1089         /* perhaps implement a Wine "/b" command line flag sometime ? */
1090         return FALSE;
1091 }
1092
1093 /***********************************************************************
1094  *           DiagOutput                         (KERNEL.340)
1095  *
1096  * writes a debug string into <windir>\bootlog.txt
1097  */
1098 void WINAPI DiagOutput16(LPCSTR str)
1099 {
1100         /* FIXME */
1101         DPRINTF("DIAGOUTPUT:%s\n", debugstr_a(str));
1102 }