user32: Allow alt+space to highlight system menu.
[wine] / dlls / winmm / mci.c
1 /*
2  * MCI internal functions
3  *
4  * Copyright 1998/1999 Eric Pouech
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 /* TODO:
22  * - implement WINMM (32bit) multitasking and use it in all MCI drivers
23  *   instead of the home grown one 
24  * - 16bit mmTaskXXX functions are currently broken because the 16
25  *   loader does not support binary command lines => provide Wine's
26  *   own mmtask.tsk not using binary command line.
27  * - correctly handle the MCI_ALL_DEVICE_ID in functions.
28  * - finish mapping 16 <=> 32 of MCI structures and commands
29  * - implement auto-open feature (ie, when a string command is issued
30  *   for a not yet opened device, MCI automatically opens it) 
31  * - use a default registry setting to replace the [mci] section in
32  *   configuration file (layout of info in registry should be compatible
33  *   with all Windows' version - which use different layouts of course)
34  * - implement automatic open
35  *      + only works on string interface, on regular devices (don't work on all
36  *        nor custom devices)
37  * - command table handling isn't thread safe
38  */
39
40 /* to be cross checked:
41  * - heapalloc for *sizeof(WCHAR) when needed
42  * - size of string in WCHAR or bytes? (#chars for MCI_INFO, #bytes for MCI_SYSINFO)
43  */
44
45 #include "config.h"
46 #include "wine/port.h"
47
48 #include <stdlib.h>
49 #include <stdarg.h>
50 #include <stdio.h>
51 #include <string.h>
52
53 #include "windef.h"
54 #include "winbase.h"
55 #include "wingdi.h"
56 #include "winreg.h"
57 #include "mmsystem.h"
58 #include "winuser.h"
59 #include "winnls.h"
60 #include "winreg.h"
61 #include "wownt32.h"
62
63 #include "digitalv.h"
64 #include "winemm.h"
65
66 #include "wine/debug.h"
67 #include "wine/unicode.h"
68
69 WINE_DEFAULT_DEBUG_CHANNEL(mci);
70
71 WINMM_MapType  (*pFnMciMapMsg16To32W)  (WORD,WORD,DWORD,DWORD_PTR*) = NULL;
72 WINMM_MapType  (*pFnMciUnMapMsg16To32W)(WORD,WORD,DWORD,DWORD_PTR) = NULL;
73 WINMM_MapType  (*pFnMciMapMsg32WTo16)  (WORD,WORD,DWORD,DWORD_PTR*) = NULL;
74 WINMM_MapType  (*pFnMciUnMapMsg32WTo16)(WORD,WORD,DWORD,DWORD_PTR) = NULL;
75
76 /* First MCI valid device ID (0 means error) */
77 #define MCI_MAGIC 0x0001
78
79 /* MCI settings */
80 static const WCHAR wszHklmMci  [] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s',' ','N','T','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','M','C','I',0};
81 static const WCHAR wszNull     [] = {0};
82 static const WCHAR wszAll      [] = {'A','L','L',0};
83 static const WCHAR wszMci      [] = {'M','C','I',0};
84 static const WCHAR wszOpen     [] = {'o','p','e','n',0};
85 static const WCHAR wszSystemIni[] = {'s','y','s','t','e','m','.','i','n','i',0};
86
87 static WINE_MCIDRIVER *MciDrivers;
88
89 /* dup a string and uppercase it */
90 static inline LPWSTR str_dup_upper( LPCWSTR str )
91 {
92     INT len = (strlenW(str) + 1) * sizeof(WCHAR);
93     LPWSTR p = HeapAlloc( GetProcessHeap(), 0, len );
94     if (p)
95     {
96         memcpy( p, str, len );
97         CharUpperW( p );
98     }
99     return p;
100 }
101
102 /**************************************************************************
103  *                              MCI_GetDriver                   [internal]
104  */
105 LPWINE_MCIDRIVER        MCI_GetDriver(UINT16 wDevID)
106 {
107     LPWINE_MCIDRIVER    wmd = 0;
108
109     EnterCriticalSection(&WINMM_cs);
110     for (wmd = MciDrivers; wmd; wmd = wmd->lpNext) {
111         if (wmd->wDeviceID == wDevID)
112             break;
113     }
114     LeaveCriticalSection(&WINMM_cs);
115     return wmd;
116 }
117
118 /**************************************************************************
119  *                              MCI_GetDriverFromString         [internal]
120  */
121 UINT    MCI_GetDriverFromString(LPCWSTR lpstrName)
122 {
123     LPWINE_MCIDRIVER    wmd;
124     UINT                ret = 0;
125
126     if (!lpstrName)
127         return 0;
128
129     if (!strcmpiW(lpstrName, wszAll))
130         return MCI_ALL_DEVICE_ID;
131
132     EnterCriticalSection(&WINMM_cs);
133     for (wmd = MciDrivers; wmd; wmd = wmd->lpNext) {
134         if (wmd->lpstrElementName && strcmpW(wmd->lpstrElementName, lpstrName) == 0) {
135             ret = wmd->wDeviceID;
136             break;
137         }
138         if (wmd->lpstrDeviceType && strcmpiW(wmd->lpstrDeviceType, lpstrName) == 0) {
139             ret = wmd->wDeviceID;
140             break;
141         }
142         if (wmd->lpstrAlias && strcmpiW(wmd->lpstrAlias, lpstrName) == 0) {
143             ret = wmd->wDeviceID;
144             break;
145         }
146     }
147     LeaveCriticalSection(&WINMM_cs);
148
149     return ret;
150 }
151
152 /**************************************************************************
153  *                      MCI_MessageToString                     [internal]
154  */
155 const char* MCI_MessageToString(UINT wMsg)
156 {
157     static char buffer[100];
158
159 #define CASE(s) case (s): return #s
160
161     switch (wMsg) {
162         CASE(DRV_LOAD);
163         CASE(DRV_ENABLE);
164         CASE(DRV_OPEN);
165         CASE(DRV_CLOSE);
166         CASE(DRV_DISABLE);
167         CASE(DRV_FREE);
168         CASE(DRV_CONFIGURE);
169         CASE(DRV_QUERYCONFIGURE);
170         CASE(DRV_INSTALL);
171         CASE(DRV_REMOVE);
172         CASE(DRV_EXITSESSION);
173         CASE(DRV_EXITAPPLICATION);
174         CASE(DRV_POWER);
175         CASE(MCI_BREAK);
176         CASE(MCI_CLOSE);
177         CASE(MCI_CLOSE_DRIVER);
178         CASE(MCI_COPY);
179         CASE(MCI_CUE);
180         CASE(MCI_CUT);
181         CASE(MCI_DELETE);
182         CASE(MCI_ESCAPE);
183         CASE(MCI_FREEZE);
184         CASE(MCI_PAUSE);
185         CASE(MCI_PLAY);
186         CASE(MCI_GETDEVCAPS);
187         CASE(MCI_INFO);
188         CASE(MCI_LOAD);
189         CASE(MCI_OPEN);
190         CASE(MCI_OPEN_DRIVER);
191         CASE(MCI_PASTE);
192         CASE(MCI_PUT);
193         CASE(MCI_REALIZE);
194         CASE(MCI_RECORD);
195         CASE(MCI_RESUME);
196         CASE(MCI_SAVE);
197         CASE(MCI_SEEK);
198         CASE(MCI_SET);
199         CASE(MCI_SPIN);
200         CASE(MCI_STATUS);
201         CASE(MCI_STEP);
202         CASE(MCI_STOP);
203         CASE(MCI_SYSINFO);
204         CASE(MCI_UNFREEZE);
205         CASE(MCI_UPDATE);
206         CASE(MCI_WHERE);
207         CASE(MCI_WINDOW);
208         /* constants for digital video */
209         CASE(MCI_CAPTURE);
210         CASE(MCI_MONITOR);
211         CASE(MCI_RESERVE);
212         CASE(MCI_SETAUDIO);
213         CASE(MCI_SIGNAL);
214         CASE(MCI_SETVIDEO);
215         CASE(MCI_QUALITY);
216         CASE(MCI_LIST);
217         CASE(MCI_UNDO);
218         CASE(MCI_CONFIGURE);
219         CASE(MCI_RESTORE);
220 #undef CASE
221     default:
222         sprintf(buffer, "MCI_<<%04X>>", wMsg);
223         return buffer;
224     }
225 }
226
227 LPWSTR MCI_strdupAtoW( LPCSTR str )
228 {
229     LPWSTR ret;
230     INT len;
231
232     if (!str) return NULL;
233     len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
234     ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
235     if (ret) MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len );
236     return ret;
237 }
238
239 LPSTR MCI_strdupWtoA( LPCWSTR str )
240 {
241     LPSTR ret;
242     INT len;
243
244     if (!str) return NULL;
245     len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL );
246     ret = HeapAlloc( GetProcessHeap(), 0, len );
247     if (ret) WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
248     return ret;
249 }
250
251 static int MCI_MapMsgAtoW(UINT msg, DWORD_PTR dwParam1, DWORD_PTR *dwParam2)
252 {
253     if (msg < DRV_RESERVED) return 0;
254
255     switch (msg)
256     {
257     case MCI_CLOSE:
258     case MCI_CONFIGURE:
259     case MCI_PLAY:
260     case MCI_SEEK:
261     case MCI_STOP:
262     case MCI_PAUSE:
263     case MCI_GETDEVCAPS:
264     case MCI_SPIN:
265     case MCI_SET:
266     case MCI_STEP:
267     case MCI_RECORD:
268     case MCI_BREAK:
269     case MCI_SOUND:
270     case MCI_STATUS:
271     case MCI_CUE:
272     case MCI_REALIZE:
273     case MCI_PUT:
274     case MCI_WHERE:
275     case MCI_FREEZE:
276     case MCI_UNFREEZE:
277     case MCI_CUT:
278     case MCI_COPY:
279     case MCI_PASTE:
280     case MCI_UPDATE:
281     case MCI_RESUME:
282     case MCI_DELETE:
283     case MCI_MONITOR:
284     case MCI_SETAUDIO:
285     case MCI_SIGNAL:
286     case MCI_SETVIDEO:
287     case MCI_LIST:
288         return 0;
289
290     case MCI_OPEN:
291         {
292             MCI_OPEN_PARMSA *mci_openA = (MCI_OPEN_PARMSA*)*dwParam2;
293             MCI_OPEN_PARMSW *mci_openW;
294             DWORD_PTR *ptr;
295
296             ptr = HeapAlloc(GetProcessHeap(), 0, sizeof(DWORD_PTR) + sizeof(*mci_openW) + 2 * sizeof(DWORD));
297             if (!ptr) return -1;
298
299             *ptr++ = *dwParam2; /* save the previous pointer */
300             *dwParam2 = (DWORD_PTR)ptr;
301             mci_openW = (MCI_OPEN_PARMSW *)ptr;
302
303             if (dwParam1 & MCI_NOTIFY)
304                 mci_openW->dwCallback = mci_openA->dwCallback;
305
306             if (dwParam1 & MCI_OPEN_TYPE)
307             {
308                 if (dwParam1 & MCI_OPEN_TYPE_ID)
309                     mci_openW->lpstrDeviceType = (LPCWSTR)mci_openA->lpstrDeviceType;
310                 else
311                     mci_openW->lpstrDeviceType = MCI_strdupAtoW(mci_openA->lpstrDeviceType);
312             }
313             if (dwParam1 & MCI_OPEN_ELEMENT)
314             {
315                 if (dwParam1 & MCI_OPEN_ELEMENT_ID)
316                     mci_openW->lpstrElementName = (LPCWSTR)mci_openA->lpstrElementName;
317                 else
318                     mci_openW->lpstrElementName = MCI_strdupAtoW(mci_openA->lpstrElementName);
319             }
320             if (dwParam1 & MCI_OPEN_ALIAS)
321                 mci_openW->lpstrAlias = MCI_strdupAtoW(mci_openA->lpstrAlias);
322             /* FIXME: this is only needed for specific types of MCI devices, and
323              * may cause a segfault if the two DWORD:s don't exist at the end of 
324              * mci_openA
325              */
326             memcpy(mci_openW + 1, mci_openA + 1, 2 * sizeof(DWORD));
327         }
328         return 1;
329
330     case MCI_WINDOW:
331         if (dwParam1 & MCI_ANIM_WINDOW_TEXT)
332         {
333             MCI_ANIM_WINDOW_PARMSA *mci_windowA = (MCI_ANIM_WINDOW_PARMSA *)*dwParam2;
334             MCI_ANIM_WINDOW_PARMSW *mci_windowW;
335
336             mci_windowW = HeapAlloc(GetProcessHeap(), 0, sizeof(*mci_windowW));
337             if (!mci_windowW) return -1;
338
339             *dwParam2 = (DWORD_PTR)mci_windowW;
340
341             mci_windowW->lpstrText = MCI_strdupAtoW(mci_windowA->lpstrText);
342
343             if (dwParam1 & MCI_NOTIFY)
344                 mci_windowW->dwCallback = mci_windowA->dwCallback;
345             if (dwParam1 & MCI_ANIM_WINDOW_HWND)
346                 mci_windowW->hWnd = mci_windowA->hWnd;
347             if (dwParam1 & MCI_ANIM_WINDOW_STATE)
348                 mci_windowW->nCmdShow = mci_windowA->nCmdShow;
349
350             return 1;
351         }
352         return 0;
353
354     case MCI_SYSINFO:
355         {
356             MCI_SYSINFO_PARMSA *mci_sysinfoA = (MCI_SYSINFO_PARMSA *)*dwParam2;
357             MCI_SYSINFO_PARMSW *mci_sysinfoW;
358             DWORD_PTR *ptr;
359
360             ptr = HeapAlloc(GetProcessHeap(), 0, sizeof(*mci_sysinfoW) + sizeof(DWORD_PTR));
361             if (!ptr) return -1;
362
363             *ptr++ = *dwParam2; /* save the previous pointer */
364             *dwParam2 = (DWORD_PTR)ptr;
365             mci_sysinfoW = (MCI_SYSINFO_PARMSW *)ptr;
366
367             if (dwParam1 & MCI_NOTIFY)
368                 mci_sysinfoW->dwCallback = mci_sysinfoA->dwCallback;
369
370             mci_sysinfoW->dwRetSize = mci_sysinfoA->dwRetSize;
371             mci_sysinfoW->lpstrReturn = HeapAlloc(GetProcessHeap(), 0, mci_sysinfoW->dwRetSize);
372             mci_sysinfoW->dwNumber = mci_sysinfoA->dwNumber;
373             mci_sysinfoW->wDeviceType = mci_sysinfoA->wDeviceType;
374             return 1;
375         }
376     case MCI_INFO:
377         {
378             MCI_INFO_PARMSA *mci_infoA = (MCI_INFO_PARMSA *)*dwParam2;
379             MCI_INFO_PARMSW *mci_infoW;
380             DWORD_PTR *ptr;
381
382             ptr = HeapAlloc(GetProcessHeap(), 0, sizeof(*mci_infoW) + sizeof(DWORD_PTR));
383             if (!ptr) return -1;
384
385             *ptr++ = *dwParam2; /* save the previous pointer */
386             *dwParam2 = (DWORD_PTR)ptr;
387             mci_infoW = (MCI_INFO_PARMSW *)ptr;
388
389             if (dwParam1 & MCI_NOTIFY)
390                 mci_infoW->dwCallback = mci_infoA->dwCallback;
391
392             mci_infoW->dwRetSize = mci_infoA->dwRetSize * sizeof(WCHAR); /* it's not the same as SYSINFO !!! */
393             mci_infoW->lpstrReturn = HeapAlloc(GetProcessHeap(), 0, mci_infoW->dwRetSize);
394             return 1;
395         }
396     case MCI_SAVE:
397         {
398             MCI_SAVE_PARMSA *mci_saveA = (MCI_SAVE_PARMSA *)*dwParam2;
399             MCI_SAVE_PARMSW *mci_saveW;
400
401             mci_saveW = HeapAlloc(GetProcessHeap(), 0, sizeof(*mci_saveW));
402             if (!mci_saveW) return -1;
403
404             *dwParam2 = (DWORD_PTR)mci_saveW;
405             if (dwParam1 & MCI_NOTIFY)
406                 mci_saveW->dwCallback = mci_saveA->dwCallback;
407             mci_saveW->lpfilename = MCI_strdupAtoW(mci_saveA->lpfilename);
408             return 1;
409         }
410     case MCI_LOAD:
411         {
412             MCI_LOAD_PARMSA *mci_loadA = (MCI_LOAD_PARMSA *)*dwParam2;
413             MCI_LOAD_PARMSW *mci_loadW;
414
415             mci_loadW = HeapAlloc(GetProcessHeap(), 0, sizeof(*mci_loadW));
416             if (!mci_loadW) return -1;
417
418             *dwParam2 = (DWORD_PTR)mci_loadW;
419             if (dwParam1 & MCI_NOTIFY)
420                 mci_loadW->dwCallback = mci_loadA->dwCallback;
421             mci_loadW->lpfilename = MCI_strdupAtoW(mci_loadA->lpfilename);
422             return 1;
423         }
424
425     case MCI_ESCAPE:
426         {
427             MCI_VD_ESCAPE_PARMSA *mci_vd_escapeA = (MCI_VD_ESCAPE_PARMSA *)*dwParam2;
428             MCI_VD_ESCAPE_PARMSW *mci_vd_escapeW;
429
430             mci_vd_escapeW = HeapAlloc(GetProcessHeap(), 0, sizeof(*mci_vd_escapeW));
431             if (!mci_vd_escapeW) return -1;
432
433             *dwParam2 = (DWORD_PTR)mci_vd_escapeW;
434             if (dwParam1 & MCI_NOTIFY)
435                 mci_vd_escapeW->dwCallback = mci_vd_escapeA->dwCallback;
436             mci_vd_escapeW->lpstrCommand = MCI_strdupAtoW(mci_vd_escapeA->lpstrCommand);
437             return 1;
438         }
439     default:
440         FIXME("Message %s needs translation\n", MCI_MessageToString(msg));
441         return -1;
442     }
443 }
444
445 static DWORD MCI_UnmapMsgAtoW(UINT msg, DWORD_PTR dwParam1, DWORD_PTR dwParam2,
446                               DWORD result)
447 {
448     switch (msg)
449     {
450     case MCI_OPEN:
451         {
452             DWORD_PTR *ptr = (DWORD_PTR *)dwParam2 - 1;
453             MCI_OPEN_PARMSA *mci_openA = (MCI_OPEN_PARMSA *)*ptr;
454             MCI_OPEN_PARMSW *mci_openW = (MCI_OPEN_PARMSW *)(ptr + 1);
455
456             mci_openA->wDeviceID = mci_openW->wDeviceID;
457
458             if (dwParam1 & MCI_OPEN_TYPE)
459             {
460                 if (!(dwParam1 & MCI_OPEN_TYPE_ID))
461                     HeapFree(GetProcessHeap(), 0, (LPWSTR)mci_openW->lpstrDeviceType);
462             }
463             if (dwParam1 & MCI_OPEN_ELEMENT)
464             {
465                 if (!(dwParam1 & MCI_OPEN_ELEMENT_ID))
466                     HeapFree(GetProcessHeap(), 0, (LPWSTR)mci_openW->lpstrElementName);
467             }
468             if (dwParam1 & MCI_OPEN_ALIAS)
469                 HeapFree(GetProcessHeap(), 0, (LPWSTR)mci_openW->lpstrAlias);
470             HeapFree(GetProcessHeap(), 0, ptr);
471         }
472         break;
473     case MCI_WINDOW:
474         if (dwParam1 & MCI_ANIM_WINDOW_TEXT)
475         {
476             MCI_ANIM_WINDOW_PARMSW *mci_windowW = (MCI_ANIM_WINDOW_PARMSW *)dwParam2;
477
478             HeapFree(GetProcessHeap(), 0, (void*)mci_windowW->lpstrText);
479             HeapFree(GetProcessHeap(), 0, mci_windowW);
480         }
481         break;
482
483     case MCI_SYSINFO:
484         {
485             DWORD_PTR *ptr = (DWORD_PTR *)dwParam2 - 1;
486             MCI_SYSINFO_PARMSA *mci_sysinfoA = (MCI_SYSINFO_PARMSA *)*ptr;
487             MCI_SYSINFO_PARMSW *mci_sysinfoW = (MCI_SYSINFO_PARMSW *)(ptr + 1);
488
489             if (!result)
490             {
491                 mci_sysinfoA->dwNumber = mci_sysinfoW->dwNumber;
492                 mci_sysinfoA->wDeviceType = mci_sysinfoW->wDeviceType;
493                 if (dwParam1 & MCI_SYSINFO_QUANTITY)
494                     *(DWORD*)mci_sysinfoA->lpstrReturn = *(DWORD*)mci_sysinfoW->lpstrReturn;
495                 else
496                     WideCharToMultiByte(CP_ACP, 0,
497                                         mci_sysinfoW->lpstrReturn, mci_sysinfoW->dwRetSize,
498                                         mci_sysinfoA->lpstrReturn, mci_sysinfoA->dwRetSize,
499                                         NULL, NULL);
500             }
501
502             HeapFree(GetProcessHeap(), 0, mci_sysinfoW->lpstrReturn);
503             HeapFree(GetProcessHeap(), 0, ptr);
504         }
505         break;
506     case MCI_INFO:
507         {
508             DWORD_PTR *ptr = (DWORD_PTR *)dwParam2 - 1;
509             MCI_INFO_PARMSA *mci_infoA = (MCI_INFO_PARMSA *)*ptr;
510             MCI_INFO_PARMSW *mci_infoW = (MCI_INFO_PARMSW *)(ptr + 1);
511
512             if (!result)
513             {
514                 WideCharToMultiByte(CP_ACP, 0,
515                                     mci_infoW->lpstrReturn, mci_infoW->dwRetSize / sizeof(WCHAR),
516                                     mci_infoA->lpstrReturn, mci_infoA->dwRetSize,
517                                     NULL, NULL);
518             }
519
520             HeapFree(GetProcessHeap(), 0, mci_infoW->lpstrReturn);
521             HeapFree(GetProcessHeap(), 0, ptr);
522         }
523         break;
524     case MCI_SAVE:
525         {
526             MCI_SAVE_PARMSW *mci_saveW = (MCI_SAVE_PARMSW *)dwParam2;
527
528             HeapFree(GetProcessHeap(), 0, (void*)mci_saveW->lpfilename);
529             HeapFree(GetProcessHeap(), 0, mci_saveW);
530         }
531         break;
532     case MCI_LOAD:
533         {
534             MCI_LOAD_PARMSW *mci_loadW = (MCI_LOAD_PARMSW *)dwParam2;
535
536             HeapFree(GetProcessHeap(), 0, (void*)mci_loadW->lpfilename);
537             HeapFree(GetProcessHeap(), 0, mci_loadW);
538         }
539         break;
540     case MCI_ESCAPE:
541         {
542             MCI_VD_ESCAPE_PARMSW *mci_vd_escapeW = (MCI_VD_ESCAPE_PARMSW *)dwParam2;
543
544             HeapFree(GetProcessHeap(), 0, (void*)mci_vd_escapeW->lpstrCommand);
545             HeapFree(GetProcessHeap(), 0, mci_vd_escapeW);
546         }
547         break;
548
549     default:
550         FIXME("Message %s needs unmapping\n", MCI_MessageToString(msg));
551         break;
552     }
553
554     return result;
555 }
556
557 /**************************************************************************
558  *                              MCI_GetDevTypeFromFileName      [internal]
559  */
560 static  DWORD   MCI_GetDevTypeFromFileName(LPCWSTR fileName, LPWSTR buf, UINT len)
561 {
562     LPCWSTR     tmp;
563     HKEY        hKey;
564     static const WCHAR keyW[] = {'S','O','F','T','W','A','R','E','\\','M','i','c','r','o','s','o','f','t','\\',
565                                  'W','i','n','d','o','w','s',' ','N','T','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
566                                  'M','C','I',' ','E','x','t','e','n','s','i','o','n','s',0};
567     if ((tmp = strrchrW(fileName, '.'))) {
568         if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, keyW,
569                            0, KEY_QUERY_VALUE, &hKey ) == ERROR_SUCCESS) {
570             DWORD dwLen = len;
571             LONG lRet = RegQueryValueExW( hKey, tmp + 1, 0, 0, (void*)buf, &dwLen ); 
572             RegCloseKey( hKey );
573             if (lRet == ERROR_SUCCESS) return 0;
574         }
575         TRACE("No ...\\MCI Extensions entry for %s found.\n", debugstr_w(tmp));
576     }
577     return MCIERR_EXTENSION_NOT_FOUND;
578 }
579
580 #define MAX_MCICMDTABLE                 20
581 #define MCI_COMMAND_TABLE_NOT_LOADED    0xFFFE
582
583 typedef struct tagWINE_MCICMDTABLE {
584     UINT                uDevType;
585     const BYTE*         lpTable;
586     UINT                nVerbs;         /* number of verbs in command table */
587     LPCWSTR*            aVerbs;         /* array of verbs to speed up the verb look up process */
588 } WINE_MCICMDTABLE, *LPWINE_MCICMDTABLE;
589
590 static WINE_MCICMDTABLE S_MciCmdTable[MAX_MCICMDTABLE];
591
592 /**************************************************************************
593  *                              MCI_IsCommandTableValid         [internal]
594  */
595 static  BOOL            MCI_IsCommandTableValid(UINT uTbl)
596 {
597     const BYTE* lmem;
598     LPCWSTR     str;
599     DWORD       flg;
600     WORD        eid;
601     int         idx = 0;
602     BOOL        inCst = FALSE;
603
604     TRACE("Dumping cmdTbl=%d [lpTable=%p devType=%d]\n",
605           uTbl, S_MciCmdTable[uTbl].lpTable, S_MciCmdTable[uTbl].uDevType);
606
607     if (uTbl >= MAX_MCICMDTABLE || !S_MciCmdTable[uTbl].lpTable)
608         return FALSE;
609
610     lmem = S_MciCmdTable[uTbl].lpTable;
611     do {
612         str = (LPCWSTR)lmem;
613         lmem += (strlenW(str) + 1) * sizeof(WCHAR);
614         flg = *(const DWORD*)lmem;
615         eid = *(const WORD*)(lmem + sizeof(DWORD));
616         lmem += sizeof(DWORD) + sizeof(WORD);
617         idx ++;
618         /* TRACE("cmd=%s %08lx %04x\n", debugstr_w(str), flg, eid); */
619         switch (eid) {
620         case MCI_COMMAND_HEAD:          if (!*str || !flg) return FALSE; idx = 0;               break;  /* check unicity of str in table */
621         case MCI_STRING:                if (inCst) return FALSE;                                break;
622         case MCI_INTEGER:               if (!*str) return FALSE;                                break;
623         case MCI_END_COMMAND:           if (*str || flg || idx == 0) return FALSE; idx = 0;     break;
624         case MCI_RETURN:                if (*str || idx != 1) return FALSE;                     break;
625         case MCI_FLAG:                  if (!*str) return FALSE;                                break;
626         case MCI_END_COMMAND_LIST:      if (*str || flg) return FALSE;  idx = 0;                break;
627         case MCI_RECT:                  if (!*str || inCst) return FALSE;                       break;
628         case MCI_CONSTANT:              if (inCst) return FALSE; inCst = TRUE;                  break;
629         case MCI_END_CONSTANT:          if (*str || flg || !inCst) return FALSE; inCst = FALSE; break;
630         default:                        return FALSE;
631         }
632     } while (eid != MCI_END_COMMAND_LIST);
633     return TRUE;
634 }
635
636 /**************************************************************************
637  *                              MCI_DumpCommandTable            [internal]
638  */
639 static  BOOL            MCI_DumpCommandTable(UINT uTbl)
640 {
641     const BYTE* lmem;
642     LPCWSTR     str;
643     DWORD       flg;
644     WORD        eid;
645
646     if (!MCI_IsCommandTableValid(uTbl)) {
647         ERR("Ooops: %d is not valid\n", uTbl);
648         return FALSE;
649     }
650
651     lmem = S_MciCmdTable[uTbl].lpTable;
652     do {
653         do {
654             str = (LPCWSTR)lmem;
655             lmem += (strlenW(str) + 1) * sizeof(WCHAR);
656             flg = *(const DWORD*)lmem;
657             eid = *(const WORD*)(lmem + sizeof(DWORD));
658             /* TRACE("cmd=%s %08lx %04x\n", debugstr_w(str), flg, eid); */
659             lmem += sizeof(DWORD) + sizeof(WORD);
660         } while (eid != MCI_END_COMMAND && eid != MCI_END_COMMAND_LIST);
661         /* EPP TRACE(" => end of command%s\n", (eid == MCI_END_COMMAND_LIST) ? " list" : ""); */
662     } while (eid != MCI_END_COMMAND_LIST);
663     return TRUE;
664 }
665
666
667 /**************************************************************************
668  *                              MCI_GetCommandTable             [internal]
669  */
670 static  UINT            MCI_GetCommandTable(UINT uDevType)
671 {
672     UINT        uTbl;
673     WCHAR       buf[32];
674     LPCWSTR     str = NULL;
675
676     /* first look up existing for existing devType */
677     for (uTbl = 0; uTbl < MAX_MCICMDTABLE; uTbl++) {
678         if (S_MciCmdTable[uTbl].lpTable && S_MciCmdTable[uTbl].uDevType == uDevType)
679             return uTbl;
680     }
681
682     /* well try to load id */
683     if (uDevType >= MCI_DEVTYPE_FIRST && uDevType <= MCI_DEVTYPE_LAST) {
684         if (LoadStringW(hWinMM32Instance, uDevType, buf, sizeof(buf) / sizeof(WCHAR))) {
685             str = buf;
686         }
687     } else if (uDevType == 0) {
688         static const WCHAR wszCore[] = {'C','O','R','E',0};
689         str = wszCore;
690     }
691     uTbl = MCI_NO_COMMAND_TABLE;
692     if (str) {
693         HRSRC   hRsrc = FindResourceW(hWinMM32Instance, str, (LPCWSTR)RT_RCDATA);
694         HANDLE  hMem = 0;
695
696         if (hRsrc) hMem = LoadResource(hWinMM32Instance, hRsrc);
697         if (hMem) {
698             uTbl = MCI_SetCommandTable(LockResource(hMem), uDevType);
699         } else {
700             WARN("No command table found in resource %p[%s]\n",
701                  hWinMM32Instance, debugstr_w(str));
702         }
703     }
704     TRACE("=> %d\n", uTbl);
705     return uTbl;
706 }
707
708 /**************************************************************************
709  *                              MCI_SetCommandTable             [internal]
710  */
711 UINT MCI_SetCommandTable(void *table, UINT uDevType)
712 {
713     int                 uTbl;
714     static      BOOL    bInitDone = FALSE;
715
716     /* <HACK>
717      * The CORE command table must be loaded first, so that MCI_GetCommandTable()
718      * can be called with 0 as a uDevType to retrieve it.
719      * </HACK>
720      */
721     if (!bInitDone) {
722         bInitDone = TRUE;
723         MCI_GetCommandTable(0);
724     }
725     TRACE("(%p, %u)\n", table, uDevType);
726     for (uTbl = 0; uTbl < MAX_MCICMDTABLE; uTbl++) {
727         if (!S_MciCmdTable[uTbl].lpTable) {
728             const BYTE* lmem;
729             LPCWSTR     str;
730             WORD        eid;
731             WORD        count;
732
733             S_MciCmdTable[uTbl].uDevType = uDevType;
734             S_MciCmdTable[uTbl].lpTable = table;
735
736             if (TRACE_ON(mci)) {
737                 MCI_DumpCommandTable(uTbl);
738             }
739
740             /* create the verbs table */
741             /* get # of entries */
742             lmem = S_MciCmdTable[uTbl].lpTable;
743             count = 0;
744             do {
745                 str = (LPCWSTR)lmem;
746                 lmem += (strlenW(str) + 1) * sizeof(WCHAR);
747                 eid = *(const WORD*)(lmem + sizeof(DWORD));
748                 lmem += sizeof(DWORD) + sizeof(WORD);
749                 if (eid == MCI_COMMAND_HEAD)
750                     count++;
751             } while (eid != MCI_END_COMMAND_LIST);
752
753             S_MciCmdTable[uTbl].aVerbs = HeapAlloc(GetProcessHeap(), 0, count * sizeof(LPCWSTR));
754             S_MciCmdTable[uTbl].nVerbs = count;
755
756             lmem = S_MciCmdTable[uTbl].lpTable;
757             count = 0;
758             do {
759                 str = (LPCWSTR)lmem;
760                 lmem += (strlenW(str) + 1) * sizeof(WCHAR);
761                 eid = *(const WORD*)(lmem + sizeof(DWORD));
762                 lmem += sizeof(DWORD) + sizeof(WORD);
763                 if (eid == MCI_COMMAND_HEAD)
764                     S_MciCmdTable[uTbl].aVerbs[count++] = str;
765             } while (eid != MCI_END_COMMAND_LIST);
766             /* assert(count == S_MciCmdTable[uTbl].nVerbs); */
767             return uTbl;
768         }
769     }
770
771     return MCI_NO_COMMAND_TABLE;
772 }
773
774 /**************************************************************************
775  *                              MCI_DeleteCommandTable          [internal]
776  */
777 BOOL    MCI_DeleteCommandTable(UINT uTbl, BOOL delete)
778 {
779     if (uTbl >= MAX_MCICMDTABLE || !S_MciCmdTable[uTbl].lpTable)
780         return FALSE;
781
782     if (delete) HeapFree(GetProcessHeap(), 0, (void*)S_MciCmdTable[uTbl].lpTable);
783     S_MciCmdTable[uTbl].lpTable = NULL;
784     HeapFree(GetProcessHeap(), 0, S_MciCmdTable[uTbl].aVerbs);
785     S_MciCmdTable[uTbl].aVerbs = 0;
786     return TRUE;
787 }
788
789 /**************************************************************************
790  *                              MCI_UnLoadMciDriver             [internal]
791  */
792 static  BOOL    MCI_UnLoadMciDriver(LPWINE_MCIDRIVER wmd)
793 {
794     LPWINE_MCIDRIVER*           tmp;
795
796     if (!wmd)
797         return TRUE;
798
799     CloseDriver(wmd->hDriver, 0, 0);
800
801     if (wmd->dwPrivate != 0)
802         WARN("Unloading mci driver with non nul dwPrivate field\n");
803
804     EnterCriticalSection(&WINMM_cs);
805     for (tmp = &MciDrivers; *tmp; tmp = &(*tmp)->lpNext) {
806         if (*tmp == wmd) {
807             *tmp = wmd->lpNext;
808             break;
809         }
810     }
811     LeaveCriticalSection(&WINMM_cs);
812
813     HeapFree(GetProcessHeap(), 0, wmd->lpstrDeviceType);
814     HeapFree(GetProcessHeap(), 0, wmd->lpstrAlias);
815     HeapFree(GetProcessHeap(), 0, wmd->lpstrElementName);
816
817     HeapFree(GetProcessHeap(), 0, wmd);
818     return TRUE;
819 }
820
821 /**************************************************************************
822  *                              MCI_OpenMciDriver               [internal]
823  */
824 static  BOOL    MCI_OpenMciDriver(LPWINE_MCIDRIVER wmd, LPCWSTR drvTyp, DWORD_PTR lp)
825 {
826     WCHAR       libName[128];
827
828     if (!DRIVER_GetLibName(drvTyp, wszMci, libName, sizeof(libName)))
829         return FALSE;
830
831     wmd->bIs32 = 0xFFFF;
832     /* First load driver */
833     if ((wmd->hDriver = (HDRVR)DRIVER_TryOpenDriver32(libName, lp))) {
834         wmd->bIs32 = TRUE;
835     } else if (WINMM_CheckForMMSystem() && pFnMciMapMsg32WTo16) {
836         WINMM_MapType   res;
837
838         switch (res = pFnMciMapMsg32WTo16(0, DRV_OPEN, 0, &lp)) {
839         case WINMM_MAP_MSGERROR:
840             TRACE("Not handled yet (DRV_OPEN)\n");
841             break;
842         case WINMM_MAP_NOMEM:
843             TRACE("Problem mapping msg=DRV_OPEN from 32W to 16\n");
844             break;
845         case WINMM_MAP_OK:
846         case WINMM_MAP_OKMEM:
847             if ((wmd->hDriver = OpenDriver(drvTyp, wszMci, lp)))
848                 wmd->bIs32 = FALSE;
849             if (res == WINMM_MAP_OKMEM)
850                 pFnMciUnMapMsg32WTo16(0, DRV_OPEN, 0, lp);
851             break;
852         }
853     }
854     return (wmd->bIs32 == 0xFFFF) ? FALSE : TRUE;
855 }
856
857 /**************************************************************************
858  *                              MCI_LoadMciDriver               [internal]
859  */
860 static  DWORD   MCI_LoadMciDriver(LPCWSTR _strDevTyp, LPWINE_MCIDRIVER* lpwmd)
861 {
862     LPWSTR                      strDevTyp = str_dup_upper(_strDevTyp);
863     LPWINE_MCIDRIVER            wmd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wmd));
864     MCI_OPEN_DRIVER_PARMSW      modp;
865     DWORD                       dwRet = 0;
866
867     if (!wmd || !strDevTyp) {
868         dwRet = MCIERR_OUT_OF_MEMORY;
869         goto errCleanUp;
870     }
871
872     wmd->lpfnYieldProc = MCI_DefYieldProc;
873     wmd->dwYieldData = VK_CANCEL;
874     wmd->CreatorThread = GetCurrentThreadId();
875
876     EnterCriticalSection(&WINMM_cs);
877     /* wmd must be inserted in list before sending opening the driver, coz' it
878      * may want to lookup at wDevID
879      */
880     wmd->lpNext = MciDrivers;
881     MciDrivers = wmd;
882
883     for (modp.wDeviceID = MCI_MAGIC;
884          MCI_GetDriver(modp.wDeviceID) != 0;
885          modp.wDeviceID++);
886
887     wmd->wDeviceID = modp.wDeviceID;
888
889     LeaveCriticalSection(&WINMM_cs);
890
891     TRACE("wDevID=%04X\n", modp.wDeviceID);
892
893     modp.lpstrParams = NULL;
894
895     if (!MCI_OpenMciDriver(wmd, strDevTyp, (DWORD_PTR)&modp)) {
896         /* silence warning if all is used... some bogus program use commands like
897          * 'open all'...
898          */
899         if (strcmpiW(strDevTyp, wszAll) == 0) {
900             dwRet = MCIERR_CANNOT_USE_ALL;
901         } else {
902             FIXME("Couldn't load driver for type %s.\n"
903                   "If you don't have a windows installation accessible from Wine,\n"
904                   "you perhaps forgot to create a [mci] section in system.ini\n",
905                   debugstr_w(strDevTyp));
906             dwRet = MCIERR_DEVICE_NOT_INSTALLED;
907         }
908         goto errCleanUp;
909     }
910
911     /* FIXME: should also check that module's description is of the form
912      * MODULENAME:[MCI] comment
913      */
914
915     /* some drivers will return 0x0000FFFF, some others 0xFFFFFFFF */
916     wmd->uSpecificCmdTable = LOWORD(modp.wCustomCommandTable);
917     wmd->uTypeCmdTable = MCI_COMMAND_TABLE_NOT_LOADED;
918
919     TRACE("Loaded driver %p (%s), type is %d, cmdTable=%08x\n",
920           wmd->hDriver, debugstr_w(strDevTyp), modp.wType, modp.wCustomCommandTable);
921
922     wmd->lpstrDeviceType = strDevTyp;
923     wmd->wType = modp.wType;
924
925     TRACE("mcidev=%d, uDevTyp=%04X wDeviceID=%04X !\n",
926           modp.wDeviceID, modp.wType, modp.wDeviceID);
927     *lpwmd = wmd;
928     return 0;
929 errCleanUp:
930     MCI_UnLoadMciDriver(wmd);
931     HeapFree(GetProcessHeap(), 0, strDevTyp);
932     *lpwmd = 0;
933     return dwRet;
934 }
935
936 /**************************************************************************
937  *                      MCI_FinishOpen                          [internal]
938  */
939 static  DWORD   MCI_FinishOpen(LPWINE_MCIDRIVER wmd, LPMCI_OPEN_PARMSW lpParms,
940                                DWORD dwParam)
941 {
942     if (dwParam & MCI_OPEN_ELEMENT)
943     {
944         wmd->lpstrElementName = HeapAlloc(GetProcessHeap(),0,(strlenW(lpParms->lpstrElementName)+1) * sizeof(WCHAR));
945         strcpyW( wmd->lpstrElementName, lpParms->lpstrElementName );
946     }
947     if (dwParam & MCI_OPEN_ALIAS)
948     {
949         wmd->lpstrAlias = HeapAlloc(GetProcessHeap(), 0, (strlenW(lpParms->lpstrAlias)+1) * sizeof(WCHAR));
950         strcpyW( wmd->lpstrAlias, lpParms->lpstrAlias);
951     }
952     lpParms->wDeviceID = wmd->wDeviceID;
953
954     return MCI_SendCommandFrom32(wmd->wDeviceID, MCI_OPEN_DRIVER, dwParam,
955                                  (DWORD)lpParms);
956 }
957
958 /**************************************************************************
959  *                              MCI_FindCommand         [internal]
960  */
961 static  LPCWSTR         MCI_FindCommand(UINT uTbl, LPCWSTR verb)
962 {
963     UINT        idx;
964
965     if (uTbl >= MAX_MCICMDTABLE || !S_MciCmdTable[uTbl].lpTable)
966         return NULL;
967
968     /* another improvement would be to have the aVerbs array sorted,
969      * so that we could use a dichotomic search on it, rather than this dumb
970      * array look up
971      */
972     for (idx = 0; idx < S_MciCmdTable[uTbl].nVerbs; idx++) {
973         if (strcmpiW(S_MciCmdTable[uTbl].aVerbs[idx], verb) == 0)
974             return S_MciCmdTable[uTbl].aVerbs[idx];
975     }
976
977     return NULL;
978 }
979
980 /**************************************************************************
981  *                              MCI_GetReturnType               [internal]
982  */
983 static  DWORD           MCI_GetReturnType(LPCWSTR lpCmd)
984 {
985     lpCmd = (LPCWSTR)((const BYTE*)(lpCmd + strlenW(lpCmd) + 1) + sizeof(DWORD) + sizeof(WORD));
986     if (*lpCmd == '\0' && *(const WORD*)((const BYTE*)(lpCmd + 1) + sizeof(DWORD)) == MCI_RETURN) {
987         return *(const DWORD*)(lpCmd + 1);
988     }
989     return 0L;
990 }
991
992 /**************************************************************************
993  *                              MCI_GetMessage                  [internal]
994  */
995 static  WORD            MCI_GetMessage(LPCWSTR lpCmd)
996 {
997     return (WORD)*(const DWORD*)(lpCmd + strlenW(lpCmd) + 1);
998 }
999
1000 /**************************************************************************
1001  *                              MCI_GetDWord                    [internal]
1002  */
1003 static  BOOL            MCI_GetDWord(LPDWORD data, LPWSTR* ptr)
1004 {
1005     DWORD       val;
1006     LPWSTR      ret;
1007
1008     val = strtoulW(*ptr, &ret, 0);
1009
1010     switch (*ret) {
1011     case '\0':  break;
1012     case ' ':   ret++; break;
1013     default:    return FALSE;
1014     }
1015
1016     *data |= val;
1017     *ptr = ret;
1018     return TRUE;
1019 }
1020
1021 /**************************************************************************
1022  *                              MCI_GetString           [internal]
1023  */
1024 static  DWORD   MCI_GetString(LPWSTR* str, LPWSTR* args)
1025 {
1026     LPWSTR      ptr = *args;
1027
1028     /* see if we have a quoted string */
1029     if (*ptr == '"') {
1030         ptr = strchrW(*str = ptr + 1, '"');
1031         if (!ptr) return MCIERR_NO_CLOSING_QUOTE;
1032         /* FIXME: shall we escape \" from string ?? */
1033         if (ptr[-1] == '\\') TRACE("Ooops: un-escaped \"\n");
1034         *ptr++ = '\0'; /* remove trailing " */
1035         if (*ptr != ' ' && *ptr != '\0') return MCIERR_EXTRA_CHARACTERS;
1036     } else {
1037         ptr = strchrW(ptr, ' ');
1038
1039         if (ptr) {
1040             *ptr++ = '\0';
1041         } else {
1042             ptr = *args + strlenW(*args);
1043         }
1044         *str = *args;
1045     }
1046
1047     *args = ptr;
1048     return 0;
1049 }
1050
1051 #define MCI_DATA_SIZE   16
1052
1053 /**************************************************************************
1054  *                              MCI_ParseOptArgs                [internal]
1055  */
1056 static  DWORD   MCI_ParseOptArgs(LPDWORD data, int _offset, LPCWSTR lpCmd,
1057                                  LPWSTR args, LPDWORD dwFlags)
1058 {
1059     int         len, offset;
1060     const char* lmem;
1061     LPCWSTR     str;
1062     DWORD       dwRet, flg, cflg = 0;
1063     WORD        eid;
1064     BOOL        inCst, found;
1065
1066     /* loop on arguments */
1067     while (*args) {
1068         lmem = (const char*)lpCmd;
1069         found = inCst = FALSE;
1070         offset = _offset;
1071
1072         /* skip any leading white space(s) */
1073         while (*args == ' ') args++;
1074         TRACE("args=%s offset=%d\n", debugstr_w(args), offset);
1075
1076         do { /* loop on options for command table for the requested verb */
1077             str = (LPCWSTR)lmem;
1078             lmem += ((len = strlenW(str)) + 1) * sizeof(WCHAR);
1079             flg = *(const DWORD*)lmem;
1080             eid = *(const WORD*)(lmem + sizeof(DWORD));
1081             lmem += sizeof(DWORD) + sizeof(WORD);
1082             /* TRACE("\tcmd=%s inCst=%c eid=%04x\n", debugstr_w(str), inCst ? 'Y' : 'N', eid); */
1083
1084             switch (eid) {
1085             case MCI_CONSTANT:
1086                 inCst = TRUE;   cflg = flg;     break;
1087             case MCI_END_CONSTANT:
1088                 /* there may be additional integral values after flag in constant */
1089                 if (inCst && MCI_GetDWord(&(data[offset]), &args)) {
1090                     *dwFlags |= cflg;
1091                 }
1092                 inCst = FALSE;  cflg = 0;
1093                 break;
1094             }
1095
1096             if (strncmpiW(args, str, len) == 0 &&
1097                 ((eid == MCI_STRING && len == 0) || args[len] == 0 || args[len] == ' ')) {
1098                 /* store good values into data[] */
1099                 args += len;
1100                 while (*args == ' ') args++;
1101                 found = TRUE;
1102
1103                 switch (eid) {
1104                 case MCI_COMMAND_HEAD:
1105                 case MCI_RETURN:
1106                 case MCI_END_COMMAND:
1107                 case MCI_END_COMMAND_LIST:
1108                 case MCI_CONSTANT:      /* done above */
1109                 case MCI_END_CONSTANT:  /* done above */
1110                     break;
1111                 case MCI_FLAG:
1112                     *dwFlags |= flg;
1113                     break;
1114                 case MCI_INTEGER:
1115                     if (inCst) {
1116                         data[offset] |= flg;
1117                         *dwFlags |= cflg;
1118                         inCst = FALSE;
1119                     } else {
1120                         *dwFlags |= flg;
1121                         if (!MCI_GetDWord(&(data[offset]), &args)) {
1122                             return MCIERR_BAD_INTEGER;
1123                         }
1124                     }
1125                     break;
1126                 case MCI_RECT:
1127                     /* store rect in data (offset...offset+3) */
1128                     *dwFlags |= flg;
1129                     if (!MCI_GetDWord(&(data[offset+0]), &args) ||
1130                         !MCI_GetDWord(&(data[offset+1]), &args) ||
1131                         !MCI_GetDWord(&(data[offset+2]), &args) ||
1132                         !MCI_GetDWord(&(data[offset+3]), &args)) {
1133                         ERR("Bad rect %s\n", debugstr_w(args));
1134                         return MCIERR_BAD_INTEGER;
1135                     }
1136                     break;
1137                 case MCI_STRING:
1138                     *dwFlags |= flg;
1139                     if ((dwRet = MCI_GetString((LPWSTR*)&data[offset], &args)))
1140                         return dwRet;
1141                     break;
1142                 default:        ERR("oops\n");
1143                 }
1144                 /* exit inside while loop, except if just entered in constant area definition */
1145                 if (!inCst || eid != MCI_CONSTANT) eid = MCI_END_COMMAND;
1146             } else {
1147                 /* have offset incremented if needed */
1148                 switch (eid) {
1149                 case MCI_COMMAND_HEAD:
1150                 case MCI_RETURN:
1151                 case MCI_END_COMMAND:
1152                 case MCI_END_COMMAND_LIST:
1153                 case MCI_CONSTANT:
1154                 case MCI_FLAG:                  break;
1155                 case MCI_INTEGER:               if (!inCst) offset++;   break;
1156                 case MCI_END_CONSTANT:
1157                 case MCI_STRING:                offset++; break;
1158                 case MCI_RECT:                  offset += 4; break;
1159                 default:                        ERR("oops\n");
1160                 }
1161             }
1162         } while (eid != MCI_END_COMMAND);
1163         if (!found) {
1164             WARN("Optarg %s not found\n", debugstr_w(args));
1165             return MCIERR_UNRECOGNIZED_COMMAND;
1166         }
1167         if (offset == MCI_DATA_SIZE) {
1168             ERR("Internal data[] buffer overflow\n");
1169             return MCIERR_PARSER_INTERNAL;
1170         }
1171     }
1172     return 0;
1173 }
1174
1175 /**************************************************************************
1176  *                              MCI_HandleReturnValues  [internal]
1177  */
1178 static  DWORD   MCI_HandleReturnValues(DWORD dwRet, LPWINE_MCIDRIVER wmd, DWORD retType, 
1179                                        LPDWORD data, LPWSTR lpstrRet, UINT uRetLen)
1180 {
1181     static const WCHAR wszLd  [] = {'%','l','d',0};
1182     static const WCHAR wszLd4 [] = {'%','l','d',' ','%','l','d',' ','%','l','d',' ','%','l','d',0};
1183     static const WCHAR wszCol3[] = {'%','d',':','%','d',':','%','d',0};
1184     static const WCHAR wszCol4[] = {'%','d',':','%','d',':','%','d',':','%','d',0};
1185
1186     if (lpstrRet) {
1187         switch (retType) {
1188         case 0: /* nothing to return */
1189             break;
1190         case MCI_INTEGER:
1191             switch (dwRet & 0xFFFF0000ul) {
1192             case 0:
1193             case MCI_INTEGER_RETURNED:
1194                 snprintfW(lpstrRet, uRetLen, wszLd, data[1]);
1195                 break;
1196             case MCI_RESOURCE_RETURNED:
1197                 /* return string which ID is HIWORD(data[1]),
1198                  * string is loaded from mmsystem.dll */
1199                 LoadStringW(hWinMM32Instance, HIWORD(data[1]), lpstrRet, uRetLen);
1200                 break;
1201             case MCI_RESOURCE_RETURNED|MCI_RESOURCE_DRIVER:
1202                 /* return string which ID is HIWORD(data[1]),
1203                  * string is loaded from driver */
1204                 /* FIXME: this is wrong for a 16 bit handle */
1205                 LoadStringW(GetDriverModuleHandle(wmd->hDriver),
1206                             HIWORD(data[1]), lpstrRet, uRetLen);
1207                 break;
1208             case MCI_COLONIZED3_RETURN:
1209                 snprintfW(lpstrRet, uRetLen, wszCol3,
1210                           LOBYTE(LOWORD(data[1])), HIBYTE(LOWORD(data[1])),
1211                           LOBYTE(HIWORD(data[1])));
1212                 break;
1213             case MCI_COLONIZED4_RETURN:
1214                 snprintfW(lpstrRet, uRetLen, wszCol4,
1215                           LOBYTE(LOWORD(data[1])), HIBYTE(LOWORD(data[1])),
1216                           LOBYTE(HIWORD(data[1])), HIBYTE(HIWORD(data[1])));
1217                 break;
1218             default:    ERR("Ooops (%04X)\n", HIWORD(dwRet));
1219             }
1220             break;
1221         case MCI_STRING:
1222             switch (dwRet & 0xFFFF0000ul) {
1223             case 0:
1224                 /* nothing to do data[1] == lpstrRet */
1225                 break;
1226             case MCI_INTEGER_RETURNED:
1227                 data[1] = *(LPDWORD)lpstrRet;
1228                 snprintfW(lpstrRet, uRetLen, wszLd, data[1]);
1229                 break;
1230             default:
1231                 WARN("Oooch. MCI_STRING and HIWORD(dwRet)=%04x\n", HIWORD(dwRet));
1232                 break;
1233             }
1234             break;
1235         case MCI_RECT:
1236             if (dwRet & 0xFFFF0000ul)
1237                 WARN("Oooch. MCI_STRING and HIWORD(dwRet)=%04x\n", HIWORD(dwRet));
1238             snprintfW(lpstrRet, uRetLen, wszLd4,
1239                       data[1], data[2], data[3], data[4]);
1240             break;
1241         default:                ERR("oops\n");
1242         }
1243     }
1244     return LOWORD(dwRet);
1245 }
1246
1247 /**************************************************************************
1248  *                              mciSendStringW          [WINMM.@]
1249  */
1250 DWORD WINAPI mciSendStringW(LPCWSTR lpstrCommand, LPWSTR lpstrRet,
1251                             UINT uRetLen, HWND hwndCallback)
1252 {
1253     LPWSTR              verb, dev, args;
1254     LPWINE_MCIDRIVER    wmd = 0;
1255     DWORD               dwFlags = 0, dwRet = 0;
1256     int                 offset = 0;
1257     DWORD               data[MCI_DATA_SIZE];
1258     DWORD               retType;
1259     LPCWSTR             lpCmd = 0;
1260     LPWSTR              devAlias = NULL;
1261     static const WCHAR  wszNew[] = {'n','e','w',0};
1262     static const WCHAR  wszSAliasS[] = {' ','a','l','i','a','s',' ',0};
1263     static const WCHAR wszTypeS[] = {'t','y','p','e',' ',0};
1264
1265     TRACE("(%s, %p, %d, %p)\n", 
1266           debugstr_w(lpstrCommand), lpstrRet, uRetLen, hwndCallback);
1267
1268     /* format is <command> <device> <optargs> */
1269     if (!(verb = HeapAlloc(GetProcessHeap(), 0, (strlenW(lpstrCommand)+1) * sizeof(WCHAR))))
1270         return MCIERR_OUT_OF_MEMORY;
1271     strcpyW( verb, lpstrCommand );
1272     CharLowerW(verb);
1273
1274     memset(data, 0, sizeof(data));
1275
1276     if (!(args = strchrW(verb, ' '))) {
1277         dwRet = MCIERR_MISSING_DEVICE_NAME;
1278         goto errCleanUp;
1279     }
1280     *args++ = '\0';
1281     if ((dwRet = MCI_GetString(&dev, &args))) {
1282         goto errCleanUp;
1283     }
1284
1285     /* Determine devType from open */
1286     if (!strcmpW(verb, wszOpen)) {
1287         LPWSTR  devType, tmp;
1288         WCHAR   buf[128];
1289
1290         /* case dev == 'new' has to be handled */
1291         if (!strcmpW(dev, wszNew)) {
1292             dev = 0;
1293             if ((devType = strstrW(args, wszTypeS)) != NULL) {
1294                 devType += 5;
1295                 tmp = strchrW(devType, ' ');
1296                 if (tmp) *tmp = '\0';
1297                 devType = str_dup_upper(devType);
1298                 if (tmp) *tmp = ' ';
1299                 /* dwFlags and data[2] will be correctly set in ParseOpt loop */
1300             } else {
1301                 WARN("open new requires device type\n");
1302                 dwRet = MCIERR_MISSING_DEVICE_NAME;
1303                 goto errCleanUp;
1304             }
1305         } else if ((devType = strchrW(dev, '!')) != NULL) {
1306             *devType++ = '\0';
1307             tmp = devType; devType = dev; dev = tmp;
1308
1309             dwFlags |= MCI_OPEN_TYPE;
1310             data[2] = (DWORD)devType;
1311             devType = str_dup_upper(devType);
1312             dwFlags |= MCI_OPEN_ELEMENT;
1313             data[3] = (DWORD)dev;
1314         } else if (DRIVER_GetLibName(dev, wszMci, buf, sizeof(buf))) {
1315             /* this is the name of a mci driver's type */
1316             tmp = strchrW(dev, ' ');
1317             if (tmp) *tmp = '\0';
1318             data[2] = (DWORD)dev;
1319             devType = str_dup_upper(dev);
1320             if (tmp) *tmp = ' ';
1321             dwFlags |= MCI_OPEN_TYPE;
1322         } else {
1323             if ((devType = strstrW(args, wszTypeS)) != NULL) {
1324                 devType += 5;
1325                 tmp = strchrW(devType, ' ');
1326                 if (tmp) *tmp = '\0';
1327                 devType = str_dup_upper(devType);
1328                 if (tmp) *tmp = ' ';
1329                 /* dwFlags and data[2] will be correctly set in ParseOpt loop */
1330             } else {
1331                 if ((dwRet = MCI_GetDevTypeFromFileName(dev, buf, sizeof(buf))))
1332                     goto errCleanUp;
1333
1334                 devType = str_dup_upper(buf);
1335             }
1336             dwFlags |= MCI_OPEN_ELEMENT;
1337             data[3] = (DWORD)dev;
1338         }
1339         if ((devAlias = strstrW(args, wszSAliasS))) {
1340             WCHAR*      tmp2;
1341             devAlias += 7;
1342             if (!(tmp = strchrW(devAlias,' '))) tmp = devAlias + strlenW(devAlias);
1343             if (tmp) *tmp = '\0';
1344             tmp2 = HeapAlloc(GetProcessHeap(), 0, (tmp - devAlias + 1) * sizeof(WCHAR) );
1345             memcpy( tmp2, devAlias, (tmp - devAlias) * sizeof(WCHAR) );
1346             tmp2[tmp - devAlias] = 0;
1347             data[4] = (DWORD)tmp2;
1348             /* should be done in regular options parsing */
1349             /* dwFlags |= MCI_OPEN_ALIAS; */
1350         } else if (dev == 0) {
1351             /* "open new" requires alias */
1352             dwRet = MCIERR_NEW_REQUIRES_ALIAS;
1353             goto errCleanUp;
1354         }
1355
1356         dwRet = MCI_LoadMciDriver(devType, &wmd);
1357         if (dwRet == MCIERR_DEVICE_NOT_INSTALLED)
1358             dwRet = MCIERR_INVALID_DEVICE_NAME;
1359         HeapFree(GetProcessHeap(), 0, devType);
1360         if (dwRet) {
1361             MCI_UnLoadMciDriver(wmd);
1362             goto errCleanUp;
1363         }
1364     } else if (!(wmd = MCI_GetDriver(mciGetDeviceIDW(dev)))) {
1365         /* auto open */
1366         static const WCHAR wszOpenWait[] = {'o','p','e','n',' ','%','s',' ','w','a','i','t',0};
1367         WCHAR   buf[128];
1368         sprintfW(buf, wszOpenWait, dev);
1369
1370         if ((dwRet = mciSendStringW(buf, NULL, 0, 0)) != 0)
1371             goto errCleanUp;
1372
1373         wmd = MCI_GetDriver(mciGetDeviceIDW(dev));
1374         if (!wmd) {
1375             /* FIXME: memory leak, MCI driver is not closed */
1376             dwRet = MCIERR_INVALID_DEVICE_ID;
1377             goto errCleanUp;
1378         }
1379     }
1380
1381     /* get the verb in the different command tables */
1382     if (wmd) {
1383         /* try the device specific command table */
1384         lpCmd = MCI_FindCommand(wmd->uSpecificCmdTable, verb);
1385         if (!lpCmd) {
1386             /* try the type specific command table */
1387             if (wmd->uTypeCmdTable == MCI_COMMAND_TABLE_NOT_LOADED)
1388                 wmd->uTypeCmdTable = MCI_GetCommandTable(wmd->wType);
1389             if (wmd->uTypeCmdTable != MCI_NO_COMMAND_TABLE)
1390                 lpCmd = MCI_FindCommand(wmd->uTypeCmdTable, verb);
1391         }
1392     }
1393     /* try core command table */
1394     if (!lpCmd) lpCmd = MCI_FindCommand(MCI_GetCommandTable(0), verb);
1395
1396     if (!lpCmd) {
1397         TRACE("Command %s not found!\n", debugstr_w(verb));
1398         dwRet = MCIERR_UNRECOGNIZED_COMMAND;
1399         goto errCleanUp;
1400     }
1401
1402     /* set up call back */
1403     if (hwndCallback != 0) {
1404         dwFlags |= MCI_NOTIFY;
1405         data[0] = (DWORD)hwndCallback;
1406     }
1407
1408     /* set return information */
1409     switch (retType = MCI_GetReturnType(lpCmd)) {
1410     case 0:             offset = 1;     break;
1411     case MCI_INTEGER:   offset = 2;     break;
1412     case MCI_STRING:    data[1] = (DWORD)lpstrRet; data[2] = uRetLen; offset = 3; break;
1413     case MCI_RECT:      offset = 5;     break;
1414     default:    ERR("oops\n");
1415     }
1416
1417     TRACE("verb=%s on dev=%s; offset=%d\n", 
1418           debugstr_w(verb), debugstr_w(dev), offset);
1419
1420     if ((dwRet = MCI_ParseOptArgs(data, offset, lpCmd, args, &dwFlags)))
1421         goto errCleanUp;
1422
1423     /* FIXME: the command should get it's own notification window set up and
1424      * ask for device closing while processing the notification mechanism
1425      */
1426     if (lpstrRet && uRetLen) *lpstrRet = '\0';
1427
1428     TRACE("[%d, %s, %08x, %08x/%s %08x/%s %08x/%s %08x/%s %08x/%s %08x/%s]\n",
1429           wmd->wDeviceID, MCI_MessageToString(MCI_GetMessage(lpCmd)), dwFlags,
1430           data[0], debugstr_w((WCHAR *)data[0]), data[1], debugstr_w((WCHAR *)data[1]),
1431           data[2], debugstr_w((WCHAR *)data[2]), data[3], debugstr_w((WCHAR *)data[3]),
1432           data[4], debugstr_w((WCHAR *)data[4]), data[5], debugstr_w((WCHAR *)data[5]));
1433
1434     if (strcmpW(verb, wszOpen) == 0) {
1435         if ((dwRet = MCI_FinishOpen(wmd, (LPMCI_OPEN_PARMSW)data, dwFlags)))
1436             MCI_UnLoadMciDriver(wmd);
1437         /* FIXME: notification is not properly shared across two opens */
1438     } else {
1439         dwRet = MCI_SendCommand(wmd->wDeviceID, MCI_GetMessage(lpCmd), dwFlags, (DWORD)data, TRUE);
1440     }
1441     TRACE("=> 1/ %x (%s)\n", dwRet, debugstr_w(lpstrRet));
1442     dwRet = MCI_HandleReturnValues(dwRet, wmd, retType, data, lpstrRet, uRetLen);
1443     TRACE("=> 2/ %x (%s)\n", dwRet, debugstr_w(lpstrRet));
1444
1445 errCleanUp:
1446     HeapFree(GetProcessHeap(), 0, verb);
1447     HeapFree(GetProcessHeap(), 0, devAlias);
1448     return dwRet;
1449 }
1450
1451 /**************************************************************************
1452  *                              mciSendStringA                  [WINMM.@]
1453  */
1454 DWORD WINAPI mciSendStringA(LPCSTR lpstrCommand, LPSTR lpstrRet,
1455                             UINT uRetLen, HWND hwndCallback)
1456 {
1457     LPWSTR      lpwstrCommand;
1458     LPWSTR      lpwstrRet = NULL;
1459     UINT        ret;
1460     INT len;
1461
1462     /* FIXME: is there something to do with lpstrReturnString ? */
1463     len = MultiByteToWideChar( CP_ACP, 0, lpstrCommand, -1, NULL, 0 );
1464     lpwstrCommand = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
1465     MultiByteToWideChar( CP_ACP, 0, lpstrCommand, -1, lpwstrCommand, len );
1466     if (lpstrRet)
1467     {
1468         lpwstrRet = HeapAlloc(GetProcessHeap(), 0, uRetLen * sizeof(WCHAR));
1469         if (!lpwstrRet) {
1470             WARN("no memory\n");
1471             HeapFree( GetProcessHeap(), 0, lpwstrCommand );
1472             return MCIERR_OUT_OF_MEMORY;
1473         }
1474     }
1475     ret = mciSendStringW(lpwstrCommand, lpwstrRet, uRetLen, hwndCallback);
1476     if (lpwstrRet)
1477         WideCharToMultiByte( CP_ACP, 0, lpwstrRet, -1, lpstrRet, uRetLen, NULL, NULL );
1478     HeapFree(GetProcessHeap(), 0, lpwstrCommand);
1479     HeapFree(GetProcessHeap(), 0, lpwstrRet);
1480     return ret;
1481 }
1482
1483 /**************************************************************************
1484  *                              mciExecute                      [WINMM.@]
1485  *                              mciExecute                      [MMSYSTEM.712]
1486  */
1487 BOOL WINAPI mciExecute(LPCSTR lpstrCommand)
1488 {
1489     char        strRet[256];
1490     DWORD       ret;
1491
1492     TRACE("(%s)!\n", lpstrCommand);
1493
1494     ret = mciSendStringA(lpstrCommand, strRet, sizeof(strRet), 0);
1495     if (ret != 0) {
1496         if (!mciGetErrorStringA(ret, strRet, sizeof(strRet))) {
1497             sprintf(strRet, "Unknown MCI error (%d)", ret);
1498         }
1499         MessageBoxA(0, strRet, "Error in mciExecute()", MB_OK);
1500     }
1501     /* FIXME: what shall I return ? */
1502     return TRUE;
1503 }
1504
1505 /**************************************************************************
1506  *                      mciLoadCommandResource                  [WINMM.@]
1507  *
1508  * Strangely, this function only exists as an UNICODE one.
1509  */
1510 UINT WINAPI mciLoadCommandResource(HINSTANCE hInst, LPCWSTR resNameW, UINT type)
1511 {
1512     HRSRC               hRsrc = 0;
1513     HGLOBAL             hMem;
1514     UINT16              ret = MCI_NO_COMMAND_TABLE;
1515
1516     TRACE("(%p, %s, %d)!\n", hInst, debugstr_w(resNameW), type);
1517
1518     /* if a file named "resname.mci" exits, then load resource "resname" from it
1519      * otherwise directly from driver
1520      * We don't support it (who uses this feature ?), but we check anyway
1521      */
1522     if (!type) {
1523 #if 0
1524         /* FIXME: we should put this back into order, but I never found a program
1525          * actually using this feature, so we may not need it
1526          */
1527         char            buf[128];
1528         OFSTRUCT        ofs;
1529
1530         strcat(strcpy(buf, resname), ".mci");
1531         if (OpenFile(buf, &ofs, OF_EXIST) != HFILE_ERROR) {
1532             FIXME("NIY: command table to be loaded from '%s'\n", ofs.szPathName);
1533         }
1534 #endif
1535     }
1536     if (!(hRsrc = FindResourceW(hInst, resNameW, (LPWSTR)RT_RCDATA))) {
1537         WARN("No command table found in resource\n");
1538     } else if ((hMem = LoadResource(hInst, hRsrc))) {
1539         ret = MCI_SetCommandTable(LockResource(hMem), type);
1540     } else {
1541         WARN("Couldn't load resource.\n");
1542     }
1543     TRACE("=> %04x\n", ret);
1544     return ret;
1545 }
1546
1547 /**************************************************************************
1548  *                      mciFreeCommandResource                  [WINMM.@]
1549  */
1550 BOOL WINAPI mciFreeCommandResource(UINT uTable)
1551 {
1552     TRACE("(%08x)!\n", uTable);
1553
1554     return MCI_DeleteCommandTable(uTable, FALSE);
1555 }
1556
1557 /**************************************************************************
1558  *                      MCI_SendCommandFrom32                   [internal]
1559  */
1560 DWORD MCI_SendCommandFrom32(MCIDEVICEID wDevID, UINT16 wMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1561 {
1562     DWORD               dwRet = MCIERR_INVALID_DEVICE_ID;
1563     LPWINE_MCIDRIVER    wmd = MCI_GetDriver(wDevID);
1564
1565     if (wmd) {
1566         if (wmd->bIs32) {
1567             dwRet = SendDriverMessage(wmd->hDriver, wMsg, dwParam1, dwParam2);
1568         } else if (pFnMciMapMsg32WTo16) {
1569             WINMM_MapType       res;
1570
1571             switch (res = pFnMciMapMsg32WTo16(wmd->wType, wMsg, dwParam1, &dwParam2)) {
1572             case WINMM_MAP_MSGERROR:
1573                 TRACE("Not handled yet (%s)\n", MCI_MessageToString(wMsg));
1574                 dwRet = MCIERR_DRIVER_INTERNAL;
1575                 break;
1576             case WINMM_MAP_NOMEM:
1577                 TRACE("Problem mapping msg=%s from 32a to 16\n", MCI_MessageToString(wMsg));
1578                 dwRet = MCIERR_OUT_OF_MEMORY;
1579                 break;
1580             case WINMM_MAP_OK:
1581             case WINMM_MAP_OKMEM:
1582                 dwRet = SendDriverMessage(wmd->hDriver, wMsg, dwParam1, dwParam2);
1583                 if (res == WINMM_MAP_OKMEM)
1584                     pFnMciUnMapMsg32WTo16(wmd->wType, wMsg, dwParam1, dwParam2);
1585                 break;
1586             }
1587         }
1588     }
1589     return dwRet;
1590 }
1591
1592 /**************************************************************************
1593  *                      MCI_SendCommandFrom16                   [internal]
1594  */
1595 DWORD MCI_SendCommandFrom16(MCIDEVICEID wDevID, UINT16 wMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1596 {
1597     DWORD               dwRet = MCIERR_INVALID_DEVICE_ID;
1598     LPWINE_MCIDRIVER    wmd = MCI_GetDriver(wDevID);
1599
1600     if (wmd) {
1601         dwRet = MCIERR_INVALID_DEVICE_ID;
1602
1603         if (wmd->bIs32 && pFnMciMapMsg16To32W) {
1604             WINMM_MapType               res;
1605
1606             switch (res = pFnMciMapMsg16To32W(wmd->wType, wMsg, dwParam1, &dwParam2)) {
1607             case WINMM_MAP_MSGERROR:
1608                 TRACE("Not handled yet (%s)\n", MCI_MessageToString(wMsg));
1609                 dwRet = MCIERR_DRIVER_INTERNAL;
1610                 break;
1611             case WINMM_MAP_NOMEM:
1612                 TRACE("Problem mapping msg=%s from 16 to 32a\n", MCI_MessageToString(wMsg));
1613                 dwRet = MCIERR_OUT_OF_MEMORY;
1614                 break;
1615             case WINMM_MAP_OK:
1616             case WINMM_MAP_OKMEM:
1617                 dwRet = SendDriverMessage(wmd->hDriver, wMsg, dwParam1, dwParam2);
1618                 if (res == WINMM_MAP_OKMEM)
1619                     pFnMciUnMapMsg16To32W(wmd->wType, wMsg, dwParam1, dwParam2);
1620                 break;
1621             }
1622         } else {
1623             dwRet = SendDriverMessage(wmd->hDriver, wMsg, dwParam1, dwParam2);
1624         }
1625     }
1626     return dwRet;
1627 }
1628
1629 /**************************************************************************
1630  *                      MCI_Open                                [internal]
1631  */
1632 static  DWORD MCI_Open(DWORD dwParam, LPMCI_OPEN_PARMSW lpParms)
1633 {
1634     WCHAR                       strDevTyp[128];
1635     DWORD                       dwRet;
1636     LPWINE_MCIDRIVER            wmd = NULL;
1637
1638     TRACE("(%08X, %p)\n", dwParam, lpParms);
1639     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1640
1641     /* only two low bytes are generic, the other ones are dev type specific */
1642 #define WINE_MCIDRIVER_SUPP     (0xFFFF0000|MCI_OPEN_SHAREABLE|MCI_OPEN_ELEMENT| \
1643                          MCI_OPEN_ALIAS|MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID| \
1644                          MCI_NOTIFY|MCI_WAIT)
1645     if ((dwParam & ~WINE_MCIDRIVER_SUPP) != 0) {
1646         FIXME("Unsupported yet dwFlags=%08lX\n", dwParam & ~WINE_MCIDRIVER_SUPP);
1647     }
1648 #undef WINE_MCIDRIVER_SUPP
1649
1650     strDevTyp[0] = 0;
1651
1652     if (dwParam & MCI_OPEN_TYPE) {
1653         if (dwParam & MCI_OPEN_TYPE_ID) {
1654             WORD uDevType = LOWORD((DWORD)lpParms->lpstrDeviceType);
1655
1656             if (uDevType < MCI_DEVTYPE_FIRST ||
1657                 uDevType > MCI_DEVTYPE_LAST ||
1658                 !LoadStringW(hWinMM32Instance, uDevType,
1659                              strDevTyp, sizeof(strDevTyp) / sizeof(WCHAR))) {
1660                 dwRet = MCIERR_BAD_INTEGER;
1661                 goto errCleanUp;
1662             }
1663         } else {
1664             LPWSTR      ptr;
1665             if (lpParms->lpstrDeviceType == NULL) {
1666                 dwRet = MCIERR_NULL_PARAMETER_BLOCK;
1667                 goto errCleanUp;
1668             }
1669             strcpyW(strDevTyp, lpParms->lpstrDeviceType);
1670             ptr = strchrW(strDevTyp, '!');
1671             if (ptr) {
1672                 /* this behavior is not documented in windows. However, since, in
1673                  * some occasions, MCI_OPEN handling is translated by WinMM into
1674                  * a call to mciSendString("open <type>"); this code shall be correct
1675                  */
1676                 if (dwParam & MCI_OPEN_ELEMENT) {
1677                     ERR("Both MCI_OPEN_ELEMENT(%s) and %s are used\n",
1678                         debugstr_w(lpParms->lpstrElementName), 
1679                         debugstr_w(strDevTyp));
1680                     dwRet = MCIERR_UNRECOGNIZED_KEYWORD;
1681                     goto errCleanUp;
1682                 }
1683                 dwParam |= MCI_OPEN_ELEMENT;
1684                 *ptr++ = 0;
1685                 /* FIXME: not a good idea to write in user supplied buffer */
1686                 lpParms->lpstrElementName = ptr;
1687             }
1688
1689         }
1690         TRACE("devType=%s !\n", debugstr_w(strDevTyp));
1691     }
1692
1693     if (dwParam & MCI_OPEN_ELEMENT) {
1694         TRACE("lpstrElementName=%s\n", debugstr_w(lpParms->lpstrElementName));
1695
1696         if (dwParam & MCI_OPEN_ELEMENT_ID) {
1697             FIXME("Unsupported yet flag MCI_OPEN_ELEMENT_ID\n");
1698             dwRet = MCIERR_UNRECOGNIZED_KEYWORD;
1699             goto errCleanUp;
1700         }
1701
1702         if (!lpParms->lpstrElementName) {
1703             dwRet = MCIERR_NULL_PARAMETER_BLOCK;
1704             goto errCleanUp;
1705         }
1706
1707         /* type, if given as a parameter, supersedes file extension */
1708         if (!strDevTyp[0] &&
1709             MCI_GetDevTypeFromFileName(lpParms->lpstrElementName,
1710                                        strDevTyp, sizeof(strDevTyp))) {
1711             static const WCHAR wszCdAudio[] = {'C','D','A','U','D','I','O',0};
1712             if (GetDriveTypeW(lpParms->lpstrElementName) != DRIVE_CDROM) {
1713                 dwRet = MCIERR_EXTENSION_NOT_FOUND;
1714                 goto errCleanUp;
1715             }
1716             /* FIXME: this will not work if several CDROM drives are installed on the machine */
1717             strcpyW(strDevTyp, wszCdAudio);
1718         }
1719     }
1720
1721     if (strDevTyp[0] == 0) {
1722         FIXME("Couldn't load driver\n");
1723         dwRet = MCIERR_INVALID_DEVICE_NAME;
1724         goto errCleanUp;
1725     }
1726
1727     if (dwParam & MCI_OPEN_ALIAS) {
1728         TRACE("Alias=%s !\n", debugstr_w(lpParms->lpstrAlias));
1729         if (!lpParms->lpstrAlias) {
1730             dwRet = MCIERR_NULL_PARAMETER_BLOCK;
1731             goto errCleanUp;
1732         }
1733     }
1734
1735     if ((dwRet = MCI_LoadMciDriver(strDevTyp, &wmd))) {
1736         goto errCleanUp;
1737     }
1738
1739     if ((dwRet = MCI_FinishOpen(wmd, lpParms, dwParam))) {
1740         TRACE("Failed to open driver (MCI_OPEN_DRIVER) [%08x], closing\n", dwRet);
1741         /* FIXME: is dwRet the correct ret code ? */
1742         goto errCleanUp;
1743     }
1744
1745     /* only handled devices fall through */
1746     TRACE("wDevID=%04X wDeviceID=%d dwRet=%d\n", wmd->wDeviceID, lpParms->wDeviceID, dwRet);
1747
1748     if (dwParam & MCI_NOTIFY)
1749         mciDriverNotify((HWND)lpParms->dwCallback, wmd->wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1750
1751     return 0;
1752 errCleanUp:
1753     if (wmd) MCI_UnLoadMciDriver(wmd);
1754
1755     if (dwParam & MCI_NOTIFY)
1756         mciDriverNotify((HWND)lpParms->dwCallback, 0, MCI_NOTIFY_FAILURE);
1757     return dwRet;
1758 }
1759
1760 /**************************************************************************
1761  *                      MCI_Close                               [internal]
1762  */
1763 static  DWORD MCI_Close(UINT16 wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
1764 {
1765     DWORD               dwRet;
1766     LPWINE_MCIDRIVER    wmd;
1767
1768     TRACE("(%04x, %08X, %p)\n", wDevID, dwParam, lpParms);
1769
1770     if (wDevID == MCI_ALL_DEVICE_ID) {
1771         LPWINE_MCIDRIVER        next;
1772
1773         EnterCriticalSection(&WINMM_cs);
1774         /* FIXME: shall I notify once after all is done, or for
1775          * each of the open drivers ? if the latest, which notif
1776          * to return when only one fails ?
1777          */
1778         for (wmd = MciDrivers; wmd; ) {
1779             next = wmd->lpNext;
1780             MCI_Close(wmd->wDeviceID, dwParam, lpParms);
1781             wmd = next;
1782         }
1783         LeaveCriticalSection(&WINMM_cs);
1784         return 0;
1785     }
1786
1787     if (!(wmd = MCI_GetDriver(wDevID))) {
1788         return MCIERR_INVALID_DEVICE_ID;
1789     }
1790
1791     dwRet = MCI_SendCommandFrom32(wDevID, MCI_CLOSE_DRIVER, dwParam, (DWORD)lpParms);
1792
1793     MCI_UnLoadMciDriver(wmd);
1794
1795     if (dwParam & MCI_NOTIFY)
1796         mciDriverNotify(lpParms ? (HWND)lpParms->dwCallback : 0,
1797                         wDevID,
1798                         dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1799
1800     return dwRet;
1801 }
1802
1803 /**************************************************************************
1804  *                      MCI_WriteString                         [internal]
1805  */
1806 DWORD   MCI_WriteString(LPWSTR lpDstStr, DWORD dstSize, LPCWSTR lpSrcStr)
1807 {
1808     DWORD       ret = 0;
1809
1810     if (lpSrcStr) {
1811         dstSize /= sizeof(WCHAR);
1812         if (dstSize <= strlenW(lpSrcStr)) {
1813             lstrcpynW(lpDstStr, lpSrcStr, dstSize - 1);
1814             ret = MCIERR_PARAM_OVERFLOW;
1815         } else {
1816             strcpyW(lpDstStr, lpSrcStr);
1817         }
1818     } else {
1819         *lpDstStr = 0;
1820     }
1821     return ret;
1822 }
1823
1824 /**************************************************************************
1825  *                      MCI_Sysinfo                             [internal]
1826  */
1827 static  DWORD MCI_SysInfo(UINT uDevID, DWORD dwFlags, LPMCI_SYSINFO_PARMSW lpParms)
1828 {
1829     DWORD               ret = MCIERR_INVALID_DEVICE_ID, cnt = 0;
1830     WCHAR               buf[2048], *s = buf, *p;
1831     LPWINE_MCIDRIVER    wmd;
1832     HKEY                hKey;
1833
1834     if (lpParms == NULL)                        return MCIERR_NULL_PARAMETER_BLOCK;
1835
1836     TRACE("(%08x, %08X, %08X[num=%d, wDevTyp=%u])\n",
1837           uDevID, dwFlags, (DWORD)lpParms, lpParms->dwNumber, lpParms->wDeviceType);
1838
1839     switch (dwFlags & ~MCI_SYSINFO_OPEN) {
1840     case MCI_SYSINFO_QUANTITY:
1841         if (lpParms->wDeviceType < MCI_DEVTYPE_FIRST || lpParms->wDeviceType > MCI_DEVTYPE_LAST) {
1842             if (dwFlags & MCI_SYSINFO_OPEN) {
1843                 TRACE("MCI_SYSINFO_QUANTITY: # of open MCI drivers\n");
1844                 EnterCriticalSection(&WINMM_cs);
1845                 for (wmd = MciDrivers; wmd; wmd = wmd->lpNext) {
1846                     cnt++;
1847                 }
1848                 LeaveCriticalSection(&WINMM_cs);
1849             } else {
1850                 TRACE("MCI_SYSINFO_QUANTITY: # of installed MCI drivers\n");
1851                 if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, wszHklmMci,
1852                                    0, KEY_QUERY_VALUE, &hKey ) == ERROR_SUCCESS) {
1853                     RegQueryInfoKeyW( hKey, 0, 0, 0, &cnt, 0, 0, 0, 0, 0, 0, 0);
1854                     RegCloseKey( hKey );
1855                 }
1856                 if (GetPrivateProfileStringW(wszMci, 0, wszNull, buf, sizeof(buf) / sizeof(buf[0]), wszSystemIni))
1857                     for (s = buf; *s; s += strlenW(s) + 1) cnt++;
1858             }
1859         } else {
1860             if (dwFlags & MCI_SYSINFO_OPEN) {
1861                 TRACE("MCI_SYSINFO_QUANTITY: # of open MCI drivers of type %u\n", lpParms->wDeviceType);
1862                 EnterCriticalSection(&WINMM_cs);
1863                 for (wmd = MciDrivers; wmd; wmd = wmd->lpNext) {
1864                     if (wmd->wType == lpParms->wDeviceType) cnt++;
1865                 }
1866                 LeaveCriticalSection(&WINMM_cs);
1867             } else {
1868                 TRACE("MCI_SYSINFO_QUANTITY: # of installed MCI drivers of type %u\n", lpParms->wDeviceType);
1869                 FIXME("Don't know how to get # of MCI devices of a given type\n");
1870                 cnt = 1;
1871             }
1872         }
1873         *(DWORD*)lpParms->lpstrReturn = cnt;
1874         TRACE("(%d) => '%d'\n", lpParms->dwNumber, *(DWORD*)lpParms->lpstrReturn);
1875         ret = MCI_INTEGER_RETURNED;
1876         break;
1877     case MCI_SYSINFO_INSTALLNAME:
1878         TRACE("MCI_SYSINFO_INSTALLNAME\n");
1879         if ((wmd = MCI_GetDriver(uDevID))) {
1880             ret = MCI_WriteString(lpParms->lpstrReturn, lpParms->dwRetSize,
1881                                   wmd->lpstrDeviceType);
1882         } else {
1883             *lpParms->lpstrReturn = 0;
1884             ret = MCIERR_INVALID_DEVICE_ID;
1885         }
1886         TRACE("(%d) => %s\n", lpParms->dwNumber, debugstr_w(lpParms->lpstrReturn));
1887         break;
1888     case MCI_SYSINFO_NAME:
1889         TRACE("MCI_SYSINFO_NAME\n");
1890         if (dwFlags & MCI_SYSINFO_OPEN) {
1891             FIXME("Don't handle MCI_SYSINFO_NAME|MCI_SYSINFO_OPEN (yet)\n");
1892             ret = MCIERR_UNRECOGNIZED_COMMAND;
1893         } else {
1894             s = NULL;
1895             if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, wszHklmMci, 0, 
1896                                KEY_QUERY_VALUE, &hKey ) == ERROR_SUCCESS) {
1897                 if (RegQueryInfoKeyW( hKey, 0, 0, 0, &cnt, 
1898                                       0, 0, 0, 0, 0, 0, 0) == ERROR_SUCCESS && 
1899                     lpParms->dwNumber <= cnt) {
1900                     DWORD bufLen = sizeof(buf);
1901                     if (RegEnumKeyExW(hKey, lpParms->dwNumber - 1, 
1902                                       buf, &bufLen, 0, 0, 0, 0) == ERROR_SUCCESS)
1903                         s = buf;
1904                 }
1905                 RegCloseKey( hKey );
1906             }
1907             if (!s) {
1908                 if (GetPrivateProfileStringW(wszMci, 0, wszNull, buf, sizeof(buf) / sizeof(buf[0]), wszSystemIni)) {
1909                     for (p = buf; *p; p += strlenW(p) + 1, cnt++) {
1910                         TRACE("%d: %s\n", cnt, debugstr_w(p));
1911                         if (cnt == lpParms->dwNumber - 1) {
1912                             s = p;
1913                             break;
1914                         }
1915                     }
1916                 }
1917             }
1918             ret = s ? MCI_WriteString(lpParms->lpstrReturn, lpParms->dwRetSize / sizeof(WCHAR), s) : MCIERR_OUTOFRANGE;
1919         }
1920         TRACE("(%d) => %s\n", lpParms->dwNumber, debugstr_w(lpParms->lpstrReturn));
1921         break;
1922     default:
1923         TRACE("Unsupported flag value=%08x\n", dwFlags);
1924         ret = MCIERR_UNRECOGNIZED_COMMAND;
1925     }
1926     return ret;
1927 }
1928
1929 /**************************************************************************
1930  *                      MCI_Break                               [internal]
1931  */
1932 static  DWORD MCI_Break(UINT wDevID, DWORD dwFlags, LPMCI_BREAK_PARMS lpParms)
1933 {
1934     DWORD       dwRet = 0;
1935
1936     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
1937
1938     if (dwFlags & MCI_NOTIFY)
1939         mciDriverNotify((HWND)lpParms->dwCallback, wDevID,
1940                         (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
1941
1942     return dwRet;
1943 }
1944
1945 /**************************************************************************
1946  *                      MCI_Sound                               [internal]
1947  */
1948 static  DWORD MCI_Sound(UINT wDevID, DWORD dwFlags, LPMCI_SOUND_PARMSW lpParms)
1949 {
1950     DWORD       dwRet = 0;
1951
1952     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
1953
1954     if (dwFlags & MCI_SOUND_NAME)
1955         dwRet = sndPlaySoundW(lpParms->lpstrSoundName, SND_SYNC) ? MMSYSERR_NOERROR : MMSYSERR_ERROR;
1956     else
1957         dwRet = MMSYSERR_ERROR; /* what should be done ??? */
1958     if (dwFlags & MCI_NOTIFY)
1959         mciDriverNotify((HWND)lpParms->dwCallback, wDevID,
1960                         (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
1961
1962     return dwRet;
1963 }
1964
1965 /**************************************************************************
1966  *                      MCI_SendCommand                         [internal]
1967  */
1968 DWORD   MCI_SendCommand(UINT wDevID, UINT16 wMsg, DWORD_PTR dwParam1,
1969                         DWORD_PTR dwParam2, BOOL bFrom32)
1970 {
1971     DWORD               dwRet = MCIERR_UNRECOGNIZED_COMMAND;
1972
1973     switch (wMsg) {
1974     case MCI_OPEN:
1975         if (bFrom32) {
1976             dwRet = MCI_Open(dwParam1, (LPMCI_OPEN_PARMSW)dwParam2);
1977         } else if (pFnMciMapMsg16To32W) {
1978             switch (pFnMciMapMsg16To32W(0, wMsg, dwParam1, &dwParam2)) {
1979             case WINMM_MAP_OK:
1980             case WINMM_MAP_OKMEM:
1981                 dwRet = MCI_Open(dwParam1, (LPMCI_OPEN_PARMSW)dwParam2);
1982                 pFnMciUnMapMsg16To32W(0, wMsg, dwParam1, dwParam2);
1983                 break;
1984             default: break; /* so that gcc does not bark */
1985             }
1986         }
1987         break;
1988     case MCI_CLOSE:
1989         if (bFrom32) {
1990             dwRet = MCI_Close(wDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1991         } else if (pFnMciMapMsg16To32W) {
1992             switch (pFnMciMapMsg16To32W(0, wMsg, dwParam1, &dwParam2)) {
1993             case WINMM_MAP_OK:
1994             case WINMM_MAP_OKMEM:
1995                 dwRet = MCI_Close(wDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1996                 pFnMciUnMapMsg16To32W(0, wMsg, dwParam1, dwParam2);
1997                 break;
1998             default: break; /* so that gcc does not bark */
1999             }
2000         }
2001         break;
2002     case MCI_SYSINFO:
2003         if (bFrom32) {
2004             dwRet = MCI_SysInfo(wDevID, dwParam1, (LPMCI_SYSINFO_PARMSW)dwParam2);
2005         } else if (pFnMciMapMsg16To32W) {
2006             switch (pFnMciMapMsg16To32W(0, wMsg, dwParam1, &dwParam2)) {
2007             case WINMM_MAP_OK:
2008             case WINMM_MAP_OKMEM:
2009                 dwRet = MCI_SysInfo(wDevID, dwParam1, (LPMCI_SYSINFO_PARMSW)dwParam2);
2010                 pFnMciUnMapMsg16To32W(0, wMsg, dwParam1, dwParam2);
2011                 break;
2012             default: break; /* so that gcc does not bark */
2013             }
2014         }
2015         break;
2016     case MCI_BREAK:
2017         if (bFrom32) {
2018             dwRet = MCI_Break(wDevID, dwParam1, (LPMCI_BREAK_PARMS)dwParam2);
2019         } else if (pFnMciMapMsg16To32W) {
2020             switch (pFnMciMapMsg16To32W(0, wMsg, dwParam1, &dwParam2)) {
2021             case WINMM_MAP_OK:
2022             case WINMM_MAP_OKMEM:
2023                 dwRet = MCI_Break(wDevID, dwParam1, (LPMCI_BREAK_PARMS)dwParam2);
2024                 pFnMciUnMapMsg16To32W(0, wMsg, dwParam1, dwParam2);
2025                 break;
2026             default: break; /* so that gcc does not bark */
2027             }
2028         }
2029         break;
2030     case MCI_SOUND:
2031         if (bFrom32) {
2032             dwRet = MCI_Sound(wDevID, dwParam1, (LPMCI_SOUND_PARMSW)dwParam2);
2033         } else if (pFnMciMapMsg16To32W) {
2034             switch (pFnMciMapMsg16To32W(0, wMsg, dwParam1, &dwParam2)) {
2035             case WINMM_MAP_OK:
2036             case WINMM_MAP_OKMEM:
2037                 dwRet = MCI_Sound(wDevID, dwParam1, (LPMCI_SOUND_PARMSW)dwParam2);
2038                 pFnMciUnMapMsg16To32W(0, wMsg, dwParam1, dwParam2);
2039                 break;
2040             default: break; /* so that gcc does not bark */
2041             }
2042         }
2043         break;
2044     default:
2045         if (wDevID == MCI_ALL_DEVICE_ID) {
2046             FIXME("unhandled MCI_ALL_DEVICE_ID\n");
2047             dwRet = MCIERR_CANNOT_USE_ALL;
2048         } else {
2049             dwRet = (bFrom32) ?
2050                 MCI_SendCommandFrom32(wDevID, wMsg, dwParam1, dwParam2) :
2051                 MCI_SendCommandFrom16(wDevID, wMsg, dwParam1, dwParam2);
2052         }
2053         break;
2054     }
2055     return dwRet;
2056 }
2057
2058 /**************************************************************************
2059  *                              MCI_CleanUp                     [internal]
2060  *
2061  * Some MCI commands need to be cleaned-up (when not called from
2062  * mciSendString), because MCI drivers return extra information for string
2063  * transformation. This function gets rid of them.
2064  */
2065 LRESULT         MCI_CleanUp(LRESULT dwRet, UINT wMsg, DWORD dwParam2)
2066 {
2067     if (LOWORD(dwRet))
2068         return LOWORD(dwRet);
2069
2070     switch (wMsg) {
2071     case MCI_GETDEVCAPS:
2072         switch (dwRet & 0xFFFF0000ul) {
2073         case 0:
2074         case MCI_COLONIZED3_RETURN:
2075         case MCI_COLONIZED4_RETURN:
2076         case MCI_INTEGER_RETURNED:
2077             /* nothing to do */
2078             break;
2079         case MCI_RESOURCE_RETURNED:
2080         case MCI_RESOURCE_RETURNED|MCI_RESOURCE_DRIVER:
2081             {
2082                 LPMCI_GETDEVCAPS_PARMS  lmgp;
2083
2084                 lmgp = (LPMCI_GETDEVCAPS_PARMS)(void*)dwParam2;
2085                 TRACE("Changing %08x to %08x\n", lmgp->dwReturn, LOWORD(lmgp->dwReturn));
2086                 lmgp->dwReturn = LOWORD(lmgp->dwReturn);
2087             }
2088             break;
2089         default:
2090             FIXME("Unsupported value for hiword (%04x) returned by DriverProc(%s)\n",
2091                   HIWORD(dwRet), MCI_MessageToString(wMsg));
2092         }
2093         break;
2094     case MCI_STATUS:
2095         switch (dwRet & 0xFFFF0000ul) {
2096         case 0:
2097         case MCI_COLONIZED3_RETURN:
2098         case MCI_COLONIZED4_RETURN:
2099         case MCI_INTEGER_RETURNED:
2100             /* nothing to do */
2101             break;
2102         case MCI_RESOURCE_RETURNED:
2103         case MCI_RESOURCE_RETURNED|MCI_RESOURCE_DRIVER:
2104             {
2105                 LPMCI_STATUS_PARMS      lsp;
2106
2107                 lsp = (LPMCI_STATUS_PARMS)(void*)dwParam2;
2108                 TRACE("Changing %08x to %08x\n", lsp->dwReturn, LOWORD(lsp->dwReturn));
2109                 lsp->dwReturn = LOWORD(lsp->dwReturn);
2110             }
2111             break;
2112         default:
2113             FIXME("Unsupported value for hiword (%04x) returned by DriverProc(%s)\n",
2114                   HIWORD(dwRet), MCI_MessageToString(wMsg));
2115         }
2116         break;
2117     case MCI_SYSINFO:
2118         switch (dwRet & 0xFFFF0000ul) {
2119         case 0:
2120         case MCI_INTEGER_RETURNED:
2121             /* nothing to do */
2122             break;
2123         default:
2124             FIXME("Unsupported value for hiword (%04x)\n", HIWORD(dwRet));
2125         }
2126         break;
2127     default:
2128         if (HIWORD(dwRet)) {
2129             FIXME("Got non null hiword for dwRet=0x%08lx for command %s\n",
2130                   dwRet, MCI_MessageToString(wMsg));
2131         }
2132         break;
2133     }
2134     return LOWORD(dwRet);
2135 }
2136
2137 /**************************************************************************
2138  *                              mciGetErrorStringW              [WINMM.@]
2139  */
2140 BOOL WINAPI mciGetErrorStringW(MCIERROR wError, LPWSTR lpstrBuffer, UINT uLength)
2141 {
2142     BOOL                ret = FALSE;
2143
2144     if (lpstrBuffer != NULL && uLength > 0 &&
2145         wError >= MCIERR_BASE && wError <= MCIERR_CUSTOM_DRIVER_BASE) {
2146
2147         if (LoadStringW(hWinMM32Instance, wError, lpstrBuffer, uLength) > 0) {
2148             ret = TRUE;
2149         }
2150     }
2151     return ret;
2152 }
2153
2154 /**************************************************************************
2155  *                              mciGetErrorStringA              [WINMM.@]
2156  */
2157 BOOL WINAPI mciGetErrorStringA(MCIERROR dwError, LPSTR lpstrBuffer, UINT uLength)
2158 {
2159     BOOL                ret = FALSE;
2160
2161     if (lpstrBuffer != NULL && uLength > 0 &&
2162         dwError >= MCIERR_BASE && dwError <= MCIERR_CUSTOM_DRIVER_BASE) {
2163
2164         if (LoadStringA(hWinMM32Instance, dwError, lpstrBuffer, uLength) > 0) {
2165             ret = TRUE;
2166         }
2167     }
2168     return ret;
2169 }
2170
2171 /**************************************************************************
2172  *                      mciDriverNotify                         [WINMM.@]
2173  */
2174 BOOL WINAPI mciDriverNotify(HWND hWndCallBack, MCIDEVICEID wDevID, UINT wStatus)
2175 {
2176     TRACE("(%p, %04x, %04X)\n", hWndCallBack, wDevID, wStatus);
2177
2178     return PostMessageW(hWndCallBack, MM_MCINOTIFY, wStatus, wDevID);
2179 }
2180
2181 /**************************************************************************
2182  *                      mciGetDriverData                        [WINMM.@]
2183  */
2184 DWORD WINAPI mciGetDriverData(MCIDEVICEID uDeviceID)
2185 {
2186     LPWINE_MCIDRIVER    wmd;
2187
2188     TRACE("(%04x)\n", uDeviceID);
2189
2190     wmd = MCI_GetDriver(uDeviceID);
2191
2192     if (!wmd) {
2193         WARN("Bad uDeviceID\n");
2194         return 0L;
2195     }
2196
2197     return wmd->dwPrivate;
2198 }
2199
2200 /**************************************************************************
2201  *                      mciSetDriverData                        [WINMM.@]
2202  */
2203 BOOL WINAPI mciSetDriverData(MCIDEVICEID uDeviceID, DWORD data)
2204 {
2205     LPWINE_MCIDRIVER    wmd;
2206
2207     TRACE("(%04x, %08x)\n", uDeviceID, data);
2208
2209     wmd = MCI_GetDriver(uDeviceID);
2210
2211     if (!wmd) {
2212         WARN("Bad uDeviceID\n");
2213         return FALSE;
2214     }
2215
2216     wmd->dwPrivate = data;
2217     return TRUE;
2218 }
2219
2220 /**************************************************************************
2221  *                              mciSendCommandW                 [WINMM.@]
2222  *
2223  */
2224 DWORD WINAPI mciSendCommandW(MCIDEVICEID wDevID, UINT wMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
2225 {
2226     DWORD       dwRet;
2227
2228     TRACE("(%08x, %s, %08lx, %08lx)\n",
2229           wDevID, MCI_MessageToString(wMsg), dwParam1, dwParam2);
2230
2231     dwRet = MCI_SendCommand(wDevID, wMsg, dwParam1, dwParam2, TRUE);
2232     dwRet = MCI_CleanUp(dwRet, wMsg, dwParam2);
2233     TRACE("=> %08x\n", dwRet);
2234     return dwRet;
2235 }
2236
2237 /**************************************************************************
2238  *                              mciSendCommandA                 [WINMM.@]
2239  */
2240 DWORD WINAPI mciSendCommandA(MCIDEVICEID wDevID, UINT wMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
2241 {
2242     DWORD ret;
2243     int mapped;
2244
2245     TRACE("(%08x, %s, %08lx, %08lx)\n",
2246           wDevID, MCI_MessageToString(wMsg), dwParam1, dwParam2);
2247
2248     mapped = MCI_MapMsgAtoW(wMsg, dwParam1, &dwParam2);
2249     if (mapped == -1)
2250     {
2251         FIXME("message %04x mapping failed\n", wMsg);
2252         return MMSYSERR_NOMEM;
2253     }
2254     ret = mciSendCommandW(wDevID, wMsg, dwParam1, dwParam2);
2255     if (mapped)
2256         MCI_UnmapMsgAtoW(wMsg, dwParam1, dwParam2, ret);
2257     return ret;
2258 }
2259
2260 /**************************************************************************
2261  *                              mciGetDeviceIDA                 [WINMM.@]
2262  */
2263 UINT WINAPI mciGetDeviceIDA(LPCSTR lpstrName)
2264 {
2265     LPWSTR w = MCI_strdupAtoW(lpstrName);
2266     UINT ret = MCIERR_OUT_OF_MEMORY;
2267
2268     if (w)
2269     {
2270         ret = mciGetDeviceIDW(w);
2271         HeapFree(GetProcessHeap(), 0, w);
2272     }
2273     return ret;
2274 }
2275
2276 /**************************************************************************
2277  *                              mciGetDeviceIDW                 [WINMM.@]
2278  */
2279 UINT WINAPI mciGetDeviceIDW(LPCWSTR lpwstrName)
2280 {
2281     return MCI_GetDriverFromString(lpwstrName); 
2282 }
2283
2284 /******************************************************************
2285  *              MyUserYield
2286  *
2287  * Internal wrapper to call USER.UserYield16 (in fact through a Wine only export from USER32).
2288  */
2289 static void MyUserYield(void)
2290 {
2291     HMODULE mod = GetModuleHandleA( "user32.dll" );
2292     if (mod)
2293     {
2294         FARPROC proc = GetProcAddress( mod, "UserYield16" );
2295         if (proc) proc();
2296     }
2297 }
2298
2299 /**************************************************************************
2300  *                              MCI_DefYieldProc                [internal]
2301  */
2302 UINT WINAPI MCI_DefYieldProc(MCIDEVICEID wDevID, DWORD data)
2303 {
2304     INT16       ret;
2305
2306     TRACE("(0x%04x, 0x%08x)\n", wDevID, data);
2307
2308     if ((HIWORD(data) != 0 && HWND_16(GetActiveWindow()) != HIWORD(data)) ||
2309         (GetAsyncKeyState(LOWORD(data)) & 1) == 0) {
2310         MyUserYield();
2311         ret = 0;
2312     } else {
2313         MSG             msg;
2314
2315         msg.hwnd = HWND_32(HIWORD(data));
2316         while (!PeekMessageW(&msg, msg.hwnd, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE));
2317         ret = -1;
2318     }
2319     return ret;
2320 }
2321
2322 /**************************************************************************
2323  *                              mciSetYieldProc                 [WINMM.@]
2324  */
2325 BOOL WINAPI mciSetYieldProc(MCIDEVICEID uDeviceID, YIELDPROC fpYieldProc, DWORD dwYieldData)
2326 {
2327     LPWINE_MCIDRIVER    wmd;
2328
2329     TRACE("(%u, %p, %08x)\n", uDeviceID, fpYieldProc, dwYieldData);
2330
2331     if (!(wmd = MCI_GetDriver(uDeviceID))) {
2332         WARN("Bad uDeviceID\n");
2333         return FALSE;
2334     }
2335
2336     wmd->lpfnYieldProc = fpYieldProc;
2337     wmd->dwYieldData   = dwYieldData;
2338     wmd->bIs32         = TRUE;
2339
2340     return TRUE;
2341 }
2342
2343 /**************************************************************************
2344  *                              mciGetDeviceIDFromElementIDA    [WINMM.@]
2345  */
2346 UINT WINAPI mciGetDeviceIDFromElementIDA(DWORD dwElementID, LPCSTR lpstrType)
2347 {
2348     LPWSTR w = MCI_strdupAtoW(lpstrType);
2349     UINT ret = 0;
2350
2351     if (w)
2352     {
2353         ret = mciGetDeviceIDFromElementIDW(dwElementID, w);
2354         HeapFree(GetProcessHeap(), 0, w);
2355     }
2356     return ret;
2357 }
2358
2359 /**************************************************************************
2360  *                              mciGetDeviceIDFromElementIDW    [WINMM.@]
2361  */
2362 UINT WINAPI mciGetDeviceIDFromElementIDW(DWORD dwElementID, LPCWSTR lpstrType)
2363 {
2364     /* FIXME: that's rather strange, there is no
2365      * mciGetDeviceIDFromElementID32A in winmm.spec
2366      */
2367     FIXME("(%u, %s) stub\n", dwElementID, debugstr_w(lpstrType));
2368     return 0;
2369 }
2370
2371 /**************************************************************************
2372  *                              mciGetYieldProc                 [WINMM.@]
2373  */
2374 YIELDPROC WINAPI mciGetYieldProc(MCIDEVICEID uDeviceID, DWORD* lpdwYieldData)
2375 {
2376     LPWINE_MCIDRIVER    wmd;
2377
2378     TRACE("(%u, %p)\n", uDeviceID, lpdwYieldData);
2379
2380     if (!(wmd = MCI_GetDriver(uDeviceID))) {
2381         WARN("Bad uDeviceID\n");
2382         return NULL;
2383     }
2384     if (!wmd->lpfnYieldProc) {
2385         WARN("No proc set\n");
2386         return NULL;
2387     }
2388     if (!wmd->bIs32) {
2389         WARN("Proc is 32 bit\n");
2390         return NULL;
2391     }
2392     return wmd->lpfnYieldProc;
2393 }
2394
2395 /**************************************************************************
2396  *                              mciGetCreatorTask               [WINMM.@]
2397  */
2398 HTASK WINAPI mciGetCreatorTask(MCIDEVICEID uDeviceID)
2399 {
2400     LPWINE_MCIDRIVER    wmd;
2401     HTASK ret = 0;
2402
2403     if ((wmd = MCI_GetDriver(uDeviceID))) ret = (HTASK)wmd->CreatorThread;
2404
2405     TRACE("(%u) => %p\n", uDeviceID, ret);
2406     return ret;
2407 }
2408
2409 /**************************************************************************
2410  *                      mciDriverYield                          [WINMM.@]
2411  */
2412 UINT WINAPI mciDriverYield(MCIDEVICEID uDeviceID)
2413 {
2414     LPWINE_MCIDRIVER    wmd;
2415     UINT                ret = 0;
2416
2417     TRACE("(%04x)\n", uDeviceID);
2418
2419     if (!(wmd = MCI_GetDriver(uDeviceID)) || !wmd->lpfnYieldProc || !wmd->bIs32) {
2420         MyUserYield();
2421     } else {
2422         ret = wmd->lpfnYieldProc(uDeviceID, wmd->dwYieldData);
2423     }
2424
2425     return ret;
2426 }