/****************************************************************************** Copyright (C) Microsoft Corporation 1985-1991. All rights reserved. Title: aviopen.c - open a AVI file *****************************************************************************/ #include "graphic.h" #ifdef _WIN32 #include #endif #ifdef USEAVIFILE #include #include 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 force using AVIFile to try and open this file. // #define AVIERR_NOT_AVIFILE 4242 // // if this is defined we will always use AVIFILE.DLL, except for // 1:1 interleaved files. // #ifdef USEAVIFILE #define USE_AVIFILE_FOR_NON_INT #endif #ifdef _WIN32 BOOL runningInWow = 0; // Assume we are not in WOW #endif /*************************************************************************** * Local function declarations ***************************************************************************/ STATICFN BOOL NEAR PASCAL InitStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi); STATICFN BOOL NEAR PASCAL InitVideoStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi); STATICFN BOOL NEAR PASCAL InitAudioStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi); STATICFN BOOL NEAR PASCAL InitOtherStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi); STATICFN void NEAR PASCAL CloseStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi); STATICFN BOOL NEAR PASCAL ParseNewHeader(NPMCIGRAPHIC npMCI); STATICFN BOOL NEAR PASCAL OpenRiffAVIFile(NPMCIGRAPHIC npMCI, BOOL bForce); STATICFN BOOL NEAR PASCAL OpenWithAVIFile(NPMCIGRAPHIC npMCI); STATICFN BOOL NEAR PASCAL OpenInterface(NPMCIGRAPHIC npMCI); #ifdef USEAVIFILE STATICFN BOOL NEAR PASCAL OpenAVIFile(NPMCIGRAPHIC npMCI, IAVIFile FAR *pf); STATICFN BOOL NEAR PASCAL OpenAVIStream(NPMCIGRAPHIC npMCI, int stream, IAVIStream FAR *pf); #endif INLINE static BOOL NEAR PASCAL IsRectBogus(RECT *prc); #ifndef _WIN32 static LONG NEAR PASCAL atol(char *sz); #endif // !_WIN32 #ifdef _WIN32XXXX #define GetFileDriveType GetDriveType #else static UINT NEAR PASCAL GetFileDriveType(LPTSTR szPath); #endif #ifndef _WIN32 SZCODE szOLENLSDLL[] = "OLE2NLS.DLL"; SZCODE szOLENLSAPI[] = "GetUserDefaultLangID"; #endif // // due to OLE, all ole calls must be single-threaded and thus on // the app thread. so we moved the Open call to the app thread // // -- called on app thread ------------------------------------------------- // /*************************************************************************** * * @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! // DPF(("mciaviOpenFile, name = >>%ls<<\n", npMCI->szFilename)); 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] == TEXT('@')) { npMCI->uDriveType = DRIVE_INTERFACE; if (IsNTWOW()) { DPF2(("Rejecting open from WOW because of an interface pointer\n")); npMCI->dwTaskError=MCIERR_UNSUPPORTED_FUNCTION; return(FALSE); // Cannot support a 16 bit interface pointer from 32 bit code } } 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] == TEXT('@') && 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, FALSE)) { #ifdef USEAVIFILE // // 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) { npMCI->dwTaskError = 0; if (OpenWithAVIFile(npMCI)) goto DoneOpening; // if this is a riff file that we could open but chose // to pass to avifile, and avifile is not present, then we need // to pass it back to OpenRiffAVIFile and force it to try // and open it properly. npMCI->dwTaskError = 0; if (OpenRiffAVIFile(npMCI, TRUE)) goto DoneOpening; if (npMCI->dwTaskError == AVIERR_NOT_AVIFILE) { npMCI->dwTaskError = MCIERR_INVALID_FILE; // We cannot return an AVI specific error as there is // no way for the UI to get access to the error text. // Hence we use a generic mci error. } } #endif goto error; } DoneOpening: npMCI->dwTaskError = 0; return TRUE; error: mciaviCloseFile(npMCI); if (npMCI->dwTaskError == 0) npMCI->dwTaskError = MCIERR_INVALID_FILE; return FALSE; } #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 * ***************************************************************************/ STATICFN 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 * ***************************************************************************/ STATICFN 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] != TEXT('@')) { DPF1(("Failing to open interface for file %ls\n",npMCI->szFilename)); return FALSE; } #ifdef UNICODE p = (IUnknown FAR *)wcstol(npMCI->szFilename+1, NULL, 10); #else p = (IUnknown FAR *)atol(npMCI->szFilename+1); #endif #ifdef USE_ISVALIDINTERFACE if (!IsValidInterface(p)) return FALSE; #endif #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); pf = NULL; // AVIMakeFileFromStreams need not set pf to NULL } 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. * neither do we Release(). If we fail, our caller will Release(), * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data. * * @rdesc TRUE means OK, otherwise mci error in dwTaskError * On a successful return npMCI->pf will be loaded with the * input IAVIFILE interface pointer. * ***************************************************************************/ STATICFN BOOL NEAR PASCAL OpenAVIFile(NPMCIGRAPHIC npMCI, IAVIFile FAR *pf) { AVIFILEINFOW info; HRESULT hr; IAVIStream FAR *ps; STREAMINFO *psi; int i; Assert(npMCI->pf == NULL); _fmemset(&info, 0, sizeof(info)); hr = pf->lpVtbl->Info(pf, &info, sizeof(info)); if (FAILED(GetScode(hr))) { npMCI->dwTaskError = MCIERR_INVALID_FILE; return FALSE; } DPF1(("OpenAVIFile: %ls\n\t#Streams=%d, Width=%d Height=%d\n", info.szFileType, info.dwStreams, info.dwWidth, info.dwHeight)); // // get rid of bad files // if (info.dwStreams == 0 || info.dwStreams > 255 || info.dwLength == 0) { npMCI->dwTaskError = MCIERR_INVALID_FILE; return FALSE; } // // save the interface pointer // 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; ps->lpVtbl->Release(ps); 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; int nFrames = (int) min(npMCI->lFrames, NFRAMES); for (l=0; lps, 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 * ***************************************************************************/ STATICFN BOOL NEAR PASCAL OpenAVIStream(NPMCIGRAPHIC npMCI, int stream, IAVIStream FAR *ps) { STREAMINFO* psi; AVISTREAMINFOW info; HRESULT hr; _fmemset(&info, 0, sizeof(info)); hr = ps->lpVtbl->Info(ps, &info, sizeof(info)); if (FAILED(GetScode(hr))) { npMCI->dwTaskError = MCIERR_INVALID_FILE; return FALSE; } DPF(("OpenAVIStream(%d) %4.4hs:%4.4hs %ls\n", stream, (LPSTR)&info.fccType, (LPSTR)&info.fccHandler, 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; // // 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 SZCODE szAVIFILE[] = TEXT("AVIFIL32.DLL"); #ifdef USE_ISVALIDINTERFACE SZCODE szCOMPOBJ[] = TEXT("COMPOB32"); #endif #else SZCODE szAVIFILE[] = "AVIFILE.DLL"; #ifdef USE_ISVALIDINTERFACE SZCODE szCOMPOBJ[] = "COMPOBJ"; #endif #endif // On NT the entry points will NOT be unicode strings, as there is // no unicode version of GetProcAddress. BUT SZCODE generate UNICODE... // on the other hand, the entry points will all be unicode entrypoints 'W' #ifdef UNICODE #define UNICODE_APPEND "W" #else #define UNICODE_APPEND #endif SZCODEA szAVIFileInit[] = "AVIFileInit"; SZCODEA szAVIFileExit[] = "AVIFileExit"; #ifdef USE_ISVALIDINTERFACE SZCODEA szIsValidInterface[] = "IsValidInterface"; #endif SZCODEA szAVIMakeFileFromStreams[] = "AVIMakeFileFromStreams"; SZCODEA szAVIStreamBeginStreaming[] = "AVIStreamBeginStreaming"; SZCODEA szAVIStreamEndStreaming[] = "AVIStreamEndStreaming"; SZCODEA szAVIFileOpen[] = "AVIFileOpen" UNICODE_APPEND; BOOL FAR InitAVIFile(NPMCIGRAPHIC npMCI) { UINT u; if (hdllAVIFILE == (HMODULE)-1) // We have already tried to load AVIFILE and failed return FALSE; if (hdllAVIFILE == NULL) { u = SetErrorMode(SEM_NOOPENFILEERRORBOX); #ifdef _WIN32 // The load and free of szAVIFILE is safe as it is protected by // the npMCI device critical section. This means that open/close // events will be synchronised. #endif hdllAVIFILE = LoadLibrary(szAVIFILE); SetErrorMode(u); #ifndef _WIN32 if ((UINT)hdllAVIFILE <= (UINT)HINSTANCE_ERROR) hdllAVIFILE = NULL; #endif if (hdllAVIFILE == NULL) { hdllAVIFILE = (HMODULE)-1; DPF(("Failed to load AVIFILE dll - error %d\n", GetLastError())); return FALSE; } #ifdef USE_ISVALIDINTERFACE hdllCOMPOBJ = GetModuleHandle(szCOMPOBJ); Assert(hdllCOMPOBJ != NULL); (FARPROC)XIsValidInterface = GetProcAddress(hdllCOMPOBJ, szIsValidInterface); Assert(XIsValidInterface != NULL); #endif (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(); // Should we check the return code? uAVIFILE++; } return TRUE; } // must be called from app thread too! /*************************************************************************** * * @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 let the DLLs go. Assert(uAVIFILE > 0); uAVIFILE--; if (uAVIFILE == 0) { FreeLibrary(hdllAVIFILE); hdllAVIFILE = NULL; #ifdef USE_ISVALIDINTERFACE hdllCOMPOBJ = NULL; #endif } return TRUE; } #endif /* USEAVIFILE */ /*************************************************************************** * * @doc INTERNAL MCIAVI * * @api BOOL | OpenRiffAVIFile | Open an RIFF AVI file * * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data. * * @parm BOOL | bForce | if FALSE, return error for files that * we think should be opened by AVIFILE instead. * * @rdesc TRUE means OK, otherwise mci error in dwTaskError * ***************************************************************************/ STATICFN BOOL NEAR PASCAL OpenRiffAVIFile(NPMCIGRAPHIC npMCI, BOOL bForce) { 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) { 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) { goto ERROR_BADFILE; } } /* Make sure it's a RIFF file */ if (ckRIFF.ckid != FOURCC_RIFF) { npMCI->dwTaskError = MCIERR_INVALID_FILE; goto ERROR_NOT_AVIFILE; } /* Otherwise, it should be an AVI file */ if (ckRIFF.fccType != formtypeAVI) { npMCI->dwTaskError = MCIERR_INVALID_FILE; goto ERROR_NOT_AVIFILE; } /* ** Descend into header LIST */ ckLIST.fccType = listtypeAVIHEADER; if (mmioDescend(hmmio, &ckLIST, &ckRIFF, MMIO_FINDLIST) != 0) { 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_RETURN; } #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. // // if there is a problem with avifile (eg dll not present) we will // be called with bForce TRUE: this means we should open it if at // all possible // if (!bForce) { 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_NOT_AVIFILE; } // // 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_RETURN; } else { goto ERROR_BADFILE; } /* Ascend out of header LIST */ if (mmioAscend(hmmio, &ckLIST, 0) != 0) { npMCI->dwTaskError = MCIERR_FILE_READ; goto ERROR_RETURN; } /* 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) { 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) { 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_RETURN; } #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_NOT_AVIFILE: #ifdef USEAVIFILE npMCI->dwTaskError = AVIERR_NOT_AVIFILE; // mark as not a AVI file goto ERROR_RETURN; // we will be called a second time #endif ERROR_BADFILE: npMCI->dwTaskError = MCIERR_INVALID_FILE; // something wrong with the file ERROR_RETURN: fRet = FALSE; goto exit; } /*************************************************************************** * @doc INTERNAL MCIAVI * * @api BOOL | ParseNewHeader | 'nuf said * ***************************************************************************/ STATICFN 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; } if (npMCI->dwTaskError) { return FALSE; } return TRUE; FileError: npMCI->dwTaskError = MCIERR_INVALID_FILE; return FALSE; DontHandleThisFile: npMCI->dwTaskError = AVIERR_NOT_AVIFILE; return FALSE; } // // --- called on worker thread --------------------------------------------- // /*************************************************************************** * * @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; // No audio streams means silence if (IsNTWOW()) { if (!npMCI->pbiFormat) { // Although there are valid video streams we do not appear to have // access to a decompressor that knows what to do with them. Hence // if we are in NT/WOW we fail the open, the call will return to // 16 bit land and there might be a codec there that can cope return(FALSE); } } 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); wLang = 0; if ((UINT)hdll > (UINT)HINSTANCE_ERROR) { if ((FARPROC) GetUserDefaultLangID = GetProcAddress(hdll, szOLENLSAPI)) { wLang = GetUserDefaultLangID(); } FreeLibrary(hdll); } #else wLang = GetUserDefaultLangID(); #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; istreams; 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; istreams; 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; } } // // If this is WOW we had better check that there is an audio codec // available. If not, we let the 16 bit side open the video. // if (IsNTWOW()) { if (SetUpAudio(npMCI, FALSE)) return FALSE; } // // fix up the movie rect // if (IsRectEmpty(&npMCI->rcMovie)) { DPF2(("Movie rect is empty\n")); SetRectEmpty(&rc); for (i=0; istreams; i++) { UnionRect(&rc,&rc,&SH(i).rcFrame); } npMCI->rcMovie = rc; DPF2(("Movie rect was empty, now [%d, %d, %d, %d]\n", rc)); } else { rc = npMCI->rcMovie; } // check we can access the interface ptr - the main reason for this // is that if we have pasted this interface ptr, we cannot access it on // the worker thread (which is where we are now). Clean up the error handling // by actually detecting this case. if (npMCI->pf) { IAVIStream * ps; long l; if ((l = AVIFileGetStream(npMCI->pf, &ps, 0, 0)) != 0) { DPF(("avifile err %d", l)); npMCI->dwTaskError = l; return FALSE; } else { if (ps->lpVtbl->FindSample(ps, 0, FIND_FROM_START|FIND_NEXT|FIND_ANY) < 0) { npMCI->dwTaskError = MCIERR_DEVICE_NOT_READY; ps->lpVtbl->Release(ps); return FALSE; } ps->lpVtbl->Release(ps); } } // // 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() ... */ if (TryPutRect(npMCI, MCI_DGV_PUT_SOURCE, &rc, &npMCI->dwTaskError)) { return FALSE; } /* * 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); if (TryPutRect(npMCI, MCI_DGV_PUT_DESTINATION, &rc, &npMCI->dwTaskError)) { return FALSE; } if (npMCI->dwTaskError) { return FALSE; } // // size the window and things. // SetWindowToDefaultSize(npMCI, FALSE); DrawBegin(npMCI, NULL); return TRUE; } /*************************************************************************** * * @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; 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) { GlobalUnlock(npMCI->hgFrameIndex); GlobalFree(npMCI->hgFrameIndex); npMCI->hpFrameIndex = NULL; npMCI->hgFrameIndex = 0; } 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. * ***************************************************************************/ STATICFN 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 * ***************************************************************************/ STATICFN 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 * ***************************************************************************/ STATICFN BOOL NEAR PASCAL InitVideoStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi) { LPBITMAPINFOHEADER lpbi; int stream = (int) (INT_PTR) (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)) { DPF2(("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.4hs:%4.4hs (Stream %d)\n", (LPSTR)&psi->sh.fccType,(LPSTR)&psi->sh.fccHandler, stream)); else DPF(("Failed to open draw handler for %4.4hs:%4.4hs (Stream %d)\n", (LPSTR)&psi->sh.fccType,(LPSTR)&psi->sh.fccHandler, stream)); } // // one video stream is the master, he controls the palette etc // for lack of a better default the first valid (i.e. one we can // handle) 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.4hs'!!!\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 by the time the // user (application) comes to retrieve the error text MCI will // have unloaded us (because the open failed) and therefore // will not be able to get our specific error text. // Hence we return a bogus generic error. // #ifdef _WIN32 SetLastError(MCIERR_AVI_NOCOMPRESSOR); #endif 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 * ***************************************************************************/ STATICFN BOOL NEAR PASCAL InitAudioStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi) { int stream = (int) (INT_PTR) (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 * ***************************************************************************/ STATICFN BOOL NEAR PASCAL InitOtherStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi) { int stream = (int) (INT_PTR) (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. * ***************************************************************************/ INLINE 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; // hpFrameIndex is modified to point partway into the block. // therefore we need to remember the handle of the block so we // can free it correctly (esp on NT) if ( (npMCI->hgFrameIndex = GlobalAlloc(GMEM_SHARE|GHND, (DWORD)(nFrames+1) * sizeof(AVIFRAMEINDEX))) && (npMCI->hpFrameIndex = GlobalLock(npMCI->hgFrameIndex))) { } else { // We failed to allocate, or we failed to lock down. Clean up // and return an error. if (npMCI->hgFrameIndex) { GlobalFree(npMCI->hgFrameIndex); npMCI->hgFrameIndex = 0; } 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. */ // won't work with the current multi-thread design // 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 * ***************************************************************************/ INLINE static BOOL NEAR PASCAL IsRectBogus(RECT* prc) { if (prc->right - prc->left <= 0 || prc->bottom - prc->top <= 0 || prc->bottom <= 0 || prc->right <= 0) return TRUE; else return FALSE; } #ifndef _WIN32 // _WIN32 has one! /*************************************************************************** * * @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; } #endif #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; } #else // _WIN32 /* * We cannot pass the call directly to GetDriveType as we have a full * pathname - and that gets rejected by GetFileDriveType. Using a * static variable is ok - the open call is serialised. The assumption * is that this routine is ONLY called with complete path information. */ TCHAR DriveType[4] = TEXT(".:\\"); static UINT NEAR PASCAL GetFileDriveType(LPTSTR szPath) { Assert(szPath != NULL); if (*szPath == TEXT('\\')) { Assert(szPath[1] == TEXT('\\')); return(DRIVE_REMOTE); } if (szPath[1] == TEXT(':')) { DriveType[0] = szPath[0]; return GetDriveType(DriveType); } return 0; } #endif