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