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

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

   Title:   aviopen.c - open a AVI file

*****************************************************************************/
#include "graphic.h"

#ifdef WIN32
#include <wchar.h>
#endif

#ifdef USEAVIFILE
    #include <initguid.h>

    DEFINE_AVIGUID(IID_IAVIFile,            0x00020020, 0, 0);
    DEFINE_AVIGUID(IID_IAVIStream,          0x00020021, 0, 0);
#endif

#define comptypeNONE mmioFOURCC('N','O','N','E')

//
//  special error to use AVIFile to open this file.
//
#define AVIERR_NOT_AVIFILE  4242

//
//  if this is defined we will always use AVIFILE.DLL, except for
//  1:1 interleaved files.
//
#define USE_AVIFILE_FOR_NON_INT

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

BOOL FAR PASCAL mciaviCloseFile(NPMCIGRAPHIC npMCI);
BOOL FAR PASCAL mciaviOpenFile(NPMCIGRAPHIC npMCI);

BOOL NEAR PASCAL InitStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi);
BOOL NEAR PASCAL InitVideoStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi);
BOOL NEAR PASCAL InitAudioStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi);
BOOL NEAR PASCAL InitOtherStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi);
void NEAR PASCAL CloseStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi);

BOOL NEAR PASCAL OpenFileInit(NPMCIGRAPHIC npMCI);

BOOL NEAR PASCAL ParseNewHeader(NPMCIGRAPHIC npMCI);
				
BOOL NEAR PASCAL OpenRiffAVIFile(NPMCIGRAPHIC npMCI);
BOOL NEAR PASCAL OpenWithAVIFile(NPMCIGRAPHIC npMCI);
BOOL NEAR PASCAL OpenInterface(NPMCIGRAPHIC npMCI);
BOOL NEAR PASCAL OpenAVIFile(NPMCIGRAPHIC npMCI, IAVIFile FAR *pf);
BOOL NEAR PASCAL OpenAVIStream(NPMCIGRAPHIC npMCI, int stream, IAVIStream FAR *pf);

static BOOL NEAR PASCAL IsRectBogus(LPRECT prc);
static LONG NEAR PASCAL atol(char *sz);

#ifdef WIN32
    #define GetFileDriveType GetDriveType
#else
    static  UINT NEAR PASCAL GetFileDriveType(LPSTR szPath);
#endif


#ifndef WIN32
SZCODE szOLENLSDLL[] = "OLE2NLS.DLL";
SZCODE szOLENLSAPI[] = "GetUserDefaultLangID";
#endif

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api BOOL | mciaviOpenFile | Open an AVI file.
 *      the filename we are to open is passed to npMCI->szFileName.
 *
 * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
 *
 * @rdesc TRUE means OK, otherwise mci error in dwTaskError
 *
 ***************************************************************************/

BOOL FAR PASCAL mciaviOpenFile(NPMCIGRAPHIC npMCI)
{
    //
    // mciaviOpenFile should not be called with a file open!
    //
    Assert(npMCI->streams == 0);
    Assert(npMCI->hmmio == NULL);
    Assert(npMCI->hpIndex == NULL);
    Assert(!(npMCI->dwFlags & (
                        MCIAVI_NOTINTERLEAVED |
			MCIAVI_ANIMATEPALETTE |
			MCIAVI_CANDRAW |
                        MCIAVI_HASINDEX)));
    //
    //  !!!support open new
    //
    if (npMCI->szFilename[0] == '\0') {
    }

    //
    // what media is this file coming from, will be important later
    // when we play.
    //
    if (npMCI->szFilename[0] == '@')
        npMCI->uDriveType = DRIVE_INTERFACE;
    else
        npMCI->uDriveType = GetFileDriveType(npMCI->szFilename);

#ifdef DEBUG
    switch (npMCI->uDriveType) {
        case DRIVE_CDROM:
            DOUT2("Media is a CD-ROM\n");
            break;

        case DRIVE_REMOTE:
            DOUT2("Media is a Network\n");
            break;

        case DRIVE_FIXED:
            DOUT2("Media is a Hard disk\n");
            break;

        case DRIVE_REMOVABLE:
            DOUT2("Media is a floppy disk\n");
            break;

        case DRIVE_INTERFACE:
            DOUT2("Media is OLE COM Interface\n");
            break;

        default:
            DPF(("Unknown Media type %d\n", npMCI->uDriveType));
            break;
    }
#endif

#ifdef USEAVIFILE
    //
    // if the "filename" is of the form: '@########' then we assume we
    // have been pased a interface pointer of some sort.
    //
    if (npMCI->szFilename[0] == '@' &&
        OpenInterface(npMCI))
	goto DoneOpening;

    // !!! This will open even AVI files this way!
    if ((npMCI->dwOptionFlags & MCIAVIO_USEAVIFILE) &&
        OpenWithAVIFile(npMCI))
        goto DoneOpening;
#endif

    if (!OpenRiffAVIFile(npMCI)) {

        //
        //  unable to open RIFF file, if it was because it was
        //  not a AVI file, then give AVIFile a try.
        //
        if (npMCI->dwTaskError != AVIERR_NOT_AVIFILE)
            goto error;

#ifdef USEAVIFILE
        npMCI->dwTaskError = 0;

        if (!OpenWithAVIFile(npMCI))
#endif
            goto error;

    }

DoneOpening:
    if (OpenFileInit(npMCI)) {
        npMCI->dwTaskError = 0;
        return TRUE;
    }

error:
    mciaviCloseFile(npMCI);

    if (npMCI->dwTaskError == 0)
        npMCI->dwTaskError = MCIERR_INVALID_FILE;

    return FALSE;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api BOOL | OpenFileInit | called after a file is opened to init things
 *
 * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
 *
 * @rdesc TRUE means OK, otherwise mci error in dwTaskError
 *
 ***************************************************************************/

BOOL NEAR PASCAL OpenFileInit(NPMCIGRAPHIC npMCI)
{
    int i;
    RECT rc;

    //
    //  lets make sure we opened something.
    //
    if (npMCI->streams == 0)
        return FALSE;

    if (npMCI->nVideoStreams + npMCI->nAudioStreams + npMCI->nOtherStreams == 0)
        return FALSE;

    if (npMCI->nVideoStreams == 0)
        npMCI->dwFlags &= ~MCIAVI_SHOWVIDEO;

    if (npMCI->nAudioStreams == 0)
        npMCI->dwFlags &= ~MCIAVI_PLAYAUDIO;

    if (npMCI->nAudioStreams > 1) {
	UINT	    wLang;
	int	    stream;
	
#ifndef WIN32
	UINT (WINAPI * GetUserDefaultLangID)(void);
	UINT	    u;
	HANDLE	    hdll;
	
        u = SetErrorMode(SEM_NOOPENFILEERRORBOX);
        hdll = LoadLibrary(szOLENLSDLL);
        SetErrorMode(u);

        if ((UINT)hdll > (UINT)HINSTANCE_ERROR)
	{
	    if ((FARPROC) GetUserDefaultLangID = GetProcAddress(hdll, szOLENLSAPI)) {
#endif
		wLang = GetUserDefaultLangID();
#ifndef WIN32
	    }
	    FreeLibrary(hdll);
	} else
	    wLang = 0;
#endif
	DPF(("Current language: %x\n", wLang));

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

		    if (!(SH(stream).dwFlags & STREAM_ENABLED))
			continue;

		    if (SH(stream).wLanguage == wLang) {
                        npMCI->nAudioStream = stream;
                        npMCI->psiAudio = SI(stream);
			break;
		    }
		}
	    }
	}
    }

    if (npMCI->dwFlags & MCIAVI_NOTINTERLEAVED) {
        npMCI->wEarlyRecords = npMCI->wEarlyVideo;
    }
    else {
        npMCI->wEarlyRecords = max(npMCI->wEarlyVideo, npMCI->wEarlyAudio);
    }

    if (npMCI->wEarlyRecords == 0 &&
	    !(npMCI->dwFlags & MCIAVI_NOTINTERLEAVED)) {
        DPF(("Interleaved file with no audio skew?\n"));
        npMCI->dwFlags |= MCIAVI_NOTINTERLEAVED;
    }

    if (npMCI->dwFlags & MCIAVI_ANIMATEPALETTE) {
        DPF(("This AVI file has palette changes.\n"));

        if (npMCI->nVideoStreams > 1) {
            npMCI->dwFlags &= ~MCIAVI_ANIMATEPALETTE;
            DPF(("...But we are going to ignore them?\n"));
        }
    }

    //
    // this must be set
    //
    if (npMCI->dwSuggestedBufferSize == 0) {
        for (i=0; i<npMCI->streams; i++)
            npMCI->dwSuggestedBufferSize =
                max(SH(i).dwSuggestedBufferSize,npMCI->dwSuggestedBufferSize);
    }

    //
    // check all fields in the main header
    //
    if (npMCI->dwScale == 0 ||
        npMCI->dwRate == 0) {
    }

////will be set when header parsed
////npMCI->dwMicroSecPerFrame      = muldiv32(npMCI->dwScale, 1000000, npMCI->dwRate);
    npMCI->dwPlayMicroSecPerFrame  = npMCI->dwMicroSecPerFrame;

