mci.c 72.8 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 40 41 42
 * - 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
 */

/* to be cross checked:
 * - heapalloc for *sizeof(WCHAR) when needed
 * - size of string in WCHAR or bytes? (#chars for MCI_INFO, #bytes for MCI_SYSINFO)
43 44
 */

Steven Edwards's avatar
Steven Edwards committed
45 46 47
#include "config.h"
#include "wine/port.h"

48
#include <stdlib.h>
49
#include <stdarg.h>
50
#include <stdio.h>
51 52
#include <string.h>

53
#include "windef.h"
54
#include "winbase.h"
55
#include "wingdi.h"
56
#include "winreg.h"
57
#include "mmsystem.h"
58
#include "winuser.h"
59
#include "winnls.h"
60
#include "winreg.h"
61
#include "wownt32.h"
62 63

#include "digitalv.h"
64
#include "winemm.h"
65

66
#include "wine/debug.h"
67
#include "wine/unicode.h"
68

69
WINE_DEFAULT_DEBUG_CHANNEL(mci);
70

71 72 73 74
WINMM_MapType  (*pFnMciMapMsg16To32W)  (WORD,WORD,DWORD,DWORD_PTR*) = NULL;
WINMM_MapType  (*pFnMciUnMapMsg16To32W)(WORD,WORD,DWORD,DWORD_PTR) = NULL;
WINMM_MapType  (*pFnMciMapMsg32WTo16)  (WORD,WORD,DWORD,DWORD_PTR*) = NULL;
WINMM_MapType  (*pFnMciUnMapMsg32WTo16)(WORD,WORD,DWORD,DWORD_PTR) = NULL;
75 76 77

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

79
/* MCI settings */
80 81 82 83 84 85
static const WCHAR wszHklmMci  [] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s',' ','N','T','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','M','C','I',0};
static const WCHAR wszNull     [] = {0};
static const WCHAR wszAll      [] = {'A','L','L',0};
static const WCHAR wszMci      [] = {'M','C','I',0};
static const WCHAR wszOpen     [] = {'o','p','e','n',0};
static const WCHAR wszSystemIni[] = {'s','y','s','t','e','m','.','i','n','i',0};
86

87
/* dup a string and uppercase it */
88
inline static LPWSTR str_dup_upper( LPCWSTR str )
89
{
90 91
    INT len = (strlenW(str) + 1) * sizeof(WCHAR);
    LPWSTR p = HeapAlloc( GetProcessHeap(), 0, len );
92 93 94
    if (p)
    {
        memcpy( p, str, len );
95
        CharUpperW( p );
96 97 98 99
    }
    return p;
}

100 101 102
/**************************************************************************
 * 				MCI_GetDriver			[internal]
 */
103
LPWINE_MCIDRIVER	MCI_GetDriver(UINT16 wDevID)
104 105 106
{
    LPWINE_MCIDRIVER	wmd = 0;

107 108
    EnterCriticalSection(&WINMM_IData.cs);
    for (wmd = WINMM_IData.lpMciDrvs; wmd; wmd = wmd->lpNext) {
109 110
	if (wmd->wDeviceID == wDevID)
	    break;
111
    }
112
    LeaveCriticalSection(&WINMM_IData.cs);
113 114 115 116 117 118
    return wmd;
}

/**************************************************************************
 * 				MCI_GetDriverFromString		[internal]
 */
119
UINT	MCI_GetDriverFromString(LPCWSTR lpstrName)
120 121 122 123 124 125
{
    LPWINE_MCIDRIVER	wmd;
    UINT		ret = 0;

    if (!lpstrName)
	return 0;
126

127
    if (!strcmpiW(lpstrName, wszAll))
128
	return MCI_ALL_DEVICE_ID;
129

130 131
    EnterCriticalSection(&WINMM_IData.cs);
    for (wmd = WINMM_IData.lpMciDrvs; wmd; wmd = wmd->lpNext) {
132
	if (wmd->lpstrElementName && strcmpW(wmd->lpstrElementName, lpstrName) == 0) {
133 134 135
	    ret = wmd->wDeviceID;
	    break;
	}
136
	if (wmd->lpstrDeviceType && strcmpiW(wmd->lpstrDeviceType, lpstrName) == 0) {
137 138 139
	    ret = wmd->wDeviceID;
	    break;
	}
140
	if (wmd->lpstrAlias && strcmpiW(wmd->lpstrAlias, lpstrName) == 0) {
141 142
	    ret = wmd->wDeviceID;
	    break;
143 144
	}
    }
145
    LeaveCriticalSection(&WINMM_IData.cs);
146

147 148 149 150 151 152
    return ret;
}

/**************************************************************************
 * 			MCI_MessageToString			[internal]
 */
153
const char* MCI_MessageToString(UINT wMsg)
154 155
{
    static char buffer[100];
156

157
#define CASE(s) case (s): return #s
158

159
    switch (wMsg) {
160 161 162 163 164 165 166 167 168 169 170 171 172
        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);
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
	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;
    }
}

225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
LPWSTR MCI_strdupAtoW( LPCSTR str )
{
    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;
}

LPSTR MCI_strdupWtoA( LPCWSTR str )
{
    LPSTR ret;
    INT len;

    if (!str) return NULL;
    len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL );
    ret = HeapAlloc( GetProcessHeap(), 0, len );
    if (ret) WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
    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_SOUND:
    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:
281 282 283 284 285
    case MCI_MONITOR:
    case MCI_SETAUDIO:
    case MCI_SIGNAL:
    case MCI_SETVIDEO:
    case MCI_LIST:
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
        return 0;

    case MCI_OPEN:
        {
            MCI_OPEN_PARMSA *mci_openA = (MCI_OPEN_PARMSA*)*dwParam2;
            MCI_OPEN_PARMSW *mci_openW;
            DWORD_PTR *ptr;

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

            *ptr++ = *dwParam2; /* save the previous pointer */
            *dwParam2 = (DWORD_PTR)ptr;
            mci_openW = (MCI_OPEN_PARMSW *)ptr;

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

            if (dwParam1 & MCI_OPEN_TYPE)
            {
                if (dwParam1 & MCI_OPEN_TYPE_ID)
307
                    mci_openW->lpstrDeviceType = (LPCWSTR)mci_openA->lpstrDeviceType;
308 309 310 311 312 313
                else
                    mci_openW->lpstrDeviceType = MCI_strdupAtoW(mci_openA->lpstrDeviceType);
            }
            if (dwParam1 & MCI_OPEN_ELEMENT)
            {
                if (dwParam1 & MCI_OPEN_ELEMENT_ID)
314
                    mci_openW->lpstrElementName = (LPCWSTR)mci_openA->lpstrElementName;
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
                else
                    mci_openW->lpstrElementName = MCI_strdupAtoW(mci_openA->lpstrElementName);
            }
            if (dwParam1 & MCI_OPEN_ALIAS)
                mci_openW->lpstrAlias = MCI_strdupAtoW(mci_openA->lpstrAlias);
            /* FIXME: this is only needed for specific types of MCI devices, and
             * may cause a segfault if the two DWORD:s don't exist at the end of 
             * mci_openA
             */
            memcpy(mci_openW + 1, mci_openA + 1, 2 * sizeof(DWORD));
        }
        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:
        {
            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;

            mci_sysinfoW->dwRetSize = mci_sysinfoA->dwRetSize;
            mci_sysinfoW->lpstrReturn = HeapAlloc(GetProcessHeap(), 0, mci_sysinfoW->dwRetSize);
            mci_sysinfoW->dwNumber = mci_sysinfoA->dwNumber;
            mci_sysinfoW->wDeviceType = mci_sysinfoA->wDeviceType;
            return 1;
        }
    case MCI_INFO:
        {
            MCI_INFO_PARMSA *mci_infoA = (MCI_INFO_PARMSA *)*dwParam2;
            MCI_INFO_PARMSW *mci_infoW;
            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;
            mci_infoW = (MCI_INFO_PARMSW *)ptr;

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

            mci_infoW->dwRetSize = mci_infoA->dwRetSize * sizeof(WCHAR); /* it's not the same as SYSINFO !!! */
            mci_infoW->lpstrReturn = HeapAlloc(GetProcessHeap(), 0, mci_infoW->dwRetSize);
            return 1;
        }
    case MCI_SAVE:
        {
            MCI_SAVE_PARMSA *mci_saveA = (MCI_SAVE_PARMSA *)*dwParam2;
            MCI_SAVE_PARMSW *mci_saveW;

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

            *dwParam2 = (DWORD_PTR)mci_saveW;
            if (dwParam1 & MCI_NOTIFY)
                mci_saveW->dwCallback = mci_saveA->dwCallback;
            mci_saveW->lpfilename = MCI_strdupAtoW(mci_saveA->lpfilename);
            return 1;
        }
    case MCI_LOAD:
        {
            MCI_LOAD_PARMSA *mci_loadA = (MCI_LOAD_PARMSA *)*dwParam2;
            MCI_LOAD_PARMSW *mci_loadW;

            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);
            return 1;
        }

    case MCI_ESCAPE:
        {
            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;
        }
    default:
        FIXME("Message %s needs translation\n", MCI_MessageToString(msg));
        return -1;
    }
}

