Added regedit unit test, a couple minor changes to regedit.
[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
36 #include "wine/mmsystem16.h"
37 #include "wine/winbase16.h"
38 #include "digitalv.h"
39 #include "winemm.h"
40
41 #include "wine/debug.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(mci);
44
45 static  int                     MCI_InstalledCount;
46 static  LPSTR                   MCI_lpInstallNames = NULL;
47
48 typedef enum {
49     MCI_MAP_NOMEM,      /* ko, memory problem */
50     MCI_MAP_MSGERROR,   /* ko, unknown message */
51     MCI_MAP_OK,         /* ok, no memory allocated. to be sent to the proc. */
52     MCI_MAP_OKMEM,      /* ok, some memory allocated, need to call UnMapMsg. to be sent to the proc. */
53 } MCI_MapType;
54
55 static  MCI_MapType     MCI_MapMsg16To32A  (WORD uDevType, WORD wMsg,                DWORD* lParam);
56 static  MCI_MapType     MCI_UnMapMsg16To32A(WORD uDevType, WORD wMsg,                DWORD  lParam);
57 static  MCI_MapType     MCI_MapMsg32ATo16  (WORD uDevType, WORD wMsg, DWORD dwFlags, DWORD* lParam);
58 static  MCI_MapType     MCI_UnMapMsg32ATo16(WORD uDevType, WORD wMsg, DWORD dwFlags, DWORD  lParam);
59
60 /* First MCI valid device ID (0 means error) */
61 #define MCI_MAGIC 0x0001
62
63 /* dup a string and uppercase it */
64 inline static LPSTR str_dup_upper( LPCSTR str )
65 {
66     INT len = strlen(str) + 1;
67     LPSTR p = HeapAlloc( GetProcessHeap(), 0, len );
68     if (p)
69     {
70         memcpy( p, str, len );
71         CharUpperA( p );
72     }
73     return p;
74 }
75
76 /**************************************************************************
77  *                              MCI_GetDriver                   [internal]
78  */
79 LPWINE_MCIDRIVER        MCI_GetDriver(UINT16 wDevID)
80 {
81     LPWINE_MCIDRIVER    wmd = 0;
82     LPWINE_MM_IDATA     iData = MULTIMEDIA_GetIData();
83
84     EnterCriticalSection(&iData->cs);
85     for (wmd = iData->lpMciDrvs; wmd; wmd = wmd->lpNext) {
86         if (wmd->wDeviceID == wDevID)
87             break;
88     }
89     LeaveCriticalSection(&iData->cs);
90     return wmd;
91 }
92
93 /**************************************************************************
94  *                              MCI_GetDriverFromString         [internal]
95  */
96 UINT    MCI_GetDriverFromString(LPCSTR lpstrName)
97 {
98     LPWINE_MCIDRIVER    wmd;
99     LPWINE_MM_IDATA     iData = MULTIMEDIA_GetIData();
100     UINT                ret = 0;
101
102     if (!lpstrName)
103         return 0;
104
105     if (!lstrcmpiA(lpstrName, "ALL"))
106         return MCI_ALL_DEVICE_ID;
107
108     EnterCriticalSection(&iData->cs);
109     for (wmd = iData->lpMciDrvs; wmd; wmd = wmd->lpNext) {
110         if (wmd->lpstrElementName && strcmp(wmd->lpstrElementName, lpstrName) == 0) {
111             ret = wmd->wDeviceID;
112             break;
113         }
114         if (wmd->lpstrDeviceType && strcasecmp(wmd->lpstrDeviceType, lpstrName) == 0) {
115             ret = wmd->wDeviceID;
116             break;
117         }
118         if (wmd->lpstrAlias && strcasecmp(wmd->lpstrAlias, lpstrName) == 0) {
119             ret = wmd->wDeviceID;
120             break;
121         }
122     }
123     LeaveCriticalSection(&iData->cs);
124
125     return ret;
126 }
127
128 /**************************************************************************
129  *                      MCI_MessageToString                     [internal]
130  */
131 const char* MCI_MessageToString(UINT16 wMsg)
132 {
133     static char buffer[100];
134
135 #define CASE(s) case (s): return #s
136
137     switch (wMsg) {
138         CASE(MCI_BREAK);
139         CASE(MCI_CLOSE);
140         CASE(MCI_CLOSE_DRIVER);
141         CASE(MCI_COPY);
142         CASE(MCI_CUE);
143         CASE(MCI_CUT);
144         CASE(MCI_DELETE);
145         CASE(MCI_ESCAPE);
146         CASE(MCI_FREEZE);
147         CASE(MCI_PAUSE);
148         CASE(MCI_PLAY);
149         CASE(MCI_GETDEVCAPS);
150         CASE(MCI_INFO);
151         CASE(MCI_LOAD);
152         CASE(MCI_OPEN);
153         CASE(MCI_OPEN_DRIVER);
154         CASE(MCI_PASTE);
155         CASE(MCI_PUT);
156         CASE(MCI_REALIZE);
157         CASE(MCI_RECORD);
158         CASE(MCI_RESUME);
159         CASE(MCI_SAVE);
160         CASE(MCI_SEEK);
161         CASE(MCI_SET);
162         CASE(MCI_SPIN);
163         CASE(MCI_STATUS);
164         CASE(MCI_STEP);
165         CASE(MCI_STOP);
166         CASE(MCI_SYSINFO);
167         CASE(MCI_UNFREEZE);
168         CASE(MCI_UPDATE);
169         CASE(MCI_WHERE);
170         CASE(MCI_WINDOW);
171         /* constants for digital video */
172         CASE(MCI_CAPTURE);
173         CASE(MCI_MONITOR);
174         CASE(MCI_RESERVE);
175         CASE(MCI_SETAUDIO);
176         CASE(MCI_SIGNAL);
177         CASE(MCI_SETVIDEO);
178         CASE(MCI_QUALITY);
179         CASE(MCI_LIST);
180         CASE(MCI_UNDO);
181         CASE(MCI_CONFIGURE);
182         CASE(MCI_RESTORE);
183 #undef CASE
184     default:
185         sprintf(buffer, "MCI_<<%04X>>", wMsg);
186         return buffer;
187     }
188 }
189
190 /**************************************************************************
191  *                              MCI_GetDevTypeFromFileName      [internal]
192  */
193 static  DWORD   MCI_GetDevTypeFromFileName(LPCSTR fileName, LPSTR buf, UINT len)
194 {
195     LPSTR       tmp;
196
197     if ((tmp = strrchr(fileName, '.'))) {
198         GetProfileStringA("mci extensions", tmp + 1, "*", buf, len);
199         if (strcmp(buf, "*") != 0) {
200             return 0;
201         }
202         TRACE("No [mci extensions] entry for '%s' found.\n", tmp);
203     }
204     return MCIERR_EXTENSION_NOT_FOUND;
205 }
206
207 #define MAX_MCICMDTABLE                 20
208 #define MCI_COMMAND_TABLE_NOT_LOADED    0xFFFE
209
210 typedef struct tagWINE_MCICMDTABLE {
211     HANDLE              hMem;
212     UINT                uDevType;
213     LPCSTR              lpTable;
214     UINT                nVerbs;         /* number of verbs in command table */
215     LPCSTR*             aVerbs;         /* array of verbs to speed up the verb look up process */
216 } WINE_MCICMDTABLE, *LPWINE_MCICMDTABLE;
217 WINE_MCICMDTABLE        S_MciCmdTable[MAX_MCICMDTABLE];
218
219 /**************************************************************************
220  *                              MCI_IsCommandTableValid         [internal]
221  */
222 static  BOOL            MCI_IsCommandTableValid(UINT uTbl)
223 {
224     LPCSTR      lmem, str;
225     DWORD       flg;
226     WORD        eid;
227     int         idx = 0;
228     BOOL        inCst = FALSE;
229
230     TRACE("Dumping cmdTbl=%d [hMem=%08x devType=%d]\n",
231           uTbl, S_MciCmdTable[uTbl].hMem, S_MciCmdTable[uTbl].uDevType);
232
233     if (uTbl >= MAX_MCICMDTABLE || !S_MciCmdTable[uTbl].hMem || !S_MciCmdTable[uTbl].lpTable)
234         return FALSE;
235
236     lmem = S_MciCmdTable[uTbl].lpTable;
237     do {
238         do {
239             str = lmem;
240             lmem += strlen(lmem) + 1;
241             flg = *(LPDWORD)lmem;
242             eid = *(LPWORD)(lmem + sizeof(DWORD));
243             lmem += sizeof(DWORD) + sizeof(WORD);
244             idx ++;
245             /* EPP          TRACE("cmd='%s' %08lx %04x\n", str, flg, eid); */
246             switch (eid) {
247             case MCI_COMMAND_HEAD:      if (!*str || !flg) return FALSE; idx = 0;                       break;  /* check unicity of str in table */
248             case MCI_STRING:            if (inCst) return FALSE;                                        break;
249             case MCI_INTEGER:           if (!*str) return FALSE;                                        break;
250             case MCI_END_COMMAND:       if (*str || flg || idx == 0) return FALSE; idx = 0;             break;
251             case MCI_RETURN:            if (*str || idx != 1) return FALSE;                             break;
252             case MCI_FLAG:              if (!*str) return FALSE;                                        break;
253             case MCI_END_COMMAND_LIST:  if (*str || flg) return FALSE;  idx = 0;                        break;
254             case MCI_RECT:              if (!*str || inCst) return FALSE;                               break;
255             case MCI_CONSTANT:          if (inCst) return FALSE; inCst = TRUE;                          break;
256             case MCI_END_CONSTANT:      if (*str || flg || !inCst) return FALSE; inCst = FALSE;         break;
257             default:                    return FALSE;
258             }
259         } while (eid != MCI_END_COMMAND_LIST);
260     } while (eid != MCI_END_COMMAND_LIST);
261     return TRUE;
262 }
263
264 /**************************************************************************
265  *                              MCI_DumpCommandTable            [internal]
266  */
267 static  BOOL            MCI_DumpCommandTable(UINT uTbl)
268 {
269     LPCSTR      lmem;
270     LPCSTR      str;
271     DWORD       flg;
272     WORD        eid;
273
274     if (!MCI_IsCommandTableValid(uTbl)) {
275         ERR("Ooops: %d is not valid\n", uTbl);
276         return FALSE;
277     }
278
279     lmem = S_MciCmdTable[uTbl].lpTable;
280     do {
281         do {
282             str = lmem;
283             lmem += strlen(lmem) + 1;
284             flg = *(LPDWORD)lmem;
285             eid = *(LPWORD)(lmem + sizeof(DWORD));
286             TRACE("cmd='%s' %08lx %04x\n", str, flg, eid);
287             lmem += sizeof(DWORD) + sizeof(WORD);
288         } while (eid != MCI_END_COMMAND && eid != MCI_END_COMMAND_LIST);
289         TRACE(" => end of command%s\n", (eid == MCI_END_COMMAND_LIST) ? " list" : "");
290     } while (eid != MCI_END_COMMAND_LIST);
291     return TRUE;
292 }
293
294 static  UINT            MCI_SetCommandTable(LPWINE_MM_IDATA iData, HANDLE hMem, UINT uDevType);
295
296 /**************************************************************************
297  *                              MCI_GetCommandTable             [internal]
298  */
299 static  UINT            MCI_GetCommandTable(LPWINE_MM_IDATA iData, UINT uDevType)
300 {
301     UINT        uTbl;
302     char        buf[32];
303     LPSTR       str = NULL;
304
305     /* first look up existing for existing devType */
306     for (uTbl = 0; uTbl < MAX_MCICMDTABLE; uTbl++) {
307         if (S_MciCmdTable[uTbl].hMem && S_MciCmdTable[uTbl].uDevType == uDevType)
308             return uTbl;
309     }
310
311     /* well try to load id */
312     if (uDevType >= MCI_DEVTYPE_FIRST && uDevType <= MCI_DEVTYPE_LAST) {
313         if (LoadStringA(iData->hWinMM32Instance, uDevType, buf, sizeof(buf))) {
314             str = buf;
315         }
316     } else if (uDevType == 0) {
317         str = "CORE";
318     }
319     uTbl = MCI_NO_COMMAND_TABLE;
320     if (str) {
321         HRSRC   hRsrc = FindResourceA(iData->hWinMM32Instance, str, (LPCSTR)RT_RCDATAA);
322         HANDLE  hMem = 0;
323
324         if (hRsrc) hMem = LoadResource(iData->hWinMM32Instance, hRsrc);
325         if (hMem) {
326             uTbl = MCI_SetCommandTable(iData, hMem, uDevType);
327         } else {
328             WARN("No command table found in resource %04x[%s]\n",
329                  iData->hWinMM32Instance, str);
330         }
331     }
332     TRACE("=> %d\n", uTbl);
333     return uTbl;
334 }
335
336 /**************************************************************************
337  *                              MCI_SetCommandTable             [internal]
338  */
339 static  UINT            MCI_SetCommandTable(LPWINE_MM_IDATA iData, HANDLE hMem,
340                                             UINT uDevType)
341 {
342     int                 uTbl;
343     static      BOOL    bInitDone = FALSE;
344
345     /* <HACK>
346      * The CORE command table must be loaded first, so that MCI_GetCommandTable()
347      * can be called with 0 as a uDevType to retrieve it.
348      * </HACK>
349      */
350     if (!bInitDone) {
351         bInitDone = TRUE;
352         for (uTbl = 0; uTbl < MAX_MCICMDTABLE; uTbl++) {
353             S_MciCmdTable[uTbl].hMem = 0;
354         }
355         MCI_GetCommandTable(iData, 0);
356     }
357
358     for (uTbl = 0; uTbl < MAX_MCICMDTABLE; uTbl++) {
359         if (S_MciCmdTable[uTbl].hMem == 0) {
360             LPCSTR      lmem, str;
361             WORD        eid;
362             WORD        count;
363
364             S_MciCmdTable[uTbl].hMem = hMem;
365             S_MciCmdTable[uTbl].uDevType = uDevType;
366             S_MciCmdTable[uTbl].lpTable = LockResource(hMem);
367
368             if (TRACE_ON(mci)) {
369                 MCI_DumpCommandTable(uTbl);
370             }
371
372             /* create the verbs table */
373             /* get # of entries */
374             lmem = S_MciCmdTable[uTbl].lpTable;
375             count = 0;
376             do {
377                 lmem += strlen(lmem) + 1;
378                 eid = *(LPWORD)(lmem + sizeof(DWORD));
379                 lmem += sizeof(DWORD) + sizeof(WORD);
380                 if (eid == MCI_COMMAND_HEAD)
381                     count++;
382             } while (eid != MCI_END_COMMAND_LIST);
383
384             S_MciCmdTable[uTbl].aVerbs = HeapAlloc(GetProcessHeap(), 0, count * sizeof(LPCSTR));
385             S_MciCmdTable[uTbl].nVerbs = count;
386
387             lmem = S_MciCmdTable[uTbl].lpTable;
388             count = 0;
389             do {
390                 str = lmem;
391                 lmem += strlen(lmem) + 1;
392                 eid = *(LPWORD)(lmem + sizeof(DWORD));
393                 lmem += sizeof(DWORD) + sizeof(WORD);
394                 if (eid == MCI_COMMAND_HEAD)
395                     S_MciCmdTable[uTbl].aVerbs[count++] = str;
396             } while (eid != MCI_END_COMMAND_LIST);
397             /* assert(count == S_MciCmdTable[uTbl].nVerbs); */
398             return uTbl;
399         }
400     }
401
402     return MCI_NO_COMMAND_TABLE;
403 }
404
405 /**************************************************************************
406  *                              MCI_DeleteCommandTable          [internal]
407  */
408 static  BOOL    MCI_DeleteCommandTable(UINT uTbl)
409 {
410     if (uTbl >= MAX_MCICMDTABLE || !S_MciCmdTable[uTbl].hMem)
411         return FALSE;
412
413     FreeResource(S_MciCmdTable[uTbl].hMem);
414     S_MciCmdTable[uTbl].hMem = 0;
415     if (S_MciCmdTable[uTbl].aVerbs) {
416         HeapFree(GetProcessHeap(), 0, S_MciCmdTable[uTbl].aVerbs);
417         S_MciCmdTable[uTbl].aVerbs = 0;
418     }
419     return TRUE;
420 }
421
422 /**************************************************************************
423  *                              MCI_UnLoadMciDriver             [internal]
424  */
425 static  BOOL    MCI_UnLoadMciDriver(LPWINE_MM_IDATA iData, LPWINE_MCIDRIVER wmd)
426 {
427     LPWINE_MCIDRIVER*           tmp;
428
429     if (!wmd)
430         return TRUE;
431
432     CloseDriver(wmd->hDriver, 0, 0);
433
434     if (wmd->dwPrivate != 0)
435         WARN("Unloading mci driver with non nul dwPrivate field\n");
436
437     EnterCriticalSection(&iData->cs);
438     for (tmp = &iData->lpMciDrvs; *tmp; tmp = &(*tmp)->lpNext) {
439         if (*tmp == wmd) {
440             *tmp = wmd->lpNext;
441             break;
442         }
443     }
444     LeaveCriticalSection(&iData->cs);
445
446     HeapFree(GetProcessHeap(), 0, wmd->lpstrDeviceType);
447     HeapFree(GetProcessHeap(), 0, wmd->lpstrAlias);
448     HeapFree(GetProcessHeap(), 0, wmd->lpstrElementName);
449
450     HeapFree(GetProcessHeap(), 0, wmd);
451     return TRUE;
452 }
453
454 /**************************************************************************
455  *                              MCI_OpenMciDriver               [internal]
456  */
457 static  BOOL    MCI_OpenMciDriver(LPWINE_MCIDRIVER wmd, LPCSTR drvTyp, LPARAM lp)
458 {
459     char        libName[128];
460
461     if (!DRIVER_GetLibName(drvTyp, "mci", libName, sizeof(libName)))
462         return FALSE;
463
464     wmd->bIs32 = 0xFFFF;
465     /* First load driver */
466     if ((wmd->hDriver = (HDRVR)DRIVER_TryOpenDriver32(libName, lp))) {
467         wmd->bIs32 = TRUE;
468     } else {
469         MCI_MapType     res;
470
471         switch (res = MCI_MapMsg32ATo16(0, DRV_OPEN, 0, &lp)) {
472         case MCI_MAP_MSGERROR:
473             TRACE("Not handled yet (DRV_OPEN)\n");
474             break;
475         case MCI_MAP_NOMEM:
476             TRACE("Problem mapping msg=DRV_OPEN from 32a to 16\n");
477             break;
478         case MCI_MAP_OK:
479         case MCI_MAP_OKMEM:
480             if ((wmd->hDriver = OpenDriverA(drvTyp, "mci", lp)))
481                 wmd->bIs32 = FALSE;
482             if (res == MCI_MAP_OKMEM)
483                 MCI_UnMapMsg32ATo16(0, DRV_OPEN, 0, lp);
484             break;
485         }
486     }
487     return (wmd->bIs32 == 0xFFFF) ? FALSE : TRUE;
488 }
489
490 /**************************************************************************
491  *                              MCI_LoadMciDriver               [internal]
492  */
493 static  DWORD   MCI_LoadMciDriver(LPWINE_MM_IDATA iData, LPCSTR _strDevTyp,
494                                   LPWINE_MCIDRIVER* lpwmd)
495 {
496     LPSTR                       strDevTyp = str_dup_upper(_strDevTyp);
497     LPWINE_MCIDRIVER            wmd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wmd));
498     MCI_OPEN_DRIVER_PARMSA      modp;
499     DWORD                       dwRet = 0;
500
501     if (!wmd || !strDevTyp) {
502         dwRet = MCIERR_OUT_OF_MEMORY;
503         goto errCleanUp;
504     }
505
506     wmd->lpfnYieldProc = MCI_DefYieldProc;
507     wmd->dwYieldData = VK_CANCEL;
508     wmd->hCreatorTask = GetCurrentTask();
509
510     EnterCriticalSection(&iData->cs);
511     /* wmd must be inserted in list before sending opening the driver, coz' it
512      * may want to lookup at wDevID
513      */
514     wmd->lpNext = iData->lpMciDrvs;
515     iData->lpMciDrvs = wmd;
516
517     for (modp.wDeviceID = MCI_MAGIC;
518          MCI_GetDriver(modp.wDeviceID) != 0;
519          modp.wDeviceID++);
520
521     wmd->wDeviceID = modp.wDeviceID;
522
523     LeaveCriticalSection(&iData->cs);
524
525     TRACE("wDevID=%04X \n", modp.wDeviceID);
526
527     modp.lpstrParams = NULL;
528
529     if (!MCI_OpenMciDriver(wmd, strDevTyp, (LPARAM)&modp)) {
530         /* silence warning if all is used... some bogus program use commands like
531          * 'open all'...
532          */
533         if (strcasecmp(strDevTyp, "all") != 0) {
534             dwRet = MCIERR_CANNOT_USE_ALL;
535         } else {
536             FIXME("Couldn't load driver for type %s.\n"
537                   "If you don't have a windows installation accessible from Wine,\n"
538                   "you perhaps forgot to create a [mci] section in system.ini\n",
539                   strDevTyp);
540             dwRet = MCIERR_DEVICE_NOT_INSTALLED;
541         }
542         goto errCleanUp;
543     }
544
545     /* FIXME: should also check that module's description is of the form
546      * MODULENAME:[MCI] comment
547      */
548
549     /* some drivers will return 0x0000FFFF, some others 0xFFFFFFFF */
550     wmd->uSpecificCmdTable = LOWORD(modp.wCustomCommandTable);
551     wmd->uTypeCmdTable = MCI_COMMAND_TABLE_NOT_LOADED;
552
553     TRACE("Loaded driver %x (%s), type is %d, cmdTable=%08x\n",
554           wmd->hDriver, strDevTyp, modp.wType, modp.wCustomCommandTable);
555
556     wmd->lpstrDeviceType = strDevTyp;
557     wmd->wType = modp.wType;
558
559     TRACE("mcidev=%d, uDevTyp=%04X wDeviceID=%04X !\n",
560           modp.wDeviceID, modp.wType, modp.wDeviceID);
561     *lpwmd = wmd;
562     return 0;
563 errCleanUp:
564     MCI_UnLoadMciDriver(iData, wmd);
565     HeapFree(GetProcessHeap(), 0, strDevTyp);
566     *lpwmd = 0;
567     return dwRet;
568 }
569
570 /**************************************************************************
571  *                      MCI_FinishOpen                          [internal]
572  */
573 static  DWORD   MCI_FinishOpen(LPWINE_MCIDRIVER wmd, LPMCI_OPEN_PARMSA lpParms,
574                                DWORD dwParam)
575 {
576     if (dwParam & MCI_OPEN_ELEMENT)
577     {
578         wmd->lpstrElementName = HeapAlloc(GetProcessHeap(),0,strlen(lpParms->lpstrElementName)+1);
579         strcpy( wmd->lpstrElementName, lpParms->lpstrElementName );
580     }
581     if (dwParam & MCI_OPEN_ALIAS)
582     {
583         wmd->lpstrAlias = HeapAlloc(GetProcessHeap(), 0, strlen(lpParms->lpstrAlias)+1);
584         strcpy( wmd->lpstrAlias, lpParms->lpstrAlias);
585     }
586     lpParms->wDeviceID = wmd->wDeviceID;
587
588     return MCI_SendCommandFrom32(wmd->wDeviceID, MCI_OPEN_DRIVER, dwParam,
589                                  (DWORD)lpParms);
590 }
591
592 /**************************************************************************
593  *                              MCI_FindCommand         [internal]
594  */
595 static  LPCSTR          MCI_FindCommand(UINT uTbl, LPCSTR verb)
596 {
597     UINT        idx;
598
599     if (uTbl >= MAX_MCICMDTABLE || S_MciCmdTable[uTbl].hMem == 0)
600         return NULL;
601
602     /* another improvement would be to have the aVerbs array sorted,
603      * so that we could use a dichotomic search on it, rather than this dumb
604      * array look up
605      */
606     for (idx = 0; idx < S_MciCmdTable[uTbl].nVerbs; idx++) {
607         if (strcmp(S_MciCmdTable[uTbl].aVerbs[idx], verb) == 0)
608             return S_MciCmdTable[uTbl].aVerbs[idx];
609     }
610
611     return NULL;
612 }
613
614 /**************************************************************************
615  *                              MCI_GetReturnType               [internal]
616  */
617 static  DWORD           MCI_GetReturnType(LPCSTR lpCmd)
618 {
619     lpCmd += strlen(lpCmd) + 1 + sizeof(DWORD) + sizeof(WORD);
620     if (*lpCmd == '\0' && *(LPWORD)(lpCmd + 1 + sizeof(DWORD)) == MCI_RETURN) {
621         return *(LPDWORD)(lpCmd + 1);
622     }
623     return 0L;
624 }
625
626 /**************************************************************************
627  *                              MCI_GetMessage                  [internal]
628  */
629 static  WORD            MCI_GetMessage(LPCSTR lpCmd)
630 {
631     return (WORD)*(LPDWORD)(lpCmd + strlen(lpCmd) + 1);
632 }
633
634 /**************************************************************************
635  *                              MCI_GetDWord                    [internal]
636  */
637 static  BOOL            MCI_GetDWord(LPDWORD data, LPSTR* ptr)
638 {
639     DWORD       val;
640     LPSTR       ret;
641
642     val = strtoul(*ptr, &ret, 0);
643
644     switch (*ret) {
645     case '\0':  break;
646     case ' ':   ret++; break;
647     default:    return FALSE;
648     }
649
650     *data |= val;
651     *ptr = ret;
652     return TRUE;
653 }
654
655 /**************************************************************************
656  *                              MCI_GetString           [internal]
657  */
658 static  DWORD   MCI_GetString(LPSTR* str, LPSTR* args)
659 {
660     LPSTR       ptr = *args;
661
662     /* see if we have a quoted string */
663     if (*ptr == '"') {
664         ptr = strchr(*str = ptr + 1, '"');
665         if (!ptr) return MCIERR_NO_CLOSING_QUOTE;
666         /* FIXME: shall we escape \" from string ?? */
667         if (ptr[-1] == '\\') TRACE("Ooops: un-escaped \"\n");
668         *ptr++ = '\0'; /* remove trailing " */
669         if (*ptr != ' ' && *ptr != '\0') return MCIERR_EXTRA_CHARACTERS;
670         *ptr++ = '\0';
671     } else {
672         ptr = strchr(ptr, ' ');
673
674         if (ptr) {
675             *ptr++ = '\0';
676         } else {
677             ptr = *args + strlen(*args);
678         }
679         *str = *args;
680     }
681
682     *args = ptr;
683     return 0;
684 }
685
686 #define MCI_DATA_SIZE   16
687
688 /**************************************************************************
689  *                              MCI_ParseOptArgs                [internal]
690  */
691 static  DWORD   MCI_ParseOptArgs(LPDWORD data, int _offset, LPCSTR lpCmd,
692                                  LPSTR args, LPDWORD dwFlags)
693 {
694     int         len, offset;
695     LPCSTR      lmem, str;
696     DWORD       dwRet, flg, cflg = 0;
697     WORD        eid;
698     BOOL        inCst, found;
699
700     /* loop on arguments */
701     while (*args) {
702         lmem = lpCmd;
703         found = inCst = FALSE;
704         offset = _offset;
705
706         /* skip any leading white space(s) */
707         while (*args == ' ') args++;
708         TRACE("args='%s' offset=%d\n", args, offset);
709
710         do { /* loop on options for command table for the requested verb */
711             str = lmem;
712             lmem += (len = strlen(lmem)) + 1;
713             flg = *(LPDWORD)lmem;
714             eid = *(LPWORD)(lmem + sizeof(DWORD));
715             lmem += sizeof(DWORD) + sizeof(WORD);
716 /* EPP      TRACE("\tcmd='%s' inCst=%c eid=%04x\n", str, inCst ? 'Y' : 'N', eid); */
717
718             switch (eid) {
719             case MCI_CONSTANT:
720                 inCst = TRUE;   cflg = flg;     break;
721             case MCI_END_CONSTANT:
722                 /* there may be additional integral values after flag in constant */
723                 if (inCst && MCI_GetDWord(&(data[offset]), &args)) {
724                     *dwFlags |= cflg;
725                 }
726                 inCst = FALSE;  cflg = 0;
727                 break;
728             }
729
730             if (strncasecmp(args, str, len) == 0 &&
731                 (args[len] == 0 || args[len] == ' ')) {
732                 /* store good values into data[] */
733                 args += len;
734                 while (*args == ' ') args++;
735                 found = TRUE;
736
737                 switch (eid) {
738                 case MCI_COMMAND_HEAD:
739                 case MCI_RETURN:
740                 case MCI_END_COMMAND:
741                 case MCI_END_COMMAND_LIST:
742                 case MCI_CONSTANT:      /* done above */
743                 case MCI_END_CONSTANT:  /* done above */
744                     break;
745                 case MCI_FLAG:
746                     *dwFlags |= flg;
747                     break;
748                 case MCI_INTEGER:
749                     if (inCst) {
750                         data[offset] |= flg;
751                         *dwFlags |= cflg;
752                         inCst = FALSE;
753                     } else {
754                         *dwFlags |= flg;
755                         if (!MCI_GetDWord(&(data[offset]), &args)) {
756                             return MCIERR_BAD_INTEGER;
757                         }
758                     }
759                     break;
760                 case MCI_RECT:
761                     /* store rect in data (offset...offset+3) */
762                     *dwFlags |= flg;
763                     if (!MCI_GetDWord(&(data[offset+0]), &args) ||
764                         !MCI_GetDWord(&(data[offset+1]), &args) ||
765                         !MCI_GetDWord(&(data[offset+2]), &args) ||
766                         !MCI_GetDWord(&(data[offset+3]), &args)) {
767                         ERR("Bad rect '%s'\n", args);
768                         return MCIERR_BAD_INTEGER;
769                     }
770                     break;
771                 case MCI_STRING:
772                     *dwFlags |= flg;
773                     if ((dwRet = MCI_GetString((LPSTR*)&data[offset], &args)))
774                         return dwRet;
775                     break;
776                 default:        ERR("oops\n");
777                 }
778                 /* exit inside while loop, except if just entered in constant area definition */
779                 if (!inCst || eid != MCI_CONSTANT) eid = MCI_END_COMMAND;
780             } else {
781                 /* have offset incremented if needed */
782                 switch (eid) {
783                 case MCI_COMMAND_HEAD:
784                 case MCI_RETURN:
785                 case MCI_END_COMMAND:
786                 case MCI_END_COMMAND_LIST:
787                 case MCI_CONSTANT:
788                 case MCI_FLAG:                  break;
789                 case MCI_INTEGER:               if (!inCst) offset++;   break;
790                 case MCI_END_CONSTANT:
791                 case MCI_STRING:                offset++; break;
792                 case MCI_RECT:                  offset += 4; break;
793                 default:                        ERR("oops\n");
794                 }
795             }
796         } while (eid != MCI_END_COMMAND);
797         if (!found) {
798             WARN("Optarg '%s' not found\n", args);
799             return MCIERR_UNRECOGNIZED_COMMAND;
800         }
801         if (offset == MCI_DATA_SIZE) {
802             ERR("Internal data[] buffer overflow\n");
803             return MCIERR_PARSER_INTERNAL;
804         }
805     }
806     return 0;
807 }
808
809 /**************************************************************************
810  *                              MCI_HandleReturnValues  [internal]
811  */
812 static  DWORD   MCI_HandleReturnValues(LPWINE_MM_IDATA iData, DWORD dwRet,
813                                        LPWINE_MCIDRIVER wmd, LPCSTR lpCmd, LPDWORD data,
814                                        LPSTR lpstrRet, UINT uRetLen)
815 {
816     if (lpstrRet) {
817         switch (MCI_GetReturnType(lpCmd)) {
818         case 0: /* nothing to return */
819             break;
820         case MCI_INTEGER:
821             switch (dwRet & 0xFFFF0000ul) {
822             case 0:
823             case MCI_INTEGER_RETURNED:
824                 snprintf(lpstrRet, uRetLen, "%ld", data[1]);
825                 break;
826             case MCI_RESOURCE_RETURNED:
827                 /* return string which ID is HIWORD(data[1]),
828                  * string is loaded from mmsystem.dll */
829                 LoadStringA(iData->hWinMM32Instance, HIWORD(data[1]),
830                             lpstrRet, uRetLen);
831                 break;
832             case MCI_RESOURCE_RETURNED|MCI_RESOURCE_DRIVER:
833                 /* return string which ID is HIWORD(data[1]),
834                  * string is loaded from driver */
835                 /* FIXME: this is wrong for a 16 bit handle */
836                 LoadStringA(GetDriverModuleHandle(wmd->hDriver),
837                             HIWORD(data[1]), lpstrRet, uRetLen);
838                 break;
839             case MCI_COLONIZED3_RETURN:
840                 snprintf(lpstrRet, uRetLen, "%d:%d:%d",
841                          LOBYTE(LOWORD(data[1])), HIBYTE(LOWORD(data[1])),
842                          LOBYTE(HIWORD(data[1])));
843                 break;
844             case MCI_COLONIZED4_RETURN:
845                 snprintf(lpstrRet, uRetLen, "%d:%d:%d:%d",
846                          LOBYTE(LOWORD(data[1])), HIBYTE(LOWORD(data[1])),
847                          LOBYTE(HIWORD(data[1])), HIBYTE(HIWORD(data[1])));
848                 break;
849             default:    ERR("Ooops (%04X)\n", HIWORD(dwRet));
850             }
851             break;
852         case MCI_STRING:
853             switch (dwRet & 0xFFFF0000ul) {
854             case 0:
855                 /* nothing to do data[1] == lpstrRet */
856                 break;
857             case MCI_INTEGER_RETURNED:
858                 data[1] = *(LPDWORD)lpstrRet;
859                 snprintf(lpstrRet, uRetLen, "%ld", data[1]);
860                 break;
861             default:
862                 WARN("Oooch. MCI_STRING and HIWORD(dwRet)=%04x\n", HIWORD(dwRet));
863                 break;
864             }
865             break;
866         case MCI_RECT:
867             if (dwRet & 0xFFFF0000ul)
868                 WARN("Oooch. MCI_STRING and HIWORD(dwRet)=%04x\n", HIWORD(dwRet));
869             snprintf(lpstrRet, uRetLen, "%ld %ld %ld %ld",
870                        data[1], data[2], data[3], data[4]);
871             break;
872         default:                ERR("oops\n");
873         }
874     }
875     return LOWORD(dwRet);
876 }
877
878 /**************************************************************************
879  *                              mciSendStringA          [WINMM.@]
880  */
881 DWORD WINAPI mciSendStringA(LPCSTR lpstrCommand, LPSTR lpstrRet,
882                             UINT uRetLen, HWND hwndCallback)
883 {
884     LPSTR               verb, dev, args;
885     LPWINE_MCIDRIVER    wmd = 0;
886     DWORD               dwFlags = 0, dwRet = 0;
887     int                 offset = 0;
888     DWORD               data[MCI_DATA_SIZE];
889     LPCSTR              lpCmd = 0;
890     LPSTR               devAlias = NULL;
891     LPWINE_MM_IDATA     iData = MULTIMEDIA_GetIData();
892     BOOL                bAutoOpen = FALSE;
893
894     TRACE("('%s', %p, %d, %X)\n", lpstrCommand, lpstrRet, uRetLen, hwndCallback);
895
896     /* format is <command> <device> <optargs> */
897     if (!(verb = HeapAlloc(GetProcessHeap(), 0, strlen(lpstrCommand)+1)))
898         return MCIERR_OUT_OF_MEMORY;
899     strcpy( verb, lpstrCommand );
900
901     memset(data, 0, sizeof(data));
902
903     if (!(args = strchr(verb, ' '))) {
904         dwRet = MCIERR_MISSING_DEVICE_NAME;
905         goto errCleanUp;
906     }
907     *args++ = '\0';
908     if ((dwRet = MCI_GetString(&dev, &args))) {
909         goto errCleanUp;
910     }
911
912     /* case dev == 'new' has to be handled */
913     if (!strcasecmp(dev, "new")) {
914         FIXME("'new': NIY as device name\n");
915         dwRet = MCIERR_MISSING_DEVICE_NAME;
916         goto errCleanUp;
917     }
918
919     /* otherwise, try to grab devType from open */
920     if (!strcmp(verb, "open")) {
921         LPSTR   devType, tmp;
922
923         if ((devType = strchr(dev, '!')) != NULL) {
924             *devType++ = '\0';
925             tmp = devType; devType = dev; dev = tmp;
926
927             dwFlags |= MCI_OPEN_TYPE;
928             data[2] = (DWORD)devType;
929             devType = str_dup_upper(devType);
930             dwFlags |= MCI_OPEN_ELEMENT;
931             data[3] = (DWORD)dev;
932         } else if (strchr(dev, '.') == NULL) {
933             tmp = strchr(dev,' ');
934             if (tmp) *tmp = '\0';
935             data[2] = (DWORD)dev;
936             devType = str_dup_upper(dev);
937             if (tmp) *tmp = ' ';
938             dwFlags |= MCI_OPEN_TYPE;
939         } else {
940             if ((devType = strstr(args, "type ")) != NULL) {
941                 devType += 5;
942                 tmp = strchr(devType, ' ');
943                 if (tmp) *tmp = '\0';
944                 devType = str_dup_upper(devType);
945                 if (tmp) *tmp = ' ';
946                 /* dwFlags and data[2] will be correctly set in ParseOpt loop */
947             } else {
948                 char    buf[32];
949                 if ((dwRet = MCI_GetDevTypeFromFileName(dev, buf, sizeof(buf))))
950                     goto errCleanUp;
951
952                 devType = str_dup_upper(buf);
953             }
954             dwFlags |= MCI_OPEN_ELEMENT;
955             data[3] = (DWORD)dev;
956         }
957         if ((devAlias = strstr(args," alias "))) {
958             char *tmp2;
959             devAlias += 7;
960             if (!(tmp = strchr(devAlias,' '))) tmp = devAlias + strlen(devAlias);
961             if (tmp) *tmp = '\0';
962             tmp2 = HeapAlloc(GetProcessHeap(), 0, tmp - devAlias + 1 );
963             memcpy( tmp2, devAlias, tmp - devAlias );
964             tmp2[tmp - devAlias] = 0;
965             data[4] = (DWORD)tmp2;
966             /* should be done in regular options parsing */
967             /* dwFlags |= MCI_OPEN_ALIAS; */
968         }
969
970         dwRet = MCI_LoadMciDriver(iData, devType, &wmd);
971         HeapFree(GetProcessHeap(), 0, devType);
972         if (dwRet) {
973             MCI_UnLoadMciDriver(iData, wmd);
974             goto errCleanUp;
975         }
976     } else if (!(wmd = MCI_GetDriver(mciGetDeviceIDA(dev)))) {
977         /* auto open */
978         char    buf[128];
979         sprintf(buf, "open %s wait", dev);
980
981         if ((dwRet = mciSendStringA(buf, NULL, 0, 0)) != 0)
982             goto errCleanUp;
983
984         wmd = MCI_GetDriver(mciGetDeviceIDA(dev));
985         if (!wmd) {
986             /* FIXME: memory leak, MCI driver is not closed */
987             dwRet = MCIERR_INVALID_DEVICE_ID;
988             goto errCleanUp;
989         }
990     }
991
992     /* get the verb in the different command tables */
993     if (wmd) {
994         /* try the device specific command table */
995         lpCmd = MCI_FindCommand(wmd->uSpecificCmdTable, verb);
996         if (!lpCmd) {
997             /* try the type specific command table */
998             if (wmd->uTypeCmdTable == MCI_COMMAND_TABLE_NOT_LOADED)
999                 wmd->uTypeCmdTable = MCI_GetCommandTable(iData, wmd->wType);
1000             if (wmd->uTypeCmdTable != MCI_NO_COMMAND_TABLE)
1001                 lpCmd = MCI_FindCommand(wmd->uTypeCmdTable, verb);
1002         }
1003     }
1004     /* try core command table */
1005     if (!lpCmd) lpCmd = MCI_FindCommand(MCI_GetCommandTable(iData, 0), verb);
1006
1007     if (!lpCmd) {
1008         TRACE("Command '%s' not found!\n", verb);
1009         dwRet = MCIERR_UNRECOGNIZED_COMMAND;
1010         goto errCleanUp;
1011     }
1012
1013     /* set up call back */
1014     if (hwndCallback != 0) {
1015         dwFlags |= MCI_NOTIFY;
1016         data[0] = (DWORD)hwndCallback;
1017     }
1018
1019     /* set return information */
1020     switch (MCI_GetReturnType(lpCmd)) {
1021     case 0:             offset = 1;     break;
1022     case MCI_INTEGER:   offset = 2;     break;
1023     case MCI_STRING:    data[1] = (DWORD)lpstrRet; data[2] = uRetLen; offset = 3; break;
1024     case MCI_RECT:      offset = 5;     break;
1025     default:    ERR("oops\n");
1026     }
1027
1028     TRACE("verb='%s' on dev='%s'; offset=%d\n", verb, dev, offset);
1029
1030     if ((dwRet = MCI_ParseOptArgs(data, offset, lpCmd, args, &dwFlags)))
1031         goto errCleanUp;
1032
1033     if (bAutoOpen && (dwFlags & MCI_NOTIFY)) {
1034         dwRet = MCIERR_NOTIFY_ON_AUTO_OPEN;
1035         goto errCleanUp;
1036     }
1037     /* FIXME: the command should get it's own notification window set up and
1038      * ask for device closing while processing the notification mechanism
1039      */
1040     if (lpstrRet && uRetLen) *lpstrRet = '\0';
1041
1042 #define STR_OF(_x) (IsBadReadPtr((char*)_x,1)?"?":(char*)(_x))
1043     TRACE("[%d, %s, %08lx, %08lx/%s %08lx/%s %08lx/%s %08lx/%s %08lx/%s %08lx/%s]\n",
1044           wmd->wDeviceID, MCI_MessageToString(MCI_GetMessage(lpCmd)), dwFlags,
1045           data[0], STR_OF(data[0]), data[1], STR_OF(data[1]),
1046           data[2], STR_OF(data[2]), data[3], STR_OF(data[3]),
1047           data[4], STR_OF(data[4]), data[5], STR_OF(data[5]));
1048 #undef STR_OF
1049
1050     if (strcmp(verb, "open") == 0) {
1051         if ((dwRet = MCI_FinishOpen(wmd, (LPMCI_OPEN_PARMSA)data, dwFlags)))
1052             MCI_UnLoadMciDriver(iData, wmd);
1053         /* FIXME: notification is not properly shared across two opens */
1054     } else {
1055         dwRet = MCI_SendCommand(wmd->wDeviceID, MCI_GetMessage(lpCmd), dwFlags, (DWORD)data, TRUE);
1056     }
1057     TRACE("=> 1/ %lx (%s)\n", dwRet, lpstrRet);
1058     dwRet = MCI_HandleReturnValues(iData, dwRet, wmd, lpCmd, data, lpstrRet, uRetLen);
1059     TRACE("=> 2/ %lx (%s)\n", dwRet, lpstrRet);
1060
1061 errCleanUp:
1062     HeapFree(GetProcessHeap(), 0, verb);
1063     HeapFree(GetProcessHeap(), 0, devAlias);
1064     return dwRet;
1065 }
1066
1067 /**************************************************************************
1068  *                              mciSendStringW                  [WINMM.@]
1069  */
1070 DWORD WINAPI mciSendStringW(LPCWSTR lpwstrCommand, LPSTR lpstrRet,
1071                             UINT uRetLen, HWND hwndCallback)
1072 {
1073     LPSTR       lpstrCommand;
1074     UINT        ret;
1075     INT len;
1076
1077     /* FIXME: is there something to do with lpstrReturnString ? */
1078     len = WideCharToMultiByte( CP_ACP, 0, lpwstrCommand, -1, NULL, 0, NULL, NULL );
1079     lpstrCommand = HeapAlloc( GetProcessHeap(), 0, len );
1080     WideCharToMultiByte( CP_ACP, 0, lpwstrCommand, -1, lpstrCommand, len, NULL, NULL );
1081     ret = mciSendStringA(lpstrCommand, lpstrRet, uRetLen, hwndCallback);
1082     HeapFree(GetProcessHeap(), 0, lpstrCommand);
1083     return ret;
1084 }
1085
1086 /**************************************************************************
1087  *                              mciSendString                   [MMSYSTEM.702]
1088  */
1089 DWORD WINAPI mciSendString16(LPCSTR lpstrCommand, LPSTR lpstrRet,
1090                              UINT16 uRetLen, HWND16 hwndCallback)
1091 {
1092     return mciSendStringA(lpstrCommand, lpstrRet, uRetLen, hwndCallback);
1093 }
1094
1095 /**************************************************************************
1096  *                              mciExecute                      [WINMM.@]
1097  *                              mciExecute                      [MMSYSTEM.712]
1098  */
1099 DWORD WINAPI mciExecute(LPCSTR lpstrCommand)
1100 {
1101     char        strRet[256];
1102     DWORD       ret;
1103
1104     TRACE("(%s)!\n", lpstrCommand);
1105
1106     ret = mciSendStringA(lpstrCommand, strRet, sizeof(strRet), 0);
1107     if (ret != 0) {
1108         if (!mciGetErrorStringA(ret, strRet, sizeof(strRet))) {
1109             sprintf(strRet, "Unknown MCI error (%ld)", ret);
1110         }
1111         MessageBoxA(0, strRet, "Error in mciExecute()", MB_OK);
1112     }
1113     /* FIXME: what shall I return ? */
1114     return 0;
1115 }
1116
1117 /**************************************************************************
1118  *                      mciLoadCommandResource                  [MMSYSTEM.705]
1119  */
1120 UINT16 WINAPI mciLoadCommandResource16(HANDLE16 hInst, LPCSTR resname, UINT16 type)
1121 {
1122     HRSRC               hRsrc = 0;
1123     HGLOBAL             hMem;
1124     UINT16              ret = MCI_NO_COMMAND_TABLE;
1125     LPWINE_MM_IDATA     iData = MULTIMEDIA_GetIData();
1126
1127     TRACE("(%04x, %s, %d)!\n", hInst, resname, type);
1128
1129     /* if file exists "resname.mci", then load resource "resname" from it
1130      * otherwise directly from driver
1131      * We don't support it (who uses this feature ?), but we check anyway
1132      */
1133     if (!type) {
1134         char            buf[128];
1135         OFSTRUCT        ofs;
1136
1137         strcat(strcpy(buf, resname), ".mci");
1138         if (OpenFile(buf, &ofs, OF_EXIST) != HFILE_ERROR) {
1139             FIXME("NIY: command table to be loaded from '%s'\n", ofs.szPathName);
1140         }
1141     }
1142     if (!(hRsrc = FindResourceA(hInst, resname, (LPCSTR)RT_RCDATAA))) {
1143         WARN("No command table found in resource\n");
1144     } else if ((hMem = LoadResource(hInst, hRsrc))) {
1145         ret = MCI_SetCommandTable(iData, hMem, type);
1146     } else {
1147         WARN("Couldn't load resource.\n");
1148     }
1149     TRACE("=> %04x\n", ret);
1150     return ret;
1151 }
1152
1153 /**************************************************************************
1154  *                      mciFreeCommandResource                  [MMSYSTEM.713]
1155  */
1156 BOOL16 WINAPI mciFreeCommandResource16(UINT16 uTable)
1157 {
1158     TRACE("(%04x)!\n", uTable);
1159
1160     return MCI_DeleteCommandTable(uTable);
1161 }
1162
1163 /**************************************************************************
1164  *                      mciLoadCommandResource                  [WINMM.@]
1165  *
1166  * Strangely, this function only exists as an UNICODE one.
1167  */
1168 UINT WINAPI mciLoadCommandResource(HINSTANCE hInst, LPCWSTR resNameW, UINT type)
1169 {
1170     HRSRC               hRsrc = 0;
1171     HGLOBAL             hMem;
1172     UINT16              ret = MCI_NO_COMMAND_TABLE;
1173     LPWINE_MM_IDATA     iData = MULTIMEDIA_GetIData();
1174
1175     TRACE("(%04x, %s, %d)!\n", hInst, debugstr_w(resNameW), type);
1176
1177 #if 0
1178     /* if file exists "resname.mci", then load resource "resname" from it
1179      * otherwise directly from driver
1180      * We don't support it (who uses this feature ?), but we check anyway
1181      */
1182     if (!type) {
1183         char            buf[128];
1184         OFSTRUCT        ofs;
1185
1186         strcat(strcpy(buf, resname), ".mci");
1187         if (OpenFile(buf, &ofs, OF_EXIST) != HFILE_ERROR) {
1188             FIXME("NIY: command table to be loaded from '%s'\n", ofs.szPathName);
1189         }
1190     }
1191 #endif
1192     if (!(hRsrc = FindResourceW(hInst, resNameW, (LPCWSTR)RT_RCDATAA))) {
1193         WARN("No command table found in resource\n");
1194     } else if ((hMem = LoadResource(hInst, hRsrc))) {
1195         ret = MCI_SetCommandTable(iData, hMem, type);
1196     } else {
1197         WARN("Couldn't load resource.\n");
1198     }
1199     TRACE("=> %04x\n", ret);
1200     return ret;
1201 }
1202
1203 #if 0
1204     LPSTR       resNameA;
1205     UINT        ret;
1206
1207     TRACE("(%04x, %s, %d)!\n", hinst, debugstr_w(resNameW), type);
1208
1209     resNameA = HEAP_strdupWtoA(GetProcessHeap(), 0, resNameW);
1210     ret = mciLoadCommandResource16(hinst, resNameA, type);
1211     HeapFree(GetProcessHeap(), 0, resNameA);
1212     return ret;
1213 #endif
1214
1215 /**************************************************************************
1216  *                      mciFreeCommandResource                  [WINMM.@]
1217  */
1218 BOOL WINAPI mciFreeCommandResource(UINT uTable)
1219 {
1220     TRACE("(%08x)!\n", uTable);
1221
1222     return MCI_DeleteCommandTable(uTable);
1223 }
1224
1225 /**************************************************************************
1226  *                      MCI_MapMsg16To32A                       [internal]
1227  */
1228 static  MCI_MapType     MCI_MapMsg16To32A(WORD uDevType, WORD wMsg, DWORD* lParam)
1229 {
1230     if (*lParam == 0)
1231         return MCI_MAP_OK;
1232     /* FIXME: to add also (with seg/linear modifications to do):
1233      * MCI_LIST, MCI_LOAD, MCI_QUALITY, MCI_RESERVE, MCI_RESTORE, MCI_SAVE
1234      * MCI_SETAUDIO, MCI_SETTUNER, MCI_SETVIDEO
1235      */
1236     switch (wMsg) {
1237         /* case MCI_CAPTURE */
1238     case MCI_CLOSE:
1239     case MCI_CLOSE_DRIVER:
1240     case MCI_CONFIGURE:
1241     case MCI_COPY:
1242     case MCI_CUE:
1243     case MCI_CUT:
1244     case MCI_DELETE:
1245     case MCI_FREEZE:
1246     case MCI_GETDEVCAPS:
1247         /* case MCI_INDEX: */
1248         /* case MCI_MARK: */
1249         /* case MCI_MONITOR: */
1250     case MCI_PASTE:
1251     case MCI_PAUSE:
1252     case MCI_PLAY:
1253     case MCI_PUT:
1254     case MCI_REALIZE:
1255     case MCI_RECORD:
1256     case MCI_RESUME:
1257     case MCI_SEEK:
1258     case MCI_SET:
1259         /* case MCI_SETTIMECODE:*/
1260         /* case MCI_SIGNAL:*/
1261     case MCI_SPIN:
1262     case MCI_STATUS:            /* FIXME: is wrong for digital video */
1263     case MCI_STEP:
1264     case MCI_STOP:
1265         /* case MCI_UNDO: */
1266     case MCI_UNFREEZE:
1267     case MCI_UPDATE:
1268     case MCI_WHERE:
1269         *lParam = (DWORD)MapSL(*lParam);
1270         return MCI_MAP_OK;
1271     case MCI_WINDOW:
1272         /* in fact, I would also need the dwFlags... to see
1273          * which members of lParam are effectively used
1274          */
1275         *lParam = (DWORD)MapSL(*lParam);
1276         FIXME("Current mapping may be wrong\n");
1277         break;
1278     case MCI_BREAK:
1279         {
1280             LPMCI_BREAK_PARMS           mbp32 = HeapAlloc(GetProcessHeap(), 0, sizeof(MCI_BREAK_PARMS));
1281             LPMCI_BREAK_PARMS16         mbp16 = MapSL(*lParam);
1282
1283             if (mbp32) {
1284                 mbp32->dwCallback = mbp16->dwCallback;
1285                 mbp32->nVirtKey = mbp16->nVirtKey;
1286                 mbp32->hwndBreak = mbp16->hwndBreak;
1287             } else {
1288                 return MCI_MAP_NOMEM;
1289             }
1290             *lParam = (DWORD)mbp32;
1291         }
1292         return MCI_MAP_OKMEM;
1293     case MCI_ESCAPE:
1294         {
1295             LPMCI_VD_ESCAPE_PARMSA      mvep32a = HeapAlloc(GetProcessHeap(), 0, sizeof(MCI_VD_ESCAPE_PARMSA));
1296             LPMCI_VD_ESCAPE_PARMS16     mvep16  = MapSL(*lParam);
1297
1298             if (mvep32a) {
1299                 mvep32a->dwCallback       = mvep16->dwCallback;
1300                 mvep32a->lpstrCommand     = MapSL(mvep16->lpstrCommand);
1301             } else {
1302                 return MCI_MAP_NOMEM;
1303             }
1304             *lParam = (DWORD)mvep32a;
1305         }
1306         return MCI_MAP_OKMEM;
1307     case MCI_INFO:
1308         {
1309             LPMCI_INFO_PARMSA   mip32a = HeapAlloc(GetProcessHeap(), 0, sizeof(MCI_INFO_PARMSA));
1310             LPMCI_INFO_PARMS16  mip16  = MapSL(*lParam);
1311
1312             /* FIXME this is wrong if device is of type
1313              * MCI_DEVTYPE_DIGITAL_VIDEO, some members are not mapped
1314              */
1315             if (mip32a) {
1316                 mip32a->dwCallback  = mip16->dwCallback;
1317                 mip32a->lpstrReturn = MapSL(mip16->lpstrReturn);
1318                 mip32a->dwRetSize   = mip16->dwRetSize;
1319             } else {
1320                 return MCI_MAP_NOMEM;
1321             }
1322             *lParam = (DWORD)mip32a;
1323         }
1324         return MCI_MAP_OKMEM;
1325     case MCI_OPEN:
1326     case MCI_OPEN_DRIVER:
1327         {
1328             LPMCI_OPEN_PARMSA   mop32a = HeapAlloc(GetProcessHeap(), 0, sizeof(LPMCI_OPEN_PARMS16) + sizeof(MCI_OPEN_PARMSA) + 2 * sizeof(DWORD));
1329             LPMCI_OPEN_PARMS16  mop16  = MapSL(*lParam);
1330
1331             if (mop32a) {
1332                 *(LPMCI_OPEN_PARMS16*)(mop32a) = mop16;
1333                 mop32a = (LPMCI_OPEN_PARMSA)((char*)mop32a + sizeof(LPMCI_OPEN_PARMS16));
1334                 mop32a->dwCallback       = mop16->dwCallback;
1335                 mop32a->wDeviceID        = mop16->wDeviceID;
1336                 mop32a->lpstrDeviceType  = MapSL(mop16->lpstrDeviceType);
1337                 mop32a->lpstrElementName = MapSL(mop16->lpstrElementName);
1338                 mop32a->lpstrAlias       = MapSL(mop16->lpstrAlias);
1339                 /* copy extended information if any...
1340                  * FIXME: this may seg fault if initial structure does not contain them and
1341                  * the reads after msip16 fail under LDT limits...
1342                  * NOTE: this should be split in two. First pass, while calling MCI_OPEN, and
1343                  * should not take care of extended parameters, and should be used by MCI_Open
1344                  * to fetch uDevType. When, this is known, the mapping for sending the
1345                  * MCI_OPEN_DRIVER shall be done depending on uDevType.
1346                  */
1347                 memcpy(mop32a + 1, mop16 + 1, 2 * sizeof(DWORD));
1348             } else {
1349                 return MCI_MAP_NOMEM;
1350             }
1351             *lParam = (DWORD)mop32a;
1352         }
1353         return MCI_MAP_OKMEM;
1354     case MCI_SYSINFO:
1355         {
1356             LPMCI_SYSINFO_PARMSA        msip32a = HeapAlloc(GetProcessHeap(), 0, sizeof(MCI_SYSINFO_PARMSA));
1357             LPMCI_SYSINFO_PARMS16       msip16  = MapSL(*lParam);
1358
1359             if (msip32a) {
1360                 msip32a->dwCallback       = msip16->dwCallback;
1361                 msip32a->lpstrReturn      = MapSL(msip16->lpstrReturn);
1362                 msip32a->dwRetSize        = msip16->dwRetSize;
1363                 msip32a->dwNumber         = msip16->dwNumber;
1364                 msip32a->wDeviceType      = msip16->wDeviceType;
1365             } else {
1366                 return MCI_MAP_NOMEM;
1367             }
1368             *lParam = (DWORD)msip32a;
1369         }
1370         return MCI_MAP_OKMEM;
1371     case DRV_LOAD:
1372     case DRV_ENABLE:
1373     case DRV_OPEN:
1374     case DRV_CLOSE:
1375     case DRV_DISABLE:
1376     case DRV_FREE:
1377     case DRV_CONFIGURE:
1378     case DRV_QUERYCONFIGURE:
1379     case DRV_INSTALL:
1380     case DRV_REMOVE:
1381     case DRV_EXITSESSION:
1382     case DRV_EXITAPPLICATION:
1383     case DRV_POWER:
1384         FIXME("This is a hack\n");
1385         return MCI_MAP_OK;
1386
1387     default:
1388         WARN("Don't know how to map msg=%s\n", MCI_MessageToString(wMsg));
1389     }
1390     return MCI_MAP_MSGERROR;
1391 }
1392
1393 /**************************************************************************
1394  *                      MCI_UnMapMsg16To32A                     [internal]
1395  */
1396 static  MCI_MapType     MCI_UnMapMsg16To32A(WORD uDevType, WORD wMsg, DWORD lParam)
1397 {
1398     switch (wMsg) {
1399         /* case MCI_CAPTURE */
1400     case MCI_CLOSE:
1401     case MCI_CLOSE_DRIVER:
1402     case MCI_CONFIGURE:
1403     case MCI_COPY:
1404     case MCI_CUE:
1405     case MCI_CUT:
1406     case MCI_DELETE:
1407     case MCI_FREEZE:
1408     case MCI_GETDEVCAPS:
1409         /* case MCI_INDEX: */
1410         /* case MCI_MARK: */
1411         /* case MCI_MONITOR: */
1412     case MCI_PASTE:
1413     case MCI_PAUSE:
1414     case MCI_PLAY:
1415     case MCI_PUT:
1416     case MCI_REALIZE:
1417     case MCI_RECORD:
1418     case MCI_RESUME:
1419     case MCI_SEEK:
1420     case MCI_SET:
1421         /* case MCI_SETTIMECODE:*/
1422         /* case MCI_SIGNAL:*/
1423     case MCI_SPIN:
1424     case MCI_STATUS:
1425     case MCI_STEP:
1426     case MCI_STOP:
1427         /* case MCI_UNDO: */
1428     case MCI_UNFREEZE:
1429     case MCI_UPDATE:
1430     case MCI_WHERE:
1431         return MCI_MAP_OK;
1432
1433     case MCI_WINDOW:
1434         /* FIXME ?? see Map function */
1435         return MCI_MAP_OK;
1436
1437     case MCI_BREAK:
1438     case MCI_ESCAPE:
1439     case MCI_INFO:
1440     case MCI_SYSINFO:
1441         HeapFree(GetProcessHeap(), 0, (LPVOID)lParam);
1442         return MCI_MAP_OK;
1443     case MCI_OPEN:
1444     case MCI_OPEN_DRIVER:
1445         if (lParam) {
1446             LPMCI_OPEN_PARMSA   mop32a = (LPMCI_OPEN_PARMSA)lParam;
1447             LPMCI_OPEN_PARMS16  mop16  = *(LPMCI_OPEN_PARMS16*)((char*)mop32a - sizeof(LPMCI_OPEN_PARMS16));
1448
1449             mop16->wDeviceID = mop32a->wDeviceID;
1450             if (!HeapFree(GetProcessHeap(), 0, (LPVOID)(lParam - sizeof(LPMCI_OPEN_PARMS16))))
1451                 FIXME("bad free line=%d\n", __LINE__);
1452         }
1453         return MCI_MAP_OK;
1454     case DRV_LOAD:
1455     case DRV_ENABLE:
1456     case DRV_OPEN:
1457     case DRV_CLOSE:
1458     case DRV_DISABLE:
1459     case DRV_FREE:
1460     case DRV_CONFIGURE:
1461     case DRV_QUERYCONFIGURE:
1462     case DRV_INSTALL:
1463     case DRV_REMOVE:
1464     case DRV_EXITSESSION:
1465     case DRV_EXITAPPLICATION:
1466     case DRV_POWER:
1467         FIXME("This is a hack\n");
1468         return MCI_MAP_OK;
1469     default:
1470         FIXME("Map/Unmap internal error on msg=%s\n", MCI_MessageToString(wMsg));
1471     }
1472     return MCI_MAP_MSGERROR;
1473 }
1474
1475 /*
1476  * 0000 stop
1477  * 0001 squeeze   signed 4 bytes to 2 bytes     *( LPINT16)D = ( INT16)*( LPINT16)S; D += 2;     S += 4
1478  * 0010 squeeze unsigned 4 bytes to 2 bytes     *(LPUINT16)D = (UINT16)*(LPUINT16)S; D += 2;     S += 4
1479  * 0100
1480  * 0101
1481  * 0110 zero 4 bytes                            *(DWORD)D = 0                        D += 4;     S += 4
1482  * 0111 copy string                             *(LPSTR*)D = seg dup(*(LPSTR*)S)     D += 4;     S += 4
1483  * 1xxx copy xxx + 1 bytes                      memcpy(D, S, xxx + 1);               D += xxx+1; S += xxx+1
1484  */
1485
1486 /**************************************************************************
1487  *                      MCI_MsgMapper32To16_Create              [internal]
1488  *
1489  * Helper for MCI_MapMsg32ATo16.
1490  * Maps the 32 bit pointer (*ptr), of size bytes, to an allocated 16 bit
1491  * segmented pointer.
1492  * map contains a list of action to be performed for the mapping (see list
1493  * above)
1494  * if keep is TRUE, keeps track of in 32 bit ptr in allocated 16 bit area.
1495  */
1496 static  MCI_MapType     MCI_MsgMapper32To16_Create(void** ptr, int size16, DWORD map, BOOLEAN keep)
1497 {
1498     void*       lp = HeapAlloc( GetProcessHeap(), 0, (keep ? sizeof(void**) : 0) + size16 );
1499     LPBYTE      p16, p32;
1500
1501     if (!lp) {
1502         return MCI_MAP_NOMEM;
1503     }
1504     p32 = (LPBYTE)(*ptr);
1505     if (keep) {
1506         *(void**)lp = *ptr;
1507         p16 = (LPBYTE)lp + sizeof(void**);
1508         *ptr = (char*)MapLS(lp) + sizeof(void**);
1509     } else {
1510         p16 = lp;
1511         *ptr = (void*)MapLS(lp);
1512     }
1513
1514     if (map == 0) {
1515         memcpy(p16, p32, size16);
1516     } else {
1517         unsigned        nibble;
1518         unsigned        sz;
1519
1520         while (map & 0xF) {
1521             nibble = map & 0xF;
1522             if (nibble & 0x8) {
1523                 sz = (nibble & 7) + 1;
1524                 memcpy(p16, p32, sz);
1525                 p16 += sz;
1526                 p32 += sz;
1527                 size16 -= sz;   /* DEBUG only */
1528             } else {
1529                 switch (nibble) {
1530                 case 0x1:
1531                     *(LPINT16)p16 = *(LPINT)p32;
1532                     p16 += sizeof(INT16);
1533                     p32 += sizeof(INT);
1534                     size16 -= sizeof(INT16);
1535                     break;
1536                 case 0x2:
1537                     *(LPUINT16)p16 = *(LPUINT)p32;
1538                     p16 += sizeof(UINT16);
1539                     p32 += sizeof(UINT);
1540                     size16 -= sizeof(UINT16);
1541                     break;
1542                 case 0x6:
1543                     *(LPDWORD)p16 = 0;
1544                     p16 += sizeof(DWORD);
1545                     p32 += sizeof(DWORD);
1546                     size16 -= sizeof(DWORD);
1547                     break;
1548                 case 0x7:
1549                     *(SEGPTR *)p16 = MapLS( *(LPSTR *)p32 );
1550                     p16 += sizeof(SEGPTR);
1551                     p32 += sizeof(LPSTR);
1552                     size16 -= sizeof(SEGPTR);
1553                     break;
1554                 default:
1555                     FIXME("Unknown nibble for mapping (%x)\n", nibble);
1556                 }
1557             }
1558             map >>= 4;
1559         }
1560         if (size16 != 0) /* DEBUG only */
1561             FIXME("Mismatch between 16 bit struct size and map nibbles serie\n");
1562     }
1563     return MCI_MAP_OKMEM;
1564 }
1565
1566 /**************************************************************************
1567  *                      MCI_MsgMapper32To16_Destroy             [internal]
1568  *
1569  * Helper for MCI_UnMapMsg32ATo16.
1570  */
1571 static  MCI_MapType     MCI_MsgMapper32To16_Destroy(void* ptr, int size16, DWORD map, BOOLEAN kept)
1572 {
1573     if (ptr) {
1574         void*           msg16 = MapSL((SEGPTR)ptr);
1575         void*           alloc;
1576         LPBYTE          p32, p16;
1577         unsigned        nibble;
1578
1579         UnMapLS( (SEGPTR)ptr );
1580         if (kept) {
1581             alloc = (char*)msg16 - sizeof(void**);
1582             p32 = *(void**)alloc;
1583             p16 = msg16;
1584
1585             if (map == 0) {
1586                 memcpy(p32, p16, size16);
1587             } else {
1588                 while (map & 0xF) {
1589                     nibble = map & 0xF;
1590                     if (nibble & 0x8) {
1591                         memcpy(p32, p16, (nibble & 7) + 1);
1592                         p16 += (nibble & 7) + 1;
1593                         p32 += (nibble & 7) + 1;
1594                         size16 -= (nibble & 7) + 1;
1595                     } else {
1596                         switch (nibble) {
1597                         case 0x1:
1598                             *(LPINT)p32 = *(LPINT16)p16;
1599                             p16 += sizeof(INT16);
1600                             p32 += sizeof(INT);
1601                             size16 -= sizeof(INT16);
1602                             break;
1603                         case 0x2:
1604                             *(LPUINT)p32 = *(LPUINT16)p16;
1605                             p16 += sizeof(UINT16);
1606                             p32 += sizeof(UINT);
1607                             size16 -= sizeof(UINT16);
1608                             break;
1609                         case 0x6:
1610                             p16 += sizeof(UINT);
1611                             p32 += sizeof(UINT);
1612                             size16 -= sizeof(UINT);
1613                             break;
1614                         case 0x7:
1615                             UnMapLS( *(SEGPTR *)p16 );
1616                             p16 += sizeof(SEGPTR);
1617                             p32 += sizeof(char*);
1618                             size16 -= sizeof(SEGPTR);
1619                             break;
1620                         default:
1621                             FIXME("Unknown nibble for mapping (%x)\n", nibble);
1622                         }
1623                     }
1624                     map >>= 4;
1625                 }
1626                 if (size16 != 0) /* DEBUG only */
1627                     FIXME("Mismatch between 16 bit struct size and map nibbles serie\n");
1628             }
1629         } else {
1630             alloc = msg16;
1631         }
1632
1633         HeapFree( GetProcessHeap(), 0, alloc );
1634     }
1635     return MCI_MAP_OK;
1636 }
1637
1638 /**************************************************************************
1639  *                      MCI_MapMsg32ATo16                       [internal]
1640  *
1641  * Map a 32-A bit MCI message to a 16 bit MCI message.
1642  */
1643 static  MCI_MapType     MCI_MapMsg32ATo16(WORD uDevType, WORD wMsg, DWORD dwFlags, DWORD* lParam)
1644 {
1645     int         size;
1646     BOOLEAN     keep = FALSE;
1647     DWORD       map = 0;
1648
1649     if (*lParam == 0)
1650         return MCI_MAP_OK;
1651
1652     /* FIXME: to add also (with seg/linear modifications to do):
1653      * MCI_LIST, MCI_LOAD, MCI_QUALITY, MCI_RESERVE, MCI_RESTORE, MCI_SAVE
1654      * MCI_SETAUDIO, MCI_SETTUNER, MCI_SETVIDEO
1655      */
1656     switch (wMsg) {
1657     case MCI_BREAK:
1658         size = sizeof(MCI_BREAK_PARMS);
1659         break;
1660         /* case MCI_CAPTURE */
1661     case MCI_CLOSE:
1662     case MCI_CLOSE_DRIVER:
1663     case MCI_CONFIGURE:
1664         size = sizeof(MCI_GENERIC_PARMS);
1665         break;
1666         /* case MCI_COPY: */
1667     case MCI_CUE:
1668         switch (uDevType) {
1669         case MCI_DEVTYPE_DIGITAL_VIDEO: size = sizeof(MCI_DGV_CUE_PARMS);       break;
1670         case MCI_DEVTYPE_VCR:           /*size = sizeof(MCI_VCR_CUE_PARMS);     break;*/        FIXME("NIY vcr\n");     return MCI_MAP_NOMEM;
1671         default:                        size = sizeof(MCI_GENERIC_PARMS);       break;
1672         }
1673         break;
1674         /* case MCI_CUT:*/
1675     case MCI_DELETE:
1676         switch (uDevType) {
1677         case MCI_DEVTYPE_DIGITAL_VIDEO: size = sizeof(MCI_DGV_DELETE_PARMS16);  map = 0x0F1111FB;       break;
1678         case MCI_DEVTYPE_WAVEFORM_AUDIO:size = sizeof(MCI_WAVE_DELETE_PARMS);   break;
1679         default:                        size = sizeof(MCI_GENERIC_PARMS);       break;
1680         }
1681         break;
1682         /* case MCI_ESCAPE: */
1683     case MCI_FREEZE:
1684         switch (uDevType) {
1685         case MCI_DEVTYPE_DIGITAL_VIDEO: size = sizeof(MCI_DGV_FREEZE_PARMS);    map = 0x0001111B;       break;
1686         case MCI_DEVTYPE_OVERLAY:       size = sizeof(MCI_OVLY_RECT_PARMS);     map = 0x0001111B;       break;
1687         default:                        size = sizeof(MCI_GENERIC_PARMS);       break;
1688         }
1689         break;
1690     case MCI_GETDEVCAPS:
1691         keep = TRUE;
1692         size = sizeof(MCI_GETDEVCAPS_PARMS);
1693         break;
1694         /* case MCI_INDEX: */
1695     case MCI_INFO:
1696         {
1697             LPMCI_INFO_PARMSA   mip32a = (LPMCI_INFO_PARMSA)(*lParam);
1698             LPMCI_INFO_PARMS16  mip16;
1699
1700             switch (uDevType) {
1701             case MCI_DEVTYPE_DIGITAL_VIDEO:     size = sizeof(MCI_DGV_INFO_PARMS16);    break;
1702             default:                            size = sizeof(MCI_INFO_PARMS16);        break;
1703             }
1704             mip16 = HeapAlloc( GetProcessHeap(), 0, size);
1705             if (mip16)
1706             {
1707                 mip16->dwCallback  = mip32a->dwCallback;
1708                 mip16->lpstrReturn = MapLS( mip32a->lpstrReturn );
1709                 mip16->dwRetSize   = mip32a->dwRetSize;
1710                 if (uDevType == MCI_DEVTYPE_DIGITAL_VIDEO) {
1711                     ((LPMCI_DGV_INFO_PARMS16)mip16)->dwItem = ((LPMCI_DGV_INFO_PARMSA)mip32a)->dwItem;
1712                 }
1713             } else {
1714                 return MCI_MAP_NOMEM;
1715             }
1716             *lParam = MapLS(mip16);
1717         }
1718         return MCI_MAP_OKMEM;
1719         /* case MCI_MARK: */
1720         /* case MCI_MONITOR: */
1721     case MCI_OPEN:
1722     case MCI_OPEN_DRIVER:
1723         {
1724             LPMCI_OPEN_PARMSA   mop32a = (LPMCI_OPEN_PARMSA)(*lParam);
1725             char* ptr = HeapAlloc( GetProcessHeap(), 0,
1726                                    sizeof(LPMCI_OPEN_PARMSA) + sizeof(MCI_OPEN_PARMS16) + 2 * sizeof(DWORD));
1727             LPMCI_OPEN_PARMS16  mop16;
1728
1729
1730             if (ptr) {
1731                 *(LPMCI_OPEN_PARMSA*)(ptr) = mop32a;
1732                 mop16 = (LPMCI_OPEN_PARMS16)(ptr + sizeof(LPMCI_OPEN_PARMSA));
1733                 mop16->dwCallback       = mop32a->dwCallback;
1734                 mop16->wDeviceID        = mop32a->wDeviceID;
1735                 if (dwFlags & MCI_OPEN_TYPE) {
1736                     if (dwFlags & MCI_OPEN_TYPE_ID) {
1737                         /* dword "transparent" value */
1738                         mop16->lpstrDeviceType = (SEGPTR)mop32a->lpstrDeviceType;
1739                     } else {
1740                         /* string */
1741                         mop16->lpstrDeviceType = MapLS( mop32a->lpstrDeviceType );
1742                     }
1743                 } else {
1744                     /* nuthin' */
1745                     mop16->lpstrDeviceType = 0;
1746                 }
1747                 if (dwFlags & MCI_OPEN_ELEMENT) {
1748                     if (dwFlags & MCI_OPEN_ELEMENT_ID) {
1749                         mop16->lpstrElementName = (SEGPTR)mop32a->lpstrElementName;
1750                     } else {
1751                         mop16->lpstrElementName = MapLS( mop32a->lpstrElementName );
1752                     }
1753                 } else {
1754                     mop16->lpstrElementName = 0;
1755                 }
1756                 if (dwFlags & MCI_OPEN_ALIAS) {
1757                     mop16->lpstrAlias = MapLS( mop32a->lpstrAlias );
1758                 } else {
1759                     mop16->lpstrAlias = 0;
1760                 }
1761                 /* copy extended information if any...
1762                  * FIXME: this may seg fault if initial structure does not contain them and
1763                  * the reads after msip16 fail under LDT limits...
1764                  * NOTE: this should be split in two. First pass, while calling MCI_OPEN, and
1765                  * should not take care of extended parameters, and should be used by MCI_Open
1766                  * to fetch uDevType. When, this is known, the mapping for sending the
1767                  * MCI_OPEN_DRIVER shall be done depending on uDevType.
1768                  */
1769                 memcpy(mop16 + 1, mop32a + 1, 2 * sizeof(DWORD));
1770             } else {
1771                 return MCI_MAP_NOMEM;
1772             }
1773             *lParam = (LPARAM)MapLS(ptr) + sizeof(LPMCI_OPEN_PARMSA);
1774         }
1775         return MCI_MAP_OKMEM;
1776         /* case MCI_PASTE:*/
1777     case MCI_PAUSE:
1778         size = sizeof(MCI_GENERIC_PARMS);
1779         break;
1780     case MCI_PLAY:
1781         size = sizeof(MCI_PLAY_PARMS);
1782         break;
1783     case MCI_PUT:
1784         switch (uDevType) {
1785         case MCI_DEVTYPE_DIGITAL_VIDEO: size = sizeof(MCI_DGV_RECT_PARMS16);    map = 0x0001111B;       break;
1786         case MCI_DEVTYPE_OVERLAY:       size = sizeof(MCI_OVLY_RECT_PARMS);     map = 0x0001111B;       break;
1787         default:                        size = sizeof(MCI_GENERIC_PARMS);       break;
1788         }
1789         break;
1790     case MCI_REALIZE:
1791         size = sizeof(MCI_GENERIC_PARMS);
1792         break;
1793     case MCI_RECORD:
1794         switch (uDevType) {
1795         case MCI_DEVTYPE_DIGITAL_VIDEO: size = sizeof(MCI_DGV_RECORD_PARMS16);  map = 0x0F1111FB;       break;
1796         case MCI_DEVTYPE_VCR:           /*size = sizeof(MCI_VCR_RECORD_PARMS);  break;*/FIXME("NIY vcr\n");     return MCI_MAP_NOMEM;
1797         default:                        size = sizeof(MCI_RECORD_PARMS);        break;
1798         }
1799         break;
1800     case MCI_RESUME:
1801         size = sizeof(MCI_GENERIC_PARMS);
1802         break;
1803     case MCI_SEEK:
1804         switch (uDevType) {
1805         case MCI_DEVTYPE_VCR:           /*size = sizeof(MCI_VCR_SEEK_PARMS);    break;*/FIXME("NIY vcr\n");     return MCI_MAP_NOMEM;
1806         default:                        size = sizeof(MCI_SEEK_PARMS);          break;
1807         }
1808         break;
1809     case MCI_SET:
1810         switch (uDevType) {
1811         case MCI_DEVTYPE_DIGITAL_VIDEO: size = sizeof(MCI_DGV_SET_PARMS);       break;
1812         case MCI_DEVTYPE_VCR:           /*size = sizeof(MCI_VCR_SET_PARMS);     break;*/FIXME("NIY vcr\n");     return MCI_MAP_NOMEM;
1813         case MCI_DEVTYPE_SEQUENCER:     size = sizeof(MCI_SEQ_SET_PARMS);       break;
1814         /* FIXME: normally the 16 and 32 bit structures are byte by byte aligned,
1815          * so not doing anything should work...
1816          */
1817         case MCI_DEVTYPE_WAVEFORM_AUDIO:size = sizeof(MCI_WAVE_SET_PARMS);      break;
1818         default:                        size = sizeof(MCI_SET_PARMS);           break;
1819         }
1820         break;
1821     case MCI_SETAUDIO:
1822         switch (uDevType) {
1823         case MCI_DEVTYPE_DIGITAL_VIDEO: size = sizeof(MCI_DGV_SETAUDIO_PARMS16);map = 0x0000077FF;      break;
1824         case MCI_DEVTYPE_VCR:           /*size = sizeof(MCI_VCR_SETAUDIO_PARMS);        break;*/FIXME("NIY vcr\n");     return MCI_MAP_NOMEM;
1825         default:                        size = sizeof(MCI_GENERIC_PARMS);       break;
1826         }
1827         break;
1828         /* case MCI_SETTIMECODE:*/
1829         /* case MCI_SIGNAL:*/
1830     case MCI_SPIN:
1831         size = sizeof(MCI_SET_PARMS);
1832         break;
1833     case MCI_STATUS:
1834         keep = TRUE;
1835         switch (uDevType) {
1836         /* FIXME:
1837          * don't know if buffer for value is the one passed through lpstrDevice
1838          * or is provided by MCI driver.
1839          * Assuming solution 2: provided by MCI driver, so zeroing on entry
1840          */
1841         case MCI_DEVTYPE_DIGITAL_VIDEO: size = sizeof(MCI_DGV_STATUS_PARMS16);  map = 0x0B6FF;          break;
1842         case MCI_DEVTYPE_VCR:           /*size = sizeof(MCI_VCR_STATUS_PARMS);  break;*/FIXME("NIY vcr\n");     return MCI_MAP_NOMEM;
1843         default:                        size = sizeof(MCI_STATUS_PARMS);        break;
1844         }
1845         break;
1846     case MCI_STEP:
1847         switch (uDevType) {
1848         case MCI_DEVTYPE_DIGITAL_VIDEO: size = sizeof(MCI_DGV_STEP_PARMS);      break;
1849         case MCI_DEVTYPE_VCR:           /*size = sizeof(MCI_VCR_STEP_PARMS);    break;*/FIXME("NIY vcr\n");     return MCI_MAP_NOMEM;
1850         case MCI_DEVTYPE_VIDEODISC:     size = sizeof(MCI_VD_STEP_PARMS);       break;
1851         default:                        size = sizeof(MCI_GENERIC_PARMS);       break;
1852         }
1853         break;
1854     case MCI_STOP:
1855         size = sizeof(MCI_SET_PARMS);
1856         break;
1857     case MCI_SYSINFO:
1858         {
1859             LPMCI_SYSINFO_PARMSA  msip32a = (LPMCI_SYSINFO_PARMSA)(*lParam);
1860             LPMCI_SYSINFO_PARMS16 msip16;
1861             char* ptr = HeapAlloc( GetProcessHeap(), 0,
1862                                    sizeof(LPMCI_SYSINFO_PARMSA) + sizeof(MCI_SYSINFO_PARMS16) );
1863
1864             if (ptr) {
1865                 *(LPMCI_SYSINFO_PARMSA*)(ptr) = msip32a;
1866                 msip16 = (LPMCI_SYSINFO_PARMS16)(ptr + sizeof(LPMCI_SYSINFO_PARMSA));
1867
1868                 msip16->dwCallback       = msip32a->dwCallback;
1869                 msip16->lpstrReturn      = MapLS( msip32a->lpstrReturn );
1870                 msip16->dwRetSize        = msip32a->dwRetSize;
1871                 msip16->dwNumber         = msip32a->dwNumber;
1872                 msip16->wDeviceType      = msip32a->wDeviceType;
1873             } else {
1874                 return MCI_MAP_NOMEM;
1875             }
1876             *lParam = (LPARAM)MapLS(ptr) + sizeof(LPMCI_SYSINFO_PARMSA);
1877         }
1878         return MCI_MAP_OKMEM;
1879         /* case MCI_UNDO: */
1880     case MCI_UNFREEZE:
1881         switch (uDevType) {
1882         case MCI_DEVTYPE_DIGITAL_VIDEO: size = sizeof(MCI_DGV_RECT_PARMS16);    map = 0x0001111B;       break;
1883         case MCI_DEVTYPE_OVERLAY:       size = sizeof(MCI_OVLY_RECT_PARMS16);   map = 0x0001111B;       break;
1884         default:                        size = sizeof(MCI_GENERIC_PARMS);       break;
1885         }
1886         break;
1887     case MCI_UPDATE:
1888         switch (uDevType) {
1889         case MCI_DEVTYPE_DIGITAL_VIDEO: size = sizeof(MCI_DGV_UPDATE_PARMS16);  map = 0x000B1111B;      break;
1890         default:                        size = sizeof(MCI_GENERIC_PARMS);       break;
1891         }
1892         break;
1893     case MCI_WHERE:
1894         switch (uDevType) {
1895         case MCI_DEVTYPE_DIGITAL_VIDEO: size = sizeof(MCI_DGV_RECT_PARMS16);    map = 0x0001111B;       keep = TRUE;    break;
1896         case MCI_DEVTYPE_OVERLAY:       size = sizeof(MCI_OVLY_RECT_PARMS16);   map = 0x0001111B;       keep = TRUE;    break;
1897         default:                        size = sizeof(MCI_GENERIC_PARMS);       break;
1898         }
1899         break;
1900     case MCI_WINDOW:
1901         switch (uDevType) {
1902         case MCI_DEVTYPE_DIGITAL_VIDEO: size = sizeof(MCI_DGV_WINDOW_PARMS16);  if (dwFlags & MCI_DGV_WINDOW_TEXT)  map = 0x7FB;        break;
1903         case MCI_DEVTYPE_OVERLAY:       size = sizeof(MCI_OVLY_WINDOW_PARMS16); if (dwFlags & MCI_OVLY_WINDOW_TEXT) map = 0x7FB;        break;
1904         default:                        size = sizeof(MCI_GENERIC_PARMS);       break;
1905         }
1906         break;
1907     case DRV_OPEN:
1908         {
1909             LPMCI_OPEN_DRIVER_PARMSA  modp32a = (LPMCI_OPEN_DRIVER_PARMSA)(*lParam);
1910             LPMCI_OPEN_DRIVER_PARMS16 modp16;
1911             char *ptr = HeapAlloc( GetProcessHeap(), 0,
1912                                   sizeof(LPMCI_OPEN_DRIVER_PARMSA) + sizeof(MCI_OPEN_DRIVER_PARMS16));
1913
1914             if (ptr) {
1915                 *(LPMCI_OPEN_DRIVER_PARMSA*)(ptr) = modp32a;
1916                 modp16 = (LPMCI_OPEN_DRIVER_PARMS16)(ptr + sizeof(LPMCI_OPEN_DRIVER_PARMSA));
1917                 modp16->wDeviceID = modp32a->wDeviceID;
1918                 modp16->lpstrParams = MapLS( modp32a->lpstrParams );
1919                 /* other fields are gonna be filled by the driver, don't copy them */
1920             } else {
1921                 return MCI_MAP_NOMEM;
1922             }
1923             *lParam = (LPARAM)MapLS(ptr) + sizeof(LPMCI_OPEN_DRIVER_PARMSA);
1924         }
1925         return MCI_MAP_OKMEM;
1926     case DRV_LOAD:
1927     case DRV_ENABLE:
1928     case DRV_CLOSE:
1929     case DRV_DISABLE:
1930     case DRV_FREE:
1931     case DRV_CONFIGURE:
1932     case DRV_QUERYCONFIGURE:
1933     case DRV_INSTALL:
1934     case DRV_REMOVE:
1935     case DRV_EXITSESSION:
1936     case DRV_EXITAPPLICATION:
1937     case DRV_POWER:
1938         return MCI_MAP_OK;
1939
1940     default:
1941         WARN("Don't know how to map msg=%s\n", MCI_MessageToString(wMsg));
1942         return MCI_MAP_MSGERROR;
1943     }
1944     return MCI_MsgMapper32To16_Create((void**)lParam, size, map, keep);
1945 }
1946
1947 /**************************************************************************
1948  *                      MCI_UnMapMsg32ATo16                     [internal]
1949  */
1950 static  MCI_MapType     MCI_UnMapMsg32ATo16(WORD uDevType, WORD wMsg, DWORD dwFlags, DWORD lParam)
1951 {
1952     int         size = 0;
1953     BOOLEAN     kept = FALSE;   /* there is no need to compute size when kept is FALSE */
1954     DWORD       map = 0;
1955
1956     switch (wMsg) {
1957     case MCI_BREAK:
1958         break;
1959         /* case MCI_CAPTURE */
1960     case MCI_CLOSE:
1961     case MCI_CLOSE_DRIVER:
1962     case MCI_CONFIGURE:
1963         break;
1964         /* case MCI_COPY: */
1965     case MCI_CUE:
1966         break;
1967         /* case MCI_CUT: */
1968     case MCI_DELETE:
1969         break;
1970         /* case MCI_ESCAPE: */
1971     case MCI_FREEZE:
1972         break;
1973     case MCI_GETDEVCAPS:
1974         kept = TRUE;
1975         size = sizeof(MCI_GETDEVCAPS_PARMS);
1976         break;
1977         /* case MCI_INDEX: */
1978     case MCI_INFO:
1979         {
1980             LPMCI_INFO_PARMS16 mip16  = (LPMCI_INFO_PARMS16)MapSL(lParam);
1981             UnMapLS( lParam );
1982             UnMapLS( mip16->lpstrReturn );
1983             HeapFree( GetProcessHeap(), 0, mip16 );
1984         }
1985         return MCI_MAP_OK;
1986         /* case MCI_MARK: */
1987         /* case MCI_MONITOR: */
1988     case MCI_OPEN:
1989     case MCI_OPEN_DRIVER:
1990         if (lParam) {
1991             LPMCI_OPEN_PARMS16  mop16  = (LPMCI_OPEN_PARMS16)MapSL(lParam);
1992             LPMCI_OPEN_PARMSA   mop32a = *(LPMCI_OPEN_PARMSA*)((char*)mop16 - sizeof(LPMCI_OPEN_PARMSA));
1993             UnMapLS( lParam );
1994             mop32a->wDeviceID = mop16->wDeviceID;
1995             if ((dwFlags & MCI_OPEN_TYPE) && !(dwFlags & MCI_OPEN_TYPE_ID))
1996                 UnMapLS( mop16->lpstrDeviceType );
1997             if ((dwFlags & MCI_OPEN_ELEMENT) && !(dwFlags & MCI_OPEN_ELEMENT_ID))
1998                 UnMapLS( mop16->lpstrElementName );
1999             if (dwFlags & MCI_OPEN_ALIAS)
2000                 UnMapLS( mop16->lpstrAlias );
2001             HeapFree( GetProcessHeap(), 0, (char*)mop16 - sizeof(LPMCI_OPEN_PARMSA) );
2002         }
2003         return MCI_MAP_OK;
2004         /* case MCI_PASTE:*/
2005     case MCI_PAUSE:
2006         break;
2007     case MCI_PLAY:
2008         break;
2009     case MCI_PUT:
2010         break;
2011     case MCI_REALIZE:
2012         break;
2013     case MCI_RECORD:
2014         break;
2015     case MCI_RESUME:
2016         break;
2017     case MCI_SEEK:
2018         break;
2019     case MCI_SET:
2020         break;
2021     case MCI_SETAUDIO:
2022         switch (uDevType) {
2023         case MCI_DEVTYPE_DIGITAL_VIDEO: map = 0x0000077FF;      break;
2024         case MCI_DEVTYPE_VCR:           /*size = sizeof(MCI_VCR_SETAUDIO_PARMS);        break;*/FIXME("NIY vcr\n");     return MCI_MAP_NOMEM;
2025         }
2026         break;
2027         /* case MCI_SETTIMECODE:*/
2028         /* case MCI_SIGNAL:*/
2029     case MCI_SPIN:
2030         break;
2031     case MCI_STATUS:
2032         kept = TRUE;
2033         switch (uDevType) {
2034         case MCI_DEVTYPE_DIGITAL_VIDEO:
2035         if (lParam) {
2036             LPMCI_DGV_STATUS_PARMS16    mdsp16  = (LPMCI_DGV_STATUS_PARMS16)MapSL(lParam);
2037             LPMCI_DGV_STATUS_PARMSA     mdsp32a = *(LPMCI_DGV_STATUS_PARMSA*)((char*)mdsp16 - sizeof(LPMCI_DGV_STATUS_PARMSA));
2038
2039             UnMapLS( lParam );
2040             if (mdsp16) {
2041                 mdsp32a->dwReturn = mdsp16->dwReturn;
2042                 if (dwFlags & MCI_DGV_STATUS_DISKSPACE) {
2043                     TRACE("MCI_STATUS (DGV) lpstrDrive=%08lx\n", mdsp16->lpstrDrive);
2044                     TRACE("MCI_STATUS (DGV) lpstrDrive=%s\n", (LPSTR)MapSL(mdsp16->lpstrDrive));
2045                     UnMapLS( mdsp16->lpstrDrive );
2046                 }
2047                 HeapFree( GetProcessHeap(), 0, (char*)mdsp16 - sizeof(LPMCI_DGV_STATUS_PARMSA) );
2048             } else {
2049                 return MCI_MAP_NOMEM;
2050             }
2051         }
2052         return MCI_MAP_OKMEM;
2053         case MCI_DEVTYPE_VCR:           /*size = sizeof(MCI_VCR_STATUS_PARMS);  break;*/FIXME("NIY vcr\n");     return MCI_MAP_NOMEM;
2054         default:                        size = sizeof(MCI_STATUS_PARMS);        break;
2055         }
2056         break;
2057     case MCI_STEP:
2058         break;
2059     case MCI_STOP:
2060         break;
2061     case MCI_SYSINFO:
2062         if (lParam) {
2063             LPMCI_SYSINFO_PARMS16       msip16  = (LPMCI_SYSINFO_PARMS16)MapSL(lParam);
2064             LPMCI_SYSINFO_PARMSA        msip32a = *(LPMCI_SYSINFO_PARMSA*)((char*)msip16 - sizeof(LPMCI_SYSINFO_PARMSA));
2065
2066             UnMapLS( lParam );
2067             if (msip16) {
2068                 msip16->dwCallback = msip32a->dwCallback;
2069                 UnMapLS( msip16->lpstrReturn );
2070                 HeapFree( GetProcessHeap(), 0, (char*)msip16 - sizeof(LPMCI_SYSINFO_PARMSA) );
2071             } else {
2072                 return MCI_MAP_NOMEM;
2073             }
2074         }
2075         return MCI_MAP_OKMEM;
2076         /* case MCI_UNDO: */
2077     case MCI_UNFREEZE:
2078         break;
2079     case MCI_UPDATE:
2080         break;
2081     case MCI_WHERE:
2082         switch (uDevType) {
2083         case MCI_DEVTYPE_DIGITAL_VIDEO: size = sizeof(MCI_DGV_RECT_PARMS16);    map = 0x0001111B;       kept = TRUE;    break;
2084         case MCI_DEVTYPE_OVERLAY:       size = sizeof(MCI_OVLY_RECT_PARMS16);   map = 0x0001111B;       kept = TRUE;    break;
2085         default:                        break;
2086         }
2087         break;
2088     case MCI_WINDOW:
2089         switch (uDevType) {
2090         case MCI_DEVTYPE_DIGITAL_VIDEO: size = sizeof(MCI_DGV_WINDOW_PARMS16);  if (dwFlags & MCI_DGV_WINDOW_TEXT)  map = 0x7666;       break;
2091         case MCI_DEVTYPE_OVERLAY:       size = sizeof(MCI_OVLY_WINDOW_PARMS16); if (dwFlags & MCI_OVLY_WINDOW_TEXT) map = 0x7666;       break;
2092         default:                        break;
2093         }
2094         /* FIXME: see map function */
2095         break;
2096
2097     case DRV_OPEN:
2098         if (lParam) {
2099             LPMCI_OPEN_DRIVER_PARMS16   modp16  = (LPMCI_OPEN_DRIVER_PARMS16)MapSL(lParam);
2100             LPMCI_OPEN_DRIVER_PARMSA    modp32a = *(LPMCI_OPEN_DRIVER_PARMSA*)((char*)modp16 - sizeof(LPMCI_OPEN_DRIVER_PARMSA));
2101
2102             UnMapLS( lParam );
2103             modp32a->wCustomCommandTable = modp16->wCustomCommandTable;
2104             modp32a->wType = modp16->wType;
2105             UnMapLS( modp16->lpstrParams );
2106             HeapFree( GetProcessHeap(), 0, (char *)modp16 - sizeof(LPMCI_OPEN_DRIVER_PARMSA) );
2107         }
2108         return MCI_MAP_OK;
2109     case DRV_LOAD:
2110     case DRV_ENABLE:
2111     case DRV_CLOSE:
2112     case DRV_DISABLE:
2113     case DRV_FREE:
2114     case DRV_CONFIGURE:
2115     case DRV_QUERYCONFIGURE:
2116     case DRV_INSTALL:
2117     case DRV_REMOVE:
2118     case DRV_EXITSESSION:
2119     case DRV_EXITAPPLICATION:
2120     case DRV_POWER:
2121         FIXME("This is a hack\n");
2122         return MCI_MAP_OK;
2123     default:
2124         FIXME("Map/Unmap internal error on msg=%s\n", MCI_MessageToString(wMsg));
2125         return MCI_MAP_MSGERROR;
2126     }
2127     return MCI_MsgMapper32To16_Destroy((void*)lParam, size, map, kept);
2128 }
2129
2130 /**************************************************************************
2131  *                      MCI_SendCommandFrom32                   [internal]
2132  */
2133 DWORD MCI_SendCommandFrom32(UINT wDevID, UINT16 wMsg, DWORD dwParam1, DWORD dwParam2)
2134 {
2135     DWORD               dwRet = MCIERR_INVALID_DEVICE_ID;
2136     LPWINE_MCIDRIVER    wmd = MCI_GetDriver(wDevID);
2137
2138     if (wmd) {
2139         if (wmd->bIs32) {
2140             dwRet = SendDriverMessage(wmd->hDriver, wMsg, dwParam1, dwParam2);
2141         } else {
2142             MCI_MapType res;
2143
2144             switch (res = MCI_MapMsg32ATo16(wmd->wType, wMsg, dwParam1, &dwParam2)) {
2145             case MCI_MAP_MSGERROR:
2146                 TRACE("Not handled yet (%s)\n", MCI_MessageToString(wMsg));
2147                 dwRet = MCIERR_DRIVER_INTERNAL;
2148                 break;
2149             case MCI_MAP_NOMEM:
2150                 TRACE("Problem mapping msg=%s from 32a to 16\n", MCI_MessageToString(wMsg));
2151                 dwRet = MCIERR_OUT_OF_MEMORY;
2152                 break;
2153             case MCI_MAP_OK:
2154             case MCI_MAP_OKMEM:
2155                 dwRet = SendDriverMessage(wmd->hDriver, wMsg, dwParam1, dwParam2);
2156                 if (res == MCI_MAP_OKMEM)
2157                     MCI_UnMapMsg32ATo16(wmd->wType, wMsg, dwParam1, dwParam2);
2158                 break;
2159             }
2160         }
2161     }
2162     return dwRet;
2163 }
2164
2165 /**************************************************************************
2166  *                      MCI_SendCommandFrom16                   [internal]
2167  */
2168 DWORD MCI_SendCommandFrom16(UINT wDevID, UINT16 wMsg, DWORD dwParam1, DWORD dwParam2)
2169 {
2170     DWORD               dwRet = MCIERR_INVALID_DEVICE_ID;
2171     LPWINE_MCIDRIVER    wmd = MCI_GetDriver(wDevID);
2172
2173     if (wmd) {
2174         dwRet = MCIERR_INVALID_DEVICE_ID;
2175
2176         if (wmd->bIs32) {
2177             MCI_MapType         res;
2178
2179             switch (res = MCI_MapMsg16To32A(wmd->wType, wMsg, &dwParam2)) {
2180             case MCI_MAP_MSGERROR:
2181                 TRACE("Not handled yet (%s)\n", MCI_MessageToString(wMsg));
2182                 dwRet = MCIERR_DRIVER_INTERNAL;
2183                 break;
2184             case MCI_MAP_NOMEM:
2185                 TRACE("Problem mapping msg=%s from 16 to 32a\n", MCI_MessageToString(wMsg));
2186                 dwRet = MCIERR_OUT_OF_MEMORY;
2187                 break;
2188             case MCI_MAP_OK:
2189             case MCI_MAP_OKMEM:
2190                 dwRet = SendDriverMessage(wmd->hDriver, wMsg, dwParam1, dwParam2);
2191                 if (res == MCI_MAP_OKMEM)
2192                     MCI_UnMapMsg16To32A(wmd->wType, wMsg, dwParam2);
2193                 break;
2194             }
2195         } else {
2196             dwRet = SendDriverMessage(wmd->hDriver, wMsg, dwParam1, dwParam2);
2197         }
2198     }
2199     return dwRet;
2200 }
2201
2202 /**************************************************************************
2203  *                      MCI_Open                                [internal]
2204  */
2205 static  DWORD MCI_Open(DWORD dwParam, LPMCI_OPEN_PARMSA lpParms)
2206 {
2207     char                        strDevTyp[128];
2208     DWORD                       dwRet;
2209     LPWINE_MCIDRIVER            wmd = NULL;
2210     LPWINE_MM_IDATA             iData = MULTIMEDIA_GetIData();
2211
2212     TRACE("(%08lX, %p)\n", dwParam, lpParms);
2213     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
2214
2215     /* only two low bytes are generic, the other ones are dev type specific */
2216 #define WINE_MCIDRIVER_SUPP     (0xFFFF0000|MCI_OPEN_SHAREABLE|MCI_OPEN_ELEMENT| \
2217                          MCI_OPEN_ALIAS|MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID| \
2218                          MCI_NOTIFY|MCI_WAIT)
2219     if ((dwParam & ~WINE_MCIDRIVER_SUPP) != 0) {
2220         FIXME("Unsupported yet dwFlags=%08lX\n", dwParam & ~WINE_MCIDRIVER_SUPP);
2221     }
2222 #undef WINE_MCIDRIVER_SUPP
2223
2224     strDevTyp[0] = 0;
2225
2226     if (dwParam & MCI_OPEN_TYPE) {
2227         if (dwParam & MCI_OPEN_TYPE_ID) {
2228             WORD uDevType = LOWORD((DWORD)lpParms->lpstrDeviceType);
2229
2230             if (uDevType < MCI_DEVTYPE_FIRST ||
2231                 uDevType > MCI_DEVTYPE_LAST ||
2232                 !LoadStringA(iData->hWinMM32Instance, uDevType, strDevTyp, sizeof(strDevTyp))) {
2233                 dwRet = MCIERR_BAD_INTEGER;
2234                 goto errCleanUp;
2235             }
2236         } else {
2237             LPSTR       ptr;
2238             if (lpParms->lpstrDeviceType == NULL) {
2239                 dwRet = MCIERR_NULL_PARAMETER_BLOCK;
2240                 goto errCleanUp;
2241             }
2242             strcpy(strDevTyp, lpParms->lpstrDeviceType);
2243             ptr = strchr(strDevTyp, '!');
2244             if (ptr) {
2245                 /* this behavior is not documented in windows. However, since, in
2246                  * some occasions, MCI_OPEN handling is translated by WinMM into
2247                  * a call to mciSendString("open <type>"); this code shall be correct
2248                  */
2249                 if (dwParam & MCI_OPEN_ELEMENT) {
2250                     ERR("Both MCI_OPEN_ELEMENT(%s) and %s are used\n",
2251                         lpParms->lpstrElementName, strDevTyp);
2252                     dwRet = MCIERR_UNRECOGNIZED_KEYWORD;
2253                     goto errCleanUp;
2254                 }
2255                 dwParam |= MCI_OPEN_ELEMENT;
2256                 *ptr++ = 0;
2257                 /* FIXME: not a good idea to write in user supplied buffer */
2258                 lpParms->lpstrElementName = ptr;
2259             }
2260
2261         }
2262         TRACE("devType='%s' !\n", strDevTyp);
2263     }
2264
2265     if (dwParam & MCI_OPEN_ELEMENT) {
2266         TRACE("lpstrElementName='%s'\n", lpParms->lpstrElementName);
2267
2268         if (dwParam & MCI_OPEN_ELEMENT_ID) {
2269             FIXME("Unsupported yet flag MCI_OPEN_ELEMENT_ID\n");
2270             dwRet = MCIERR_UNRECOGNIZED_KEYWORD;
2271             goto errCleanUp;
2272         }
2273
2274         if (!lpParms->lpstrElementName) {
2275             dwRet = MCIERR_NULL_PARAMETER_BLOCK;
2276             goto errCleanUp;
2277         }
2278
2279         /* type, if given as a parameter, supersedes file extension */
2280         if (!strDevTyp[0] &&
2281             MCI_GetDevTypeFromFileName(lpParms->lpstrElementName,
2282                                        strDevTyp, sizeof(strDevTyp))) {
2283             if (GetDriveTypeA(lpParms->lpstrElementName) != DRIVE_CDROM) {
2284                 dwRet = MCIERR_EXTENSION_NOT_FOUND;
2285                 goto errCleanUp;
2286             }
2287             /* FIXME: this will not work if several CDROM drives are installed on the machine */
2288             strcpy(strDevTyp, "CDAUDIO");
2289         }
2290     }
2291
2292     if (strDevTyp[0] == 0) {
2293         FIXME("Couldn't load driver\n");
2294         dwRet = MCIERR_INVALID_DEVICE_NAME;
2295         goto errCleanUp;
2296     }
2297
2298     if (dwParam & MCI_OPEN_ALIAS) {
2299         TRACE("Alias='%s' !\n", lpParms->lpstrAlias);
2300         if (!lpParms->lpstrAlias) {
2301             dwRet = MCIERR_NULL_PARAMETER_BLOCK;
2302             goto errCleanUp;
2303         }
2304     }
2305
2306     if ((dwRet = MCI_LoadMciDriver(iData, strDevTyp, &wmd))) {
2307         goto errCleanUp;
2308     }
2309
2310     if ((dwRet = MCI_FinishOpen(wmd, lpParms, dwParam))) {
2311         TRACE("Failed to open driver (MCI_OPEN_DRIVER) [%08lx], closing\n", dwRet);
2312         /* FIXME: is dwRet the correct ret code ? */
2313         goto errCleanUp;
2314     }
2315
2316     /* only handled devices fall through */
2317     TRACE("wDevID=%04X wDeviceID=%d dwRet=%ld\n", wmd->wDeviceID, lpParms->wDeviceID, dwRet);
2318
2319     if (dwParam & MCI_NOTIFY)
2320         mciDriverNotify16(lpParms->dwCallback, wmd->wDeviceID, MCI_NOTIFY_SUCCESSFUL);
2321
2322     return 0;
2323 errCleanUp:
2324     if (wmd) MCI_UnLoadMciDriver(iData, wmd);
2325
2326     if (dwParam & MCI_NOTIFY)
2327         mciDriverNotify16(lpParms->dwCallback, 0, MCI_NOTIFY_FAILURE);
2328     return dwRet;
2329 }
2330
2331 /**************************************************************************
2332  *                      MCI_Close                               [internal]
2333  */
2334 static  DWORD MCI_Close(UINT16 wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
2335 {
2336     DWORD               dwRet;
2337     LPWINE_MCIDRIVER    wmd;
2338     LPWINE_MM_IDATA     iData = MULTIMEDIA_GetIData();
2339
2340     TRACE("(%04x, %08lX, %p)\n", wDevID, dwParam, lpParms);
2341
2342     if (wDevID == MCI_ALL_DEVICE_ID) {
2343         LPWINE_MCIDRIVER        next;
2344
2345         EnterCriticalSection(&iData->cs);
2346         /* FIXME: shall I notify once after all is done, or for
2347          * each of the open drivers ? if the latest, which notif
2348          * to return when only one fails ?
2349          */
2350         for (wmd = iData->lpMciDrvs; wmd; ) {
2351             next = wmd->lpNext;
2352             MCI_Close(wmd->wDeviceID, dwParam, lpParms);
2353             wmd = next;
2354         }
2355         LeaveCriticalSection(&iData->cs);
2356         return 0;
2357     }
2358
2359     if (!(wmd = MCI_GetDriver(wDevID))) {
2360         return MCIERR_INVALID_DEVICE_ID;
2361     }
2362
2363     dwRet = MCI_SendCommandFrom32(wDevID, MCI_CLOSE_DRIVER, dwParam, (DWORD)lpParms);
2364
2365     MCI_UnLoadMciDriver(iData, wmd);
2366
2367     if (dwParam & MCI_NOTIFY)
2368         mciDriverNotify16(lpParms->dwCallback, wDevID,
2369                           (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
2370
2371     return dwRet;
2372 }
2373
2374 /**************************************************************************
2375  *                      MCI_WriteString                         [internal]
2376  */
2377 DWORD   MCI_WriteString(LPSTR lpDstStr, DWORD dstSize, LPCSTR lpSrcStr)
2378 {
2379     DWORD       ret = 0;
2380
2381     if (lpSrcStr) {
2382         if (dstSize <= strlen(lpSrcStr)) {
2383             lstrcpynA(lpDstStr, lpSrcStr, dstSize - 1);
2384             ret = MCIERR_PARAM_OVERFLOW;
2385         } else {
2386             strcpy(lpDstStr, lpSrcStr);
2387         }
2388     } else {
2389         *lpDstStr = 0;
2390     }
2391     return ret;
2392 }
2393
2394 /**************************************************************************
2395  *                      MCI_Sysinfo                             [internal]
2396  */
2397 static  DWORD MCI_SysInfo(UINT uDevID, DWORD dwFlags, LPMCI_SYSINFO_PARMSA lpParms)
2398 {
2399     DWORD               ret = MCIERR_INVALID_DEVICE_ID;
2400     LPWINE_MCIDRIVER    wmd;
2401     LPWINE_MM_IDATA     iData = MULTIMEDIA_GetIData();
2402
2403     if (lpParms == NULL)                        return MCIERR_NULL_PARAMETER_BLOCK;
2404
2405     TRACE("(%08x, %08lX, %08lX[num=%ld, wDevTyp=%u])\n",
2406           uDevID, dwFlags, (DWORD)lpParms, lpParms->dwNumber, lpParms->wDeviceType);
2407
2408     switch (dwFlags & ~MCI_SYSINFO_OPEN) {
2409     case MCI_SYSINFO_QUANTITY:
2410         {
2411             DWORD       cnt = 0;
2412
2413             if (lpParms->wDeviceType < MCI_DEVTYPE_FIRST ||
2414                 lpParms->wDeviceType > MCI_DEVTYPE_LAST) {
2415                 if (dwFlags & MCI_SYSINFO_OPEN) {
2416                     TRACE("MCI_SYSINFO_QUANTITY: # of open MCI drivers\n");
2417                     EnterCriticalSection(&iData->cs);
2418                     for (wmd = iData->lpMciDrvs; wmd; wmd = wmd->lpNext) {
2419                         cnt++;
2420                     }
2421                     LeaveCriticalSection(&iData->cs);
2422                 } else {
2423                     TRACE("MCI_SYSINFO_QUANTITY: # of installed MCI drivers\n");
2424                     cnt = MCI_InstalledCount;
2425                 }
2426             } else {
2427                 if (dwFlags & MCI_SYSINFO_OPEN) {
2428                     TRACE("MCI_SYSINFO_QUANTITY: # of open MCI drivers of type %u\n",
2429                           lpParms->wDeviceType);
2430                     EnterCriticalSection(&iData->cs);
2431                     for (wmd = iData->lpMciDrvs; wmd; wmd = wmd->lpNext) {
2432                         if (wmd->wType == lpParms->wDeviceType)
2433                             cnt++;
2434                     }
2435                     LeaveCriticalSection(&iData->cs);
2436                 } else {
2437                     TRACE("MCI_SYSINFO_QUANTITY: # of installed MCI drivers of type %u\n",
2438                           lpParms->wDeviceType);
2439                     FIXME("Don't know how to get # of MCI devices of a given type\n");
2440                     cnt = 1;
2441                 }
2442             }
2443             *(DWORD*)lpParms->lpstrReturn = cnt;
2444         }
2445         TRACE("(%ld) => '%ld'\n", lpParms->dwNumber, *(DWORD*)lpParms->lpstrReturn);
2446         ret = MCI_INTEGER_RETURNED;
2447         break;
2448     case MCI_SYSINFO_INSTALLNAME:
2449         TRACE("MCI_SYSINFO_INSTALLNAME \n");
2450         if ((wmd = MCI_GetDriver(uDevID))) {
2451             ret = MCI_WriteString(lpParms->lpstrReturn, lpParms->dwRetSize,
2452                                   wmd->lpstrDeviceType);
2453         } else {
2454             *lpParms->lpstrReturn = 0;
2455             ret = MCIERR_INVALID_DEVICE_ID;
2456         }
2457         TRACE("(%ld) => '%s'\n", lpParms->dwNumber, lpParms->lpstrReturn);
2458         break;
2459     case MCI_SYSINFO_NAME:
2460         TRACE("MCI_SYSINFO_NAME\n");
2461         if (dwFlags & MCI_SYSINFO_OPEN) {
2462             FIXME("Don't handle MCI_SYSINFO_NAME|MCI_SYSINFO_OPEN (yet)\n");
2463             ret = MCIERR_UNRECOGNIZED_COMMAND;
2464         } else if (lpParms->dwNumber > MCI_InstalledCount) {
2465             ret = MCIERR_OUTOFRANGE;
2466         } else {
2467             DWORD       count = lpParms->dwNumber;
2468             LPSTR       ptr = MCI_lpInstallNames;
2469
2470             while (--count > 0) ptr += strlen(ptr) + 1;
2471             ret = MCI_WriteString(lpParms->lpstrReturn, lpParms->dwRetSize, ptr);
2472         }
2473         TRACE("(%ld) => '%s'\n", lpParms->dwNumber, lpParms->lpstrReturn);
2474         break;
2475     default:
2476         TRACE("Unsupported flag value=%08lx\n", dwFlags);
2477         ret = MCIERR_UNRECOGNIZED_COMMAND;
2478     }
2479     return ret;
2480 }
2481
2482 /**************************************************************************
2483  *                      MCI_Break                               [internal]
2484  */
2485 static  DWORD MCI_Break(UINT wDevID, DWORD dwFlags, LPMCI_BREAK_PARMS lpParms)
2486 {
2487     DWORD       dwRet = 0;
2488
2489     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
2490
2491     if (dwFlags & MCI_NOTIFY)
2492         mciDriverNotify16(lpParms->dwCallback, wDevID,
2493                           (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
2494
2495     return dwRet;
2496 }
2497
2498 /**************************************************************************
2499  *                      MCI_SendCommand                         [internal]
2500  */
2501 DWORD   MCI_SendCommand(UINT wDevID, UINT16 wMsg, DWORD dwParam1,
2502                         DWORD dwParam2, BOOL bFrom32)
2503 {
2504     DWORD               dwRet = MCIERR_UNRECOGNIZED_COMMAND;
2505
2506     switch (wMsg) {
2507     case MCI_OPEN:
2508         if (bFrom32) {
2509             dwRet = MCI_Open(dwParam1, (LPMCI_OPEN_PARMSA)dwParam2);
2510         } else {
2511             switch (MCI_MapMsg16To32A(0, wMsg, &dwParam2)) {
2512             case MCI_MAP_OK:
2513             case MCI_MAP_OKMEM:
2514                 dwRet = MCI_Open(dwParam1, (LPMCI_OPEN_PARMSA)dwParam2);
2515                 MCI_UnMapMsg16To32A(0, wMsg, dwParam2);
2516                 break;
2517             default: break; /* so that gcc does not bark */
2518             }
2519         }
2520         break;
2521     case MCI_CLOSE:
2522         if (bFrom32) {
2523             dwRet = MCI_Close(wDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
2524         } else {
2525             switch (MCI_MapMsg16To32A(0, wMsg, &dwParam2)) {
2526             case MCI_MAP_OK:
2527             case MCI_MAP_OKMEM:
2528                 dwRet = MCI_Close(wDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
2529                 MCI_UnMapMsg16To32A(0, wMsg, dwParam2);
2530                 break;
2531             default: break; /* so that gcc does not bark */
2532             }
2533         }
2534         break;
2535     case MCI_SYSINFO:
2536         if (bFrom32) {
2537             dwRet = MCI_SysInfo(wDevID, dwParam1, (LPMCI_SYSINFO_PARMSA)dwParam2);
2538         } else {
2539             switch (MCI_MapMsg16To32A(0, wMsg, &dwParam2)) {
2540             case MCI_MAP_OK:
2541             case MCI_MAP_OKMEM:
2542                 dwRet = MCI_SysInfo(wDevID, dwParam1, (LPMCI_SYSINFO_PARMSA)dwParam2);
2543                 MCI_UnMapMsg16To32A(0, wMsg, dwParam2);
2544                 break;
2545             default: break; /* so that gcc doesnot  bark */
2546             }
2547         }
2548         break;
2549     case MCI_BREAK:
2550         if (bFrom32) {
2551             dwRet = MCI_Break(wDevID, dwParam1, (LPMCI_BREAK_PARMS)dwParam2);
2552         } else {
2553             switch (MCI_MapMsg16To32A(0, wMsg, &dwParam2)) {
2554             case MCI_MAP_OK:
2555             case MCI_MAP_OKMEM:
2556                 dwRet = MCI_Break(wDevID, dwParam1, (LPMCI_BREAK_PARMS)dwParam2);
2557                 MCI_UnMapMsg16To32A(0, wMsg, dwParam2);
2558                 break;
2559             default: break; /* so that gcc does not bark */
2560             }
2561         }
2562         break;
2563     case MCI_SOUND:
2564         /* FIXME: it seems that MCI_SOUND needs the same handling as MCI_BREAK
2565          * but I couldn't get any doc on this MCI message
2566          */
2567         break;
2568     default:
2569         if (wDevID == MCI_ALL_DEVICE_ID) {
2570             FIXME("unhandled MCI_ALL_DEVICE_ID\n");
2571             dwRet = MCIERR_CANNOT_USE_ALL;
2572         } else {
2573             dwRet = (bFrom32) ?
2574                 MCI_SendCommandFrom32(wDevID, wMsg, dwParam1, dwParam2) :
2575                 MCI_SendCommandFrom16(wDevID, wMsg, dwParam1, dwParam2);
2576         }
2577         break;
2578     }
2579     return dwRet;
2580 }
2581
2582 /**************************************************************************
2583  *                              MCI_CleanUp                     [internal]
2584  *
2585  * Some MCI commands need to be cleaned-up (when not called from
2586  * mciSendString), because MCI drivers return extra information for string
2587  * transformation. This function gets rid of them.
2588  */
2589 LRESULT         MCI_CleanUp(LRESULT dwRet, UINT wMsg, DWORD dwParam2, BOOL bIs32)
2590 {
2591     if (LOWORD(dwRet))
2592         return LOWORD(dwRet);
2593
2594     switch (wMsg) {
2595     case MCI_GETDEVCAPS:
2596         switch (dwRet & 0xFFFF0000ul) {
2597         case 0:
2598         case MCI_COLONIZED3_RETURN:
2599         case MCI_COLONIZED4_RETURN:
2600         case MCI_INTEGER_RETURNED:
2601             /* nothing to do */
2602             break;
2603         case MCI_RESOURCE_RETURNED:
2604         case MCI_RESOURCE_RETURNED|MCI_RESOURCE_DRIVER:
2605             {
2606                 LPMCI_GETDEVCAPS_PARMS  lmgp;
2607
2608                 lmgp = (LPMCI_GETDEVCAPS_PARMS)(bIs32 ? (void*)dwParam2 : MapSL(dwParam2));
2609                 TRACE("Changing %08lx to %08lx\n", lmgp->dwReturn, (DWORD)LOWORD(lmgp->dwReturn));
2610                 lmgp->dwReturn = LOWORD(lmgp->dwReturn);
2611             }
2612             break;
2613         default:
2614             FIXME("Unsupported value for hiword (%04x) returned by DriverProc(%s)\n",
2615                   HIWORD(dwRet), MCI_MessageToString(wMsg));
2616         }
2617         break;
2618     case MCI_STATUS:
2619         switch (dwRet & 0xFFFF0000ul) {
2620         case 0:
2621         case MCI_COLONIZED3_RETURN:
2622         case MCI_COLONIZED4_RETURN:
2623         case MCI_INTEGER_RETURNED:
2624             /* nothing to do */
2625             break;
2626         case MCI_RESOURCE_RETURNED:
2627         case MCI_RESOURCE_RETURNED|MCI_RESOURCE_DRIVER:
2628             {
2629                 LPMCI_STATUS_PARMS      lsp;
2630
2631                 lsp = (LPMCI_STATUS_PARMS)(bIs32 ? (void*)dwParam2 : MapSL(dwParam2));
2632                 TRACE("Changing %08lx to %08lx\n", lsp->dwReturn, (DWORD)LOWORD(lsp->dwReturn));
2633                 lsp->dwReturn = LOWORD(lsp->dwReturn);
2634             }
2635             break;
2636         default:
2637             FIXME("Unsupported value for hiword (%04x) returned by DriverProc(%s)\n",
2638                   HIWORD(dwRet), MCI_MessageToString(wMsg));
2639         }
2640         break;
2641     case MCI_SYSINFO:
2642         switch (dwRet & 0xFFFF0000ul) {
2643         case 0:
2644         case MCI_INTEGER_RETURNED:
2645             /* nothing to do */
2646             break;
2647         default:
2648             FIXME("Unsupported value for hiword (%04x)\n", HIWORD(dwRet));
2649         }
2650         break;
2651     default:
2652         if (HIWORD(dwRet)) {
2653             FIXME("Got non null hiword for dwRet=0x%08lx for command %s\n",
2654                   dwRet, MCI_MessageToString(wMsg));
2655         }
2656         break;
2657     }
2658     return LOWORD(dwRet);
2659 }
2660
2661 /**************************************************************************
2662  *                      MULTIMEDIA_MciInit                      [internal]
2663  *
2664  * Initializes the MCI internal variables.
2665  *
2666  */
2667 BOOL MULTIMEDIA_MciInit(void)
2668 {
2669     LPSTR       ptr1, ptr2;
2670     HKEY        hWineConf;
2671     HKEY        hkey;
2672     DWORD       err;
2673     DWORD       type;
2674     DWORD       count = 2048;
2675
2676     MCI_InstalledCount = 0;
2677     ptr1 = MCI_lpInstallNames = HeapAlloc(GetProcessHeap(), 0, count);
2678
2679     if (!MCI_lpInstallNames)
2680         return FALSE;
2681
2682     /* FIXME: should do also some registry diving here ? */
2683     if (!(err = RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config", &hWineConf)) &&
2684         !(err = RegOpenKeyA(hWineConf, "options", &hkey))) {
2685         err = RegQueryValueExA(hkey, "mci", 0, &type, MCI_lpInstallNames, &count);
2686         RegCloseKey(hkey);
2687
2688     }
2689     if (!err) {
2690         TRACE("Wine => '%s' \n", ptr1);
2691         while ((ptr2 = strchr(ptr1, ':')) != 0) {
2692             *ptr2++ = 0;
2693             TRACE("---> '%s' \n", ptr1);
2694             MCI_InstalledCount++;
2695             ptr1 = ptr2;
2696         }
2697         MCI_InstalledCount++;
2698         TRACE("---> '%s' \n", ptr1);
2699         ptr1 += strlen(ptr1) + 1;
2700     } else {
2701         GetPrivateProfileStringA("mci", NULL, "", MCI_lpInstallNames, count, "SYSTEM.INI");
2702         while (strlen(ptr1) > 0) {
2703             TRACE("---> '%s' \n", ptr1);
2704             ptr1 += strlen(ptr1) + 1;
2705             MCI_InstalledCount++;
2706         }
2707     }
2708     RegCloseKey(hWineConf);
2709     return TRUE;
2710 }
2711