#define COMMON_SCALE    10000
    //
    // convert the rate/scale into something that is normalized to 1000
    //
    npMCI->dwRate = muldiv32(npMCI->dwRate, COMMON_SCALE, npMCI->dwScale);
    npMCI->dwScale = COMMON_SCALE;

    //
    // walk all streams and fix them up.
    //
    for (i=0; i<npMCI->streams; i++) {
        STREAMINFO *psi = SI(i);
        LONG lStart;
        LONG lEnd;

        //
        // convert the rate/scale into something that is normalized to 1000
        //
        psi->sh.dwRate = muldiv32(psi->sh.dwRate, COMMON_SCALE, psi->sh.dwScale);
        psi->sh.dwScale = COMMON_SCALE;

        //
        // trim any streams that hang over the movie.
        //
        lStart = MovieToStream(psi, 0);
        lEnd   = MovieToStream(psi, npMCI->lFrames);

        if ((LONG)(psi->sh.dwStart + psi->sh.dwLength) > lEnd) {

            DPF(("Stream #%d is too long, was %ld now %ld\n", i,
                psi->sh.dwLength, lEnd - psi->sh.dwStart));

            psi->sh.dwLength = lEnd - psi->sh.dwStart;
        }
    }

    //
    // fix up the movie rect
    //
    if (IsRectEmpty(&npMCI->rcMovie)) {
        DPF2(("Movie rect is empty\n"));

        SetRectEmpty(&rc);

        for (i=0; i<npMCI->streams; i++)
            UnionRect(&rc,&rc,&SH(i).rcFrame);

        npMCI->rcMovie = rc;
    }

    rc = npMCI->rcMovie;

    //
    // always read the index, so we can skip frames even on CD!
    //
    ReadIndex(npMCI);

    DPF(("Key frames are every (on average): %ld frames (%ld ms)\n",npMCI->dwKeyFrameInfo, MovieToTime(npMCI->dwKeyFrameInfo)));

    // force things to happen, in case we're re-loading
    SetRectEmpty(&npMCI->rcSource);
    SetRectEmpty(&npMCI->rcDest);

    /* this will call DrawDibBegin() ... */
    DevicePut(npMCI, &rc, MCI_DGV_PUT_SOURCE);

    /*
     * also set the dest rect. This should be done
     * by the WM_SIZE message sent during SetWindowToDefaultSize.
     * On NT, the WM_SIZE message is not sent synchronously since it
     * is an inter-thread sendmessage (the winproc is on the original thread
     * whereas we are currently running on the avi thread). The winproc
     * thread may well not get the WM_SIZE message until much too late, so
     * set the dest rect here. Note: don't use ResetDestRect since that
     * also relies on the window size, which is not set yet.
     */

    /* double frame size of destination if zoom by 2 */

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

    DevicePut(npMCI, &rc, MCI_DGV_PUT_DESTINATION);

    //
    // size the window and things.
    //
    SetWindowToDefaultSize(npMCI);

    DrawBegin(npMCI, NULL);
    return TRUE;
}

#ifdef USEAVIFILE

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api BOOL | OpenWithAVIFile | Open an file using AVIFile
 *
 * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
 *
 * @rdesc TRUE means OK, otherwise mci error in dwTaskError
 *
 ***************************************************************************/