static DWORD MCI_UnmapMsgAtoW(UINT msg, DWORD_PTR dwParam1, DWORD_PTR dwParam2,
                              DWORD result)
{
    switch (msg)
    {
    case MCI_OPEN:
        {
            DWORD_PTR *ptr = (DWORD_PTR *)dwParam2 - 1;
            MCI_OPEN_PARMSA *mci_openA = (MCI_OPEN_PARMSA *)*ptr;
            MCI_OPEN_PARMSW *mci_openW = (MCI_OPEN_PARMSW *)(ptr + 1);

            mci_openA->wDeviceID = mci_openW->wDeviceID;

            if (dwParam1 & MCI_OPEN_TYPE)
            {
                if (!(dwParam1 & MCI_OPEN_TYPE_ID))
459
                    HeapFree(GetProcessHeap(), 0, (LPWSTR)mci_openW->lpstrDeviceType);
460 461 462 463
            }
            if (dwParam1 & MCI_OPEN_ELEMENT)
            {
                if (!(dwParam1 & MCI_OPEN_ELEMENT_ID))
464
                    HeapFree(GetProcessHeap(), 0, (LPWSTR)mci_openW->lpstrElementName);
465 466
            }
            if (dwParam1 & MCI_OPEN_ALIAS)
467
                HeapFree(GetProcessHeap(), 0, (LPWSTR)mci_openW->lpstrAlias);
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
            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:
        {
            DWORD_PTR *ptr = (DWORD_PTR *)dwParam2 - 1;
            MCI_SYSINFO_PARMSA *mci_sysinfoA = (MCI_SYSINFO_PARMSA *)*ptr;
            MCI_SYSINFO_PARMSW *mci_sysinfoW = (MCI_SYSINFO_PARMSW *)(ptr + 1);

            if (!result)
            {
                mci_sysinfoA->dwNumber = mci_sysinfoW->dwNumber;
                mci_sysinfoA->wDeviceType = mci_sysinfoW->wDeviceType;
                if (dwParam1 & MCI_SYSINFO_QUANTITY)
                    *(DWORD*)mci_sysinfoA->lpstrReturn = *(DWORD*)mci_sysinfoW->lpstrReturn;
                else
                    WideCharToMultiByte(CP_ACP, 0,
                                        mci_sysinfoW->lpstrReturn, mci_sysinfoW->dwRetSize,
                                        mci_sysinfoA->lpstrReturn, mci_sysinfoA->dwRetSize,
                                        NULL, NULL);
            }

            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;
            MCI_INFO_PARMSW *mci_infoW = (MCI_INFO_PARMSW *)(ptr + 1);

            if (!result)
            {
                WideCharToMultiByte(CP_ACP, 0,
                                    mci_infoW->lpstrReturn, mci_infoW->dwRetSize / sizeof(WCHAR),
                                    mci_infoA->lpstrReturn, mci_infoA->dwRetSize,
                                    NULL, NULL);
            }

            HeapFree(GetProcessHeap(), 0, mci_infoW->lpstrReturn);
            HeapFree(GetProcessHeap(), 0, ptr);
        }
        break;
    case MCI_SAVE:
        {
            MCI_SAVE_PARMSW *mci_saveW = (MCI_SAVE_PARMSW *)dwParam2;

            HeapFree(GetProcessHeap(), 0, (void*)mci_saveW->lpfilename);
            HeapFree(GetProcessHeap(), 0, mci_saveW);
        }
        break;
    case MCI_LOAD:
        {
            MCI_LOAD_PARMSW *mci_loadW = (MCI_LOAD_PARMSW *)dwParam2;

            HeapFree(GetProcessHeap(), 0, (void*)mci_loadW->lpfilename);
            HeapFree(GetProcessHeap(), 0, mci_loadW);
        }
        break;
    case MCI_ESCAPE:
        {
            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;
    }

    return result;
}

555 556 557
/**************************************************************************
 * 				MCI_GetDevTypeFromFileName	[internal]
 */
558
static	DWORD	MCI_GetDevTypeFromFileName(LPCWSTR fileName, LPWSTR buf, UINT len)
559
{
560
    LPCWSTR	tmp;
561
    HKEY	hKey;
562 563 564 565 566
    static const WCHAR keyW[] = {'S','O','F','T','W','A','R','E','\\','M','i','c','r','o','s','o','f','t','\\',
                                 'W','i','n','d','o','w','s',' ','N','T','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
                                 'M','C','I',' ','E','x','t','e','n','s','i','o','n','s',0};
    if ((tmp = strrchrW(fileName, '.'))) {
	if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, keyW,
567 568
			   0, KEY_QUERY_VALUE, &hKey ) == ERROR_SUCCESS) {
	    DWORD dwLen = len;
569
	    LONG lRet = RegQueryValueExW( hKey, tmp + 1, 0, 0, (void*)buf, &dwLen ); 
570 571 572
	    RegCloseKey( hKey );
	    if (lRet == ERROR_SUCCESS) return 0;
        }
573
	TRACE("No ...\\MCI Extensions entry for %s found.\n", debugstr_w(tmp));
574 575 576 577 578 579 580 581
    }
    return MCIERR_EXTENSION_NOT_FOUND;
}

#define	MAX_MCICMDTABLE			20
#define MCI_COMMAND_TABLE_NOT_LOADED	0xFFFE

typedef struct tagWINE_MCICMDTABLE {
582
    UINT		uDevType;
583
    const BYTE*		lpTable;
584
    UINT		nVerbs;		/* number of verbs in command table */
585
    LPCWSTR*		aVerbs;		/* array of verbs to speed up the verb look up process */
586
} WINE_MCICMDTABLE, *LPWINE_MCICMDTABLE;
587 588

static WINE_MCICMDTABLE S_MciCmdTable[MAX_MCICMDTABLE];
589 590 591 592 593 594

/**************************************************************************
 * 				MCI_IsCommandTableValid		[internal]
 */
static	BOOL		MCI_IsCommandTableValid(UINT uTbl)
{
595 596
    const BYTE* lmem;
    LPCWSTR     str;
597 598 599 600 601
    DWORD	flg;
    WORD	eid;
    int		idx = 0;
    BOOL	inCst = FALSE;

602 603
    TRACE("Dumping cmdTbl=%d [lpTable=%p devType=%d]\n",
	  uTbl, S_MciCmdTable[uTbl].lpTable, S_MciCmdTable[uTbl].uDevType);
604

605
    if (uTbl >= MAX_MCICMDTABLE || !S_MciCmdTable[uTbl].lpTable)
606 607 608 609
	return FALSE;

    lmem = S_MciCmdTable[uTbl].lpTable;
    do {
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629
        str = (LPCWSTR)lmem;
        lmem += (strlenW(str) + 1) * sizeof(WCHAR);
        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;
        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;
        }
630 631 632 633 634 635 636 637 638
    } while (eid != MCI_END_COMMAND_LIST);
    return TRUE;
}

/**************************************************************************
 * 				MCI_DumpCommandTable		[internal]
 */
static	BOOL		MCI_DumpCommandTable(UINT uTbl)
{
639 640
    const BYTE*	lmem;
    LPCWSTR	str;
641 642
    DWORD	flg;
    WORD	eid;
643

644 645 646 647 648 649 650 651
    if (!MCI_IsCommandTableValid(uTbl)) {
	ERR("Ooops: %d is not valid\n", uTbl);
	return FALSE;
    }

    lmem = S_MciCmdTable[uTbl].lpTable;
    do {
	do {
652 653
	    str = (LPCWSTR)lmem;
	    lmem += (strlenW(str) + 1) * sizeof(WCHAR);
Eric Pouech's avatar
Eric Pouech committed
654 655
	    flg = *(const DWORD*)lmem;
	    eid = *(const WORD*)(lmem + sizeof(DWORD));
656
            /* TRACE("cmd=%s %08lx %04x\n", debugstr_w(str), flg, eid); */
657 658
	    lmem += sizeof(DWORD) + sizeof(WORD);
	} while (eid != MCI_END_COMMAND && eid != MCI_END_COMMAND_LIST);
659
        /* EPP TRACE(" => end of command%s\n", (eid == MCI_END_COMMAND_LIST) ? " list" : ""); */
660 661 662 663 664 665 666 667
    } while (eid != MCI_END_COMMAND_LIST);
    return TRUE;
}


/**************************************************************************
 * 				MCI_GetCommandTable		[internal]
 */
668
static	UINT		MCI_GetCommandTable(UINT uDevType)
669 670
{
    UINT	uTbl;
671 672
    WCHAR	buf[32];
    LPCWSTR	str = NULL;
673

674 675
    /* first look up existing for existing devType */
    for (uTbl = 0; uTbl < MAX_MCICMDTABLE; uTbl++) {
676
	if (S_MciCmdTable[uTbl].lpTable && S_MciCmdTable[uTbl].uDevType == uDevType)
677
	    return uTbl;
678 679 680 681
    }

    /* well try to load id */
    if (uDevType >= MCI_DEVTYPE_FIRST && uDevType <= MCI_DEVTYPE_LAST) {
682
	if (LoadStringW(WINMM_IData.hWinMM32Instance, uDevType, buf, sizeof(buf) / sizeof(WCHAR))) {
683 684 685
	    str = buf;
	}
    } else if (uDevType == 0) {
686 687
        static const WCHAR wszCore[] = {'C','O','R','E',0};
	str = wszCore;
688 689 690
    }
    uTbl = MCI_NO_COMMAND_TABLE;
    if (str) {
691
	HRSRC 	hRsrc = FindResourceW(WINMM_IData.hWinMM32Instance, str, (LPCWSTR)RT_RCDATA);
692 693
	HANDLE	hMem = 0;

694
	if (hRsrc) hMem = LoadResource(WINMM_IData.hWinMM32Instance, hRsrc);
695
	if (hMem) {
696
	    uTbl = MCI_SetCommandTable(LockResource(hMem), uDevType);
697
	} else {
698
	    WARN("No command table found in resource %p[%s]\n",
699
		 WINMM_IData.hWinMM32Instance, debugstr_w(str));
700 701 702 703 704 705 706 707 708
	}
    }
    TRACE("=> %d\n", uTbl);
    return uTbl;
}

/**************************************************************************
 * 				MCI_SetCommandTable		[internal]
 */
709
UINT MCI_SetCommandTable(void *table, UINT uDevType)
710
{
711 712 713 714 715 716 717 718 719 720
    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;
721
	MCI_GetCommandTable(0);
722
    }
723
    TRACE("(%p, %u)\n", table, uDevType);
