mci.c 74.6 KB
Newer Older
1 2 3 4
/*
 * MCI internal functions
 *
 * Copyright 1998/1999 Eric Pouech
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * 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
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 20
 */

21 22 23 24 25 26 27 28 29 30 31 32 33
/* TODO:
 * - implement WINMM (32bit) multitasking and use it in all MCI drivers
 *   instead of the home grown one 
 * - 16bit mmTaskXXX functions are currently broken because the 16
 *   loader does not support binary command lines => provide Wine's
 *   own mmtask.tsk not using binary command line.
 * - correctly handle the MCI_ALL_DEVICE_ID in functions.
 * - finish mapping 16 <=> 32 of MCI structures and commands
 * - implement auto-open feature (ie, when a string command is issued
 *   for a not yet opened device, MCI automatically opens it) 
 * - use a default registry setting to replace the [mci] section in
 *   configuration file (layout of info in registry should be compatible
 *   with all Windows' version - which use different layouts of course)
34 35 36 37 38 39
 * - implement automatic open
 *      + only works on string interface, on regular devices (don't work on all
 *        nor custom devices)
 * - command table handling isn't thread safe
 */

40
#include <stdlib.h>
41
#include <stdarg.h>
42
#include <stdio.h>
43 44
#include <string.h>

45
#include "windef.h"
46
#include "winbase.h"
47
#include "wingdi.h"
48
#include "mmsystem.h"
49
#include "winuser.h"
50
#include "winnls.h"
51
#include "winreg.h"
52
#include "wownt32.h"
53 54

#include "digitalv.h"
55
#include "winemm.h"
56

57
#include "wine/debug.h"
58

59
WINE_DEFAULT_DEBUG_CHANNEL(mci);
60

61 62
/* First MCI valid device ID (0 means error) */
#define MCI_MAGIC 0x0001
63

64
/* MCI settings */
65
static const WCHAR wszHklmMci  [] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\MCI";
66

67 68
static WINE_MCIDRIVER *MciDrivers;

69
static UINT WINAPI MCI_DefYieldProc(MCIDEVICEID wDevID, DWORD data);
70
static UINT MCI_SetCommandTable(HGLOBAL hMem, UINT uDevType);
71

72
/* dup a string and uppercase it */
73
static inline LPWSTR str_dup_upper( LPCWSTR str )
74
{
75
    INT len = (lstrlenW(str) + 1) * sizeof(WCHAR);
76
    LPWSTR p = HeapAlloc( GetProcessHeap(), 0, len );
77 78 79
    if (p)
    {
        memcpy( p, str, len );
80
        CharUpperW( p );
81 82 83 84
    }
    return p;
}

85 86 87
/**************************************************************************
 * 				MCI_GetDriver			[internal]
 */
88
static LPWINE_MCIDRIVER	MCI_GetDriver(UINT wDevID)
89 90 91
{
    LPWINE_MCIDRIVER	wmd = 0;

92 93
    EnterCriticalSection(&WINMM_cs);
    for (wmd = MciDrivers; wmd; wmd = wmd->lpNext) {
94 95
	if (wmd->wDeviceID == wDevID)
	    break;
96
    }
97
    LeaveCriticalSection(&WINMM_cs);
98 99 100 101 102 103
    return wmd;
}

/**************************************************************************
 * 				MCI_GetDriverFromString		[internal]
 */
104
static UINT MCI_GetDriverFromString(LPCWSTR lpstrName)
105 106 107 108 109 110
{
    LPWINE_MCIDRIVER	wmd;
    UINT		ret = 0;

    if (!lpstrName)
	return 0;
111

112
    if (!wcsicmp(lpstrName, L"ALL"))
113
	return MCI_ALL_DEVICE_ID;
114

115 116
    EnterCriticalSection(&WINMM_cs);
    for (wmd = MciDrivers; wmd; wmd = wmd->lpNext) {
117
	if (wmd->lpstrAlias && wcsicmp(wmd->lpstrAlias, lpstrName) == 0) {
118 119 120
	    ret = wmd->wDeviceID;
	    break;
	}
121
    }
122
    LeaveCriticalSection(&WINMM_cs);
123

124 125 126 127 128 129
    return ret;
}

/**************************************************************************
 * 			MCI_MessageToString			[internal]
 */
130
static const char* MCI_MessageToString(UINT wMsg)
131 132
{
#define CASE(s) case (s): return #s
133

134
    switch (wMsg) {
135 136 137 138 139 140 141 142 143 144 145 146 147
        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);
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
	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);
Jörg Höhle's avatar
Jörg Höhle committed
172
	CASE(MCI_SOUND);
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
	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:
196
        return wine_dbg_sprintf("MCI_<<%04X>>", wMsg);
197 198 199
    }
}

200
static LPWSTR MCI_strdupAtoW( LPCSTR str )
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
{
    LPWSTR ret;
    INT len;

    if (!str) return NULL;
    len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
    ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
    if (ret) MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len );
    return ret;
}

static int MCI_MapMsgAtoW(UINT msg, DWORD_PTR dwParam1, DWORD_PTR *dwParam2)
{
    if (msg < DRV_RESERVED) return 0;

    switch (msg)
    {
    case MCI_CLOSE:
    case MCI_CONFIGURE:
    case MCI_PLAY:
    case MCI_SEEK:
    case MCI_STOP:
    case MCI_PAUSE:
    case MCI_GETDEVCAPS:
    case MCI_SPIN:
    case MCI_SET:
    case MCI_STEP:
    case MCI_RECORD:
    case MCI_BREAK:
    case MCI_STATUS:
    case MCI_CUE:
    case MCI_REALIZE:
    case MCI_PUT:
    case MCI_WHERE:
    case MCI_FREEZE:
    case MCI_UNFREEZE:
    case MCI_CUT:
    case MCI_COPY:
    case MCI_PASTE:
    case MCI_UPDATE:
    case MCI_RESUME:
    case MCI_DELETE:
243 244
    case MCI_MONITOR:
    case MCI_SIGNAL:
245
    case MCI_UNDO:
246 247 248
        return 0;

    case MCI_OPEN:
249 250 251 252
        {   /* MCI_ANIM_OPEN_PARMS is the largest known MCI_OPEN_PARMS
             * structure, larger than MCI_WAVE_OPEN_PARMS */
            MCI_ANIM_OPEN_PARMSA *mci_openA = (MCI_ANIM_OPEN_PARMSA*)*dwParam2;
            MCI_ANIM_OPEN_PARMSW *mci_openW;
253 254
            DWORD_PTR *ptr;

255
            ptr = HeapAlloc(GetProcessHeap(), 0, sizeof(DWORD_PTR) + sizeof(*mci_openW));
256 257 258 259
            if (!ptr) return -1;

            *ptr++ = *dwParam2; /* save the previous pointer */
            *dwParam2 = (DWORD_PTR)ptr;
260
            mci_openW = (MCI_ANIM_OPEN_PARMSW *)ptr;
261 262 263 264 265 266 267

            if (dwParam1 & MCI_NOTIFY)
                mci_openW->dwCallback = mci_openA->dwCallback;

            if (dwParam1 & MCI_OPEN_TYPE)
            {
                if (dwParam1 & MCI_OPEN_TYPE_ID)
268
                    mci_openW->lpstrDeviceType = (LPCWSTR)mci_openA->lpstrDeviceType;
269 270 271 272 273 274
                else
                    mci_openW->lpstrDeviceType = MCI_strdupAtoW(mci_openA->lpstrDeviceType);
            }
            if (dwParam1 & MCI_OPEN_ELEMENT)
            {
                if (dwParam1 & MCI_OPEN_ELEMENT_ID)
275
                    mci_openW->lpstrElementName = (LPCWSTR)mci_openA->lpstrElementName;
276 277 278 279 280
                else
                    mci_openW->lpstrElementName = MCI_strdupAtoW(mci_openA->lpstrElementName);
            }
            if (dwParam1 & MCI_OPEN_ALIAS)
                mci_openW->lpstrAlias = MCI_strdupAtoW(mci_openA->lpstrAlias);
281 282 283 284
            /* We don't know how many DWORD follow, as
             * the structure depends on the device. */
            if (HIWORD(dwParam1))
                memcpy(&mci_openW->dwStyle, &mci_openA->dwStyle, sizeof(MCI_ANIM_OPEN_PARMSW) - sizeof(MCI_OPEN_PARMSW));
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
        }
        return 1;

    case MCI_WINDOW:
        if (dwParam1 & MCI_ANIM_WINDOW_TEXT)
        {
            MCI_ANIM_WINDOW_PARMSA *mci_windowA = (MCI_ANIM_WINDOW_PARMSA *)*dwParam2;
            MCI_ANIM_WINDOW_PARMSW *mci_windowW;

            mci_windowW = HeapAlloc(GetProcessHeap(), 0, sizeof(*mci_windowW));
            if (!mci_windowW) return -1;

            *dwParam2 = (DWORD_PTR)mci_windowW;

            mci_windowW->lpstrText = MCI_strdupAtoW(mci_windowA->lpstrText);

            if (dwParam1 & MCI_NOTIFY)
                mci_windowW->dwCallback = mci_windowA->dwCallback;
            if (dwParam1 & MCI_ANIM_WINDOW_HWND)
                mci_windowW->hWnd = mci_windowA->hWnd;
            if (dwParam1 & MCI_ANIM_WINDOW_STATE)
                mci_windowW->nCmdShow = mci_windowA->nCmdShow;

            return 1;
        }
        return 0;

    case MCI_SYSINFO:
313
        if (dwParam1 & (MCI_SYSINFO_INSTALLNAME | MCI_SYSINFO_NAME))
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
        {
            MCI_SYSINFO_PARMSA *mci_sysinfoA = (MCI_SYSINFO_PARMSA *)*dwParam2;
            MCI_SYSINFO_PARMSW *mci_sysinfoW;
            DWORD_PTR *ptr;

            ptr = HeapAlloc(GetProcessHeap(), 0, sizeof(*mci_sysinfoW) + sizeof(DWORD_PTR));
            if (!ptr) return -1;

            *ptr++ = *dwParam2; /* save the previous pointer */
            *dwParam2 = (DWORD_PTR)ptr;
            mci_sysinfoW = (MCI_SYSINFO_PARMSW *)ptr;

            if (dwParam1 & MCI_NOTIFY)
                mci_sysinfoW->dwCallback = mci_sysinfoA->dwCallback;

329
            /* Size is measured in numbers of characters, despite what MSDN says. */
330
            mci_sysinfoW->dwRetSize = mci_sysinfoA->dwRetSize;
331
            mci_sysinfoW->lpstrReturn = HeapAlloc(GetProcessHeap(), 0, mci_sysinfoW->dwRetSize * sizeof(WCHAR));
332 333 334 335
            mci_sysinfoW->dwNumber = mci_sysinfoA->dwNumber;
            mci_sysinfoW->wDeviceType = mci_sysinfoA->wDeviceType;
            return 1;
        }
336
        return 0;
337 338
    case MCI_INFO:
        {
339 340
            MCI_DGV_INFO_PARMSA *mci_infoA = (MCI_DGV_INFO_PARMSA *)*dwParam2;
            MCI_DGV_INFO_PARMSW *mci_infoW;
341 342 343 344 345 346 347
            DWORD_PTR *ptr;

            ptr = HeapAlloc(GetProcessHeap(), 0, sizeof(*mci_infoW) + sizeof(DWORD_PTR));
            if (!ptr) return -1;

            *ptr++ = *dwParam2; /* save the previous pointer */
            *dwParam2 = (DWORD_PTR)ptr;
348
            mci_infoW = (MCI_DGV_INFO_PARMSW *)ptr;
349 350 351 352

            if (dwParam1 & MCI_NOTIFY)
                mci_infoW->dwCallback = mci_infoA->dwCallback;

353
            /* Size is measured in numbers of characters. */
354
            mci_infoW->dwRetSize = mci_infoA->dwRetSize;
355
            mci_infoW->lpstrReturn = HeapAlloc(GetProcessHeap(), 0, mci_infoW->dwRetSize * sizeof(WCHAR));
356 357
            if (dwParam1 & MCI_DGV_INFO_ITEM)
                mci_infoW->dwItem = mci_infoA->dwItem;
358 359 360 361
            return 1;
        }
    case MCI_SAVE:
    case MCI_LOAD:
362 363 364 365 366
    case MCI_CAPTURE:
    case MCI_RESTORE:
        {   /* All these commands have the same layout: callback + string + optional rect */
            MCI_OVLY_LOAD_PARMSA *mci_loadA = (MCI_OVLY_LOAD_PARMSA *)*dwParam2;
            MCI_OVLY_LOAD_PARMSW *mci_loadW;
367 368 369 370 371 372 373 374

            mci_loadW = HeapAlloc(GetProcessHeap(), 0, sizeof(*mci_loadW));
            if (!mci_loadW) return -1;

            *dwParam2 = (DWORD_PTR)mci_loadW;
            if (dwParam1 & MCI_NOTIFY)
                mci_loadW->dwCallback = mci_loadA->dwCallback;
            mci_loadW->lpfilename = MCI_strdupAtoW(mci_loadA->lpfilename);
375 376 377 378 379
            if ((MCI_SAVE    == msg && dwParam1 & MCI_DGV_RECT) ||
                (MCI_LOAD    == msg && dwParam1 & MCI_OVLY_RECT) ||
                (MCI_CAPTURE == msg && dwParam1 & MCI_DGV_CAPTURE_AT) ||
                (MCI_RESTORE == msg && dwParam1 & MCI_DGV_RESTORE_AT))
                mci_loadW->rc = mci_loadA->rc;
380 381
            return 1;
        }
382
    case MCI_SOUND:
383
    case MCI_ESCAPE:
384
        {   /* All these commands have the same layout: callback + string */
385 386 387 388 389 390 391 392 393 394 395 396
            MCI_VD_ESCAPE_PARMSA *mci_vd_escapeA = (MCI_VD_ESCAPE_PARMSA *)*dwParam2;
            MCI_VD_ESCAPE_PARMSW *mci_vd_escapeW;

            mci_vd_escapeW = HeapAlloc(GetProcessHeap(), 0, sizeof(*mci_vd_escapeW));
            if (!mci_vd_escapeW) return -1;

            *dwParam2 = (DWORD_PTR)mci_vd_escapeW;
            if (dwParam1 & MCI_NOTIFY)
                mci_vd_escapeW->dwCallback = mci_vd_escapeA->dwCallback;
            mci_vd_escapeW->lpstrCommand = MCI_strdupAtoW(mci_vd_escapeA->lpstrCommand);
            return 1;
        }
397 398 399 400 401 402 403 404 405
    case MCI_SETAUDIO:
    case MCI_SETVIDEO:
        if (!(dwParam1 & (MCI_DGV_SETVIDEO_QUALITY | MCI_DGV_SETVIDEO_ALG
                        | MCI_DGV_SETAUDIO_QUALITY | MCI_DGV_SETAUDIO_ALG)))
            return 0;
        /* fall through to default */
    case MCI_RESERVE:
    case MCI_QUALITY:
    case MCI_LIST:
406 407
    default:
        FIXME("Message %s needs translation\n", MCI_MessageToString(msg));
408
        return 0; /* pass through untouched */
409 410 411
    }
}

