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