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