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