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