/******************************************************************************

   Copyright (C) Microsoft Corporation 1985-1991. All rights reserved.

   Title:   graphic.c - Multimedia Systems Media Control Interface
            driver for AVI.

*****************************************************************************/

// Define HEARTBEAT to create a permanent thread which can periodically
// dump mciavi device status
//#define HEARTBEAT

#include "graphic.h"
#include "dispdib.h"
//#include "cnfgdlg.h"
#include <string.h>
#ifdef EXPIRE
#include <dos.h>
#endif
#include "avitask.h"

#ifdef DEBUG
#define static
#endif

#ifdef _WIN32
extern BOOL FAR PASCAL WowUseMciavi16(VOID);
#endif


//
//  This is the version number of MSVIDEO.DLL we need in order to run
//  build 81 is when we added the VideoForWindowsVersion() function to
//  MSVIDEO.DLL
//
//  in build 85
//    we removed the ICDecompressOpen() function and it became a macro.
//    we added a parameter to ICGetDisplayFormat()
//    we make DrawDibProfileDisplay() take a parameter
//
//  in build 108
//    Added ICOpenFunction() to open a hic using a function directly,
//      without calling ICInstall
//    Added some more ICDRAW_ messages
//
//  in build 109
//    Addded ICMessage() to compman
//    removed ICDrawSuggest() made it a macro.
//    Added ICMODE_FASTDECOMPRESS to ICLocate()
//
//  Under NT the first build is sufficient !!! Is this true now?
//
#ifdef _WIN32
#define MSVIDEO_VERSION     (0x01000000)          // 1.00.00.00
#else
#define MSVIDEO_VERSION     (0x010a0000l+109)     // 1.10.00.109
#endif

/* statics */
static INT              swCommandTable = -1;

#if 0
#ifdef _WIN32
static SZCODE           szDisplayDibLib[] = TEXT("DISPDB32.DLL");
#else
static SZCODE           szDisplayDibLib[] = TEXT("DISPDIB.DLL");
#endif
#endif

/*
 * files should be UNICODE. function names should not
 */

#ifdef _WIN32
STATICDT SZCODE         szMSVideo[]       = TEXT("MSVFW32");  // With GetModuleHandle
         const TCHAR    szReject[]        = TEXT("RejectWOWOpenCalls");
#else
static SZCODE           szMSVideo[]       = TEXT("MSVIDEO");
#endif

BOOL   gfEvil;          // TRUE if we cant close cuz dialog box is up
BOOL   gfEvilSysMenu;   // TRUE if we cant close cuz system menu is up

NPMCIGRAPHIC npMCIList; // list of all open instances.
#ifdef _WIN32
CRITICAL_SECTION MCIListCritSec;  // Must protect access to MCIList entries
#ifdef DEBUG
DWORD            ListOwner;
#endif // debug
#endif


/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api void | GraphicInit | This function is called when the DriverProc
 *      gets a DRV_LOAD message.
 *
 ***************************************************************************/
