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