Don't touch the This pointer after it has been freed.
[wine] / dlls / avifil32 / api.c
1 /*
2  * Copyright 1999 Marcus Meissner
3  * Copyright 2002 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
22 #include "winbase.h"
23 #include "winnls.h"
24 #include "winuser.h"
25 #include "winreg.h"
26 #include "winerror.h"
27 #include "windowsx.h"
28
29 #include "ole2.h"
30 #include "shellapi.h"
31 #include "vfw.h"
32 #include "msacm.h"
33
34 #include "avifile_private.h"
35
36 #include "wine/debug.h"
37 #include "wine/unicode.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
40
41 /***********************************************************************
42  * copied from dlls/shell32/undocshell.h
43  */
44 HRESULT WINAPI SHCoCreateInstance(LPCSTR lpszClsid,REFCLSID rClsid,
45                                   LPUNKNOWN pUnkOuter,REFIID riid,LPVOID *ppv);
46
47 /***********************************************************************
48  * for AVIBuildFilterW -- uses fixed size table
49  */
50 #define MAX_FILTERS 30 /* 30 => 7kB */
51
52 typedef struct _AVIFilter {
53   WCHAR szClsid[40];
54   WCHAR szExtensions[MAX_FILTERS * 7];
55 } AVIFilter;
56
57 /***********************************************************************
58  * for AVISaveOptions
59  */
60 static struct {
61   UINT                  uFlags;
62   INT                   nStreams;
63   PAVISTREAM           *ppavis;
64   LPAVICOMPRESSOPTIONS *ppOptions;
65   INT                   nCurrent;
66 } SaveOpts;
67
68 /***********************************************************************
69  * copied from dlls/ole32/compobj.c
70  */
71 static HRESULT AVIFILE_CLSIDFromString(LPCSTR idstr, LPCLSID id)
72 {
73   BYTE *s = (BYTE*)idstr;
74   BYTE *p;
75   INT   i;
76   BYTE table[256];
77
78   if (!s) {
79     memset(s, 0, sizeof(CLSID));
80     return S_OK;
81   } else {  /* validate the CLSID string */
82     if (lstrlenA(s) != 38)
83       return CO_E_CLASSSTRING;
84
85     if ((s[0]!='{') || (s[9]!='-') || (s[14]!='-') || (s[19]!='-') ||
86         (s[24]!='-') || (s[37]!='}'))
87       return CO_E_CLASSSTRING;
88
89     for (i = 1; i < 37; i++) {
90       if ((i == 9) || (i == 14) || (i == 19) || (i == 24))
91         continue;
92       if (!(((s[i] >= '0') && (s[i] <= '9'))  ||
93             ((s[i] >= 'a') && (s[i] <= 'f'))  ||
94             ((s[i] >= 'A') && (s[i] <= 'F')))
95           )
96         return CO_E_CLASSSTRING;
97     }
98   }
99
100   TRACE("%s -> %p\n", s, id);
101
102   /* quick lookup table */
103   memset(table, 0, 256);
104
105   for (i = 0; i < 10; i++)
106     table['0' + i] = i;
107
108   for (i = 0; i < 6; i++) {
109     table['A' + i] = i+10;
110     table['a' + i] = i+10;
111   }
112
113   /* in form {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} */
114   p = (BYTE *) id;
115
116   s++;  /* skip leading brace  */
117   for (i = 0; i < 4; i++) {
118     p[3 - i] = table[*s]<<4 | table[*(s+1)];
119     s += 2;
120   }
121   p += 4;
122   s++;  /* skip - */
123
124   for (i = 0; i < 2; i++) {
125     p[1-i] = table[*s]<<4 | table[*(s+1)];
126     s += 2;
127   }
128   p += 2;
129   s++;  /* skip - */
130
131   for (i = 0; i < 2; i++) {
132     p[1-i] = table[*s]<<4 | table[*(s+1)];
133     s += 2;
134   }
135   p += 2;
136   s++;  /* skip - */
137
138   /* these are just sequential bytes */
139   for (i = 0; i < 2; i++) {
140     *p++ = table[*s]<<4 | table[*(s+1)];
141     s += 2;
142   }
143   s++;  /* skip - */
144
145   for (i = 0; i < 6; i++) {
146     *p++ = table[*s]<<4 | table[*(s+1)];
147     s += 2;
148   }
149
150   return S_OK;
151 }
152
153 static BOOL AVIFILE_GetFileHandlerByExtension(LPCWSTR szFile, LPCLSID lpclsid)
154 {
155   CHAR   szRegKey[25];
156   CHAR   szValue[100];
157   LPWSTR szExt = strrchrW(szFile, '.');
158   LONG   len = sizeof(szValue) / sizeof(szValue[0]);
159
160   if (szExt == NULL)
161     return FALSE;
162
163   szExt++;
164
165   wsprintfA(szRegKey, "AVIFile\\Extensions\\%.3ls", szExt);
166   if (RegQueryValueA(HKEY_CLASSES_ROOT, szRegKey, szValue, &len) != ERROR_SUCCESS)
167     return FALSE;
168
169   return (AVIFILE_CLSIDFromString(szValue, lpclsid) == S_OK);
170 }
171
172 /***********************************************************************
173  *              AVIFileInit             (AVIFIL32.@)
174  *              AVIFileInit             (AVIFILE.100)
175  */
176 void WINAPI AVIFileInit(void) {
177   /* need to load ole32.dll if not already done and get some functions */
178   FIXME("(): stub!\n");
179 }
180
181 /***********************************************************************
182  *              AVIFileExit             (AVIFIL32.@)
183  *              AVIFileExit             (AVIFILE.101)
184  */
185 void WINAPI AVIFileExit(void) {
186   /* need to free ole32.dll if we are the last exit call */
187   FIXME("(): stub!\n");
188 }
189
190 /***********************************************************************
191  *              AVIFileOpenA            (AVIFIL32.@)
192  *              AVIFileOpenA            (AVIFILE.102)
193  */
194 HRESULT WINAPI AVIFileOpenA(PAVIFILE *ppfile, LPCSTR szFile, UINT uMode,
195                             LPCLSID lpHandler)
196 {
197   LPWSTR  wszFile = NULL;
198   HRESULT hr;
199   int     len;
200
201   TRACE("(%p,%s,0x%08X,%s)\n", ppfile, debugstr_a(szFile), uMode,
202         debugstr_guid(lpHandler));
203
204   /* check parameters */
205   if (ppfile == NULL || szFile == NULL)
206     return AVIERR_BADPARAM;
207
208   /* convert ASCII string to Unicode and call unicode function */
209   len = lstrlenA(szFile);
210   if (len <= 0)
211     return AVIERR_BADPARAM;
212
213   wszFile = (LPWSTR)LocalAlloc(LPTR, (len + 1) * sizeof(WCHAR));
214   if (wszFile == NULL)
215     return AVIERR_MEMORY;
216
217   MultiByteToWideChar(CP_ACP, 0, szFile, -1, wszFile, len + 1);
218   wszFile[len + 1] = 0;
219
220   hr = AVIFileOpenW(ppfile, wszFile, uMode, lpHandler);
221
222   LocalFree((HLOCAL)wszFile);
223
224   return hr;
225 }
226
227 /***********************************************************************
228  *              AVIFileOpenW            (AVIFIL32.@)
229  */
230 HRESULT WINAPI AVIFileOpenW(PAVIFILE *ppfile, LPCWSTR szFile, UINT uMode,
231                             LPCLSID lpHandler)
232 {
233   IPersistFile *ppersist = NULL;
234   CLSID         clsidHandler;
235   HRESULT       hr;
236
237   TRACE("(%p,%s,0x%X,%s)\n", ppfile, debugstr_w(szFile), uMode,
238         debugstr_guid(lpHandler));
239
240   /* check parameters */
241   if (ppfile == NULL || szFile == NULL)
242     return AVIERR_BADPARAM;
243
244   *ppfile = NULL;
245
246   /* if no handler then try guessing it by extension */
247   if (lpHandler == NULL) {
248     if (! AVIFILE_GetFileHandlerByExtension(szFile, &clsidHandler))
249       return AVIERR_UNSUPPORTED;
250   } else
251     memcpy(&clsidHandler, lpHandler, sizeof(clsidHandler));
252
253   /* crete instance of handler */
254   hr = SHCoCreateInstance(NULL, &clsidHandler, NULL,
255                           &IID_IAVIFile, (LPVOID*)ppfile);
256   if (FAILED(hr) || *ppfile == NULL)
257     return hr;
258
259   /* ask for IPersistFile interface for loading/creating the file */
260   hr = IAVIFile_QueryInterface(*ppfile, &IID_IPersistFile, (LPVOID*)&ppersist);
261   if (FAILED(hr) || ppersist == NULL) {
262     IAVIFile_Release(*ppfile);
263     *ppfile = NULL;
264     return hr;
265   }
266
267   hr = IPersistFile_Load(ppersist, szFile, uMode);
268   IPersistFile_Release(ppersist);
269   if (FAILED(hr)) {
270     IAVIFile_Release(*ppfile);
271     *ppfile = NULL;
272   }
273
274   return hr;
275 }
276
277 /***********************************************************************
278  *              AVIFileAddRef           (AVIFIL32.@)
279  *              AVIFileAddRef           (AVIFILE.140)
280  */
281 ULONG WINAPI AVIFileAddRef(PAVIFILE pfile)
282 {
283   TRACE("(%p)\n", pfile);
284
285   if (pfile == NULL) {
286     ERR(": bad handle passed!\n");
287     return 0;
288   }
289
290   return IAVIFile_AddRef(pfile);
291 }
292
293 /***********************************************************************
294  *              AVIFileRelease          (AVIFIL32.@)
295  *              AVIFileRelease          (AVIFILE.141)
296  */
297 ULONG WINAPI AVIFileRelease(PAVIFILE pfile)
298 {
299   TRACE("(%p)\n", pfile);
300
301   if (pfile == NULL) {
302     ERR(": bad handle passed!\n");
303     return 0;
304   }
305
306   return IAVIFile_Release(pfile);
307 }
308
309 /***********************************************************************
310  *              AVIFileInfoA            (AVIFIL32.@)
311  */
312 HRESULT WINAPI AVIFileInfoA(PAVIFILE pfile, LPAVIFILEINFOA afi, LONG size)
313 {
314   AVIFILEINFOW afiw;
315   HRESULT      hres;
316
317   TRACE("(%p,%p,%ld)\n", pfile, afi, size);
318
319   if (pfile == NULL)
320     return AVIERR_BADHANDLE;
321   if (size < sizeof(AVIFILEINFOA))
322     return AVIERR_BADSIZE;
323
324   hres = IAVIFile_Info(pfile, &afiw, sizeof(afiw));
325
326   memcpy(afi, &afiw, sizeof(*afi) - sizeof(afi->szFileType));
327   WideCharToMultiByte(CP_ACP, 0, afiw.szFileType, -1, afi->szFileType,
328                       sizeof(afi->szFileType), NULL, NULL);
329   afi->szFileType[sizeof(afi->szFileType) - 1] = 0;
330
331   return hres;
332 }
333
334 /***********************************************************************
335  *              AVIFileInfoW            (AVIFIL32.@)
336  */
337 HRESULT WINAPI AVIFileInfoW(PAVIFILE pfile, LPAVIFILEINFOW afiw, LONG size)
338 {
339   TRACE("(%p,%p,%ld)\n", pfile, afiw, size);
340
341   if (pfile == NULL)
342     return AVIERR_BADHANDLE;
343
344   return IAVIFile_Info(pfile, afiw, size);
345 }
346
347 /***********************************************************************
348  *              AVIFileGetStream        (AVIFIL32.@)
349  *              AVIFileGetStream        (AVIFILE.143)
350  */
351 HRESULT WINAPI AVIFileGetStream(PAVIFILE pfile, PAVISTREAM *avis,
352                                 DWORD fccType, LONG lParam)
353 {
354   TRACE("(%p,%p,'%4.4s',%ld)\n", pfile, avis, (char*)&fccType, lParam);
355
356   if (pfile == NULL)
357     return AVIERR_BADHANDLE;
358
359   return IAVIFile_GetStream(pfile, avis, fccType, lParam);
360 }
361
362 /***********************************************************************
363  *              AVIFileCreateStreamA    (AVIFIL32.@)
364  */
365 HRESULT WINAPI AVIFileCreateStreamA(PAVIFILE pfile, PAVISTREAM *ppavi,
366                                     LPAVISTREAMINFOA psi)
367 {
368   AVISTREAMINFOW        psiw;
369
370   TRACE("(%p,%p,%p)\n", pfile, ppavi, psi);
371
372   if (pfile == NULL)
373     return AVIERR_BADHANDLE;
374
375   /* Only the szName at the end is different */
376   memcpy(&psiw, psi, sizeof(*psi) - sizeof(psi->szName));
377   MultiByteToWideChar(CP_ACP, 0, psi->szName, -1, psiw.szName,
378                       sizeof(psiw.szName) / sizeof(psiw.szName[0]));
379
380   return IAVIFile_CreateStream(pfile, ppavi, &psiw);
381 }
382
383 /***********************************************************************
384  *              AVIFileCreateStreamW    (AVIFIL32.@)
385  */
386 HRESULT WINAPI AVIFileCreateStreamW(PAVIFILE pfile, PAVISTREAM *avis,
387                                     LPAVISTREAMINFOW asi)
388 {
389   TRACE("(%p,%p,%p)\n", pfile, avis, asi);
390
391   return IAVIFile_CreateStream(pfile, avis, asi);
392 }
393
394 /***********************************************************************
395  *              AVIFileWriteData        (AVIFIL32.@)
396  */
397 HRESULT WINAPI AVIFileWriteData(PAVIFILE pfile,DWORD fcc,LPVOID lp,LONG size)
398 {
399   TRACE("(%p,'%4.4s',%p,%ld)\n", pfile, (char*)&fcc, lp, size);
400
401   if (pfile == NULL)
402     return AVIERR_BADHANDLE;
403
404   return IAVIFile_WriteData(pfile, fcc, lp, size);
405 }
406
407 /***********************************************************************
408  *              AVIFileReadData         (AVIFIL32.@)
409  */
410 HRESULT WINAPI AVIFileReadData(PAVIFILE pfile,DWORD fcc,LPVOID lp,LPLONG size)
411 {
412   TRACE("(%p,'%4.4s',%p,%p)\n", pfile, (char*)&fcc, lp, size);
413
414   if (pfile == NULL)
415     return AVIERR_BADHANDLE;
416
417   return IAVIFile_ReadData(pfile, fcc, lp, size);
418 }
419
420 /***********************************************************************
421  *              AVIFileEndRecord        (AVIFIL32.@)
422  */
423 HRESULT WINAPI AVIFileEndRecord(PAVIFILE pfile)
424 {
425   TRACE("(%p)\n", pfile);
426
427   if (pfile == NULL)
428     return AVIERR_BADHANDLE;
429
430   return IAVIFile_EndRecord(pfile);
431 }
432
433 /***********************************************************************
434  *              AVIStreamAddRef         (AVIFIL32.@)
435  */
436 ULONG WINAPI AVIStreamAddRef(PAVISTREAM pstream)
437 {
438   TRACE("(%p)\n", pstream);
439
440   if (pstream == NULL) {
441     ERR(": bad handle passed!\n");
442     return 0;
443   }
444
445   return IAVIStream_AddRef(pstream);
446 }
447
448 /***********************************************************************
449  *              AVIStreamRelease        (AVIFIL32.@)
450  */
451 ULONG WINAPI AVIStreamRelease(PAVISTREAM pstream)
452 {
453   TRACE("(%p)\n", pstream);
454
455   if (pstream == NULL) {
456     ERR(": bad handle passed!\n");
457     return 0;
458   }
459
460   return IAVIStream_Release(pstream);
461 }
462
463 HRESULT WINAPI AVIStreamCreate(PAVISTREAM *ppavi, LONG lParam1, LONG lParam2,
464                                LPCLSID pclsidHandler)
465 {
466   HRESULT hr;
467
468   TRACE("(%p,0x%08lX,0x%08lX,%s)\n", ppavi, lParam1, lParam2,
469         debugstr_guid(pclsidHandler));
470
471   if (ppavi == NULL)
472     return AVIERR_BADPARAM;
473
474   *ppavi = NULL;
475   if (pclsidHandler == NULL)
476     return AVIERR_UNSUPPORTED;
477
478   hr = SHCoCreateInstance(NULL, pclsidHandler, NULL,
479                           &IID_IAVIStream, (LPVOID*)ppavi);
480   if (FAILED(hr) || *ppavi == NULL)
481     return hr;
482
483   hr = IAVIStream_Create(*ppavi, lParam1, lParam2);
484   if (FAILED(hr)) {
485     IAVIStream_Release(*ppavi);
486     *ppavi = NULL;
487   }
488
489   return hr;
490 }
491
492 /***********************************************************************
493  *              AVIStreamInfoA          (AVIFIL32.@)
494  */
495 HRESULT WINAPI AVIStreamInfoA(PAVISTREAM pstream, LPAVISTREAMINFOA asi,
496                               LONG size)
497 {
498   AVISTREAMINFOW asiw;
499   HRESULT        hres;
500
501   TRACE("(%p,%p,%ld)\n", pstream, asi, size);
502
503   if (pstream == NULL)
504     return AVIERR_BADHANDLE;
505   if (size < sizeof(AVISTREAMINFOA))
506     return AVIERR_BADSIZE;
507
508   hres = IAVIStream_Info(pstream, &asiw, sizeof(asiw));
509
510   memcpy(asi, &asiw, sizeof(asiw) - sizeof(asiw.szName));
511   WideCharToMultiByte(CP_ACP, 0, asiw.szName, -1, asi->szName,
512                       sizeof(asi->szName), NULL, NULL);
513   asi->szName[sizeof(asi->szName) - 1] = 0;
514
515   return hres;
516 }
517
518 /***********************************************************************
519  *              AVIStreamInfoW          (AVIFIL32.@)
520  */
521 HRESULT WINAPI AVIStreamInfoW(PAVISTREAM pstream, LPAVISTREAMINFOW asi,
522                               LONG size)
523 {
524   TRACE("(%p,%p,%ld)\n", pstream, asi, size);
525
526   if (pstream == NULL)
527     return AVIERR_BADHANDLE;
528
529   return IAVIStream_Info(pstream, asi, size);
530 }
531
532 /***********************************************************************
533  *              AVIStreamFindSample     (AVIFIL32.@)
534  */
535 HRESULT WINAPI AVIStreamFindSample(PAVISTREAM pstream, LONG pos, DWORD flags)
536 {
537   TRACE("(%p,%ld,0x%lX)\n", pstream, pos, flags);
538
539   if (pstream == NULL)
540     return -1;
541
542   return IAVIStream_FindSample(pstream, pos, flags);
543 }
544
545 /***********************************************************************
546  *              AVIStreamReadFormat     (AVIFIL32.@)
547  */
548 HRESULT WINAPI AVIStreamReadFormat(PAVISTREAM pstream, LONG pos,
549                                    LPVOID format, LPLONG formatsize)
550 {
551   TRACE("(%p,%ld,%p,%p)\n", pstream, pos, format, formatsize);
552
553   if (pstream == NULL)
554     return AVIERR_BADHANDLE;
555
556   return IAVIStream_ReadFormat(pstream, pos, format, formatsize);
557 }
558
559 /***********************************************************************
560  *              AVIStreamSetFormat      (AVIFIL32.@)
561  */
562 HRESULT WINAPI AVIStreamSetFormat(PAVISTREAM pstream, LONG pos,
563                                   LPVOID format, LONG formatsize)
564 {
565   TRACE("(%p,%ld,%p,%ld)\n", pstream, pos, format, formatsize);
566
567   if (pstream == NULL)
568     return AVIERR_BADHANDLE;
569
570   return IAVIStream_SetFormat(pstream, pos, format, formatsize);
571 }
572
573 /***********************************************************************
574  *              AVIStreamRead           (AVIFIL32.@)
575  */
576 HRESULT WINAPI AVIStreamRead(PAVISTREAM pstream, LONG start, LONG samples,
577                              LPVOID buffer, LONG buffersize,
578                              LPLONG bytesread, LPLONG samplesread)
579 {
580   TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", pstream, start, samples, buffer,
581         buffersize, bytesread, samplesread);
582
583   if (pstream == NULL)
584     return AVIERR_BADHANDLE;
585
586   return IAVIStream_Read(pstream, start, samples, buffer, buffersize,
587                          bytesread, samplesread);
588 }
589
590 /***********************************************************************
591  *              AVIStreamWrite          (AVIFIL32.@)
592  */
593 HRESULT WINAPI AVIStreamWrite(PAVISTREAM pstream, LONG start, LONG samples,
594                               LPVOID buffer, LONG buffersize, DWORD flags,
595                               LPLONG sampwritten, LPLONG byteswritten)
596 {
597   TRACE("(%p,%ld,%ld,%p,%ld,0x%lX,%p,%p)\n", pstream, start, samples, buffer,
598         buffersize, flags, sampwritten, byteswritten);
599
600   if (pstream == NULL)
601     return AVIERR_BADHANDLE;
602
603   return IAVIStream_Write(pstream, start, samples, buffer, buffersize,
604                           flags, sampwritten, byteswritten);
605 }
606
607 /***********************************************************************
608  *              AVIStreamReadData       (AVIFIL32.@)
609  */
610 HRESULT WINAPI AVIStreamReadData(PAVISTREAM pstream, DWORD fcc, LPVOID lp,
611                                  LPLONG lpread)
612 {
613   TRACE("(%p,'%4.4s',%p,%p)\n", pstream, (char*)&fcc, lp, lpread);
614
615   if (pstream == NULL)
616     return AVIERR_BADHANDLE;
617
618   return IAVIStream_ReadData(pstream, fcc, lp, lpread);
619 }
620
621 /***********************************************************************
622  *              AVIStreamWriteData      (AVIFIL32.@)
623  */
624 HRESULT WINAPI AVIStreamWriteData(PAVISTREAM pstream, DWORD fcc, LPVOID lp,
625                                   LONG size)
626 {
627   TRACE("(%p,'%4.4s',%p,%ld)\n", pstream, (char*)&fcc, lp, size);
628
629   if (pstream == NULL)
630     return AVIERR_BADHANDLE;
631
632   return IAVIStream_WriteData(pstream, fcc, lp, size);
633 }
634
635 /***********************************************************************
636  *              AVIStreamGetFrameOpen   (AVIFIL32.@)
637  */
638 PGETFRAME WINAPI AVIStreamGetFrameOpen(PAVISTREAM pstream,
639                                        LPBITMAPINFOHEADER lpbiWanted)
640 {
641   PGETFRAME pg = NULL;
642
643   TRACE("(%p,%p)\n", pstream, lpbiWanted);
644
645   if (FAILED(IAVIStream_QueryInterface(pstream, &IID_IGetFrame, (LPVOID*)&pg)) ||
646       pg == NULL) {
647     pg = AVIFILE_CreateGetFrame(pstream);
648     if (pg == NULL)
649       return NULL;
650   }
651
652   if (FAILED(IGetFrame_SetFormat(pg, lpbiWanted, NULL, 0, 0, -1, -1))) {
653     IGetFrame_Release(pg);
654     return NULL;
655   }
656
657   return pg;
658 }
659
660 /***********************************************************************
661  *              AVIStreamGetFrame       (AVIFIL32.@)
662  */
663 LPVOID WINAPI AVIStreamGetFrame(PGETFRAME pg, LONG pos)
664 {
665   TRACE("(%p,%ld)\n", pg, pos);
666
667   if (pg == NULL)
668     return NULL;
669
670   return IGetFrame_GetFrame(pg, pos);
671 }
672
673 /***********************************************************************
674  *              AVIStreamGetFrameClose (AVIFIL32.@)
675  */
676 HRESULT WINAPI AVIStreamGetFrameClose(PGETFRAME pg)
677 {
678   TRACE("(%p)\n", pg);
679
680   if (pg != NULL)
681     return IGetFrame_Release(pg);
682   return 0;
683 }
684
685 /***********************************************************************
686  *              AVIMakeCompressedStream (AVIFIL32.@)
687  */
688 HRESULT WINAPI AVIMakeCompressedStream(PAVISTREAM *ppsCompressed,
689                                        PAVISTREAM psSource,
690                                        LPAVICOMPRESSOPTIONS aco,
691                                        LPCLSID pclsidHandler)
692 {
693   AVISTREAMINFOW asiw;
694   CHAR           szRegKey[25];
695   CHAR           szValue[100];
696   CLSID          clsidHandler;
697   HRESULT        hr;
698   LONG           size = sizeof(szValue);
699
700   TRACE("(%p,%p,%p,%s)\n", ppsCompressed, psSource, aco,
701         debugstr_guid(pclsidHandler));
702
703   if (ppsCompressed == NULL)
704     return AVIERR_BADPARAM;
705   if (psSource == NULL)
706     return AVIERR_BADHANDLE;
707
708   *ppsCompressed = NULL;
709
710   /* if no handler given get default ones based on streamtype */
711   if (pclsidHandler == NULL) {
712     hr = IAVIStream_Info(psSource, &asiw, sizeof(asiw));
713     if (FAILED(hr))
714       return hr;
715
716     wsprintfA(szRegKey, "AVIFile\\Compressors\\%4.4s", (char*)&asiw.fccType);
717     if (RegQueryValueA(HKEY_CLASSES_ROOT, szRegKey, szValue, &size) != ERROR_SUCCESS)
718       return AVIERR_UNSUPPORTED;
719     if (AVIFILE_CLSIDFromString(szValue, &clsidHandler) != S_OK)
720       return AVIERR_UNSUPPORTED;
721   } else
722     memcpy(&clsidHandler, pclsidHandler, sizeof(clsidHandler));
723
724   hr = SHCoCreateInstance(NULL, &clsidHandler, NULL,
725                           &IID_IAVIStream, (LPVOID*)ppsCompressed);
726   if (FAILED(hr) || *ppsCompressed == NULL)
727     return hr;
728
729   hr = IAVIStream_Create(*ppsCompressed, (LPARAM)psSource, (LPARAM)aco);
730   if (FAILED(hr)) {
731     IAVIStream_Release(*ppsCompressed);
732     *ppsCompressed = NULL;
733   }
734
735   return hr;
736 }
737
738 /***********************************************************************
739  *              AVIStreamOpenFromFileA  (AVIFIL32.@)
740  */
741 HRESULT WINAPI AVIStreamOpenFromFileA(PAVISTREAM *ppavi, LPCSTR szFile,
742                                       DWORD fccType, LONG lParam,
743                                       UINT mode, LPCLSID pclsidHandler)
744 {
745   PAVIFILE pfile = NULL;
746   HRESULT  hr;
747
748   TRACE("(%p,%s,'%4.4s',%ld,0x%X,%s)\n", ppavi, debugstr_a(szFile),
749         (char*)&fccType, lParam, mode, debugstr_guid(pclsidHandler));
750
751   if (ppavi == NULL || szFile == NULL)
752     return AVIERR_BADPARAM;
753
754   *ppavi = NULL;
755
756   hr = AVIFileOpenA(&pfile, szFile, mode, pclsidHandler);
757   if (FAILED(hr) || pfile == NULL)
758     return hr;
759
760   hr = IAVIFile_GetStream(pfile, ppavi, fccType, lParam);
761   IAVIFile_Release(pfile);
762
763   return hr;
764 }
765
766 /***********************************************************************
767  *              AVIStreamOpenFromFileW  (AVIFIL32.@)
768  */
769 HRESULT WINAPI AVIStreamOpenFromFileW(PAVISTREAM *ppavi, LPCWSTR szFile,
770                                       DWORD fccType, LONG lParam,
771                                       UINT mode, LPCLSID pclsidHandler)
772 {
773   PAVIFILE pfile = NULL;
774   HRESULT  hr;
775
776   TRACE("(%p,%s,'%4.4s',%ld,0x%X,%s)\n", ppavi, debugstr_w(szFile),
777         (char*)&fccType, lParam, mode, debugstr_guid(pclsidHandler));
778
779   if (ppavi == NULL || szFile == NULL)
780     return AVIERR_BADPARAM;
781
782   *ppavi = NULL;
783
784   hr = AVIFileOpenW(&pfile, szFile, mode, pclsidHandler);
785   if (FAILED(hr) || pfile == NULL)
786     return hr;
787
788   hr = IAVIFile_GetStream(pfile, ppavi, fccType, lParam);
789   IAVIFile_Release(pfile);
790
791   return hr;
792 }
793
794 /***********************************************************************
795  *              AVIStreamStart          (AVIFIL32.@)
796  */
797 LONG WINAPI AVIStreamStart(PAVISTREAM pstream)
798 {
799   AVISTREAMINFOW asiw;
800
801   TRACE("(%p)\n", pstream);
802
803   if (pstream == NULL)
804     return 0;
805
806   if (FAILED(IAVIStream_Info(pstream, &asiw, sizeof(asiw))))
807     return 0;
808
809   return asiw.dwStart;
810 }
811
812 /***********************************************************************
813  *              AVIStreamLength         (AVIFIL32.@)
814  */
815 LONG WINAPI AVIStreamLength(PAVISTREAM pstream)
816 {
817   AVISTREAMINFOW asiw;
818
819   TRACE("(%p)\n", pstream);
820
821   if (pstream == NULL)
822     return 0;
823
824   if (FAILED(IAVIStream_Info(pstream, &asiw, sizeof(asiw))))
825     return 0;
826
827   return asiw.dwLength;
828 }
829
830 /***********************************************************************
831  *              AVIStreamSampleToTime   (AVIFIL32.@)
832  */
833 LONG WINAPI AVIStreamSampleToTime(PAVISTREAM pstream, LONG lSample)
834 {
835   AVISTREAMINFOW asiw;
836
837   TRACE("(%p,%ld)\n", pstream, lSample);
838
839   if (pstream == NULL)
840     return -1;
841
842   if (FAILED(IAVIStream_Info(pstream, &asiw, sizeof(asiw))))
843     return -1;
844   if (asiw.dwRate == 0)
845     return -1;
846
847   return (LONG)(((float)lSample * asiw.dwScale * 1000.0) / asiw.dwRate);
848 }
849
850 /***********************************************************************
851  *              AVIStreamTimeToSample   (AVIFIL32.@)
852  */
853 LONG WINAPI AVIStreamTimeToSample(PAVISTREAM pstream, LONG lTime)
854 {
855   AVISTREAMINFOW asiw;
856
857   TRACE("(%p,%ld)\n", pstream, lTime);
858
859   if (pstream == NULL)
860     return -1;
861
862   if (FAILED(IAVIStream_Info(pstream, &asiw, sizeof(asiw))))
863     return -1;
864   if (asiw.dwScale == 0)
865     return -1;
866
867   return (LONG)(((float)lTime * asiw.dwRate) / asiw.dwScale / 1000.0);
868 }
869
870 /***********************************************************************
871  *              AVIBuildFilterA         (AVIFIL32.@)
872  */
873 HRESULT WINAPI AVIBuildFilterA(LPSTR szFilter, LONG cbFilter, BOOL fSaving)
874 {
875   LPWSTR  wszFilter;
876   HRESULT hr;
877
878   TRACE("(%p,%ld,%d)\n", szFilter, cbFilter, fSaving);
879
880   /* check parameters */
881   if (szFilter == NULL)
882     return AVIERR_BADPARAM;
883   if (cbFilter < 2)
884     return AVIERR_BADSIZE;
885
886   szFilter[0] = 0;
887   szFilter[1] = 0;
888
889   wszFilter = (LPWSTR)GlobalAllocPtr(GHND, cbFilter);
890   if (wszFilter == NULL)
891     return AVIERR_MEMORY;
892
893   hr = AVIBuildFilterW(wszFilter, cbFilter, fSaving);
894   if (SUCCEEDED(hr)) {
895     WideCharToMultiByte(CP_ACP, 0, wszFilter, cbFilter,
896                         szFilter, cbFilter, NULL, NULL);
897   }
898
899   GlobalFreePtr(wszFilter);
900
901   return hr;
902 }
903
904 /***********************************************************************
905  *              AVIBuildFilterW         (AVIFIL32.@)
906  */
907 HRESULT WINAPI AVIBuildFilterW(LPWSTR szFilter, LONG cbFilter, BOOL fSaving)
908 {
909   static const WCHAR szClsid[] = {'C','L','S','I','D',0};
910   static const WCHAR szExtensionFmt[] = {';','*','.','%','s',0};
911   static const WCHAR szAVIFileExtensions[] =
912     {'A','V','I','F','i','l','e','\\','E','x','t','e','n','s','i','o','n','s',0};
913
914   AVIFilter *lp;
915   WCHAR      szAllFiles[40];
916   WCHAR      szFileExt[10];
917   WCHAR      szValue[128];
918   HKEY       hKey;
919   LONG       n, i;
920   LONG       size;
921   LONG       count = 0;
922
923   TRACE("(%p,%ld,%d)\n", szFilter, cbFilter, fSaving);
924
925   /* check parameters */
926   if (szFilter == NULL)
927     return AVIERR_BADPARAM;
928   if (cbFilter < 2)
929     return AVIERR_BADSIZE;
930
931   lp = (AVIFilter*)GlobalAllocPtr(GHND, MAX_FILTERS * sizeof(AVIFilter));
932   if (lp == NULL)
933     return AVIERR_MEMORY;
934
935   /*
936    * 1. iterate over HKEY_CLASSES_ROOT\\AVIFile\\Extensions and collect
937    *    extensions and CLSID's
938    * 2. iterate over collected CLSID's and copy it's description and it's
939    *    extensions to szFilter if it fits
940    *
941    * First filter is named "All multimedia files" and it's filter is a
942    * collection of all possible extensions except "*.*".
943    */
944   if (RegOpenKeyW(HKEY_CLASSES_ROOT, szAVIFileExtensions, &hKey) != S_OK) {
945     GlobalFreePtr(lp);
946     return AVIERR_ERROR;
947   }
948   for (n = 0;RegEnumKeyW(hKey, n, szFileExt, sizeof(szFileExt)) == S_OK;n++) {
949     /* get CLSID to extension */
950     size = sizeof(szValue)/sizeof(szValue[0]);
951     if (RegQueryValueW(hKey, szFileExt, szValue, &size) != S_OK)
952       break;
953
954     /* search if the CLSID is already known */
955     for (i = 1; i <= count; i++) {
956       if (lstrcmpW(lp[i].szClsid, szValue) == 0)
957         break; /* a new one */
958     }
959
960     if (count - i == -1) {
961       /* it's a new CLSID */
962
963       /* FIXME: How do we get info's about read/write capabilities? */
964
965       if (count >= MAX_FILTERS) {
966         /* try to inform user of our full fixed size table */
967         ERR(": More than %d filters found! Adjust MAX_FILTERS in dlls/avifil32/api.c\n", MAX_FILTERS);
968         break;
969       }
970
971       lstrcpyW(lp[i].szClsid, szValue);
972
973       count++;
974     }
975
976     /* append extension to the filter */
977     wsprintfW(szValue, szExtensionFmt, szFileExt);
978     if (lp[i].szExtensions[0] == 0)
979       lstrcatW(lp[i].szExtensions, szValue + 1);
980     else
981       lstrcatW(lp[i].szExtensions, szValue);
982
983     /* also append to the "all multimedia"-filter */
984     if (lp[0].szExtensions[0] == 0)
985       lstrcatW(lp[0].szExtensions, szValue + 1);
986     else
987       lstrcatW(lp[0].szExtensions, szValue);
988   }
989   RegCloseKey(hKey);
990
991   /* 2. get descriptions for the CLSIDs and fill out szFilter */
992   if (RegOpenKeyW(HKEY_CLASSES_ROOT, szClsid, &hKey) != S_OK) {
993     GlobalFreePtr(lp);
994     return AVIERR_ERROR;
995   }
996   for (n = 0; n <= count; n++) {
997     /* first the description */
998     if (n != 0) {
999       size = sizeof(szValue)/sizeof(szValue[0]);
1000       if (RegQueryValueW(hKey, lp[n].szClsid, szValue, &size) == S_OK) {
1001         size = lstrlenW(szValue);
1002         lstrcpynW(szFilter, szValue, cbFilter);
1003       }
1004     } else
1005       size = LoadStringW(AVIFILE_hModule,IDS_ALLMULTIMEDIA,szFilter,cbFilter);
1006
1007     /* check for enough space */
1008     size++;
1009     if (cbFilter < size + lstrlenW(lp[n].szExtensions) + 2) {
1010       szFilter[0] = 0;
1011       szFilter[1] = 0;
1012       GlobalFreePtr(lp);
1013       RegCloseKey(hKey);
1014       return AVIERR_BUFFERTOOSMALL;
1015     }
1016     cbFilter -= size;
1017     szFilter += size;
1018
1019     /* and then the filter */
1020     lstrcpynW(szFilter, lp[n].szExtensions, cbFilter);
1021     size = lstrlenW(lp[n].szExtensions) + 1;
1022     cbFilter -= size;
1023     szFilter += size;
1024   }
1025
1026   RegCloseKey(hKey);
1027   GlobalFreePtr(lp);
1028
1029   /* add "All files" "*.*" filter if enough space left */
1030   size = LoadStringW(AVIFILE_hModule, IDS_ALLFILES,
1031                      szAllFiles, sizeof(szAllFiles)) + 1;
1032   if (cbFilter > size) {
1033     int i;
1034
1035     /* replace '@' with \000 to seperate description of filter */
1036     for (i = 0; i < size && szAllFiles[i] != 0; i++) {
1037       if (szAllFiles[i] == '@') {
1038         szAllFiles[i] = 0;
1039         break;
1040       }
1041     }
1042       
1043     memcpy(szFilter, szAllFiles, size * sizeof(szAllFiles[0]));
1044     szFilter += size;
1045     szFilter[0] = 0;
1046
1047     return AVIERR_OK;
1048   } else {
1049     szFilter[0] = 0;
1050     return AVIERR_BUFFERTOOSMALL;
1051   }
1052 }
1053
1054 static BOOL AVISaveOptionsFmtChoose(HWND hWnd)
1055 {
1056   LPAVICOMPRESSOPTIONS pOptions = SaveOpts.ppOptions[SaveOpts.nCurrent];
1057   AVISTREAMINFOW       sInfo;
1058
1059   TRACE("(%p)\n", hWnd);
1060
1061   if (pOptions == NULL || SaveOpts.ppavis[SaveOpts.nCurrent] == NULL) {
1062     ERR(": bad state!\n");
1063     return FALSE;
1064   }
1065
1066   if (FAILED(AVIStreamInfoW(SaveOpts.ppavis[SaveOpts.nCurrent],
1067                             &sInfo, sizeof(sInfo)))) {
1068     ERR(": AVIStreamInfoW failed!\n");
1069     return FALSE;
1070   }
1071
1072   if (sInfo.fccType == streamtypeVIDEO) {
1073     COMPVARS cv;
1074     BOOL     ret;
1075
1076     memset(&cv, 0, sizeof(cv));
1077
1078     if ((pOptions->dwFlags & AVICOMPRESSF_VALID) == 0) {
1079       memset(pOptions, 0, sizeof(AVICOMPRESSOPTIONS));
1080       pOptions->fccType    = streamtypeVIDEO;
1081       pOptions->fccHandler = comptypeDIB;
1082       pOptions->dwQuality  = ICQUALITY_DEFAULT;
1083     }
1084
1085     cv.cbSize     = sizeof(cv);
1086     cv.dwFlags    = ICMF_COMPVARS_VALID;
1087     /*cv.fccType    = pOptions->fccType; */
1088     cv.fccHandler = pOptions->fccHandler;
1089     cv.lQ         = pOptions->dwQuality;
1090     cv.lpState    = pOptions->lpParms;
1091     cv.cbState    = pOptions->cbParms;
1092     if (pOptions->dwFlags & AVICOMPRESSF_KEYFRAMES)
1093       cv.lKey = pOptions->dwKeyFrameEvery;
1094     else
1095       cv.lKey = 0;
1096     if (pOptions->dwFlags & AVICOMPRESSF_DATARATE)
1097       cv.lDataRate = pOptions->dwBytesPerSecond / 1024; /* need kBytes */
1098     else
1099       cv.lDataRate = 0;
1100
1101     ret = ICCompressorChoose(hWnd, SaveOpts.uFlags, NULL,
1102                              SaveOpts.ppavis[SaveOpts.nCurrent], &cv, NULL);
1103
1104     if (ret) {
1105       pOptions->lpParms   = cv.lpState;
1106       pOptions->cbParms   = cv.cbState;
1107       pOptions->dwQuality = cv.lQ;
1108       if (cv.lKey != 0) {
1109         pOptions->dwKeyFrameEvery = cv.lKey;
1110         pOptions->dwFlags |= AVICOMPRESSF_KEYFRAMES;
1111       } else
1112         pOptions->dwFlags &= ~AVICOMPRESSF_KEYFRAMES;
1113       if (cv.lDataRate != 0) {
1114         pOptions->dwBytesPerSecond = cv.lDataRate * 1024; /* need bytes */
1115         pOptions->dwFlags |= AVICOMPRESSF_DATARATE;
1116       } else
1117         pOptions->dwFlags &= ~AVICOMPRESSF_DATARATE;
1118       pOptions->dwFlags  |= AVICOMPRESSF_VALID;
1119     }
1120     ICCompressorFree(&cv);
1121
1122     return ret;
1123   } else if (sInfo.fccType == streamtypeAUDIO) {
1124     ACMFORMATCHOOSEW afmtc;
1125     MMRESULT         ret;
1126     LONG             size;
1127
1128     /* FIXME: check ACM version -- Which version is needed? */
1129
1130     memset(&afmtc, 0, sizeof(afmtc));
1131     afmtc.cbStruct  = sizeof(afmtc);
1132     afmtc.fdwStyle  = 0;
1133     afmtc.hwndOwner = hWnd;
1134
1135     acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT, &size);
1136     if ((pOptions->cbFormat == 0 || pOptions->lpFormat == NULL) && size != 0) {
1137       pOptions->lpFormat = GlobalAllocPtr(GMEM_MOVEABLE, size);
1138       pOptions->cbFormat = size;
1139     } else if (pOptions->cbFormat < size) {
1140       pOptions->lpFormat = GlobalReAllocPtr(pOptions->lpFormat, size, GMEM_MOVEABLE);
1141       pOptions->cbFormat = size;
1142     }
1143     if (pOptions->lpFormat == NULL)
1144       return FALSE;
1145     afmtc.pwfx  = pOptions->lpFormat;
1146     afmtc.cbwfx = pOptions->cbFormat;
1147
1148     size = 0;
1149     AVIStreamFormatSize(SaveOpts.ppavis[SaveOpts.nCurrent],
1150                         sInfo.dwStart, &size);
1151     if (size < sizeof(PCMWAVEFORMAT))
1152       size = sizeof(PCMWAVEFORMAT);
1153     afmtc.pwfxEnum = GlobalAllocPtr(GHND, size);
1154     if (afmtc.pwfxEnum != NULL) {
1155       AVIStreamReadFormat(SaveOpts.ppavis[SaveOpts.nCurrent],
1156                           sInfo.dwStart, afmtc.pwfxEnum, &size);
1157       afmtc.fdwEnum = ACM_FORMATENUMF_CONVERT;
1158     }
1159
1160     ret = acmFormatChooseW(&afmtc);
1161     if (ret == S_OK)
1162       pOptions->dwFlags |= AVICOMPRESSF_VALID;
1163
1164     if (afmtc.pwfxEnum != NULL)
1165       GlobalFreePtr(afmtc.pwfxEnum);
1166
1167     return (ret == S_OK ? TRUE : FALSE);
1168   } else {
1169     ERR(": unknown streamtype 0x%08lX\n", sInfo.fccType);
1170     return FALSE;
1171   }
1172 }
1173
1174 static void AVISaveOptionsUpdate(HWND hWnd)
1175 {
1176   static const WCHAR szVideoFmt[]={'%','l','d','x','%','l','d','x','%','d',0};
1177   static const WCHAR szAudioFmt[]={'%','s',' ','%','s',0};
1178
1179   WCHAR          szFormat[128];
1180   AVISTREAMINFOW sInfo;
1181   LPVOID         lpFormat;
1182   LONG           size;
1183
1184   TRACE("(%p)\n", hWnd);
1185
1186   SaveOpts.nCurrent = SendDlgItemMessageW(hWnd,IDC_STREAM,CB_GETCURSEL,0,0);
1187   if (SaveOpts.nCurrent < 0)
1188     return;
1189
1190   if (FAILED(AVIStreamInfoW(SaveOpts.ppavis[SaveOpts.nCurrent], &sInfo, sizeof(sInfo))))
1191     return;
1192
1193   AVIStreamFormatSize(SaveOpts.ppavis[SaveOpts.nCurrent],sInfo.dwStart,&size);
1194   if (size > 0) {
1195     szFormat[0] = 0;
1196
1197     /* read format to build format descriotion string */
1198     lpFormat = GlobalAllocPtr(GHND, size);
1199     if (lpFormat != NULL) {
1200       if (SUCCEEDED(AVIStreamReadFormat(SaveOpts.ppavis[SaveOpts.nCurrent],sInfo.dwStart,lpFormat, &size))) {
1201         if (sInfo.fccType == streamtypeVIDEO) {
1202           LPBITMAPINFOHEADER lpbi = lpFormat;
1203           ICINFO icinfo;
1204
1205           wsprintfW(szFormat, szVideoFmt, lpbi->biWidth,
1206                     lpbi->biHeight, lpbi->biBitCount);
1207
1208           if (lpbi->biCompression != BI_RGB) {
1209             HIC    hic;
1210
1211             hic = ICLocate(ICTYPE_VIDEO, sInfo.fccHandler, lpFormat,
1212                            NULL, ICMODE_DECOMPRESS);
1213             if (hic != (HIC)NULL) {
1214               if (ICGetInfo(hic, &icinfo, sizeof(icinfo)) == S_OK)
1215                 lstrcatW(szFormat, icinfo.szDescription);
1216               ICClose(hic);
1217             }
1218           } else {
1219             LoadStringW(AVIFILE_hModule, IDS_UNCOMPRESSED,
1220                         icinfo.szDescription, sizeof(icinfo.szDescription));
1221             lstrcatW(szFormat, icinfo.szDescription);
1222           }
1223         } else if (sInfo.fccType == streamtypeAUDIO) {
1224           ACMFORMATTAGDETAILSW aftd;
1225           ACMFORMATDETAILSW    afd;
1226
1227           memset(&aftd, 0, sizeof(aftd));
1228           memset(&afd, 0, sizeof(afd));
1229
1230           aftd.cbStruct     = sizeof(aftd);
1231           aftd.dwFormatTag  = afd.dwFormatTag =
1232             ((PWAVEFORMATEX)lpFormat)->wFormatTag;
1233           aftd.cbFormatSize = afd.cbwfx = size;
1234
1235           afd.cbStruct      = sizeof(afd);
1236           afd.pwfx          = lpFormat;
1237
1238           if (acmFormatTagDetailsW((HACMDRIVER)NULL, &aftd,
1239                                    ACM_FORMATTAGDETAILSF_FORMATTAG) == S_OK) {
1240             if (acmFormatDetailsW(NULL,&afd,ACM_FORMATDETAILSF_FORMAT) == S_OK)
1241               wsprintfW(szFormat, szAudioFmt, afd.szFormat, aftd.szFormatTag);
1242           }
1243         }
1244       }
1245       GlobalFreePtr(lpFormat);
1246     }
1247
1248     /* set text for format description */
1249     SetDlgItemTextW(hWnd, IDC_FORMATTEXT, szFormat);
1250
1251     /* Disable option button for unsupported streamtypes */
1252     if (sInfo.fccType == streamtypeVIDEO ||
1253         sInfo.fccType == streamtypeAUDIO)
1254       EnableWindow(GetDlgItem(hWnd, IDC_OPTIONS), TRUE);
1255     else
1256       EnableWindow(GetDlgItem(hWnd, IDC_OPTIONS), FALSE);
1257   }
1258
1259 }
1260
1261 BOOL CALLBACK AVISaveOptionsDlgProc(HWND hWnd, UINT uMsg,
1262                                     WPARAM wParam, LPARAM lParam)
1263 {
1264   DWORD dwInterleave;
1265   BOOL  bIsInterleaved;
1266   INT   n;
1267
1268   /*TRACE("(%p,%u,0x%04X,0x%08lX)\n", hWnd, uMsg, wParam, lParam);*/
1269
1270   switch (uMsg) {
1271   case WM_INITDIALOG:
1272     SaveOpts.nCurrent = 0;
1273     if (SaveOpts.nStreams == 1) {
1274       EndDialog(hWnd, AVISaveOptionsFmtChoose(hWnd));
1275       return FALSE;
1276     }
1277
1278     /* add streams */
1279     for (n = 0; n < SaveOpts.nStreams; n++) {
1280       AVISTREAMINFOW sInfo;
1281
1282       AVIStreamInfoW(SaveOpts.ppavis[n], &sInfo, sizeof(sInfo));
1283       SendDlgItemMessageW(hWnd, IDC_STREAM, CB_ADDSTRING,
1284                           0L, (LPARAM)sInfo.szName);
1285     }
1286
1287     /* select first stream */
1288     SendDlgItemMessageW(hWnd, IDC_STREAM, CB_SETCURSEL, 0, 0);
1289     SendMessageW(hWnd, WM_COMMAND,
1290                  GET_WM_COMMAND_MPS(IDC_STREAM, hWnd, CBN_SELCHANGE));
1291
1292     /* initialize interleave */
1293     if (SaveOpts.ppOptions[0] != NULL &&
1294         (SaveOpts.ppOptions[0]->dwFlags & AVICOMPRESSF_VALID)) {
1295       bIsInterleaved = (SaveOpts.ppOptions[0]->dwFlags & AVICOMPRESSF_INTERLEAVE);
1296       dwInterleave = SaveOpts.ppOptions[0]->dwInterleaveEvery;
1297     } else {
1298       bIsInterleaved = TRUE;
1299       dwInterleave   = 0;
1300     }
1301     CheckDlgButton(hWnd, IDC_INTERLEAVE, bIsInterleaved);
1302     SetDlgItemInt(hWnd, IDC_INTERLEAVEEVERY, dwInterleave, FALSE);
1303     EnableWindow(GetDlgItem(hWnd, IDC_INTERLEAVEEVERY), bIsInterleaved);
1304     break;
1305   case WM_COMMAND:
1306     switch (GET_WM_COMMAND_CMD(wParam, lParam)) {
1307     case IDOK:
1308       /* get data from controls and save them */
1309       dwInterleave   = GetDlgItemInt(hWnd, IDC_INTERLEAVEEVERY, NULL, 0);
1310       bIsInterleaved = IsDlgButtonChecked(hWnd, IDC_INTERLEAVE);
1311       for (n = 0; n < SaveOpts.nStreams; n++) {
1312         if (SaveOpts.ppOptions[n] != NULL) {
1313           if (bIsInterleaved) {
1314             SaveOpts.ppOptions[n]->dwFlags |= AVICOMPRESSF_INTERLEAVE;
1315             SaveOpts.ppOptions[n]->dwInterleaveEvery = dwInterleave;
1316           } else
1317             SaveOpts.ppOptions[n]->dwFlags &= ~AVICOMPRESSF_INTERLEAVE;
1318         }
1319       }
1320       /* fall through */
1321     case IDCANCEL:
1322       EndDialog(hWnd, GET_WM_COMMAND_CMD(wParam, lParam) == IDOK);
1323       break;
1324     case IDC_INTERLEAVE:
1325       EnableWindow(GetDlgItem(hWnd, IDC_INTERLEAVEEVERY),
1326                    IsDlgButtonChecked(hWnd, IDC_INTERLEAVE));
1327       break;
1328     case IDC_STREAM:
1329       if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE) {
1330         /* update control elements */
1331         AVISaveOptionsUpdate(hWnd);
1332       }
1333       break;
1334     case IDC_OPTIONS:
1335       AVISaveOptionsFmtChoose(hWnd);
1336       break;
1337     };
1338     return FALSE;
1339   };
1340
1341   return TRUE;
1342 }
1343
1344 /***********************************************************************
1345  *              AVISaveOptions          (AVIFIL32.@)
1346  */
1347 BOOL WINAPI AVISaveOptions(HWND hWnd, UINT uFlags, INT nStreams,
1348                            PAVISTREAM *ppavi, LPAVICOMPRESSOPTIONS *ppOptions)
1349 {
1350   LPAVICOMPRESSOPTIONS pSavedOptions = NULL;
1351   INT ret, n;
1352
1353   TRACE("(%p,0x%X,%d,%p,%p)\n", hWnd, uFlags, nStreams,
1354         ppavi, ppOptions);
1355
1356   /* check parameters */
1357   if (nStreams <= 0 || ppavi == NULL || ppOptions == NULL)
1358     return AVIERR_BADPARAM;
1359
1360   /* save options for case user press cancel */
1361   if (ppOptions != NULL && nStreams > 1) {
1362     pSavedOptions = GlobalAllocPtr(GHND,nStreams * sizeof(AVICOMPRESSOPTIONS));
1363     if (pSavedOptions == NULL)
1364       return FALSE;
1365
1366     for (n = 0; n < nStreams; n++) {
1367       if (ppOptions[n] != NULL)
1368         memcpy(pSavedOptions + n, ppOptions[n], sizeof(AVICOMPRESSOPTIONS));
1369     }
1370   }
1371
1372   SaveOpts.uFlags    = uFlags;
1373   SaveOpts.nStreams  = nStreams;
1374   SaveOpts.ppavis    = ppavi;
1375   SaveOpts.ppOptions = ppOptions;
1376
1377   ret = DialogBoxW(AVIFILE_hModule, MAKEINTRESOURCEW(IDD_SAVEOPTIONS),
1378                    hWnd, AVISaveOptionsDlgProc);
1379
1380   if (ret == -1)
1381     ret = FALSE;
1382
1383   /* restore options when user pressed cancel */
1384   if (pSavedOptions != NULL && ret == FALSE) {
1385     for (n = 0; n < nStreams; n++) {
1386       if (ppOptions[n] != NULL)
1387         memcpy(ppOptions[n], pSavedOptions + n, sizeof(AVICOMPRESSOPTIONS));
1388     }
1389     GlobalFreePtr(pSavedOptions);
1390   }
1391
1392   return (BOOL)ret;
1393 }
1394
1395 /***********************************************************************
1396  *              AVISaveOptionsFree      (AVIFIL32.@)
1397  */
1398 HRESULT WINAPI AVISaveOptionsFree(INT nStreams,LPAVICOMPRESSOPTIONS*ppOptions)
1399 {
1400   TRACE("(%d,%p)\n", nStreams, ppOptions);
1401
1402   if (nStreams < 0 || ppOptions == NULL)
1403     return AVIERR_BADPARAM;
1404
1405   for (; nStreams > 0; nStreams--) {
1406     if (ppOptions[nStreams] != NULL) {
1407       ppOptions[nStreams]->dwFlags &= ~AVICOMPRESSF_VALID;
1408
1409       if (ppOptions[nStreams]->lpParms != NULL) {
1410         GlobalFreePtr(ppOptions[nStreams]->lpParms);
1411         ppOptions[nStreams]->lpParms = NULL;
1412         ppOptions[nStreams]->cbParms = 0;
1413       }
1414       if (ppOptions[nStreams]->lpFormat != NULL) {
1415         GlobalFreePtr(ppOptions[nStreams]->lpFormat);
1416         ppOptions[nStreams]->lpFormat = NULL;
1417         ppOptions[nStreams]->cbFormat = 0;
1418       }
1419     }
1420   }
1421
1422   return AVIERR_OK;
1423 }