~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/avifil32/avifile.c

Version: ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~ [ wine-1.0-rc5 ] ~ [ wine-1.0-rc4 ] ~ [ wine-1.0-rc3 ] ~ [ wine-1.0-rc2 ] ~ [ wine-1.0-rc1 ] ~ [ wine-0.9.61 ] ~ [ wine-0.9.60 ] ~ [ wine-0.9.59 ] ~ [ wine-0.9.58 ] ~ [ wine-0.9.57 ] ~ [ wine-0.9.56 ] ~ [ wine-0.9.55 ] ~ [ wine-0.9.54 ] ~ [ wine-0.9.53 ] ~ [ wine-0.9.52 ] ~ [ wine-0.9.51 ] ~ [ wine-0.9.50 ] ~ [ wine-0.9.49 ] ~ [ wine-0.9.48 ] ~ [ wine-0.9.47 ] ~ [ wine-0.9.46 ] ~ [ wine-0.9.45 ] ~ [ wine-0.9.44 ] ~ [ wine-0.9.43 ] ~ [ wine-0.9.42 ] ~ [ wine-0.9.41 ] ~ [ wine-0.9.40 ] ~ [ wine-0.9.39 ] ~ [ wine-0.9.38 ] ~ [ wine-0.9.37 ] ~ [ wine-0.9.36 ] ~ [ wine-0.9.35 ] ~ [ wine-0.9.34 ] ~ [ wine-0.9.33 ] ~ [ wine-0.9.32 ] ~ [ wine-0.9.31 ] ~ [ wine-0.9.30 ] ~ [ wine-0.9.29 ] ~ [ wine-0.9.28 ] ~ [ wine-0.9.27 ] ~ [ wine-0.9.26 ] ~ [ wine-0.9.25 ] ~ [ wine-0.9.24 ] ~ [ wine-0.9.23 ] ~ [ wine-0.9.22 ] ~ [ wine-0.9.21 ] ~ [ wine-0.9.20 ] ~ [ wine-0.9.19 ] ~ [ wine-0.9.18 ] ~ [ wine-0.9.17 ] ~ [ wine-0.9.16 ] ~ [ wine-0.9.15 ] ~ [ wine-0.9.14 ] ~ [ wine-0.9.13 ] ~ [ wine-0.9.12 ] ~ [ wine-0.9.11 ] ~ [ wine-0.9.10 ] ~ [ wine-0.9.9 ] ~ [ wine-0.9.8 ] ~ [ wine-0.9.7 ] ~ [ wine-0.9.6 ] ~ [ wine-0.9.5 ] ~ [ wine-0.9.4 ] ~ [ wine-0.9.3 ] ~ [ wine-0.9.2 ] ~ [ wine-0.9.1 ] ~ [ wine-0.9 ] ~ [ wine20050930 ] ~ [ wine20050830 ] ~ [ wine20050725 ] ~ [ wine20050628 ] ~ [ wine20050524 ] ~ [ wine20050419 ] ~ [ wine20050310 ] ~ [ wine20050211 ] ~ [ wine20050111 ] ~ [ wine20041201 ] ~ [ wine20041019 ] ~ [ wine20040914 ] ~ [ wine20040813 ] ~ [ wine20040716 ] ~ [ wine20040615 ] ~ [ wine20040505 ] ~ [ wine20040408 ] ~ [ wine20040309 ] ~ [ wine20040213 ] ~ [ wine20040121 ] ~ [ wine20031212 ] ~ [ wine20031118 ] ~ [ wine20031016 ] ~ [ wine20030911 ] ~ [ wine20030813 ] ~ [ wine20030709 ] ~ [ wine20030618 ] ~ [ wine20030508 ] ~ [ wine20030408 ] ~ [ wine20030318 ] ~ [ wine20030219 ] ~ [ wine20030115 ] ~ [ wine20021219 ] ~ [ wine20021125 ] ~ [ wine20021031 ] ~ [ wine20021007 ] ~ [ wine20020904 ] ~ [ wine20020804 ] ~ [ wine20020710 ] ~ [ wine20020605 ] ~ [ wine20020509 ] ~ [ wine20020411 ] ~ [ wine20020310 ] ~ [ wine20020228 ] ~ [ wine20011226 ] ~ [ wine20011108 ] ~ [ wine20011004 ] ~ [ wine20010824 ] ~ [ wine20010731 ] ~ [ wine20010629 ] ~ [ wine20010510 ] ~ [ wine20010418 ] ~ [ wine20010326 ] ~ [ wine20010305 ] ~ [ wine20010216 ] ~ [ wine20010112 ] ~ [ wine20001222 ] ~ [ wine20001202 ] ~ [ wine20001026 ] ~ [ wine20001002 ] ~ [ wine20000909 ] ~ [ wine20000821 ] ~ [ wine20000801 ] ~ [ wine20000716 ] ~ [ wine20000326 ] ~ [ wine20000227 ] ~ [ wine20000130 ] ~ [ wine20000109 ] ~

  1 /*
  2  * Copyright 1999 Marcus Meissner
  3  * Copyright 2002-2003 Michael Günnewig
  4  *
  5  * This library is free software; you can redistribute it and/or
  6  * modify it under the terms of the GNU Lesser General Public
  7  * License as published by the Free Software Foundation; either
  8  * version 2.1 of the License, or (at your option) any later version.
  9  *
 10  * This library is distributed in the hope that it will be useful,
 11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  * Lesser General Public License for more details.
 14  *
 15  * You should have received a copy of the GNU Lesser General Public
 16  * License along with this library; if not, write to the Free Software
 17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 18  */
 19 
 20 /* TODO:
 21  *  - IAVIStreaming interface is missing for the IAVIStreamImpl
 22  *  - IAVIStream_fnFindSample: FIND_INDEX isn't supported.
 23  *  - IAVIStream_fnReadFormat: formatchanges aren't read in.
 24  *  - IAVIStream_fnDelete: a stub.
 25  *  - IAVIStream_fnSetInfo: a stub.
 26  *  - make thread safe
 27  *
 28  * KNOWN Bugs:
 29  *  - native version can hangup when reading a file generated with this DLL.
 30  *    When index is missing it works, but index seems to be okay.
 31  */
 32 
 33 #include <assert.h>
 34 #include <stdarg.h>
 35 
 36 #include "windef.h"
 37 #include "winbase.h"
 38 #include "wingdi.h"
 39 #include "winuser.h"
 40 #include "winnls.h"
 41 #include "winerror.h"
 42 #include "mmsystem.h"
 43 #include "vfw.h"
 44 
 45 #include "avifile_private.h"
 46 #include "extrachunk.h"
 47 
 48 #include "wine/unicode.h"
 49 #include "wine/debug.h"
 50 
 51 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
 52 
 53 #ifndef IDX_PER_BLOCK
 54 #define IDX_PER_BLOCK 2730
 55 #endif
 56 
 57 /***********************************************************************/
 58 
 59 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile* iface,REFIID refiid,LPVOID *obj);
 60 static ULONG   WINAPI IAVIFile_fnAddRef(IAVIFile* iface);
 61 static ULONG   WINAPI IAVIFile_fnRelease(IAVIFile* iface);
 62 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile*iface,AVIFILEINFOW*afi,LONG size);
 63 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile*iface,PAVISTREAM*avis,DWORD fccType,LONG lParam);
 64 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile*iface,PAVISTREAM*avis,AVISTREAMINFOW*asi);
 65 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG size);
 66 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG *size);
 67 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile*iface);
 68 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile*iface,DWORD fccType,LONG lParam);
 69 
 70 static const struct IAVIFileVtbl iavift = {
 71   IAVIFile_fnQueryInterface,
 72   IAVIFile_fnAddRef,
 73   IAVIFile_fnRelease,
 74   IAVIFile_fnInfo,
 75   IAVIFile_fnGetStream,
 76   IAVIFile_fnCreateStream,
 77   IAVIFile_fnWriteData,
 78   IAVIFile_fnReadData,
 79   IAVIFile_fnEndRecord,
 80   IAVIFile_fnDeleteStream
 81 };
 82 
 83 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile*iface,REFIID refiid,LPVOID*obj);
 84 static ULONG   WINAPI IPersistFile_fnAddRef(IPersistFile*iface);
 85 static ULONG   WINAPI IPersistFile_fnRelease(IPersistFile*iface);
 86 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile*iface,CLSID*pClassID);
 87 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile*iface);
 88 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile*iface,LPCOLESTR pszFileName,DWORD dwMode);
 89 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile*iface,LPCOLESTR pszFileName,BOOL fRemember);
 90 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile*iface,LPCOLESTR pszFileName);
 91 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile*iface,LPOLESTR*ppszFileName);
 92 
 93 static const struct IPersistFileVtbl ipersistft = {
 94   IPersistFile_fnQueryInterface,
 95   IPersistFile_fnAddRef,
 96   IPersistFile_fnRelease,
 97   IPersistFile_fnGetClassID,
 98   IPersistFile_fnIsDirty,
 99   IPersistFile_fnLoad,
100   IPersistFile_fnSave,
101   IPersistFile_fnSaveCompleted,
102   IPersistFile_fnGetCurFile
103 };
104 
105 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj);
106 static ULONG   WINAPI IAVIStream_fnAddRef(IAVIStream*iface);
107 static ULONG   WINAPI IAVIStream_fnRelease(IAVIStream* iface);
108 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2);
109 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size);
110 static LONG    WINAPI IAVIStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags);
111 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize);
112 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize);
113 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread);
114 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten);
115 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream*iface,LONG start,LONG samples);
116 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread);
117 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size);
118 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen);
119 
120 static const struct IAVIStreamVtbl iavist = {
121   IAVIStream_fnQueryInterface,
122   IAVIStream_fnAddRef,
123   IAVIStream_fnRelease,
124   IAVIStream_fnCreate,
125   IAVIStream_fnInfo,
126   IAVIStream_fnFindSample,
127   IAVIStream_fnReadFormat,
128   IAVIStream_fnSetFormat,
129   IAVIStream_fnRead,
130   IAVIStream_fnWrite,
131   IAVIStream_fnDelete,
132   IAVIStream_fnReadData,
133   IAVIStream_fnWriteData,
134   IAVIStream_fnSetInfo
135 };
136 
137 typedef struct _IAVIFileImpl IAVIFileImpl;
138 
139 typedef struct _IPersistFileImpl {
140   /* IUnknown stuff */
141   const IPersistFileVtbl *lpVtbl;
142 
143   /* IPersistFile stuff */
144   IAVIFileImpl     *paf;
145 } IPersistFileImpl;
146 
147 typedef struct _IAVIStreamImpl {
148   /* IUnknown stuff */
149   const IAVIStreamVtbl *lpVtbl;
150   LONG              ref;
151 
152   /* IAVIStream stuff */
153   IAVIFileImpl     *paf;
154   DWORD             nStream;       /* the n-th stream in file */
155   AVISTREAMINFOW    sInfo;
156 
157   LPVOID            lpFormat;
158   DWORD             cbFormat;
159 
160   LPVOID            lpHandlerData;
161   DWORD             cbHandlerData;
162 
163   EXTRACHUNKS       extra;
164 
165   LPDWORD           lpBuffer;
166   DWORD             cbBuffer;       /* size of lpBuffer */
167   DWORD             dwCurrentFrame; /* frame/block currently in lpBuffer */
168 
169   LONG              lLastFrame;    /* last correct index in idxFrames */
170   AVIINDEXENTRY    *idxFrames;
171   DWORD             nIdxFrames;     /* upper index limit of idxFrames */
172   AVIINDEXENTRY    *idxFmtChanges;
173   DWORD             nIdxFmtChanges; /* upper index limit of idxFmtChanges */
174 } IAVIStreamImpl;
175 
176 struct _IAVIFileImpl {
177   /* IUnknown stuff */
178   const IAVIFileVtbl     *lpVtbl;
179   LONG              ref;
180 
181   /* IAVIFile stuff... */
182   IPersistFileImpl  iPersistFile;
183 
184   AVIFILEINFOW      fInfo;
185   IAVIStreamImpl   *ppStreams[MAX_AVISTREAMS];
186 
187   EXTRACHUNKS       fileextra;
188 
189   DWORD             dwMoviChunkPos;  /* some stuff for saving ... */
190   DWORD             dwIdxChunkPos;
191   DWORD             dwNextFramePos;
192   DWORD             dwInitialFrames;
193 
194   MMCKINFO          ckLastRecord;
195   AVIINDEXENTRY    *idxRecords;      /* won't be updated while loading */
196   DWORD             nIdxRecords;     /* current fill level */
197   DWORD             cbIdxRecords;    /* size of idxRecords */
198 
199   /* IPersistFile stuff ... */
200   HMMIO             hmmio;
201   LPWSTR            szFileName;
202   UINT              uMode;
203   BOOL              fDirty;
204 };
205 
206 /***********************************************************************/
207 
208 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size,
209                                 DWORD offset, DWORD flags);
210 static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This);
211 static DWORD   AVIFILE_ComputeMoviStart(IAVIFileImpl *This);
212 static void    AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr,
213                                           const AVISTREAMINFOW *asi);
214 static void    AVIFILE_DestructAVIStream(IAVIStreamImpl *This);
215 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This);
216 static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset);
217 static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp,
218                                   LONG count, DWORD pos, BOOL *bAbsolute);
219 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD start,
220                                  LPVOID buffer, LONG size);
221 static void    AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos,
222                                       LPLONG offset);
223 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This);
224 static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This);
225 static ULONG   AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fccType,
226                                     LONG lSkip);
227 static void    AVIFILE_UpdateInfo(IAVIFileImpl *This);
228 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
229                                   FOURCC ckid, DWORD flags, LPVOID buffer,
230                                   LONG size);
231 
232 HRESULT AVIFILE_CreateAVIFile(REFIID riid, LPVOID *ppv)
233 {
234   IAVIFileImpl *pfile;
235   HRESULT       hr;
236 
237   assert(riid != NULL && ppv != NULL);
238 
239   *ppv = NULL;
240 
241   pfile = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIFileImpl));
242   if (pfile == NULL)
243     return AVIERR_MEMORY;
244 
245   pfile->lpVtbl = &iavift;
246   pfile->ref = 0;
247   pfile->iPersistFile.lpVtbl = &ipersistft;
248   pfile->iPersistFile.paf = pfile;
249 
250   hr = IAVIFile_QueryInterface((IAVIFile*)pfile, riid, ppv);
251   if (FAILED(hr))
252     HeapFree(GetProcessHeap(), 0, pfile);
253 
254   return hr;
255 }
256 
257 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID refiid,
258                                                 LPVOID *obj)
259 {
260   IAVIFileImpl *This = (IAVIFileImpl *)iface;
261 
262   TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj);
263 
264   if (IsEqualGUID(&IID_IUnknown, refiid) ||
265       IsEqualGUID(&IID_IAVIFile, refiid)) {
266     *obj = iface;
267     IAVIFile_AddRef(iface);
268 
269     return S_OK;
270   } else if (IsEqualGUID(&IID_IPersistFile, refiid)) {
271     *obj = &This->iPersistFile;
272     IAVIFile_AddRef(iface);
273 
274     return S_OK;
275   }
276 
277   return OLE_E_ENUM_NOMORE;
278 }
279 
280 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface)
281 {
282   IAVIFileImpl *This = (IAVIFileImpl *)iface;
283   ULONG ref = InterlockedIncrement(&This->ref);
284 
285   TRACE("(%p) -> %d\n", iface, ref);
286 
287   return ref;
288 }
289 
290 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface)
291 {
292   IAVIFileImpl *This = (IAVIFileImpl *)iface;
293   UINT i;
294   ULONG ref = InterlockedDecrement(&This->ref);
295 
296   TRACE("(%p) -> %d\n", iface, ref);
297 
298   if (!ref) {
299     if (This->fDirty) {
300       /* need to write headers to file */
301       AVIFILE_SaveFile(This);
302     }
303 
304     for (i = 0; i < This->fInfo.dwStreams; i++) {
305       if (This->ppStreams[i] != NULL) {
306         if (This->ppStreams[i]->ref != 0) {
307           ERR(": someone has still %u reference to stream %u (%p)!\n",
308                This->ppStreams[i]->ref, i, This->ppStreams[i]);
309         }
310         AVIFILE_DestructAVIStream(This->ppStreams[i]);
311         HeapFree(GetProcessHeap(), 0, This->ppStreams[i]);
312         This->ppStreams[i] = NULL;
313       }
314     }
315 
316     if (This->idxRecords != NULL) {
317       HeapFree(GetProcessHeap(), 0, This->idxRecords);
318       This->idxRecords  = NULL;
319       This->nIdxRecords = 0;
320     }
321 
322     if (This->fileextra.lp != NULL) {
323       HeapFree(GetProcessHeap(), 0, This->fileextra.lp);
324       This->fileextra.lp = NULL;
325       This->fileextra.cb = 0;
326     }
327 
328     HeapFree(GetProcessHeap(), 0, This->szFileName);
329     This->szFileName = NULL;
330 
331     if (This->hmmio != NULL) {
332       mmioClose(This->hmmio, 0);
333       This->hmmio = NULL;
334     }
335 
336     HeapFree(GetProcessHeap(), 0, This);
337   }
338   return ref;
339 }
340 
341 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, LPAVIFILEINFOW afi,
342                                       LONG size)
343 {
344   IAVIFileImpl *This = (IAVIFileImpl *)iface;
345 
346   TRACE("(%p,%p,%d)\n",iface,afi,size);
347 
348   if (afi == NULL)
349     return AVIERR_BADPARAM;
350   if (size < 0)
351     return AVIERR_BADSIZE;
352 
353   AVIFILE_UpdateInfo(This);
354 
355   memcpy(afi, &This->fInfo, min((DWORD)size, sizeof(This->fInfo)));
356 
357   if ((DWORD)size < sizeof(This->fInfo))
358     return AVIERR_BUFFERTOOSMALL;
359   return AVIERR_OK;
360 }
361 
362 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, PAVISTREAM *avis,
363                                            DWORD fccType, LONG lParam)
364 {
365   IAVIFileImpl *This = (IAVIFileImpl *)iface;
366 
367   ULONG nStream;
368 
369   TRACE("(%p,%p,0x%08X,%d)\n", iface, avis, fccType, lParam);
370 
371   if (avis == NULL || lParam < 0)
372     return AVIERR_BADPARAM;
373 
374   nStream = AVIFILE_SearchStream(This, fccType, lParam);
375 
376   /* Does the requested stream exist? */
377   if (nStream < This->fInfo.dwStreams &&
378       This->ppStreams[nStream] != NULL) {
379     *avis = (PAVISTREAM)This->ppStreams[nStream];
380     IAVIStream_AddRef(*avis);
381 
382     return AVIERR_OK;
383   }
384 
385   /* Sorry, but the specified stream doesn't exist */
386   return AVIERR_NODATA;
387 }
388 
389 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface,PAVISTREAM *avis,
390                                               LPAVISTREAMINFOW asi)
391 {
392   IAVIFileImpl *This = (IAVIFileImpl *)iface;
393 
394   DWORD n;
395 
396   TRACE("(%p,%p,%p)\n", iface, avis, asi);
397 
398   /* check parameters */
399   if (avis == NULL || asi == NULL)
400     return AVIERR_BADPARAM;
401 
402   *avis = NULL;
403 
404   /* Does the user have write permission? */
405   if ((This->uMode & MMIO_RWMODE) == 0)
406     return AVIERR_READONLY;
407 
408   /* Can we add another stream? */
409   n = This->fInfo.dwStreams;
410   if (n >= MAX_AVISTREAMS || This->dwMoviChunkPos != 0) {
411     /* already reached max nr of streams
412      * or have already written frames to disk */
413     return AVIERR_UNSUPPORTED;
414   }
415 
416   /* check AVISTREAMINFO for some really needed things */
417   if (asi->fccType == 0 || asi->dwScale == 0 || asi->dwRate == 0)
418     return AVIERR_BADFORMAT;
419 
420   /* now it seems to be save to add the stream */
421   assert(This->ppStreams[n] == NULL);
422   This->ppStreams[n] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
423                                                    sizeof(IAVIStreamImpl));
424   if (This->ppStreams[n] == NULL)
425     return AVIERR_MEMORY;
426 
427   /* initialize the new allocated stream */
428   AVIFILE_ConstructAVIStream(This, n, asi);
429 
430   This->fInfo.dwStreams++;
431   This->fDirty = TRUE;
432 
433   /* update our AVIFILEINFO structure */
434   AVIFILE_UpdateInfo(This);
435 
436   /* return it */
437   *avis = (PAVISTREAM)This->ppStreams[n];
438   IAVIStream_AddRef(*avis);
439 
440   return AVIERR_OK;
441 }
442 
443 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid,
444                                            LPVOID lpData, LONG size)
445 {
446   IAVIFileImpl *This = (IAVIFileImpl *)iface;
447 
448   TRACE("(%p,0x%08X,%p,%d)\n", iface, ckid, lpData, size);
449 
450   /* check parameters */
451   if (lpData == NULL)
452     return AVIERR_BADPARAM;
453   if (size < 0)
454     return AVIERR_BADSIZE;
455 
456   /* Do we have write permission? */
457   if ((This->uMode & MMIO_RWMODE) == 0)
458     return AVIERR_READONLY;
459 
460   This->fDirty = TRUE;
461 
462   return WriteExtraChunk(&This->fileextra, ckid, lpData, size);
463 }
464 
465 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid,
466                                           LPVOID lpData, LONG *size)
467 {
468   IAVIFileImpl *This = (IAVIFileImpl *)iface;
469 
470   TRACE("(%p,0x%08X,%p,%p)\n", iface, ckid, lpData, size);
471 
472   return ReadExtraChunk(&This->fileextra, ckid, lpData, size);
473 }
474 
475 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface)
476 {
477   IAVIFileImpl *This = (IAVIFileImpl *)iface;
478 
479   TRACE("(%p)\n",iface);
480 
481   if ((This->uMode & MMIO_RWMODE) == 0)
482     return AVIERR_READONLY;
483 
484   This->fDirty = TRUE;
485 
486   /* no frames written to any stream? -- compute start of 'movi'-chunk */
487   if (This->dwMoviChunkPos == 0)
488     AVIFILE_ComputeMoviStart(This);
489 
490   This->fInfo.dwFlags  |= AVIFILEINFO_ISINTERLEAVED;
491 
492   /* already written frames to any stream, ... */
493   if (This->ckLastRecord.dwFlags & MMIO_DIRTY) {
494     /* close last record */
495     if (mmioAscend(This->hmmio, &This->ckLastRecord, 0) != 0)
496       return AVIERR_FILEWRITE;
497 
498     AVIFILE_AddRecord(This);
499 
500     if (This->fInfo.dwSuggestedBufferSize < This->ckLastRecord.cksize + 3 * sizeof(DWORD))
501       This->fInfo.dwSuggestedBufferSize = This->ckLastRecord.cksize + 3 * sizeof(DWORD);
502   }
503 
504   /* write out a new record into file, but don't close it */
505   This->ckLastRecord.cksize  = 0;
506   This->ckLastRecord.fccType = listtypeAVIRECORD;
507   if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1)
508     return AVIERR_FILEWRITE;
509   if (mmioCreateChunk(This->hmmio, &This->ckLastRecord, MMIO_CREATELIST) != 0)
510     return AVIERR_FILEWRITE;
511   This->dwNextFramePos += 3 * sizeof(DWORD);
512 
513   return AVIERR_OK;
514 }
515 
516 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType,
517                                               LONG lParam)
518 {
519   IAVIFileImpl *This = (IAVIFileImpl *)iface;
520 
521   ULONG nStream;
522 
523   TRACE("(%p,0x%08X,%d)\n", iface, fccType, lParam);
524 
525   /* check parameter */
526   if (lParam < 0)
527     return AVIERR_BADPARAM;
528 
529   /* Have user write permissions? */
530   if ((This->uMode & MMIO_RWMODE) == 0)
531     return AVIERR_READONLY;
532 
533   nStream = AVIFILE_SearchStream(This, fccType, lParam);
534 
535   /* Does the requested stream exist? */
536   if (nStream < This->fInfo.dwStreams &&
537       This->ppStreams[nStream] != NULL) {
538     /* ... so delete it now */
539     HeapFree(GetProcessHeap(), 0, This->ppStreams[nStream]);
540 
541     if (This->fInfo.dwStreams - nStream > 0)
542       memcpy(This->ppStreams + nStream, This->ppStreams + nStream + 1,
543              (This->fInfo.dwStreams - nStream) * sizeof(IAVIStreamImpl*));
544 
545     This->ppStreams[This->fInfo.dwStreams] = NULL;
546     This->fInfo.dwStreams--;
547     This->fDirty = TRUE;
548 
549     /* This->fInfo will be updated further when asked for */
550     return AVIERR_OK;
551   } else
552     return AVIERR_NODATA;
553 }
554 
555 /***********************************************************************/
556 
557 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface,
558                                                     REFIID refiid, LPVOID *obj)
559 {
560   IPersistFileImpl *This = (IPersistFileImpl *)iface;
561 
562   assert(This->paf != NULL);
563 
564   return IAVIFile_QueryInterface((PAVIFILE)This->paf, refiid, obj);
565 }
566 
567 static ULONG   WINAPI IPersistFile_fnAddRef(IPersistFile *iface)
568 {
569   IPersistFileImpl *This = (IPersistFileImpl *)iface;
570 
571   assert(This->paf != NULL);
572 
573   return IAVIFile_AddRef((PAVIFILE)This->paf);
574 }
575 
576 static ULONG   WINAPI IPersistFile_fnRelease(IPersistFile *iface)
577 {
578   IPersistFileImpl *This = (IPersistFileImpl *)iface;
579 
580   assert(This->paf != NULL);
581 
582   return IAVIFile_Release((PAVIFILE)This->paf);
583 }
584 
585 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface,
586                                                 LPCLSID pClassID)
587 {
588   TRACE("(%p,%p)\n", iface, pClassID);
589 
590   if (pClassID == NULL)
591     return AVIERR_BADPARAM;
592 
593   *pClassID = CLSID_AVIFile;
594 
595   return AVIERR_OK;
596 }
597 
598 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface)
599 {
600   IPersistFileImpl *This = (IPersistFileImpl *)iface;
601 
602   TRACE("(%p)\n", iface);
603 
604   assert(This->paf != NULL);
605 
606   return (This->paf->fDirty ? S_OK : S_FALSE);
607 }
608 
609 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface,
610                                           LPCOLESTR pszFileName, DWORD dwMode)
611 {
612   IPersistFileImpl *This = (IPersistFileImpl *)iface;
613 
614   int len;
615 
616   TRACE("(%p,%s,0x%08X)\n", iface, debugstr_w(pszFileName), dwMode);
617 
618   /* check parameter */
619   if (pszFileName == NULL)
620     return AVIERR_BADPARAM;
621 
622   assert(This->paf != NULL);
623   if (This->paf->hmmio != NULL)
624     return AVIERR_ERROR; /* No reuse of this object for another file! */
625 
626   /* remember mode and name */
627   This->paf->uMode = dwMode;
628 
629   len = lstrlenW(pszFileName) + 1;
630   This->paf->szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
631   if (This->paf->szFileName == NULL)
632     return AVIERR_MEMORY;
633   lstrcpyW(This->paf->szFileName, pszFileName);
634 
635   /* try to open the file */
636   This->paf->hmmio = mmioOpenW(This->paf->szFileName, NULL,
637                                MMIO_ALLOCBUF | dwMode);
638   if (This->paf->hmmio == NULL) {
639     /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */
640     LPSTR szFileName;
641 
642     len = WideCharToMultiByte(CP_ACP, 0, This->paf->szFileName, -1,
643                                NULL, 0, NULL, NULL);
644     szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(CHAR));
645     if (szFileName == NULL)
646       return AVIERR_MEMORY;
647 
648     WideCharToMultiByte(CP_ACP, 0, This->paf->szFileName, -1, szFileName,
649                         len, NULL, NULL);
650 
651     This->paf->hmmio = mmioOpenA(szFileName, NULL, MMIO_ALLOCBUF | dwMode);
652     HeapFree(GetProcessHeap(), 0, szFileName);
653     if (This->paf->hmmio == NULL)
654       return AVIERR_FILEOPEN;
655   }
656 
657   /* should we create a new file? */
658   if (dwMode & OF_CREATE) {
659     memset(& This->paf->fInfo, 0, sizeof(This->paf->fInfo));
660     This->paf->fInfo.dwFlags = AVIFILEINFO_HASINDEX | AVIFILEINFO_TRUSTCKTYPE;
661 
662     return AVIERR_OK;
663   } else
664     return AVIFILE_LoadFile(This->paf);
665 }
666 
667 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface,
668                                           LPCOLESTR pszFileName,BOOL fRemember)
669 {
670   TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember);
671 
672   /* We write directly to disk, so nothing to do. */
673 
674   return AVIERR_OK;
675 }
676 
677 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface,
678                                                    LPCOLESTR pszFileName)
679 {
680   TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName));
681 
682   /* We write directly to disk, so nothing to do. */
683 
684   return AVIERR_OK;
685 }
686 
687 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface,
688                                                 LPOLESTR *ppszFileName)
689 {
690   IPersistFileImpl *This = (IPersistFileImpl *)iface;
691 
692   TRACE("(%p,%p)\n", iface, ppszFileName);
693 
694   if (ppszFileName == NULL)
695     return AVIERR_BADPARAM;
696 
697   *ppszFileName = NULL;
698 
699   assert(This->paf != NULL);
700 
701   if (This->paf->szFileName != NULL) {
702     int len = lstrlenW(This->paf->szFileName) + 1;
703 
704     *ppszFileName = CoTaskMemAlloc(len * sizeof(WCHAR));
705     if (*ppszFileName == NULL)
706       return AVIERR_MEMORY;
707 
708     strcpyW(*ppszFileName, This->paf->szFileName);
709   }
710 
711   return AVIERR_OK;
712 }
713 
714 /***********************************************************************/
715 
716 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface,
717                                                   REFIID refiid, LPVOID *obj)
718 {
719   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
720 
721   TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj);
722 
723   if (IsEqualGUID(&IID_IUnknown, refiid) ||
724       IsEqualGUID(&IID_IAVIStream, refiid)) {
725     *obj = This;
726     IAVIStream_AddRef(iface);
727 
728     return S_OK;
729   }
730   /* FIXME: IAVIStreaming interface */
731 
732   return OLE_E_ENUM_NOMORE;
733 }
734 
735 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface)
736 {
737   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
738   ULONG ref = InterlockedIncrement(&This->ref);
739 
740   TRACE("(%p) -> %d\n", iface, ref);
741 
742   /* also add ref to parent, so that it doesn't kill us */
743   if (This->paf != NULL)
744     IAVIFile_AddRef((PAVIFILE)This->paf);
745 
746   return ref;
747 }
748 
749 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface)
750 {
751   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
752   ULONG ref = InterlockedDecrement(&This->ref);
753 
754   TRACE("(%p) -> %d\n", iface, ref);
755 
756   if (This->paf != NULL)
757     IAVIFile_Release((PAVIFILE)This->paf);
758 
759   return ref;
760 }
761 
762 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1,
763                                           LPARAM lParam2)
764 {
765   TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2);
766 
767   /* This IAVIStream interface needs an AVIFile */
768   return AVIERR_UNSUPPORTED;
769 }
770 
771 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi,
772                                         LONG size)
773 {
774   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
775 
776   TRACE("(%p,%p,%d)\n", iface, psi, size);
777 
778   if (psi == NULL)
779     return AVIERR_BADPARAM;
780   if (size < 0)
781     return AVIERR_BADSIZE;
782 
783   memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo)));
784 
785   if ((DWORD)size < sizeof(This->sInfo))
786     return AVIERR_BUFFERTOOSMALL;
787   return AVIERR_OK;
788 }
789 
790 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos,
791                                            LONG flags)
792 {
793   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
794 
795   LONG offset = 0;
796 
797   TRACE("(%p,%d,0x%08X)\n",iface,pos,flags);
798 
799   if (flags & FIND_FROM_START) {
800     pos = This->sInfo.dwStart;
801     flags &= ~(FIND_FROM_START|FIND_PREV);
802     flags |= FIND_NEXT;
803   }
804 
805   if (This->sInfo.dwSampleSize != 0) {
806     /* convert samples into block number with offset */
807     AVIFILE_SamplesToBlock(This, &pos, &offset);
808   }
809 
810   if (flags & FIND_TYPE) {
811     if (flags & FIND_KEY) {
812       while (0 <= pos && pos <= This->lLastFrame) {
813         if (This->idxFrames[pos].dwFlags & AVIIF_KEYFRAME)
814           goto RETURN_FOUND;
815 
816         if (flags & FIND_NEXT)
817           pos++;
818         else
819           pos--;
820       };
821     } else if (flags & FIND_ANY) {
822       while (0 <= pos && pos <= This->lLastFrame) {
823         if (This->idxFrames[pos].dwChunkLength > 0)
824           goto RETURN_FOUND;
825 
826         if (flags & FIND_NEXT)
827           pos++;
828         else
829           pos--;
830 
831       };
832     } else if ((flags & FIND_FORMAT) && This->idxFmtChanges != NULL &&
833                This->sInfo.fccType == streamtypeVIDEO) {
834       if (flags & FIND_NEXT) {
835         ULONG n;
836 
837         for (n = 0; n < This->sInfo.dwFormatChangeCount; n++)
838           if (This->idxFmtChange