724
    for (uTbl = 0; uTbl < MAX_MCICMDTABLE; uTbl++) {
725
	if (!S_MciCmdTable[uTbl].lpTable) {
726 727
	    const BYTE* lmem;
	    LPCWSTR 	str;
728 729 730
	    WORD	eid;
	    WORD	count;

731
	    S_MciCmdTable[uTbl].uDevType = uDevType;
732
	    S_MciCmdTable[uTbl].lpTable = table;
733 734 735 736

	    if (TRACE_ON(mci)) {
		MCI_DumpCommandTable(uTbl);
	    }
737 738 739 740 741 742

	    /* create the verbs table */
	    /* get # of entries */
	    lmem = S_MciCmdTable[uTbl].lpTable;
	    count = 0;
	    do {
743 744
		str = (LPCWSTR)lmem;
		lmem += (strlenW(str) + 1) * sizeof(WCHAR);
Eric Pouech's avatar
Eric Pouech committed
745
		eid = *(const WORD*)(lmem + sizeof(DWORD));
746 747 748 749 750
		lmem += sizeof(DWORD) + sizeof(WORD);
		if (eid == MCI_COMMAND_HEAD)
		    count++;
	    } while (eid != MCI_END_COMMAND_LIST);

751
	    S_MciCmdTable[uTbl].aVerbs = HeapAlloc(GetProcessHeap(), 0, count * sizeof(LPCWSTR));
752 753 754 755 756
	    S_MciCmdTable[uTbl].nVerbs = count;

	    lmem = S_MciCmdTable[uTbl].lpTable;
	    count = 0;
	    do {
757 758
		str = (LPCWSTR)lmem;
		lmem += (strlenW(str) + 1) * sizeof(WCHAR);
Eric Pouech's avatar
Eric Pouech committed
759
		eid = *(const WORD*)(lmem + sizeof(DWORD));
760 761 762 763 764
		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); */
765 766 767 768 769 770 771 772 773 774
	    return uTbl;
	}
    }

    return MCI_NO_COMMAND_TABLE;
}

/**************************************************************************
 * 				MCI_DeleteCommandTable		[internal]
 */
775
BOOL	MCI_DeleteCommandTable(UINT uTbl, BOOL delete)
776
{
777
    if (uTbl >= MAX_MCICMDTABLE || !S_MciCmdTable[uTbl].lpTable)
778 779
	return FALSE;

780
    if (delete) HeapFree(GetProcessHeap(), 0, (void*)S_MciCmdTable[uTbl].lpTable);
781
    S_MciCmdTable[uTbl].lpTable = NULL;
782 783
    HeapFree(GetProcessHeap(), 0, S_MciCmdTable[uTbl].aVerbs);
    S_MciCmdTable[uTbl].aVerbs = 0;
784 785 786 787 788 789
    return TRUE;
}

/**************************************************************************
 * 				MCI_UnLoadMciDriver		[internal]
 */
790
static	BOOL	MCI_UnLoadMciDriver(LPWINE_MCIDRIVER wmd)
791
{
792
    LPWINE_MCIDRIVER*		tmp;
793 794 795 796

    if (!wmd)
	return TRUE;

797
    CloseDriver(wmd->hDriver, 0, 0);
798 799 800 801

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

802 803
    EnterCriticalSection(&WINMM_IData.cs);
    for (tmp = &WINMM_IData.lpMciDrvs; *tmp; tmp = &(*tmp)->lpNext) {
804 805 806 807 808
	if (*tmp == wmd) {
	    *tmp = wmd->lpNext;
	    break;
	}
    }
809
    LeaveCriticalSection(&WINMM_IData.cs);
810 811 812 813 814 815 816 817

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

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

819 820 821
/**************************************************************************
 * 				MCI_OpenMciDriver		[internal]
 */
822
static	BOOL	MCI_OpenMciDriver(LPWINE_MCIDRIVER wmd, LPCWSTR drvTyp, DWORD_PTR lp)
823
{
824
    WCHAR	libName[128];
825

826
    if (!DRIVER_GetLibName(drvTyp, wszMci, libName, sizeof(libName)))
827 828 829 830 831 832
	return FALSE;

    wmd->bIs32 = 0xFFFF;
    /* First load driver */
    if ((wmd->hDriver = (HDRVR)DRIVER_TryOpenDriver32(libName, lp))) {
	wmd->bIs32 = TRUE;
833
    } else if (WINMM_CheckForMMSystem() && pFnMciMapMsg32WTo16) {
834
	WINMM_MapType 	res;
835

836
	switch (res = pFnMciMapMsg32WTo16(0, DRV_OPEN, 0, &lp)) {
837
	case WINMM_MAP_MSGERROR:
838
	    TRACE("Not handled yet (DRV_OPEN)\n");
839
	    break;
840
	case WINMM_MAP_NOMEM:
841
	    TRACE("Problem mapping msg=DRV_OPEN from 32W to 16\n");
842
	    break;
843 844
	case WINMM_MAP_OK:
	case WINMM_MAP_OKMEM:
845
	    if ((wmd->hDriver = OpenDriver(drvTyp, wszMci, lp)))
846
		wmd->bIs32 = FALSE;
847
	    if (res == WINMM_MAP_OKMEM)
848
		pFnMciUnMapMsg32WTo16(0, DRV_OPEN, 0, lp);
849 850 851 852 853 854
	    break;
	}
    }
    return (wmd->bIs32 == 0xFFFF) ? FALSE : TRUE;
}

855 856 857
/**************************************************************************
 * 				MCI_LoadMciDriver		[internal]
 */
858
static	DWORD	MCI_LoadMciDriver(LPCWSTR _strDevTyp, LPWINE_MCIDRIVER* lpwmd)
859
{
860
    LPWSTR			strDevTyp = str_dup_upper(_strDevTyp);
861
    LPWINE_MCIDRIVER		wmd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wmd));
862
    MCI_OPEN_DRIVER_PARMSW	modp;
863
    DWORD			dwRet = 0;
864

865 866 867 868
    if (!wmd || !strDevTyp) {
	dwRet = MCIERR_OUT_OF_MEMORY;
	goto errCleanUp;
    }
869

870 871
    wmd->lpfnYieldProc = MCI_DefYieldProc;
    wmd->dwYieldData = VK_CANCEL;
872
    wmd->CreatorThread = GetCurrentThreadId();
873

874
    EnterCriticalSection(&WINMM_IData.cs);
875 876 877
    /* wmd must be inserted in list before sending opening the driver, coz' it
     * may want to lookup at wDevID
     */
878 879
    wmd->lpNext = WINMM_IData.lpMciDrvs;
    WINMM_IData.lpMciDrvs = wmd;
880

881 882
    for (modp.wDeviceID = MCI_MAGIC;
	 MCI_GetDriver(modp.wDeviceID) != 0;
883 884 885 886
	 modp.wDeviceID++);

    wmd->wDeviceID = modp.wDeviceID;

887
    LeaveCriticalSection(&WINMM_IData.cs);
888

889
    TRACE("wDevID=%04X\n", modp.wDeviceID);
890 891

    modp.lpstrParams = NULL;
892

893
    if (!MCI_OpenMciDriver(wmd, strDevTyp, (DWORD_PTR)&modp)) {
894 895 896
	/* silence warning if all is used... some bogus program use commands like
	 * 'open all'...
	 */
897
	if (strcmpiW(strDevTyp, wszAll) == 0) {
898 899 900 901 902
	    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",
903
		  debugstr_w(strDevTyp));
904 905
	    dwRet = MCIERR_DEVICE_NOT_INSTALLED;
	}
906
	goto errCleanUp;
907
    }
908

909 910 911 912 913 914 915 916
    /* 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;

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

920 921 922
    wmd->lpstrDeviceType = strDevTyp;
    wmd->wType = modp.wType;

923
    TRACE("mcidev=%d, uDevTyp=%04X wDeviceID=%04X !\n",
924 925 926 927
	  modp.wDeviceID, modp.wType, modp.wDeviceID);
    *lpwmd = wmd;
    return 0;
errCleanUp:
928
    MCI_UnLoadMciDriver(wmd);
929 930 931 932
    HeapFree(GetProcessHeap(), 0, strDevTyp);
    *lpwmd = 0;
    return dwRet;
}
933

934 935 936
/**************************************************************************
 * 			MCI_FinishOpen				[internal]
 */
937
static	DWORD	MCI_FinishOpen(LPWINE_MCIDRIVER wmd, LPMCI_OPEN_PARMSW lpParms,
938 939 940
			       DWORD dwParam)
{
    if (dwParam & MCI_OPEN_ELEMENT)
941
    {
942 943
        wmd->lpstrElementName = HeapAlloc(GetProcessHeap(),0,(strlenW(lpParms->lpstrElementName)+1) * sizeof(WCHAR));
        strcpyW( wmd->lpstrElementName, lpParms->lpstrElementName );
944
    }
945
    if (dwParam & MCI_OPEN_ALIAS)
946
    {
947 948
        wmd->lpstrAlias = HeapAlloc(GetProcessHeap(), 0, (strlenW(lpParms->lpstrAlias)+1) * sizeof(WCHAR));
        strcpyW( wmd->lpstrAlias, lpParms->lpstrAlias);
949
    }
950 951
    lpParms->wDeviceID = wmd->wDeviceID;

952
    return MCI_SendCommandFrom32(wmd->wDeviceID, MCI_OPEN_DRIVER, dwParam,
953 954 955 956 957 958
				 (DWORD)lpParms);
}

/**************************************************************************
 * 				MCI_FindCommand		[internal]
 */
959
static	LPCWSTR		MCI_FindCommand(UINT uTbl, LPCWSTR verb)
960
{
961
    UINT	idx;
962

963
    if (uTbl >= MAX_MCICMDTABLE || !S_MciCmdTable[uTbl].lpTable)
964 965
	return NULL;

966 967 968 969 970
    /* 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++) {
971
	if (strcmpiW(S_MciCmdTable[uTbl].aVerbs[idx], verb) == 0)
972 973
	    return S_MciCmdTable[uTbl].aVerbs[idx];
    }
974 975 976 977 978 979 980

    return NULL;
}

/**************************************************************************
 * 				MCI_GetReturnType		[internal]
 */
981
static	DWORD		MCI_GetReturnType(LPCWSTR lpCmd)
982
{
983 984
    lpCmd = (LPCWSTR)((const BYTE*)(lpCmd + strlenW(lpCmd) + 1) + sizeof(DWORD) + sizeof(WORD));
    if (*lpCmd == '\0' && *(const WORD*)((const BYTE*)(lpCmd + 1) + sizeof(DWORD)) == MCI_RETURN) {
Eric Pouech's avatar
Eric Pouech committed
985
	return *(const DWORD*)(lpCmd + 1);
986 987 988 989 990 991 992
    }
    return 0L;
}

/**************************************************************************
 * 				MCI_GetMessage			[internal]
 */
993
static	WORD		MCI_GetMessage(LPCWSTR lpCmd)
994
{
995
    return (WORD)*(const DWORD*)(lpCmd + strlenW(lpCmd) + 1);
996 997 998 999 1000
}

/**************************************************************************
 * 				MCI_GetDWord			[internal]
 */
1001
static	BOOL		MCI_GetDWord(LPDWORD data, LPWSTR* ptr)
1002 1003
{
    DWORD	val;
1004
    LPWSTR	ret;
1005

1006
    val = strtoulW(*ptr, &ret, 0);
1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021

    switch (*ret) {
    case '\0':	break;
    case ' ':	ret++; break;
    default:	return FALSE;
    }

    *data |= val;
    *ptr = ret;
    return TRUE;
}

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

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

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

    *args = ptr;
    return 0;
}

