xref: /reactos/dll/win32/avifil32/editstream.c (revision 44836a6e)
1 /*
2  * Copyright 2003 Michael Günnewig
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include <assert.h>
20 #include <stdarg.h>
21 
22 #include "windef.h"
23 #include "winbase.h"
24 #include "winuser.h"
25 #include "wingdi.h"
26 #include "winerror.h"
27 #include "mmsystem.h"
28 #include "vfw.h"
29 
30 #include "avifile_private.h"
31 #include "extrachunk.h"
32 
33 #include "wine/debug.h"
34 #include "initguid.h"
35 
36 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
37 
38 /***********************************************************************/
39 
40 /* internal interface to get access to table of stream in an editable stream */
41 
42 DEFINE_AVIGUID(IID_IEditStreamInternal, 0x0002000A,0,0);
43 
44 typedef struct _EditStreamTable {
45   PAVISTREAM pStream;  /* stream which contains the data */
46   DWORD      dwStart;  /* where starts the part which is also our */
47   DWORD      dwLength; /* how many is also in this stream */
48 } EditStreamTable;
49 
50 #define EditStreamEnd(This,streamNr) ((This)->pStreams[streamNr].dwStart + \
51                                       (This)->pStreams[streamNr].dwLength)
52 
53 typedef struct _IAVIEditStreamImpl IAVIEditStreamImpl;
54 
55 struct _IAVIEditStreamImpl {
56   IAVIEditStream       IAVIEditStream_iface;
57   IAVIStream           IAVIStream_iface;
58 
59   LONG                 ref;
60 
61   AVISTREAMINFOW       sInfo;
62 
63   EditStreamTable     *pStreams;
64   DWORD                nStreams;   /* current fill level of pStreams table */
65   DWORD                nTableSize; /* size of pStreams table */
66 
67   BOOL                 bDecompress;
68   PAVISTREAM           pCurStream;
69   PGETFRAME            pg;         /* IGetFrame for pCurStream */
70   LPBITMAPINFOHEADER   lpFrame;    /* frame of pCurStream */
71 };
72 
73 static IAVIEditStreamImpl *AVIFILE_CreateEditStream(IAVIStream *stream);
74 
impl_from_IAVIEditStream(IAVIEditStream * iface)75 static inline IAVIEditStreamImpl *impl_from_IAVIEditStream(IAVIEditStream *iface)
76 {
77     return CONTAINING_RECORD(iface, IAVIEditStreamImpl, IAVIEditStream_iface);
78 }
79 
impl_from_IAVIStream(IAVIStream * iface)80 static inline IAVIEditStreamImpl *impl_from_IAVIStream(IAVIStream *iface)
81 {
82     return CONTAINING_RECORD(iface, IAVIEditStreamImpl, IAVIStream_iface);
83 }
84 
85 /***********************************************************************/
86 
AVIFILE_FindStreamInTable(IAVIEditStreamImpl * const This,DWORD pos,PAVISTREAM * ppStream,DWORD * streamPos,DWORD * streamNr,BOOL bFindSample)87 static HRESULT AVIFILE_FindStreamInTable(IAVIEditStreamImpl* const This,
88 					 DWORD pos,PAVISTREAM *ppStream,
89 					 DWORD* streamPos,
90 					 DWORD* streamNr,BOOL bFindSample)
91 {
92   DWORD n;
93 
94   TRACE("(%p,%u,%p,%p,%p,%d)\n",This,pos,ppStream,streamPos,
95         streamNr,bFindSample);
96 
97   if (pos < This->sInfo.dwStart)
98     return AVIERR_BADPARAM;
99 
100   pos -= This->sInfo.dwStart;
101   for (n = 0; n < This->nStreams; n++) {
102     if (pos < This->pStreams[n].dwLength) {
103       *ppStream  = This->pStreams[n].pStream;
104       *streamPos = This->pStreams[n].dwStart + pos;
105       if (streamNr != NULL)
106         *streamNr = n;
107 
108       return AVIERR_OK;
109     }
110     pos -= This->pStreams[n].dwLength;
111   }
112   if (pos == 0 && bFindSample) {
113     *ppStream  = This->pStreams[--n].pStream;
114     *streamPos = EditStreamEnd(This, n);
115     if (streamNr != NULL)
116       *streamNr = n;
117 
118     TRACE(" -- pos=0 && b=1 -> (%p,%u,%u)\n",*ppStream, *streamPos, n);
119     return AVIERR_OK;
120   } else {
121     *ppStream = NULL;
122     *streamPos = 0;
123     if (streamNr != NULL)
124       *streamNr = 0;
125 
126     TRACE(" -> ERROR (NULL,0,0)\n");
127     return AVIERR_BADPARAM;
128   }
129 }
130 
AVIFILE_ReadFrame(IAVIEditStreamImpl * const This,PAVISTREAM pstream,LONG pos)131 static LPVOID AVIFILE_ReadFrame(IAVIEditStreamImpl* const This,
132                                 PAVISTREAM pstream, LONG pos)
133 {
134   PGETFRAME pg;
135 
136   TRACE("(%p,%p,%d)\n",This,pstream,pos);
137 
138   if (pstream == NULL)
139     return NULL;
140 
141   /* if stream changes make sure that only palette changes */
142   if (This->pCurStream != pstream) {
143     pg = AVIStreamGetFrameOpen(pstream, NULL);
144     if (pg == NULL)
145       return NULL;
146     if (This->pg != NULL) {
147       if (IGetFrame_SetFormat(pg, This->lpFrame, NULL, 0, 0, -1, -1) != S_OK) {
148         AVIStreamGetFrameClose(pg);
149         ERR(": IGetFrame_SetFormat failed\n");
150         return NULL;
151       }
152       AVIStreamGetFrameClose(This->pg);
153     }
154     This->pg         = pg;
155     This->pCurStream = pstream;
156   }
157 
158   /* now get the decompressed frame */
159   This->lpFrame = AVIStreamGetFrame(This->pg, pos);
160   if (This->lpFrame != NULL)
161     This->sInfo.dwSuggestedBufferSize = This->lpFrame->biSizeImage;
162 
163   return This->lpFrame;
164 }
165 
AVIFILE_RemoveStream(IAVIEditStreamImpl * const This,DWORD nr)166 static HRESULT AVIFILE_RemoveStream(IAVIEditStreamImpl* const This, DWORD nr)
167 {
168   assert(This != NULL);
169   assert(nr < This->nStreams);
170 
171   /* remove part nr */
172   IAVIStream_Release(This->pStreams[nr].pStream);
173   This->nStreams--;
174   if (nr < This->nStreams)
175     memmove(&This->pStreams[nr], &This->pStreams[nr + 1],
176             (This->nStreams - nr) * sizeof(This->pStreams[0]));
177   This->pStreams[This->nStreams].pStream  = NULL;
178   This->pStreams[This->nStreams].dwStart  = 0;
179   This->pStreams[This->nStreams].dwLength = 0;
180 
181   /* try to merge the part before the deleted one and the one after it */
182   if (0 < nr && 0 < This->nStreams &&
183       This->pStreams[nr - 1].pStream == This->pStreams[nr].pStream) {
184     if (EditStreamEnd(This, nr - 1) == This->pStreams[nr].dwStart) {
185       This->pStreams[nr - 1].dwLength += This->pStreams[nr].dwLength;
186       return AVIFILE_RemoveStream(This, nr);
187     }
188   }
189 
190   return AVIERR_OK;
191 }
192 
AVIFILE_FormatsEqual(PAVISTREAM avi1,PAVISTREAM avi2)193 static BOOL AVIFILE_FormatsEqual(PAVISTREAM avi1, PAVISTREAM avi2)
194 {
195   LPVOID fmt1 = NULL, fmt2 = NULL;
196   LONG size1, size2, start1, start2;
197   BOOL status = FALSE;
198 
199   assert(avi1 != NULL && avi2 != NULL);
200 
201   /* get stream starts and check format sizes */
202   start1 = AVIStreamStart(avi1);
203   start2 = AVIStreamStart(avi2);
204   if (FAILED(AVIStreamFormatSize(avi1, start1, &size1)))
205     return FALSE;
206   if (FAILED(AVIStreamFormatSize(avi2, start2, &size2)))
207     return FALSE;
208   if (size1 != size2)
209     return FALSE;
210 
211   /* sizes match, now get formats and compare them */
212   fmt1 = HeapAlloc(GetProcessHeap(), 0, size1);
213   if (fmt1 == NULL)
214     return FALSE;
215   if (SUCCEEDED(AVIStreamReadFormat(avi1, start1, fmt1, &size1))) {
216     fmt2 = HeapAlloc(GetProcessHeap(), 0, size1);
217     if (fmt2 != NULL) {
218       if (SUCCEEDED(AVIStreamReadFormat(avi2, start2, fmt2, &size1)))
219         status = (memcmp(fmt1, fmt2, size1) == 0);
220     }
221   }
222 
223   HeapFree(GetProcessHeap(), 0, fmt2);
224   HeapFree(GetProcessHeap(), 0, fmt1);
225 
226   return status;
227 }
228 
229 /***********************************************************************/
230 
IAVIEditStream_fnQueryInterface(IAVIEditStream * iface,REFIID refiid,LPVOID * obj)231 static HRESULT WINAPI IAVIEditStream_fnQueryInterface(IAVIEditStream*iface,REFIID refiid,LPVOID *obj)
232 {
233   IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
234 
235   TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj);
236 
237   if (IsEqualGUID(&IID_IUnknown, refiid) ||
238       IsEqualGUID(&IID_IAVIEditStream, refiid) ||
239       IsEqualGUID(&IID_IEditStreamInternal, refiid)) {
240     *obj = iface;
241     IAVIEditStream_AddRef(iface);
242 
243     return S_OK;
244   } else if (IsEqualGUID(&IID_IAVIStream, refiid)) {
245     *obj = &This->IAVIStream_iface;
246     IAVIEditStream_AddRef(iface);
247 
248     return S_OK;
249   }
250 
251   return E_NOINTERFACE;
252 }
253 
IAVIEditStream_fnAddRef(IAVIEditStream * iface)254 static ULONG   WINAPI IAVIEditStream_fnAddRef(IAVIEditStream*iface)
255 {
256   IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
257   ULONG ref = InterlockedIncrement(&This->ref);
258 
259   TRACE("(%p) -> %d\n", iface, ref);
260 
261   return ref;
262 }
263 
IAVIEditStream_fnRelease(IAVIEditStream * iface)264 static ULONG   WINAPI IAVIEditStream_fnRelease(IAVIEditStream*iface)
265 {
266   IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
267   DWORD i;
268   ULONG ref = InterlockedDecrement(&This->ref);
269 
270   TRACE("(%p) -> %d\n", iface, ref);
271 
272   if (!ref) {
273     /* release memory */
274     if (This->pg != NULL)
275       AVIStreamGetFrameClose(This->pg);
276     if (This->pStreams != NULL) {
277       for (i = 0; i < This->nStreams; i++) {
278         if (This->pStreams[i].pStream != NULL)
279           IAVIStream_Release(This->pStreams[i].pStream);
280       }
281       HeapFree(GetProcessHeap(), 0, This->pStreams);
282     }
283 
284     HeapFree(GetProcessHeap(), 0, This);
285   }
286   return ref;
287 }
288 
IAVIEditStream_fnCut(IAVIEditStream * iface,LONG * plStart,LONG * plLength,PAVISTREAM * ppResult)289 static HRESULT WINAPI IAVIEditStream_fnCut(IAVIEditStream*iface,LONG*plStart,
290                                            LONG*plLength,PAVISTREAM*ppResult)
291 {
292   IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
293   PAVISTREAM stream;
294   DWORD      start, len, streamPos, streamNr;
295   HRESULT    hr;
296 
297   TRACE("(%p,%p,%p,%p)\n",iface,plStart,plLength,ppResult);
298 
299   if (ppResult != NULL)
300     *ppResult = NULL;
301   if (plStart == NULL || plLength == NULL || *plStart < 0)
302     return AVIERR_BADPARAM;
303 
304   /* if asked for cut part copy it before deleting */
305   if (ppResult != NULL) {
306     hr = IAVIEditStream_Copy(iface, plStart, plLength, ppResult);
307     if (FAILED(hr))
308       return hr;
309   }
310 
311   start = *plStart;
312   len   = *plLength;
313 
314   /* now delete the requested part */
315   while (len > 0) {
316     hr = AVIFILE_FindStreamInTable(This, start, &stream,
317                                    &streamPos, &streamNr, FALSE);
318     if (FAILED(hr))
319       return hr;
320     if (This->pStreams[streamNr].dwStart == streamPos) {
321       /* deleting from start of part */
322       if (len < This->pStreams[streamNr].dwLength) {
323         start += len;
324         This->pStreams[streamNr].dwStart  += len;
325         This->pStreams[streamNr].dwLength -= len;
326         This->sInfo.dwLength -= len;
327         len = 0;
328 
329         /* we must return decompressed data now */
330         This->bDecompress = TRUE;
331       } else {
332         /* deleting hole part */
333         len -= This->pStreams[streamNr].dwLength;
334         AVIFILE_RemoveStream(This,streamNr);
335       }
336     } else if (EditStreamEnd(This, streamNr) <= streamPos + len) {
337       /* deleting at end of a part */
338       DWORD count = EditStreamEnd(This, streamNr) - streamPos;
339       This->sInfo.dwLength -= count;
340       len                  -= count;
341       This->pStreams[streamNr].dwLength =
342         streamPos - This->pStreams[streamNr].dwStart;
343     } else {
344       /* splitting */
345       if (This->nStreams + 1 >= This->nTableSize) {
346         This->pStreams = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->pStreams,
347                                      (This->nTableSize + 32) * sizeof(EditStreamTable));
348         if (This->pStreams == NULL)
349           return AVIERR_MEMORY;
350         This->nTableSize += 32;
351       }
352       memmove(This->pStreams + streamNr + 1, This->pStreams + streamNr,
353               (This->nStreams - streamNr) * sizeof(EditStreamTable));
354       This->nStreams++;
355 
356       IAVIStream_AddRef(This->pStreams[streamNr + 1].pStream);
357       This->pStreams[streamNr + 1].dwStart  = streamPos + len;
358       This->pStreams[streamNr + 1].dwLength =
359         EditStreamEnd(This, streamNr) - This->pStreams[streamNr + 1].dwStart;
360 
361       This->pStreams[streamNr].dwLength =
362         streamPos - This->pStreams[streamNr].dwStart;
363       This->sInfo.dwLength -= len;
364       len = 0;
365     }
366   }
367 
368   This->sInfo.dwEditCount++;
369 
370   return AVIERR_OK;
371 }
372 
IAVIEditStream_fnCopy(IAVIEditStream * iface,LONG * plStart,LONG * plLength,PAVISTREAM * ppResult)373 static HRESULT WINAPI IAVIEditStream_fnCopy(IAVIEditStream*iface,LONG*plStart,
374                                             LONG*plLength,PAVISTREAM*ppResult)
375 {
376   IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
377   IAVIEditStreamImpl* pEdit;
378   HRESULT hr;
379   LONG start = 0;
380 
381   TRACE("(%p,%p,%p,%p)\n",iface,plStart,plLength,ppResult);
382 
383   if (ppResult == NULL)
384     return AVIERR_BADPARAM;
385   *ppResult = NULL;
386   if (plStart == NULL || plLength == NULL || *plStart < 0 || *plLength < 0)
387     return AVIERR_BADPARAM;
388 
389   /* check bounds */
390   if (*(LPDWORD)plLength > This->sInfo.dwLength)
391     *(LPDWORD)plLength = This->sInfo.dwLength;
392   if (*(LPDWORD)plStart < This->sInfo.dwStart) {
393     *(LPDWORD)plLength -= This->sInfo.dwStart - *(LPDWORD)plStart;
394     *(LPDWORD)plStart   = This->sInfo.dwStart;
395     if (*plLength < 0)
396       return AVIERR_BADPARAM;
397   }
398   if (*(LPDWORD)plStart + *(LPDWORD)plLength > This->sInfo.dwStart + This->sInfo.dwLength)
399     *(LPDWORD)plLength = This->sInfo.dwStart + This->sInfo.dwLength -
400       *(LPDWORD)plStart;
401 
402   pEdit = AVIFILE_CreateEditStream(NULL);
403   if (pEdit == NULL)
404     return AVIERR_MEMORY;
405 
406   hr = IAVIEditStream_Paste(&pEdit->IAVIEditStream_iface, &start, plLength, &This->IAVIStream_iface,
407                             *plStart, *plStart + *plLength);
408   *plStart = start;
409   if (FAILED(hr))
410     IAVIEditStream_Release(&pEdit->IAVIEditStream_iface);
411   else
412     *ppResult = &This->IAVIStream_iface;
413 
414   return hr;
415 }
416 
IAVIEditStream_fnPaste(IAVIEditStream * iface,LONG * plStart,LONG * plLength,PAVISTREAM pSource,LONG lStart,LONG lLength)417 static HRESULT WINAPI IAVIEditStream_fnPaste(IAVIEditStream*iface,LONG*plStart,
418                                              LONG*plLength,PAVISTREAM pSource,
419                                              LONG lStart,LONG lLength)
420 {
421   IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
422   AVISTREAMINFOW      srcInfo;
423   IAVIEditStreamImpl *pEdit = NULL;
424   PAVISTREAM          pStream;
425   DWORD               startPos, endPos, streamNr, nStreams;
426   ULONG               n;
427 
428   TRACE("(%p,%p,%p,%p,%d,%d)\n",iface,plStart,plLength,
429 	pSource,lStart,lLength);
430 
431   if (pSource == NULL)
432     return AVIERR_BADHANDLE;
433   if (plStart == NULL || *plStart < 0)
434     return AVIERR_BADPARAM;
435   if (This->sInfo.dwStart + This->sInfo.dwLength < *plStart)
436     return AVIERR_BADPARAM; /* Can't paste with holes */
437   if (FAILED(IAVIStream_Info(pSource, &srcInfo, sizeof(srcInfo))))
438     return AVIERR_ERROR;
439   if (lStart < srcInfo.dwStart || lStart >= srcInfo.dwStart + srcInfo.dwLength)
440     return AVIERR_BADPARAM;
441   if (This->sInfo.fccType == 0) {
442     /* This stream is empty */
443     IAVIStream_Info(pSource, &This->sInfo, sizeof(This->sInfo));
444     This->sInfo.dwStart  = *plStart;
445     This->sInfo.dwLength = 0;
446   }
447   if (This->sInfo.fccType != srcInfo.fccType)
448     return AVIERR_UNSUPPORTED; /* different stream types */
449   if (lLength == -1) /* Copy the hole stream */
450     lLength = srcInfo.dwLength;
451   if (lStart + lLength > srcInfo.dwStart + srcInfo.dwLength)
452     lLength = srcInfo.dwStart + srcInfo.dwLength - lStart;
453   if (lLength + *plStart >= 0x80000000)
454     return AVIERR_MEMORY;
455 
456   /* streamtype specific tests */
457   if (srcInfo.fccType == streamtypeVIDEO) {
458     LONG size;
459 
460     size = srcInfo.rcFrame.right - srcInfo.rcFrame.left;
461     if (size != This->sInfo.rcFrame.right - This->sInfo.rcFrame.left)
462       return AVIERR_UNSUPPORTED; /* FIXME: Can't GetFrame convert it? */
463     size = srcInfo.rcFrame.bottom - srcInfo.rcFrame.top;
464     if (size != This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top)
465       return AVIERR_UNSUPPORTED; /* FIXME: Can't GetFrame convert it? */
466   } else if (srcInfo.fccType == streamtypeAUDIO) {
467     if (!AVIFILE_FormatsEqual(&This->IAVIStream_iface, pSource))
468       return AVIERR_UNSUPPORTED;
469   } else {
470     /* FIXME: streamtypeMIDI and streamtypeTEXT */
471     return AVIERR_UNSUPPORTED;
472   }
473 
474   /* try to get an IEditStreamInternal interface */
475   if (SUCCEEDED(IAVIStream_QueryInterface(pSource, &IID_IEditStreamInternal, (LPVOID*)&pEdit)))
476       IAVIEditStream_Release(&pEdit->IAVIEditStream_iface);  /* pSource holds a reference */
477 
478   /* for video must check for change of format */
479   if (This->sInfo.fccType == streamtypeVIDEO) {
480     if (! This->bDecompress) {
481       /* Need to decompress if any of the following conditions matches:
482        *  - pSource is an editable stream which decompresses
483        *  - the nearest keyframe of pSource isn't lStart
484        *  - the nearest keyframe of this stream isn't *plStart
485        *  - the format of pSource doesn't match this one
486        */
487       if ((pEdit != NULL && pEdit->bDecompress) ||
488 	  AVIStreamNearestKeyFrame(pSource, lStart) != lStart ||
489           AVIStreamNearestKeyFrame(&This->IAVIStream_iface, *plStart) != *plStart ||
490           (This->nStreams > 0 && !AVIFILE_FormatsEqual(&This->IAVIStream_iface, pSource))) {
491 	/* Use first stream part to get format to convert everything to */
492 	AVIFILE_ReadFrame(This, This->pStreams[0].pStream,
493 			  This->pStreams[0].dwStart);
494 
495 	/* Check if we could convert the source streams to the desired format... */
496 	if (pEdit != NULL) {
497 	  if (FAILED(AVIFILE_FindStreamInTable(pEdit, lStart, &pStream,
498 					       &startPos, &streamNr, TRUE)))
499 	    return AVIERR_INTERNAL;
500 	  for (n = lStart; n < lStart + lLength; streamNr++) {
501 	    if (AVIFILE_ReadFrame(This, pEdit->pStreams[streamNr].pStream, startPos) == NULL)
502 	      return AVIERR_BADFORMAT;
503 	    startPos = pEdit->pStreams[streamNr].dwStart;
504 	    n += pEdit->pStreams[streamNr].dwLength;
505 	  }
506 	} else if (AVIFILE_ReadFrame(This, pSource, lStart) == NULL)
507 	  return AVIERR_BADFORMAT;
508 
509 	This->bDecompress      = TRUE;
510 	This->sInfo.fccHandler = 0;
511       }
512     } else if (AVIFILE_ReadFrame(This, pSource, lStart) == NULL)
513       return AVIERR_BADFORMAT; /* Can't convert source to own format */
514   } /* FIXME: something special for the other formats? */
515 
516   /* Make sure we have enough memory for parts */
517   if (pEdit != NULL) {
518     DWORD nLastStream;
519 
520     AVIFILE_FindStreamInTable(pEdit, lStart + lLength, &pStream,
521 			      &endPos, &nLastStream, TRUE);
522     AVIFILE_FindStreamInTable(pEdit, lStart, &pStream,
523 			      &startPos, &streamNr, FALSE);
524     if (nLastStream == streamNr)
525       nLastStream++;
526 
527     nStreams = nLastStream - streamNr;
528   } else
529     nStreams = 1;
530   if (This->nStreams + nStreams + 1 > This->nTableSize) {
531     n = This->nStreams + nStreams + 33;
532 
533     This->pStreams = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->pStreams, n * sizeof(EditStreamTable));
534     if (This->pStreams == NULL)
535       return AVIERR_MEMORY;
536     This->nTableSize = n;
537   }
538 
539   if (plLength != NULL)
540     *plLength = lLength;
541 
542   /* now do the real work */
543   if (This->sInfo.dwStart + This->sInfo.dwLength > *plStart) {
544     AVIFILE_FindStreamInTable(This, *plStart, &pStream,
545 			      &startPos, &streamNr, FALSE);
546     if (startPos != This->pStreams[streamNr].dwStart) {
547       /* split stream streamNr at startPos */
548       memmove(This->pStreams + streamNr + nStreams + 1,
549 	      This->pStreams + streamNr,
550 	      (This->nStreams + nStreams - streamNr + 1) * sizeof(EditStreamTable));
551 
552       This->pStreams[streamNr + 2].dwLength =
553 	EditStreamEnd(This, streamNr + 2) - startPos;
554       This->pStreams[streamNr + 2].dwStart = startPos;
555       This->pStreams[streamNr].dwLength =
556 	startPos - This->pStreams[streamNr].dwStart;
557       IAVIStream_AddRef(This->pStreams[streamNr].pStream);
558       streamNr++;
559     } else {
560       /* insert before stream at streamNr */
561       memmove(This->pStreams + streamNr + nStreams, This->pStreams + streamNr,
562 	      (This->nStreams + nStreams - streamNr) * sizeof(EditStreamTable));
563     }
564   } else /* append the streams */
565     streamNr = This->nStreams;
566 
567   if (pEdit != NULL) {
568     /* insert the parts of the editable stream instead of itself */
569     AVIFILE_FindStreamInTable(pEdit, lStart + lLength, &pStream,
570 			      &endPos, NULL, FALSE);
571     AVIFILE_FindStreamInTable(pEdit, lStart, &pStream, &startPos, &n, FALSE);
572 
573     memcpy(This->pStreams + streamNr, pEdit->pStreams + n,
574 	   nStreams * sizeof(EditStreamTable));
575     if (This->pStreams[streamNr].dwStart < startPos) {
576       This->pStreams[streamNr].dwLength =
577 	EditStreamEnd(This, streamNr) - startPos;
578       This->pStreams[streamNr].dwStart  = startPos;
579     }
580     if (endPos < EditStreamEnd(This, streamNr + nStreams))
581       This->pStreams[streamNr + nStreams].dwLength =
582 	endPos - This->pStreams[streamNr + nStreams].dwStart;
583   } else {
584     /* a simple stream */
585     This->pStreams[streamNr].pStream  = pSource;
586     This->pStreams[streamNr].dwStart  = lStart;
587     This->pStreams[streamNr].dwLength = lLength;
588   }
589 
590   for (n = 0; n < nStreams; n++) {
591     IAVIStream_AddRef(This->pStreams[streamNr + n].pStream);
592     if (0 < streamNr + n &&
593 	This->pStreams[streamNr + n - 1].pStream != This->pStreams[streamNr + n].pStream) {
594       This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
595       This->sInfo.dwFormatChangeCount++;
596     }
597   }
598   This->sInfo.dwEditCount++;
599   This->sInfo.dwLength += lLength;
600   This->nStreams += nStreams;
601 
602   return AVIERR_OK;
603 }
604 
IAVIEditStream_fnClone(IAVIEditStream * iface,PAVISTREAM * ppResult)605 static HRESULT WINAPI IAVIEditStream_fnClone(IAVIEditStream*iface,
606                                              PAVISTREAM*ppResult)
607 {
608   IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
609   IAVIEditStreamImpl* pEdit;
610   DWORD i;
611 
612   TRACE("(%p,%p)\n",iface,ppResult);
613 
614   if (ppResult == NULL)
615     return AVIERR_BADPARAM;
616   *ppResult = NULL;
617 
618   pEdit = AVIFILE_CreateEditStream(NULL);
619   if (pEdit == NULL)
620     return AVIERR_MEMORY;
621   if (This->nStreams > pEdit->nTableSize) {
622     pEdit->pStreams = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pEdit->pStreams,
623                                   This->nStreams * sizeof(EditStreamTable));
624     if (pEdit->pStreams == NULL)
625       return AVIERR_MEMORY;
626     pEdit->nTableSize = This->nStreams;
627   }
628   pEdit->nStreams = This->nStreams;
629   memcpy(pEdit->pStreams, This->pStreams,
630          This->nStreams * sizeof(EditStreamTable));
631   memcpy(&pEdit->sInfo,&This->sInfo,sizeof(This->sInfo));
632   for (i = 0; i < This->nStreams; i++) {
633     if (pEdit->pStreams[i].pStream != NULL)
634       IAVIStream_AddRef(pEdit->pStreams[i].pStream);
635   }
636 
637   *ppResult = &This->IAVIStream_iface;
638 
639   return AVIERR_OK;
640 }
641 
IAVIEditStream_fnSetInfo(IAVIEditStream * iface,LPAVISTREAMINFOW asi,LONG size)642 static HRESULT WINAPI IAVIEditStream_fnSetInfo(IAVIEditStream*iface,
643                                                LPAVISTREAMINFOW asi,LONG size)
644 {
645   IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
646 
647   TRACE("(%p,%p,%d)\n",iface,asi,size);
648 
649   /* check parameters */
650   if (size >= 0 && size < sizeof(AVISTREAMINFOW))
651     return AVIERR_BADSIZE;
652 
653   This->sInfo.wLanguage = asi->wLanguage;
654   This->sInfo.wPriority = asi->wPriority;
655   This->sInfo.dwStart   = asi->dwStart;
656   This->sInfo.dwRate    = asi->dwRate;
657   This->sInfo.dwScale   = asi->dwScale;
658   This->sInfo.dwQuality = asi->dwQuality;
659   This->sInfo.rcFrame   = asi->rcFrame;
660   memcpy(This->sInfo.szName, asi->szName, sizeof(asi->szName));
661   This->sInfo.dwEditCount++;
662 
663   return AVIERR_OK;
664 }
665 
666 static const struct IAVIEditStreamVtbl ieditstream = {
667   IAVIEditStream_fnQueryInterface,
668   IAVIEditStream_fnAddRef,
669   IAVIEditStream_fnRelease,
670   IAVIEditStream_fnCut,
671   IAVIEditStream_fnCopy,
672   IAVIEditStream_fnPaste,
673   IAVIEditStream_fnClone,
674   IAVIEditStream_fnSetInfo
675 };
676 
IEditAVIStream_fnQueryInterface(IAVIStream * iface,REFIID refiid,LPVOID * obj)677 static HRESULT WINAPI IEditAVIStream_fnQueryInterface(IAVIStream*iface,
678                                                       REFIID refiid,LPVOID*obj)
679 {
680   IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
681   return IAVIEditStream_QueryInterface(&This->IAVIEditStream_iface,refiid,obj);
682 }
683 
IEditAVIStream_fnAddRef(IAVIStream * iface)684 static ULONG   WINAPI IEditAVIStream_fnAddRef(IAVIStream*iface)
685 {
686   IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
687   return IAVIEditStream_AddRef(&This->IAVIEditStream_iface);
688 }
689 
IEditAVIStream_fnRelease(IAVIStream * iface)690 static ULONG   WINAPI IEditAVIStream_fnRelease(IAVIStream*iface)
691 {
692   IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
693   return IAVIEditStream_Release(&This->IAVIEditStream_iface);
694 }
695 
IEditAVIStream_fnCreate(IAVIStream * iface,LPARAM lParam1,LPARAM lParam2)696 static HRESULT WINAPI IEditAVIStream_fnCreate(IAVIStream*iface,
697                                               LPARAM lParam1,LPARAM lParam2)
698 {
699   IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
700 
701   if (lParam2 != 0)
702     return AVIERR_ERROR;
703 
704   if (This->pStreams == NULL) {
705     This->pStreams = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 256 * sizeof(EditStreamTable));
706     if (This->pStreams == NULL)
707       return AVIERR_MEMORY;
708     This->nTableSize = 256;
709   }
710 
711   if (lParam1 != 0) {
712     IAVIStream_Info((PAVISTREAM)lParam1, &This->sInfo, sizeof(This->sInfo));
713     IAVIStream_AddRef((PAVISTREAM)lParam1);
714     This->pStreams[0].pStream  = (PAVISTREAM)lParam1;
715     This->pStreams[0].dwStart  = This->sInfo.dwStart;
716     This->pStreams[0].dwLength = This->sInfo.dwLength;
717     This->nStreams = 1;
718   }
719   return AVIERR_OK;
720 }
721 
IEditAVIStream_fnInfo(IAVIStream * iface,AVISTREAMINFOW * psi,LONG size)722 static HRESULT WINAPI IEditAVIStream_fnInfo(IAVIStream*iface,
723                                             AVISTREAMINFOW *psi,LONG size)
724 {
725   IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
726 
727   TRACE("(%p,%p,%d)\n",iface,psi,size);
728 
729   if (psi == NULL)
730     return AVIERR_BADPARAM;
731   if (size < 0)
732     return AVIERR_BADSIZE;
733 
734   if (This->bDecompress)
735     This->sInfo.fccHandler = 0;
736 
737   memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo)));
738 
739   if ((DWORD)size < sizeof(This->sInfo))
740     return AVIERR_BUFFERTOOSMALL;
741   return AVIERR_OK;
742 }
743 
IEditAVIStream_fnFindSample(IAVIStream * iface,LONG pos,LONG flags)744 static LONG    WINAPI IEditAVIStream_fnFindSample(IAVIStream*iface,LONG pos,
745                                                   LONG flags)
746 {
747   IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
748   PAVISTREAM stream;
749   DWORD      streamPos, streamNr;
750 
751   TRACE("(%p,%d,0x%08X)\n",iface,pos,flags);
752 
753   if (flags & FIND_FROM_START)
754     pos = (LONG)This->sInfo.dwStart;
755 
756   /* outside of stream? */
757   if (pos < (LONG)This->sInfo.dwStart ||
758       (LONG)This->sInfo.dwStart + (LONG)This->sInfo.dwLength <= pos)
759     return -1;
760 
761   /* map our position to a stream and position in it */
762   if (AVIFILE_FindStreamInTable(This, pos, &stream, &streamPos,
763                                 &streamNr, TRUE) != S_OK)
764     return -1; /* doesn't exist */
765 
766   if (This->bDecompress) {
767     /* only one stream -- format changes only at start */
768     if (flags & FIND_FORMAT)
769       return (flags & FIND_NEXT ? -1 : 0);
770 
771     /* FIXME: map positions back to us */
772     return IAVIStream_FindSample(stream, streamPos, flags);
773   } else {
774     /* assume change of format every frame */
775     return pos;
776   }
777 }
778 
IEditAVIStream_fnReadFormat(IAVIStream * iface,LONG pos,LPVOID format,LONG * fmtsize)779 static HRESULT WINAPI IEditAVIStream_fnReadFormat(IAVIStream*iface,LONG pos,
780                                                   LPVOID format,LONG*fmtsize)
781 {
782   IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
783   LPBITMAPINFOHEADER  lp;
784   PAVISTREAM          stream;
785   DWORD               n;
786   HRESULT             hr;
787 
788   TRACE("(%p,%d,%p,%p)\n",iface,pos,format,fmtsize);
789 
790   if (fmtsize == NULL || pos < This->sInfo.dwStart ||
791       This->sInfo.dwStart + This->sInfo.dwLength <= pos)
792     return AVIERR_BADPARAM;
793 
794   /* find stream corresponding to position */
795   hr = AVIFILE_FindStreamInTable(This, pos, &stream, &n, NULL, FALSE);
796   if (FAILED(hr))
797     return hr;
798 
799   if (! This->bDecompress)
800     return IAVIStream_ReadFormat(stream, n, format, fmtsize);
801 
802   lp = AVIFILE_ReadFrame(This, stream, n);
803   if (lp == NULL)
804     return AVIERR_ERROR;
805   if (lp->biBitCount <= 8) {
806     n  = (lp->biClrUsed > 0 ? lp->biClrUsed : 1 << lp->biBitCount);
807     n *= sizeof(RGBQUAD);
808   } else
809     n = 0;
810   n += lp->biSize;
811 
812   memcpy(format, lp, min((LONG)n, *fmtsize));
813   hr = ((LONG)n > *fmtsize ? AVIERR_BUFFERTOOSMALL : AVIERR_OK);
814   *fmtsize = n;
815 
816   return hr;
817 }
818 
IEditAVIStream_fnSetFormat(IAVIStream * iface,LONG pos,LPVOID format,LONG formatsize)819 static HRESULT WINAPI IEditAVIStream_fnSetFormat(IAVIStream*iface,LONG pos,
820                                                  LPVOID format,LONG formatsize)
821 {
822   TRACE("(%p,%d,%p,%d)\n",iface,pos,format,formatsize);
823 
824   return AVIERR_UNSUPPORTED;
825 }
826 
IEditAVIStream_fnRead(IAVIStream * iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG * bytesread,LONG * samplesread)827 static HRESULT WINAPI IEditAVIStream_fnRead(IAVIStream*iface,LONG start,
828                                             LONG samples,LPVOID buffer,
829                                             LONG buffersize,LONG*bytesread,
830                                             LONG*samplesread)
831 {
832   IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
833   PAVISTREAM stream;
834   DWORD   streamPos, streamNr;
835   LONG    readBytes, readSamples, count;
836   HRESULT hr;
837 
838   TRACE("(%p,%d,%d,%p,%d,%p,%p) -- 0x%08X\n",iface,start,samples,
839         buffer,buffersize,bytesread,samplesread,This->sInfo.fccType);
840 
841   /* check parameters */
842   if (bytesread != NULL)
843     *bytesread = 0;
844   if (samplesread != NULL)
845     *samplesread = 0;
846   if (buffersize < 0)
847     return AVIERR_BADSIZE;
848   if ((DWORD)start < This->sInfo.dwStart ||
849       This->sInfo.dwStart + This->sInfo.dwLength < (DWORD)start)
850     return AVIERR_BADPARAM;
851 
852   if (! This->bDecompress) {
853     /* audio like data -- sample-based */
854     do {
855       if (samples == 0)
856         return AVIERR_OK; /* nothing at all or already done */
857 
858       if (FAILED(AVIFILE_FindStreamInTable(This, start, &stream,
859                                            &streamPos, &streamNr, FALSE)))
860         return AVIERR_ERROR;
861 
862       /* limit to end of the stream */
863       count = samples;
864       if (streamPos + count > EditStreamEnd(This, streamNr))
865         count = EditStreamEnd(This, streamNr) - streamPos;
866 
867       hr = IAVIStream_Read(stream, streamPos, count, buffer, buffersize,
868                            &readBytes, &readSamples);
869       if (FAILED(hr))
870         return hr;
871       if (readBytes == 0 && readSamples == 0 && count != 0)
872         return AVIERR_FILEREAD; /* for bad stream implementations */
873 
874       if (samplesread != NULL)
875         *samplesread += readSamples;
876       if (bytesread != NULL)
877         *bytesread += readBytes;
878       if (buffer != NULL) {
879         buffer = ((LPBYTE)buffer)+readBytes;
880         buffersize     -= readBytes;
881       }
882       start   += count;
883       samples -= count;
884     } while (This->sInfo.dwStart + This->sInfo.dwLength > start);
885   } else {
886     /* video like data -- frame-based */
887     LPBITMAPINFOHEADER lp;
888 
889     if (samples == 0)
890       return AVIERR_OK;
891 
892     if (FAILED(AVIFILE_FindStreamInTable(This, start, &stream,
893                                          &streamPos, &streamNr, FALSE)))
894       return AVIERR_ERROR;
895 
896     lp = AVIFILE_ReadFrame(This, stream, streamPos);
897     if (lp == NULL)
898       return AVIERR_ERROR;
899 
900     if (buffer != NULL) {
901       /* need size of format to skip */
902       if (lp->biBitCount <= 8) {
903         count  = lp->biClrUsed > 0 ? lp->biClrUsed : 1 << lp->biBitCount;
904         count *= sizeof(RGBQUAD);
905       } else
906         count = 0;
907       count += lp->biSize;
908 
909       if (buffersize < lp->biSizeImage)
910         return AVIERR_BUFFERTOOSMALL;
911       memcpy(buffer, (LPBYTE)lp + count, lp->biSizeImage);
912     }
913 
914     if (bytesread != NULL)
915       *bytesread = lp->biSizeImage;
916     if (samplesread != NULL)
917       *samplesread = 1;
918   }
919 
920   return AVIERR_OK;
921 }
922 
IEditAVIStream_fnWrite(IAVIStream * iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG * sampwritten,LONG * byteswritten)923 static HRESULT WINAPI IEditAVIStream_fnWrite(IAVIStream*iface,LONG start,
924                                              LONG samples,LPVOID buffer,
925                                              LONG buffersize,DWORD flags,
926                                              LONG*sampwritten,LONG*byteswritten)
927 {
928   TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n",iface,start,samples,buffer,
929         buffersize,flags,sampwritten,byteswritten);
930 
931   /* be sure return parameters have correct values */
932   if (sampwritten != NULL)
933     *sampwritten = 0;
934   if (byteswritten != NULL)
935     *byteswritten = 0;
936 
937   return AVIERR_UNSUPPORTED;
938 }
939 
IEditAVIStream_fnDelete(IAVIStream * iface,LONG start,LONG samples)940 static HRESULT WINAPI IEditAVIStream_fnDelete(IAVIStream*iface,LONG start,
941                                               LONG samples)
942 {
943   IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
944 
945   TRACE("(%p,%d,%d)\n",iface,start,samples);
946 
947   return IAVIEditStream_Cut(&This->IAVIEditStream_iface,&start,&samples,NULL);
948 }
949 
IEditAVIStream_fnReadData(IAVIStream * iface,DWORD fcc,LPVOID lp,LONG * lpread)950 static HRESULT WINAPI IEditAVIStream_fnReadData(IAVIStream*iface,DWORD fcc,
951                                                 LPVOID lp,LONG *lpread)
952 {
953   IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
954   DWORD n;
955 
956   TRACE("(%p,0x%08X,%p,%p)\n",iface,fcc,lp,lpread);
957 
958   /* check parameters */
959   if (lp == NULL || lpread == NULL)
960     return AVIERR_BADPARAM;
961 
962   /* simply ask every stream and return the first block found */
963   for (n = 0; n < This->nStreams; n++) {
964     HRESULT hr = IAVIStream_ReadData(This->pStreams[n].pStream,fcc,lp,lpread);
965 
966     if (SUCCEEDED(hr))
967       return hr;
968   }
969 
970   *lpread = 0;
971   return AVIERR_NODATA;
972 }
973 
IEditAVIStream_fnWriteData(IAVIStream * iface,DWORD fcc,LPVOID lp,LONG size)974 static HRESULT WINAPI IEditAVIStream_fnWriteData(IAVIStream*iface,DWORD fcc,
975                                                  LPVOID lp,LONG size)
976 {
977   TRACE("(%p,0x%08X,%p,%d)\n",iface,fcc,lp,size);
978 
979   return AVIERR_UNSUPPORTED;
980 }
981 
IEditAVIStream_fnSetInfo(IAVIStream * iface,AVISTREAMINFOW * info,LONG len)982 static HRESULT WINAPI IEditAVIStream_fnSetInfo(IAVIStream*iface,
983                                                AVISTREAMINFOW*info,LONG len)
984 {
985   IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
986 
987   TRACE("(%p,%p,%d)\n",iface,info,len);
988 
989   return IAVIEditStream_SetInfo(&This->IAVIEditStream_iface,info,len);
990 }
991 
992 static const struct IAVIStreamVtbl ieditstast = {
993   IEditAVIStream_fnQueryInterface,
994   IEditAVIStream_fnAddRef,
995   IEditAVIStream_fnRelease,
996   IEditAVIStream_fnCreate,
997   IEditAVIStream_fnInfo,
998   IEditAVIStream_fnFindSample,
999   IEditAVIStream_fnReadFormat,
1000   IEditAVIStream_fnSetFormat,
1001   IEditAVIStream_fnRead,
1002   IEditAVIStream_fnWrite,
1003   IEditAVIStream_fnDelete,
1004   IEditAVIStream_fnReadData,
1005   IEditAVIStream_fnWriteData,
1006   IEditAVIStream_fnSetInfo
1007 };
1008 
AVIFILE_CreateEditStream(IAVIStream * pstream)1009 static IAVIEditStreamImpl *AVIFILE_CreateEditStream(IAVIStream *pstream)
1010 {
1011   IAVIEditStreamImpl *pedit = NULL;
1012 
1013   pedit = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIEditStreamImpl));
1014   if (pedit == NULL)
1015     return NULL;
1016 
1017   pedit->IAVIEditStream_iface.lpVtbl = &ieditstream;
1018   pedit->IAVIStream_iface.lpVtbl = &ieditstast;
1019   pedit->ref = 1;
1020 
1021   IAVIStream_Create(&pedit->IAVIStream_iface, (LPARAM)pstream, 0);
1022 
1023   return pedit;
1024 }
1025 
1026 /***********************************************************************
1027  *             CreateEditableStream     (AVIFIL32.@)
1028  */
CreateEditableStream(IAVIStream ** editable,IAVIStream * src)1029 HRESULT WINAPI CreateEditableStream(IAVIStream **editable, IAVIStream *src)
1030 {
1031     IAVIEditStream *edit = NULL;
1032     IAVIEditStreamImpl *editobj;
1033     HRESULT hr;
1034 
1035     TRACE("(%p,%p)\n", editable, src);
1036 
1037     if (!editable)
1038         return AVIERR_BADPARAM;
1039     *editable = NULL;
1040 
1041     if (src) {
1042         hr = IAVIStream_QueryInterface(src, &IID_IAVIEditStream, (void**)&edit);
1043         if (SUCCEEDED(hr) && edit) {
1044             hr = IAVIEditStream_Clone(edit, editable);
1045             IAVIEditStream_Release(edit);
1046 
1047             return hr;
1048         }
1049     }
1050 
1051     /* Need own implementation of IAVIEditStream */
1052     editobj = AVIFILE_CreateEditStream(src);
1053     if (!editobj)
1054         return AVIERR_MEMORY;
1055     *editable = &editobj->IAVIStream_iface;
1056 
1057     return S_OK;
1058 }
1059