Lookup driver information in registry and system.ini.
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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  */
35
36 #include "config.h"
37 #include "wine/port.h"
38
39 #include <stdlib.h>
40 #include <stdarg.h>
41 #include <stdio.h>
42 #include <string.h>
43
44 #include "windef.h"
45 #include "winbase.h"
46 #include "wingdi.h"
47 #include "winreg.h"
48 #include "mmsystem.h"
49 #include "winuser.h"
50 #include "winnls.h"
51 #include "winreg.h"
52
53 #include "digitalv.h"
54 #include "winemm.h"
55
56 #include "wine/debug.h"
57
58 WINE_DEFAULT_DEBUG_CHANNEL(mci);
59
60 WINMM_MapType  (*pFnMciMapMsg16To32A)  (WORD,WORD,DWORD*) /* = NULL */;
61 WINMM_MapType  (*pFnMciUnMapMsg16To32A)(WORD,WORD,DWORD) /* = NULL */;
62 WINMM_MapType  (*pFnMciMapMsg32ATo16)  (WORD,WORD,DWORD,DWORD*) /* = NULL */;
63 WINMM_MapType  (*pFnMciUnMapMsg32ATo16)(WORD,WORD,DWORD,DWORD) /* = NULL */;
64
65 /* First MCI valid device ID (0 means error) */
66 #define MCI_MAGIC 0x0001
67
68 /* MCI settings */
69 #define HKLM_MCI "Software\\Microsoft\\Windows NT\\CurrentVersion\\MCI"
70
71 /* dup a string and uppercase it */
72 inline static LPSTR str_dup_upper( LPCSTR str )
73 {
74     INT len = strlen(str) + 1;
75     LPSTR p = HeapAlloc( GetProcessHeap(), 0, len );
76     if (p)
77     {
78         memcpy( p, str, len );
79         CharUpperA( p );
80     }
81     return p;
82 }
83
84 /**************************************************************************
85  *                              MCI_GetDriver                   [internal]
86  */
87 LPWINE_MCIDRIVER        MCI_GetDriver(UINT16 wDevID)
88 {
89     LPWINE_MCIDRIVER    wmd = 0;
90
91     EnterCriticalSection(&WINMM_IData->cs);
92     for (wmd = WINMM_IData->lpMciDrvs; wmd; wmd = wmd->lpNext) {
93         if (wmd->wDeviceID == wDevID)
94             break;
95     }
96     LeaveCriticalSection(&WINMM_IData->cs);
97     return wmd;
98 }
99
100 /**************************************************************************
101  *                              MCI_GetDriverFromString         [internal]
102  */
103 UINT    MCI_GetDriverFromString(LPCSTR lpstrName)
104 {
105     LPWINE_MCIDRIVER    wmd;
106     UINT                ret = 0;
107
108     if (!lpstrName)
109         return 0;
110
111     if (!lstrcmpiA(lpstrName, "ALL"))
112         return MCI_ALL_DEVICE_ID;
113
114     EnterCriticalSection(&WINMM_IData->cs);
115     for (wmd = WINMM_IData->lpMciDrvs; wmd; wmd = wmd->lpNext) {
116         if (wmd->lpstrElementName && strcmp(wmd->lpstrElementName, lpstrName) == 0) {
117             ret = wmd->wDeviceID;
118             break;
119         }
120         if (wmd->lpstrDeviceType && strcasecmp(wmd->lpstrDeviceType, lpstrName) == 0) {
121             ret = wmd->wDeviceID;
122             break;
123         }
124         if (wmd->lpstrAlias && strcasecmp(wmd->lpstrAlias, lpstrName) == 0) {
125             ret = wmd->wDeviceID;
126             break;
127         }
128     }
129     LeaveCriticalSection(&WINMM_IData->cs);
130
131     return ret;
132 }
133
134 /**************************************************************************
135  *                      MCI_MessageToString                     [internal]
136  */
137 const char* MCI_MessageToString(UINT16 wMsg)
138 {
139     static char buffer[100];
140
141 #define CASE(s) case (s): return #s
142
143     switch (wMsg) {
144         CASE(MCI_BREAK);
145         CASE(MCI_CLOSE);
146         CASE(MCI_CLOSE_DRIVER);
147         CASE(MCI_COPY);
148         CASE(MCI_CUE);
149         CASE(MCI_CUT);
150         CASE(MCI_DELETE);
151         CASE(MCI_ESCAPE);
152         CASE(MCI_FREEZE);
153         CASE(MCI_PAUSE);
154         CASE(MCI_PLAY);
155         CASE(MCI_GETDEVCAPS);
156         CASE(MCI_INFO);
157         CASE(MCI_LOAD);
158         CASE(MCI_OPEN);
159         CASE(MCI_OPEN_DRIVER);
160         CASE(MCI_PASTE);
161         CASE(MCI_PUT);
162         CASE(MCI_REALIZE);
163         CASE(MCI_RECORD);
164         CASE(MCI_RESUME);
165         CASE(MCI_SAVE);
166         CASE(MCI_SEEK);
167         CASE(MCI_SET);
168         CASE(MCI_SPIN);
169         CASE(MCI_STATUS);
170         CASE(MCI_STEP);
171         CASE(MCI_STOP);
172         CASE(MCI_SYSINFO);
173         CASE(MCI_UNFREEZE);
174         CASE(MCI_UPDATE);
175         CASE(MCI_WHERE);
176         CASE(MCI_WINDOW);
177         /* constants for digital video */
178         CASE(MCI_CAPTURE);
179         CASE(MCI_MONITOR);
180         CASE(MCI_RESERVE);
181         CASE(MCI_SETAUDIO);
182         CASE(MCI_SIGNAL);
183         CASE(MCI_SETVIDEO);
184         CASE(MCI_QUALITY);
185         CASE(MCI_LIST);
186         CASE(MCI_UNDO);
187         CASE(MCI_CONFIGURE);
188         CASE(MCI_RESTORE);
189 #undef CASE
190     default:
191         sprintf(buffer, "MCI_<<%04X>>", wMsg);
192         return buffer;
193     }
194 }
195
196 /**************************************************************************
197  *                              MCI_GetDevTypeFromFileName      [internal]
198  */
199 static  DWORD   MCI_GetDevTypeFromFileName(LPCSTR fileName, LPSTR buf, UINT len)
200 {
201     LPSTR       tmp;
202     HKEY        hKey;
203
204     if ((tmp = strrchr(fileName, '.'))) {
205         if (RegOpenKeyExA( HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\MCI Extensions",
206                            0, KEY_QUERY_VALUE, &hKey ) == ERROR_SUCCESS) {
207             DWORD dwLen = len;
208             LONG lRet = RegQueryValueExA( hKey, tmp + 1, 0, 0, buf, &dwLen ); 
209             RegCloseKey( hKey );
210             if (lRet == ERROR_SUCCESS) return 0;
211         }
212         TRACE("No ...\\MCI Extensions entry for '%s' found.\n", tmp);
213     }
214     return MCIERR_EXTENSION_NOT_FOUND;
215 }
216
217 #define MAX_MCICMDTABLE                 20
218 #define MCI_COMMAND_TABLE_NOT_LOADED    0xFFFE
219
220 typedef struct tagWINE_MCICMDTABLE {
221     UINT                uDevType;
222     LPCSTR              lpTable;
223     UINT                nVerbs;         /* number of verbs in command table */
224     LPCSTR*             aVerbs;         /* array of verbs to speed up the verb look up process */
225 } WINE_MCICMDTABLE, *LPWINE_MCICMDTABLE;
226
227 static WINE_MCICMDTABLE S_MciCmdTable[MAX_MCICMDTABLE];
228
229 /**************************************************************************
230  *                              MCI_IsCommandTableValid         [internal]
231  */
232 static  BOOL            MCI_IsCommandTableValid(UINT uTbl)
233 {
234     LPCSTR      lmem, str;
235     DWORD       flg;
236     WORD        eid;
237     int         idx = 0;
238     BOOL        inCst = FALSE;
239
240     TRACE("Dumping cmdTbl=%d [lpTable=%p devType=%d]\n",
241           uTbl, S_MciCmdTable[uTbl].lpTable, S_MciCmdTable[uTbl].uDevType);
242
243     if (uTbl >= MAX_MCICMDTABLE || !S_MciCmdTable[uTbl].lpTable)
244         return FALSE;
245
246     lmem = S_MciCmdTable[uTbl].lpTable;
247     do {
248         do {
249             str = lmem;
250             lmem += strlen(lmem) + 1;
251             flg = *(LPDWORD)lmem;
252             eid = *(LPWORD)(lmem + sizeof(DWORD));
253             lmem += sizeof(DWORD) + sizeof(WORD);
254             idx ++;
255             /* EPP          TRACE("cmd='%s' %08lx %04x\n", str, flg, eid); */
256             switch (eid) {
257             case MCI_COMMAND_HEAD:      if (!*str || !flg) return FALSE; idx = 0;                       break;  /* check unicity of str in table */
258             case MCI_STRING:            if (inCst) return FALSE;                                        break;
259             case MCI_INTEGER:           if (!*str) return FALSE;                                        break;
260             case MCI_END_COMMAND:       if (*str || flg || idx == 0) return FALSE; idx = 0;             break;
261             case MCI_RETURN:            if (*str || idx != 1) return FALSE;                             break;
262             case MCI_FLAG:              if (!*str) return FALSE;                                        break;
263             case MCI_END_COMMAND_LIST:  if (*str || flg) return FALSE;  idx = 0;                        break;
264             case MCI_RECT:              if (!*str || inCst) return FALSE;                               break;
265             case MCI_CONSTANT:          if (inCst) return FALSE; inCst = TRUE;                          break;
266             case MCI_END_CONSTANT:      if (*str || flg || !inCst) return FALSE; inCst = FALSE;         break;
267             default:                    return FALSE;
268             }
269         } while (eid != MCI_END_COMMAND_LIST);
270     } while (eid != MCI_END_COMMAND_LIST);
271     return TRUE;
272 }
273
274 /**************************************************************************
275  *                              MCI_DumpCommandTable            [internal]
276  */
277 static  BOOL            MCI_DumpCommandTable(UINT uTbl)
278 {
279     LPCSTR      lmem;
280     LPCSTR      str;
281     DWORD       flg;
282     WORD        eid;
283
284     if (!MCI_IsCommandTableValid(uTbl)) {
285         ERR("Ooops: %d is not valid\n", uTbl);
286         return FALSE;
287     }
288
289     lmem = S_MciCmdTable[uTbl].lpTable;
290     do {
291         do {
292             str = lmem;
293             lmem += strlen(lmem) + 1;
294             flg = *(LPDWORD)lmem;
295             eid = *(LPWORD)(lmem + sizeof(DWORD));
296             TRACE("cmd='%s' %08lx %04x\n", str, flg, eid);
297             lmem += sizeof(DWORD) + sizeof(WORD);
298         } while (eid != MCI_END_COMMAND && eid != MCI_END_COMMAND_LIST);
299         TRACE(" => end of command%s\n", (eid == MCI_END_COMMAND_LIST) ? " list" : "");
300     } while (eid != MCI_END_COMMAND_LIST);
301     return TRUE;
302 }
303
304
305 /**************************************************************************
306  *                              MCI_GetCommandTable             [internal]
307  */
308 static  UINT            MCI_GetCommandTable(UINT uDevType)
309 {
310     UINT        uTbl;
311     char        buf[32];
312     LPCSTR      str = NULL;
313
314     /* first look up existing for existing devType */
315     for (uTbl = 0; uTbl < MAX_MCICMDTABLE; uTbl++) {
316         if (S_MciCmdTable[uTbl].lpTable && S_MciCmdTable[uTbl].uDevType == uDevType)
317             return uTbl;
318     }
319
320     /* well try to load id */
321     if (uDevType >= MCI_DEVTYPE_FIRST && uDevType <= MCI_DEVTYPE_LAST) {
322         if (LoadStringA(WINMM_IData->hWinMM32Instance, uDevType, buf, sizeof(buf))) {
323             str = buf;
324         }
325     } else if (uDevType == 0) {
326         str = "CORE";
327     }
328     uTbl = MCI_NO_COMMAND_TABLE;
329     if (str) {
330         HRSRC   hRsrc = FindResourceA(WINMM_IData->hWinMM32Instance, str, (LPCSTR)RT_RCDATA);
331         HANDLE  hMem = 0;
332
333         if (hRsrc) hMem = LoadResource(WINMM_IData->hWinMM32Instance, hRsrc);
334         if (hMem) {
335             uTbl = MCI_SetCommandTable(LockResource(hMem), uDevType);
336         } else {
337             WARN("No command table found in resource %p[%s]\n",
338                  WINMM_IData->hWinMM32Instance, str);
339         }
340     }
341     TRACE("=> %d\n", uTbl);
342     return uTbl;
343 }
344
345 /**************************************************************************
346  *                              MCI_SetCommandTable             [internal]
347  */
348 UINT MCI_SetCommandTable(void *table, UINT uDevType)
349 {
350     int                 uTbl;
351     static      BOOL    bInitDone = FALSE;
352
353     /* <HACK>
354      * The CORE command table must be loaded first, so that MCI_GetCommandTable()
355      * can be called with 0 as a uDevType to retrieve it.
356      * </HACK>
357      */
358     if (!bInitDone) {
359         bInitDone = TRUE;
360         MCI_GetCommandTable(0);
361     }
362
363     for (uTbl = 0; uTbl < MAX_MCICMDTABLE; uTbl++) {
364         if (!S_MciCmdTable[uTbl].lpTable) {
365             LPCSTR      lmem, str;
366             WORD        eid;
367             WORD        count;
368
369             S_MciCmdTable[uTbl].uDevType = uDevType;
370             S_MciCmdTable[uTbl].lpTable = table;
371
372             if (TRACE_ON(mci)) {
373                 MCI_DumpCommandTable(uTbl);
374             }
375
376             /* create the verbs table */
377             /* get # of entries */
378             lmem = S_MciCmdTable[uTbl].lpTable;
379             count = 0;
380             do {
381                 lmem += strlen(lmem) + 1;
382                 eid = *(LPWORD)(lmem + sizeof(DWORD));
383                 lmem += sizeof(DWORD) + sizeof(WORD);
384                 if (eid == MCI_COMMAND_HEAD)
385                     count++;
386             } while (eid != MCI_END_COMMAND_LIST);
387
388             S_MciCmdTable[uTbl].aVerbs = HeapAlloc(GetProcessHeap(), 0, count * sizeof(LPCSTR));
389             S_MciCmdTable[uTbl].nVerbs = count;
390
391             lmem = S_MciCmdTable[uTbl].lpTable;
392             count = 0;
393             do {
394                 str = lmem;
395                 lmem += strlen(lmem) + 1;
396                 eid = *(LPWORD)(lmem + sizeof(DWORD));
397                 lmem += sizeof(DWORD) + sizeof(WORD);
398                 if (eid == MCI_COMMAND_HEAD)
399                     S_MciCmdTable[uTbl].aVerbs[count++] = str;
400             } while (eid != MCI_END_COMMAND_LIST);
401             /* assert(count == S_MciCmdTable[uTbl].nVerbs); */
402             return uTbl;
403         }
404     }
405
406     return MCI_NO_COMMAND_TABLE;
407 }
408
409 /**************************************************************************
410  *                              MCI_DeleteCommandTable          [internal]
411  */
412 static  BOOL    MCI_DeleteCommandTable(UINT uTbl)
413 {
414     if (uTbl >= MAX_MCICMDTABLE || !S_MciCmdTable[uTbl].lpTable)
415         return FALSE;
416
417     S_MciCmdTable[uTbl].lpTable = NULL;
418     if (S_MciCmdTable[uTbl].aVerbs) {
419         HeapFree(GetProcessHeap(), 0, S_MciCmdTable[uTbl].aVerbs);
420         S_MciCmdTable[uTbl].aVerbs = 0;
421     }
422     return TRUE;
423 }
424
425 /**************************************************************************
426  *                              MCI_UnLoadMciDriver             [internal]
427  */
428 static  BOOL    MCI_UnLoadMciDriver(LPWINE_MCIDRIVER wmd)
429 {
430     LPWINE_MCIDRIVER*           tmp;
431
432     if (!wmd)
433         return TRUE;
434
435     CloseDriver(wmd->hDriver, 0, 0);
436
437     if (wmd->dwPrivate != 0)
438         WARN("Unloading mci driver with non nul dwPrivate field\n");
439
440     EnterCriticalSection(&WINMM_IData->cs);
441     for (tmp = &WINMM_IData->lpMciDrvs; *tmp; tmp = &(*tmp)->lpNext) {
442         if (*tmp == wmd) {
443             *tmp = wmd->lpNext;
444             break;
445         }
446     }
447     LeaveCriticalSection(&WINMM_IData->cs);
448
449     HeapFree(GetProcessHeap(), 0, wmd->lpstrDeviceType);
450     HeapFree(GetProcessHeap(), 0, wmd->lpstrAlias);
451     HeapFree(GetProcessHeap(), 0, wmd->lpstrElementName);
452
453     HeapFree(GetProcessHeap(), 0, wmd);
454     return TRUE;
455 }
456
457 /**************************************************************************
458  *                              MCI_OpenMciDriver               [internal]
459  */
460 static  BOOL    MCI_OpenMciDriver(LPWINE_MCIDRIVER wmd, LPCSTR drvTyp, LPARAM lp)
461 {
462     char        libName[128];
463
464     if (!DRIVER_GetLibName(drvTyp, "mci", libName, sizeof(libName)))
465         return FALSE;
466
467     wmd->bIs32 = 0xFFFF;
468     /* First load driver */
469     if ((wmd->hDriver = (HDRVR)DRIVER_TryOpenDriver32(libName, lp))) {
470         wmd->bIs32 = TRUE;
471     } else if (WINMM_CheckForMMSystem() && pFnMciMapMsg32ATo16) {
472         WINMM_MapType   res;
473
474         switch (res = pFnMciMapMsg32ATo16(0, DRV_OPEN, 0, &lp)) {
475         case WINMM_MAP_MSGERROR:
476             TRACE("Not handled yet (DRV_OPEN)\n");
477             break;
478         case WINMM_MAP_NOMEM:
479             TRACE("Problem mapping msg=DRV_OPEN from 32a to 16\n");
480             break;
481         case WINMM_MAP_OK:
482         case WINMM_MAP_OKMEM:
483             if ((wmd->hDriver = OpenDriverA(drvTyp, "mci", lp)))
484                 wmd->bIs32 = FALSE;
485             if (res == WINMM_MAP_OKMEM)
486                 pFnMciUnMapMsg32ATo16(0, DRV_OPEN, 0, lp);
487             break;
488         }
489     }
490     return (wmd->bIs32 == 0xFFFF) ? FALSE : TRUE;
491 }
492
493 /**************************************************************************
494  *                              MCI_LoadMciDriver               [internal]
495  */
496 static  DWORD   MCI_LoadMciDriver(LPCSTR _strDevTyp, LPWINE_MCIDRIVER* lpwmd)
497 {
498     LPSTR                       strDevTyp = str_dup_upper(_strDevTyp);
499     LPWINE_MCIDRIVER            wmd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wmd));
500     MCI_OPEN_DRIVER_PARMSA      modp;
501     DWORD                       dwRet = 0;
502
503     if (!wmd || !strDevTyp) {
504         dwRet = MCIERR_OUT_OF_MEMORY;
505         goto errCleanUp;
506     }
507
508     wmd->lpfnYieldProc = MCI_DefYieldProc;
509     wmd->dwYieldData = VK_CANCEL;
510     wmd->CreatorThread = GetCurrentThreadId();
511
512     EnterCriticalSection(&WINMM_IData->cs);
513     /* wmd must be inserted in list before sending opening the driver, coz' it
514      * may want to lookup at wDevID
515      */
516     wmd->lpNext = WINMM_IData->lpMciDrvs;
517     WINMM_IData->lpMciDrvs = wmd;
518
519     for (modp.wDeviceID = MCI_MAGIC;
520          MCI_GetDriver(modp.wDeviceID) != 0;
521          modp.wDeviceID++);
522
523     wmd->wDeviceID = modp.wDeviceID;
524
525     LeaveCriticalSection(&WINMM_IData->cs);
526
527     TRACE("wDevID=%04X \n", modp.wDeviceID);
528
529     modp.lpstrParams = NULL;
530
531     if (!MCI_OpenMciDriver(wmd, strDevTyp, (LPARAM)&modp)) {
532         /* silence warning if all is used... some bogus program use commands like
533          * 'open all'...
534          */
535         if (strcasecmp(strDevTyp, "all") == 0) {
536             dwRet = MCIERR_CANNOT_USE_ALL;
537         } else {
538             FIXME("Couldn't load driver for type %s.\n"
539                   "If you don't have a windows installation accessible from Wine,\n"
540                   "you perhaps forgot to create a [mci] section in system.ini\n",
541                   strDevTyp);
542             dwRet = MCIERR_DEVICE_NOT_INSTALLED;
543         }
544         goto errCleanUp;
545     }
546
547     /* FIXME: should also check that module's description is of the form
548      * MODULENAME:[MCI] comment
549      */
550
551     /* some drivers will return 0x0000FFFF, some others 0xFFFFFFFF */
552     wmd->uSpecificCmdTable = LOWORD(modp.wCustomCommandTable);
553     wmd->uTypeCmdTable = MCI_COMMAND_TABLE_NOT_LOADED;
554
555     TRACE("Loaded driver %p (%s), type is %d, cmdTable=%08x\n",
556           wmd->hDriver, strDevTyp, modp.wType, modp.wCustomCommandTable);
557
558     wmd->lpstrDeviceType = strDevTyp;
559     wmd->wType = modp.wType;
560
561     TRACE("mcidev=%d, uDevTyp=%04X wDeviceID=%04X !\n",
562           modp.wDeviceID, modp.wType, modp.wDeviceID);
563     *lpwmd = wmd;
564     return 0;
565 errCleanUp:
566     MCI_UnLoadMciDriver(wmd);
567     HeapFree(GetProcessHeap(), 0, strDevTyp);
568     *lpwmd = 0;
569     return dwRet;
570 }
571
572 /**************************************************************************
573  *                      MCI_FinishOpen                          [internal]
574  */
575 static  DWORD   MCI_FinishOpen(LPWINE_MCIDRIVER wmd, LPMCI_OPEN_PARMSA lpParms,
576                                DWORD dwParam)
577 {
578     if (dwParam & MCI_OPEN_ELEMENT)
579     {
580         wmd->lpstrElementName = HeapAlloc(GetProcessHeap(),0,strlen(lpParms->lpstrElementName)+1);
581         strcpy( wmd->lpstrElementName, lpParms->lpstrElementName );
582     }
583     if (dwParam & MCI_OPEN_ALIAS)
584     {
585         wmd->lpstrAlias = HeapAlloc(GetProcessHeap(), 0, strlen(lpParms->lpstrAlias)+1);
586         strcpy( wmd->lpstrAlias, lpParms->lpstrAlias);
587     }
588     lpParms->wDeviceID = wmd->wDeviceID;
589
590     return MCI_SendCommandFrom32(wmd->wDeviceID, MCI_OPEN_DRIVER, dwParam,
591                                  (DWORD)lpParms);
592 }
593
594 /**************************************************************************
595  *                              MCI_FindCommand         [internal]
596  */
597 static  LPCSTR          MCI_FindCommand(UINT uTbl, LPCSTR verb)
598 {
599     UINT        idx;
600
601     if (uTbl >= MAX_MCICMDTABLE || !S_MciCmdTable[uTbl].lpTable)
602         return NULL;
603
604     /* another improvement would be to have the aVerbs array sorted,
605      * so that we could use a dichotomic search on it, rather than this dumb
606      * array look up
607      */
608     for (idx = 0; idx < S_MciCmdTable[uTbl].nVerbs; idx++) {
609         if (strcasecmp(S_MciCmdTable[uTbl].aVerbs[idx], verb) == 0)
610             return S_MciCmdTable[uTbl].aVerbs[idx];
611     }
612
613     return NULL;
614 }
615
616 /**************************************************************************
617  *                              MCI_GetReturnType               [internal]
618  */
619 static  DWORD           MCI_GetReturnType(LPCSTR lpCmd)
620 {
621     lpCmd += strlen(lpCmd) + 1 + sizeof(DWORD) + sizeof(WORD);
622     if (*lpCmd == '\0' && *(LPWORD)(lpCmd + 1 + sizeof(DWORD)) == MCI_RETURN) {
623         return *(LPDWORD)(lpCmd + 1);
624     }
625     return 0L;
626 }
627
628 /**************************************************************************
629  *                              MCI_GetMessage                  [internal]
630  */
631 static  WORD            MCI_GetMessage(LPCSTR lpCmd)
632 {
633     return (WORD)*(LPDWORD)(lpCmd + strlen(lpCmd) + 1);
634 }
635
636 /**************************************************************************
637  *                              MCI_GetDWord                    [internal]
638  */
639 static  BOOL            MCI_GetDWord(LPDWORD data, LPSTR* ptr)
640 {
641     DWORD       val;
642     LPSTR       ret;
643
644     val = strtoul(*ptr, &ret, 0);
645
646     switch (*ret) {
647     case '\0':  break;
648     case ' ':   ret++; break;
649     default:    return FALSE;
650     }
651
652     *data |= val;
653     *ptr = ret;
654     return TRUE;
655 }
656
657 /**************************************************************************
658  *                              MCI_GetString           [internal]
659  */
660 static  DWORD   MCI_GetString(LPSTR* str, LPSTR* args)
661 {
662     LPSTR       ptr = *args;
663
664     /* see if we have a quoted string */
665     if (*ptr == '"') {
666         ptr = strchr(*str = ptr + 1, '"');
667         if (!ptr) return MCIERR_NO_CLOSING_QUOTE;
668         /* FIXME: shall we escape \" from string ?? */
669         if (ptr[-1] == '\\') TRACE("Ooops: un-escaped \"\n");
670         *ptr++ = '\0'; /* remove trailing " */
671         if (*ptr != ' ' && *ptr != '\0') return MCIERR_EXTRA_CHARACTERS;
672         *ptr++ = '\0';
673     } else {
674         ptr = strchr(ptr, ' ');
675
676         if (ptr) {
677             *ptr++ = '\0';
678         } else {
679             ptr = *args + strlen(*args);
680         }
681         *str = *args;
682     }
683
684     *args = ptr;
685     return 0;
686 }
687
688 #define MCI_DATA_SIZE   16
689
690 /**************************************************************************
691  *                              MCI_ParseOptArgs                [internal]
692  */
693 static  DWORD   MCI_ParseOptArgs(LPDWORD data, int _offset, LPCSTR lpCmd,
694                                  LPSTR args, LPDWORD dwFlags)
695 {
696     int         len, offset;
697     LPCSTR      lmem, str;
698     DWORD       dwRet, flg, cflg = 0;
699     WORD        eid;
700     BOOL        inCst, found;
701
702     /* loop on arguments */
703     while (*args) {
704         lmem = lpCmd;
705         found = inCst = FALSE;
706         offset = _offset;
707
708         /* skip any leading white space(s) */
709         while (*args == ' ') args++;
710         TRACE("args='%s' offset=%d\n", args, offset);
711
712         do { /* loop on options for command table for the requested verb */
713             str = lmem;
714             lmem += (len = strlen(lmem)) + 1;
715             flg = *(LPDWORD)lmem;
716             eid = *(LPWORD)(lmem + sizeof(DWORD));
717             lmem += sizeof(DWORD) + sizeof(WORD);
718 /* EPP      TRACE("\tcmd='%s' inCst=%c eid=%04x\n", str, inCst ? 'Y' : 'N', eid); */
719
720             switch (eid) {
721             case MCI_CONSTANT:
722                 inCst = TRUE;   cflg = flg;     break;
723             case MCI_END_CONSTANT:
724                 /* there may be additional integral values after flag in constant */
725                 if (inCst && MCI_GetDWord(&(data[offset]), &args)) {
726                     *dwFlags |= cflg;
727                 }
728                 inCst = FALSE;  cflg = 0;
729                 break;
730             }
731
732             if (strncasecmp(args, str, len) == 0 &&
733                 (args[len] == 0 || args[len] == ' ')) {
734                 /* store good values into data[] */
735                 args += len;
736                 while (*args == ' ') args++;
737                 found = TRUE;
738
739                 switch (eid) {
740                 case MCI_COMMAND_HEAD:
741                 case MCI_RETURN:
742                 case MCI_END_COMMAND:
743                 case MCI_END_COMMAND_LIST:
744                 case MCI_CONSTANT:      /* done above */
745                 case MCI_END_CONSTANT:  /* done above */
746                     break;
747                 case MCI_FLAG:
748                     *dwFlags |= flg;
749                     break;
750                 case MCI_INTEGER:
751                     if (inCst) {
752                         data[offset] |= flg;
753                         *dwFlags |= cflg;
754                         inCst = FALSE;
755                     } else {
756                         *dwFlags |= flg;
757                         if (!MCI_GetDWord(&(data[offset]), &args)) {
758                             return MCIERR_BAD_INTEGER;
759                         }
760                     }
761                     break;
762                 case MCI_RECT:
763                     /* store rect in data (offset...offset+3) */
764                     *dwFlags |= flg;
765                     if (!MCI_GetDWord(&(data[offset+0]), &args) ||
766                         !MCI_GetDWord(&(data[offset+1]), &args) ||
767                         !MCI_GetDWord(&(data[offset+2]), &args) ||
768                         !MCI_GetDWord(&(data[offset+3]), &args)) {
769                         ERR("Bad rect '%s'\n", args);
770                         return MCIERR_BAD_INTEGER;
771                     }
772                     break;
773                 case MCI_STRING:
774                     *dwFlags |= flg;
775                     if ((dwRet = MCI_GetString((LPSTR*)&data[offset], &args)))
776                         return dwRet;
777                     break;
778                 default:        ERR("oops\n");
779                 }
780                 /* exit inside while loop, except if just entered in constant area definition */
781                 if (!inCst || eid != MCI_CONSTANT) eid = MCI_END_COMMAND;
782             } else {
783                 /* have offset incremented if needed */
784                 switch (eid) {
785                 case MCI_COMMAND_HEAD:
786                 case MCI_RETURN:
787                 case MCI_END_COMMAND:
788                 case MCI_END_COMMAND_LIST:
789                 case MCI_CONSTANT:
790                 case MCI_FLAG:                  break;
791                 case MCI_INTEGER:               if (!inCst) offset++;   break;
792                 case MCI_END_CONSTANT:
793                 case MCI_STRING:                offset++; break;
794                 case MCI_RECT:                  offset += 4; break;
795                 default:                        ERR("oops\n");
796                 }
797             }
798         } while (eid != MCI_END_COMMAND);
799         if (!found) {
800             WARN("Optarg '%s' not found\n", args);
801             return MCIERR_UNRECOGNIZED_COMMAND;
802         }
803         if (offset == MCI_DATA_SIZE) {
804             ERR("Internal data[] buffer overflow\n");
805             return MCIERR_PARSER_INTERNAL;
806         }
807     }
808     return 0;
809 }
810
811 /**************************************************************************
812  *                              MCI_HandleReturnValues  [internal]
813  */
814 static  DWORD   MCI_HandleReturnValues(DWORD dwRet, LPWINE_MCIDRIVER wmd, DWORD retType, 
815                                        LPDWORD data, LPSTR lpstrRet, UINT uRetLen)
816 {
817     if (lpstrRet) {
818         switch (retType) {
819         case 0: /* nothing to return */
820             break;
821         case MCI_INTEGER:
822             switch (dwRet & 0xFFFF0000ul) {
823             case 0:
824             case MCI_INTEGER_RETURNED:
825                 snprintf(lpstrRet, uRetLen, "%ld", data[1]);
826                 break;
827             case MCI_RESOURCE_RETURNED:
828                 /* return string which ID is HIWORD(data[1]),
829                  * string is loaded from mmsystem.dll */
830                 LoadStringA(WINMM_IData->hWinMM32Instance, HIWORD(data[1]),
831                             lpstrRet, uRetLen);
832                 break;
833             case MCI_RESOURCE_RETURNED|MCI_RESOURCE_DRIVER:
834                 /* return string which ID is HIWORD(data[1]),
835                  * string is loaded from driver */
836                 /* FIXME: this is wrong for a 16 bit handle */
837                 LoadStringA(GetDriverModuleHandle(wmd->hDriver),
838                             HIWORD(data[1]), lpstrRet, uRetLen);
839                 break;
840             case MCI_COLONIZED3_RETURN:
841                 snprintf(lpstrRet, uRetLen, "%d:%d:%d",
842                          LOBYTE(LOWORD(data[1])), HIBYTE(LOWORD(data[1])),
843                          LOBYTE(HIWORD(data[1])));
844                 break;
845             case MCI_COLONIZED4_RETURN:
846                 snprintf(lpstrRet, uRetLen, "%d:%d:%d:%d",
847                          LOBYTE(LOWORD(data[1])), HIBYTE(LOWORD(data[1])),
848                          LOBYTE(HIWORD(data[1])), HIBYTE(HIWORD(data[1])));
849                 break;
850             default:    ERR("Ooops (%04X)\n", HIWORD(dwRet));
851             }
852             break;
853         case MCI_STRING:
854             switch (dwRet & 0xFFFF0000ul) {
855             case 0:
856                 /* nothing to do data[1] == lpstrRet */
857                 break;
858             case MCI_INTEGER_RETURNED:
859                 data[1] = *(LPDWORD)lpstrRet;
860                 snprintf(lpstrRet, uRetLen, "%ld", data[1]);
861                 break;
862             default:
863                 WARN("Oooch. MCI_STRING and HIWORD(dwRet)=%04x\n", HIWORD(dwRet));
864                 break;
865             }
866             break;
867         case MCI_RECT:
868             if (dwRet & 0xFFFF0000ul)
869                 WARN("Oooch. MCI_STRING and HIWORD(dwRet)=%04x\n", HIWORD(dwRet));
870             snprintf(lpstrRet, uRetLen, "%ld %ld %ld %ld",
871                        data[1], data[2], data[3], data[4]);
872             break;
873         default:                ERR("oops\n");
874         }
875     }
876     return LOWORD(dwRet);
877 }
878
879 /**************************************************************************
880  *                              mciSendStringA          [WINMM.@]
881  */
882 DWORD WINAPI mciSendStringA(LPCSTR lpstrCommand, LPSTR lpstrRet,
883                             UINT uRetLen, HWND hwndCallback)
884 {
885     LPSTR               verb, dev, args;
886     LPWINE_MCIDRIVER    wmd = 0;
887     DWORD               dwFlags = 0, dwRet = 0;
888     int                 offset = 0;
889     DWORD               data[MCI_DATA_SIZE];
890     DWORD               retType;
891     LPCSTR              lpCmd = 0;
892     LPSTR               devAlias = NULL;
893     BOOL                bAutoOpen = FALSE;
894
895     TRACE("('%s', %p, %d, %p)\n", lpstrCommand, lpstrRet, uRetLen, hwndCallback);
896
897     /* format is <command> <device> <optargs> */
898     if (!(verb = HeapAlloc(GetProcessHeap(), 0, strlen(lpstrCommand)+1)))
899         return MCIERR_OUT_OF_MEMORY;
900     strcpy( verb, lpstrCommand );
901     CharLowerA(verb);
902
903     memset(data, 0, sizeof(data));
904
905     if (!(args = strchr(verb, ' '))) {
906         dwRet = MCIERR_MISSING_DEVICE_NAME;
907         goto errCleanUp;
908     }
909     *args++ = '\0';
910     if ((dwRet = MCI_GetString(&dev, &args))) {
911         goto errCleanUp;
912     }
913
914     /* case dev == 'new' has to be handled */
915     if (!strcmp(dev, "new")) {
916         FIXME("'new': NIY as device name\n");
917         dwRet = MCIERR_MISSING_DEVICE_NAME;
918         goto errCleanUp;
919     }
920
921     /* otherwise, try to grab devType from open */
922     if (!strcmp(verb, "open")) {
923         LPSTR   devType, tmp;
924
925         if ((devType = strchr(dev, '!')) != NULL) {
926             *devType++ = '\0';
927             tmp = devType; devType = dev; dev = tmp;
928
929             dwFlags |= MCI_OPEN_TYPE;
930             data[2] = (DWORD)devType;
931             devType = str_dup_upper(devType);
932             dwFlags |= MCI_OPEN_ELEMENT;
933             data[3] = (DWORD)dev;
934         } else if (strchr(dev, '.') == NULL) {
935             tmp = strchr(dev,' ');
936             if (tmp) *tmp = '\0';
937             data[2] = (DWORD)dev;
938             devType = str_dup_upper(dev);
939             if (tmp) *tmp = ' ';
940             dwFlags |= MCI_OPEN_TYPE;
941         } else {
942             if ((devType = strstr(args, "type ")) != NULL) {
943                 devType += 5;
944                 tmp = strchr(devType, ' ');
945                 if (tmp) *tmp = '\0';
946                 devType = str_dup_upper(devType);
947                 if (tmp) *tmp = ' ';
948                 /* dwFlags and data[2] will be correctly set in ParseOpt loop */
949             } else {
950                 char    buf[32];
951                 if ((dwRet = MCI_GetDevTypeFromFileName(dev, buf, sizeof(buf))))
952                     goto errCleanUp;
953
954                 devType = str_dup_upper(buf);
955             }
956             dwFlags |= MCI_OPEN_ELEMENT;
957             data[3] = (DWORD)dev;
958         }
959         if ((devAlias = strstr(args," alias "))) {
960             char *tmp2;
961             devAlias += 7;
962             if (!(tmp = strchr(devAlias,' '))) tmp = devAlias + strlen(devAlias);
963             if (tmp) *tmp = '\0';
964             tmp2 = HeapAlloc(GetProcessHeap(), 0, tmp - devAlias + 1 );
965             memcpy( tmp2, devAlias, tmp - devAlias );
966             tmp2[tmp - devAlias] = 0;
967             data[4] = (DWORD)tmp2;
968             /* should be done in regular options parsing */
969             /* dwFlags |= MCI_OPEN_ALIAS; */
970         }
971
972         dwRet = MCI_LoadMciDriver(devType, &wmd);
973         if (dwRet == MCIERR_DEVICE_NOT_INSTALLED)
974             dwRet = MCIERR_INVALID_DEVICE_NAME;
975         HeapFree(GetProcessHeap(), 0, devType);
976         if (dwRet) {
977             MCI_UnLoadMciDriver(wmd);
978             goto errCleanUp;
979         }
980     } else if (!(wmd = MCI_GetDriver(mciGetDeviceIDA(dev)))) {
981         /* auto open */
982         char    buf[128];
983         sprintf(buf, "open %s wait", dev);
984
985         if ((dwRet = mciSendStringA(buf, NULL, 0, 0)) != 0)
986             goto errCleanUp;
987
988         wmd = MCI_GetDriver(mciGetDeviceIDA(dev));
989         if (!wmd) {
990             /* FIXME: memory leak, MCI driver is not closed */
991             dwRet = MCIERR_INVALID_DEVICE_ID;
992             goto errCleanUp;
993         }
994     }
995
996     /* get the verb in the different command tables */
997     if (wmd) {
998         /* try the device specific command table */
999         lpCmd = MCI_FindCommand(wmd->uSpecificCmdTable, verb);
1000         if (!lpCmd) {
1001             /* try the type specific command table */
1002             if (wmd->uTypeCmdTable == MCI_COMMAND_TABLE_NOT_LOADED)
1003                 wmd->uTypeCmdTable = MCI_GetCommandTable(wmd->wType);
1004             if (wmd->uTypeCmdTable != MCI_NO_COMMAND_TABLE)
1005                 lpCmd = MCI_FindCommand(wmd->uTypeCmdTable, verb);
1006         }
1007     }
1008     /* try core command table */
1009     if (!lpCmd) lpCmd = MCI_FindCommand(MCI_GetCommandTable(0), verb);
1010
1011     if (!lpCmd) {
1012         TRACE("Command '%s' not found!\n", verb);
1013         dwRet = MCIERR_UNRECOGNIZED_COMMAND;
1014         goto errCleanUp;
1015     }
1016
1017     /* set up call back */
1018     if (hwndCallback != 0) {
1019         dwFlags |= MCI_NOTIFY;
1020         data[0] = (DWORD)hwndCallback;
1021     }
1022
1023     /* set return information */
1024     switch (retType = MCI_GetReturnType(lpCmd)) {
1025     case 0:             offset = 1;     break;
1026     case MCI_INTEGER:   offset = 2;     break;
1027     case MCI_STRING:    data[1] = (DWORD)lpstrRet; data[2] = uRetLen; offset = 3; break;
1028     case MCI_RECT:      offset = 5;     break;
1029     default:    ERR("oops\n");
1030     }
1031
1032     TRACE("verb='%s' on dev='%s'; offset=%d\n", verb, dev, offset);
1033
1034     if ((dwRet = MCI_ParseOptArgs(data, offset, lpCmd, args, &dwFlags)))
1035         goto errCleanUp;
1036
1037     if (bAutoOpen && (dwFlags & MCI_NOTIFY)) {
1038         dwRet = MCIERR_NOTIFY_ON_AUTO_OPEN;
1039         goto errCleanUp;
1040     }
1041     /* FIXME: the command should get it's own notification window set up and
1042      * ask for device closing while processing the notification mechanism
1043      */
1044     if (lpstrRet && uRetLen) *lpstrRet = '\0';
1045
1046     TRACE("[%d, %s, %08lx, %08lx/%s %08lx/%s %08lx/%s %08lx/%s %08lx/%s %08lx/%s]\n",
1047           wmd->wDeviceID, MCI_MessageToString(MCI_GetMessage(lpCmd)), dwFlags,
1048           data[0], debugstr_a((char *)data[0]), data[1], debugstr_a((char *)data[1]),
1049           data[2], debugstr_a((char *)data[2]), data[3], debugstr_a((char *)data[3]),
1050           data[4], debugstr_a((char *)data[4]), data[5], debugstr_a((char *)data[5]));
1051
1052     if (strcmp(verb, "open") == 0) {
1053         if ((dwRet = MCI_FinishOpen(wmd, (LPMCI_OPEN_PARMSA)data, dwFlags)))
1054             MCI_UnLoadMciDriver(wmd);
1055         /* FIXME: notification is not properly shared across two opens */
1056     } else {
1057         dwRet = MCI_SendCommand(wmd->wDeviceID, MCI_GetMessage(lpCmd), dwFlags, (DWORD)data, TRUE);
1058     }
1059     TRACE("=> 1/ %lx (%s)\n", dwRet, lpstrRet);
1060     dwRet = MCI_HandleReturnValues(dwRet, wmd, retType, data, lpstrRet, uRetLen);
1061     TRACE("=> 2/ %lx (%s)\n", dwRet, lpstrRet);
1062
1063 errCleanUp:
1064     HeapFree(GetProcessHeap(), 0, verb);
1065     HeapFree(GetProcessHeap(), 0, devAlias);
1066     return dwRet;
1067 }
1068
1069 /**************************************************************************
1070  *                              mciSendStringW                  [WINMM.@]
1071  */
1072 DWORD WINAPI mciSendStringW(LPCWSTR lpwstrCommand, LPWSTR lpwstrRet,
1073                             UINT uRetLen, HWND hwndCallback)
1074 {
1075     LPSTR       lpstrCommand;
1076     LPSTR       lpstrRet = NULL;
1077     UINT        ret;
1078     INT len;
1079
1080     /* FIXME: is there something to do with lpstrReturnString ? */
1081     len = WideCharToMultiByte( CP_ACP, 0, lpwstrCommand, -1, NULL, 0, NULL, NULL );
1082     lpstrCommand = HeapAlloc( GetProcessHeap(), 0, len );
1083     WideCharToMultiByte( CP_ACP, 0, lpwstrCommand, -1, lpstrCommand, len, NULL, NULL );
1084     if (lpwstrRet)
1085     {
1086         lpstrRet = HeapAlloc(GetProcessHeap(), 0, uRetLen * sizeof(WCHAR));
1087         if (!lpstrRet) return MMSYSERR_NOMEM;
1088     }
1089     ret = mciSendStringA(lpstrCommand, lpstrRet, uRetLen, hwndCallback);
1090     if (lpwstrRet)
1091         MultiByteToWideChar( CP_ACP, 0, lpstrRet, -1, lpwstrRet, uRetLen );
1092     HeapFree(GetProcessHeap(), 0, lpstrCommand);
1093     if (lpstrRet) HeapFree(GetProcessHeap(), 0, lpstrRet);
1094     return ret;
1095 }
1096
1097 /**************************************************************************
1098  *                              mciExecute                      [WINMM.@]
1099  *                              mciExecute                      [MMSYSTEM.712]
1100  */
1101 DWORD WINAPI mciExecute(LPCSTR lpstrCommand)
1102 {
1103     char        strRet[256];
1104     DWORD       ret;
1105
1106     TRACE("(%s)!\n", lpstrCommand);
1107
1108     ret = mciSendStringA(lpstrCommand, strRet, sizeof(strRet), 0);
1109     if (ret != 0) {
1110         if (!mciGetErrorStringA(ret, strRet, sizeof(strRet))) {
1111             sprintf(strRet, "Unknown MCI error (%ld)", ret);
1112         }
1113         MessageBoxA(0, strRet, "Error in mciExecute()", MB_OK);
1114     }
1115     /* FIXME: what shall I return ? */
1116     return 0;
1117 }
1118
1119 /**************************************************************************
1120  *                      mciLoadCommandResource                  [WINMM.@]
1121  *
1122  * Strangely, this function only exists as an UNICODE one.
1123  */
1124 UINT WINAPI mciLoadCommandResource(HINSTANCE hInst, LPCWSTR resNameW, UINT type)
1125 {
1126     HRSRC               hRsrc = 0;
1127     HGLOBAL             hMem;
1128     UINT16              ret = MCI_NO_COMMAND_TABLE;
1129
1130     TRACE("(%p, %s, %d)!\n", hInst, debugstr_w(resNameW), type);
1131
1132     /* if a file named "resname.mci" exits, then load resource "resname" from it
1133      * otherwise directly from driver
1134      * We don't support it (who uses this feature ?), but we check anyway
1135      */
1136     if (!type) {
1137 #if 0
1138         /* FIXME: we should put this back into order, but I never found a program
1139          * actually using this feature, so we not need it
1140          */
1141         char            buf[128];
1142         OFSTRUCT        ofs;
1143
1144         strcat(strcpy(buf, resname), ".mci");
1145         if (OpenFile(buf, &ofs, OF_EXIST) != HFILE_ERROR) {
1146             FIXME("NIY: command table to be loaded from '%s'\n", ofs.szPathName);
1147         }
1148 #endif
1149     }
1150     if (!(hRsrc = FindResourceW(hInst, resNameW, (LPWSTR)RT_RCDATA))) {
1151         WARN("No command table found in resource\n");
1152     } else if ((hMem = LoadResource(hInst, hRsrc))) {
1153         ret = MCI_SetCommandTable(LockResource(hMem), type);
1154     } else {
1155         WARN("Couldn't load resource.\n");
1156     }
1157     TRACE("=> %04x\n", ret);
1158     return ret;
1159 }
1160
1161 /**************************************************************************
1162  *                      mciFreeCommandResource                  [WINMM.@]
1163  */
1164 BOOL WINAPI mciFreeCommandResource(UINT uTable)
1165 {
1166     TRACE("(%08x)!\n", uTable);
1167
1168     return MCI_DeleteCommandTable(uTable);
1169 }
1170
1171 /**************************************************************************
1172  *                      MCI_SendCommandFrom32                   [internal]
1173  */
1174 DWORD MCI_SendCommandFrom32(MCIDEVICEID wDevID, UINT16 wMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1175 {
1176     DWORD               dwRet = MCIERR_INVALID_DEVICE_ID;
1177     LPWINE_MCIDRIVER    wmd = MCI_GetDriver(wDevID);
1178
1179     if (wmd) {
1180         if (wmd->bIs32) {
1181             dwRet = SendDriverMessage(wmd->hDriver, wMsg, dwParam1, dwParam2);
1182         } else if (pFnMciMapMsg32ATo16) {
1183             WINMM_MapType       res;
1184
1185             switch (res = pFnMciMapMsg32ATo16(wmd->wType, wMsg, dwParam1, &dwParam2)) {
1186             case WINMM_MAP_MSGERROR:
1187                 TRACE("Not handled yet (%s)\n", MCI_MessageToString(wMsg));
1188                 dwRet = MCIERR_DRIVER_INTERNAL;
1189                 break;
1190             case WINMM_MAP_NOMEM:
1191                 TRACE("Problem mapping msg=%s from 32a to 16\n", MCI_MessageToString(wMsg));
1192                 dwRet = MCIERR_OUT_OF_MEMORY;
1193                 break;
1194             case WINMM_MAP_OK:
1195             case WINMM_MAP_OKMEM:
1196                 dwRet = SendDriverMessage(wmd->hDriver, wMsg, dwParam1, dwParam2);
1197                 if (res == WINMM_MAP_OKMEM)
1198                     pFnMciUnMapMsg32ATo16(wmd->wType, wMsg, dwParam1, dwParam2);
1199                 break;
1200             }
1201         }
1202     }
1203     return dwRet;
1204 }
1205
1206 /**************************************************************************
1207  *                      MCI_SendCommandFrom16                   [internal]
1208  */
1209 DWORD MCI_SendCommandFrom16(MCIDEVICEID wDevID, UINT16 wMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1210 {
1211     DWORD               dwRet = MCIERR_INVALID_DEVICE_ID;
1212     LPWINE_MCIDRIVER    wmd = MCI_GetDriver(wDevID);
1213
1214     if (wmd) {
1215         dwRet = MCIERR_INVALID_DEVICE_ID;
1216
1217         if (wmd->bIs32 && pFnMciMapMsg16To32A) {
1218             WINMM_MapType               res;
1219
1220             switch (res = pFnMciMapMsg16To32A(wmd->wType, wMsg, &dwParam2)) {
1221             case WINMM_MAP_MSGERROR:
1222                 TRACE("Not handled yet (%s)\n", MCI_MessageToString(wMsg));
1223                 dwRet = MCIERR_DRIVER_INTERNAL;
1224                 break;
1225             case WINMM_MAP_NOMEM:
1226                 TRACE("Problem mapping msg=%s from 16 to 32a\n", MCI_MessageToString(wMsg));
1227                 dwRet = MCIERR_OUT_OF_MEMORY;
1228                 break;
1229             case WINMM_MAP_OK:
1230             case WINMM_MAP_OKMEM:
1231                 dwRet = SendDriverMessage(wmd->hDriver, wMsg, dwParam1, dwParam2);
1232                 if (res == WINMM_MAP_OKMEM)
1233                     pFnMciUnMapMsg16To32A(wmd->wType, wMsg, dwParam2);
1234                 break;
1235             }
1236         } else {
1237             dwRet = SendDriverMessage(wmd->hDriver, wMsg, dwParam1, dwParam2);
1238         }
1239     }
1240     return dwRet;
1241 }
1242
1243 /**************************************************************************
1244  *                      MCI_Open                                [internal]
1245  */
1246 static  DWORD MCI_Open(DWORD dwParam, LPMCI_OPEN_PARMSA lpParms)
1247 {
1248     char                        strDevTyp[128];
1249     DWORD                       dwRet;
1250     LPWINE_MCIDRIVER            wmd = NULL;
1251
1252     TRACE("(%08lX, %p)\n", dwParam, lpParms);
1253     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1254
1255     /* only two low bytes are generic, the other ones are dev type specific */
1256 #define WINE_MCIDRIVER_SUPP     (0xFFFF0000|MCI_OPEN_SHAREABLE|MCI_OPEN_ELEMENT| \
1257                          MCI_OPEN_ALIAS|MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID| \
1258                          MCI_NOTIFY|MCI_WAIT)
1259     if ((dwParam & ~WINE_MCIDRIVER_SUPP) != 0) {
1260         FIXME("Unsupported yet dwFlags=%08lX\n", dwParam & ~WINE_MCIDRIVER_SUPP);
1261     }
1262 #undef WINE_MCIDRIVER_SUPP
1263
1264     strDevTyp[0] = 0;
1265
1266     if (dwParam & MCI_OPEN_TYPE) {
1267         if (dwParam & MCI_OPEN_TYPE_ID) {
1268             WORD uDevType = LOWORD((DWORD)lpParms->lpstrDeviceType);
1269
1270             if (uDevType < MCI_DEVTYPE_FIRST ||
1271                 uDevType > MCI_DEVTYPE_LAST ||
1272                 !LoadStringA(WINMM_IData->hWinMM32Instance, uDevType, strDevTyp, sizeof(strDevTyp))) {
1273                 dwRet = MCIERR_BAD_INTEGER;
1274                 goto errCleanUp;
1275             }
1276         } else {
1277             LPSTR       ptr;
1278             if (lpParms->lpstrDeviceType == NULL) {
1279                 dwRet = MCIERR_NULL_PARAMETER_BLOCK;
1280                 goto errCleanUp;
1281             }
1282             strcpy(strDevTyp, lpParms->lpstrDeviceType);
1283             ptr = strchr(strDevTyp, '!');
1284             if (ptr) {
1285                 /* this behavior is not documented in windows. However, since, in
1286                  * some occasions, MCI_OPEN handling is translated by WinMM into
1287                  * a call to mciSendString("open <type>"); this code shall be correct
1288                  */
1289                 if (dwParam & MCI_OPEN_ELEMENT) {
1290                     ERR("Both MCI_OPEN_ELEMENT(%s) and %s are used\n",
1291                         lpParms->lpstrElementName, strDevTyp);
1292                     dwRet = MCIERR_UNRECOGNIZED_KEYWORD;
1293                     goto errCleanUp;
1294                 }
1295                 dwParam |= MCI_OPEN_ELEMENT;
1296                 *ptr++ = 0;
1297                 /* FIXME: not a good idea to write in user supplied buffer */
1298                 lpParms->lpstrElementName = ptr;
1299             }
1300
1301         }
1302         TRACE("devType='%s' !\n", strDevTyp);
1303     }
1304
1305     if (dwParam & MCI_OPEN_ELEMENT) {
1306         TRACE("lpstrElementName='%s'\n", lpParms->lpstrElementName);
1307
1308         if (dwParam & MCI_OPEN_ELEMENT_ID) {
1309             FIXME("Unsupported yet flag MCI_OPEN_ELEMENT_ID\n");
1310             dwRet = MCIERR_UNRECOGNIZED_KEYWORD;
1311             goto errCleanUp;
1312         }
1313
1314         if (!lpParms->lpstrElementName) {
1315             dwRet = MCIERR_NULL_PARAMETER_BLOCK;
1316             goto errCleanUp;
1317         }
1318
1319         /* type, if given as a parameter, supersedes file extension */
1320         if (!strDevTyp[0] &&
1321             MCI_GetDevTypeFromFileName(lpParms->lpstrElementName,
1322                                        strDevTyp, sizeof(strDevTyp))) {
1323             if (GetDriveTypeA(lpParms->lpstrElementName) != DRIVE_CDROM) {
1324                 dwRet = MCIERR_EXTENSION_NOT_FOUND;
1325                 goto errCleanUp;
1326             }
1327             /* FIXME: this will not work if several CDROM drives are installed on the machine */
1328             strcpy(strDevTyp, "CDAUDIO");
1329         }
1330     }
1331
1332     if (strDevTyp[0] == 0) {
1333         FIXME("Couldn't load driver\n");
1334         dwRet = MCIERR_INVALID_DEVICE_NAME;
1335         goto errCleanUp;
1336     }
1337
1338     if (dwParam & MCI_OPEN_ALIAS) {
1339         TRACE("Alias='%s' !\n", lpParms->lpstrAlias);
1340         if (!lpParms->lpstrAlias) {
1341             dwRet = MCIERR_NULL_PARAMETER_BLOCK;
1342             goto errCleanUp;
1343         }
1344     }
1345
1346     if ((dwRet = MCI_LoadMciDriver(strDevTyp, &wmd))) {
1347         goto errCleanUp;
1348     }
1349
1350     if ((dwRet = MCI_FinishOpen(wmd, lpParms, dwParam))) {
1351         TRACE("Failed to open driver (MCI_OPEN_DRIVER) [%08lx], closing\n", dwRet);
1352         /* FIXME: is dwRet the correct ret code ? */
1353         goto errCleanUp;
1354     }
1355
1356     /* only handled devices fall through */
1357     TRACE("wDevID=%04X wDeviceID=%d dwRet=%ld\n", wmd->wDeviceID, lpParms->wDeviceID, dwRet);
1358
1359     if (dwParam & MCI_NOTIFY)
1360         mciDriverNotify((HWND)lpParms->dwCallback, wmd->wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1361
1362     return 0;
1363 errCleanUp:
1364     if (wmd) MCI_UnLoadMciDriver(wmd);
1365
1366     if (dwParam & MCI_NOTIFY)
1367         mciDriverNotify((HWND)lpParms->dwCallback, 0, MCI_NOTIFY_FAILURE);
1368     return dwRet;
1369 }
1370
1371 /**************************************************************************
1372  *                      MCI_Close                               [internal]
1373  */
1374 static  DWORD MCI_Close(UINT16 wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
1375 {
1376     DWORD               dwRet;
1377     LPWINE_MCIDRIVER    wmd;
1378
1379     TRACE("(%04x, %08lX, %p)\n", wDevID, dwParam, lpParms);
1380
1381     if (wDevID == MCI_ALL_DEVICE_ID) {
1382         LPWINE_MCIDRIVER        next;
1383
1384         EnterCriticalSection(&WINMM_IData->cs);
1385         /* FIXME: shall I notify once after all is done, or for
1386          * each of the open drivers ? if the latest, which notif
1387          * to return when only one fails ?
1388          */
1389         for (wmd = WINMM_IData->lpMciDrvs; wmd; ) {
1390             next = wmd->lpNext;
1391             MCI_Close(wmd->wDeviceID, dwParam, lpParms);
1392             wmd = next;
1393         }
1394         LeaveCriticalSection(&WINMM_IData->cs);
1395         return 0;
1396     }
1397
1398     if (!(wmd = MCI_GetDriver(wDevID))) {
1399         return MCIERR_INVALID_DEVICE_ID;
1400     }
1401
1402     dwRet = MCI_SendCommandFrom32(wDevID, MCI_CLOSE_DRIVER, dwParam, (DWORD)lpParms);
1403
1404     MCI_UnLoadMciDriver(wmd);
1405
1406     if (dwParam & MCI_NOTIFY)
1407         mciDriverNotify((HWND)lpParms->dwCallback, wDevID,
1408                         (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
1409
1410     return dwRet;
1411 }
1412
1413 /**************************************************************************
1414  *                      MCI_WriteString                         [internal]
1415  */
1416 DWORD   MCI_WriteString(LPSTR lpDstStr, DWORD dstSize, LPCSTR lpSrcStr)
1417 {
1418     DWORD       ret = 0;
1419
1420     if (lpSrcStr) {
1421         if (dstSize <= strlen(lpSrcStr)) {
1422             lstrcpynA(lpDstStr, lpSrcStr, dstSize - 1);
1423             ret = MCIERR_PARAM_OVERFLOW;
1424         } else {
1425             strcpy(lpDstStr, lpSrcStr);
1426         }
1427     } else {
1428         *lpDstStr = 0;
1429     }
1430     return ret;
1431 }
1432
1433 /**************************************************************************
1434  *                      MCI_Sysinfo                             [internal]
1435  */
1436 static  DWORD MCI_SysInfo(UINT uDevID, DWORD dwFlags, LPMCI_SYSINFO_PARMSA lpParms)
1437 {
1438     DWORD               ret = MCIERR_INVALID_DEVICE_ID, cnt = 0;
1439     CHAR                buf[2048], *s = buf, *p;
1440     LPWINE_MCIDRIVER    wmd;
1441     HKEY                hKey;
1442
1443     if (lpParms == NULL)                        return MCIERR_NULL_PARAMETER_BLOCK;
1444
1445     TRACE("(%08x, %08lX, %08lX[num=%ld, wDevTyp=%u])\n",
1446           uDevID, dwFlags, (DWORD)lpParms, lpParms->dwNumber, lpParms->wDeviceType);
1447
1448     switch (dwFlags & ~MCI_SYSINFO_OPEN) {
1449     case MCI_SYSINFO_QUANTITY:
1450         if (lpParms->wDeviceType < MCI_DEVTYPE_FIRST || lpParms->wDeviceType > MCI_DEVTYPE_LAST) {
1451             if (dwFlags & MCI_SYSINFO_OPEN) {
1452                 TRACE("MCI_SYSINFO_QUANTITY: # of open MCI drivers\n");
1453                 EnterCriticalSection(&WINMM_IData->cs);
1454                 for (wmd = WINMM_IData->lpMciDrvs; wmd; wmd = wmd->lpNext) {
1455                     cnt++;
1456                 }
1457                 LeaveCriticalSection(&WINMM_IData->cs);
1458             } else {
1459                 TRACE("MCI_SYSINFO_QUANTITY: # of installed MCI drivers\n");
1460                 if (RegOpenKeyExA( HKEY_LOCAL_MACHINE, HKLM_MCI,
1461                                    0, KEY_QUERY_VALUE, &hKey ) == ERROR_SUCCESS) {
1462                     RegQueryInfoKeyA( hKey, 0, 0, 0, &cnt, 0, 0, 0, 0, 0, 0, 0);
1463                     RegCloseKey( hKey );
1464                 }
1465                 if (GetPrivateProfileStringA("mci", 0, "", buf, sizeof(buf), "system.ini"))
1466                     for(s = buf; *s; s += strlen(s) + 1) cnt++;
1467             }
1468         } else {
1469             if (dwFlags & MCI_SYSINFO_OPEN) {
1470                 TRACE("MCI_SYSINFO_QUANTITY: # of open MCI drivers of type %u\n", lpParms->wDeviceType);
1471                 EnterCriticalSection(&WINMM_IData->cs);
1472                 for (wmd = WINMM_IData->lpMciDrvs; wmd; wmd = wmd->lpNext) {
1473                     if (wmd->wType == lpParms->wDeviceType) cnt++;
1474                 }
1475                 LeaveCriticalSection(&WINMM_IData->cs);
1476             } else {
1477                 TRACE("MCI_SYSINFO_QUANTITY: # of installed MCI drivers of type %u\n", lpParms->wDeviceType);
1478                 FIXME("Don't know how to get # of MCI devices of a given type\n");
1479                 cnt = 1;
1480             }
1481         }
1482         *(DWORD*)lpParms->lpstrReturn = cnt;
1483         TRACE("(%ld) => '%ld'\n", lpParms->dwNumber, *(DWORD*)lpParms->lpstrReturn);
1484         ret = MCI_INTEGER_RETURNED;
1485         break;
1486     case MCI_SYSINFO_INSTALLNAME:
1487         TRACE("MCI_SYSINFO_INSTALLNAME \n");
1488         if ((wmd = MCI_GetDriver(uDevID))) {
1489             ret = MCI_WriteString(lpParms->lpstrReturn, lpParms->dwRetSize,
1490                                   wmd->lpstrDeviceType);
1491         } else {
1492             *lpParms->lpstrReturn = 0;
1493             ret = MCIERR_INVALID_DEVICE_ID;
1494         }
1495         TRACE("(%ld) => '%s'\n", lpParms->dwNumber, lpParms->lpstrReturn);
1496         break;
1497     case MCI_SYSINFO_NAME:
1498         TRACE("MCI_SYSINFO_NAME\n");
1499         if (dwFlags & MCI_SYSINFO_OPEN) {
1500             FIXME("Don't handle MCI_SYSINFO_NAME|MCI_SYSINFO_OPEN (yet)\n");
1501             ret = MCIERR_UNRECOGNIZED_COMMAND;
1502         } else {
1503             DWORD lRet;
1504             s = 0;
1505             lRet = RegOpenKeyExA( HKEY_LOCAL_MACHINE, HKLM_MCI, 0, KEY_QUERY_VALUE, &hKey );
1506             if (lRet == ERROR_SUCCESS) {
1507                 lRet = RegQueryInfoKeyA( hKey, 0, 0, 0, &cnt, 0, 0, 0, 0, 0, 0, 0);
1508                 if (lRet == ERROR_SUCCESS && lpParms->dwNumber <= cnt) {
1509                     DWORD bufLen = sizeof(buf);
1510                     lRet = RegEnumKeyExA(hKey, lpParms->dwNumber - 1, buf, &bufLen, 0, 0, 0, 0);
1511                     if (lRet == ERROR_SUCCESS) s = buf;
1512                 }
1513                 RegCloseKey( hKey );
1514             }
1515             if (!s) {
1516                 if (GetPrivateProfileStringA("mci", 0, "", buf, sizeof(buf), "system.ini")) {
1517                     for(p = buf; *p; p += strlen(s) + 1, cnt++) {
1518                         if (cnt == lpParms->dwNumber - 1) {
1519                             s = p;
1520                             break;
1521                         }
1522                     }
1523                 }
1524             }
1525             ret = s ? MCI_WriteString(lpParms->lpstrReturn, lpParms->dwRetSize, s) : MCIERR_OUTOFRANGE;
1526         }
1527         TRACE("(%ld) => '%s'\n", lpParms->dwNumber, lpParms->lpstrReturn);
1528         break;
1529     default:
1530         TRACE("Unsupported flag value=%08lx\n", dwFlags);
1531         ret = MCIERR_UNRECOGNIZED_COMMAND;
1532     }
1533     return ret;
1534 }
1535
1536 /**************************************************************************
1537  *                      MCI_Break                               [internal]
1538  */
1539 static  DWORD MCI_Break(UINT wDevID, DWORD dwFlags, LPMCI_BREAK_PARMS lpParms)
1540 {
1541     DWORD       dwRet = 0;
1542
1543     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
1544
1545     if (dwFlags & MCI_NOTIFY)
1546         mciDriverNotify((HWND)lpParms->dwCallback, wDevID,
1547                         (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
1548
1549     return dwRet;
1550 }
1551
1552 /**************************************************************************
1553  *                      MCI_Sound                               [internal]
1554  */
1555 static  DWORD MCI_Sound(UINT wDevID, DWORD dwFlags, LPMCI_SOUND_PARMS lpParms)
1556 {
1557     DWORD       dwRet = 0;
1558
1559     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
1560
1561     if (dwFlags & MCI_SOUND_NAME)
1562         dwRet = sndPlaySoundA(lpParms->lpstrSoundName, SND_SYNC) ? MMSYSERR_NOERROR : MMSYSERR_ERROR;
1563     else
1564         dwRet = MMSYSERR_ERROR; /* what should be done ??? */
1565     if (dwFlags & MCI_NOTIFY)
1566         mciDriverNotify((HWND)lpParms->dwCallback, wDevID,
1567                         (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
1568
1569     return dwRet;
1570 }
1571
1572 /**************************************************************************
1573  *                      MCI_SendCommand                         [internal]
1574  */
1575 DWORD   MCI_SendCommand(UINT wDevID, UINT16 wMsg, DWORD dwParam1,
1576                         DWORD dwParam2, BOOL bFrom32)
1577 {
1578     DWORD               dwRet = MCIERR_UNRECOGNIZED_COMMAND;
1579
1580     switch (wMsg) {
1581     case MCI_OPEN:
1582         if (bFrom32) {
1583             dwRet = MCI_Open(dwParam1, (LPMCI_OPEN_PARMSA)dwParam2);
1584         } else if (pFnMciMapMsg16To32A) {
1585             switch (pFnMciMapMsg16To32A(0, wMsg, &dwParam2)) {
1586             case WINMM_MAP_OK:
1587             case WINMM_MAP_OKMEM:
1588                 dwRet = MCI_Open(dwParam1, (LPMCI_OPEN_PARMSA)dwParam2);
1589                 pFnMciUnMapMsg16To32A(0, wMsg, dwParam2);
1590                 break;
1591             default: break; /* so that gcc does not bark */
1592             }
1593         }
1594         break;
1595     case MCI_CLOSE:
1596         if (bFrom32) {
1597             dwRet = MCI_Close(wDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1598         } else if (pFnMciMapMsg16To32A) {
1599             switch (pFnMciMapMsg16To32A(0, wMsg, &dwParam2)) {
1600             case WINMM_MAP_OK:
1601             case WINMM_MAP_OKMEM:
1602                 dwRet = MCI_Close(wDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1603                 pFnMciUnMapMsg16To32A(0, wMsg, dwParam2);
1604                 break;
1605             default: break; /* so that gcc does not bark */
1606             }
1607         }
1608         break;
1609     case MCI_SYSINFO:
1610         if (bFrom32) {
1611             dwRet = MCI_SysInfo(wDevID, dwParam1, (LPMCI_SYSINFO_PARMSA)dwParam2);
1612         } else if (pFnMciMapMsg16To32A) {
1613             switch (pFnMciMapMsg16To32A(0, wMsg, &dwParam2)) {
1614             case WINMM_MAP_OK:
1615             case WINMM_MAP_OKMEM:
1616                 dwRet = MCI_SysInfo(wDevID, dwParam1, (LPMCI_SYSINFO_PARMSA)dwParam2);
1617                 pFnMciUnMapMsg16To32A(0, wMsg, dwParam2);
1618                 break;
1619             default: break; /* so that gcc does not bark */
1620             }
1621         }
1622         break;
1623     case MCI_BREAK:
1624         if (bFrom32) {
1625             dwRet = MCI_Break(wDevID, dwParam1, (LPMCI_BREAK_PARMS)dwParam2);
1626         } else if (pFnMciMapMsg16To32A) {
1627             switch (pFnMciMapMsg16To32A(0, wMsg, &dwParam2)) {
1628             case WINMM_MAP_OK:
1629             case WINMM_MAP_OKMEM:
1630                 dwRet = MCI_Break(wDevID, dwParam1, (LPMCI_BREAK_PARMS)dwParam2);
1631                 pFnMciUnMapMsg16To32A(0, wMsg, dwParam2);
1632                 break;
1633             default: break; /* so that gcc does not bark */
1634             }
1635         }
1636         break;
1637     case MCI_SOUND:
1638         if (bFrom32) {
1639             dwRet = MCI_Sound(wDevID, dwParam1, (LPMCI_SOUND_PARMS)dwParam2);
1640         } else if (pFnMciMapMsg16To32A) {
1641             switch (pFnMciMapMsg16To32A(0, wMsg, &dwParam2)) {
1642             case WINMM_MAP_OK:
1643             case WINMM_MAP_OKMEM:
1644                 dwRet = MCI_Sound(wDevID, dwParam1, (LPMCI_SOUND_PARMS)dwParam2);
1645                 pFnMciUnMapMsg16To32A(0, wMsg, dwParam2);
1646                 break;
1647             default: break; /* so that gcc does not bark */
1648             }
1649         }
1650         break;
1651     default:
1652         if (wDevID == MCI_ALL_DEVICE_ID) {
1653             FIXME("unhandled MCI_ALL_DEVICE_ID\n");
1654             dwRet = MCIERR_CANNOT_USE_ALL;
1655         } else {
1656             dwRet = (bFrom32) ?
1657                 MCI_SendCommandFrom32(wDevID, wMsg, dwParam1, dwParam2) :
1658                 MCI_SendCommandFrom16(wDevID, wMsg, dwParam1, dwParam2);
1659         }
1660         break;
1661     }
1662     return dwRet;
1663 }
1664
1665 /**************************************************************************
1666  *                              MCI_CleanUp                     [internal]
1667  *
1668  * Some MCI commands need to be cleaned-up (when not called from
1669  * mciSendString), because MCI drivers return extra information for string
1670  * transformation. This function gets rid of them.
1671  */
1672 LRESULT         MCI_CleanUp(LRESULT dwRet, UINT wMsg, DWORD dwParam2)
1673 {
1674     if (LOWORD(dwRet))
1675         return LOWORD(dwRet);
1676
1677     switch (wMsg) {
1678     case MCI_GETDEVCAPS:
1679         switch (dwRet & 0xFFFF0000ul) {
1680         case 0:
1681         case MCI_COLONIZED3_RETURN:
1682         case MCI_COLONIZED4_RETURN:
1683         case MCI_INTEGER_RETURNED:
1684             /* nothing to do */
1685             break;
1686         case MCI_RESOURCE_RETURNED:
1687         case MCI_RESOURCE_RETURNED|MCI_RESOURCE_DRIVER:
1688             {
1689                 LPMCI_GETDEVCAPS_PARMS  lmgp;
1690
1691                 lmgp = (LPMCI_GETDEVCAPS_PARMS)(void*)dwParam2;
1692                 TRACE("Changing %08lx to %08lx\n", lmgp->dwReturn, (DWORD)LOWORD(lmgp->dwReturn));
1693                 lmgp->dwReturn = LOWORD(lmgp->dwReturn);
1694             }
1695             break;
1696         default:
1697             FIXME("Unsupported value for hiword (%04x) returned by DriverProc(%s)\n",
1698                   HIWORD(dwRet), MCI_MessageToString(wMsg));
1699         }
1700         break;
1701     case MCI_STATUS:
1702         switch (dwRet & 0xFFFF0000ul) {
1703         case 0:
1704         case MCI_COLONIZED3_RETURN:
1705         case MCI_COLONIZED4_RETURN:
1706         case MCI_INTEGER_RETURNED:
1707             /* nothing to do */
1708             break;
1709         case MCI_RESOURCE_RETURNED:
1710         case MCI_RESOURCE_RETURNED|MCI_RESOURCE_DRIVER:
1711             {
1712                 LPMCI_STATUS_PARMS      lsp;
1713
1714                 lsp = (LPMCI_STATUS_PARMS)(void*)dwParam2;
1715                 TRACE("Changing %08lx to %08lx\n", lsp->dwReturn, (DWORD)LOWORD(lsp->dwReturn));
1716                 lsp->dwReturn = LOWORD(lsp->dwReturn);
1717             }
1718             break;
1719         default:
1720             FIXME("Unsupported value for hiword (%04x) returned by DriverProc(%s)\n",
1721                   HIWORD(dwRet), MCI_MessageToString(wMsg));
1722         }
1723         break;
1724     case MCI_SYSINFO:
1725         switch (dwRet & 0xFFFF0000ul) {
1726         case 0:
1727         case MCI_INTEGER_RETURNED:
1728             /* nothing to do */
1729             break;
1730         default:
1731             FIXME("Unsupported value for hiword (%04x)\n", HIWORD(dwRet));
1732         }
1733         break;
1734     default:
1735         if (HIWORD(dwRet)) {
1736             FIXME("Got non null hiword for dwRet=0x%08lx for command %s\n",
1737                   dwRet, MCI_MessageToString(wMsg));
1738         }
1739         break;
1740     }
1741     return LOWORD(dwRet);
1742 }