avifil32: Registry access calls don't return HRESULT codes.
[wine] / dlls / avifil32 / api.c
1 /*
2  * Copyright 1999 Marcus Meissner
3  * Copyright 2002-2003 Michael Günnewig
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19
20 #include <stdarg.h>
21
22 #define COBJMACROS
23
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winnls.h"
27 #include "wingdi.h"
28 #include "winuser.h"
29 #include "winreg.h"
30 #include "winerror.h"
31
32 #include "ole2.h"
33 #include "shellapi.h"
34 #include "shlobj.h"
35 #include "vfw.h"
36 #include "msacm.h"
37
38 #include "avifile_private.h"
39
40 #include "wine/debug.h"
41 #include "wine/unicode.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
44
45
46 /***********************************************************************
47  * for AVIBuildFilterW -- uses fixed size table
48  */
49 #define MAX_FILTERS 30 /* 30 => 7kB */
50
51 typedef struct _AVIFilter {
52   WCHAR szClsid[40];
53   WCHAR szExtensions[MAX_FILTERS * 7];
54 } AVIFilter;
55
56 /***********************************************************************
57  * for AVISaveOptions
58  */
59 static struct {
60   UINT                  uFlags;
61   INT                   nStreams;
62   PAVISTREAM           *ppavis;
63   LPAVICOMPRESSOPTIONS *ppOptions;
64   INT                   nCurrent;
65 } SaveOpts;
66
67 /***********************************************************************
68  * copied from dlls/ole32/compobj.c
69  */
70 static HRESULT AVIFILE_CLSIDFromString(LPCSTR idstr, LPCLSID id)
71 {
72   BYTE const *s;
73   BYTE *p;
74   INT   i;
75   BYTE table[256];
76
77   if (!idstr) {
78     memset(id, 0, sizeof(CLSID));
79     return S_OK;
80   }
81
82   /* validate the CLSID string */
83   if (lstrlenA(idstr) != 38)
84     return CO_E_CLASSSTRING;
85
86   s = (BYTE const*)idstr;
87   if ((s[0]!='{') || (s[9]!='-') || (s[14]!='-') || (s[19]!='-') ||
88       (s[24]!='-') || (s[37]!='}'))
89     return CO_E_CLASSSTRING;
90
91   for (i = 1; i < 37; i++) {
92     if ((i == 9) || (i == 14) || (i == 19) || (i == 24))
93       continue;
94     if (!(((s[i] >= '0') && (s[i] <= '9'))  ||
95         ((s[i] >= 'a') && (s[i] <= 'f'))  ||
96         ((s[i] >= 'A') && (s[i] <= 'F')))
97        )
98       return CO_E_CLASSSTRING;
99   }
100
101   TRACE("%s -> %p\n", s, id);
102
103   /* quick lookup table */
104   memset(table, 0, 256);
105
106   for (i = 0; i < 10; i++)
107     table['0' + i] = i;
108
109   for (i = 0; i < 6; i++) {
110     table['A' + i] = i+10;
111     table['a' + i] = i+10;
112   }
113
114   /* in form {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} */
115   p = (BYTE *) id;
116
117   s++;  /* skip leading brace  */
118   for (i = 0; i < 4; i++) {
119     p[3 - i] = table[*s]<<4 | table[*(s+1)];
120     s += 2;
121   }
122   p += 4;
123   s++;  /* skip - */
124
125   for (i = 0; i < 2; i++) {
126     p[1-i] = table[*s]<<4 | table[*(s+1)];
127     s += 2;
128   }
129   p += 2;
130   s++;  /* skip - */
131
132   for (i = 0; i < 2; i++) {
133     p[1-i] = table[*s]<<4 | table[*(s+1)];
134     s += 2;
135   }
136   p += 2;
137   s++;  /* skip - */
138
139   /* these are just sequential bytes */
140   for (i = 0; i < 2; i++) {
141     *p++ = table[*s]<<4 | table[*(s+1)];
142     s += 2;
143   }
144   s++;  /* skip - */
145
146   for (i = 0; i < 6; i++) {
147     *p++ = table[*s]<<4 | table[*(s+1)];
148     s += 2;
149   }
150
151   return S_OK;
152 }
153
154 static BOOL AVIFILE_GetFileHandlerByExtension(LPCWSTR szFile, LPCLSID lpclsid)
155 {
156   CHAR   szRegKey[25];
157   CHAR   szValue[100];
158   LPWSTR szExt = strrchrW(szFile, '.');
159   LONG   len = sizeof(szValue) / sizeof(szValue[0]);
160
161   if (szExt == NULL)
162     return FALSE;
163
164   szExt++;
165
166   wsprintfA(szRegKey, "AVIFile\\Extensions\\%.3ls", szExt);
167   if (RegQueryValueA(HKEY_CLASSES_ROOT, szRegKey, szValue, &len) != ERROR_SUCCESS)
168     return FALSE;
169
170   return (AVIFILE_CLSIDFromString(szValue, lpclsid) == S_OK);
171 }
172
173 /***********************************************************************
174  *              AVIFileInit             (AVIFIL32.@)
175  */
176 void WINAPI AVIFileInit(void) {
177   OleInitialize(NULL);
178 }
179
180 /***********************************************************************
181  *              AVIFileExit             (AVIFIL32.@)
182  */
183 void WINAPI AVIFileExit(void) {
184   /* need to free ole32.dll if we are the last exit call */
185   /* OleUninitialize() */
186   FIXME("(): stub!\n");
187 }
188
189 /***********************************************************************
190  *              AVIFileOpen             (AVIFIL32.@)
191  *              AVIFileOpenA            (AVIFIL32.@)
192  */
193 HRESULT WINAPI AVIFileOpenA(PAVIFILE *ppfile, LPCSTR szFile, UINT uMode,
194                             LPCLSID lpHandler)
195 {
196   LPWSTR  wszFile = NULL;
197   HRESULT hr;
198   int     len;
199
200   TRACE("(%p,%s,0x%08X,%s)\n", ppfile, debugstr_a(szFile), uMode,
201         debugstr_guid(lpHandler));
202
203   /* check parameters */
204   if (ppfile == NULL || szFile == NULL)
205     return AVIERR_BADPARAM;
206
207   /* convert ASCII string to Unicode and call unicode function */
208   len = MultiByteToWideChar(CP_ACP, 0, szFile, -1, NULL, 0);
209   if (len <= 0)
210     return AVIERR_BADPARAM;
211
212   wszFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
213   if (wszFile == NULL)
214     return AVIERR_MEMORY;
215
216   MultiByteToWideChar(CP_ACP, 0, szFile, -1, wszFile, len);
217
218   hr = AVIFileOpenW(ppfile, wszFile, uMode, lpHandler);
219
220   HeapFree(GetProcessHeap(), 0, wszFile);
221
222   return hr;
223 }
224
225 /***********************************************************************
226  *              AVIFileOpenW            (AVIFIL32.@)
227  */
228 HRESULT WINAPI AVIFileOpenW(PAVIFILE *ppfile, LPCWSTR szFile, UINT uMode,
229                             LPCLSID lpHandler)
230 {
231   IPersistFile *ppersist = NULL;
232   CLSID         clsidHandler;
233   HRESULT       hr;
234
235   TRACE("(%p,%s,0x%X,%s)\n", ppfile, debugstr_w(szFile), uMode,
236         debugstr_guid(lpHandler));
237
238   /* check parameters */
239   if (ppfile == NULL || szFile == NULL)
240     return AVIERR_BADPARAM;
241
242   *ppfile = NULL;
243
244   /* if no handler then try guessing it by extension */
245   if (lpHandler == NULL) {
246     if (! AVIFILE_GetFileHandlerByExtension(szFile, &clsidHandler))
247       clsidHandler = CLSID_AVIFile;
248   } else
249     clsidHandler = *lpHandler;
250
251   /* create instance of handler */
252   hr = CoCreateInstance(&clsidHandler, NULL, CLSCTX_INPROC, &IID_IAVIFile, (LPVOID*)ppfile);
253   if (FAILED(hr) || *ppfile == NULL)
254     return hr;
255
256   /* ask for IPersistFile interface for loading/creating the file */
257   hr = IAVIFile_QueryInterface(*ppfile, &IID_IPersistFile, (LPVOID*)&ppersist);
258   if (FAILED(hr) || ppersist == NULL) {
259     IAVIFile_Release(*ppfile);
260     *ppfile = NULL;
261     return hr;
262   }
263
264   hr = IPersistFile_Load(ppersist, szFile, uMode);
265   IPersistFile_Release(ppersist);
266   if (FAILED(hr)) {
267     IAVIFile_Release(*ppfile);
268     *ppfile = NULL;
269   }
270
271   return hr;
272 }
273
274 /***********************************************************************
275  *              AVIFileAddRef           (AVIFIL32.@)
276  */
277 ULONG WINAPI AVIFileAddRef(PAVIFILE pfile)
278 {
279   TRACE("(%p)\n", pfile);
280
281   if (pfile == NULL) {
282     ERR(": bad handle passed!\n");
283     return 0;
284   }
285
286   return IAVIFile_AddRef(pfile);
287 }
288
289 /***********************************************************************
290  *              AVIFileRelease          (AVIFIL32.@)
291  */
292 ULONG WINAPI AVIFileRelease(PAVIFILE pfile)
293 {
294   TRACE("(%p)\n", pfile);
295
296   if (pfile == NULL) {
297     ERR(": bad handle passed!\n");
298     return 0;
299   }
300
301   return IAVIFile_Release(pfile);
302 }
303
304 /***********************************************************************
305  *              AVIFileInfo             (AVIFIL32.@)
306  *              AVIFileInfoA            (AVIFIL32.@)
307  */
308 HRESULT WINAPI AVIFileInfoA(PAVIFILE pfile, LPAVIFILEINFOA afi, LONG size)
309 {
310   AVIFILEINFOW afiw;
311   HRESULT      hres;
312
313   TRACE("(%p,%p,%d)\n", pfile, afi, size);
314
315   if (pfile == NULL)
316     return AVIERR_BADHANDLE;
317   if ((DWORD)size < sizeof(AVIFILEINFOA))
318     return AVIERR_BADSIZE;
319
320   hres = IAVIFile_Info(pfile, &afiw, sizeof(afiw));
321
322   memcpy(afi, &afiw, sizeof(*afi) - sizeof(afi->szFileType));
323   WideCharToMultiByte(CP_ACP, 0, afiw.szFileType, -1, afi->szFileType,
324                       sizeof(afi->szFileType), NULL, NULL);
325   afi->szFileType[sizeof(afi->szFileType) - 1] = 0;
326
327   return hres;
328 }
329
330 /***********************************************************************
331  *              AVIFileInfoW            (AVIFIL32.@)
332  */
333 HRESULT WINAPI AVIFileInfoW(PAVIFILE pfile, LPAVIFILEINFOW afiw, LONG size)
334 {
335   TRACE("(%p,%p,%d)\n", pfile, afiw, size);
336
337   if (pfile == NULL)
338     return AVIERR_BADHANDLE;
339
340   return IAVIFile_Info(pfile, afiw, size);
341 }
342
343 /***********************************************************************
344  *              AVIFileGetStream        (AVIFIL32.@)
345  */
346 HRESULT WINAPI AVIFileGetStream(PAVIFILE pfile, PAVISTREAM *avis,
347                                 DWORD fccType, LONG lParam)
348 {
349   TRACE("(%p,%p,'%4.4s',%d)\n", pfile, avis, (char*)&fccType, lParam);
350
351   if (pfile == NULL)
352     return AVIERR_BADHANDLE;
353
354   return IAVIFile_GetStream(pfile, avis, fccType, lParam);
355 }
356
357 /***********************************************************************
358  *              AVIFileCreateStream     (AVIFIL32.@)
359  *              AVIFileCreateStreamA    (AVIFIL32.@)
360  */
361 HRESULT WINAPI AVIFileCreateStreamA(PAVIFILE pfile, PAVISTREAM *ppavi,
362                                     LPAVISTREAMINFOA psi)
363 {
364   AVISTREAMINFOW        psiw;
365
366   TRACE("(%p,%p,%p)\n", pfile, ppavi, psi);
367
368   if (pfile == NULL)
369     return AVIERR_BADHANDLE;
370
371   /* Only the szName at the end is different */
372   memcpy(&psiw, psi, sizeof(*psi) - sizeof(psi->szName));
373   MultiByteToWideChar(CP_ACP, 0, psi->szName, -1, psiw.szName,
374                       sizeof(psiw.szName) / sizeof(psiw.szName[0]));
375
376   return IAVIFile_CreateStream(pfile, ppavi, &psiw);
377 }
378
379 /***********************************************************************
380  *              AVIFileCreateStreamW    (AVIFIL32.@)
381  */
382 HRESULT WINAPI AVIFileCreateStreamW(PAVIFILE pfile, PAVISTREAM *avis,
383                                     LPAVISTREAMINFOW asi)
384 {
385   TRACE("(%p,%p,%p)\n", pfile, avis, asi);
386
387   if (pfile == NULL)
388     return AVIERR_BADHANDLE;
389
390   return IAVIFile_CreateStream(pfile, avis, asi);
391 }
392
393 /***********************************************************************
394  *              AVIFileWriteData        (AVIFIL32.@)
395  */
396 HRESULT WINAPI AVIFileWriteData(PAVIFILE pfile,DWORD fcc,LPVOID lp,LONG size)
397 {
398   TRACE("(%p,'%4.4s',%p,%d)\n", pfile, (char*)&fcc, lp, size);
399
400   if (pfile == NULL)
401     return AVIERR_BADHANDLE;
402
403   return IAVIFile_WriteData(pfile, fcc, lp, size);
404 }
405
406 /***********************************************************************
407  *              AVIFileReadData         (AVIFIL32.@)
408  */
409 HRESULT WINAPI AVIFileReadData(PAVIFILE pfile,DWORD fcc,LPVOID lp,LPLONG size)
410 {
411   TRACE("(%p,'%4.4s',%p,%p)\n", pfile, (char*)&fcc, lp, size);
412
413   if (pfile == NULL)
414     return AVIERR_BADHANDLE;
415
416   return IAVIFile_ReadData(pfile, fcc, lp, size);
417 }
418
419 /***********************************************************************
420  *              AVIFileEndRecord        (AVIFIL32.@)
421  */
422 HRESULT WINAPI AVIFileEndRecord(PAVIFILE pfile)
423 {
424   TRACE("(%p)\n", pfile);
425
426   if (pfile == NULL)
427     return AVIERR_BADHANDLE;
428
429   return IAVIFile_EndRecord(pfile);
430 }
431
432 /***********************************************************************
433  *              AVIStreamAddRef         (AVIFIL32.@)
434  */
435 ULONG WINAPI AVIStreamAddRef(PAVISTREAM pstream)
436 {
437   TRACE("(%p)\n", pstream);
438
439   if (pstream == NULL) {
440     ERR(": bad handle passed!\n");
441     return 0;
442   }
443
444   return IAVIStream_AddRef(pstream);
445 }
446
447 /***********************************************************************
448  *              AVIStreamRelease        (AVIFIL32.@)
449  */
450 ULONG WINAPI AVIStreamRelease(PAVISTREAM pstream)
451 {
452   TRACE("(%p)\n", pstream);
453
454   if (pstream == NULL) {
455     ERR(": bad handle passed!\n");
456     return 0;
457   }
458
459   return IAVIStream_Release(pstream);
460 }
461
462 /***********************************************************************
463  *              AVIStreamCreate         (AVIFIL32.@)
464  */
465 HRESULT WINAPI AVIStreamCreate(PAVISTREAM *ppavi, LONG lParam1, LONG lParam2,
466                                LPCLSID pclsidHandler)
467 {
468   HRESULT hr;
469
470   TRACE("(%p,0x%08X,0x%08X,%s)\n", ppavi, lParam1, lParam2,
471         debugstr_guid(pclsidHandler));
472
473   if (ppavi == NULL)
474     return AVIERR_BADPARAM;
475
476   *ppavi = NULL;
477   if (pclsidHandler == NULL)
478     return AVIERR_UNSUPPORTED;
479
480   hr = CoCreateInstance(pclsidHandler, NULL, CLSCTX_INPROC, &IID_IAVIStream, (LPVOID*)ppavi);
481   if (FAILED(hr) || *ppavi == NULL)
482     return hr;
483
484   hr = IAVIStream_Create(*ppavi, lParam1, lParam2);
485   if (FAILED(hr)) {
486     IAVIStream_Release(*ppavi);
487     *ppavi = NULL;
488   }
489
490   return hr;
491 }
492
493 /***********************************************************************
494  *              AVIStreamInfo           (AVIFIL32.@)
495  *              AVIStreamInfoA          (AVIFIL32.@)
496  */
497 HRESULT WINAPI AVIStreamInfoA(PAVISTREAM pstream, LPAVISTREAMINFOA asi,
498                               LONG size)
499 {
500   AVISTREAMINFOW asiw;
501   HRESULT        hres;
502
503   TRACE("(%p,%p,%d)\n", pstream, asi, size);
504
505   if (pstream == NULL)
506     return AVIERR_BADHANDLE;
507   if ((DWORD)size < sizeof(AVISTREAMINFOA))
508     return AVIERR_BADSIZE;
509
510   hres = IAVIStream_Info(pstream, &asiw, sizeof(asiw));
511
512   memcpy(asi, &asiw, sizeof(asiw) - sizeof(asiw.szName));
513   WideCharToMultiByte(CP_ACP, 0, asiw.szName, -1, asi->szName,
514                       sizeof(asi->szName), NULL, NULL);
515   asi->szName[sizeof(asi->szName) - 1] = 0;
516
517   return hres;
518 }
519
520 /***********************************************************************
521  *              AVIStreamInfoW          (AVIFIL32.@)
522  */
523 HRESULT WINAPI AVIStreamInfoW(PAVISTREAM pstream, LPAVISTREAMINFOW asi,
524                               LONG size)
525 {
526   TRACE("(%p,%p,%d)\n", pstream, asi, size);
527
528   if (pstream == NULL)
529     return AVIERR_BADHANDLE;
530
531   return IAVIStream_Info(pstream, asi, size);
532 }
533
534 /***********************************************************************
535  *              AVIStreamFindSample     (AVIFIL32.@)
536  */
537 LONG WINAPI AVIStreamFindSample(PAVISTREAM pstream, LONG pos, LONG flags)
538 {
539   TRACE("(%p,%d,0x%X)\n", pstream, pos, flags);
540
541   if (pstream == NULL)
542     return -1;
543
544   return IAVIStream_FindSample(pstream, pos, flags);
545 }
546
547 /***********************************************************************
548  *              AVIStreamReadFormat     (AVIFIL32.@)
549  */
550 HRESULT WINAPI AVIStreamReadFormat(PAVISTREAM pstream, LONG pos,
551                                    LPVOID format, LPLONG formatsize)
552 {
553   TRACE("(%p,%d,%p,%p)\n", pstream, pos, format, formatsize);
554
555   if (pstream == NULL)
556     return AVIERR_BADHANDLE;
557
558   return IAVIStream_ReadFormat(pstream, pos, format, formatsize);
559 }
560
561 /***********************************************************************
562  *              AVIStreamSetFormat      (AVIFIL32.@)
563  */
564 HRESULT WINAPI AVIStreamSetFormat(PAVISTREAM pstream, LONG pos,
565                                   LPVOID format, LONG formatsize)
566 {
567   TRACE("(%p,%d,%p,%d)\n", pstream, pos, format, formatsize);
568
569   if (pstream == NULL)
570     return AVIERR_BADHANDLE;
571
572   return IAVIStream_SetFormat(pstream, pos, format, formatsize);
573 }
574
575 /***********************************************************************
576  *              AVIStreamRead           (AVIFIL32.@)
577  */
578 HRESULT WINAPI AVIStreamRead(PAVISTREAM pstream, LONG start, LONG samples,
579                              LPVOID buffer, LONG buffersize,
580                              LPLONG bytesread, LPLONG samplesread)
581 {
582   TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", pstream, start, samples, buffer,
583         buffersize, bytesread, samplesread);
584
585   if (pstream == NULL)
586     return AVIERR_BADHANDLE;
587
588   return IAVIStream_Read(pstream, start, samples, buffer, buffersize,
589                          bytesread, samplesread);
590 }
591
592 /***********************************************************************
593  *              AVIStreamWrite          (AVIFIL32.@)
594  */
595 HRESULT WINAPI AVIStreamWrite(PAVISTREAM pstream, LONG start, LONG samples,
596                               LPVOID buffer, LONG buffersize, DWORD flags,
597                               LPLONG sampwritten, LPLONG byteswritten)
598 {
599   TRACE("(%p,%d,%d,%p,%d,0x%X,%p,%p)\n", pstream, start, samples, buffer,
600         buffersize, flags, sampwritten, byteswritten);
601
602   if (pstream == NULL)
603     return AVIERR_BADHANDLE;
604
605   return IAVIStream_Write(pstream, start, samples, buffer, buffersize,
606                           flags, sampwritten, byteswritten);
607 }
608
609 /***********************************************************************
610  *              AVIStreamReadData       (AVIFIL32.@)
611  */
612 HRESULT WINAPI AVIStreamReadData(PAVISTREAM pstream, DWORD fcc, LPVOID lp,
613                                  LPLONG lpread)
614 {
615   TRACE("(%p,'%4.4s',%p,%p)\n", pstream, (char*)&fcc, lp, lpread);
616
617   if (pstream == NULL)
618     return AVIERR_BADHANDLE;
619
620   return IAVIStream_ReadData(pstream, fcc, lp, lpread);
621 }
622
623 /***********************************************************************
624  *              AVIStreamWriteData      (AVIFIL32.@)
625  */
626 HRESULT WINAPI AVIStreamWriteData(PAVISTREAM pstream, DWORD fcc, LPVOID lp,
627                                   LONG size)
628 {
629   TRACE("(%p,'%4.4s',%p,%d)\n", pstream, (char*)&fcc, lp, size);
630
631   if (pstream == NULL)
632     return AVIERR_BADHANDLE;
633
634   return IAVIStream_WriteData(pstream, fcc, lp, size);
635 }
636
637 /***********************************************************************
638  *              AVIStreamGetFrameOpen   (AVIFIL32.@)
639  */
640 PGETFRAME WINAPI AVIStreamGetFrameOpen(PAVISTREAM pstream,
641                                        LPBITMAPINFOHEADER lpbiWanted)
642 {
643   PGETFRAME pg = NULL;
644
645   TRACE("(%p,%p)\n", pstream, lpbiWanted);
646
647   if (FAILED(IAVIStream_QueryInterface(pstream, &IID_IGetFrame, (LPVOID*)&pg)) ||
648       pg == NULL) {
649     pg = AVIFILE_CreateGetFrame(pstream);
650     if (pg == NULL)
651       return NULL;
652   }
653
654   if (FAILED(IGetFrame_SetFormat(pg, lpbiWanted, NULL, 0, 0, -1, -1))) {
655     IGetFrame_Release(pg);
656     return NULL;
657   }
658
659   return pg;
660 }
661
662 /***********************************************************************
663  *              AVIStreamGetFrame       (AVIFIL32.@)
664  */
665 LPVOID WINAPI AVIStreamGetFrame(PGETFRAME pg, LONG pos)
666 {
667   TRACE("(%p,%d)\n", pg, pos);
668
669   if (pg == NULL)
670     return NULL;
671
672   return IGetFrame_GetFrame(pg, pos);
673 }
674
675 /***********************************************************************
676  *              AVIStreamGetFrameClose  (AVIFIL32.@)
677  */
678 HRESULT WINAPI AVIStreamGetFrameClose(PGETFRAME pg)
679 {
680   TRACE("(%p)\n", pg);
681
682   if (pg != NULL)
683     return IGetFrame_Release(pg);
684   return 0;
685 }
686
687 /***********************************************************************
688  *              AVIMakeCompressedStream (AVIFIL32.@)
689  */
690 HRESULT WINAPI AVIMakeCompressedStream(PAVISTREAM *ppsCompressed,
691                                        PAVISTREAM psSource,
692                                        LPAVICOMPRESSOPTIONS aco,
693                                        LPCLSID pclsidHandler)
694 {
695   AVISTREAMINFOW asiw;
696   CHAR           szRegKey[25];
697   CHAR           szValue[100];
698   CLSID          clsidHandler;
699   HRESULT        hr;
700   LONG           size = sizeof(szValue);
701
702   TRACE("(%p,%p,%p,%s)\n", ppsCompressed, psSource, aco,
703         debugstr_guid(pclsidHandler));
704
705   if (ppsCompressed == NULL)
706     return AVIERR_BADPARAM;
707   if (psSource == NULL)
708     return AVIERR_BADHANDLE;
709
710   *ppsCompressed = NULL;
711
712   /* if no handler given get default ones based on streamtype */
713   if (pclsidHandler == NULL) {
714     hr = IAVIStream_Info(psSource, &asiw, sizeof(asiw));
715     if (FAILED(hr))
716       return hr;
717
718     wsprintfA(szRegKey, "AVIFile\\Compressors\\%4.4s", (char*)&asiw.fccType);
719     if (RegQueryValueA(HKEY_CLASSES_ROOT, szRegKey, szValue, &size) != ERROR_SUCCESS)
720       return AVIERR_UNSUPPORTED;
721     if (AVIFILE_CLSIDFromString(szValue, &clsidHandler) != S_OK)
722       return AVIERR_UNSUPPORTED;
723   } else
724     clsidHandler = *pclsidHandler;
725
726   hr = CoCreateInstance(&clsidHandler, NULL, CLSCTX_INPROC, &IID_IAVIStream, (LPVOID*)ppsCompressed);
727   if (FAILED(hr) || *ppsCompressed == NULL)
728     return hr;
729
730   hr = IAVIStream_Create(*ppsCompressed, (LPARAM)psSource, (LPARAM)aco);
731   if (FAILED(hr)) {
732     IAVIStream_Release(*ppsCompressed);
733     *ppsCompressed = NULL;
734   }
735
736   return hr;
737 }
738
739 /***********************************************************************
740  *              AVIMakeFileFromStreams  (AVIFIL32.@)
741  */
742 HRESULT WINAPI AVIMakeFileFromStreams(PAVIFILE *ppfile, int nStreams,
743                                       PAVISTREAM *ppStreams)
744 {
745   TRACE("(%p,%d,%p)\n", ppfile, nStreams, ppStreams);
746
747   if (nStreams < 0 || ppfile == NULL || ppStreams == NULL)
748     return AVIERR_BADPARAM;
749
750   *ppfile = AVIFILE_CreateAVITempFile(nStreams, ppStreams);
751   if (*ppfile == NULL)
752     return AVIERR_MEMORY;
753
754   return AVIERR_OK;
755 }
756
757 /***********************************************************************
758  *              AVIStreamOpenFromFile   (AVIFIL32.@)
759  *              AVIStreamOpenFromFileA  (AVIFIL32.@)
760  */
761 HRESULT WINAPI AVIStreamOpenFromFileA(PAVISTREAM *ppavi, LPCSTR szFile,
762                                       DWORD fccType, LONG lParam,
763                                       UINT mode, LPCLSID pclsidHandler)
764 {
765   PAVIFILE pfile = NULL;
766   HRESULT  hr;
767
768   TRACE("(%p,%s,'%4.4s',%d,0x%X,%s)\n", ppavi, debugstr_a(szFile),
769         (char*)&fccType, lParam, mode, debugstr_guid(pclsidHandler));
770
771   if (ppavi == NULL || szFile == NULL)
772     return AVIERR_BADPARAM;
773
774   *ppavi = NULL;
775
776   hr = AVIFileOpenA(&pfile, szFile, mode, pclsidHandler);
777   if (FAILED(hr) || pfile == NULL)
778     return hr;
779
780   hr = IAVIFile_GetStream(pfile, ppavi, fccType, lParam);
781   IAVIFile_Release(pfile);
782
783   return hr;
784 }
785
786 /***********************************************************************
787  *              AVIStreamOpenFromFileW  (AVIFIL32.@)
788  */
789 HRESULT WINAPI AVIStreamOpenFromFileW(PAVISTREAM *ppavi, LPCWSTR szFile,
790                                       DWORD fccType, LONG lParam,
791                                       UINT mode, LPCLSID pclsidHandler)
792 {
793   PAVIFILE pfile = NULL;
794   HRESULT  hr;
795
796   TRACE("(%p,%s,'%4.4s',%d,0x%X,%s)\n", ppavi, debugstr_w(szFile),
797         (char*)&fccType, lParam, mode, debugstr_guid(pclsidHandler));
798
799   if (ppavi == NULL || szFile == NULL)
800     return AVIERR_BADPARAM;
801
802   *ppavi = NULL;
803
804   hr = AVIFileOpenW(&pfile, szFile, mode, pclsidHandler);
805   if (FAILED(hr) || pfile == NULL)
806     return hr;
807
808   hr = IAVIFile_GetStream(pfile, ppavi, fccType, lParam);
809   IAVIFile_Release(pfile);
810
811   return hr;
812 }
813
814 /***********************************************************************
815  *              AVIStreamBeginStreaming (AVIFIL32.@)
816  */
817 LONG WINAPI AVIStreamBeginStreaming(PAVISTREAM pavi, LONG lStart, LONG lEnd, LONG lRate)
818 {
819   IAVIStreaming* pstream = NULL;
820   HRESULT hr;
821
822   TRACE("(%p,%d,%d,%d)\n", pavi, lStart, lEnd, lRate);
823
824   if (pavi == NULL)
825     return AVIERR_BADHANDLE;
826
827   hr = IAVIStream_QueryInterface(pavi, &IID_IAVIStreaming, (LPVOID*)&pstream);
828   if (SUCCEEDED(hr) && pstream != NULL) {
829     hr = IAVIStreaming_Begin(pstream, lStart, lEnd, lRate);
830     IAVIStreaming_Release(pstream);
831   } else
832     hr = AVIERR_OK;
833
834   return hr;
835 }
836
837 /***********************************************************************
838  *              AVIStreamEndStreaming   (AVIFIL32.@)
839  */
840 LONG WINAPI AVIStreamEndStreaming(PAVISTREAM pavi)
841 {
842   IAVIStreaming* pstream = NULL;
843   HRESULT hr;
844
845   TRACE("(%p)\n", pavi);
846
847   hr = IAVIStream_QueryInterface(pavi, &IID_IAVIStreaming, (LPVOID*)&pstream);
848   if (SUCCEEDED(hr) && pstream != NULL) {
849     IAVIStreaming_End(pstream);
850     IAVIStreaming_Release(pstream);
851   }
852
853  return AVIERR_OK;
854 }
855
856 /***********************************************************************
857  *              AVIStreamStart          (AVIFIL32.@)
858  */
859 LONG WINAPI AVIStreamStart(PAVISTREAM pstream)
860 {
861   AVISTREAMINFOW asiw;
862
863   TRACE("(%p)\n", pstream);
864
865   if (pstream == NULL)
866     return 0;
867
868   if (FAILED(IAVIStream_Info(pstream, &asiw, sizeof(asiw))))
869     return 0;
870
871   return asiw.dwStart;
872 }
873
874 /***********************************************************************
875  *              AVIStreamLength         (AVIFIL32.@)
876  */
877 LONG WINAPI AVIStreamLength(PAVISTREAM pstream)
878 {
879   AVISTREAMINFOW asiw;
880
881   TRACE("(%p)\n", pstream);
882
883   if (pstream == NULL)
884     return 0;
885
886   if (FAILED(IAVIStream_Info(pstream, &asiw, sizeof(asiw))))
887     return 0;
888
889   return asiw.dwLength;
890 }
891
892 /***********************************************************************
893  *              AVIStreamSampleToTime   (AVIFIL32.@)
894  */
895 LONG WINAPI AVIStreamSampleToTime(PAVISTREAM pstream, LONG lSample)
896 {
897   AVISTREAMINFOW asiw;
898   LONG time;
899
900   TRACE("(%p,%d)\n", pstream, lSample);
901
902   if (pstream == NULL)
903     return -1;
904
905   if (FAILED(IAVIStream_Info(pstream, &asiw, sizeof(asiw))))
906     return -1;
907   if (asiw.dwRate == 0)
908     return -1;
909
910   /* limit to stream bounds */
911   if (lSample < asiw.dwStart)
912     lSample = asiw.dwStart;
913   if (lSample > asiw.dwStart + asiw.dwLength)
914     lSample = asiw.dwStart + asiw.dwLength;
915
916   if (asiw.dwRate / asiw.dwScale < 1000)
917     time = (LONG)(((float)lSample * asiw.dwScale * 1000) / asiw.dwRate);
918   else
919     time = (LONG)(((float)lSample * asiw.dwScale * 1000 + (asiw.dwRate - 1)) / asiw.dwRate);
920
921   TRACE(" -> %d\n",time);
922   return time;
923 }
924
925 /***********************************************************************
926  *              AVIStreamTimeToSample   (AVIFIL32.@)
927  */
928 LONG WINAPI AVIStreamTimeToSample(PAVISTREAM pstream, LONG lTime)
929 {
930   AVISTREAMINFOW asiw;
931   ULONG sample;
932
933   TRACE("(%p,%d)\n", pstream, lTime);
934
935   if (pstream == NULL || lTime < 0)
936     return -1;
937
938   if (FAILED(IAVIStream_Info(pstream, &asiw, sizeof(asiw))))
939     return -1;
940   if (asiw.dwScale == 0)
941     return -1;
942
943   if (asiw.dwRate / asiw.dwScale < 1000)
944     sample = (LONG)((((float)asiw.dwRate * lTime) / (asiw.dwScale * 1000)));
945   else
946     sample = (LONG)(((float)asiw.dwRate * lTime + (asiw.dwScale * 1000 - 1)) / (asiw.dwScale * 1000));
947
948   /* limit to stream bounds */
949   if (sample < asiw.dwStart)
950     sample = asiw.dwStart;
951   if (sample > asiw.dwStart + asiw.dwLength)
952     sample = asiw.dwStart + asiw.dwLength;
953
954   TRACE(" -> %d\n", sample);
955   return sample;
956 }
957
958 /***********************************************************************
959  *              AVIBuildFilter          (AVIFIL32.@)
960  *              AVIBuildFilterA         (AVIFIL32.@)
961  */
962 HRESULT WINAPI AVIBuildFilterA(LPSTR szFilter, LONG cbFilter, BOOL fSaving)
963 {
964   LPWSTR  wszFilter;
965   HRESULT hr;
966
967   TRACE("(%p,%d,%d)\n", szFilter, cbFilter, fSaving);
968
969   /* check parameters */
970   if (szFilter == NULL)
971     return AVIERR_BADPARAM;
972   if (cbFilter < 2)
973     return AVIERR_BADSIZE;
974
975   szFilter[0] = 0;
976   szFilter[1] = 0;
977
978   wszFilter = HeapAlloc(GetProcessHeap(), 0, cbFilter * sizeof(WCHAR));
979   if (wszFilter == NULL)
980     return AVIERR_MEMORY;
981
982   hr = AVIBuildFilterW(wszFilter, cbFilter, fSaving);
983   if (SUCCEEDED(hr)) {
984     WideCharToMultiByte(CP_ACP, 0, wszFilter, cbFilter,
985                         szFilter, cbFilter, NULL, NULL);
986   }
987
988   HeapFree(GetProcessHeap(), 0, wszFilter);
989
990   return hr;
991 }
992
993 /***********************************************************************
994  *              AVIBuildFilterW         (AVIFIL32.@)
995  */
996 HRESULT WINAPI AVIBuildFilterW(LPWSTR szFilter, LONG cbFilter, BOOL fSaving)
997 {
998   static const WCHAR szClsid[] = {'C','L','S','I','D',0};
999   static const WCHAR szExtensionFmt[] = {';','*','.','%','s',0};
1000   static const WCHAR szAVIFileExtensions[] =
1001     {'A','V','I','F','i','l','e','\\','E','x','t','e','n','s','i','o','n','s',0};
1002
1003   AVIFilter *lp;
1004   WCHAR      szAllFiles[40];
1005   WCHAR      szFileExt[10];
1006   WCHAR      szValue[128];
1007   HKEY       hKey;
1008   DWORD      n, i;
1009   LONG       size;
1010   DWORD      count = 0;
1011
1012   TRACE("(%p,%d,%d)\n", szFilter, cbFilter, fSaving);
1013
1014   /* check parameters */
1015   if (szFilter == NULL)
1016     return AVIERR_BADPARAM;
1017   if (cbFilter < 2)
1018     return AVIERR_BADSIZE;
1019
1020   lp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_FILTERS * sizeof(AVIFilter));
1021   if (lp == NULL)
1022     return AVIERR_MEMORY;
1023
1024   /*
1025    * 1. iterate over HKEY_CLASSES_ROOT\\AVIFile\\Extensions and collect
1026    *    extensions and CLSID's
1027    * 2. iterate over collected CLSID's and copy its description and its
1028    *    extensions to szFilter if it fits
1029    *
1030    * First filter is named "All multimedia files" and its filter is a
1031    * collection of all possible extensions except "*.*".
1032    */
1033   if (RegOpenKeyW(HKEY_CLASSES_ROOT, szAVIFileExtensions, &hKey) != ERROR_SUCCESS) {
1034     HeapFree(GetProcessHeap(), 0, lp);
1035     return AVIERR_ERROR;
1036   }
1037   for (n = 0;RegEnumKeyW(hKey, n, szFileExt, sizeof(szFileExt)/sizeof(szFileExt[0])) == ERROR_SUCCESS;n++) {
1038     /* get CLSID to extension */
1039     size = sizeof(szValue);
1040     if (RegQueryValueW(hKey, szFileExt, szValue, &size) != ERROR_SUCCESS)
1041       break;
1042
1043     /* search if the CLSID is already known */
1044     for (i = 1; i <= count; i++) {
1045       if (lstrcmpW(lp[i].szClsid, szValue) == 0)
1046         break; /* a new one */
1047     }
1048
1049     if (i == count + 1) {
1050       /* it's a new CLSID */
1051
1052       /* FIXME: How do we get info's about read/write capabilities? */
1053
1054       if (count >= MAX_FILTERS) {
1055         /* try to inform user of our full fixed size table */
1056         ERR(": More than %d filters found! Adjust MAX_FILTERS in dlls/avifil32/api.c\n", MAX_FILTERS);
1057         break;
1058       }
1059
1060       lstrcpyW(lp[i].szClsid, szValue);
1061
1062       count++;
1063     }
1064
1065     /* append extension to the filter */
1066     wsprintfW(szValue, szExtensionFmt, szFileExt);
1067     if (lp[i].szExtensions[0] == 0)
1068       lstrcatW(lp[i].szExtensions, szValue + 1);
1069     else
1070       lstrcatW(lp[i].szExtensions, szValue);
1071
1072     /* also append to the "all multimedia"-filter */
1073     if (lp[0].szExtensions[0] == 0)
1074       lstrcatW(lp[0].szExtensions, szValue + 1);
1075     else
1076       lstrcatW(lp[0].szExtensions, szValue);
1077   }
1078   RegCloseKey(hKey);
1079
1080   /* 2. get descriptions for the CLSIDs and fill out szFilter */
1081   if (RegOpenKeyW(HKEY_CLASSES_ROOT, szClsid, &hKey) != ERROR_SUCCESS) {
1082     HeapFree(GetProcessHeap(), 0, lp);
1083     return AVIERR_ERROR;
1084   }
1085   for (n = 0; n <= count; n++) {
1086     /* first the description */
1087     if (n != 0) {
1088       size = sizeof(szValue);
1089       if (RegQueryValueW(hKey, lp[n].szClsid, szValue, &size) == ERROR_SUCCESS) {
1090         size = lstrlenW(szValue);
1091         lstrcpynW(szFilter, szValue, cbFilter);
1092       }
1093     } else
1094       size = LoadStringW(AVIFILE_hModule,IDS_ALLMULTIMEDIA,szFilter,cbFilter);
1095
1096     /* check for enough space */
1097     size++;
1098     if (cbFilter < size + lstrlenW(lp[n].szExtensions) + 2) {
1099       szFilter[0] = 0;
1100       szFilter[1] = 0;
1101       HeapFree(GetProcessHeap(), 0, lp);
1102       RegCloseKey(hKey);
1103       return AVIERR_BUFFERTOOSMALL;
1104     }
1105     cbFilter -= size;
1106     szFilter += size;
1107
1108     /* and then the filter */
1109     lstrcpynW(szFilter, lp[n].szExtensions, cbFilter);
1110     size = lstrlenW(lp[n].szExtensions) + 1;
1111     cbFilter -= size;
1112     szFilter += size;
1113   }
1114
1115   RegCloseKey(hKey);
1116   HeapFree(GetProcessHeap(), 0, lp);
1117
1118   /* add "All files" "*.*" filter if enough space left */
1119   size = LoadStringW(AVIFILE_hModule, IDS_ALLFILES,
1120                      szAllFiles, sizeof(szAllFiles)/sizeof(szAllFiles[0])) + 1;
1121   if (cbFilter > size) {
1122     int i;
1123
1124     /* replace '@' with \000 to separate description of filter */
1125     for (i = 0; i < size && szAllFiles[i] != 0; i++) {
1126       if (szAllFiles[i] == '@') {
1127         szAllFiles[i] = 0;
1128         break;
1129       }
1130     }
1131       
1132     memcpy(szFilter, szAllFiles, size * sizeof(szAllFiles[0]));
1133     szFilter += size;
1134     szFilter[0] = 0;
1135
1136     return AVIERR_OK;
1137   } else {
1138     szFilter[0] = 0;
1139     return AVIERR_BUFFERTOOSMALL;
1140   }
1141 }
1142
1143 static BOOL AVISaveOptionsFmtChoose(HWND hWnd)
1144 {
1145   LPAVICOMPRESSOPTIONS pOptions = SaveOpts.ppOptions[SaveOpts.nCurrent];
1146   AVISTREAMINFOW       sInfo;
1147
1148   TRACE("(%p)\n", hWnd);
1149
1150   if (pOptions == NULL || SaveOpts.ppavis[SaveOpts.nCurrent] == NULL) {
1151     ERR(": bad state!\n");
1152     return FALSE;
1153   }
1154
1155   if (FAILED(AVIStreamInfoW(SaveOpts.ppavis[SaveOpts.nCurrent],
1156                             &sInfo, sizeof(sInfo)))) {
1157     ERR(": AVIStreamInfoW failed!\n");
1158     return FALSE;
1159   }
1160
1161   if (sInfo.fccType == streamtypeVIDEO) {
1162     COMPVARS cv;
1163     BOOL     ret;
1164
1165     memset(&cv, 0, sizeof(cv));
1166
1167     if ((pOptions->dwFlags & AVICOMPRESSF_VALID) == 0) {
1168       memset(pOptions, 0, sizeof(AVICOMPRESSOPTIONS));
1169       pOptions->fccType    = streamtypeVIDEO;
1170       pOptions->fccHandler = comptypeDIB;
1171       pOptions->dwQuality  = (DWORD)ICQUALITY_DEFAULT;
1172     }
1173
1174     cv.cbSize     = sizeof(cv);
1175     cv.dwFlags    = ICMF_COMPVARS_VALID;
1176     /*cv.fccType    = pOptions->fccType; */
1177     cv.fccHandler = pOptions->fccHandler;
1178     cv.lQ         = pOptions->dwQuality;
1179     cv.lpState    = pOptions->lpParms;
1180     cv.cbState    = pOptions->cbParms;
1181     if (pOptions->dwFlags & AVICOMPRESSF_KEYFRAMES)
1182       cv.lKey = pOptions->dwKeyFrameEvery;
1183     else
1184       cv.lKey = 0;
1185     if (pOptions->dwFlags & AVICOMPRESSF_DATARATE)
1186       cv.lDataRate = pOptions->dwBytesPerSecond / 1024; /* need kBytes */
1187     else
1188       cv.lDataRate = 0;
1189
1190     ret = ICCompressorChoose(hWnd, SaveOpts.uFlags, NULL,
1191                              SaveOpts.ppavis[SaveOpts.nCurrent], &cv, NULL);
1192
1193     if (ret) {
1194       pOptions->fccHandler = cv.fccHandler;
1195       pOptions->lpParms   = cv.lpState;
1196       pOptions->cbParms   = cv.cbState;
1197       pOptions->dwQuality = cv.lQ;
1198       if (cv.lKey != 0) {
1199         pOptions->dwKeyFrameEvery = cv.lKey;
1200         pOptions->dwFlags |= AVICOMPRESSF_KEYFRAMES;
1201       } else
1202         pOptions->dwFlags &= ~AVICOMPRESSF_KEYFRAMES;
1203       if (cv.lDataRate != 0) {
1204         pOptions->dwBytesPerSecond = cv.lDataRate * 1024; /* need bytes */
1205         pOptions->dwFlags |= AVICOMPRESSF_DATARATE;
1206       } else
1207         pOptions->dwFlags &= ~AVICOMPRESSF_DATARATE;
1208       pOptions->dwFlags  |= AVICOMPRESSF_VALID;
1209     }
1210     ICCompressorFree(&cv);
1211
1212     return ret;
1213   } else if (sInfo.fccType == streamtypeAUDIO) {
1214     ACMFORMATCHOOSEW afmtc;
1215     MMRESULT         ret;
1216     LONG             size;
1217
1218     /* FIXME: check ACM version -- Which version is needed? */
1219
1220     memset(&afmtc, 0, sizeof(afmtc));
1221     afmtc.cbStruct  = sizeof(afmtc);
1222     afmtc.fdwStyle  = 0;
1223     afmtc.hwndOwner = hWnd;
1224
1225     acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT, &size);
1226     if ((pOptions->cbFormat == 0 || pOptions->lpFormat == NULL) && size != 0) {
1227       pOptions->lpFormat = HeapAlloc(GetProcessHeap(), 0, size);
1228       if (!pOptions->lpFormat) return FALSE;
1229       pOptions->cbFormat = size;
1230     } else if (pOptions->cbFormat < (DWORD)size) {
1231       void *new_buffer = HeapReAlloc(GetProcessHeap(), 0, pOptions->lpFormat, size);
1232       if (!new_buffer) return FALSE;
1233       pOptions->lpFormat = new_buffer;
1234       pOptions->cbFormat = size;
1235     }
1236     afmtc.pwfx  = pOptions->lpFormat;
1237     afmtc.cbwfx = pOptions->cbFormat;
1238
1239     size = 0;
1240     AVIStreamFormatSize(SaveOpts.ppavis[SaveOpts.nCurrent],
1241                         sInfo.dwStart, &size);
1242     if (size < (LONG)sizeof(PCMWAVEFORMAT))
1243       size = sizeof(PCMWAVEFORMAT);
1244     afmtc.pwfxEnum = HeapAlloc(GetProcessHeap(), 0, size);
1245     if (afmtc.pwfxEnum != NULL) {
1246       AVIStreamReadFormat(SaveOpts.ppavis[SaveOpts.nCurrent],
1247                           sInfo.dwStart, afmtc.pwfxEnum, &size);
1248       afmtc.fdwEnum = ACM_FORMATENUMF_CONVERT;
1249     }
1250
1251     ret = acmFormatChooseW(&afmtc);
1252     if (ret == S_OK)
1253       pOptions->dwFlags |= AVICOMPRESSF_VALID;
1254
1255     HeapFree(GetProcessHeap(), 0, afmtc.pwfxEnum);
1256     return (ret == S_OK ? TRUE : FALSE);
1257   } else {
1258     ERR(": unknown streamtype 0x%08X\n", sInfo.fccType);
1259     return FALSE;
1260   }
1261 }
1262
1263 static void AVISaveOptionsUpdate(HWND hWnd)
1264 {
1265   static const WCHAR szVideoFmt[]={'%','l','d','x','%','l','d','x','%','d',0};
1266   static const WCHAR szAudioFmt[]={'%','s',' ','%','s',0};
1267
1268   WCHAR          szFormat[128];
1269   AVISTREAMINFOW sInfo;
1270   LPVOID         lpFormat;
1271   LONG           size;
1272
1273   TRACE("(%p)\n", hWnd);
1274
1275   SaveOpts.nCurrent = SendDlgItemMessageW(hWnd,IDC_STREAM,CB_GETCURSEL,0,0);
1276   if (SaveOpts.nCurrent < 0)
1277     return;
1278
1279   if (FAILED(AVIStreamInfoW(SaveOpts.ppavis[SaveOpts.nCurrent], &sInfo, sizeof(sInfo))))
1280     return;
1281
1282   AVIStreamFormatSize(SaveOpts.ppavis[SaveOpts.nCurrent],sInfo.dwStart,&size);
1283   if (size > 0) {
1284     szFormat[0] = 0;
1285
1286     /* read format to build format description string */
1287     lpFormat = HeapAlloc(GetProcessHeap(), 0, size);
1288     if (lpFormat != NULL) {
1289       if (SUCCEEDED(AVIStreamReadFormat(SaveOpts.ppavis[SaveOpts.nCurrent],sInfo.dwStart,lpFormat, &size))) {
1290         if (sInfo.fccType == streamtypeVIDEO) {
1291           LPBITMAPINFOHEADER lpbi = lpFormat;
1292           ICINFO icinfo;
1293
1294           wsprintfW(szFormat, szVideoFmt, lpbi->biWidth,
1295                     lpbi->biHeight, lpbi->biBitCount);
1296
1297           if (lpbi->biCompression != BI_RGB) {
1298             HIC    hic;
1299
1300             hic = ICLocate(ICTYPE_VIDEO, sInfo.fccHandler, lpFormat,
1301                            NULL, ICMODE_DECOMPRESS);
1302             if (hic != NULL) {
1303               if (ICGetInfo(hic, &icinfo, sizeof(icinfo)) == S_OK)
1304                 lstrcatW(szFormat, icinfo.szDescription);
1305               ICClose(hic);
1306             }
1307           } else {
1308             LoadStringW(AVIFILE_hModule, IDS_UNCOMPRESSED,
1309                         icinfo.szDescription,
1310                         sizeof(icinfo.szDescription)/sizeof(icinfo.szDescription[0]));
1311             lstrcatW(szFormat, icinfo.szDescription);
1312           }
1313         } else if (sInfo.fccType == streamtypeAUDIO) {
1314           ACMFORMATTAGDETAILSW aftd;
1315           ACMFORMATDETAILSW    afd;
1316
1317           memset(&aftd, 0, sizeof(aftd));
1318           memset(&afd, 0, sizeof(afd));
1319
1320           aftd.cbStruct     = sizeof(aftd);
1321           aftd.dwFormatTag  = afd.dwFormatTag =
1322             ((PWAVEFORMATEX)lpFormat)->wFormatTag;
1323           aftd.cbFormatSize = afd.cbwfx = size;
1324
1325           afd.cbStruct      = sizeof(afd);
1326           afd.pwfx          = lpFormat;
1327
1328           if (acmFormatTagDetailsW(NULL, &aftd,
1329                                    ACM_FORMATTAGDETAILSF_FORMATTAG) == S_OK) {
1330             if (acmFormatDetailsW(NULL,&afd,ACM_FORMATDETAILSF_FORMAT) == S_OK)
1331               wsprintfW(szFormat, szAudioFmt, afd.szFormat, aftd.szFormatTag);
1332           }
1333         }
1334       }
1335       HeapFree(GetProcessHeap(), 0, lpFormat);
1336     }
1337
1338     /* set text for format description */
1339     SetDlgItemTextW(hWnd, IDC_FORMATTEXT, szFormat);
1340
1341     /* Disable option button for unsupported streamtypes */
1342     if (sInfo.fccType == streamtypeVIDEO ||
1343         sInfo.fccType == streamtypeAUDIO)
1344       EnableWindow(GetDlgItem(hWnd, IDC_OPTIONS), TRUE);
1345     else
1346       EnableWindow(GetDlgItem(hWnd, IDC_OPTIONS), FALSE);
1347   }
1348
1349 }
1350
1351 static INT_PTR CALLBACK AVISaveOptionsDlgProc(HWND hWnd, UINT uMsg,
1352                                               WPARAM wParam, LPARAM lParam)
1353 {
1354   DWORD dwInterleave;
1355   BOOL  bIsInterleaved;
1356   INT   n;
1357
1358   /*TRACE("(%p,%u,0x%04X,0x%08lX)\n", hWnd, uMsg, wParam, lParam);*/
1359
1360   switch (uMsg) {
1361   case WM_INITDIALOG:
1362     SaveOpts.nCurrent = 0;
1363     if (SaveOpts.nStreams == 1) {
1364       EndDialog(hWnd, AVISaveOptionsFmtChoose(hWnd));
1365       return TRUE;
1366     }
1367
1368     /* add streams */
1369     for (n = 0; n < SaveOpts.nStreams; n++) {
1370       AVISTREAMINFOW sInfo;
1371
1372       AVIStreamInfoW(SaveOpts.ppavis[n], &sInfo, sizeof(sInfo));
1373       SendDlgItemMessageW(hWnd, IDC_STREAM, CB_ADDSTRING,
1374                           0L, (LPARAM)sInfo.szName);
1375     }
1376
1377     /* select first stream */
1378     SendDlgItemMessageW(hWnd, IDC_STREAM, CB_SETCURSEL, 0, 0);
1379     SendMessageW(hWnd, WM_COMMAND, MAKELONG(IDC_STREAM, CBN_SELCHANGE), (LPARAM)hWnd);
1380
1381     /* initialize interleave */
1382     if (SaveOpts.ppOptions[0] != NULL &&
1383         (SaveOpts.ppOptions[0]->dwFlags & AVICOMPRESSF_VALID)) {
1384       bIsInterleaved = (SaveOpts.ppOptions[0]->dwFlags & AVICOMPRESSF_INTERLEAVE);
1385       dwInterleave = SaveOpts.ppOptions[0]->dwInterleaveEvery;
1386     } else {
1387       bIsInterleaved = TRUE;
1388       dwInterleave   = 0;
1389     }
1390     CheckDlgButton(hWnd, IDC_INTERLEAVE, bIsInterleaved);
1391     SetDlgItemInt(hWnd, IDC_INTERLEAVEEVERY, dwInterleave, FALSE);
1392     EnableWindow(GetDlgItem(hWnd, IDC_INTERLEAVEEVERY), bIsInterleaved);
1393     break;
1394   case WM_COMMAND:
1395     switch (LOWORD(wParam)) {
1396     case IDOK:
1397       /* get data from controls and save them */
1398       dwInterleave   = GetDlgItemInt(hWnd, IDC_INTERLEAVEEVERY, NULL, 0);
1399       bIsInterleaved = IsDlgButtonChecked(hWnd, IDC_INTERLEAVE);
1400       for (n = 0; n < SaveOpts.nStreams; n++) {
1401         if (SaveOpts.ppOptions[n] != NULL) {
1402           if (bIsInterleaved) {
1403             SaveOpts.ppOptions[n]->dwFlags |= AVICOMPRESSF_INTERLEAVE;
1404             SaveOpts.ppOptions[n]->dwInterleaveEvery = dwInterleave;
1405           } else
1406             SaveOpts.ppOptions[n]->dwFlags &= ~AVICOMPRESSF_INTERLEAVE;
1407         }
1408       }
1409       /* fall through */
1410     case IDCANCEL:
1411       EndDialog(hWnd, LOWORD(wParam) == IDOK);
1412       break;
1413     case IDC_INTERLEAVE:
1414       EnableWindow(GetDlgItem(hWnd, IDC_INTERLEAVEEVERY),
1415                    IsDlgButtonChecked(hWnd, IDC_INTERLEAVE));
1416       break;
1417     case IDC_STREAM:
1418       if (HIWORD(wParam) == CBN_SELCHANGE) {
1419         /* update control elements */
1420         AVISaveOptionsUpdate(hWnd);
1421       }
1422       break;
1423     case IDC_OPTIONS:
1424       AVISaveOptionsFmtChoose(hWnd);
1425       break;
1426     };
1427     return TRUE;
1428   };
1429
1430   return FALSE;
1431 }
1432
1433 /***********************************************************************
1434  *              AVISaveOptions          (AVIFIL32.@)
1435  */
1436 BOOL WINAPI AVISaveOptions(HWND hWnd, UINT uFlags, INT nStreams,
1437                            PAVISTREAM *ppavi, LPAVICOMPRESSOPTIONS *ppOptions)
1438 {
1439   LPAVICOMPRESSOPTIONS pSavedOptions = NULL;
1440   INT ret, n;
1441
1442   TRACE("(%p,0x%X,%d,%p,%p)\n", hWnd, uFlags, nStreams,
1443         ppavi, ppOptions);
1444
1445   /* check parameters */
1446   if (nStreams <= 0 || ppavi == NULL || ppOptions == NULL)
1447     return AVIERR_BADPARAM;
1448
1449   /* save options in case the user presses cancel */
1450   if (ppOptions != NULL && nStreams > 1) {
1451     pSavedOptions = HeapAlloc(GetProcessHeap(), 0, nStreams * sizeof(AVICOMPRESSOPTIONS));
1452     if (pSavedOptions == NULL)
1453       return FALSE;
1454
1455     for (n = 0; n < nStreams; n++) {
1456       if (ppOptions[n] != NULL)
1457         memcpy(pSavedOptions + n, ppOptions[n], sizeof(AVICOMPRESSOPTIONS));
1458     }
1459   }
1460
1461   SaveOpts.uFlags    = uFlags;
1462   SaveOpts.nStreams  = nStreams;
1463   SaveOpts.ppavis    = ppavi;
1464   SaveOpts.ppOptions = ppOptions;
1465
1466   ret = DialogBoxW(AVIFILE_hModule, MAKEINTRESOURCEW(IDD_SAVEOPTIONS),
1467                    hWnd, AVISaveOptionsDlgProc);
1468
1469   if (ret == -1)
1470     ret = FALSE;
1471
1472   /* restore options when user pressed cancel */
1473   if (pSavedOptions != NULL) {
1474     if (ret == FALSE) {
1475       for (n = 0; n < nStreams; n++) {
1476         if (ppOptions[n] != NULL)
1477           memcpy(ppOptions[n], pSavedOptions + n, sizeof(AVICOMPRESSOPTIONS));
1478       }
1479     }
1480     HeapFree(GetProcessHeap(), 0, pSavedOptions);
1481   }
1482
1483   return ret;
1484 }
1485
1486 /***********************************************************************
1487  *              AVISaveOptionsFree      (AVIFIL32.@)
1488  */
1489 HRESULT WINAPI AVISaveOptionsFree(INT nStreams,LPAVICOMPRESSOPTIONS*ppOptions)
1490 {
1491   TRACE("(%d,%p)\n", nStreams, ppOptions);
1492
1493   if (nStreams < 0 || ppOptions == NULL)
1494     return AVIERR_BADPARAM;
1495
1496   for (nStreams--; nStreams >= 0; nStreams--) {
1497     if (ppOptions[nStreams] != NULL) {
1498       ppOptions[nStreams]->dwFlags &= ~AVICOMPRESSF_VALID;
1499
1500       if (ppOptions[nStreams]->lpParms != NULL) {
1501         HeapFree(GetProcessHeap(), 0, ppOptions[nStreams]->lpParms);
1502         ppOptions[nStreams]->lpParms = NULL;
1503         ppOptions[nStreams]->cbParms = 0;
1504       }
1505       if (ppOptions[nStreams]->lpFormat != NULL) {
1506         HeapFree(GetProcessHeap(), 0, ppOptions[nStreams]->lpFormat);
1507         ppOptions[nStreams]->lpFormat = NULL;
1508         ppOptions[nStreams]->cbFormat = 0;
1509       }
1510     }
1511   }
1512
1513   return AVIERR_OK;
1514 }
1515
1516 /***********************************************************************
1517  *              AVISaveVA               (AVIFIL32.@)
1518  */
1519 HRESULT WINAPI AVISaveVA(LPCSTR szFile, CLSID *pclsidHandler,
1520                          AVISAVECALLBACK lpfnCallback, int nStream,
1521                          PAVISTREAM *ppavi, LPAVICOMPRESSOPTIONS *plpOptions)
1522 {
1523   LPWSTR  wszFile = NULL;
1524   HRESULT hr;
1525   int     len;
1526
1527   TRACE("%s,%p,%p,%d,%p,%p)\n", debugstr_a(szFile), pclsidHandler,
1528         lpfnCallback, nStream, ppavi, plpOptions);
1529
1530   if (szFile == NULL || ppavi == NULL || plpOptions == NULL)
1531     return AVIERR_BADPARAM;
1532
1533   /* convert ASCII string to Unicode and call Unicode function */
1534   len = MultiByteToWideChar(CP_ACP, 0, szFile, -1, NULL, 0);
1535   if (len <= 0)
1536     return AVIERR_BADPARAM;
1537
1538   wszFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1539   if (wszFile == NULL)
1540     return AVIERR_MEMORY;
1541
1542   MultiByteToWideChar(CP_ACP, 0, szFile, -1, wszFile, len);
1543
1544   hr = AVISaveVW(wszFile, pclsidHandler, lpfnCallback,
1545                  nStream, ppavi, plpOptions);
1546
1547   HeapFree(GetProcessHeap(), 0, wszFile);
1548
1549   return hr;
1550 }
1551
1552 /***********************************************************************
1553  *              AVIFILE_AVISaveDefaultCallback  (internal)
1554  */
1555 static BOOL WINAPI AVIFILE_AVISaveDefaultCallback(INT progress)
1556 {
1557   TRACE("(%d)\n", progress);
1558
1559   return FALSE;
1560 }
1561
1562 /***********************************************************************
1563  *              AVISaveVW               (AVIFIL32.@)
1564  */
1565 HRESULT WINAPI AVISaveVW(LPCWSTR szFile, CLSID *pclsidHandler,
1566                          AVISAVECALLBACK lpfnCallback, int nStreams,
1567                          PAVISTREAM *ppavi, LPAVICOMPRESSOPTIONS *plpOptions)
1568 {
1569   LONG           lStart[MAX_AVISTREAMS];
1570   PAVISTREAM     pOutStreams[MAX_AVISTREAMS];
1571   PAVISTREAM     pInStreams[MAX_AVISTREAMS];
1572   AVIFILEINFOW   fInfo;
1573   AVISTREAMINFOW sInfo;
1574
1575   PAVIFILE       pfile = NULL; /* the output AVI file */
1576   LONG           lFirstVideo = -1;
1577   int            curStream;
1578
1579   /* for interleaving ... */
1580   DWORD          dwInterleave = 0; /* interleave rate */
1581   DWORD          dwFileInitialFrames;
1582   LONG           lFileLength;
1583   LONG           lSampleInc;
1584
1585   /* for reading/writing the data ... */
1586   LPVOID         lpBuffer = NULL;
1587   LONG           cbBuffer;        /* real size of lpBuffer */
1588   LONG           lBufferSize;     /* needed bytes for format(s), etc. */
1589   LONG           lReadBytes;
1590   LONG           lReadSamples;
1591   HRESULT        hres;
1592
1593   TRACE("(%s,%p,%p,%d,%p,%p)\n", debugstr_w(szFile), pclsidHandler,
1594         lpfnCallback, nStreams, ppavi, plpOptions);
1595
1596   if (szFile == NULL || ppavi == NULL || plpOptions == NULL)
1597     return AVIERR_BADPARAM;
1598   if (nStreams >= MAX_AVISTREAMS) {
1599     WARN("Can't write AVI with %d streams only supports %d -- change MAX_AVISTREAMS!\n", nStreams, MAX_AVISTREAMS);
1600     return AVIERR_INTERNAL;
1601   }
1602
1603   if (lpfnCallback == NULL)
1604     lpfnCallback = AVIFILE_AVISaveDefaultCallback;
1605
1606   /* clear local variable(s) */
1607   for (curStream = 0; curStream < nStreams; curStream++) {
1608     pInStreams[curStream]  = NULL;
1609     pOutStreams[curStream] = NULL;
1610   }
1611
1612   /* open output AVI file (create it if it doesn't exist) */
1613   hres = AVIFileOpenW(&pfile, szFile, OF_CREATE|OF_SHARE_EXCLUSIVE|OF_WRITE,
1614                       pclsidHandler);
1615   if (FAILED(hres))
1616     return hres;
1617   AVIFileInfoW(pfile, &fInfo, sizeof(fInfo)); /* for dwCaps */
1618
1619   /* initialize our data structures part 1 */
1620   for (curStream = 0; curStream < nStreams; curStream++) {
1621     PAVISTREAM pCurStream = ppavi[curStream];
1622
1623     hres = AVIStreamInfoW(pCurStream, &sInfo, sizeof(sInfo));
1624     if (FAILED(hres))
1625       goto error;
1626
1627     /* search first video stream and check for interleaving */
1628     if (sInfo.fccType == streamtypeVIDEO) {
1629       /* remember first video stream -- needed for interleaving */
1630       if (lFirstVideo < 0)
1631         lFirstVideo = curStream;
1632     } else if (!dwInterleave && plpOptions != NULL) {
1633       /* check if any non-video stream wants to be interleaved */
1634       WARN("options.flags=0x%X options.dwInterleave=%u\n",plpOptions[curStream]->dwFlags,plpOptions[curStream]->dwInterleaveEvery);
1635       if (plpOptions[curStream] != NULL &&
1636           plpOptions[curStream]->dwFlags & AVICOMPRESSF_INTERLEAVE)
1637         dwInterleave = plpOptions[curStream]->dwInterleaveEvery;
1638     }
1639
1640     /* create de-/compressed stream interface if needed */
1641     pInStreams[curStream] = NULL;
1642     if (plpOptions != NULL && plpOptions[curStream] != NULL) {
1643       if (plpOptions[curStream]->fccHandler ||
1644           plpOptions[curStream]->lpFormat != NULL) {
1645         DWORD dwKeySave = plpOptions[curStream]->dwKeyFrameEvery;
1646
1647         if (fInfo.dwCaps & AVIFILECAPS_ALLKEYFRAMES)
1648           plpOptions[curStream]->dwKeyFrameEvery = 1;
1649
1650         hres = AVIMakeCompressedStream(&pInStreams[curStream], pCurStream,
1651                                        plpOptions[curStream], NULL);
1652         plpOptions[curStream]->dwKeyFrameEvery = dwKeySave;
1653         if (FAILED(hres) || pInStreams[curStream] == NULL) {
1654           pInStreams[curStream] = NULL;
1655           goto error;
1656         }
1657
1658         /* test stream interface and update stream-info */
1659         hres = AVIStreamInfoW(pInStreams[curStream], &sInfo, sizeof(sInfo));
1660         if (FAILED(hres))
1661           goto error;
1662       }
1663     }
1664
1665     /* now handle streams which will only be copied */
1666     if (pInStreams[curStream] == NULL) {
1667       pCurStream = pInStreams[curStream] = ppavi[curStream];
1668       AVIStreamAddRef(pCurStream);
1669     } else
1670       pCurStream = pInStreams[curStream];
1671
1672     lStart[curStream] = sInfo.dwStart;
1673   } /* for all streams */
1674
1675   /* check that first video stream is the first stream */
1676   if (lFirstVideo > 0) {
1677     PAVISTREAM pTmp = pInStreams[lFirstVideo];
1678     LONG lTmp = lStart[lFirstVideo];
1679
1680     pInStreams[lFirstVideo] = pInStreams[0];
1681     pInStreams[0] = pTmp;
1682     lStart[lFirstVideo] = lStart[0];
1683     lStart[0] = lTmp;
1684     lFirstVideo = 0;
1685   }
1686
1687   /* allocate buffer for formats, data, etc. of an initial size of 64 kBytes*/
1688   cbBuffer = 0x00010000;
1689   lpBuffer = HeapAlloc(GetProcessHeap(), 0, cbBuffer);
1690   if (lpBuffer == NULL) {
1691     hres = AVIERR_MEMORY;
1692     goto error;
1693   }
1694
1695   AVIStreamInfoW(pInStreams[0], &sInfo, sizeof(sInfo));
1696   lFileLength = sInfo.dwLength;
1697   dwFileInitialFrames = 0;
1698   if (lFirstVideo >= 0) {
1699     /* check for correct version of the format
1700      *  -- need at least BITMAPINFOHEADER or newer
1701      */
1702     lSampleInc = 1;
1703     lBufferSize = cbBuffer;
1704     hres = AVIStreamReadFormat(pInStreams[lFirstVideo], AVIStreamStart(pInStreams[lFirstVideo]), lpBuffer, &lBufferSize);
1705     if (lBufferSize < (LONG)sizeof(BITMAPINFOHEADER))
1706       hres = AVIERR_INTERNAL;
1707     if (FAILED(hres))
1708       goto error;
1709   } else /* use one second blocks for interleaving if no video present */
1710     lSampleInc = AVIStreamTimeToSample(pInStreams[0], 1000000);
1711
1712   /* create output streams */
1713   for (curStream = 0; curStream < nStreams; curStream++) {
1714     AVIStreamInfoW(pInStreams[curStream], &sInfo, sizeof(sInfo));
1715
1716     sInfo.dwInitialFrames = 0;
1717     if (dwInterleave != 0 && curStream > 0 && sInfo.fccType != streamtypeVIDEO) {
1718       /* 750 ms initial frames for non-video streams */
1719       sInfo.dwInitialFrames = AVIStreamTimeToSample(pInStreams[0], 750);
1720     }
1721
1722     hres = AVIFileCreateStreamW(pfile, &pOutStreams[curStream], &sInfo);
1723     if (pOutStreams[curStream] != NULL && SUCCEEDED(hres)) {
1724       /* copy initial format for this stream */
1725       lBufferSize = cbBuffer;
1726       hres = AVIStreamReadFormat(pInStreams[curStream], sInfo.dwStart,
1727                                  lpBuffer, &lBufferSize);
1728       if (FAILED(hres))
1729         goto error;
1730       hres = AVIStreamSetFormat(pOutStreams[curStream], 0, lpBuffer, lBufferSize);
1731       if (FAILED(hres))
1732         goto error;
1733
1734       /* try to copy stream handler data */
1735       lBufferSize = cbBuffer;
1736       hres = AVIStreamReadData(pInStreams[curStream], ckidSTREAMHANDLERDATA,
1737                                lpBuffer, &lBufferSize);
1738       if (SUCCEEDED(hres) && lBufferSize > 0) {
1739         hres = AVIStreamWriteData(pOutStreams[curStream],ckidSTREAMHANDLERDATA,
1740                                   lpBuffer, lBufferSize);
1741         if (FAILED(hres))
1742           goto error;
1743       }
1744
1745       if (dwFileInitialFrames < sInfo.dwInitialFrames)
1746         dwFileInitialFrames = sInfo.dwInitialFrames;
1747       lReadBytes =
1748         AVIStreamSampleToSample(pOutStreams[0], pInStreams[curStream],
1749                                 sInfo.dwLength);
1750       if (lFileLength < lReadBytes)
1751         lFileLength = lReadBytes;
1752     } else {
1753       /* creation of de-/compression stream interface failed */
1754       WARN("creation of (de-)compression stream failed for stream %d\n",curStream);
1755       AVIStreamRelease(pInStreams[curStream]);
1756       if (curStream + 1 >= nStreams) {
1757         /* move the others one up */
1758         PAVISTREAM *ppas = &pInStreams[curStream];
1759         int            n = nStreams - (curStream + 1);
1760
1761         do {
1762           *ppas = pInStreams[curStream + 1];
1763         } while (--n);
1764       }
1765       nStreams--;
1766       curStream--;
1767     }
1768   } /* create output streams for all input streams */
1769
1770   /* have we still something to write, or lost everything? */
1771   if (nStreams <= 0)
1772     goto error;
1773
1774   if (dwInterleave) {
1775     LONG lCurFrame = -dwFileInitialFrames;
1776
1777     /* interleaved file */
1778     if (dwInterleave == 1)
1779       AVIFileEndRecord(pfile);
1780
1781     for (; lCurFrame < lFileLength; lCurFrame += lSampleInc) {
1782       for (curStream = 0; curStream < nStreams; curStream++) {
1783         LONG lLastSample;
1784
1785         hres = AVIStreamInfoW(pOutStreams[curStream], &sInfo, sizeof(sInfo));
1786         if (FAILED(hres))
1787           goto error;
1788
1789         /* initial frames phase at the end for this stream? */
1790         if (-(LONG)sInfo.dwInitialFrames > lCurFrame)
1791           continue;
1792
1793         if ((lFileLength - lSampleInc) <= lCurFrame) {
1794           lLastSample = AVIStreamLength(pInStreams[curStream]);
1795           lFirstVideo = lLastSample + AVIStreamStart(pInStreams[curStream]);
1796         } else {
1797           if (curStream != 0) {
1798             lFirstVideo =
1799               AVIStreamSampleToSample(pInStreams[curStream], pInStreams[0],
1800                                       (sInfo.fccType == streamtypeVIDEO ? 
1801                                        (LONG)dwInterleave : lSampleInc) +
1802                                       sInfo.dwInitialFrames + lCurFrame);
1803           } else
1804             lFirstVideo = lSampleInc + (sInfo.dwInitialFrames + lCurFrame);
1805
1806           lLastSample = AVIStreamEnd(pInStreams[curStream]);
1807           if (lLastSample <= lFirstVideo)
1808             lFirstVideo = lLastSample;
1809         }
1810
1811         /* copy needed samples now */
1812         WARN("copy from stream %d samples %d to %d...\n",curStream,
1813               lStart[curStream],lFirstVideo);
1814         while (lFirstVideo > lStart[curStream]) {
1815           DWORD flags = 0;
1816
1817           /* copy format in case it can change */
1818           lBufferSize = cbBuffer;
1819           hres = AVIStreamReadFormat(pInStreams[curStream], lStart[curStream],
1820                                      lpBuffer, &lBufferSize);
1821           if (FAILED(hres))
1822             goto error;
1823           AVIStreamSetFormat(pOutStreams[curStream], lStart[curStream],
1824                              lpBuffer, lBufferSize);
1825
1826           /* try to read data until we got it, or error */
1827           do {
1828             hres = AVIStreamRead(pInStreams[curStream], lStart[curStream],
1829                                  lFirstVideo - lStart[curStream], lpBuffer,
1830                                  cbBuffer, &lReadBytes, &lReadSamples);
1831           } while ((hres == AVIERR_BUFFERTOOSMALL) &&
1832                    (lpBuffer = HeapReAlloc(GetProcessHeap(), 0, lpBuffer, cbBuffer *= 2)) != NULL);
1833           if (lpBuffer == NULL)
1834             hres = AVIERR_MEMORY;
1835           if (FAILED(hres))
1836             goto error;
1837
1838           if (AVIStreamIsKeyFrame(pInStreams[curStream], (LONG)sInfo.dwStart))
1839             flags = AVIIF_KEYFRAME;
1840           hres = AVIStreamWrite(pOutStreams[curStream], -1, lReadSamples,
1841                                 lpBuffer, lReadBytes, flags, NULL, NULL);
1842           if (FAILED(hres))
1843             goto error;
1844
1845           lStart[curStream] += lReadSamples;
1846         }
1847         lStart[curStream] = lFirstVideo;
1848       } /* stream by stream */
1849
1850       /* need to close this block? */
1851       if (dwInterleave == 1) {
1852         hres = AVIFileEndRecord(pfile);
1853         if (FAILED(hres))
1854           break;
1855       }
1856
1857       /* show progress */
1858       if (lpfnCallback(MulDiv(dwFileInitialFrames + lCurFrame, 100,
1859                               dwFileInitialFrames + lFileLength))) {
1860         hres = AVIERR_USERABORT;
1861         break;
1862       }
1863     } /* copy frame by frame */
1864   } else {
1865     /* non-interleaved file */
1866
1867     for (curStream = 0; curStream < nStreams; curStream++) {
1868       /* show progress */
1869       if (lpfnCallback(MulDiv(curStream, 100, nStreams))) {
1870         hres = AVIERR_USERABORT;
1871         goto error;
1872       }
1873
1874       AVIStreamInfoW(pInStreams[curStream], &sInfo, sizeof(sInfo));
1875
1876       if (sInfo.dwSampleSize != 0) {
1877         /* sample-based data like audio */
1878         while (sInfo.dwStart < sInfo.dwLength) {
1879           LONG lSamples = cbBuffer / sInfo.dwSampleSize;
1880
1881           /* copy format in case it can change */
1882           lBufferSize = cbBuffer;
1883           hres = AVIStreamReadFormat(pInStreams[curStream], sInfo.dwStart,
1884                                      lpBuffer, &lBufferSize);
1885           if (FAILED(hres))
1886             goto error;
1887           AVIStreamSetFormat(pOutStreams[curStream], sInfo.dwStart,
1888                              lpBuffer, lBufferSize);
1889
1890           /* limit to stream boundaries */
1891           if (lSamples != (LONG)(sInfo.dwLength - sInfo.dwStart))
1892             lSamples = sInfo.dwLength - sInfo.dwStart;
1893
1894           /* now try to read until we get it, or an error occurs */
1895           do {
1896             lReadBytes   = cbBuffer;
1897             lReadSamples = 0;
1898             hres = AVIStreamRead(pInStreams[curStream],sInfo.dwStart,lSamples,
1899                                  lpBuffer,cbBuffer,&lReadBytes,&lReadSamples);
1900           } while ((hres == AVIERR_BUFFERTOOSMALL) &&
1901                    (lpBuffer = HeapReAlloc(GetProcessHeap(), 0, lpBuffer, cbBuffer *= 2)) != NULL);
1902           if (lpBuffer == NULL)
1903             hres = AVIERR_MEMORY;
1904           if (FAILED(hres))
1905             goto error;
1906           if (lReadSamples != 0) {
1907             sInfo.dwStart += lReadSamples;
1908             hres = AVIStreamWrite(pOutStreams[curStream], -1, lReadSamples,
1909                                   lpBuffer, lReadBytes, 0, NULL , NULL);
1910             if (FAILED(hres))
1911               goto error;
1912
1913             /* show progress */
1914             if (lpfnCallback(MulDiv(sInfo.dwStart,100,nStreams*sInfo.dwLength)+
1915                              MulDiv(curStream, 100, nStreams))) {
1916               hres = AVIERR_USERABORT;
1917               goto error;
1918             }
1919           } else {
1920             if ((sInfo.dwLength - sInfo.dwStart) != 1) {
1921               hres = AVIERR_FILEREAD;
1922               goto error;
1923             }
1924           }
1925         }
1926       } else {
1927         /* block-based data like video */
1928         for (; sInfo.dwStart < sInfo.dwLength; sInfo.dwStart++) {
1929           DWORD flags = 0;
1930
1931           /* copy format in case it can change */
1932           lBufferSize = cbBuffer;
1933           hres = AVIStreamReadFormat(pInStreams[curStream], sInfo.dwStart,
1934                                      lpBuffer, &lBufferSize);
1935           if (FAILED(hres))
1936             goto error;
1937           AVIStreamSetFormat(pOutStreams[curStream], sInfo.dwStart,
1938                              lpBuffer, lBufferSize);
1939
1940           /* try to read block and resize buffer if necessary */
1941           do {
1942             lReadSamples = 0;
1943             lReadBytes   = cbBuffer;
1944             hres = AVIStreamRead(pInStreams[curStream], sInfo.dwStart, 1,
1945                                  lpBuffer, cbBuffer,&lReadBytes,&lReadSamples);
1946           } while ((hres == AVIERR_BUFFERTOOSMALL) &&
1947                    (lpBuffer = HeapReAlloc(GetProcessHeap(), 0, lpBuffer, cbBuffer *= 2)) != NULL);
1948           if (lpBuffer == NULL)
1949             hres = AVIERR_MEMORY;
1950           if (FAILED(hres))
1951             goto error;
1952           if (lReadSamples != 1) {
1953             hres = AVIERR_FILEREAD;
1954             goto error;
1955           }
1956
1957           if (AVIStreamIsKeyFrame(pInStreams[curStream], (LONG)sInfo.dwStart))
1958             flags = AVIIF_KEYFRAME;
1959           hres = AVIStreamWrite(pOutStreams[curStream], -1, lReadSamples,
1960                                 lpBuffer, lReadBytes, flags, NULL, NULL);
1961           if (FAILED(hres))
1962             goto error;
1963
1964           /* show progress */
1965           if (lpfnCallback(MulDiv(sInfo.dwStart,100,nStreams*sInfo.dwLength)+
1966                            MulDiv(curStream, 100, nStreams))) {
1967             hres = AVIERR_USERABORT;
1968             goto error;
1969           }
1970         } /* copy all blocks */
1971       }
1972     } /* copy data stream by stream */
1973   }
1974
1975  error:
1976   HeapFree(GetProcessHeap(), 0, lpBuffer);
1977   if (pfile != NULL) {
1978     for (curStream = 0; curStream < nStreams; curStream++) {
1979       if (pOutStreams[curStream] != NULL)
1980         AVIStreamRelease(pOutStreams[curStream]);
1981       if (pInStreams[curStream] != NULL)
1982         AVIStreamRelease(pInStreams[curStream]);
1983     }
1984
1985     AVIFileRelease(pfile);
1986   }
1987
1988   return hres;
1989 }
1990
1991 /***********************************************************************
1992  *              CreateEditableStream    (AVIFIL32.@)
1993  */
1994 HRESULT WINAPI CreateEditableStream(PAVISTREAM *ppEditable, PAVISTREAM pSource)
1995 {
1996   IAVIEditStream *pEdit = NULL;
1997   HRESULT         hr;
1998
1999   TRACE("(%p,%p)\n", ppEditable, pSource);
2000
2001   if (ppEditable == NULL)
2002     return AVIERR_BADPARAM;
2003
2004   *ppEditable = NULL;
2005
2006   if (pSource != NULL) {
2007     hr = IAVIStream_QueryInterface(pSource, &IID_IAVIEditStream,
2008                                    (LPVOID*)&pEdit);
2009     if (SUCCEEDED(hr) && pEdit != NULL) {
2010       hr = IAVIEditStream_Clone(pEdit, ppEditable);
2011       IAVIEditStream_Release(pEdit);
2012
2013       return hr;
2014     }
2015   }
2016
2017   /* need own implementation of IAVIEditStream */
2018   pEdit = AVIFILE_CreateEditStream(pSource);
2019   if (pEdit == NULL)
2020     return AVIERR_MEMORY;
2021
2022   hr = IAVIEditStream_QueryInterface(pEdit, &IID_IAVIStream,
2023                                      (LPVOID*)ppEditable);
2024   IAVIEditStream_Release(pEdit);
2025
2026   return hr;
2027 }
2028
2029 /***********************************************************************
2030  *              EditStreamClone         (AVIFIL32.@)
2031  */
2032 HRESULT WINAPI EditStreamClone(PAVISTREAM pStream, PAVISTREAM *ppResult)
2033 {
2034   PAVIEDITSTREAM pEdit = NULL;
2035   HRESULT        hr;
2036
2037   TRACE("(%p,%p)\n", pStream, ppResult);
2038
2039   if (pStream == NULL)
2040     return AVIERR_BADHANDLE;
2041   if (ppResult == NULL)
2042     return AVIERR_BADPARAM;
2043
2044   *ppResult = NULL;
2045
2046   hr = IAVIStream_QueryInterface(pStream, &IID_IAVIEditStream,(LPVOID*)&pEdit);
2047   if (SUCCEEDED(hr) && pEdit != NULL) {
2048     hr = IAVIEditStream_Clone(pEdit, ppResult);
2049
2050     IAVIEditStream_Release(pEdit);
2051   } else
2052     hr = AVIERR_UNSUPPORTED;
2053
2054   return hr;
2055 }
2056
2057 /***********************************************************************
2058  *              EditStreamCopy          (AVIFIL32.@)
2059  */
2060 HRESULT WINAPI EditStreamCopy(PAVISTREAM pStream, LONG *plStart,
2061                               LONG *plLength, PAVISTREAM *ppResult)
2062 {
2063   PAVIEDITSTREAM pEdit = NULL;
2064   HRESULT        hr;
2065
2066   TRACE("(%p,%p,%p,%p)\n", pStream, plStart, plLength, ppResult);
2067
2068   if (pStream == NULL)
2069     return AVIERR_BADHANDLE;
2070   if (plStart == NULL || plLength == NULL || ppResult == NULL)
2071     return AVIERR_BADPARAM;
2072
2073   *ppResult = NULL;
2074
2075   hr = IAVIStream_QueryInterface(pStream, &IID_IAVIEditStream,(LPVOID*)&pEdit);
2076   if (SUCCEEDED(hr) && pEdit != NULL) {
2077     hr = IAVIEditStream_Copy(pEdit, plStart, plLength, ppResult);
2078
2079     IAVIEditStream_Release(pEdit);
2080   } else
2081     hr = AVIERR_UNSUPPORTED;
2082
2083   return hr;
2084 }
2085
2086 /***********************************************************************
2087  *              EditStreamCut           (AVIFIL32.@)
2088  */
2089 HRESULT WINAPI EditStreamCut(PAVISTREAM pStream, LONG *plStart,
2090                              LONG *plLength, PAVISTREAM *ppResult)
2091 {
2092   PAVIEDITSTREAM pEdit = NULL;
2093   HRESULT        hr;
2094
2095   TRACE("(%p,%p,%p,%p)\n", pStream, plStart, plLength, ppResult);
2096
2097   if (ppResult != NULL)
2098     *ppResult = NULL;
2099   if (pStream == NULL)
2100     return AVIERR_BADHANDLE;
2101   if (plStart == NULL || plLength == NULL)
2102     return AVIERR_BADPARAM;
2103
2104   hr = IAVIStream_QueryInterface(pStream, &IID_IAVIEditStream,(LPVOID*)&pEdit);
2105   if (SUCCEEDED(hr) && pEdit != NULL) {
2106     hr = IAVIEditStream_Cut(pEdit, plStart, plLength, ppResult);
2107
2108     IAVIEditStream_Release(pEdit);
2109   } else
2110     hr = AVIERR_UNSUPPORTED;
2111
2112   return hr;
2113 }
2114
2115 /***********************************************************************
2116  *              EditStreamPaste         (AVIFIL32.@)
2117  */
2118 HRESULT WINAPI EditStreamPaste(PAVISTREAM pDest, LONG *plStart, LONG *plLength,
2119                                PAVISTREAM pSource, LONG lStart, LONG lEnd)
2120 {
2121   PAVIEDITSTREAM pEdit = NULL;
2122   HRESULT        hr;
2123
2124   TRACE("(%p,%p,%p,%p,%d,%d)\n", pDest, plStart, plLength,
2125         pSource, lStart, lEnd);
2126
2127   if (pDest == NULL || pSource == NULL)
2128     return AVIERR_BADHANDLE;
2129   if (plStart == NULL || plLength == NULL || lStart < 0)
2130     return AVIERR_BADPARAM;
2131
2132   hr = IAVIStream_QueryInterface(pDest, &IID_IAVIEditStream,(LPVOID*)&pEdit);
2133   if (SUCCEEDED(hr) && pEdit != NULL) {
2134     hr = IAVIEditStream_Paste(pEdit, plStart, plLength, pSource, lStart, lEnd);
2135
2136     IAVIEditStream_Release(pEdit);
2137   } else
2138     hr = AVIERR_UNSUPPORTED;
2139
2140   return hr;
2141 }
2142
2143 /***********************************************************************
2144  *              EditStreamSetInfoA      (AVIFIL32.@)
2145  */
2146 HRESULT WINAPI EditStreamSetInfoA(PAVISTREAM pstream, LPAVISTREAMINFOA asi,
2147                                   LONG size)
2148 {
2149   AVISTREAMINFOW asiw;
2150
2151   TRACE("(%p,%p,%d)\n", pstream, asi, size);
2152
2153   if (pstream == NULL)
2154     return AVIERR_BADHANDLE;
2155   if ((DWORD)size < sizeof(AVISTREAMINFOA))
2156     return AVIERR_BADSIZE;
2157
2158   memcpy(&asiw, asi, sizeof(asiw) - sizeof(asiw.szName));
2159   MultiByteToWideChar(CP_ACP, 0, asi->szName, -1,
2160                       asiw.szName, sizeof(asiw.szName)/sizeof(WCHAR));
2161
2162   return EditStreamSetInfoW(pstream, &asiw, sizeof(asiw));
2163 }
2164
2165 /***********************************************************************
2166  *              EditStreamSetInfoW      (AVIFIL32.@)
2167  */
2168 HRESULT WINAPI EditStreamSetInfoW(PAVISTREAM pstream, LPAVISTREAMINFOW asi,
2169                                   LONG size)
2170 {
2171   PAVIEDITSTREAM pEdit = NULL;
2172   HRESULT        hr;
2173
2174   TRACE("(%p,%p,%d)\n", pstream, asi, size);
2175
2176   hr = IAVIStream_QueryInterface(pstream, &IID_IAVIEditStream,(LPVOID*)&pEdit);
2177   if (SUCCEEDED(hr) && pEdit != NULL) {
2178     hr = IAVIEditStream_SetInfo(pEdit, asi, size);
2179
2180     IAVIEditStream_Release(pEdit);
2181   } else
2182     hr = AVIERR_UNSUPPORTED;
2183
2184   return hr;
2185 }
2186
2187 /***********************************************************************
2188  *              EditStreamSetNameA      (AVIFIL32.@)
2189  */
2190 HRESULT WINAPI EditStreamSetNameA(PAVISTREAM pstream, LPCSTR szName)
2191 {
2192   AVISTREAMINFOA asia;
2193   HRESULT        hres;
2194
2195   TRACE("(%p,%s)\n", pstream, debugstr_a(szName));
2196
2197   if (pstream == NULL)
2198     return AVIERR_BADHANDLE;
2199   if (szName == NULL)
2200     return AVIERR_BADPARAM;
2201
2202   hres = AVIStreamInfoA(pstream, &asia, sizeof(asia));
2203   if (FAILED(hres))
2204     return hres;
2205
2206   memset(asia.szName, 0, sizeof(asia.szName));
2207   lstrcpynA(asia.szName, szName, sizeof(asia.szName)/sizeof(asia.szName[0]));
2208
2209   return EditStreamSetInfoA(pstream, &asia, sizeof(asia));
2210 }
2211
2212 /***********************************************************************
2213  *              EditStreamSetNameW      (AVIFIL32.@)
2214  */
2215 HRESULT WINAPI EditStreamSetNameW(PAVISTREAM pstream, LPCWSTR szName)
2216 {
2217   AVISTREAMINFOW asiw;
2218   HRESULT        hres;
2219
2220   TRACE("(%p,%s)\n", pstream, debugstr_w(szName));
2221
2222   if (pstream == NULL)
2223     return AVIERR_BADHANDLE;
2224   if (szName == NULL)
2225     return AVIERR_BADPARAM;
2226
2227   hres = IAVIStream_Info(pstream, &asiw, sizeof(asiw));
2228   if (FAILED(hres))
2229     return hres;
2230
2231   memset(asiw.szName, 0, sizeof(asiw.szName));
2232   lstrcpynW(asiw.szName, szName, sizeof(asiw.szName)/sizeof(asiw.szName[0]));
2233
2234   return EditStreamSetInfoW(pstream, &asiw, sizeof(asiw));
2235 }
2236
2237 /***********************************************************************
2238  *              AVIClearClipboard       (AVIFIL32.@)
2239  */
2240 HRESULT WINAPI AVIClearClipboard(void)
2241 {
2242   TRACE("()\n");
2243
2244   return AVIERR_UNSUPPORTED; /* OleSetClipboard(NULL); */
2245 }
2246
2247 /***********************************************************************
2248  *              AVIGetFromClipboard     (AVIFIL32.@)
2249  */
2250 HRESULT WINAPI AVIGetFromClipboard(PAVIFILE *ppfile)
2251 {
2252   FIXME("(%p), stub!\n", ppfile);
2253
2254   *ppfile = NULL;
2255
2256   return AVIERR_UNSUPPORTED;
2257 }
2258
2259 /***********************************************************************
2260  *      AVIMakeStreamFromClipboard (AVIFIL32.@)
2261  */
2262 HRESULT WINAPI AVIMakeStreamFromClipboard(UINT cfFormat, HANDLE hGlobal,
2263                                           PAVISTREAM * ppstream)
2264 {
2265   FIXME("(0x%08x,%p,%p), stub!\n", cfFormat, hGlobal, ppstream);
2266
2267   if (ppstream == NULL)
2268     return AVIERR_BADHANDLE;
2269
2270   return AVIERR_UNSUPPORTED;
2271 }
2272
2273 /***********************************************************************
2274  *              AVIPutFileOnClipboard   (AVIFIL32.@)
2275  */
2276 HRESULT WINAPI AVIPutFileOnClipboard(PAVIFILE pfile)
2277 {
2278   FIXME("(%p), stub!\n", pfile);
2279
2280   if (pfile == NULL)
2281     return AVIERR_BADHANDLE;
2282
2283   return AVIERR_UNSUPPORTED;
2284 }
2285
2286 HRESULT WINAPIV AVISaveA(LPCSTR szFile, CLSID * pclsidHandler, AVISAVECALLBACK lpfnCallback,
2287                         int nStreams, PAVISTREAM pavi, LPAVICOMPRESSOPTIONS lpOptions, ...)
2288 {
2289     FIXME("(%s,%p,%p,0x%08x,%p,%p), stub!\n", debugstr_a(szFile), pclsidHandler, lpfnCallback,
2290           nStreams, pavi, lpOptions);
2291
2292     return AVIERR_UNSUPPORTED;
2293 }
2294
2295 HRESULT WINAPIV AVISaveW(LPCWSTR szFile, CLSID * pclsidHandler, AVISAVECALLBACK lpfnCallback,
2296                         int nStreams, PAVISTREAM pavi, LPAVICOMPRESSOPTIONS lpOptions, ...)
2297 {
2298     FIXME("(%s,%p,%p,0x%08x,%p,%p), stub!\n", debugstr_w(szFile), pclsidHandler, lpfnCallback,
2299           nStreams, pavi, lpOptions);
2300
2301     return AVIERR_UNSUPPORTED;
2302 }