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