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