#define	MCI_DATA_SIZE	16

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

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

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

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

1082
	    switch (eid) {
1083
	    case MCI_CONSTANT:
1084
		inCst = TRUE;	cflg = flg;	break;
1085
	    case MCI_END_CONSTANT:
1086 1087 1088 1089
		/* there may be additional integral values after flag in constant */
		if (inCst && MCI_GetDWord(&(data[offset]), &args)) {
		    *dwFlags |= cflg;
		}
1090
		inCst = FALSE;	cflg = 0;
1091 1092 1093
		break;
	    }

1094
	    if (strncmpiW(args, str, len) == 0 &&
1095
                ((eid == MCI_STRING && len == 0) || args[len] == 0 || args[len] == ' ')) {
1096 1097
		/* store good values into data[] */
		args += len;
1098
		while (*args == ' ') args++;
1099 1100 1101 1102 1103 1104 1105 1106 1107 1108
		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;
1109
		case MCI_FLAG:
1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123
		    *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;
1124
		case MCI_RECT:
1125 1126 1127 1128 1129 1130
		    /* 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)) {
1131
			ERR("Bad rect %s\n", debugstr_w(args));
1132 1133 1134 1135 1136
			return MCIERR_BAD_INTEGER;
		    }
		    break;
		case MCI_STRING:
		    *dwFlags |= flg;
1137
		    if ((dwRet = MCI_GetString((LPWSTR*)&data[offset], &args)))
1138 1139
			return dwRet;
		    break;
1140
		default:	ERR("oops\n");
1141
		}
1142 1143
		/* exit inside while loop, except if just entered in constant area definition */
		if (!inCst || eid != MCI_CONSTANT) eid = MCI_END_COMMAND;
1144 1145 1146 1147 1148 1149 1150 1151 1152 1153
	    } 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;
1154
		case MCI_END_CONSTANT:
1155 1156
		case MCI_STRING:		offset++; break;
		case MCI_RECT:			offset += 4; break;
1157
		default:			ERR("oops\n");
1158 1159 1160 1161
		}
	    }
	} while (eid != MCI_END_COMMAND);
	if (!found) {
1162
	    WARN("Optarg %s not found\n", debugstr_w(args));
1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175
	    return MCIERR_UNRECOGNIZED_COMMAND;
	}
	if (offset == MCI_DATA_SIZE) {
	    ERR("Internal data[] buffer overflow\n");
	    return MCIERR_PARSER_INTERNAL;
	}
    }
    return 0;
}

/**************************************************************************
 * 				MCI_HandleReturnValues	[internal]
 */
1176
static	DWORD	MCI_HandleReturnValues(DWORD dwRet, LPWINE_MCIDRIVER wmd, DWORD retType, 
1177
                                       LPDWORD data, LPWSTR lpstrRet, UINT uRetLen)
1178
{
1179 1180 1181 1182 1183
    static const WCHAR wszLd  [] = {'%','l','d',0};
    static const WCHAR wszLd4 [] = {'%','l','d',' ','%','l','d',' ','%','l','d',' ','%','l','d',0};
    static const WCHAR wszCol3[] = {'%','d',':','%','d',':','%','d',0};
    static const WCHAR wszCol4[] = {'%','d',':','%','d',':','%','d',':','%','d',0};

1184
    if (lpstrRet) {
1185
	switch (retType) {
1186 1187
	case 0: /* nothing to return */
	    break;
1188
	case MCI_INTEGER:
1189 1190 1191
	    switch (dwRet & 0xFFFF0000ul) {
	    case 0:
	    case MCI_INTEGER_RETURNED:
1192
		snprintfW(lpstrRet, uRetLen, wszLd, data[1]);
1193 1194
		break;
	    case MCI_RESOURCE_RETURNED:
1195
		/* return string which ID is HIWORD(data[1]),
1196
		 * string is loaded from mmsystem.dll */
1197
		LoadStringW(WINMM_IData.hWinMM32Instance, HIWORD(data[1]),
1198 1199 1200
			    lpstrRet, uRetLen);
		break;
	    case MCI_RESOURCE_RETURNED|MCI_RESOURCE_DRIVER:
1201
		/* return string which ID is HIWORD(data[1]),
1202
		 * string is loaded from driver */
1203
		/* FIXME: this is wrong for a 16 bit handle */
1204
		LoadStringW(GetDriverModuleHandle(wmd->hDriver),
1205
			    HIWORD(data[1]), lpstrRet, uRetLen);
1206 1207
		break;
	    case MCI_COLONIZED3_RETURN:
1208 1209 1210
		snprintfW(lpstrRet, uRetLen, wszCol3,
			  LOBYTE(LOWORD(data[1])), HIBYTE(LOWORD(data[1])),
			  LOBYTE(HIWORD(data[1])));
1211 1212
		break;
	    case MCI_COLONIZED4_RETURN:
1213 1214 1215
		snprintfW(lpstrRet, uRetLen, wszCol4,
			  LOBYTE(LOWORD(data[1])), HIBYTE(LOWORD(data[1])),
			  LOBYTE(HIWORD(data[1])), HIBYTE(HIWORD(data[1])));
1216 1217 1218 1219
		break;
	    default:	ERR("Ooops (%04X)\n", HIWORD(dwRet));
	    }
	    break;
1220
	case MCI_STRING:
1221 1222 1223 1224 1225 1226
	    switch (dwRet & 0xFFFF0000ul) {
	    case 0:
		/* nothing to do data[1] == lpstrRet */
		break;
	    case MCI_INTEGER_RETURNED:
		data[1] = *(LPDWORD)lpstrRet;
1227
		snprintfW(lpstrRet, uRetLen, wszLd, data[1]);
1228 1229 1230 1231 1232 1233
		break;
	    default:
		WARN("Oooch. MCI_STRING and HIWORD(dwRet)=%04x\n", HIWORD(dwRet));
		break;
	    }
	    break;
1234 1235
	case MCI_RECT:
	    if (dwRet & 0xFFFF0000ul)
1236
		WARN("Oooch. MCI_STRING and HIWORD(dwRet)=%04x\n", HIWORD(dwRet));
1237 1238
	    snprintfW(lpstrRet, uRetLen, wszLd4,
                      data[1], data[2], data[3], data[4]);
1239 1240 1241 1242 1243 1244 1245 1246
	    break;
	default:		ERR("oops\n");
	}
    }
    return LOWORD(dwRet);
}

/**************************************************************************
1247
 * 				mciSendStringW		[WINMM.@]
1248
 */
