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