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