1249
DWORD WINAPI mciSendStringW(LPCWSTR lpstrCommand, LPWSTR lpstrRet,
1250 1251
			    UINT uRetLen, HWND hwndCallback)
{
1252
    LPWSTR		verb, dev, args;
1253 1254 1255 1256
    LPWINE_MCIDRIVER	wmd = 0;
    DWORD		dwFlags = 0, dwRet = 0;
    int			offset = 0;
    DWORD		data[MCI_DATA_SIZE];
1257
    DWORD		retType;
1258 1259 1260 1261
    LPCWSTR		lpCmd = 0;
    LPWSTR		devAlias = NULL;
    static const WCHAR  wszNew[] = {'n','e','w',0};
    static const WCHAR  wszSAliasS[] = {' ','a','l','i','a','s',' ',0};
1262
    static const WCHAR wszTypeS[] = {'t','y','p','e',' ',0};
1263

1264 1265
    TRACE("(%s, %p, %d, %p)\n", 
          debugstr_w(lpstrCommand), lpstrRet, uRetLen, hwndCallback);
1266 1267

    /* format is <command> <device> <optargs> */
1268
    if (!(verb = HeapAlloc(GetProcessHeap(), 0, (strlenW(lpstrCommand)+1) * sizeof(WCHAR))))
1269
	return MCIERR_OUT_OF_MEMORY;
1270 1271
    strcpyW( verb, lpstrCommand );
    CharLowerW(verb);
1272 1273 1274

    memset(data, 0, sizeof(data));

1275
    if (!(args = strchrW(verb, ' '))) {
1276 1277 1278 1279 1280 1281 1282 1283
	dwRet = MCIERR_MISSING_DEVICE_NAME;
	goto errCleanUp;
    }
    *args++ = '\0';
    if ((dwRet = MCI_GetString(&dev, &args))) {
	goto errCleanUp;
    }

1284
    /* Determine devType from open */
1285 1286
    if (!strcmpW(verb, wszOpen)) {
	LPWSTR	devType, tmp;
1287
        WCHAR	buf[128];
1288

1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304
	/* case dev == 'new' has to be handled */
	if (!strcmpW(dev, wszNew)) {
	    dev = 0;
	    if ((devType = strstrW(args, wszTypeS)) != NULL) {
		devType += 5;
		tmp = strchrW(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 {
		WARN("open new requires device type\n");
		dwRet = MCIERR_MISSING_DEVICE_NAME;
		goto errCleanUp;
	    }
	} else if ((devType = strchrW(dev, '!')) != NULL) {
1305
	    *devType++ = '\0';
1306 1307 1308 1309
	    tmp = devType; devType = dev; dev = tmp;

	    dwFlags |= MCI_OPEN_TYPE;
	    data[2] = (DWORD)devType;
1310
	    devType = str_dup_upper(devType);
1311 1312
	    dwFlags |= MCI_OPEN_ELEMENT;
	    data[3] = (DWORD)dev;
1313 1314 1315
	} else if (DRIVER_GetLibName(dev, wszMci, buf, sizeof(buf))) {
            /* this is the name of a mci driver's type */
	    tmp = strchrW(dev, ' ');
1316 1317
	    if (tmp) *tmp = '\0';
	    data[2] = (DWORD)dev;
1318
	    devType = str_dup_upper(dev);
1319 1320
	    if (tmp) *tmp = ' ';
	    dwFlags |= MCI_OPEN_TYPE;
1321
	} else {
1322
	    if ((devType = strstrW(args, wszTypeS)) != NULL) {
1323
		devType += 5;
1324
		tmp = strchrW(devType, ' ');
1325
		if (tmp) *tmp = '\0';
1326
		devType = str_dup_upper(devType);
1327 1328 1329 1330 1331 1332
		if (tmp) *tmp = ' ';
		/* dwFlags and data[2] will be correctly set in ParseOpt loop */
	    } else {
		if ((dwRet = MCI_GetDevTypeFromFileName(dev, buf, sizeof(buf))))
		    goto errCleanUp;

1333
		devType = str_dup_upper(buf);
1334 1335 1336 1337
	    }
	    dwFlags |= MCI_OPEN_ELEMENT;
	    data[3] = (DWORD)dev;
	}
1338 1339
	if ((devAlias = strstrW(args, wszSAliasS))) {
            WCHAR*      tmp2;
1340
	    devAlias += 7;
1341
	    if (!(tmp = strchrW(devAlias,' '))) tmp = devAlias + strlenW(devAlias);
1342
	    if (tmp) *tmp = '\0';
1343 1344
            tmp2 = HeapAlloc(GetProcessHeap(), 0, (tmp - devAlias + 1) * sizeof(WCHAR) );
            memcpy( tmp2, devAlias, (tmp - devAlias) * sizeof(WCHAR) );
1345 1346
            tmp2[tmp - devAlias] = 0;
            data[4] = (DWORD)tmp2;
1347 1348
	    /* should be done in regular options parsing */
	    /* dwFlags |= MCI_OPEN_ALIAS; */
1349 1350 1351 1352
	} else if (dev == 0) {
	    /* "open new" requires alias */
	    dwRet = MCIERR_NEW_REQUIRES_ALIAS;
	    goto errCleanUp;
1353 1354
	}

1355
	dwRet = MCI_LoadMciDriver(devType, &wmd);
1356 1357
	if (dwRet == MCIERR_DEVICE_NOT_INSTALLED)
	    dwRet = MCIERR_INVALID_DEVICE_NAME;
1358 1359
	HeapFree(GetProcessHeap(), 0, devType);
	if (dwRet) {
1360
	    MCI_UnLoadMciDriver(wmd);
1361 1362
	    goto errCleanUp;
	}
1363
    } else if (!(wmd = MCI_GetDriver(mciGetDeviceIDW(dev)))) {
1364
	/* auto open */
1365
        static const WCHAR wszOpenWait[] = {'o','p','e','n',' ','%','s',' ','w','a','i','t',0};
1366 1367
	WCHAR   buf[128];
	sprintfW(buf, wszOpenWait, dev);
1368

1369
	if ((dwRet = mciSendStringW(buf, NULL, 0, 0)) != 0)
1370
	    goto errCleanUp;
1371

1372
	wmd = MCI_GetDriver(mciGetDeviceIDW(dev));
1373
	if (!wmd) {
1374
	    /* FIXME: memory leak, MCI driver is not closed */
1375 1376 1377
	    dwRet = MCIERR_INVALID_DEVICE_ID;
	    goto errCleanUp;
	}
1378 1379 1380
    }

    /* get the verb in the different command tables */
1381 1382 1383 1384 1385 1386
    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)
1387
		wmd->uTypeCmdTable = MCI_GetCommandTable(wmd->wType);
1388 1389 1390
	    if (wmd->uTypeCmdTable != MCI_NO_COMMAND_TABLE)
		lpCmd = MCI_FindCommand(wmd->uTypeCmdTable, verb);
	}
1391 1392
    }
    /* try core command table */
1393
    if (!lpCmd) lpCmd = MCI_FindCommand(MCI_GetCommandTable(0), verb);
1394 1395

    if (!lpCmd) {
1396
	TRACE("Command %s not found!\n", debugstr_w(verb));
1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407
	dwRet = MCIERR_UNRECOGNIZED_COMMAND;
	goto errCleanUp;
    }

    /* set up call back */
    if (hwndCallback != 0) {
	dwFlags |= MCI_NOTIFY;
	data[0] = (DWORD)hwndCallback;
    }

    /* set return information */
1408
    switch (retType = MCI_GetReturnType(lpCmd)) {
1409 1410 1411 1412 1413 1414 1415
    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");
    }

1416 1417
    TRACE("verb=%s on dev=%s; offset=%d\n", 
          debugstr_w(verb), debugstr_w(dev), offset);
1418 1419 1420 1421

    if ((dwRet = MCI_ParseOptArgs(data, offset, lpCmd, args, &dwFlags)))
	goto errCleanUp;

1422 1423 1424
    /* FIXME: the command should get it's own notification window set up and
     * ask for device closing while processing the notification mechanism
     */
1425 1426
    if (lpstrRet && uRetLen) *lpstrRet = '\0';

1427
    TRACE("[%d, %s, %08x, %08x/%s %08x/%s %08x/%s %08x/%s %08x/%s %08x/%s]\n",
1428
	  wmd->wDeviceID, MCI_MessageToString(MCI_GetMessage(lpCmd)), dwFlags,
1429 1430 1431
	  data[0], debugstr_w((WCHAR *)data[0]), data[1], debugstr_w((WCHAR *)data[1]),
	  data[2], debugstr_w((WCHAR *)data[2]), data[3], debugstr_w((WCHAR *)data[3]),
	  data[4], debugstr_w((WCHAR *)data[4]), data[5], debugstr_w((WCHAR *)data[5]));
1432

1433 1434
    if (strcmpW(verb, wszOpen) == 0) {
	if ((dwRet = MCI_FinishOpen(wmd, (LPMCI_OPEN_PARMSW)data, dwFlags)))
1435
	    MCI_UnLoadMciDriver(wmd);
1436 1437 1438 1439
	/* FIXME: notification is not properly shared across two opens */
    } else {
	dwRet = MCI_SendCommand(wmd->wDeviceID, MCI_GetMessage(lpCmd), dwFlags, (DWORD)data, TRUE);
    }
1440
    TRACE("=> 1/ %x (%s)\n", dwRet, debugstr_w(lpstrRet));
1441
    dwRet = MCI_HandleReturnValues(dwRet, wmd, retType, data, lpstrRet, uRetLen);
1442
    TRACE("=> 2/ %x (%s)\n", dwRet, debugstr_w(lpstrRet));
1443 1444 1445

errCleanUp:
    HeapFree(GetProcessHeap(), 0, verb);
1446
    HeapFree(GetProcessHeap(), 0, devAlias);
1447 1448 1449 1450
    return dwRet;
}

/**************************************************************************
1451
 * 				mciSendStringA			[WINMM.@]
1452
 */
1453
DWORD WINAPI mciSendStringA(LPCSTR lpstrCommand, LPSTR lpstrRet,
1454
			    UINT uRetLen, HWND hwndCallback)
1455
{
1456 1457
    LPWSTR 	lpwstrCommand;
    LPWSTR      lpwstrRet = NULL;
1458
    UINT	ret;
1459
    INT len;
1460

1461
    /* FIXME: is there something to do with lpstrReturnString ? */
1462 1463 1464 1465
    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)
1466
    {
1467
        lpwstrRet = HeapAlloc(GetProcessHeap(), 0, uRetLen * sizeof(WCHAR));
1468 1469 1470 1471 1472
        if (!lpwstrRet) {
            WARN("no memory\n");
            HeapFree( GetProcessHeap(), 0, lpwstrCommand );
            return MCIERR_OUT_OF_MEMORY;
        }
1473
    }
1474
    ret = mciSendStringW(lpwstrCommand, lpwstrRet, uRetLen, hwndCallback);
1475
    if (lpwstrRet)
1476 1477 1478
        WideCharToMultiByte( CP_ACP, 0, lpwstrRet, -1, lpstrRet, uRetLen, NULL, NULL );
    HeapFree(GetProcessHeap(), 0, lpwstrCommand);
    HeapFree(GetProcessHeap(), 0, lpwstrRet);
1479
    return ret;
1480 1481
}

1482
/**************************************************************************
1483
 * 				mciExecute			[WINMM.@]
Patrik Stridvall's avatar
Patrik Stridvall committed
1484
 * 				mciExecute			[MMSYSTEM.712]
1485
 */
1486
BOOL WINAPI mciExecute(LPCSTR lpstrCommand)
1487 1488 1489
{
    char	strRet[256];
    DWORD	ret;
1490

1491 1492
    TRACE("(%s)!\n", lpstrCommand);

1493
    ret = mciSendStringA(lpstrCommand, strRet, sizeof(strRet), 0);
1494 1495
    if (ret != 0) {
	if (!mciGetErrorStringA(ret, strRet, sizeof(strRet))) {
1496
	    sprintf(strRet, "Unknown MCI error (%d)", ret);
1497
	}
1498
	MessageBoxA(0, strRet, "Error in mciExecute()", MB_OK);
1499 1500
    }
    /* FIXME: what shall I return ? */
1501
    return TRUE;
1502
}
1503

1504
/**************************************************************************
1505
 *                    	mciLoadCommandResource  		[WINMM.@]
1506 1507 1508
 *
 * Strangely, this function only exists as an UNICODE one.
 */
1509
UINT WINAPI mciLoadCommandResource(HINSTANCE hInst, LPCWSTR resNameW, UINT type)
1510
{
1511 1512 1513 1514
    HRSRC	        hRsrc = 0;
    HGLOBAL      	hMem;
    UINT16		ret = MCI_NO_COMMAND_TABLE;

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

1517
    /* if a file named "resname.mci" exits, then load resource "resname" from it
1518 1519 1520 1521
     * otherwise directly from driver
     * We don't support it (who uses this feature ?), but we check anyway
     */
    if (!type) {
1522 1523
#if 0
        /* FIXME: we should put this back into order, but I never found a program
1524
         * actually using this feature, so we may not need it
1525
         */
1526 1527 1528 1529 1530 1531 1532 1533
	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
1534
    }
1535
    if (!(hRsrc = FindResourceW(hInst, resNameW, (LPWSTR)RT_RCDATA))) {
1536 1537
	WARN("No command table found in resource\n");
    } else if ((hMem = LoadResource(hInst, hRsrc))) {
1538
	ret = MCI_SetCommandTable(LockResource(hMem), type);
1539 1540 1541 1542 1543 1544 1545
    } else {
	WARN("Couldn't load resource.\n");
    }
    TRACE("=> %04x\n", ret);
    return ret;
}

