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