/* * Copyright 1999 Marcus Meissner * Copyright 2001 Hidenori TAKESHIMA * * FIXME - implements editing/writing. */ #include #include #include #include "winbase.h" #include "winnls.h" #include "mmsystem.h" #include "winerror.h" #include "vfw.h" #include "debugtools.h" #include "avifile_private.h" DEFAULT_DEBUG_CHANNEL(avifile); #define AVIFILE_STREAMS_MAX 4 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile* iface,REFIID refiid,LPVOID *obj); static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile* iface); static ULONG WINAPI IAVIFile_fnRelease(IAVIFile* iface); static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile*iface,AVIFILEINFOW*afi,LONG size); static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile*iface,PAVISTREAM*avis,DWORD fccType,LONG lParam); static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile*iface,PAVISTREAM*avis,AVISTREAMINFOW*asi); static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG size); static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG *size); static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile*iface); static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile*iface,DWORD fccType,LONG lParam); struct ICOM_VTABLE(IAVIFile) iavift = { ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE IAVIFile_fnQueryInterface, IAVIFile_fnAddRef, IAVIFile_fnRelease, IAVIFile_fnInfo, IAVIFile_fnGetStream, IAVIFile_fnCreateStream, IAVIFile_fnWriteData, IAVIFile_fnReadData, IAVIFile_fnEndRecord, IAVIFile_fnDeleteStream, /* IAVIFILE_fnOpen */ /* FIXME? */ }; typedef struct IAVIFileImpl { ICOM_VFIELD(IAVIFile); /* IUnknown stuff */ DWORD ref; /* IAVIFile stuff */ HANDLE hf; DWORD dwAVIFileCaps; DWORD dwAVIFileScale; DWORD dwAVIFileRate; DWORD dwAVIFileLength; DWORD dwAVIFileEditCount; MainAVIHeader hdr; IAVIStream* pStreams[AVIFILE_STREAMS_MAX]; AVIStreamHeader strhdrs[AVIFILE_STREAMS_MAX]; DWORD dwMoviTop; DWORD dwCountOfIndexEntry; AVIINDEXENTRY* pIndexEntry; AVIINDEXENTRY* pStreamIndexEntry[AVIFILE_STREAMS_MAX+1]; } IAVIFileImpl; /**************************************************************************** * AVI file parser. */ static HRESULT AVIFILE_IAVIFile_ReadNextChunkHeader( IAVIFileImpl* This, FOURCC* pfcc, DWORD* pdwSize ) { BYTE buf[8]; DWORD dwRead; if ( ( !ReadFile( This->hf, buf, 8, &dwRead, NULL ) ) || ( 8 != dwRead ) ) return AVIERR_FILEREAD; *pfcc = mmioFOURCC(buf[0],buf[1],buf[2],buf[3]); *pdwSize = ( ((DWORD)buf[4]) ) | ( ((DWORD)buf[5]) << 8 ) | ( ((DWORD)buf[6]) << 16 ) | ( ((DWORD)buf[7]) << 24 ); return S_OK; } static HRESULT AVIFILE_IAVIFile_SkipChunkData( IAVIFileImpl* This, DWORD dwChunkSize ) { LONG lHigh = 0; DWORD dwRes; if ( dwChunkSize == 0 ) return S_OK; SetLastError(NO_ERROR); dwRes = SetFilePointer( This->hf, (LONG)dwChunkSize, &lHigh, FILE_CURRENT ); if ( dwRes == (DWORD)0xffffffff && GetLastError() != NO_ERROR ) return AVIERR_FILEREAD; return S_OK; } static HRESULT AVIFILE_IAVIFile_ReadChunkData( IAVIFileImpl* This, DWORD dwChunkSize, LPVOID lpvBuf, DWORD dwBufSize, LPDWORD lpdwRead ) { if ( dwBufSize > dwChunkSize ) dwBufSize = dwChunkSize; if ( ( !ReadFile( This->hf, lpvBuf, dwBufSize, lpdwRead, NULL ) ) || ( dwBufSize != *lpdwRead ) ) return AVIERR_FILEREAD; return AVIFILE_IAVIFile_SkipChunkData( This, dwChunkSize - dwBufSize ); } static HRESULT AVIFILE_IAVIFile_SeekToSpecifiedChunk( IAVIFileImpl* This, FOURCC fccType, DWORD* pdwLen ) { HRESULT hr; FOURCC fcc; BYTE buf[4]; DWORD dwRead; while ( 1 ) { hr = AVIFILE_IAVIFile_ReadNextChunkHeader( This, &fcc, pdwLen ); if ( hr != S_OK ) return hr; if ( fcc == fccType ) return S_OK; if ( fcc == FOURCC_LIST ) { if ( ( !ReadFile( This->hf, buf, 4, &dwRead, NULL ) ) || ( 4 != dwRead ) ) return AVIERR_FILEREAD; } else { hr = AVIFILE_IAVIFile_SkipChunkData( This, *pdwLen ); if ( hr != S_OK ) return hr; } } } WINE_AVISTREAM_DATA* AVIFILE_Alloc_IAVIStreamData( DWORD dwFmtLen ) { WINE_AVISTREAM_DATA* pData; pData = (WINE_AVISTREAM_DATA*) HeapAlloc( AVIFILE_data.hHeap,HEAP_ZERO_MEMORY, sizeof(WINE_AVISTREAM_DATA) ); if ( pData == NULL ) return NULL; if ( dwFmtLen > 0 ) { pData->pbFmt = (BYTE*) HeapAlloc( AVIFILE_data.hHeap,HEAP_ZERO_MEMORY, sizeof(BYTE)*dwFmtLen ); if ( pData->pbFmt == NULL ) { AVIFILE_Free_IAVIStreamData( pData ); return NULL; } } pData->dwFmtLen = dwFmtLen; return pData; } void AVIFILE_Free_IAVIStreamData( WINE_AVISTREAM_DATA* pData ) { if ( pData != NULL ) { if ( pData->pbFmt != NULL ) HeapFree( AVIFILE_data.hHeap,0,pData->pbFmt ); HeapFree( AVIFILE_data.hHeap,0,pData ); } } static void AVIFILE_IAVIFile_InitIndexTable( IAVIFileImpl* This, AVIINDEXENTRY* pIndexBuf, AVIINDEXENTRY* pIndexData, DWORD dwCountOfIndexEntry ) { DWORD dwStreamIndex; DWORD dwIndex; FOURCC ckid; dwStreamIndex = 0; for ( ; dwStreamIndex < (AVIFILE_STREAMS_MAX+1); dwStreamIndex ++ ) This->pStreamIndexEntry[dwStreamIndex] = NULL; dwStreamIndex = 0; for ( ; dwStreamIndex < This->hdr.dwStreams; dwStreamIndex ++ ) { ckid = mmioFOURCC('0','0'+dwStreamIndex,0,0); TRACE( "testing ckid %c%c%c%c\n", (int)(ckid>> 0)&0xff, (int)(ckid>> 8)&0xff, (int)(ckid>>16)&0xff, (int)(ckid>>24)&0xff ); This->pStreamIndexEntry[dwStreamIndex] = pIndexBuf; FIXME( "pIndexBuf = %p\n", pIndexBuf ); for ( dwIndex = 0; dwIndex < dwCountOfIndexEntry; dwIndex++ ) { TRACE( "ckid %c%c%c%c\n", (int)(pIndexData[dwIndex].ckid>> 0)&0xff, (int)(pIndexData[dwIndex].ckid>> 8)&0xff, (int)(pIndexData[dwIndex].ckid>>16)&0xff, (int)(pIndexData[dwIndex].ckid>>24)&0xff ); if ( (pIndexData[dwIndex].ckid & mmioFOURCC(0xff,0xff,0,0)) == ckid ) { memcpy( pIndexBuf, &pIndexData[dwIndex], sizeof(AVIINDEXENTRY) ); pIndexBuf ++; } } FIXME( "pIndexBuf = %p\n", pIndexBuf ); } This->pStreamIndexEntry[This->hdr.dwStreams] = pIndexBuf; } /**************************************************************************** * Create an IAVIFile object. */ static HRESULT AVIFILE_IAVIFile_Construct( IAVIFileImpl* This ); static void AVIFILE_IAVIFile_Destruct( IAVIFileImpl* This ); HRESULT AVIFILE_CreateIAVIFile(void** ppobj) { IAVIFileImpl *This; HRESULT hr; TRACE("(%p)\n",ppobj); *ppobj = NULL; This = (IAVIFileImpl*)HeapAlloc(AVIFILE_data.hHeap,HEAP_ZERO_MEMORY, sizeof(IAVIFileImpl)); if ( This == NULL ) return AVIERR_MEMORY; This->ref = 1; ICOM_VTBL(This) = &iavift; hr = AVIFILE_IAVIFile_Construct( This ); if ( hr != S_OK ) { AVIFILE_IAVIFile_Destruct( This ); return hr; } TRACE("new -> %p\n",This); *ppobj = (LPVOID)This; return S_OK; } /**************************************************************************** * IUnknown interface */ static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile* iface,REFIID refiid,LPVOID *obj) { ICOM_THIS(IAVIFileImpl,iface); TRACE("(%p)->QueryInterface(%s,%p)\n",This,debugstr_guid(refiid),obj); if ( IsEqualGUID(&IID_IUnknown,refiid) || IsEqualGUID(&IID_IAVIFile,refiid) ) { *obj = iface; IAVIFile_AddRef(iface); return S_OK; } return OLE_E_ENUM_NOMORE; } static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile* iface) { ICOM_THIS(IAVIFileImpl,iface); TRACE("(%p)->AddRef()\n",iface); return ++(This->ref); } static ULONG WINAPI IAVIFile_fnRelease(IAVIFile* iface) { ICOM_THIS(IAVIFileImpl,iface); TRACE("(%p)->Release()\n",iface); if ( (--(This->ref)) > 0 ) return This->ref; AVIFILE_IAVIFile_Destruct(This); HeapFree(AVIFILE_data.hHeap,0,iface); return 0; } /**************************************************************************** * IAVIFile interface */ static HRESULT AVIFILE_IAVIFile_Construct( IAVIFileImpl* This ) { DWORD dwIndex; This->hf = INVALID_HANDLE_VALUE; This->dwAVIFileCaps = 0; This->dwAVIFileScale = 0; This->dwAVIFileRate = 0; This->dwAVIFileLength = 0; This->dwAVIFileEditCount = 0; for ( dwIndex = 0; dwIndex < AVIFILE_STREAMS_MAX; dwIndex++ ) This->pStreams[dwIndex] = NULL; This->dwCountOfIndexEntry = 0; This->pIndexEntry = NULL; AVIFILE_data.dwClassObjRef ++; return S_OK; } static void AVIFILE_IAVIFile_Destruct( IAVIFileImpl* This ) { DWORD dwIndex; if ( This->pIndexEntry != NULL ) { HeapFree(AVIFILE_data.hHeap,0,This->pIndexEntry); This->pIndexEntry = NULL; } for ( dwIndex = 0; dwIndex < AVIFILE_STREAMS_MAX; dwIndex++ ) { if ( This->pStreams[dwIndex] != NULL ) { IAVIStream_Release( This->pStreams[dwIndex] ); This->pStreams[dwIndex] = NULL; } } if ( This->hf != INVALID_HANDLE_VALUE ) CloseHandle( This->hf ); AVIFILE_data.dwClassObjRef --; } static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile*iface,AVIFILEINFOW*afi,LONG size) { ICOM_THIS(IAVIFileImpl,iface); AVIFILEINFOW fiw; FIXME("(%p)->Info(%p,%ld)\n",iface,afi,size); memset( &fiw, 0, sizeof(fiw) ); fiw.dwMaxBytesPerSec = This->hdr.dwMaxBytesPerSec; fiw.dwFlags = This->hdr.dwFlags; fiw.dwCaps = This->dwAVIFileCaps; fiw.dwStreams = This->hdr.dwStreams; fiw.dwSuggestedBufferSize = This->hdr.dwSuggestedBufferSize; fiw.dwWidth = This->hdr.dwWidth; fiw.dwHeight = This->hdr.dwHeight; fiw.dwScale = This->dwAVIFileScale; /* FIXME */ fiw.dwRate = This->dwAVIFileRate; /* FIXME */ fiw.dwLength = This->dwAVIFileLength; /* FIXME */ fiw.dwEditCount = This->dwAVIFileEditCount; /* FIXME */ /* fiw.szFileType[64]; */ if ( size > sizeof(AVIFILEINFOW) ) size = sizeof(AVIFILEINFOW); memcpy( afi, &fiw, size ); return S_OK; } static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile*iface,PAVISTREAM*avis,DWORD fccType,LONG lParam) { ICOM_THIS(IAVIFileImpl,iface); FIXME("(%p)->GetStream(%p,0x%08lx,%ld)\n",iface,avis,fccType,lParam); if ( fccType != 0 ) return E_FAIL; if ( lParam < 0 || lParam >= This->hdr.dwStreams ) return E_FAIL; *avis = This->pStreams[lParam]; IAVIStream_AddRef( *avis ); return S_OK; } static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile*iface,PAVISTREAM*avis,AVISTREAMINFOW*asi) { ICOM_THIS(IAVIFileImpl,iface); FIXME("(%p,%p,%p)\n",This,avis,asi); return E_FAIL; } static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG size) { ICOM_THIS(IAVIFileImpl,iface); FIXME("(%p)->WriteData(0x%08lx,%p,%ld)\n",This,ckid,lpData,size); /* FIXME: write data to file */ return E_FAIL; } static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG *size) { ICOM_THIS(IAVIFileImpl,iface); FIXME("(%p)->ReadData(0x%08lx,%p,%p)\n",This,ckid,lpData,size); /* FIXME: read at most size bytes from file */ return E_FAIL; } static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile*iface) { ICOM_THIS(IAVIFileImpl,iface); FIXME("(%p)->EndRecord()\n",This); /* FIXME: end record? */ return E_FAIL; } static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile*iface,DWORD fccType,LONG lParam) { ICOM_THIS(IAVIFileImpl,iface); FIXME("(%p)->DeleteStream(0x%08lx,%ld)\n",This,fccType,lParam); /* FIXME: delete stream? */ return E_FAIL; } /***************************************************************************** * AVIFILE_IAVIFile_Open (internal) */ HRESULT AVIFILE_IAVIFile_Open( PAVIFILE paf, LPCWSTR szFile, UINT uMode ) { ICOM_THIS(IAVIFileImpl,paf); HRESULT hr; DWORD dwAcc; DWORD dwShared; DWORD dwCreate; BYTE buf[12]; DWORD dwRead; FOURCC fccFileType; DWORD dwLen; DWORD dwIndex; FIXME("(%p)->Open(%p,%u)\n",This,szFile,uMode); if ( This->hf != INVALID_HANDLE_VALUE ) { CloseHandle( This->hf ); This->hf = INVALID_HANDLE_VALUE; } switch ( uMode & 0x3 ) { case OF_READ: /* 0x0 */ dwAcc = GENERIC_READ; dwCreate = OPEN_EXISTING; This->dwAVIFileCaps = AVIFILECAPS_CANREAD; break; case OF_WRITE: /* 0x1 */ dwAcc = GENERIC_WRITE; dwCreate = OPEN_ALWAYS; This->dwAVIFileCaps = AVIFILECAPS_CANWRITE; break; case OF_READWRITE: /* 0x2 */ dwAcc = GENERIC_READ|GENERIC_WRITE; dwCreate = OPEN_ALWAYS; This->dwAVIFileCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; break; default: return E_FAIL; } if ( This->dwAVIFileCaps & AVIFILECAPS_CANWRITE ) { FIXME( "editing AVI is currently not supported!\n" ); return E_FAIL; } switch ( uMode & 0x70 ) { case OF_SHARE_COMPAT: /* 0x00 */ dwShared = FILE_SHARE_READ|FILE_SHARE_WRITE; break; case OF_SHARE_EXCLUSIVE: /* 0x10 */ dwShared = 0; break; case OF_SHARE_DENY_WRITE: /* 0x20 */ dwShared = FILE_SHARE_READ; break; case OF_SHARE_DENY_READ: /* 0x30 */ dwShared = FILE_SHARE_WRITE; break; case OF_SHARE_DENY_NONE: /* 0x40 */ dwShared = FILE_SHARE_READ|FILE_SHARE_WRITE; break; default: return E_FAIL; } if ( uMode & OF_CREATE ) dwCreate = CREATE_ALWAYS; This->hf = CreateFileW( szFile, dwAcc, dwShared, NULL, dwCreate, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL ); if ( This->hf == INVALID_HANDLE_VALUE ) return AVIERR_FILEOPEN; if ( dwAcc & GENERIC_READ ) { if ( !ReadFile( This->hf, buf, 12, &dwRead, NULL ) ) return AVIERR_FILEREAD; if ( dwRead == 12 ) { if ( mmioFOURCC(buf[0],buf[1],buf[2],buf[3]) != FOURCC_RIFF ) return AVIERR_BADFORMAT; fccFileType = mmioFOURCC(buf[8],buf[9],buf[10],buf[11]); if ( fccFileType != formtypeAVI ) return AVIERR_BADFORMAT; /* get AVI main header. */ hr = AVIFILE_IAVIFile_SeekToSpecifiedChunk( This, ckidAVIMAINHDR, &dwLen ); if ( hr != S_OK ) return hr; if ( dwLen < (sizeof(DWORD)*10) ) return AVIERR_BADFORMAT; hr = AVIFILE_IAVIFile_ReadChunkData( This, dwLen, &(This->hdr), sizeof(MainAVIHeader), &dwLen ); if ( This->hdr.dwStreams == 0 || This->hdr.dwStreams > AVIFILE_STREAMS_MAX ) return AVIERR_BADFORMAT; /* get stream headers. */ dwIndex = 0; while ( dwIndex < This->hdr.dwStreams ) { WINE_AVISTREAM_DATA* pData; hr = AVIFILE_IAVIFile_SeekToSpecifiedChunk( This, ckidSTREAMHEADER, &dwLen ); if ( hr != S_OK ) return hr; if ( dwLen < (sizeof(DWORD)*12) ) return AVIERR_BADFORMAT; hr = AVIFILE_IAVIFile_ReadChunkData( This, dwLen, &This->strhdrs[dwIndex], sizeof(AVIStreamHeader), &dwLen ); hr = AVIFILE_IAVIFile_SeekToSpecifiedChunk( This, ckidSTREAMFORMAT, &dwLen ); if ( hr != S_OK ) return hr; pData = AVIFILE_Alloc_IAVIStreamData( dwLen ); if ( pData == NULL ) return AVIERR_MEMORY; hr = AVIFILE_IAVIFile_ReadChunkData( This, dwLen, pData->pbFmt, dwLen, &dwLen ); if ( hr != S_OK ) { AVIFILE_Free_IAVIStreamData( pData ); return hr; } pData->dwStreamIndex = dwIndex; pData->pstrhdr = &This->strhdrs[dwIndex]; hr = AVIStreamCreate(&This->pStreams[dwIndex], (LONG)paf, (LONG)(pData), NULL ); if ( hr != S_OK ) { AVIFILE_Free_IAVIStreamData( pData ); return hr; } if ( (This->strhdrs[dwIndex].fccType == mmioFOURCC('v','i','d','s')) || (This->strhdrs[dwIndex].fccType == mmioFOURCC('V','I','D','S')) ) { This->dwAVIFileScale = This->strhdrs[dwIndex].dwScale; This->dwAVIFileRate = This->strhdrs[dwIndex].dwRate; This->dwAVIFileLength = This->strhdrs[dwIndex].dwLength; } else if ( This->dwAVIFileScale == 0 ) { This->dwAVIFileScale = This->strhdrs[dwIndex].dwScale; This->dwAVIFileRate = This->strhdrs[dwIndex].dwRate; This->dwAVIFileLength = This->strhdrs[dwIndex].dwLength; } dwIndex ++; } /* skip movi. */ while ( 1 ) { hr = AVIFILE_IAVIFile_SeekToSpecifiedChunk( This, FOURCC_LIST, &dwLen ); if ( hr != S_OK ) return hr; if ( dwLen < 4 ) return AVIERR_BADFORMAT; This->dwMoviTop = SetFilePointer( This->hf,0,NULL,FILE_CURRENT ); if ( This->dwMoviTop == 0xffffffff ) return AVIERR_BADFORMAT; if ( ( !ReadFile(This->hf, buf, 4, &dwRead, NULL) ) || ( dwRead != 4 ) ) return AVIERR_FILEREAD; hr = AVIFILE_IAVIFile_SkipChunkData( This, dwLen - 4 ); if ( hr != S_OK ) return hr; if ( mmioFOURCC(buf[0],buf[1],buf[2],buf[3]) == mmioFOURCC('m', 'o', 'v', 'i') ) break; } /* get idx1. */ hr = AVIFILE_IAVIFile_SeekToSpecifiedChunk( This, ckidAVINEWINDEX, &dwLen ); if ( hr != S_OK ) return hr; This->dwCountOfIndexEntry = dwLen / sizeof(AVIINDEXENTRY); This->pIndexEntry = (AVIINDEXENTRY*) HeapAlloc(AVIFILE_data.hHeap,HEAP_ZERO_MEMORY, sizeof(AVIINDEXENTRY) * This->dwCountOfIndexEntry * 2 ); if ( This->pIndexEntry == NULL ) return AVIERR_MEMORY; hr = AVIFILE_IAVIFile_ReadChunkData( This, dwLen, This->pIndexEntry + This->dwCountOfIndexEntry, sizeof(AVIINDEXENTRY) * This->dwCountOfIndexEntry, &dwLen ); if ( hr != S_OK ) return hr; AVIFILE_IAVIFile_InitIndexTable( This, This->pIndexEntry, This->pIndexEntry + This->dwCountOfIndexEntry, This->dwCountOfIndexEntry ); } else { /* FIXME - create the handle has GENERIC_WRITE access. */ return AVIERR_FILEREAD; } } else { return AVIERR_FILEOPEN; /* FIXME */ } return S_OK; } /***************************************************************************** * AVIFILE_IAVIFile_GetIndexTable (internal) */ HRESULT AVIFILE_IAVIFile_GetIndexTable( PAVIFILE paf, DWORD dwStreamIndex, AVIINDEXENTRY** ppIndexEntry, DWORD* pdwCountOfIndexEntry ) { ICOM_THIS(IAVIFileImpl,paf); if ( dwStreamIndex < 0 || dwStreamIndex >= This->hdr.dwStreams ) { FIXME( "invalid stream index %lu\n", dwStreamIndex ); return E_FAIL; } FIXME( "cur %p, next %p\n", This->pStreamIndexEntry[dwStreamIndex], This->pStreamIndexEntry[dwStreamIndex+1] ); *ppIndexEntry = This->pStreamIndexEntry[dwStreamIndex]; *pdwCountOfIndexEntry = This->pStreamIndexEntry[dwStreamIndex+1] - This->pStreamIndexEntry[dwStreamIndex]; return S_OK; } /***************************************************************************** * AVIFILE_IAVIFile_ReadMovieData (internal) */ HRESULT AVIFILE_IAVIFile_ReadMovieData( PAVIFILE paf, DWORD dwOffset, DWORD dwLength, LPVOID lpvBuf ) { ICOM_THIS(IAVIFileImpl,paf); LONG lHigh = 0; DWORD dwRes; if ( dwLength == 0 ) return S_OK; SetLastError(NO_ERROR); dwRes = SetFilePointer( This->hf, (LONG)(dwOffset+This->dwMoviTop), &lHigh, FILE_BEGIN ); if ( dwRes == (DWORD)0xffffffff && GetLastError() != NO_ERROR ) return AVIERR_FILEREAD; if ( ( !ReadFile(This->hf, lpvBuf, dwLength, &dwRes, NULL) ) || ( dwLength != dwRes ) ) { FIXME( "error in ReadFile()\n" ); return AVIERR_FILEREAD; } return S_OK; }