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