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