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