BOOL FAR PASCAL GraphicInit(void)
{
    InitializeDebugOutput("MCIAVI");

    if (!GraphicWindowInit())
        return FALSE;

    // The command table is name MCIAVI - same as the Ini/Registry/Module
    swCommandTable = mciLoadCommandResource(ghModule, szIni, 0);
#ifdef _WIN32
    InitializeCriticalSection(&MCIListCritSec);
#endif

    return TRUE;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicDrvOpen | This function is called when the DriverProc
 *      gets a DRV_OPEN message. This happens each time that a new movie
 *      is opened thru MCI.
 *
 * @parm LPMCI_OPEN_DRIVER_PARMS | lpOpen | Far pointer to the standard
 *      MCI open parameters
 *
 * @rdesc Returns the mci device id. The installable driver interface will
 *      pass this ID to the DriverProc in the dwDriverID parameter on all
 *      subsequent messages. To fail the open, return 0L.
 *
 ***************************************************************************/

DWORD PASCAL GraphicDrvOpen(LPMCI_OPEN_DRIVER_PARMS lpOpen)
{
    /* Specify the custom command table and the device type  */

    lpOpen->wCustomCommandTable = swCommandTable;
    lpOpen->wType = MCI_DEVTYPE_DIGITAL_VIDEO;

    /* Set the device ID to the MCI Device ID */

    return (DWORD) (UINT)lpOpen->wDeviceID;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api void | GraphicFree | This function is called when the DriverProc
 *      gets a DRV_FREE message. This happens when the drivers open count
 *      reaches 0.
 *
 ***************************************************************************/
#ifdef HEARTBEAT
BOOL    fTerminate      = FALSE;
HANDLE  hHeartBeatThread= 0;
#endif

void PASCAL GraphicFree(void)
{
    if (swCommandTable != -1) {
                mciFreeCommandResource(swCommandTable);
                swCommandTable = -1;
    }

#ifdef HEARTBEAT
    if (hHeartBeatThread) {
                fTerminate=TRUE;
                WaitForSingleObject(hHeartBeatThread, 5000);
                CloseHandle(hHeartBeatThread);
    }
#endif

#ifdef _WIN32
    /*
     * unregister class so we can re-register it next time we are loaded
     */
    GraphicWindowFree();
#ifdef REMOTESTEAL
    if (hkey) {
                RegCloseKey(hkey);
                hkey = 0;
    }
#endif
    DeleteCriticalSection(&MCIListCritSec);
#endif
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicDelayedNotify | This is a utility function that
 *      sends a notification saved with GraphicSaveCallback to mmsystem
 *      which posts a message to the application. Called on either worker
 *      or (occasionally if ever?) user thread.
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data.
 *
 * @parm UINT | wStatus | The type of notification to use can be one of
 *      MCI_NOTIFY_SUCCESSFUL, MCI_NOTIFY_SUPERSEDED, MCI_NOTIFY_ABORTED
 *      or MCI_NOTIFY_FAILURE (see MCI ispec.)
 *
 ***************************************************************************/

void FAR PASCAL GraphicDelayedNotify(NPMCIGRAPHIC npMCI, UINT wStatus)
{
    /* Send any saved notification */

    if (npMCI->hCallback) {

        DPF2(("GraphicDelayedNotify, npMCI=%8x, Status is %x\n", npMCI, wStatus));
        // If the system menu is the only thing keeping us from closing, bring
        // it down and then close.
        if (gfEvilSysMenu)
            SendMessage(npMCI->hwndPlayback, WM_CANCELMODE, 0, 0);

        // If a dialog box is up, and keeping us from closing, we can't send the
        // notify or it will close us.
        if (!gfEvil)
            mciDriverNotify(npMCI->hCallback, npMCI->wDevID, wStatus);

        npMCI->hCallback = NULL;
    }
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicImmediateNotify | This is a utility function that
 *      sends a successful notification message to mmsystem if the
 *      notification flag is set and the error field is 0.
 *
 * @parm UINT | wDevID | device ID.
 *
 * @parm LPMCI_GENERIC_PARMS | lpParms | Far pointer to an MCI parameter
 *      block. The first field of every MCI parameter block is the
 *      callback handle.
 *
 * @parm DWORD | dwFlags | Parm. block flags - used to check whether the
 *      callback handle is valid.
 *
 * @parm DWORD | dwErr | Notification only occurs if the command is not
 *      returning an error.
 *
 ***************************************************************************/

void FAR PASCAL GraphicImmediateNotify(UINT wDevID,
    LPMCI_GENERIC_PARMS lpParms,
    DWORD dwFlags, DWORD dwErr)
{
    if (!LOWORD(dwErr) && (dwFlags & MCI_NOTIFY)) {
        //Don't have an npMCI - see GraphicDelayedNotify
        //if (gfEvil)
            //SendMessage(npMCI->hwndPlayback, WM_CANCELMODE, 0, 0);

        // If a dialog box is up, and keeping us from closing, we can't send the
        // notify or it will close us.
        if (!gfEvil) // !!! EVIL !!!
            mciDriverNotify((HANDLE) (UINT)lpParms->dwCallback,
                                        wDevID, MCI_NOTIFY_SUCCESSFUL);
    }
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicSaveCallback | This is a utility function that saves
 *      a new callback in the instance data block.
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data.
 *
 * @parm HANDLE | hCallback | callback handle
 *
 ***************************************************************************/

void NEAR PASCAL GraphicSaveCallback (NPMCIGRAPHIC npMCI, HANDLE hCallback)
{
    /* If there's an old callback, kill it. */
    GraphicDelayedNotify(npMCI, MCI_NOTIFY_SUPERSEDED);

    /* Save new notification callback window handle */
    npMCI->hCallback = hCallback;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicClose | This function closes the movie and
 *  releases the instance data.
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data.
 *
 * @rdesc Returns an MCI error code.
 *
 ***************************************************************************/

DWORD PASCAL GraphicClose (NPMCIGRAPHIC npMCI)
{
    DWORD dwRet = 0L;
    NPMCIGRAPHIC p;

    if (npMCI) {

        SetNTFlags(npMCI, NTF_CLOSING);
#ifdef DEBUG
        npMCI->mciid = MCIIDX;
        if (TestNTFlags(npMCI, NTF_AUDIO_OFF)) {
            DPF1(("Trying to close while audio command active\n"));
            DebugBreak();
        }
#endif

        dwRet = DeviceClose (npMCI);
        Assert(dwRet == 0);

        // If the system menu is the only thing keeping us from closing, bring
        // it down and then close.
        if (gfEvilSysMenu)
            SendMessage(npMCI->hwndPlayback, WM_CANCELMODE, 0, 0);

        if (gfEvil) {
            DPF(("************************************************\n"));
            DPF(("** EVIL: Failing the close because we'd die   **\n"));
            DPF(("************************************************\n"));
            ResetNTFlags(npMCI, NTF_CLOSING);


            return MCIERR_DRIVER_INTERNAL;
        }

        //
        // find this instance on the list - and remove it
        // Do this before destroying any element in this instance
        //
        EnterList();
        if (npMCI == npMCIList) {
            npMCIList = npMCI->npMCINext;
        }
        else {
            for (p=npMCIList; p && p->npMCINext != npMCI; p=p->npMCINext)
                ;

            Assert(p && p->npMCINext == npMCI);

            p->npMCINext = npMCI->npMCINext;
        }
        LeaveList();

        if (npMCI->szFilename) {
            LocalFree((HANDLE) (npMCI->szFilename));
        }


#ifdef _WIN32

#ifdef DEBUG
       if (npMCI->hmmio) {
           DPF1(("hmmio is still open, npMCI=%8x\n", npMCI));
       }
       if (npMCI->hmmioAudio) {
           DPF1(("hmmioAudio is still open, npMCI=%8x\n", npMCI));
       }
#endif

#endif

#ifdef INTERVAL_TIMES
    // Free the bucket space
    if (npMCI->paIntervals) {
        LocalFree(npMCI->paIntervals);
    }
#endif

        /* Free the instance data block allocated in GraphicOpen */

        LocalFree((HANDLE)npMCI);
    }

    return dwRet;
}

DWORD NEAR PASCAL FixFileName(NPMCIGRAPHIC npMCI, LPCTSTR lpName)
{
    TCHAR       ach[256];

    ach[(NUMELMS(ach)) - 1] = TEXT('\0');

#ifndef _WIN32
    _fstrncpy(ach, (LPTSTR) lpName, NUMELMS(ach) - 1);
#else
    wcsncpy(ach, (LPTSTR) lpName, NUMELMS(ach) - 1);
#endif

    //
    // treat any string that starts with a '@' as valid and pass it to the
    // device any way.
    //
    if (ach[0] != TEXT('@'))
    {
        if (!mmioOpen(ach, NULL, MMIO_PARSE))
            return MCIERR_FILENAME_REQUIRED;
    }

    Assert(0 == npMCI->szFilename);
    npMCI->szFilename = (NPTSTR) LocalAlloc(LPTR,
                                    sizeof(TCHAR) * (lstrlen(ach) + 1));

    if (!npMCI->szFilename) {
        return MCIERR_OUT_OF_MEMORY;
    }

    lstrcpy(npMCI->szFilename, ach);

    return 0L;
}


/**************************************************************************

***************************************************************************/


#define SLASH(c)     ((c) == TEXT('/') || (c) == TEXT('\\'))

LPCTSTR FAR FileName(LPCTSTR szPath)
{
    LPCTSTR   sz;

    sz = &szPath[lstrlen(szPath)];
    for (; sz>szPath && !SLASH(*sz) && *sz!=TEXT(':');) {
        LPCTSTR pszOld = sz;
        if ((sz = CharPrev(szPath, sz)) == pszOld)
           break;
    }
    return (sz>szPath ? sz + 1 : sz);
}


/****************************************************************************
****************************************************************************/

STATICFN DWORD NEAR PASCAL GetMSVideoVersion()
{
    HANDLE h;

    extern DWORD FAR PASCAL VideoForWindowsVersion(void);

    //
    // don't call VideoForWindowsVersion() if it does not exist or KERNEL
    // will kill us with a undef dynalink error.
    //
    if ((h = GetModuleHandle(szMSVideo)) && GetProcAddress(h, (LPSTR) MAKEINTATOM(2)))
        return VideoForWindowsVersion();
    else
        return 0;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicOpen | This function opens a movie file,
 *      initializes an instance data block, and creates the default
 *      stage window.
 *
 * @parm NPMCIGRAPHIC FAR * | lpnpMCI | Far pointer to a near pointer
 *      to instance data block to be filled in by this function.
 *
 * @parm DWORD | dwFlags | Flags for the open message.
 *
 * @parm LPMCI_DGV_OPEN_PARMS | Parameters for the open message.
 *
 * @parm UINT | wDeviceID | The MCI Device ID for this instance.
 *
 * @rdesc Returns an MCI error code.
 *
 ***************************************************************************/
DWORD PASCAL GraphicOpen (NPMCIGRAPHIC FAR * lpnpMCI, DWORD dwFlags,
    LPMCI_DGV_OPEN_PARMS lpOpen, UINT wDeviceID)
{
    NPMCIGRAPHIC npMCI;
    DWORD       dwRet;

    if (IsNTWOW()) {
        // Thunking nasties... by default we will open a 16 bit request
        // on the 32 bit side.  Most of the time this is what is wanted.
        // However, there can be situations when the user must stay on the
        // 16 bit side (e.g. use of 16 bit drawprocs).  This is not known
        // at this stage.  Hence we provide a way through the configure
        // dialog for the user to tell us to reject 16 bit open calls.
        // The call will then return to 16 bit where it will work.
        // However, we provide a way for applications to override this
        // default.

        // All that is said above is good. Except not many people will notice
        // this little "Advanced" button ... In any event code below
        // uses wow32's special function to extract this needed information
        // (compatibility flag)

#ifdef _WIN32
        if (WowUseMciavi16()) {
           DPF2(("Compat Flag -- mciavi16 should be used\n"));
           return(MCIERR_DEVICE_OPEN);
        }
#endif

        if ((mmGetProfileInt(szIni, szReject, 0) && !(dwFlags & MCI_DGV_OPEN_32BIT))
           || (dwFlags & MCI_DGV_OPEN_16BIT) ) {
            DPF2(("Opening device on 16 bit side\n"));
            return MCIERR_DEVICE_OPEN; // return an error... any error

        }

        /*
        ** The only wow apps that I now off that specify the MCI_DGV_OPEN_PARENT
        ** flag are MS Dangerous Creatures and Tool Book.  These apps go on to
        ** subclass the AVIWnd32 window.  This is very bad on WOW as the
        ** subclassing stops all messages being processed for the AVIWnd32
        ** window.  Therefore, I will reject all open requests that specify
        ** this MCI_DGV_OPEN_PARENT flag.  This is pretty drastic but I don't
        ** know of any other way of identifying these rogue apps.
        **
        ** StephenE 16th Aug 1994.
        */
        else if (dwFlags & MCI_DGV_OPEN_PARENT) {
            //if (dwFlags & MCI_DGV_OPEN_32BIT) {
            //    DPF0(("Now what are we meant to do???\n"));
            //}
            DPF2(("Failing open because APP specified MCI_DGV_OPEN_PARENT\n"));
            return MCIERR_DEVICE_OPEN; // return an error... any error
        }
        else {
            DPF2(("might be able to open device on 32 bit side\n"));
        }
    }

    if (dwFlags & MCI_OPEN_SHAREABLE) {

        if (lpOpen->lpstrElementName == NULL ||
            lpOpen->lpstrElementName[0] != '@') {
            return MCIERR_UNSUPPORTED_FUNCTION;
        }
    }

    //
    //  check the verion of MSVIDEO.DLL before going any further
    //  if we run a "new" version of MCIAVI on a old MSVIDEO.DLL
    //  then bad things will happen.  We assume all MSVIDEO.DLLs
    //  will be backward compatible so we check for any version
    //  greater than the expected version.
    //

    DPF(("GraphicOpen: Video For Windows Version %d.%02d.%02d.%02d\n", HIBYTE(HIWORD(GetMSVideoVersion())), LOBYTE(HIWORD(GetMSVideoVersion())), HIBYTE(LOWORD(GetMSVideoVersion())), LOBYTE(LOWORD(GetMSVideoVersion())) ));

    if (GetMSVideoVersion() < MSVIDEO_VERSION)
    {
        TCHAR achError[128];
        TCHAR ach[40];

        LoadString(ghModule, MCIAVI_BADMSVIDEOVERSION, achError, NUMELMS(achError));
        LoadString(ghModule, MCIAVI_PRODUCTNAME, ach, NUMELMS(ach));
        MessageBox(NULL,achError,ach,
#ifdef BIDI
                MB_RTL_READING |
#endif
        MB_OK|MB_SYSTEMMODAL|MB_ICONEXCLAMATION);

        return MCIERR_DRIVER_INTERNAL;
    }

#ifndef _WIN32
#pragma message("Support passing in MMIOHANDLEs with OPEN_ELEMENT_ID?")
#endif

    if (lpOpen->lpstrElementName == NULL) {
        // they're doing an "open new".

        // !!! handle this, probably by not actually reading a file.
        // ack.
    }

    /* Be sure we have a real, non-empty filename, not an id. */
    if ((!(dwFlags & MCI_OPEN_ELEMENT))
            || (lpOpen->lpstrElementName == NULL)
            || (*(lpOpen->lpstrElementName) == '\0'))
        return MCIERR_UNSUPPORTED_FUNCTION;

    // Allocate an instance data block. Code ASSUMES Zero Init.

    if (!(npMCI = (NPMCIGRAPHIC) LocalAlloc(LPTR, sizeof (MCIGRAPHIC))))
        return MCIERR_OUT_OF_MEMORY;

#ifdef DEBUG
    npMCI->mciid = MCIID;
#ifdef HEARTBEAT
    {
        // Create a thread that regularly dumps the state of AVI devices.
        DWORD   HeartBeat(LPVOID lpvThreadData);
        if (!hHeartBeatThread) {
            DWORD       dwThreadId;
            hHeartBeatThread = CreateThread(NULL, 0, HeartBeat, (LPVOID)0, 0, &dwThreadId);
            if (hHeartBeatThread) {
                // We hold the thread handle open until we are unloaded
                DPF(("Created a heart beat thread, id=%x\n", dwThreadId));
            } else {
                // Errors are ignored...
            }
        }
    }
#endif
#endif

    //
    // add this device to our list
    //
    EnterList();
    npMCI->npMCINext = npMCIList;
    npMCIList = npMCI;
    LeaveList();

    npMCI->wMessageCurrent = MCI_OPEN;


    // Allocate some space for the filename
    // Copy the filename into the data block
    dwRet = FixFileName(npMCI, lpOpen->lpstrElementName);
    if (dwRet != 0L) {
        GraphicClose(npMCI);
        return dwRet;
    }

    // For the default window, the caller may
    // supply style and parent window. Note that the window
    // is now created on the background thread in mcaviTask.

    if (dwFlags & MCI_DGV_OPEN_PARENT) {
        if (!IsWindow(lpOpen->hWndParent)) {
            DPF(("Invalid parent window (%d) supplied\n", lpOpen->hWndParent));
            GraphicClose(npMCI);
            return(MCIERR_CREATEWINDOW);
        }
        npMCI->hwndParent = lpOpen->hWndParent;
    }
    else
        npMCI->hwndParent = NULL;

    if (dwFlags & MCI_DGV_OPEN_WS) {
        npMCI->dwStyle = lpOpen->dwStyle;
    } else {
        npMCI->dwStyle = WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX |
                  WS_SYSMENU | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
    }

    /* Fill in some more of the instance data.
    ** The rest of the fields are completed in DeviceOpen.
    */

    // see comment in graphic.h
    // If the default window won't be resizable, I don't think we should pay
    // attention to zoom by 2 or fixed % window size defaults.
    npMCI->fOKToUseDefaultSizing = (BOOL)((npMCI->dwStyle & WS_THICKFRAME) != 0);

    npMCI->hCallingTask = GetCurrentTask();
    npMCI->hCallback = NULL;
    npMCI->wDevID = wDeviceID;
    npMCI->hwndDefault = NULL;
    npMCI->hwndPlayback = NULL;
    npMCI->dwTimeFormat = MCI_FORMAT_FRAMES;
    npMCI->dwSpeedFactor = 1000;
    npMCI->dwVolume = MAKELONG(500, 500);
    npMCI->lTo = 0L;
    npMCI->dwFlags = MCIAVI_PLAYAUDIO | MCIAVI_SHOWVIDEO;
    npMCI->dwOptionFlags = ReadConfigInfo() | MCIAVIO_STRETCHTOWINDOW;

    // perform device-specific initialization

    dwRet = DeviceOpen(npMCI, dwFlags);

    if (dwRet != 0) {
        // see above - we release and delete the critsec within GraphicClose
        GraphicClose(npMCI);
        return dwRet;
    }

    // Reset these globals every time we open a device.  This is better
    // than setting them every time we start playing...
    gwHurryTolerance = mmGetProfileInt(szIni, TEXT("Hurry"), 2);
    gwSkipTolerance = mmGetProfileInt(szIni, TEXT("Skip"), gwHurryTolerance * 2);
    gwMaxSkipEver = mmGetProfileInt(szIni, TEXT("MaxSkip"), max(60, gwSkipTolerance * 10));

#ifdef INTERVAL_TIMES
    // Allocate the bucket space
    {
        LONG n = npMCI->lFrames;
        // Allocate bucket space for the whole movie... might be
        // expensive(!) so we reduce the count if no memory is
        // available
        DPF2(("Trying to allocate bucket space for %d frames\n", n));
        while( !(npMCI->paIntervals = LocalAlloc(LPTR, n*sizeof(LONG))) && n>10)
            n /= 2;

        if (npMCI->paIntervals) {
            npMCI->cIntervals = n;
            DPF2(("Allocated bucket space for %d frames\n", n));
        }
    }
#endif

    *lpnpMCI = npMCI;

    npMCI->wMessageCurrent = 0;

    return 0L;
}


/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicLoad | This function supports the MCI_LOAD command.
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data block
 *
 * @parm DWORD | dwFlags | Flags for the Load message.
 *
 * @parm LPMCI_DGV_LOAD_PARMS | lpLoad | Parameters for the LOAD message.
 *
 * @rdesc Returns an MCI error code.
 *
 ***************************************************************************/
DWORD NEAR PASCAL GraphicLoad(NPMCIGRAPHIC npMCI,
    DWORD dwFlags, LPMCI_DGV_LOAD_PARMS lpLoad)
{
    return MCIERR_UNSUPPORTED_FUNCTION;
}


/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicSeek | This function sets the current frame. The
 *      device state after a seek is MCI_MODE_PAUSE
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data block
 *
 * @parm DWORD | dwFlags | Flags for the seek message.
 *
 * @parm LPMCI_DGV_SEEK_PARMS | lpSeek | Parameters for the seek message.
 *
 * @rdesc Returns an MCI error code.
 *
 ***************************************************************************/

DWORD NEAR PASCAL GraphicSeek (NPMCIGRAPHIC npMCI, DWORD dwFlags,
    LPMCI_SEEK_PARMS lpSeek)
{
    LONG        lTo;
    BOOL        fTest = FALSE;
    LPARAM       dwCallback = 0;

    /* Do some range checking then pass onto the device-specific routine. */

    if (dwFlags & MCI_TEST) {
        dwFlags &= ~(MCI_TEST);
        fTest = TRUE;
    }

    /* Ignore the WAIT and NOTIFY flags when parsing the seek parameters */

    switch (dwFlags & (~(MCI_WAIT | MCI_NOTIFY))) {
        case MCI_TO:
            lTo = ConvertToFrames(npMCI, lpSeek->dwTo);
            break;

        case MCI_SEEK_TO_START:
            lTo = 0;
            break;

        case MCI_SEEK_TO_END:
            lTo = npMCI->lFrames;
            break;

        case 0:
            return MCIERR_MISSING_PARAMETER;

        default:
            if (dwFlags & ~(MCI_TO |
                            MCI_SEEK_TO_START |
                            MCI_SEEK_TO_END |
                            MCI_WAIT |
                            MCI_NOTIFY))
                return MCIERR_UNRECOGNIZED_KEYWORD;
            else
                return MCIERR_FLAGS_NOT_COMPATIBLE;
            break;
    }

    if (!IsWindow(npMCI->hwndPlayback))
        return MCIERR_NO_WINDOW;

    if (lTo < 0 || lTo > npMCI->lFrames)
        return MCIERR_OUTOFRANGE;

    if (fTest)
        return 0L;

    if (dwFlags & (MCI_NOTIFY)) {
        dwCallback = lpSeek->dwCallback;
    }

    return DeviceSeek(npMCI, lTo, dwFlags, dwCallback);
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicCue | This function gets the movie ready to play,
 *      but leaves it paused.
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data block
 *
 * @parm DWORD | dwFlags | Flags for the cue message.
 *
 * @parm LPMCI_DGV_CUE_PARMS | lpCue | Parameters for the cue message.
 *
 * @rdesc Returns an MCI error code.
 *
 ***************************************************************************/

DWORD NEAR PASCAL GraphicCue(NPMCIGRAPHIC npMCI, DWORD dwFlags,
    LPMCI_DGV_CUE_PARMS lpCue)
{
    LONG                lTo;
    DWORD               dwRet = 0L;

    if (dwFlags & MCI_DGV_CUE_INPUT)
        return MCIERR_UNSUPPORTED_FUNCTION;

    if (dwFlags & MCI_DGV_CUE_NOSHOW)
        return MCIERR_UNSUPPORTED_FUNCTION;

    if (dwFlags & MCI_TO) {
        lTo = ConvertToFrames(npMCI, lpCue->dwTo);

        if (lTo < 0L || lTo > npMCI->lFrames)
            return MCIERR_OUTOFRANGE;
    }

    /* If the test flag is set, return without doing anything. */
    /* Question: do we have to check for more possible errors? */
    if (dwFlags & MCI_TEST)
        return 0L;


    dwRet = DeviceCue(npMCI, lTo, dwFlags, lpCue->dwCallback);

    return dwRet;
}

#ifndef _WIN32
#ifdef EXPIRE
//
// return the current date....
//
//       dx = year
//       ah = month
//       al = day
//
#pragma optimize("", off)
DWORD DosGetDate(void)
{
    if (0)
        return 0;

    _asm {
        mov     ah,2ah
        int     21h
        mov     ax,dx
        mov     dx,cx
    }
}
#pragma optimize("", on)
#endif
#endif

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicPlay | This function starts playback of the movie. If
 *      the reverse flag is specified, the movie plays backwards. If the fast
 *      or slow flags are specified the movie plays faster or slower.
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data block
 *
 * @parm DWORD | dwFlags | Flags for the play message.
 *
 * @parm LPMCI_DGV_PLAY_PARMS | lpPlay | Parameters for the play message.
 *
 * @rdesc Returns an MCI error code.
 *
 ***************************************************************************/

DWORD NEAR PASCAL GraphicPlay (NPMCIGRAPHIC npMCI, DWORD dwFlags,
    LPMCI_PLAY_PARMS lpPlay )
{
        HINSTANCE       hInst;
    TCHAR       achMod[MAX_PATH];
    BOOL        fMPlayer;

#ifdef EXPIRE
#pragma message("Remove the expiration code after Beta ships")
    if (DosGetDate() >= EXPIRE)
    {
        return MCIERR_AVI_EXPIRED;
    }
#endif

    // If we haven't specifically asked for fullscreen or window, and we are
    // the default window, listen to the configure options and maybe force
    // fullscreen.  We will also obey the fullscreen default for MPLAYER's
    // window, to make mplayer just like using the default window.
    hInst = GetWindowInstance(npMCI->hwndPlayback);
    if (hInst)
                GetModuleFileName(hInst, achMod, sizeof(achMod) / sizeof(achMod[0]));
    fMPlayer = lstrcmpi(FileName(achMod), TEXT ("MPLAY32.EXE")) == 0;

    if (!(dwFlags & (MCI_MCIAVI_PLAY_FULLSCREEN | MCI_MCIAVI_PLAY_WINDOW))
                && (npMCI->hwndPlayback == npMCI->hwndDefault || fMPlayer)) {

        if ((npMCI->dwOptionFlags & MCIAVIO_USEVGABYDEFAULT) ||
                (npMCI->rcDest.left == 0 &&
                        npMCI->rcDest.right == GetSystemMetrics(SM_CXSCREEN)) ||
                (npMCI->rcDest.top == 0 &&
                        npMCI->rcDest.bottom == GetSystemMetrics(SM_CYSCREEN))) {
            dwFlags |= MCI_MCIAVI_PLAY_FULLSCREEN;
        }
    }

        // see comment in graphic.h on npMCI->fOKToUseDefaultSizing
        // We're playing, which will end up showing the window
        // and we may want to pay attention to the registry default
        // sizing (zoom by 2, fixed % of screen size, etc.)
        // We also do this code when somebody calls "window state show".
        // As an extra note I should say that it's important that we
        // don't change it until now so that if somebody opens a file
        // and does a "where destination" they get the original size,
        // because if they're playing in their own window, it will
        // not be altered anyway.
        if (npMCI->fOKToUseDefaultSizing) {
            SetWindowToDefaultSize(npMCI, TRUE);
            ResetDestRect(npMCI, TRUE);
        }

        // Never do this again
        npMCI->fOKToUseDefaultSizing = FALSE;


    // everything else needs to be on the worker thread to be reliable.
    return DevicePlay(npMCI, dwFlags, lpPlay, lpPlay->dwCallback);
}



/***************************************************************************
 *
 * @doc INTERNAL MCIWAVE
 *
 * @api DWORD | GraphicStep | This function steps through several frames
 *      of a movie. If the reverse flag is set, then the step is backwards.
 *      If the step count is not specified then it defaults to 1. If the
 *      step count plus the current position exceeds the movie length, the
 *      step is out of range.
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data block
 *
 * @parm DWORD | dwFlags | Flags for the step message.
 *
 * @parm LPMCI_DGV_STEP_PARMS | lpStep | Parameters for the step message.
 *
 * @rdesc Returns an MCI error code.
 *
 ***************************************************************************/

DWORD NEAR PASCAL GraphicStep (NPMCIGRAPHIC npMCI, DWORD dwFlags,
    LPMCI_DGV_STEP_PARMS lpStep)
{
    LONG        lFrameCur;
    LONG        lFrames;
    DWORD       dwRet;
    BOOL        fReverse;
    BOOL        fSeekExactOff;

    fReverse = (dwFlags & MCI_DGV_STEP_REVERSE) == MCI_DGV_STEP_REVERSE;

    // Default to 1 frame step if frame count is not specified

    if (dwFlags & MCI_DGV_STEP_FRAMES) {
        lFrames = (LONG) lpStep->dwFrames;

        if (fReverse) {
            if (lFrames < 0)
                return MCIERR_FLAGS_NOT_COMPATIBLE;
        }
    } else
        lFrames = 1;


    lFrames = fReverse ? -lFrames : lFrames;

    /* stop before figuring out whether frame count is within range, */
    /* unless the TEST flag is set. */

    if (!(dwFlags & MCI_TEST)) {
        if (dwRet = DeviceStop(npMCI, MCI_WAIT))
            return dwRet;
    }

    if (dwRet = DevicePosition(npMCI, &lFrameCur))
        return dwRet;

    if ((lFrames + lFrameCur > npMCI->lFrames) ||
                (lFrames + lFrameCur < 0))
        return MCIERR_OUTOFRANGE;

    if (!IsWindow(npMCI->hwndPlayback))
        return MCIERR_NO_WINDOW;

    /* If the test flag is set, return without doing anything. */
    /* Question: do we have to check for more possible errors? */
    if (dwFlags & MCI_TEST)
        return 0L;


    fSeekExactOff = (npMCI->dwOptionFlags & MCIAVIO_SEEKEXACT) == 0;

    npMCI->dwOptionFlags |= MCIAVIO_SEEKEXACT;

    npMCI->dwFlags |= MCIAVI_NEEDTOSHOW;

    if (fSeekExactOff) {
        /* If we were not in seek exact mode, make seek finish
        ** before we turn seek exact back off.
        */
        dwRet = DeviceSeek(npMCI, lFrames + lFrameCur, dwFlags | MCI_WAIT,
                    lpStep->dwCallback);
        npMCI->dwOptionFlags &= ~(MCIAVIO_SEEKEXACT);
    } else {
        dwRet = DeviceSeek(npMCI, lFrames + lFrameCur, dwFlags,
                    lpStep->dwCallback);
    }

    return dwRet;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicStop | This function stops playback of the movie.
 *      After a stop the state will be MCI_MODE_STOP. The frame counter
 *      is not reset.
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data block
 *
 * @parm DWORD | dwFlags | Flags for the stop message.
 *
 * @rdesc Returns an MCI error code.
 *
 ***************************************************************************/

DWORD NEAR PASCAL GraphicStop (NPMCIGRAPHIC npMCI, DWORD dwFlags,
                                        LPMCI_GENERIC_PARMS lpParms)
{
    if (!IsWindow(npMCI->hwndPlayback))
        return MCIERR_NO_WINDOW;

    if (dwFlags & MCI_DGV_STOP_HOLD)
        return MCIERR_UNSUPPORTED_FUNCTION;

    /* If the test flag is set, return without doing anything. */
    /* Question: do we have to check for more possible errors? */
    if (dwFlags & MCI_TEST)
        return 0L;

    // this notify should be done on the worker thread if needed, but actually
    // I don't think we need it, since the play code will issue it (either
    // as an abort or as a success if it reached the right target).
    //GraphicDelayedNotify (npMCI, MCI_NOTIFY_ABORTED);

    /* Do we need to handle notify here? */
    /* Do we have the swing at Luton? */
    return DeviceStop(npMCI, dwFlags);
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicPause | Pauses movie playback.
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data block
 *
 * @rdesc Returns an MCI error code.
 *
 ***************************************************************************/

DWORD NEAR PASCAL GraphicPause(NPMCIGRAPHIC npMCI, DWORD dwFlags,
                                        LPMCI_GENERIC_PARMS lpParms)
{
    LPARAM dwCallback;
    if (!IsWindow(npMCI->hwndPlayback))
        return MCIERR_NO_WINDOW;

    /* If the test flag is set, return without doing anything. */
    /* Question: do we have to check for more possible errors? */
    if (dwFlags & MCI_TEST)
        return 0L;

    if (lpParms) {
        dwCallback = lpParms->dwCallback;
    } else {
        dwCallback = 0;
    }
    return DevicePause(npMCI, dwFlags, dwCallback);
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicResume | This function resumes playback of a paused
 *      movie.
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data block
 *
 * @rdesc Returns an MCI error code.
 *
 ***************************************************************************/

DWORD NEAR PASCAL GraphicResume (NPMCIGRAPHIC npMCI,
    DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
{
    LPARAM      dwCallback;

    //  Resume used to be only allowed if MCIAVI is paused or playing
    //  This is a little strange as some of the MCI commands automatically
    //  change PAUSED into STOPPED. There is no reason why we shouldn't
    //  treat resume as equivalent to play.  (As does CDPLAYER.)
    //
    // if you decide to disagree, then don't forget that the DeviceMode()
    // check can only safely be done *on the worker thread*


    if (!IsWindow(npMCI->hwndPlayback))
        return MCIERR_NO_WINDOW;

    /* If the test flag is set, return without doing anything. */
    /* Question: do we have to check for more possible errors? */
    if (dwFlags & MCI_TEST)
        return 0L;


    if (lpParms) {
        dwCallback = lpParms->dwCallback;
    } else {
        dwCallback = 0;
    }
    return DeviceResume(npMCI, dwFlags & MCI_WAIT, dwCallback);
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicStatus | This function returns numeric status info.
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data block
 *
 * @parm DWORD | dwFlags | Flags for the status message.
 *
 * @parm LPMCI_STATUS_PARMS | lpPlay | Parameters for the status message.
 *
 * @rdesc Returns an MCI error code.
 *
 ***************************************************************************/

DWORD NEAR PASCAL GraphicStatus (NPMCIGRAPHIC npMCI,
    DWORD dwFlags, LPMCI_DGV_STATUS_PARMS lpStatus)
{
    DWORD dwRet = 0L;

    if (dwFlags & (MCI_DGV_STATUS_DISKSPACE))
        return MCIERR_UNSUPPORTED_FUNCTION;

    if (dwFlags & MCI_STATUS_ITEM) {

        lpStatus->dwReturn = 0L;

        if ((dwFlags & MCI_TRACK) &&
                !((lpStatus->dwItem == MCI_STATUS_POSITION) ||
                        (lpStatus->dwItem == MCI_STATUS_LENGTH)))
            return MCIERR_FLAGS_NOT_COMPATIBLE;

        if ((dwFlags & MCI_STATUS_START) &&
                        (lpStatus->dwItem != MCI_STATUS_POSITION))
            return MCIERR_FLAGS_NOT_COMPATIBLE;

        if (dwFlags & MCI_DGV_STATUS_REFERENCE)
            return MCIERR_FLAGS_NOT_COMPATIBLE;

        switch (lpStatus->dwItem) {
            case MCI_STATUS_POSITION:

                if (dwFlags & MCI_TRACK) {
                    /* POSITION with TRACK means return the start of the  */
                    /* track. */

                    if (lpStatus->dwTrack != 1)
                        dwRet = MCIERR_OUTOFRANGE;
                    else
                        /* return start frame of track (always 0) */
                        lpStatus->dwReturn = 0L;
                } else if (dwFlags & MCI_STATUS_START)
                    // POSITION with START means return the starting playable
                    // position of the media.
                    lpStatus->dwReturn = 0L;
                else {
                    /* Otherwise return current frame */
                    dwRet = DevicePosition(npMCI, (LPLONG) &lpStatus->dwReturn);
                    lpStatus->dwReturn = ConvertFromFrames(npMCI,
                                                (LONG) lpStatus->dwReturn);
                }
                break;

            case MCI_STATUS_LENGTH:


                if (dwFlags & MCI_TRACK && lpStatus->dwTrack != 1) {
                    /* LENGTH with TRACK means return the length of track */

                    lpStatus->dwReturn = 0L;
                    dwRet = MCIERR_OUTOFRANGE;
                }

                lpStatus->dwReturn = ConvertFromFrames(npMCI, npMCI->lFrames);
                break;

            case MCI_STATUS_NUMBER_OF_TRACKS:
            case MCI_STATUS_CURRENT_TRACK:

                lpStatus->dwReturn = 1L;
                break;

            case MCI_STATUS_READY:

                /* Return TRUE if device can receive commands */
                if (DeviceMode(npMCI) != MCI_MODE_NOT_READY)
                    lpStatus->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
                else
                    lpStatus->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
                dwRet = MCI_RESOURCE_RETURNED;
                break;

            case MCI_STATUS_MODE:
            {
                WORD    wMode;
                wMode = (WORD) DeviceMode(npMCI);
                lpStatus->dwReturn = (DWORD) MAKEMCIRESOURCE(wMode, wMode);
                dwRet = MCI_RESOURCE_RETURNED;
            }
                break;

            case MCI_DGV_STATUS_PAUSE_MODE:
                if (DeviceMode(npMCI) != MCI_MODE_PAUSE)
                    dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
                else {
                    lpStatus->dwReturn = MAKEMCIRESOURCE(MCI_MODE_PLAY, MCI_MODE_PLAY);
                    dwRet = MCI_RESOURCE_RETURNED;
                }
                break;

            case MCI_STATUS_MEDIA_PRESENT:

                lpStatus->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
                dwRet = MCI_RESOURCE_RETURNED;
                break;

            case MCI_DGV_STATUS_FORWARD:
                if (npMCI->dwFlags & MCIAVI_REVERSE)
                    lpStatus->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
                else
                    lpStatus->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
                dwRet = MCI_RESOURCE_RETURNED;
                break;

            case MCI_DGV_STATUS_HWND:

                lpStatus->dwReturn = (DWORD_PTR)(UINT_PTR)npMCI->hwndPlayback;
                if (!IsWindow(npMCI->hwndPlayback))
                    dwRet = MCIERR_NO_WINDOW;
                break;

            case MCI_DGV_STATUS_HPAL:

//              lpStatus->dwReturn = (DWORD) (UINT) DrawDibGetPalette(npMCI->hdd);

                lpStatus->dwReturn = 0;

                if (npMCI->nVideoStreams == 0) {
                    dwRet = MCIERR_UNSUPPORTED_FUNCTION;
                } else {
                    LRESULT l = ICSendMessage(npMCI->hicDraw, ICM_DRAW_GET_PALETTE, 0, 0);

                    if (l == ICERR_UNSUPPORTED) {
                        dwRet = MCIERR_UNSUPPORTED_FUNCTION;
                    } else {
                        lpStatus->dwReturn = l;
                        dwRet = 0;
                    }
                }
                DPF2(("Status HPAL returns: %lu\n", lpStatus->dwReturn));
                break;

            case MCI_STATUS_TIME_FORMAT:

                lpStatus->dwReturn = MAKEMCIRESOURCE(npMCI->dwTimeFormat,
                                npMCI->dwTimeFormat + MCI_FORMAT_RETURN_BASE);
                dwRet = MCI_RESOURCE_RETURNED;
                break;

            case MCI_DGV_STATUS_AUDIO:
                lpStatus->dwReturn = (npMCI->dwFlags & MCIAVI_PLAYAUDIO) ?
                                        (MAKEMCIRESOURCE(MCI_ON, MCI_ON_S)) :
                                        (MAKEMCIRESOURCE(MCI_OFF, MCI_OFF_S));
                dwRet = MCI_RESOURCE_RETURNED | MCI_RESOURCE_DRIVER;
                break;

            case MCI_DGV_STATUS_WINDOW_VISIBLE:
                if (npMCI->hwndPlayback && IsWindowVisible(npMCI->hwndPlayback))
                    lpStatus->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
                else
                    lpStatus->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
                dwRet = MCI_RESOURCE_RETURNED;
                break;

            case MCI_DGV_STATUS_WINDOW_MINIMIZED:
                if (npMCI->hwndPlayback && IsIconic(npMCI->hwndPlayback))
                    lpStatus->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
                else
                    lpStatus->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
                dwRet = MCI_RESOURCE_RETURNED;
                break;

            case MCI_DGV_STATUS_WINDOW_MAXIMIZED:
                if (npMCI->hwndPlayback && IsZoomed(npMCI->hwndPlayback))
                    lpStatus->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
                else
                    lpStatus->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
                dwRet = MCI_RESOURCE_RETURNED;
                break;

            case MCI_DGV_STATUS_SAMPLESPERSEC:
            case MCI_DGV_STATUS_AVGBYTESPERSEC:
            case MCI_DGV_STATUS_BLOCKALIGN:
            case MCI_DGV_STATUS_BITSPERSAMPLE:
                dwRet = MCIERR_UNSUPPORTED_FUNCTION;
                break;

            case MCI_DGV_STATUS_BITSPERPEL:
                if (npMCI->psiVideo)
                    lpStatus->dwReturn = ((LPBITMAPINFOHEADER)npMCI->psiVideo->lpFormat)->biBitCount;
                else
                    dwRet = MCIERR_UNSUPPORTED_FUNCTION;
                break;

#ifndef _WIN32
#pragma message("Are we going to support brightness/color/contrast/tint?")
#endif
            case MCI_DGV_STATUS_BRIGHTNESS:
            case MCI_DGV_STATUS_COLOR:
            case MCI_DGV_STATUS_CONTRAST:
            case MCI_DGV_STATUS_TINT:
            case MCI_DGV_STATUS_GAMMA:
            case MCI_DGV_STATUS_SHARPNESS:
            case MCI_DGV_STATUS_FILE_MODE:
            case MCI_DGV_STATUS_FILE_COMPLETION:
            case MCI_DGV_STATUS_KEY_INDEX:
            case MCI_DGV_STATUS_KEY_COLOR:
                dwRet = MCIERR_UNSUPPORTED_FUNCTION;
                break;

            case MCI_DGV_STATUS_FILEFORMAT:
// Fall through to Unsupported case...
//                lpStatus->dwReturn = MAKEMCIRESOURCE(MCI_DGV_FF_AVI,
//                                              MCI_DGV_FF_AVI);
//              dwRet = MCI_RESOURCE_RETURNED | MCI_RESOURCE_DRIVER;
//              break;
//
            case MCI_DGV_STATUS_BASS:
            case MCI_DGV_STATUS_TREBLE:
                dwRet = MCIERR_UNSUPPORTED_FUNCTION;
                break;

            case MCI_DGV_STATUS_VOLUME:
            {
                WORD    wLeftVolume, wRightVolume;
                // Be sure volume is up to date....
                DeviceGetVolume(npMCI);

                wLeftVolume = LOWORD(npMCI->dwVolume);
                wRightVolume = LOWORD(npMCI->dwVolume);

                switch (dwFlags & (MCI_DGV_STATUS_LEFT | MCI_DGV_STATUS_RIGHT)) {
                    case MCI_DGV_STATUS_LEFT:
                        lpStatus->dwReturn = (DWORD) wLeftVolume;
                    break;

                    case 0:
                        lpStatus->dwReturn = (DWORD) wRightVolume;
                    break;

                    default:
                        lpStatus->dwReturn = ((DWORD) wLeftVolume + (DWORD) wRightVolume) / 2;
                    break;
                }
            }
                break;

            case MCI_DGV_STATUS_MONITOR:
                lpStatus->dwReturn = (DWORD)
                                     MAKEMCIRESOURCE(MCI_DGV_MONITOR_FILE,
                                                MCI_DGV_FILE_S);
                dwRet = MCI_RESOURCE_RETURNED | MCI_RESOURCE_DRIVER;
                break;

            case MCI_DGV_STATUS_SEEK_EXACTLY:
                lpStatus->dwReturn =
                                (npMCI->dwOptionFlags & MCIAVIO_SEEKEXACT) ?
                                        (MAKEMCIRESOURCE(MCI_ON, MCI_ON_S)) :
                                        (MAKEMCIRESOURCE(MCI_OFF, MCI_OFF_S));
                dwRet = MCI_RESOURCE_RETURNED | MCI_RESOURCE_DRIVER;
                break;

            case MCI_DGV_STATUS_SIZE:
                /* We haven't reserved any space, so return zero. */
                lpStatus->dwReturn = 0L;
                break;

            case MCI_DGV_STATUS_SMPTE:
                dwRet = MCIERR_UNSUPPORTED_FUNCTION;
                break;

            case MCI_DGV_STATUS_UNSAVED:
                lpStatus->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
                dwRet = MCI_RESOURCE_RETURNED;
                break;

            case MCI_DGV_STATUS_VIDEO:
                lpStatus->dwReturn = (npMCI->dwFlags & MCIAVI_SHOWVIDEO) ?
                                        (MAKEMCIRESOURCE(MCI_ON, MCI_ON_S)) :
                                        (MAKEMCIRESOURCE(MCI_OFF, MCI_OFF_S));
                dwRet = MCI_RESOURCE_RETURNED | MCI_RESOURCE_DRIVER;
                break;

            case MCI_DGV_STATUS_SPEED:
                lpStatus->dwReturn = npMCI->dwSpeedFactor;
                break;

            case MCI_DGV_STATUS_FRAME_RATE:
            {
                DWORD   dwTemp;

                dwTemp = npMCI->dwMicroSecPerFrame;

                /* If they haven't specifically asked for the "nominal"
                ** rate of play, adjust by the current speed.
                */
                if (!(dwFlags & MCI_DGV_STATUS_NOMINAL))
                    dwTemp = muldiv32(dwTemp, 1000L, npMCI->dwSpeedFactor);

                if (dwTemp == 0)
                    lpStatus->dwReturn = 1000;
                else
                    /* Our return value is in "thousandths of frames/sec",
                    ** and dwTemp is the number of microseconds per frame.
                    ** Thus, we divide a billion microseconds by dwTemp.
                    */
                    lpStatus->dwReturn = muldiv32(1000000L, 1000L, dwTemp);
                break;
            }

            case MCI_DGV_STATUS_AUDIO_STREAM:
                lpStatus->dwReturn = 0;
                if (npMCI->nAudioStreams) {
                    int stream;

                    for (stream = 0; stream < npMCI->streams; stream++) {
                        if (SH(stream).fccType == streamtypeAUDIO)
                            ++lpStatus->dwReturn;

                        if (stream == npMCI->nAudioStream)
                            break;
                    }
                }
                break;

            case MCI_DGV_STATUS_VIDEO_STREAM:
            case MCI_DGV_STATUS_AUDIO_INPUT:
            case MCI_DGV_STATUS_AUDIO_RECORD:
            case MCI_DGV_STATUS_AUDIO_SOURCE:
            case MCI_DGV_STATUS_VIDEO_RECORD:
            case MCI_DGV_STATUS_VIDEO_SOURCE:
            case MCI_DGV_STATUS_VIDEO_SRC_NUM:
            case MCI_DGV_STATUS_MONITOR_METHOD:
            case MCI_DGV_STATUS_STILL_FILEFORMAT:
                dwRet = MCIERR_UNSUPPORTED_FUNCTION;
                break;

            case MCI_AVI_STATUS_FRAMES_SKIPPED:
                lpStatus->dwReturn = npMCI->lSkippedFrames;
                break;

            case MCI_AVI_STATUS_AUDIO_BREAKS:
                lpStatus->dwReturn = npMCI->lAudioBreaks;
                break;

            case MCI_AVI_STATUS_LAST_PLAY_SPEED:
                lpStatus->dwReturn = npMCI->dwSpeedPercentage;
                break;

            default:
                dwRet = MCIERR_UNSUPPORTED_FUNCTION;
                break;
        } /* end switch (item) */
    } else if (dwFlags & MCI_DGV_STATUS_REFERENCE) {

        if (lpStatus->dwReference > (DWORD) npMCI->lFrames)
            dwRet = MCIERR_OUTOFRANGE;

        else if (npMCI->psiVideo) {
            DWORD dwReference;
            dwReference = MovieToStream(npMCI->psiVideo,
                    lpStatus->dwReference);

            lpStatus->dwReturn = FindPrevKeyFrame(npMCI, npMCI->psiVideo,
                    dwReference);

            lpStatus->dwReturn = StreamToMovie(npMCI->psiVideo,
                    (DWORD) lpStatus->dwReturn);
        }
        else {
            lpStatus->dwReturn = 0;
        }
    } else /* item flag not set */
        dwRet = MCIERR_MISSING_PARAMETER;

    if ((dwFlags & MCI_TEST) && (LOWORD(dwRet) == 0)) {
        /* There is no error, but the test flag is on.  Return as little
        ** as possible.
        */
        dwRet = 0;
        lpStatus->dwReturn = 0;
    }

    return dwRet;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicInfo | This function returns alphanumeric information.
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data block
 *
 * @parm DWORD | dwFlags | Flags for the info. message.
 *
 * @parm LPMCI_INFO_PARMS | lpPlay | Parameters for the info message.
 *
 * @rdesc Returns an MCI error code.
 *
 ***************************************************************************/

DWORD NEAR PASCAL GraphicInfo(NPMCIGRAPHIC npMCI, DWORD dwFlags,
    LPMCI_DGV_INFO_PARMS lpInfo)
{
    DWORD       dwRet = 0L;
    TCHAR       ch = TEXT('\0');
    BOOL        fTest = FALSE;

    if (!lpInfo->lpstrReturn)
        return MCIERR_PARAM_OVERFLOW;

    if (dwFlags & MCI_TEST)
        fTest = TRUE;

    dwFlags &= ~(MCI_WAIT | MCI_NOTIFY | MCI_TEST);

    switch (dwFlags) {
    case 0L:
        return MCIERR_MISSING_PARAMETER;

    case MCI_INFO_FILE:
        if (!npMCI)
            return MCIERR_UNSUPPORTED_FUNCTION;

        if (lpInfo->dwRetSize < (DWORD)(lstrlen(npMCI->szFilename) + 1)) {
            ch = npMCI->szFilename[lpInfo->dwRetSize];
            npMCI->szFilename[lpInfo->dwRetSize] = '\0';
            dwRet = MCIERR_PARAM_OVERFLOW;
        }
        lstrcpy (lpInfo->lpstrReturn, npMCI->szFilename);
        if (ch)
            npMCI->szFilename[lpInfo->dwRetSize] = ch;
        break;

    case MCI_INFO_PRODUCT:

#ifdef _WIN32
    {
        UINT n;
#ifdef DEBUG
        TCHAR versionstring[80];
#endif
        n = LoadString(ghModule, MCIAVI_PRODUCTNAME, lpInfo->lpstrReturn,
                (UINT)lpInfo->dwRetSize);
#ifdef DEBUG
        #include "verinfo.h"

        n += wsprintf(versionstring,
            TEXT(" (%d.%02d.%02d)"), MMVERSION, MMREVISION, MMRELEASE);
        if (n <= lpInfo->dwRetSize) {
            lstrcat(lpInfo->lpstrReturn, versionstring);
        }
#endif
    }
#else // Win16 version follows
#ifdef DEBUG
        #include "..\verinfo\usa\verinfo.h"

        wsprintf(lpInfo->lpstrReturn,
            TEXT("VfW %d.%02d.%02d"), MMVERSION, MMREVISION, MMRELEASE);
#else
        /* !!! Not returning PARAM_OVERFLOW here but I am above - lazy eh */
        LoadString(ghModule, MCIAVI_PRODUCTNAME, lpInfo->lpstrReturn,
                (UINT)lpInfo->dwRetSize);
#endif
#endif
        break;

    case MCI_DGV_INFO_TEXT:
        if (!npMCI)
            return MCIERR_UNSUPPORTED_FUNCTION;

        if (IsWindow(npMCI->hwndPlayback))
            GetWindowText(npMCI->hwndPlayback, lpInfo->lpstrReturn,
                                        LOWORD(lpInfo->dwRetSize));
        else
            dwRet = MCIERR_NO_WINDOW;
        break;

    case MCI_INFO_VERSION:
        /* !!! Not returning PARAM_OVERFLOW here but I am above - lazy eh */
        LoadString(ghModule, MCIAVI_VERSION, lpInfo->lpstrReturn,
                (UINT)lpInfo->dwRetSize);
        break;

        case MCI_DGV_INFO_USAGE:
            dwRet = MCIERR_UNSUPPORTED_FUNCTION;
            break;

    case MCI_DGV_INFO_ITEM:
        switch (lpInfo->dwItem) {
        case MCI_DGV_INFO_AUDIO_QUALITY:
        case MCI_DGV_INFO_VIDEO_QUALITY:
        case MCI_DGV_INFO_STILL_QUALITY:
        case MCI_DGV_INFO_AUDIO_ALG:
        case MCI_DGV_INFO_VIDEO_ALG:
        case MCI_DGV_INFO_STILL_ALG:
        default:
            dwRet = MCIERR_UNSUPPORTED_FUNCTION;
            break;
        }
        break;

    default:
        dwRet = MCIERR_FLAGS_NOT_COMPATIBLE;
        break;
    }

    if (fTest && (LOWORD(dwRet) == 0)) {
        /* There is no error, but the test flag is on.  Return as little
        ** as possible.
        */
        dwRet = 0;
        if (lpInfo->dwRetSize)
            lpInfo->lpstrReturn[0] = '\0';
    }

    return dwRet;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicSet | This function sets various options.
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data block
 *
 * @parm DWORD | dwFlags | Flags for the set message.
 *
 * @parm LPMCI_SET_PARMS | lpSet | Parameters for the set message.
 *
 * @rdesc Returns an MCI error code.
 *
 ***************************************************************************/

DWORD NEAR PASCAL GraphicSet (NPMCIGRAPHIC npMCI,
    DWORD dwFlags, LPMCI_DGV_SET_PARMS lpSet)
{
    DWORD       dwRet = 0L;
    DWORD       dwAction;

    if (dwFlags & MCI_DGV_SET_FILEFORMAT)
        return MCIERR_UNSUPPORTED_FUNCTION;

    if (dwFlags & MCI_DGV_SET_STILL)
        return MCIERR_UNSUPPORTED_FUNCTION;

    dwAction = dwFlags & (MCI_SET_TIME_FORMAT           |
                         MCI_SET_VIDEO                  |
                         MCI_SET_AUDIO                  |
                         MCI_DGV_SET_SEEK_EXACTLY       |
                         MCI_DGV_SET_SPEED
                             );

    /* Turn off all but the following three flags */
    dwFlags &=  (MCI_SET_ON                             |
                         MCI_SET_OFF                    |
                         MCI_TEST
                             );

    /* First, check if the parameters are all okay */

    if (!dwAction)
        return MCIERR_UNSUPPORTED_FUNCTION;

    if (dwAction & MCI_SET_TIME_FORMAT) {
        if (lpSet->dwTimeFormat != MCI_FORMAT_FRAMES
                && lpSet->dwTimeFormat != MCI_FORMAT_MILLISECONDS)
            return MCIERR_UNSUPPORTED_FUNCTION;
    }

    if ((dwAction & MCI_SET_AUDIO) &&
                (lpSet->dwAudio != MCI_SET_AUDIO_ALL)) {
        return MCIERR_UNSUPPORTED_FUNCTION;
    }

    if (dwAction & MCI_DGV_SET_SPEED) {
        if (lpSet->dwSpeed > 100000L)
            return MCIERR_OUTOFRANGE;
    }

    switch (dwFlags & (MCI_SET_ON | MCI_SET_OFF)) {
        case 0:
            if (dwAction & (MCI_SET_AUDIO |
                                MCI_SET_VIDEO |
                                MCI_DGV_SET_SEEK_EXACTLY))
                return MCIERR_MISSING_PARAMETER;
            break;

        case MCI_SET_ON | MCI_SET_OFF:
            return MCIERR_FLAGS_NOT_COMPATIBLE;

        default:
            if (dwAction & (MCI_DGV_SET_SPEED | MCI_SET_TIME_FORMAT))
                return MCIERR_FLAGS_NOT_COMPATIBLE;
            break;
    }

    /* If the test flag is set, return without doing anything. */
    /* Question: do we have to check for more possible errors? */
    if (dwFlags & MCI_TEST)
        return 0L;

    /* Now, actually carry out the command */
    if (dwAction & MCI_SET_TIME_FORMAT)
        npMCI->dwTimeFormat = lpSet->dwTimeFormat;

    if (dwAction & MCI_SET_VIDEO) {
        npMCI->dwFlags &= ~(MCIAVI_SHOWVIDEO);
        if (dwFlags & MCI_SET_ON) {
            npMCI->dwFlags |= MCIAVI_SHOWVIDEO;
            InvalidateRect(npMCI->hwndPlayback, NULL, FALSE);
        }
    }

    if (dwAction & MCI_DGV_SET_SEEK_EXACTLY) {
        npMCI->dwOptionFlags &= ~(MCIAVIO_SEEKEXACT);
        if (dwFlags & MCI_SET_ON)
            npMCI->dwOptionFlags |= MCIAVIO_SEEKEXACT;
    }

    if (dwAction & MCI_DGV_SET_SPEED) {
        dwRet = DeviceSetSpeed(npMCI, lpSet->dwSpeed);
    }

    if (dwRet == 0L && (dwAction & MCI_SET_AUDIO)) {
        dwRet = DeviceMute(npMCI, dwFlags & MCI_SET_OFF ? TRUE : FALSE);
    }

    return dwRet;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicSetAudio | This function sets various audio options.
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data block
 *
 * @parm DWORD | dwFlags | Flags for the set audio message.
 *
 * @parm LPMCI_SET_PARMS | lpSet | Parameters for the set audio message.
 *
 * @rdesc Returns an MCI error code.
 *
 ***************************************************************************/
DWORD NEAR PASCAL GraphicSetAudio (NPMCIGRAPHIC npMCI,
    DWORD dwFlags, LPMCI_DGV_SETAUDIO_PARMS lpSet)
{
    DWORD       dwRet = 0L;

    if (npMCI->nAudioStreams == 0) {
        return MCIERR_UNSUPPORTED_FUNCTION;
    }

    if ((dwFlags & MCI_DGV_SETAUDIO_ITEM) &&
            (lpSet->dwItem == MCI_DGV_SETAUDIO_VOLUME) &&
            (dwFlags & MCI_DGV_SETAUDIO_VALUE)) {
        WORD    wLeft, wRight;

        if (dwFlags & (MCI_DGV_SETAUDIO_ALG |
                   MCI_DGV_SETAUDIO_QUALITY |
                   MCI_DGV_SETAUDIO_RECORD |
                   MCI_DGV_SETAUDIO_CLOCKTIME))
            return MCIERR_UNSUPPORTED_FUNCTION;
        if (lpSet->dwValue > 1000L)
            return MCIERR_OUTOFRANGE;
        if (dwFlags & MCI_TEST)
            return 0L;

        // Be sure volume is up to date....
        DeviceGetVolume(npMCI);

        wLeft = LOWORD(npMCI->dwVolume);
        wRight = HIWORD(npMCI->dwVolume);
        if (!(dwFlags & MCI_DGV_SETAUDIO_RIGHT))
            wLeft = (WORD) lpSet->dwValue;

        if (!(dwFlags & MCI_DGV_SETAUDIO_LEFT))
            wRight = (WORD) lpSet->dwValue;

        dwRet = DeviceSetVolume(npMCI, MAKELONG(wLeft, wRight));
    } else if ((dwFlags & MCI_DGV_SETAUDIO_ITEM) &&
            (lpSet->dwItem == MCI_DGV_SETAUDIO_STREAM) &&
            (dwFlags & MCI_DGV_SETAUDIO_VALUE)) {
        if (dwFlags & (MCI_DGV_SETAUDIO_ALG |
                   MCI_DGV_SETAUDIO_QUALITY |
                   MCI_DGV_SETAUDIO_RECORD |
                   MCI_DGV_SETAUDIO_LEFT |
                   MCI_DGV_SETAUDIO_CLOCKTIME |
                   MCI_DGV_SETAUDIO_RIGHT))
            return MCIERR_UNSUPPORTED_FUNCTION;
        if (lpSet->dwValue > (DWORD) npMCI->nAudioStreams || lpSet->dwValue == 0)
            return MCIERR_OUTOFRANGE;
        if (dwFlags & MCI_TEST)
            return 0L;
        dwRet = DeviceSetAudioStream(npMCI, (WORD) lpSet->dwValue);
    } else if (dwFlags & (MCI_DGV_SETAUDIO_ITEM |
                   MCI_DGV_SETAUDIO_VALUE |
                   MCI_DGV_SETAUDIO_ALG |
                   MCI_DGV_SETAUDIO_QUALITY |
                   MCI_DGV_SETAUDIO_RECORD |
                   MCI_DGV_SETAUDIO_LEFT |
                   MCI_DGV_SETAUDIO_CLOCKTIME |
                   MCI_DGV_SETAUDIO_RIGHT))
        return MCIERR_UNSUPPORTED_FUNCTION;

    dwRet = 0;
    switch (dwFlags & (MCI_SET_ON | MCI_SET_OFF)) {

        case MCI_SET_ON | MCI_SET_OFF:
            dwRet = MCIERR_FLAGS_NOT_COMPATIBLE;
            break;

        case MCI_SET_OFF:
            dwRet = (DWORD)TRUE;
            // Drop through and call DeviceMute

        case MCI_SET_ON:
            if (!(dwFlags & MCI_TEST))
                dwRet = DeviceMute(npMCI, dwRet);
            else
                dwRet = 0;
            break;

        default:
            if (!(dwFlags & MCI_DGV_SETAUDIO_ITEM))
                dwRet = MCIERR_MISSING_PARAMETER;
            break;
    }

    return dwRet;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicSetVideo | This function sets various Video options.
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data block
 *
 * @parm DWORD | dwFlags | Flags for the set video message.
 *
 * @parm LPMCI_SET_PARMS | lpSet | Parameters for the set video message.
 *
 * @rdesc Returns an MCI error code.
 *
 ***************************************************************************/
DWORD NEAR PASCAL GraphicSetVideo (NPMCIGRAPHIC npMCI,
    DWORD dwFlags, LPMCI_DGV_SETVIDEO_PARMS lpSet)
{
    DWORD       dwRet = 0L;

    if (dwFlags & (MCI_DGV_SETVIDEO_RECORD |
                    MCI_DGV_SETVIDEO_SRC_NUMBER |
                    MCI_DGV_SETVIDEO_QUALITY |
                    MCI_DGV_SETVIDEO_ALG |
                    MCI_DGV_SETVIDEO_STILL |
                    MCI_DGV_SETVIDEO_CLOCKTIME
                        ))
        return MCIERR_UNSUPPORTED_FUNCTION;

    if ((dwFlags & MCI_DGV_SETVIDEO_OVER) &&
            (lpSet->dwItem != MCI_AVI_SETVIDEO_PALETTE_COLOR))
        return MCIERR_UNSUPPORTED_FUNCTION;

    if (dwFlags & MCI_DGV_SETVIDEO_ITEM) {
        switch (lpSet->dwItem) {
            case MCI_AVI_SETVIDEO_PALETTE_COLOR:
                if (!(dwFlags & MCI_DGV_SETVIDEO_OVER))
                    return MCIERR_MISSING_PARAMETER;

                if (lpSet->dwOver >= npMCI->pbiFormat->biClrUsed) {
                    return MCIERR_OUTOFRANGE;
                }

                return DeviceSetPaletteColor(npMCI,
                          lpSet->dwOver,
                          lpSet->dwValue);

            case MCI_DGV_SETVIDEO_PALHANDLE:
                if (dwFlags & MCI_DGV_SETVIDEO_VALUE) {
                    if (lpSet->dwValue &&
                        lpSet->dwValue != MCI_AVI_SETVIDEO_PALETTE_HALFTONE &&
                        GetObjectType((HPALETTE) lpSet->dwValue) != OBJ_PAL)
                        return MCIERR_AVI_BADPALETTE;
                }

                if (!(dwFlags & MCI_TEST))
                    dwRet = DeviceSetPalette(npMCI,
                                ((dwFlags & MCI_DGV_SETVIDEO_VALUE) ?
                                        (HPALETTE) lpSet->dwValue : NULL));
                break;

            case MCI_DGV_SETVIDEO_STREAM:

                if (!(dwFlags & MCI_DGV_SETVIDEO_VALUE))
                    return MCIERR_UNSUPPORTED_FUNCTION;

                if (lpSet->dwValue == 0 ||
                    lpSet->dwValue > (DWORD)npMCI->nVideoStreams + npMCI->nOtherStreams)
                    return MCIERR_OUTOFRANGE;

                if (dwFlags & MCI_SET_ON)
                    DPF(("SetVideoStream to #%d on\n", (int)lpSet->dwValue));
                else if (dwFlags & MCI_SET_OFF)
                    DPF(("SetVideoStream to #%d off\n", (int)lpSet->dwValue));
                else
                    DPF(("SetVideoStream to #%d\n", (int)lpSet->dwValue));

                if (!(dwFlags & MCI_TEST)) {
                    dwRet = DeviceSetVideoStream(npMCI, (UINT)lpSet->dwValue,
                          !(dwFlags & MCI_SET_OFF));
                }
                break;

            case MCI_AVI_SETVIDEO_DRAW_PROCEDURE:

                if (DeviceMode(npMCI) != MCI_MODE_STOP)
                    return MCIERR_UNSUPPORTED_FUNCTION;

                if (npMCI->hicDrawDefault) {
                    if (npMCI->hicDrawDefault != (HIC) -1)
                        ICClose(npMCI->hicDrawDefault);
                    npMCI->hicDrawDefault = 0;
                    npMCI->dwFlags &= ~(MCIAVI_USERDRAWPROC);
                }

                if (lpSet->dwValue) {

                    if (IsBadCodePtr((FARPROC) lpSet->dwValue)) {
                        DPF(("Bad code pointer!!!!\n"));
                        return MCIERR_OUTOFRANGE; //!!!MCIERR_BAD_PARAM;
                    }

                    npMCI->hicDrawDefault = ICOpenFunction(streamtypeVIDEO,
                        FOURCC_AVIDraw,ICMODE_DRAW,(FARPROC) lpSet->dwValue);

                    if (!npMCI->hicDrawDefault) {
                        return MCIERR_INTERNAL;
                    }
                    DPF(("Successfully set new draw procedure....\n"));

                    npMCI->dwFlags |= MCIAVI_USERDRAWPROC;
                }

                npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN;
                InvalidateRect(npMCI->hwndPlayback, NULL, FALSE);
                return 0;

            default:
                dwRet = MCIERR_UNSUPPORTED_FUNCTION;
                break;
        }
    } else if (dwFlags & (MCI_SET_ON | MCI_SET_OFF)) {
        switch (dwFlags & (MCI_SET_ON | MCI_SET_OFF)) {
        case MCI_SET_ON:
            if (!(dwFlags & MCI_TEST)) {
                InvalidateRect(npMCI->hwndPlayback, NULL, FALSE);
                npMCI->dwFlags |= MCIAVI_SHOWVIDEO;
            }
            break;
        case MCI_SET_OFF:
            if (!(dwFlags & MCI_TEST))
                npMCI->dwFlags &= ~(MCIAVI_SHOWVIDEO);
            break;
        case MCI_SET_ON | MCI_SET_OFF:
            dwRet = MCIERR_FLAGS_NOT_COMPATIBLE;
            break;
        }
    } else
        dwRet = MCIERR_MISSING_PARAMETER;

    return dwRet;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicSignal | This function sets signals.
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data block
 *
 * @parm DWORD | dwFlags | Flags for the set PositionAdvise message.
 *
 * @parm LPMCI_SIGNAL_PARMS | lpSignal | Parameters for the signal
 *      message.
 *
 * @rdesc Returns an MCI error code.
 *
 ***************************************************************************/
DWORD NEAR PASCAL GraphicSignal(NPMCIGRAPHIC npMCI,
    DWORD dwFlags, LPMCI_DGV_SIGNAL_PARMS lpSignal)
{
    DWORD       dwRet = 0L;
    DWORD       dwUser;
    DWORD       dwPosition;
    DWORD       dwPeriod;

    dwUser = (dwFlags & MCI_DGV_SIGNAL_USERVAL) ? lpSignal->dwUserParm : 0L;

    if (dwFlags & MCI_DGV_SIGNAL_CANCEL) {
        if (dwFlags & (MCI_DGV_SIGNAL_AT |
                       MCI_DGV_SIGNAL_EVERY |
                       MCI_DGV_SIGNAL_POSITION))
            return MCIERR_FLAGS_NOT_COMPATIBLE;

        if (!npMCI->dwSignals)
            return MCIERR_NONAPPLICABLE_FUNCTION;

        if (dwUser && (npMCI->signal.dwUserParm != dwUser))
            return MCIERR_NONAPPLICABLE_FUNCTION;

        if (!(dwFlags & MCI_TEST))
            --npMCI->dwSignals;
    } else {
        if ((npMCI->dwSignals != 0) && (dwUser != npMCI->signal.dwUserParm)) {
            /* !!! Should we allow more than one signal? */
            return MCIERR_DGV_DEVICE_LIMIT;
        }

        if (dwFlags & MCI_DGV_SIGNAL_AT) {
            /* Use position passed in */
            dwPosition = ConvertToFrames(npMCI, lpSignal->dwPosition);
            if (dwPosition > (DWORD) npMCI->lFrames)
                return MCIERR_OUTOFRANGE;
        } else {
            /* Get current position */
            DevicePosition(npMCI, (LPLONG) &dwPosition);
        }

        if (dwFlags & MCI_DGV_SIGNAL_EVERY) {
            dwPeriod = (DWORD) ConvertToFrames(npMCI, lpSignal->dwPeriod);

            if (dwPeriod == 0 || (dwPeriod > (DWORD) npMCI->lFrames))
                return MCIERR_OUTOFRANGE;
        } else {
            /* It's a one-time signal */
            dwPeriod = 0L;
        }

        if (dwFlags & MCI_TEST)
            return 0;

        npMCI->signal.dwPosition = dwPosition;
        npMCI->signal.dwPeriod = dwPeriod;
        npMCI->signal.dwUserParm = dwUser;
        npMCI->signal.dwCallback = lpSignal->dwCallback;
        npMCI->dwSignalFlags = dwFlags;

        /* The signal isn't really activated until we do this. */
        if (!npMCI->dwSignals)
            ++npMCI->dwSignals;
    }

    return 0L;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicList | This function supports the MCI_LIST command.
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data block
 *
 * @parm DWORD | dwFlags | Flags for the List message.
 *
 * @parm LPMCI_DGV_LIST_PARMS | lpList | Parameters for the list message.
 *
 * @rdesc Returns an MCI error code.
 *
 ***************************************************************************/
DWORD NEAR PASCAL GraphicList(NPMCIGRAPHIC npMCI,
    DWORD dwFlags, LPMCI_DGV_LIST_PARMS lpList)
{
    return MCIERR_UNSUPPORTED_FUNCTION;
}


/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicGetDevCaps | This function returns  device
 *      capabilities
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data block
 *
 * @parm DWORD | dwFlags | Flags for the GetDevCaps message.
 *
 * @parm LPMCI_GETDEVCAPS_PARMS | lpCaps | Parameters for the GetDevCaps
 *      message.
 *
 * @rdesc Returns an MCI error code.
 *
 ***************************************************************************/

DWORD NEAR PASCAL GraphicGetDevCaps (NPMCIGRAPHIC npMCI,
    DWORD dwFlags, LPMCI_GETDEVCAPS_PARMS lpCaps )
{

    DWORD dwRet = 0L;


    if (dwFlags & MCI_GETDEVCAPS_ITEM)
        {

        switch (lpCaps->dwItem)
            {
            case MCI_GETDEVCAPS_CAN_RECORD:
            case MCI_GETDEVCAPS_CAN_EJECT:
            case MCI_GETDEVCAPS_CAN_SAVE:
            case MCI_DGV_GETDEVCAPS_CAN_LOCK:
            case MCI_DGV_GETDEVCAPS_CAN_STR_IN:
            case MCI_DGV_GETDEVCAPS_CAN_FREEZE:
            case MCI_DGV_GETDEVCAPS_HAS_STILL:

                lpCaps->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
                dwRet = MCI_RESOURCE_RETURNED;
                break;

            case MCI_DGV_GETDEVCAPS_CAN_REVERSE:
            case MCI_GETDEVCAPS_CAN_PLAY:
            case MCI_GETDEVCAPS_HAS_AUDIO:
            case MCI_GETDEVCAPS_HAS_VIDEO:
            case MCI_GETDEVCAPS_USES_FILES:
            case MCI_GETDEVCAPS_COMPOUND_DEVICE:
            case MCI_DGV_GETDEVCAPS_PALETTES:
            case MCI_DGV_GETDEVCAPS_CAN_STRETCH:
            case MCI_DGV_GETDEVCAPS_CAN_TEST:
                lpCaps->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
                dwRet = MCI_RESOURCE_RETURNED;
                break;

            case MCI_GETDEVCAPS_DEVICE_TYPE:

                lpCaps->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_DIGITAL_VIDEO,
                                            MCI_DEVTYPE_DIGITAL_VIDEO);
                dwRet = MCI_RESOURCE_RETURNED;
                break;

            case MCI_DGV_GETDEVCAPS_MAX_WINDOWS:
            case MCI_DGV_GETDEVCAPS_MAXIMUM_RATE:
            case MCI_DGV_GETDEVCAPS_MINIMUM_RATE:
            default:

                dwRet = MCIERR_UNSUPPORTED_FUNCTION;
                break;
            }
        }
    else
        dwRet = MCIERR_MISSING_PARAMETER;

    if ((dwFlags & MCI_TEST) && (LOWORD(dwRet) == 0)) {
        /* There is no error, but the test flag is on.  Return as little
        ** as possible.
        */
        dwRet = 0;
        lpCaps->dwReturn = 0;
    }

    return (dwRet);
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicWindow | This function controls the stage window
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data block
 *
 * @parm DWORD | dwFlags | Flags for the window message.
 *
 * @parm LPMCI_DGV_WINDOW_PARMS | lpPlay | Parameters for the window message.
 *
 * @rdesc Returns an MCI error code.
 *
 ***************************************************************************/

DWORD NEAR PASCAL GraphicWindow (NPMCIGRAPHIC npMCI, DWORD dwFlags,
    LPMCI_DGV_WINDOW_PARMS lpWindow)
{
    DWORD   dwRet = 0L;
    int     i = 0;
    HWND    hWndNew;

    if (dwFlags & MCI_DGV_WINDOW_HWND) {
        // Set a new stage window. If the parameter is NULL, then
        // use the default window. Otherwise, hide the default
        // window and use the given window handle.

        if (!lpWindow->hWnd)
            hWndNew = npMCI->hwndDefault;
        else
            hWndNew = lpWindow->hWnd;

        if (!IsWindow(hWndNew))
            return MCIERR_NO_WINDOW;

        /* If the test flag is set, return without doing anything. */
        /* Question: do we have to check for more possible errors? */
        if (dwFlags & MCI_TEST)
            return 0L;

        // only change if the new window handle is different from the current
        // stage window handle

        if (hWndNew != npMCI->hwndPlayback) {
            dwRet = DeviceSetWindow(npMCI, hWndNew);


            if (npMCI->hwndPlayback != npMCI->hwndDefault) {

                // see comment in graphic.h
                // They've specified their own window for playback.  Don't use
                // the default registry sizing
                npMCI->fOKToUseDefaultSizing = FALSE;

                if (IsWindow(npMCI->hwndDefault))
                    ShowWindow(npMCI->hwndDefault, SW_HIDE);
            }
        }
    }

    /* If the test flag is set, return without doing anything. */
    /* Question: do we have to check for more possible errors? */
    if (dwFlags & MCI_TEST)
        return dwRet;

    if (!dwRet) {
                if (IsWindow(npMCI->hwndPlayback)) {
                        if (dwFlags & MCI_DGV_WINDOW_STATE) {
                                // see comment in graphic.h on npMCI->fOKToUseDefaultSizing
                                // This is the moment of truth.  We're showing the window
                                // and we may want to pay attention to the registry default
                                // sizing (zoom by 2, fixed % of screen size, etc.)
                                // As an extra note I should say that it's important that we
                                // don't change it until now so that if somebody opens a file
                                // and does a "where destination" they get the original size,
                                // because if they're playing in their own window, it will
                                // not be altered anyway.
                                if (lpWindow->nCmdShow != SW_HIDE) {
                                        if (npMCI->fOKToUseDefaultSizing) {
                                                SetWindowToDefaultSize(npMCI, TRUE);
                                                ResetDestRect(npMCI, TRUE);
                                        }

                                        // Never do this again
                                        npMCI->fOKToUseDefaultSizing = FALSE;
                                }

                        ShowWindow (npMCI->hwndPlayback, lpWindow->nCmdShow);
                        }

            if (dwFlags & MCI_DGV_WINDOW_TEXT)
                        SetWindowText(npMCI->hwndPlayback, lpWindow->lpstrText);
        } else
            dwRet = MCIERR_NO_WINDOW;
    }

    return dwRet;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicPut | This function sets the offset and extent
 *      of the animation within the client area of the stage window.
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data block
 *
 * @parm DWORD | dwFlags | Flags for the put message.
 *
 * @parm LPMCI_DGV_RECT_PARMS | lpDestination | Parameters for the
 *      destination message.
 *
 * @rdesc Returns an MCI error code.
 *
 ***************************************************************************/

DWORD NEAR PASCAL GraphicPut ( NPMCIGRAPHIC npMCI,
    DWORD dwFlags, LPMCI_DGV_RECT_PARMS lpParms)
{
    BOOL        frc;
    RECT        rc;

    if (dwFlags & (MCI_DGV_PUT_FRAME | MCI_DGV_PUT_VIDEO))
        return MCIERR_UNSUPPORTED_FUNCTION;

    frc = (dwFlags & MCI_DGV_RECT) == MCI_DGV_RECT;

    if (!IsWindow(npMCI->hwndPlayback))
        return MCIERR_NO_WINDOW;

    switch (dwFlags & (MCI_DGV_PUT_SOURCE | MCI_DGV_PUT_DESTINATION |
                            MCI_DGV_PUT_WINDOW)) {
        case 0L:
            return MCIERR_MISSING_PARAMETER;

        case MCI_DGV_PUT_SOURCE:
            // If a rectangle is supplied, use it.
            if (frc) {
                rc.left = lpParms->ptOffset.x;
                rc.top = lpParms->ptOffset.y;
                rc.right = lpParms->ptOffset.x + lpParms->ptExtent.x;
                rc.bottom = lpParms->ptOffset.y + lpParms->ptExtent.y;
                DPF2(("GraphicPut_Source: rc [%d %d %d %d]\n", rc));

                if (lpParms->ptExtent.x <= 0) {
                    rc.right = rc.left + (npMCI->rcDest.right - npMCI->rcDest.left);
                }
                if (lpParms->ptExtent.y <= 0) {
                    rc.bottom = rc.top + (npMCI->rcDest.bottom - npMCI->rcDest.top);
                }
            } else {
                /* Reset to default */
                rc = npMCI->rcMovie;
                DPF2(("GraphicPut_Source (default): rc [%d %d %d %d]\n", rc));
            }
            break;

        case MCI_DGV_PUT_DESTINATION:
            // If a rectangle is supplied, use it.
            if (frc) {
                rc.left = lpParms->ptOffset.x;
                rc.top = lpParms->ptOffset.y;
                rc.right = lpParms->ptOffset.x + lpParms->ptExtent.x;
                rc.bottom = lpParms->ptOffset.y + lpParms->ptExtent.y;
                DPF2(("GraphicPut_Destination: rc [%d %d %d %d]\n", rc));

                if (lpParms->ptExtent.x <= 0) {
                    rc.right = rc.left + (npMCI->rcDest.right - npMCI->rcDest.left);
                }
                if (lpParms->ptExtent.y <= 0) {
                    rc.bottom = rc.top + (npMCI->rcDest.bottom - npMCI->rcDest.top);
                }

            } else {
                /* Reset to size of stage window */
                GetClientRect(npMCI->hwndPlayback, &rc);
                DPF2(("GraphicPut_Destination (default): rc [%d %d %d %d]\n", rc));
            }
            break;

        case MCI_DGV_PUT_WINDOW:
            if (dwFlags & MCI_TEST)
                return 0L;

            // De-minimize their window, so we don't end up with
            // a giant icon....
            if (IsIconic(npMCI->hwndPlayback))
                ShowWindow(npMCI->hwndPlayback, SW_RESTORE);

            // If a rectangle is supplied, use it.
            if (frc) {
                RECT    rcOld;

                rc.left = lpParms->ptOffset.x;
                rc.right = lpParms->ptOffset.x + lpParms->ptExtent.x;
                rc.top = lpParms->ptOffset.y;
                rc.bottom = lpParms->ptOffset.y + lpParms->ptExtent.y;
                if (dwFlags & MCI_DGV_PUT_CLIENT) {
                    AdjustWindowRect(&rc,
                                    GetWindowLong(npMCI->hwndPlayback, GWL_STYLE),
                                    FALSE);
                }

                // Default to just moving if width, height == 0....
                GetWindowRect(npMCI->hwndPlayback, &rcOld);
                if (lpParms->ptExtent.x <= 0) {
                    rc.right = rc.left + (rcOld.right - rcOld.left);
                }
                if (lpParms->ptExtent.y <= 0) {
                    rc.bottom = rc.top + (rcOld.bottom - rcOld.top);
                }

                MoveWindow(npMCI->hwndPlayback,
                            rc.left, rc.top,
                            rc.right - rc.left, rc.bottom - rc.top, TRUE);
            } else {
                // !!! What should we do if there's no rectangle?

                /* Reset to "natural" size? */
                rc = npMCI->rcMovie;

                if (npMCI->dwOptionFlags & MCIAVIO_ZOOMBY2)
                    SetRect(&rc, 0, 0, rc.right*2, rc.bottom*2);

                AdjustWindowRect(&rc, GetWindowLong(npMCI->hwndPlayback, GWL_STYLE),
                                            FALSE);

                SetWindowPos(npMCI->hwndPlayback, NULL, 0, 0,
                                rc.right - rc.left, rc.bottom - rc.top,
                                SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
            }

            // Premiere 1.0 depends on the window always being visible
            // after a PUT_WINDOW command.  Make it so.
            ShowWindow(npMCI->hwndPlayback, SW_RESTORE);
            return 0L;

        default:
            return MCIERR_FLAGS_NOT_COMPATIBLE;
    }

    if (dwFlags & MCI_DGV_PUT_CLIENT)
        return MCIERR_FLAGS_NOT_COMPATIBLE;

    /* If the test flag is set, return without doing anything. */
    /* Question: do we have to check for more possible errors? */
    if (dwFlags & MCI_TEST)
        return 0L;

    // see comment in graphic.h
    // Any "put" command is dicking with the playback window and I think that's
    // grounds to not use the default window sizing.
    npMCI->fOKToUseDefaultSizing = FALSE;

    return DevicePut(npMCI, &rc, dwFlags);
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicWhere | This function returns the current
 *      source and destination rectangles, in offset/extent form.
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data block
 *
 * @parm DWORD | dwFlags | Flags for the query source message.
 *
 * @parm LPMCI_DGV_RECT_PARMS | lpParms | Parameters for the message.
 *
 * @rdesc Returns an MCI error code.
 *
 ***************************************************************************/

DWORD NEAR PASCAL GraphicWhere(NPMCIGRAPHIC npMCI, DWORD dwFlags,
    LPMCI_DGV_RECT_PARMS lpParms)
{
    RECT        rc;

    if (dwFlags & (MCI_DGV_WHERE_FRAME | MCI_DGV_WHERE_VIDEO))
        return MCIERR_UNSUPPORTED_FUNCTION;

    // !!! WHERE_WINDOW?

    switch (dwFlags & (MCI_DGV_WHERE_SOURCE | MCI_DGV_WHERE_DESTINATION |
                            MCI_DGV_WHERE_WINDOW)) {
        case 0L:
            return MCIERR_MISSING_PARAMETER;

        case MCI_DGV_WHERE_SOURCE:
            if (dwFlags & MCI_DGV_WHERE_MAX) {
                lpParms->ptOffset.x = npMCI->rcMovie.left;
                lpParms->ptOffset.y = npMCI->rcMovie.top;
                lpParms->ptExtent.x = npMCI->rcMovie.right - npMCI->rcMovie.left;
                lpParms->ptExtent.y = npMCI->rcMovie.bottom - npMCI->rcMovie.top;
                DPF2(("Where source (max): [%d, %d, %d, %d]\n", npMCI->rcMovie));
            } else {
                lpParms->ptOffset.x = npMCI->rcSource.left;
                lpParms->ptOffset.y = npMCI->rcSource.top;
                lpParms->ptExtent.x = npMCI->rcSource.right  - npMCI->rcSource.left;
                lpParms->ptExtent.y = npMCI->rcSource.bottom - npMCI->rcSource.top;
                DPF2(("Where source: [%d, %d, %d, %d]\n", npMCI->rcSource));
            }
            break;

        case MCI_DGV_WHERE_DESTINATION:
            if (dwFlags & MCI_DGV_WHERE_MAX) {
                /* Return size of window */
                GetClientRect(npMCI->hwndPlayback, &rc);
                lpParms->ptOffset.x = 0;
                lpParms->ptOffset.y = 0;
                lpParms->ptExtent.x = rc.right;
                lpParms->ptExtent.y = rc.bottom;
                DPF2(("Where destination (max): [%d, %d, %d, %d]\n", rc));
            } else {
                /* Return current destination size */
                lpParms->ptOffset.x = npMCI->rcDest.left;
                lpParms->ptOffset.y = npMCI->rcDest.top;
                lpParms->ptExtent.x = npMCI->rcDest.right - npMCI->rcDest.left;
                lpParms->ptExtent.y = npMCI->rcDest.bottom - npMCI->rcDest.top;
                DPF2(("Where destination: [%d, %d, %d, %d]\n", npMCI->rcDest));
            }
            break;

        case MCI_DGV_WHERE_WINDOW:
            if (dwFlags & MCI_DGV_WHERE_MAX) {
                /* Return maximum size of window */
                GetClientRect(npMCI->hwndPlayback, &rc);
                lpParms->ptOffset.x = 0;
                lpParms->ptOffset.y = 0;
                lpParms->ptExtent.x = GetSystemMetrics(SM_CXSCREEN);
                lpParms->ptExtent.y = GetSystemMetrics(SM_CYSCREEN);
            } else {
                /* Return size of window */
                GetWindowRect(npMCI->hwndPlayback, &rc);
                lpParms->ptOffset.x = rc.left;
                lpParms->ptOffset.y = rc.top;
                lpParms->ptExtent.x = rc.right - rc.left;
                lpParms->ptExtent.y = rc.bottom - rc.top;
            }
            break;

        default:
            return MCIERR_FLAGS_NOT_COMPATIBLE;
    }

    return 0L;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicRealize | This function realizes the current palette
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data block
 *
 * @parm DWORD | dwFlags | Flags for the message.
 *
 * @rdesc Returns an MCI error code.
 *
 ***************************************************************************/

DWORD NEAR PASCAL GraphicRealize(NPMCIGRAPHIC npMCI, DWORD dwFlags)
{
    /* If the test flag is set, return without doing anything. */
    /* Question: do we have to check for more possible errors? */
    if (dwFlags & MCI_TEST)
        return 0L;

    npMCI->fForceBackground = (dwFlags & MCI_DGV_REALIZE_BKGD) != 0;

    return DeviceRealize(npMCI);
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicUpdate | This function refreshes the current frame.
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data block
 *
 * @parm DWORD | dwFlags | Flags for the message.
 *
 * @parm LPMCI_DGV_UPDATE_PARMS | lpParms | Parameters for the message.
 *
 * @rdesc Returns an MCI error code.
 *
 ***************************************************************************/

DWORD NEAR PASCAL GraphicUpdate(NPMCIGRAPHIC npMCI, DWORD dwFlags,
    LPMCI_DGV_UPDATE_PARMS lpParms)
{
    RECT    rc;

    rc.left   = lpParms->ptOffset.x;
    rc.top    = lpParms->ptOffset.y;
    rc.right  = lpParms->ptOffset.x + lpParms->ptExtent.x;
    rc.bottom = lpParms->ptOffset.y + lpParms->ptExtent.y;

    if (!(dwFlags & MCI_DGV_UPDATE_HDC)) {
        InvalidateRect(npMCI->hwndPlayback, (dwFlags & MCI_DGV_RECT) ? &rc : NULL, TRUE);

        // this will cause the winproc thread to do the repaint now
        UpdateWindow(npMCI->hwndPlayback);
        return 0;
    }

    /* If the test flag is set, return without doing anything. */
    /* Question: do we have to check for more possible errors? */
    if (dwFlags & MCI_TEST)
        return 0L;

    return DeviceUpdate (npMCI, dwFlags, lpParms);
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | GraphicConfig | This function brings up the configure dialog.
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data block
 *
 * @parm DWORD | dwFlags | Flags for the message.
 *        The only flag bit checked is MCI_TEST
 *
 * @rdesc Returns 0.
 *
 ***************************************************************************/

DWORD FAR PASCAL GraphicConfig(NPMCIGRAPHIC npMCI, DWORD dwFlags)
{
    DWORD dwOptions = npMCI->dwOptionFlags;

    if (!(dwFlags & MCI_TEST)) {
        BOOL f;
        gfEvil++;

        f = ConfigDialog(NULL, npMCI);
        if (f) {

#ifdef DEBUG
            //
            // in DEBUG always reset the dest rect because the user may
            // have played with the DEBUG DrawDib options and we will
            // need to call DrawDibBegin() again.
            //
            if (TRUE) {
#else
            if ((npMCI->dwOptionFlags & (MCIAVIO_STUPIDMODE|MCIAVIO_ZOOMBY2
                                        |MCIAVIO_WINDOWSIZEMASK))
                        != (dwOptions & (MCIAVIO_STUPIDMODE|MCIAVIO_ZOOMBY2
                                        |MCIAVIO_WINDOWSIZEMASK)) ) {
#endif

                npMCI->lFrameDrawn = (- (LONG) npMCI->wEarlyRecords) - 1;
                SetWindowToDefaultSize(npMCI, TRUE);

                // don't do this on the user thread
                //SetRectEmpty(&npMCI->rcDest); //This will force a change!

                ResetDestRect(npMCI, TRUE);
            }
        }
        else {
            npMCI->dwOptionFlags = dwOptions;
        }
        gfEvil--;
    }

    return 0L;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | mciSpecial | This function handles all the MCI
 *      commands that don't require instance data such as open.
 *
 * @parm UINT | wDeviceID | The MCI device ID
 *
 * @parm UINT | wMessage | The requested action to be performed.
 *
 * @parm DWORD | dwFlags | Flags for the message.
 *
 * @parm DWORD | lpParms | Parameters for this message.
 *
 * @rdesc Error Constant. 0L on success
 *
 ***************************************************************************/

DWORD NEAR PASCAL mciSpecial (UINT wDeviceID, UINT wMessage, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
{
    NPMCIGRAPHIC npMCI = 0L;
    DWORD dwRet;

    /* since there in no instance block, there is no saved notification */
    /* to abort. */

    switch (wMessage) {
        case MCI_OPEN_DRIVER:
            if (dwFlags & (MCI_OPEN_ELEMENT | MCI_OPEN_ELEMENT_ID))
                dwRet = GraphicOpen (&npMCI, dwFlags,
                            (LPMCI_DGV_OPEN_PARMS) lpParms, wDeviceID);
            else
                dwRet = 0L;

            mciSetDriverData (wDeviceID, (UINT_PTR)npMCI);
            break;

        case MCI_GETDEVCAPS:
            dwRet = GraphicGetDevCaps(NULL, dwFlags,
                            (LPMCI_GETDEVCAPS_PARMS)lpParms);
            break;

        case MCI_CONFIGURE:

            if (!(dwFlags & MCI_TEST))
                ConfigDialog(NULL, NULL);

            dwRet = 0L;
            break;

        case MCI_INFO:
            dwRet = GraphicInfo(NULL, dwFlags, (LPMCI_DGV_INFO_PARMS)lpParms);
            break;

        case MCI_CLOSE_DRIVER:
            dwRet = 0L;
            break;

        default:
            dwRet = MCIERR_UNSUPPORTED_FUNCTION;
            break;
    }

    GraphicImmediateNotify (wDeviceID, lpParms, dwFlags, dwRet);
    return (dwRet);
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | mciDriverEntry | This function is the MCI handler
 *
 * @parm UINT | wDeviceID | The MCI device ID
 *
 * @parm UINT | wMessage | The requested action to be performed.
 *
 * @parm DWORD | dwFlags | Flags for the message.
 *
 * @parm DWORD | lpParms | Parameters for this message.
 *
 * @rdesc Error Constant. 0L on success
 *
 ***************************************************************************/

DWORD PASCAL mciDriverEntry (UINT wDeviceID, UINT wMessage, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
{
    NPMCIGRAPHIC npMCI = 0L;
    DWORD dwRet = MCIERR_UNRECOGNIZED_COMMAND;
    BOOL fDelayed = FALSE;
    BOOL fNested = FALSE;

    /* All current commands require a parameter block. */

    if (!lpParms && (dwFlags & MCI_NOTIFY))
        return (MCIERR_MISSING_PARAMETER);

    npMCI = (NPMCIGRAPHIC) mciGetDriverData(wDeviceID);

    if (!npMCI)
        return mciSpecial(wDeviceID, wMessage, dwFlags, lpParms);
#ifdef DEBUG
    else
        Assert(npMCI->mciid == MCIID);
#endif

    /*
    ** If a WOW app has subclassed the AVIWnd32 window,
    ** this is very bad as it stops all "Sent" message processing on
    ** that window, "Posted" messages seem to be OK.  This means
    ** that it is not possible to close the movie window.
    */
    if ( IsNTWOW() ) {

          DPF2(( "WOW mcidriverentry\n"));
          if ( IsWindow(npMCI->hwndDefault) ) {

              WNDPROC wndProc = (WNDPROC)GetWindowLongPtr( npMCI->hwndDefault,
                                                        GWLP_WNDPROC );
              if ( wndProc != GraphicWndProc ) {

                  DPF2(( "WOW app has subclassed AVIWnd32 window - correcting\n" ));
                  SetWindowLongPtr( npMCI->hwndDefault, GWLP_WNDPROC,
                                 (LONG_PTR)GraphicWndProc );
              }
              wndProc = (WNDPROC)GetClassLongPtr(npMCI->hwndDefault, GCLP_WNDPROC);
              if (wndProc != GraphicWndProc) {
                  DPF2(( "WOW app has subclassed AVIWnd32 class! - correcting\n" ));
                  SetClassLongPtr( npMCI->hwndDefault, GCLP_WNDPROC,
                                 (LONG_PTR)GraphicWndProc );
              }
          }
    }

    if (npMCI->wMessageCurrent) {
        fNested = TRUE;

        if (wMessage != MCI_STATUS && wMessage != MCI_GETDEVCAPS &&
                    wMessage != MCI_INFO) {
            DPF(("Warning!!!!!\n"));
            DPF(("Warning!!!!!     MCIAVI reentered: received %x while processing %x\n", wMessage, npMCI->wMessageCurrent));
            DPF(("Warning!!!!!\n"));
//          Assert(0);
//          return MCIERR_DEVICE_NOT_READY;
        }
    } else
        npMCI->wMessageCurrent = wMessage;

    switch (wMessage) {

        case MCI_CLOSE_DRIVER:


            // Question:  Should we set the driver data to NULL
            // before closing the device?  It would seem the right order.
            // So... we have moved this line before the call to GraphicClose
            mciSetDriverData(wDeviceID, 0L);

            // note that GraphicClose will release and delete the critsec
            dwRet = GraphicClose(npMCI);

            npMCI = NULL;
            break;

        case MCI_PLAY:

            dwRet = GraphicPlay(npMCI, dwFlags, (LPMCI_PLAY_PARMS)lpParms);
            fDelayed = TRUE;
            break;

        case MCI_CUE:

            dwRet = GraphicCue(npMCI, dwFlags, (LPMCI_DGV_CUE_PARMS)lpParms);
            fDelayed = TRUE;
            break;

        case MCI_STEP:

            dwRet = GraphicStep(npMCI, dwFlags, (LPMCI_DGV_STEP_PARMS)lpParms);
            fDelayed = TRUE;
            break;

        case MCI_STOP:

            dwRet = GraphicStop(npMCI, dwFlags, lpParms);
            break;

        case MCI_SEEK:

            dwRet = GraphicSeek (npMCI, dwFlags, (LPMCI_SEEK_PARMS)lpParms);
            fDelayed = TRUE;
            break;

        case MCI_PAUSE:

            dwRet = GraphicPause(npMCI, dwFlags, lpParms);
            fDelayed = TRUE;
            break;

        case MCI_RESUME:

            dwRet = GraphicResume(npMCI, dwFlags, lpParms);
            fDelayed = TRUE;
            break;

        case MCI_SET:

            dwRet = GraphicSet(npMCI, dwFlags,
                                (LPMCI_DGV_SET_PARMS)lpParms);
            break;

        case MCI_STATUS:

            dwRet = GraphicStatus(npMCI, dwFlags,
                                (LPMCI_DGV_STATUS_PARMS)lpParms);
            break;

        case MCI_INFO:

            dwRet = GraphicInfo (npMCI, dwFlags, (LPMCI_DGV_INFO_PARMS)lpParms);
            break;

        case MCI_GETDEVCAPS:

            dwRet = GraphicGetDevCaps(npMCI, dwFlags, (LPMCI_GETDEVCAPS_PARMS)lpParms);
            break;

        case MCI_REALIZE:

            dwRet = GraphicRealize(npMCI, dwFlags);
            break;

        case MCI_UPDATE:

            dwRet = GraphicUpdate(npMCI, dwFlags, (LPMCI_DGV_UPDATE_PARMS)lpParms);
            break;

        case MCI_WINDOW:

            dwRet = GraphicWindow(npMCI, dwFlags, (LPMCI_DGV_WINDOW_PARMS)lpParms);
            break;

        case MCI_PUT:

            dwRet = GraphicPut(npMCI, dwFlags, (LPMCI_DGV_RECT_PARMS)lpParms);
            break;

        case MCI_WHERE:

            dwRet = GraphicWhere(npMCI, dwFlags, (LPMCI_DGV_RECT_PARMS)lpParms);
            break;

        case MCI_CONFIGURE:
            dwRet = GraphicConfig(npMCI, dwFlags);
            break;

        case MCI_SETAUDIO:
            dwRet = GraphicSetAudio(npMCI, dwFlags,
                        (LPMCI_DGV_SETAUDIO_PARMS) lpParms);
            break;

        case MCI_SETVIDEO:
            dwRet = GraphicSetVideo(npMCI, dwFlags,
                        (LPMCI_DGV_SETVIDEO_PARMS) lpParms);
            break;

        case MCI_SIGNAL:
            dwRet = GraphicSignal(npMCI, dwFlags,
                        (LPMCI_DGV_SIGNAL_PARMS) lpParms);
            break;

        case MCI_LIST:
            dwRet = GraphicList(npMCI, dwFlags,
                        (LPMCI_DGV_LIST_PARMS) lpParms);
            break;

        case MCI_LOAD:
            dwRet = GraphicLoad(npMCI, dwFlags,
                                  (LPMCI_DGV_LOAD_PARMS) lpParms);
            break;

        case MCI_RECORD:
        case MCI_SAVE:

        case MCI_CUT:
        case MCI_COPY:
        case MCI_PASTE:
        case MCI_UNDO:

        case MCI_DELETE:
        case MCI_CAPTURE:
        case MCI_QUALITY:
        case MCI_MONITOR:
        case MCI_RESERVE:
        case MCI_FREEZE:
        case MCI_UNFREEZE:
            dwRet = MCIERR_UNSUPPORTED_FUNCTION;
            break;

            /* Do we need this case? */
        default:
            dwRet = MCIERR_UNRECOGNIZED_COMMAND;
            break;
    }

    if (!fDelayed || (dwFlags & MCI_TEST)) {
        /* We haven't processed the notify yet. */
        if (npMCI && (dwFlags & MCI_NOTIFY) && (!LOWORD(dwRet)))
            /* Throw away the old notify */
            GraphicDelayedNotify(npMCI, MCI_NOTIFY_SUPERSEDED);

        /* And send the new one out immediately. */
        GraphicImmediateNotify(wDeviceID, lpParms, dwFlags, dwRet);
    }

    if (npMCI) {
        /* Everything from here on relies on npMCI still being around */

        /* If there's an error, don't save the callback.... */
        if (fDelayed && dwRet != 0 && (dwFlags & MCI_NOTIFY)) {

            // this might be too late, of course, but shouldn't do
            // any harm
            npMCI->hCallback = 0;
        }

        //
        //  see if we need to tell the DRAW device about moving.
        //  MPlayer is sending the status and position command alot
        //  so this is a "timer"
        //
        //  !!!do we need to do it this often?
        //
        if (npMCI->dwFlags & MCIAVI_WANTMOVE)
            CheckWindowMove(npMCI, FALSE);

        if (!fNested)
            npMCI->wMessageCurrent = 0;

    }

    return dwRet;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api LONG | ConvertToFrames | Convert from the current time format into
 *      frames.
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data block
 *
 * @parm DWORD | dwTime | Input time.
 *
 ***************************************************************************/
LONG NEAR PASCAL ConvertToFrames(NPMCIGRAPHIC npMCI, DWORD dwTime)
{
    if (npMCI->dwTimeFormat == MCI_FORMAT_FRAMES) {
        return (LONG) dwTime;
    } else {
        if (npMCI->dwMicroSecPerFrame > 1000) {
        /* This needs to round down--muldiv32 likes to round off. */
        return (LONG) muldivrd32(dwTime, 1000L, npMCI->dwMicroSecPerFrame);
        } else {
            return (LONG) muldivru32(dwTime, 1000L, npMCI->dwMicroSecPerFrame);
        }
    }
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api DWORD | ConvertFromFrames | Convert from frames into the current
 *      time format.
 *
 * @parm NPMCIGRAPHIC | npMCI | Near pointer to instance data block
 *
 * @parm LONG | lFrame | Frame number to convert.
 *
 ***************************************************************************/
DWORD NEAR PASCAL ConvertFromFrames(NPMCIGRAPHIC npMCI, LONG lFrame)
{
    if (npMCI->dwTimeFormat == MCI_FORMAT_FRAMES) {
        return (DWORD)lFrame;
    } else {
        if (npMCI->dwMicroSecPerFrame > 1000)
        return muldivru32(lFrame, npMCI->dwMicroSecPerFrame, 1000L);
        else
            return muldivrd32(lFrame, npMCI->dwMicroSecPerFrame, 1000L);
    }
}

#ifdef HEARTBEAT
DWORD Interval = 60000; // 1 minute
DWORD HeartBeatBreak = FALSE;
DWORD HeartBeatDump = FALSE;

DWORD HeartBeat(LPVOID lpvThreadData)
{

    NPMCIGRAPHIC npMCI;
    int n;
    while (TRUE) {

        for (n=Interval/1000; n--; ) {
            Sleep(1000);
            if (fTerminate) {
                ExitThread(0);
            }
        }

        if (HeartBeatDump) {

            LPTSTR pszFilename;

            EnterList();
            npMCI = npMCIList;
            while (npMCI) {

                pszFilename = npMCI->szFilename;
                if (!pszFilename) {
                    pszFilename = L"<NULL>";
                }
                DPF0(("\nDevice dump : %8x : Name > %ls <\n"
                           "\t: CmdCritSec owner=%x, count=%x "
                           "\t: WinCritSec owner=%x, count=%x "
                             ": HDCCritSec owner=%x, count=%x\n"
                           "\t State=%x   hWave=%x",
                       npMCI,
                        pszFilename,
                        (npMCI->CmdCritSec).OwningThread, (npMCI->CmdCritSec).RecursionCount,
                        (npMCI->WinCritSec).OwningThread, (npMCI->WinCritSec).RecursionCount,
                        (npMCI->HDCCritSec).OwningThread, (npMCI->HDCCritSec).RecursionCount,
                        npMCI->wTaskState, npMCI->hWave));

                npMCI = npMCI->npMCINext;
            }
            LeaveList();
        }

        if (HeartBeatBreak) {
            DebugBreak();
        }
    }
    return (0);
}
#endif