412
static void MCI_UnmapMsgAtoW(UINT msg, DWORD_PTR dwParam1, DWORD_PTR dwParam2,
413 414 415 416 417 418 419 420
                              DWORD result)
{
    switch (msg)
    {
    case MCI_OPEN:
        {
            DWORD_PTR *ptr = (DWORD_PTR *)dwParam2 - 1;
            MCI_OPEN_PARMSA *mci_openA = (MCI_OPEN_PARMSA *)*ptr;
421
            MCI_OPEN_PARMSW *mci_openW = (MCI_OPEN_PARMSW *)dwParam2;
422 423 424 425 426 427

            mci_openA->wDeviceID = mci_openW->wDeviceID;

            if (dwParam1 & MCI_OPEN_TYPE)
            {
                if (!(dwParam1 & MCI_OPEN_TYPE_ID))
428
                    HeapFree(GetProcessHeap(), 0, (LPWSTR)mci_openW->lpstrDeviceType);
429 430 431 432
            }
            if (dwParam1 & MCI_OPEN_ELEMENT)
            {
                if (!(dwParam1 & MCI_OPEN_ELEMENT_ID))
433
                    HeapFree(GetProcessHeap(), 0, (LPWSTR)mci_openW->lpstrElementName);
434 435
            }
            if (dwParam1 & MCI_OPEN_ALIAS)
436
                HeapFree(GetProcessHeap(), 0, (LPWSTR)mci_openW->lpstrAlias);
437 438 439 440 441 442 443 444 445 446 447 448 449 450
            HeapFree(GetProcessHeap(), 0, ptr);
        }
        break;
    case MCI_WINDOW:
        if (dwParam1 & MCI_ANIM_WINDOW_TEXT)
        {
            MCI_ANIM_WINDOW_PARMSW *mci_windowW = (MCI_ANIM_WINDOW_PARMSW *)dwParam2;

            HeapFree(GetProcessHeap(), 0, (void*)mci_windowW->lpstrText);
            HeapFree(GetProcessHeap(), 0, mci_windowW);
        }
        break;

    case MCI_SYSINFO:
451
        if (dwParam1 & (MCI_SYSINFO_INSTALLNAME | MCI_SYSINFO_NAME))
452 453 454
        {
            DWORD_PTR *ptr = (DWORD_PTR *)dwParam2 - 1;
            MCI_SYSINFO_PARMSA *mci_sysinfoA = (MCI_SYSINFO_PARMSA *)*ptr;
455
            MCI_SYSINFO_PARMSW *mci_sysinfoW = (MCI_SYSINFO_PARMSW *)dwParam2;
456 457 458

            if (!result)
            {
459
                WideCharToMultiByte(CP_ACP, 0,
460
                                    mci_sysinfoW->lpstrReturn, -1,
461 462
                                    mci_sysinfoA->lpstrReturn, mci_sysinfoA->dwRetSize,
                                    NULL, NULL);
463 464 465 466 467 468 469 470 471 472
            }

            HeapFree(GetProcessHeap(), 0, mci_sysinfoW->lpstrReturn);
            HeapFree(GetProcessHeap(), 0, ptr);
        }
        break;
    case MCI_INFO:
        {
            DWORD_PTR *ptr = (DWORD_PTR *)dwParam2 - 1;
            MCI_INFO_PARMSA *mci_infoA = (MCI_INFO_PARMSA *)*ptr;
473
            MCI_INFO_PARMSW *mci_infoW = (MCI_INFO_PARMSW *)dwParam2;
474 475 476 477

            if (!result)
            {
                WideCharToMultiByte(CP_ACP, 0,
478
                                    mci_infoW->lpstrReturn, -1,
479 480 481 482 483 484 485 486 487 488
                                    mci_infoA->lpstrReturn, mci_infoA->dwRetSize,
                                    NULL, NULL);
            }

            HeapFree(GetProcessHeap(), 0, mci_infoW->lpstrReturn);
            HeapFree(GetProcessHeap(), 0, ptr);
        }
        break;
    case MCI_SAVE:
    case MCI_LOAD:
489 490 491 492
    case MCI_CAPTURE:
    case MCI_RESTORE:
        {   /* All these commands have the same layout: callback + string + optional rect */
            MCI_OVLY_LOAD_PARMSW *mci_loadW = (MCI_OVLY_LOAD_PARMSW *)dwParam2;
493 494 495 496 497

            HeapFree(GetProcessHeap(), 0, (void*)mci_loadW->lpfilename);
            HeapFree(GetProcessHeap(), 0, mci_loadW);
        }
        break;
498
    case MCI_SOUND:
499
    case MCI_ESCAPE:
500
        {   /* All these commands have the same layout: callback + string */
501 502 503 504 505 506 507 508 509 510 511 512 513
            MCI_VD_ESCAPE_PARMSW *mci_vd_escapeW = (MCI_VD_ESCAPE_PARMSW *)dwParam2;

            HeapFree(GetProcessHeap(), 0, (void*)mci_vd_escapeW->lpstrCommand);
            HeapFree(GetProcessHeap(), 0, mci_vd_escapeW);
        }
        break;

    default:
        FIXME("Message %s needs unmapping\n", MCI_MessageToString(msg));
        break;
    }
}

514 515 516
/**************************************************************************
 * 				MCI_GetDevTypeFromFileName	[internal]
 */
517
static	DWORD	MCI_GetDevTypeFromFileName(LPCWSTR fileName, LPWSTR buf, UINT len)
518
{
519
    LPCWSTR	tmp;
520
    HKEY	hKey;
521
    if ((tmp = wcsrchr(fileName, '.'))) {
522 523
	if (RegOpenKeyExW( HKEY_LOCAL_MACHINE,
			   L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\MCI Extensions",
524 525
			   0, KEY_QUERY_VALUE, &hKey ) == ERROR_SUCCESS) {
	    DWORD dwLen = len;
526
	    LONG lRet = RegQueryValueExW( hKey, tmp + 1, 0, 0, (void*)buf, &dwLen ); 
527 528 529
	    RegCloseKey( hKey );
	    if (lRet == ERROR_SUCCESS) return 0;
        }
530
	TRACE("No ...\\MCI Extensions entry for %s found.\n", debugstr_w(tmp));
531 532 533 534
    }
    return MCIERR_EXTENSION_NOT_FOUND;
}

535 536 537 538 539 540 541 542
/**************************************************************************
 * 				MCI_GetDevTypeFromResource	[internal]
 */
static	UINT	MCI_GetDevTypeFromResource(LPCWSTR lpstrName)
{
    WCHAR	buf[32];
    UINT	uDevType;
    for (uDevType = MCI_DEVTYPE_FIRST; uDevType <= MCI_DEVTYPE_LAST; uDevType++) {
543
	if (LoadStringW(hWinMM32Instance, uDevType, buf, ARRAY_SIZE(buf))) {
544
	    /* FIXME: ignore digits suffix */
545
	    if (!wcsicmp(buf, lpstrName))
546 547 548 549 550 551
		return uDevType;
	}
    }
    return 0;
}

552 553 554 555
#define	MAX_MCICMDTABLE			20
#define MCI_COMMAND_TABLE_NOT_LOADED	0xFFFE

typedef struct tagWINE_MCICMDTABLE {
556
    UINT		uDevType;
557
    HGLOBAL             hMem;
558
    const BYTE*		lpTable;
559
    UINT		nVerbs;		/* number of verbs in command table */
560
    LPCWSTR*		aVerbs;		/* array of verbs to speed up the verb look up process */
561
} WINE_MCICMDTABLE, *LPWINE_MCICMDTABLE;
562 563

static WINE_MCICMDTABLE S_MciCmdTable[MAX_MCICMDTABLE];
564 565 566 567 568 569

/**************************************************************************
 * 				MCI_IsCommandTableValid		[internal]
 */
static	BOOL		MCI_IsCommandTableValid(UINT uTbl)
{
570 571
    const BYTE* lmem;
    LPCWSTR     str;
572 573 574 575 576
    DWORD	flg;
    WORD	eid;
    int		idx = 0;
    BOOL	inCst = FALSE;

577 578
    TRACE("Dumping cmdTbl=%d [lpTable=%p devType=%d]\n",
	  uTbl, S_MciCmdTable[uTbl].lpTable, S_MciCmdTable[uTbl].uDevType);
579

580
    if (uTbl >= MAX_MCICMDTABLE || !S_MciCmdTable[uTbl].lpTable)
581 582 583 584
	return FALSE;

    lmem = S_MciCmdTable[uTbl].lpTable;
    do {
585
        str = (LPCWSTR)lmem;
586
        lmem += (lstrlenW(str) + 1) * sizeof(WCHAR);
587 588 589 590 591 592 593 594
        flg = *(const DWORD*)lmem;
        eid = *(const WORD*)(lmem + sizeof(DWORD));
        lmem += sizeof(DWORD) + sizeof(WORD);
        idx ++;
        /* TRACE("cmd=%s %08lx %04x\n", debugstr_w(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;
595 596 597
        case MCI_HWND:                  /* Occurs inside MCI_CONSTANT as in "window handle default" */
        case MCI_HPAL:
        case MCI_HDC:
598 599 600 601 602 603 604 605 606 607
        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;
        }
608 609 610 611 612 613 614 615 616
    } while (eid != MCI_END_COMMAND_LIST);
    return TRUE;
}

/**************************************************************************
 * 				MCI_DumpCommandTable		[internal]
 */
static	BOOL		MCI_DumpCommandTable(UINT uTbl)
{
617 618
    const BYTE*	lmem;
    LPCWSTR	str;
619
    WORD	eid;
620

621 622 623 624 625 626 627 628
    if (!MCI_IsCommandTableValid(uTbl)) {
	ERR("Ooops: %d is not valid\n", uTbl);
	return FALSE;
    }

    lmem = S_MciCmdTable[uTbl].lpTable;
    do {
	do {
629
	    /* DWORD flg; */
630
	    str = (LPCWSTR)lmem;
631
	    lmem += (lstrlenW(str) + 1) * sizeof(WCHAR);
632
	    /* flg = *(const DWORD*)lmem; */
Eric Pouech's avatar
Eric Pouech committed
633
	    eid = *(const WORD*)(lmem + sizeof(DWORD));
634
            /* TRACE("cmd=%s %08lx %04x\n", debugstr_w(str), flg, eid); */
635 636
	    lmem += sizeof(DWORD) + sizeof(WORD);
	} while (eid != MCI_END_COMMAND && eid != MCI_END_COMMAND_LIST);
637
        /* EPP TRACE(" => end of command%s\n", (eid == MCI_END_COMMAND_LIST) ? " list" : ""); */
638 639 640 641 642 643 644 645
    } while (eid != MCI_END_COMMAND_LIST);
    return TRUE;
}


/**************************************************************************
 * 				MCI_GetCommandTable		[internal]
 */
