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