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