1546
/**************************************************************************
1547
 *                    	mciFreeCommandResource			[WINMM.@]
1548
 */
1549
BOOL WINAPI mciFreeCommandResource(UINT uTable)
1550
{
1551 1552
    TRACE("(%08x)!\n", uTable);

1553
    return MCI_DeleteCommandTable(uTable, FALSE);
1554 1555 1556
}

/**************************************************************************
1557
 * 			MCI_SendCommandFrom32			[internal]
1558
 */
1559
DWORD MCI_SendCommandFrom32(MCIDEVICEID wDevID, UINT16 wMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1560
{
1561
    DWORD		dwRet = MCIERR_INVALID_DEVICE_ID;
1562 1563
    LPWINE_MCIDRIVER	wmd = MCI_GetDriver(wDevID);

1564 1565 1566
    if (wmd) {
	if (wmd->bIs32) {
	    dwRet = SendDriverMessage(wmd->hDriver, wMsg, dwParam1, dwParam2);
1567
	} else if (pFnMciMapMsg32WTo16) {
1568
	    WINMM_MapType	res;
1569

1570
	    switch (res = pFnMciMapMsg32WTo16(wmd->wType, wMsg, dwParam1, &dwParam2)) {
1571
	    case WINMM_MAP_MSGERROR:
1572 1573 1574
		TRACE("Not handled yet (%s)\n", MCI_MessageToString(wMsg));
		dwRet = MCIERR_DRIVER_INTERNAL;
		break;
1575
	    case WINMM_MAP_NOMEM:
1576 1577 1578
		TRACE("Problem mapping msg=%s from 32a to 16\n", MCI_MessageToString(wMsg));
		dwRet = MCIERR_OUT_OF_MEMORY;
		break;
1579 1580
	    case WINMM_MAP_OK:
	    case WINMM_MAP_OKMEM:
1581
		dwRet = SendDriverMessage(wmd->hDriver, wMsg, dwParam1, dwParam2);
1582
		if (res == WINMM_MAP_OKMEM)
1583
		    pFnMciUnMapMsg32WTo16(wmd->wType, wMsg, dwParam1, dwParam2);
1584
		break;
1585 1586 1587 1588 1589 1590 1591 1592 1593
	    }
	}
    }
    return dwRet;
}

/**************************************************************************
 * 			MCI_SendCommandFrom16			[internal]
 */
1594
DWORD MCI_SendCommandFrom16(MCIDEVICEID wDevID, UINT16 wMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1595
{
1596
    DWORD		dwRet = MCIERR_INVALID_DEVICE_ID;
1597 1598
    LPWINE_MCIDRIVER	wmd = MCI_GetDriver(wDevID);

1599
    if (wmd) {
1600
	dwRet = MCIERR_INVALID_DEVICE_ID;
1601

1602
	if (wmd->bIs32 && pFnMciMapMsg16To32W) {
1603
	    WINMM_MapType		res;
1604

1605
	    switch (res = pFnMciMapMsg16To32W(wmd->wType, wMsg, dwParam1, &dwParam2)) {
1606
	    case WINMM_MAP_MSGERROR:
1607
		TRACE("Not handled yet (%s)\n", MCI_MessageToString(wMsg));
1608
		dwRet = MCIERR_DRIVER_INTERNAL;
1609
		break;
1610
	    case WINMM_MAP_NOMEM:
1611
		TRACE("Problem mapping msg=%s from 16 to 32a\n", MCI_MessageToString(wMsg));
1612 1613
		dwRet = MCIERR_OUT_OF_MEMORY;
		break;
1614 1615
	    case WINMM_MAP_OK:
	    case WINMM_MAP_OKMEM:
1616
		dwRet = SendDriverMessage(wmd->hDriver, wMsg, dwParam1, dwParam2);
1617
		if (res == WINMM_MAP_OKMEM)
1618
		    pFnMciUnMapMsg16To32W(wmd->wType, wMsg, dwParam1, dwParam2);
1619
		break;
1620
	    }
1621 1622
	} else {
	    dwRet = SendDriverMessage(wmd->hDriver, wMsg, dwParam1, dwParam2);
1623 1624 1625 1626 1627 1628 1629 1630
	}
    }
    return dwRet;
}

/**************************************************************************
 * 			MCI_Open				[internal]
 */
1631
static	DWORD MCI_Open(DWORD dwParam, LPMCI_OPEN_PARMSW lpParms)
1632
{
1633
    WCHAR			strDevTyp[128];
1634
    DWORD 			dwRet;
1635
    LPWINE_MCIDRIVER		wmd = NULL;
1636

1637
    TRACE("(%08X, %p)\n", dwParam, lpParms);
1638
    if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1639 1640

    /* only two low bytes are generic, the other ones are dev type specific */
1641
#define WINE_MCIDRIVER_SUPP	(0xFFFF0000|MCI_OPEN_SHAREABLE|MCI_OPEN_ELEMENT| \
1642 1643
                         MCI_OPEN_ALIAS|MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID| \
                         MCI_NOTIFY|MCI_WAIT)
1644 1645
    if ((dwParam & ~WINE_MCIDRIVER_SUPP) != 0) {
	FIXME("Unsupported yet dwFlags=%08lX\n", dwParam & ~WINE_MCIDRIVER_SUPP);
1646
    }
1647
#undef WINE_MCIDRIVER_SUPP
1648 1649 1650

    strDevTyp[0] = 0;

1651 1652 1653 1654 1655 1656
    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 ||
1657
		!LoadStringW(WINMM_IData.hWinMM32Instance, uDevType, 
1658
                             strDevTyp, sizeof(strDevTyp) / sizeof(WCHAR))) {
1659 1660 1661 1662
		dwRet = MCIERR_BAD_INTEGER;
		goto errCleanUp;
	    }
	} else {
1663
	    LPWSTR	ptr;
1664 1665 1666 1667
	    if (lpParms->lpstrDeviceType == NULL) {
		dwRet = MCIERR_NULL_PARAMETER_BLOCK;
		goto errCleanUp;
	    }
1668 1669
	    strcpyW(strDevTyp, lpParms->lpstrDeviceType);
	    ptr = strchrW(strDevTyp, '!');
1670 1671 1672 1673 1674 1675 1676
	    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",
1677 1678
			debugstr_w(lpParms->lpstrElementName), 
                        debugstr_w(strDevTyp));
1679 1680 1681 1682 1683 1684 1685 1686
		    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;
	    }
1687

1688
	}
1689
	TRACE("devType=%s !\n", debugstr_w(strDevTyp));
1690 1691
    }

1692
    if (dwParam & MCI_OPEN_ELEMENT) {
1693
	TRACE("lpstrElementName=%s\n", debugstr_w(lpParms->lpstrElementName));
1694 1695 1696 1697 1698 1699 1700

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

1701 1702 1703 1704 1705
	if (!lpParms->lpstrElementName) {
	    dwRet = MCIERR_NULL_PARAMETER_BLOCK;
	    goto errCleanUp;
	}

1706
	/* type, if given as a parameter, supersedes file extension */
1707 1708
	if (!strDevTyp[0] &&
	    MCI_GetDevTypeFromFileName(lpParms->lpstrElementName,
1709
				       strDevTyp, sizeof(strDevTyp))) {
1710 1711
            static const WCHAR wszCdAudio[] = {'C','D','A','U','D','I','O',0};
	    if (GetDriveTypeW(lpParms->lpstrElementName) != DRIVE_CDROM) {
1712 1713
		dwRet = MCIERR_EXTENSION_NOT_FOUND;
		goto errCleanUp;
1714
	    }
1715
	    /* FIXME: this will not work if several CDROM drives are installed on the machine */
1716
	    strcpyW(strDevTyp, wszCdAudio);
1717 1718
	}
    }
1719

1720
    if (strDevTyp[0] == 0) {
1721
	FIXME("Couldn't load driver\n");
1722
	dwRet = MCIERR_INVALID_DEVICE_NAME;
1723
	goto errCleanUp;
1724
    }
1725

1726
    if (dwParam & MCI_OPEN_ALIAS) {
1727
	TRACE("Alias=%s !\n", debugstr_w(lpParms->lpstrAlias));
1728 1729 1730 1731 1732
	if (!lpParms->lpstrAlias) {
	    dwRet = MCIERR_NULL_PARAMETER_BLOCK;
	    goto errCleanUp;
	}
    }
1733

1734
    if ((dwRet = MCI_LoadMciDriver(strDevTyp, &wmd))) {
1735
	goto errCleanUp;
1736
    }
1737

1738
    if ((dwRet = MCI_FinishOpen(wmd, lpParms, dwParam))) {
1739
	TRACE("Failed to open driver (MCI_OPEN_DRIVER) [%08x], closing\n", dwRet);
1740 1741
	/* FIXME: is dwRet the correct ret code ? */
	goto errCleanUp;
1742
    }
1743 1744

    /* only handled devices fall through */
1745
    TRACE("wDevID=%04X wDeviceID=%d dwRet=%d\n", wmd->wDeviceID, lpParms->wDeviceID, dwRet);
1746

1747
    if (dwParam & MCI_NOTIFY)
1748
	mciDriverNotify((HWND)lpParms->dwCallback, wmd->wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1749

1750 1751
    return 0;
errCleanUp:
1752
    if (wmd) MCI_UnLoadMciDriver(wmd);
1753

1754
    if (dwParam & MCI_NOTIFY)
1755
	mciDriverNotify((HWND)lpParms->dwCallback, 0, MCI_NOTIFY_FAILURE);
1756 1757 1758 1759 1760 1761
    return dwRet;
}

/**************************************************************************
 * 			MCI_Close				[internal]
 */
