mcicda: Exclude unused headers.
[wine] / dlls / ntdll / version.c
1 /*
2  * Windows and DOS version functions
3  *
4  * Copyright 1997 Marcus Meissner
5  * Copyright 1998 Patrik Stridvall
6  * Copyright 1998, 2003 Andreas Mohr
7  * Copyright 1997, 2003 Alexandre Julliard
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 #define WIN32_NO_STATUS
33 #include "windef.h"
34 #include "wine/unicode.h"
35 #include "wine/debug.h"
36 #include "ntdll_misc.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(ver);
39
40 typedef enum
41 {
42     WIN20,   /* Windows 2.0 */
43     WIN30,   /* Windows 3.0 */
44     WIN31,   /* Windows 3.1 */
45     WIN95,   /* Windows 95 */
46     WIN98,   /* Windows 98 */
47     WINME,   /* Windows Me */
48     NT351,   /* Windows NT 3.51 */
49     NT40,    /* Windows NT 4.0 */
50     NT2K,    /* Windows 2000 */
51     WINXP,   /* Windows XP */
52     WIN2K3,  /* Windows 2003 */
53     WINVISTA,/* Windows Vista */
54     NB_WINDOWS_VERSIONS
55 } WINDOWS_VERSION;
56
57 /* FIXME: compare values below with original and fix.
58  * An *excellent* win9x version page (ALL versions !)
59  * can be found at www.mdgx.com/ver.htm */
60 static const RTL_OSVERSIONINFOEXW VersionData[NB_WINDOWS_VERSIONS] =
61 {
62     /* WIN20 FIXME: verify values */
63     {
64         sizeof(RTL_OSVERSIONINFOEXW), 2, 0, 0, VER_PLATFORM_WIN32s,
65         {'W','i','n','3','2','s',' ','1','.','3',0},
66         0, 0, 0, 0, 0
67     },
68     /* WIN30 FIXME: verify values */
69     {
70         sizeof(RTL_OSVERSIONINFOEXW), 3, 0, 0, VER_PLATFORM_WIN32s,
71         {'W','i','n','3','2','s',' ','1','.','3',0},
72         0, 0, 0, 0, 0
73     },
74     /* WIN31 */
75     {
76         sizeof(RTL_OSVERSIONINFOEXW), 3, 10, 0, VER_PLATFORM_WIN32s,
77         {'W','i','n','3','2','s',' ','1','.','3',0},
78         0, 0, 0, 0, 0
79     },
80     /* WIN95 */
81     {
82         /* Win95:       4, 0, 0x40003B6, ""
83          * Win95sp1:    4, 0, 0x40003B6, " A " (according to doc)
84          * Win95osr2:   4, 0, 0x4000457, " B " (according to doc)
85          * Win95osr2.1: 4, 3, 0x40304BC, " B " (according to doc)
86          * Win95osr2.5: 4, 3, 0x40304BE, " C " (according to doc)
87          * Win95a/b can be discerned via regkey SubVersionNumber
88          * See also:
89          * http://support.microsoft.com/support/kb/articles/q158/2/38.asp
90          */
91         sizeof(RTL_OSVERSIONINFOEXW), 4, 0, 0x40003B6, VER_PLATFORM_WIN32_WINDOWS,
92         {0},
93         0, 0, 0, 0, 0
94     },
95     /* WIN98 (second edition) */
96     {
97         /* Win98:   4, 10, 0x40A07CE, " "   4.10.1998
98          * Win98SE: 4, 10, 0x40A08AE, " A " 4.10.2222
99          */
100         sizeof(RTL_OSVERSIONINFOEXW), 4, 10, 0x40A08AE, VER_PLATFORM_WIN32_WINDOWS,
101         {' ','A',' ',0},
102         0, 0, 0, 0, 0
103     },
104     /* WINME */
105     {
106         sizeof(RTL_OSVERSIONINFOEXW), 4, 90, 0x45A0BB8, VER_PLATFORM_WIN32_WINDOWS,
107         {' ',0},
108         0, 0, 0, 0, 0
109     },
110     /* NT351 */
111     {
112         sizeof(RTL_OSVERSIONINFOEXW), 3, 51, 0x421, VER_PLATFORM_WIN32_NT,
113         {'S','e','r','v','i','c','e',' ','P','a','c','k',' ','2',0},
114         0, 0, 0, 0, 0
115     },
116     /* NT40 */
117     {
118         sizeof(RTL_OSVERSIONINFOEXW), 4, 0, 0x565, VER_PLATFORM_WIN32_NT,
119         {'S','e','r','v','i','c','e',' ','P','a','c','k',' ','6','a',0},
120         6, 0, 0, VER_NT_WORKSTATION, 0
121     },
122     /* NT2K */
123     {
124         sizeof(RTL_OSVERSIONINFOEXW), 5, 0, 0x893, VER_PLATFORM_WIN32_NT,
125         {'S','e','r','v','i','c','e',' ','P','a','c','k',' ','4',0},
126         4, 0, 0, VER_NT_WORKSTATION, 30 /* FIXME: Great, a reserved field with a value! */
127     },
128     /* WINXP */
129     {
130         sizeof(RTL_OSVERSIONINFOEXW), 5, 1, 0xA28, VER_PLATFORM_WIN32_NT,
131         {'S','e','r','v','i','c','e',' ','P','a','c','k',' ','2',0},
132         2, 0, VER_SUITE_SINGLEUSERTS, VER_NT_WORKSTATION, 30 /* FIXME: Great, a reserved field with a value! */
133     },
134     /* WIN2K3 */
135     {
136         sizeof(RTL_OSVERSIONINFOEXW), 5, 2, 0xECE, VER_PLATFORM_WIN32_NT,
137         {'S','e','r','v','i','c','e',' ','P','a','c','k',' ','1',0},
138         1, 0, VER_SUITE_SINGLEUSERTS, VER_NT_SERVER, 0
139     },
140     /* WINVISTA */
141     {
142         sizeof(RTL_OSVERSIONINFOEXW), 6, 0, 0x1770, VER_PLATFORM_WIN32_NT,
143         {' ',0},
144         0, 0, VER_SUITE_SINGLEUSERTS, VER_NT_WORKSTATION, 0
145     }
146 };
147
148 static const char * const WinVersionNames[NB_WINDOWS_VERSIONS] =
149 { /* no spaces in here ! */
150     "win20",                      /* WIN20 */
151     "win30",                      /* WIN30 */
152     "win31",                      /* WIN31 */
153     "win95",                      /* WIN95 */
154     "win98",                      /* WIN98 */
155     "winme",                      /* WINME */
156     "nt351",                      /* NT351 */
157     "nt40",                       /* NT40 */
158     "win2000,win2k,nt2k,nt2000",  /* NT2K */
159     "winxp",                      /* WINXP */
160     "win2003,win2k3",             /* WIN2K3 */
161     "vista,winvista"              /* WINVISTA*/
162 };
163
164
165 /* initialized to null so that we crash if we try to retrieve the version too early at startup */
166 static const RTL_OSVERSIONINFOEXW *current_version;
167
168
169 /**********************************************************************
170  *         get_nt_registry_version
171  *
172  * Fetch the version information from the NT-style registry keys.
173  */
174 static BOOL get_nt_registry_version( RTL_OSVERSIONINFOEXW *version )
175 {
176     static const WCHAR version_keyW[] = {'M','a','c','h','i','n','e','\\',
177                                          'S','o','f','t','w','a','r','e','\\',
178                                          'M','i','c','r','o','s','o','f','t','\\',
179                                          'W','i','n','d','o','w','s',' ','N','T','\\',
180                                          'C','u','r','r','e','n','t','V','e','r','s','i','o','n',0};
181     static const WCHAR service_pack_keyW[] = {'M','a','c','h','i','n','e','\\',
182                                               'S','y','s','t','e','m','\\',
183                                               'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
184                                               'C','o','n','t','r','o','l','\\',
185                                               'W','i','n','d','o','w','s',0};
186     static const WCHAR product_keyW[] = {'M','a','c','h','i','n','e','\\',
187                                          'S','y','s','t','e','m','\\',
188                                          'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
189                                          'C','o','n','t','r','o','l','\\',
190                                          'P','r','o','d','u','c','t','O','p','t','i','o','n','s',0};
191     static const WCHAR CurrentBuildNumberW[] = {'C','u','r','r','e','n','t','B','u','i','l','d','N','u','m','b','e','r',0};
192     static const WCHAR CSDVersionW[] = {'C','S','D','V','e','r','s','i','o','n',0};
193     static const WCHAR CurrentVersionW[] = {'C','u','r','r','e','n','t','V','e','r','s','i','o','n',0};
194     static const WCHAR ProductTypeW[] = {'P','r','o','d','u','c','t','T','y','p','e',0};
195     static const WCHAR WinNTW[]    = {'W','i','n','N','T',0};
196     static const WCHAR ServerNTW[] = {'S','e','r','v','e','r','N','T',0};
197     static const WCHAR LanmanNTW[] = {'L','a','n','m','a','n','N','T',0};
198
199     OBJECT_ATTRIBUTES attr;
200     UNICODE_STRING nameW, valueW;
201     HANDLE hkey, hkey2;
202     char tmp[64];
203     DWORD count;
204     BOOL ret = FALSE;
205     KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)tmp;
206
207     attr.Length = sizeof(attr);
208     attr.RootDirectory = 0;
209     attr.ObjectName = &nameW;
210     attr.Attributes = 0;
211     attr.SecurityDescriptor = NULL;
212     attr.SecurityQualityOfService = NULL;
213     RtlInitUnicodeString( &nameW, version_keyW );
214
215     if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) return FALSE;
216
217     memset( version, 0, sizeof(*version) );
218
219     RtlInitUnicodeString( &valueW, CurrentVersionW );
220     if (!NtQueryValueKey( hkey, &valueW, KeyValuePartialInformation, tmp, sizeof(tmp)-1, &count ))
221     {
222         WCHAR *p, *str = (WCHAR *)info->Data;
223         str[info->DataLength / sizeof(WCHAR)] = 0;
224         p = strchrW( str, '.' );
225         if (p)
226         {
227             *p++ = 0;
228             version->dwMinorVersion = atoiW( p );
229         }
230         version->dwMajorVersion = atoiW( str );
231     }
232
233     if (version->dwMajorVersion)   /* we got the main version, now fetch the other fields */
234     {
235         ret = TRUE;
236         version->dwPlatformId = VER_PLATFORM_WIN32_NT;
237
238         /* get build number */
239
240         RtlInitUnicodeString( &valueW, CurrentBuildNumberW );
241         if (!NtQueryValueKey( hkey, &valueW, KeyValuePartialInformation, tmp, sizeof(tmp)-1, &count ))
242         {
243             WCHAR *str = (WCHAR *)info->Data;
244             str[info->DataLength / sizeof(WCHAR)] = 0;
245             version->dwBuildNumber = atoiW( str );
246         }
247
248         /* get version description */
249
250         RtlInitUnicodeString( &valueW, CSDVersionW );
251         if (!NtQueryValueKey( hkey, &valueW, KeyValuePartialInformation, tmp, sizeof(tmp)-1, &count ))
252         {
253             DWORD len = min( info->DataLength, sizeof(version->szCSDVersion) - sizeof(WCHAR) );
254             memcpy( version->szCSDVersion, info->Data, len );
255             version->szCSDVersion[len / sizeof(WCHAR)] = 0;
256         }
257
258         /* get service pack version */
259
260         RtlInitUnicodeString( &nameW, service_pack_keyW );
261         if (!NtOpenKey( &hkey2, KEY_ALL_ACCESS, &attr ))
262         {
263             RtlInitUnicodeString( &valueW, CSDVersionW );
264             if (!NtQueryValueKey( hkey2, &valueW, KeyValuePartialInformation, tmp, sizeof(tmp), &count ))
265             {
266                 if (info->DataLength >= sizeof(DWORD))
267                 {
268                     DWORD dw = *(DWORD *)info->Data;
269                     version->wServicePackMajor = LOWORD(dw) >> 8;
270                     version->wServicePackMinor = LOWORD(dw) & 0xff;
271                 }
272             }
273             NtClose( hkey2 );
274         }
275
276         /* get product type */
277
278         RtlInitUnicodeString( &nameW, product_keyW );
279         if (!NtOpenKey( &hkey2, KEY_ALL_ACCESS, &attr ))
280         {
281             RtlInitUnicodeString( &valueW, ProductTypeW );
282             if (!NtQueryValueKey( hkey2, &valueW, KeyValuePartialInformation, tmp, sizeof(tmp)-1, &count ))
283             {
284                 WCHAR *str = (WCHAR *)info->Data;
285                 str[info->DataLength / sizeof(WCHAR)] = 0;
286                 if (!strcmpiW( str, WinNTW )) version->wProductType = VER_NT_WORKSTATION;
287                 else if (!strcmpiW( str, LanmanNTW )) version->wProductType = VER_NT_DOMAIN_CONTROLLER;
288                 else if (!strcmpiW( str, ServerNTW )) version->wProductType = VER_NT_SERVER;
289             }
290             NtClose( hkey2 );
291         }
292
293         /* FIXME: get wSuiteMask */
294     }
295
296     NtClose( hkey );
297     return ret;
298 }
299
300
301 /**********************************************************************
302  *         get_win9x_registry_version
303  *
304  * Fetch the version information from the Win9x-style registry keys.
305  */
306 static BOOL get_win9x_registry_version( RTL_OSVERSIONINFOEXW *version )
307 {
308     static const WCHAR version_keyW[] = {'M','a','c','h','i','n','e','\\',
309                                          'S','o','f','t','w','a','r','e','\\',
310                                          'M','i','c','r','o','s','o','f','t','\\',
311                                          'W','i','n','d','o','w','s','\\',
312                                          'C','u','r','r','e','n','t','V','e','r','s','i','o','n',0};
313     static const WCHAR VersionNumberW[] = {'V','e','r','s','i','o','n','N','u','m','b','e','r',0};
314     static const WCHAR SubVersionNumberW[] = {'S','u','b','V','e','r','s','i','o','n','N','u','m','b','e','r',0};
315
316     OBJECT_ATTRIBUTES attr;
317     UNICODE_STRING nameW, valueW;
318     HANDLE hkey;
319     char tmp[64];
320     DWORD count;
321     BOOL ret = FALSE;
322     KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)tmp;
323
324     attr.Length = sizeof(attr);
325     attr.RootDirectory = 0;
326     attr.ObjectName = &nameW;
327     attr.Attributes = 0;
328     attr.SecurityDescriptor = NULL;
329     attr.SecurityQualityOfService = NULL;
330     RtlInitUnicodeString( &nameW, version_keyW );
331
332     if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) return FALSE;
333
334     memset( version, 0, sizeof(*version) );
335
336     RtlInitUnicodeString( &valueW, VersionNumberW );
337     if (!NtQueryValueKey( hkey, &valueW, KeyValuePartialInformation, tmp, sizeof(tmp)-1, &count ))
338     {
339         WCHAR *p, *str = (WCHAR *)info->Data;
340         str[info->DataLength / sizeof(WCHAR)] = 0;
341         p = strchrW( str, '.' );
342         if (p) *p++ = 0;
343         version->dwMajorVersion = atoiW( str );
344         if (p)
345         {
346             str = p;
347             p = strchrW( str, '.' );
348             if (p)
349             {
350                 *p++ = 0;
351                 version->dwBuildNumber = atoiW( p );
352             }
353             version->dwMinorVersion = atoiW( str );
354         }
355         /* build number contains version too on Win9x */
356         version->dwBuildNumber |= MAKEWORD( version->dwMinorVersion, version->dwMajorVersion ) << 16;
357     }
358
359     if (version->dwMajorVersion)   /* we got the main version, now fetch the other fields */
360     {
361         ret = TRUE;
362         version->dwPlatformId = VER_PLATFORM_WIN32_WINDOWS;
363
364         RtlInitUnicodeString( &valueW, SubVersionNumberW );
365         if (!NtQueryValueKey( hkey, &valueW, KeyValuePartialInformation, tmp, sizeof(tmp)-1, &count ))
366         {
367             DWORD len = min( info->DataLength, sizeof(version->szCSDVersion) - sizeof(WCHAR) );
368             memcpy( version->szCSDVersion, info->Data, len );
369             version->szCSDVersion[len / sizeof(WCHAR)] = 0;
370         }
371     }
372
373     NtClose( hkey );
374     return ret;
375 }
376
377
378 /**********************************************************************
379  *         parse_win_version
380  *
381  * Parse the contents of the Version key.
382  */
383 static BOOL parse_win_version( HANDLE hkey )
384 {
385     static const WCHAR VersionW[] = {'V','e','r','s','i','o','n',0};
386
387     UNICODE_STRING valueW;
388     char tmp[64], buffer[50];
389     KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)tmp;
390     DWORD count, len;
391     int i;
392
393     RtlInitUnicodeString( &valueW, VersionW );
394     if (NtQueryValueKey( hkey, &valueW, KeyValuePartialInformation, tmp, sizeof(tmp), &count ))
395         return FALSE;
396
397     RtlUnicodeToMultiByteN( buffer, sizeof(buffer)-1, &len, (WCHAR *)info->Data, info->DataLength );
398     buffer[len] = 0;
399
400     for (i = 0; i < NB_WINDOWS_VERSIONS; i++)
401     {
402         const char *p, *pCurr = WinVersionNames[i];
403         /* iterate through all winver aliases separated by comma */
404         do {
405             p = strchr(pCurr, ',');
406             len = p ? p - pCurr : strlen(pCurr);
407             if ( (!strncmp( pCurr, buffer, len )) && (buffer[len] == 0) )
408             {
409                 current_version = &VersionData[i];
410                 TRACE( "got win version %s\n", WinVersionNames[i] );
411                 return TRUE;
412             }
413             pCurr = p+1;
414         } while (p);
415     }
416
417     MESSAGE("Invalid Windows version value '%s' specified in config file.\n", buffer );
418     MESSAGE("Valid versions are:" );
419     for (i = 0; i < NB_WINDOWS_VERSIONS; i++)
420     {
421         /* only list the first, "official" alias in case of aliases */
422         const char *pCurr = WinVersionNames[i];
423         const char *p = strchr(pCurr, ',');
424         len = (p) ? p - pCurr : strlen(pCurr);
425
426         MESSAGE(" '%.*s'%c", (int)len, pCurr, (i == NB_WINDOWS_VERSIONS - 1) ? '\n' : ',' );
427     }
428     return FALSE;
429 }
430
431
432 /**********************************************************************
433  *         version_init
434  */
435 void version_init( const WCHAR *appname )
436 {
437     static const WCHAR configW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e',0};
438     static const WCHAR appdefaultsW[] = {'A','p','p','D','e','f','a','u','l','t','s','\\',0};
439
440     OBJECT_ATTRIBUTES attr;
441     UNICODE_STRING nameW;
442     HANDLE root, hkey, config_key;
443     BOOL got_win_ver = FALSE;
444
445     current_version = &VersionData[NT2K];  /* default if nothing else is specified */
446
447     RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
448     attr.Length = sizeof(attr);
449     attr.RootDirectory = root;
450     attr.ObjectName = &nameW;
451     attr.Attributes = 0;
452     attr.SecurityDescriptor = NULL;
453     attr.SecurityQualityOfService = NULL;
454     RtlInitUnicodeString( &nameW, configW );
455
456     /* @@ Wine registry key: HKCU\Software\Wine */
457     if (NtOpenKey( &config_key, KEY_ALL_ACCESS, &attr )) config_key = 0;
458     NtClose( root );
459     if (!config_key) goto done;
460
461     /* open AppDefaults\\appname key */
462     if (appname && *appname)
463     {
464         const WCHAR *p;
465         WCHAR appversion[MAX_PATH+20];
466
467         if ((p = strrchrW( appname, '/' ))) appname = p + 1;
468         if ((p = strrchrW( appname, '\\' ))) appname = p + 1;
469
470         strcpyW( appversion, appdefaultsW );
471         strcatW( appversion, appname );
472         RtlInitUnicodeString( &nameW, appversion );
473         attr.RootDirectory = config_key;
474
475         /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe */
476         if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
477         {
478             TRACE( "getting version from %s\n", debugstr_w(appversion) );
479             got_win_ver = parse_win_version( hkey );
480             NtClose( hkey );
481         }
482     }
483
484     if (!got_win_ver)
485     {
486         TRACE( "getting default version\n" );
487         got_win_ver = parse_win_version( config_key );
488     }
489     NtClose( config_key );
490
491 done:
492     if (!got_win_ver)
493     {
494         static RTL_OSVERSIONINFOEXW registry_version;
495
496         TRACE( "getting registry version\n" );
497         if (get_nt_registry_version( &registry_version ) ||
498             get_win9x_registry_version( &registry_version ))
499             current_version = &registry_version;
500     }
501
502
503     NtCurrentTeb()->Peb->OSMajorVersion = current_version->dwMajorVersion;
504     NtCurrentTeb()->Peb->OSMinorVersion = current_version->dwMinorVersion;
505     NtCurrentTeb()->Peb->OSBuildNumber  = current_version->dwBuildNumber;
506     NtCurrentTeb()->Peb->OSPlatformId   = current_version->dwPlatformId;
507
508     TRACE( "got %d.%d plaform %d build %x name %s service pack %d.%d product %d\n",
509            current_version->dwMajorVersion, current_version->dwMinorVersion,
510            current_version->dwPlatformId, current_version->dwBuildNumber,
511            debugstr_w(current_version->szCSDVersion),
512            current_version->wServicePackMajor, current_version->wServicePackMinor,
513            current_version->wProductType );
514 }
515
516
517 /***********************************************************************
518  *         RtlGetVersion   (NTDLL.@)
519  */
520 NTSTATUS WINAPI RtlGetVersion( RTL_OSVERSIONINFOEXW *info )
521 {
522     info->dwMajorVersion = current_version->dwMajorVersion;
523     info->dwMinorVersion = current_version->dwMinorVersion;
524     info->dwBuildNumber  = current_version->dwBuildNumber;
525     info->dwPlatformId   = current_version->dwPlatformId;
526     strcpyW( info->szCSDVersion, current_version->szCSDVersion );
527     if(info->dwOSVersionInfoSize == sizeof(RTL_OSVERSIONINFOEXW))
528     {
529         info->wServicePackMajor = current_version->wServicePackMajor;
530         info->wServicePackMinor = current_version->wServicePackMinor;
531         info->wSuiteMask        = current_version->wSuiteMask;
532         info->wProductType      = current_version->wProductType;
533     }
534     return STATUS_SUCCESS;
535 }
536
537
538 /******************************************************************************
539  *  RtlGetNtVersionNumbers   (NTDLL.@)
540  *
541  * Get the version numbers of the run time library.
542  *
543  * PARAMS
544  *  major [O] Destination for the Major version
545  *  minor [O] Destination for the Minor version
546  *  build [O] Destination for the Build version
547  *
548  * RETURNS
549  *  Nothing.
550  *
551  * NOTES
552  * Introduced in Windows XP (NT5.1)
553  */
554 void WINAPI RtlGetNtVersionNumbers( LPDWORD major, LPDWORD minor, LPDWORD build )
555 {
556     if (major) *major = current_version->dwMajorVersion;
557     if (minor) *minor = current_version->dwMinorVersion;
558     /* FIXME: Does anybody know the real formula? */
559     if (build) *build = (0xF0000000 | current_version->dwBuildNumber);
560 }
561
562
563 /******************************************************************************
564  *  RtlGetNtProductType   (NTDLL.@)
565  */
566 BOOLEAN WINAPI RtlGetNtProductType( LPDWORD type )
567 {
568     if (type) *type = current_version->wProductType;
569     return TRUE;
570 }
571
572
573 static inline NTSTATUS version_compare_values(ULONG left, ULONG right, UCHAR condition)
574 {
575     switch (condition) {
576         case VER_EQUAL:
577             if (left != right) return STATUS_REVISION_MISMATCH;
578             break;
579         case VER_GREATER:
580             if (left <= right) return STATUS_REVISION_MISMATCH;
581             break;
582         case VER_GREATER_EQUAL:
583             if (left < right) return STATUS_REVISION_MISMATCH;
584             break;
585         case VER_LESS:
586             if (left >= right) return STATUS_REVISION_MISMATCH;
587             break;
588         case VER_LESS_EQUAL:
589             if (left > right) return STATUS_REVISION_MISMATCH;
590             break;
591         default:
592             return STATUS_REVISION_MISMATCH;
593     }
594     return STATUS_SUCCESS;
595 }
596
597 /******************************************************************************
598  *        RtlVerifyVersionInfo   (NTDLL.@)
599  */
600 NTSTATUS WINAPI RtlVerifyVersionInfo( const RTL_OSVERSIONINFOEXW *info,
601                                       DWORD dwTypeMask, DWORDLONG dwlConditionMask )
602 {
603     RTL_OSVERSIONINFOEXW ver;
604     NTSTATUS status;
605
606     TRACE("(%p,0x%x,0x%s)\n", info, dwTypeMask, wine_dbgstr_longlong(dwlConditionMask));
607
608     ver.dwOSVersionInfoSize = sizeof(ver);
609     if ((status = RtlGetVersion( &ver )) != STATUS_SUCCESS) return status;
610
611     if(!(dwTypeMask && dwlConditionMask)) return STATUS_INVALID_PARAMETER;
612
613     if(dwTypeMask & VER_PRODUCT_TYPE)
614     {
615         status = version_compare_values(ver.wProductType, info->wProductType, dwlConditionMask >> 7*3 & 0x07);
616         if (status != STATUS_SUCCESS)
617             return status;
618     }
619     if(dwTypeMask & VER_SUITENAME)
620         switch(dwlConditionMask >> 6*3 & 0x07)
621         {
622             case VER_AND:
623                 if((info->wSuiteMask & ver.wSuiteMask) != info->wSuiteMask)
624                     return STATUS_REVISION_MISMATCH;
625                 break;
626             case VER_OR:
627                 if(!(info->wSuiteMask & ver.wSuiteMask) && info->wSuiteMask)
628                     return STATUS_REVISION_MISMATCH;
629                 break;
630             default:
631                 return STATUS_INVALID_PARAMETER;
632         }
633     if(dwTypeMask & VER_PLATFORMID)
634     {
635         status = version_compare_values(ver.dwPlatformId, info->dwPlatformId, dwlConditionMask >> 3*3 & 0x07);
636         if (status != STATUS_SUCCESS)
637             return status;
638     }
639     if(dwTypeMask & VER_BUILDNUMBER)
640     {
641         status = version_compare_values(ver.dwBuildNumber, info->dwBuildNumber, dwlConditionMask >> 2*3 & 0x07);
642         if (status != STATUS_SUCCESS)
643             return status;
644     }
645
646     if(dwTypeMask & (VER_MAJORVERSION|VER_MINORVERSION|VER_SERVICEPACKMAJOR|VER_SERVICEPACKMINOR))
647     {
648         unsigned char condition = 0;
649         BOOLEAN do_next_check = TRUE;
650
651         if(dwTypeMask & VER_MAJORVERSION)
652             condition = dwlConditionMask >> 1*3 & 0x07;
653         else if(dwTypeMask & VER_MINORVERSION)
654             condition = dwlConditionMask >> 0*3 & 0x07;
655         else if(dwTypeMask & VER_SERVICEPACKMAJOR)
656             condition = dwlConditionMask >> 5*3 & 0x07;
657         else if(dwTypeMask & VER_SERVICEPACKMINOR)
658             condition = dwlConditionMask >> 4*3 & 0x07;
659
660         if(dwTypeMask & VER_MAJORVERSION)
661         {
662             status = version_compare_values(ver.dwMajorVersion, info->dwMajorVersion, condition);
663             do_next_check = (ver.dwMajorVersion == info->dwMajorVersion) &&
664                 ((condition != VER_EQUAL) || (status == STATUS_SUCCESS));
665         }
666         if((dwTypeMask & VER_MINORVERSION) && do_next_check)
667         {
668             status = version_compare_values(ver.dwMinorVersion, info->dwMinorVersion, condition);
669             do_next_check = (ver.dwMinorVersion == info->dwMinorVersion) &&
670                 ((condition != VER_EQUAL) || (status == STATUS_SUCCESS));
671         }
672         if((dwTypeMask & VER_SERVICEPACKMAJOR) && do_next_check)
673         {
674             status = version_compare_values(ver.wServicePackMajor, info->wServicePackMajor, condition);
675             do_next_check = (ver.wServicePackMajor == info->wServicePackMajor) &&
676                 ((condition != VER_EQUAL) || (status == STATUS_SUCCESS));
677         }
678         if((dwTypeMask & VER_SERVICEPACKMINOR) && do_next_check)
679         {
680             status = version_compare_values(ver.wServicePackMinor, info->wServicePackMinor, condition);
681         }
682
683         if (status != STATUS_SUCCESS)
684             return status;
685     }
686
687     return STATUS_SUCCESS;
688 }