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