1762
static	DWORD MCI_Close(UINT16 wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
1763
{
1764 1765 1766
    DWORD		dwRet;
    LPWINE_MCIDRIVER	wmd;

1767
    TRACE("(%04x, %08X, %p)\n", wDevID, dwParam, lpParms);
1768 1769

    if (wDevID == MCI_ALL_DEVICE_ID) {
1770 1771
	LPWINE_MCIDRIVER	next;

1772
	EnterCriticalSection(&WINMM_IData.cs);
1773
	/* FIXME: shall I notify once after all is done, or for
1774 1775 1776
	 * each of the open drivers ? if the latest, which notif
	 * to return when only one fails ?
	 */
1777
	for (wmd = WINMM_IData.lpMciDrvs; wmd; ) {
1778 1779 1780
	    next = wmd->lpNext;
	    MCI_Close(wmd->wDeviceID, dwParam, lpParms);
	    wmd = next;
1781
	}
1782
	LeaveCriticalSection(&WINMM_IData.cs);
1783 1784 1785 1786 1787
	return 0;
    }

    if (!(wmd = MCI_GetDriver(wDevID))) {
	return MCIERR_INVALID_DEVICE_ID;
1788 1789
    }

1790
    dwRet = MCI_SendCommandFrom32(wDevID, MCI_CLOSE_DRIVER, dwParam, (DWORD)lpParms);
1791

1792
    MCI_UnLoadMciDriver(wmd);
1793

1794
    if (dwParam & MCI_NOTIFY)
1795 1796 1797
        mciDriverNotify(lpParms ? (HWND)lpParms->dwCallback : 0,
                        wDevID,
                        dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1798

1799 1800 1801 1802 1803 1804
    return dwRet;
}

/**************************************************************************
 * 			MCI_WriteString				[internal]
 */
1805
DWORD	MCI_WriteString(LPWSTR lpDstStr, DWORD dstSize, LPCWSTR lpSrcStr)
1806
{
1807
    DWORD	ret = 0;
1808

1809
    if (lpSrcStr) {
1810 1811 1812
        dstSize /= sizeof(WCHAR);
	if (dstSize <= strlenW(lpSrcStr)) {
	    lstrcpynW(lpDstStr, lpSrcStr, dstSize - 1);
1813 1814
	    ret = MCIERR_PARAM_OVERFLOW;
	} else {
1815
	    strcpyW(lpDstStr, lpSrcStr);
1816
	}
1817
    } else {
1818
	*lpDstStr = 0;
1819 1820 1821 1822 1823 1824 1825
    }
    return ret;
}

/**************************************************************************
 * 			MCI_Sysinfo				[internal]
 */
1826
static	DWORD MCI_SysInfo(UINT uDevID, DWORD dwFlags, LPMCI_SYSINFO_PARMSW lpParms)
1827
{
1828
    DWORD		ret = MCIERR_INVALID_DEVICE_ID, cnt = 0;
1829
    WCHAR		buf[2048], *s = buf, *p;
1830
    LPWINE_MCIDRIVER	wmd;
1831
    HKEY		hKey;
1832 1833 1834

    if (lpParms == NULL)			return MCIERR_NULL_PARAMETER_BLOCK;

1835
    TRACE("(%08x, %08X, %08X[num=%d, wDevTyp=%u])\n",
1836
	  uDevID, dwFlags, (DWORD)lpParms, lpParms->dwNumber, lpParms->wDeviceType);
1837

1838 1839
    switch (dwFlags & ~MCI_SYSINFO_OPEN) {
    case MCI_SYSINFO_QUANTITY:
1840 1841 1842
	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");
1843 1844
		EnterCriticalSection(&WINMM_IData.cs);
		for (wmd = WINMM_IData.lpMciDrvs; wmd; wmd = wmd->lpNext) {
1845
		    cnt++;
1846
		}
1847
		LeaveCriticalSection(&WINMM_IData.cs);
1848
	    } else {
1849
		TRACE("MCI_SYSINFO_QUANTITY: # of installed MCI drivers\n");
1850
		if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, wszHklmMci,
1851
			  	   0, KEY_QUERY_VALUE, &hKey ) == ERROR_SUCCESS) {
1852
		    RegQueryInfoKeyW( hKey, 0, 0, 0, &cnt, 0, 0, 0, 0, 0, 0, 0);
1853
		    RegCloseKey( hKey );
1854
		}
1855
		if (GetPrivateProfileStringW(wszMci, 0, wszNull, buf, sizeof(buf) / sizeof(buf[0]), wszSystemIni))
1856
		    for (s = buf; *s; s += strlenW(s) + 1) cnt++;
1857 1858 1859 1860
	    }
	} else {
	    if (dwFlags & MCI_SYSINFO_OPEN) {
		TRACE("MCI_SYSINFO_QUANTITY: # of open MCI drivers of type %u\n", lpParms->wDeviceType);
1861 1862
		EnterCriticalSection(&WINMM_IData.cs);
		for (wmd = WINMM_IData.lpMciDrvs; wmd; wmd = wmd->lpNext) {
1863 1864
		    if (wmd->wType == lpParms->wDeviceType) cnt++;
		}
1865
		LeaveCriticalSection(&WINMM_IData.cs);
1866 1867 1868 1869
	    } 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;
1870 1871
	    }
	}
1872
	*(DWORD*)lpParms->lpstrReturn = cnt;
1873
	TRACE("(%d) => '%d'\n", lpParms->dwNumber, *(DWORD*)lpParms->lpstrReturn);
1874
	ret = MCI_INTEGER_RETURNED;
1875 1876
	break;
    case MCI_SYSINFO_INSTALLNAME:
1877
	TRACE("MCI_SYSINFO_INSTALLNAME\n");
1878
	if ((wmd = MCI_GetDriver(uDevID))) {
1879
	    ret = MCI_WriteString(lpParms->lpstrReturn, lpParms->dwRetSize,
1880
				  wmd->lpstrDeviceType);
1881 1882 1883 1884
	} else {
	    *lpParms->lpstrReturn = 0;
	    ret = MCIERR_INVALID_DEVICE_ID;
	}
1885
	TRACE("(%d) => %s\n", lpParms->dwNumber, debugstr_w(lpParms->lpstrReturn));
1886 1887
	break;
    case MCI_SYSINFO_NAME:
1888
	TRACE("MCI_SYSINFO_NAME\n");
1889
	if (dwFlags & MCI_SYSINFO_OPEN) {
1890
	    FIXME("Don't handle MCI_SYSINFO_NAME|MCI_SYSINFO_OPEN (yet)\n");
1891 1892
	    ret = MCIERR_UNRECOGNIZED_COMMAND;
	} else {
1893 1894 1895 1896 1897 1898
	    s = NULL;
	    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) {
1899
    		    DWORD bufLen = sizeof(buf);
1900 1901 1902
		    if (RegEnumKeyExW(hKey, lpParms->dwNumber - 1, 
                                      buf, &bufLen, 0, 0, 0, 0) == ERROR_SUCCESS)
                        s = buf;
1903 1904 1905 1906
		}
	        RegCloseKey( hKey );
	    }
	    if (!s) {
1907
		if (GetPrivateProfileStringW(wszMci, 0, wszNull, buf, sizeof(buf) / sizeof(buf[0]), wszSystemIni)) {
1908
		    for (p = buf; *p; p += strlenW(p) + 1, cnt++) {
1909
                        TRACE("%d: %s\n", cnt, debugstr_w(p));
1910 1911 1912 1913 1914 1915 1916
			if (cnt == lpParms->dwNumber - 1) {
			    s = p;
			    break;
			}
		    }
		}
	    }
1917
	    ret = s ? MCI_WriteString(lpParms->lpstrReturn, lpParms->dwRetSize / sizeof(WCHAR), s) : MCIERR_OUTOFRANGE;
1918
	}
1919
	TRACE("(%d) => %s\n", lpParms->dwNumber, debugstr_w(lpParms->lpstrReturn));
1920 1921
	break;
    default:
1922
	TRACE("Unsupported flag value=%08x\n", dwFlags);
1923 1924 1925 1926 1927
	ret = MCIERR_UNRECOGNIZED_COMMAND;
    }
    return ret;
}

1928 1929 1930
/**************************************************************************
 * 			MCI_Break				[internal]
 */
