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