646
static	UINT		MCI_GetCommandTable(UINT uDevType)
647 648
{
    UINT	uTbl;
649 650
    WCHAR	buf[32];
    LPCWSTR	str = NULL;
651

652 653
    /* first look up existing for existing devType */
    for (uTbl = 0; uTbl < MAX_MCICMDTABLE; uTbl++) {
654
	if (S_MciCmdTable[uTbl].lpTable && S_MciCmdTable[uTbl].uDevType == uDevType)
655
	    return uTbl;
656 657 658 659
    }

    /* well try to load id */
    if (uDevType >= MCI_DEVTYPE_FIRST && uDevType <= MCI_DEVTYPE_LAST) {
660
	if (LoadStringW(hWinMM32Instance, uDevType, buf, ARRAY_SIZE(buf))) {
661 662 663
	    str = buf;
	}
    } else if (uDevType == 0) {
664
	str = L"CORE";
665 666 667
    }
    uTbl = MCI_NO_COMMAND_TABLE;
    if (str) {
668
	HRSRC 	hRsrc = FindResourceW(hWinMM32Instance, str, (LPCWSTR)RT_RCDATA);
669 670
	HANDLE	hMem = 0;

671
	if (hRsrc) hMem = LoadResource(hWinMM32Instance, hRsrc);
672
	if (hMem) {
673
	    uTbl = MCI_SetCommandTable(hMem, uDevType);
674
	} else {
675
	    WARN("No command table found in resource %p[%s]\n",
676
		 hWinMM32Instance, debugstr_w(str));
677 678 679 680 681 682 683 684 685
	}
    }
    TRACE("=> %d\n", uTbl);
    return uTbl;
}

/**************************************************************************
 * 				MCI_SetCommandTable		[internal]
 */
686
static UINT MCI_SetCommandTable(HGLOBAL hMem, UINT uDevType)
687
{
688 689 690 691 692 693 694 695 696 697
    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;
698
	MCI_GetCommandTable(0);
699
    }
700
    TRACE("(%p, %u)\n", hMem, uDevType);
701
    for (uTbl = 0; uTbl < MAX_MCICMDTABLE; uTbl++) {
702
	if (!S_MciCmdTable[uTbl].lpTable) {
703 704
	    const BYTE* lmem;
	    LPCWSTR 	str;
705 706 707
	    WORD	eid;
	    WORD	count;

708
	    S_MciCmdTable[uTbl].uDevType = uDevType;
709 710
	    S_MciCmdTable[uTbl].lpTable = LockResource(hMem);
	    S_MciCmdTable[uTbl].hMem = hMem;
711 712 713 714

	    if (TRACE_ON(mci)) {
		MCI_DumpCommandTable(uTbl);
	    }
715 716 717 718 719 720

	    /* create the verbs table */
	    /* get # of entries */
	    lmem = S_MciCmdTable[uTbl].lpTable;
	    count = 0;
	    do {
721
		str = (LPCWSTR)lmem;
722
		lmem += (lstrlenW(str) + 1) * sizeof(WCHAR);
Eric Pouech's avatar
Eric Pouech committed
723
		eid = *(const WORD*)(lmem + sizeof(DWORD));
724 725 726 727 728
		lmem += sizeof(DWORD) + sizeof(WORD);
		if (eid == MCI_COMMAND_HEAD)
		    count++;
	    } while (eid != MCI_END_COMMAND_LIST);

729
	    S_MciCmdTable[uTbl].aVerbs = HeapAlloc(GetProcessHeap(), 0, count * sizeof(LPCWSTR));
730 731 732 733 734
	    S_MciCmdTable[uTbl].nVerbs = count;

	    lmem = S_MciCmdTable[uTbl].lpTable;
	    count = 0;
	    do {
735
		str = (LPCWSTR)lmem;
736
		lmem += (lstrlenW(str) + 1) * sizeof(WCHAR);
Eric Pouech's avatar
Eric Pouech committed
737
		eid = *(const WORD*)(lmem + sizeof(DWORD));
738 739 740 741 742
		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); */
743 744 745 746 747 748 749 750 751 752
	    return uTbl;
	}
    }

    return MCI_NO_COMMAND_TABLE;
}

/**************************************************************************
 * 				MCI_UnLoadMciDriver		[internal]
 */
753
static	BOOL	MCI_UnLoadMciDriver(LPWINE_MCIDRIVER wmd)
754
{
755
    LPWINE_MCIDRIVER*		tmp;
756 757 758 759

    if (!wmd)
	return TRUE;

760
    CloseDriver(wmd->hDriver, 0, 0);
761 762 763 764

    if (wmd->dwPrivate != 0)
	WARN("Unloading mci driver with non nul dwPrivate field\n");

765 766
    EnterCriticalSection(&WINMM_cs);
    for (tmp = &MciDrivers; *tmp; tmp = &(*tmp)->lpNext) {
767 768 769 770 771
	if (*tmp == wmd) {
	    *tmp = wmd->lpNext;
	    break;
	}
    }
772
    LeaveCriticalSection(&WINMM_cs);
773 774 775 776 777 778 779

    HeapFree(GetProcessHeap(), 0, wmd->lpstrDeviceType);
    HeapFree(GetProcessHeap(), 0, wmd->lpstrAlias);

    HeapFree(GetProcessHeap(), 0, wmd);
    return TRUE;
}
780

781 782 783
/**************************************************************************
 * 				MCI_OpenMciDriver		[internal]
 */
784
static	BOOL	MCI_OpenMciDriver(LPWINE_MCIDRIVER wmd, LPCWSTR drvTyp, DWORD_PTR lp)
785
{
786
    WCHAR	libName[128];
787

788
    if (!DRIVER_GetLibName(drvTyp, L"MCI", libName, sizeof(libName)))
789 790 791
	return FALSE;

    /* First load driver */
792 793
    wmd->hDriver = (HDRVR)DRIVER_TryOpenDriver32(libName, lp);
    return wmd->hDriver != NULL;
794 795
}

796 797 798
/**************************************************************************
 * 				MCI_LoadMciDriver		[internal]
 */
799
static	DWORD	MCI_LoadMciDriver(LPCWSTR _strDevTyp, LPWINE_MCIDRIVER* lpwmd)
800
{
801
    LPWSTR			strDevTyp = str_dup_upper(_strDevTyp);
802
    LPWINE_MCIDRIVER		wmd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wmd));
803
    MCI_OPEN_DRIVER_PARMSW	modp;
804
    DWORD			dwRet = 0;
805

806 807 808 809
    if (!wmd || !strDevTyp) {
	dwRet = MCIERR_OUT_OF_MEMORY;
	goto errCleanUp;
    }
810

811 812
    wmd->lpfnYieldProc = MCI_DefYieldProc;
    wmd->dwYieldData = VK_CANCEL;
813
    wmd->CreatorThread = GetCurrentThreadId();
814

815
    EnterCriticalSection(&WINMM_cs);
Austin English's avatar
Austin English committed
816
    /* wmd must be inserted in list before sending opening the driver, because it
817 818
     * may want to lookup at wDevID
     */
819 820
    wmd->lpNext = MciDrivers;
    MciDrivers = wmd;
821

822 823
    for (modp.wDeviceID = MCI_MAGIC;
	 MCI_GetDriver(modp.wDeviceID) != 0;
824 825 826 827
	 modp.wDeviceID++);

    wmd->wDeviceID = modp.wDeviceID;

828
    LeaveCriticalSection(&WINMM_cs);
829

830
    TRACE("wDevID=%04X\n", modp.wDeviceID);
831 832

    modp.lpstrParams = NULL;
833

834
    if (!MCI_OpenMciDriver(wmd, strDevTyp, (DWORD_PTR)&modp)) {
835 836 837
	/* silence warning if all is used... some bogus program use commands like
	 * 'open all'...
	 */
838
	if (wcsicmp(strDevTyp, L"ALL") == 0) {
839 840
	    dwRet = MCIERR_CANNOT_USE_ALL;
	} else {
841
	    FIXME("Couldn't load driver for type %s.\n",
842
		  debugstr_w(strDevTyp));
843 844
	    dwRet = MCIERR_DEVICE_NOT_INSTALLED;
	}
845
	goto errCleanUp;
846
    }
847

848 849 850 851 852 853 854 855
    /* 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;

856
    TRACE("Loaded driver %p (%s), type is %d, cmdTable=%08x\n",
857
	  wmd->hDriver, debugstr_w(strDevTyp), modp.wType, modp.wCustomCommandTable);
858

859 860 861
    wmd->lpstrDeviceType = strDevTyp;
    wmd->wType = modp.wType;

862
    TRACE("mcidev=%d, uDevTyp=%04X wDeviceID=%04X !\n",
863 864 865 866
	  modp.wDeviceID, modp.wType, modp.wDeviceID);
    *lpwmd = wmd;
    return 0;
errCleanUp:
867
    MCI_UnLoadMciDriver(wmd);
868 869 870 871
    HeapFree(GetProcessHeap(), 0, strDevTyp);
    *lpwmd = 0;
    return dwRet;
}
872

873 874 875 876 877 878 879 880 881
/**************************************************************************
 * 			MCI_SendCommandFrom32			[internal]
 */
static DWORD MCI_SendCommandFrom32(MCIDEVICEID wDevID, UINT16 wMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
    DWORD		dwRet = MCIERR_INVALID_DEVICE_ID;
    LPWINE_MCIDRIVER	wmd = MCI_GetDriver(wDevID);

    if (wmd) {
882 883 884
        if(wmd->CreatorThread != GetCurrentThreadId())
            return MCIERR_INVALID_DEVICE_NAME;

885
        dwRet = SendDriverMessage(wmd->hDriver, wMsg, dwParam1, dwParam2);
886 887 888 889
    }
    return dwRet;
}

890 891
/**************************************************************************
 * 			MCI_FinishOpen				[internal]
892 893 894 895 896 897 898
 *
 * Three modes of operation:
 * 1 open foo.ext ...        -> OPEN_ELEMENT with lpstrElementName=foo.ext
 *   open sequencer!foo.ext     same         with lpstrElementName=foo.ext
 * 2 open new type waveaudio -> OPEN_ELEMENT with empty ("") lpstrElementName
 * 3 open sequencer          -> OPEN_ELEMENT unset, and
 *   capability sequencer       (auto-open)  likewise
899
 */
900
static	DWORD	MCI_FinishOpen(LPWINE_MCIDRIVER wmd, LPMCI_OPEN_PARMSW lpParms,
901 902
			       DWORD dwParam)
{
903 904
    LPCWSTR alias = NULL;
    /* Open always defines an alias for further reference */
905
    if (dwParam & MCI_OPEN_ALIAS) {         /* open ... alias */
906
        alias = lpParms->lpstrAlias;
907 908 909
        if (MCI_GetDriverFromString(alias))
            return MCIERR_DUPLICATE_ALIAS;
    } else {
910 911 912 913 914
        if ((dwParam & MCI_OPEN_ELEMENT)    /* open file.wav */
            && !(dwParam & MCI_OPEN_ELEMENT_ID))
            alias = lpParms->lpstrElementName;
        else if (dwParam & MCI_OPEN_TYPE )  /* open cdaudio */
            alias = wmd->lpstrDeviceType;
915 916
        if (alias && MCI_GetDriverFromString(alias))
            return MCIERR_DEVICE_OPEN;
917
    }
918
    if (alias) {
919
        wmd->lpstrAlias = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(alias)+1) * sizeof(WCHAR));
920
        if (!wmd->lpstrAlias) return MCIERR_OUT_OF_MEMORY;
921
        lstrcpyW( wmd->lpstrAlias, alias);
922 923
        /* In most cases, natives adds MCI_OPEN_ALIAS to the flags passed to the driver.
         * Don't.  The drivers don't care about the winmm alias. */
924
    }
925 926
    lpParms->wDeviceID = wmd->wDeviceID;

927
    return MCI_SendCommandFrom32(wmd->wDeviceID, MCI_OPEN_DRIVER, dwParam,
928
				 (DWORD_PTR)lpParms);
929 930 931 932 933
}

/**************************************************************************
 * 				MCI_FindCommand		[internal]
 */
