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