| /* -*- tab-width: 8; c-basic-offset: 4 -*- */ | 
 |  | 
 | /* | 
 |  * MCI internal functions | 
 |  * | 
 |  * Copyright 1998/1999 Eric Pouech | 
 |  * | 
 |  * This library is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU Lesser General Public | 
 |  * License as published by the Free Software Foundation; either | 
 |  * version 2.1 of the License, or (at your option) any later version. | 
 |  * | 
 |  * This library is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 |  * Lesser General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU Lesser General Public | 
 |  * License along with this library; if not, write to the Free Software | 
 |  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
 |  */ | 
 |  | 
 | #include <stdlib.h> | 
 | #include <stdio.h> | 
 | #include <string.h> | 
 |  | 
 | #include "mmsystem.h" | 
 | #include "winbase.h" | 
 | #include "wingdi.h" | 
 | #include "winreg.h" | 
 | #include "winuser.h" | 
 |  | 
 | #include "wine/mmsystem16.h" | 
 | #include "wine/winbase16.h" | 
 | #include "digitalv.h" | 
 | #include "winemm.h" | 
 |  | 
 | #include "wine/debug.h" | 
 |  | 
 | WINE_DEFAULT_DEBUG_CHANNEL(mci); | 
 |  | 
 | static	int			MCI_InstalledCount; | 
 | static	LPSTR			MCI_lpInstallNames = NULL; | 
 |  | 
 | typedef enum { | 
 |     MCI_MAP_NOMEM, 	/* ko, memory problem */ | 
 |     MCI_MAP_MSGERROR, 	/* ko, unknown message */ | 
 |     MCI_MAP_OK, 	/* ok, no memory allocated. to be sent to the proc. */ | 
 |     MCI_MAP_OKMEM, 	/* ok, some memory allocated, need to call UnMapMsg. to be sent to the proc. */ | 
 | } MCI_MapType; | 
 |  | 
 | static	MCI_MapType	MCI_MapMsg16To32A  (WORD uDevType, WORD wMsg,                DWORD* lParam); | 
 | static	MCI_MapType	MCI_UnMapMsg16To32A(WORD uDevType, WORD wMsg,                DWORD  lParam); | 
 | static	MCI_MapType	MCI_MapMsg32ATo16  (WORD uDevType, WORD wMsg, DWORD dwFlags, DWORD* lParam); | 
 | static	MCI_MapType	MCI_UnMapMsg32ATo16(WORD uDevType, WORD wMsg, DWORD dwFlags, DWORD  lParam); | 
 |  | 
 | /* First MCI valid device ID (0 means error) */ | 
 | #define MCI_MAGIC 0x0001 | 
 |  | 
 | /* dup a string and uppercase it */ | 
 | inline static LPSTR str_dup_upper( LPCSTR str ) | 
 | { | 
 |     INT len = strlen(str) + 1; | 
 |     LPSTR p = HeapAlloc( GetProcessHeap(), 0, len ); | 
 |     if (p) | 
 |     { | 
 |         memcpy( p, str, len ); | 
 |         CharUpperA( p ); | 
 |     } | 
 |     return p; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 				MCI_GetDriver			[internal] | 
 |  */ | 
 | LPWINE_MCIDRIVER	MCI_GetDriver(UINT16 wDevID)  | 
 | { | 
 |     LPWINE_MCIDRIVER	wmd = 0; | 
 |     LPWINE_MM_IDATA	iData = MULTIMEDIA_GetIData(); | 
 |  | 
 |     EnterCriticalSection(&iData->cs); | 
 |     for (wmd = iData->lpMciDrvs; wmd; wmd = wmd->lpNext) { | 
 | 	if (wmd->wDeviceID == wDevID) | 
 | 	    break; | 
 |     } | 
 |     LeaveCriticalSection(&iData->cs); | 
 |     return wmd; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 				MCI_GetDriverFromString		[internal] | 
 |  */ | 
 | UINT	MCI_GetDriverFromString(LPCSTR lpstrName) | 
 | { | 
 |     LPWINE_MCIDRIVER	wmd; | 
 |     LPWINE_MM_IDATA	iData = MULTIMEDIA_GetIData(); | 
 |     UINT		ret = 0; | 
 |  | 
 |     if (!lpstrName) | 
 | 	return 0; | 
 |      | 
 |     if (!lstrcmpiA(lpstrName, "ALL")) | 
 | 	return MCI_ALL_DEVICE_ID; | 
 |      | 
 |     EnterCriticalSection(&iData->cs); | 
 |     for (wmd = iData->lpMciDrvs; wmd; wmd = wmd->lpNext) { | 
 | 	if (wmd->lpstrElementName && strcmp(wmd->lpstrElementName, lpstrName) == 0) { | 
 | 	    ret = wmd->wDeviceID; | 
 | 	    break; | 
 | 	} | 
 | 	if (wmd->lpstrDeviceType && strcasecmp(wmd->lpstrDeviceType, lpstrName) == 0) { | 
 | 	    ret = wmd->wDeviceID; | 
 | 	    break; | 
 | 	} | 
 | 	if (wmd->lpstrAlias && strcasecmp(wmd->lpstrAlias, lpstrName) == 0) { | 
 | 	    ret = wmd->wDeviceID; | 
 | 	    break; | 
 | 	} | 
 |     } | 
 |     LeaveCriticalSection(&iData->cs); | 
 |      | 
 |     return ret; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 			MCI_MessageToString			[internal] | 
 |  */ | 
 | const char* MCI_MessageToString(UINT16 wMsg) | 
 | { | 
 |     static char buffer[100]; | 
 |      | 
 | #define CASE(s) case (s): return #s | 
 |      | 
 |     switch (wMsg) { | 
 | 	CASE(MCI_BREAK); | 
 | 	CASE(MCI_CLOSE); | 
 | 	CASE(MCI_CLOSE_DRIVER); | 
 | 	CASE(MCI_COPY); | 
 | 	CASE(MCI_CUE); | 
 | 	CASE(MCI_CUT); | 
 | 	CASE(MCI_DELETE); | 
 | 	CASE(MCI_ESCAPE); | 
 | 	CASE(MCI_FREEZE); | 
 | 	CASE(MCI_PAUSE); | 
 | 	CASE(MCI_PLAY); | 
 | 	CASE(MCI_GETDEVCAPS); | 
 | 	CASE(MCI_INFO); | 
 | 	CASE(MCI_LOAD); | 
 | 	CASE(MCI_OPEN); | 
 | 	CASE(MCI_OPEN_DRIVER); | 
 | 	CASE(MCI_PASTE); | 
 | 	CASE(MCI_PUT); | 
 | 	CASE(MCI_REALIZE); | 
 | 	CASE(MCI_RECORD); | 
 | 	CASE(MCI_RESUME); | 
 | 	CASE(MCI_SAVE); | 
 | 	CASE(MCI_SEEK); | 
 | 	CASE(MCI_SET); | 
 | 	CASE(MCI_SPIN); | 
 | 	CASE(MCI_STATUS); | 
 | 	CASE(MCI_STEP); | 
 | 	CASE(MCI_STOP); | 
 | 	CASE(MCI_SYSINFO); | 
 | 	CASE(MCI_UNFREEZE); | 
 | 	CASE(MCI_UPDATE); | 
 | 	CASE(MCI_WHERE); | 
 | 	CASE(MCI_WINDOW); | 
 | 	/* constants for digital video */ | 
 | 	CASE(MCI_CAPTURE); | 
 | 	CASE(MCI_MONITOR); | 
 | 	CASE(MCI_RESERVE); | 
 | 	CASE(MCI_SETAUDIO); | 
 | 	CASE(MCI_SIGNAL); | 
 | 	CASE(MCI_SETVIDEO); | 
 | 	CASE(MCI_QUALITY); | 
 | 	CASE(MCI_LIST); | 
 | 	CASE(MCI_UNDO); | 
 | 	CASE(MCI_CONFIGURE); | 
 | 	CASE(MCI_RESTORE); | 
 | #undef CASE | 
 |     default: | 
 | 	sprintf(buffer, "MCI_<<%04X>>", wMsg); | 
 | 	return buffer; | 
 |     } | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 				MCI_GetDevTypeFromFileName	[internal] | 
 |  */ | 
 | static	DWORD	MCI_GetDevTypeFromFileName(LPCSTR fileName, LPSTR buf, UINT len) | 
 | { | 
 |     LPSTR	tmp; | 
 |  | 
 |     if ((tmp = strrchr(fileName, '.'))) { | 
 | 	GetProfileStringA("mci extensions", tmp + 1, "*", buf, len); | 
 | 	if (strcmp(buf, "*") != 0) { | 
 | 	    return 0; | 
 | 	} | 
 | 	TRACE("No [mci extensions] entry for '%s' found.\n", tmp); | 
 |     } | 
 |     return MCIERR_EXTENSION_NOT_FOUND; | 
 | } | 
 |  | 
 | #define	MAX_MCICMDTABLE			20 | 
 | #define MCI_COMMAND_TABLE_NOT_LOADED	0xFFFE | 
 |  | 
 | typedef struct tagWINE_MCICMDTABLE { | 
 |     HANDLE		hMem; | 
 |     UINT		uDevType; | 
 |     LPCSTR		lpTable; | 
 |     UINT		nVerbs;		/* number of verbs in command table */ | 
 |     LPCSTR*		aVerbs;		/* array of verbs to speed up the verb look up process */ | 
 | } WINE_MCICMDTABLE, *LPWINE_MCICMDTABLE; | 
 | WINE_MCICMDTABLE	S_MciCmdTable[MAX_MCICMDTABLE]; | 
 |  | 
 | /************************************************************************** | 
 |  * 				MCI_IsCommandTableValid		[internal] | 
 |  */ | 
 | static	BOOL		MCI_IsCommandTableValid(UINT uTbl) | 
 | { | 
 |     LPCSTR	lmem, str; | 
 |     DWORD	flg; | 
 |     WORD	eid; | 
 |     int		idx = 0; | 
 |     BOOL	inCst = FALSE; | 
 |  | 
 |     TRACE("Dumping cmdTbl=%d [hMem=%08x devType=%d]\n",  | 
 | 	  uTbl, S_MciCmdTable[uTbl].hMem, S_MciCmdTable[uTbl].uDevType); | 
 |  | 
 |     if (uTbl >= MAX_MCICMDTABLE || !S_MciCmdTable[uTbl].hMem || !S_MciCmdTable[uTbl].lpTable)  | 
 | 	return FALSE; | 
 |  | 
 |     lmem = S_MciCmdTable[uTbl].lpTable; | 
 |     do { | 
 | 	do { | 
 | 	    str = lmem; | 
 | 	    lmem += strlen(lmem) + 1; | 
 | 	    flg = *(LPDWORD)lmem; | 
 | 	    eid = *(LPWORD)(lmem + sizeof(DWORD)); | 
 | 	    lmem += sizeof(DWORD) + sizeof(WORD); | 
 | 	    idx ++; | 
 | 	    /* EPP 	    TRACE("cmd='%s' %08lx %04x\n", str, flg, eid); */ | 
 | 	    switch (eid) { | 
 | 	    case MCI_COMMAND_HEAD:	if (!*str || !flg) return FALSE; idx = 0;			break;	/* check unicity of str in table */ | 
 | 	    case MCI_STRING:            if (inCst) return FALSE;					break; | 
 | 	    case MCI_INTEGER:           if (!*str) return FALSE;					break; | 
 | 	    case MCI_END_COMMAND:       if (*str || flg || idx == 0) return FALSE; idx = 0;		break; | 
 | 	    case MCI_RETURN:		if (*str || idx != 1) return FALSE;				break; | 
 | 	    case MCI_FLAG:		if (!*str) return FALSE;					break; | 
 | 	    case MCI_END_COMMAND_LIST:	if (*str || flg) return FALSE;	idx = 0;			break; | 
 | 	    case MCI_RECT:		if (!*str || inCst) return FALSE;				break; | 
 | 	    case MCI_CONSTANT:          if (inCst) return FALSE; inCst = TRUE;				break; | 
 | 	    case MCI_END_CONSTANT:	if (*str || flg || !inCst) return FALSE; inCst = FALSE;		break; | 
 | 	    default:			return FALSE; | 
 | 	    } | 
 | 	} while (eid != MCI_END_COMMAND_LIST); | 
 |     } while (eid != MCI_END_COMMAND_LIST); | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 				MCI_DumpCommandTable		[internal] | 
 |  */ | 
 | static	BOOL		MCI_DumpCommandTable(UINT uTbl) | 
 | { | 
 |     LPCSTR	lmem; | 
 |     LPCSTR	str; | 
 |     DWORD	flg; | 
 |     WORD	eid; | 
 |      | 
 |     if (!MCI_IsCommandTableValid(uTbl)) { | 
 | 	ERR("Ooops: %d is not valid\n", uTbl); | 
 | 	return FALSE; | 
 |     } | 
 |  | 
 |     lmem = S_MciCmdTable[uTbl].lpTable; | 
 |     do { | 
 | 	do { | 
 | 	    str = lmem; | 
 | 	    lmem += strlen(lmem) + 1; | 
 | 	    flg = *(LPDWORD)lmem; | 
 | 	    eid = *(LPWORD)(lmem + sizeof(DWORD)); | 
 | 	    TRACE("cmd='%s' %08lx %04x\n", str, flg, eid); | 
 | 	    lmem += sizeof(DWORD) + sizeof(WORD); | 
 | 	} while (eid != MCI_END_COMMAND && eid != MCI_END_COMMAND_LIST); | 
 | 	TRACE(" => end of command%s\n", (eid == MCI_END_COMMAND_LIST) ? " list" : ""); | 
 |     } while (eid != MCI_END_COMMAND_LIST); | 
 |     return TRUE; | 
 | } | 
 |  | 
 | static	UINT		MCI_SetCommandTable(LPWINE_MM_IDATA iData, HANDLE hMem, UINT uDevType); | 
 |  | 
 | /************************************************************************** | 
 |  * 				MCI_GetCommandTable		[internal] | 
 |  */ | 
 | static	UINT		MCI_GetCommandTable(LPWINE_MM_IDATA iData, UINT uDevType) | 
 | { | 
 |     UINT	uTbl; | 
 |     char	buf[32]; | 
 |     LPSTR	str = NULL; | 
 |      | 
 |     /* first look up existing for existing devType */ | 
 |     for (uTbl = 0; uTbl < MAX_MCICMDTABLE; uTbl++) { | 
 | 	if (S_MciCmdTable[uTbl].hMem && S_MciCmdTable[uTbl].uDevType == uDevType) | 
 | 	    return uTbl;	 | 
 |     } | 
 |  | 
 |     /* well try to load id */ | 
 |     if (uDevType >= MCI_DEVTYPE_FIRST && uDevType <= MCI_DEVTYPE_LAST) { | 
 | 	if (LoadStringA(iData->hWinMM32Instance, uDevType, buf, sizeof(buf))) { | 
 | 	    str = buf; | 
 | 	} | 
 |     } else if (uDevType == 0) { | 
 | 	str = "CORE"; | 
 |     } | 
 |     uTbl = MCI_NO_COMMAND_TABLE; | 
 |     if (str) { | 
 | 	HRSRC 	hRsrc = FindResourceA(iData->hWinMM32Instance, str, (LPCSTR)RT_RCDATAA); | 
 | 	HANDLE	hMem = 0; | 
 |  | 
 | 	if (hRsrc) hMem = LoadResource(iData->hWinMM32Instance, hRsrc); | 
 | 	if (hMem) { | 
 | 	    uTbl = MCI_SetCommandTable(iData, hMem, uDevType); | 
 | 	} else { | 
 | 	    WARN("No command table found in resource %04x[%s]\n",  | 
 | 		 iData->hWinMM32Instance, str); | 
 | 	} | 
 |     } | 
 |     TRACE("=> %d\n", uTbl); | 
 |     return uTbl; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 				MCI_SetCommandTable		[internal] | 
 |  */ | 
 | static	UINT		MCI_SetCommandTable(LPWINE_MM_IDATA iData, HANDLE hMem,  | 
 | 					    UINT uDevType) | 
 | {     | 
 |     int		        uTbl; | 
 |     static	BOOL	bInitDone = FALSE; | 
 |  | 
 |     /* <HACK> | 
 |      * The CORE command table must be loaded first, so that MCI_GetCommandTable() | 
 |      * can be called with 0 as a uDevType to retrieve it. | 
 |      * </HACK> | 
 |      */ | 
 |     if (!bInitDone) { | 
 | 	bInitDone = TRUE; | 
 | 	for (uTbl = 0; uTbl < MAX_MCICMDTABLE; uTbl++) { | 
 | 	    S_MciCmdTable[uTbl].hMem = 0; | 
 | 	} | 
 | 	MCI_GetCommandTable(iData, 0); | 
 |     } | 
 |  | 
 |     for (uTbl = 0; uTbl < MAX_MCICMDTABLE; uTbl++) { | 
 | 	if (S_MciCmdTable[uTbl].hMem == 0) { | 
 | 	    LPCSTR 	lmem, str; | 
 | 	    WORD	eid; | 
 | 	    WORD	count; | 
 |  | 
 | 	    S_MciCmdTable[uTbl].hMem = hMem; | 
 | 	    S_MciCmdTable[uTbl].uDevType = uDevType; | 
 | 	    S_MciCmdTable[uTbl].lpTable = LockResource(hMem); | 
 |  | 
 | 	    if (TRACE_ON(mci)) { | 
 | 		MCI_DumpCommandTable(uTbl); | 
 | 	    } | 
 |  | 
 | 	    /* create the verbs table */ | 
 | 	    /* get # of entries */ | 
 | 	    lmem = S_MciCmdTable[uTbl].lpTable; | 
 | 	    count = 0; | 
 | 	    do { | 
 | 		lmem += strlen(lmem) + 1; | 
 | 		eid = *(LPWORD)(lmem + sizeof(DWORD)); | 
 | 		lmem += sizeof(DWORD) + sizeof(WORD); | 
 | 		if (eid == MCI_COMMAND_HEAD) | 
 | 		    count++; | 
 | 	    } while (eid != MCI_END_COMMAND_LIST); | 
 |  | 
 | 	    S_MciCmdTable[uTbl].aVerbs = HeapAlloc(GetProcessHeap(), 0, count * sizeof(LPCSTR)); | 
 | 	    S_MciCmdTable[uTbl].nVerbs = count; | 
 |  | 
 | 	    lmem = S_MciCmdTable[uTbl].lpTable; | 
 | 	    count = 0; | 
 | 	    do { | 
 | 		str = lmem; | 
 | 		lmem += strlen(lmem) + 1; | 
 | 		eid = *(LPWORD)(lmem + sizeof(DWORD)); | 
 | 		lmem += sizeof(DWORD) + sizeof(WORD); | 
 | 		if (eid == MCI_COMMAND_HEAD) | 
 | 		    S_MciCmdTable[uTbl].aVerbs[count++] = str; | 
 | 	    } while (eid != MCI_END_COMMAND_LIST); | 
 | 	    /* assert(count == S_MciCmdTable[uTbl].nVerbs); */ | 
 | 	    return uTbl; | 
 | 	} | 
 |     } | 
 |  | 
 |     return MCI_NO_COMMAND_TABLE; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 				MCI_DeleteCommandTable		[internal] | 
 |  */ | 
 | static	BOOL	MCI_DeleteCommandTable(UINT uTbl) | 
 | { | 
 |     if (uTbl >= MAX_MCICMDTABLE || !S_MciCmdTable[uTbl].hMem) | 
 | 	return FALSE; | 
 |  | 
 |     FreeResource(S_MciCmdTable[uTbl].hMem); | 
 |     S_MciCmdTable[uTbl].hMem = 0; | 
 |     if (S_MciCmdTable[uTbl].aVerbs) { | 
 | 	HeapFree(GetProcessHeap(), 0, S_MciCmdTable[uTbl].aVerbs); | 
 | 	S_MciCmdTable[uTbl].aVerbs = 0; | 
 |     } | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 				MCI_UnLoadMciDriver		[internal] | 
 |  */ | 
 | static	BOOL	MCI_UnLoadMciDriver(LPWINE_MM_IDATA iData, LPWINE_MCIDRIVER wmd) | 
 | { | 
 |     LPWINE_MCIDRIVER*		tmp; | 
 |  | 
 |     if (!wmd) | 
 | 	return TRUE; | 
 |  | 
 |     CloseDriver(wmd->hDriver, 0, 0); | 
 |  | 
 |     if (wmd->dwPrivate != 0) | 
 | 	WARN("Unloading mci driver with non nul dwPrivate field\n"); | 
 |  | 
 |     EnterCriticalSection(&iData->cs); | 
 |     for (tmp = &iData->lpMciDrvs; *tmp; tmp = &(*tmp)->lpNext) { | 
 | 	if (*tmp == wmd) { | 
 | 	    *tmp = wmd->lpNext; | 
 | 	    break; | 
 | 	} | 
 |     } | 
 |     LeaveCriticalSection(&iData->cs); | 
 |  | 
 |     HeapFree(GetProcessHeap(), 0, wmd->lpstrDeviceType); | 
 |     HeapFree(GetProcessHeap(), 0, wmd->lpstrAlias); | 
 |     HeapFree(GetProcessHeap(), 0, wmd->lpstrElementName); | 
 |  | 
 |     HeapFree(GetProcessHeap(), 0, wmd); | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 				MCI_OpenMciDriver		[internal] | 
 |  */ | 
 | static	BOOL	MCI_OpenMciDriver(LPWINE_MCIDRIVER wmd, LPCSTR drvTyp, LPARAM lp) | 
 | { | 
 |     char	libName[128]; | 
 |      | 
 |     if (!DRIVER_GetLibName(drvTyp, "mci", libName, sizeof(libName))) | 
 | 	return FALSE; | 
 |  | 
 |     wmd->bIs32 = 0xFFFF; | 
 |     /* First load driver */ | 
 |     if ((wmd->hDriver = (HDRVR)DRIVER_TryOpenDriver32(libName, lp))) { | 
 | 	wmd->bIs32 = TRUE; | 
 |     } else { | 
 | 	MCI_MapType 	res; | 
 |  | 
 | 	switch (res = MCI_MapMsg32ATo16(0, DRV_OPEN, 0, &lp)) { | 
 | 	case MCI_MAP_MSGERROR: | 
 | 	    TRACE("Not handled yet (DRV_OPEN)\n"); | 
 | 	    break; | 
 | 	case MCI_MAP_NOMEM: | 
 | 	    TRACE("Problem mapping msg=DRV_OPEN from 32a to 16\n"); | 
 | 	    break; | 
 | 	case MCI_MAP_OK: | 
 | 	case MCI_MAP_OKMEM: | 
 | 	    if ((wmd->hDriver = OpenDriverA(drvTyp, "mci", lp))) | 
 | 		wmd->bIs32 = FALSE; | 
 | 	    if (res == MCI_MAP_OKMEM) | 
 | 		MCI_UnMapMsg32ATo16(0, DRV_OPEN, 0, lp); | 
 | 	    break; | 
 | 	} | 
 |     } | 
 |     return (wmd->bIs32 == 0xFFFF) ? FALSE : TRUE; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 				MCI_LoadMciDriver		[internal] | 
 |  */ | 
 | static	DWORD	MCI_LoadMciDriver(LPWINE_MM_IDATA iData, LPCSTR _strDevTyp,  | 
 | 				  LPWINE_MCIDRIVER* lpwmd) | 
 | { | 
 |     LPSTR			strDevTyp = str_dup_upper(_strDevTyp); | 
 |     LPWINE_MCIDRIVER		wmd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wmd)); | 
 |     MCI_OPEN_DRIVER_PARMSA	modp; | 
 |     DWORD			dwRet = 0; | 
 |      | 
 |     if (!wmd || !strDevTyp) { | 
 | 	dwRet = MCIERR_OUT_OF_MEMORY; | 
 | 	goto errCleanUp; | 
 |     } | 
 |   | 
 |     wmd->lpfnYieldProc = MCI_DefYieldProc; | 
 |     wmd->dwYieldData = VK_CANCEL; | 
 |     wmd->hCreatorTask = GetCurrentTask(); | 
 |  | 
 |     EnterCriticalSection(&iData->cs); | 
 |     /* wmd must be inserted in list before sending opening the driver, coz' it | 
 |      * may want to lookup at wDevID | 
 |      */ | 
 |     wmd->lpNext = iData->lpMciDrvs; | 
 |     iData->lpMciDrvs = wmd; | 
 |  | 
 |     for (modp.wDeviceID = MCI_MAGIC;  | 
 | 	 MCI_GetDriver(modp.wDeviceID) != 0;  | 
 | 	 modp.wDeviceID++); | 
 |  | 
 |     wmd->wDeviceID = modp.wDeviceID; | 
 |  | 
 |     LeaveCriticalSection(&iData->cs); | 
 |  | 
 |     TRACE("wDevID=%04X \n", modp.wDeviceID); | 
 |  | 
 |     modp.lpstrParams = NULL; | 
 |      | 
 |     if (!MCI_OpenMciDriver(wmd, strDevTyp, (LPARAM)&modp)) { | 
 | 	/* silence warning if all is used... some bogus program use commands like | 
 | 	 * 'open all'... | 
 | 	 */ | 
 | 	if (strcasecmp(strDevTyp, "all") != 0) { | 
 | 	    dwRet = MCIERR_CANNOT_USE_ALL; | 
 | 	} else { | 
 | 	    FIXME("Couldn't load driver for type %s.\n" | 
 | 		  "If you don't have a windows installation accessible from Wine,\n" | 
 | 		  "you perhaps forgot to create a [mci] section in system.ini\n", | 
 | 		  strDevTyp); | 
 | 	    dwRet = MCIERR_DEVICE_NOT_INSTALLED; | 
 | 	} | 
 | 	goto errCleanUp; | 
 |     } | 
 |   | 
 |     /* FIXME: should also check that module's description is of the form | 
 |      * MODULENAME:[MCI] comment | 
 |      */ | 
 |  | 
 |     /* some drivers will return 0x0000FFFF, some others 0xFFFFFFFF */ | 
 |     wmd->uSpecificCmdTable = LOWORD(modp.wCustomCommandTable); | 
 |     wmd->uTypeCmdTable = MCI_COMMAND_TABLE_NOT_LOADED; | 
 |  | 
 |     TRACE("Loaded driver %x (%s), type is %d, cmdTable=%08x\n",  | 
 | 	  wmd->hDriver, strDevTyp, modp.wType, modp.wCustomCommandTable); | 
 |      | 
 |     wmd->lpstrDeviceType = strDevTyp; | 
 |     wmd->wType = modp.wType; | 
 |  | 
 |     TRACE("mcidev=%d, uDevTyp=%04X wDeviceID=%04X !\n",  | 
 | 	  modp.wDeviceID, modp.wType, modp.wDeviceID); | 
 |     *lpwmd = wmd; | 
 |     return 0; | 
 | errCleanUp: | 
 |     MCI_UnLoadMciDriver(iData, wmd); | 
 |     HeapFree(GetProcessHeap(), 0, strDevTyp); | 
 |     *lpwmd = 0; | 
 |     return dwRet; | 
 | } | 
 |      | 
 | /************************************************************************** | 
 |  * 			MCI_FinishOpen				[internal] | 
 |  */ | 
 | static	DWORD	MCI_FinishOpen(LPWINE_MCIDRIVER wmd, LPMCI_OPEN_PARMSA lpParms,  | 
 | 			       DWORD dwParam) | 
 | { | 
 |     if (dwParam & MCI_OPEN_ELEMENT) | 
 |     { | 
 |         wmd->lpstrElementName = HeapAlloc(GetProcessHeap(),0,strlen(lpParms->lpstrElementName)+1); | 
 |         strcpy( wmd->lpstrElementName, lpParms->lpstrElementName ); | 
 |     } | 
 |     if (dwParam & MCI_OPEN_ALIAS) | 
 |     { | 
 |         wmd->lpstrAlias = HeapAlloc(GetProcessHeap(), 0, strlen(lpParms->lpstrAlias)+1); | 
 |         strcpy( wmd->lpstrAlias, lpParms->lpstrAlias); | 
 |     } | 
 |     lpParms->wDeviceID = wmd->wDeviceID; | 
 |  | 
 |     return MCI_SendCommandFrom32(wmd->wDeviceID, MCI_OPEN_DRIVER, dwParam,  | 
 | 				 (DWORD)lpParms); | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 				MCI_FindCommand		[internal] | 
 |  */ | 
 | static	LPCSTR		MCI_FindCommand(UINT uTbl, LPCSTR verb) | 
 | { | 
 |     UINT	idx; | 
 |  | 
 |     if (uTbl >= MAX_MCICMDTABLE || S_MciCmdTable[uTbl].hMem == 0) | 
 | 	return NULL; | 
 |  | 
 |     /* another improvement would be to have the aVerbs array sorted, | 
 |      * so that we could use a dichotomic search on it, rather than this dumb | 
 |      * array look up | 
 |      */ | 
 |     for (idx = 0; idx < S_MciCmdTable[uTbl].nVerbs; idx++) { | 
 | 	if (strcmp(S_MciCmdTable[uTbl].aVerbs[idx], verb) == 0) | 
 | 	    return S_MciCmdTable[uTbl].aVerbs[idx]; | 
 |     } | 
 |  | 
 |     return NULL; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 				MCI_GetReturnType		[internal] | 
 |  */ | 
 | static	DWORD		MCI_GetReturnType(LPCSTR lpCmd) | 
 | { | 
 |     lpCmd += strlen(lpCmd) + 1 + sizeof(DWORD) + sizeof(WORD); | 
 |     if (*lpCmd == '\0' && *(LPWORD)(lpCmd + 1 + sizeof(DWORD)) == MCI_RETURN) { | 
 | 	return *(LPDWORD)(lpCmd + 1); | 
 |     } | 
 |     return 0L; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 				MCI_GetMessage			[internal] | 
 |  */ | 
 | static	WORD		MCI_GetMessage(LPCSTR lpCmd) | 
 | { | 
 |     return (WORD)*(LPDWORD)(lpCmd + strlen(lpCmd) + 1); | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 				MCI_GetDWord			[internal] | 
 |  */ | 
 | static	BOOL		MCI_GetDWord(LPDWORD data, LPSTR* ptr) | 
 | { | 
 |     DWORD	val; | 
 |     LPSTR	ret; | 
 |  | 
 |     val = strtoul(*ptr, &ret, 0); | 
 |  | 
 |     switch (*ret) { | 
 |     case '\0':	break; | 
 |     case ' ':	ret++; break; | 
 |     default:	return FALSE; | 
 |     } | 
 |  | 
 |     *data |= val; | 
 |     *ptr = ret; | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 				MCI_GetString		[internal] | 
 |  */ | 
 | static	DWORD	MCI_GetString(LPSTR* str, LPSTR* args) | 
 | { | 
 |     LPSTR	ptr = *args; | 
 |  | 
 |     /* see if we have a quoted string */ | 
 |     if (*ptr == '"') { | 
 | 	ptr = strchr(*str = ptr + 1, '"'); | 
 | 	if (!ptr) return MCIERR_NO_CLOSING_QUOTE; | 
 | 	/* FIXME: shall we escape \" from string ?? */ | 
 | 	if (ptr[-1] == '\\') TRACE("Ooops: un-escaped \"\n"); | 
 | 	*ptr++ = '\0'; /* remove trailing " */ | 
 | 	if (*ptr != ' ' && *ptr != '\0') return MCIERR_EXTRA_CHARACTERS; | 
 | 	*ptr++ = '\0'; | 
 |     } else { | 
 | 	ptr = strchr(ptr, ' '); | 
 |  | 
 | 	if (ptr) { | 
 | 	    *ptr++ = '\0'; | 
 | 	} else { | 
 | 	    ptr = *args + strlen(*args); | 
 | 	} | 
 | 	*str = *args; | 
 |     } | 
 |  | 
 |     *args = ptr; | 
 |     return 0; | 
 | } | 
 |  | 
 | #define	MCI_DATA_SIZE	16 | 
 |  | 
 | /************************************************************************** | 
 |  * 				MCI_ParseOptArgs		[internal] | 
 |  */ | 
 | static	DWORD	MCI_ParseOptArgs(LPDWORD data, int _offset, LPCSTR lpCmd,  | 
 | 				 LPSTR args, LPDWORD dwFlags) | 
 | { | 
 |     int		len, offset; | 
 |     LPCSTR	lmem, str; | 
 |     DWORD	dwRet, flg, cflg = 0; | 
 |     WORD	eid; | 
 |     BOOL	inCst, found; | 
 |  | 
 |     /* loop on arguments */ | 
 |     while (*args) { | 
 | 	lmem = lpCmd; | 
 | 	found = inCst = FALSE; | 
 | 	offset = _offset; | 
 |  | 
 | 	/* skip any leading white space(s) */ | 
 | 	while (*args == ' ') args++; | 
 | 	TRACE("args='%s' offset=%d\n", args, offset); | 
 | 	     | 
 | 	do { /* loop on options for command table for the requested verb */ | 
 | 	    str = lmem; | 
 | 	    lmem += (len = strlen(lmem)) + 1; | 
 | 	    flg = *(LPDWORD)lmem; | 
 | 	    eid = *(LPWORD)(lmem + sizeof(DWORD)); | 
 | 	    lmem += sizeof(DWORD) + sizeof(WORD); | 
 | /* EPP 	    TRACE("\tcmd='%s' inCst=%c eid=%04x\n", str, inCst ? 'Y' : 'N', eid); */ | 
 | 	     | 
 | 	    switch (eid) { | 
 | 	    case MCI_CONSTANT:		 | 
 | 		inCst = TRUE;	cflg = flg;	break; | 
 | 	    case MCI_END_CONSTANT:	 | 
 | 		/* there may be additional integral values after flag in constant */ | 
 | 		if (inCst && MCI_GetDWord(&(data[offset]), &args)) { | 
 | 		    *dwFlags |= cflg; | 
 | 		} | 
 | 		inCst = FALSE;	cflg = 0;	 | 
 | 		break; | 
 | 	    } | 
 |  | 
 | 	    if (strncasecmp(args, str, len) == 0 &&  | 
 | 		(args[len] == 0 || args[len] == ' ')) { | 
 | 		/* store good values into data[] */ | 
 | 		args += len; | 
 | 		while (*args == ' ') args++; | 
 | 		found = TRUE; | 
 |  | 
 | 		switch (eid) { | 
 | 		case MCI_COMMAND_HEAD: | 
 | 		case MCI_RETURN: | 
 | 		case MCI_END_COMMAND: | 
 | 		case MCI_END_COMMAND_LIST: | 
 | 		case MCI_CONSTANT: 	/* done above */ | 
 | 		case MCI_END_CONSTANT:  /* done above */ | 
 | 		    break; | 
 | 		case MCI_FLAG:			 | 
 | 		    *dwFlags |= flg; | 
 | 		    break; | 
 | 		case MCI_INTEGER: | 
 | 		    if (inCst) { | 
 | 			data[offset] |= flg; | 
 | 			*dwFlags |= cflg; | 
 | 			inCst = FALSE; | 
 | 		    } else { | 
 | 			*dwFlags |= flg; | 
 | 			if (!MCI_GetDWord(&(data[offset]), &args)) { | 
 | 			    return MCIERR_BAD_INTEGER; | 
 | 			} | 
 | 		    } | 
 | 		    break; | 
 | 		case MCI_RECT:			 | 
 | 		    /* store rect in data (offset...offset+3) */ | 
 | 		    *dwFlags |= flg; | 
 | 		    if (!MCI_GetDWord(&(data[offset+0]), &args) || | 
 | 			!MCI_GetDWord(&(data[offset+1]), &args) || | 
 | 			!MCI_GetDWord(&(data[offset+2]), &args) || | 
 | 			!MCI_GetDWord(&(data[offset+3]), &args)) { | 
 | 			ERR("Bad rect '%s'\n", args); | 
 | 			return MCIERR_BAD_INTEGER; | 
 | 		    } | 
 | 		    break; | 
 | 		case MCI_STRING: | 
 | 		    *dwFlags |= flg; | 
 | 		    if ((dwRet = MCI_GetString((LPSTR*)&data[offset], &args))) | 
 | 			return dwRet; | 
 | 		    break; | 
 | 		default:	ERR("oops\n"); | 
 | 		} | 
 | 		/* exit inside while loop, except if just entered in constant area definition */ | 
 | 		if (!inCst || eid != MCI_CONSTANT) eid = MCI_END_COMMAND; | 
 | 	    } else { | 
 | 		/* have offset incremented if needed */ | 
 | 		switch (eid) { | 
 | 		case MCI_COMMAND_HEAD: | 
 | 		case MCI_RETURN: | 
 | 		case MCI_END_COMMAND: | 
 | 		case MCI_END_COMMAND_LIST: | 
 | 		case MCI_CONSTANT: | 
 | 		case MCI_FLAG:			break; | 
 | 		case MCI_INTEGER:		if (!inCst) offset++;	break; | 
 | 		case MCI_END_CONSTANT:		 | 
 | 		case MCI_STRING:		offset++; break; | 
 | 		case MCI_RECT:			offset += 4; break; | 
 | 		default:			ERR("oops\n"); | 
 | 		} | 
 | 	    } | 
 | 	} while (eid != MCI_END_COMMAND); | 
 | 	if (!found) { | 
 | 	    WARN("Optarg '%s' not found\n", args); | 
 | 	    return MCIERR_UNRECOGNIZED_COMMAND; | 
 | 	} | 
 | 	if (offset == MCI_DATA_SIZE) { | 
 | 	    ERR("Internal data[] buffer overflow\n"); | 
 | 	    return MCIERR_PARSER_INTERNAL; | 
 | 	} | 
 |     } | 
 |     return 0; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 				MCI_HandleReturnValues	[internal] | 
 |  */ | 
 | static	DWORD	MCI_HandleReturnValues(LPWINE_MM_IDATA iData, DWORD dwRet,  | 
 | 				       LPWINE_MCIDRIVER wmd, LPCSTR lpCmd, LPDWORD data,  | 
 | 				       LPSTR lpstrRet, UINT uRetLen) | 
 | { | 
 |     if (lpstrRet) { | 
 | 	switch (MCI_GetReturnType(lpCmd)) { | 
 | 	case 0: /* nothing to return */ | 
 | 	    break; | 
 | 	case MCI_INTEGER:	 | 
 | 	    switch (dwRet & 0xFFFF0000ul) { | 
 | 	    case 0: | 
 | 	    case MCI_INTEGER_RETURNED: | 
 | 		snprintf(lpstrRet, uRetLen, "%ld", data[1]); | 
 | 		break; | 
 | 	    case MCI_RESOURCE_RETURNED: | 
 | 		/* return string which ID is HIWORD(data[1]),  | 
 | 		 * string is loaded from mmsystem.dll */ | 
 | 		LoadStringA(iData->hWinMM32Instance, HIWORD(data[1]),  | 
 | 			    lpstrRet, uRetLen); | 
 | 		break; | 
 | 	    case MCI_RESOURCE_RETURNED|MCI_RESOURCE_DRIVER: | 
 | 		/* return string which ID is HIWORD(data[1]),  | 
 | 		 * string is loaded from driver */ | 
 | 		/* FIXME: this is wrong for a 16 bit handle */ | 
 | 		LoadStringA(GetDriverModuleHandle(wmd->hDriver),  | 
 | 			    HIWORD(data[1]), lpstrRet, uRetLen); | 
 | 		break; | 
 | 	    case MCI_COLONIZED3_RETURN: | 
 | 		snprintf(lpstrRet, uRetLen, "%d:%d:%d",  | 
 | 			 LOBYTE(LOWORD(data[1])), HIBYTE(LOWORD(data[1])),  | 
 | 			 LOBYTE(HIWORD(data[1]))); | 
 | 		break; | 
 | 	    case MCI_COLONIZED4_RETURN: | 
 | 		snprintf(lpstrRet, uRetLen, "%d:%d:%d:%d",  | 
 | 			 LOBYTE(LOWORD(data[1])), HIBYTE(LOWORD(data[1])),  | 
 | 			 LOBYTE(HIWORD(data[1])), HIBYTE(HIWORD(data[1]))); | 
 | 		break; | 
 | 	    default:	ERR("Ooops (%04X)\n", HIWORD(dwRet)); | 
 | 	    } | 
 | 	    break; | 
 | 	case MCI_STRING:	 | 
 | 	    switch (dwRet & 0xFFFF0000ul) { | 
 | 	    case 0: | 
 | 		/* nothing to do data[1] == lpstrRet */ | 
 | 		break; | 
 | 	    case MCI_INTEGER_RETURNED: | 
 | 		data[1] = *(LPDWORD)lpstrRet; | 
 | 		snprintf(lpstrRet, uRetLen, "%ld", data[1]); | 
 | 		break; | 
 | 	    default: | 
 | 		WARN("Oooch. MCI_STRING and HIWORD(dwRet)=%04x\n", HIWORD(dwRet)); | 
 | 		break; | 
 | 	    } | 
 | 	    break; | 
 | 	case MCI_RECT:	 | 
 | 	    if (dwRet & 0xFFFF0000ul)	 | 
 | 		WARN("Oooch. MCI_STRING and HIWORD(dwRet)=%04x\n", HIWORD(dwRet)); | 
 | 	    snprintf(lpstrRet, uRetLen, "%ld %ld %ld %ld",  | 
 | 		       data[1], data[2], data[3], data[4]);	 | 
 | 	    break; | 
 | 	default:		ERR("oops\n"); | 
 | 	} | 
 |     } | 
 |     return LOWORD(dwRet); | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 				mciSendStringA		[WINMM.@] | 
 |  */ | 
 | DWORD WINAPI mciSendStringA(LPCSTR lpstrCommand, LPSTR lpstrRet,  | 
 | 			    UINT uRetLen, HWND hwndCallback) | 
 | { | 
 |     LPSTR		verb, dev, args; | 
 |     LPWINE_MCIDRIVER	wmd = 0; | 
 |     DWORD		dwFlags = 0, dwRet = 0; | 
 |     int			offset = 0; | 
 |     DWORD		data[MCI_DATA_SIZE]; | 
 |     LPCSTR		lpCmd = 0; | 
 |     LPSTR		devAlias = NULL; | 
 |     LPWINE_MM_IDATA	iData = MULTIMEDIA_GetIData(); | 
 |     BOOL		bAutoOpen = FALSE; | 
 |  | 
 |     TRACE("('%s', %p, %d, %X)\n", lpstrCommand, lpstrRet, uRetLen, hwndCallback); | 
 |  | 
 |     /* format is <command> <device> <optargs> */ | 
 |     if (!(verb = HeapAlloc(GetProcessHeap(), 0, strlen(lpstrCommand)+1))) | 
 | 	return MCIERR_OUT_OF_MEMORY; | 
 |     strcpy( verb, lpstrCommand ); | 
 |  | 
 |     memset(data, 0, sizeof(data)); | 
 |  | 
 |     if (!(args = strchr(verb, ' '))) { | 
 | 	dwRet = MCIERR_MISSING_DEVICE_NAME; | 
 | 	goto errCleanUp; | 
 |     } | 
 |     *args++ = '\0'; | 
 |     if ((dwRet = MCI_GetString(&dev, &args))) { | 
 | 	goto errCleanUp; | 
 |     } | 
 |  | 
 |     /* case dev == 'new' has to be handled */ | 
 |     if (!strcasecmp(dev, "new")) { | 
 | 	FIXME("'new': NIY as device name\n"); | 
 | 	dwRet = MCIERR_MISSING_DEVICE_NAME; | 
 | 	goto errCleanUp; | 
 |     } | 
 |  | 
 |     /* otherwise, try to grab devType from open */ | 
 |     if (!strcmp(verb, "open")) { | 
 | 	LPSTR	devType, tmp; | 
 |  | 
 | 	if ((devType = strchr(dev, '!')) != NULL) { | 
 | 	    *devType++ = '\0';	     | 
 | 	    tmp = devType; devType = dev; dev = tmp; | 
 |  | 
 | 	    dwFlags |= MCI_OPEN_TYPE; | 
 | 	    data[2] = (DWORD)devType; | 
 | 	    devType = str_dup_upper(devType); | 
 | 	    dwFlags |= MCI_OPEN_ELEMENT; | 
 | 	    data[3] = (DWORD)dev; | 
 | 	} else if (strchr(dev, '.') == NULL) { | 
 | 	    tmp = strchr(dev,' '); | 
 | 	    if (tmp) *tmp = '\0'; | 
 | 	    data[2] = (DWORD)dev; | 
 | 	    devType = str_dup_upper(dev); | 
 | 	    if (tmp) *tmp = ' '; | 
 | 	    dwFlags |= MCI_OPEN_TYPE; | 
 | 	} else { | 
 | 	    if ((devType = strstr(args, "type ")) != NULL) { | 
 | 		devType += 5; | 
 | 		tmp = strchr(devType, ' '); | 
 | 		if (tmp) *tmp = '\0'; | 
 | 		devType = str_dup_upper(devType); | 
 | 		if (tmp) *tmp = ' '; | 
 | 		/* dwFlags and data[2] will be correctly set in ParseOpt loop */ | 
 | 	    } else { | 
 | 		char	buf[32]; | 
 | 		if ((dwRet = MCI_GetDevTypeFromFileName(dev, buf, sizeof(buf)))) | 
 | 		    goto errCleanUp; | 
 |  | 
 | 		devType = str_dup_upper(buf); | 
 | 	    } | 
 | 	    dwFlags |= MCI_OPEN_ELEMENT; | 
 | 	    data[3] = (DWORD)dev; | 
 | 	} | 
 | 	if ((devAlias = strstr(args," alias "))) { | 
 |             char *tmp2; | 
 | 	    devAlias += 7; | 
 | 	    if (!(tmp = strchr(devAlias,' '))) tmp = devAlias + strlen(devAlias); | 
 | 	    if (tmp) *tmp = '\0'; | 
 |             tmp2 = HeapAlloc(GetProcessHeap(), 0, tmp - devAlias + 1 ); | 
 |             memcpy( tmp2, devAlias, tmp - devAlias ); | 
 |             tmp2[tmp - devAlias] = 0; | 
 |             data[4] = (DWORD)tmp2; | 
 | 	    /* should be done in regular options parsing */ | 
 | 	    /* dwFlags |= MCI_OPEN_ALIAS; */ | 
 | 	} | 
 |  | 
 | 	dwRet = MCI_LoadMciDriver(iData, devType, &wmd); | 
 | 	HeapFree(GetProcessHeap(), 0, devType); | 
 | 	if (dwRet) { | 
 | 	    MCI_UnLoadMciDriver(iData, wmd); | 
 | 	    goto errCleanUp; | 
 | 	} | 
 |     } else if (!(wmd = MCI_GetDriver(mciGetDeviceIDA(dev)))) { | 
 | 	/* auto open */ | 
 | 	char	buf[128]; | 
 | 	sprintf(buf, "open %s wait", dev); | 
 | 	 | 
 | 	if ((dwRet = mciSendStringA(buf, NULL, 0, 0)) != 0) | 
 | 	    goto errCleanUp; | 
 | 	 | 
 | 	wmd = MCI_GetDriver(mciGetDeviceIDA(dev)); | 
 | 	if (!wmd) { | 
 | 	    /* FIXME: memory leak, MCI driver is not closed */ | 
 | 	    dwRet = MCIERR_INVALID_DEVICE_ID; | 
 | 	    goto errCleanUp; | 
 | 	} | 
 |     } | 
 |  | 
 |     /* get the verb in the different command tables */ | 
 |     if (wmd) { | 
 | 	/* try the device specific command table */ | 
 | 	lpCmd = MCI_FindCommand(wmd->uSpecificCmdTable, verb); | 
 | 	if (!lpCmd) { | 
 | 	    /* try the type specific command table */ | 
 | 	    if (wmd->uTypeCmdTable == MCI_COMMAND_TABLE_NOT_LOADED) | 
 | 		wmd->uTypeCmdTable = MCI_GetCommandTable(iData, wmd->wType); | 
 | 	    if (wmd->uTypeCmdTable != MCI_NO_COMMAND_TABLE) | 
 | 		lpCmd = MCI_FindCommand(wmd->uTypeCmdTable, verb); | 
 | 	} | 
 |     } | 
 |     /* try core command table */ | 
 |     if (!lpCmd) lpCmd = MCI_FindCommand(MCI_GetCommandTable(iData, 0), verb); | 
 |  | 
 |     if (!lpCmd) { | 
 | 	TRACE("Command '%s' not found!\n", verb); | 
 | 	dwRet = MCIERR_UNRECOGNIZED_COMMAND; | 
 | 	goto errCleanUp; | 
 |     } | 
 |  | 
 |     /* set up call back */ | 
 |     if (hwndCallback != 0) { | 
 | 	dwFlags |= MCI_NOTIFY; | 
 | 	data[0] = (DWORD)hwndCallback; | 
 |     } | 
 |  | 
 |     /* set return information */ | 
 |     switch (MCI_GetReturnType(lpCmd)) { | 
 |     case 0:		offset = 1;	break; | 
 |     case MCI_INTEGER:	offset = 2;	break; | 
 |     case MCI_STRING:	data[1] = (DWORD)lpstrRet; data[2] = uRetLen; offset = 3; break; | 
 |     case MCI_RECT:	offset = 5;	break; | 
 |     default:	ERR("oops\n"); | 
 |     } | 
 |  | 
 |     TRACE("verb='%s' on dev='%s'; offset=%d\n", verb, dev, offset); | 
 |  | 
 |     if ((dwRet = MCI_ParseOptArgs(data, offset, lpCmd, args, &dwFlags))) | 
 | 	goto errCleanUp; | 
 |  | 
 |     if (bAutoOpen && (dwFlags & MCI_NOTIFY)) { | 
 | 	dwRet = MCIERR_NOTIFY_ON_AUTO_OPEN; | 
 | 	goto errCleanUp; | 
 |     } | 
 |     /* FIXME: the command should get it's own notification window set up and | 
 |      * ask for device closing while processing the notification mechanism | 
 |      */ | 
 |     if (lpstrRet && uRetLen) *lpstrRet = '\0'; | 
 |  | 
 | #define	STR_OF(_x) (IsBadReadPtr((char*)_x,1)?"?":(char*)(_x)) | 
 |     TRACE("[%d, %s, %08lx, %08lx/%s %08lx/%s %08lx/%s %08lx/%s %08lx/%s %08lx/%s]\n", | 
 | 	  wmd->wDeviceID, MCI_MessageToString(MCI_GetMessage(lpCmd)), dwFlags,  | 
 | 	  data[0], STR_OF(data[0]), data[1], STR_OF(data[1]), | 
 | 	  data[2], STR_OF(data[2]), data[3], STR_OF(data[3]), | 
 | 	  data[4], STR_OF(data[4]), data[5], STR_OF(data[5])); | 
 | #undef STR_OF | 
 |  | 
 |     if (strcmp(verb, "open") == 0) { | 
 | 	if ((dwRet = MCI_FinishOpen(wmd, (LPMCI_OPEN_PARMSA)data, dwFlags))) | 
 | 	    MCI_UnLoadMciDriver(iData, wmd); | 
 | 	/* FIXME: notification is not properly shared across two opens */ | 
 |     } else { | 
 | 	dwRet = MCI_SendCommand(wmd->wDeviceID, MCI_GetMessage(lpCmd), dwFlags, (DWORD)data, TRUE); | 
 |     } | 
 |     TRACE("=> 1/ %lx (%s)\n", dwRet, lpstrRet); | 
 |     dwRet = MCI_HandleReturnValues(iData, dwRet, wmd, lpCmd, data, lpstrRet, uRetLen); | 
 |     TRACE("=> 2/ %lx (%s)\n", dwRet, lpstrRet); | 
 |  | 
 | errCleanUp: | 
 |     HeapFree(GetProcessHeap(), 0, verb); | 
 |     HeapFree(GetProcessHeap(), 0, devAlias); | 
 |     return dwRet; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 				mciSendStringW			[WINMM.@] | 
 |  */ | 
 | DWORD WINAPI mciSendStringW(LPCWSTR lpwstrCommand, LPSTR lpstrRet,  | 
 | 			    UINT uRetLen, HWND hwndCallback) | 
 | { | 
 |     LPSTR 	lpstrCommand; | 
 |     UINT	ret; | 
 |     INT len; | 
 |  | 
 |     /* FIXME: is there something to do with lpstrReturnString ? */ | 
 |     len = WideCharToMultiByte( CP_ACP, 0, lpwstrCommand, -1, NULL, 0, NULL, NULL ); | 
 |     lpstrCommand = HeapAlloc( GetProcessHeap(), 0, len ); | 
 |     WideCharToMultiByte( CP_ACP, 0, lpwstrCommand, -1, lpstrCommand, len, NULL, NULL ); | 
 |     ret = mciSendStringA(lpstrCommand, lpstrRet, uRetLen, hwndCallback); | 
 |     HeapFree(GetProcessHeap(), 0, lpstrCommand); | 
 |     return ret; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 				mciSendString			[MMSYSTEM.702] | 
 |  */ | 
 | DWORD WINAPI mciSendString16(LPCSTR lpstrCommand, LPSTR lpstrRet,  | 
 | 			     UINT16 uRetLen, HWND16 hwndCallback) | 
 | { | 
 |     return mciSendStringA(lpstrCommand, lpstrRet, uRetLen, hwndCallback); | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 				mciExecute			[WINMM.@] | 
 |  * 				mciExecute			[MMSYSTEM.712] | 
 |  */ | 
 | DWORD WINAPI mciExecute(LPCSTR lpstrCommand) | 
 | { | 
 |     char	strRet[256]; | 
 |     DWORD	ret; | 
 |  | 
 |     TRACE("(%s)!\n", lpstrCommand); | 
 |  | 
 |     ret = mciSendStringA(lpstrCommand, strRet, sizeof(strRet), 0); | 
 |     if (ret != 0) { | 
 | 	if (!mciGetErrorStringA(ret, strRet, sizeof(strRet))) { | 
 | 	    sprintf(strRet, "Unknown MCI error (%ld)", ret); | 
 | 	} | 
 | 	MessageBoxA(0, strRet, "Error in mciExecute()", MB_OK);  | 
 |     } | 
 |     /* FIXME: what shall I return ? */ | 
 |     return 0; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  *                    	mciLoadCommandResource			[MMSYSTEM.705] | 
 |  */ | 
 | UINT16 WINAPI mciLoadCommandResource16(HANDLE16 hInst, LPCSTR resname, UINT16 type) | 
 | { | 
 |     HRSRC	        hRsrc = 0; | 
 |     HGLOBAL      	hMem; | 
 |     UINT16		ret = MCI_NO_COMMAND_TABLE; | 
 |     LPWINE_MM_IDATA 	iData = MULTIMEDIA_GetIData(); | 
 |  | 
 |     TRACE("(%04x, %s, %d)!\n", hInst, resname, type); | 
 |  | 
 |     /* if file exists "resname.mci", then load resource "resname" from it | 
 |      * otherwise directly from driver | 
 |      * We don't support it (who uses this feature ?), but we check anyway | 
 |      */ | 
 |     if (!type) { | 
 | 	char		buf[128]; | 
 | 	OFSTRUCT       	ofs; | 
 |  | 
 | 	strcat(strcpy(buf, resname), ".mci"); | 
 | 	if (OpenFile(buf, &ofs, OF_EXIST) != HFILE_ERROR) { | 
 | 	    FIXME("NIY: command table to be loaded from '%s'\n", ofs.szPathName); | 
 | 	} | 
 |     } | 
 |     if (!(hRsrc = FindResourceA(hInst, resname, (LPCSTR)RT_RCDATAA))) { | 
 | 	WARN("No command table found in resource\n"); | 
 |     } else if ((hMem = LoadResource(hInst, hRsrc))) { | 
 | 	ret = MCI_SetCommandTable(iData, hMem, type); | 
 |     } else { | 
 | 	WARN("Couldn't load resource.\n"); | 
 |     } | 
 |     TRACE("=> %04x\n", ret); | 
 |     return ret; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  *                    	mciFreeCommandResource			[MMSYSTEM.713] | 
 |  */ | 
 | BOOL16 WINAPI mciFreeCommandResource16(UINT16 uTable) | 
 | { | 
 |     TRACE("(%04x)!\n", uTable); | 
 |  | 
 |     return MCI_DeleteCommandTable(uTable); | 
 | } | 
 |   | 
 | /************************************************************************** | 
 |  *                    	mciLoadCommandResource  		[WINMM.@] | 
 |  * | 
 |  * Strangely, this function only exists as an UNICODE one. | 
 |  */ | 
 | UINT WINAPI mciLoadCommandResource(HINSTANCE hInst, LPCWSTR resNameW, UINT type) | 
 | { | 
 |     HRSRC	        hRsrc = 0; | 
 |     HGLOBAL      	hMem; | 
 |     UINT16		ret = MCI_NO_COMMAND_TABLE; | 
 |     LPWINE_MM_IDATA 	iData = MULTIMEDIA_GetIData(); | 
 |  | 
 |     TRACE("(%04x, %s, %d)!\n", hInst, debugstr_w(resNameW), type); | 
 |  | 
 | #if 0 | 
 |     /* if file exists "resname.mci", then load resource "resname" from it | 
 |      * otherwise directly from driver | 
 |      * We don't support it (who uses this feature ?), but we check anyway | 
 |      */ | 
 |     if (!type) { | 
 | 	char		buf[128]; | 
 | 	OFSTRUCT       	ofs; | 
 |  | 
 | 	strcat(strcpy(buf, resname), ".mci"); | 
 | 	if (OpenFile(buf, &ofs, OF_EXIST) != HFILE_ERROR) { | 
 | 	    FIXME("NIY: command table to be loaded from '%s'\n", ofs.szPathName); | 
 | 	} | 
 |     } | 
 | #endif | 
 |     if (!(hRsrc = FindResourceW(hInst, resNameW, (LPCWSTR)RT_RCDATAA))) { | 
 | 	WARN("No command table found in resource\n"); | 
 |     } else if ((hMem = LoadResource(hInst, hRsrc))) { | 
 | 	ret = MCI_SetCommandTable(iData, hMem, type); | 
 |     } else { | 
 | 	WARN("Couldn't load resource.\n"); | 
 |     } | 
 |     TRACE("=> %04x\n", ret); | 
 |     return ret; | 
 | } | 
 |  | 
 | #if 0 | 
 |     LPSTR 	resNameA; | 
 |     UINT	ret; | 
 |  | 
 |     TRACE("(%04x, %s, %d)!\n", hinst, debugstr_w(resNameW), type); | 
 |  | 
 |     resNameA = HEAP_strdupWtoA(GetProcessHeap(), 0, resNameW); | 
 |     ret = mciLoadCommandResource16(hinst, resNameA, type); | 
 |     HeapFree(GetProcessHeap(), 0, resNameA); | 
 |     return ret; | 
 | #endif | 
 |  | 
 | /************************************************************************** | 
 |  *                    	mciFreeCommandResource			[WINMM.@] | 
 |  */ | 
 | BOOL WINAPI mciFreeCommandResource(UINT uTable) | 
 | { | 
 |     TRACE("(%08x)!\n", uTable); | 
 |  | 
 |     return MCI_DeleteCommandTable(uTable); | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 			MCI_MapMsg16To32A			[internal] | 
 |  */ | 
 | static	MCI_MapType	MCI_MapMsg16To32A(WORD uDevType, WORD wMsg, DWORD* lParam) | 
 | { | 
 |     if (*lParam == 0) | 
 | 	return MCI_MAP_OK; | 
 |     /* FIXME: to add also (with seg/linear modifications to do): | 
 |      * MCI_LIST, MCI_LOAD, MCI_QUALITY, MCI_RESERVE, MCI_RESTORE, MCI_SAVE | 
 |      * MCI_SETAUDIO, MCI_SETTUNER, MCI_SETVIDEO | 
 |      */ | 
 |     switch (wMsg) { | 
 | 	/* case MCI_CAPTURE */ | 
 |     case MCI_CLOSE:		 | 
 |     case MCI_CLOSE_DRIVER:	 | 
 |     case MCI_CONFIGURE: | 
 |     case MCI_COPY: | 
 |     case MCI_CUE: | 
 |     case MCI_CUT: | 
 |     case MCI_DELETE: | 
 |     case MCI_FREEZE: | 
 |     case MCI_GETDEVCAPS: | 
 | 	/* case MCI_INDEX: */ | 
 | 	/* case MCI_MARK: */ | 
 | 	/* case MCI_MONITOR: */ | 
 |     case MCI_PASTE: | 
 |     case MCI_PAUSE:		 | 
 |     case MCI_PLAY:		 | 
 |     case MCI_PUT: | 
 |     case MCI_REALIZE: | 
 |     case MCI_RECORD:		 | 
 |     case MCI_RESUME:		 | 
 |     case MCI_SEEK:		 | 
 |     case MCI_SET: | 
 | 	/* case MCI_SETTIMECODE:*/ | 
 | 	/* case MCI_SIGNAL:*/ | 
 |     case MCI_SPIN: | 
 |     case MCI_STATUS:		/* FIXME: is wrong for digital video */ | 
 |     case MCI_STEP: | 
 |     case MCI_STOP:		 | 
 | 	/* case MCI_UNDO: */ | 
 |     case MCI_UNFREEZE: | 
 |     case MCI_UPDATE: | 
 |     case MCI_WHERE: | 
 | 	*lParam = (DWORD)MapSL(*lParam); | 
 | 	return MCI_MAP_OK; | 
 |     case MCI_WINDOW: | 
 | 	/* in fact, I would also need the dwFlags... to see  | 
 | 	 * which members of lParam are effectively used  | 
 | 	 */ | 
 | 	*lParam = (DWORD)MapSL(*lParam); | 
 | 	FIXME("Current mapping may be wrong\n"); | 
 | 	break; | 
 |     case MCI_BREAK: | 
 | 	{ | 
 |             LPMCI_BREAK_PARMS		mbp32 = HeapAlloc(GetProcessHeap(), 0, sizeof(MCI_BREAK_PARMS)); | 
 | 	    LPMCI_BREAK_PARMS16		mbp16 = MapSL(*lParam); | 
 |  | 
 | 	    if (mbp32) { | 
 | 		mbp32->dwCallback = mbp16->dwCallback; | 
 | 		mbp32->nVirtKey = mbp16->nVirtKey; | 
 | 		mbp32->hwndBreak = mbp16->hwndBreak; | 
 | 	    } else { | 
 | 		return MCI_MAP_NOMEM; | 
 | 	    } | 
 | 	    *lParam = (DWORD)mbp32; | 
 | 	} | 
 | 	return MCI_MAP_OKMEM; | 
 |     case MCI_ESCAPE: | 
 | 	{ | 
 |             LPMCI_VD_ESCAPE_PARMSA	mvep32a = HeapAlloc(GetProcessHeap(), 0, sizeof(MCI_VD_ESCAPE_PARMSA)); | 
 | 	    LPMCI_VD_ESCAPE_PARMS16	mvep16  = MapSL(*lParam); | 
 |  | 
 | 	    if (mvep32a) { | 
 | 		mvep32a->dwCallback       = mvep16->dwCallback; | 
 | 		mvep32a->lpstrCommand     = MapSL(mvep16->lpstrCommand); | 
 | 	    } else { | 
 | 		return MCI_MAP_NOMEM; | 
 | 	    } | 
 | 	    *lParam = (DWORD)mvep32a; | 
 | 	} | 
 | 	return MCI_MAP_OKMEM; | 
 |     case MCI_INFO: | 
 | 	{ | 
 |             LPMCI_INFO_PARMSA	mip32a = HeapAlloc(GetProcessHeap(), 0, sizeof(MCI_INFO_PARMSA)); | 
 | 	    LPMCI_INFO_PARMS16	mip16  = MapSL(*lParam); | 
 |  | 
 | 	    /* FIXME this is wrong if device is of type  | 
 | 	     * MCI_DEVTYPE_DIGITAL_VIDEO, some members are not mapped | 
 | 	     */ | 
 | 	    if (mip32a) { | 
 | 		mip32a->dwCallback  = mip16->dwCallback; | 
 | 		mip32a->lpstrReturn = MapSL(mip16->lpstrReturn); | 
 | 		mip32a->dwRetSize   = mip16->dwRetSize; | 
 | 	    } else { | 
 | 		return MCI_MAP_NOMEM; | 
 | 	    } | 
 | 	    *lParam = (DWORD)mip32a; | 
 | 	} | 
 | 	return MCI_MAP_OKMEM; | 
 |     case MCI_OPEN: | 
 |     case MCI_OPEN_DRIVER:	 | 
 | 	{ | 
 |             LPMCI_OPEN_PARMSA	mop32a = HeapAlloc(GetProcessHeap(), 0, sizeof(LPMCI_OPEN_PARMS16) + sizeof(MCI_OPEN_PARMSA) + 2 * sizeof(DWORD)); | 
 | 	    LPMCI_OPEN_PARMS16	mop16  = MapSL(*lParam); | 
 |  | 
 | 	    if (mop32a) { | 
 | 		*(LPMCI_OPEN_PARMS16*)(mop32a) = mop16; | 
 | 		mop32a = (LPMCI_OPEN_PARMSA)((char*)mop32a + sizeof(LPMCI_OPEN_PARMS16)); | 
 | 		mop32a->dwCallback       = mop16->dwCallback; | 
 | 		mop32a->wDeviceID        = mop16->wDeviceID; | 
 | 		mop32a->lpstrDeviceType  = MapSL(mop16->lpstrDeviceType); | 
 | 		mop32a->lpstrElementName = MapSL(mop16->lpstrElementName); | 
 | 		mop32a->lpstrAlias       = MapSL(mop16->lpstrAlias); | 
 | 		/* copy extended information if any... | 
 | 		 * FIXME: this may seg fault if initial structure does not contain them and | 
 | 		 * the reads after msip16 fail under LDT limits... | 
 | 		 * NOTE: this should be split in two. First pass, while calling MCI_OPEN, and  | 
 | 		 * should not take care of extended parameters, and should be used by MCI_Open | 
 | 		 * to fetch uDevType. When, this is known, the mapping for sending the  | 
 | 		 * MCI_OPEN_DRIVER shall be done depending on uDevType. | 
 | 		 */ | 
 | 		memcpy(mop32a + 1, mop16 + 1, 2 * sizeof(DWORD)); | 
 | 	    } else { | 
 | 		return MCI_MAP_NOMEM; | 
 | 	    } | 
 | 	    *lParam = (DWORD)mop32a; | 
 | 	} | 
 | 	return MCI_MAP_OKMEM; | 
 |     case MCI_SYSINFO: | 
 | 	{ | 
 |             LPMCI_SYSINFO_PARMSA	msip32a = HeapAlloc(GetProcessHeap(), 0, sizeof(MCI_SYSINFO_PARMSA)); | 
 | 	    LPMCI_SYSINFO_PARMS16	msip16  = MapSL(*lParam); | 
 |  | 
 | 	    if (msip32a) { | 
 | 		msip32a->dwCallback       = msip16->dwCallback; | 
 | 		msip32a->lpstrReturn      = MapSL(msip16->lpstrReturn); | 
 | 		msip32a->dwRetSize        = msip16->dwRetSize; | 
 | 		msip32a->dwNumber         = msip16->dwNumber; | 
 | 		msip32a->wDeviceType      = msip16->wDeviceType; | 
 | 	    } else { | 
 | 		return MCI_MAP_NOMEM; | 
 | 	    } | 
 | 	    *lParam = (DWORD)msip32a; | 
 | 	} | 
 | 	return MCI_MAP_OKMEM; | 
 |     case DRV_LOAD: | 
 |     case DRV_ENABLE: | 
 |     case DRV_OPEN: | 
 |     case DRV_CLOSE: | 
 |     case DRV_DISABLE: | 
 |     case DRV_FREE: | 
 |     case DRV_CONFIGURE: | 
 |     case DRV_QUERYCONFIGURE: | 
 |     case DRV_INSTALL: | 
 |     case DRV_REMOVE: | 
 |     case DRV_EXITSESSION: | 
 |     case DRV_EXITAPPLICATION: | 
 |     case DRV_POWER: | 
 | 	FIXME("This is a hack\n"); | 
 | 	return MCI_MAP_OK; | 
 |  | 
 |     default: | 
 | 	WARN("Don't know how to map msg=%s\n", MCI_MessageToString(wMsg)); | 
 |     } | 
 |     return MCI_MAP_MSGERROR; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 			MCI_UnMapMsg16To32A			[internal] | 
 |  */ | 
 | static	MCI_MapType	MCI_UnMapMsg16To32A(WORD uDevType, WORD wMsg, DWORD lParam) | 
 | { | 
 |     switch (wMsg) { | 
 | 	/* case MCI_CAPTURE */ | 
 |     case MCI_CLOSE: | 
 |     case MCI_CLOSE_DRIVER:	 | 
 |     case MCI_CONFIGURE: | 
 |     case MCI_COPY: | 
 |     case MCI_CUE: | 
 |     case MCI_CUT: | 
 |     case MCI_DELETE: | 
 |     case MCI_FREEZE: | 
 |     case MCI_GETDEVCAPS: | 
 | 	/* case MCI_INDEX: */ | 
 | 	/* case MCI_MARK: */ | 
 | 	/* case MCI_MONITOR: */ | 
 |     case MCI_PASTE: | 
 |     case MCI_PAUSE:		 | 
 |     case MCI_PLAY:		 | 
 |     case MCI_PUT: | 
 |     case MCI_REALIZE: | 
 |     case MCI_RECORD:		 | 
 |     case MCI_RESUME:		 | 
 |     case MCI_SEEK:		 | 
 |     case MCI_SET: | 
 | 	/* case MCI_SETTIMECODE:*/ | 
 | 	/* case MCI_SIGNAL:*/ | 
 |     case MCI_SPIN: | 
 |     case MCI_STATUS:		 | 
 |     case MCI_STEP: | 
 |     case MCI_STOP:		 | 
 | 	/* case MCI_UNDO: */ | 
 |     case MCI_UNFREEZE: | 
 |     case MCI_UPDATE: | 
 |     case MCI_WHERE: | 
 | 	return MCI_MAP_OK; | 
 |  | 
 |     case MCI_WINDOW: | 
 | 	/* FIXME ?? see Map function */ | 
 | 	return MCI_MAP_OK; | 
 |  | 
 |     case MCI_BREAK: | 
 |     case MCI_ESCAPE: | 
 |     case MCI_INFO: | 
 |     case MCI_SYSINFO: | 
 | 	HeapFree(GetProcessHeap(), 0, (LPVOID)lParam); | 
 | 	return MCI_MAP_OK; | 
 |     case MCI_OPEN: | 
 |     case MCI_OPEN_DRIVER:	 | 
 | 	if (lParam) { | 
 |             LPMCI_OPEN_PARMSA	mop32a = (LPMCI_OPEN_PARMSA)lParam; | 
 | 	    LPMCI_OPEN_PARMS16	mop16  = *(LPMCI_OPEN_PARMS16*)((char*)mop32a - sizeof(LPMCI_OPEN_PARMS16)); | 
 | 	     | 
 | 	    mop16->wDeviceID = mop32a->wDeviceID; | 
 | 	    if (!HeapFree(GetProcessHeap(), 0, (LPVOID)(lParam - sizeof(LPMCI_OPEN_PARMS16)))) | 
 | 		FIXME("bad free line=%d\n", __LINE__); | 
 | 	} | 
 | 	return MCI_MAP_OK; | 
 |     case DRV_LOAD: | 
 |     case DRV_ENABLE: | 
 |     case DRV_OPEN: | 
 |     case DRV_CLOSE: | 
 |     case DRV_DISABLE: | 
 |     case DRV_FREE: | 
 |     case DRV_CONFIGURE: | 
 |     case DRV_QUERYCONFIGURE: | 
 |     case DRV_INSTALL: | 
 |     case DRV_REMOVE: | 
 |     case DRV_EXITSESSION: | 
 |     case DRV_EXITAPPLICATION: | 
 |     case DRV_POWER: | 
 | 	FIXME("This is a hack\n"); | 
 | 	return MCI_MAP_OK; | 
 |     default: | 
 | 	FIXME("Map/Unmap internal error on msg=%s\n", MCI_MessageToString(wMsg)); | 
 |     } | 
 |     return MCI_MAP_MSGERROR; | 
 | } | 
 |  | 
 | /* | 
 |  * 0000 stop | 
 |  * 0001 squeeze   signed 4 bytes to 2 bytes     *( LPINT16)D = ( INT16)*( LPINT16)S; D += 2;     S += 4 | 
 |  * 0010 squeeze unsigned 4 bytes to 2 bytes     *(LPUINT16)D = (UINT16)*(LPUINT16)S; D += 2;     S += 4 | 
 |  * 0100 | 
 |  * 0101  | 
 |  * 0110 zero 4 bytes                            *(DWORD)D = 0                        D += 4;     S += 4 | 
 |  * 0111 copy string                             *(LPSTR*)D = seg dup(*(LPSTR*)S)     D += 4;     S += 4 | 
 |  * 1xxx copy xxx + 1 bytes                      memcpy(D, S, xxx + 1);               D += xxx+1; S += xxx+1 | 
 |  */ | 
 |  | 
 | /************************************************************************** | 
 |  * 			MCI_MsgMapper32To16_Create		[internal] | 
 |  * | 
 |  * Helper for MCI_MapMsg32ATo16.  | 
 |  * Maps the 32 bit pointer (*ptr), of size bytes, to an allocated 16 bit  | 
 |  * segmented pointer. | 
 |  * map contains a list of action to be performed for the mapping (see list | 
 |  * above) | 
 |  * if keep is TRUE, keeps track of in 32 bit ptr in allocated 16 bit area. | 
 |  */ | 
 | static	MCI_MapType	MCI_MsgMapper32To16_Create(void** ptr, int size16, DWORD map, BOOLEAN keep) | 
 | { | 
 |     void*	lp = HeapAlloc( GetProcessHeap(), 0, (keep ? sizeof(void**) : 0) + size16 ); | 
 |     LPBYTE	p16, p32; | 
 |  | 
 |     if (!lp) { | 
 | 	return MCI_MAP_NOMEM; | 
 |     } | 
 |     p32 = (LPBYTE)(*ptr); | 
 |     if (keep) { | 
 | 	*(void**)lp = *ptr; | 
 | 	p16 = (LPBYTE)lp + sizeof(void**); | 
 | 	*ptr = (char*)MapLS(lp) + sizeof(void**); | 
 |     } else { | 
 | 	p16 = lp; | 
 | 	*ptr = (void*)MapLS(lp); | 
 |     } | 
 |      | 
 |     if (map == 0) { | 
 | 	memcpy(p16, p32, size16); | 
 |     } else { | 
 | 	unsigned	nibble; | 
 | 	unsigned	sz; | 
 |  | 
 | 	while (map & 0xF) { | 
 | 	    nibble = map & 0xF; | 
 | 	    if (nibble & 0x8) { | 
 | 		sz = (nibble & 7) + 1; | 
 | 		memcpy(p16, p32, sz); | 
 | 		p16 += sz; | 
 | 		p32 += sz; | 
 | 		size16 -= sz;	/* DEBUG only */ | 
 | 	    } else { | 
 | 		switch (nibble) { | 
 | 		case 0x1: | 
 |                     *(LPINT16)p16 = *(LPINT)p32; | 
 |                     p16 += sizeof(INT16); | 
 |                     p32 += sizeof(INT); | 
 |                     size16 -= sizeof(INT16); | 
 |                     break; | 
 | 		case 0x2: | 
 |                     *(LPUINT16)p16 = *(LPUINT)p32; | 
 |                     p16 += sizeof(UINT16); | 
 |                     p32 += sizeof(UINT); | 
 |                     size16 -= sizeof(UINT16); | 
 |                     break; | 
 | 		case 0x6: | 
 |                     *(LPDWORD)p16 = 0; | 
 |                     p16 += sizeof(DWORD); | 
 |                     p32 += sizeof(DWORD); | 
 |                     size16 -= sizeof(DWORD); | 
 |                     break; | 
 | 		case 0x7: | 
 |                     *(SEGPTR *)p16 = MapLS( *(LPSTR *)p32 ); | 
 |                     p16 += sizeof(SEGPTR); | 
 |                     p32 += sizeof(LPSTR); | 
 |                     size16 -= sizeof(SEGPTR); | 
 |                     break; | 
 | 		default: | 
 |                     FIXME("Unknown nibble for mapping (%x)\n", nibble); | 
 | 		} | 
 | 	    } | 
 | 	    map >>= 4; | 
 | 	} | 
 | 	if (size16 != 0) /* DEBUG only */ | 
 | 	    FIXME("Mismatch between 16 bit struct size and map nibbles serie\n"); | 
 |     } | 
 |     return MCI_MAP_OKMEM; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 			MCI_MsgMapper32To16_Destroy		[internal] | 
 |  * | 
 |  * Helper for MCI_UnMapMsg32ATo16.  | 
 |  */ | 
 | static	MCI_MapType	MCI_MsgMapper32To16_Destroy(void* ptr, int size16, DWORD map, BOOLEAN kept) | 
 | { | 
 |     if (ptr) { | 
 | 	void*		msg16 = MapSL((SEGPTR)ptr); | 
 | 	void*		alloc; | 
 | 	LPBYTE		p32, p16; | 
 | 	unsigned	nibble; | 
 |  | 
 |         UnMapLS( (SEGPTR)ptr ); | 
 | 	if (kept) { | 
 | 	    alloc = (char*)msg16 - sizeof(void**); | 
 | 	    p32 = *(void**)alloc; | 
 | 	    p16 = msg16; | 
 |  | 
 | 	    if (map == 0) { | 
 | 		memcpy(p32, p16, size16); | 
 | 	    } else { | 
 | 		while (map & 0xF) { | 
 | 		    nibble = map & 0xF; | 
 | 		    if (nibble & 0x8) { | 
 | 			memcpy(p32, p16, (nibble & 7) + 1); | 
 | 			p16 += (nibble & 7) + 1; | 
 | 			p32 += (nibble & 7) + 1; | 
 | 			size16 -= (nibble & 7) + 1; | 
 | 		    } else { | 
 | 			switch (nibble) { | 
 | 			case 0x1: | 
 |                             *(LPINT)p32 = *(LPINT16)p16; | 
 |                             p16 += sizeof(INT16); | 
 |                             p32 += sizeof(INT); | 
 |                             size16 -= sizeof(INT16); | 
 |                             break; | 
 | 			case 0x2: | 
 |                             *(LPUINT)p32 = *(LPUINT16)p16; | 
 |                             p16 += sizeof(UINT16); | 
 |                             p32 += sizeof(UINT); | 
 |                             size16 -= sizeof(UINT16); | 
 |                             break; | 
 | 			case 0x6: | 
 |                             p16 += sizeof(UINT); | 
 |                             p32 += sizeof(UINT); | 
 |                             size16 -= sizeof(UINT); | 
 |                             break; | 
 | 			case 0x7: | 
 |                             UnMapLS( *(SEGPTR *)p16 ); | 
 |                             p16 += sizeof(SEGPTR); | 
 |                             p32 += sizeof(char*); | 
 |                             size16 -= sizeof(SEGPTR); | 
 |                             break; | 
 | 			default: | 
 |                             FIXME("Unknown nibble for mapping (%x)\n", nibble); | 
 | 			} | 
 | 		    } | 
 | 		    map >>= 4; | 
 | 		} | 
 | 		if (size16 != 0) /* DEBUG only */ | 
 | 		    FIXME("Mismatch between 16 bit struct size and map nibbles serie\n"); | 
 | 	    } | 
 | 	} else { | 
 | 	    alloc = msg16; | 
 | 	} | 
 |  | 
 |         HeapFree( GetProcessHeap(), 0, alloc ); | 
 |     } | 
 |     return MCI_MAP_OK; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 			MCI_MapMsg32ATo16			[internal] | 
 |  * | 
 |  * Map a 32-A bit MCI message to a 16 bit MCI message. | 
 |  */ | 
 | static	MCI_MapType	MCI_MapMsg32ATo16(WORD uDevType, WORD wMsg, DWORD dwFlags, DWORD* lParam) | 
 | { | 
 |     int		size; | 
 |     BOOLEAN     keep = FALSE; | 
 |     DWORD	map = 0; | 
 |  | 
 |     if (*lParam == 0) | 
 | 	return MCI_MAP_OK; | 
 |  | 
 |     /* FIXME: to add also (with seg/linear modifications to do): | 
 |      * MCI_LIST, MCI_LOAD, MCI_QUALITY, MCI_RESERVE, MCI_RESTORE, MCI_SAVE | 
 |      * MCI_SETAUDIO, MCI_SETTUNER, MCI_SETVIDEO | 
 |      */ | 
 |     switch (wMsg) { | 
 |     case MCI_BREAK: | 
 | 	size = sizeof(MCI_BREAK_PARMS); | 
 | 	break; | 
 | 	/* case MCI_CAPTURE */ | 
 |     case MCI_CLOSE: | 
 |     case MCI_CLOSE_DRIVER:	 | 
 |     case MCI_CONFIGURE: | 
 | 	size = sizeof(MCI_GENERIC_PARMS);	 | 
 | 	break; | 
 | 	/* case MCI_COPY: */ | 
 |     case MCI_CUE: | 
 | 	switch (uDevType) { | 
 | 	case MCI_DEVTYPE_DIGITAL_VIDEO:	size = sizeof(MCI_DGV_CUE_PARMS);	break; | 
 | 	case MCI_DEVTYPE_VCR:		/*size = sizeof(MCI_VCR_CUE_PARMS);	break;*/	FIXME("NIY vcr\n");	return MCI_MAP_NOMEM; | 
 | 	default:			size = sizeof(MCI_GENERIC_PARMS);	break; | 
 | 	} | 
 | 	break; | 
 | 	/* case MCI_CUT:*/ | 
 |     case MCI_DELETE: | 
 | 	switch (uDevType) { | 
 | 	case MCI_DEVTYPE_DIGITAL_VIDEO:	size = sizeof(MCI_DGV_DELETE_PARMS16);	map = 0x0F1111FB;	break; | 
 | 	case MCI_DEVTYPE_WAVEFORM_AUDIO:size = sizeof(MCI_WAVE_DELETE_PARMS);	break; | 
 | 	default:			size = sizeof(MCI_GENERIC_PARMS);	break; | 
 | 	} | 
 | 	break; | 
 | 	/* case MCI_ESCAPE: */ | 
 |     case MCI_FREEZE: | 
 | 	switch (uDevType) { | 
 | 	case MCI_DEVTYPE_DIGITAL_VIDEO:	size = sizeof(MCI_DGV_FREEZE_PARMS);	map = 0x0001111B; 	break; | 
 | 	case MCI_DEVTYPE_OVERLAY:	size = sizeof(MCI_OVLY_RECT_PARMS);	map = 0x0001111B;	break; | 
 | 	default:			size = sizeof(MCI_GENERIC_PARMS);	break; | 
 | 	} | 
 | 	break; | 
 |     case MCI_GETDEVCAPS: | 
 | 	keep = TRUE; | 
 | 	size = sizeof(MCI_GETDEVCAPS_PARMS); | 
 | 	break; | 
 | 	/* case MCI_INDEX: */ | 
 |     case MCI_INFO: | 
 | 	{ | 
 |             LPMCI_INFO_PARMSA	mip32a = (LPMCI_INFO_PARMSA)(*lParam); | 
 | 	    LPMCI_INFO_PARMS16	mip16; | 
 | 	     | 
 | 	    switch (uDevType) { | 
 | 	    case MCI_DEVTYPE_DIGITAL_VIDEO:	size = sizeof(MCI_DGV_INFO_PARMS16);	break; | 
 | 	    default:				size = sizeof(MCI_INFO_PARMS16);	break; | 
 | 	    } | 
 |             mip16 = HeapAlloc( GetProcessHeap(), 0, size); | 
 |             if (mip16) | 
 |             { | 
 | 		mip16->dwCallback  = mip32a->dwCallback; | 
 | 		mip16->lpstrReturn = MapLS( mip32a->lpstrReturn ); | 
 | 		mip16->dwRetSize   = mip32a->dwRetSize; | 
 | 		if (uDevType == MCI_DEVTYPE_DIGITAL_VIDEO) { | 
 | 		    ((LPMCI_DGV_INFO_PARMS16)mip16)->dwItem = ((LPMCI_DGV_INFO_PARMSA)mip32a)->dwItem; | 
 | 		} | 
 | 	    } else { | 
 | 		return MCI_MAP_NOMEM; | 
 | 	    } | 
 |             *lParam = MapLS(mip16); | 
 | 	} | 
 | 	return MCI_MAP_OKMEM; | 
 | 	/* case MCI_MARK: */ | 
 | 	/* case MCI_MONITOR: */ | 
 |     case MCI_OPEN: | 
 |     case MCI_OPEN_DRIVER:	 | 
 | 	{ | 
 |             LPMCI_OPEN_PARMSA	mop32a = (LPMCI_OPEN_PARMSA)(*lParam); | 
 |             char* ptr = HeapAlloc( GetProcessHeap(), 0, | 
 |                                    sizeof(LPMCI_OPEN_PARMSA) + sizeof(MCI_OPEN_PARMS16) + 2 * sizeof(DWORD)); | 
 | 	    LPMCI_OPEN_PARMS16	mop16; | 
 |  | 
 |  | 
 | 	    if (ptr) { | 
 | 		*(LPMCI_OPEN_PARMSA*)(ptr) = mop32a; | 
 | 		mop16 = (LPMCI_OPEN_PARMS16)(ptr + sizeof(LPMCI_OPEN_PARMSA)); | 
 | 		mop16->dwCallback       = mop32a->dwCallback; | 
 | 		mop16->wDeviceID        = mop32a->wDeviceID; | 
 | 		if (dwFlags & MCI_OPEN_TYPE) { | 
 | 		    if (dwFlags & MCI_OPEN_TYPE_ID) { | 
 | 			/* dword "transparent" value */ | 
 | 			mop16->lpstrDeviceType = (SEGPTR)mop32a->lpstrDeviceType; | 
 | 		    } else { | 
 | 			/* string */ | 
 | 			mop16->lpstrDeviceType = MapLS( mop32a->lpstrDeviceType ); | 
 | 		    } | 
 | 		} else { | 
 | 		    /* nuthin' */ | 
 | 		    mop16->lpstrDeviceType = 0; | 
 | 		} | 
 | 		if (dwFlags & MCI_OPEN_ELEMENT) { | 
 | 		    if (dwFlags & MCI_OPEN_ELEMENT_ID) { | 
 | 			mop16->lpstrElementName = (SEGPTR)mop32a->lpstrElementName; | 
 | 		    } else { | 
 | 			mop16->lpstrElementName = MapLS( mop32a->lpstrElementName ); | 
 | 		    } | 
 | 		} else { | 
 | 		    mop16->lpstrElementName = 0; | 
 | 		} | 
 | 		if (dwFlags & MCI_OPEN_ALIAS) { | 
 | 		    mop16->lpstrAlias = MapLS( mop32a->lpstrAlias ); | 
 | 		} else { | 
 | 		    mop16->lpstrAlias = 0; | 
 | 		} | 
 | 		/* copy extended information if any... | 
 | 		 * FIXME: this may seg fault if initial structure does not contain them and | 
 | 		 * the reads after msip16 fail under LDT limits... | 
 | 		 * NOTE: this should be split in two. First pass, while calling MCI_OPEN, and  | 
 | 		 * should not take care of extended parameters, and should be used by MCI_Open | 
 | 		 * to fetch uDevType. When, this is known, the mapping for sending the  | 
 | 		 * MCI_OPEN_DRIVER shall be done depending on uDevType. | 
 | 		 */ | 
 | 		memcpy(mop16 + 1, mop32a + 1, 2 * sizeof(DWORD)); | 
 | 	    } else { | 
 | 		return MCI_MAP_NOMEM; | 
 | 	    } | 
 | 	    *lParam = (LPARAM)MapLS(ptr) + sizeof(LPMCI_OPEN_PARMSA); | 
 | 	} | 
 | 	return MCI_MAP_OKMEM; | 
 | 	/* case MCI_PASTE:*/ | 
 |     case MCI_PAUSE: | 
 | 	size = sizeof(MCI_GENERIC_PARMS); | 
 | 	break; | 
 |     case MCI_PLAY: | 
 | 	size = sizeof(MCI_PLAY_PARMS); | 
 | 	break; | 
 |     case MCI_PUT: | 
 | 	switch (uDevType) { | 
 | 	case MCI_DEVTYPE_DIGITAL_VIDEO:	size = sizeof(MCI_DGV_RECT_PARMS16);	map = 0x0001111B;	break; | 
 | 	case MCI_DEVTYPE_OVERLAY:	size = sizeof(MCI_OVLY_RECT_PARMS);	map = 0x0001111B;	break; | 
 | 	default:			size = sizeof(MCI_GENERIC_PARMS);	break; | 
 | 	} | 
 | 	break; | 
 |     case MCI_REALIZE: | 
 | 	size = sizeof(MCI_GENERIC_PARMS); | 
 | 	break; | 
 |     case MCI_RECORD: | 
 | 	switch (uDevType) { | 
 | 	case MCI_DEVTYPE_DIGITAL_VIDEO:	size = sizeof(MCI_DGV_RECORD_PARMS16);	map = 0x0F1111FB;	break; | 
 | 	case MCI_DEVTYPE_VCR:		/*size = sizeof(MCI_VCR_RECORD_PARMS);	break;*/FIXME("NIY vcr\n");	return MCI_MAP_NOMEM; | 
 | 	default:			size = sizeof(MCI_RECORD_PARMS);	break; | 
 | 	} | 
 | 	break; | 
 |     case MCI_RESUME:		 | 
 | 	size = sizeof(MCI_GENERIC_PARMS); | 
 | 	break; | 
 |     case MCI_SEEK: | 
 | 	switch (uDevType) { | 
 | 	case MCI_DEVTYPE_VCR:		/*size = sizeof(MCI_VCR_SEEK_PARMS);	break;*/FIXME("NIY vcr\n");	return MCI_MAP_NOMEM; | 
 | 	default:			size = sizeof(MCI_SEEK_PARMS);		break; | 
 | 	} | 
 | 	break; | 
 |     case MCI_SET: | 
 | 	switch (uDevType) { | 
 | 	case MCI_DEVTYPE_DIGITAL_VIDEO:	size = sizeof(MCI_DGV_SET_PARMS);	break; | 
 | 	case MCI_DEVTYPE_VCR:		/*size = sizeof(MCI_VCR_SET_PARMS);	break;*/FIXME("NIY vcr\n");	return MCI_MAP_NOMEM; | 
 | 	case MCI_DEVTYPE_SEQUENCER:	size = sizeof(MCI_SEQ_SET_PARMS);	break; | 
 |         /* FIXME: normally the 16 and 32 bit structures are byte by byte aligned,  | 
 | 	 * so not doing anything should work... | 
 | 	 */ | 
 | 	case MCI_DEVTYPE_WAVEFORM_AUDIO:size = sizeof(MCI_WAVE_SET_PARMS);	break; | 
 | 	default:			size = sizeof(MCI_SET_PARMS);		break; | 
 | 	} | 
 | 	break;	 | 
 |     case MCI_SETAUDIO: | 
 | 	switch (uDevType) { | 
 | 	case MCI_DEVTYPE_DIGITAL_VIDEO:	size = sizeof(MCI_DGV_SETAUDIO_PARMS16);map = 0x0000077FF;	break; | 
 | 	case MCI_DEVTYPE_VCR:		/*size = sizeof(MCI_VCR_SETAUDIO_PARMS);	break;*/FIXME("NIY vcr\n");	return MCI_MAP_NOMEM; | 
 | 	default:			size = sizeof(MCI_GENERIC_PARMS);	break; | 
 | 	} | 
 | 	break; | 
 | 	/* case MCI_SETTIMECODE:*/ | 
 | 	/* case MCI_SIGNAL:*/ | 
 |     case MCI_SPIN: | 
 | 	size = sizeof(MCI_SET_PARMS);		 | 
 | 	break; | 
 |     case MCI_STATUS: | 
 | 	keep = TRUE; | 
 | 	switch (uDevType) { | 
 | 	/* FIXME: | 
 | 	 * don't know if buffer for value is the one passed through lpstrDevice  | 
 | 	 * or is provided by MCI driver. | 
 | 	 * Assuming solution 2: provided by MCI driver, so zeroing on entry | 
 | 	 */ | 
 | 	case MCI_DEVTYPE_DIGITAL_VIDEO:	size = sizeof(MCI_DGV_STATUS_PARMS16);	map = 0x0B6FF;		break; | 
 | 	case MCI_DEVTYPE_VCR:		/*size = sizeof(MCI_VCR_STATUS_PARMS);	break;*/FIXME("NIY vcr\n");	return MCI_MAP_NOMEM; | 
 | 	default:			size = sizeof(MCI_STATUS_PARMS);	break; | 
 | 	} | 
 | 	break; | 
 |     case MCI_STEP: | 
 | 	switch (uDevType) { | 
 | 	case MCI_DEVTYPE_DIGITAL_VIDEO:	size = sizeof(MCI_DGV_STEP_PARMS);	break; | 
 | 	case MCI_DEVTYPE_VCR:		/*size = sizeof(MCI_VCR_STEP_PARMS);	break;*/FIXME("NIY vcr\n");	return MCI_MAP_NOMEM; | 
 | 	case MCI_DEVTYPE_VIDEODISC:	size = sizeof(MCI_VD_STEP_PARMS);	break; | 
 | 	default:			size = sizeof(MCI_GENERIC_PARMS);	break; | 
 | 	} | 
 | 	break;	 | 
 |     case MCI_STOP:		 | 
 | 	size = sizeof(MCI_SET_PARMS);		 | 
 | 	break; | 
 |     case MCI_SYSINFO: | 
 | 	{ | 
 |             LPMCI_SYSINFO_PARMSA  msip32a = (LPMCI_SYSINFO_PARMSA)(*lParam); | 
 |             LPMCI_SYSINFO_PARMS16 msip16; | 
 |             char* ptr = HeapAlloc( GetProcessHeap(), 0, | 
 |                                    sizeof(LPMCI_SYSINFO_PARMSA) + sizeof(MCI_SYSINFO_PARMS16) ); | 
 |  | 
 | 	    if (ptr) { | 
 | 		*(LPMCI_SYSINFO_PARMSA*)(ptr) = msip32a; | 
 | 		msip16 = (LPMCI_SYSINFO_PARMS16)(ptr + sizeof(LPMCI_SYSINFO_PARMSA)); | 
 |  | 
 | 		msip16->dwCallback       = msip32a->dwCallback; | 
 | 		msip16->lpstrReturn      = MapLS( msip32a->lpstrReturn ); | 
 | 		msip16->dwRetSize        = msip32a->dwRetSize; | 
 | 		msip16->dwNumber         = msip32a->dwNumber; | 
 | 		msip16->wDeviceType      = msip32a->wDeviceType; | 
 | 	    } else { | 
 | 		return MCI_MAP_NOMEM; | 
 | 	    } | 
 | 	    *lParam = (LPARAM)MapLS(ptr) + sizeof(LPMCI_SYSINFO_PARMSA); | 
 | 	} | 
 | 	return MCI_MAP_OKMEM; | 
 | 	/* case MCI_UNDO: */ | 
 |     case MCI_UNFREEZE: | 
 | 	switch (uDevType) { | 
 | 	case MCI_DEVTYPE_DIGITAL_VIDEO:	size = sizeof(MCI_DGV_RECT_PARMS16);	map = 0x0001111B;	break; | 
 | 	case MCI_DEVTYPE_OVERLAY:	size = sizeof(MCI_OVLY_RECT_PARMS16);	map = 0x0001111B;	break; | 
 | 	default:			size = sizeof(MCI_GENERIC_PARMS);	break; | 
 | 	} | 
 | 	break; | 
 |     case MCI_UPDATE: | 
 | 	switch (uDevType) { | 
 | 	case MCI_DEVTYPE_DIGITAL_VIDEO:	size = sizeof(MCI_DGV_UPDATE_PARMS16);	map = 0x000B1111B;	break; | 
 | 	default:			size = sizeof(MCI_GENERIC_PARMS);	break; | 
 | 	} | 
 | 	break; | 
 |     case MCI_WHERE: | 
 | 	switch (uDevType) { | 
 | 	case MCI_DEVTYPE_DIGITAL_VIDEO:	size = sizeof(MCI_DGV_RECT_PARMS16);	map = 0x0001111B;	keep = TRUE;	break; | 
 | 	case MCI_DEVTYPE_OVERLAY:	size = sizeof(MCI_OVLY_RECT_PARMS16);	map = 0x0001111B;	keep = TRUE;	break; | 
 | 	default:			size = sizeof(MCI_GENERIC_PARMS);	break; | 
 | 	} | 
 | 	break; | 
 |     case MCI_WINDOW: | 
 | 	switch (uDevType) { | 
 | 	case MCI_DEVTYPE_DIGITAL_VIDEO:	size = sizeof(MCI_DGV_WINDOW_PARMS16);	if (dwFlags & MCI_DGV_WINDOW_TEXT)  map = 0x7FB;	break; | 
 | 	case MCI_DEVTYPE_OVERLAY:	size = sizeof(MCI_OVLY_WINDOW_PARMS16);	if (dwFlags & MCI_OVLY_WINDOW_TEXT) map = 0x7FB;	break; | 
 | 	default:			size = sizeof(MCI_GENERIC_PARMS);	break; | 
 | 	} | 
 | 	break; | 
 |     case DRV_OPEN: | 
 | 	{ | 
 |             LPMCI_OPEN_DRIVER_PARMSA  modp32a = (LPMCI_OPEN_DRIVER_PARMSA)(*lParam); | 
 |             LPMCI_OPEN_DRIVER_PARMS16 modp16; | 
 |             char *ptr = HeapAlloc( GetProcessHeap(), 0, | 
 |                                   sizeof(LPMCI_OPEN_DRIVER_PARMSA) + sizeof(MCI_OPEN_DRIVER_PARMS16)); | 
 |  | 
 | 	    if (ptr) { | 
 | 		*(LPMCI_OPEN_DRIVER_PARMSA*)(ptr) = modp32a; | 
 | 		modp16 = (LPMCI_OPEN_DRIVER_PARMS16)(ptr + sizeof(LPMCI_OPEN_DRIVER_PARMSA)); | 
 | 		modp16->wDeviceID = modp32a->wDeviceID; | 
 | 		modp16->lpstrParams = MapLS( modp32a->lpstrParams ); | 
 | 		/* other fields are gonna be filled by the driver, don't copy them */ | 
 |  	    } else { | 
 | 		return MCI_MAP_NOMEM; | 
 | 	    } | 
 | 	    *lParam = (LPARAM)MapLS(ptr) + sizeof(LPMCI_OPEN_DRIVER_PARMSA); | 
 | 	} | 
 | 	return MCI_MAP_OKMEM; | 
 |     case DRV_LOAD: | 
 |     case DRV_ENABLE: | 
 |     case DRV_CLOSE: | 
 |     case DRV_DISABLE: | 
 |     case DRV_FREE: | 
 |     case DRV_CONFIGURE: | 
 |     case DRV_QUERYCONFIGURE: | 
 |     case DRV_INSTALL: | 
 |     case DRV_REMOVE: | 
 |     case DRV_EXITSESSION: | 
 |     case DRV_EXITAPPLICATION: | 
 |     case DRV_POWER: | 
 | 	return MCI_MAP_OK; | 
 |  | 
 |     default: | 
 | 	WARN("Don't know how to map msg=%s\n", MCI_MessageToString(wMsg)); | 
 | 	return MCI_MAP_MSGERROR; | 
 |     } | 
 |     return MCI_MsgMapper32To16_Create((void**)lParam, size, map, keep); | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 			MCI_UnMapMsg32ATo16			[internal] | 
 |  */ | 
 | static	MCI_MapType	MCI_UnMapMsg32ATo16(WORD uDevType, WORD wMsg, DWORD dwFlags, DWORD lParam) | 
 | { | 
 |     int		size = 0; | 
 |     BOOLEAN     kept = FALSE;	/* there is no need to compute size when kept is FALSE */ | 
 |     DWORD 	map = 0; | 
 |  | 
 |     switch (wMsg) { | 
 |     case MCI_BREAK: | 
 |         break; | 
 | 	/* case MCI_CAPTURE */ | 
 |     case MCI_CLOSE:	 | 
 |     case MCI_CLOSE_DRIVER:	 | 
 |     case MCI_CONFIGURE: | 
 | 	break; | 
 | 	/* case MCI_COPY: */ | 
 |     case MCI_CUE:	 | 
 | 	break; | 
 | 	/* case MCI_CUT: */ | 
 |     case MCI_DELETE: | 
 | 	break; | 
 | 	/* case MCI_ESCAPE: */ | 
 |     case MCI_FREEZE: | 
 | 	break; | 
 |     case MCI_GETDEVCAPS: | 
 | 	kept = TRUE; | 
 | 	size = sizeof(MCI_GETDEVCAPS_PARMS); | 
 | 	break; | 
 | 	/* case MCI_INDEX: */ | 
 |     case MCI_INFO: | 
 | 	{ | 
 |             LPMCI_INFO_PARMS16 mip16  = (LPMCI_INFO_PARMS16)MapSL(lParam); | 
 |             UnMapLS( lParam ); | 
 |             UnMapLS( mip16->lpstrReturn ); | 
 |             HeapFree( GetProcessHeap(), 0, mip16 ); | 
 | 	} | 
 | 	return MCI_MAP_OK; | 
 | 	/* case MCI_MARK: */ | 
 | 	/* case MCI_MONITOR: */ | 
 |     case MCI_OPEN: | 
 |     case MCI_OPEN_DRIVER:	 | 
 | 	if (lParam) { | 
 |             LPMCI_OPEN_PARMS16	mop16  = (LPMCI_OPEN_PARMS16)MapSL(lParam); | 
 | 	    LPMCI_OPEN_PARMSA	mop32a = *(LPMCI_OPEN_PARMSA*)((char*)mop16 - sizeof(LPMCI_OPEN_PARMSA)); | 
 |             UnMapLS( lParam ); | 
 | 	    mop32a->wDeviceID = mop16->wDeviceID; | 
 |             if ((dwFlags & MCI_OPEN_TYPE) && !(dwFlags & MCI_OPEN_TYPE_ID)) | 
 |                 UnMapLS( mop16->lpstrDeviceType ); | 
 |             if ((dwFlags & MCI_OPEN_ELEMENT) && !(dwFlags & MCI_OPEN_ELEMENT_ID)) | 
 |                 UnMapLS( mop16->lpstrElementName ); | 
 |             if (dwFlags & MCI_OPEN_ALIAS) | 
 |                 UnMapLS( mop16->lpstrAlias ); | 
 |             HeapFree( GetProcessHeap(), 0, (char*)mop16 - sizeof(LPMCI_OPEN_PARMSA) ); | 
 | 	} | 
 | 	return MCI_MAP_OK; | 
 | 	/* case MCI_PASTE:*/ | 
 |     case MCI_PAUSE: | 
 | 	break; | 
 |     case MCI_PLAY:		 | 
 | 	break; | 
 |     case MCI_PUT: | 
 | 	break; | 
 |     case MCI_REALIZE: | 
 | 	break; | 
 |     case MCI_RECORD: | 
 | 	break; | 
 |     case MCI_RESUME: | 
 | 	break; | 
 |     case MCI_SEEK: | 
 | 	break; | 
 |     case MCI_SET: | 
 | 	break; | 
 |     case MCI_SETAUDIO: | 
 | 	switch (uDevType) { | 
 | 	case MCI_DEVTYPE_DIGITAL_VIDEO:	map = 0x0000077FF;	break; | 
 | 	case MCI_DEVTYPE_VCR:		/*size = sizeof(MCI_VCR_SETAUDIO_PARMS);	break;*/FIXME("NIY vcr\n");	return MCI_MAP_NOMEM; | 
 | 	} | 
 | 	break; | 
 | 	/* case MCI_SETTIMECODE:*/ | 
 | 	/* case MCI_SIGNAL:*/ | 
 |     case MCI_SPIN: | 
 | 	break; | 
 |     case MCI_STATUS: | 
 | 	kept = TRUE; | 
 | 	switch (uDevType) { | 
 | 	case MCI_DEVTYPE_DIGITAL_VIDEO:	 | 
 | 	if (lParam) { | 
 |             LPMCI_DGV_STATUS_PARMS16	mdsp16  = (LPMCI_DGV_STATUS_PARMS16)MapSL(lParam); | 
 | 	    LPMCI_DGV_STATUS_PARMSA	mdsp32a = *(LPMCI_DGV_STATUS_PARMSA*)((char*)mdsp16 - sizeof(LPMCI_DGV_STATUS_PARMSA)); | 
 |  | 
 |             UnMapLS( lParam ); | 
 | 	    if (mdsp16) { | 
 | 		mdsp32a->dwReturn = mdsp16->dwReturn; | 
 | 		if (dwFlags & MCI_DGV_STATUS_DISKSPACE) { | 
 | 		    TRACE("MCI_STATUS (DGV) lpstrDrive=%08lx\n", mdsp16->lpstrDrive); | 
 | 		    TRACE("MCI_STATUS (DGV) lpstrDrive=%s\n", (LPSTR)MapSL(mdsp16->lpstrDrive)); | 
 |                     UnMapLS( mdsp16->lpstrDrive ); | 
 | 		} | 
 |                 HeapFree( GetProcessHeap(), 0, (char*)mdsp16 - sizeof(LPMCI_DGV_STATUS_PARMSA) ); | 
 | 	    } else { | 
 | 		return MCI_MAP_NOMEM; | 
 | 	    } | 
 | 	} | 
 | 	return MCI_MAP_OKMEM; | 
 | 	case MCI_DEVTYPE_VCR:		/*size = sizeof(MCI_VCR_STATUS_PARMS);	break;*/FIXME("NIY vcr\n");	return MCI_MAP_NOMEM; | 
 | 	default:			size = sizeof(MCI_STATUS_PARMS);	break; | 
 | 	} | 
 | 	break; | 
 |     case MCI_STEP: | 
 | 	break; | 
 |     case MCI_STOP: | 
 | 	break; | 
 |     case MCI_SYSINFO: | 
 | 	if (lParam) { | 
 |             LPMCI_SYSINFO_PARMS16	msip16  = (LPMCI_SYSINFO_PARMS16)MapSL(lParam); | 
 | 	    LPMCI_SYSINFO_PARMSA	msip32a = *(LPMCI_SYSINFO_PARMSA*)((char*)msip16 - sizeof(LPMCI_SYSINFO_PARMSA)); | 
 |  | 
 |             UnMapLS( lParam ); | 
 | 	    if (msip16) { | 
 |                 msip16->dwCallback = msip32a->dwCallback; | 
 |                 UnMapLS( msip16->lpstrReturn ); | 
 |                 HeapFree( GetProcessHeap(), 0, (char*)msip16 - sizeof(LPMCI_SYSINFO_PARMSA) ); | 
 | 	    } else { | 
 | 		return MCI_MAP_NOMEM; | 
 | 	    } | 
 | 	} | 
 | 	return MCI_MAP_OKMEM; | 
 | 	/* case MCI_UNDO: */ | 
 |     case MCI_UNFREEZE: | 
 | 	break; | 
 |     case MCI_UPDATE: | 
 | 	break; | 
 |     case MCI_WHERE: | 
 | 	switch (uDevType) { | 
 | 	case MCI_DEVTYPE_DIGITAL_VIDEO:	size = sizeof(MCI_DGV_RECT_PARMS16);	map = 0x0001111B;	kept = TRUE;	break; | 
 | 	case MCI_DEVTYPE_OVERLAY:	size = sizeof(MCI_OVLY_RECT_PARMS16);	map = 0x0001111B;	kept = TRUE;	break; | 
 | 	default:			break; | 
 | 	} | 
 | 	break; | 
 |     case MCI_WINDOW: | 
 | 	switch (uDevType) { | 
 | 	case MCI_DEVTYPE_DIGITAL_VIDEO:	size = sizeof(MCI_DGV_WINDOW_PARMS16);	if (dwFlags & MCI_DGV_WINDOW_TEXT)  map = 0x7666;	break; | 
 | 	case MCI_DEVTYPE_OVERLAY:	size = sizeof(MCI_OVLY_WINDOW_PARMS16);	if (dwFlags & MCI_OVLY_WINDOW_TEXT) map = 0x7666;	break; | 
 | 	default:			break; | 
 | 	} | 
 | 	/* FIXME: see map function */ | 
 | 	break; | 
 |  | 
 |     case DRV_OPEN: | 
 | 	if (lParam) { | 
 |             LPMCI_OPEN_DRIVER_PARMS16	modp16  = (LPMCI_OPEN_DRIVER_PARMS16)MapSL(lParam); | 
 | 	    LPMCI_OPEN_DRIVER_PARMSA	modp32a = *(LPMCI_OPEN_DRIVER_PARMSA*)((char*)modp16 - sizeof(LPMCI_OPEN_DRIVER_PARMSA)); | 
 |  | 
 |             UnMapLS( lParam ); | 
 | 	    modp32a->wCustomCommandTable = modp16->wCustomCommandTable; | 
 | 	    modp32a->wType = modp16->wType; | 
 |             UnMapLS( modp16->lpstrParams ); | 
 |             HeapFree( GetProcessHeap(), 0, (char *)modp16 - sizeof(LPMCI_OPEN_DRIVER_PARMSA) ); | 
 | 	} | 
 | 	return MCI_MAP_OK; | 
 |     case DRV_LOAD: | 
 |     case DRV_ENABLE: | 
 |     case DRV_CLOSE: | 
 |     case DRV_DISABLE: | 
 |     case DRV_FREE: | 
 |     case DRV_CONFIGURE: | 
 |     case DRV_QUERYCONFIGURE: | 
 |     case DRV_INSTALL: | 
 |     case DRV_REMOVE: | 
 |     case DRV_EXITSESSION: | 
 |     case DRV_EXITAPPLICATION: | 
 |     case DRV_POWER: | 
 | 	FIXME("This is a hack\n"); | 
 | 	return MCI_MAP_OK; | 
 |     default: | 
 | 	FIXME("Map/Unmap internal error on msg=%s\n", MCI_MessageToString(wMsg)); | 
 | 	return MCI_MAP_MSGERROR; | 
 |     } | 
 |     return MCI_MsgMapper32To16_Destroy((void*)lParam, size, map, kept); | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 			MCI_SendCommandFrom32			[internal] | 
 |  */ | 
 | DWORD MCI_SendCommandFrom32(UINT wDevID, UINT16 wMsg, DWORD dwParam1, DWORD dwParam2) | 
 | { | 
 |     DWORD		dwRet = MCIERR_INVALID_DEVICE_ID; | 
 |     LPWINE_MCIDRIVER	wmd = MCI_GetDriver(wDevID); | 
 |  | 
 |     if (wmd) { | 
 | 	if (wmd->bIs32) { | 
 | 	    dwRet = SendDriverMessage(wmd->hDriver, wMsg, dwParam1, dwParam2); | 
 | 	} else { | 
 | 	    MCI_MapType	res; | 
 | 		 | 
 | 	    switch (res = MCI_MapMsg32ATo16(wmd->wType, wMsg, dwParam1, &dwParam2)) { | 
 | 	    case MCI_MAP_MSGERROR: | 
 | 		TRACE("Not handled yet (%s)\n", MCI_MessageToString(wMsg)); | 
 | 		dwRet = MCIERR_DRIVER_INTERNAL; | 
 | 		break; | 
 | 	    case MCI_MAP_NOMEM: | 
 | 		TRACE("Problem mapping msg=%s from 32a to 16\n", MCI_MessageToString(wMsg)); | 
 | 		dwRet = MCIERR_OUT_OF_MEMORY; | 
 | 		break; | 
 | 	    case MCI_MAP_OK: | 
 | 	    case MCI_MAP_OKMEM: | 
 | 		dwRet = SendDriverMessage(wmd->hDriver, wMsg, dwParam1, dwParam2); | 
 | 		if (res == MCI_MAP_OKMEM) | 
 | 		    MCI_UnMapMsg32ATo16(wmd->wType, wMsg, dwParam1, dwParam2); | 
 | 		break; | 
 | 	    } | 
 | 	} | 
 |     } | 
 |     return dwRet; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 			MCI_SendCommandFrom16			[internal] | 
 |  */ | 
 | DWORD MCI_SendCommandFrom16(UINT wDevID, UINT16 wMsg, DWORD dwParam1, DWORD dwParam2) | 
 | { | 
 |     DWORD		dwRet = MCIERR_INVALID_DEVICE_ID; | 
 |     LPWINE_MCIDRIVER	wmd = MCI_GetDriver(wDevID); | 
 |  | 
 |     if (wmd) { | 
 | 	dwRet = MCIERR_INVALID_DEVICE_ID; | 
 |  | 
 | 	if (wmd->bIs32) { | 
 | 	    MCI_MapType		res; | 
 | 	     | 
 | 	    switch (res = MCI_MapMsg16To32A(wmd->wType, wMsg, &dwParam2)) { | 
 | 	    case MCI_MAP_MSGERROR: | 
 | 		TRACE("Not handled yet (%s)\n", MCI_MessageToString(wMsg)); | 
 | 		dwRet = MCIERR_DRIVER_INTERNAL; | 
 | 		break; | 
 | 	    case MCI_MAP_NOMEM: | 
 | 		TRACE("Problem mapping msg=%s from 16 to 32a\n", MCI_MessageToString(wMsg)); | 
 | 		dwRet = MCIERR_OUT_OF_MEMORY; | 
 | 		break; | 
 | 	    case MCI_MAP_OK: | 
 | 	    case MCI_MAP_OKMEM: | 
 | 		dwRet = SendDriverMessage(wmd->hDriver, wMsg, dwParam1, dwParam2); | 
 | 		if (res == MCI_MAP_OKMEM) | 
 | 		    MCI_UnMapMsg16To32A(wmd->wType, wMsg, dwParam2); | 
 | 		break; | 
 | 	    } | 
 | 	} else { | 
 | 	    dwRet = SendDriverMessage(wmd->hDriver, wMsg, dwParam1, dwParam2); | 
 | 	} | 
 |     } | 
 |     return dwRet; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 			MCI_Open				[internal] | 
 |  */ | 
 | static	DWORD MCI_Open(DWORD dwParam, LPMCI_OPEN_PARMSA lpParms) | 
 | { | 
 |     char			strDevTyp[128]; | 
 |     DWORD 			dwRet;  | 
 |     LPWINE_MCIDRIVER		wmd = NULL; | 
 |     LPWINE_MM_IDATA		iData = MULTIMEDIA_GetIData(); | 
 |  | 
 |     TRACE("(%08lX, %p)\n", dwParam, lpParms); | 
 |     if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; | 
 |  | 
 |     /* only two low bytes are generic, the other ones are dev type specific */ | 
 | #define WINE_MCIDRIVER_SUPP	(0xFFFF0000|MCI_OPEN_SHAREABLE|MCI_OPEN_ELEMENT| \ | 
 |                          MCI_OPEN_ALIAS|MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID| \ | 
 |                          MCI_NOTIFY|MCI_WAIT) | 
 |     if ((dwParam & ~WINE_MCIDRIVER_SUPP) != 0) { | 
 | 	FIXME("Unsupported yet dwFlags=%08lX\n", dwParam & ~WINE_MCIDRIVER_SUPP); | 
 |     } | 
 | #undef WINE_MCIDRIVER_SUPP | 
 |  | 
 |     strDevTyp[0] = 0; | 
 |  | 
 |     if (dwParam & MCI_OPEN_TYPE) { | 
 | 	if (dwParam & MCI_OPEN_TYPE_ID) { | 
 | 	    WORD uDevType = LOWORD((DWORD)lpParms->lpstrDeviceType); | 
 |  | 
 | 	    if (uDevType < MCI_DEVTYPE_FIRST || | 
 | 		uDevType > MCI_DEVTYPE_LAST || | 
 | 		!LoadStringA(iData->hWinMM32Instance, uDevType, strDevTyp, sizeof(strDevTyp))) { | 
 | 		dwRet = MCIERR_BAD_INTEGER; | 
 | 		goto errCleanUp; | 
 | 	    } | 
 | 	} else { | 
 | 	    LPSTR	ptr; | 
 | 	    if (lpParms->lpstrDeviceType == NULL) { | 
 | 		dwRet = MCIERR_NULL_PARAMETER_BLOCK; | 
 | 		goto errCleanUp; | 
 | 	    } | 
 | 	    strcpy(strDevTyp, lpParms->lpstrDeviceType); | 
 | 	    ptr = strchr(strDevTyp, '!'); | 
 | 	    if (ptr) { | 
 | 		/* this behavior is not documented in windows. However, since, in | 
 | 		 * some occasions, MCI_OPEN handling is translated by WinMM into | 
 | 		 * a call to mciSendString("open <type>"); this code shall be correct | 
 | 		 */ | 
 | 		if (dwParam & MCI_OPEN_ELEMENT) { | 
 | 		    ERR("Both MCI_OPEN_ELEMENT(%s) and %s are used\n", | 
 | 			lpParms->lpstrElementName, strDevTyp); | 
 | 		    dwRet = MCIERR_UNRECOGNIZED_KEYWORD; | 
 | 		    goto errCleanUp; | 
 | 		} | 
 | 		dwParam |= MCI_OPEN_ELEMENT; | 
 | 		*ptr++ = 0; | 
 | 		/* FIXME: not a good idea to write in user supplied buffer */ | 
 | 		lpParms->lpstrElementName = ptr; | 
 | 	    } | 
 | 		 | 
 | 	} | 
 | 	TRACE("devType='%s' !\n", strDevTyp); | 
 |     } | 
 |  | 
 |     if (dwParam & MCI_OPEN_ELEMENT) { | 
 | 	TRACE("lpstrElementName='%s'\n", lpParms->lpstrElementName); | 
 |  | 
 | 	if (dwParam & MCI_OPEN_ELEMENT_ID) { | 
 | 	    FIXME("Unsupported yet flag MCI_OPEN_ELEMENT_ID\n"); | 
 | 	    dwRet = MCIERR_UNRECOGNIZED_KEYWORD; | 
 | 	    goto errCleanUp; | 
 | 	} | 
 |  | 
 | 	if (!lpParms->lpstrElementName) { | 
 | 	    dwRet = MCIERR_NULL_PARAMETER_BLOCK; | 
 | 	    goto errCleanUp; | 
 | 	} | 
 |  | 
 | 	/* type, if given as a parameter, supersedes file extension */ | 
 | 	if (!strDevTyp[0] &&  | 
 | 	    MCI_GetDevTypeFromFileName(lpParms->lpstrElementName,  | 
 | 				       strDevTyp, sizeof(strDevTyp))) { | 
 | 	    if (GetDriveTypeA(lpParms->lpstrElementName) != DRIVE_CDROM) { | 
 | 		dwRet = MCIERR_EXTENSION_NOT_FOUND; | 
 | 		goto errCleanUp; | 
 | 	    } | 
 | 	    /* FIXME: this will not work if several CDROM drives are installed on the machine */ | 
 | 	    strcpy(strDevTyp, "CDAUDIO"); | 
 | 	} | 
 |     } | 
 |      | 
 |     if (strDevTyp[0] == 0) { | 
 | 	FIXME("Couldn't load driver\n"); | 
 | 	dwRet = MCIERR_INVALID_DEVICE_NAME; | 
 | 	goto errCleanUp; | 
 |     } | 
 |  | 
 |     if (dwParam & MCI_OPEN_ALIAS) { | 
 | 	TRACE("Alias='%s' !\n", lpParms->lpstrAlias); | 
 | 	if (!lpParms->lpstrAlias) { | 
 | 	    dwRet = MCIERR_NULL_PARAMETER_BLOCK; | 
 | 	    goto errCleanUp; | 
 | 	} | 
 |     } | 
 |  | 
 |     if ((dwRet = MCI_LoadMciDriver(iData, strDevTyp, &wmd))) { | 
 | 	goto errCleanUp; | 
 |     } | 
 |  | 
 |     if ((dwRet = MCI_FinishOpen(wmd, lpParms, dwParam))) { | 
 | 	TRACE("Failed to open driver (MCI_OPEN_DRIVER) [%08lx], closing\n", dwRet); | 
 | 	/* FIXME: is dwRet the correct ret code ? */ | 
 | 	goto errCleanUp; | 
 |     } | 
 |  | 
 |     /* only handled devices fall through */ | 
 |     TRACE("wDevID=%04X wDeviceID=%d dwRet=%ld\n", wmd->wDeviceID, lpParms->wDeviceID, dwRet); | 
 |  | 
 |     if (dwParam & MCI_NOTIFY) | 
 | 	mciDriverNotify16(lpParms->dwCallback, wmd->wDeviceID, MCI_NOTIFY_SUCCESSFUL); | 
 |      | 
 |     return 0; | 
 | errCleanUp: | 
 |     if (wmd) MCI_UnLoadMciDriver(iData, wmd); | 
 |  | 
 |     if (dwParam & MCI_NOTIFY) | 
 | 	mciDriverNotify16(lpParms->dwCallback, 0, MCI_NOTIFY_FAILURE); | 
 |     return dwRet; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 			MCI_Close				[internal] | 
 |  */ | 
 | static	DWORD MCI_Close(UINT16 wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms) | 
 | { | 
 |     DWORD		dwRet; | 
 |     LPWINE_MCIDRIVER	wmd; | 
 |     LPWINE_MM_IDATA	iData = MULTIMEDIA_GetIData(); | 
 |  | 
 |     TRACE("(%04x, %08lX, %p)\n", wDevID, dwParam, lpParms); | 
 |  | 
 |     if (wDevID == MCI_ALL_DEVICE_ID) { | 
 | 	LPWINE_MCIDRIVER	next; | 
 |  | 
 | 	EnterCriticalSection(&iData->cs); | 
 | 	/* FIXME: shall I notify once after all is done, or for  | 
 | 	 * each of the open drivers ? if the latest, which notif | 
 | 	 * to return when only one fails ? | 
 | 	 */ | 
 | 	for (wmd = iData->lpMciDrvs; wmd; ) { | 
 | 	    next = wmd->lpNext; | 
 | 	    MCI_Close(wmd->wDeviceID, dwParam, lpParms); | 
 | 	    wmd = next; | 
 | 	}	 | 
 | 	LeaveCriticalSection(&iData->cs); | 
 | 	return 0; | 
 |     } | 
 |  | 
 |     if (!(wmd = MCI_GetDriver(wDevID))) { | 
 | 	return MCIERR_INVALID_DEVICE_ID; | 
 |     } | 
 |  | 
 |     dwRet = MCI_SendCommandFrom32(wDevID, MCI_CLOSE_DRIVER, dwParam, (DWORD)lpParms); | 
 |  | 
 |     MCI_UnLoadMciDriver(iData, wmd); | 
 |  | 
 |     if (dwParam & MCI_NOTIFY) | 
 | 	mciDriverNotify16(lpParms->dwCallback, wDevID, | 
 | 			  (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE); | 
 |      | 
 |     return dwRet; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 			MCI_WriteString				[internal] | 
 |  */ | 
 | DWORD	MCI_WriteString(LPSTR lpDstStr, DWORD dstSize, LPCSTR lpSrcStr) | 
 | { | 
 |     DWORD	ret = 0; | 
 |  | 
 |     if (lpSrcStr) { | 
 | 	if (dstSize <= strlen(lpSrcStr)) { | 
 | 	    lstrcpynA(lpDstStr, lpSrcStr, dstSize - 1); | 
 | 	    ret = MCIERR_PARAM_OVERFLOW; | 
 | 	} else { | 
 | 	    strcpy(lpDstStr, lpSrcStr); | 
 | 	}	 | 
 |     } else { | 
 | 	*lpDstStr = 0; | 
 |     } | 
 |     return ret; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 			MCI_Sysinfo				[internal] | 
 |  */ | 
 | static	DWORD MCI_SysInfo(UINT uDevID, DWORD dwFlags, LPMCI_SYSINFO_PARMSA lpParms) | 
 | { | 
 |     DWORD		ret = MCIERR_INVALID_DEVICE_ID; | 
 |     LPWINE_MCIDRIVER	wmd; | 
 |     LPWINE_MM_IDATA	iData = MULTIMEDIA_GetIData(); | 
 |  | 
 |     if (lpParms == NULL)			return MCIERR_NULL_PARAMETER_BLOCK; | 
 |  | 
 |     TRACE("(%08x, %08lX, %08lX[num=%ld, wDevTyp=%u])\n",  | 
 | 	  uDevID, dwFlags, (DWORD)lpParms, lpParms->dwNumber, lpParms->wDeviceType); | 
 |      | 
 |     switch (dwFlags & ~MCI_SYSINFO_OPEN) { | 
 |     case MCI_SYSINFO_QUANTITY: | 
 | 	{ | 
 | 	    DWORD	cnt = 0; | 
 | 	     | 
 | 	    if (lpParms->wDeviceType < MCI_DEVTYPE_FIRST ||  | 
 | 		lpParms->wDeviceType > MCI_DEVTYPE_LAST) { | 
 | 		if (dwFlags & MCI_SYSINFO_OPEN) { | 
 | 		    TRACE("MCI_SYSINFO_QUANTITY: # of open MCI drivers\n"); | 
 | 		    EnterCriticalSection(&iData->cs); | 
 | 		    for (wmd = iData->lpMciDrvs; wmd; wmd = wmd->lpNext) { | 
 | 			cnt++; | 
 | 		    } | 
 | 		    LeaveCriticalSection(&iData->cs); | 
 | 		} else { | 
 | 		    TRACE("MCI_SYSINFO_QUANTITY: # of installed MCI drivers\n"); | 
 | 		    cnt = MCI_InstalledCount; | 
 | 		} | 
 | 	    } else { | 
 | 		if (dwFlags & MCI_SYSINFO_OPEN) { | 
 | 		    TRACE("MCI_SYSINFO_QUANTITY: # of open MCI drivers of type %u\n",  | 
 | 			  lpParms->wDeviceType); | 
 | 		    EnterCriticalSection(&iData->cs); | 
 | 		    for (wmd = iData->lpMciDrvs; wmd; wmd = wmd->lpNext) { | 
 | 			if (wmd->wType == lpParms->wDeviceType) | 
 | 			    cnt++; | 
 | 		    } | 
 | 		    LeaveCriticalSection(&iData->cs); | 
 | 		} else { | 
 | 		    TRACE("MCI_SYSINFO_QUANTITY: # of installed MCI drivers of type %u\n",  | 
 | 			  lpParms->wDeviceType); | 
 | 		    FIXME("Don't know how to get # of MCI devices of a given type\n"); | 
 | 		    cnt = 1; | 
 | 		} | 
 | 	    } | 
 | 	    *(DWORD*)lpParms->lpstrReturn = cnt; | 
 | 	} | 
 | 	TRACE("(%ld) => '%ld'\n", lpParms->dwNumber, *(DWORD*)lpParms->lpstrReturn); | 
 | 	ret = MCI_INTEGER_RETURNED; | 
 | 	break; | 
 |     case MCI_SYSINFO_INSTALLNAME: | 
 | 	TRACE("MCI_SYSINFO_INSTALLNAME \n"); | 
 | 	if ((wmd = MCI_GetDriver(uDevID))) { | 
 | 	    ret = MCI_WriteString(lpParms->lpstrReturn, lpParms->dwRetSize,  | 
 | 				  wmd->lpstrDeviceType); | 
 | 	} else { | 
 | 	    *lpParms->lpstrReturn = 0; | 
 | 	    ret = MCIERR_INVALID_DEVICE_ID; | 
 | 	} | 
 | 	TRACE("(%ld) => '%s'\n", lpParms->dwNumber, lpParms->lpstrReturn); | 
 | 	break; | 
 |     case MCI_SYSINFO_NAME: | 
 | 	TRACE("MCI_SYSINFO_NAME\n"); | 
 | 	if (dwFlags & MCI_SYSINFO_OPEN) { | 
 | 	    FIXME("Don't handle MCI_SYSINFO_NAME|MCI_SYSINFO_OPEN (yet)\n"); | 
 | 	    ret = MCIERR_UNRECOGNIZED_COMMAND; | 
 | 	} else if (lpParms->dwNumber > MCI_InstalledCount) { | 
 | 	    ret = MCIERR_OUTOFRANGE; | 
 | 	} else { | 
 | 	    DWORD	count = lpParms->dwNumber; | 
 | 	    LPSTR	ptr = MCI_lpInstallNames; | 
 |  | 
 | 	    while (--count > 0) ptr += strlen(ptr) + 1; | 
 | 	    ret = MCI_WriteString(lpParms->lpstrReturn, lpParms->dwRetSize, ptr); | 
 | 	} | 
 | 	TRACE("(%ld) => '%s'\n", lpParms->dwNumber, lpParms->lpstrReturn); | 
 | 	break; | 
 |     default: | 
 | 	TRACE("Unsupported flag value=%08lx\n", dwFlags); | 
 | 	ret = MCIERR_UNRECOGNIZED_COMMAND; | 
 |     } | 
 |     return ret; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 			MCI_Break				[internal] | 
 |  */ | 
 | static	DWORD MCI_Break(UINT wDevID, DWORD dwFlags, LPMCI_BREAK_PARMS lpParms) | 
 | { | 
 |     DWORD	dwRet = 0; | 
 |      | 
 |     if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK; | 
 |  | 
 |     if (dwFlags & MCI_NOTIFY) | 
 | 	mciDriverNotify16(lpParms->dwCallback, wDevID, | 
 | 			  (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE); | 
 |  | 
 |     return dwRet; | 
 | } | 
 |      | 
 | /************************************************************************** | 
 |  * 			MCI_SendCommand				[internal] | 
 |  */ | 
 | DWORD	MCI_SendCommand(UINT wDevID, UINT16 wMsg, DWORD dwParam1,  | 
 | 			DWORD dwParam2, BOOL bFrom32) | 
 | { | 
 |     DWORD		dwRet = MCIERR_UNRECOGNIZED_COMMAND; | 
 |  | 
 |     switch (wMsg) { | 
 |     case MCI_OPEN: | 
 | 	if (bFrom32) { | 
 | 	    dwRet = MCI_Open(dwParam1, (LPMCI_OPEN_PARMSA)dwParam2); | 
 | 	} else { | 
 | 	    switch (MCI_MapMsg16To32A(0, wMsg, &dwParam2)) { | 
 | 	    case MCI_MAP_OK: | 
 | 	    case MCI_MAP_OKMEM: | 
 | 		dwRet = MCI_Open(dwParam1, (LPMCI_OPEN_PARMSA)dwParam2); | 
 | 		MCI_UnMapMsg16To32A(0, wMsg, dwParam2); | 
 | 		break; | 
 | 	    default: break; /* so that gcc does not bark */ | 
 | 	    } | 
 | 	} | 
 | 	break; | 
 |     case MCI_CLOSE: | 
 | 	if (bFrom32) { | 
 | 	    dwRet = MCI_Close(wDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2); | 
 | 	} else { | 
 | 	    switch (MCI_MapMsg16To32A(0, wMsg, &dwParam2)) { | 
 | 	    case MCI_MAP_OK: | 
 | 	    case MCI_MAP_OKMEM: | 
 | 		dwRet = MCI_Close(wDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2); | 
 | 		MCI_UnMapMsg16To32A(0, wMsg, dwParam2); | 
 | 		break; | 
 | 	    default: break; /* so that gcc does not bark */ | 
 | 	    } | 
 | 	} | 
 | 	break; | 
 |     case MCI_SYSINFO: | 
 | 	if (bFrom32) { | 
 | 	    dwRet = MCI_SysInfo(wDevID, dwParam1, (LPMCI_SYSINFO_PARMSA)dwParam2); | 
 | 	} else { | 
 | 	    switch (MCI_MapMsg16To32A(0, wMsg, &dwParam2)) { | 
 | 	    case MCI_MAP_OK: | 
 | 	    case MCI_MAP_OKMEM: | 
 | 		dwRet = MCI_SysInfo(wDevID, dwParam1, (LPMCI_SYSINFO_PARMSA)dwParam2); | 
 | 		MCI_UnMapMsg16To32A(0, wMsg, dwParam2); | 
 | 		break; | 
 | 	    default: break; /* so that gcc doesnot  bark */ | 
 | 	    } | 
 | 	} | 
 | 	break; | 
 |     case MCI_BREAK: | 
 | 	if (bFrom32) { | 
 | 	    dwRet = MCI_Break(wDevID, dwParam1, (LPMCI_BREAK_PARMS)dwParam2); | 
 | 	} else { | 
 | 	    switch (MCI_MapMsg16To32A(0, wMsg, &dwParam2)) { | 
 | 	    case MCI_MAP_OK: | 
 | 	    case MCI_MAP_OKMEM: | 
 | 		dwRet = MCI_Break(wDevID, dwParam1, (LPMCI_BREAK_PARMS)dwParam2); | 
 | 		MCI_UnMapMsg16To32A(0, wMsg, dwParam2); | 
 | 		break; | 
 | 	    default: break; /* so that gcc does not bark */ | 
 | 	    } | 
 | 	} | 
 | 	break; | 
 |     case MCI_SOUND: | 
 | 	/* FIXME: it seems that MCI_SOUND needs the same handling as MCI_BREAK | 
 | 	 * but I couldn't get any doc on this MCI message | 
 | 	 */ | 
 | 	break; | 
 |     default: | 
 | 	if (wDevID == MCI_ALL_DEVICE_ID) { | 
 | 	    FIXME("unhandled MCI_ALL_DEVICE_ID\n"); | 
 | 	    dwRet = MCIERR_CANNOT_USE_ALL; | 
 | 	} else { | 
 | 	    dwRet = (bFrom32) ? | 
 | 		MCI_SendCommandFrom32(wDevID, wMsg, dwParam1, dwParam2) : | 
 | 		MCI_SendCommandFrom16(wDevID, wMsg, dwParam1, dwParam2); | 
 | 	}	     | 
 | 	break; | 
 |     } | 
 |     return dwRet; | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 				MCI_CleanUp			[internal] | 
 |  * | 
 |  * Some MCI commands need to be cleaned-up (when not called from  | 
 |  * mciSendString), because MCI drivers return extra information for string | 
 |  * transformation. This function gets rid of them. | 
 |  */ | 
 | LRESULT		MCI_CleanUp(LRESULT dwRet, UINT wMsg, DWORD dwParam2, BOOL bIs32) | 
 | { | 
 |     if (LOWORD(dwRet))  | 
 | 	return LOWORD(dwRet); | 
 |  | 
 |     switch (wMsg) { | 
 |     case MCI_GETDEVCAPS: | 
 | 	switch (dwRet & 0xFFFF0000ul) { | 
 | 	case 0: | 
 | 	case MCI_COLONIZED3_RETURN: | 
 | 	case MCI_COLONIZED4_RETURN: | 
 | 	case MCI_INTEGER_RETURNED: | 
 | 	    /* nothing to do */ | 
 | 	    break; | 
 | 	case MCI_RESOURCE_RETURNED: | 
 | 	case MCI_RESOURCE_RETURNED|MCI_RESOURCE_DRIVER: | 
 | 	    { | 
 | 		LPMCI_GETDEVCAPS_PARMS	lmgp; | 
 |  | 
 | 		lmgp = (LPMCI_GETDEVCAPS_PARMS)(bIs32 ? (void*)dwParam2 : MapSL(dwParam2)); | 
 | 		TRACE("Changing %08lx to %08lx\n", lmgp->dwReturn, (DWORD)LOWORD(lmgp->dwReturn)); | 
 | 		lmgp->dwReturn = LOWORD(lmgp->dwReturn); | 
 | 	    }  | 
 | 	    break; | 
 | 	default: | 
 | 	    FIXME("Unsupported value for hiword (%04x) returned by DriverProc(%s)\n",  | 
 | 		  HIWORD(dwRet), MCI_MessageToString(wMsg)); | 
 | 	} | 
 | 	break; | 
 |     case MCI_STATUS: | 
 | 	switch (dwRet & 0xFFFF0000ul) { | 
 | 	case 0: | 
 | 	case MCI_COLONIZED3_RETURN: | 
 | 	case MCI_COLONIZED4_RETURN: | 
 | 	case MCI_INTEGER_RETURNED: | 
 | 	    /* nothing to do */ | 
 | 	    break; | 
 | 	case MCI_RESOURCE_RETURNED: | 
 | 	case MCI_RESOURCE_RETURNED|MCI_RESOURCE_DRIVER: | 
 | 	    { | 
 | 		LPMCI_STATUS_PARMS	lsp; | 
 |  | 
 | 		lsp = (LPMCI_STATUS_PARMS)(bIs32 ? (void*)dwParam2 : MapSL(dwParam2)); | 
 | 		TRACE("Changing %08lx to %08lx\n", lsp->dwReturn, (DWORD)LOWORD(lsp->dwReturn)); | 
 | 		lsp->dwReturn = LOWORD(lsp->dwReturn); | 
 | 	    } | 
 | 	    break; | 
 | 	default: | 
 | 	    FIXME("Unsupported value for hiword (%04x) returned by DriverProc(%s)\n",  | 
 | 		  HIWORD(dwRet), MCI_MessageToString(wMsg)); | 
 | 	} | 
 | 	break; | 
 |     case MCI_SYSINFO: | 
 | 	switch (dwRet & 0xFFFF0000ul) { | 
 | 	case 0: | 
 | 	case MCI_INTEGER_RETURNED: | 
 | 	    /* nothing to do */ | 
 | 	    break; | 
 | 	default: | 
 | 	    FIXME("Unsupported value for hiword (%04x)\n", HIWORD(dwRet));	 | 
 | 	} | 
 | 	break; | 
 |     default: | 
 | 	if (HIWORD(dwRet)) { | 
 | 	    FIXME("Got non null hiword for dwRet=0x%08lx for command %s\n",  | 
 | 		  dwRet, MCI_MessageToString(wMsg)); | 
 | 	} | 
 | 	break; | 
 |     } | 
 |     return LOWORD(dwRet); | 
 | } | 
 |  | 
 | /************************************************************************** | 
 |  * 			MULTIMEDIA_MciInit			[internal] | 
 |  * | 
 |  * Initializes the MCI internal variables. | 
 |  * | 
 |  */ | 
 | BOOL MULTIMEDIA_MciInit(void) | 
 | { | 
 |     LPSTR	ptr1, ptr2; | 
 |     HKEY	hWineConf; | 
 |     HKEY	hkey; | 
 |     DWORD	err; | 
 |     DWORD 	type; | 
 |     DWORD 	count = 2048; | 
 |  | 
 |     MCI_InstalledCount = 0; | 
 |     ptr1 = MCI_lpInstallNames = HeapAlloc(GetProcessHeap(), 0, count); | 
 |  | 
 |     if (!MCI_lpInstallNames) | 
 | 	return FALSE; | 
 |  | 
 |     /* FIXME: should do also some registry diving here ? */ | 
 |     if (!(err = RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config", &hWineConf)) && | 
 | 	!(err = RegOpenKeyA(hWineConf, "options", &hkey))) { | 
 | 	err = RegQueryValueExA(hkey, "mci", 0, &type, MCI_lpInstallNames, &count); | 
 | 	RegCloseKey(hkey); | 
 | 	 | 
 |     } | 
 |     if (!err) { | 
 | 	TRACE("Wine => '%s' \n", ptr1); | 
 | 	while ((ptr2 = strchr(ptr1, ':')) != 0) { | 
 | 	    *ptr2++ = 0; | 
 | 	    TRACE("---> '%s' \n", ptr1); | 
 | 	    MCI_InstalledCount++; | 
 | 	    ptr1 = ptr2; | 
 | 	} | 
 | 	MCI_InstalledCount++; | 
 | 	TRACE("---> '%s' \n", ptr1); | 
 | 	ptr1 += strlen(ptr1) + 1; | 
 |     } else { | 
 | 	GetPrivateProfileStringA("mci", NULL, "", MCI_lpInstallNames, count, "SYSTEM.INI"); | 
 | 	while (strlen(ptr1) > 0) { | 
 | 	    TRACE("---> '%s' \n", ptr1); | 
 | 	    ptr1 += strlen(ptr1) + 1; | 
 | 	    MCI_InstalledCount++; | 
 | 	} | 
 |     } | 
 |     RegCloseKey(hWineConf); | 
 |     return TRUE; | 
 | } | 
 |  |