|  | /* | 
|  | * Copyright 1999 Marcus Meissner | 
|  | * Copyright 2002-2003 Michael Günnewig | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General Public | 
|  | * License along with this library; if not, write to the Free Software | 
|  | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
|  | */ | 
|  |  | 
|  | /* TODO: | 
|  | *  - IAVIStreaming interface is missing for the IAVIStreamImpl | 
|  | *  - IAVIStream_fnFindSample: FIND_INDEX isn't supported. | 
|  | *  - IAVIStream_fnReadFormat: formatchanges aren't read in. | 
|  | *  - IAVIStream_fnDelete: a stub. | 
|  | *  - IAVIStream_fnSetInfo: a stub. | 
|  | *  - make thread safe | 
|  | * | 
|  | * KNOWN Bugs: | 
|  | *  - native version can hangup when reading a file generated with this DLL. | 
|  | *    When index is missing it works, but index seems to be okay. | 
|  | */ | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <stdarg.h> | 
|  |  | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "wingdi.h" | 
|  | #include "winuser.h" | 
|  | #include "winnls.h" | 
|  | #include "winerror.h" | 
|  | #include "mmsystem.h" | 
|  | #include "vfw.h" | 
|  |  | 
|  | #include "avifile_private.h" | 
|  | #include "extrachunk.h" | 
|  |  | 
|  | #include "wine/unicode.h" | 
|  | #include "wine/debug.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(avifile); | 
|  |  | 
|  | #ifndef IDX_PER_BLOCK | 
|  | #define IDX_PER_BLOCK 2730 | 
|  | #endif | 
|  |  | 
|  | /***********************************************************************/ | 
|  |  | 
|  | 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); | 
|  |  | 
|  | static const struct IAVIFileVtbl iavift = { | 
|  | IAVIFile_fnQueryInterface, | 
|  | IAVIFile_fnAddRef, | 
|  | IAVIFile_fnRelease, | 
|  | IAVIFile_fnInfo, | 
|  | IAVIFile_fnGetStream, | 
|  | IAVIFile_fnCreateStream, | 
|  | IAVIFile_fnWriteData, | 
|  | IAVIFile_fnReadData, | 
|  | IAVIFile_fnEndRecord, | 
|  | IAVIFile_fnDeleteStream | 
|  | }; | 
|  |  | 
|  | static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile*iface,REFIID refiid,LPVOID*obj); | 
|  | static ULONG   WINAPI IPersistFile_fnAddRef(IPersistFile*iface); | 
|  | static ULONG   WINAPI IPersistFile_fnRelease(IPersistFile*iface); | 
|  | static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile*iface,CLSID*pClassID); | 
|  | static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile*iface); | 
|  | static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile*iface,LPCOLESTR pszFileName,DWORD dwMode); | 
|  | static HRESULT WINAPI IPersistFile_fnSave(IPersistFile*iface,LPCOLESTR pszFileName,BOOL fRemember); | 
|  | static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile*iface,LPCOLESTR pszFileName); | 
|  | static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile*iface,LPOLESTR*ppszFileName); | 
|  |  | 
|  | static const struct IPersistFileVtbl ipersistft = { | 
|  | IPersistFile_fnQueryInterface, | 
|  | IPersistFile_fnAddRef, | 
|  | IPersistFile_fnRelease, | 
|  | IPersistFile_fnGetClassID, | 
|  | IPersistFile_fnIsDirty, | 
|  | IPersistFile_fnLoad, | 
|  | IPersistFile_fnSave, | 
|  | IPersistFile_fnSaveCompleted, | 
|  | IPersistFile_fnGetCurFile | 
|  | }; | 
|  |  | 
|  | static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj); | 
|  | static ULONG   WINAPI IAVIStream_fnAddRef(IAVIStream*iface); | 
|  | static ULONG   WINAPI IAVIStream_fnRelease(IAVIStream* iface); | 
|  | static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2); | 
|  | static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size); | 
|  | static LONG    WINAPI IAVIStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags); | 
|  | static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize); | 
|  | static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize); | 
|  | static HRESULT WINAPI IAVIStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread); | 
|  | static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten); | 
|  | static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream*iface,LONG start,LONG samples); | 
|  | static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread); | 
|  | static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size); | 
|  | static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen); | 
|  |  | 
|  | static const struct IAVIStreamVtbl iavist = { | 
|  | IAVIStream_fnQueryInterface, | 
|  | IAVIStream_fnAddRef, | 
|  | IAVIStream_fnRelease, | 
|  | IAVIStream_fnCreate, | 
|  | IAVIStream_fnInfo, | 
|  | IAVIStream_fnFindSample, | 
|  | IAVIStream_fnReadFormat, | 
|  | IAVIStream_fnSetFormat, | 
|  | IAVIStream_fnRead, | 
|  | IAVIStream_fnWrite, | 
|  | IAVIStream_fnDelete, | 
|  | IAVIStream_fnReadData, | 
|  | IAVIStream_fnWriteData, | 
|  | IAVIStream_fnSetInfo | 
|  | }; | 
|  |  | 
|  | typedef struct _IAVIFileImpl IAVIFileImpl; | 
|  |  | 
|  | typedef struct _IPersistFileImpl { | 
|  | /* IUnknown stuff */ | 
|  | const IPersistFileVtbl *lpVtbl; | 
|  |  | 
|  | /* IPersistFile stuff */ | 
|  | IAVIFileImpl     *paf; | 
|  | } IPersistFileImpl; | 
|  |  | 
|  | typedef struct _IAVIStreamImpl { | 
|  | /* IUnknown stuff */ | 
|  | const IAVIStreamVtbl *lpVtbl; | 
|  | LONG		    ref; | 
|  |  | 
|  | /* IAVIStream stuff */ | 
|  | IAVIFileImpl     *paf; | 
|  | DWORD             nStream;       /* the n-th stream in file */ | 
|  | AVISTREAMINFOW    sInfo; | 
|  |  | 
|  | LPVOID            lpFormat; | 
|  | DWORD             cbFormat; | 
|  |  | 
|  | LPVOID            lpHandlerData; | 
|  | DWORD             cbHandlerData; | 
|  |  | 
|  | EXTRACHUNKS       extra; | 
|  |  | 
|  | LPDWORD           lpBuffer; | 
|  | DWORD             cbBuffer;       /* size of lpBuffer */ | 
|  | DWORD             dwCurrentFrame; /* frame/block currently in lpBuffer */ | 
|  |  | 
|  | LONG              lLastFrame;    /* last correct index in idxFrames */ | 
|  | AVIINDEXENTRY    *idxFrames; | 
|  | DWORD             nIdxFrames;     /* upper index limit of idxFrames */ | 
|  | AVIINDEXENTRY    *idxFmtChanges; | 
|  | DWORD             nIdxFmtChanges; /* upper index limit of idxFmtChanges */ | 
|  | } IAVIStreamImpl; | 
|  |  | 
|  | struct _IAVIFileImpl { | 
|  | /* IUnknown stuff */ | 
|  | const IAVIFileVtbl     *lpVtbl; | 
|  | LONG		    ref; | 
|  |  | 
|  | /* IAVIFile stuff... */ | 
|  | IPersistFileImpl  iPersistFile; | 
|  |  | 
|  | AVIFILEINFOW      fInfo; | 
|  | IAVIStreamImpl   *ppStreams[MAX_AVISTREAMS]; | 
|  |  | 
|  | EXTRACHUNKS       fileextra; | 
|  |  | 
|  | DWORD             dwMoviChunkPos;  /* some stuff for saving ... */ | 
|  | DWORD             dwIdxChunkPos; | 
|  | DWORD             dwNextFramePos; | 
|  | DWORD             dwInitialFrames; | 
|  |  | 
|  | MMCKINFO          ckLastRecord; | 
|  | AVIINDEXENTRY    *idxRecords;      /* won't be updated while loading */ | 
|  | DWORD             nIdxRecords;     /* current fill level */ | 
|  | DWORD             cbIdxRecords;    /* size of idxRecords */ | 
|  |  | 
|  | /* IPersistFile stuff ... */ | 
|  | HMMIO             hmmio; | 
|  | LPWSTR            szFileName; | 
|  | UINT              uMode; | 
|  | BOOL              fDirty; | 
|  | }; | 
|  |  | 
|  | /***********************************************************************/ | 
|  |  | 
|  | static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, | 
|  | DWORD offset, DWORD flags); | 
|  | static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This); | 
|  | static DWORD   AVIFILE_ComputeMoviStart(IAVIFileImpl *This); | 
|  | static void    AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, | 
|  | const AVISTREAMINFOW *asi); | 
|  | static void    AVIFILE_DestructAVIStream(IAVIStreamImpl *This); | 
|  | static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This); | 
|  | static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset); | 
|  | static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp, | 
|  | LONG count, DWORD pos, BOOL *bAbsolute); | 
|  | static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD start, | 
|  | LPVOID buffer, DWORD size); | 
|  | static void    AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos, | 
|  | LPLONG offset); | 
|  | static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This); | 
|  | static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This); | 
|  | static ULONG   AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fccType, | 
|  | LONG lSkip); | 
|  | static void    AVIFILE_UpdateInfo(IAVIFileImpl *This); | 
|  | static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block, | 
|  | FOURCC ckid, DWORD flags, LPCVOID buffer, | 
|  | LONG size); | 
|  |  | 
|  | HRESULT AVIFILE_CreateAVIFile(REFIID riid, LPVOID *ppv) | 
|  | { | 
|  | IAVIFileImpl *pfile; | 
|  | HRESULT       hr; | 
|  |  | 
|  | assert(riid != NULL && ppv != NULL); | 
|  |  | 
|  | *ppv = NULL; | 
|  |  | 
|  | pfile = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIFileImpl)); | 
|  | if (pfile == NULL) | 
|  | return AVIERR_MEMORY; | 
|  |  | 
|  | pfile->lpVtbl = &iavift; | 
|  | pfile->ref = 0; | 
|  | pfile->iPersistFile.lpVtbl = &ipersistft; | 
|  | pfile->iPersistFile.paf = pfile; | 
|  |  | 
|  | hr = IAVIFile_QueryInterface((IAVIFile*)pfile, riid, ppv); | 
|  | if (FAILED(hr)) | 
|  | HeapFree(GetProcessHeap(), 0, pfile); | 
|  |  | 
|  | return hr; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID refiid, | 
|  | LPVOID *obj) | 
|  | { | 
|  | IAVIFileImpl *This = (IAVIFileImpl *)iface; | 
|  |  | 
|  | TRACE("(%p,%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; | 
|  | } else if (IsEqualGUID(&IID_IPersistFile, refiid)) { | 
|  | *obj = &This->iPersistFile; | 
|  | IAVIFile_AddRef(iface); | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | return OLE_E_ENUM_NOMORE; | 
|  | } | 
|  |  | 
|  | static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface) | 
|  | { | 
|  | IAVIFileImpl *This = (IAVIFileImpl *)iface; | 
|  | ULONG ref = InterlockedIncrement(&This->ref); | 
|  |  | 
|  | TRACE("(%p) -> %d\n", iface, ref); | 
|  |  | 
|  | return ref; | 
|  | } | 
|  |  | 
|  | static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface) | 
|  | { | 
|  | IAVIFileImpl *This = (IAVIFileImpl *)iface; | 
|  | UINT i; | 
|  | ULONG ref = InterlockedDecrement(&This->ref); | 
|  |  | 
|  | TRACE("(%p) -> %d\n", iface, ref); | 
|  |  | 
|  | if (!ref) { | 
|  | if (This->fDirty) { | 
|  | /* need to write headers to file */ | 
|  | AVIFILE_SaveFile(This); | 
|  | } | 
|  |  | 
|  | for (i = 0; i < This->fInfo.dwStreams; i++) { | 
|  | if (This->ppStreams[i] != NULL) { | 
|  | if (This->ppStreams[i]->ref != 0) { | 
|  | ERR(": someone has still %u reference to stream %u (%p)!\n", | 
|  | This->ppStreams[i]->ref, i, This->ppStreams[i]); | 
|  | } | 
|  | AVIFILE_DestructAVIStream(This->ppStreams[i]); | 
|  | HeapFree(GetProcessHeap(), 0, This->ppStreams[i]); | 
|  | This->ppStreams[i] = NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (This->idxRecords != NULL) { | 
|  | HeapFree(GetProcessHeap(), 0, This->idxRecords); | 
|  | This->idxRecords  = NULL; | 
|  | This->nIdxRecords = 0; | 
|  | } | 
|  |  | 
|  | if (This->fileextra.lp != NULL) { | 
|  | HeapFree(GetProcessHeap(), 0, This->fileextra.lp); | 
|  | This->fileextra.lp = NULL; | 
|  | This->fileextra.cb = 0; | 
|  | } | 
|  |  | 
|  | HeapFree(GetProcessHeap(), 0, This->szFileName); | 
|  | This->szFileName = NULL; | 
|  |  | 
|  | if (This->hmmio != NULL) { | 
|  | mmioClose(This->hmmio, 0); | 
|  | This->hmmio = NULL; | 
|  | } | 
|  |  | 
|  | HeapFree(GetProcessHeap(), 0, This); | 
|  | } | 
|  | return ref; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, LPAVIFILEINFOW afi, | 
|  | LONG size) | 
|  | { | 
|  | IAVIFileImpl *This = (IAVIFileImpl *)iface; | 
|  |  | 
|  | TRACE("(%p,%p,%d)\n",iface,afi,size); | 
|  |  | 
|  | if (afi == NULL) | 
|  | return AVIERR_BADPARAM; | 
|  | if (size < 0) | 
|  | return AVIERR_BADSIZE; | 
|  |  | 
|  | AVIFILE_UpdateInfo(This); | 
|  |  | 
|  | memcpy(afi, &This->fInfo, min((DWORD)size, sizeof(This->fInfo))); | 
|  |  | 
|  | if ((DWORD)size < sizeof(This->fInfo)) | 
|  | return AVIERR_BUFFERTOOSMALL; | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, PAVISTREAM *avis, | 
|  | DWORD fccType, LONG lParam) | 
|  | { | 
|  | IAVIFileImpl *This = (IAVIFileImpl *)iface; | 
|  |  | 
|  | ULONG nStream; | 
|  |  | 
|  | TRACE("(%p,%p,0x%08X,%d)\n", iface, avis, fccType, lParam); | 
|  |  | 
|  | if (avis == NULL || lParam < 0) | 
|  | return AVIERR_BADPARAM; | 
|  |  | 
|  | nStream = AVIFILE_SearchStream(This, fccType, lParam); | 
|  |  | 
|  | /* Does the requested stream exist? */ | 
|  | if (nStream < This->fInfo.dwStreams && | 
|  | This->ppStreams[nStream] != NULL) { | 
|  | *avis = (PAVISTREAM)This->ppStreams[nStream]; | 
|  | IAVIStream_AddRef(*avis); | 
|  |  | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | /* Sorry, but the specified stream doesn't exist */ | 
|  | return AVIERR_NODATA; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface,PAVISTREAM *avis, | 
|  | LPAVISTREAMINFOW asi) | 
|  | { | 
|  | IAVIFileImpl *This = (IAVIFileImpl *)iface; | 
|  |  | 
|  | DWORD n; | 
|  |  | 
|  | TRACE("(%p,%p,%p)\n", iface, avis, asi); | 
|  |  | 
|  | /* check parameters */ | 
|  | if (avis == NULL || asi == NULL) | 
|  | return AVIERR_BADPARAM; | 
|  |  | 
|  | *avis = NULL; | 
|  |  | 
|  | /* Does the user have write permission? */ | 
|  | if ((This->uMode & MMIO_RWMODE) == 0) | 
|  | return AVIERR_READONLY; | 
|  |  | 
|  | /* Can we add another stream? */ | 
|  | n = This->fInfo.dwStreams; | 
|  | if (n >= MAX_AVISTREAMS || This->dwMoviChunkPos != 0) { | 
|  | /* already reached max nr of streams | 
|  | * or have already written frames to disk */ | 
|  | return AVIERR_UNSUPPORTED; | 
|  | } | 
|  |  | 
|  | /* check AVISTREAMINFO for some really needed things */ | 
|  | if (asi->fccType == 0 || asi->dwScale == 0 || asi->dwRate == 0) | 
|  | return AVIERR_BADFORMAT; | 
|  |  | 
|  | /* now it seems to be save to add the stream */ | 
|  | assert(This->ppStreams[n] == NULL); | 
|  | This->ppStreams[n] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, | 
|  | sizeof(IAVIStreamImpl)); | 
|  | if (This->ppStreams[n] == NULL) | 
|  | return AVIERR_MEMORY; | 
|  |  | 
|  | /* initialize the new allocated stream */ | 
|  | AVIFILE_ConstructAVIStream(This, n, asi); | 
|  |  | 
|  | This->fInfo.dwStreams++; | 
|  | This->fDirty = TRUE; | 
|  |  | 
|  | /* update our AVIFILEINFO structure */ | 
|  | AVIFILE_UpdateInfo(This); | 
|  |  | 
|  | /* return it */ | 
|  | *avis = (PAVISTREAM)This->ppStreams[n]; | 
|  | IAVIStream_AddRef(*avis); | 
|  |  | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid, | 
|  | LPVOID lpData, LONG size) | 
|  | { | 
|  | IAVIFileImpl *This = (IAVIFileImpl *)iface; | 
|  |  | 
|  | TRACE("(%p,0x%08X,%p,%d)\n", iface, ckid, lpData, size); | 
|  |  | 
|  | /* check parameters */ | 
|  | if (lpData == NULL) | 
|  | return AVIERR_BADPARAM; | 
|  | if (size < 0) | 
|  | return AVIERR_BADSIZE; | 
|  |  | 
|  | /* Do we have write permission? */ | 
|  | if ((This->uMode & MMIO_RWMODE) == 0) | 
|  | return AVIERR_READONLY; | 
|  |  | 
|  | This->fDirty = TRUE; | 
|  |  | 
|  | return WriteExtraChunk(&This->fileextra, ckid, lpData, size); | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid, | 
|  | LPVOID lpData, LONG *size) | 
|  | { | 
|  | IAVIFileImpl *This = (IAVIFileImpl *)iface; | 
|  |  | 
|  | TRACE("(%p,0x%08X,%p,%p)\n", iface, ckid, lpData, size); | 
|  |  | 
|  | return ReadExtraChunk(&This->fileextra, ckid, lpData, size); | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface) | 
|  | { | 
|  | IAVIFileImpl *This = (IAVIFileImpl *)iface; | 
|  |  | 
|  | TRACE("(%p)\n",iface); | 
|  |  | 
|  | if ((This->uMode & MMIO_RWMODE) == 0) | 
|  | return AVIERR_READONLY; | 
|  |  | 
|  | This->fDirty = TRUE; | 
|  |  | 
|  | /* no frames written to any stream? -- compute start of 'movi'-chunk */ | 
|  | if (This->dwMoviChunkPos == 0) | 
|  | AVIFILE_ComputeMoviStart(This); | 
|  |  | 
|  | This->fInfo.dwFlags  |= AVIFILEINFO_ISINTERLEAVED; | 
|  |  | 
|  | /* already written frames to any stream, ... */ | 
|  | if (This->ckLastRecord.dwFlags & MMIO_DIRTY) { | 
|  | /* close last record */ | 
|  | if (mmioAscend(This->hmmio, &This->ckLastRecord, 0) != 0) | 
|  | return AVIERR_FILEWRITE; | 
|  |  | 
|  | AVIFILE_AddRecord(This); | 
|  |  | 
|  | if (This->fInfo.dwSuggestedBufferSize < This->ckLastRecord.cksize + 3 * sizeof(DWORD)) | 
|  | This->fInfo.dwSuggestedBufferSize = This->ckLastRecord.cksize + 3 * sizeof(DWORD); | 
|  | } | 
|  |  | 
|  | /* write out a new record into file, but don't close it */ | 
|  | This->ckLastRecord.cksize  = 0; | 
|  | This->ckLastRecord.fccType = listtypeAVIRECORD; | 
|  | if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1) | 
|  | return AVIERR_FILEWRITE; | 
|  | if (mmioCreateChunk(This->hmmio, &This->ckLastRecord, MMIO_CREATELIST) != 0) | 
|  | return AVIERR_FILEWRITE; | 
|  | This->dwNextFramePos += 3 * sizeof(DWORD); | 
|  |  | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType, | 
|  | LONG lParam) | 
|  | { | 
|  | IAVIFileImpl *This = (IAVIFileImpl *)iface; | 
|  |  | 
|  | ULONG nStream; | 
|  |  | 
|  | TRACE("(%p,0x%08X,%d)\n", iface, fccType, lParam); | 
|  |  | 
|  | /* check parameter */ | 
|  | if (lParam < 0) | 
|  | return AVIERR_BADPARAM; | 
|  |  | 
|  | /* Have user write permissions? */ | 
|  | if ((This->uMode & MMIO_RWMODE) == 0) | 
|  | return AVIERR_READONLY; | 
|  |  | 
|  | nStream = AVIFILE_SearchStream(This, fccType, lParam); | 
|  |  | 
|  | /* Does the requested stream exist? */ | 
|  | if (nStream < This->fInfo.dwStreams && | 
|  | This->ppStreams[nStream] != NULL) { | 
|  | /* ... so delete it now */ | 
|  | HeapFree(GetProcessHeap(), 0, This->ppStreams[nStream]); | 
|  |  | 
|  | if (This->fInfo.dwStreams - nStream > 0) | 
|  | memcpy(This->ppStreams + nStream, This->ppStreams + nStream + 1, | 
|  | (This->fInfo.dwStreams - nStream) * sizeof(IAVIStreamImpl*)); | 
|  |  | 
|  | This->ppStreams[This->fInfo.dwStreams] = NULL; | 
|  | This->fInfo.dwStreams--; | 
|  | This->fDirty = TRUE; | 
|  |  | 
|  | /* This->fInfo will be updated further when asked for */ | 
|  | return AVIERR_OK; | 
|  | } else | 
|  | return AVIERR_NODATA; | 
|  | } | 
|  |  | 
|  | /***********************************************************************/ | 
|  |  | 
|  | static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface, | 
|  | REFIID refiid, LPVOID *obj) | 
|  | { | 
|  | IPersistFileImpl *This = (IPersistFileImpl *)iface; | 
|  |  | 
|  | assert(This->paf != NULL); | 
|  |  | 
|  | return IAVIFile_QueryInterface((PAVIFILE)This->paf, refiid, obj); | 
|  | } | 
|  |  | 
|  | static ULONG   WINAPI IPersistFile_fnAddRef(IPersistFile *iface) | 
|  | { | 
|  | IPersistFileImpl *This = (IPersistFileImpl *)iface; | 
|  |  | 
|  | assert(This->paf != NULL); | 
|  |  | 
|  | return IAVIFile_AddRef((PAVIFILE)This->paf); | 
|  | } | 
|  |  | 
|  | static ULONG   WINAPI IPersistFile_fnRelease(IPersistFile *iface) | 
|  | { | 
|  | IPersistFileImpl *This = (IPersistFileImpl *)iface; | 
|  |  | 
|  | assert(This->paf != NULL); | 
|  |  | 
|  | return IAVIFile_Release((PAVIFILE)This->paf); | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface, | 
|  | LPCLSID pClassID) | 
|  | { | 
|  | TRACE("(%p,%p)\n", iface, pClassID); | 
|  |  | 
|  | if (pClassID == NULL) | 
|  | return AVIERR_BADPARAM; | 
|  |  | 
|  | *pClassID = CLSID_AVIFile; | 
|  |  | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface) | 
|  | { | 
|  | IPersistFileImpl *This = (IPersistFileImpl *)iface; | 
|  |  | 
|  | TRACE("(%p)\n", iface); | 
|  |  | 
|  | assert(This->paf != NULL); | 
|  |  | 
|  | return (This->paf->fDirty ? S_OK : S_FALSE); | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface, | 
|  | LPCOLESTR pszFileName, DWORD dwMode) | 
|  | { | 
|  | IPersistFileImpl *This = (IPersistFileImpl *)iface; | 
|  |  | 
|  | int len; | 
|  |  | 
|  | TRACE("(%p,%s,0x%08X)\n", iface, debugstr_w(pszFileName), dwMode); | 
|  |  | 
|  | /* check parameter */ | 
|  | if (pszFileName == NULL) | 
|  | return AVIERR_BADPARAM; | 
|  |  | 
|  | assert(This->paf != NULL); | 
|  | if (This->paf->hmmio != NULL) | 
|  | return AVIERR_ERROR; /* No reuse of this object for another file! */ | 
|  |  | 
|  | /* remember mode and name */ | 
|  | This->paf->uMode = dwMode; | 
|  |  | 
|  | len = lstrlenW(pszFileName) + 1; | 
|  | This->paf->szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); | 
|  | if (This->paf->szFileName == NULL) | 
|  | return AVIERR_MEMORY; | 
|  | lstrcpyW(This->paf->szFileName, pszFileName); | 
|  |  | 
|  | /* try to open the file */ | 
|  | This->paf->hmmio = mmioOpenW(This->paf->szFileName, NULL, | 
|  | MMIO_ALLOCBUF | dwMode); | 
|  | if (This->paf->hmmio == NULL) { | 
|  | /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */ | 
|  | LPSTR szFileName; | 
|  |  | 
|  | len = WideCharToMultiByte(CP_ACP, 0, This->paf->szFileName, -1, | 
|  | NULL, 0, NULL, NULL); | 
|  | szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(CHAR)); | 
|  | if (szFileName == NULL) | 
|  | return AVIERR_MEMORY; | 
|  |  | 
|  | WideCharToMultiByte(CP_ACP, 0, This->paf->szFileName, -1, szFileName, | 
|  | len, NULL, NULL); | 
|  |  | 
|  | This->paf->hmmio = mmioOpenA(szFileName, NULL, MMIO_ALLOCBUF | dwMode); | 
|  | HeapFree(GetProcessHeap(), 0, szFileName); | 
|  | if (This->paf->hmmio == NULL) | 
|  | return AVIERR_FILEOPEN; | 
|  | } | 
|  |  | 
|  | /* should we create a new file? */ | 
|  | if (dwMode & OF_CREATE) { | 
|  | memset(& This->paf->fInfo, 0, sizeof(This->paf->fInfo)); | 
|  | This->paf->fInfo.dwFlags = AVIFILEINFO_HASINDEX | AVIFILEINFO_TRUSTCKTYPE; | 
|  |  | 
|  | return AVIERR_OK; | 
|  | } else | 
|  | return AVIFILE_LoadFile(This->paf); | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface, | 
|  | LPCOLESTR pszFileName,BOOL fRemember) | 
|  | { | 
|  | TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember); | 
|  |  | 
|  | /* We write directly to disk, so nothing to do. */ | 
|  |  | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface, | 
|  | LPCOLESTR pszFileName) | 
|  | { | 
|  | TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName)); | 
|  |  | 
|  | /* We write directly to disk, so nothing to do. */ | 
|  |  | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface, | 
|  | LPOLESTR *ppszFileName) | 
|  | { | 
|  | IPersistFileImpl *This = (IPersistFileImpl *)iface; | 
|  |  | 
|  | TRACE("(%p,%p)\n", iface, ppszFileName); | 
|  |  | 
|  | if (ppszFileName == NULL) | 
|  | return AVIERR_BADPARAM; | 
|  |  | 
|  | *ppszFileName = NULL; | 
|  |  | 
|  | assert(This->paf != NULL); | 
|  |  | 
|  | if (This->paf->szFileName != NULL) { | 
|  | int len = lstrlenW(This->paf->szFileName) + 1; | 
|  |  | 
|  | *ppszFileName = CoTaskMemAlloc(len * sizeof(WCHAR)); | 
|  | if (*ppszFileName == NULL) | 
|  | return AVIERR_MEMORY; | 
|  |  | 
|  | strcpyW(*ppszFileName, This->paf->szFileName); | 
|  | } | 
|  |  | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | /***********************************************************************/ | 
|  |  | 
|  | static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface, | 
|  | REFIID refiid, LPVOID *obj) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  |  | 
|  | TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj); | 
|  |  | 
|  | if (IsEqualGUID(&IID_IUnknown, refiid) || | 
|  | IsEqualGUID(&IID_IAVIStream, refiid)) { | 
|  | *obj = This; | 
|  | IAVIStream_AddRef(iface); | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  | /* FIXME: IAVIStreaming interface */ | 
|  |  | 
|  | return OLE_E_ENUM_NOMORE; | 
|  | } | 
|  |  | 
|  | static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  | ULONG ref = InterlockedIncrement(&This->ref); | 
|  |  | 
|  | TRACE("(%p) -> %d\n", iface, ref); | 
|  |  | 
|  | /* also add ref to parent, so that it doesn't kill us */ | 
|  | if (This->paf != NULL) | 
|  | IAVIFile_AddRef((PAVIFILE)This->paf); | 
|  |  | 
|  | return ref; | 
|  | } | 
|  |  | 
|  | static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  | ULONG ref = InterlockedDecrement(&This->ref); | 
|  |  | 
|  | TRACE("(%p) -> %d\n", iface, ref); | 
|  |  | 
|  | if (This->paf != NULL) | 
|  | IAVIFile_Release((PAVIFILE)This->paf); | 
|  |  | 
|  | return ref; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1, | 
|  | LPARAM lParam2) | 
|  | { | 
|  | TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2); | 
|  |  | 
|  | /* This IAVIStream interface needs an AVIFile */ | 
|  | return AVIERR_UNSUPPORTED; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi, | 
|  | LONG size) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  |  | 
|  | TRACE("(%p,%p,%d)\n", iface, psi, size); | 
|  |  | 
|  | if (psi == NULL) | 
|  | return AVIERR_BADPARAM; | 
|  | if (size < 0) | 
|  | return AVIERR_BADSIZE; | 
|  |  | 
|  | memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo))); | 
|  |  | 
|  | if ((DWORD)size < sizeof(This->sInfo)) | 
|  | return AVIERR_BUFFERTOOSMALL; | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos, | 
|  | LONG flags) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  |  | 
|  | LONG offset = 0; | 
|  |  | 
|  | TRACE("(%p,%d,0x%08X)\n",iface,pos,flags); | 
|  |  | 
|  | if (flags & FIND_FROM_START) { | 
|  | pos = This->sInfo.dwStart; | 
|  | flags &= ~(FIND_FROM_START|FIND_PREV); | 
|  | flags |= FIND_NEXT; | 
|  | } | 
|  |  | 
|  | if (This->sInfo.dwSampleSize != 0) { | 
|  | /* convert samples into block number with offset */ | 
|  | AVIFILE_SamplesToBlock(This, &pos, &offset); | 
|  | } | 
|  |  | 
|  | if (flags & FIND_TYPE) { | 
|  | if (flags & FIND_KEY) { | 
|  | while (0 <= pos && pos <= This->lLastFrame) { | 
|  | if (This->idxFrames[pos].dwFlags & AVIIF_KEYFRAME) | 
|  | goto RETURN_FOUND; | 
|  |  | 
|  | if (flags & FIND_NEXT) | 
|  | pos++; | 
|  | else | 
|  | pos--; | 
|  | }; | 
|  | } else if (flags & FIND_ANY) { | 
|  | while (0 <= pos && pos <= This->lLastFrame) { | 
|  | if (This->idxFrames[pos].dwChunkLength > 0) | 
|  | goto RETURN_FOUND; | 
|  |  | 
|  | if (flags & FIND_NEXT) | 
|  | pos++; | 
|  | else | 
|  | pos--; | 
|  |  | 
|  | }; | 
|  | } else if ((flags & FIND_FORMAT) && This->idxFmtChanges != NULL && | 
|  | This->sInfo.fccType == streamtypeVIDEO) { | 
|  | if (flags & FIND_NEXT) { | 
|  | ULONG n; | 
|  |  | 
|  | for (n = 0; n < This->sInfo.dwFormatChangeCount; n++) | 
|  | if (This->idxFmtChanges[n].ckid >= pos) { | 
|  | pos = This->idxFmtChanges[n].ckid; | 
|  | goto RETURN_FOUND; | 
|  | } | 
|  | } else { | 
|  | LONG n; | 
|  |  | 
|  | for (n = (LONG)This->sInfo.dwFormatChangeCount; n >= 0; n--) { | 
|  | if (This->idxFmtChanges[n].ckid <= pos) { | 
|  | pos = This->idxFmtChanges[n].ckid; | 
|  | goto RETURN_FOUND; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (pos > (LONG)This->sInfo.dwStart) | 
|  | return 0; /* format changes always for first frame */ | 
|  | } | 
|  | } | 
|  |  | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | RETURN_FOUND: | 
|  | if (pos < (LONG)This->sInfo.dwStart) | 
|  | return -1; | 
|  |  | 
|  | switch (flags & FIND_RET) { | 
|  | case FIND_LENGTH: | 
|  | /* physical size */ | 
|  | pos = This->idxFrames[pos].dwChunkLength; | 
|  | break; | 
|  | case FIND_OFFSET: | 
|  | /* physical position */ | 
|  | pos = This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD) | 
|  | + offset * This->sInfo.dwSampleSize; | 
|  | break; | 
|  | case FIND_SIZE: | 
|  | /* logical size */ | 
|  | if (This->sInfo.dwSampleSize) | 
|  | pos = This->sInfo.dwSampleSize; | 
|  | else | 
|  | pos = 1; | 
|  | break; | 
|  | case FIND_INDEX: | 
|  | FIXME(": FIND_INDEX flag is not supported!\n"); | 
|  | /* This is an index in the index-table on disc. */ | 
|  | break; | 
|  | }; /* else logical position */ | 
|  |  | 
|  | return pos; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos, | 
|  | LPVOID format, LONG *formatsize) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  |  | 
|  | TRACE("(%p,%d,%p,%p)\n", iface, pos, format, formatsize); | 
|  |  | 
|  | if (formatsize == NULL) | 
|  | return AVIERR_BADPARAM; | 
|  |  | 
|  | /* only interested in needed buffersize? */ | 
|  | if (format == NULL || *formatsize <= 0) { | 
|  | *formatsize = This->cbFormat; | 
|  |  | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | /* copy initial format (only as much as will fit) */ | 
|  | memcpy(format, This->lpFormat, min(*(DWORD*)formatsize, This->cbFormat)); | 
|  | if (*(DWORD*)formatsize < This->cbFormat) { | 
|  | *formatsize = This->cbFormat; | 
|  | return AVIERR_BUFFERTOOSMALL; | 
|  | } | 
|  |  | 
|  | /* Could format change? When yes will it change? */ | 
|  | if ((This->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) && | 
|  | pos > This->sInfo.dwStart) { | 
|  | LONG lLastFmt; | 
|  |  | 
|  | lLastFmt = IAVIStream_fnFindSample(iface, pos, FIND_FORMAT|FIND_PREV); | 
|  | if (lLastFmt > 0) { | 
|  | FIXME(": need to read formatchange for %d -- unimplemented!\n",lLastFmt); | 
|  | } | 
|  | } | 
|  |  | 
|  | *formatsize = This->cbFormat; | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos, | 
|  | LPVOID format, LONG formatsize) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  |  | 
|  | LPBITMAPINFOHEADER lpbiNew = format; | 
|  |  | 
|  | TRACE("(%p,%d,%p,%d)\n", iface, pos, format, formatsize); | 
|  |  | 
|  | /* check parameters */ | 
|  | if (format == NULL || formatsize <= 0) | 
|  | return AVIERR_BADPARAM; | 
|  |  | 
|  | /* Do we have write permission? */ | 
|  | if ((This->paf->uMode & MMIO_RWMODE) == 0) | 
|  | return AVIERR_READONLY; | 
|  |  | 
|  | /* can only set format before frame is written! */ | 
|  | if (This->lLastFrame > pos) | 
|  | return AVIERR_UNSUPPORTED; | 
|  |  | 
|  | /* initial format or a formatchange? */ | 
|  | if (This->lpFormat == NULL) { | 
|  | /* initial format */ | 
|  | if (This->paf->dwMoviChunkPos != 0) | 
|  | return AVIERR_ERROR; /* user has used API in wrong sequence! */ | 
|  |  | 
|  | This->lpFormat = HeapAlloc(GetProcessHeap(), 0, formatsize); | 
|  | if (This->lpFormat == NULL) | 
|  | return AVIERR_MEMORY; | 
|  | This->cbFormat = formatsize; | 
|  |  | 
|  | memcpy(This->lpFormat, format, formatsize); | 
|  |  | 
|  | /* update some infos about stream */ | 
|  | if (This->sInfo.fccType == streamtypeVIDEO) { | 
|  | LONG lDim; | 
|  |  | 
|  | lDim = This->sInfo.rcFrame.right - This->sInfo.rcFrame.left; | 
|  | if (lDim < lpbiNew->biWidth) | 
|  | This->sInfo.rcFrame.right = This->sInfo.rcFrame.left + lpbiNew->biWidth; | 
|  | lDim = This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top; | 
|  | if (lDim < lpbiNew->biHeight) | 
|  | This->sInfo.rcFrame.bottom = This->sInfo.rcFrame.top + lpbiNew->biHeight; | 
|  | } else if (This->sInfo.fccType == streamtypeAUDIO) | 
|  | This->sInfo.dwSampleSize = ((LPWAVEFORMATEX)This->lpFormat)->nBlockAlign; | 
|  |  | 
|  | return AVIERR_OK; | 
|  | } else { | 
|  | MMCKINFO           ck; | 
|  | LPBITMAPINFOHEADER lpbiOld = This->lpFormat; | 
|  | RGBQUAD           *rgbNew  = (RGBQUAD*)((LPBYTE)lpbiNew + lpbiNew->biSize); | 
|  | AVIPALCHANGE      *lppc = NULL; | 
|  | UINT               n; | 
|  |  | 
|  | /* perhaps format change, check it ... */ | 
|  | if (This->cbFormat != formatsize) | 
|  | return AVIERR_UNSUPPORTED; | 
|  |  | 
|  | /* no format change, only the initial one */ | 
|  | if (memcmp(This->lpFormat, format, formatsize) == 0) | 
|  | return AVIERR_OK; | 
|  |  | 
|  | /* check that's only the palette, which changes */ | 
|  | if (lpbiOld->biSize        != lpbiNew->biSize || | 
|  | lpbiOld->biWidth       != lpbiNew->biWidth || | 
|  | lpbiOld->biHeight      != lpbiNew->biHeight || | 
|  | lpbiOld->biPlanes      != lpbiNew->biPlanes || | 
|  | lpbiOld->biBitCount    != lpbiNew->biBitCount || | 
|  | lpbiOld->biCompression != lpbiNew->biCompression || | 
|  | lpbiOld->biClrUsed     != lpbiNew->biClrUsed) | 
|  | return AVIERR_UNSUPPORTED; | 
|  |  | 
|  | This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES; | 
|  |  | 
|  | /* simply say all colors have changed */ | 
|  | ck.ckid   = MAKEAVICKID(cktypePALchange, This->nStream); | 
|  | ck.cksize = 2 * sizeof(WORD) + lpbiOld->biClrUsed * sizeof(PALETTEENTRY); | 
|  | lppc = HeapAlloc(GetProcessHeap(), 0, ck.cksize); | 
|  | if (lppc == NULL) | 
|  | return AVIERR_MEMORY; | 
|  |  | 
|  | lppc->bFirstEntry = 0; | 
|  | lppc->bNumEntries = (lpbiOld->biClrUsed < 256 ? lpbiOld->biClrUsed : 0); | 
|  | lppc->wFlags      = 0; | 
|  | for (n = 0; n < lpbiOld->biClrUsed; n++) { | 
|  | lppc->peNew[n].peRed   = rgbNew[n].rgbRed; | 
|  | lppc->peNew[n].peGreen = rgbNew[n].rgbGreen; | 
|  | lppc->peNew[n].peBlue  = rgbNew[n].rgbBlue; | 
|  | lppc->peNew[n].peFlags = 0; | 
|  | } | 
|  |  | 
|  | if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1 || | 
|  | mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK || | 
|  | mmioWrite(This->paf->hmmio, (HPSTR)lppc, ck.cksize) != ck.cksize || | 
|  | mmioAscend(This->paf->hmmio, &ck, 0) != S_OK) | 
|  | { | 
|  | HeapFree(GetProcessHeap(), 0, lppc); | 
|  | return AVIERR_FILEWRITE; | 
|  | } | 
|  |  | 
|  | This->paf->dwNextFramePos += ck.cksize + 2 * sizeof(DWORD); | 
|  |  | 
|  | HeapFree(GetProcessHeap(), 0, lppc); | 
|  |  | 
|  | return AVIFILE_AddFrame(This, cktypePALchange, n, ck.dwDataOffset, 0); | 
|  | } | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start, | 
|  | LONG samples, LPVOID buffer, | 
|  | LONG buffersize, LPLONG bytesread, | 
|  | LPLONG samplesread) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  |  | 
|  | DWORD    size; | 
|  | HRESULT  hr; | 
|  |  | 
|  | TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", iface, start, samples, buffer, | 
|  | buffersize, bytesread, samplesread); | 
|  |  | 
|  | /* clear return parameters if given */ | 
|  | if (bytesread != NULL) | 
|  | *bytesread = 0; | 
|  | if (samplesread != NULL) | 
|  | *samplesread = 0; | 
|  |  | 
|  | /* check parameters */ | 
|  | if ((LONG)This->sInfo.dwStart > start) | 
|  | return AVIERR_NODATA; /* couldn't read before start of stream */ | 
|  | if (This->sInfo.dwStart + This->sInfo.dwLength < (DWORD)start) | 
|  | return AVIERR_NODATA; /* start is past end of stream */ | 
|  |  | 
|  | /* should we read as much as possible? */ | 
|  | if (samples == -1) { | 
|  | /* User should know how much we have read */ | 
|  | if (bytesread == NULL && samplesread == NULL) | 
|  | return AVIERR_BADPARAM; | 
|  |  | 
|  | if (This->sInfo.dwSampleSize != 0) | 
|  | samples = buffersize / This->sInfo.dwSampleSize; | 
|  | else | 
|  | samples = 1; | 
|  | } | 
|  |  | 
|  | /* limit to end of stream */ | 
|  | if ((LONG)This->sInfo.dwLength < samples) | 
|  | samples = This->sInfo.dwLength; | 
|  | if ((start - This->sInfo.dwStart) > (This->sInfo.dwLength - samples)) | 
|  | samples = This->sInfo.dwLength - (start - This->sInfo.dwStart); | 
|  |  | 
|  | /* nothing to read? Then leave ... */ | 
|  | if (samples == 0) | 
|  | return AVIERR_OK; | 
|  |  | 
|  | if (This->sInfo.dwSampleSize != 0) { | 
|  | /* fixed samplesize -- we can read over frame/block boundaries */ | 
|  | LONG block = start; | 
|  | LONG offset = 0; | 
|  |  | 
|  | if (!buffer) | 
|  | { | 
|  | if (bytesread) | 
|  | *bytesread = samples*This->sInfo.dwSampleSize; | 
|  | if (samplesread) | 
|  | *samplesread = samples; | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | /* convert start sample to block,offset pair */ | 
|  | AVIFILE_SamplesToBlock(This, &block, &offset); | 
|  |  | 
|  | /* convert samples to bytes */ | 
|  | samples *= This->sInfo.dwSampleSize; | 
|  |  | 
|  | while (samples > 0 && buffersize > 0) { | 
|  | LONG blocksize; | 
|  | if (block != This->dwCurrentFrame) { | 
|  | hr = AVIFILE_ReadBlock(This, block, NULL, 0); | 
|  | if (FAILED(hr)) | 
|  | return hr; | 
|  | } | 
|  |  | 
|  | size = min((DWORD)samples, (DWORD)buffersize); | 
|  | blocksize = This->lpBuffer[1]; | 
|  | TRACE("blocksize = %u\n",blocksize); | 
|  | size = min(size, blocksize - offset); | 
|  | memcpy(buffer, ((BYTE*)&This->lpBuffer[2]) + offset, size); | 
|  |  | 
|  | block++; | 
|  | offset = 0; | 
|  | buffer = ((LPBYTE)buffer)+size; | 
|  | samples    -= size; | 
|  | buffersize -= size; | 
|  |  | 
|  | /* fill out return parameters if given */ | 
|  | if (bytesread != NULL) | 
|  | *bytesread   += size; | 
|  | if (samplesread != NULL) | 
|  | *samplesread += size / This->sInfo.dwSampleSize; | 
|  | } | 
|  |  | 
|  | if (samples == 0) | 
|  | return AVIERR_OK; | 
|  | else | 
|  | return AVIERR_BUFFERTOOSMALL; | 
|  | } else { | 
|  | /* variable samplesize -- we can only read one full frame/block */ | 
|  | if (samples > 1) | 
|  | samples = 1; | 
|  |  | 
|  | assert(start <= This->lLastFrame); | 
|  | size = This->idxFrames[start].dwChunkLength; | 
|  | if (buffer != NULL && buffersize >= size) { | 
|  | hr = AVIFILE_ReadBlock(This, start, buffer, size); | 
|  | if (FAILED(hr)) | 
|  | return hr; | 
|  | } else if (buffer != NULL) | 
|  | return AVIERR_BUFFERTOOSMALL; | 
|  |  | 
|  | /* fill out return parameters if given */ | 
|  | if (bytesread != NULL) | 
|  | *bytesread = size; | 
|  | if (samplesread != NULL) | 
|  | *samplesread = samples; | 
|  |  | 
|  | return AVIERR_OK; | 
|  | } | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start, | 
|  | LONG samples, LPVOID buffer, | 
|  | LONG buffersize, DWORD flags, | 
|  | LPLONG sampwritten, | 
|  | LPLONG byteswritten) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  |  | 
|  | FOURCC  ckid; | 
|  | HRESULT hr; | 
|  |  | 
|  | TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n", iface, start, samples, | 
|  | buffer, buffersize, flags, sampwritten, byteswritten); | 
|  |  | 
|  | /* clear return parameters if given */ | 
|  | if (sampwritten != NULL) | 
|  | *sampwritten = 0; | 
|  | if (byteswritten != NULL) | 
|  | *byteswritten = 0; | 
|  |  | 
|  | /* check parameters */ | 
|  | if (buffer == NULL && (buffersize > 0 || samples > 0)) | 
|  | return AVIERR_BADPARAM; | 
|  |  | 
|  | /* Have we write permission? */ | 
|  | if ((This->paf->uMode & MMIO_RWMODE) == 0) | 
|  | return AVIERR_READONLY; | 
|  |  | 
|  | switch (This->sInfo.fccType) { | 
|  | case streamtypeAUDIO: | 
|  | ckid = MAKEAVICKID(cktypeWAVEbytes, This->nStream); | 
|  | break; | 
|  | default: | 
|  | if ((flags & AVIIF_KEYFRAME) && buffersize != 0) | 
|  | ckid = MAKEAVICKID(cktypeDIBbits, This->nStream); | 
|  | else | 
|  | ckid = MAKEAVICKID(cktypeDIBcompressed, This->nStream); | 
|  | break; | 
|  | }; | 
|  |  | 
|  | /* append to end of stream? */ | 
|  | if (start == -1) { | 
|  | if (This->lLastFrame == -1) | 
|  | start = This->sInfo.dwStart; | 
|  | else | 
|  | start = This->sInfo.dwLength; | 
|  | } else if (This->lLastFrame == -1) | 
|  | This->sInfo.dwStart = start; | 
|  |  | 
|  | if (This->sInfo.dwSampleSize != 0) { | 
|  | /* fixed sample size -- audio like */ | 
|  | if (samples * This->sInfo.dwSampleSize != buffersize) | 
|  | return AVIERR_BADPARAM; | 
|  |  | 
|  | /* Couldn't skip audio-like data -- User must supply appropriate silence */ | 
|  | if (This->sInfo.dwLength != start) | 
|  | return AVIERR_UNSUPPORTED; | 
|  |  | 
|  | /* Convert position to frame/block */ | 
|  | start = This->lLastFrame + 1; | 
|  |  | 
|  | if ((This->paf->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) == 0) { | 
|  | FIXME(": not interleaved, could collect audio data!\n"); | 
|  | } | 
|  | } else { | 
|  | /* variable sample size -- video like */ | 
|  | if (samples > 1) | 
|  | return AVIERR_UNSUPPORTED; | 
|  |  | 
|  | /* must we fill up with empty frames? */ | 
|  | if (This->lLastFrame != -1) { | 
|  | FOURCC ckid2 = MAKEAVICKID(cktypeDIBcompressed, This->nStream); | 
|  |  | 
|  | while (start > This->lLastFrame + 1) { | 
|  | hr = AVIFILE_WriteBlock(This, This->lLastFrame + 1, ckid2, 0, NULL, 0); | 
|  | if (FAILED(hr)) | 
|  | return hr; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* write the block now */ | 
|  | hr = AVIFILE_WriteBlock(This, start, ckid, flags, buffer, buffersize); | 
|  | if (SUCCEEDED(hr)) { | 
|  | /* fill out return parameters if given */ | 
|  | if (sampwritten != NULL) | 
|  | *sampwritten = samples; | 
|  | if (byteswritten != NULL) | 
|  | *byteswritten = buffersize; | 
|  | } | 
|  |  | 
|  | return hr; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start, | 
|  | LONG samples) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  |  | 
|  | FIXME("(%p,%d,%d): stub\n", iface, start, samples); | 
|  |  | 
|  | /* check parameters */ | 
|  | if (start < 0 || samples < 0) | 
|  | return AVIERR_BADPARAM; | 
|  |  | 
|  | /* Delete before start of stream? */ | 
|  | if (start + samples < This->sInfo.dwStart) | 
|  | return AVIERR_OK; | 
|  |  | 
|  | /* Delete after end of stream? */ | 
|  | if (start > This->sInfo.dwLength) | 
|  | return AVIERR_OK; | 
|  |  | 
|  | /* For the rest we need write permissions */ | 
|  | if ((This->paf->uMode & MMIO_RWMODE) == 0) | 
|  | return AVIERR_READONLY; | 
|  |  | 
|  | /* 1. overwrite the data with JUNK | 
|  | * | 
|  | * if ISINTERLEAVED { | 
|  | *   2. concat all neighboured JUNK-blocks in this record to one | 
|  | *   3. if this record only contains JUNK and is at end set dwNextFramePos | 
|  | *      to start of this record, repeat this. | 
|  | * } else { | 
|  | *   2. concat all neighboured JUNK-blocks. | 
|  | *   3. if the JUNK block is at the end, then set dwNextFramePos to | 
|  | *      start of this block. | 
|  | * } | 
|  | */ | 
|  |  | 
|  | return AVIERR_UNSUPPORTED; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc, | 
|  | LPVOID lp, LPLONG lpread) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  |  | 
|  | TRACE("(%p,0x%08X,%p,%p)\n", iface, fcc, lp, lpread); | 
|  |  | 
|  | if (fcc == ckidSTREAMHANDLERDATA) { | 
|  | if (This->lpHandlerData != NULL && This->cbHandlerData > 0) { | 
|  | if (lp == NULL || *lpread <= 0) { | 
|  | *lpread = This->cbHandlerData; | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | memcpy(lp, This->lpHandlerData, min(This->cbHandlerData, *lpread)); | 
|  | if (*lpread < This->cbHandlerData) | 
|  | return AVIERR_BUFFERTOOSMALL; | 
|  | return AVIERR_OK; | 
|  | } else | 
|  | return AVIERR_NODATA; | 
|  | } else | 
|  | return ReadExtraChunk(&This->extra, fcc, lp, lpread); | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc, | 
|  | LPVOID lp, LONG size) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  |  | 
|  | TRACE("(%p,0x%08x,%p,%d)\n", iface, fcc, lp, size); | 
|  |  | 
|  | /* check parameters */ | 
|  | if (lp == NULL) | 
|  | return AVIERR_BADPARAM; | 
|  | if (size <= 0) | 
|  | return AVIERR_BADSIZE; | 
|  |  | 
|  | /* need write permission */ | 
|  | if ((This->paf->uMode & MMIO_RWMODE) == 0) | 
|  | return AVIERR_READONLY; | 
|  |  | 
|  | /* already written something to this file? */ | 
|  | if (This->paf->dwMoviChunkPos != 0) { | 
|  | /* the data will be inserted before the 'movi' chunk, so check for | 
|  | * enough space */ | 
|  | DWORD dwPos = AVIFILE_ComputeMoviStart(This->paf); | 
|  |  | 
|  | /* ckid,size => 2 * sizeof(DWORD) */ | 
|  | dwPos += 2 * sizeof(DWORD) + size; | 
|  | if (size >= This->paf->dwMoviChunkPos - 2 * sizeof(DWORD)) | 
|  | return AVIERR_UNSUPPORTED; /* not enough space left */ | 
|  | } | 
|  |  | 
|  | This->paf->fDirty = TRUE; | 
|  |  | 
|  | if (fcc == ckidSTREAMHANDLERDATA) { | 
|  | if (This->lpHandlerData != NULL) { | 
|  | FIXME(": handler data already set -- overwirte?\n"); | 
|  | return AVIERR_UNSUPPORTED; | 
|  | } | 
|  |  | 
|  | This->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, size); | 
|  | if (This->lpHandlerData == NULL) | 
|  | return AVIERR_MEMORY; | 
|  | This->cbHandlerData = size; | 
|  | memcpy(This->lpHandlerData, lp, size); | 
|  |  | 
|  | return AVIERR_OK; | 
|  | } else | 
|  | return WriteExtraChunk(&This->extra, fcc, lp, size); | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream *iface, | 
|  | LPAVISTREAMINFOW info, LONG infolen) | 
|  | { | 
|  | FIXME("(%p,%p,%d): stub\n", iface, info, infolen); | 
|  |  | 
|  | return E_FAIL; | 
|  | } | 
|  |  | 
|  | /***********************************************************************/ | 
|  |  | 
|  | static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, DWORD offset, DWORD flags) | 
|  | { | 
|  | UINT n; | 
|  |  | 
|  | /* pre-conditions */ | 
|  | assert(This != NULL); | 
|  |  | 
|  | switch (TWOCCFromFOURCC(ckid)) { | 
|  | case cktypeDIBbits: | 
|  | if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE) | 
|  | flags |= AVIIF_KEYFRAME; | 
|  | break; | 
|  | case cktypeDIBcompressed: | 
|  | if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE) | 
|  | flags &= ~AVIIF_KEYFRAME; | 
|  | break; | 
|  | case cktypePALchange: | 
|  | if (This->sInfo.fccType != streamtypeVIDEO) { | 
|  | ERR(": found palette change in non-video stream!\n"); | 
|  | return AVIERR_BADFORMAT; | 
|  | } | 
|  |  | 
|  | if (This->idxFmtChanges == NULL || This->nIdxFmtChanges <= This->sInfo.dwFormatChangeCount) { | 
|  | DWORD new_count = This->nIdxFmtChanges + 16; | 
|  | void *new_buffer; | 
|  |  | 
|  | if (This->idxFmtChanges == NULL) { | 
|  | This->idxFmtChanges = | 
|  | HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * sizeof(AVIINDEXENTRY)); | 
|  | if (!This->idxFmtChanges) return AVIERR_MEMORY; | 
|  | } else { | 
|  | new_buffer = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxFmtChanges, | 
|  | new_count * sizeof(AVIINDEXENTRY)); | 
|  | if (!new_buffer) return AVIERR_MEMORY; | 
|  | This->idxFmtChanges = new_buffer; | 
|  | } | 
|  | This->nIdxFmtChanges = new_count; | 
|  | } | 
|  |  | 
|  | This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES; | 
|  | n = ++This->sInfo.dwFormatChangeCount; | 
|  | This->idxFmtChanges[n].ckid          = This->lLastFrame; | 
|  | This->idxFmtChanges[n].dwFlags       = 0; | 
|  | This->idxFmtChanges[n].dwChunkOffset = offset; | 
|  | This->idxFmtChanges[n].dwChunkLength = size; | 
|  |  | 
|  | return AVIERR_OK; | 
|  | case cktypeWAVEbytes: | 
|  | if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE) | 
|  | flags |= AVIIF_KEYFRAME; | 
|  | break; | 
|  | default: | 
|  | WARN(": unknown TWOCC 0x%04X found\n", TWOCCFromFOURCC(ckid)); | 
|  | break; | 
|  | }; | 
|  |  | 
|  | /* first frame is always a keyframe */ | 
|  | if (This->lLastFrame == -1) | 
|  | flags |= AVIIF_KEYFRAME; | 
|  |  | 
|  | if (This->sInfo.dwSuggestedBufferSize < size) | 
|  | This->sInfo.dwSuggestedBufferSize = size; | 
|  |  | 
|  | /* get memory for index */ | 
|  | if (This->idxFrames == NULL || This->lLastFrame + 1 >= This->nIdxFrames) { | 
|  | This->nIdxFrames += 512; | 
|  | if (This->idxFrames == NULL) | 
|  | This->idxFrames = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->nIdxFrames * sizeof(AVIINDEXENTRY)); | 
|  | else | 
|  | This->idxFrames = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxFrames, | 
|  | This->nIdxFrames * sizeof(AVIINDEXENTRY)); | 
|  | if (This->idxFrames == NULL) | 
|  | return AVIERR_MEMORY; | 
|  | } | 
|  |  | 
|  | This->lLastFrame++; | 
|  | This->idxFrames[This->lLastFrame].ckid          = ckid; | 
|  | This->idxFrames[This->lLastFrame].dwFlags       = flags; | 
|  | This->idxFrames[This->lLastFrame].dwChunkOffset = offset; | 
|  | This->idxFrames[This->lLastFrame].dwChunkLength = size; | 
|  |  | 
|  | /* update AVISTREAMINFO structure if necessary */ | 
|  | if (This->sInfo.dwLength <= This->lLastFrame) | 
|  | This->sInfo.dwLength = This->lLastFrame + 1; | 
|  |  | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This) | 
|  | { | 
|  | /* pre-conditions */ | 
|  | assert(This != NULL && This->ppStreams[0] != NULL); | 
|  |  | 
|  | if (This->idxRecords == NULL || This->cbIdxRecords == 0) { | 
|  | This->cbIdxRecords += 1024 * sizeof(AVIINDEXENTRY); | 
|  | This->idxRecords = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->cbIdxRecords); | 
|  | if (This->idxRecords == NULL) | 
|  | return AVIERR_MEMORY; | 
|  | } | 
|  |  | 
|  | assert(This->nIdxRecords < This->cbIdxRecords/sizeof(AVIINDEXENTRY)); | 
|  |  | 
|  | This->idxRecords[This->nIdxRecords].ckid          = listtypeAVIRECORD; | 
|  | This->idxRecords[This->nIdxRecords].dwFlags       = AVIIF_LIST; | 
|  | This->idxRecords[This->nIdxRecords].dwChunkOffset = | 
|  | This->ckLastRecord.dwDataOffset - 2 * sizeof(DWORD); | 
|  | This->idxRecords[This->nIdxRecords].dwChunkLength = | 
|  | This->ckLastRecord.cksize; | 
|  | This->nIdxRecords++; | 
|  |  | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | static DWORD   AVIFILE_ComputeMoviStart(IAVIFileImpl *This) | 
|  | { | 
|  | DWORD dwPos; | 
|  | DWORD nStream; | 
|  |  | 
|  | /* RIFF,hdrl,movi,avih => (3 * 3 + 2) * sizeof(DWORD) = 11 * sizeof(DWORD) */ | 
|  | dwPos = 11 * sizeof(DWORD) + sizeof(MainAVIHeader); | 
|  |  | 
|  | for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { | 
|  | IAVIStreamImpl *pStream = This->ppStreams[nStream]; | 
|  |  | 
|  | /* strl,strh,strf => (3 + 2 * 2) * sizeof(DWORD) = 7 * sizeof(DWORD) */ | 
|  | dwPos += 7 * sizeof(DWORD) + sizeof(AVIStreamHeader); | 
|  | dwPos += ((pStream->cbFormat + 1) & ~1U); | 
|  | if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0) | 
|  | dwPos += 2 * sizeof(DWORD) + ((pStream->cbHandlerData + 1) & ~1U); | 
|  | if (lstrlenW(pStream->sInfo.szName) > 0) | 
|  | dwPos += 2 * sizeof(DWORD) + ((lstrlenW(pStream->sInfo.szName) + 1) & ~1U); | 
|  | } | 
|  |  | 
|  | if (This->dwMoviChunkPos == 0) { | 
|  | This->dwNextFramePos = dwPos; | 
|  |  | 
|  | /* pad to multiple of AVI_HEADERSIZE only if we are more than 8 bytes away from it */ | 
|  | if (((dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1)) - dwPos > 2 * sizeof(DWORD)) | 
|  | This->dwNextFramePos = (dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1); | 
|  |  | 
|  | This->dwMoviChunkPos = This->dwNextFramePos - sizeof(DWORD); | 
|  | } | 
|  |  | 
|  | return dwPos; | 
|  | } | 
|  |  | 
|  | static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, const AVISTREAMINFOW *asi) | 
|  | { | 
|  | IAVIStreamImpl *pstream; | 
|  |  | 
|  | /* pre-conditions */ | 
|  | assert(paf != NULL); | 
|  | assert(nr < MAX_AVISTREAMS); | 
|  | assert(paf->ppStreams[nr] != NULL); | 
|  |  | 
|  | pstream = paf->ppStreams[nr]; | 
|  |  | 
|  | pstream->lpVtbl         = &iavist; | 
|  | pstream->ref            = 0; | 
|  | pstream->paf            = paf; | 
|  | pstream->nStream        = nr; | 
|  | pstream->dwCurrentFrame = (DWORD)-1; | 
|  | pstream->lLastFrame    = -1; | 
|  |  | 
|  | if (asi != NULL) { | 
|  | memcpy(&pstream->sInfo, asi, sizeof(pstream->sInfo)); | 
|  |  | 
|  | if (asi->dwLength > 0) { | 
|  | /* pre-allocate mem for frame-index structure */ | 
|  | pstream->idxFrames = | 
|  | HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asi->dwLength * sizeof(AVIINDEXENTRY)); | 
|  | if (pstream->idxFrames != NULL) | 
|  | pstream->nIdxFrames = asi->dwLength; | 
|  | } | 
|  | if (asi->dwFormatChangeCount > 0) { | 
|  | /* pre-allocate mem for formatchange-index structure */ | 
|  | pstream->idxFmtChanges = | 
|  | HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asi->dwFormatChangeCount * sizeof(AVIINDEXENTRY)); | 
|  | if (pstream->idxFmtChanges != NULL) | 
|  | pstream->nIdxFmtChanges = asi->dwFormatChangeCount; | 
|  | } | 
|  |  | 
|  | /* These values will be computed */ | 
|  | pstream->sInfo.dwLength              = 0; | 
|  | pstream->sInfo.dwSuggestedBufferSize = 0; | 
|  | pstream->sInfo.dwFormatChangeCount   = 0; | 
|  | pstream->sInfo.dwEditCount           = 1; | 
|  | if (pstream->sInfo.dwSampleSize > 0) | 
|  | SetRectEmpty(&pstream->sInfo.rcFrame); | 
|  | } | 
|  |  | 
|  | pstream->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; | 
|  | } | 
|  |  | 
|  | static void    AVIFILE_DestructAVIStream(IAVIStreamImpl *This) | 
|  | { | 
|  | /* pre-conditions */ | 
|  | assert(This != NULL); | 
|  |  | 
|  | This->dwCurrentFrame = (DWORD)-1; | 
|  | This->lLastFrame    = -1; | 
|  | This->paf = NULL; | 
|  | if (This->idxFrames != NULL) { | 
|  | HeapFree(GetProcessHeap(), 0, This->idxFrames); | 
|  | This->idxFrames  = NULL; | 
|  | This->nIdxFrames = 0; | 
|  | } | 
|  | HeapFree(GetProcessHeap(), 0, This->idxFmtChanges); | 
|  | This->idxFmtChanges = NULL; | 
|  | if (This->lpBuffer != NULL) { | 
|  | HeapFree(GetProcessHeap(), 0, This->lpBuffer); | 
|  | This->lpBuffer = NULL; | 
|  | This->cbBuffer = 0; | 
|  | } | 
|  | if (This->lpHandlerData != NULL) { | 
|  | HeapFree(GetProcessHeap(), 0, This->lpHandlerData); | 
|  | This->lpHandlerData = NULL; | 
|  | This->cbHandlerData = 0; | 
|  | } | 
|  | if (This->extra.lp != NULL) { | 
|  | HeapFree(GetProcessHeap(), 0, This->extra.lp); | 
|  | This->extra.lp = NULL; | 
|  | This->extra.cb = 0; | 
|  | } | 
|  | if (This->lpFormat != NULL) { | 
|  | HeapFree(GetProcessHeap(), 0, This->lpFormat); | 
|  | This->lpFormat = NULL; | 
|  | This->cbFormat = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This) | 
|  | { | 
|  | MainAVIHeader   MainAVIHdr; | 
|  | MMCKINFO        ckRIFF; | 
|  | MMCKINFO        ckLIST1; | 
|  | MMCKINFO        ckLIST2; | 
|  | MMCKINFO        ck; | 
|  | IAVIStreamImpl *pStream; | 
|  | DWORD           nStream; | 
|  | HRESULT         hr; | 
|  |  | 
|  | if (This->hmmio == NULL) | 
|  | return AVIERR_FILEOPEN; | 
|  |  | 
|  | /* initialize stream ptr's */ | 
|  | memset(This->ppStreams, 0, sizeof(This->ppStreams)); | 
|  |  | 
|  | /* try to get "RIFF" chunk -- must not be at beginning of file! */ | 
|  | ckRIFF.fccType = formtypeAVI; | 
|  | if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) { | 
|  | ERR(": not an AVI!\n"); | 
|  | return AVIERR_FILEREAD; | 
|  | } | 
|  |  | 
|  | /* get "LIST" "hdrl" */ | 
|  | ckLIST1.fccType = listtypeAVIHEADER; | 
|  | hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF, MMIO_FINDLIST); | 
|  | if (FAILED(hr)) | 
|  | return hr; | 
|  |  | 
|  | /* get "avih" chunk */ | 
|  | ck.ckid = ckidAVIMAINHDR; | 
|  | hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, MMIO_FINDCHUNK); | 
|  | if (FAILED(hr)) | 
|  | return hr; | 
|  |  | 
|  | if (ck.cksize != sizeof(MainAVIHdr)) { | 
|  | ERR(": invalid size of %d for MainAVIHeader!\n", ck.cksize); | 
|  | return AVIERR_BADFORMAT; | 
|  | } | 
|  | if (mmioRead(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize) | 
|  | return AVIERR_FILEREAD; | 
|  |  | 
|  | /* check for MAX_AVISTREAMS limit */ | 
|  | if (MainAVIHdr.dwStreams > MAX_AVISTREAMS) { | 
|  | WARN("file contains %u streams, but only supports %d -- change MAX_AVISTREAMS!\n", MainAVIHdr.dwStreams, MAX_AVISTREAMS); | 
|  | return AVIERR_UNSUPPORTED; | 
|  | } | 
|  |  | 
|  | /* adjust permissions if copyrighted material in file */ | 
|  | if (MainAVIHdr.dwFlags & AVIFILEINFO_COPYRIGHTED) { | 
|  | This->uMode &= ~MMIO_RWMODE; | 
|  | This->uMode |= MMIO_READ; | 
|  | } | 
|  |  | 
|  | /* convert MainAVIHeader into AVIFILINFOW */ | 
|  | memset(&This->fInfo, 0, sizeof(This->fInfo)); | 
|  | This->fInfo.dwRate                = MainAVIHdr.dwMicroSecPerFrame; | 
|  | This->fInfo.dwScale               = 1000000; | 
|  | This->fInfo.dwMaxBytesPerSec      = MainAVIHdr.dwMaxBytesPerSec; | 
|  | This->fInfo.dwFlags               = MainAVIHdr.dwFlags; | 
|  | This->fInfo.dwCaps                = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; | 
|  | This->fInfo.dwLength              = MainAVIHdr.dwTotalFrames; | 
|  | This->fInfo.dwStreams             = MainAVIHdr.dwStreams; | 
|  | This->fInfo.dwSuggestedBufferSize = 0; | 
|  | This->fInfo.dwWidth               = MainAVIHdr.dwWidth; | 
|  | This->fInfo.dwHeight              = MainAVIHdr.dwHeight; | 
|  | LoadStringW(AVIFILE_hModule, IDS_AVIFILETYPE, This->fInfo.szFileType, | 
|  | sizeof(This->fInfo.szFileType)/sizeof(This->fInfo.szFileType[0])); | 
|  |  | 
|  | /* go back to into header list */ | 
|  | if (mmioAscend(This->hmmio, &ck, 0) != S_OK) | 
|  | return AVIERR_FILEREAD; | 
|  |  | 
|  | /* foreach stream exists a "LIST","strl" chunk */ | 
|  | for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { | 
|  | /* get next nested chunk in this "LIST","strl" */ | 
|  | if (mmioDescend(This->hmmio, &ckLIST2, &ckLIST1, 0) != S_OK) | 
|  | return AVIERR_FILEREAD; | 
|  |  | 
|  | /* nested chunk must be of type "LIST","strl" -- when not normally JUNK */ | 
|  | if (ckLIST2.ckid == FOURCC_LIST && | 
|  | ckLIST2.fccType == listtypeSTREAMHEADER) { | 
|  | pStream = This->ppStreams[nStream] = | 
|  | HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIStreamImpl)); | 
|  | if (pStream == NULL) | 
|  | return AVIERR_MEMORY; | 
|  | AVIFILE_ConstructAVIStream(This, nStream, NULL); | 
|  |  | 
|  | ck.ckid = 0; | 
|  | while (mmioDescend(This->hmmio, &ck, &ckLIST2, 0) == S_OK) { | 
|  | switch (ck.ckid) { | 
|  | case ckidSTREAMHANDLERDATA: | 
|  | if (pStream->lpHandlerData != NULL) | 
|  | return AVIERR_BADFORMAT; | 
|  | pStream->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, ck.cksize); | 
|  | if (pStream->lpHandlerData == NULL) | 
|  | return AVIERR_MEMORY; | 
|  | pStream->cbHandlerData = ck.cksize; | 
|  |  | 
|  | if (mmioRead(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize) | 
|  | return AVIERR_FILEREAD; | 
|  | break; | 
|  | case ckidSTREAMFORMAT: | 
|  | if (pStream->lpFormat != NULL) | 
|  | return AVIERR_BADFORMAT; | 
|  | if (ck.cksize == 0) | 
|  | break; | 
|  |  | 
|  | pStream->lpFormat = HeapAlloc(GetProcessHeap(), 0, ck.cksize); | 
|  | if (pStream->lpFormat == NULL) | 
|  | return AVIERR_MEMORY; | 
|  | pStream->cbFormat = ck.cksize; | 
|  |  | 
|  | if (mmioRead(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize) | 
|  | return AVIERR_FILEREAD; | 
|  |  | 
|  | if (pStream->sInfo.fccType == streamtypeVIDEO) { | 
|  | LPBITMAPINFOHEADER lpbi = pStream->lpFormat; | 
|  |  | 
|  | /* some corrections to the video format */ | 
|  | if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8) | 
|  | lpbi->biClrUsed = 1u << lpbi->biBitCount; | 
|  | if (lpbi->biCompression == BI_RGB && lpbi->biSizeImage == 0) | 
|  | lpbi->biSizeImage = DIBWIDTHBYTES(*lpbi) * lpbi->biHeight; | 
|  | if (lpbi->biCompression != BI_RGB && lpbi->biBitCount == 8) { | 
|  | if (pStream->sInfo.fccHandler == mmioFOURCC('R','L','E','0') || | 
|  | pStream->sInfo.fccHandler == mmioFOURCC('R','L','E',' ')) | 
|  | lpbi->biCompression = BI_RLE8; | 
|  | } | 
|  | if (lpbi->biCompression == BI_RGB && | 
|  | (pStream->sInfo.fccHandler == 0 || | 
|  | pStream->sInfo.fccHandler == mmioFOURCC('N','O','N','E'))) | 
|  | pStream->sInfo.fccHandler = comptypeDIB; | 
|  |  | 
|  | /* init rcFrame if it's empty */ | 
|  | if (IsRectEmpty(&pStream->sInfo.rcFrame)) | 
|  | SetRect(&pStream->sInfo.rcFrame, 0, 0, lpbi->biWidth, lpbi->biHeight); | 
|  | } | 
|  | break; | 
|  | case ckidSTREAMHEADER: | 
|  | { | 
|  | static const WCHAR streamTypeFmt[] = {'%','4','.','4','h','s',0}; | 
|  |  | 
|  | AVIStreamHeader streamHdr; | 
|  | WCHAR           szType[25]; | 
|  | WCHAR           streamNameFmt[25]; | 
|  | UINT            count; | 
|  | LONG            n = ck.cksize; | 
|  |  | 
|  | if (ck.cksize > sizeof(streamHdr)) | 
|  | n = sizeof(streamHdr); | 
|  |  | 
|  | if (mmioRead(This->hmmio, (HPSTR)&streamHdr, n) != n) | 
|  | return AVIERR_FILEREAD; | 
|  |  | 
|  | pStream->sInfo.fccType               = streamHdr.fccType; | 
|  | pStream->sInfo.fccHandler            = streamHdr.fccHandler; | 
|  | pStream->sInfo.dwFlags               = streamHdr.dwFlags; | 
|  | pStream->sInfo.wPriority             = streamHdr.wPriority; | 
|  | pStream->sInfo.wLanguage             = streamHdr.wLanguage; | 
|  | pStream->sInfo.dwInitialFrames       = streamHdr.dwInitialFrames; | 
|  | pStream->sInfo.dwScale               = streamHdr.dwScale; | 
|  | pStream->sInfo.dwRate                = streamHdr.dwRate; | 
|  | pStream->sInfo.dwStart               = streamHdr.dwStart; | 
|  | pStream->sInfo.dwLength              = streamHdr.dwLength; | 
|  | pStream->sInfo.dwSuggestedBufferSize = 0; | 
|  | pStream->sInfo.dwQuality             = streamHdr.dwQuality; | 
|  | pStream->sInfo.dwSampleSize          = streamHdr.dwSampleSize; | 
|  | pStream->sInfo.rcFrame.left          = streamHdr.rcFrame.left; | 
|  | pStream->sInfo.rcFrame.top           = streamHdr.rcFrame.top; | 
|  | pStream->sInfo.rcFrame.right         = streamHdr.rcFrame.right; | 
|  | pStream->sInfo.rcFrame.bottom        = streamHdr.rcFrame.bottom; | 
|  | pStream->sInfo.dwEditCount           = 0; | 
|  | pStream->sInfo.dwFormatChangeCount   = 0; | 
|  |  | 
|  | /* generate description for stream like "filename.avi Type #n" */ | 
|  | if (streamHdr.fccType == streamtypeVIDEO) | 
|  | LoadStringW(AVIFILE_hModule, IDS_VIDEO, szType, sizeof(szType)/sizeof(szType[0])); | 
|  | else if (streamHdr.fccType == streamtypeAUDIO) | 
|  | LoadStringW(AVIFILE_hModule, IDS_AUDIO, szType, sizeof(szType)/sizeof(szType[0])); | 
|  | else | 
|  | wsprintfW(szType, streamTypeFmt, (char*)&streamHdr.fccType); | 
|  |  | 
|  | /* get count of this streamtype up to this stream */ | 
|  | count = 0; | 
|  | for (n = nStream; 0 <= n; n--) { | 
|  | if (This->ppStreams[n]->sInfo.fccHandler == streamHdr.fccType) | 
|  | count++; | 
|  | } | 
|  |  | 
|  | memset(pStream->sInfo.szName, 0, sizeof(pStream->sInfo.szName)); | 
|  |  | 
|  | LoadStringW(AVIFILE_hModule, IDS_AVISTREAMFORMAT, streamNameFmt, sizeof(streamNameFmt)/sizeof(streamNameFmt[0])); | 
|  |  | 
|  | /* FIXME: avoid overflow -- better use wsnprintfW, which doesn't exists ! */ | 
|  | wsprintfW(pStream->sInfo.szName, streamNameFmt, | 
|  | AVIFILE_BasenameW(This->szFileName), szType, count); | 
|  | } | 
|  | break; | 
|  | case ckidSTREAMNAME: | 
|  | { /* streamname will be saved as ASCII string */ | 
|  | LPSTR str = HeapAlloc(GetProcessHeap(), 0, ck.cksize); | 
|  | if (str == NULL) | 
|  | return AVIERR_MEMORY; | 
|  |  | 
|  | if (mmioRead(This->hmmio, str, ck.cksize) != ck.cksize) | 
|  | { | 
|  | HeapFree(GetProcessHeap(), 0, str); | 
|  | return AVIERR_FILEREAD; | 
|  | } | 
|  |  | 
|  | MultiByteToWideChar(CP_ACP, 0, str, -1, pStream->sInfo.szName, | 
|  | sizeof(pStream->sInfo.szName)/sizeof(pStream->sInfo.szName[0])); | 
|  |  | 
|  | HeapFree(GetProcessHeap(), 0, str); | 
|  | } | 
|  | break; | 
|  | case ckidAVIPADDING: | 
|  | case mmioFOURCC('p','a','d','d'): | 
|  | break; | 
|  | default: | 
|  | WARN(": found extra chunk 0x%08X\n", ck.ckid); | 
|  | hr = ReadChunkIntoExtra(&pStream->extra, This->hmmio, &ck); | 
|  | if (FAILED(hr)) | 
|  | return hr; | 
|  | }; | 
|  | if (pStream->lpFormat != NULL && pStream->sInfo.fccType == streamtypeAUDIO) | 
|  | { | 
|  | WAVEFORMATEX *wfx = pStream->lpFormat;          /* wfx->nBlockAlign = wfx->nChannels * wfx->wBitsPerSample / 8; could be added */ | 
|  | pStream->sInfo.dwSampleSize = wfx->nBlockAlign; /* to deal with corrupt wfx->nBlockAlign but Windows doesn't do this */ | 
|  | TRACE("Block size reset to %u, chan=%u bpp=%u\n", wfx->nBlockAlign, wfx->nChannels, wfx->wBitsPerSample); | 
|  | pStream->sInfo.dwScale = 1; | 
|  | pStream->sInfo.dwRate = wfx->nSamplesPerSec; | 
|  | } | 
|  | if (mmioAscend(This->hmmio, &ck, 0) != S_OK) | 
|  | return AVIERR_FILEREAD; | 
|  | } | 
|  | } else { | 
|  | /* nested chunks in "LIST","hdrl" which are not of type "LIST","strl" */ | 
|  | hr = ReadChunkIntoExtra(&This->fileextra, This->hmmio, &ckLIST2); | 
|  | if (FAILED(hr)) | 
|  | return hr; | 
|  | } | 
|  | if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK) | 
|  | return AVIERR_FILEREAD; | 
|  | } | 
|  |  | 
|  | /* read any extra headers in "LIST","hdrl" */ | 
|  | FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, 0); | 
|  | if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK) | 
|  | return AVIERR_FILEREAD; | 
|  |  | 
|  | /* search "LIST","movi" chunk in "RIFF","AVI " */ | 
|  | ckLIST1.fccType = listtypeAVIMOVIE; | 
|  | hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF, | 
|  | MMIO_FINDLIST); | 
|  | if (FAILED(hr)) | 
|  | return hr; | 
|  |  | 
|  | This->dwMoviChunkPos = ckLIST1.dwDataOffset; | 
|  | This->dwIdxChunkPos  = ckLIST1.cksize + ckLIST1.dwDataOffset; | 
|  | if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK) | 
|  | return AVIERR_FILEREAD; | 
|  |  | 
|  | /* try to find an index */ | 
|  | ck.ckid = ckidAVINEWINDEX; | 
|  | hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, | 
|  | &ck, &ckRIFF, MMIO_FINDCHUNK); | 
|  | if (SUCCEEDED(hr) && ck.cksize > 0) { | 
|  | if (FAILED(AVIFILE_LoadIndex(This, ck.cksize, ckLIST1.dwDataOffset))) | 
|  | This->fInfo.dwFlags &= ~AVIFILEINFO_HASINDEX; | 
|  | } | 
|  |  | 
|  | /* when we haven't found an index or it's bad, then build one | 
|  | * by parsing 'movi' chunk */ | 
|  | if ((This->fInfo.dwFlags & AVIFILEINFO_HASINDEX) == 0) { | 
|  | for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) | 
|  | This->ppStreams[nStream]->lLastFrame = -1; | 
|  |  | 
|  | if (mmioSeek(This->hmmio, ckLIST1.dwDataOffset + sizeof(DWORD), SEEK_SET) == -1) { | 
|  | ERR(": Oops, can't seek back to 'movi' chunk!\n"); | 
|  | return AVIERR_FILEREAD; | 
|  | } | 
|  |  | 
|  | /* seek through the 'movi' list until end */ | 
|  | while (mmioDescend(This->hmmio, &ck, &ckLIST1, 0) == S_OK) { | 
|  | if (ck.ckid != FOURCC_LIST) { | 
|  | if (mmioAscend(This->hmmio, &ck, 0) == S_OK) { | 
|  | nStream = StreamFromFOURCC(ck.ckid); | 
|  |  | 
|  | if (nStream > This->fInfo.dwStreams) | 
|  | return AVIERR_BADFORMAT; | 
|  |  | 
|  | AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize, | 
|  | ck.dwDataOffset - 2 * sizeof(DWORD), 0); | 
|  | } else { | 
|  | nStream = StreamFromFOURCC(ck.ckid); | 
|  | WARN(": file seems to be truncated!\n"); | 
|  | if (nStream <= This->fInfo.dwStreams && | 
|  | This->ppStreams[nStream]->sInfo.dwSampleSize > 0) { | 
|  | ck.cksize = mmioSeek(This->hmmio, 0, SEEK_END); | 
|  | if (ck.cksize != -1) { | 
|  | ck.cksize -= ck.dwDataOffset; | 
|  | ck.cksize &= ~(This->ppStreams[nStream]->sInfo.dwSampleSize - 1); | 
|  |  | 
|  | AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize, | 
|  | ck.dwDataOffset - 2 * sizeof(DWORD), 0); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) | 
|  | { | 
|  | DWORD sugbuf =  This->ppStreams[nStream]->sInfo.dwSuggestedBufferSize; | 
|  | if (This->fInfo.dwSuggestedBufferSize < sugbuf) | 
|  | This->fInfo.dwSuggestedBufferSize = sugbuf; | 
|  | } | 
|  |  | 
|  | /* find other chunks */ | 
|  | FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckRIFF, 0); | 
|  |  | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset) | 
|  | { | 
|  | AVIINDEXENTRY *lp; | 
|  | DWORD          pos, n; | 
|  | HRESULT        hr = AVIERR_OK; | 
|  | BOOL           bAbsolute = TRUE; | 
|  |  | 
|  | lp = HeapAlloc(GetProcessHeap(), 0, IDX_PER_BLOCK * sizeof(AVIINDEXENTRY)); | 
|  | if (lp == NULL) | 
|  | return AVIERR_MEMORY; | 
|  |  | 
|  | /* adjust limits for index tables, so that inserting will be faster */ | 
|  | for (n = 0; n < This->fInfo.dwStreams; n++) { | 
|  | IAVIStreamImpl *pStream = This->ppStreams[n]; | 
|  |  | 
|  | pStream->lLastFrame = -1; | 
|  |  | 
|  | if (pStream->idxFrames != NULL) { | 
|  | HeapFree(GetProcessHeap(), 0, pStream->idxFrames); | 
|  | pStream->idxFrames  = NULL; | 
|  | pStream->nIdxFrames = 0; | 
|  | } | 
|  |  | 
|  | if (pStream->sInfo.dwSampleSize != 0) { | 
|  | if (n > 0 && This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) { | 
|  | pStream->nIdxFrames = This->ppStreams[0]->nIdxFrames; | 
|  | } else if (pStream->sInfo.dwSuggestedBufferSize) { | 
|  | pStream->nIdxFrames = | 
|  | pStream->sInfo.dwLength / pStream->sInfo.dwSuggestedBufferSize; | 
|  | } | 
|  | } else | 
|  | pStream->nIdxFrames = pStream->sInfo.dwLength; | 
|  |  | 
|  | pStream->idxFrames = | 
|  | HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pStream->nIdxFrames * sizeof(AVIINDEXENTRY)); | 
|  | if (pStream->idxFrames == NULL && pStream->nIdxFrames > 0) { | 
|  | pStream->nIdxFrames = 0; | 
|  | HeapFree(GetProcessHeap(), 0, lp); | 
|  | return AVIERR_MEMORY; | 
|  | } | 
|  | } | 
|  |  | 
|  | pos = (DWORD)-1; | 
|  | while (size != 0) { | 
|  | LONG read = min(IDX_PER_BLOCK * sizeof(AVIINDEXENTRY), size); | 
|  |  | 
|  | if (mmioRead(This->hmmio, (HPSTR)lp, read) != read) { | 
|  | hr = AVIERR_FILEREAD; | 
|  | break; | 
|  | } | 
|  | size -= read; | 
|  |  | 
|  | if (pos == (DWORD)-1) | 
|  | pos = offset - lp->dwChunkOffset + sizeof(DWORD); | 
|  |  | 
|  | AVIFILE_ParseIndex(This, lp, read / sizeof(AVIINDEXENTRY), | 
|  | pos, &bAbsolute); | 
|  | } | 
|  |  | 
|  | HeapFree(GetProcessHeap(), 0, lp); | 
|  |  | 
|  | /* checking ... */ | 
|  | for (n = 0; n < This->fInfo.dwStreams; n++) { | 
|  | IAVIStreamImpl *pStream = This->ppStreams[n]; | 
|  |  | 
|  | if (pStream->sInfo.dwSampleSize == 0 && | 
|  | pStream->sInfo.dwLength != pStream->lLastFrame+1) | 
|  | ERR("stream %u length mismatch: dwLength=%u found=%d\n", | 
|  | n, pStream->sInfo.dwLength, pStream->lLastFrame); | 
|  | } | 
|  |  | 
|  | return hr; | 
|  | } | 
|  |  | 
|  | static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp, | 
|  | LONG count, DWORD pos, BOOL *bAbsolute) | 
|  | { | 
|  | if (lp == NULL) | 
|  | return AVIERR_BADPARAM; | 
|  |  | 
|  | for (; count > 0; count--, lp++) { | 
|  | WORD nStream = StreamFromFOURCC(lp->ckid); | 
|  |  | 
|  | if (lp->ckid == listtypeAVIRECORD || nStream == 0x7F) | 
|  | continue; /* skip these */ | 
|  |  | 
|  | if (nStream > This->fInfo.dwStreams) | 
|  | return AVIERR_BADFORMAT; | 
|  |  | 
|  | if (*bAbsolute && lp->dwChunkOffset < This->dwMoviChunkPos) | 
|  | *bAbsolute = FALSE; | 
|  |  | 
|  | if (*bAbsolute) | 
|  | lp->dwChunkOffset += sizeof(DWORD); | 
|  | else | 
|  | lp->dwChunkOffset += pos; | 
|  |  | 
|  | if (FAILED(AVIFILE_AddFrame(This->ppStreams[nStream], lp->ckid, lp->dwChunkLength, lp->dwChunkOffset, lp->dwFlags))) | 
|  | return AVIERR_MEMORY; | 
|  | } | 
|  |  | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD pos, | 
|  | LPVOID buffer, DWORD size) | 
|  | { | 
|  | /* pre-conditions */ | 
|  | assert(This != NULL); | 
|  | assert(This->paf != NULL); | 
|  | assert(This->paf->hmmio != NULL); | 
|  | assert(This->sInfo.dwStart <= pos && pos < This->sInfo.dwLength); | 
|  | assert(pos <= This->lLastFrame); | 
|  |  | 
|  | /* should we read as much as block gives us? */ | 
|  | if (size == 0 || size > This->idxFrames[pos].dwChunkLength) | 
|  | size = This->idxFrames[pos].dwChunkLength; | 
|  |  | 
|  | /* read into out own buffer or given one? */ | 
|  | if (buffer == NULL) { | 
|  | /* we also read the chunk */ | 
|  | size += 2 * sizeof(DWORD); | 
|  |  | 
|  | /* check that buffer is big enough -- don't trust dwSuggestedBufferSize */ | 
|  | if (This->lpBuffer == NULL || This->cbBuffer < size) { | 
|  | DWORD maxSize = max(size, This->sInfo.dwSuggestedBufferSize); | 
|  |  | 
|  | if (This->lpBuffer == NULL) { | 
|  | This->lpBuffer = HeapAlloc(GetProcessHeap(), 0, maxSize); | 
|  | if (!This->lpBuffer) return AVIERR_MEMORY; | 
|  | } else { | 
|  | void *new_buffer = HeapReAlloc(GetProcessHeap(), 0, This->lpBuffer, maxSize); | 
|  | if (!new_buffer) return AVIERR_MEMORY; | 
|  | This->lpBuffer = new_buffer; | 
|  | } | 
|  | This->cbBuffer = maxSize; | 
|  | } | 
|  |  | 
|  | /* now read the complete chunk into our buffer */ | 
|  | if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset, SEEK_SET) == -1) | 
|  | return AVIERR_FILEREAD; | 
|  | if (mmioRead(This->paf->hmmio, (HPSTR)This->lpBuffer, size) != size) | 
|  | return AVIERR_FILEREAD; | 
|  |  | 
|  | /* check if it was the correct block which we have read */ | 
|  | if (This->lpBuffer[0] != This->idxFrames[pos].ckid || | 
|  | This->lpBuffer[1] != This->idxFrames[pos].dwChunkLength) { | 
|  | ERR(": block %d not found at 0x%08X\n", pos, This->idxFrames[pos].dwChunkOffset); | 
|  | ERR(": Index says: '%4.4s'(0x%08X) size 0x%08X\n", | 
|  | (char*)&This->idxFrames[pos].ckid, This->idxFrames[pos].ckid, | 
|  | This->idxFrames[pos].dwChunkLength); | 
|  | ERR(": Data  says: '%4.4s'(0x%08X) size 0x%08X\n", | 
|  | (char*)&This->lpBuffer[0], This->lpBuffer[0], This->lpBuffer[1]); | 
|  | return AVIERR_FILEREAD; | 
|  | } | 
|  | } else { | 
|  | if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD), SEEK_SET) == -1) | 
|  | return AVIERR_FILEREAD; | 
|  | if (mmioRead(This->paf->hmmio, buffer, size) != size) | 
|  | return AVIERR_FILEREAD; | 
|  | } | 
|  |  | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | static void AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos, LPLONG offset) | 
|  | { | 
|  | LONG block; | 
|  |  | 
|  | /* pre-conditions */ | 
|  | assert(This != NULL); | 
|  | assert(pos != NULL); | 
|  | assert(offset != NULL); | 
|  | assert(This->sInfo.dwSampleSize != 0); | 
|  | assert(*pos >= This->sInfo.dwStart); | 
|  |  | 
|  | /* convert start sample to start bytes */ | 
|  | (*offset)  = (*pos) - This->sInfo.dwStart; | 
|  | (*offset) *= This->sInfo.dwSampleSize; | 
|  |  | 
|  | /* convert bytes to block number */ | 
|  | for (block = 0; block <= This->lLastFrame; block++) { | 
|  | if (This->idxFrames[block].dwChunkLength <= *offset) | 
|  | (*offset) -= This->idxFrames[block].dwChunkLength; | 
|  | else | 
|  | break; | 
|  | } | 
|  |  | 
|  | *pos = block; | 
|  | } | 
|  |  | 
|  | static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This) | 
|  | { | 
|  | MainAVIHeader   MainAVIHdr; | 
|  | IAVIStreamImpl* pStream; | 
|  | MMCKINFO        ckRIFF; | 
|  | MMCKINFO        ckLIST1; | 
|  | MMCKINFO        ckLIST2; | 
|  | MMCKINFO        ck; | 
|  | DWORD           nStream; | 
|  | DWORD           dwPos; | 
|  | HRESULT         hr; | 
|  |  | 
|  | /* initialize some things */ | 
|  | if (This->dwMoviChunkPos == 0) | 
|  | AVIFILE_ComputeMoviStart(This); | 
|  |  | 
|  | /* written one record to much? */ | 
|  | if (This->ckLastRecord.dwFlags & MMIO_DIRTY) { | 
|  | This->dwNextFramePos -= 3 * sizeof(DWORD); | 
|  | if (This->nIdxRecords > 0) | 
|  | This->nIdxRecords--; | 
|  | } | 
|  |  | 
|  | AVIFILE_UpdateInfo(This); | 
|  |  | 
|  | assert(This->fInfo.dwScale != 0); | 
|  |  | 
|  | memset(&MainAVIHdr, 0, sizeof(MainAVIHdr)); | 
|  | MainAVIHdr.dwMicroSecPerFrame    = MulDiv(This->fInfo.dwRate, 1000000, | 
|  | This->fInfo.dwScale); | 
|  | MainAVIHdr.dwMaxBytesPerSec      = This->fInfo.dwMaxBytesPerSec; | 
|  | MainAVIHdr.dwPaddingGranularity  = AVI_HEADERSIZE; | 
|  | MainAVIHdr.dwFlags               = This->fInfo.dwFlags; | 
|  | MainAVIHdr.dwTotalFrames         = This->fInfo.dwLength; | 
|  | MainAVIHdr.dwInitialFrames       = 0; | 
|  | MainAVIHdr.dwStreams             = This->fInfo.dwStreams; | 
|  | MainAVIHdr.dwSuggestedBufferSize = This->fInfo.dwSuggestedBufferSize; | 
|  | MainAVIHdr.dwWidth               = This->fInfo.dwWidth; | 
|  | MainAVIHdr.dwHeight              = This->fInfo.dwHeight; | 
|  | MainAVIHdr.dwInitialFrames       = This->dwInitialFrames; | 
|  |  | 
|  | /* now begin writing ... */ | 
|  | mmioSeek(This->hmmio, 0, SEEK_SET); | 
|  |  | 
|  | /* RIFF chunk */ | 
|  | ckRIFF.cksize  = 0; | 
|  | ckRIFF.fccType = formtypeAVI; | 
|  | if (mmioCreateChunk(This->hmmio, &ckRIFF, MMIO_CREATERIFF) != S_OK) | 
|  | return AVIERR_FILEWRITE; | 
|  |  | 
|  | /* AVI headerlist */ | 
|  | ckLIST1.cksize  = 0; | 
|  | ckLIST1.fccType = listtypeAVIHEADER; | 
|  | if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK) | 
|  | return AVIERR_FILEWRITE; | 
|  |  | 
|  | /* MainAVIHeader */ | 
|  | ck.ckid    = ckidAVIMAINHDR; | 
|  | ck.cksize  = sizeof(MainAVIHdr); | 
|  | ck.fccType = 0; | 
|  | if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) | 
|  | return AVIERR_FILEWRITE; | 
|  | if (mmioWrite(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize) | 
|  | return AVIERR_FILEWRITE; | 
|  | if (mmioAscend(This->hmmio, &ck, 0) != S_OK) | 
|  | return AVIERR_FILEWRITE; | 
|  |  | 
|  | /* write the headers of each stream into a separate streamheader list */ | 
|  | for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { | 
|  | AVIStreamHeader strHdr; | 
|  |  | 
|  | pStream = This->ppStreams[nStream]; | 
|  |  | 
|  | /* begin the new streamheader list */ | 
|  | ckLIST2.cksize  = 0; | 
|  | ckLIST2.fccType = listtypeSTREAMHEADER; | 
|  | if (mmioCreateChunk(This->hmmio, &ckLIST2, MMIO_CREATELIST) != S_OK) | 
|  | return AVIERR_FILEWRITE; | 
|  |  | 
|  | /* create an AVIStreamHeader from the AVSTREAMINFO */ | 
|  | strHdr.fccType               = pStream->sInfo.fccType; | 
|  | strHdr.fccHandler            = pStream->sInfo.fccHandler; | 
|  | strHdr.dwFlags               = pStream->sInfo.dwFlags; | 
|  | strHdr.wPriority             = pStream->sInfo.wPriority; | 
|  | strHdr.wLanguage             = pStream->sInfo.wLanguage; | 
|  | strHdr.dwInitialFrames       = pStream->sInfo.dwInitialFrames; | 
|  | strHdr.dwScale               = pStream->sInfo.dwScale; | 
|  | strHdr.dwRate                = pStream->sInfo.dwRate; | 
|  | strHdr.dwStart               = pStream->sInfo.dwStart; | 
|  | strHdr.dwLength              = pStream->sInfo.dwLength; | 
|  | strHdr.dwSuggestedBufferSize = pStream->sInfo.dwSuggestedBufferSize; | 
|  | strHdr.dwQuality             = pStream->sInfo.dwQuality; | 
|  | strHdr.dwSampleSize          = pStream->sInfo.dwSampleSize; | 
|  | strHdr.rcFrame.left          = pStream->sInfo.rcFrame.left; | 
|  | strHdr.rcFrame.top           = pStream->sInfo.rcFrame.top; | 
|  | strHdr.rcFrame.right         = pStream->sInfo.rcFrame.right; | 
|  | strHdr.rcFrame.bottom        = pStream->sInfo.rcFrame.bottom; | 
|  |  | 
|  | /* now write the AVIStreamHeader */ | 
|  | ck.ckid   = ckidSTREAMHEADER; | 
|  | ck.cksize = sizeof(strHdr); | 
|  | if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) | 
|  | return AVIERR_FILEWRITE; | 
|  | if (mmioWrite(This->hmmio, (HPSTR)&strHdr, ck.cksize) != ck.cksize) | 
|  | return AVIERR_FILEWRITE; | 
|  | if (mmioAscend(This->hmmio, &ck, 0) != S_OK) | 
|  | return AVIERR_FILEWRITE; | 
|  |  | 
|  | /* ... the hopefully ever present streamformat ... */ | 
|  | ck.ckid   = ckidSTREAMFORMAT; | 
|  | ck.cksize = pStream->cbFormat; | 
|  | if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) | 
|  | return AVIERR_FILEWRITE; | 
|  | if (pStream->lpFormat != NULL && ck.cksize > 0) { | 
|  | if (mmioWrite(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize) | 
|  | return AVIERR_FILEWRITE; | 
|  | } | 
|  | if (mmioAscend(This->hmmio, &ck, 0) != S_OK) | 
|  | return AVIERR_FILEWRITE; | 
|  |  | 
|  | /* ... some optional existing handler data ... */ | 
|  | if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0) { | 
|  | ck.ckid   = ckidSTREAMHANDLERDATA; | 
|  | ck.cksize = pStream->cbHandlerData; | 
|  | if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) | 
|  | return AVIERR_FILEWRITE; | 
|  | if (mmioWrite(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize) | 
|  | return AVIERR_FILEWRITE; | 
|  | if (mmioAscend(This->hmmio, &ck, 0) != S_OK) | 
|  | return AVIERR_FILEWRITE; | 
|  | } | 
|  |  | 
|  | /* ... some optional additional extra chunk for this stream ... */ | 
|  | if (pStream->extra.lp != NULL && pStream->extra.cb > 0) { | 
|  | /* the chunk header(s) are already in the structure */ | 
|  | if (mmioWrite(This->hmmio, pStream->extra.lp, pStream->extra.cb) != pStream->extra.cb) | 
|  | return AVIERR_FILEWRITE; | 
|  | } | 
|  |  | 
|  | /* ... an optional name for this stream ... */ | 
|  | if (lstrlenW(pStream->sInfo.szName) > 0) { | 
|  | LPSTR str; | 
|  |  | 
|  | ck.ckid   = ckidSTREAMNAME; | 
|  | ck.cksize = lstrlenW(pStream->sInfo.szName) + 1; | 
|  | if (ck.cksize & 1) /* align */ | 
|  | ck.cksize++; | 
|  | if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) | 
|  | return AVIERR_FILEWRITE; | 
|  |  | 
|  | /* the streamname must be saved in ASCII not Unicode */ | 
|  | str = HeapAlloc(GetProcessHeap(), 0, ck.cksize); | 
|  | if (str == NULL) | 
|  | return AVIERR_MEMORY; | 
|  | WideCharToMultiByte(CP_ACP, 0, pStream->sInfo.szName, -1, str, | 
|  | ck.cksize, NULL, NULL); | 
|  |  | 
|  | if (mmioWrite(This->hmmio, str, ck.cksize) != ck.cksize) { | 
|  | HeapFree(GetProcessHeap(), 0, str); | 
|  | return AVIERR_FILEWRITE; | 
|  | } | 
|  |  | 
|  | HeapFree(GetProcessHeap(), 0, str); | 
|  | if (mmioAscend(This->hmmio, &ck, 0) != S_OK) | 
|  | return AVIERR_FILEWRITE; | 
|  | } | 
|  |  | 
|  | /* close streamheader list for this stream */ | 
|  | if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK) | 
|  | return AVIERR_FILEWRITE; | 
|  | } /* for (0 <= nStream < MainAVIHdr.dwStreams) */ | 
|  |  | 
|  | /* close the aviheader list */ | 
|  | if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK) | 
|  | return AVIERR_FILEWRITE; | 
|  |  | 
|  | /* check for padding to pre-guessed 'movi'-chunk position */ | 
|  | dwPos = ckLIST1.dwDataOffset + ckLIST1.cksize; | 
|  | if (This->dwMoviChunkPos - 2 * sizeof(DWORD) > dwPos) { | 
|  | ck.ckid   = ckidAVIPADDING; | 
|  | ck.cksize = This->dwMoviChunkPos - dwPos - 4 * sizeof(DWORD); | 
|  | assert((LONG)ck.cksize >= 0); | 
|  |  | 
|  | if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) | 
|  | return AVIERR_FILEWRITE; | 
|  | if (mmioSeek(This->hmmio, ck.cksize, SEEK_CUR) == -1) | 
|  | return AVIERR_FILEWRITE; | 
|  | if (mmioAscend(This->hmmio, &ck, 0) != S_OK) | 
|  | return AVIERR_FILEWRITE; | 
|  | } | 
|  |  | 
|  | /* now write the 'movi' chunk */ | 
|  | mmioSeek(This->hmmio, This->dwMoviChunkPos - 2 * sizeof(DWORD), SEEK_SET); | 
|  | ckLIST1.cksize  = 0; | 
|  | ckLIST1.fccType = listtypeAVIMOVIE; | 
|  | if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK) | 
|  | return AVIERR_FILEWRITE; | 
|  | if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1) | 
|  | return AVIERR_FILEWRITE; | 
|  | if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK) | 
|  | return AVIERR_FILEWRITE; | 
|  |  | 
|  | /* write 'idx1' chunk */ | 
|  | hr = AVIFILE_SaveIndex(This); | 
|  | if (FAILED(hr)) | 
|  | return hr; | 
|  |  | 
|  | /* write optional extra file chunks */ | 
|  | if (This->fileextra.lp != NULL && This->fileextra.cb > 0) { | 
|  | /* as for the streams, are the chunk header(s) in the structure */ | 
|  | if (mmioWrite(This->hmmio, This->fileextra.lp, This->fileextra.cb) != This->fileextra.cb) | 
|  | return AVIERR_FILEWRITE; | 
|  | } | 
|  |  | 
|  | /* close RIFF chunk */ | 
|  | if (mmioAscend(This->hmmio, &ckRIFF, 0) != S_OK) | 
|  | return AVIERR_FILEWRITE; | 
|  |  | 
|  | /* add some JUNK at end for bad parsers */ | 
|  | memset(&ckRIFF, 0, sizeof(ckRIFF)); | 
|  | mmioWrite(This->hmmio, (HPSTR)&ckRIFF, sizeof(ckRIFF)); | 
|  | mmioFlush(This->hmmio, 0); | 
|  |  | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This) | 
|  | { | 
|  | IAVIStreamImpl *pStream; | 
|  | AVIINDEXENTRY   idx; | 
|  | MMCKINFO        ck; | 
|  | DWORD           nStream; | 
|  | LONG            n; | 
|  |  | 
|  | ck.ckid   = ckidAVINEWINDEX; | 
|  | ck.cksize = 0; | 
|  | if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) | 
|  | return AVIERR_FILEWRITE; | 
|  |  | 
|  | if (This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) { | 
|  | /* is interleaved -- write block of corresponding frames */ | 
|  | LONG lInitialFrames = 0; | 
|  | LONG stepsize; | 
|  | LONG i; | 
|  |  | 
|  | if (This->ppStreams[0]->sInfo.dwSampleSize == 0) | 
|  | stepsize = 1; | 
|  | else | 
|  | stepsize = AVIStreamTimeToSample((PAVISTREAM)This->ppStreams[0], 1000000); | 
|  |  | 
|  | assert(stepsize > 0); | 
|  |  | 
|  | for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { | 
|  | if (lInitialFrames < This->ppStreams[nStream]->sInfo.dwInitialFrames) | 
|  | lInitialFrames = This->ppStreams[nStream]->sInfo.dwInitialFrames; | 
|  | } | 
|  |  | 
|  | for (i = -lInitialFrames; i < (LONG)This->fInfo.dwLength - lInitialFrames; | 
|  | i += stepsize) { | 
|  | DWORD nFrame = lInitialFrames + i; | 
|  |  | 
|  | assert(nFrame < This->nIdxRecords); | 
|  |  | 
|  | idx.ckid          = listtypeAVIRECORD; | 
|  | idx.dwFlags       = AVIIF_LIST; | 
|  | idx.dwChunkLength = This->idxRecords[nFrame].dwChunkLength; | 
|  | idx.dwChunkOffset = This->idxRecords[nFrame].dwChunkOffset | 
|  | - This->dwMoviChunkPos; | 
|  | if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) | 
|  | return AVIERR_FILEWRITE; | 
|  |  | 
|  | for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { | 
|  | pStream = This->ppStreams[nStream]; | 
|  |  | 
|  | /* heave we reached start of this stream? */ | 
|  | if (-(LONG)pStream->sInfo.dwInitialFrames > i) | 
|  | continue; | 
|  |  | 
|  | if (pStream->sInfo.dwInitialFrames < lInitialFrames) | 
|  | nFrame -= (lInitialFrames - pStream->sInfo.dwInitialFrames); | 
|  |  | 
|  | /* reached end of this stream? */ | 
|  | if (pStream->lLastFrame <= nFrame) | 
|  | continue; | 
|  |  | 
|  | if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) && | 
|  | pStream->sInfo.dwFormatChangeCount != 0 && | 
|  | pStream->idxFmtChanges != NULL) { | 
|  | DWORD pos; | 
|  |  | 
|  | for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) { | 
|  | if (pStream->idxFmtChanges[pos].ckid == nFrame) { | 
|  | idx.dwFlags = AVIIF_NOTIME; | 
|  | idx.ckid    = MAKEAVICKID(cktypePALchange, pStream->nStream); | 
|  | idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength; | 
|  | idx.dwChunkOffset = pStream->idxFmtChanges[pos].dwChunkOffset | 
|  | - This->dwMoviChunkPos; | 
|  |  | 
|  | if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) | 
|  | return AVIERR_FILEWRITE; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } /* if have formatchanges */ | 
|  |  | 
|  | idx.ckid          = pStream->idxFrames[nFrame].ckid; | 
|  | idx.dwFlags       = pStream->idxFrames[nFrame].dwFlags; | 
|  | idx.dwChunkLength = pStream->idxFrames[nFrame].dwChunkLength; | 
|  | idx.dwChunkOffset = pStream->idxFrames[nFrame].dwChunkOffset | 
|  | - This->dwMoviChunkPos; | 
|  | if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) | 
|  | return AVIERR_FILEWRITE; | 
|  | } | 
|  | } | 
|  | } else { | 
|  | /* not interleaved -- write index for each stream at once */ | 
|  | for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { | 
|  | pStream = This->ppStreams[nStream]; | 
|  |  | 
|  | for (n = 0; n <= pStream->lLastFrame; n++) { | 
|  | if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) && | 
|  | (pStream->sInfo.dwFormatChangeCount != 0)) { | 
|  | DWORD pos; | 
|  |  | 
|  | for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) { | 
|  | if (pStream->idxFmtChanges[pos].ckid == n) { | 
|  | idx.dwFlags = AVIIF_NOTIME; | 
|  | idx.ckid    = MAKEAVICKID(cktypePALchange, pStream->nStream); | 
|  | idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength; | 
|  | idx.dwChunkOffset = | 
|  | pStream->idxFmtChanges[pos].dwChunkOffset - This->dwMoviChunkPos; | 
|  | if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) | 
|  | return AVIERR_FILEWRITE; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } /* if have formatchanges */ | 
|  |  | 
|  | idx.ckid          = pStream->idxFrames[n].ckid; | 
|  | idx.dwFlags       = pStream->idxFrames[n].dwFlags; | 
|  | idx.dwChunkLength = pStream->idxFrames[n].dwChunkLength; | 
|  | idx.dwChunkOffset = pStream->idxFrames[n].dwChunkOffset | 
|  | - This->dwMoviChunkPos; | 
|  |  | 
|  | if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) | 
|  | return AVIERR_FILEWRITE; | 
|  | } | 
|  | } | 
|  | } /* if not interleaved */ | 
|  |  | 
|  | if (mmioAscend(This->hmmio, &ck, 0) != S_OK) | 
|  | return AVIERR_FILEWRITE; | 
|  |  | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | static ULONG  AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fcc, LONG lSkip) | 
|  | { | 
|  | UINT i; | 
|  | UINT nStream; | 
|  |  | 
|  | /* pre-condition */ | 
|  | assert(lSkip >= 0); | 
|  |  | 
|  | if (fcc != 0) { | 
|  | /* search the number of the specified stream */ | 
|  | nStream = (ULONG)-1; | 
|  | for (i = 0; i < This->fInfo.dwStreams; i++) { | 
|  | assert(This->ppStreams[i] != NULL); | 
|  |  | 
|  | if (This->ppStreams[i]->sInfo.fccType == fcc) { | 
|  | if (lSkip == 0) { | 
|  | nStream = i; | 
|  | break; | 
|  | } else | 
|  | lSkip--; | 
|  | } | 
|  | } | 
|  | } else | 
|  | nStream = lSkip; | 
|  |  | 
|  | return nStream; | 
|  | } | 
|  |  | 
|  | static void    AVIFILE_UpdateInfo(IAVIFileImpl *This) | 
|  | { | 
|  | UINT i; | 
|  |  | 
|  | /* pre-conditions */ | 
|  | assert(This != NULL); | 
|  |  | 
|  | This->fInfo.dwMaxBytesPerSec      = 0; | 
|  | This->fInfo.dwCaps                = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; | 
|  | This->fInfo.dwSuggestedBufferSize = 0; | 
|  | This->fInfo.dwWidth               = 0; | 
|  | This->fInfo.dwHeight              = 0; | 
|  | This->fInfo.dwScale               = 0; | 
|  | This->fInfo.dwRate                = 0; | 
|  | This->fInfo.dwLength              = 0; | 
|  | This->dwInitialFrames             = 0; | 
|  |  | 
|  | for (i = 0; i < This->fInfo.dwStreams; i++) { | 
|  | AVISTREAMINFOW *psi; | 
|  | DWORD           n; | 
|  |  | 
|  | /* pre-conditions */ | 
|  | assert(This->ppStreams[i] != NULL); | 
|  |  | 
|  | psi = &This->ppStreams[i]->sInfo; | 
|  | assert(psi->dwScale != 0); | 
|  | assert(psi->dwRate != 0); | 
|  |  | 
|  | if (i == 0) { | 
|  | /* use first stream timings as base */ | 
|  | This->fInfo.dwScale  = psi->dwScale; | 
|  | This->fInfo.dwRate   = psi->dwRate; | 
|  | This->fInfo.dwLength = psi->dwLength; | 
|  | } else { | 
|  | n = AVIStreamSampleToSample((PAVISTREAM)This->ppStreams[0], | 
|  | (PAVISTREAM)This->ppStreams[i],psi->dwLength); | 
|  | if (This->fInfo.dwLength < n) | 
|  | This->fInfo.dwLength = n; | 
|  | } | 
|  |  | 
|  | if (This->dwInitialFrames < psi->dwInitialFrames) | 
|  | This->dwInitialFrames = psi->dwInitialFrames; | 
|  |  | 
|  | if (This->fInfo.dwSuggestedBufferSize < psi->dwSuggestedBufferSize) | 
|  | This->fInfo.dwSuggestedBufferSize = psi->dwSuggestedBufferSize; | 
|  |  | 
|  | if (psi->dwSampleSize != 0) { | 
|  | /* fixed sample size -- exact computation */ | 
|  | This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSampleSize, psi->dwRate, | 
|  | psi->dwScale); | 
|  | } else { | 
|  | /* variable sample size -- only upper limit */ | 
|  | This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSuggestedBufferSize, | 
|  | psi->dwRate, psi->dwScale); | 
|  |  | 
|  | /* update dimensions */ | 
|  | n = psi->rcFrame.right - psi->rcFrame.left; | 
|  | if (This->fInfo.dwWidth < n) | 
|  | This->fInfo.dwWidth = n; | 
|  | n = psi->rcFrame.bottom - psi->rcFrame.top; | 
|  | if (This->fInfo.dwHeight < n) | 
|  | This->fInfo.dwHeight = n; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block, | 
|  | FOURCC ckid, DWORD flags, LPCVOID buffer, | 
|  | LONG size) | 
|  | { | 
|  | MMCKINFO ck; | 
|  |  | 
|  | ck.ckid    = ckid; | 
|  | ck.cksize  = size; | 
|  | ck.fccType = 0; | 
|  |  | 
|  | /* if no frame/block is already written, we must compute start of movi chunk */ | 
|  | if (This->paf->dwMoviChunkPos == 0) | 
|  | AVIFILE_ComputeMoviStart(This->paf); | 
|  |  | 
|  | if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1) | 
|  | return AVIERR_FILEWRITE; | 
|  |  | 
|  | if (mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK) | 
|  | return AVIERR_FILEWRITE; | 
|  | if (buffer != NULL && size > 0) { | 
|  | if (mmioWrite(This->paf->hmmio, buffer, size) != size) | 
|  | return AVIERR_FILEWRITE; | 
|  | } | 
|  | if (mmioAscend(This->paf->hmmio, &ck, 0) != S_OK) | 
|  | return AVIERR_FILEWRITE; | 
|  |  | 
|  | This->paf->fDirty         = TRUE; | 
|  | This->paf->dwNextFramePos = mmioSeek(This->paf->hmmio, 0, SEEK_CUR); | 
|  |  | 
|  | return AVIFILE_AddFrame(This, ckid, size, | 
|  | ck.dwDataOffset - 2 * sizeof(DWORD), flags); | 
|  | } |