1931
static	DWORD MCI_Break(UINT wDevID, DWORD dwFlags, LPMCI_BREAK_PARMS lpParms)
1932 1933
{
    DWORD	dwRet = 0;
1934

1935 1936 1937
    if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;

    if (dwFlags & MCI_NOTIFY)
1938
	mciDriverNotify((HWND)lpParms->dwCallback, wDevID,
1939
                        (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
1940 1941 1942

    return dwRet;
}
1943

1944 1945 1946
/**************************************************************************
 * 			MCI_Sound				[internal]
 */
1947
static	DWORD MCI_Sound(UINT wDevID, DWORD dwFlags, LPMCI_SOUND_PARMSW lpParms)
1948 1949 1950 1951 1952 1953
{
    DWORD	dwRet = 0;

    if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;

    if (dwFlags & MCI_SOUND_NAME)
1954
        dwRet = sndPlaySoundW(lpParms->lpstrSoundName, SND_SYNC) ? MMSYSERR_NOERROR : MMSYSERR_ERROR;
1955 1956 1957 1958 1959 1960 1961 1962 1963
    else
        dwRet = MMSYSERR_ERROR; /* what should be done ??? */
    if (dwFlags & MCI_NOTIFY)
	mciDriverNotify((HWND)lpParms->dwCallback, wDevID,
                        (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);

    return dwRet;
}

1964 1965 1966
/**************************************************************************
 * 			MCI_SendCommand				[internal]
 */
1967 1968
DWORD	MCI_SendCommand(UINT wDevID, UINT16 wMsg, DWORD_PTR dwParam1,
			DWORD_PTR dwParam2, BOOL bFrom32)
1969 1970 1971 1972 1973 1974
{
    DWORD		dwRet = MCIERR_UNRECOGNIZED_COMMAND;

    switch (wMsg) {
    case MCI_OPEN:
	if (bFrom32) {
1975 1976
	    dwRet = MCI_Open(dwParam1, (LPMCI_OPEN_PARMSW)dwParam2);
	} else if (pFnMciMapMsg16To32W) {
1977
	    switch (pFnMciMapMsg16To32W(0, wMsg, dwParam1, &dwParam2)) {
1978 1979
	    case WINMM_MAP_OK:
	    case WINMM_MAP_OKMEM:
1980
		dwRet = MCI_Open(dwParam1, (LPMCI_OPEN_PARMSW)dwParam2);
1981
		pFnMciUnMapMsg16To32W(0, wMsg, dwParam1, dwParam2);
1982 1983 1984 1985 1986 1987 1988 1989
		break;
	    default: break; /* so that gcc does not bark */
	    }
	}
	break;
    case MCI_CLOSE:
	if (bFrom32) {
	    dwRet = MCI_Close(wDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1990
	} else if (pFnMciMapMsg16To32W) {
1991
	    switch (pFnMciMapMsg16To32W(0, wMsg, dwParam1, &dwParam2)) {
1992 1993
	    case WINMM_MAP_OK:
	    case WINMM_MAP_OKMEM:
1994
		dwRet = MCI_Close(wDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1995
		pFnMciUnMapMsg16To32W(0, wMsg, dwParam1, dwParam2);
1996 1997 1998 1999 2000 2001 2002
		break;
	    default: break; /* so that gcc does not bark */
	    }
	}
	break;
    case MCI_SYSINFO:
	if (bFrom32) {
2003 2004
	    dwRet = MCI_SysInfo(wDevID, dwParam1, (LPMCI_SYSINFO_PARMSW)dwParam2);
	} else if (pFnMciMapMsg16To32W) {
2005
	    switch (pFnMciMapMsg16To32W(0, wMsg, dwParam1, &dwParam2)) {
2006 2007
	    case WINMM_MAP_OK:
	    case WINMM_MAP_OKMEM:
2008
		dwRet = MCI_SysInfo(wDevID, dwParam1, (LPMCI_SYSINFO_PARMSW)dwParam2);
2009
		pFnMciUnMapMsg16To32W(0, wMsg, dwParam1, dwParam2);
2010
		break;
2011
	    default: break; /* so that gcc does not bark */
2012 2013 2014 2015 2016 2017
	    }
	}
	break;
    case MCI_BREAK:
	if (bFrom32) {
	    dwRet = MCI_Break(wDevID, dwParam1, (LPMCI_BREAK_PARMS)dwParam2);
2018
	} else if (pFnMciMapMsg16To32W) {
2019
	    switch (pFnMciMapMsg16To32W(0, wMsg, dwParam1, &dwParam2)) {
2020 2021
	    case WINMM_MAP_OK:
	    case WINMM_MAP_OKMEM:
2022
		dwRet = MCI_Break(wDevID, dwParam1, (LPMCI_BREAK_PARMS)dwParam2);
2023
		pFnMciUnMapMsg16To32W(0, wMsg, dwParam1, dwParam2);
2024 2025 2026 2027 2028 2029
		break;
	    default: break; /* so that gcc does not bark */
	    }
	}
	break;
    case MCI_SOUND:
2030
	if (bFrom32) {
2031 2032
	    dwRet = MCI_Sound(wDevID, dwParam1, (LPMCI_SOUND_PARMSW)dwParam2);
	} else if (pFnMciMapMsg16To32W) {
2033
	    switch (pFnMciMapMsg16To32W(0, wMsg, dwParam1, &dwParam2)) {
2034 2035
	    case WINMM_MAP_OK:
	    case WINMM_MAP_OKMEM:
2036
		dwRet = MCI_Sound(wDevID, dwParam1, (LPMCI_SOUND_PARMSW)dwParam2);
2037
		pFnMciUnMapMsg16To32W(0, wMsg, dwParam1, dwParam2);
2038 2039 2040 2041
		break;
	    default: break; /* so that gcc does not bark */
	    }
	}
2042 2043 2044 2045 2046 2047 2048 2049 2050
	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);
2051
	}
2052 2053 2054 2055 2056
	break;
    }
    return dwRet;
}

2057 2058 2059
/**************************************************************************
 * 				MCI_CleanUp			[internal]
 *
2060
 * Some MCI commands need to be cleaned-up (when not called from
2061
 * mciSendString), because MCI drivers return extra information for string
2062
 * transformation. This function gets rid of them.
2063
 */
2064
LRESULT		MCI_CleanUp(LRESULT dwRet, UINT wMsg, DWORD dwParam2)
2065
{
2066
    if (LOWORD(dwRet))
2067 2068
	return LOWORD(dwRet);

2069 2070 2071 2072 2073 2074 2075
    switch (wMsg) {
    case MCI_GETDEVCAPS:
	switch (dwRet & 0xFFFF0000ul) {
	case 0:
	case MCI_COLONIZED3_RETURN:
	case MCI_COLONIZED4_RETURN:
	case MCI_INTEGER_RETURNED:
2076 2077 2078 2079
	    /* nothing to do */
	    break;
	case MCI_RESOURCE_RETURNED:
	case MCI_RESOURCE_RETURNED|MCI_RESOURCE_DRIVER:
2080
	    {
2081
		LPMCI_GETDEVCAPS_PARMS	lmgp;
2082

2083
		lmgp = (LPMCI_GETDEVCAPS_PARMS)(void*)dwParam2;
2084
		TRACE("Changing %08x to %08x\n", lmgp->dwReturn, LOWORD(lmgp->dwReturn));
2085
		lmgp->dwReturn = LOWORD(lmgp->dwReturn);
2086
	    }
2087 2088
	    break;
	default:
2089
	    FIXME("Unsupported value for hiword (%04x) returned by DriverProc(%s)\n",
2090
		  HIWORD(dwRet), MCI_MessageToString(wMsg));
2091 2092 2093 2094 2095 2096 2097 2098
	}
	break;
    case MCI_STATUS:
	switch (dwRet & 0xFFFF0000ul) {
	case 0:
	case MCI_COLONIZED3_RETURN:
	case MCI_COLONIZED4_RETURN:
	case MCI_INTEGER_RETURNED:
2099 2100 2101 2102
	    /* nothing to do */
	    break;
	case MCI_RESOURCE_RETURNED:
	case MCI_RESOURCE_RETURNED|MCI_RESOURCE_DRIVER:
2103
	    {
2104
		LPMCI_STATUS_PARMS	lsp;
2105

2106
		lsp = (LPMCI_STATUS_PARMS)(void*)dwParam2;
2107
		TRACE("Changing %08x to %08x\n", lsp->dwReturn, LOWORD(lsp->dwReturn));
2108 2109 2110 2111
		lsp->dwReturn = LOWORD(lsp->dwReturn);
	    }
	    break;
	default:
2112
	    FIXME("Unsupported value for hiword (%04x) returned by DriverProc(%s)\n",
2113 2114 2115 2116 2117 2118 2119
		  HIWORD(dwRet), MCI_MessageToString(wMsg));
	}
	break;
    case MCI_SYSINFO:
	switch (dwRet & 0xFFFF0000ul) {
	case 0:
	case MCI_INTEGER_RETURNED:
2120
	    /* nothing to do */
2121 2122
	    break;
	default:
2123
	    FIXME("Unsupported value for hiword (%04x)\n", HIWORD(dwRet));
2124 2125 2126
	}
	break;
    default:
2127
	if (HIWORD(dwRet)) {
2128
	    FIXME("Got non null hiword for dwRet=0x%08lx for command %s\n",
2129
		  dwRet, MCI_MessageToString(wMsg));
2130
	}
2131 2132
	break;
    }
2133
    return LOWORD(dwRet);
2134
}
2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145

/**************************************************************************
 * 				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) {

2146
	if (LoadStringW(WINMM_IData.hWinMM32Instance,
2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163
			wError, lpstrBuffer, uLength) > 0) {
	    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) {

2164
	if (LoadStringA(WINMM_IData.hWinMM32Instance,
2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207
			dwError, lpstrBuffer, uLength) > 0) {
	    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.@]
 */
DWORD WINAPI mciGetDriverData(MCIDEVICEID uDeviceID)
{
    LPWINE_MCIDRIVER	wmd;

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

    wmd = MCI_GetDriver(uDeviceID);

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

    return wmd->dwPrivate;
}

/**************************************************************************
 * 			mciSetDriverData			[WINMM.@]
 */
BOOL WINAPI mciSetDriverData(MCIDEVICEID uDeviceID, DWORD data)
{
    LPWINE_MCIDRIVER	wmd;

2208
    TRACE("(%04x, %08x)\n", uDeviceID, data);
2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233

    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);

    dwRet = MCI_SendCommand(wDevID, wMsg, dwParam1, dwParam2, TRUE);
    dwRet = MCI_CleanUp(dwRet, wMsg, dwParam2);
2234
    TRACE("=> %08x\n", dwRet);
2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306
    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);
        return MMSYSERR_NOMEM;
    }
    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); 
}

/******************************************************************
 *		MyUserYield
 *
 * Internal wrapper to call USER.UserYield16 (in fact through a Wine only export from USER32).
 */
static void MyUserYield(void)
{
    HMODULE mod = GetModuleHandleA( "user32.dll" );
    if (mod)
    {
        FARPROC proc = GetProcAddress( mod, "UserYield16" );
        if (proc) proc();
    }
}

/**************************************************************************
 * 				MCI_DefYieldProc	       	[internal]
 */
UINT WINAPI MCI_DefYieldProc(MCIDEVICEID wDevID, DWORD data)
{
    INT16	ret;

2307
    TRACE("(0x%04x, 0x%08x)\n", wDevID, data);
2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329

    if ((HIWORD(data) != 0 && HWND_16(GetActiveWindow()) != HIWORD(data)) ||
	(GetAsyncKeyState(LOWORD(data)) & 1) == 0) {
	MyUserYield();
	ret = 0;
    } else {
	MSG		msg;

	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;

2330
    TRACE("(%u, %p, %08x)\n", uDeviceID, fpYieldProc, dwYieldData);
2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367

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

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

    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
     */
2368
    FIXME("(%u, %s) stub\n", dwElementID, debugstr_w(lpstrType));
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 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427
    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;
    }
    if (!wmd->bIs32) {
	WARN("Proc is 32 bit\n");
	return NULL;
    }
    return wmd->lpfnYieldProc;
}

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

    if ((wmd = MCI_GetDriver(uDeviceID))) ret = (HTASK)wmd->CreatorThread;

    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);

    if (!(wmd = MCI_GetDriver(uDeviceID)) || !wmd->lpfnYieldProc || !wmd->bIs32) {
	MyUserYield();
    } else {
	ret = wmd->lpfnYieldProc(uDeviceID, wmd->dwYieldData);
    }

    return ret;
}