xref: /reactos/dll/win32/avifil32/avifile.c (revision 84ccccab)
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 "avifile_private.h"
34 
35 #ifndef IDX_PER_BLOCK
36 #define IDX_PER_BLOCK 2730
37 #endif
38 
39 typedef struct _IAVIFileImpl IAVIFileImpl;
40 
41 typedef struct _IAVIStreamImpl {
42   IAVIStream        IAVIStream_iface;
43   LONG              ref;
44 
45   IAVIFileImpl     *paf;
46   DWORD             nStream;       /* the n-th stream in file */
47   AVISTREAMINFOW    sInfo;
48 
49   LPVOID            lpFormat;
50   DWORD             cbFormat;
51 
52   LPVOID            lpHandlerData;
53   DWORD             cbHandlerData;
54 
55   EXTRACHUNKS       extra;
56 
57   LPDWORD           lpBuffer;
58   DWORD             cbBuffer;       /* size of lpBuffer */
59   DWORD             dwCurrentFrame; /* frame/block currently in lpBuffer */
60 
61   LONG              lLastFrame;    /* last correct index in idxFrames */
62   AVIINDEXENTRY    *idxFrames;
63   DWORD             nIdxFrames;     /* upper index limit of idxFrames */
64   AVIINDEXENTRY    *idxFmtChanges;
65   DWORD             nIdxFmtChanges; /* upper index limit of idxFmtChanges */
66 } IAVIStreamImpl;
67 
68 static inline IAVIStreamImpl *impl_from_IAVIStream(IAVIStream *iface)
69 {
70   return CONTAINING_RECORD(iface, IAVIStreamImpl, IAVIStream_iface);
71 }
72 
73 struct _IAVIFileImpl {
74   IUnknown          IUnknown_inner;
75   IAVIFile          IAVIFile_iface;
76   IPersistFile      IPersistFile_iface;
77   IUnknown         *outer_unk;
78   LONG              ref;
79 
80   AVIFILEINFOW      fInfo;
81   IAVIStreamImpl   *ppStreams[MAX_AVISTREAMS];
82 
83   EXTRACHUNKS       fileextra;
84 
85   DWORD             dwMoviChunkPos;  /* some stuff for saving ... */
86   DWORD             dwIdxChunkPos;
87   DWORD             dwNextFramePos;
88   DWORD             dwInitialFrames;
89 
90   MMCKINFO          ckLastRecord;
91   AVIINDEXENTRY    *idxRecords;      /* won't be updated while loading */
92   DWORD             nIdxRecords;     /* current fill level */
93   DWORD             cbIdxRecords;    /* size of idxRecords */
94 
95   /* IPersistFile stuff ... */
96   HMMIO             hmmio;
97   LPWSTR            szFileName;
98   UINT              uMode;
99   BOOL              fDirty;
100 };
101 
102 static inline IAVIFileImpl *impl_from_IUnknown(IUnknown *iface)
103 {
104   return CONTAINING_RECORD(iface, IAVIFileImpl, IUnknown_inner);
105 }
106 
107 static inline IAVIFileImpl *impl_from_IAVIFile(IAVIFile *iface)
108 {
109   return CONTAINING_RECORD(iface, IAVIFileImpl, IAVIFile_iface);
110 }
111 
112 static inline IAVIFileImpl *impl_from_IPersistFile(IPersistFile *iface)
113 {
114   return CONTAINING_RECORD(iface, IAVIFileImpl, IPersistFile_iface);
115 }
116 
117 /***********************************************************************/
118 
119 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size,
120 				DWORD offset, DWORD flags);
121 static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This);
122 static DWORD   AVIFILE_ComputeMoviStart(IAVIFileImpl *This);
123 static void    AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr,
124 					  const AVISTREAMINFOW *asi);
125 static void    AVIFILE_DestructAVIStream(IAVIStreamImpl *This);
126 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This);
127 static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset);
128 static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp,
129 				  LONG count, DWORD pos, BOOL *bAbsolute);
130 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD start,
131 				 LPVOID buffer, DWORD size);
132 static void    AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos,
133 				      LPLONG offset);
134 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This);
135 static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This);
136 static ULONG   AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fccType,
137 				    LONG lSkip);
138 static void    AVIFILE_UpdateInfo(IAVIFileImpl *This);
139 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
140 				  FOURCC ckid, DWORD flags, LPCVOID buffer,
141 				  LONG size);
142 
143 static HRESULT WINAPI IUnknown_fnQueryInterface(IUnknown *iface, REFIID riid, void **ppv)
144 {
145   IAVIFileImpl *This = impl_from_IUnknown(iface);
146 
147   TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv);
148 
149   if (!ppv) {
150     WARN("invalid parameter\n");
151     return E_INVALIDARG;
152   }
153   *ppv = NULL;
154 
155   if (IsEqualIID(riid, &IID_IUnknown))
156     *ppv = &This->IUnknown_inner;
157   else if (IsEqualIID(riid, &IID_IAVIFile))
158     *ppv = &This->IAVIFile_iface;
159   else if (IsEqualGUID(riid, &IID_IPersistFile))
160     *ppv = &This->IPersistFile_iface;
161   else {
162     WARN("unknown IID %s\n", debugstr_guid(riid));
163     return E_NOINTERFACE;
164   }
165 
166   /* Violation of the COM aggregation ref counting rule */
167   IUnknown_AddRef(&This->IUnknown_inner);
168   return S_OK;
169 }
170 
171 static ULONG WINAPI IUnknown_fnAddRef(IUnknown *iface)
172 {
173   IAVIFileImpl *This = impl_from_IUnknown(iface);
174   ULONG ref = InterlockedIncrement(&This->ref);
175 
176   TRACE("(%p) ref=%d\n", This, ref);
177 
178   return ref;
179 }
180 
181 static ULONG WINAPI IUnknown_fnRelease(IUnknown *iface)
182 {
183   IAVIFileImpl *This = impl_from_IUnknown(iface);
184   ULONG ref = InterlockedDecrement(&This->ref);
185   UINT i;
186 
187   TRACE("(%p) ref=%d\n", This, ref);
188 
189   if (!ref) {
190     if (This->fDirty)
191       AVIFILE_SaveFile(This);
192 
193     for (i = 0; i < This->fInfo.dwStreams; i++) {
194       if (This->ppStreams[i] != NULL) {
195         if (This->ppStreams[i]->ref != 0)
196           ERR(": someone has still %u reference to stream %u (%p)!\n",
197               This->ppStreams[i]->ref, i, This->ppStreams[i]);
198         AVIFILE_DestructAVIStream(This->ppStreams[i]);
199         HeapFree(GetProcessHeap(), 0, This->ppStreams[i]);
200         This->ppStreams[i] = NULL;
201       }
202     }
203 
204     if (This->idxRecords != NULL) {
205       HeapFree(GetProcessHeap(), 0, This->idxRecords);
206       This->idxRecords  = NULL;
207       This->nIdxRecords = 0;
208     }
209 
210     if (This->fileextra.lp != NULL) {
211       HeapFree(GetProcessHeap(), 0, This->fileextra.lp);
212       This->fileextra.lp = NULL;
213       This->fileextra.cb = 0;
214     }
215 
216     HeapFree(GetProcessHeap(), 0, This->szFileName);
217     This->szFileName = NULL;
218 
219     if (This->hmmio != NULL) {
220       mmioClose(This->hmmio, 0);
221       This->hmmio = NULL;
222     }
223 
224     HeapFree(GetProcessHeap(), 0, This);
225   }
226   return ref;
227 }
228 
229 static const IUnknownVtbl unk_vtbl =
230 {
231   IUnknown_fnQueryInterface,
232   IUnknown_fnAddRef,
233   IUnknown_fnRelease
234 };
235 
236 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID riid, void **ppv)
237 {
238   IAVIFileImpl *This = impl_from_IAVIFile(iface);
239 
240   return IUnknown_QueryInterface(This->outer_unk, riid, ppv);
241 }
242 
243 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface)
244 {
245   IAVIFileImpl *This = impl_from_IAVIFile(iface);
246 
247   return IUnknown_AddRef(This->outer_unk);
248 }
249 
250 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface)
251 {
252   IAVIFileImpl *This = impl_from_IAVIFile(iface);
253 
254   return IUnknown_Release(This->outer_unk);
255 }
256 
257 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, AVIFILEINFOW *afi, LONG size)
258 {
259   IAVIFileImpl *This = impl_from_IAVIFile(iface);
260 
261   TRACE("(%p,%p,%d)\n",iface,afi,size);
262 
263   if (afi == NULL)
264     return AVIERR_BADPARAM;
265   if (size < 0)
266     return AVIERR_BADSIZE;
267 
268   AVIFILE_UpdateInfo(This);
269 
270   memcpy(afi, &This->fInfo, min((DWORD)size, sizeof(This->fInfo)));
271 
272   if ((DWORD)size < sizeof(This->fInfo))
273     return AVIERR_BUFFERTOOSMALL;
274   return AVIERR_OK;
275 }
276 
277 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, IAVIStream **avis, DWORD fccType,
278     LONG lParam)
279 {
280   IAVIFileImpl *This = impl_from_IAVIFile(iface);
281   ULONG nStream;
282 
283   TRACE("(%p,%p,0x%08X,%d)\n", iface, avis, fccType, lParam);
284 
285   if (avis == NULL || lParam < 0)
286     return AVIERR_BADPARAM;
287 
288   nStream = AVIFILE_SearchStream(This, fccType, lParam);
289 
290   /* Does the requested stream exist? */
291   if (nStream < This->fInfo.dwStreams &&
292       This->ppStreams[nStream] != NULL) {
293     *avis = &This->ppStreams[nStream]->IAVIStream_iface;
294     IAVIStream_AddRef(*avis);
295 
296     return AVIERR_OK;
297   }
298 
299   /* Sorry, but the specified stream doesn't exist */
300   *avis = NULL;
301   return AVIERR_NODATA;
302 }
303 
304 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface, IAVIStream **avis,
305     AVISTREAMINFOW *asi)
306 {
307   IAVIFileImpl *This = impl_from_IAVIFile(iface);
308   DWORD n;
309 
310   TRACE("(%p,%p,%p)\n", iface, avis, asi);
311 
312   /* check parameters */
313   if (avis == NULL || asi == NULL)
314     return AVIERR_BADPARAM;
315 
316   *avis = NULL;
317 
318   /* Does the user have write permission? */
319   if ((This->uMode & MMIO_RWMODE) == 0)
320     return AVIERR_READONLY;
321 
322   /* Can we add another stream? */
323   n = This->fInfo.dwStreams;
324   if (n >= MAX_AVISTREAMS || This->dwMoviChunkPos != 0) {
325     /* already reached max nr of streams
326      * or have already written frames to disk */
327     return AVIERR_UNSUPPORTED;
328   }
329 
330   /* check AVISTREAMINFO for some really needed things */
331   if (asi->fccType == 0 || asi->dwScale == 0 || asi->dwRate == 0)
332     return AVIERR_BADFORMAT;
333 
334   /* now it seems to be save to add the stream */
335   assert(This->ppStreams[n] == NULL);
336   This->ppStreams[n] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
337 						   sizeof(IAVIStreamImpl));
338   if (This->ppStreams[n] == NULL)
339     return AVIERR_MEMORY;
340 
341   /* initialize the new allocated stream */
342   AVIFILE_ConstructAVIStream(This, n, asi);
343 
344   This->fInfo.dwStreams++;
345   This->fDirty = TRUE;
346 
347   /* update our AVIFILEINFO structure */
348   AVIFILE_UpdateInfo(This);
349 
350   /* return it */
351   *avis = &This->ppStreams[n]->IAVIStream_iface;
352   IAVIStream_AddRef(*avis);
353 
354   return AVIERR_OK;
355 }
356 
357 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid, void *lpData, LONG size)
358 {
359   IAVIFileImpl *This = impl_from_IAVIFile(iface);
360 
361   TRACE("(%p,0x%08X,%p,%d)\n", iface, ckid, lpData, size);
362 
363   /* check parameters */
364   if (lpData == NULL)
365     return AVIERR_BADPARAM;
366   if (size < 0)
367     return AVIERR_BADSIZE;
368 
369   /* Do we have write permission? */
370   if ((This->uMode & MMIO_RWMODE) == 0)
371     return AVIERR_READONLY;
372 
373   This->fDirty = TRUE;
374 
375   return WriteExtraChunk(&This->fileextra, ckid, lpData, size);
376 }
377 
378 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid, void *lpData, LONG *size)
379 {
380   IAVIFileImpl *This = impl_from_IAVIFile(iface);
381 
382   TRACE("(%p,0x%08X,%p,%p)\n", iface, ckid, lpData, size);
383 
384   return ReadExtraChunk(&This->fileextra, ckid, lpData, size);
385 }
386 
387 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface)
388 {
389   IAVIFileImpl *This = impl_from_IAVIFile(iface);
390 
391   TRACE("(%p)\n",iface);
392 
393   if ((This->uMode & MMIO_RWMODE) == 0)
394     return AVIERR_READONLY;
395 
396   This->fDirty = TRUE;
397 
398   /* no frames written to any stream? -- compute start of 'movi'-chunk */
399   if (This->dwMoviChunkPos == 0)
400     AVIFILE_ComputeMoviStart(This);
401 
402   This->fInfo.dwFlags  |= AVIFILEINFO_ISINTERLEAVED;
403 
404   /* already written frames to any stream, ... */
405   if (This->ckLastRecord.dwFlags & MMIO_DIRTY) {
406     /* close last record */
407     if (mmioAscend(This->hmmio, &This->ckLastRecord, 0) != 0)
408       return AVIERR_FILEWRITE;
409 
410     AVIFILE_AddRecord(This);
411 
412     if (This->fInfo.dwSuggestedBufferSize < This->ckLastRecord.cksize + 3 * sizeof(DWORD))
413       This->fInfo.dwSuggestedBufferSize = This->ckLastRecord.cksize + 3 * sizeof(DWORD);
414   }
415 
416   /* write out a new record into file, but don't close it */
417   This->ckLastRecord.cksize  = 0;
418   This->ckLastRecord.fccType = listtypeAVIRECORD;
419   if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1)
420     return AVIERR_FILEWRITE;
421   if (mmioCreateChunk(This->hmmio, &This->ckLastRecord, MMIO_CREATELIST) != 0)
422     return AVIERR_FILEWRITE;
423   This->dwNextFramePos += 3 * sizeof(DWORD);
424 
425   return AVIERR_OK;
426 }
427 
428 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType, LONG lParam)
429 {
430   IAVIFileImpl *This = impl_from_IAVIFile(iface);
431   ULONG nStream;
432 
433   TRACE("(%p,0x%08X,%d)\n", iface, fccType, lParam);
434 
435   /* check parameter */
436   if (lParam < 0)
437     return AVIERR_BADPARAM;
438 
439   /* Have user write permissions? */
440   if ((This->uMode & MMIO_RWMODE) == 0)
441     return AVIERR_READONLY;
442 
443   nStream = AVIFILE_SearchStream(This, fccType, lParam);
444 
445   /* Does the requested stream exist? */
446   if (nStream < This->fInfo.dwStreams &&
447       This->ppStreams[nStream] != NULL) {
448     /* ... so delete it now */
449     HeapFree(GetProcessHeap(), 0, This->ppStreams[nStream]);
450     This->fInfo.dwStreams--;
451     if (nStream < This->fInfo.dwStreams)
452       memmove(&This->ppStreams[nStream], &This->ppStreams[nStream + 1],
453              (This->fInfo.dwStreams - nStream) * sizeof(This->ppStreams[0]));
454 
455     This->ppStreams[This->fInfo.dwStreams] = NULL;
456     This->fDirty = TRUE;
457 
458     /* This->fInfo will be updated further when asked for */
459     return AVIERR_OK;
460   } else
461     return AVIERR_NODATA;
462 }
463 
464 static const struct IAVIFileVtbl avif_vt = {
465   IAVIFile_fnQueryInterface,
466   IAVIFile_fnAddRef,
467   IAVIFile_fnRelease,
468   IAVIFile_fnInfo,
469   IAVIFile_fnGetStream,
470   IAVIFile_fnCreateStream,
471   IAVIFile_fnWriteData,
472   IAVIFile_fnReadData,
473   IAVIFile_fnEndRecord,
474   IAVIFile_fnDeleteStream
475 };
476 
477 
478 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface, REFIID riid, void **ppv)
479 {
480   IAVIFileImpl *This = impl_from_IPersistFile(iface);
481 
482   return IUnknown_QueryInterface(This->outer_unk, riid, ppv);
483 }
484 
485 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile *iface)
486 {
487   IAVIFileImpl *This = impl_from_IPersistFile(iface);
488 
489   return IUnknown_AddRef(This->outer_unk);
490 }
491 
492 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile *iface)
493 {
494   IAVIFileImpl *This = impl_from_IPersistFile(iface);
495 
496   return IUnknown_Release(This->outer_unk);
497 }
498 
499 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface, LPCLSID pClassID)
500 {
501   TRACE("(%p,%p)\n", iface, pClassID);
502 
503   if (pClassID == NULL)
504     return AVIERR_BADPARAM;
505 
506   *pClassID = CLSID_AVIFile;
507 
508   return AVIERR_OK;
509 }
510 
511 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface)
512 {
513   IAVIFileImpl *This = impl_from_IPersistFile(iface);
514 
515   TRACE("(%p)\n", iface);
516 
517   return (This->fDirty ? S_OK : S_FALSE);
518 }
519 
520 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface, LPCOLESTR pszFileName, DWORD dwMode)
521 {
522   IAVIFileImpl *This = impl_from_IPersistFile(iface);
523   int len;
524 
525   TRACE("(%p,%s,0x%08X)\n", iface, debugstr_w(pszFileName), dwMode);
526 
527   /* check parameter */
528   if (pszFileName == NULL)
529     return AVIERR_BADPARAM;
530 
531   if (This->hmmio != NULL)
532     return AVIERR_ERROR; /* No reuse of this object for another file! */
533 
534   /* remember mode and name */
535   This->uMode = dwMode;
536 
537   len = lstrlenW(pszFileName) + 1;
538   This->szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
539   if (This->szFileName == NULL)
540     return AVIERR_MEMORY;
541   lstrcpyW(This->szFileName, pszFileName);
542 
543   /* try to open the file */
544   This->hmmio = mmioOpenW(This->szFileName, NULL, MMIO_ALLOCBUF | dwMode);
545   if (This->hmmio == NULL) {
546     /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */
547     LPSTR szFileName;
548 
549     len = WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, NULL, 0, NULL, NULL);
550     szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(CHAR));
551     if (szFileName == NULL)
552       return AVIERR_MEMORY;
553 
554     WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, szFileName, len, NULL, NULL);
555 
556     This->hmmio = mmioOpenA(szFileName, NULL, MMIO_ALLOCBUF | dwMode);
557     HeapFree(GetProcessHeap(), 0, szFileName);
558     if (This->hmmio == NULL)
559       return AVIERR_FILEOPEN;
560   }
561 
562   /* should we create a new file? */
563   if (dwMode & OF_CREATE) {
564     memset(& This->fInfo, 0, sizeof(This->fInfo));
565     This->fInfo.dwFlags = AVIFILEINFO_HASINDEX | AVIFILEINFO_TRUSTCKTYPE;
566 
567     return AVIERR_OK;
568   } else
569     return AVIFILE_LoadFile(This);
570 }
571 
572 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface, LPCOLESTR pszFileName,
573     BOOL fRemember)
574 {
575   TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember);
576 
577   /* We write directly to disk, so nothing to do. */
578 
579   return AVIERR_OK;
580 }
581 
582 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface, LPCOLESTR pszFileName)
583 {
584   TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName));
585 
586   /* We write directly to disk, so nothing to do. */
587 
588   return AVIERR_OK;
589 }
590 
591 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface, LPOLESTR *ppszFileName)
592 {
593   IAVIFileImpl *This = impl_from_IPersistFile(iface);
594 
595   TRACE("(%p,%p)\n", iface, ppszFileName);
596 
597   if (ppszFileName == NULL)
598     return AVIERR_BADPARAM;
599 
600   *ppszFileName = NULL;
601 
602   if (This->szFileName != NULL) {
603     int len = lstrlenW(This->szFileName) + 1;
604 
605     *ppszFileName = CoTaskMemAlloc(len * sizeof(WCHAR));
606     if (*ppszFileName == NULL)
607       return AVIERR_MEMORY;
608 
609     strcpyW(*ppszFileName, This->szFileName);
610   }
611 
612   return AVIERR_OK;
613 }
614 
615 static const struct IPersistFileVtbl pf_vt = {
616   IPersistFile_fnQueryInterface,
617   IPersistFile_fnAddRef,
618   IPersistFile_fnRelease,
619   IPersistFile_fnGetClassID,
620   IPersistFile_fnIsDirty,
621   IPersistFile_fnLoad,
622   IPersistFile_fnSave,
623   IPersistFile_fnSaveCompleted,
624   IPersistFile_fnGetCurFile
625 };
626 
627 HRESULT AVIFILE_CreateAVIFile(IUnknown *pUnkOuter, REFIID riid, void **ppv)
628 {
629   IAVIFileImpl *obj;
630   HRESULT hr;
631 
632   *ppv = NULL;
633   obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIFileImpl));
634   if (!obj)
635     return AVIERR_MEMORY;
636 
637   obj->IUnknown_inner.lpVtbl = &unk_vtbl;
638   obj->IAVIFile_iface.lpVtbl = &avif_vt;
639   obj->IPersistFile_iface.lpVtbl = &pf_vt;
640   obj->ref = 1;
641   if (pUnkOuter)
642     obj->outer_unk = pUnkOuter;
643   else
644     obj->outer_unk = &obj->IUnknown_inner;
645 
646   hr = IUnknown_QueryInterface(&obj->IUnknown_inner, riid, ppv);
647   IUnknown_Release(&obj->IUnknown_inner);
648 
649   return hr;
650 }
651 
652 
653 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface, REFIID riid, void **ppv)
654 {
655   IAVIStreamImpl *This = impl_from_IAVIStream(iface);
656 
657   TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv);
658 
659   if (!ppv) {
660     WARN("invalid parameter\n");
661     return E_INVALIDARG;
662   }
663   *ppv = NULL;
664 
665   if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IAVIStream, riid)) {
666     *ppv = iface;
667     IAVIStream_AddRef(iface);
668 
669     return S_OK;
670   }
671   /* FIXME: IAVIStreaming interface */
672 
673   return E_NOINTERFACE;
674 }
675 
676 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface)
677 {
678   IAVIStreamImpl *This = impl_from_IAVIStream(iface);
679   ULONG ref = InterlockedIncrement(&This->ref);
680 
681   TRACE("(%p) ref=%d\n", This, ref);
682 
683   /* also add ref to parent, so that it doesn't kill us */
684   if (This->paf != NULL)
685     IAVIFile_AddRef(&This->paf->IAVIFile_iface);
686 
687   return ref;
688 }
689 
690 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream *iface)
691 {
692   IAVIStreamImpl *This = impl_from_IAVIStream(iface);
693   ULONG ref = InterlockedDecrement(&This->ref);
694 
695   TRACE("(%p) ref=%d\n", This, ref);
696 
697   if (This->paf != NULL)
698     IAVIFile_Release(&This->paf->IAVIFile_iface);
699 
700   return ref;
701 }
702 
703 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1, LPARAM lParam2)
704 {
705   TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2);
706 
707   /* This IAVIStream interface needs an AVIFile */
708   return AVIERR_UNSUPPORTED;
709 }
710 
711 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface, AVISTREAMINFOW *psi, LONG size)
712 {
713   IAVIStreamImpl *This = impl_from_IAVIStream(iface);
714 
715   TRACE("(%p,%p,%d)\n", iface, psi, size);
716 
717   if (psi == NULL)
718     return AVIERR_BADPARAM;
719   if (size < 0)
720     return AVIERR_BADSIZE;
721 
722   memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo)));
723 
724   if ((DWORD)size < sizeof(This->sInfo))
725     return AVIERR_BUFFERTOOSMALL;
726   return AVIERR_OK;
727 }
728 
729 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos, LONG flags)
730 {
731   IAVIStreamImpl *This = impl_from_IAVIStream(iface);
732   LONG offset = 0;
733 
734   TRACE("(%p,%d,0x%08X)\n",iface,pos,flags);
735 
736   if (flags & FIND_FROM_START) {
737     pos = This->sInfo.dwStart;
738     flags &= ~(FIND_FROM_START|FIND_PREV);
739     flags |= FIND_NEXT;
740   }
741 
742   if (This->sInfo.dwSampleSize != 0) {
743     /* convert samples into block number with offset */
744     AVIFILE_SamplesToBlock(This, &pos, &offset);
745   }
746 
747   if (flags & FIND_TYPE) {
748     if (flags & FIND_KEY) {
749       while (0 <= pos && pos <= This->lLastFrame) {
750 	if (This->idxFrames[pos].dwFlags & AVIIF_KEYFRAME)
751 	  goto RETURN_FOUND;
752 
753 	if (flags & FIND_NEXT)
754 	  pos++;
755 	else
756 	  pos--;
757       };
758     } else if (flags & FIND_ANY) {
759       while (0 <= pos && pos <= This->lLastFrame) {
760 	if (This->idxFrames[pos].dwChunkLength > 0)
761 	  goto RETURN_FOUND;
762 
763 	if (flags & FIND_NEXT)
764 	  pos++;
765 	else
766 	  pos--;
767 
768       };
769     } else if ((flags & FIND_FORMAT) && This->idxFmtChanges != NULL &&
770 	       This->sInfo.fccType == streamtypeVIDEO) {
771       if (flags & FIND_NEXT) {
772 	ULONG n;
773 
774 	for (n = 0; n < This->sInfo.dwFormatChangeCount; n++)
775 	  if (This->idxFmtChanges[n].ckid >= pos) {
776             pos = This->idxFmtChanges[n].ckid;
777 	    goto RETURN_FOUND;
778           }
779       } else {
780 	LONG n;
781 
782 	for (n = (LONG)This->sInfo.dwFormatChangeCount; n >= 0; n--) {
783 	  if (This->idxFmtChanges[n].ckid <= pos) {
784             pos = This->idxFmtChanges[n].ckid;
785 	    goto RETURN_FOUND;
786           }
787 	}
788 
789 	if (pos > (LONG)This->sInfo.dwStart)
790 	  return 0; /* format changes always for first frame */
791       }
792     }
793 
794     return -1;
795   }
796 
797  RETURN_FOUND:
798   if (pos < (LONG)This->sInfo.dwStart)
799     return -1;
800 
801   switch (flags & FIND_RET) {
802   case FIND_LENGTH:
803     /* physical size */
804     pos = This->idxFrames[pos].dwChunkLength;
805     break;
806   case FIND_OFFSET:
807     /* physical position */
808     pos = This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD)
809       + offset * This->sInfo.dwSampleSize;
810     break;
811   case FIND_SIZE:
812     /* logical size */
813     if (This->sInfo.dwSampleSize)
814       pos = This->sInfo.dwSampleSize;
815     else
816       pos = 1;
817     break;
818   case FIND_INDEX:
819     FIXME(": FIND_INDEX flag is not supported!\n");
820     /* This is an index in the index-table on disc. */
821     break;
822   }; /* else logical position */
823 
824   return pos;
825 }
826 
827 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos, void *format,
828     LONG *formatsize)
829 {
830   IAVIStreamImpl *This = impl_from_IAVIStream(iface);
831 
832   TRACE("(%p,%d,%p,%p)\n", iface, pos, format, formatsize);
833 
834   if (formatsize == NULL)
835     return AVIERR_BADPARAM;
836 
837   /* only interested in needed buffersize? */
838   if (format == NULL || *formatsize <= 0) {
839     *formatsize = This->cbFormat;
840 
841     return AVIERR_OK;
842   }
843 
844   /* copy initial format (only as much as will fit) */
845   memcpy(format, This->lpFormat, min(*(DWORD*)formatsize, This->cbFormat));
846   if (*(DWORD*)formatsize < This->cbFormat) {
847     *formatsize = This->cbFormat;
848     return AVIERR_BUFFERTOOSMALL;
849   }
850 
851   /* Could format change? When yes will it change? */
852   if ((This->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
853       pos > This->sInfo.dwStart) {
854     LONG lLastFmt;
855 
856     lLastFmt = IAVIStream_fnFindSample(iface, pos, FIND_FORMAT|FIND_PREV);
857     if (lLastFmt > 0) {
858       FIXME(": need to read formatchange for %d -- unimplemented!\n",lLastFmt);
859     }
860   }
861 
862   *formatsize = This->cbFormat;
863   return AVIERR_OK;
864 }
865 
866 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos, void *format,
867     LONG formatsize)
868 {
869   IAVIStreamImpl *This = impl_from_IAVIStream(iface);
870   BITMAPINFOHEADER *lpbiNew = format;
871 
872   TRACE("(%p,%d,%p,%d)\n", iface, pos, format, formatsize);
873 
874   /* check parameters */
875   if (format == NULL || formatsize <= 0)
876     return AVIERR_BADPARAM;
877 
878   /* Do we have write permission? */
879   if ((This->paf->uMode & MMIO_RWMODE) == 0)
880     return AVIERR_READONLY;
881 
882   /* can only set format before frame is written! */
883   if (This->lLastFrame > pos)
884     return AVIERR_UNSUPPORTED;
885 
886   /* initial format or a formatchange? */
887   if (This->lpFormat == NULL) {
888     /* initial format */
889     if (This->paf->dwMoviChunkPos != 0)
890       return AVIERR_ERROR; /* user has used API in wrong sequence! */
891 
892     This->lpFormat = HeapAlloc(GetProcessHeap(), 0, formatsize);
893     if (This->lpFormat == NULL)
894       return AVIERR_MEMORY;
895     This->cbFormat = formatsize;
896 
897     memcpy(This->lpFormat, format, formatsize);
898 
899     /* update some infos about stream */
900     if (This->sInfo.fccType == streamtypeVIDEO) {
901       LONG lDim;
902 
903       lDim = This->sInfo.rcFrame.right - This->sInfo.rcFrame.left;
904       if (lDim < lpbiNew->biWidth)
905 	This->sInfo.rcFrame.right = This->sInfo.rcFrame.left + lpbiNew->biWidth;
906       lDim = This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top;
907       if (lDim < lpbiNew->biHeight)
908 	This->sInfo.rcFrame.bottom = This->sInfo.rcFrame.top + lpbiNew->biHeight;
909     } else if (This->sInfo.fccType == streamtypeAUDIO)
910       This->sInfo.dwSampleSize = ((LPWAVEFORMATEX)This->lpFormat)->nBlockAlign;
911 
912     return AVIERR_OK;
913   } else {
914     MMCKINFO           ck;
915     LPBITMAPINFOHEADER lpbiOld = This->lpFormat;
916     RGBQUAD           *rgbNew  = (RGBQUAD*)((LPBYTE)lpbiNew + lpbiNew->biSize);
917     AVIPALCHANGE      *lppc = NULL;
918     UINT               n;
919 
920     /* perhaps format change, check it ... */
921     if (This->cbFormat != formatsize)
922       return AVIERR_UNSUPPORTED;
923 
924     /* no format change, only the initial one */
925     if (memcmp(This->lpFormat, format, formatsize) == 0)
926       return AVIERR_OK;
927 
928     /* check that's only the palette, which changes */
929     if (lpbiOld->biSize        != lpbiNew->biSize ||
930 	lpbiOld->biWidth       != lpbiNew->biWidth ||
931 	lpbiOld->biHeight      != lpbiNew->biHeight ||
932 	lpbiOld->biPlanes      != lpbiNew->biPlanes ||
933 	lpbiOld->biBitCount    != lpbiNew->biBitCount ||
934 	lpbiOld->biCompression != lpbiNew->biCompression ||
935 	lpbiOld->biClrUsed     != lpbiNew->biClrUsed)
936       return AVIERR_UNSUPPORTED;
937 
938     This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
939 
940     /* simply say all colors have changed */
941     ck.ckid   = MAKEAVICKID(cktypePALchange, This->nStream);
942     ck.cksize = 2 * sizeof(WORD) + lpbiOld->biClrUsed * sizeof(PALETTEENTRY);
943     lppc = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
944     if (lppc == NULL)
945       return AVIERR_MEMORY;
946 
947     lppc->bFirstEntry = 0;
948     lppc->bNumEntries = (lpbiOld->biClrUsed < 256 ? lpbiOld->biClrUsed : 0);
949     lppc->wFlags      = 0;
950     for (n = 0; n < lpbiOld->biClrUsed; n++) {
951       lppc->peNew[n].peRed   = rgbNew[n].rgbRed;
952       lppc->peNew[n].peGreen = rgbNew[n].rgbGreen;
953       lppc->peNew[n].peBlue  = rgbNew[n].rgbBlue;
954       lppc->peNew[n].peFlags = 0;
955     }
956 
957     if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1 ||
958         mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK ||
959         mmioWrite(This->paf->hmmio, (HPSTR)lppc, ck.cksize) != ck.cksize ||
960         mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
961     {
962       HeapFree(GetProcessHeap(), 0, lppc);
963       return AVIERR_FILEWRITE;
964     }
965 
966     This->paf->dwNextFramePos += ck.cksize + 2 * sizeof(DWORD);
967 
968     HeapFree(GetProcessHeap(), 0, lppc);
969 
970     return AVIFILE_AddFrame(This, cktypePALchange, n, ck.dwDataOffset, 0);
971   }
972 }
973 
974 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start, LONG samples, void *buffer,
975     LONG buffersize, LONG *bytesread, LONG *samplesread)
976 {
977   IAVIStreamImpl *This = impl_from_IAVIStream(iface);
978   DWORD size;
979   HRESULT hr;
980 
981   TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", iface, start, samples, buffer,
982  	buffersize, bytesread, samplesread);
983 
984   /* clear return parameters if given */
985   if (bytesread != NULL)
986     *bytesread = 0;
987   if (samplesread != NULL)
988     *samplesread = 0;
989 
990   /* check parameters */
991   if ((LONG)This->sInfo.dwStart > start)
992     return AVIERR_NODATA; /* couldn't read before start of stream */
993   if (This->sInfo.dwStart + This->sInfo.dwLength < (DWORD)start)
994     return AVIERR_NODATA; /* start is past end of stream */
995 
996   /* should we read as much as possible? */
997   if (samples == -1) {
998     /* User should know how much we have read */
999     if (bytesread == NULL && samplesread == NULL)
1000       return AVIERR_BADPARAM;
1001 
1002     if (This->sInfo.dwSampleSize != 0)
1003       samples = buffersize / This->sInfo.dwSampleSize;
1004     else
1005       samples = 1;
1006   }
1007 
1008   /* limit to end of stream */
1009   if ((LONG)This->sInfo.dwLength < samples)
1010     samples = This->sInfo.dwLength;
1011   if ((start - This->sInfo.dwStart) > (This->sInfo.dwLength - samples))
1012     samples = This->sInfo.dwLength - (start - This->sInfo.dwStart);
1013 
1014   /* nothing to read? Then leave ... */
1015   if (samples == 0)
1016     return AVIERR_OK;
1017 
1018   if (This->sInfo.dwSampleSize != 0) {
1019     /* fixed samplesize -- we can read over frame/block boundaries */
1020     LONG block = start;
1021     LONG offset = 0;
1022 
1023     if (!buffer)
1024     {
1025       if (bytesread)
1026         *bytesread = samples*This->sInfo.dwSampleSize;
1027       if (samplesread)
1028         *samplesread = samples;
1029       return AVIERR_OK;
1030     }
1031 
1032     /* convert start sample to block,offset pair */
1033     AVIFILE_SamplesToBlock(This, &block, &offset);
1034 
1035     /* convert samples to bytes */
1036     samples *= This->sInfo.dwSampleSize;
1037 
1038     while (samples > 0 && buffersize > 0) {
1039       LONG blocksize;
1040       if (block != This->dwCurrentFrame) {
1041 	hr = AVIFILE_ReadBlock(This, block, NULL, 0);
1042 	if (FAILED(hr))
1043 	  return hr;
1044       }
1045 
1046       size = min((DWORD)samples, (DWORD)buffersize);
1047       blocksize = This->lpBuffer[1];
1048       TRACE("blocksize = %u\n",blocksize);
1049       size = min(size, blocksize - offset);
1050       memcpy(buffer, ((BYTE*)&This->lpBuffer[2]) + offset, size);
1051 
1052       block++;
1053       offset = 0;
1054       buffer = ((LPBYTE)buffer)+size;
1055       samples    -= size;
1056       buffersize -= size;
1057 
1058       /* fill out return parameters if given */
1059       if (bytesread != NULL)
1060 	*bytesread   += size;
1061       if (samplesread != NULL)
1062 	*samplesread += size / This->sInfo.dwSampleSize;
1063     }
1064 
1065     if (samples == 0)
1066       return AVIERR_OK;
1067     else
1068       return AVIERR_BUFFERTOOSMALL;
1069   } else {
1070     /* variable samplesize -- we can only read one full frame/block */
1071     if (samples > 1)
1072       samples = 1;
1073 
1074     assert(start <= This->lLastFrame);
1075     size = This->idxFrames[start].dwChunkLength;
1076     if (buffer != NULL && buffersize >= size) {
1077       hr = AVIFILE_ReadBlock(This, start, buffer, size);
1078       if (FAILED(hr))
1079 	return hr;
1080     } else if (buffer != NULL)
1081       return AVIERR_BUFFERTOOSMALL;
1082 
1083     /* fill out return parameters if given */
1084     if (bytesread != NULL)
1085       *bytesread = size;
1086     if (samplesread != NULL)
1087       *samplesread = samples;
1088 
1089     return AVIERR_OK;
1090   }
1091 }
1092 
1093 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start, LONG samples, void *buffer,
1094     LONG buffersize, DWORD flags, LONG *sampwritten, LONG *byteswritten)
1095 {
1096   IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1097   FOURCC ckid;
1098   HRESULT hr;
1099 
1100   TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n", iface, start, samples,
1101 	buffer, buffersize, flags, sampwritten, byteswritten);
1102 
1103   /* clear return parameters if given */
1104   if (sampwritten != NULL)
1105     *sampwritten = 0;
1106   if (byteswritten != NULL)
1107     *byteswritten = 0;
1108 
1109   /* check parameters */
1110   if (buffer == NULL && (buffersize > 0 || samples > 0))
1111     return AVIERR_BADPARAM;
1112 
1113   /* Have we write permission? */
1114   if ((This->paf->uMode & MMIO_RWMODE) == 0)
1115     return AVIERR_READONLY;
1116 
1117   switch (This->sInfo.fccType) {
1118   case streamtypeAUDIO:
1119     ckid = MAKEAVICKID(cktypeWAVEbytes, This->nStream);
1120     break;
1121   default:
1122     if ((flags & AVIIF_KEYFRAME) && buffersize != 0)
1123       ckid = MAKEAVICKID(cktypeDIBbits, This->nStream);
1124     else
1125       ckid = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
1126     break;
1127   };
1128 
1129   /* append to end of stream? */
1130   if (start == -1) {
1131     if (This->lLastFrame == -1)
1132       start = This->sInfo.dwStart;
1133     else
1134       start = This->sInfo.dwLength;
1135   } else if (This->lLastFrame == -1)
1136     This->sInfo.dwStart = start;
1137 
1138   if (This->sInfo.dwSampleSize != 0) {
1139     /* fixed sample size -- audio like */
1140     if (samples * This->sInfo.dwSampleSize != buffersize)
1141       return AVIERR_BADPARAM;
1142 
1143     /* Couldn't skip audio-like data -- User must supply appropriate silence */
1144     if (This->sInfo.dwLength != start)
1145       return AVIERR_UNSUPPORTED;
1146 
1147     /* Convert position to frame/block */
1148     start = This->lLastFrame + 1;
1149 
1150     if ((This->paf->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) == 0) {
1151       FIXME(": not interleaved, could collect audio data!\n");
1152     }
1153   } else {
1154     /* variable sample size -- video like */
1155     if (samples > 1)
1156       return AVIERR_UNSUPPORTED;
1157 
1158     /* must we fill up with empty frames? */
1159     if (This->lLastFrame != -1) {
1160       FOURCC ckid2 = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
1161 
1162       while (start > This->lLastFrame + 1) {
1163 	hr = AVIFILE_WriteBlock(This, This->lLastFrame + 1, ckid2, 0, NULL, 0);
1164 	if (FAILED(hr))
1165 	  return hr;
1166       }
1167     }
1168   }
1169 
1170   /* write the block now */
1171   hr = AVIFILE_WriteBlock(This, start, ckid, flags, buffer, buffersize);
1172   if (SUCCEEDED(hr)) {
1173     /* fill out return parameters if given */
1174     if (sampwritten != NULL)
1175       *sampwritten = samples;
1176     if (byteswritten != NULL)
1177       *byteswritten = buffersize;
1178   }
1179 
1180   return hr;
1181 }
1182 
1183 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start, LONG samples)
1184 {
1185   IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1186 
1187   FIXME("(%p,%d,%d): stub\n", iface, start, samples);
1188 
1189   /* check parameters */
1190   if (start < 0 || samples < 0)
1191     return AVIERR_BADPARAM;
1192 
1193   /* Delete before start of stream? */
1194   if (start + samples < This->sInfo.dwStart)
1195     return AVIERR_OK;
1196 
1197   /* Delete after end of stream? */
1198   if (start > This->sInfo.dwLength)
1199     return AVIERR_OK;
1200 
1201   /* For the rest we need write permissions */
1202   if ((This->paf->uMode & MMIO_RWMODE) == 0)
1203     return AVIERR_READONLY;
1204 
1205   /* 1. overwrite the data with JUNK
1206    *
1207    * if ISINTERLEAVED {
1208    *   2. concat all neighboured JUNK-blocks in this record to one
1209    *   3. if this record only contains JUNK and is at end set dwNextFramePos
1210    *      to start of this record, repeat this.
1211    * } else {
1212    *   2. concat all neighboured JUNK-blocks.
1213    *   3. if the JUNK block is at the end, then set dwNextFramePos to
1214    *      start of this block.
1215    * }
1216    */
1217 
1218   return AVIERR_UNSUPPORTED;
1219 }
1220 
1221 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc, void *lp, LONG *lpread)
1222 {
1223   IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1224 
1225   TRACE("(%p,0x%08X,%p,%p)\n", iface, fcc, lp, lpread);
1226 
1227   if (fcc == ckidSTREAMHANDLERDATA) {
1228     if (This->lpHandlerData != NULL && This->cbHandlerData > 0) {
1229       if (lp == NULL || *lpread <= 0) {
1230 	*lpread = This->cbHandlerData;
1231 	return AVIERR_OK;
1232       }
1233 
1234       memcpy(lp, This->lpHandlerData, min(This->cbHandlerData, *lpread));
1235       if (*lpread < This->cbHandlerData)
1236 	return AVIERR_BUFFERTOOSMALL;
1237       return AVIERR_OK;
1238     } else
1239       return AVIERR_NODATA;
1240   } else
1241     return ReadExtraChunk(&This->extra, fcc, lp, lpread);
1242 }
1243 
1244 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc, void *lp, LONG size)
1245 {
1246   IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1247 
1248   TRACE("(%p,0x%08x,%p,%d)\n", iface, fcc, lp, size);
1249 
1250   /* check parameters */
1251   if (lp == NULL)
1252     return AVIERR_BADPARAM;
1253   if (size <= 0)
1254     return AVIERR_BADSIZE;
1255 
1256   /* need write permission */
1257   if ((This->paf->uMode & MMIO_RWMODE) == 0)
1258     return AVIERR_READONLY;
1259 
1260   /* already written something to this file? */
1261   if (This->paf->dwMoviChunkPos != 0) {
1262     /* the data will be inserted before the 'movi' chunk, so check for
1263      * enough space */
1264     DWORD dwPos = AVIFILE_ComputeMoviStart(This->paf);
1265 
1266     /* ckid,size => 2 * sizeof(DWORD) */
1267     dwPos += 2 * sizeof(DWORD) + size;
1268     if (dwPos >= This->paf->dwMoviChunkPos - 2 * sizeof(DWORD))
1269       return AVIERR_UNSUPPORTED; /* not enough space left */
1270   }
1271 
1272   This->paf->fDirty = TRUE;
1273 
1274   if (fcc == ckidSTREAMHANDLERDATA) {
1275     if (This->lpHandlerData != NULL) {
1276       FIXME(": handler data already set -- overwrite?\n");
1277       return AVIERR_UNSUPPORTED;
1278     }
1279 
1280     This->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, size);
1281     if (This->lpHandlerData == NULL)
1282       return AVIERR_MEMORY;
1283     This->cbHandlerData = size;
1284     memcpy(This->lpHandlerData, lp, size);
1285 
1286     return AVIERR_OK;
1287   } else
1288     return WriteExtraChunk(&This->extra, fcc, lp, size);
1289 }
1290 
1291 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream *iface, AVISTREAMINFOW *info, LONG infolen)
1292 {
1293   FIXME("(%p,%p,%d): stub\n", iface, info, infolen);
1294 
1295   return E_FAIL;
1296 }
1297 
1298 static const struct IAVIStreamVtbl avist_vt = {
1299   IAVIStream_fnQueryInterface,
1300   IAVIStream_fnAddRef,
1301   IAVIStream_fnRelease,
1302   IAVIStream_fnCreate,
1303   IAVIStream_fnInfo,
1304   IAVIStream_fnFindSample,
1305   IAVIStream_fnReadFormat,
1306   IAVIStream_fnSetFormat,
1307   IAVIStream_fnRead,
1308   IAVIStream_fnWrite,
1309   IAVIStream_fnDelete,
1310   IAVIStream_fnReadData,
1311   IAVIStream_fnWriteData,
1312   IAVIStream_fnSetInfo
1313 };
1314 
1315 
1316 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, DWORD offset, DWORD flags)
1317 {
1318   UINT n;
1319 
1320   /* pre-conditions */
1321   assert(This != NULL);
1322 
1323   switch (TWOCCFromFOURCC(ckid)) {
1324   case cktypeDIBbits:
1325     if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1326       flags |= AVIIF_KEYFRAME;
1327     break;
1328   case cktypeDIBcompressed:
1329     if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1330       flags &= ~AVIIF_KEYFRAME;
1331     break;
1332   case cktypePALchange:
1333     if (This->sInfo.fccType != streamtypeVIDEO) {
1334       ERR(": found palette change in non-video stream!\n");
1335       return AVIERR_BADFORMAT;
1336     }
1337 
1338     if (This->idxFmtChanges == NULL || This->nIdxFmtChanges <= This->sInfo.dwFormatChangeCount) {
1339       DWORD new_count = This->nIdxFmtChanges + 16;
1340       void *new_buffer;
1341 
1342       if (This->idxFmtChanges == NULL) {
1343 	This->idxFmtChanges =
1344           HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * sizeof(AVIINDEXENTRY));
1345         if (!This->idxFmtChanges) return AVIERR_MEMORY;
1346       } else {
1347         new_buffer = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxFmtChanges,
1348                 new_count * sizeof(AVIINDEXENTRY));
1349         if (!new_buffer) return AVIERR_MEMORY;
1350         This->idxFmtChanges = new_buffer;
1351       }
1352       This->nIdxFmtChanges = new_count;
1353     }
1354 
1355     This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
1356     n = ++This->sInfo.dwFormatChangeCount;
1357     This->idxFmtChanges[n].ckid          = This->lLastFrame;
1358     This->idxFmtChanges[n].dwFlags       = 0;
1359     This->idxFmtChanges[n].dwChunkOffset = offset;
1360     This->idxFmtChanges[n].dwChunkLength = size;
1361 
1362     return AVIERR_OK;
1363   case cktypeWAVEbytes:
1364     if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1365       flags |= AVIIF_KEYFRAME;
1366     break;
1367   default:
1368     WARN(": unknown TWOCC 0x%04X found\n", TWOCCFromFOURCC(ckid));
1369     break;
1370   };
1371 
1372   /* first frame is always a keyframe */
1373   if (This->lLastFrame == -1)
1374     flags |= AVIIF_KEYFRAME;
1375 
1376   if (This->sInfo.dwSuggestedBufferSize < size)
1377     This->sInfo.dwSuggestedBufferSize = size;
1378 
1379   /* get memory for index */
1380   if (This->idxFrames == NULL || This->lLastFrame + 1 >= This->nIdxFrames) {
1381     This->nIdxFrames += 512;
1382     if (This->idxFrames == NULL)
1383       This->idxFrames = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->nIdxFrames * sizeof(AVIINDEXENTRY));
1384       else
1385 	This->idxFrames = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxFrames,
1386 			   This->nIdxFrames * sizeof(AVIINDEXENTRY));
1387     if (This->idxFrames == NULL)
1388       return AVIERR_MEMORY;
1389   }
1390 
1391   This->lLastFrame++;
1392   This->idxFrames[This->lLastFrame].ckid          = ckid;
1393   This->idxFrames[This->lLastFrame].dwFlags       = flags;
1394   This->idxFrames[This->lLastFrame].dwChunkOffset = offset;
1395   This->idxFrames[This->lLastFrame].dwChunkLength = size;
1396 
1397   /* update AVISTREAMINFO structure if necessary */
1398   if (This->sInfo.dwLength <= This->lLastFrame)
1399     This->sInfo.dwLength = This->lLastFrame + 1;
1400 
1401   return AVIERR_OK;
1402 }
1403 
1404 static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This)
1405 {
1406   /* pre-conditions */
1407   assert(This != NULL && This->ppStreams[0] != NULL);
1408 
1409   if (This->idxRecords == NULL || This->cbIdxRecords / sizeof(AVIINDEXENTRY) <= This->nIdxRecords) {
1410     DWORD new_count = This->cbIdxRecords + 1024 * sizeof(AVIINDEXENTRY);
1411     void *mem;
1412     if (!This->idxRecords)
1413       mem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, new_count);
1414     else
1415       mem = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxRecords, new_count);
1416     if (mem) {
1417       This->cbIdxRecords = new_count;
1418       This->idxRecords = mem;
1419     } else {
1420       HeapFree(GetProcessHeap(), 0, This->idxRecords);
1421       This->idxRecords = NULL;
1422       return AVIERR_MEMORY;
1423     }
1424   }
1425 
1426   assert(This->nIdxRecords < This->cbIdxRecords/sizeof(AVIINDEXENTRY));
1427 
1428   This->idxRecords[This->nIdxRecords].ckid          = listtypeAVIRECORD;
1429   This->idxRecords[This->nIdxRecords].dwFlags       = AVIIF_LIST;
1430   This->idxRecords[This->nIdxRecords].dwChunkOffset =
1431     This->ckLastRecord.dwDataOffset - 2 * sizeof(DWORD);
1432   This->idxRecords[This->nIdxRecords].dwChunkLength =
1433     This->ckLastRecord.cksize;
1434   This->nIdxRecords++;
1435 
1436   return AVIERR_OK;
1437 }
1438 
1439 static DWORD   AVIFILE_ComputeMoviStart(IAVIFileImpl *This)
1440 {
1441   DWORD dwPos;
1442   DWORD nStream;
1443 
1444   /* RIFF,hdrl,movi,avih => (3 * 3 + 2) * sizeof(DWORD) = 11 * sizeof(DWORD) */
1445   dwPos = 11 * sizeof(DWORD) + sizeof(MainAVIHeader);
1446 
1447   for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
1448     IAVIStreamImpl *pStream = This->ppStreams[nStream];
1449 
1450     /* strl,strh,strf => (3 + 2 * 2) * sizeof(DWORD) = 7 * sizeof(DWORD) */
1451     dwPos += 7 * sizeof(DWORD) + sizeof(AVIStreamHeader);
1452     dwPos += ((pStream->cbFormat + 1) & ~1U);
1453     if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0)
1454       dwPos += 2 * sizeof(DWORD) + ((pStream->cbHandlerData + 1) & ~1U);
1455     if (pStream->sInfo.szName[0])
1456       dwPos += 2 * sizeof(DWORD) + ((lstrlenW(pStream->sInfo.szName) + 1) & ~1U);
1457   }
1458 
1459   if (This->dwMoviChunkPos == 0) {
1460     This->dwNextFramePos = dwPos;
1461 
1462     /* pad to multiple of AVI_HEADERSIZE only if we are more than 8 bytes away from it */
1463     if (((dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1)) - dwPos > 2 * sizeof(DWORD))
1464       This->dwNextFramePos = (dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1);
1465 
1466     This->dwMoviChunkPos = This->dwNextFramePos - sizeof(DWORD);
1467   }
1468 
1469   return dwPos;
1470 }
1471 
1472 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, const AVISTREAMINFOW *asi)
1473 {
1474   IAVIStreamImpl *pstream;
1475 
1476   /* pre-conditions */
1477   assert(paf != NULL);
1478   assert(nr < MAX_AVISTREAMS);
1479   assert(paf->ppStreams[nr] != NULL);
1480 
1481   pstream = paf->ppStreams[nr];
1482 
1483   pstream->IAVIStream_iface.lpVtbl = &avist_vt;
1484   pstream->ref            = 0;
1485   pstream->paf            = paf;
1486   pstream->nStream        = nr;
1487   pstream->dwCurrentFrame = (DWORD)-1;
1488   pstream->lLastFrame    = -1;
1489 
1490   if (asi != NULL) {
1491     memcpy(&pstream->sInfo, asi, sizeof(pstream->sInfo));
1492 
1493     if (asi->dwLength > 0) {
1494       /* pre-allocate mem for frame-index structure */
1495       pstream->idxFrames =
1496 	HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asi->dwLength * sizeof(AVIINDEXENTRY));
1497       if (pstream->idxFrames != NULL)
1498 	pstream->nIdxFrames = asi->dwLength;
1499     }
1500     if (asi->dwFormatChangeCount > 0) {
1501       /* pre-allocate mem for formatchange-index structure */
1502       pstream->idxFmtChanges =
1503 	HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asi->dwFormatChangeCount * sizeof(AVIINDEXENTRY));
1504       if (pstream->idxFmtChanges != NULL)
1505 	pstream->nIdxFmtChanges = asi->dwFormatChangeCount;
1506     }
1507 
1508     /* These values will be computed */
1509     pstream->sInfo.dwLength              = 0;
1510     pstream->sInfo.dwSuggestedBufferSize = 0;
1511     pstream->sInfo.dwFormatChangeCount   = 0;
1512     pstream->sInfo.dwEditCount           = 1;
1513     if (pstream->sInfo.dwSampleSize > 0)
1514       SetRectEmpty(&pstream->sInfo.rcFrame);
1515   }
1516 
1517   pstream->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
1518 }
1519 
1520 static void    AVIFILE_DestructAVIStream(IAVIStreamImpl *This)
1521 {
1522   /* pre-conditions */
1523   assert(This != NULL);
1524 
1525   This->dwCurrentFrame = (DWORD)-1;
1526   This->lLastFrame    = -1;
1527   This->paf = NULL;
1528   if (This->idxFrames != NULL) {
1529     HeapFree(GetProcessHeap(), 0, This->idxFrames);
1530     This->idxFrames  = NULL;
1531     This->nIdxFrames = 0;
1532   }
1533   HeapFree(GetProcessHeap(), 0, This->idxFmtChanges);
1534   This->idxFmtChanges = NULL;
1535   if (This->lpBuffer != NULL) {
1536     HeapFree(GetProcessHeap(), 0, This->lpBuffer);
1537     This->lpBuffer = NULL;
1538     This->cbBuffer = 0;
1539   }
1540   if (This->lpHandlerData != NULL) {
1541     HeapFree(GetProcessHeap(), 0, This->lpHandlerData);
1542     This->lpHandlerData = NULL;
1543     This->cbHandlerData = 0;
1544   }
1545   if (This->extra.lp != NULL) {
1546     HeapFree(GetProcessHeap(), 0, This->extra.lp);
1547     This->extra.lp = NULL;
1548     This->extra.cb = 0;
1549   }
1550   if (This->lpFormat != NULL) {
1551     HeapFree(GetProcessHeap(), 0, This->lpFormat);
1552     This->lpFormat = NULL;
1553     This->cbFormat = 0;
1554   }
1555 }
1556 
1557 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This)
1558 {
1559   MainAVIHeader   MainAVIHdr;
1560   MMCKINFO        ckRIFF;
1561   MMCKINFO        ckLIST1;
1562   MMCKINFO        ckLIST2;
1563   MMCKINFO        ck;
1564   IAVIStreamImpl *pStream;
1565   DWORD           nStream;
1566   HRESULT         hr;
1567 
1568   if (This->hmmio == NULL)
1569     return AVIERR_FILEOPEN;
1570 
1571   /* initialize stream ptr's */
1572   memset(This->ppStreams, 0, sizeof(This->ppStreams));
1573 
1574   /* try to get "RIFF" chunk -- must not be at beginning of file! */
1575   ckRIFF.fccType = formtypeAVI;
1576   if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) {
1577     ERR(": not an AVI!\n");
1578     return AVIERR_FILEREAD;
1579   }
1580 
1581   /* get "LIST" "hdrl" */
1582   ckLIST1.fccType = listtypeAVIHEADER;
1583   hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF, MMIO_FINDLIST);
1584   if (FAILED(hr))
1585     return hr;
1586 
1587   /* get "avih" chunk */
1588   ck.ckid = ckidAVIMAINHDR;
1589   hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, MMIO_FINDCHUNK);
1590   if (FAILED(hr))
1591     return hr;
1592 
1593   if (ck.cksize != sizeof(MainAVIHdr)) {
1594     ERR(": invalid size of %d for MainAVIHeader!\n", ck.cksize);
1595     return AVIERR_BADFORMAT;
1596   }
1597   if (mmioRead(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
1598     return AVIERR_FILEREAD;
1599 
1600   /* check for MAX_AVISTREAMS limit */
1601   if (MainAVIHdr.dwStreams > MAX_AVISTREAMS) {
1602     WARN("file contains %u streams, but only supports %d -- change MAX_AVISTREAMS!\n", MainAVIHdr.dwStreams, MAX_AVISTREAMS);
1603     return AVIERR_UNSUPPORTED;
1604   }
1605 
1606   /* adjust permissions if copyrighted material in file */
1607   if (MainAVIHdr.dwFlags & AVIFILEINFO_COPYRIGHTED) {
1608     This->uMode &= ~MMIO_RWMODE;
1609     This->uMode |= MMIO_READ;
1610   }
1611 
1612   /* convert MainAVIHeader into AVIFILINFOW */
1613   memset(&This->fInfo, 0, sizeof(This->fInfo));
1614   This->fInfo.dwRate                = MainAVIHdr.dwMicroSecPerFrame;
1615   This->fInfo.dwScale               = 1000000;
1616   This->fInfo.dwMaxBytesPerSec      = MainAVIHdr.dwMaxBytesPerSec;
1617   This->fInfo.dwFlags               = MainAVIHdr.dwFlags;
1618   This->fInfo.dwCaps                = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
1619   This->fInfo.dwLength              = MainAVIHdr.dwTotalFrames;
1620   This->fInfo.dwStreams             = MainAVIHdr.dwStreams;
1621   This->fInfo.dwSuggestedBufferSize = 0;
1622   This->fInfo.dwWidth               = MainAVIHdr.dwWidth;
1623   This->fInfo.dwHeight              = MainAVIHdr.dwHeight;
1624   LoadStringW(AVIFILE_hModule, IDS_AVIFILETYPE, This->fInfo.szFileType,
1625 	      sizeof(This->fInfo.szFileType)/sizeof(This->fInfo.szFileType[0]));
1626 
1627   /* go back to into header list */
1628   if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1629     return AVIERR_FILEREAD;
1630 
1631   /* foreach stream exists a "LIST","strl" chunk */
1632   for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
1633     /* get next nested chunk in this "LIST","strl" */
1634     if (mmioDescend(This->hmmio, &ckLIST2, &ckLIST1, 0) != S_OK)
1635       return AVIERR_FILEREAD;
1636 
1637     /* nested chunk must be of type "LIST","strl" -- when not normally JUNK */
1638     if (ckLIST2.ckid == FOURCC_LIST &&
1639 	ckLIST2.fccType == listtypeSTREAMHEADER) {
1640       pStream = This->ppStreams[nStream] =
1641 	HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIStreamImpl));
1642       if (pStream == NULL)
1643 	return AVIERR_MEMORY;
1644       AVIFILE_ConstructAVIStream(This, nStream, NULL);
1645 
1646       ck.ckid = 0;
1647       while (mmioDescend(This->hmmio, &ck, &ckLIST2, 0) == S_OK) {
1648 	switch (ck.ckid) {
1649 	case ckidSTREAMHANDLERDATA:
1650 	  if (pStream->lpHandlerData != NULL)
1651 	    return AVIERR_BADFORMAT;
1652 	  pStream->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
1653 	  if (pStream->lpHandlerData == NULL)
1654 	    return AVIERR_MEMORY;
1655 	  pStream->cbHandlerData = ck.cksize;
1656 
1657           if (mmioRead(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize)
1658 	    return AVIERR_FILEREAD;
1659 	  break;
1660 	case ckidSTREAMFORMAT:
1661 	  if (pStream->lpFormat != NULL)
1662 	    return AVIERR_BADFORMAT;
1663           if (ck.cksize == 0)
1664             break;
1665 
1666 	  pStream->lpFormat = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
1667 	  if (pStream->lpFormat == NULL)
1668 	    return AVIERR_MEMORY;
1669 	  pStream->cbFormat = ck.cksize;
1670 
1671           if (mmioRead(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize)
1672 	    return AVIERR_FILEREAD;
1673 
1674 	  if (pStream->sInfo.fccType == streamtypeVIDEO) {
1675             LPBITMAPINFOHEADER lpbi = pStream->lpFormat;
1676 
1677 	    /* some corrections to the video format */
1678 	    if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
1679 	      lpbi->biClrUsed = 1u << lpbi->biBitCount;
1680 	    if (lpbi->biCompression == BI_RGB && lpbi->biSizeImage == 0)
1681 	      lpbi->biSizeImage = DIBWIDTHBYTES(*lpbi) * lpbi->biHeight;
1682 	    if (lpbi->biCompression != BI_RGB && lpbi->biBitCount == 8) {
1683 	      if (pStream->sInfo.fccHandler == mmioFOURCC('R','L','E','0') ||
1684 		  pStream->sInfo.fccHandler == mmioFOURCC('R','L','E',' '))
1685 		lpbi->biCompression = BI_RLE8;
1686 	    }
1687 	    if (lpbi->biCompression == BI_RGB &&
1688 		(pStream->sInfo.fccHandler == 0 ||
1689 		 pStream->sInfo.fccHandler == mmioFOURCC('N','O','N','E')))
1690 	      pStream->sInfo.fccHandler = comptypeDIB;
1691 
1692 	    /* init rcFrame if it's empty */
1693 	    if (IsRectEmpty(&pStream->sInfo.rcFrame))
1694 	      SetRect(&pStream->sInfo.rcFrame, 0, 0, lpbi->biWidth, lpbi->biHeight);
1695 	  }
1696 	  break;
1697 	case ckidSTREAMHEADER:
1698 	  {
1699 	    static const WCHAR streamTypeFmt[] = {'%','4','.','4','h','s',0};
1700 	    static const WCHAR streamNameFmt[] = {'%','s',' ','%','s',' ','#','%','d',0};
1701 
1702 	    AVIStreamHeader streamHdr;
1703 	    WCHAR           szType[25];
1704 	    UINT            count;
1705 	    LONG            n = ck.cksize;
1706 
1707 	    if (ck.cksize > sizeof(streamHdr))
1708 	      n = sizeof(streamHdr);
1709 
1710 	    if (mmioRead(This->hmmio, (HPSTR)&streamHdr, n) != n)
1711 	      return AVIERR_FILEREAD;
1712 
1713 	    pStream->sInfo.fccType               = streamHdr.fccType;
1714 	    pStream->sInfo.fccHandler            = streamHdr.fccHandler;
1715 	    pStream->sInfo.dwFlags               = streamHdr.dwFlags;
1716 	    pStream->sInfo.wPriority             = streamHdr.wPriority;
1717 	    pStream->sInfo.wLanguage             = streamHdr.wLanguage;
1718 	    pStream->sInfo.dwInitialFrames       = streamHdr.dwInitialFrames;
1719 	    pStream->sInfo.dwScale               = streamHdr.dwScale;
1720 	    pStream->sInfo.dwRate                = streamHdr.dwRate;
1721 	    pStream->sInfo.dwStart               = streamHdr.dwStart;
1722 	    pStream->sInfo.dwLength              = streamHdr.dwLength;
1723 	    pStream->sInfo.dwSuggestedBufferSize = 0;
1724 	    pStream->sInfo.dwQuality             = streamHdr.dwQuality;
1725 	    pStream->sInfo.dwSampleSize          = streamHdr.dwSampleSize;
1726 	    pStream->sInfo.rcFrame.left          = streamHdr.rcFrame.left;
1727 	    pStream->sInfo.rcFrame.top           = streamHdr.rcFrame.top;
1728 	    pStream->sInfo.rcFrame.right         = streamHdr.rcFrame.right;
1729 	    pStream->sInfo.rcFrame.bottom        = streamHdr.rcFrame.bottom;
1730 	    pStream->sInfo.dwEditCount           = 0;
1731 	    pStream->sInfo.dwFormatChangeCount   = 0;
1732 
1733 	    /* generate description for stream like "filename.avi Type #n" */
1734 	    if (streamHdr.fccType == streamtypeVIDEO)
1735 	      LoadStringW(AVIFILE_hModule, IDS_VIDEO, szType, sizeof(szType)/sizeof(szType[0]));
1736 	    else if (streamHdr.fccType == streamtypeAUDIO)
1737 	      LoadStringW(AVIFILE_hModule, IDS_AUDIO, szType, sizeof(szType)/sizeof(szType[0]));
1738 	    else
1739 	      wsprintfW(szType, streamTypeFmt, (char*)&streamHdr.fccType);
1740 
1741 	    /* get count of this streamtype up to this stream */
1742 	    count = 0;
1743 	    for (n = nStream; 0 <= n; n--) {
1744 	      if (This->ppStreams[n]->sInfo.fccHandler == streamHdr.fccType)
1745 		count++;
1746 	    }
1747 
1748 	    memset(pStream->sInfo.szName, 0, sizeof(pStream->sInfo.szName));
1749 
1750 	    /* FIXME: avoid overflow -- better use wsnprintfW, which doesn't exists ! */
1751 	    wsprintfW(pStream->sInfo.szName, streamNameFmt,
1752 		      AVIFILE_BasenameW(This->szFileName), szType, count);
1753 	  }
1754 	  break;
1755 	case ckidSTREAMNAME:
1756 	  { /* streamname will be saved as ASCII string */
1757 	    LPSTR str = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
1758 	    if (str == NULL)
1759 	      return AVIERR_MEMORY;
1760 
1761 	    if (mmioRead(This->hmmio, str, ck.cksize) != ck.cksize)
1762 	    {
1763 	      HeapFree(GetProcessHeap(), 0, str);
1764 	      return AVIERR_FILEREAD;
1765 	    }
1766 
1767 	    MultiByteToWideChar(CP_ACP, 0, str, -1, pStream->sInfo.szName,
1768 				sizeof(pStream->sInfo.szName)/sizeof(pStream->sInfo.szName[0]));
1769 
1770 	    HeapFree(GetProcessHeap(), 0, str);
1771 	  }
1772 	  break;
1773 	case ckidAVIPADDING:
1774 	case mmioFOURCC('p','a','d','d'):
1775 	  break;
1776 	default:
1777           WARN(": found extra chunk 0x%08X\n", ck.ckid);
1778 	  hr = ReadChunkIntoExtra(&pStream->extra, This->hmmio, &ck);
1779 	  if (FAILED(hr))
1780 	    return hr;
1781 	};
1782 	if (pStream->lpFormat != NULL && pStream->sInfo.fccType == streamtypeAUDIO)
1783 	{
1784 	  WAVEFORMATEX *wfx = pStream->lpFormat;          /* wfx->nBlockAlign = wfx->nChannels * wfx->wBitsPerSample / 8; could be added */
1785 	  pStream->sInfo.dwSampleSize = wfx->nBlockAlign; /* to deal with corrupt wfx->nBlockAlign but Windows doesn't do this */
1786 	  TRACE("Block size reset to %u, chan=%u bpp=%u\n", wfx->nBlockAlign, wfx->nChannels, wfx->wBitsPerSample);
1787 	  pStream->sInfo.dwScale = 1;
1788 	  pStream->sInfo.dwRate = wfx->nSamplesPerSec;
1789 	}
1790 	if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1791 	  return AVIERR_FILEREAD;
1792       }
1793     } else {
1794       /* nested chunks in "LIST","hdrl" which are not of type "LIST","strl" */
1795       hr = ReadChunkIntoExtra(&This->fileextra, This->hmmio, &ckLIST2);
1796       if (FAILED(hr))
1797 	return hr;
1798     }
1799     if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
1800       return AVIERR_FILEREAD;
1801   }
1802 
1803   /* read any extra headers in "LIST","hdrl" */
1804   FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, 0);
1805   if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
1806     return AVIERR_FILEREAD;
1807 
1808   /* search "LIST","movi" chunk in "RIFF","AVI " */
1809   ckLIST1.fccType = listtypeAVIMOVIE;
1810   hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF,
1811 			      MMIO_FINDLIST);
1812   if (FAILED(hr))
1813     return hr;
1814 
1815   This->dwMoviChunkPos = ckLIST1.dwDataOffset;
1816   This->dwIdxChunkPos  = ckLIST1.cksize + ckLIST1.dwDataOffset;
1817   if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
1818     return AVIERR_FILEREAD;
1819 
1820   /* try to find an index */
1821   ck.ckid = ckidAVINEWINDEX;
1822   hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio,
1823 			      &ck, &ckRIFF, MMIO_FINDCHUNK);
1824   if (SUCCEEDED(hr) && ck.cksize > 0) {
1825     if (FAILED(AVIFILE_LoadIndex(This, ck.cksize, ckLIST1.dwDataOffset)))
1826       This->fInfo.dwFlags &= ~AVIFILEINFO_HASINDEX;
1827   }
1828 
1829   /* when we haven't found an index or it's bad, then build one
1830    * by parsing 'movi' chunk */
1831   if ((This->fInfo.dwFlags & AVIFILEINFO_HASINDEX) == 0) {
1832     for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
1833       This->ppStreams[nStream]->lLastFrame = -1;
1834 
1835     if (mmioSeek(This->hmmio, ckLIST1.dwDataOffset + sizeof(DWORD), SEEK_SET) == -1) {
1836       ERR(": Oops, can't seek back to 'movi' chunk!\n");
1837       return AVIERR_FILEREAD;
1838     }
1839 
1840     /* seek through the 'movi' list until end */
1841     while (mmioDescend(This->hmmio, &ck, &ckLIST1, 0) == S_OK) {
1842       if (ck.ckid != FOURCC_LIST) {
1843 	if (mmioAscend(This->hmmio, &ck, 0) == S_OK) {
1844 	  nStream = StreamFromFOURCC(ck.ckid);
1845 
1846 	  if (nStream > This->fInfo.dwStreams)
1847 	    return AVIERR_BADFORMAT;
1848 
1849 	  AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
1850 			   ck.dwDataOffset - 2 * sizeof(DWORD), 0);
1851 	} else {
1852 	  nStream = StreamFromFOURCC(ck.ckid);
1853 	  WARN(": file seems to be truncated!\n");
1854 	  if (nStream <= This->fInfo.dwStreams &&
1855 	      This->ppStreams[nStream]->sInfo.dwSampleSize > 0) {
1856 	    ck.cksize = mmioSeek(This->hmmio, 0, SEEK_END);
1857 	    if (ck.cksize != -1) {
1858 	      ck.cksize -= ck.dwDataOffset;
1859 	      ck.cksize &= ~(This->ppStreams[nStream]->sInfo.dwSampleSize - 1);
1860 
1861 	      AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
1862 			       ck.dwDataOffset - 2 * sizeof(DWORD), 0);
1863 	    }
1864 	  }
1865 	}
1866       }
1867     }
1868   }
1869 
1870   for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
1871   {
1872     DWORD sugbuf =  This->ppStreams[nStream]->sInfo.dwSuggestedBufferSize;
1873     if (This->fInfo.dwSuggestedBufferSize < sugbuf)
1874       This->fInfo.dwSuggestedBufferSize = sugbuf;
1875   }
1876 
1877   /* find other chunks */
1878   FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckRIFF, 0);
1879 
1880   return AVIERR_OK;
1881 }
1882 
1883 static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset)
1884 {
1885   AVIINDEXENTRY *lp;
1886   DWORD          pos, n;
1887   HRESULT        hr = AVIERR_OK;
1888   BOOL           bAbsolute = TRUE;
1889 
1890   lp = HeapAlloc(GetProcessHeap(), 0, IDX_PER_BLOCK * sizeof(AVIINDEXENTRY));
1891   if (lp == NULL)
1892     return AVIERR_MEMORY;
1893 
1894   /* adjust limits for index tables, so that inserting will be faster */
1895   for (n = 0; n < This->fInfo.dwStreams; n++) {
1896     IAVIStreamImpl *pStream = This->ppStreams[n];
1897 
1898     pStream->lLastFrame = -1;
1899 
1900     if (pStream->idxFrames != NULL) {
1901       HeapFree(GetProcessHeap(), 0, pStream->idxFrames);
1902       pStream->idxFrames  = NULL;
1903       pStream->nIdxFrames = 0;
1904     }
1905 
1906     if (pStream->sInfo.dwSampleSize != 0) {
1907       if (n > 0 && This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
1908 	pStream->nIdxFrames = This->ppStreams[0]->nIdxFrames;
1909       } else if (pStream->sInfo.dwSuggestedBufferSize) {
1910 	pStream->nIdxFrames =
1911 	  pStream->sInfo.dwLength / pStream->sInfo.dwSuggestedBufferSize;
1912       }
1913     } else
1914       pStream->nIdxFrames = pStream->sInfo.dwLength;
1915 
1916     pStream->idxFrames =
1917       HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pStream->nIdxFrames * sizeof(AVIINDEXENTRY));
1918     if (pStream->idxFrames == NULL && pStream->nIdxFrames > 0) {
1919       pStream->nIdxFrames = 0;
1920       HeapFree(GetProcessHeap(), 0, lp);
1921       return AVIERR_MEMORY;
1922     }
1923   }
1924 
1925   pos = (DWORD)-1;
1926   while (size != 0) {
1927     LONG read = min(IDX_PER_BLOCK * sizeof(AVIINDEXENTRY), size);
1928 
1929     if (mmioRead(This->hmmio, (HPSTR)lp, read) != read) {
1930       hr = AVIERR_FILEREAD;
1931       break;
1932     }
1933     size -= read;
1934 
1935     if (pos == (DWORD)-1)
1936       pos = offset - lp->dwChunkOffset + sizeof(DWORD);
1937 
1938     AVIFILE_ParseIndex(This, lp, read / sizeof(AVIINDEXENTRY),
1939 		       pos, &bAbsolute);
1940   }
1941 
1942   HeapFree(GetProcessHeap(), 0, lp);
1943 
1944   /* checking ... */
1945   for (n = 0; n < This->fInfo.dwStreams; n++) {
1946     IAVIStreamImpl *pStream = This->ppStreams[n];
1947 
1948     if (pStream->sInfo.dwSampleSize == 0 &&
1949 	pStream->sInfo.dwLength != pStream->lLastFrame+1)
1950       ERR("stream %u length mismatch: dwLength=%u found=%d\n",
1951 	   n, pStream->sInfo.dwLength, pStream->lLastFrame);
1952   }
1953 
1954   return hr;
1955 }
1956 
1957 static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp,
1958 				  LONG count, DWORD pos, BOOL *bAbsolute)
1959 {
1960   if (lp == NULL)
1961     return AVIERR_BADPARAM;
1962 
1963   for (; count > 0; count--, lp++) {
1964     WORD nStream = StreamFromFOURCC(lp->ckid);
1965 
1966     if (lp->ckid == listtypeAVIRECORD || nStream == 0x7F)
1967       continue; /* skip these */
1968 
1969     if (nStream > This->fInfo.dwStreams)
1970       return AVIERR_BADFORMAT;
1971 
1972     /* Video frames can be either indexed in a relative position to the
1973      * "movi" chunk or in a absolute position in the file. If the index
1974      * is relative the frame offset will always be so small that it will
1975      * virtually never reach the "movi" offset so we can detect if the
1976      * video is relative very fast.
1977      */
1978     if (*bAbsolute && lp->dwChunkOffset < This->dwMoviChunkPos)
1979       *bAbsolute = FALSE;
1980 
1981     if (!*bAbsolute)
1982       lp->dwChunkOffset += pos; /* make the offset absolute */
1983 
1984     if (FAILED(AVIFILE_AddFrame(This->ppStreams[nStream], lp->ckid, lp->dwChunkLength, lp->dwChunkOffset, lp->dwFlags)))
1985       return AVIERR_MEMORY;
1986   }
1987 
1988   return AVIERR_OK;
1989 }
1990 
1991 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD pos,
1992 				 LPVOID buffer, DWORD size)
1993 {
1994   /* pre-conditions */
1995   assert(This != NULL);
1996   assert(This->paf != NULL);
1997   assert(This->paf->hmmio != NULL);
1998   assert(This->sInfo.dwStart <= pos && pos < This->sInfo.dwLength);
1999   assert(pos <= This->lLastFrame);
2000 
2001   /* should we read as much as block gives us? */
2002   if (size == 0 || size > This->idxFrames[pos].dwChunkLength)
2003     size = This->idxFrames[pos].dwChunkLength;
2004 
2005   /* read into out own buffer or given one? */
2006   if (buffer == NULL) {
2007     /* we also read the chunk */
2008     size += 2 * sizeof(DWORD);
2009 
2010     /* check that buffer is big enough -- don't trust dwSuggestedBufferSize */
2011     if (This->lpBuffer == NULL || This->cbBuffer < size) {
2012       DWORD maxSize = max(size, This->sInfo.dwSuggestedBufferSize);
2013 
2014       if (This->lpBuffer == NULL) {
2015 	This->lpBuffer = HeapAlloc(GetProcessHeap(), 0, maxSize);
2016         if (!This->lpBuffer) return AVIERR_MEMORY;
2017       } else {
2018         void *new_buffer = HeapReAlloc(GetProcessHeap(), 0, This->lpBuffer, maxSize);
2019         if (!new_buffer) return AVIERR_MEMORY;
2020         This->lpBuffer = new_buffer;
2021       }
2022       This->cbBuffer = maxSize;
2023     }
2024 
2025     /* now read the complete chunk into our buffer */
2026     if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset, SEEK_SET) == -1)
2027       return AVIERR_FILEREAD;
2028     if (mmioRead(This->paf->hmmio, (HPSTR)This->lpBuffer, size) != size)
2029       return AVIERR_FILEREAD;
2030 
2031     /* check if it was the correct block which we have read */
2032     if (This->lpBuffer[0] != This->idxFrames[pos].ckid ||
2033 	This->lpBuffer[1] != This->idxFrames[pos].dwChunkLength) {
2034       ERR(": block %d not found at 0x%08X\n", pos, This->idxFrames[pos].dwChunkOffset);
2035       ERR(": Index says: '%4.4s'(0x%08X) size 0x%08X\n",
2036 	  (char*)&This->idxFrames[pos].ckid, This->idxFrames[pos].ckid,
2037 	  This->idxFrames[pos].dwChunkLength);
2038       ERR(": Data  says: '%4.4s'(0x%08X) size 0x%08X\n",
2039 	  (char*)&This->lpBuffer[0], This->lpBuffer[0], This->lpBuffer[1]);
2040       return AVIERR_FILEREAD;
2041     }
2042   } else {
2043     if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD), SEEK_SET) == -1)
2044       return AVIERR_FILEREAD;
2045     if (mmioRead(This->paf->hmmio, buffer, size) != size)
2046       return AVIERR_FILEREAD;
2047   }
2048 
2049   return AVIERR_OK;
2050 }
2051 
2052 static void AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos, LPLONG offset)
2053 {
2054   LONG block;
2055 
2056   /* pre-conditions */
2057   assert(This != NULL);
2058   assert(pos != NULL);
2059   assert(offset != NULL);
2060   assert(This->sInfo.dwSampleSize != 0);
2061   assert(*pos >= This->sInfo.dwStart);
2062 
2063   /* convert start sample to start bytes */
2064   (*offset)  = (*pos) - This->sInfo.dwStart;
2065   (*offset) *= This->sInfo.dwSampleSize;
2066 
2067   /* convert bytes to block number */
2068   for (block = 0; block <= This->lLastFrame; block++) {
2069     if (This->idxFrames[block].dwChunkLength <= *offset)
2070       (*offset) -= This->idxFrames[block].dwChunkLength;
2071     else
2072       break;
2073   }
2074 
2075   *pos = block;
2076 }
2077 
2078 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This)
2079 {
2080   MainAVIHeader   MainAVIHdr;
2081   IAVIStreamImpl* pStream;
2082   MMCKINFO        ckRIFF;
2083   MMCKINFO        ckLIST1;
2084   MMCKINFO        ckLIST2;
2085   MMCKINFO        ck;
2086   DWORD           nStream;
2087   DWORD           dwPos;
2088   HRESULT         hr;
2089 
2090   /* initialize some things */
2091   if (This->dwMoviChunkPos == 0)
2092     AVIFILE_ComputeMoviStart(This);
2093 
2094   /* written one record too much? */
2095   if (This->ckLastRecord.dwFlags & MMIO_DIRTY) {
2096     This->dwNextFramePos -= 3 * sizeof(DWORD);
2097     if (This->nIdxRecords > 0)
2098       This->nIdxRecords--;
2099   }
2100 
2101   AVIFILE_UpdateInfo(This);
2102 
2103   assert(This->fInfo.dwScale != 0);
2104 
2105   memset(&MainAVIHdr, 0, sizeof(MainAVIHdr));
2106   MainAVIHdr.dwMicroSecPerFrame    = MulDiv(This->fInfo.dwRate, 1000000,
2107 					    This->fInfo.dwScale);
2108   MainAVIHdr.dwMaxBytesPerSec      = This->fInfo.dwMaxBytesPerSec;
2109   MainAVIHdr.dwPaddingGranularity  = AVI_HEADERSIZE;
2110   MainAVIHdr.dwFlags               = This->fInfo.dwFlags;
2111   MainAVIHdr.dwTotalFrames         = This->fInfo.dwLength;
2112   MainAVIHdr.dwInitialFrames       = 0;
2113   MainAVIHdr.dwStreams             = This->fInfo.dwStreams;
2114   MainAVIHdr.dwSuggestedBufferSize = This->fInfo.dwSuggestedBufferSize;
2115   MainAVIHdr.dwWidth               = This->fInfo.dwWidth;
2116   MainAVIHdr.dwHeight              = This->fInfo.dwHeight;
2117   MainAVIHdr.dwInitialFrames       = This->dwInitialFrames;
2118 
2119   /* now begin writing ... */
2120   mmioSeek(This->hmmio, 0, SEEK_SET);
2121 
2122   /* RIFF chunk */
2123   ckRIFF.cksize  = 0;
2124   ckRIFF.fccType = formtypeAVI;
2125   if (mmioCreateChunk(This->hmmio, &ckRIFF, MMIO_CREATERIFF) != S_OK)
2126     return AVIERR_FILEWRITE;
2127 
2128   /* AVI headerlist */
2129   ckLIST1.cksize  = 0;
2130   ckLIST1.fccType = listtypeAVIHEADER;
2131   if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
2132     return AVIERR_FILEWRITE;
2133 
2134   /* MainAVIHeader */
2135   ck.ckid    = ckidAVIMAINHDR;
2136   ck.cksize  = sizeof(MainAVIHdr);
2137   ck.fccType = 0;
2138   if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2139     return AVIERR_FILEWRITE;
2140   if (mmioWrite(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
2141     return AVIERR_FILEWRITE;
2142   if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2143     return AVIERR_FILEWRITE;
2144 
2145   /* write the headers of each stream into a separate streamheader list */
2146   for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2147     AVIStreamHeader strHdr;
2148 
2149     pStream = This->ppStreams[nStream];
2150 
2151     /* begin the new streamheader list */
2152     ckLIST2.cksize  = 0;
2153     ckLIST2.fccType = listtypeSTREAMHEADER;
2154     if (mmioCreateChunk(This->hmmio, &ckLIST2, MMIO_CREATELIST) != S_OK)
2155       return AVIERR_FILEWRITE;
2156 
2157     /* create an AVIStreamHeader from the AVSTREAMINFO */
2158     strHdr.fccType               = pStream->sInfo.fccType;
2159     strHdr.fccHandler            = pStream->sInfo.fccHandler;
2160     strHdr.dwFlags               = pStream->sInfo.dwFlags;
2161     strHdr.wPriority             = pStream->sInfo.wPriority;
2162     strHdr.wLanguage             = pStream->sInfo.wLanguage;
2163     strHdr.dwInitialFrames       = pStream->sInfo.dwInitialFrames;
2164     strHdr.dwScale               = pStream->sInfo.dwScale;
2165     strHdr.dwRate                = pStream->sInfo.dwRate;
2166     strHdr.dwStart               = pStream->sInfo.dwStart;
2167     strHdr.dwLength              = pStream->sInfo.dwLength;
2168     strHdr.dwSuggestedBufferSize = pStream->sInfo.dwSuggestedBufferSize;
2169     strHdr.dwQuality             = pStream->sInfo.dwQuality;
2170     strHdr.dwSampleSize          = pStream->sInfo.dwSampleSize;
2171     strHdr.rcFrame.left          = pStream->sInfo.rcFrame.left;
2172     strHdr.rcFrame.top           = pStream->sInfo.rcFrame.top;
2173     strHdr.rcFrame.right         = pStream->sInfo.rcFrame.right;
2174     strHdr.rcFrame.bottom        = pStream->sInfo.rcFrame.bottom;
2175 
2176     /* now write the AVIStreamHeader */
2177     ck.ckid   = ckidSTREAMHEADER;
2178     ck.cksize = sizeof(strHdr);
2179     if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2180       return AVIERR_FILEWRITE;
2181     if (mmioWrite(This->hmmio, (HPSTR)&strHdr, ck.cksize) != ck.cksize)
2182       return AVIERR_FILEWRITE;
2183     if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2184       return AVIERR_FILEWRITE;
2185 
2186     /* ... the hopefully ever present streamformat ... */
2187     ck.ckid   = ckidSTREAMFORMAT;
2188     ck.cksize = pStream->cbFormat;
2189     if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2190       return AVIERR_FILEWRITE;
2191     if (pStream->lpFormat != NULL && ck.cksize > 0) {
2192       if (mmioWrite(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize)
2193 	return AVIERR_FILEWRITE;
2194     }
2195     if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2196       return AVIERR_FILEWRITE;
2197 
2198     /* ... some optional existing handler data ... */
2199     if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0) {
2200       ck.ckid   = ckidSTREAMHANDLERDATA;
2201       ck.cksize = pStream->cbHandlerData;
2202       if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2203 	return AVIERR_FILEWRITE;
2204       if (mmioWrite(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize)
2205 	return AVIERR_FILEWRITE;
2206       if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2207 	return AVIERR_FILEWRITE;
2208     }
2209 
2210     /* ... some optional additional extra chunk for this stream ... */
2211     if (pStream->extra.lp != NULL && pStream->extra.cb > 0) {
2212       /* the chunk header(s) are already in the structure */
2213       if (mmioWrite(This->hmmio, pStream->extra.lp, pStream->extra.cb) != pStream->extra.cb)
2214 	return AVIERR_FILEWRITE;
2215     }
2216 
2217     /* ... an optional name for this stream ... */
2218     if (pStream->sInfo.szName[0]) {
2219       LPSTR str;
2220 
2221       ck.ckid   = ckidSTREAMNAME;
2222       ck.cksize = lstrlenW(pStream->sInfo.szName) + 1;
2223       if (ck.cksize & 1) /* align */
2224 	ck.cksize++;
2225       if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2226 	return AVIERR_FILEWRITE;
2227 
2228       /* the streamname must be saved in ASCII not Unicode */
2229       str = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
2230       if (str == NULL)
2231 	return AVIERR_MEMORY;
2232       WideCharToMultiByte(CP_ACP, 0, pStream->sInfo.szName, -1, str,
2233 			  ck.cksize, NULL, NULL);
2234 
2235       if (mmioWrite(This->hmmio, str, ck.cksize) != ck.cksize) {
2236 	HeapFree(GetProcessHeap(), 0, str);
2237 	return AVIERR_FILEWRITE;
2238       }
2239 
2240       HeapFree(GetProcessHeap(), 0, str);
2241       if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2242 	return AVIERR_FILEWRITE;
2243     }
2244 
2245     /* close streamheader list for this stream */
2246     if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
2247       return AVIERR_FILEWRITE;
2248   } /* for (0 <= nStream < MainAVIHdr.dwStreams) */
2249 
2250   /* close the aviheader list */
2251   if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
2252     return AVIERR_FILEWRITE;
2253 
2254   /* check for padding to pre-guessed 'movi'-chunk position */
2255   dwPos = ckLIST1.dwDataOffset + ckLIST1.cksize;
2256   if (This->dwMoviChunkPos - 2 * sizeof(DWORD) > dwPos) {
2257     ck.ckid   = ckidAVIPADDING;
2258     ck.cksize = This->dwMoviChunkPos - dwPos - 4 * sizeof(DWORD);
2259     assert((LONG)ck.cksize >= 0);
2260 
2261     if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2262       return AVIERR_FILEWRITE;
2263     if (mmioSeek(This->hmmio, ck.cksize, SEEK_CUR) == -1)
2264       return AVIERR_FILEWRITE;
2265     if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2266       return AVIERR_FILEWRITE;
2267   }
2268 
2269   /* now write the 'movi' chunk */
2270   mmioSeek(This->hmmio, This->dwMoviChunkPos - 2 * sizeof(DWORD), SEEK_SET);
2271   ckLIST1.cksize  = 0;
2272   ckLIST1.fccType = listtypeAVIMOVIE;
2273   if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
2274     return AVIERR_FILEWRITE;
2275   if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1)
2276     return AVIERR_FILEWRITE;
2277   if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
2278     return AVIERR_FILEWRITE;
2279 
2280   /* write 'idx1' chunk */
2281   hr = AVIFILE_SaveIndex(This);
2282   if (FAILED(hr))
2283     return hr;
2284 
2285   /* write optional extra file chunks */
2286   if (This->fileextra.lp != NULL && This->fileextra.cb > 0) {
2287     /* as for the streams, are the chunk header(s) in the structure */
2288     if (mmioWrite(This->hmmio, This->fileextra.lp, This->fileextra.cb) != This->fileextra.cb)
2289       return AVIERR_FILEWRITE;
2290   }
2291 
2292   /* close RIFF chunk */
2293   if (mmioAscend(This->hmmio, &ckRIFF, 0) != S_OK)
2294     return AVIERR_FILEWRITE;
2295 
2296   /* add some JUNK at end for bad parsers */
2297   memset(&ckRIFF, 0, sizeof(ckRIFF));
2298   mmioWrite(This->hmmio, (HPSTR)&ckRIFF, sizeof(ckRIFF));
2299   mmioFlush(This->hmmio, 0);
2300 
2301   return AVIERR_OK;
2302 }
2303 
2304 static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This)
2305 {
2306   IAVIStreamImpl *pStream;
2307   AVIINDEXENTRY   idx;
2308   MMCKINFO        ck;
2309   DWORD           nStream;
2310   LONG            n;
2311 
2312   ck.ckid   = ckidAVINEWINDEX;
2313   ck.cksize = 0;
2314   if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2315     return AVIERR_FILEWRITE;
2316 
2317   if (This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
2318     /* is interleaved -- write block of corresponding frames */
2319     LONG lInitialFrames = 0;
2320     LONG stepsize;
2321     LONG i;
2322 
2323     if (This->ppStreams[0]->sInfo.dwSampleSize == 0)
2324       stepsize = 1;
2325     else
2326       stepsize = AVIStreamTimeToSample(&This->ppStreams[0]->IAVIStream_iface, 1000000);
2327 
2328     assert(stepsize > 0);
2329 
2330     for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2331       if (lInitialFrames < This->ppStreams[nStream]->sInfo.dwInitialFrames)
2332 	lInitialFrames = This->ppStreams[nStream]->sInfo.dwInitialFrames;
2333     }
2334 
2335     for (i = -lInitialFrames; i < (LONG)This->fInfo.dwLength - lInitialFrames;
2336 	 i += stepsize) {
2337       DWORD nFrame = lInitialFrames + i;
2338 
2339       assert(nFrame < This->nIdxRecords);
2340 
2341       idx.ckid          = listtypeAVIRECORD;
2342       idx.dwFlags       = AVIIF_LIST;
2343       idx.dwChunkLength = This->idxRecords[nFrame].dwChunkLength;
2344       idx.dwChunkOffset = This->idxRecords[nFrame].dwChunkOffset
2345 	- This->dwMoviChunkPos;
2346       if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2347 	return AVIERR_FILEWRITE;
2348 
2349       for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2350 	pStream = This->ppStreams[nStream];
2351 
2352 	/* heave we reached start of this stream? */
2353 	if (-(LONG)pStream->sInfo.dwInitialFrames > i)
2354 	  continue;
2355 
2356 	if (pStream->sInfo.dwInitialFrames < lInitialFrames)
2357 	  nFrame -= (lInitialFrames - pStream->sInfo.dwInitialFrames);
2358 
2359 	/* reached end of this stream? */
2360 	if (pStream->lLastFrame <= nFrame)
2361 	  continue;
2362 
2363 	if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
2364 	    pStream->sInfo.dwFormatChangeCount != 0 &&
2365 	    pStream->idxFmtChanges != NULL) {
2366 	  DWORD pos;
2367 
2368 	  for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
2369 	    if (pStream->idxFmtChanges[pos].ckid == nFrame) {
2370 	      idx.dwFlags = AVIIF_NOTIME;
2371 	      idx.ckid    = MAKEAVICKID(cktypePALchange, pStream->nStream);
2372 	      idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
2373 	      idx.dwChunkOffset = pStream->idxFmtChanges[pos].dwChunkOffset
2374 		- This->dwMoviChunkPos;
2375 
2376 	      if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2377 		return AVIERR_FILEWRITE;
2378 	      break;
2379 	    }
2380 	  }
2381 	} /* if have formatchanges */
2382 
2383 	idx.ckid          = pStream->idxFrames[nFrame].ckid;
2384 	idx.dwFlags       = pStream->idxFrames[nFrame].dwFlags;
2385 	idx.dwChunkLength = pStream->idxFrames[nFrame].dwChunkLength;
2386 	idx.dwChunkOffset = pStream->idxFrames[nFrame].dwChunkOffset
2387 	  - This->dwMoviChunkPos;
2388 	if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2389 	  return AVIERR_FILEWRITE;
2390       }
2391     }
2392   } else {
2393     /* not interleaved -- write index for each stream at once */
2394     for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2395       pStream = This->ppStreams[nStream];
2396 
2397       for (n = 0; n <= pStream->lLastFrame; n++) {
2398 	if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
2399 	    (pStream->sInfo.dwFormatChangeCount != 0)) {
2400 	  DWORD pos;
2401 
2402 	  for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
2403 	    if (pStream->idxFmtChanges[pos].ckid == n) {
2404 	      idx.dwFlags = AVIIF_NOTIME;
2405 	      idx.ckid    = MAKEAVICKID(cktypePALchange, pStream->nStream);
2406 	      idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
2407 	      idx.dwChunkOffset =
2408 		pStream->idxFmtChanges[pos].dwChunkOffset - This->dwMoviChunkPos;
2409 	      if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2410 		return AVIERR_FILEWRITE;
2411 	      break;
2412 	    }
2413 	  }
2414 	} /* if have formatchanges */
2415 
2416 	idx.ckid          = pStream->idxFrames[n].ckid;
2417 	idx.dwFlags       = pStream->idxFrames[n].dwFlags;
2418 	idx.dwChunkLength = pStream->idxFrames[n].dwChunkLength;
2419 	idx.dwChunkOffset = pStream->idxFrames[n].dwChunkOffset
2420 	  - This->dwMoviChunkPos;
2421 
2422 	if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2423 	  return AVIERR_FILEWRITE;
2424       }
2425     }
2426   } /* if not interleaved */
2427 
2428   if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2429     return AVIERR_FILEWRITE;
2430 
2431   return AVIERR_OK;
2432 }
2433 
2434 static ULONG  AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fcc, LONG lSkip)
2435 {
2436   UINT i;
2437   UINT nStream;
2438 
2439   /* pre-condition */
2440   assert(lSkip >= 0);
2441 
2442   if (fcc != 0) {
2443     /* search the number of the specified stream */
2444     nStream = (ULONG)-1;
2445     for (i = 0; i < This->fInfo.dwStreams; i++) {
2446       assert(This->ppStreams[i] != NULL);
2447 
2448       if (This->ppStreams[i]->sInfo.fccType == fcc) {
2449 	if (lSkip == 0) {
2450 	  nStream = i;
2451 	  break;
2452 	} else
2453 	  lSkip--;
2454       }
2455     }
2456   } else
2457     nStream = lSkip;
2458 
2459   return nStream;
2460 }
2461 
2462 static void    AVIFILE_UpdateInfo(IAVIFileImpl *This)
2463 {
2464   UINT i;
2465 
2466   /* pre-conditions */
2467   assert(This != NULL);
2468 
2469   This->fInfo.dwMaxBytesPerSec      = 0;
2470   This->fInfo.dwCaps                = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
2471   This->fInfo.dwSuggestedBufferSize = 0;
2472   This->fInfo.dwWidth               = 0;
2473   This->fInfo.dwHeight              = 0;
2474   This->fInfo.dwScale               = 0;
2475   This->fInfo.dwRate                = 0;
2476   This->fInfo.dwLength              = 0;
2477   This->dwInitialFrames             = 0;
2478 
2479   for (i = 0; i < This->fInfo.dwStreams; i++) {
2480     AVISTREAMINFOW *psi;
2481     DWORD           n;
2482 
2483     /* pre-conditions */
2484     assert(This->ppStreams[i] != NULL);
2485 
2486     psi = &This->ppStreams[i]->sInfo;
2487     assert(psi->dwScale != 0);
2488     assert(psi->dwRate != 0);
2489 
2490     if (i == 0) {
2491       /* use first stream timings as base */
2492       This->fInfo.dwScale  = psi->dwScale;
2493       This->fInfo.dwRate   = psi->dwRate;
2494       This->fInfo.dwLength = psi->dwLength;
2495     } else {
2496       n = AVIStreamSampleToSample(&This->ppStreams[0]->IAVIStream_iface,
2497                                   &This->ppStreams[i]->IAVIStream_iface, psi->dwLength);
2498       if (This->fInfo.dwLength < n)
2499 	This->fInfo.dwLength = n;
2500     }
2501 
2502     if (This->dwInitialFrames < psi->dwInitialFrames)
2503       This->dwInitialFrames = psi->dwInitialFrames;
2504 
2505     if (This->fInfo.dwSuggestedBufferSize < psi->dwSuggestedBufferSize)
2506       This->fInfo.dwSuggestedBufferSize = psi->dwSuggestedBufferSize;
2507 
2508     if (psi->dwSampleSize != 0) {
2509       /* fixed sample size -- exact computation */
2510       This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSampleSize, psi->dwRate,
2511 					     psi->dwScale);
2512     } else {
2513       /* variable sample size -- only upper limit */
2514       This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSuggestedBufferSize,
2515 					     psi->dwRate, psi->dwScale);
2516 
2517       /* update dimensions */
2518       n = psi->rcFrame.right - psi->rcFrame.left;
2519       if (This->fInfo.dwWidth < n)
2520 	This->fInfo.dwWidth = n;
2521       n = psi->rcFrame.bottom - psi->rcFrame.top;
2522       if (This->fInfo.dwHeight < n)
2523 	This->fInfo.dwHeight = n;
2524     }
2525   }
2526 }
2527 
2528 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
2529 				  FOURCC ckid, DWORD flags, LPCVOID buffer,
2530 				  LONG size)
2531 {
2532   MMCKINFO ck;
2533 
2534   ck.ckid    = ckid;
2535   ck.cksize  = size;
2536   ck.fccType = 0;
2537 
2538   /* if no frame/block is already written, we must compute start of movi chunk */
2539   if (This->paf->dwMoviChunkPos == 0)
2540     AVIFILE_ComputeMoviStart(This->paf);
2541 
2542   if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1)
2543     return AVIERR_FILEWRITE;
2544 
2545   if (mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK)
2546     return AVIERR_FILEWRITE;
2547   if (buffer != NULL && size > 0) {
2548     if (mmioWrite(This->paf->hmmio, buffer, size) != size)
2549       return AVIERR_FILEWRITE;
2550   }
2551   if (mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
2552     return AVIERR_FILEWRITE;
2553 
2554   This->paf->fDirty         = TRUE;
2555   This->paf->dwNextFramePos = mmioSeek(This->paf->hmmio, 0, SEEK_CUR);
2556 
2557   return AVIFILE_AddFrame(This, ckid, size,
2558 			  ck.dwDataOffset - 2 * sizeof(DWORD), flags);
2559 }
2560