934
static	LPCWSTR		MCI_FindCommand(UINT uTbl, LPCWSTR verb)
935
{
936
    UINT	idx;
937

938
    if (uTbl >= MAX_MCICMDTABLE || !S_MciCmdTable[uTbl].lpTable)
939 940
	return NULL;

941 942 943 944 945
    /* 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++) {
946
	if (wcsicmp(S_MciCmdTable[uTbl].aVerbs[idx], verb) == 0)
947 948
	    return S_MciCmdTable[uTbl].aVerbs[idx];
    }
949 950 951 952 953 954 955

    return NULL;
}

/**************************************************************************
 * 				MCI_GetReturnType		[internal]
 */
956
static	DWORD		MCI_GetReturnType(LPCWSTR lpCmd)
957
{
958
    lpCmd = (LPCWSTR)((const BYTE*)(lpCmd + lstrlenW(lpCmd) + 1) + sizeof(DWORD) + sizeof(WORD));
959
    if (*lpCmd == '\0' && *(const WORD*)((const BYTE*)(lpCmd + 1) + sizeof(DWORD)) == MCI_RETURN) {
Eric Pouech's avatar
Eric Pouech committed
960
	return *(const DWORD*)(lpCmd + 1);
961 962 963 964 965 966 967
    }
    return 0L;
}

/**************************************************************************
 * 				MCI_GetMessage			[internal]
 */
968
static	WORD		MCI_GetMessage(LPCWSTR lpCmd)
969
{
970
    return (WORD)*(const DWORD*)(lpCmd + lstrlenW(lpCmd) + 1);
971 972 973 974
}

/**************************************************************************
 * 				MCI_GetDWord			[internal]
975 976 977
 *
 * Accept 0 -1 255 255:0 255:255:255:255 :::1 1::: 2::3 ::4: 12345678
 * Refuse -1:0 0:-1 :: 256:0 1:256 0::::1
978
 */
979
static	BOOL		MCI_GetDWord(DWORD* data, LPWSTR* ptr)
980
{
981 982 983 984 985 986 987 988
    LPWSTR	ret = *ptr;
    DWORD	total = 0, shift = 0;
    BOOL	sign = FALSE, digits = FALSE;

    while (*ret == ' ' || *ret == '\t') ret++;
    if (*ret == '-') {
	ret++;
	sign = TRUE;
989
    }
990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007
    for(;;) {
	DWORD	val = 0;
	while ('0' <= *ret && *ret <= '9') {
	    val = *ret++ - '0' + 10 * val;
	    digits = TRUE;
	}
	switch (*ret) {
	case '\0':	break;
	case '\t':
	case ' ':	ret++; break;
	default:	return FALSE;
	case ':':
	    if ((val >= 256) || (shift >= 24))	return FALSE;
	    total |= val << shift;
	    shift += 8;
	    ret++;
	    continue;
	}
1008

1009 1010 1011 1012 1013 1014 1015
	if (!digits)				return FALSE;
	if (shift && (val >= 256 || sign))	return FALSE;
	total |= val << shift;
	*data = sign ? -total : total;
	*ptr = ret;
	return TRUE;
    }
1016 1017 1018 1019 1020
}

/**************************************************************************
 * 				MCI_GetString		[internal]
 */
1021
static	DWORD	MCI_GetString(LPWSTR* str, LPWSTR* args)
1022
{
1023
    LPWSTR      ptr = *args;
1024 1025 1026

    /* see if we have a quoted string */
    if (*ptr == '"') {
1027
	ptr = wcschr(*str = ptr + 1, '"');
1028 1029 1030 1031 1032 1033
	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;
    } else {
1034
	ptr = wcschr(ptr, ' ');
1035 1036 1037 1038

	if (ptr) {
	    *ptr++ = '\0';
	} else {
1039
	    ptr = *args + lstrlenW(*args);
1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052
	}
	*str = *args;
    }

    *args = ptr;
    return 0;
}

#define	MCI_DATA_SIZE	16

/**************************************************************************
 * 				MCI_ParseOptArgs		[internal]
 */
1053
static	DWORD	MCI_ParseOptArgs(DWORD* data, int _offset, LPCWSTR lpCmd,
1054
				 LPWSTR args, LPDWORD dwFlags)
1055 1056
{
    int		len, offset;
1057 1058
    const char* lmem;
    LPCWSTR     str;
1059 1060 1061 1062 1063 1064
    DWORD	dwRet, flg, cflg = 0;
    WORD	eid;
    BOOL	inCst, found;

    /* loop on arguments */
    while (*args) {
1065
	lmem = (const char*)lpCmd;
1066 1067
	found = inCst = FALSE;
	offset = _offset;
1068 1069 1070

	/* skip any leading white space(s) */
	while (*args == ' ') args++;
1071
	TRACE("args=%s\n", debugstr_w(args));
1072

1073
	do { /* loop on options for command table for the requested verb */
1074
	    str = (LPCWSTR)lmem;
1075
	    lmem += ((len = lstrlenW(str)) + 1) * sizeof(WCHAR);
Eric Pouech's avatar
Eric Pouech committed
1076 1077
	    flg = *(const DWORD*)lmem;
	    eid = *(const WORD*)(lmem + sizeof(DWORD));
1078
	    lmem += sizeof(DWORD) + sizeof(WORD);
1079
            /* TRACE("\tcmd=%s inCst=%c eid=%04x\n", debugstr_w(str), inCst ? 'Y' : 'N', eid); */
1080

1081
	    switch (eid) {
1082
	    case MCI_CONSTANT:
1083
		inCst = TRUE;	cflg = flg;	break;
1084
	    case MCI_END_CONSTANT:
1085 1086 1087 1088
		/* there may be additional integral values after flag in constant */
		if (inCst && MCI_GetDWord(&(data[offset]), &args)) {
		    *dwFlags |= cflg;
		}
1089
		inCst = FALSE;	cflg = 0;
1090
		break;
1091 1092
	    case MCI_RETURN:
		if (offset != _offset) {
1093
		    FIXME("MCI_RETURN not in first position\n");
1094 1095
		    return MCIERR_PARSER_INTERNAL;
		}
1096 1097
	    }

1098
	    if (wcsnicmp(args, str, len) == 0 &&
1099
                ((eid == MCI_STRING && len == 0) || args[len] == 0 || args[len] == ' ')) {
1100 1101
		/* store good values into data[] */
		args += len;
1102
		while (*args == ' ') args++;
1103 1104 1105 1106 1107 1108 1109 1110 1111 1112
		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;
1113
		case MCI_FLAG:
1114
		    *dwFlags |= flg;
1115
		    TRACE("flag=%08x\n", flg);
1116
		    break;
1117 1118 1119
		case MCI_HWND:
		case MCI_HPAL:
		case MCI_HDC:
1120 1121 1122 1123 1124
		case MCI_INTEGER:
		    if (inCst) {
			data[offset] |= flg;
			*dwFlags |= cflg;
			inCst = FALSE;
1125
			TRACE("flag=%08x constant=%08x\n", cflg, flg);
1126 1127 1128 1129 1130
		    } else {
			*dwFlags |= flg;
			if (!MCI_GetDWord(&(data[offset]), &args)) {
			    return MCIERR_BAD_INTEGER;
			}
1131
			TRACE("flag=%08x int=%d\n", flg, data[offset]);
1132 1133
		    }
		    break;
1134
		case MCI_RECT:
1135
		    /* store rect in data (offset..offset+3) */
1136 1137 1138 1139 1140 1141 1142
		    *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)) {
			return MCIERR_BAD_INTEGER;
		    }
1143
		    TRACE("flag=%08x for rectangle\n", flg);
1144 1145 1146
		    break;
		case MCI_STRING:
		    *dwFlags |= flg;
1147
		    if ((dwRet = MCI_GetString((LPWSTR*)&data[offset], &args)))
1148
			return dwRet;
1149
		    TRACE("flag=%08x string=%s\n", flg, debugstr_w(*(LPWSTR*)&data[offset]));
1150
		    break;
1151
		default:	ERR("oops\n");
1152
		}
1153 1154
		/* exit inside while loop, except if just entered in constant area definition */
		if (!inCst || eid != MCI_CONSTANT) eid = MCI_END_COMMAND;
1155 1156 1157 1158 1159 1160 1161 1162 1163
	    } 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;
1164 1165 1166
		case MCI_HWND:
		case MCI_HPAL:
		case MCI_HDC:			if (!inCst) offset += sizeof(HANDLE)/sizeof(DWORD); break;
1167
		case MCI_INTEGER:		if (!inCst) offset++;	break;
1168 1169
		case MCI_END_CONSTANT:		offset++; break;
		case MCI_STRING:		offset += sizeof(LPWSTR)/sizeof(DWORD); break;
1170
		case MCI_RECT:			offset += 4; break;
1171
		default:			ERR("oops\n");
1172 1173 1174 1175
		}
	    }
	} while (eid != MCI_END_COMMAND);
	if (!found) {
1176
	    WARN("Optarg %s not found\n", debugstr_w(args));
1177 1178 1179
	    return MCIERR_UNRECOGNIZED_COMMAND;
	}
	if (offset == MCI_DATA_SIZE) {
1180
	    FIXME("Internal data[] buffer overflow\n");
1181 1182 1183 1184 1185 1186 1187 1188 1189
	    return MCIERR_PARSER_INTERNAL;
	}
    }
    return 0;
}

/**************************************************************************
 * 				MCI_HandleReturnValues	[internal]
 */
1190
static	DWORD	MCI_HandleReturnValues(DWORD dwRet, LPWINE_MCIDRIVER wmd, DWORD retType,
1191
                                       MCI_GENERIC_PARMS *params, LPWSTR lpstrRet, UINT uRetLen)
1192 1193
{
    if (lpstrRet) {
1194
	switch (retType) {
1195 1196
	case 0: /* nothing to return */
	    break;
1197
	case MCI_INTEGER:
1198 1199
        {
            DWORD data = *(DWORD *)(params + 1);
1200 1201 1202
	    switch (dwRet & 0xFFFF0000ul) {
	    case 0:
	    case MCI_INTEGER_RETURNED:
1203
		swprintf(lpstrRet, uRetLen, L"%d", data);
1204 1205
		break;
	    case MCI_RESOURCE_RETURNED:
1206
		/* return string which ID is HIWORD(data),
1207
		 * string is loaded from mmsystem.dll */
1208
		LoadStringW(hWinMM32Instance, HIWORD(data), lpstrRet, uRetLen);
1209 1210
		break;
	    case MCI_RESOURCE_RETURNED|MCI_RESOURCE_DRIVER:
1211
		/* return string which ID is HIWORD(data),
1212
		 * string is loaded from driver */
1213
		/* FIXME: this is wrong for a 16 bit handle */
1214
		LoadStringW(GetDriverModuleHandle(wmd->hDriver),
1215
			    HIWORD(data), lpstrRet, uRetLen);
1216 1217
		break;
	    case MCI_COLONIZED3_RETURN:
1218
		swprintf(lpstrRet, uRetLen, L"%02d:%02d:%02d",
1219 1220
			  LOBYTE(LOWORD(data)), HIBYTE(LOWORD(data)),
			  LOBYTE(HIWORD(data)));
1221 1222
		break;
	    case MCI_COLONIZED4_RETURN:
1223
		swprintf(lpstrRet, uRetLen, L"%02d:%02d:%02d:%02d",
1224 1225
			  LOBYTE(LOWORD(data)), HIBYTE(LOWORD(data)),
			  LOBYTE(HIWORD(data)), HIBYTE(HIWORD(data)));
1226 1227 1228 1229
		break;
	    default:	ERR("Ooops (%04X)\n", HIWORD(dwRet));
	    }
	    break;
1230
        }
1231 1232 1233 1234 1235 1236 1237
#ifdef MCI_INTEGER64
	case MCI_INTEGER64:
        {
	    DWORD_PTR data = *(DWORD_PTR *)(params + 1);
	    switch (dwRet & 0xFFFF0000ul) {
	    case 0:
	    case MCI_INTEGER_RETURNED:
1238
		swprintf(lpstrRet, uRetLen, L"%ld", data);
1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252
		break;
	    case MCI_RESOURCE_RETURNED:
		/* return string which ID is HIWORD(data),
		 * string is loaded from mmsystem.dll */
		LoadStringW(hWinMM32Instance, HIWORD(data), lpstrRet, uRetLen);
		break;
	    case MCI_RESOURCE_RETURNED|MCI_RESOURCE_DRIVER:
		/* return string which ID is HIWORD(data),
		 * string is loaded from driver */
		/* FIXME: this is wrong for a 16 bit handle */
		LoadStringW(GetDriverModuleHandle(wmd->hDriver),
			    HIWORD(data), lpstrRet, uRetLen);
		break;
	    case MCI_COLONIZED3_RETURN:
1253
		swprintf(lpstrRet, uRetLen, L"%02d:%02d:%02d",
1254 1255 1256 1257
			  LOBYTE(LOWORD(data)), HIBYTE(LOWORD(data)),
			  LOBYTE(HIWORD(data)));
		break;
	    case MCI_COLONIZED4_RETURN:
1258
		swprintf(lpstrRet, uRetLen, L"%02d:%02d:%02d:%02d",
1259 1260 1261 1262 1263 1264 1265 1266
			  LOBYTE(LOWORD(data)), HIBYTE(LOWORD(data)),
			  LOBYTE(HIWORD(data)), HIBYTE(HIWORD(data)));
		break;
	    default:	ERR("Ooops (%04X)\n", HIWORD(dwRet));
	    }
	    break;
        }
#endif
1267
	case MCI_STRING:
1268 1269
	    switch (dwRet & 0xFFFF0000ul) {
	    case 0:
1270
		/* nothing to do data[0] == lpstrRet */
1271 1272
		break;
	    case MCI_INTEGER_RETURNED:
1273 1274 1275
            {
                DWORD *data = (DWORD *)(params + 1);
		*data = *(LPDWORD)lpstrRet;
1276
		swprintf(lpstrRet, uRetLen, L"%d", *data);
1277
		break;
1278
            }
1279 1280 1281 1282 1283
	    default:
		WARN("Oooch. MCI_STRING and HIWORD(dwRet)=%04x\n", HIWORD(dwRet));
		break;
	    }
	    break;
1284
	case MCI_RECT:
1285 1286
        {
            DWORD *data = (DWORD *)(params + 1);
1287
	    if (dwRet & 0xFFFF0000ul)
1288
		WARN("Oooch. MCI_STRING and HIWORD(dwRet)=%04x\n", HIWORD(dwRet));
1289
	    swprintf(lpstrRet, uRetLen, L"%d %d %d %d", data[0], data[1], data[2], data[3]);
1290
	    break;
1291
        }
1292
	default:		FIXME("Unknown MCI return type %d\n", retType);
1293 1294 1295 1296 1297 1298
	}
    }
    return LOWORD(dwRet);
}

/**************************************************************************
1299
 * 				mciSendStringW		[WINMM.@]
1300
 */
