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