BOOL NEAR PASCAL OpenWithAVIFile(NPMCIGRAPHIC npMCI)
{
    IAVIFile FAR *pf = NULL;

    if (!InitAVIFile(npMCI))
        return FALSE;

    AVIFileOpen(&pf, npMCI->szFilename, MMIO_READ, 0);

    if (pf == NULL) {
        npMCI->dwTaskError = MCIERR_INVALID_FILE;
        return FALSE;
    }

    if (!OpenAVIFile(npMCI, pf)) {
        mciaviCloseFile(npMCI);
        pf->lpVtbl->Release(pf);
        return FALSE;
    }

    return TRUE;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api BOOL | OpenInterface | Open an interface pointer
 *
 * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
 *
 * @rdesc TRUE means OK, otherwise mci error in dwTaskError
 *
 ***************************************************************************/

BOOL NEAR PASCAL OpenInterface(NPMCIGRAPHIC npMCI)
{
    IUnknown FAR *p;
    IAVIFile FAR *pf=NULL;
    IAVIStream FAR *ps=NULL;

    if (!InitAVIFile(npMCI))
        return FALSE;

    if (npMCI->szFilename[0] != '@')
        return FALSE;

#ifdef UNICODE
    p = (IUnknown FAR *)wcstol(npMCI->szFilename+1, NULL, 10);
#else
    p = (IUnknown FAR *)atol(npMCI->szFilename+1);
#endif

    if (!IsValidInterface(p))
        return FALSE;

#ifndef WIN32
    //!!!we need to do the PSP stuff? or will the TASK stuff in
    //!!!COMPOBJ mess us up?
    {
    extern void FAR SetPSP(UINT psp);
    SetPSP(npMCI->pspParent);
    }
#endif

    p->lpVtbl->QueryInterface(p, &IID_IAVIFile, (LPVOID FAR *)&pf);

    if (pf != NULL)
    {
        if (OpenAVIFile(npMCI, pf))
            return TRUE;

        pf->lpVtbl->Release(pf);
    }

    p->lpVtbl->QueryInterface(p, &IID_IAVIStream, (LPVOID FAR *)&ps);

    if (ps != NULL)
    {
        AVIMakeFileFromStreams(&pf, 1, &ps);
        ps->lpVtbl->Release(ps);

        if (pf == NULL)
            return FALSE;

        if (OpenAVIFile(npMCI, pf))
            return TRUE;

        pf->lpVtbl->Release(pf);
        return FALSE;
    }

    return FALSE;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api BOOL | OpenAVIFile | Open an a AVIFile object
 *
 *  NOTE we do not do call AddRef() we assume we dont need to.
 *
 * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
 *
 * @rdesc TRUE means OK, otherwise mci error in dwTaskError
 *
 ***************************************************************************/

BOOL NEAR PASCAL OpenAVIFile(NPMCIGRAPHIC npMCI, IAVIFile FAR *pf)
{
    AVIFILEINFO info;
    HRESULT hr;
    IAVIStream FAR *ps;
    STREAMINFO *psi;
    int i;

    Assert(npMCI->pf == NULL);

    _fmemset(&info, 0, sizeof(info));
    hr = AVIFileInfo(pf, &info, sizeof(info));

    if (FAILED(GetScode(hr))) {
	npMCI->dwTaskError = MCIERR_INVALID_FILE;
        return FALSE;
    }

    DPF(("OpenAVIFile: %s\n", (LPSTR)info.szFileType));

    //
    // get rid of bad files
    //
    if (info.dwStreams == 0 || info.dwStreams > 255 || info.dwLength == 0) {
	npMCI->dwTaskError = MCIERR_INVALID_FILE;
        return FALSE;
    }

    //
    // make a copy of the VTable, for later use
    //
    npMCI->pf = pf;
////npMCI->vt = *pf->lpVtbl;

    npMCI->dwFlags |= MCIAVI_HASINDEX;

    npMCI->dwMicroSecPerFrame = muldiv32(info.dwScale, 1000000, info.dwRate);

    npMCI->lFrames  = (LONG)info.dwLength;
    npMCI->dwRate   = info.dwRate;
    npMCI->dwScale  = info.dwScale;

    npMCI->streams  = (int)info.dwStreams;

    npMCI->dwBytesPerSec = info.dwMaxBytesPerSec;
    npMCI->dwSuggestedBufferSize = info.dwSuggestedBufferSize + 2*sizeof(DWORD);

    SetRect(&npMCI->rcMovie,0,0,(int)info.dwWidth,(int)info.dwHeight);

    npMCI->paStreamInfo = (STREAMINFO*)
        LocalAlloc(LPTR,npMCI->streams * sizeof(STREAMINFO));

    if (npMCI->paStreamInfo == NULL) {
        npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
        npMCI->pf = NULL;
        return FALSE;
    }

    for (i = 0; i < npMCI->streams; i++) {

        ps = NULL;
        AVIFileGetStream(pf, &ps, 0, i);

        if (ps == NULL) {
            npMCI->dwTaskError = MCIERR_INVALID_FILE;
            npMCI->pf = NULL;
            return FALSE;
        }

        if (!OpenAVIStream(npMCI, i, ps))
            DPF(("Error opening stream %d!\n", i));

        if (npMCI->dwTaskError) {
            npMCI->pf = NULL;
            return FALSE;
        }
    }

    //
    // compute the key frames every value
    //
    // do this by finding the key frame average over the first few frames.
    //
    #define NFRAMES 250

    if (psi = npMCI->psiVideo) {
        LONG l;
        int nKeyFrames=0;

        for (l=0; l<NFRAMES; l++) {
            if (AVIStreamFindSample(psi->ps, psi->sh.dwStart+l, FIND_PREV|FIND_KEY) == l)
                nKeyFrames++;
        }

        if (nKeyFrames > 1)
            npMCI->dwKeyFrameInfo = (DWORD)((NFRAMES + nKeyFrames/2)/nKeyFrames);
        else
            npMCI->dwKeyFrameInfo = 0;
    }

    return TRUE;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api BOOL | OpenAVIStream | Open an a AVIStream object
 *
 * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
 *
 * @rdesc TRUE means OK, otherwise mci error in dwTaskError
 *
 ***************************************************************************/

BOOL NEAR PASCAL OpenAVIStream(NPMCIGRAPHIC npMCI, int stream, IAVIStream FAR *ps)
{
    STREAMINFO* psi;
    AVISTREAMINFO info;
    HRESULT hr;

    _fmemset(&info, 0, sizeof(info));
    hr = AVIStreamInfo(ps, &info, sizeof(info));

    if (FAILED(GetScode(hr))) {
	npMCI->dwTaskError = MCIERR_INVALID_FILE;
        return FALSE;
    }

    DPF(("OpenAVIStream(%d) %4.4s:%4.4s %s\n", stream, (LPSTR)&info.fccType, (LPSTR)&info.fccHandler, (LPSTR)info.szName));

    //
    // init the STREAMINFO from the IAVIStream
    //
    psi = SI(stream);

    psi->ps = ps;           // save interface
////psi->vt = *ps->lpVtbl;  // save VTable   !!!needed?

    psi->sh.fccType                 = info.fccType;
    psi->sh.fccHandler              = info.fccHandler;
    psi->sh.dwFlags                 = info.dwFlags;
    psi->sh.wPriority               = info.wPriority;
    psi->sh.wLanguage               = info.wLanguage;
    psi->sh.dwInitialFrames         = 0; // info.dwInitialFrames;
    psi->sh.dwScale                 = info.dwScale;
    psi->sh.dwRate                  = info.dwRate;
    psi->sh.dwStart                 = info.dwStart;
    psi->sh.dwLength                = info.dwLength;
    psi->sh.dwSuggestedBufferSize   = info.dwSuggestedBufferSize;
    psi->sh.dwQuality               = info.dwQuality;
    psi->sh.dwSampleSize            = info.dwSampleSize;
    psi->sh.rcFrame                 = info.rcFrame;
    DPF0(("OpenAVIStream: #%d, rc [%d %d %d %d]\n", stream, info.rcFrame));

    //
    // get the format of the stream.
    //
    AVIStreamFormatSize(ps, 0, &psi->cbFormat);
    psi->lpFormat = GlobalAllocPtr(GMEM_MOVEABLE, psi->cbFormat);

    if (!psi->lpFormat) {
        npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
        return FALSE;
    }

    AVIStreamReadFormat(psi->ps, 0, psi->lpFormat, &psi->cbFormat);

    //
    // get the extra data for the stream.
    //
    AVIStreamReadData(psi->ps,ckidSTREAMHANDLERDATA, NULL, &psi->cbData);

    if (psi->cbData > 0) {
        psi->lpData = GlobalAllocPtr(GMEM_MOVEABLE, psi->cbData);

        if (!psi->lpData) {
            npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
            return FALSE;
        }

        AVIStreamReadData(psi->ps, ckidSTREAMHANDLERDATA, NULL, &psi->cbData);
    }

    return InitStream(npMCI, psi);
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api BOOL | InitAVIFile | called to RTL to AVIFILE.DLL
 *
 * @rdesc TRUE means OK, otherwise error
 *
 ***************************************************************************/

#ifdef WIN32
    // For the moment the 16 bit and 32 bit AVIFILE DLLs share the same
    // name.  If the history of NT is to be repeated this will change.
    SZCODE szAVIFILE[] = TEXT("AVIFILE.DLL");
    SZCODE szCOMPOBJ[] = TEXT("COMPOB32");
#else
    SZCODE szAVIFILE[] = "AVIFILE.DLL";
    SZCODE szCOMPOBJ[] = "COMPOBJ";
#endif

// On NT the entry points will NOT be unicode strings, as there is
// no unicode version of GetProcAddress.  BUT SZCODE generate UNICODE...
SZCODEA szAVIFileInit[]      = "AVIFileInit";
SZCODEA szAVIFileExit[]      = "AVIFileExit";
SZCODEA szIsValidInterface[] = "IsValidInterface";
SZCODEA szAVIMakeFileFromStreams[] = "AVIMakeFileFromStreams";
SZCODEA szAVIStreamBeginStreaming[] = "AVIStreamBeginStreaming";
SZCODEA szAVIStreamEndStreaming[] = "AVIStreamEndStreaming";
#ifdef UNICODE
  // There has GOT to be a neat way of combining macros so that we can
  // assign AVIFileOpenA/W to the name string definining the entry point,
  // and still get avifilex.h to get AVIFileOpen function calls replaced by
  // using the function variable.
  SZCODEA szAVIFileOpen[]      = "AVIFileOpenW";
#else
  SZCODEA szAVIFileOpen[]      = "AVIFileOpen";
#endif

BOOL FAR InitAVIFile(NPMCIGRAPHIC npMCI)
{
    UINT u;

    if (hdllAVIFILE == (HMODULE)-1)
        return FALSE;

    if (hdllAVIFILE == NULL) {

        u = SetErrorMode(SEM_NOOPENFILEERRORBOX);
        hdllAVIFILE = LoadLibrary(szAVIFILE);
        SetErrorMode(u);

#ifndef WIN32
        if ((UINT)hdllAVIFILE <= (UINT)HINSTANCE_ERROR)
            hdllAVIFILE = NULL;
#endif

        if (hdllAVIFILE == NULL) {
            hdllAVIFILE = (HMODULE)-1;
            return FALSE;
        }

        hdllCOMPOBJ = GetModuleHandle(szCOMPOBJ);
        (FARPROC)XIsValidInterface = GetProcAddress(hdllCOMPOBJ, szIsValidInterface);

        Assert(hdllCOMPOBJ != NULL);
        Assert(XIsValidInterface != NULL);

        (FARPROC)XAVIFileInit = GetProcAddress(hdllAVIFILE, szAVIFileInit);
        (FARPROC)XAVIFileExit = GetProcAddress(hdllAVIFILE, szAVIFileExit);
        (FARPROC)XAVIFileOpen = GetProcAddress(hdllAVIFILE, szAVIFileOpen);
        (FARPROC)XAVIMakeFileFromStreams = GetProcAddress(hdllAVIFILE, szAVIMakeFileFromStreams);
        (FARPROC)XAVIStreamBeginStreaming = GetProcAddress(hdllAVIFILE, szAVIStreamBeginStreaming);
        (FARPROC)XAVIStreamEndStreaming = GetProcAddress(hdllAVIFILE, szAVIStreamEndStreaming);

        Assert(XAVIFileInit != NULL);
        Assert(XAVIFileExit != NULL);
        Assert(XAVIFileOpen != NULL);
        Assert(XAVIMakeFileFromStreams != NULL);
    }

    //
    // we need to call AVIFileInit() and AVIFileExit() for each task that
    // is using AVIFile or things will not work right.
    //
    if (!(npMCI->dwFlags & MCIAVI_USING_AVIFILE)) {

        npMCI->dwFlags |= MCIAVI_USING_AVIFILE;

        AVIFileInit();
        uAVIFILE++;
    }

    return TRUE;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api BOOL | FreeAVIFile | called to un-RTL to AVIFILE.DLL
 *
 * @rdesc TRUE means OK, otherwise error
 *
 ***************************************************************************/

BOOL FAR FreeAVIFile(NPMCIGRAPHIC npMCI)
{
    if (!(npMCI->dwFlags & MCIAVI_USING_AVIFILE))
        return FALSE;

    Assert(hdllAVIFILE != (HMODULE)-1 && hdllAVIFILE != NULL);

    // free this tasks use of AVIFile
    AVIFileExit();

    // if no more people using AVIFile lets the DLLs go.
    Assert(uAVIFILE > 0);
    uAVIFILE--;

    if (uAVIFILE == 0) {
        FreeLibrary(hdllAVIFILE);
        hdllAVIFILE = NULL;
        hdllCOMPOBJ = NULL;
    }
}

#endif /* USEAVIFILE */


/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api BOOL | OpenRiffAVIFile | Open an RIFF AVI file
 *
 * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
 *
 * @rdesc TRUE means OK, otherwise mci error in dwTaskError
 *
 ***************************************************************************/

BOOL NEAR PASCAL OpenRiffAVIFile(NPMCIGRAPHIC npMCI)
{
    HMMIO		hmmio;
    HANDLE		h = NULL;
    BOOL		fRet = TRUE;
    MMIOINFO		mmioInfo;
    MMCKINFO		ckRIFF;
    MMCKINFO		ckLIST;
    MMCKINFO            ckRECORD;

    _fmemset(&mmioInfo, 0, sizeof(MMIOINFO));
    mmioInfo.htask = (HANDLE)npMCI->hCallingTask;

    hmmio = mmioOpen(npMCI->szFilename, &mmioInfo, MMIO_READ | MMIO_DENYWRITE);

    if (hmmio == NULL)
        hmmio = mmioOpen(npMCI->szFilename, &mmioInfo, MMIO_READ);

    if (!hmmio) {
	switch (mmioInfo.wErrorRet) {
	    case MMIOERR_OUTOFMEMORY:
		npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
		break;
	    case MMIOERR_FILENOTFOUND:
	    case MMIOERR_CANNOTOPEN:
	    default:
		npMCI->dwTaskError = MCIERR_FILE_NOT_FOUND;
		break;
	}
	fRet = FALSE;
	goto exit;
    }

    npMCI->hmmio = hmmio;

    /*
    ** Descend into RIFF file
    */
    if (mmioDescend(hmmio, &ckRIFF, NULL, 0) != 0) {
	npMCI->dwTaskError = MCIERR_INVALID_FILE;
	goto ERROR_BADFILE;
    }

    /*
     * check for a 'QuickTime AVI' file, a QuickTime AVI file is a
     * QuickTime public movie with a AVI file in the 'mdat' atom.
     */
    if (ckRIFF.cksize == mmioFOURCC('m','d','a','t'))
    {
        DPF(("File is a QuickTime public movie\n"));

        /*
         * now the 'mdat' atom better be a RIFF/AVI or we cant handle it.
         */
        if (mmioDescend(hmmio, &ckRIFF, NULL, 0) != 0) {
            npMCI->dwTaskError = MCIERR_INVALID_FILE;
            goto ERROR_BADFILE;
        }
    }

    /* Make sure it's a RIFF file */
    if (ckRIFF.ckid != FOURCC_RIFF) {
	npMCI->dwTaskError = MCIERR_INVALID_FILE;
        goto ERROR_NOTAVIFILE;
    }

    /* Otherwise, it should be an AVI file */
    if (ckRIFF.fccType != formtypeAVI) {
	npMCI->dwTaskError = MCIERR_INVALID_FILE;
        goto ERROR_NOTAVIFILE;
    }

    /*
    ** Descend into header LIST
    */
    ckLIST.fccType = listtypeAVIHEADER;
    if (mmioDescend(hmmio, &ckLIST, &ckRIFF, MMIO_FINDLIST) != 0) {
	npMCI->dwTaskError = MCIERR_INVALID_FILE;
	goto ERROR_BADFILE;
    }

    /* Leave space at end of buffer for pad word */
    h = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, ckLIST.cksize -
				    sizeof(DWORD) +
				    sizeof(DWORD));

    if (!h) {
	npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
	return FALSE;
    }

    npMCI->lp = npMCI->lpBuffer = (LPSTR) GlobalLock(h);

    DPF(("Reading header list: %lu bytes.\n", ckLIST.cksize - sizeof(DWORD)));

    if (mmioRead(hmmio, npMCI->lp, ckLIST.cksize - sizeof(DWORD))
			    != (LONG) (ckLIST.cksize - sizeof(DWORD))) {
	npMCI->dwTaskError = MCIERR_FILE_READ;
	goto ERROR_BADFILE;
    }

#ifdef USE_AVIFILE_FOR_NON_INT
    //
    //  we check here for AVI RIFF files we dont want to handle with our
    //  built in code, and want to pass on to AVIFILE.DLL
    //
    //  we handle the following files:
    //
    //      interleaved
    //
    //  we pass on the following files to AVIFILE.DLL
    //
    //      non-interleaved
    //
    //  pretty simple right now, just interleaved non-interlaved
    //  but could get as complex as you want.
    //
    {
    MainAVIHeader FAR * lpHdr;

    lpHdr = (MainAVIHeader FAR *)((BYTE FAR *)npMCI->lp + 8);

    if (!(lpHdr->dwFlags & AVIF_ISINTERLEAVED) ||
        lpHdr->dwInitialFrames == 0) {

        DOUT("File is not interleaved, giving it to AVIFILE.DLL\n");
        goto ERROR_NOTAVIFILE;
    }

    //
    // ok now we have a 1:1 interleved file.
    //
    // always use our code on a CD-ROM, but on other media...
    //
    switch (npMCI->uDriveType) {
        case DRIVE_CDROM:
            break;

        case DRIVE_REMOTE:
        case DRIVE_FIXED:
        case DRIVE_REMOVABLE:
            break;

        default:
            break;
    }
    }
#endif

    if (PEEK_DWORD() == ckidAVIMAINHDR) {
	if (!ParseNewHeader(npMCI))
	    goto ERROR_BADFILE;
    } else {
	npMCI->dwTaskError = MCIERR_INVALID_FILE;
	goto ERROR_BADFILE;
    }

    /* Ascend out of header LIST */
    if (mmioAscend(hmmio, &ckLIST, 0) != 0) {
	npMCI->dwTaskError = MCIERR_FILE_READ;
	goto ERROR_BADFILE;
    }

    /* Initially, no frame has been drawn */
    npMCI->lFrameDrawn = (- (LONG) npMCI->wEarlyRecords) - 1;

    /*
    ** Descend into big 'Movie LIST'
    */
    ckLIST.fccType = listtypeAVIMOVIE;
    if (mmioDescend(hmmio, &ckLIST, &ckRIFF, MMIO_FINDLIST) != 0) {
	npMCI->dwTaskError = MCIERR_INVALID_FILE;
	goto ERROR_BADFILE;
    }

    npMCI->dwMovieListOffset = ckLIST.dwDataOffset;

    /* Calculate end of 'movi' list, in case we need to read the index */
    npMCI->dwBigListEnd = ckLIST.dwDataOffset + ckLIST.cksize +
				(ckLIST.cksize & 1);	

    /*
    ** Descend into header of first chunk
    */
    if (mmioDescend(hmmio, &ckRECORD, &ckLIST, 0) != 0) {
        npMCI->dwTaskError = MCIERR_INVALID_FILE;
        goto ERROR_BADFILE;
    }
    npMCI->dwFirstRecordType = ckRECORD.ckid;
    npMCI->dwFirstRecordSize = ckRECORD.cksize + 2 * sizeof(DWORD);
    npMCI->dwFirstRecordPosition = mmioSeek(hmmio, 0, SEEK_CUR);

    if (mmioAscend(hmmio, &ckRECORD, 0) != 0) {
	npMCI->dwTaskError = MCIERR_FILE_READ;
	goto ERROR_BADFILE;
    }

#ifdef DEBUG
    DPF2(("First record (%4.4s) 0x%lx bytes at position 0x%lx.\n",
                (LPSTR)&npMCI->dwFirstRecordType,
                npMCI->dwFirstRecordSize,
                npMCI->dwFirstRecordPosition));

    if (npMCI->dwFirstRecordPosition & 0x7ff) {
	DPF(("!!\n"));
	DPF(("!!  This file is not properly aligned to a 2K boundary.\n"));
	DPF(("!!  It may not play well from CD-ROM.\n"));
	DPF(("!!\n"));
    }
#endif

exit:
    if (!fRet)
        mciaviCloseFile(npMCI);

    if (h) {
        npMCI->lpBuffer = NULL;
        npMCI->dwBufferSize = 0L;
	GlobalUnlock(h);
	GlobalFree(h);
    }

    return fRet;

ERROR_NOTAVIFILE:
    npMCI->dwTaskError = AVIERR_NOT_AVIFILE;        // mark as not a AVI file

ERROR_BADFILE:
    fRet = FALSE;
    goto exit;
}

/***************************************************************************
 * @doc INTERNAL MCIAVI
 *
 * @api BOOL | ParseNewHeader | 'nuf said
 *
 ***************************************************************************/

BOOL NEAR PASCAL ParseNewHeader(NPMCIGRAPHIC npMCI)
{
    DWORD		dwHeaderSize;
    MainAVIHeader FAR *	lpHdr;
    int			stream;

    if (GET_DWORD() != ckidAVIMAINHDR) {
	goto FileError;
    }

    dwHeaderSize = GET_DWORD(); /* Skip size */

    /* Now, we're pointing at the actual header */
    lpHdr = (MainAVIHeader FAR *) npMCI->lp;

    npMCI->lFrames = (LONG)lpHdr->dwTotalFrames;
    npMCI->dwMicroSecPerFrame = lpHdr->dwMicroSecPerFrame;
    npMCI->dwRate = 1000000;
    npMCI->dwScale = npMCI->dwMicroSecPerFrame;

    /* Reject some bad values */
    if (!lpHdr->dwStreams || lpHdr->dwStreams > 255 || !npMCI->lFrames) {
	goto FileError;
    }

    npMCI->streams = (int) lpHdr->dwStreams;
    npMCI->dwBytesPerSec = lpHdr->dwMaxBytesPerSec;
    npMCI->wEarlyRecords = (UINT) lpHdr->dwInitialFrames;
    npMCI->dwSuggestedBufferSize = lpHdr->dwSuggestedBufferSize;

    SetRect(&npMCI->rcMovie,0,0,(int)lpHdr->dwWidth,(int)lpHdr->dwHeight);

    npMCI->dwFlags |= MCIAVI_HASINDEX;

    if (!(lpHdr->dwFlags & AVIF_ISINTERLEAVED)) {
	DPF(("File is not interleaved.\n"));
	npMCI->dwFlags |= MCIAVI_NOTINTERLEAVED;
    }

    SKIP_BYTES(dwHeaderSize);	/* Skip rest of chunk */

    npMCI->paStreamInfo = (STREAMINFO NEAR *)
		    LocalAlloc(LPTR, npMCI->streams * sizeof(STREAMINFO));
    // !!! error check

    for (stream = 0; stream < npMCI->streams; stream++) {
	AVIStreamHeader FAR *	lpStream;
	HPSTR			hpNextChunk;
        STREAMINFO *            psi = &npMCI->paStreamInfo[stream];
	
	if (GET_DWORD() != FOURCC_LIST) {
	    goto FileError;
	}

	dwHeaderSize = GET_DWORD(); /* Skip size */

	hpNextChunk = npMCI->lp + (dwHeaderSize + (dwHeaderSize & 1));
	
	if (GET_DWORD() != listtypeSTREAMHEADER) {
	    goto FileError;
	}
	
	/* Now, we're at the begging of the stream's header chunks. */

	if (GET_DWORD() != ckidSTREAMHEADER) {
	    goto FileError;
	}

	dwHeaderSize = GET_DWORD(); /* Skip size */

	/* Now, we're pointing at the stream header */
	lpStream = (AVIStreamHeader FAR *) npMCI->lp;
        hmemcpy(&psi->sh, lpStream, min(dwHeaderSize, sizeof(psi->sh)));

        //
        // reject files with more than one video stream.
        //
        if (psi->sh.fccType == streamtypeVIDEO &&
            npMCI->nVideoStreams >= 1) {
            DPF(("File has multiple video streams, giving it to AVIFILE.DLL\n"));
            goto DontHandleThisFile;
        }

	SKIP_BYTES(dwHeaderSize);
	
        /* Read the  format */
        if (GET_DWORD() != ckidSTREAMFORMAT) {
            goto FileError;
        }

        dwHeaderSize = GET_DWORD(); /* Skip size */

        if (dwHeaderSize > 16384L) {
	    goto FileError;
	}
	
        psi->cbFormat = dwHeaderSize;
        psi->lpFormat = GlobalAllocPtr(GHND,dwHeaderSize);
        if (!psi->lpFormat) {
            npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
            return FALSE;
        }
	
        hmemcpy(psi->lpFormat, npMCI->lp, dwHeaderSize);
	
        SKIP_BYTES(dwHeaderSize);

        if (PEEK_DWORD() == ckidSTREAMHANDLERDATA) {
	    GET_DWORD();
            dwHeaderSize = GET_DWORD(); /* Skip size */
	
            psi->cbData = dwHeaderSize;
            psi->lpData = GlobalAllocPtr(GHND,dwHeaderSize);

            if (!psi->lpData) {
                npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
		return FALSE;
            }

            hmemcpy(psi->lpData, npMCI->lp, dwHeaderSize);

            /* Skip to end of Data chunk */
            SKIP_BYTES(dwHeaderSize);
        } else {
            psi->cbData = 0;
            psi->lpData = NULL;
        }

        InitStream(npMCI, psi);
	
	npMCI->lp = hpNextChunk;
    }

    return TRUE;

FileError:
    npMCI->dwTaskError = MCIERR_INVALID_FILE;
    return FALSE;

DontHandleThisFile:
    npMCI->dwTaskError = AVIERR_NOT_AVIFILE;
    return FALSE;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api BOOL | mciaviCloseFile | Close an AVI file.
 *
 * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
 *
 * @rdesc TRUE means OK, otherwise mci error in dwTaskError
 *
 ***************************************************************************/

BOOL FAR PASCAL mciaviCloseFile (NPMCIGRAPHIC npMCI)
{
    if (!npMCI)
        return FALSE;

#ifdef DEBUG
    npMCI->mciid = MCIIDX;
#endif

    if (npMCI->lpMMIOBuffer) {
        GlobalFreePtr(npMCI->lpMMIOBuffer);
        npMCI->lpMMIOBuffer = NULL;
    }

    npMCI->hicDraw = NULL;

    if (npMCI->hicDrawDefault) {
	if (npMCI->hicDrawDefault != (HIC) -1)
	    ICClose(npMCI->hicDrawDefault);
        npMCI->hicDrawDefault = NULL;
    }

    if (npMCI->hicDrawFull) {
	if (npMCI->hicDrawFull != (HIC) -1)
	    ICClose(npMCI->hicDrawFull);
        npMCI->hicDrawFull = NULL;
    }

    if (npMCI->hicDecompress) {
	// !!! What if we never began it?
	ICDecompressEnd(npMCI->hicDecompress);

        ICClose(npMCI->hicDecompress);
        npMCI->hicDecompress = NULL;
    }

    if (npMCI->hicInternal) {
        ICClose(npMCI->hicInternal);
        npMCI->hicInternal = NULL;
    }

    if (npMCI->hicInternalFull) {
        ICClose(npMCI->hicInternalFull);
        npMCI->hicInternalFull = NULL;
    }

    if (npMCI->hmmio) {
	mmioClose(npMCI->hmmio, 0);
        npMCI->hmmio = NULL;
    }

    if (npMCI->hmmioAudio) {
	mmioClose(npMCI->hmmioAudio, 0);
        npMCI->hmmioAudio = NULL;
    }

    if (npMCI->pWF) {
	LocalFree((HANDLE) npMCI->pWF);
        npMCI->pWF = NULL;
    }

    if (npMCI->pbiFormat) {
	GlobalFreePtr(npMCI->pbiFormat);
        npMCI->pbiFormat = NULL;
    }

//  if (npMCI->hpal) {
//      DeleteObject(npMCI->hpal);
//      npMCI->hpal = NULL;
//  }

    if (npMCI->hpDecompress) {
        GlobalFreePtr(npMCI->hpDecompress);
        npMCI->hpDecompress = NULL;
    }

    if (npMCI->hpIndex) {
        GlobalFreePtr(npMCI->hpIndex);
        npMCI->hpIndex = NULL;
    }

    if (npMCI->hpFrameIndex) {
        GlobalFreePtr(npMCI->hpFrameIndex);  //!!!NTBUG not same pointer!
        npMCI->hpFrameIndex = NULL;
    }

    if (npMCI->pVolumeTable) {
        LocalFree((HLOCAL)npMCI->pVolumeTable);
        npMCI->pVolumeTable = NULL;
    }

#ifdef USEAVIFILE
    if (npMCI->pf) {
	AVIFileClose(npMCI->pf);
	npMCI->pf = NULL;
    }
#endif

    if (npMCI->paStreamInfo) {
        int i;

        for (i = 0; i < npMCI->streams; i++)
            CloseStream(npMCI, &npMCI->paStreamInfo[i]);

        LocalFree((HLOCAL)npMCI->paStreamInfo);
        npMCI->paStreamInfo = NULL;
    }

    npMCI->streams = 0;
    npMCI->nAudioStreams = 0;
    npMCI->nVideoStreams = 0;
    npMCI->nErrorStreams = 0;
    npMCI->nOtherStreams = 0;

    npMCI->wEarlyVideo = 0;
    npMCI->wEarlyAudio = 0;
    npMCI->wEarlyRecords = 0;

    //!!! I bet we need to clear more
    npMCI->dwFlags &= ~(MCIAVI_NOTINTERLEAVED |
			MCIAVI_ANIMATEPALETTE |
			MCIAVI_CANDRAW |
                        MCIAVI_HASINDEX);

    return TRUE;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api BOOL | CloseStream | Close an StreamAVI file.
 *
 ***************************************************************************/

void NEAR PASCAL CloseStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
{
    psi->dwFlags &= ~STREAM_ENABLED;
////psi->sh.fccType = 0;
////psi->sh.fccHandler = 0;

    if (psi->lpFormat)
        GlobalFreePtr(psi->lpFormat);
    psi->lpFormat = NULL;

    if (psi->lpData)
        GlobalFreePtr(psi->lpData);
    psi->lpData = NULL;

    if (psi->hicDraw)
        ICClose(psi->hicDraw);
    psi->hicDraw = NULL;

#ifdef USEAVIFILE
    if (psi->ps)
        AVIStreamClose(psi->ps);
    psi->ps = NULL;
#endif
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api BOOL | InitStream | initialize a stream
 *
 ***************************************************************************/

BOOL NEAR PASCAL InitStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
{
    BOOL f;

    //
    // set flags
    //
    if (psi->sh.dwFlags & AVISF_VIDEO_PALCHANGES)
        psi->dwFlags |= STREAM_PALCHANGES;

    psi->lStart = (LONG)psi->sh.dwStart;
    psi->lEnd   = (LONG)psi->sh.dwStart + psi->sh.dwLength;

    if (psi->sh.fccType == streamtypeVIDEO &&
        !(npMCI->dwFlags & MCIAVI_NOTINTERLEAVED))
        psi->lStart -= (LONG)psi->sh.dwInitialFrames;

    switch(psi->sh.fccType) {
        case streamtypeVIDEO:
            f = InitVideoStream(npMCI, psi);
            break;

        case streamtypeAUDIO:
            f = InitAudioStream(npMCI, psi);
            break;

        default:
            f = InitOtherStream(npMCI, psi);
            break;
            }

    if (!f)  {
        psi->dwFlags |= STREAM_ERROR;
        npMCI->nErrorStreams++;
        CloseStream(npMCI, psi);
    }

    //
    // disable the stream if the file header says to
    //
    if (psi->sh.dwFlags & AVISF_DISABLED) {
        psi->dwFlags &= ~STREAM_ENABLED;
    }

    return f;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api BOOL | InitVideoStream | initialize a video stream
 *
 ***************************************************************************/

BOOL NEAR PASCAL InitVideoStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
{
    LPBITMAPINFOHEADER lpbi;
    int stream = psi - npMCI->paStreamInfo;

    npMCI->wEarlyVideo = (UINT)psi->sh.dwInitialFrames;

    if (psi->sh.dwFlags & AVISF_VIDEO_PALCHANGES) {
        //!!! is this right.
        npMCI->dwFlags |= MCIAVI_ANIMATEPALETTE;
    }

    if (IsRectBogus(&psi->sh.rcFrame)) {
        DPF(("BOGUS Stream rectangle [%d %d %d %d]\n", psi->sh.rcFrame));
        SetRectEmpty(&psi->sh.rcFrame);
    }

    // In case the rectangle is totally wrong, chop it down to size....
    // !!! What if the user _wants_ a zero-size RECT?
    IntersectRect(&psi->sh.rcFrame, &psi->sh.rcFrame, &npMCI->rcMovie);

    if (IsRectEmpty(&psi->sh.rcFrame)) {
        DPF(("Video stream rect is empty, correcting\n"));
        SetRect(&psi->sh.rcFrame, 0, 0,
            (int)((LPBITMAPINFOHEADER)psi->lpFormat)->biWidth,
            (int)((LPBITMAPINFOHEADER)psi->lpFormat)->biHeight);
    }

    //
    // make sure the biCompression is right for RLE files.
    //
    lpbi = (LPBITMAPINFOHEADER)psi->lpFormat;

    if (psi->sh.fccHandler == 0) {

        if (lpbi->biCompression == 0)
            psi->sh.fccHandler = comptypeDIB;

        if (lpbi->biCompression == BI_RLE8 && lpbi->biBitCount == 8)
            psi->sh.fccHandler = comptypeRLE;

        if (lpbi->biCompression > 256)
            psi->sh.fccHandler = lpbi->biCompression;
    }

    if (lpbi->biCompression <= BI_RLE8 && lpbi->biBitCount == 8) {

        if (psi->sh.fccHandler == comptypeRLE0 ||
            psi->sh.fccHandler == comptypeRLE)
            lpbi->biCompression = BI_RLE8;

// Assuming a DIB handler has RGB data will blow up files that have RLE data.
// Unfortunately, VidEdit writes out files like this.
//        if (psi->sh.fccHandler == comptypeDIB)
//            lpbi->biCompression = BI_RGB;
    }

    //
    // make sure the color table is set to the right size
    //
    if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
        lpbi->biClrUsed = (1 << (int)lpbi->biBitCount);

    //
    // try to open draw handler
    //
    if (psi->sh.fccHandler) {
        psi->hicDraw = ICDrawOpen(psi->sh.fccType,psi->sh.fccHandler,psi->lpFormat);

        if (psi->hicDraw)
            DPF(("Opened draw handler %4.4s:%4.4s\n", (LPSTR)&psi->sh.fccType,(LPSTR)&psi->sh.fccHandler));
    }

    //
    // one video stream is the master, he controls the palette etc
    // for lack of a better default the first video stream will
    // become the master.
    //
    if (npMCI->pbiFormat == NULL) {

        npMCI->nVideoStream = stream;
        npMCI->psiVideo = psi;

        npMCI->pbiFormat = (LPBITMAPINFOHEADER)
                    GlobalAllocPtr(GMEM_MOVEABLE, psi->cbFormat);

	if (!npMCI->pbiFormat) {
	    npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
	    return FALSE;
	}

	//
        // copy the entire format over
	//
        hmemcpy(npMCI->pbiFormat,psi->lpFormat,psi->cbFormat);

        npMCI->bih = *npMCI->pbiFormat;
        npMCI->bih.biSize = sizeof(BITMAPINFOHEADER);
        npMCI->bih.biCompression = BI_RGB;

        if (npMCI->bih.biClrUsed) {
            /* save the original colors. */
            hmemcpy(npMCI->argb, (LPBYTE)npMCI->pbiFormat + npMCI->pbiFormat->biSize,
                            (int)npMCI->bih.biClrUsed * sizeof(RGBQUAD));
            hmemcpy(npMCI->argbOriginal, (LPSTR) npMCI->pbiFormat + npMCI->pbiFormat->biSize,
                            (int)npMCI->bih.biClrUsed * sizeof(RGBQUAD));
        }

	//
	// now open the decompressor, try fastdecompress if it will do it.
	//
        npMCI->hicDecompress = ICLocate(ICTYPE_VIDEO,psi->sh.fccHandler,
                    psi->lpFormat,NULL,ICMODE_FASTDECOMPRESS);

	// fast decompress may not be supported
        if (npMCI->hicDecompress == NULL) {
            npMCI->hicDecompress = ICDecompressOpen(ICTYPE_VIDEO,
                        psi->sh.fccHandler,psi->lpFormat,NULL);
        }

	//
	// set any state data.
	//
        if (npMCI->hicDecompress && psi->cbData) {
            ICSetState(npMCI->hicDecompress, psi->lpData, psi->cbData);
        }

	if (psi->hicDraw == NULL && npMCI->hicDecompress == NULL &&
            psi->sh.fccHandler != comptypeRLE0 &&
            psi->sh.fccHandler != comptypeNONE &&
            psi->sh.fccHandler != comptypeDIB &&
            psi->sh.fccHandler != comptypeRLE &&
	    psi->sh.fccHandler != 0) {

            DPF(("Unable to open compressor '%4.4ls'!!!\n", (LPSTR) &psi->sh.fccHandler));

            npMCI->nVideoStream = -1;
            npMCI->psiVideo = NULL;

	    GlobalFreePtr(npMCI->pbiFormat);
            npMCI->pbiFormat = NULL;

            //
            // we would like to return a custom, error but MCI will not
            // find the error string because it has unloaded us (because
            // the open failed), so we return a bogus generic error.
            //
	    if (npMCI->streams == 1)	// this is the only stream
                npMCI->dwTaskError = MMSYSERR_NODRIVER; // MCIERR_AVI_NOCOMPRESSOR;

	    return FALSE;   // cant load this video stream
	}
    }
    else {
        //
        // this is not the default video stream find a draw handler that
        // can deal with the stream.
        //

        //
        // try VIDS.DRAW
        //
        // if that fails open a draw handler not-specific to the format
        //
        if (psi->hicDraw == NULL) {

            psi->hicDraw = ICOpen(psi->sh.fccType,FOURCC_AVIDraw,ICMODE_DRAW);

            if (psi->hicDraw)
                DOUT("Opened draw handler VIDS.DRAW\n");

            if (psi->hicDraw && ICDrawQuery(psi->hicDraw,psi->lpFormat) != ICERR_OK) {
                DOUT("Closing VIDS.DRAW because it cant handle this format");
                ICClose(psi->hicDraw);
                psi->hicDraw = NULL;
            }
        }

        //
        // if that fails open our internal handler.
        //
        if (psi->hicDraw == NULL) {

            psi->hicDraw = ICOpenFunction(psi->sh.fccType,
                FOURCC_AVIDraw,ICMODE_DRAW,(FARPROC)ICAVIDrawProc);

            if (psi->hicDraw)
                DOUT("Opened Internal draw handler\n");
        }
    }

    npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN;

    psi->dwFlags |= STREAM_VIDEO;       // is a video stream
    psi->dwFlags |= STREAM_ENABLED;
    npMCI->nVideoStreams++;

    return TRUE;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api BOOL | InitAudioStream | initialize a audio stream
 *
 ***************************************************************************/

BOOL NEAR PASCAL InitAudioStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
{
    int stream = psi - npMCI->paStreamInfo;
    LPWAVEFORMAT pwf;

    npMCI->wEarlyAudio = (UINT)psi->sh.dwInitialFrames;

    pwf = (LPWAVEFORMAT)psi->lpFormat;

    if (pwf->nChannels == 0 || pwf->nSamplesPerSec == 0) {
        return FALSE;
    }

    if (pwf->wFormatTag == WAVE_FORMAT_PCM) {
        pwf->nBlockAlign = pwf->nChannels *
            ((((LPPCMWAVEFORMAT)pwf)->wBitsPerSample + 7) / 8);

        pwf->nAvgBytesPerSec = pwf->nBlockAlign * pwf->nSamplesPerSec;
    }

    psi->sh.dwSampleSize = pwf->nBlockAlign;

    psi->dwFlags |= STREAM_AUDIO;       // audio stream
    psi->dwFlags |= STREAM_ENABLED;     // enabled by default.

    //
    //  make sure dwRate and dwScale are right
    //  dwRate/dwScale should be blocks/sec
    //
    Assert(muldiv32(pwf->nAvgBytesPerSec,1000,pwf->nBlockAlign) ==
           muldiv32(psi->sh.dwRate, 1000, psi->sh.dwScale));

    //
    //  just to be safe set these ourself to the right value.
    //
    psi->sh.dwRate  = pwf->nAvgBytesPerSec;
    psi->sh.dwScale = pwf->nBlockAlign;

    //
    // only one audio stream can be active at once
    // for lack of a better default the first audio stream will
    // become the active one.
    //
    if (npMCI->nAudioStreams == 0) {
        npMCI->nAudioStream = stream;
        npMCI->psiAudio = psi;
    }

    npMCI->nAudioStreams++;
    return TRUE;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api BOOL | InitOtherStream | initialize a random stream
 *
 ***************************************************************************/

BOOL NEAR PASCAL InitOtherStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
{
    int stream = psi - npMCI->paStreamInfo;

    /* Open the specified video compressor */
    psi->hicDraw = ICDrawOpen(psi->sh.fccType,psi->sh.fccHandler,psi->lpFormat);

    if (psi->hicDraw == NULL) {
        DPF(("Unable to play stream!\n"));
	return FALSE;
    }

    if (psi->cbData > 0) {
        ICSetState(psi->hicDraw, psi->lpData, psi->cbData);
    }
	
    psi->dwFlags |= STREAM_ENABLED;
////psi->dwFlags |= STREAM_OTHER;
    npMCI->nOtherStreams++;
    return TRUE;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api BOOL | CleanIndex | This function cleans up the index loaded by
 *  ReadIndex() it does the following when cleaning up the index:
 *
 *      converts all offsets to be absolute
 *
 *      converts "alpha" format index's into new format.
 *
 *      computes the max buffer size needed to read this file.
 *
 ***************************************************************************/

static BOOL NEAR CleanIndex(NPMCIGRAPHIC npMCI)
{
    LONG        lScan;
    AVIINDEXENTRY FAR * px;
    AVIINDEXENTRY FAR * pxRec=NULL;
    DWORD       lIndexAdjust;

    Assert(npMCI->hpIndex != NULL);

    px = (AVIINDEXENTRY FAR *)npMCI->hpIndex;

#ifdef ALPHAFILES
    if (npMCI->dwFlags & MCIAVI_USINGALPHAFORMAT)
	lIndexAdjust = 0;
    else
#endif
    if (// (avihdr.dwFlags & AVIF_MUSTUSEINDEX) ||
                (px->dwChunkOffset < 100))
	lIndexAdjust = npMCI->dwMovieListOffset;
    else
	lIndexAdjust = (npMCI->dwMovieListOffset + sizeof(DWORD)) -
                            px->dwChunkOffset;

//!!! only compute this for the video stream! (or interleaved...)
    npMCI->dwSuggestedBufferSize = 0; // lets get this exactly right

    DPF(("Adjusting index by %ld bytes....\n", lIndexAdjust));

    /* Can we do anything to see if the index is valid? */
    for (lScan = 0; lScan < (LONG)npMCI->macIndex;
                lScan++, ++((AVIINDEXENTRY _huge *)px)) {
        DWORD   ckid;

        //
        // adjust the offset to be absolute
        //
        px->dwChunkOffset += lIndexAdjust;

        // get ckid
        ckid = px->ckid;

        //
        // make sure the buffer size is right, ignore audio chunks because
        // they are either in a 'rec' or we will be reading them into
        // internal buffers not the main buffer.365
        //
        if (((npMCI->dwFlags & MCIAVI_NOTINTERLEAVED) ||
            ckid == listtypeAVIRECORD) &&
            TWOCCFromFOURCC(ckid) != cktypeWAVEbytes) {

            if (px->dwChunkLength + 8 > npMCI->dwSuggestedBufferSize)
                npMCI->dwSuggestedBufferSize = px->dwChunkLength + 12;
        }

#ifdef ALPHAFILES
        //
        // convert a "old" index to a new index
        //
        if (npMCI->dwFlags & MCIAVI_USINGALPHAFORMAT) {
            switch(ckid) {
                case ckidDIBbits:
                    px->dwFlags |= AVIIF_KEYFRAME;
                    px->ckid = MAKEAVICKID(cktypeDIBbits, 0);
                    break;

                case ckidDIBcompressed:
                    px->ckid = MAKEAVICKID(cktypeDIBcompressed, 0);
                    break;

                case ckidDIBhalfframe:
                    px->ckid = MAKEAVICKID(cktypeDIBhalf, 0);
                    break;

                case ckidPALchange:
                    px->ckid = MAKEAVICKID(cktypePALchange, 0);
                    break;

                case ckidWAVEbytes:
                    px->ckid = MAKEAVICKID(cktypeWAVEbytes, 1);
                    break;

                case ckidWAVEsilence:
                    px->ckid = MAKEAVICKID(cktypeWAVEsilence, 1);
                    break;

                case ckidAVIPADDING:
                case ckidOLDPADDING:
                    px->ckid = ckidAVIPADDING;
                    break;
            }

            ckid = px->ckid;
        }
#endif
	
        //
        // do special things with the video stream.
        //

        if (StreamFromFOURCC(ckid) == (UINT)npMCI->nVideoStream) {

            //
            // fix up bogus index's by adding any missing AVIIF_KEYFRAME
            // bits. ie this only applies for RLE files.
            //
            if (TWOCCFromFOURCC(ckid) == cktypeDIBbits &&
                VIDFMT(npMCI->nVideoStream)->biCompression <= BI_RLE8)

                px->dwFlags |= AVIIF_KEYFRAME;

            //
            // for video streams, make sure the palette changes are marked
            // as a no time chunk
            //
            if (TWOCCFromFOURCC(ckid) == cktypePALchange)
                px->dwFlags |= AVIIF_NOTIME/*|AVIIF_PALCHANGE*/;

            //
            //  make sure the 'REC ' list has the right flags.
            //
            if (pxRec) {
                if ((px->dwFlags & AVIIF_KEYFRAME) !=
                    (pxRec->dwFlags & AVIIF_KEYFRAME)) {

                    // Record list does not have correct flags

                    pxRec->dwFlags &= ~AVIIF_KEYFRAME;
                    pxRec->dwFlags |= (px->dwFlags & AVIIF_KEYFRAME);
                }
            }
        }

        if (ckid == listtypeAVIRECORD) {

            pxRec = px;

            if (npMCI->dwFlags & MCIAVI_NOTINTERLEAVED) {
                DPF(("Non interleaved file with a 'REC ' in it?\n"));
                npMCI->wEarlyRecords = max(npMCI->wEarlyVideo, npMCI->wEarlyAudio);

                if (npMCI->wEarlyRecords > 0) {
                    DPF(("Interlaved file with bad header\n"));
                    npMCI->dwFlags &= ~MCIAVI_NOTINTERLEAVED;
                }
            }
	}
    }

    return TRUE;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api BOOL | MakeFrameIndex | makes the frame index
 *
 *      the frame index is a array of AVIFRAMEINDEX entries, one for each
 *      frame in the movie.  using the frame index we can easily find
 *      a given frame, along with it's keyframe and palette.
 *
 ***************************************************************************/

static BOOL NEAR MakeFrameIndex(NPMCIGRAPHIC npMCI)
{
    LONG        nFrames;
    LONG        iFrameStart;
    LONG        iFrame;
    LONG        iKeyFrame;
    LONG        nKeyFrames;
    LONG        iScan;
    LONG        iNewIndex;
    LONG        iPalette;
    BOOL        fInterleaved;
    DWORD       ckid;
    STREAMINFO *psi;

    AVIINDEXENTRY _huge * pNewIndex;
    AVIINDEXENTRY _huge * pIndexEntry;
    AVIFRAMEINDEX _huge * pFrameIndex;

    if (npMCI->nVideoStreams == 0)
        return TRUE;

    if (npMCI->hpFrameIndex != NULL)
        return TRUE;

    psi = npMCI->psiVideo;
    Assert(psi != NULL);

    fInterleaved = !(npMCI->dwFlags & MCIAVI_NOTINTERLEAVED);

    if (fInterleaved &&
        muldiv32(npMCI->dwRate, 1000, npMCI->dwScale) !=
        muldiv32(psi->sh.dwRate, 1000, psi->sh.dwScale)) {
        //
        //  master video stream should match the movie rate!
        //
        AssertSz(FALSE, "Video stream differnet rate than movie");
        npMCI->dwRate  = psi->sh.dwRate;
        npMCI->dwScale = psi->sh.dwScale;
    }

    if (fInterleaved)
        iFrameStart = -(LONG)npMCI->wEarlyRecords;
    else
        iFrameStart = -(LONG)npMCI->wEarlyVideo;

    nFrames = npMCI->lFrames - iFrameStart;

    npMCI->hpFrameIndex = (LPVOID)GlobalAllocPtr(GMEM_SHARE|GHND,
        (DWORD)(nFrames+1) * sizeof(AVIFRAMEINDEX));

    if (npMCI->hpFrameIndex == NULL) {
	DPF(("Couldn't allocate memory for frame index!\n"));
        return FALSE;
    }

    //
    //  do this so we can just index the array with the frame number
    //  (positive or neg)
    //
    npMCI->hpFrameIndex += (-iFrameStart);

    pFrameIndex = npMCI->hpFrameIndex;

    iFrame    = iFrameStart;
    iKeyFrame = -(LONG)npMCI->wEarlyVideo; // iFrameStart;
    iNewIndex = 0;
    iPalette  = -1; // first palette
    nKeyFrames= 0;

#ifdef USEAVIFILE
    if (npMCI->pf) {
        PAVISTREAM ps = SI(npMCI->nVideoStream)->ps;

        for (iFrame = 0; iFrame < npMCI->lFrames; iFrame++) {

            LONG iKey;

            iKey      = AVIStreamFindSample(ps,iFrame,FIND_PREV|FIND_KEY);
            iPalette  = AVIStreamFindSample(ps,iFrame,FIND_PREV|FIND_FORMAT);

            if (iKey != -1)
                iKeyFrame = iKey;

            if (iPalette == -1)
                iPalette = 0;

            pFrameIndex[iFrame].iPrevKey = (UINT)(iFrame - iKeyFrame);
            pFrameIndex[iFrame].iNextKey = 0;
            pFrameIndex[iFrame].iPalette = (WORD)iPalette;
	    pFrameIndex[iFrame].dwOffset = 0;
            pFrameIndex[iFrame].dwLength = 0;

            Assert(iPalette <= 0xFFFF);

            if (iFrame - iKeyFrame > 0xFFFF) {
                //!!! we need to set a flag!
                //!!! we need to throw out the index!
                AssertSz(FALSE, "File has too few key frames");
                pFrameIndex[iFrame].iPrevKey = 0;
            }
        }

	goto ack;
    }
#endif

    Assert(npMCI->hpIndex != NULL);
    Assert(npMCI->macIndex != 0L);
    pNewIndex   = npMCI->hpIndex;
    pIndexEntry = npMCI->hpIndex;

    for (iScan = 0; iScan < (LONG)npMCI->macIndex; iScan++, pIndexEntry++) {

        ckid = pIndexEntry->ckid;

        //
        // check for palette changes.
        //
        if (StreamFromFOURCC(ckid) == (UINT)npMCI->nVideoStream &&
            TWOCCFromFOURCC(ckid) == cktypePALchange) {

            iPalette = iNewIndex;

            pNewIndex[iNewIndex++] = *pIndexEntry;

            if (fInterleaved)
                pFrameIndex[iFrame-1].iPalette = (WORD)iPalette;
        }

        //
        // remove the video stream from the master index
        //
        if ((ckid != listtypeAVIRECORD) &&
            (StreamFromFOURCC(ckid) != (UINT)npMCI->nVideoStream)) {
            pNewIndex[iNewIndex++] = *pIndexEntry;
        }

        //
        //  in interleaved files a "frame" happens every list record
        //
        //  in non-interleaved files a "frame" happens every piece of
        //  data in the video stream (except no time chunks)
        //
        if (fInterleaved) {

            if (ckid != listtypeAVIRECORD)
                continue;

        } else {

            if ((StreamFromFOURCC(ckid) != (UINT)npMCI->nVideoStream) ||
                (pIndexEntry->dwFlags & AVIIF_NOTIME))

                continue;
        }

        AssertSz(iFrame < npMCI->lFrames,"Too many frames in index!");

        if (iFrame >= npMCI->lFrames) {
	    break;
        }

        if (pIndexEntry->dwFlags & AVIIF_KEYFRAME) {
            iKeyFrame = iFrame;
            nKeyFrames++;
        }

        pFrameIndex[iFrame].iPrevKey = (UINT)(iFrame - iKeyFrame);
        pFrameIndex[iFrame].iNextKey = 0;
        pFrameIndex[iFrame].iPalette = (WORD)iPalette;
        pFrameIndex[iFrame].dwOffset = pIndexEntry->dwChunkOffset;
        pFrameIndex[iFrame].dwLength = pIndexEntry->dwChunkLength;

        if (fInterleaved)
            pFrameIndex[iFrame].dwOffset += 3 * sizeof(DWORD);

        Assert(iPalette <= 0xFFFF);

        if (iFrame - iKeyFrame > 0xFFFF) {
            //!!! we need to set a flag!
            //!!! we need to throw out the index!
            AssertSz(FALSE, "File has too few key frames");
            pFrameIndex[iFrame].iPrevKey = 0;
        }

        iFrame++;
    }
ack:
    //
    //  iFrame better equal npMCI->lFrames
    //
    Assert(iFrame == npMCI->lFrames);

    if (iFrame < npMCI->lFrames)
        npMCI->lFrames = iFrame;

    //
    // make a "dummy" last frame
    //
    pFrameIndex[iFrame].iPrevKey = (UINT)(iFrame - iKeyFrame);
    pFrameIndex[iFrame].iNextKey = 0;
    pFrameIndex[iFrame].iPalette = (WORD)iPalette;
    pFrameIndex[iFrame].dwOffset = 0;
    pFrameIndex[iFrame].dwLength = 0;

    //
    // compute the key frames every value
    //
    if (nKeyFrames) {

        if (nKeyFrames > 1)
            npMCI->dwKeyFrameInfo = (DWORD)((nFrames + nKeyFrames/2)/nKeyFrames);
        else
            npMCI->dwKeyFrameInfo = 0;
    }

    //
    //  now go through the index, and fix all the iNextKey fields
    //
    pFrameIndex = npMCI->hpFrameIndex;
////iKeyFrame = npMCI->lFrames; //!!! what should this be set to? zero?

    for (iFrame = npMCI->lFrames; iFrame>=iFrameStart; iFrame--)
    {
        if (pFrameIndex[iFrame].iPrevKey == 0)
            iKeyFrame = iFrame;

        if (iKeyFrame >= iFrame)
            pFrameIndex[iFrame].iNextKey = (UINT)(iKeyFrame - iFrame);
        else
            pFrameIndex[iFrame].iNextKey = 0xFFFF;      // way far away

        if (iKeyFrame - iFrame > 0xFFFF) {
            //!!! we need to set a flag!
            //!!! we need to throw out the index!
            AssertSz(FALSE, "File has too few key frames");
            pFrameIndex[iFrame].iNextKey = 0;
        }
    }

    //
    // we dont need the index, if we are using AVIFile or
    // we have a interleaved file.  when the file is interleaved
    // we never do random access (except for palette changes)
    //
    // !!!this is not true, we need the index iff we have a audio only
    // file or we play a interleaved file real slow.
    //
    if (npMCI->pf /* ||
        (fInterleaved && !(npMCI->dwFlags & MCIAVI_ANIMATEPALETTE))*/ ) {
        DOUT("The Master index must go!\n");
        iNewIndex = 0;
    }

    //
    // now re-alloc the master index down to size.
    //
    // !!! do we even need the master index anymore, for interleaved files?
    //
    DPF(("Master index was %ld entries now %ld\n",npMCI->macIndex, iNewIndex));

    npMCI->macIndex = iNewIndex;

    if (iNewIndex > 0) {
        npMCI->hpIndex = (AVIINDEXENTRY _huge *)
		GlobalReAllocPtr(npMCI->hpIndex,
				 (LONG)iNewIndex * sizeof(AVIINDEXENTRY),
				 GMEM_MOVEABLE | GMEM_SHARE);

        Assert(npMCI->hpIndex != NULL);
    }
    else {
        if (npMCI->hpIndex)
            GlobalFreePtr(npMCI->hpIndex);
        npMCI->hpIndex = NULL;
    }

    return TRUE;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api BOOL | ReadIndex | Read the index into npMCI->hpIndex.  Should
 *	only be called if the HASINDEX flag is set.
 *
 * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data
 *
 * @rdesc TRUE means no errors, false means unable to read index.
 *
 ***************************************************************************/
BOOL FAR PASCAL ReadIndex(NPMCIGRAPHIC npMCI)
{
    MMCKINFO    ck;
    DWORD       dwOldPos;

    if (npMCI->hpIndex || npMCI->hpFrameIndex)
	return TRUE;

    if (!(npMCI->dwFlags & MCIAVI_HASINDEX))
	return FALSE;

    if (npMCI->pf) {
	MakeFrameIndex(npMCI);
	return TRUE;
    }

#if 0
    if (GetCurrentTask() != npMCI->hTask) {

	/* this function is called (from GraphicStatus) when
	 * possibly playing - so we have to suspend play while we read
	 * the index.
	 */
	TEMPORARYSTATE  ts;

	if (StopTemporarily(npMCI, &ts) == 0) {
            mciaviTaskMessage(npMCI, TASKREADINDEX);
	    RestartAgain(npMCI, &ts);
            return (npMCI->hpIndex != NULL);
        }
	return(FALSE);
    }
#else
    if (GetCurrentTask() != npMCI->hTask)
        return FALSE;
#endif

    dwOldPos = mmioSeek(npMCI->hmmio, 0, SEEK_CUR);

    DPF(("Reading index: starting from %lx\n", npMCI->dwBigListEnd));

    if (mmioSeek(npMCI->hmmio, npMCI->dwBigListEnd, SEEK_SET) == -1) {
IndexReadError:		
	DPF(("Error reading index!\n"));
        npMCI->dwFlags &= ~(MCIAVI_HASINDEX);
	mmioSeek(npMCI->hmmio, dwOldPos, SEEK_SET);
	return FALSE;
    }

    ck.ckid = ckidAVINEWINDEX;	
    if (mmioDescend(npMCI->hmmio, &ck, NULL, MMIO_FINDCHUNK) != 0) {
	goto IndexReadError;
    }

    /* A zero-size index isn't much good. */
    if (ck.cksize == 0)
	goto IndexReadError;

    npMCI->macIndex = ck.cksize / sizeof(AVIINDEXENTRY);
    npMCI->hpIndex = (AVIINDEXENTRY _huge *)
		     GlobalAllocPtr(GMEM_SHARE | GMEM_MOVEABLE, ck.cksize);

    if (!npMCI->hpIndex) {
	DPF(("Insufficient memory to read index.\n"));
	goto IndexReadError;
    }

#ifndef WIN32
    Assert(OFFSETOF(npMCI->hpIndex) == 0);
#endif

    if (mmioRead(npMCI->hmmio, (HPSTR) npMCI->hpIndex, ck.cksize) != (LONG) ck.cksize) {
	Assert(0);
	goto IndexReadError;
    }

    CleanIndex(npMCI);
    MakeFrameIndex(npMCI);

////should we do this for audio? remove video data?
////MakeStreamIndex(npMCI, ???);

    mmioSeek(npMCI->hmmio, dwOldPos, SEEK_SET);
    return TRUE;
}

/***************************************************************************
 * @doc INTERNAL MCIAVI
 *
 * @api BOOL | IsRectBogus | 'nuf said
 *
 ***************************************************************************/

static BOOL NEAR PASCAL IsRectBogus(LPRECT prc)
{
    if (prc->right  - prc->left <= 0 ||
        prc->bottom - prc->top <= 0 ||
        prc->bottom <= 0 ||
        prc->right <= 0)

        return TRUE;
    else
        return FALSE;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api LONG | atol | local version of atol
 *
 ***************************************************************************/

static LONG NEAR PASCAL atol(char *sz)
{
    LONG l = 0;

    while (*sz && *sz >= '0' && *sz <= '9')
    	l = l*10 + *sz++ - '0';
    	
    return l;    	
}	

#ifndef WIN32

/*--------------------------------------------------------------------------
 *
 *  IsCDROMDrive() -
 *
 * Purpose:  Return non-zero if a RAM drive
 *
 *  wDrive   drive index (0=A, 1=B, ...)
 *
 *  return   TRUE/FALSE
 *-------------------------------------------------------------------------*/

#pragma optimize("", off)
static BOOL NEAR PASCAL IsCDROMDrive(UINT wDrive)
{
    BOOL f;

    _asm {
        mov ax, 1500h     /* first test for presence of MSCDEX */
        xor bx, bx
        int 2fh
        mov ax, bx        /* MSCDEX is not there if bx is still zero */
        or  ax, ax        /* ...so return FALSE from this function */
        jz  no_mscdex
        mov ax, 150bh     /* MSCDEX driver check API */
        mov cx, wDrive    /* ...cx is drive index */
        int 2fh
no_mscdex:
	mov f,ax
    }
    return f;
}
#pragma optimize("", on)

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api BOOL | IsNetFile | is the passed file on a network drive?
 *
 ***************************************************************************/

static BOOL NEAR PASCAL IsNetFile(LPTSTR szFile)
{
    OFSTRUCT            of;

    if (OpenFile(szFile, &of, OF_PARSE) == -1)
        return FALSE;

    AnsiUpper(of.szPathName);

    if (of.szPathName[0] == '\\' && of.szPathName[1] == '\\')
        return TRUE;

    if (of.szPathName[0] == '/' && of.szPathName[1] == '/')
        return TRUE;
    if (of.szPathName[1] == ':' &&
        GetDriveType(of.szPathName[0] - 'A') == DRIVE_REMOTE &&
        !IsCDROMDrive(of.szPathName[0] - 'A'))

        return TRUE;

    return FALSE;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api BOOL | IsCDROMFile | is the passed file on a CD-ROM drive?
 *
 ***************************************************************************/

static BOOL NEAR PASCAL IsCDROMFile(LPTSTR szFile)
{
    OFSTRUCT of;

    if (OpenFile(szFile, &of, OF_PARSE) == -1)
        return FALSE;

    AnsiUpper(of.szPathName);

    if (of.szPathName[0] == '\\' && of.szPathName[1] == '\\')
        return FALSE;

    if (of.szPathName[0] == '/' && of.szPathName[1] == '/')
        return FALSE;

    if (of.szPathName[1] == ':' &&
        GetDriveType(of.szPathName[0] - 'A') == DRIVE_REMOTE &&
        IsCDROMDrive(of.szPathName[0] - 'A'))

        return TRUE;

    return FALSE;
}

/***************************************************************************
 *
 * @doc INTERNAL MCIAVI
 *
 * @api UINT | GetFileDriveType | return drive type given a file
 *
 *      DRIVE_CDROM
 *      DRIVE_REMOTE
 *      DRIVE_FIXED
 *      DRIVE_REMOVABLE
 *
 ***************************************************************************/

static UINT NEAR PASCAL GetFileDriveType(LPSTR szPath)
{
    if (IsCDROMFile(szPath))
        return DRIVE_CDROM;

    if (IsNetFile(szPath))
        return DRIVE_REMOTE;

    if (szPath[1] == ':')
        return GetDriveType(szPath[0] - 'A');

    return 0;
}

#endif