1301
DWORD WINAPI mciSendStringW(LPCWSTR lpstrCommand, LPWSTR lpstrRet,
1302 1303
			    UINT uRetLen, HWND hwndCallback)
{
1304
    LPWSTR		verb, dev, args, devType = NULL;
1305
    LPWINE_MCIDRIVER	wmd = 0;
1306
    MCIDEVICEID		uDevID, auto_open = 0;
1307 1308
    DWORD		dwFlags = 0, dwRet = 0;
    int			offset = 0;
1309
    DWORD		retType;
1310
    LPCWSTR		lpCmd = 0;
1311
    WORD		wMsg = 0;
1312 1313 1314 1315 1316 1317 1318 1319
    union
    {
        MCI_GENERIC_PARMS  generic;
        MCI_OPEN_PARMSW    open;
        MCI_SOUND_PARMSW   sound;
        MCI_SYSINFO_PARMSW sysinfo;
        DWORD              dw[MCI_DATA_SIZE];
    } data;
1320

1321 1322
    TRACE("(%s, %p, %d, %p)\n", 
          debugstr_w(lpstrCommand), lpstrRet, uRetLen, hwndCallback);
1323
    if (lpstrRet && uRetLen) *lpstrRet = '\0';
1324

1325 1326 1327
    if (!lpstrCommand[0])
        return MCIERR_MISSING_COMMAND_STRING;

1328
    /* format is <command> <device> <optargs> */
1329
    if (!(verb = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(lpstrCommand)+1) * sizeof(WCHAR))))
1330
	return MCIERR_OUT_OF_MEMORY;
1331
    lstrcpyW( verb, lpstrCommand );
1332
    CharLowerW(verb);
1333

1334
    memset(&data, 0, sizeof(data));
1335

1336
    if (!(args = wcschr(verb, ' '))) {
1337 1338 1339 1340 1341 1342 1343
	dwRet = MCIERR_MISSING_DEVICE_NAME;
	goto errCleanUp;
    }
    *args++ = '\0';
    if ((dwRet = MCI_GetString(&dev, &args))) {
	goto errCleanUp;
    }
1344
    uDevID = wcsicmp(dev, L"ALL") ? 0 : MCI_ALL_DEVICE_ID;
1345

1346
    /* Determine devType from open */
1347
    if (!wcscmp(verb, L"open")) {
1348
	LPWSTR	tmp;
1349
        WCHAR	buf[128];
1350

1351
	/* case dev == 'new' has to be handled */
1352
	if (!wcscmp(dev, L"new")) {
1353
	    dev = 0;
1354
	    if ((devType = wcsstr(args, L"type ")) != NULL) {
1355
		devType += 5;
1356
		tmp = wcschr(devType, ' ');
1357 1358 1359 1360 1361 1362 1363 1364 1365
		if (tmp) *tmp = '\0';
		devType = str_dup_upper(devType);
		if (tmp) *tmp = ' ';
		/* dwFlags and data[2] will be correctly set in ParseOpt loop */
	    } else {
		WARN("open new requires device type\n");
		dwRet = MCIERR_MISSING_DEVICE_NAME;
		goto errCleanUp;
	    }
1366
	    dwFlags |= MCI_OPEN_ELEMENT;
1367
	    data.open.lpstrElementName = &L""[0];
1368
	} else if ((devType = wcschr(dev, '!')) != NULL) {
1369
	    *devType++ = '\0';
1370 1371 1372
	    tmp = devType; devType = dev; dev = tmp;

	    dwFlags |= MCI_OPEN_TYPE;
1373
	    data.open.lpstrDeviceType = devType;
1374
	    devType = str_dup_upper(devType);
1375
	    dwFlags |= MCI_OPEN_ELEMENT;
1376
	    data.open.lpstrElementName = dev;
1377
	} else if (DRIVER_GetLibName(dev, L"MCI", buf, sizeof(buf))) {
1378
            /* this is the name of a mci driver's type */
1379
	    tmp = wcschr(dev, ' ');
1380
	    if (tmp) *tmp = '\0';
1381
	    data.open.lpstrDeviceType = dev;
1382
	    devType = str_dup_upper(dev);
1383 1384
	    if (tmp) *tmp = ' ';
	    dwFlags |= MCI_OPEN_TYPE;
1385
	} else {
1386
	    if ((devType = wcsstr(args, L"type ")) != NULL) {
1387
		devType += 5;
1388
		tmp = wcschr(devType, ' ');
1389
		if (tmp) *tmp = '\0';
1390
		devType = str_dup_upper(devType);
1391
		if (tmp) *tmp = ' ';
1392
		/* dwFlags and lpstrDeviceType will be correctly set in ParseOpt loop */
1393 1394 1395 1396
	    } else {
		if ((dwRet = MCI_GetDevTypeFromFileName(dev, buf, sizeof(buf))))
		    goto errCleanUp;

1397
		devType = str_dup_upper(buf);
1398 1399
	    }
	    dwFlags |= MCI_OPEN_ELEMENT;
1400
	    data.open.lpstrElementName = dev;
1401
	}
1402 1403 1404 1405
	if (MCI_ALL_DEVICE_ID == uDevID) {
	    dwRet = MCIERR_CANNOT_USE_ALL;
	    goto errCleanUp;
	}
1406
	if (!wcsstr(args, L" alias ") && !dev) {
1407 1408
	    dwRet = MCIERR_NEW_REQUIRES_ALIAS;
	    goto errCleanUp;
1409 1410
	}

1411
	dwRet = MCI_LoadMciDriver(devType, &wmd);
1412 1413
	if (dwRet == MCIERR_DEVICE_NOT_INSTALLED)
	    dwRet = MCIERR_INVALID_DEVICE_NAME;
1414
	if (dwRet)
1415
	    goto errCleanUp;
1416 1417 1418 1419 1420 1421 1422 1423 1424 1425
    } else if ((MCI_ALL_DEVICE_ID != uDevID) && !(wmd = MCI_GetDriver(mciGetDeviceIDW(dev)))
	       && (lpCmd = MCI_FindCommand(MCI_GetCommandTable(0), verb))) {
	/* auto-open uses the core command table */
	switch (MCI_GetMessage(lpCmd)) {
	case MCI_SOUND:   /* command does not use a device name */
	case MCI_SYSINFO:
	    break;
	case MCI_CLOSE:   /* don't auto-open for close */
	case MCI_BREAK:   /* no auto-open for system commands */
	    dwRet = MCIERR_INVALID_DEVICE_NAME;
1426
	    goto errCleanUp;
1427 1428 1429 1430
	    break;
	default:
	    {
		WCHAR   buf[138], retbuf[6];
1431
		swprintf(buf, ARRAY_SIZE(buf), L"open %s wait", dev);
1432
		/* open via mciSendString handles quoting, dev!file syntax and alias creation */
1433
		if ((dwRet = mciSendStringW(buf, retbuf, ARRAY_SIZE(retbuf), 0)) != 0)
1434
		    goto errCleanUp;
1435
		auto_open = wcstoul(retbuf, NULL, 10);
1436 1437 1438 1439 1440 1441 1442 1443 1444 1445
		TRACE("auto-opened %u for %s\n", auto_open, debugstr_w(dev));

		/* FIXME: test for notify flag (how to preparse?) before opening */
		wmd = MCI_GetDriver(auto_open);
		if (!wmd) {
		    ERR("No auto-open device %u\n", auto_open);
		    dwRet = MCIERR_INVALID_DEVICE_ID;
		    goto errCleanUp;
		}
	    }
1446
	}
1447 1448 1449
    }

    /* get the verb in the different command tables */
1450 1451 1452 1453 1454 1455
    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)
1456
		wmd->uTypeCmdTable = MCI_GetCommandTable(wmd->wType);
1457 1458 1459
	    if (wmd->uTypeCmdTable != MCI_NO_COMMAND_TABLE)
		lpCmd = MCI_FindCommand(wmd->uTypeCmdTable, verb);
	}
1460 1461
    }
    /* try core command table */
1462
    if (!lpCmd) lpCmd = MCI_FindCommand(MCI_GetCommandTable(0), verb);
1463 1464

    if (!lpCmd) {
1465
	TRACE("Command %s not found!\n", debugstr_w(verb));
1466 1467 1468
	dwRet = MCIERR_UNRECOGNIZED_COMMAND;
	goto errCleanUp;
    }
1469
    wMsg = MCI_GetMessage(lpCmd);
1470 1471

    /* set return information */
1472
    offset = sizeof(data.generic);
1473
    switch (retType = MCI_GetReturnType(lpCmd)) {
1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486
    case 0:
        break;
    case MCI_INTEGER:
        offset += sizeof(DWORD);
        break;
    case MCI_STRING:
        data.sysinfo.lpstrReturn = lpstrRet;
        data.sysinfo.dwRetSize = uRetLen;
        offset = FIELD_OFFSET( MCI_SYSINFO_PARMSW, dwNumber );
        break;
    case MCI_RECT:
        offset += 4 * sizeof(DWORD);
        break;
1487 1488 1489 1490 1491
#ifdef MCI_INTEGER64
    case MCI_INTEGER64:
	offset += sizeof(DWORD_PTR);
        break;
#endif
1492
    default:
1493 1494 1495
	FIXME("Unknown MCI return type %d\n", retType);
	dwRet = MCIERR_PARSER_INTERNAL;
	goto errCleanUp;
1496 1497
    }

1498 1499
    TRACE("verb=%s on dev=%s; offset=%d\n", 
          debugstr_w(verb), debugstr_w(dev), offset);
1500

1501
    if ((dwRet = MCI_ParseOptArgs(data.dw, offset / sizeof(DWORD), lpCmd, args, &dwFlags)))
1502 1503
	goto errCleanUp;

1504
    /* set up call back */
1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515
    if (auto_open) {
	if (dwFlags & MCI_NOTIFY) {
	    dwRet = MCIERR_NOTIFY_ON_AUTO_OPEN;
	    goto errCleanUp;
	}
	/* FIXME: the command should get its own notification window set up and
	 * ask for device closing while processing the notification mechanism.
	 * hwndCallback = ...
	 * dwFlags |= MCI_NOTIFY;
	 * In the meantime special-case all commands but PLAY and RECORD below. */
    }
1516
    if (dwFlags & MCI_NOTIFY) {
1517
	data.generic.dwCallback = (DWORD_PTR)hwndCallback;
1518 1519
    }

1520 1521
    switch (wMsg) {
    case MCI_OPEN:
1522
	if (wcscmp(verb, L"open")) {
1523
	    FIXME("Cannot open with command %s\n", debugstr_w(verb));
1524
	    dwRet = MCIERR_DRIVER_INTERNAL;
1525 1526 1527 1528 1529 1530 1531 1532 1533
	    wMsg = 0;
	    goto errCleanUp;
	}
	break;
    case MCI_SYSINFO:
	/* Requirements on dev depend on the flags:
	 * alias with INSTALLNAME, name like "digitalvideo"
	 * with QUANTITY and NAME. */
	{
1534
	    data.sysinfo.wDeviceType = MCI_ALL_DEVICE_ID;
1535 1536 1537
	    if (uDevID != MCI_ALL_DEVICE_ID) {
		if (dwFlags & MCI_SYSINFO_INSTALLNAME)
		    wmd = MCI_GetDriver(mciGetDeviceIDW(dev));
1538
		else if (!(data.sysinfo.wDeviceType = MCI_GetDevTypeFromResource(dev))) {
1539 1540 1541 1542 1543 1544
		    dwRet = MCIERR_DEVICE_TYPE_REQUIRED;
		    goto errCleanUp;
		}
	    }
	}
	break;
Jörg Höhle's avatar
Jörg Höhle committed
1545 1546 1547
    case MCI_SOUND:
	/* FIXME: name is optional, "sound" is a valid command.
	 * FIXME: Parse "sound notify" as flag, not as name. */
1548
	data.sound.lpstrSoundName = dev;
Jörg Höhle's avatar
Jörg Höhle committed
1549 1550
	dwFlags |= MCI_SOUND_NAME;
	break;
1551 1552
    }

1553
    TRACE("[%d, %s, %08x, %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x]\n",
1554
	  wmd ? wmd->wDeviceID : uDevID, MCI_MessageToString(wMsg), dwFlags,
1555 1556
	  data.dw[0], data.dw[1], data.dw[2], data.dw[3], data.dw[4],
	  data.dw[5], data.dw[6], data.dw[7], data.dw[8], data.dw[9]);
1557

1558
    if (wMsg == MCI_OPEN) {
1559
	if ((dwRet = MCI_FinishOpen(wmd, &data.open, dwFlags)))
1560
	    goto errCleanUp;
1561 1562
	/* FIXME: notification is not properly shared across two opens */
    } else {
1563
	dwRet = MCI_SendCommand(wmd ? wmd->wDeviceID : uDevID, wMsg, dwFlags, (DWORD_PTR)&data);
1564
    }
1565
    if (!LOWORD(dwRet)) {
1566
	TRACE("=> 1/ %x (%s)\n", dwRet, debugstr_w(lpstrRet));
1567 1568
	dwRet = MCI_HandleReturnValues(dwRet, wmd, retType, &data.generic, lpstrRet, uRetLen);
	TRACE("=> 2/ %x (%s)\n", dwRet, debugstr_w(lpstrRet));
1569 1570
    } else
	TRACE("=> %x\n", dwRet);
1571 1572

