ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

avifile.c
Go to the documentation of this file.
00001 /*
00002  * Copyright 1999 Marcus Meissner
00003  * Copyright 2002-2003 Michael Günnewig
00004  *
00005  * This library is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU Lesser General Public
00007  * License as published by the Free Software Foundation; either
00008  * version 2.1 of the License, or (at your option) any later version.
00009  *
00010  * This library is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  * Lesser General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU Lesser General Public
00016  * License along with this library; if not, write to the Free Software
00017  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00018  */
00019 
00020 /* TODO:
00021  *  - IAVIStreaming interface is missing for the IAVIStreamImpl
00022  *  - IAVIStream_fnFindSample: FIND_INDEX isn't supported.
00023  *  - IAVIStream_fnReadFormat: formatchanges aren't read in.
00024  *  - IAVIStream_fnDelete: a stub.
00025  *  - IAVIStream_fnSetInfo: a stub.
00026  *  - make thread safe
00027  *
00028  * KNOWN Bugs:
00029  *  - native version can hangup when reading a file generated with this DLL.
00030  *    When index is missing it works, but index seems to be okay.
00031  */
00032 
00033 #include <assert.h>
00034 #include <stdarg.h>
00035 
00036 #include "windef.h"
00037 #include "winbase.h"
00038 #include "wingdi.h"
00039 #include "winuser.h"
00040 #include "winnls.h"
00041 #include "winerror.h"
00042 #include "mmsystem.h"
00043 #include "vfw.h"
00044 
00045 #include "avifile_private.h"
00046 #include "extrachunk.h"
00047 
00048 #include "wine/unicode.h"
00049 #include "wine/debug.h"
00050 
00051 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
00052 
00053 #ifndef IDX_PER_BLOCK
00054 #define IDX_PER_BLOCK 2730
00055 #endif
00056 
00057 /***********************************************************************/
00058 
00059 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile* iface,REFIID refiid,LPVOID *obj);
00060 static ULONG   WINAPI IAVIFile_fnAddRef(IAVIFile* iface);
00061 static ULONG   WINAPI IAVIFile_fnRelease(IAVIFile* iface);
00062 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile*iface,AVIFILEINFOW*afi,LONG size);
00063 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile*iface,PAVISTREAM*avis,DWORD fccType,LONG lParam);
00064 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile*iface,PAVISTREAM*avis,AVISTREAMINFOW*asi);
00065 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG size);
00066 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG *size);
00067 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile*iface);
00068 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile*iface,DWORD fccType,LONG lParam);
00069 
00070 static const struct IAVIFileVtbl iavift = {
00071   IAVIFile_fnQueryInterface,
00072   IAVIFile_fnAddRef,
00073   IAVIFile_fnRelease,
00074   IAVIFile_fnInfo,
00075   IAVIFile_fnGetStream,
00076   IAVIFile_fnCreateStream,
00077   IAVIFile_fnWriteData,
00078   IAVIFile_fnReadData,
00079   IAVIFile_fnEndRecord,
00080   IAVIFile_fnDeleteStream
00081 };
00082 
00083 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile*iface,REFIID refiid,LPVOID*obj);
00084 static ULONG   WINAPI IPersistFile_fnAddRef(IPersistFile*iface);
00085 static ULONG   WINAPI IPersistFile_fnRelease(IPersistFile*iface);
00086 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile*iface,CLSID*pClassID);
00087 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile*iface);
00088 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile*iface,LPCOLESTR pszFileName,DWORD dwMode);
00089 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile*iface,LPCOLESTR pszFileName,BOOL fRemember);
00090 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile*iface,LPCOLESTR pszFileName);
00091 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile*iface,LPOLESTR*ppszFileName);
00092 
00093 static const struct IPersistFileVtbl ipersistft = {
00094   IPersistFile_fnQueryInterface,
00095   IPersistFile_fnAddRef,
00096   IPersistFile_fnRelease,
00097   IPersistFile_fnGetClassID,
00098   IPersistFile_fnIsDirty,
00099   IPersistFile_fnLoad,
00100   IPersistFile_fnSave,
00101   IPersistFile_fnSaveCompleted,
00102   IPersistFile_fnGetCurFile
00103 };
00104 
00105 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj);
00106 static ULONG   WINAPI IAVIStream_fnAddRef(IAVIStream*iface);
00107 static ULONG   WINAPI IAVIStream_fnRelease(IAVIStream* iface);
00108 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2);
00109 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size);
00110 static LONG    WINAPI IAVIStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags);
00111 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize);
00112 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize);
00113 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread);
00114 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten);
00115 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream*iface,LONG start,LONG samples);
00116 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread);
00117 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size);
00118 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen);
00119 
00120 static const struct IAVIStreamVtbl iavist = {
00121   IAVIStream_fnQueryInterface,
00122   IAVIStream_fnAddRef,
00123   IAVIStream_fnRelease,
00124   IAVIStream_fnCreate,
00125   IAVIStream_fnInfo,
00126   IAVIStream_fnFindSample,
00127   IAVIStream_fnReadFormat,
00128   IAVIStream_fnSetFormat,
00129   IAVIStream_fnRead,
00130   IAVIStream_fnWrite,
00131   IAVIStream_fnDelete,
00132   IAVIStream_fnReadData,
00133   IAVIStream_fnWriteData,
00134   IAVIStream_fnSetInfo
00135 };
00136 
00137 typedef struct _IAVIFileImpl IAVIFileImpl;
00138 
00139 typedef struct _IPersistFileImpl {
00140   /* IUnknown stuff */
00141   const IPersistFileVtbl *lpVtbl;
00142 
00143   /* IPersistFile stuff */
00144   IAVIFileImpl     *paf;
00145 } IPersistFileImpl;
00146 
00147 typedef struct _IAVIStreamImpl {
00148   /* IUnknown stuff */
00149   const IAVIStreamVtbl *lpVtbl;
00150   LONG          ref;
00151 
00152   /* IAVIStream stuff */
00153   IAVIFileImpl     *paf;
00154   DWORD             nStream;       /* the n-th stream in file */
00155   AVISTREAMINFOW    sInfo;
00156 
00157   LPVOID            lpFormat;
00158   DWORD             cbFormat;
00159 
00160   LPVOID            lpHandlerData;
00161   DWORD             cbHandlerData;
00162 
00163   EXTRACHUNKS       extra;
00164 
00165   LPDWORD           lpBuffer;
00166   DWORD             cbBuffer;       /* size of lpBuffer */
00167   DWORD             dwCurrentFrame; /* frame/block currently in lpBuffer */
00168 
00169   LONG              lLastFrame;    /* last correct index in idxFrames */
00170   AVIINDEXENTRY    *idxFrames;
00171   DWORD             nIdxFrames;     /* upper index limit of idxFrames */
00172   AVIINDEXENTRY    *idxFmtChanges;
00173   DWORD             nIdxFmtChanges; /* upper index limit of idxFmtChanges */
00174 } IAVIStreamImpl;
00175 
00176 struct _IAVIFileImpl {
00177   /* IUnknown stuff */
00178   const IAVIFileVtbl     *lpVtbl;
00179   LONG          ref;
00180 
00181   /* IAVIFile stuff... */
00182   IPersistFileImpl  iPersistFile;
00183 
00184   AVIFILEINFOW      fInfo;
00185   IAVIStreamImpl   *ppStreams[MAX_AVISTREAMS];
00186 
00187   EXTRACHUNKS       fileextra;
00188 
00189   DWORD             dwMoviChunkPos;  /* some stuff for saving ... */
00190   DWORD             dwIdxChunkPos;
00191   DWORD             dwNextFramePos;
00192   DWORD             dwInitialFrames;
00193 
00194   MMCKINFO          ckLastRecord;
00195   AVIINDEXENTRY    *idxRecords;      /* won't be updated while loading */
00196   DWORD             nIdxRecords;     /* current fill level */
00197   DWORD             cbIdxRecords;    /* size of idxRecords */
00198 
00199   /* IPersistFile stuff ... */
00200   HMMIO             hmmio;
00201   LPWSTR            szFileName;
00202   UINT              uMode;
00203   BOOL              fDirty;
00204 };
00205 
00206 /***********************************************************************/
00207 
00208 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size,
00209                 DWORD offset, DWORD flags);
00210 static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This);
00211 static DWORD   AVIFILE_ComputeMoviStart(IAVIFileImpl *This);
00212 static void    AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr,
00213                       const AVISTREAMINFOW *asi);
00214 static void    AVIFILE_DestructAVIStream(IAVIStreamImpl *This);
00215 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This);
00216 static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset);
00217 static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp,
00218                   LONG count, DWORD pos, BOOL *bAbsolute);
00219 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD start,
00220                  LPVOID buffer, DWORD size);
00221 static void    AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos,
00222                       LPLONG offset);
00223 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This);
00224 static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This);
00225 static ULONG   AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fccType,
00226                     LONG lSkip);
00227 static void    AVIFILE_UpdateInfo(IAVIFileImpl *This);
00228 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
00229                   FOURCC ckid, DWORD flags, LPCVOID buffer,
00230                   LONG size);
00231 
00232 HRESULT AVIFILE_CreateAVIFile(REFIID riid, LPVOID *ppv)
00233 {
00234   IAVIFileImpl *pfile;
00235   HRESULT       hr;
00236 
00237   assert(riid != NULL && ppv != NULL);
00238 
00239   *ppv = NULL;
00240 
00241   pfile = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIFileImpl));
00242   if (pfile == NULL)
00243     return AVIERR_MEMORY;
00244 
00245   pfile->lpVtbl = &iavift;
00246   pfile->ref = 0;
00247   pfile->iPersistFile.lpVtbl = &ipersistft;
00248   pfile->iPersistFile.paf = pfile;
00249 
00250   hr = IAVIFile_QueryInterface((IAVIFile*)pfile, riid, ppv);
00251   if (FAILED(hr))
00252     HeapFree(GetProcessHeap(), 0, pfile);
00253 
00254   return hr;
00255 }
00256 
00257 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID refiid,
00258                         LPVOID *obj)
00259 {
00260   IAVIFileImpl *This = (IAVIFileImpl *)iface;
00261 
00262   TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj);
00263 
00264   if (IsEqualGUID(&IID_IUnknown, refiid) ||
00265       IsEqualGUID(&IID_IAVIFile, refiid)) {
00266     *obj = iface;
00267     IAVIFile_AddRef(iface);
00268 
00269     return S_OK;
00270   } else if (IsEqualGUID(&IID_IPersistFile, refiid)) {
00271     *obj = &This->iPersistFile;
00272     IAVIFile_AddRef(iface);
00273 
00274     return S_OK;
00275   }
00276 
00277   return OLE_E_ENUM_NOMORE;
00278 }
00279 
00280 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface)
00281 {
00282   IAVIFileImpl *This = (IAVIFileImpl *)iface;
00283   ULONG ref = InterlockedIncrement(&This->ref);
00284 
00285   TRACE("(%p) -> %d\n", iface, ref);
00286 
00287   return ref;
00288 }
00289 
00290 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface)
00291 {
00292   IAVIFileImpl *This = (IAVIFileImpl *)iface;
00293   UINT i;
00294   ULONG ref = InterlockedDecrement(&This->ref);
00295 
00296   TRACE("(%p) -> %d\n", iface, ref);
00297 
00298   if (!ref) {
00299     if (This->fDirty) {
00300       /* need to write headers to file */
00301       AVIFILE_SaveFile(This);
00302     }
00303 
00304     for (i = 0; i < This->fInfo.dwStreams; i++) {
00305       if (This->ppStreams[i] != NULL) {
00306     if (This->ppStreams[i]->ref != 0) {
00307           ERR(": someone has still %u reference to stream %u (%p)!\n",
00308            This->ppStreams[i]->ref, i, This->ppStreams[i]);
00309     }
00310     AVIFILE_DestructAVIStream(This->ppStreams[i]);
00311     HeapFree(GetProcessHeap(), 0, This->ppStreams[i]);
00312     This->ppStreams[i] = NULL;
00313       }
00314     }
00315 
00316     if (This->idxRecords != NULL) {
00317       HeapFree(GetProcessHeap(), 0, This->idxRecords);
00318       This->idxRecords  = NULL;
00319       This->nIdxRecords = 0;
00320     }
00321 
00322     if (This->fileextra.lp != NULL) {
00323       HeapFree(GetProcessHeap(), 0, This->fileextra.lp);
00324       This->fileextra.lp = NULL;
00325       This->fileextra.cb = 0;
00326     }
00327 
00328     HeapFree(GetProcessHeap(), 0, This->szFileName);
00329     This->szFileName = NULL;
00330 
00331     if (This->hmmio != NULL) {
00332       mmioClose(This->hmmio, 0);
00333       This->hmmio = NULL;
00334     }
00335 
00336     HeapFree(GetProcessHeap(), 0, This);
00337   }
00338   return ref;
00339 }
00340 
00341 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, LPAVIFILEINFOW afi,
00342                       LONG size)
00343 {
00344   IAVIFileImpl *This = (IAVIFileImpl *)iface;
00345 
00346   TRACE("(%p,%p,%d)\n",iface,afi,size);
00347 
00348   if (afi == NULL)
00349     return AVIERR_BADPARAM;
00350   if (size < 0)
00351     return AVIERR_BADSIZE;
00352 
00353   AVIFILE_UpdateInfo(This);
00354 
00355   memcpy(afi, &This->fInfo, min((DWORD)size, sizeof(This->fInfo)));
00356 
00357   if ((DWORD)size < sizeof(This->fInfo))
00358     return AVIERR_BUFFERTOOSMALL;
00359   return AVIERR_OK;
00360 }
00361 
00362 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, PAVISTREAM *avis,
00363                        DWORD fccType, LONG lParam)
00364 {
00365   IAVIFileImpl *This = (IAVIFileImpl *)iface;
00366 
00367   ULONG nStream;
00368 
00369   TRACE("(%p,%p,0x%08X,%d)\n", iface, avis, fccType, lParam);
00370 
00371   if (avis == NULL || lParam < 0)
00372     return AVIERR_BADPARAM;
00373 
00374   nStream = AVIFILE_SearchStream(This, fccType, lParam);
00375 
00376   /* Does the requested stream exist? */
00377   if (nStream < This->fInfo.dwStreams &&
00378       This->ppStreams[nStream] != NULL) {
00379     *avis = (PAVISTREAM)This->ppStreams[nStream];
00380     IAVIStream_AddRef(*avis);
00381 
00382     return AVIERR_OK;
00383   }
00384 
00385   /* Sorry, but the specified stream doesn't exist */
00386   return AVIERR_NODATA;
00387 }
00388 
00389 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface,PAVISTREAM *avis,
00390                           LPAVISTREAMINFOW asi)
00391 {
00392   IAVIFileImpl *This = (IAVIFileImpl *)iface;
00393 
00394   DWORD n;
00395 
00396   TRACE("(%p,%p,%p)\n", iface, avis, asi);
00397 
00398   /* check parameters */
00399   if (avis == NULL || asi == NULL)
00400     return AVIERR_BADPARAM;
00401 
00402   *avis = NULL;
00403 
00404   /* Does the user have write permission? */
00405   if ((This->uMode & MMIO_RWMODE) == 0)
00406     return AVIERR_READONLY;
00407 
00408   /* Can we add another stream? */
00409   n = This->fInfo.dwStreams;
00410   if (n >= MAX_AVISTREAMS || This->dwMoviChunkPos != 0) {
00411     /* already reached max nr of streams
00412      * or have already written frames to disk */
00413     return AVIERR_UNSUPPORTED;
00414   }
00415 
00416   /* check AVISTREAMINFO for some really needed things */
00417   if (asi->fccType == 0 || asi->dwScale == 0 || asi->dwRate == 0)
00418     return AVIERR_BADFORMAT;
00419 
00420   /* now it seems to be save to add the stream */
00421   assert(This->ppStreams[n] == NULL);
00422   This->ppStreams[n] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
00423                            sizeof(IAVIStreamImpl));
00424   if (This->ppStreams[n] == NULL)
00425     return AVIERR_MEMORY;
00426 
00427   /* initialize the new allocated stream */
00428   AVIFILE_ConstructAVIStream(This, n, asi);
00429 
00430   This->fInfo.dwStreams++;
00431   This->fDirty = TRUE;
00432 
00433   /* update our AVIFILEINFO structure */
00434   AVIFILE_UpdateInfo(This);
00435 
00436   /* return it */
00437   *avis = (PAVISTREAM)This->ppStreams[n];
00438   IAVIStream_AddRef(*avis);
00439 
00440   return AVIERR_OK;
00441 }
00442 
00443 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid,
00444                        LPVOID lpData, LONG size)
00445 {
00446   IAVIFileImpl *This = (IAVIFileImpl *)iface;
00447 
00448   TRACE("(%p,0x%08X,%p,%d)\n", iface, ckid, lpData, size);
00449 
00450   /* check parameters */
00451   if (lpData == NULL)
00452     return AVIERR_BADPARAM;
00453   if (size < 0)
00454     return AVIERR_BADSIZE;
00455 
00456   /* Do we have write permission? */
00457   if ((This->uMode & MMIO_RWMODE) == 0)
00458     return AVIERR_READONLY;
00459 
00460   This->fDirty = TRUE;
00461 
00462   return WriteExtraChunk(&This->fileextra, ckid, lpData, size);
00463 }
00464 
00465 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid,
00466                       LPVOID lpData, LONG *size)
00467 {
00468   IAVIFileImpl *This = (IAVIFileImpl *)iface;
00469 
00470   TRACE("(%p,0x%08X,%p,%p)\n", iface, ckid, lpData, size);
00471 
00472   return ReadExtraChunk(&This->fileextra, ckid, lpData, size);
00473 }
00474 
00475 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface)
00476 {
00477   IAVIFileImpl *This = (IAVIFileImpl *)iface;
00478 
00479   TRACE("(%p)\n",iface);
00480 
00481   if ((This->uMode & MMIO_RWMODE) == 0)
00482     return AVIERR_READONLY;
00483 
00484   This->fDirty = TRUE;
00485 
00486   /* no frames written to any stream? -- compute start of 'movi'-chunk */
00487   if (This->dwMoviChunkPos == 0)
00488     AVIFILE_ComputeMoviStart(This);
00489 
00490   This->fInfo.dwFlags  |= AVIFILEINFO_ISINTERLEAVED;
00491 
00492   /* already written frames to any stream, ... */
00493   if (This->ckLastRecord.dwFlags & MMIO_DIRTY) {
00494     /* close last record */
00495     if (mmioAscend(This->hmmio, &This->ckLastRecord, 0) != 0)
00496       return AVIERR_FILEWRITE;
00497 
00498     AVIFILE_AddRecord(This);
00499 
00500     if (This->fInfo.dwSuggestedBufferSize < This->ckLastRecord.cksize + 3 * sizeof(DWORD))
00501       This->fInfo.dwSuggestedBufferSize = This->ckLastRecord.cksize + 3 * sizeof(DWORD);
00502   }
00503 
00504   /* write out a new record into file, but don't close it */
00505   This->ckLastRecord.cksize  = 0;
00506   This->ckLastRecord.fccType = listtypeAVIRECORD;
00507   if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1)
00508     return AVIERR_FILEWRITE;
00509   if (mmioCreateChunk(This->hmmio, &This->ckLastRecord, MMIO_CREATELIST) != 0)
00510     return AVIERR_FILEWRITE;
00511   This->dwNextFramePos += 3 * sizeof(DWORD);
00512 
00513   return AVIERR_OK;
00514 }
00515 
00516 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType,
00517                           LONG lParam)
00518 {
00519   IAVIFileImpl *This = (IAVIFileImpl *)iface;
00520 
00521   ULONG nStream;
00522 
00523   TRACE("(%p,0x%08X,%d)\n", iface, fccType, lParam);
00524 
00525   /* check parameter */
00526   if (lParam < 0)
00527     return AVIERR_BADPARAM;
00528 
00529   /* Have user write permissions? */
00530   if ((This->uMode & MMIO_RWMODE) == 0)
00531     return AVIERR_READONLY;
00532 
00533   nStream = AVIFILE_SearchStream(This, fccType, lParam);
00534 
00535   /* Does the requested stream exist? */
00536   if (nStream < This->fInfo.dwStreams &&
00537       This->ppStreams[nStream] != NULL) {
00538     /* ... so delete it now */
00539     HeapFree(GetProcessHeap(), 0, This->ppStreams[nStream]);
00540 
00541     if (This->fInfo.dwStreams - nStream > 0)
00542       memcpy(This->ppStreams + nStream, This->ppStreams + nStream + 1,
00543          (This->fInfo.dwStreams - nStream) * sizeof(IAVIStreamImpl*));
00544 
00545     This->ppStreams[This->fInfo.dwStreams] = NULL;
00546     This->fInfo.dwStreams--;
00547     This->fDirty = TRUE;
00548 
00549     /* This->fInfo will be updated further when asked for */
00550     return AVIERR_OK;
00551   } else
00552     return AVIERR_NODATA;
00553 }
00554 
00555 /***********************************************************************/
00556 
00557 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface,
00558                             REFIID refiid, LPVOID *obj)
00559 {
00560   IPersistFileImpl *This = (IPersistFileImpl *)iface;
00561 
00562   assert(This->paf != NULL);
00563 
00564   return IAVIFile_QueryInterface((PAVIFILE)This->paf, refiid, obj);
00565 }
00566 
00567 static ULONG   WINAPI IPersistFile_fnAddRef(IPersistFile *iface)
00568 {
00569   IPersistFileImpl *This = (IPersistFileImpl *)iface;
00570 
00571   assert(This->paf != NULL);
00572 
00573   return IAVIFile_AddRef((PAVIFILE)This->paf);
00574 }
00575 
00576 static ULONG   WINAPI IPersistFile_fnRelease(IPersistFile *iface)
00577 {
00578   IPersistFileImpl *This = (IPersistFileImpl *)iface;
00579 
00580   assert(This->paf != NULL);
00581 
00582   return IAVIFile_Release((PAVIFILE)This->paf);
00583 }
00584 
00585 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface,
00586                         LPCLSID pClassID)
00587 {
00588   TRACE("(%p,%p)\n", iface, pClassID);
00589 
00590   if (pClassID == NULL)
00591     return AVIERR_BADPARAM;
00592 
00593   *pClassID = CLSID_AVIFile;
00594 
00595   return AVIERR_OK;
00596 }
00597 
00598 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface)
00599 {
00600   IPersistFileImpl *This = (IPersistFileImpl *)iface;
00601 
00602   TRACE("(%p)\n", iface);
00603 
00604   assert(This->paf != NULL);
00605 
00606   return (This->paf->fDirty ? S_OK : S_FALSE);
00607 }
00608 
00609 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface,
00610                       LPCOLESTR pszFileName, DWORD dwMode)
00611 {
00612   IPersistFileImpl *This = (IPersistFileImpl *)iface;
00613 
00614   int len;
00615 
00616   TRACE("(%p,%s,0x%08X)\n", iface, debugstr_w(pszFileName), dwMode);
00617 
00618   /* check parameter */
00619   if (pszFileName == NULL)
00620     return AVIERR_BADPARAM;
00621 
00622   assert(This->paf != NULL);
00623   if (This->paf->hmmio != NULL)
00624     return AVIERR_ERROR; /* No reuse of this object for another file! */
00625 
00626   /* remember mode and name */
00627   This->paf->uMode = dwMode;
00628 
00629   len = lstrlenW(pszFileName) + 1;
00630   This->paf->szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
00631   if (This->paf->szFileName == NULL)
00632     return AVIERR_MEMORY;
00633   lstrcpyW(This->paf->szFileName, pszFileName);
00634 
00635   /* try to open the file */
00636   This->paf->hmmio = mmioOpenW(This->paf->szFileName, NULL,
00637                    MMIO_ALLOCBUF | dwMode);
00638   if (This->paf->hmmio == NULL) {
00639     /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */
00640     LPSTR szFileName;
00641 
00642     len = WideCharToMultiByte(CP_ACP, 0, This->paf->szFileName, -1,
00643                                NULL, 0, NULL, NULL);
00644     szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(CHAR));
00645     if (szFileName == NULL)
00646       return AVIERR_MEMORY;
00647 
00648     WideCharToMultiByte(CP_ACP, 0, This->paf->szFileName, -1, szFileName,
00649             len, NULL, NULL);
00650 
00651     This->paf->hmmio = mmioOpenA(szFileName, NULL, MMIO_ALLOCBUF | dwMode);
00652     HeapFree(GetProcessHeap(), 0, szFileName);
00653     if (This->paf->hmmio == NULL)
00654       return AVIERR_FILEOPEN;
00655   }
00656 
00657   /* should we create a new file? */
00658   if (dwMode & OF_CREATE) {
00659     memset(& This->paf->fInfo, 0, sizeof(This->paf->fInfo));
00660     This->paf->fInfo.dwFlags = AVIFILEINFO_HASINDEX | AVIFILEINFO_TRUSTCKTYPE;
00661 
00662     return AVIERR_OK;
00663   } else
00664     return AVIFILE_LoadFile(This->paf);
00665 }
00666 
00667 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface,
00668                       LPCOLESTR pszFileName,BOOL fRemember)
00669 {
00670   TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember);
00671 
00672   /* We write directly to disk, so nothing to do. */
00673 
00674   return AVIERR_OK;
00675 }
00676 
00677 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface,
00678                            LPCOLESTR pszFileName)
00679 {
00680   TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName));
00681 
00682   /* We write directly to disk, so nothing to do. */
00683 
00684   return AVIERR_OK;
00685 }
00686 
00687 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface,
00688                         LPOLESTR *ppszFileName)
00689 {
00690   IPersistFileImpl *This = (IPersistFileImpl *)iface;
00691 
00692   TRACE("(%p,%p)\n", iface, ppszFileName);
00693 
00694   if (ppszFileName == NULL)
00695     return AVIERR_BADPARAM;
00696 
00697   *ppszFileName = NULL;
00698 
00699   assert(This->paf != NULL);
00700 
00701   if (This->paf->szFileName != NULL) {
00702     int len = lstrlenW(This->paf->szFileName) + 1;
00703 
00704     *ppszFileName = CoTaskMemAlloc(len * sizeof(WCHAR));
00705     if (*ppszFileName == NULL)
00706       return AVIERR_MEMORY;
00707 
00708     strcpyW(*ppszFileName, This->paf->szFileName);
00709   }
00710 
00711   return AVIERR_OK;
00712 }
00713 
00714 /***********************************************************************/
00715 
00716 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface,
00717                           REFIID refiid, LPVOID *obj)
00718 {
00719   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
00720 
00721   TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj);
00722 
00723   if (IsEqualGUID(&IID_IUnknown, refiid) ||
00724       IsEqualGUID(&IID_IAVIStream, refiid)) {
00725     *obj = This;
00726     IAVIStream_AddRef(iface);
00727 
00728     return S_OK;
00729   }
00730   /* FIXME: IAVIStreaming interface */
00731 
00732   return OLE_E_ENUM_NOMORE;
00733 }
00734 
00735 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface)
00736 {
00737   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
00738   ULONG ref = InterlockedIncrement(&This->ref);
00739 
00740   TRACE("(%p) -> %d\n", iface, ref);
00741 
00742   /* also add ref to parent, so that it doesn't kill us */
00743   if (This->paf != NULL)
00744     IAVIFile_AddRef((PAVIFILE)This->paf);
00745 
00746   return ref;
00747 }
00748 
00749 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface)
00750 {
00751   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
00752   ULONG ref = InterlockedDecrement(&This->ref);
00753 
00754   TRACE("(%p) -> %d\n", iface, ref);
00755 
00756   if (This->paf != NULL)
00757     IAVIFile_Release((PAVIFILE)This->paf);
00758 
00759   return ref;
00760 }
00761 
00762 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1,
00763                       LPARAM lParam2)
00764 {
00765   TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2);
00766 
00767   /* This IAVIStream interface needs an AVIFile */
00768   return AVIERR_UNSUPPORTED;
00769 }
00770 
00771 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi,
00772                     LONG size)
00773 {
00774   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
00775 
00776   TRACE("(%p,%p,%d)\n", iface, psi, size);
00777 
00778   if (psi == NULL)
00779     return AVIERR_BADPARAM;
00780   if (size < 0)
00781     return AVIERR_BADSIZE;
00782 
00783   memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo)));
00784 
00785   if ((DWORD)size < sizeof(This->sInfo))
00786     return AVIERR_BUFFERTOOSMALL;
00787   return AVIERR_OK;
00788 }
00789 
00790 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos,
00791                        LONG flags)
00792 {
00793   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
00794 
00795   LONG offset = 0;
00796 
00797   TRACE("(%p,%d,0x%08X)\n",iface,pos,flags);
00798 
00799   if (flags & FIND_FROM_START) {
00800     pos = This->sInfo.dwStart;
00801     flags &= ~(FIND_FROM_START|FIND_PREV);
00802     flags |= FIND_NEXT;
00803   }
00804 
00805   if (This->sInfo.dwSampleSize != 0) {
00806     /* convert samples into block number with offset */
00807     AVIFILE_SamplesToBlock(This, &pos, &offset);
00808   }
00809 
00810   if (flags & FIND_TYPE) {
00811     if (flags & FIND_KEY) {
00812       while (0 <= pos && pos <= This->lLastFrame) {
00813     if (This->idxFrames[pos].dwFlags & AVIIF_KEYFRAME)
00814       goto RETURN_FOUND;
00815 
00816     if (flags & FIND_NEXT)
00817       pos++;
00818     else
00819       pos--;
00820       };
00821     } else if (flags & FIND_ANY) {
00822       while (0 <= pos && pos <= This->lLastFrame) {
00823     if (This->idxFrames[pos].dwChunkLength > 0)
00824       goto RETURN_FOUND;
00825 
00826     if (flags & FIND_NEXT)
00827       pos++;
00828     else
00829       pos--;
00830 
00831       };
00832     } else if ((flags & FIND_FORMAT) && This->idxFmtChanges != NULL &&
00833            This->sInfo.fccType == streamtypeVIDEO) {
00834       if (flags & FIND_NEXT) {
00835     ULONG n;
00836 
00837     for (n = 0; n < This->sInfo.dwFormatChangeCount; n++)
00838       if (This->idxFmtChanges[n].ckid >= pos) {
00839             pos = This->idxFmtChanges[n].ckid;
00840         goto RETURN_FOUND;
00841           }
00842       } else {
00843     LONG n;
00844 
00845     for (n = (LONG)This->sInfo.dwFormatChangeCount; n >= 0; n--) {
00846       if (This->idxFmtChanges[n].ckid <= pos) {
00847             pos = This->idxFmtChanges[n].ckid;
00848         goto RETURN_FOUND;
00849           }
00850     }
00851 
00852     if (pos > (LONG)This->sInfo.dwStart)
00853       return 0; /* format changes always for first frame */
00854       }
00855     }
00856 
00857     return -1;
00858   }
00859 
00860  RETURN_FOUND:
00861   if (pos < (LONG)This->sInfo.dwStart)
00862     return -1;
00863 
00864   switch (flags & FIND_RET) {
00865   case FIND_LENGTH:
00866     /* physical size */
00867     pos = This->idxFrames[pos].dwChunkLength;
00868     break;
00869   case FIND_OFFSET:
00870     /* physical position */
00871     pos = This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD)
00872       + offset * This->sInfo.dwSampleSize;
00873     break;
00874   case FIND_SIZE:
00875     /* logical size */
00876     if (This->sInfo.dwSampleSize)
00877       pos = This->sInfo.dwSampleSize;
00878     else
00879       pos = 1;
00880     break;
00881   case FIND_INDEX:
00882     FIXME(": FIND_INDEX flag is not supported!\n");
00883     /* This is an index in the index-table on disc. */
00884     break;
00885   }; /* else logical position */
00886 
00887   return pos;
00888 }
00889 
00890 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos,
00891                           LPVOID format, LONG *formatsize)
00892 {
00893   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
00894 
00895   TRACE("(%p,%d,%p,%p)\n", iface, pos, format, formatsize);
00896 
00897   if (formatsize == NULL)
00898     return AVIERR_BADPARAM;
00899 
00900   /* only interested in needed buffersize? */
00901   if (format == NULL || *formatsize <= 0) {
00902     *formatsize = This->cbFormat;
00903 
00904     return AVIERR_OK;
00905   }
00906 
00907   /* copy initial format (only as much as will fit) */
00908   memcpy(format, This->lpFormat, min(*(DWORD*)formatsize, This->cbFormat));
00909   if (*(DWORD*)formatsize < This->cbFormat) {
00910     *formatsize = This->cbFormat;
00911     return AVIERR_BUFFERTOOSMALL;
00912   }
00913 
00914   /* Could format change? When yes will it change? */
00915   if ((This->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
00916       pos > This->sInfo.dwStart) {
00917     LONG lLastFmt;
00918 
00919     lLastFmt = IAVIStream_fnFindSample(iface, pos, FIND_FORMAT|FIND_PREV);
00920     if (lLastFmt > 0) {
00921       FIXME(": need to read formatchange for %d -- unimplemented!\n",lLastFmt);
00922     }
00923   }
00924 
00925   *formatsize = This->cbFormat;
00926   return AVIERR_OK;
00927 }
00928 
00929 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos,
00930                          LPVOID format, LONG formatsize)
00931 {
00932   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
00933 
00934   LPBITMAPINFOHEADER lpbiNew = format;
00935 
00936   TRACE("(%p,%d,%p,%d)\n", iface, pos, format, formatsize);
00937 
00938   /* check parameters */
00939   if (format == NULL || formatsize <= 0)
00940     return AVIERR_BADPARAM;
00941 
00942   /* Do we have write permission? */
00943   if ((This->paf->uMode & MMIO_RWMODE) == 0)
00944     return AVIERR_READONLY;
00945 
00946   /* can only set format before frame is written! */
00947   if (This->lLastFrame > pos)
00948     return AVIERR_UNSUPPORTED;
00949 
00950   /* initial format or a formatchange? */
00951   if (This->lpFormat == NULL) {
00952     /* initial format */
00953     if (This->paf->dwMoviChunkPos != 0)
00954       return AVIERR_ERROR; /* user has used API in wrong sequence! */
00955 
00956     This->lpFormat = HeapAlloc(GetProcessHeap(), 0, formatsize);
00957     if (This->lpFormat == NULL)
00958       return AVIERR_MEMORY;
00959     This->cbFormat = formatsize;
00960 
00961     memcpy(This->lpFormat, format, formatsize);
00962 
00963     /* update some infos about stream */
00964     if (This->sInfo.fccType == streamtypeVIDEO) {
00965       LONG lDim;
00966 
00967       lDim = This->sInfo.rcFrame.right - This->sInfo.rcFrame.left;
00968       if (lDim < lpbiNew->biWidth)
00969     This->sInfo.rcFrame.right = This->sInfo.rcFrame.left + lpbiNew->biWidth;
00970       lDim = This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top;
00971       if (lDim < lpbiNew->biHeight)
00972     This->sInfo.rcFrame.bottom = This->sInfo.rcFrame.top + lpbiNew->biHeight;
00973     } else if (This->sInfo.fccType == streamtypeAUDIO)
00974       This->sInfo.dwSampleSize = ((LPWAVEFORMATEX)This->lpFormat)->nBlockAlign;
00975 
00976     return AVIERR_OK;
00977   } else {
00978     MMCKINFO           ck;
00979     LPBITMAPINFOHEADER lpbiOld = This->lpFormat;
00980     RGBQUAD           *rgbNew  = (RGBQUAD*)((LPBYTE)lpbiNew + lpbiNew->biSize);
00981     AVIPALCHANGE      *lppc = NULL;
00982     UINT               n;
00983 
00984     /* perhaps format change, check it ... */
00985     if (This->cbFormat != formatsize)
00986       return AVIERR_UNSUPPORTED;
00987 
00988     /* no format change, only the initial one */
00989     if (memcmp(This->lpFormat, format, formatsize) == 0)
00990       return AVIERR_OK;
00991 
00992     /* check that's only the palette, which changes */
00993     if (lpbiOld->biSize        != lpbiNew->biSize ||
00994     lpbiOld->biWidth       != lpbiNew->biWidth ||
00995     lpbiOld->biHeight      != lpbiNew->biHeight ||
00996     lpbiOld->biPlanes      != lpbiNew->biPlanes ||
00997     lpbiOld->biBitCount    != lpbiNew->biBitCount ||
00998     lpbiOld->biCompression != lpbiNew->biCompression ||
00999     lpbiOld->biClrUsed     != lpbiNew->biClrUsed)
01000       return AVIERR_UNSUPPORTED;
01001 
01002     This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
01003 
01004     /* simply say all colors have changed */
01005     ck.ckid   = MAKEAVICKID(cktypePALchange, This->nStream);
01006     ck.cksize = 2 * sizeof(WORD) + lpbiOld->biClrUsed * sizeof(PALETTEENTRY);
01007     lppc = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
01008     if (lppc == NULL)
01009       return AVIERR_MEMORY;
01010 
01011     lppc->bFirstEntry = 0;
01012     lppc->bNumEntries = (lpbiOld->biClrUsed < 256 ? lpbiOld->biClrUsed : 0);
01013     lppc->wFlags      = 0;
01014     for (n = 0; n < lpbiOld->biClrUsed; n++) {
01015       lppc->peNew[n].peRed   = rgbNew[n].rgbRed;
01016       lppc->peNew[n].peGreen = rgbNew[n].rgbGreen;
01017       lppc->peNew[n].peBlue  = rgbNew[n].rgbBlue;
01018       lppc->peNew[n].peFlags = 0;
01019     }
01020 
01021     if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1 ||
01022         mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK ||
01023         mmioWrite(This->paf->hmmio, (HPSTR)lppc, ck.cksize) != ck.cksize ||
01024         mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
01025     {
01026       HeapFree(GetProcessHeap(), 0, lppc);
01027       return AVIERR_FILEWRITE;
01028     }
01029 
01030     This->paf->dwNextFramePos += ck.cksize + 2 * sizeof(DWORD);
01031 
01032     HeapFree(GetProcessHeap(), 0, lppc);
01033 
01034     return AVIFILE_AddFrame(This, cktypePALchange, n, ck.dwDataOffset, 0);
01035   }
01036 }
01037 
01038 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start,
01039                     LONG samples, LPVOID buffer,
01040                     LONG buffersize, LPLONG bytesread,
01041                     LPLONG samplesread)
01042 {
01043   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
01044 
01045   DWORD    size;
01046   HRESULT  hr;
01047 
01048   TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", iface, start, samples, buffer,
01049     buffersize, bytesread, samplesread);
01050 
01051   /* clear return parameters if given */
01052   if (bytesread != NULL)
01053     *bytesread = 0;
01054   if (samplesread != NULL)
01055     *samplesread = 0;
01056 
01057   /* check parameters */
01058   if ((LONG)This->sInfo.dwStart > start)
01059     return AVIERR_NODATA; /* couldn't read before start of stream */
01060   if (This->sInfo.dwStart + This->sInfo.dwLength < (DWORD)start)
01061     return AVIERR_NODATA; /* start is past end of stream */
01062 
01063   /* should we read as much as possible? */
01064   if (samples == -1) {
01065     /* User should know how much we have read */
01066     if (bytesread == NULL && samplesread == NULL)
01067       return AVIERR_BADPARAM;
01068 
01069     if (This->sInfo.dwSampleSize != 0)
01070       samples = buffersize / This->sInfo.dwSampleSize;
01071     else
01072       samples = 1;
01073   }
01074 
01075   /* limit to end of stream */
01076   if ((LONG)This->sInfo.dwLength < samples)
01077     samples = This->sInfo.dwLength;
01078   if ((start - This->sInfo.dwStart) > (This->sInfo.dwLength - samples))
01079     samples = This->sInfo.dwLength - (start - This->sInfo.dwStart);
01080 
01081   /* nothing to read? Then leave ... */
01082   if (samples == 0)
01083     return AVIERR_OK;
01084 
01085   if (This->sInfo.dwSampleSize != 0) {
01086     /* fixed samplesize -- we can read over frame/block boundaries */
01087     LONG block = start;
01088     LONG offset = 0;
01089 
01090     if (!buffer)
01091     {
01092       if (bytesread)
01093         *bytesread = samples*This->sInfo.dwSampleSize;
01094       if (samplesread)
01095         *samplesread = samples;
01096       return AVIERR_OK;
01097     }
01098 
01099     /* convert start sample to block,offset pair */
01100     AVIFILE_SamplesToBlock(This, &block, &offset);
01101 
01102     /* convert samples to bytes */
01103     samples *= This->sInfo.dwSampleSize;
01104 
01105     while (samples > 0 && buffersize > 0) {
01106       LONG blocksize;
01107       if (block != This->dwCurrentFrame) {
01108     hr = AVIFILE_ReadBlock(This, block, NULL, 0);
01109     if (FAILED(hr))
01110       return hr;
01111       }
01112 
01113       size = min((DWORD)samples, (DWORD)buffersize);
01114       blocksize = This->lpBuffer[1];
01115       TRACE("blocksize = %u\n",blocksize);
01116       size = min(size, blocksize - offset);
01117       memcpy(buffer, ((BYTE*)&This->lpBuffer[2]) + offset, size);
01118 
01119       block++;
01120       offset = 0;
01121       buffer = ((LPBYTE)buffer)+size;
01122       samples    -= size;
01123       buffersize -= size;
01124 
01125       /* fill out return parameters if given */
01126       if (bytesread != NULL)
01127     *bytesread   += size;
01128       if (samplesread != NULL)
01129     *samplesread += size / This->sInfo.dwSampleSize;
01130     }
01131 
01132     if (samples == 0)
01133       return AVIERR_OK;
01134     else
01135       return AVIERR_BUFFERTOOSMALL;
01136   } else {
01137     /* variable samplesize -- we can only read one full frame/block */
01138     if (samples > 1)
01139       samples = 1;
01140 
01141     assert(start <= This->lLastFrame);
01142     size = This->idxFrames[start].dwChunkLength;
01143     if (buffer != NULL && buffersize >= size) {
01144       hr = AVIFILE_ReadBlock(This, start, buffer, size);
01145       if (FAILED(hr))
01146     return hr;
01147     } else if (buffer != NULL)
01148       return AVIERR_BUFFERTOOSMALL;
01149 
01150     /* fill out return parameters if given */
01151     if (bytesread != NULL)
01152       *bytesread = size;
01153     if (samplesread != NULL)
01154       *samplesread = samples;
01155 
01156     return AVIERR_OK;
01157   }
01158 }
01159 
01160 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start,
01161                      LONG samples, LPVOID buffer,
01162                      LONG buffersize, DWORD flags,
01163                      LPLONG sampwritten,
01164                      LPLONG byteswritten)
01165 {
01166   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
01167 
01168   FOURCC  ckid;
01169   HRESULT hr;
01170 
01171   TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n", iface, start, samples,
01172     buffer, buffersize, flags, sampwritten, byteswritten);
01173 
01174   /* clear return parameters if given */
01175   if (sampwritten != NULL)
01176     *sampwritten = 0;
01177   if (byteswritten != NULL)
01178     *byteswritten = 0;
01179 
01180   /* check parameters */
01181   if (buffer == NULL && (buffersize > 0 || samples > 0))
01182     return AVIERR_BADPARAM;
01183 
01184   /* Have we write permission? */
01185   if ((This->paf->uMode & MMIO_RWMODE) == 0)
01186     return AVIERR_READONLY;
01187 
01188   switch (This->sInfo.fccType) {
01189   case streamtypeAUDIO:
01190     ckid = MAKEAVICKID(cktypeWAVEbytes, This->nStream);
01191     break;
01192   default:
01193     if ((flags & AVIIF_KEYFRAME) && buffersize != 0)
01194       ckid = MAKEAVICKID(cktypeDIBbits, This->nStream);
01195     else
01196       ckid = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
01197     break;
01198   };
01199 
01200   /* append to end of stream? */
01201   if (start == -1) {
01202     if (This->lLastFrame == -1)
01203       start = This->sInfo.dwStart;
01204     else
01205       start = This->sInfo.dwLength;
01206   } else if (This->lLastFrame == -1)
01207     This->sInfo.dwStart = start;
01208 
01209   if (This->sInfo.dwSampleSize != 0) {
01210     /* fixed sample size -- audio like */
01211     if (samples * This->sInfo.dwSampleSize != buffersize)
01212       return AVIERR_BADPARAM;
01213 
01214     /* Couldn't skip audio-like data -- User must supply appropriate silence */
01215     if (This->sInfo.dwLength != start)
01216       return AVIERR_UNSUPPORTED;
01217 
01218     /* Convert position to frame/block */
01219     start = This->lLastFrame + 1;
01220 
01221     if ((This->paf->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) == 0) {
01222       FIXME(": not interleaved, could collect audio data!\n");
01223     }
01224   } else {
01225     /* variable sample size -- video like */
01226     if (samples > 1)
01227       return AVIERR_UNSUPPORTED;
01228 
01229     /* must we fill up with empty frames? */
01230     if (This->lLastFrame != -1) {
01231       FOURCC ckid2 = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
01232 
01233       while (start > This->lLastFrame + 1) {
01234     hr = AVIFILE_WriteBlock(This, This->lLastFrame + 1, ckid2, 0, NULL, 0);
01235     if (FAILED(hr))
01236       return hr;
01237       }
01238     }
01239   }
01240 
01241   /* write the block now */
01242   hr = AVIFILE_WriteBlock(This, start, ckid, flags, buffer, buffersize);
01243   if (SUCCEEDED(hr)) {
01244     /* fill out return parameters if given */
01245     if (sampwritten != NULL)
01246       *sampwritten = samples;
01247     if (byteswritten != NULL)
01248       *byteswritten = buffersize;
01249   }
01250 
01251   return hr;
01252 }
01253 
01254 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start,
01255                       LONG samples)
01256 {
01257   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
01258 
01259   FIXME("(%p,%d,%d): stub\n", iface, start, samples);
01260 
01261   /* check parameters */
01262   if (start < 0 || samples < 0)
01263     return AVIERR_BADPARAM;
01264 
01265   /* Delete before start of stream? */
01266   if (start + samples < This->sInfo.dwStart)
01267     return AVIERR_OK;
01268 
01269   /* Delete after end of stream? */
01270   if (start > This->sInfo.dwLength)
01271     return AVIERR_OK;
01272 
01273   /* For the rest we need write permissions */
01274   if ((This->paf->uMode & MMIO_RWMODE) == 0)
01275     return AVIERR_READONLY;
01276 
01277   /* 1. overwrite the data with JUNK
01278    *
01279    * if ISINTERLEAVED {
01280    *   2. concat all neighboured JUNK-blocks in this record to one
01281    *   3. if this record only contains JUNK and is at end set dwNextFramePos
01282    *      to start of this record, repeat this.
01283    * } else {
01284    *   2. concat all neighboured JUNK-blocks.
01285    *   3. if the JUNK block is at the end, then set dwNextFramePos to
01286    *      start of this block.
01287    * }
01288    */
01289 
01290   return AVIERR_UNSUPPORTED;
01291 }
01292 
01293 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc,
01294                         LPVOID lp, LPLONG lpread)
01295 {
01296   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
01297 
01298   TRACE("(%p,0x%08X,%p,%p)\n", iface, fcc, lp, lpread);
01299 
01300   if (fcc == ckidSTREAMHANDLERDATA) {
01301     if (This->lpHandlerData != NULL && This->cbHandlerData > 0) {
01302       if (lp == NULL || *lpread <= 0) {
01303     *lpread = This->cbHandlerData;
01304     return AVIERR_OK;
01305       }
01306 
01307       memcpy(lp, This->lpHandlerData, min(This->cbHandlerData, *lpread));
01308       if (*lpread < This->cbHandlerData)
01309     return AVIERR_BUFFERTOOSMALL;
01310       return AVIERR_OK;
01311     } else
01312       return AVIERR_NODATA;
01313   } else
01314     return ReadExtraChunk(&This->extra, fcc, lp, lpread);
01315 }
01316 
01317 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc,
01318                          LPVOID lp, LONG size)
01319 {
01320   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
01321 
01322   TRACE("(%p,0x%08x,%p,%d)\n", iface, fcc, lp, size);
01323 
01324   /* check parameters */
01325   if (lp == NULL)
01326     return AVIERR_BADPARAM;
01327   if (size <= 0)
01328     return AVIERR_BADSIZE;
01329 
01330   /* need write permission */
01331   if ((This->paf->uMode & MMIO_RWMODE) == 0)
01332     return AVIERR_READONLY;
01333 
01334   /* already written something to this file? */
01335   if (This->paf->dwMoviChunkPos != 0) {
01336     /* the data will be inserted before the 'movi' chunk, so check for
01337      * enough space */
01338     DWORD dwPos = AVIFILE_ComputeMoviStart(This->paf);
01339 
01340     /* ckid,size => 2 * sizeof(DWORD) */
01341     dwPos += 2 * sizeof(DWORD) + size;
01342     if (dwPos >= This->paf->dwMoviChunkPos - 2 * sizeof(DWORD))
01343       return AVIERR_UNSUPPORTED; /* not enough space left */
01344   }
01345 
01346   This->paf->fDirty = TRUE;
01347 
01348   if (fcc == ckidSTREAMHANDLERDATA) {
01349     if (This->lpHandlerData != NULL) {
01350       FIXME(": handler data already set -- overwirte?\n");
01351       return AVIERR_UNSUPPORTED;
01352     }
01353 
01354     This->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, size);
01355     if (This->lpHandlerData == NULL)
01356       return AVIERR_MEMORY;
01357     This->cbHandlerData = size;
01358     memcpy(This->lpHandlerData, lp, size);
01359 
01360     return AVIERR_OK;
01361   } else
01362     return WriteExtraChunk(&This->extra, fcc, lp, size);
01363 }
01364 
01365 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream *iface,
01366                        LPAVISTREAMINFOW info, LONG infolen)
01367 {
01368   FIXME("(%p,%p,%d): stub\n", iface, info, infolen);
01369 
01370   return E_FAIL;
01371 }
01372 
01373 /***********************************************************************/
01374 
01375 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, DWORD offset, DWORD flags)
01376 {
01377   UINT n;
01378 
01379   /* pre-conditions */
01380   assert(This != NULL);
01381 
01382   switch (TWOCCFromFOURCC(ckid)) {
01383   case cktypeDIBbits:
01384     if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
01385       flags |= AVIIF_KEYFRAME;
01386     break;
01387   case cktypeDIBcompressed:
01388     if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
01389       flags &= ~AVIIF_KEYFRAME;
01390     break;
01391   case cktypePALchange:
01392     if (This->sInfo.fccType != streamtypeVIDEO) {
01393       ERR(": found palette change in non-video stream!\n");
01394       return AVIERR_BADFORMAT;
01395     }
01396 
01397     if (This->idxFmtChanges == NULL || This->nIdxFmtChanges <= This->sInfo.dwFormatChangeCount) {
01398       DWORD new_count = This->nIdxFmtChanges + 16;
01399       void *new_buffer;
01400 
01401       if (This->idxFmtChanges == NULL) {
01402     This->idxFmtChanges =
01403           HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * sizeof(AVIINDEXENTRY));
01404         if (!This->idxFmtChanges) return AVIERR_MEMORY;
01405       } else {
01406         new_buffer = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxFmtChanges,
01407                 new_count * sizeof(AVIINDEXENTRY));
01408         if (!new_buffer) return AVIERR_MEMORY;
01409         This->idxFmtChanges = new_buffer;
01410       }
01411       This->nIdxFmtChanges = new_count;
01412     }
01413 
01414     This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
01415     n = ++This->sInfo.dwFormatChangeCount;
01416     This->idxFmtChanges[n].ckid          = This->lLastFrame;
01417     This->idxFmtChanges[n].dwFlags       = 0;
01418     This->idxFmtChanges[n].dwChunkOffset = offset;
01419     This->idxFmtChanges[n].dwChunkLength = size;
01420 
01421     return AVIERR_OK;
01422   case cktypeWAVEbytes:
01423     if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
01424       flags |= AVIIF_KEYFRAME;
01425     break;
01426   default:
01427     WARN(": unknown TWOCC 0x%04X found\n", TWOCCFromFOURCC(ckid));
01428     break;
01429   };
01430 
01431   /* first frame is always a keyframe */
01432   if (This->lLastFrame == -1)
01433     flags |= AVIIF_KEYFRAME;
01434 
01435   if (This->sInfo.dwSuggestedBufferSize < size)
01436     This->sInfo.dwSuggestedBufferSize = size;
01437 
01438   /* get memory for index */
01439   if (This->idxFrames == NULL || This->lLastFrame + 1 >= This->nIdxFrames) {
01440     This->nIdxFrames += 512;
01441     if (This->idxFrames == NULL)
01442       This->idxFrames = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->nIdxFrames * sizeof(AVIINDEXENTRY));
01443       else
01444     This->idxFrames = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxFrames,
01445                This->nIdxFrames * sizeof(AVIINDEXENTRY));
01446     if (This->idxFrames == NULL)
01447       return AVIERR_MEMORY;
01448   }
01449 
01450   This->lLastFrame++;
01451   This->idxFrames[This->lLastFrame].ckid          = ckid;
01452   This->idxFrames[This->lLastFrame].dwFlags       = flags;
01453   This->idxFrames[This->lLastFrame].dwChunkOffset = offset;
01454   This->idxFrames[This->lLastFrame].dwChunkLength = size;
01455 
01456   /* update AVISTREAMINFO structure if necessary */
01457   if (This->sInfo.dwLength <= This->lLastFrame)
01458     This->sInfo.dwLength = This->lLastFrame + 1;
01459 
01460   return AVIERR_OK;
01461 }
01462 
01463 static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This)
01464 {
01465   /* pre-conditions */
01466   assert(This != NULL && This->ppStreams[0] != NULL);
01467 
01468   if (This->idxRecords == NULL || This->cbIdxRecords == 0) {
01469     This->cbIdxRecords += 1024 * sizeof(AVIINDEXENTRY);
01470     This->idxRecords = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->cbIdxRecords);
01471     if (This->idxRecords == NULL)
01472       return AVIERR_MEMORY;
01473   }
01474 
01475   assert(This->nIdxRecords < This->cbIdxRecords/sizeof(AVIINDEXENTRY));
01476 
01477   This->idxRecords[This->nIdxRecords].ckid          = listtypeAVIRECORD;
01478   This->idxRecords[This->nIdxRecords].dwFlags       = AVIIF_LIST;
01479   This->idxRecords[This->nIdxRecords].dwChunkOffset =
01480     This->ckLastRecord.dwDataOffset - 2 * sizeof(DWORD);
01481   This->idxRecords[This->nIdxRecords].dwChunkLength =
01482     This->ckLastRecord.cksize;
01483   This->nIdxRecords++;
01484 
01485   return AVIERR_OK;
01486 }
01487 
01488 static DWORD   AVIFILE_ComputeMoviStart(IAVIFileImpl *This)
01489 {
01490   DWORD dwPos;
01491   DWORD nStream;
01492 
01493   /* RIFF,hdrl,movi,avih => (3 * 3 + 2) * sizeof(DWORD) = 11 * sizeof(DWORD) */
01494   dwPos = 11 * sizeof(DWORD) + sizeof(MainAVIHeader);
01495 
01496   for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
01497     IAVIStreamImpl *pStream = This->ppStreams[nStream];
01498 
01499     /* strl,strh,strf => (3 + 2 * 2) * sizeof(DWORD) = 7 * sizeof(DWORD) */
01500     dwPos += 7 * sizeof(DWORD) + sizeof(AVIStreamHeader);
01501     dwPos += ((pStream->cbFormat + 1) & ~1U);
01502     if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0)
01503       dwPos += 2 * sizeof(DWORD) + ((pStream->cbHandlerData + 1) & ~1U);
01504     if (lstrlenW(pStream->sInfo.szName) > 0)
01505       dwPos += 2 * sizeof(DWORD) + ((lstrlenW(pStream->sInfo.szName) + 1) & ~1U);
01506   }
01507 
01508   if (This->dwMoviChunkPos == 0) {
01509     This->dwNextFramePos = dwPos;
01510 
01511     /* pad to multiple of AVI_HEADERSIZE only if we are more than 8 bytes away from it */
01512     if (((dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1)) - dwPos > 2 * sizeof(DWORD))
01513       This->dwNextFramePos = (dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1);
01514 
01515     This->dwMoviChunkPos = This->dwNextFramePos - sizeof(DWORD);
01516   }
01517 
01518   return dwPos;
01519 }
01520 
01521 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, const AVISTREAMINFOW *asi)
01522 {
01523   IAVIStreamImpl *pstream;
01524 
01525   /* pre-conditions */
01526   assert(paf != NULL);
01527   assert(nr < MAX_AVISTREAMS);
01528   assert(paf->ppStreams[nr] != NULL);
01529 
01530   pstream = paf->ppStreams[nr];
01531 
01532   pstream->lpVtbl         = &iavist;
01533   pstream->ref            = 0;
01534   pstream->paf            = paf;
01535   pstream->nStream        = nr;
01536   pstream->dwCurrentFrame = (DWORD)-1;
01537   pstream->lLastFrame    = -1;
01538 
01539   if (asi != NULL) {
01540     memcpy(&pstream->sInfo, asi, sizeof(pstream->sInfo));
01541 
01542     if (asi->dwLength > 0) {
01543       /* pre-allocate mem for frame-index structure */
01544       pstream->idxFrames =
01545     HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asi->dwLength * sizeof(AVIINDEXENTRY));
01546       if (pstream->idxFrames != NULL)
01547     pstream->nIdxFrames = asi->dwLength;
01548     }
01549     if (asi->dwFormatChangeCount > 0) {
01550       /* pre-allocate mem for formatchange-index structure */
01551       pstream->idxFmtChanges =
01552     HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asi->dwFormatChangeCount * sizeof(AVIINDEXENTRY));
01553       if (pstream->idxFmtChanges != NULL)
01554     pstream->nIdxFmtChanges = asi->dwFormatChangeCount;
01555     }
01556 
01557     /* These values will be computed */
01558     pstream->sInfo.dwLength              = 0;
01559     pstream->sInfo.dwSuggestedBufferSize = 0;
01560     pstream->sInfo.dwFormatChangeCount   = 0;
01561     pstream->sInfo.dwEditCount           = 1;
01562     if (pstream->sInfo.dwSampleSize > 0)
01563       SetRectEmpty(&pstream->sInfo.rcFrame);
01564   }
01565 
01566   pstream->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
01567 }
01568 
01569 static void    AVIFILE_DestructAVIStream(IAVIStreamImpl *This)
01570 {
01571   /* pre-conditions */
01572   assert(This != NULL);
01573 
01574   This->dwCurrentFrame = (DWORD)-1;
01575   This->lLastFrame    = -1;
01576   This->paf = NULL;
01577   if (This->idxFrames != NULL) {
01578     HeapFree(GetProcessHeap(), 0, This->idxFrames);
01579     This->idxFrames  = NULL;
01580     This->nIdxFrames = 0;
01581   }
01582   HeapFree(GetProcessHeap(), 0, This->idxFmtChanges);
01583   This->idxFmtChanges = NULL;
01584   if (This->lpBuffer != NULL) {
01585     HeapFree(GetProcessHeap(), 0, This->lpBuffer);
01586     This->lpBuffer = NULL;
01587     This->cbBuffer = 0;
01588   }
01589   if (This->lpHandlerData != NULL) {
01590     HeapFree(GetProcessHeap(), 0, This->lpHandlerData);
01591     This->lpHandlerData = NULL;
01592     This->cbHandlerData = 0;
01593   }
01594   if (This->extra.lp != NULL) {
01595     HeapFree(GetProcessHeap(), 0, This->extra.lp);
01596     This->extra.lp = NULL;
01597     This->extra.cb = 0;
01598   }
01599   if (This->lpFormat != NULL) {
01600     HeapFree(GetProcessHeap(), 0, This->lpFormat);
01601     This->lpFormat = NULL;
01602     This->cbFormat = 0;
01603   }
01604 }
01605 
01606 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This)
01607 {
01608   MainAVIHeader   MainAVIHdr;
01609   MMCKINFO        ckRIFF;
01610   MMCKINFO        ckLIST1;
01611   MMCKINFO        ckLIST2;
01612   MMCKINFO        ck;
01613   IAVIStreamImpl *pStream;
01614   DWORD           nStream;
01615   HRESULT         hr;
01616 
01617   if (This->hmmio == NULL)
01618     return AVIERR_FILEOPEN;
01619 
01620   /* initialize stream ptr's */
01621   memset(This->ppStreams, 0, sizeof(This->ppStreams));
01622 
01623   /* try to get "RIFF" chunk -- must not be at beginning of file! */
01624   ckRIFF.fccType = formtypeAVI;
01625   if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) {
01626     ERR(": not an AVI!\n");
01627     return AVIERR_FILEREAD;
01628   }
01629 
01630   /* get "LIST" "hdrl" */
01631   ckLIST1.fccType = listtypeAVIHEADER;
01632   hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF, MMIO_FINDLIST);
01633   if (FAILED(hr))
01634     return hr;
01635 
01636   /* get "avih" chunk */
01637   ck.ckid = ckidAVIMAINHDR;
01638   hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, MMIO_FINDCHUNK);
01639   if (FAILED(hr))
01640     return hr;
01641 
01642   if (ck.cksize != sizeof(MainAVIHdr)) {
01643     ERR(": invalid size of %d for MainAVIHeader!\n", ck.cksize);
01644     return AVIERR_BADFORMAT;
01645   }
01646   if (mmioRead(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
01647     return AVIERR_FILEREAD;
01648 
01649   /* check for MAX_AVISTREAMS limit */
01650   if (MainAVIHdr.dwStreams > MAX_AVISTREAMS) {
01651     WARN("file contains %u streams, but only supports %d -- change MAX_AVISTREAMS!\n", MainAVIHdr.dwStreams, MAX_AVISTREAMS);
01652     return AVIERR_UNSUPPORTED;
01653   }
01654 
01655   /* adjust permissions if copyrighted material in file */
01656   if (MainAVIHdr.dwFlags & AVIFILEINFO_COPYRIGHTED) {
01657     This->uMode &= ~MMIO_RWMODE;
01658     This->uMode |= MMIO_READ;
01659   }
01660 
01661   /* convert MainAVIHeader into AVIFILINFOW */
01662   memset(&This->fInfo, 0, sizeof(This->fInfo));
01663   This->fInfo.dwRate                = MainAVIHdr.dwMicroSecPerFrame;
01664   This->fInfo.dwScale               = 1000000;
01665   This->fInfo.dwMaxBytesPerSec      = MainAVIHdr.dwMaxBytesPerSec;
01666   This->fInfo.dwFlags               = MainAVIHdr.dwFlags;
01667   This->fInfo.dwCaps                = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
01668   This->fInfo.dwLength              = MainAVIHdr.dwTotalFrames;
01669   This->fInfo.dwStreams             = MainAVIHdr.dwStreams;
01670   This->fInfo.dwSuggestedBufferSize = 0;
01671   This->fInfo.dwWidth               = MainAVIHdr.dwWidth;
01672   This->fInfo.dwHeight              = MainAVIHdr.dwHeight;
01673   LoadStringW(AVIFILE_hModule, IDS_AVIFILETYPE, This->fInfo.szFileType,
01674           sizeof(This->fInfo.szFileType)/sizeof(This->fInfo.szFileType[0]));
01675 
01676   /* go back to into header list */
01677   if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
01678     return AVIERR_FILEREAD;
01679 
01680   /* foreach stream exists a "LIST","strl" chunk */
01681   for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
01682     /* get next nested chunk in this "LIST","strl" */
01683     if (mmioDescend(This->hmmio, &ckLIST2, &ckLIST1, 0) != S_OK)
01684       return AVIERR_FILEREAD;
01685 
01686     /* nested chunk must be of type "LIST","strl" -- when not normally JUNK */
01687     if (ckLIST2.ckid == FOURCC_LIST &&
01688     ckLIST2.fccType == listtypeSTREAMHEADER) {
01689       pStream = This->ppStreams[nStream] =
01690     HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIStreamImpl));
01691       if (pStream == NULL)
01692     return AVIERR_MEMORY;
01693       AVIFILE_ConstructAVIStream(This, nStream, NULL);
01694 
01695       ck.ckid = 0;
01696       while (mmioDescend(This->hmmio, &ck, &ckLIST2, 0) == S_OK) {
01697     switch (ck.ckid) {
01698     case ckidSTREAMHANDLERDATA:
01699       if (pStream->lpHandlerData != NULL)
01700         return AVIERR_BADFORMAT;
01701       pStream->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
01702       if (pStream->lpHandlerData == NULL)
01703         return AVIERR_MEMORY;
01704       pStream->cbHandlerData = ck.cksize;
01705 
01706           if (mmioRead(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize)
01707         return AVIERR_FILEREAD;
01708       break;
01709     case ckidSTREAMFORMAT:
01710       if (pStream->lpFormat != NULL)
01711         return AVIERR_BADFORMAT;
01712           if (ck.cksize == 0)
01713             break;
01714 
01715       pStream->lpFormat = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
01716       if (pStream->lpFormat == NULL)
01717         return AVIERR_MEMORY;
01718       pStream->cbFormat = ck.cksize;
01719 
01720           if (mmioRead(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize)
01721         return AVIERR_FILEREAD;
01722 
01723       if (pStream->sInfo.fccType == streamtypeVIDEO) {
01724             LPBITMAPINFOHEADER lpbi = pStream->lpFormat;
01725 
01726         /* some corrections to the video format */
01727         if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
01728           lpbi->biClrUsed = 1u << lpbi->biBitCount;
01729         if (lpbi->biCompression == BI_RGB && lpbi->biSizeImage == 0)
01730           lpbi->biSizeImage = DIBWIDTHBYTES(*lpbi) * lpbi->biHeight;
01731         if (lpbi->biCompression != BI_RGB && lpbi->biBitCount == 8) {
01732           if (pStream->sInfo.fccHandler == mmioFOURCC('R','L','E','0') ||
01733           pStream->sInfo.fccHandler == mmioFOURCC('R','L','E',' '))
01734         lpbi->biCompression = BI_RLE8;
01735         }
01736         if (lpbi->biCompression == BI_RGB &&
01737         (pStream->sInfo.fccHandler == 0 ||
01738          pStream->sInfo.fccHandler == mmioFOURCC('N','O','N','E')))
01739           pStream->sInfo.fccHandler = comptypeDIB;
01740 
01741         /* init rcFrame if it's empty */
01742         if (IsRectEmpty(&pStream->sInfo.rcFrame))
01743           SetRect(&pStream->sInfo.rcFrame, 0, 0, lpbi->biWidth, lpbi->biHeight);
01744       }
01745       break;
01746     case ckidSTREAMHEADER:
01747       {
01748         static const WCHAR streamTypeFmt[] = {'%','4','.','4','h','s',0};
01749         static const WCHAR streamNameFmt[] = {'%','s',' ','%','s',' ','#','%','d',0};
01750 
01751         AVIStreamHeader streamHdr;
01752         WCHAR           szType[25];
01753         UINT            count;
01754         LONG            n = ck.cksize;
01755 
01756         if (ck.cksize > sizeof(streamHdr))
01757           n = sizeof(streamHdr);
01758 
01759         if (mmioRead(This->hmmio, (HPSTR)&streamHdr, n) != n)
01760           return AVIERR_FILEREAD;
01761 
01762         pStream->sInfo.fccType               = streamHdr.fccType;
01763         pStream->sInfo.fccHandler            = streamHdr.fccHandler;
01764         pStream->sInfo.dwFlags               = streamHdr.dwFlags;
01765         pStream->sInfo.wPriority             = streamHdr.wPriority;
01766         pStream->sInfo.wLanguage             = streamHdr.wLanguage;
01767         pStream->sInfo.dwInitialFrames       = streamHdr.dwInitialFrames;
01768         pStream->sInfo.dwScale               = streamHdr.dwScale;
01769         pStream->sInfo.dwRate                = streamHdr.dwRate;
01770         pStream->sInfo.dwStart               = streamHdr.dwStart;
01771         pStream->sInfo.dwLength              = streamHdr.dwLength;
01772         pStream->sInfo.dwSuggestedBufferSize = 0;
01773         pStream->sInfo.dwQuality             = streamHdr.dwQuality;
01774         pStream->sInfo.dwSampleSize          = streamHdr.dwSampleSize;
01775         pStream->sInfo.rcFrame.left          = streamHdr.rcFrame.left;
01776         pStream->sInfo.rcFrame.top           = streamHdr.rcFrame.top;
01777         pStream->sInfo.rcFrame.right         = streamHdr.rcFrame.right;
01778         pStream->sInfo.rcFrame.bottom        = streamHdr.rcFrame.bottom;
01779         pStream->sInfo.dwEditCount           = 0;
01780         pStream->sInfo.dwFormatChangeCount   = 0;
01781 
01782         /* generate description for stream like "filename.avi Type #n" */
01783         if (streamHdr.fccType == streamtypeVIDEO)
01784           LoadStringW(AVIFILE_hModule, IDS_VIDEO, szType, sizeof(szType)/sizeof(szType[0]));
01785         else if (streamHdr.fccType == streamtypeAUDIO)
01786           LoadStringW(AVIFILE_hModule, IDS_AUDIO, szType, sizeof(szType)/sizeof(szType[0]));
01787         else
01788           wsprintfW(szType, streamTypeFmt, (char*)&streamHdr.fccType);
01789 
01790         /* get count of this streamtype up to this stream */
01791         count = 0;
01792         for (n = nStream; 0 <= n; n--) {
01793           if (This->ppStreams[n]->sInfo.fccHandler == streamHdr.fccType)
01794         count++;
01795         }
01796 
01797         memset(pStream->sInfo.szName, 0, sizeof(pStream->sInfo.szName));
01798 
01799         /* FIXME: avoid overflow -- better use wsnprintfW, which doesn't exists ! */
01800         wsprintfW(pStream->sInfo.szName, streamNameFmt,
01801               AVIFILE_BasenameW(This->szFileName), szType, count);
01802       }
01803       break;
01804     case ckidSTREAMNAME:
01805       { /* streamname will be saved as ASCII string */
01806         LPSTR str = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
01807         if (str == NULL)
01808           return AVIERR_MEMORY;
01809 
01810         if (mmioRead(This->hmmio, str, ck.cksize) != ck.cksize)
01811         {
01812           HeapFree(GetProcessHeap(), 0, str);
01813           return AVIERR_FILEREAD;
01814         }
01815 
01816         MultiByteToWideChar(CP_ACP, 0, str, -1, pStream->sInfo.szName,
01817                 sizeof(pStream->sInfo.szName)/sizeof(pStream->sInfo.szName[0]));
01818 
01819         HeapFree(GetProcessHeap(), 0, str);
01820       }
01821       break;
01822     case ckidAVIPADDING:
01823     case mmioFOURCC('p','a','d','d'):
01824       break;
01825     default:
01826           WARN(": found extra chunk 0x%08X\n", ck.ckid);
01827       hr = ReadChunkIntoExtra(&pStream->extra, This->hmmio, &ck);
01828       if (FAILED(hr))
01829         return hr;
01830     };
01831     if (pStream->lpFormat != NULL && pStream->sInfo.fccType == streamtypeAUDIO)
01832     {
01833       WAVEFORMATEX *wfx = pStream->lpFormat;          /* wfx->nBlockAlign = wfx->nChannels * wfx->wBitsPerSample / 8; could be added */
01834       pStream->sInfo.dwSampleSize = wfx->nBlockAlign; /* to deal with corrupt wfx->nBlockAlign but Windows doesn't do this */
01835       TRACE("Block size reset to %u, chan=%u bpp=%u\n", wfx->nBlockAlign, wfx->nChannels, wfx->wBitsPerSample);
01836       pStream->sInfo.dwScale = 1;
01837       pStream->sInfo.dwRate = wfx->nSamplesPerSec;
01838     }
01839     if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
01840       return AVIERR_FILEREAD;
01841       }
01842     } else {
01843       /* nested chunks in "LIST","hdrl" which are not of type "LIST","strl" */
01844       hr = ReadChunkIntoExtra(&This->fileextra, This->hmmio, &ckLIST2);
01845       if (FAILED(hr))
01846     return hr;
01847     }
01848     if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
01849       return AVIERR_FILEREAD;
01850   }
01851 
01852   /* read any extra headers in "LIST","hdrl" */
01853   FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, 0);
01854   if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
01855     return AVIERR_FILEREAD;
01856 
01857   /* search "LIST","movi" chunk in "RIFF","AVI " */
01858   ckLIST1.fccType = listtypeAVIMOVIE;
01859   hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF,
01860                   MMIO_FINDLIST);
01861   if (FAILED(hr))
01862     return hr;
01863 
01864   This->dwMoviChunkPos = ckLIST1.dwDataOffset;
01865   This->dwIdxChunkPos  = ckLIST1.cksize + ckLIST1.dwDataOffset;
01866   if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
01867     return AVIERR_FILEREAD;
01868 
01869   /* try to find an index */
01870   ck.ckid = ckidAVINEWINDEX;
01871   hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio,
01872                   &ck, &ckRIFF, MMIO_FINDCHUNK);
01873   if (SUCCEEDED(hr) && ck.cksize > 0) {
01874     if (FAILED(AVIFILE_LoadIndex(This, ck.cksize, ckLIST1.dwDataOffset)))
01875       This->fInfo.dwFlags &= ~AVIFILEINFO_HASINDEX;
01876   }
01877 
01878   /* when we haven't found an index or it's bad, then build one
01879    * by parsing 'movi' chunk */
01880   if ((This->fInfo.dwFlags & AVIFILEINFO_HASINDEX) == 0) {
01881     for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
01882       This->ppStreams[nStream]->lLastFrame = -1;
01883 
01884     if (mmioSeek(This->hmmio, ckLIST1.dwDataOffset + sizeof(DWORD), SEEK_SET) == -1) {
01885       ERR(": Oops, can't seek back to 'movi' chunk!\n");
01886       return AVIERR_FILEREAD;
01887     }
01888 
01889     /* seek through the 'movi' list until end */
01890     while (mmioDescend(This->hmmio, &ck, &ckLIST1, 0) == S_OK) {
01891       if (ck.ckid != FOURCC_LIST) {
01892     if (mmioAscend(This->hmmio, &ck, 0) == S_OK) {
01893       nStream = StreamFromFOURCC(ck.ckid);
01894 
01895       if (nStream > This->fInfo.dwStreams)
01896         return AVIERR_BADFORMAT;
01897 
01898       AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
01899                ck.dwDataOffset - 2 * sizeof(DWORD), 0);
01900     } else {
01901       nStream = StreamFromFOURCC(ck.ckid);
01902       WARN(": file seems to be truncated!\n");
01903       if (nStream <= This->fInfo.dwStreams &&
01904           This->ppStreams[nStream]->sInfo.dwSampleSize > 0) {
01905         ck.cksize = mmioSeek(This->hmmio, 0, SEEK_END);
01906         if (ck.cksize != -1) {
01907           ck.cksize -= ck.dwDataOffset;
01908           ck.cksize &= ~(This->ppStreams[nStream]->sInfo.dwSampleSize - 1);
01909 
01910           AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
01911                    ck.dwDataOffset - 2 * sizeof(DWORD), 0);
01912         }
01913       }
01914     }
01915       }
01916     }
01917   }
01918 
01919   for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
01920   {
01921     DWORD sugbuf =  This->ppStreams[nStream]->sInfo.dwSuggestedBufferSize;
01922     if (This->fInfo.dwSuggestedBufferSize < sugbuf)
01923       This->fInfo.dwSuggestedBufferSize = sugbuf;
01924   }
01925 
01926   /* find other chunks */
01927   FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckRIFF, 0);
01928 
01929   return AVIERR_OK;
01930 }
01931 
01932 static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset)
01933 {
01934   AVIINDEXENTRY *lp;
01935   DWORD          pos, n;
01936   HRESULT        hr = AVIERR_OK;
01937   BOOL           bAbsolute = TRUE;
01938 
01939   lp = HeapAlloc(GetProcessHeap(), 0, IDX_PER_BLOCK * sizeof(AVIINDEXENTRY));
01940   if (lp == NULL)
01941     return AVIERR_MEMORY;
01942 
01943   /* adjust limits for index tables, so that inserting will be faster */
01944   for (n = 0; n < This->fInfo.dwStreams; n++) {
01945     IAVIStreamImpl *pStream = This->ppStreams[n];
01946 
01947     pStream->lLastFrame = -1;
01948 
01949     if (pStream->idxFrames != NULL) {
01950       HeapFree(GetProcessHeap(), 0, pStream->idxFrames);
01951       pStream->idxFrames  = NULL;
01952       pStream->nIdxFrames = 0;
01953     }
01954 
01955     if (pStream->sInfo.dwSampleSize != 0) {
01956       if (n > 0 && This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
01957     pStream->nIdxFrames = This->ppStreams[0]->nIdxFrames;
01958       } else if (pStream->sInfo.dwSuggestedBufferSize) {
01959     pStream->nIdxFrames =
01960       pStream->sInfo.dwLength / pStream->sInfo.dwSuggestedBufferSize;
01961       }
01962     } else
01963       pStream->nIdxFrames = pStream->sInfo.dwLength;
01964 
01965     pStream->idxFrames =
01966       HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pStream->nIdxFrames * sizeof(AVIINDEXENTRY));
01967     if (pStream->idxFrames == NULL && pStream->nIdxFrames > 0) {
01968       pStream->nIdxFrames = 0;
01969       HeapFree(GetProcessHeap(), 0, lp);
01970       return AVIERR_MEMORY;
01971     }
01972   }
01973 
01974   pos = (DWORD)-1;
01975   while (size != 0) {
01976     LONG read = min(IDX_PER_BLOCK * sizeof(AVIINDEXENTRY), size);
01977 
01978     if (mmioRead(This->hmmio, (HPSTR)lp, read) != read) {
01979       hr = AVIERR_FILEREAD;
01980       break;
01981     }
01982     size -= read;
01983 
01984     if (pos == (DWORD)-1)
01985       pos = offset - lp->dwChunkOffset + sizeof(DWORD);
01986 
01987     AVIFILE_ParseIndex(This, lp, read / sizeof(AVIINDEXENTRY),
01988                pos, &bAbsolute);
01989   }
01990 
01991   HeapFree(GetProcessHeap(), 0, lp);
01992 
01993   /* checking ... */
01994   for (n = 0; n < This->fInfo.dwStreams; n++) {
01995     IAVIStreamImpl *pStream = This->ppStreams[n];
01996 
01997     if (pStream->sInfo.dwSampleSize == 0 &&
01998     pStream->sInfo.dwLength != pStream->lLastFrame+1)
01999       ERR("stream %u length mismatch: dwLength=%u found=%d\n",
02000        n, pStream->sInfo.dwLength, pStream->lLastFrame);
02001   }
02002 
02003   return hr;
02004 }
02005 
02006 static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp,
02007                   LONG count, DWORD pos, BOOL *bAbsolute)
02008 {
02009   if (lp == NULL)
02010     return AVIERR_BADPARAM;
02011 
02012   for (; count > 0; count--, lp++) {
02013     WORD nStream = StreamFromFOURCC(lp->ckid);
02014 
02015     if (lp->ckid == listtypeAVIRECORD || nStream == 0x7F)
02016       continue; /* skip these */
02017 
02018     if (nStream > This->fInfo.dwStreams)
02019       return AVIERR_BADFORMAT;
02020 
02021     if (*bAbsolute && lp->dwChunkOffset < This->dwMoviChunkPos)
02022       *bAbsolute = FALSE;
02023 
02024     if (*bAbsolute)
02025       lp->dwChunkOffset += sizeof(DWORD);
02026     else
02027       lp->dwChunkOffset += pos;
02028 
02029     if (FAILED(AVIFILE_AddFrame(This->ppStreams[nStream], lp->ckid, lp->dwChunkLength, lp->dwChunkOffset, lp->dwFlags)))
02030       return AVIERR_MEMORY;
02031   }
02032 
02033   return AVIERR_OK;
02034 }
02035 
02036 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD pos,
02037                  LPVOID buffer, DWORD size)
02038 {
02039   /* pre-conditions */
02040   assert(This != NULL);
02041   assert(This->paf != NULL);
02042   assert(This->paf->hmmio != NULL);
02043   assert(This->sInfo.dwStart <= pos && pos < This->sInfo.dwLength);
02044   assert(pos <= This->lLastFrame);
02045 
02046   /* should we read as much as block gives us? */
02047   if (size == 0 || size > This->idxFrames[pos].dwChunkLength)
02048     size = This->idxFrames[pos].dwChunkLength;
02049 
02050   /* read into out own buffer or given one? */
02051   if (buffer == NULL) {
02052     /* we also read the chunk */
02053     size += 2 * sizeof(DWORD);
02054 
02055     /* check that buffer is big enough -- don't trust dwSuggestedBufferSize */
02056     if (This->lpBuffer == NULL || This->cbBuffer < size) {
02057       DWORD maxSize = max(size, This->sInfo.dwSuggestedBufferSize);
02058 
02059       if (This->lpBuffer == NULL) {
02060     This->lpBuffer = HeapAlloc(GetProcessHeap(), 0, maxSize);
02061         if (!This->lpBuffer) return AVIERR_MEMORY;
02062       } else {
02063         void *new_buffer = HeapReAlloc(GetProcessHeap(), 0, This->lpBuffer, maxSize);
02064         if (!new_buffer) return AVIERR_MEMORY;
02065         This->lpBuffer = new_buffer;
02066       }
02067       This->cbBuffer = maxSize;
02068     }
02069 
02070     /* now read the complete chunk into our buffer */
02071     if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset, SEEK_SET) == -1)
02072       return AVIERR_FILEREAD;
02073     if (mmioRead(This->paf->hmmio, (HPSTR)This->lpBuffer, size) != size)
02074       return AVIERR_FILEREAD;
02075 
02076     /* check if it was the correct block which we have read */
02077     if (This->lpBuffer[0] != This->idxFrames[pos].ckid ||
02078     This->lpBuffer[1] != This->idxFrames[pos].dwChunkLength) {
02079       ERR(": block %d not found at 0x%08X\n", pos, This->idxFrames[pos].dwChunkOffset);
02080       ERR(": Index says: '%4.4s'(0x%08X) size 0x%08X\n",
02081       (char*)&This->idxFrames[pos].ckid, This->idxFrames[pos].ckid,
02082       This->idxFrames[pos].dwChunkLength);
02083       ERR(": Data  says: '%4.4s'(0x%08X) size 0x%08X\n",
02084       (char*)&This->lpBuffer[0], This->lpBuffer[0], This->lpBuffer[1]);
02085       return AVIERR_FILEREAD;
02086     }
02087   } else {
02088     if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD), SEEK_SET) == -1)
02089       return AVIERR_FILEREAD;
02090     if (mmioRead(This->paf->hmmio, buffer, size) != size)
02091       return AVIERR_FILEREAD;
02092   }
02093 
02094   return AVIERR_OK;
02095 }
02096 
02097 static void AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos, LPLONG offset)
02098 {
02099   LONG block;
02100 
02101   /* pre-conditions */
02102   assert(This != NULL);
02103   assert(pos != NULL);
02104   assert(offset != NULL);
02105   assert(This->sInfo.dwSampleSize != 0);
02106   assert(*pos >= This->sInfo.dwStart);
02107 
02108   /* convert start sample to start bytes */
02109   (*offset)  = (*pos) - This->sInfo.dwStart;
02110   (*offset) *= This->sInfo.dwSampleSize;
02111 
02112   /* convert bytes to block number */
02113   for (block = 0; block <= This->lLastFrame; block++) {
02114     if (This->idxFrames[block].dwChunkLength <= *offset)
02115       (*offset) -= This->idxFrames[block].dwChunkLength;
02116     else
02117       break;
02118   }
02119 
02120   *pos = block;
02121 }
02122 
02123 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This)
02124 {
02125   MainAVIHeader   MainAVIHdr;
02126   IAVIStreamImpl* pStream;
02127   MMCKINFO        ckRIFF;
02128   MMCKINFO        ckLIST1;
02129   MMCKINFO        ckLIST2;
02130   MMCKINFO        ck;
02131   DWORD           nStream;
02132   DWORD           dwPos;
02133   HRESULT         hr;
02134 
02135   /* initialize some things */
02136   if (This->dwMoviChunkPos == 0)
02137     AVIFILE_ComputeMoviStart(This);
02138 
02139   /* written one record to much? */
02140   if (This->ckLastRecord.dwFlags & MMIO_DIRTY) {
02141     This->dwNextFramePos -= 3 * sizeof(DWORD);
02142     if (This->nIdxRecords > 0)
02143       This->nIdxRecords--;
02144   }
02145 
02146   AVIFILE_UpdateInfo(This);
02147 
02148   assert(This->fInfo.dwScale != 0);
02149 
02150   memset(&MainAVIHdr, 0, sizeof(MainAVIHdr));
02151   MainAVIHdr.dwMicroSecPerFrame    = MulDiv(This->fInfo.dwRate, 1000000,
02152                         This->fInfo.dwScale);
02153   MainAVIHdr.dwMaxBytesPerSec      = This->fInfo.dwMaxBytesPerSec;
02154   MainAVIHdr.dwPaddingGranularity  = AVI_HEADERSIZE;
02155   MainAVIHdr.dwFlags               = This->fInfo.dwFlags;
02156   MainAVIHdr.dwTotalFrames         = This->fInfo.dwLength;
02157   MainAVIHdr.dwInitialFrames       = 0;
02158   MainAVIHdr.dwStreams             = This->fInfo.dwStreams;
02159   MainAVIHdr.dwSuggestedBufferSize = This->fInfo.dwSuggestedBufferSize;
02160   MainAVIHdr.dwWidth               = This->fInfo.dwWidth;
02161   MainAVIHdr.dwHeight              = This->fInfo.dwHeight;
02162   MainAVIHdr.dwInitialFrames       = This->dwInitialFrames;
02163 
02164   /* now begin writing ... */
02165   mmioSeek(This->hmmio, 0, SEEK_SET);
02166 
02167   /* RIFF chunk */
02168   ckRIFF.cksize  = 0;
02169   ckRIFF.fccType = formtypeAVI;
02170   if (mmioCreateChunk(This->hmmio, &ckRIFF, MMIO_CREATERIFF) != S_OK)
02171     return AVIERR_FILEWRITE;
02172 
02173   /* AVI headerlist */
02174   ckLIST1.cksize  = 0;
02175   ckLIST1.fccType = listtypeAVIHEADER;
02176   if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
02177     return AVIERR_FILEWRITE;
02178 
02179   /* MainAVIHeader */
02180   ck.ckid    = ckidAVIMAINHDR;
02181   ck.cksize  = sizeof(MainAVIHdr);
02182   ck.fccType = 0;
02183   if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
02184     return AVIERR_FILEWRITE;
02185   if (mmioWrite(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
02186     return AVIERR_FILEWRITE;
02187   if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
02188     return AVIERR_FILEWRITE;
02189 
02190   /* write the headers of each stream into a separate streamheader list */
02191   for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
02192     AVIStreamHeader strHdr;
02193 
02194     pStream = This->ppStreams[nStream];
02195 
02196     /* begin the new streamheader list */
02197     ckLIST2.cksize  = 0;
02198     ckLIST2.fccType = listtypeSTREAMHEADER;
02199     if (mmioCreateChunk(This->hmmio, &ckLIST2, MMIO_CREATELIST) != S_OK)
02200       return AVIERR_FILEWRITE;
02201 
02202     /* create an AVIStreamHeader from the AVSTREAMINFO */
02203     strHdr.fccType               = pStream->sInfo.fccType;
02204     strHdr.fccHandler            = pStream->sInfo.fccHandler;
02205     strHdr.dwFlags               = pStream->sInfo.dwFlags;
02206     strHdr.wPriority             = pStream->sInfo.wPriority;
02207     strHdr.wLanguage             = pStream->sInfo.wLanguage;
02208     strHdr.dwInitialFrames       = pStream->sInfo.dwInitialFrames;
02209     strHdr.dwScale               = pStream->sInfo.dwScale;
02210     strHdr.dwRate                = pStream->sInfo.dwRate;
02211     strHdr.dwStart               = pStream->sInfo.dwStart;
02212     strHdr.dwLength              = pStream->sInfo.dwLength;
02213     strHdr.dwSuggestedBufferSize = pStream->sInfo.dwSuggestedBufferSize;
02214     strHdr.dwQuality             = pStream->sInfo.dwQuality;
02215     strHdr.dwSampleSize          = pStream->sInfo.dwSampleSize;
02216     strHdr.rcFrame.left          = pStream->sInfo.rcFrame.left;
02217     strHdr.rcFrame.top           = pStream->sInfo.rcFrame.top;
02218     strHdr.rcFrame.right         = pStream->sInfo.rcFrame.right;
02219     strHdr.rcFrame.bottom        = pStream->sInfo.rcFrame.bottom;
02220 
02221     /* now write the AVIStreamHeader */
02222     ck.ckid   = ckidSTREAMHEADER;
02223     ck.cksize = sizeof(strHdr);
02224     if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
02225       return AVIERR_FILEWRITE;
02226     if (mmioWrite(This->hmmio, (HPSTR)&strHdr, ck.cksize) != ck.cksize)
02227       return AVIERR_FILEWRITE;
02228     if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
02229       return AVIERR_FILEWRITE;
02230 
02231     /* ... the hopefully ever present streamformat ... */
02232     ck.ckid   = ckidSTREAMFORMAT;
02233     ck.cksize = pStream->cbFormat;
02234     if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
02235       return AVIERR_FILEWRITE;
02236     if (pStream->lpFormat != NULL && ck.cksize > 0) {
02237       if (mmioWrite(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize)
02238     return AVIERR_FILEWRITE;
02239     }
02240     if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
02241       return AVIERR_FILEWRITE;
02242 
02243     /* ... some optional existing handler data ... */
02244     if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0) {
02245       ck.ckid   = ckidSTREAMHANDLERDATA;
02246       ck.cksize = pStream->cbHandlerData;
02247       if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
02248     return AVIERR_FILEWRITE;
02249       if (mmioWrite(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize)
02250     return AVIERR_FILEWRITE;
02251       if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
02252     return AVIERR_FILEWRITE;
02253     }
02254 
02255     /* ... some optional additional extra chunk for this stream ... */
02256     if (pStream->extra.lp != NULL && pStream->extra.cb > 0) {
02257       /* the chunk header(s) are already in the structure */
02258       if (mmioWrite(This->hmmio, pStream->extra.lp, pStream->extra.cb) != pStream->extra.cb)
02259     return AVIERR_FILEWRITE;
02260     }
02261 
02262     /* ... an optional name for this stream ... */
02263     if (lstrlenW(pStream->sInfo.szName) > 0) {
02264       LPSTR str;
02265 
02266       ck.ckid   = ckidSTREAMNAME;
02267       ck.cksize = lstrlenW(pStream->sInfo.szName) + 1;
02268       if (ck.cksize & 1) /* align */
02269     ck.cksize++;
02270       if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
02271     return AVIERR_FILEWRITE;
02272 
02273       /* the streamname must be saved in ASCII not Unicode */
02274       str = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
02275       if (str == NULL)
02276     return AVIERR_MEMORY;
02277       WideCharToMultiByte(CP_ACP, 0, pStream->sInfo.szName, -1, str,
02278               ck.cksize, NULL, NULL);
02279 
02280       if (mmioWrite(This->hmmio, str, ck.cksize) != ck.cksize) {
02281     HeapFree(GetProcessHeap(), 0, str); 
02282     return AVIERR_FILEWRITE;
02283       }
02284 
02285       HeapFree(GetProcessHeap(), 0, str);
02286       if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
02287     return AVIERR_FILEWRITE;
02288     }
02289 
02290     /* close streamheader list for this stream */
02291     if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
02292       return AVIERR_FILEWRITE;
02293   } /* for (0 <= nStream < MainAVIHdr.dwStreams) */
02294 
02295   /* close the aviheader list */
02296   if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
02297     return AVIERR_FILEWRITE;
02298 
02299   /* check for padding to pre-guessed 'movi'-chunk position */
02300   dwPos = ckLIST1.dwDataOffset + ckLIST1.cksize;
02301   if (This->dwMoviChunkPos - 2 * sizeof(DWORD) > dwPos) {
02302     ck.ckid   = ckidAVIPADDING;
02303     ck.cksize = This->dwMoviChunkPos - dwPos - 4 * sizeof(DWORD);
02304     assert((LONG)ck.cksize >= 0);
02305 
02306     if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
02307       return AVIERR_FILEWRITE;
02308     if (mmioSeek(This->hmmio, ck.cksize, SEEK_CUR) == -1)
02309       return AVIERR_FILEWRITE;
02310     if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
02311       return AVIERR_FILEWRITE;
02312   }
02313 
02314   /* now write the 'movi' chunk */
02315   mmioSeek(This->hmmio, This->dwMoviChunkPos - 2 * sizeof(DWORD), SEEK_SET);
02316   ckLIST1.cksize  = 0;
02317   ckLIST1.fccType = listtypeAVIMOVIE;
02318   if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
02319     return AVIERR_FILEWRITE;
02320   if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1)
02321     return AVIERR_FILEWRITE;
02322   if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
02323     return AVIERR_FILEWRITE;
02324 
02325   /* write 'idx1' chunk */
02326   hr = AVIFILE_SaveIndex(This);
02327   if (FAILED(hr))
02328     return hr;
02329 
02330   /* write optional extra file chunks */
02331   if (This->fileextra.lp != NULL && This->fileextra.cb > 0) {
02332     /* as for the streams, are the chunk header(s) in the structure */
02333     if (mmioWrite(This->hmmio, This->fileextra.lp, This->fileextra.cb) != This->fileextra.cb)
02334       return AVIERR_FILEWRITE;
02335   }
02336 
02337   /* close RIFF chunk */
02338   if (mmioAscend(This->hmmio, &ckRIFF, 0) != S_OK)
02339     return AVIERR_FILEWRITE;
02340 
02341   /* add some JUNK at end for bad parsers */
02342   memset(&ckRIFF, 0, sizeof(ckRIFF));
02343   mmioWrite(This->hmmio, (HPSTR)&ckRIFF, sizeof(ckRIFF));
02344   mmioFlush(This->hmmio, 0);
02345 
02346   return AVIERR_OK;
02347 }
02348 
02349 static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This)
02350 {
02351   IAVIStreamImpl *pStream;
02352   AVIINDEXENTRY   idx;
02353   MMCKINFO        ck;
02354   DWORD           nStream;
02355   LONG            n;
02356 
02357   ck.ckid   = ckidAVINEWINDEX;
02358   ck.cksize = 0;
02359   if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
02360     return AVIERR_FILEWRITE;
02361 
02362   if (This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
02363     /* is interleaved -- write block of corresponding frames */
02364     LONG lInitialFrames = 0;
02365     LONG stepsize;
02366     LONG i;
02367 
02368     if (This->ppStreams[0]->sInfo.dwSampleSize == 0)
02369       stepsize = 1;
02370     else
02371       stepsize = AVIStreamTimeToSample((PAVISTREAM)This->ppStreams[0], 1000000);
02372 
02373     assert(stepsize > 0);
02374 
02375     for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
02376       if (lInitialFrames < This->ppStreams[nStream]->sInfo.dwInitialFrames)
02377     lInitialFrames = This->ppStreams[nStream]->sInfo.dwInitialFrames;
02378     }
02379 
02380     for (i = -lInitialFrames; i < (LONG)This->fInfo.dwLength - lInitialFrames;
02381      i += stepsize) {
02382       DWORD nFrame = lInitialFrames + i;
02383 
02384       assert(nFrame < This->nIdxRecords);
02385 
02386       idx.ckid          = listtypeAVIRECORD;
02387       idx.dwFlags       = AVIIF_LIST;
02388       idx.dwChunkLength = This->idxRecords[nFrame].dwChunkLength;
02389       idx.dwChunkOffset = This->idxRecords[nFrame].dwChunkOffset
02390     - This->dwMoviChunkPos;
02391       if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
02392     return AVIERR_FILEWRITE;
02393 
02394       for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
02395     pStream = This->ppStreams[nStream];
02396 
02397     /* heave we reached start of this stream? */
02398     if (-(LONG)pStream->sInfo.dwInitialFrames > i)
02399       continue;
02400 
02401     if (pStream->sInfo.dwInitialFrames < lInitialFrames)
02402       nFrame -= (lInitialFrames - pStream->sInfo.dwInitialFrames);
02403 
02404     /* reached end of this stream? */
02405     if (pStream->lLastFrame <= nFrame)
02406       continue;
02407 
02408     if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
02409         pStream->sInfo.dwFormatChangeCount != 0 &&
02410         pStream->idxFmtChanges != NULL) {
02411       DWORD pos;
02412 
02413       for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
02414         if (pStream->idxFmtChanges[pos].ckid == nFrame) {
02415           idx.dwFlags = AVIIF_NOTIME;
02416           idx.ckid    = MAKEAVICKID(cktypePALchange, pStream->nStream);
02417           idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
02418           idx.dwChunkOffset = pStream->idxFmtChanges[pos].dwChunkOffset
02419         - This->dwMoviChunkPos;
02420 
02421           if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
02422         return AVIERR_FILEWRITE;
02423           break;
02424         }
02425       }
02426     } /* if have formatchanges */
02427 
02428     idx.ckid          = pStream->idxFrames[nFrame].ckid;
02429     idx.dwFlags       = pStream->idxFrames[nFrame].dwFlags;
02430     idx.dwChunkLength = pStream->idxFrames[nFrame].dwChunkLength;
02431     idx.dwChunkOffset = pStream->idxFrames[nFrame].dwChunkOffset
02432       - This->dwMoviChunkPos;
02433     if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
02434       return AVIERR_FILEWRITE;
02435       }
02436     }
02437   } else {
02438     /* not interleaved -- write index for each stream at once */
02439     for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
02440       pStream = This->ppStreams[nStream];
02441 
02442       for (n = 0; n <= pStream->lLastFrame; n++) {
02443     if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
02444         (pStream->sInfo.dwFormatChangeCount != 0)) {
02445       DWORD pos;
02446 
02447       for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
02448         if (pStream->idxFmtChanges[pos].ckid == n) {
02449           idx.dwFlags = AVIIF_NOTIME;
02450           idx.ckid    = MAKEAVICKID(cktypePALchange, pStream->nStream);
02451           idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
02452           idx.dwChunkOffset =
02453         pStream->idxFmtChanges[pos].dwChunkOffset - This->dwMoviChunkPos;
02454           if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
02455         return AVIERR_FILEWRITE;
02456           break;
02457         }
02458       }
02459     } /* if have formatchanges */
02460 
02461     idx.ckid          = pStream->idxFrames[n].ckid;
02462     idx.dwFlags       = pStream->idxFrames[n].dwFlags;
02463     idx.dwChunkLength = pStream->idxFrames[n].dwChunkLength;
02464     idx.dwChunkOffset = pStream->idxFrames[n].dwChunkOffset
02465       - This->dwMoviChunkPos;
02466 
02467     if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
02468       return AVIERR_FILEWRITE;
02469       }
02470     }
02471   } /* if not interleaved */
02472 
02473   if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
02474     return AVIERR_FILEWRITE;
02475 
02476   return AVIERR_OK;
02477 }
02478 
02479 static ULONG  AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fcc, LONG lSkip)
02480 {
02481   UINT i;
02482   UINT nStream;
02483 
02484   /* pre-condition */
02485   assert(lSkip >= 0);
02486 
02487   if (fcc != 0) {
02488     /* search the number of the specified stream */
02489     nStream = (ULONG)-1;
02490     for (i = 0; i < This->fInfo.dwStreams; i++) {
02491       assert(This->ppStreams[i] != NULL);
02492 
02493       if (This->ppStreams[i]->sInfo.fccType == fcc) {
02494     if (lSkip == 0) {
02495       nStream = i;
02496       break;
02497     } else
02498       lSkip--;
02499       }
02500     }
02501   } else
02502     nStream = lSkip;
02503 
02504   return nStream;
02505 }
02506 
02507 static void    AVIFILE_UpdateInfo(IAVIFileImpl *This)
02508 {
02509   UINT i;
02510 
02511   /* pre-conditions */
02512   assert(This != NULL);
02513 
02514   This->fInfo.dwMaxBytesPerSec      = 0;
02515   This->fInfo.dwCaps                = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
02516   This->fInfo.dwSuggestedBufferSize = 0;
02517   This->fInfo.dwWidth               = 0;
02518   This->fInfo.dwHeight              = 0;
02519   This->fInfo.dwScale               = 0;
02520   This->fInfo.dwRate                = 0;
02521   This->fInfo.dwLength              = 0;
02522   This->dwInitialFrames             = 0;
02523 
02524   for (i = 0; i < This->fInfo.dwStreams; i++) {
02525     AVISTREAMINFOW *psi;
02526     DWORD           n;
02527 
02528     /* pre-conditions */
02529     assert(This->ppStreams[i] != NULL);
02530 
02531     psi = &This->ppStreams[i]->sInfo;
02532     assert(psi->dwScale != 0);
02533     assert(psi->dwRate != 0);
02534 
02535     if (i == 0) {
02536       /* use first stream timings as base */
02537       This->fInfo.dwScale  = psi->dwScale;
02538       This->fInfo.dwRate   = psi->dwRate;
02539       This->fInfo.dwLength = psi->dwLength;
02540     } else {
02541       n = AVIStreamSampleToSample((PAVISTREAM)This->ppStreams[0],
02542                   (PAVISTREAM)This->ppStreams[i],psi->dwLength);
02543       if (This->fInfo.dwLength < n)
02544     This->fInfo.dwLength = n;
02545     }
02546 
02547     if (This->dwInitialFrames < psi->dwInitialFrames)
02548       This->dwInitialFrames = psi->dwInitialFrames;
02549 
02550     if (This->fInfo.dwSuggestedBufferSize < psi->dwSuggestedBufferSize)
02551       This->fInfo.dwSuggestedBufferSize = psi->dwSuggestedBufferSize;
02552 
02553     if (psi->dwSampleSize != 0) {
02554       /* fixed sample size -- exact computation */
02555       This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSampleSize, psi->dwRate,
02556                          psi->dwScale);
02557     } else {
02558       /* variable sample size -- only upper limit */
02559       This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSuggestedBufferSize,
02560                          psi->dwRate, psi->dwScale);
02561 
02562       /* update dimensions */
02563       n = psi->rcFrame.right - psi->rcFrame.left;
02564       if (This->fInfo.dwWidth < n)
02565     This->fInfo.dwWidth = n;
02566       n = psi->rcFrame.bottom - psi->rcFrame.top;
02567       if (This->fInfo.dwHeight < n)
02568     This->fInfo.dwHeight = n;
02569     }
02570   }
02571 }
02572 
02573 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
02574                   FOURCC ckid, DWORD flags, LPCVOID buffer,
02575                   LONG size)
02576 {
02577   MMCKINFO ck;
02578 
02579   ck.ckid    = ckid;
02580   ck.cksize  = size;
02581   ck.fccType = 0;
02582 
02583   /* if no frame/block is already written, we must compute start of movi chunk */
02584   if (This->paf->dwMoviChunkPos == 0)
02585     AVIFILE_ComputeMoviStart(This->paf);
02586 
02587   if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1)
02588     return AVIERR_FILEWRITE;
02589 
02590   if (mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK)
02591     return AVIERR_FILEWRITE;
02592   if (buffer != NULL && size > 0) {
02593     if (mmioWrite(This->paf->hmmio, buffer, size) != size)
02594       return AVIERR_FILEWRITE;
02595   }
02596   if (mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
02597     return AVIERR_FILEWRITE;
02598 
02599   This->paf->fDirty         = TRUE;
02600   This->paf->dwNextFramePos = mmioSeek(This->paf->hmmio, 0, SEEK_CUR);
02601 
02602   return AVIFILE_AddFrame(This, ckid, size,
02603               ck.dwDataOffset - 2 * sizeof(DWORD), flags);
02604 }

Generated on Fri May 25 2012 04:20:49 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.