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