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