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