errCleanUp:
1573 1574 1575 1576 1577 1578 1579
    if (auto_open) {
	/* PLAY and RECORD are the only known non-immediate commands */
	if (LOWORD(dwRet) || !(wMsg == MCI_PLAY || wMsg == MCI_RECORD))
	    MCI_SendCommand(auto_open, MCI_CLOSE, 0, 0);
	else
	    FIXME("leaking auto-open device %u\n", auto_open);
    }
1580 1581
    if (wMsg == MCI_OPEN && LOWORD(dwRet) && wmd)
	MCI_UnLoadMciDriver(wmd);
1582
    HeapFree(GetProcessHeap(), 0, devType);
1583 1584 1585 1586 1587
    HeapFree(GetProcessHeap(), 0, verb);
    return dwRet;
}

/**************************************************************************
1588
 * 				mciSendStringA			[WINMM.@]
1589
 */
1590
DWORD WINAPI mciSendStringA(LPCSTR lpstrCommand, LPSTR lpstrRet,
1591
			    UINT uRetLen, HWND hwndCallback)
1592
{
1593 1594
    LPWSTR 	lpwstrCommand;
    LPWSTR      lpwstrRet = NULL;
1595
    UINT	ret;
1596
    INT len;
1597

1598
    /* FIXME: is there something to do with lpstrReturnString ? */
1599 1600 1601 1602
    len = MultiByteToWideChar( CP_ACP, 0, lpstrCommand, -1, NULL, 0 );
    lpwstrCommand = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
    MultiByteToWideChar( CP_ACP, 0, lpstrCommand, -1, lpwstrCommand, len );
    if (lpstrRet)
1603
    {
1604
        if (uRetLen) *lpstrRet = '\0'; /* NT-w2k3 use memset(lpstrRet, 0, uRetLen); */
1605
        lpwstrRet = HeapAlloc(GetProcessHeap(), 0, uRetLen * sizeof(WCHAR));
1606 1607 1608 1609
        if (!lpwstrRet) {
            HeapFree( GetProcessHeap(), 0, lpwstrCommand );
            return MCIERR_OUT_OF_MEMORY;
        }
1610
    }
1611
    ret = mciSendStringW(lpwstrCommand, lpwstrRet, uRetLen, hwndCallback);
1612
    if (!ret && lpwstrRet)
1613 1614 1615
        WideCharToMultiByte( CP_ACP, 0, lpwstrRet, -1, lpstrRet, uRetLen, NULL, NULL );
    HeapFree(GetProcessHeap(), 0, lpwstrCommand);
    HeapFree(GetProcessHeap(), 0, lpwstrRet);
1616
    return ret;
1617 1618
}

1619
/**************************************************************************
1620
 * 				mciExecute			[WINMM.@]
1621
 */
1622
BOOL WINAPI mciExecute(LPCSTR lpstrCommand)
1623 1624 1625
{
    char	strRet[256];
    DWORD	ret;
1626

1627 1628
    TRACE("(%s)!\n", lpstrCommand);

1629
    ret = mciSendStringA(lpstrCommand, strRet, sizeof(strRet), 0);
1630 1631
    if (ret != 0) {
	if (!mciGetErrorStringA(ret, strRet, sizeof(strRet))) {
1632
	    sprintf(strRet, "Unknown MCI error (%d)", ret);
1633
	}
1634
	MessageBoxA(0, strRet, "Error in mciExecute()", MB_OK);
1635 1636
    }
    /* FIXME: what shall I return ? */
1637
    return TRUE;
1638
}
1639

1640
/**************************************************************************
1641
 *                    	mciLoadCommandResource  		[WINMM.@]
1642
 *
1643
 * Strangely, this function only exists as a UNICODE one.
1644
 */
1645
UINT WINAPI mciLoadCommandResource(HINSTANCE hInst, LPCWSTR resNameW, UINT type)
1646
{
1647 1648 1649
    UINT        ret = MCI_NO_COMMAND_TABLE;
    HRSRC	hRsrc = 0;
    HGLOBAL     hMem;
1650

1651
    TRACE("(%p, %s, %d)!\n", hInst, debugstr_w(resNameW), type);
1652

1653
    /* if a file named "resname.mci" exits, then load resource "resname" from it
1654 1655 1656 1657
     * otherwise directly from driver
     * We don't support it (who uses this feature ?), but we check anyway
     */
    if (!type) {
1658 1659
#if 0
        /* FIXME: we should put this back into order, but I never found a program
1660
         * actually using this feature, so we may not need it
1661
         */
1662 1663 1664 1665 1666 1667 1668 1669
	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
1670
    }
1671 1672 1673 1674
    if ((hRsrc = FindResourceW(hInst, resNameW, (LPWSTR)RT_RCDATA)) &&
        (hMem = LoadResource(hInst, hRsrc))) {
        ret = MCI_SetCommandTable(hMem, type);
        FreeResource(hMem);
1675
    }
1676 1677
    else WARN("No command table found in module for %s\n", debugstr_w(resNameW));

1678 1679 1680 1681
    TRACE("=> %04x\n", ret);
    return ret;
}

1682
/**************************************************************************
1683
 *                    	mciFreeCommandResource			[WINMM.@]
1684
 */
1685
BOOL WINAPI mciFreeCommandResource(UINT uTable)
1686
{
1687 1688
    TRACE("(%08x)!\n", uTable);

1689 1690 1691 1692 1693 1694 1695 1696 1697 1698
    if (uTable >= MAX_MCICMDTABLE || !S_MciCmdTable[uTable].lpTable)
	return FALSE;

    FreeResource(S_MciCmdTable[uTable].hMem);
    S_MciCmdTable[uTable].hMem = NULL;
    S_MciCmdTable[uTable].lpTable = NULL;
    HeapFree(GetProcessHeap(), 0, S_MciCmdTable[uTable].aVerbs);
    S_MciCmdTable[uTable].aVerbs = 0;
    S_MciCmdTable[uTable].nVerbs = 0;
    return TRUE;
1699 1700 1701 1702 1703
}

/**************************************************************************
 * 			MCI_Open				[internal]
 */
1704
static	DWORD MCI_Open(DWORD dwParam, LPMCI_OPEN_PARMSW lpParms)
1705
{
1706
    WCHAR			strDevTyp[128];
1707
    DWORD 			dwRet;
1708
    LPWINE_MCIDRIVER		wmd = NULL;
1709

1710
    TRACE("(%08X, %p)\n", dwParam, lpParms);
1711
    if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1712 1713

    /* only two low bytes are generic, the other ones are dev type specific */
1714
#define WINE_MCIDRIVER_SUPP	(0xFFFF0000|MCI_OPEN_SHAREABLE|MCI_OPEN_ELEMENT| \
1715 1716
                         MCI_OPEN_ALIAS|MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID| \
                         MCI_NOTIFY|MCI_WAIT)
1717
    if ((dwParam & ~WINE_MCIDRIVER_SUPP) != 0)
1718
        FIXME("Unsupported yet dwFlags=%08X\n", dwParam);
1719
#undef WINE_MCIDRIVER_SUPP
1720 1721 1722

    strDevTyp[0] = 0;

1723 1724
    if (dwParam & MCI_OPEN_TYPE) {
	if (dwParam & MCI_OPEN_TYPE_ID) {
1725
	    WORD uDevType = LOWORD(lpParms->lpstrDeviceType);
1726

1727 1728
	    if (uDevType < MCI_DEVTYPE_FIRST || uDevType > MCI_DEVTYPE_LAST ||
		!LoadStringW(hWinMM32Instance, uDevType, strDevTyp, ARRAY_SIZE(strDevTyp))) {
1729 1730 1731 1732
		dwRet = MCIERR_BAD_INTEGER;
		goto errCleanUp;
	    }
	} else {
1733
	    LPWSTR	ptr;
1734 1735 1736 1737
	    if (lpParms->lpstrDeviceType == NULL) {
		dwRet = MCIERR_NULL_PARAMETER_BLOCK;
		goto errCleanUp;
	    }
1738 1739
	    lstrcpyW(strDevTyp, lpParms->lpstrDeviceType);
	    ptr = wcschr(strDevTyp, '!');
1740 1741 1742 1743 1744 1745 1746
	    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",
1747 1748
			debugstr_w(lpParms->lpstrElementName), 
                        debugstr_w(strDevTyp));
1749 1750 1751 1752 1753 1754 1755 1756
		    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;
	    }
1757

1758
	}
1759
	TRACE("devType=%s !\n", debugstr_w(strDevTyp));
1760 1761
    }

1762
    if (dwParam & MCI_OPEN_ELEMENT) {
1763
	TRACE("lpstrElementName=%s\n", debugstr_w(lpParms->lpstrElementName));
1764 1765 1766 1767 1768 1769 1770

	if (dwParam & MCI_OPEN_ELEMENT_ID) {
	    FIXME("Unsupported yet flag MCI_OPEN_ELEMENT_ID\n");
	    dwRet = MCIERR_UNRECOGNIZED_KEYWORD;
	    goto errCleanUp;
	}

1771 1772 1773 1774 1775
	if (!lpParms->lpstrElementName) {
	    dwRet = MCIERR_NULL_PARAMETER_BLOCK;
	    goto errCleanUp;
	}

1776
	/* type, if given as a parameter, supersedes file extension */
1777 1778
	if (!strDevTyp[0] &&
	    MCI_GetDevTypeFromFileName(lpParms->lpstrElementName,
1779
				       strDevTyp, sizeof(strDevTyp))) {
1780
	    if (GetDriveTypeW(lpParms->lpstrElementName) != DRIVE_CDROM) {
1781 1782
		dwRet = MCIERR_EXTENSION_NOT_FOUND;
		goto errCleanUp;
1783
	    }
1784
	    /* FIXME: this will not work if several CDROM drives are installed on the machine */
1785
	    lstrcpyW(strDevTyp, L"CDAUDIO");
1786 1787
	}
    }
1788

1789
    if (strDevTyp[0] == 0) {
1790
	FIXME("Couldn't load driver\n");
1791
	dwRet = MCIERR_INVALID_DEVICE_NAME;
1792
	goto errCleanUp;
1793
    }
1794

1795
    if (dwParam & MCI_OPEN_ALIAS) {
1796
	TRACE("Alias=%s !\n", debugstr_w(lpParms->lpstrAlias));
1797 1798 1799 1800 1801
	if (!lpParms->lpstrAlias) {
	    dwRet = MCIERR_NULL_PARAMETER_BLOCK;
	    goto errCleanUp;
	}
    }
1802

1803
    if ((dwRet = MCI_LoadMciDriver(strDevTyp, &wmd))) {
1804
	goto errCleanUp;
1805
    }
1806

1807
    if ((dwRet = MCI_FinishOpen(wmd, lpParms, dwParam))) {
1808
	TRACE("Failed to open driver (MCI_OPEN_DRIVER) [%08x], closing\n", dwRet);
1809 1810
	/* FIXME: is dwRet the correct ret code ? */
	goto errCleanUp;
1811
    }
1812 1813

    /* only handled devices fall through */
1814
    TRACE("wDevID=%04X wDeviceID=%d dwRet=%d\n", wmd->wDeviceID, lpParms->wDeviceID, dwRet);
1815
    return 0;
1816

1817
errCleanUp:
1818
    if (wmd) MCI_UnLoadMciDriver(wmd);
1819 1820 1821 1822 1823 1824
    return dwRet;
}

/**************************************************************************
 * 			MCI_Close				[internal]
 */
