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