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