1825
static	DWORD MCI_Close(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
1826
{
1827 1828 1829
    DWORD		dwRet;
    LPWINE_MCIDRIVER	wmd;

1830
    TRACE("(%04x, %08X, %p)\n", wDevID, dwParam, lpParms);
1831

1832
    /* Every device must handle MCI_NOTIFY on its own. */
1833
    if ((UINT16)wDevID == (UINT16)MCI_ALL_DEVICE_ID) {
1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847
	while (MciDrivers) {
            /* Retrieve the device ID under lock, but send the message without,
             * the driver might be calling some winmm functions from another
             * thread before being fully stopped.
             */
            EnterCriticalSection(&WINMM_cs);
            if (!MciDrivers)
            {
                LeaveCriticalSection(&WINMM_cs);
                break;
            }
            wDevID = MciDrivers->wDeviceID;
            LeaveCriticalSection(&WINMM_cs);
            MCI_Close(wDevID, dwParam, lpParms);
1848
	}
1849 1850 1851 1852 1853
	return 0;
    }

    if (!(wmd = MCI_GetDriver(wDevID))) {
	return MCIERR_INVALID_DEVICE_ID;
1854 1855
    }

1856 1857 1858
    if(wmd->CreatorThread != GetCurrentThreadId())
        return MCIERR_INVALID_DEVICE_NAME;

1859
    dwRet = MCI_SendCommandFrom32(wDevID, MCI_CLOSE_DRIVER, dwParam, (DWORD_PTR)lpParms);
1860

1861
    MCI_UnLoadMciDriver(wmd);
1862

1863 1864 1865 1866 1867 1868
    return dwRet;
}

/**************************************************************************
 * 			MCI_WriteString				[internal]
 */
1869
static DWORD MCI_WriteString(LPWSTR lpDstStr, DWORD dstSize, LPCWSTR lpSrcStr)
1870
{
1871
    DWORD	ret = 0;
1872

1873
    if (lpSrcStr) {
1874
	if (dstSize <= lstrlenW(lpSrcStr)) {
1875 1876
	    ret = MCIERR_PARAM_OVERFLOW;
	} else {
1877
	    lstrcpyW(lpDstStr, lpSrcStr);
1878
	}
1879
    } else {
1880
	*lpDstStr = 0;
1881 1882 1883 1884 1885 1886 1887
    }
    return ret;
}

/**************************************************************************
 * 			MCI_Sysinfo				[internal]
 */
1888
static	DWORD MCI_SysInfo(UINT uDevID, DWORD dwFlags, LPMCI_SYSINFO_PARMSW lpParms)
1889
{
1890
    DWORD		ret = MCIERR_INVALID_DEVICE_ID, cnt = 0;
1891
    WCHAR		buf[2048], *s, *p;
1892
    LPWINE_MCIDRIVER	wmd;
1893
    HKEY		hKey;
1894

1895 1896
    if (lpParms == NULL)			return MCIERR_NULL_PARAMETER_BLOCK;
    if (lpParms->lpstrReturn == NULL)		return MCIERR_PARAM_OVERFLOW;
1897

1898 1899
    TRACE("(%08x, %08X, %p[num=%d, wDevTyp=%u])\n",
	  uDevID, dwFlags, lpParms, lpParms->dwNumber, lpParms->wDeviceType);
1900 1901
    if ((WORD)MCI_ALL_DEVICE_ID == LOWORD(uDevID))
	uDevID = MCI_ALL_DEVICE_ID; /* Be compatible with Win9x */
1902

1903
    switch (dwFlags & ~(MCI_SYSINFO_OPEN|MCI_NOTIFY|MCI_WAIT)) {
1904
    case MCI_SYSINFO_QUANTITY:
1905 1906 1907 1908 1909
	if (lpParms->dwRetSize < sizeof(DWORD))
	    return MCIERR_PARAM_OVERFLOW;
	/* Win9x returns 0 for 0 < uDevID < (UINT16)MCI_ALL_DEVICE_ID */
	if (uDevID == MCI_ALL_DEVICE_ID) {
	    /* wDeviceType == MCI_ALL_DEVICE_ID is not recognized. */
1910 1911
	    if (dwFlags & MCI_SYSINFO_OPEN) {
		TRACE("MCI_SYSINFO_QUANTITY: # of open MCI drivers\n");
1912 1913
		EnterCriticalSection(&WINMM_cs);
		for (wmd = MciDrivers; wmd; wmd = wmd->lpNext) {
1914
		    cnt++;
1915
		}
1916
		LeaveCriticalSection(&WINMM_cs);
1917
	    } else {
1918
		TRACE("MCI_SYSINFO_QUANTITY: # of installed MCI drivers\n");
1919
		if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, wszHklmMci,
1920
			  	   0, KEY_QUERY_VALUE, &hKey ) == ERROR_SUCCESS) {
1921
		    RegQueryInfoKeyW( hKey, 0, 0, 0, &cnt, 0, 0, 0, 0, 0, 0, 0);
1922
		    RegCloseKey( hKey );
1923
		}
1924
		if (GetPrivateProfileStringW(L"MCI", 0, L"", buf, ARRAY_SIZE(buf), L"system.ini"))
1925
		    for (s = buf; *s; s += lstrlenW(s) + 1) cnt++;
1926 1927 1928
	    }
	} else {
	    if (dwFlags & MCI_SYSINFO_OPEN) {
1929
		TRACE("MCI_SYSINFO_QUANTITY: # of open MCI drivers of type %d\n", lpParms->wDeviceType);
1930 1931
		EnterCriticalSection(&WINMM_cs);
		for (wmd = MciDrivers; wmd; wmd = wmd->lpNext) {
1932 1933
		    if (wmd->wType == lpParms->wDeviceType) cnt++;
		}
1934
		LeaveCriticalSection(&WINMM_cs);
1935
	    } else {
1936
		TRACE("MCI_SYSINFO_QUANTITY: # of installed MCI drivers of type %d\n", lpParms->wDeviceType);
1937
		FIXME("Don't know how to get # of MCI devices of a given type\n");
1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948
		/* name = LoadStringW(hWinMM32Instance, LOWORD(lpParms->wDeviceType))
		 * then lookup registry and/or system.ini for name, ignoring digits suffix */
		switch (LOWORD(lpParms->wDeviceType)) {
		case MCI_DEVTYPE_CD_AUDIO:
		case MCI_DEVTYPE_WAVEFORM_AUDIO:
		case MCI_DEVTYPE_SEQUENCER:
		    cnt = 1;
		    break;
		default: /* "digitalvideo" gets 0 because it's not in the registry */
		    cnt = 0;
		}
1949 1950
	    }
	}
1951
	*(DWORD*)lpParms->lpstrReturn = cnt;
1952
	TRACE("(%d) => '%d'\n", lpParms->dwNumber, *(DWORD*)lpParms->lpstrReturn);
1953
	ret = MCI_INTEGER_RETURNED;
1954
	/* return ret; Only Win9x sends a notification in this case. */
1955 1956
	break;
    case MCI_SYSINFO_INSTALLNAME:
1957
	TRACE("MCI_SYSINFO_INSTALLNAME\n");
1958
	if ((wmd = MCI_GetDriver(uDevID))) {
1959
	    ret = MCI_WriteString(lpParms->lpstrReturn, lpParms->dwRetSize,
1960
				  wmd->lpstrDeviceType);
1961
	} else {
1962 1963
	    ret = (uDevID == MCI_ALL_DEVICE_ID)
		? MCIERR_CANNOT_USE_ALL : MCIERR_INVALID_DEVICE_NAME;
1964
	}
1965
	TRACE("(%d) => %s\n", lpParms->dwNumber, debugstr_w(lpParms->lpstrReturn));
1966 1967
	break;
    case MCI_SYSINFO_NAME:
1968
	s = NULL;
1969
	if (dwFlags & MCI_SYSINFO_OPEN) {
1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985
	    /* Win9x returns 0 for 0 < uDevID < (UINT16)MCI_ALL_DEVICE_ID */
	    TRACE("MCI_SYSINFO_NAME: nth alias of type %d\n",
		  uDevID == MCI_ALL_DEVICE_ID ? MCI_ALL_DEVICE_ID : lpParms->wDeviceType);
	    EnterCriticalSection(&WINMM_cs);
	    for (wmd = MciDrivers; wmd; wmd = wmd->lpNext) {
		/* wDeviceType == MCI_ALL_DEVICE_ID is not recognized. */
		if (uDevID == MCI_ALL_DEVICE_ID ||
		    lpParms->wDeviceType == wmd->wType) {
		    cnt++;
		    if (cnt == lpParms->dwNumber) {
			s = wmd->lpstrAlias;
			break;
		    }
		}
	    }
	    LeaveCriticalSection(&WINMM_cs);
1986
	    ret = s ? MCI_WriteString(lpParms->lpstrReturn, lpParms->dwRetSize, s) : MCIERR_OUTOFRANGE;
1987 1988
	} else if (MCI_ALL_DEVICE_ID == uDevID) {
	    TRACE("MCI_SYSINFO_NAME: device #%d\n", lpParms->dwNumber);
1989 1990 1991 1992 1993
	    if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, wszHklmMci, 0, 
                               KEY_QUERY_VALUE, &hKey ) == ERROR_SUCCESS) {
		if (RegQueryInfoKeyW( hKey, 0, 0, 0, &cnt, 
                                      0, 0, 0, 0, 0, 0, 0) == ERROR_SUCCESS && 
                    lpParms->dwNumber <= cnt) {
1994
		    DWORD bufLen = ARRAY_SIZE(buf);
1995 1996 1997
		    if (RegEnumKeyExW(hKey, lpParms->dwNumber - 1, 
                                      buf, &bufLen, 0, 0, 0, 0) == ERROR_SUCCESS)
                        s = buf;
1998 1999 2000 2001
		}
	        RegCloseKey( hKey );
	    }
	    if (!s) {
2002
		if (GetPrivateProfileStringW(L"MCI", 0, L"", buf, ARRAY_SIZE(buf), L"system.ini")) {
2003
		    for (p = buf; *p; p += lstrlenW(p) + 1, cnt++) {
2004
                        TRACE("%d: %s\n", cnt, debugstr_w(p));
2005 2006 2007 2008 2009 2010 2011
			if (cnt == lpParms->dwNumber - 1) {
			    s = p;
			    break;
			}
		    }
		}
	    }
2012
	    ret = s ? MCI_WriteString(lpParms->lpstrReturn, lpParms->dwRetSize, s) : MCIERR_OUTOFRANGE;
2013 2014 2015 2016 2017 2018 2019 2020
	} else {
	    FIXME("MCI_SYSINFO_NAME: nth device of type %d\n", lpParms->wDeviceType);
	    /* Cheating: what is asked for is the nth device from the registry. */
	    if (1 != lpParms->dwNumber || /* Handle only one of each kind. */
		lpParms->wDeviceType < MCI_DEVTYPE_FIRST || lpParms->wDeviceType > MCI_DEVTYPE_LAST)
		ret = MCIERR_OUTOFRANGE;
	    else {
		LoadStringW(hWinMM32Instance, LOWORD(lpParms->wDeviceType),
2021
			    lpParms->lpstrReturn, lpParms->dwRetSize);
2022 2023
		ret = 0;
	    }
2024
	}
2025
	TRACE("(%d) => %s\n", lpParms->dwNumber, debugstr_w(lpParms->lpstrReturn));
2026 2027
	break;
    default:
2028
	TRACE("Unsupported flag value=%08x\n", dwFlags);
2029
	ret = MCIERR_UNRECOGNIZED_KEYWORD;
2030
    }
2031 2032
    if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
	mciDriverNotify((HWND)lpParms->dwCallback, uDevID, MCI_NOTIFY_SUCCESSFUL);
2033 2034 2035
    return ret;
}

2036 2037 2038
/**************************************************************************
 * 			MCI_Break				[internal]
 */
2039
static	DWORD MCI_Break(UINT wDevID, DWORD dwFlags, LPMCI_BREAK_PARMS lpParms)
2040
{
2041 2042
    DWORD dwRet;

2043 2044
    if (lpParms == NULL)
        return MCIERR_NULL_PARAMETER_BLOCK;
2045

2046 2047 2048 2049 2050
    TRACE("(%08x, %08X, vkey %04X, hwnd %p)\n", wDevID, dwFlags,
          lpParms->nVirtKey, lpParms->hwndBreak);

    dwRet = MCI_SendCommandFrom32(wDevID, MCI_BREAK, dwFlags, (DWORD_PTR)lpParms);
    if (!dwRet && (dwFlags & MCI_NOTIFY))
2051
        mciDriverNotify((HWND)lpParms->dwCallback, wDevID, MCI_NOTIFY_SUCCESSFUL);
2052
    return dwRet;
2053
}
2054

2055 2056 2057
/**************************************************************************
 * 			MCI_Sound				[internal]
 */
2058
static	DWORD MCI_Sound(UINT wDevID, DWORD dwFlags, LPMCI_SOUND_PARMSW lpParms)
2059
{
Jörg Höhle's avatar
Jörg Höhle committed
2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071
    DWORD	dwRet;

    if (dwFlags & MCI_SOUND_NAME) {
	if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
	else dwRet = PlaySoundW(lpParms->lpstrSoundName, NULL,
				SND_ALIAS    | (dwFlags & MCI_WAIT ? SND_SYNC : SND_ASYNC))
		? 0 : MCIERR_HARDWARE;
    } else   dwRet = PlaySoundW((LPCWSTR)SND_ALIAS_SYSTEMDEFAULT, NULL,
				SND_ALIAS_ID | (dwFlags & MCI_WAIT ? SND_SYNC : SND_ASYNC))
		? 0 : MCIERR_HARDWARE;

    if (!dwRet && lpParms && (dwFlags & MCI_NOTIFY))
2072
        mciDriverNotify((HWND)lpParms->dwCallback, wDevID, MCI_NOTIFY_SUCCESSFUL);
2073 2074 2075
    return dwRet;
}

2076 2077 2078
/**************************************************************************
 * 			MCI_SendCommand				[internal]
 */
2079
DWORD	MCI_SendCommand(UINT wDevID, UINT16 wMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
2080 2081 2082 2083 2084
{
    DWORD		dwRet = MCIERR_UNRECOGNIZED_COMMAND;

    switch (wMsg) {
    case MCI_OPEN:
2085
        dwRet = MCI_Open(dwParam1, (LPMCI_OPEN_PARMSW)dwParam2);
2086 2087
	break;
    case MCI_CLOSE:
2088
        dwRet = MCI_Close(wDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
2089 2090
	break;
    case MCI_SYSINFO:
2091
        dwRet = MCI_SysInfo(wDevID, dwParam1, (LPMCI_SYSINFO_PARMSW)dwParam2);
2092 2093
	break;
    case MCI_BREAK:
2094
        dwRet = MCI_Break(wDevID, dwParam1, (LPMCI_BREAK_PARMS)dwParam2);
2095 2096
	break;
    case MCI_SOUND:
2097
        dwRet = MCI_Sound(wDevID, dwParam1, (LPMCI_SOUND_PARMSW)dwParam2);
2098 2099
	break;
    default:
2100
      if ((UINT16)wDevID == (UINT16)MCI_ALL_DEVICE_ID) {
2101 2102 2103
	    FIXME("unhandled MCI_ALL_DEVICE_ID\n");
	    dwRet = MCIERR_CANNOT_USE_ALL;
	} else {
2104
	    dwRet = MCI_SendCommandFrom32(wDevID, wMsg, dwParam1, dwParam2);
2105
	}
2106 2107 2108 2109 2110
	break;
    }
    return dwRet;
}

2111 2112 2113
/**************************************************************************
 * 				MCI_CleanUp			[internal]
 *
2114
 * Some MCI commands need to be cleaned-up (when not called from
2115
 * mciSendString), because MCI drivers return extra information for string
2116
 * transformation. This function gets rid of them.
2117
 */
2118
static LRESULT	MCI_CleanUp(LRESULT dwRet, UINT wMsg, DWORD_PTR dwParam2)
2119
{
2120
    if (LOWORD(dwRet))
2121 2122
	return LOWORD(dwRet);

2123 2124 2125 2126 2127 2128 2129
    switch (wMsg) {
    case MCI_GETDEVCAPS:
	switch (dwRet & 0xFFFF0000ul) {
	case 0:
	case MCI_COLONIZED3_RETURN:
	case MCI_COLONIZED4_RETURN:
	case MCI_INTEGER_RETURNED:
2130 2131 2132 2133
	    /* nothing to do */
	    break;
	case MCI_RESOURCE_RETURNED:
	case MCI_RESOURCE_RETURNED|MCI_RESOURCE_DRIVER:
2134
	    {
2135
		LPMCI_GETDEVCAPS_PARMS	lmgp;
2136

2137
		lmgp = (LPMCI_GETDEVCAPS_PARMS)dwParam2;
2138
		TRACE("Changing %08x to %08x\n", lmgp->dwReturn, LOWORD(lmgp->dwReturn));
2139
		lmgp->dwReturn = LOWORD(lmgp->dwReturn);
2140
	    }
2141 2142
	    break;
	default:
2143
	    FIXME("Unsupported value for hiword (%04x) returned by DriverProc(%s)\n",
2144
		  HIWORD(dwRet), MCI_MessageToString(wMsg));
2145 2146 2147 2148 2149 2150 2151 2152
	}
	break;
    case MCI_STATUS:
	switch (dwRet & 0xFFFF0000ul) {
	case 0:
	case MCI_COLONIZED3_RETURN:
	case MCI_COLONIZED4_RETURN:
	case MCI_INTEGER_RETURNED:
2153 2154 2155 2156
	    /* nothing to do */
	    break;
	case MCI_RESOURCE_RETURNED:
	case MCI_RESOURCE_RETURNED|MCI_RESOURCE_DRIVER:
2157
	    {
2158
		LPMCI_STATUS_PARMS	lsp;
2159

2160 2161
		lsp = (LPMCI_STATUS_PARMS)dwParam2;
		TRACE("Changing %08lx to %08x\n", lsp->dwReturn, LOWORD(lsp->dwReturn));
2162 2163 2164 2165
		lsp->dwReturn = LOWORD(lsp->dwReturn);
	    }
	    break;
	default:
2166
	    FIXME("Unsupported value for hiword (%04x) returned by DriverProc(%s)\n",
2167 2168 2169 2170 2171 2172 2173
		  HIWORD(dwRet), MCI_MessageToString(wMsg));
	}
	break;
    case MCI_SYSINFO:
	switch (dwRet & 0xFFFF0000ul) {
	case 0:
	case MCI_INTEGER_RETURNED:
2174
	    /* nothing to do */
2175 2176
	    break;
	default:
2177
	    FIXME("Unsupported value for hiword (%04x)\n", HIWORD(dwRet));
2178 2179 2180
	}
	break;
    default:
2181
	if (HIWORD(dwRet)) {
2182
	    FIXME("Got non null hiword for dwRet=0x%08lx for command %s\n",
2183
		  dwRet, MCI_MessageToString(wMsg));
2184
	}
2185 2186
	break;
    }
2187
    return LOWORD(dwRet);
2188
}
2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199

/**************************************************************************
 * 				mciGetErrorStringW		[WINMM.@]
 */
BOOL WINAPI mciGetErrorStringW(MCIERROR wError, LPWSTR lpstrBuffer, UINT uLength)
{
    BOOL		ret = FALSE;

    if (lpstrBuffer != NULL && uLength > 0 &&
	wError >= MCIERR_BASE && wError <= MCIERR_CUSTOM_DRIVER_BASE) {

2200
	if (LoadStringW(hWinMM32Instance, wError, lpstrBuffer, uLength) > 0) {
2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216
	    ret = TRUE;
	}
    }
    return ret;
}

/**************************************************************************
 * 				mciGetErrorStringA		[WINMM.@]
 */
BOOL WINAPI mciGetErrorStringA(MCIERROR dwError, LPSTR lpstrBuffer, UINT uLength)
{
    BOOL		ret = FALSE;

    if (lpstrBuffer != NULL && uLength > 0 &&
	dwError >= MCIERR_BASE && dwError <= MCIERR_CUSTOM_DRIVER_BASE) {

2217
	if (LoadStringA(hWinMM32Instance, dwError, lpstrBuffer, uLength) > 0) {
2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236
	    ret = TRUE;
	}
    }
    return ret;
}

/**************************************************************************
 *			mciDriverNotify				[WINMM.@]
 */
BOOL WINAPI mciDriverNotify(HWND hWndCallBack, MCIDEVICEID wDevID, UINT wStatus)
{
    TRACE("(%p, %04x, %04X)\n", hWndCallBack, wDevID, wStatus);

    return PostMessageW(hWndCallBack, MM_MCINOTIFY, wStatus, wDevID);
}

/**************************************************************************
 * 			mciGetDriverData			[WINMM.@]
 */
2237
DWORD_PTR WINAPI mciGetDriverData(MCIDEVICEID uDeviceID)
2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255
{
    LPWINE_MCIDRIVER	wmd;

    TRACE("(%04x)\n", uDeviceID);

    wmd = MCI_GetDriver(uDeviceID);

    if (!wmd) {
	WARN("Bad uDeviceID\n");
	return 0L;
    }

    return wmd->dwPrivate;
}

/**************************************************************************
 * 			mciSetDriverData			[WINMM.@]
 */
2256
BOOL WINAPI mciSetDriverData(MCIDEVICEID uDeviceID, DWORD_PTR data)
2257 2258 2259
{
    LPWINE_MCIDRIVER	wmd;

2260
    TRACE("(%04x, %08lx)\n", uDeviceID, data);
2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283

    wmd = MCI_GetDriver(uDeviceID);

    if (!wmd) {
	WARN("Bad uDeviceID\n");
	return FALSE;
    }

    wmd->dwPrivate = data;
    return TRUE;
}

/**************************************************************************
 * 				mciSendCommandW			[WINMM.@]
 *
 */
DWORD WINAPI mciSendCommandW(MCIDEVICEID wDevID, UINT wMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
    DWORD	dwRet;

    TRACE("(%08x, %s, %08lx, %08lx)\n",
	  wDevID, MCI_MessageToString(wMsg), dwParam1, dwParam2);

2284
    dwRet = MCI_SendCommand(wDevID, wMsg, dwParam1, dwParam2);
2285
    dwRet = MCI_CleanUp(dwRet, wMsg, dwParam2);
2286
    TRACE("=> %08x\n", dwRet);
2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304
    return dwRet;
}

/**************************************************************************
 * 				mciSendCommandA			[WINMM.@]
 */
DWORD WINAPI mciSendCommandA(MCIDEVICEID wDevID, UINT wMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
    DWORD ret;
    int mapped;

    TRACE("(%08x, %s, %08lx, %08lx)\n",
	  wDevID, MCI_MessageToString(wMsg), dwParam1, dwParam2);

    mapped = MCI_MapMsgAtoW(wMsg, dwParam1, &dwParam2);
    if (mapped == -1)
    {
        FIXME("message %04x mapping failed\n", wMsg);
2305
        return MCIERR_OUT_OF_MEMORY;
2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339
    }
    ret = mciSendCommandW(wDevID, wMsg, dwParam1, dwParam2);
    if (mapped)
        MCI_UnmapMsgAtoW(wMsg, dwParam1, dwParam2, ret);
    return ret;
}

/**************************************************************************
 * 				mciGetDeviceIDA    		[WINMM.@]
 */
UINT WINAPI mciGetDeviceIDA(LPCSTR lpstrName)
{
    LPWSTR w = MCI_strdupAtoW(lpstrName);
    UINT ret = MCIERR_OUT_OF_MEMORY;

    if (w)
    {
        ret = mciGetDeviceIDW(w);
        HeapFree(GetProcessHeap(), 0, w);
    }
    return ret;
}

/**************************************************************************
 * 				mciGetDeviceIDW		       	[WINMM.@]
 */
UINT WINAPI mciGetDeviceIDW(LPCWSTR lpwstrName)
{
    return MCI_GetDriverFromString(lpwstrName); 
}

/**************************************************************************
 * 				MCI_DefYieldProc	       	[internal]
 */
2340
static UINT WINAPI MCI_DefYieldProc(MCIDEVICEID wDevID, DWORD data)
2341 2342
{
    INT16	ret;
2343
    MSG		msg;
2344

2345
    TRACE("(0x%04x, 0x%08x)\n", wDevID, data);
2346 2347 2348

    if ((HIWORD(data) != 0 && HWND_16(GetActiveWindow()) != HIWORD(data)) ||
	(GetAsyncKeyState(LOWORD(data)) & 1) == 0) {
2349
        PeekMessageW(&msg, 0, 0, 0, PM_REMOVE | PM_QS_SENDMESSAGE);
2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365
	ret = 0;
    } else {
	msg.hwnd = HWND_32(HIWORD(data));
	while (!PeekMessageW(&msg, msg.hwnd, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE));
	ret = -1;
    }
    return ret;
}

/**************************************************************************
 * 				mciSetYieldProc			[WINMM.@]
 */
BOOL WINAPI mciSetYieldProc(MCIDEVICEID uDeviceID, YIELDPROC fpYieldProc, DWORD dwYieldData)
{
    LPWINE_MCIDRIVER	wmd;

2366
    TRACE("(%u, %p, %08x)\n", uDeviceID, fpYieldProc, dwYieldData);
2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402

    if (!(wmd = MCI_GetDriver(uDeviceID))) {
	WARN("Bad uDeviceID\n");
	return FALSE;
    }

    wmd->lpfnYieldProc = fpYieldProc;
    wmd->dwYieldData   = dwYieldData;

    return TRUE;
}

/**************************************************************************
 * 				mciGetDeviceIDFromElementIDA	[WINMM.@]
 */
UINT WINAPI mciGetDeviceIDFromElementIDA(DWORD dwElementID, LPCSTR lpstrType)
{
    LPWSTR w = MCI_strdupAtoW(lpstrType);
    UINT ret = 0;

    if (w)
    {
        ret = mciGetDeviceIDFromElementIDW(dwElementID, w);
        HeapFree(GetProcessHeap(), 0, w);
    }
    return ret;
}

/**************************************************************************
 * 				mciGetDeviceIDFromElementIDW	[WINMM.@]
 */
UINT WINAPI mciGetDeviceIDFromElementIDW(DWORD dwElementID, LPCWSTR lpstrType)
{
    /* FIXME: that's rather strange, there is no
     * mciGetDeviceIDFromElementID32A in winmm.spec
     */
2403
    FIXME("(%u, %s) stub\n", dwElementID, debugstr_w(lpstrType));
2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423
    return 0;
}

/**************************************************************************
 * 				mciGetYieldProc			[WINMM.@]
 */
YIELDPROC WINAPI mciGetYieldProc(MCIDEVICEID uDeviceID, DWORD* lpdwYieldData)
{
    LPWINE_MCIDRIVER	wmd;

    TRACE("(%u, %p)\n", uDeviceID, lpdwYieldData);

    if (!(wmd = MCI_GetDriver(uDeviceID))) {
	WARN("Bad uDeviceID\n");
	return NULL;
    }
    if (!wmd->lpfnYieldProc) {
	WARN("No proc set\n");
	return NULL;
    }
2424
    if (lpdwYieldData) *lpdwYieldData = wmd->dwYieldData;
2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435
    return wmd->lpfnYieldProc;
}

/**************************************************************************
 * 				mciGetCreatorTask		[WINMM.@]
 */
HTASK WINAPI mciGetCreatorTask(MCIDEVICEID uDeviceID)
{
    LPWINE_MCIDRIVER	wmd;
    HTASK ret = 0;

2436
    if ((wmd = MCI_GetDriver(uDeviceID))) ret = (HTASK)(DWORD_PTR)wmd->CreatorThread;
2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451

    TRACE("(%u) => %p\n", uDeviceID, ret);
    return ret;
}

/**************************************************************************
 * 			mciDriverYield				[WINMM.@]
 */
UINT WINAPI mciDriverYield(MCIDEVICEID uDeviceID)
{
    LPWINE_MCIDRIVER	wmd;
    UINT		ret = 0;

    TRACE("(%04x)\n", uDeviceID);

2452
    if (!(wmd = MCI_GetDriver(uDeviceID)) || !wmd->lpfnYieldProc) {
2453 2454
        MSG msg;
        PeekMessageW(&msg, 0, 0, 0, PM_REMOVE | PM_QS_SENDMESSAGE);
2455 2456 2457 2458 2459 2460
    } else {
	ret = wmd->lpfnYieldProc(uDeviceID, wmd->dwYieldData);
    }

    return ret;
}