fdopen: don't rewind the file after creating the FILE* handle. Added
[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  *              AVIFileOpen             (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  *              AVIFileInfo             (AVIFIL32.@)
311  *              AVIFileInfoA            (AVIFIL32.@)
312  *              AVIFileInfo             (AVIFILE.142)
313  */
314 HRESULT WINAPI AVIFileInfoA(PAVIFILE pfile, LPAVIFILEINFOA afi, LONG size)
315 {
316   AVIFILEINFOW afiw;
317   HRESULT      hres;
318
319   TRACE("(%p,%p,%ld)\n", pfile, afi, size);
320
321   if (pfile == NULL)
322     return AVIERR_BADHANDLE;
323   if (size < sizeof(AVIFILEINFOA))
324     return AVIERR_BADSIZE;
325
326   hres = IAVIFile_Info(pfile, &afiw, sizeof(afiw));
327
328   memcpy(afi, &afiw, sizeof(*afi) - sizeof(afi->szFileType));
329   WideCharToMultiByte(CP_ACP, 0, afiw.szFileType, -1, afi->szFileType,
330                       sizeof(afi->szFileType), NULL, NULL);
331   afi->szFileType[sizeof(afi->szFileType) - 1] = 0;
332
333   return hres;
334 }
335
336 /***********************************************************************
337  *              AVIFileInfoW            (AVIFIL32.@)
338  */
339 HRESULT WINAPI AVIFileInfoW(PAVIFILE pfile, LPAVIFILEINFOW afiw, LONG size)
340 {
341   TRACE("(%p,%p,%ld)\n", pfile, afiw, size);
342
343   if (pfile == NULL)
344     return AVIERR_BADHANDLE;
345
346   return IAVIFile_Info(pfile, afiw, size);
347 }
348
349 /***********************************************************************
350  *              AVIFileGetStream        (AVIFIL32.@)
351  *              AVIFileGetStream        (AVIFILE.143)
352  */
353 HRESULT WINAPI AVIFileGetStream(PAVIFILE pfile, PAVISTREAM *avis,
354                                 DWORD fccType, LONG lParam)
355 {
356   TRACE("(%p,%p,'%4.4s',%ld)\n", pfile, avis, (char*)&fccType, lParam);
357
358   if (pfile == NULL)
359     return AVIERR_BADHANDLE;
360
361   return IAVIFile_GetStream(pfile, avis, fccType, lParam);
362 }
363
364 /***********************************************************************
365  *              AVIFileCreateStreamA    (AVIFIL32.@)
366  */
367 HRESULT WINAPI AVIFileCreateStreamA(PAVIFILE pfile, PAVISTREAM *ppavi,
368                                     LPAVISTREAMINFOA psi)
369 {
370   AVISTREAMINFOW        psiw;
371
372   TRACE("(%p,%p,%p)\n", pfile, ppavi, psi);
373
374   if (pfile == NULL)
375     return AVIERR_BADHANDLE;
376
377   /* Only the szName at the end is different */
378   memcpy(&psiw, psi, sizeof(*psi) - sizeof(psi->szName));
379   MultiByteToWideChar(CP_ACP, 0, psi->szName, -1, psiw.szName,
380                       sizeof(psiw.szName) / sizeof(psiw.szName[0]));
381
382   return IAVIFile_CreateStream(pfile, ppavi, &psiw);
383 }
384
385 /***********************************************************************
386  *              AVIFileCreateStreamW    (AVIFIL32.@)
387  */
388 HRESULT WINAPI AVIFileCreateStreamW(PAVIFILE pfile, PAVISTREAM *avis,
389                                     LPAVISTREAMINFOW asi)
390 {
391   TRACE("(%p,%p,%p)\n", pfile, avis, asi);
392
393   return IAVIFile_CreateStream(pfile, avis, asi);
394 }
395
396 /***********************************************************************
397  *              AVIFileWriteData        (AVIFIL32.@)
398  */
399 HRESULT WINAPI AVIFileWriteData(PAVIFILE pfile,DWORD fcc,LPVOID lp,LONG size)
400 {
401   TRACE("(%p,'%4.4s',%p,%ld)\n", pfile, (char*)&fcc, lp, size);
402
403   if (pfile == NULL)
404     return AVIERR_BADHANDLE;
405
406   return IAVIFile_WriteData(pfile, fcc, lp, size);
407 }
408
409 /***********************************************************************
410  *              AVIFileReadData         (AVIFIL32.@)
411  */
412 HRESULT WINAPI AVIFileReadData(PAVIFILE pfile,DWORD fcc,LPVOID lp,LPLONG size)
413 {
414   TRACE("(%p,'%4.4s',%p,%p)\n", pfile, (char*)&fcc, lp, size);
415
416   if (pfile == NULL)
417     return AVIERR_BADHANDLE;
418
419   return IAVIFile_ReadData(pfile, fcc, lp, size);
420 }
421
422 /***********************************************************************
423  *              AVIFileEndRecord        (AVIFIL32.@)
424  *              AVIFileEndRecord        (AVIFILE.148)
425  */
426 HRESULT WINAPI AVIFileEndRecord(PAVIFILE pfile)
427 {
428   TRACE("(%p)\n", pfile);
429
430   if (pfile == NULL)
431     return AVIERR_BADHANDLE;
432
433   return IAVIFile_EndRecord(pfile);
434 }
435
436 /***********************************************************************
437  *              AVIStreamAddRef         (AVIFIL32.@)
438  *              AVIStreamAddRef         (AVIFILE.160)
439  */
440 ULONG WINAPI AVIStreamAddRef(PAVISTREAM pstream)
441 {
442   TRACE("(%p)\n", pstream);
443
444   if (pstream == NULL) {
445     ERR(": bad handle passed!\n");
446     return 0;
447   }
448
449   return IAVIStream_AddRef(pstream);
450 }
451
452 /***********************************************************************
453  *              AVIStreamRelease        (AVIFIL32.@)
454  *              AVIStreamRelease        (AVIFILE.161)
455  */
456 ULONG WINAPI AVIStreamRelease(PAVISTREAM pstream)
457 {
458   TRACE("(%p)\n", pstream);
459
460   if (pstream == NULL) {
461     ERR(": bad handle passed!\n");
462     return 0;
463   }
464
465   return IAVIStream_Release(pstream);
466 }
467
468 /***********************************************************************
469  *              AVIStreamCreate         (AVIFIL32.@)
470  *              AVIStreamCreate         (AVIFILE.104)
471  */
472 HRESULT WINAPI AVIStreamCreate(PAVISTREAM *ppavi, LONG lParam1, LONG lParam2,
473                                LPCLSID pclsidHandler)
474 {
475   HRESULT hr;
476
477   TRACE("(%p,0x%08lX,0x%08lX,%s)\n", ppavi, lParam1, lParam2,
478         debugstr_guid(pclsidHandler));
479
480   if (ppavi == NULL)
481     return AVIERR_BADPARAM;
482
483   *ppavi = NULL;
484   if (pclsidHandler == NULL)
485     return AVIERR_UNSUPPORTED;
486
487   hr = SHCoCreateInstance(NULL, pclsidHandler, NULL,
488                           &IID_IAVIStream, (LPVOID*)ppavi);
489   if (FAILED(hr) || *ppavi == NULL)
490     return hr;
491
492   hr = IAVIStream_Create(*ppavi, lParam1, lParam2);
493   if (FAILED(hr)) {
494     IAVIStream_Release(*ppavi);
495     *ppavi = NULL;
496   }
497
498   return hr;
499 }
500
501 /***********************************************************************
502  *              AVIStreamInfoA          (AVIFIL32.@)
503  */
504 HRESULT WINAPI AVIStreamInfoA(PAVISTREAM pstream, LPAVISTREAMINFOA asi,
505                               LONG size)
506 {
507   AVISTREAMINFOW asiw;
508   HRESULT        hres;
509
510   TRACE("(%p,%p,%ld)\n", pstream, asi, size);
511
512   if (pstream == NULL)
513     return AVIERR_BADHANDLE;
514   if (size < sizeof(AVISTREAMINFOA))
515     return AVIERR_BADSIZE;
516
517   hres = IAVIStream_Info(pstream, &asiw, sizeof(asiw));
518
519   memcpy(asi, &asiw, sizeof(asiw) - sizeof(asiw.szName));
520   WideCharToMultiByte(CP_ACP, 0, asiw.szName, -1, asi->szName,
521                       sizeof(asi->szName), NULL, NULL);
522   asi->szName[sizeof(asi->szName) - 1] = 0;
523
524   return hres;
525 }
526
527 /***********************************************************************
528  *              AVIStreamInfoW          (AVIFIL32.@)
529  */
530 HRESULT WINAPI AVIStreamInfoW(PAVISTREAM pstream, LPAVISTREAMINFOW asi,
531                               LONG size)
532 {
533   TRACE("(%p,%p,%ld)\n", pstream, asi, size);
534
535   if (pstream == NULL)
536     return AVIERR_BADHANDLE;
537
538   return IAVIStream_Info(pstream, asi, size);
539 }
540
541 /***********************************************************************
542  *              AVIStreamFindSample     (AVIFIL32.@)
543  */
544 HRESULT WINAPI AVIStreamFindSample(PAVISTREAM pstream, LONG pos, DWORD flags)
545 {
546   TRACE("(%p,%ld,0x%lX)\n", pstream, pos, flags);
547
548   if (pstream == NULL)
549     return -1;
550
551   return IAVIStream_FindSample(pstream, pos, flags);
552 }
553
554 /***********************************************************************
555  *              AVIStreamReadFormat     (AVIFIL32.@)
556  */
557 HRESULT WINAPI AVIStreamReadFormat(PAVISTREAM pstream, LONG pos,
558                                    LPVOID format, LPLONG formatsize)
559 {
560   TRACE("(%p,%ld,%p,%p)\n", pstream, pos, format, formatsize);
561
562   if (pstream == NULL)
563     return AVIERR_BADHANDLE;
564
565   return IAVIStream_ReadFormat(pstream, pos, format, formatsize);
566 }
567
568 /***********************************************************************
569  *              AVIStreamSetFormat      (AVIFIL32.@)
570  */
571 HRESULT WINAPI AVIStreamSetFormat(PAVISTREAM pstream, LONG pos,
572                                   LPVOID format, LONG formatsize)
573 {
574   TRACE("(%p,%ld,%p,%ld)\n", pstream, pos, format, formatsize);
575
576   if (pstream == NULL)
577     return AVIERR_BADHANDLE;
578
579   return IAVIStream_SetFormat(pstream, pos, format, formatsize);
580 }
581
582 /***********************************************************************
583  *              AVIStreamRead           (AVIFIL32.@)
584  */
585 HRESULT WINAPI AVIStreamRead(PAVISTREAM pstream, LONG start, LONG samples,
586                              LPVOID buffer, LONG buffersize,
587                              LPLONG bytesread, LPLONG samplesread)
588 {
589   TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", pstream, start, samples, buffer,
590         buffersize, bytesread, samplesread);
591
592   if (pstream == NULL)
593     return AVIERR_BADHANDLE;
594
595   return IAVIStream_Read(pstream, start, samples, buffer, buffersize,
596                          bytesread, samplesread);
597 }
598
599 /***********************************************************************
600  *              AVIStreamWrite          (AVIFIL32.@)
601  */
602 HRESULT WINAPI AVIStreamWrite(PAVISTREAM pstream, LONG start, LONG samples,
603                               LPVOID buffer, LONG buffersize, DWORD flags,
604                               LPLONG sampwritten, LPLONG byteswritten)
605 {
606   TRACE("(%p,%ld,%ld,%p,%ld,0x%lX,%p,%p)\n", pstream, start, samples, buffer,
607         buffersize, flags, sampwritten, byteswritten);
608
609   if (pstream == NULL)
610     return AVIERR_BADHANDLE;
611
612   return IAVIStream_Write(pstream, start, samples, buffer, buffersize,
613                           flags, sampwritten, byteswritten);
614 }
615
616 /***********************************************************************
617  *              AVIStreamReadData       (AVIFIL32.@)
618  */
619 HRESULT WINAPI AVIStreamReadData(PAVISTREAM pstream, DWORD fcc, LPVOID lp,
620                                  LPLONG lpread)
621 {
622   TRACE("(%p,'%4.4s',%p,%p)\n", pstream, (char*)&fcc, lp, lpread);
623
624   if (pstream == NULL)
625     return AVIERR_BADHANDLE;
626
627   return IAVIStream_ReadData(pstream, fcc, lp, lpread);
628 }
629
630 /***********************************************************************
631  *              AVIStreamWriteData      (AVIFIL32.@)
632  */
633 HRESULT WINAPI AVIStreamWriteData(PAVISTREAM pstream, DWORD fcc, LPVOID lp,
634                                   LONG size)
635 {
636   TRACE("(%p,'%4.4s',%p,%ld)\n", pstream, (char*)&fcc, lp, size);
637
638   if (pstream == NULL)
639     return AVIERR_BADHANDLE;
640
641   return IAVIStream_WriteData(pstream, fcc, lp, size);
642 }
643
644 /***********************************************************************
645  *              AVIStreamGetFrameOpen   (AVIFIL32.@)
646  */
647 PGETFRAME WINAPI AVIStreamGetFrameOpen(PAVISTREAM pstream,
648                                        LPBITMAPINFOHEADER lpbiWanted)
649 {
650   PGETFRAME pg = NULL;
651
652   TRACE("(%p,%p)\n", pstream, lpbiWanted);
653
654   if (FAILED(IAVIStream_QueryInterface(pstream, &IID_IGetFrame, (LPVOID*)&pg)) ||
655       pg == NULL) {
656     pg = AVIFILE_CreateGetFrame(pstream);
657     if (pg == NULL)
658       return NULL;
659   }
660
661   if (FAILED(IGetFrame_SetFormat(pg, lpbiWanted, NULL, 0, 0, -1, -1))) {
662     IGetFrame_Release(pg);
663     return NULL;
664   }
665
666   return pg;
667 }
668
669 /***********************************************************************
670  *              AVIStreamGetFrame       (AVIFIL32.@)
671  */
672 LPVOID WINAPI AVIStreamGetFrame(PGETFRAME pg, LONG pos)
673 {
674   TRACE("(%p,%ld)\n", pg, pos);
675
676   if (pg == NULL)
677     return NULL;
678
679   return IGetFrame_GetFrame(pg, pos);
680 }
681
682 /***********************************************************************
683  *              AVIStreamGetFrameClose (AVIFIL32.@)
684  */
685 HRESULT WINAPI AVIStreamGetFrameClose(PGETFRAME pg)
686 {
687   TRACE("(%p)\n", pg);
688
689   if (pg != NULL)
690     return IGetFrame_Release(pg);
691   return 0;
692 }
693
694 /***********************************************************************
695  *              AVIMakeCompressedStream (AVIFIL32.@)
696  */
697 HRESULT WINAPI AVIMakeCompressedStream(PAVISTREAM *ppsCompressed,
698                                        PAVISTREAM psSource,
699                                        LPAVICOMPRESSOPTIONS aco,
700                                        LPCLSID pclsidHandler)
701 {
702   AVISTREAMINFOW asiw;
703   CHAR           szRegKey[25];
704   CHAR           szValue[100];
705   CLSID          clsidHandler;
706   HRESULT        hr;
707   LONG           size = sizeof(szValue);
708
709   TRACE("(%p,%p,%p,%s)\n", ppsCompressed, psSource, aco,
710         debugstr_guid(pclsidHandler));
711
712   if (ppsCompressed == NULL)
713     return AVIERR_BADPARAM;
714   if (psSource == NULL)
715     return AVIERR_BADHANDLE;
716
717   *ppsCompressed = NULL;
718
719   /* if no handler given get default ones based on streamtype */
720   if (pclsidHandler == NULL) {
721     hr = IAVIStream_Info(psSource, &asiw, sizeof(asiw));
722     if (FAILED(hr))
723       return hr;
724
725     wsprintfA(szRegKey, "AVIFile\\Compressors\\%4.4s", (char*)&asiw.fccType);
726     if (RegQueryValueA(HKEY_CLASSES_ROOT, szRegKey, szValue, &size) != ERROR_SUCCESS)
727       return AVIERR_UNSUPPORTED;
728     if (AVIFILE_CLSIDFromString(szValue, &clsidHandler) != S_OK)
729       return AVIERR_UNSUPPORTED;
730   } else
731     memcpy(&clsidHandler, pclsidHandler, sizeof(clsidHandler));
732
733   hr = SHCoCreateInstance(NULL, &clsidHandler, NULL,
734                           &IID_IAVIStream, (LPVOID*)ppsCompressed);
735   if (FAILED(hr) || *ppsCompressed == NULL)
736     return hr;
737
738   hr = IAVIStream_Create(*ppsCompressed, (LPARAM)psSource, (LPARAM)aco);
739   if (FAILED(hr)) {
740     IAVIStream_Release(*ppsCompressed);
741     *ppsCompressed = NULL;
742   }
743
744   return hr;
745 }
746
747 /***********************************************************************
748  *              AVIStreamOpenFromFile   (AVIFILE.103)
749  *              AVIStreamOpenFromFileA  (AVIFIL32.@)
750  */
751 HRESULT WINAPI AVIStreamOpenFromFileA(PAVISTREAM *ppavi, LPCSTR szFile,
752                                       DWORD fccType, LONG lParam,
753                                       UINT mode, LPCLSID pclsidHandler)
754 {
755   PAVIFILE pfile = NULL;
756   HRESULT  hr;
757
758   TRACE("(%p,%s,'%4.4s',%ld,0x%X,%s)\n", ppavi, debugstr_a(szFile),
759         (char*)&fccType, lParam, mode, debugstr_guid(pclsidHandler));
760
761   if (ppavi == NULL || szFile == NULL)
762     return AVIERR_BADPARAM;
763
764   *ppavi = NULL;
765
766   hr = AVIFileOpenA(&pfile, szFile, mode, pclsidHandler);
767   if (FAILED(hr) || pfile == NULL)
768     return hr;
769
770   hr = IAVIFile_GetStream(pfile, ppavi, fccType, lParam);
771   IAVIFile_Release(pfile);
772
773   return hr;
774 }
775
776 /***********************************************************************
777  *              AVIStreamOpenFromFileW  (AVIFIL32.@)
778  */
779 HRESULT WINAPI AVIStreamOpenFromFileW(PAVISTREAM *ppavi, LPCWSTR szFile,
780                                       DWORD fccType, LONG lParam,
781                                       UINT mode, LPCLSID pclsidHandler)
782 {
783   PAVIFILE pfile = NULL;
784   HRESULT  hr;
785
786   TRACE("(%p,%s,'%4.4s',%ld,0x%X,%s)\n", ppavi, debugstr_w(szFile),
787         (char*)&fccType, lParam, mode, debugstr_guid(pclsidHandler));
788
789   if (ppavi == NULL || szFile == NULL)
790     return AVIERR_BADPARAM;
791
792   *ppavi = NULL;
793
794   hr = AVIFileOpenW(&pfile, szFile, mode, pclsidHandler);
795   if (FAILED(hr) || pfile == NULL)
796     return hr;
797
798   hr = IAVIFile_GetStream(pfile, ppavi, fccType, lParam);
799   IAVIFile_Release(pfile);
800
801   return hr;
802 }
803
804 /***********************************************************************
805  *              AVIStreamStart          (AVIFILE.130)
806  *              AVIStreamStart          (AVIFIL32.@)
807  */
808 LONG WINAPI AVIStreamStart(PAVISTREAM pstream)
809 {
810   AVISTREAMINFOW asiw;
811
812   TRACE("(%p)\n", pstream);
813
814   if (pstream == NULL)
815     return 0;
816
817   if (FAILED(IAVIStream_Info(pstream, &asiw, sizeof(asiw))))
818     return 0;
819
820   return asiw.dwStart;
821 }
822
823 /***********************************************************************
824  *              AVIStreamLength         (AVIFILE.131)
825  *              AVIStreamLength         (AVIFIL32.@)
826  */
827 LONG WINAPI AVIStreamLength(PAVISTREAM pstream)
828 {
829   AVISTREAMINFOW asiw;
830
831   TRACE("(%p)\n", pstream);
832
833   if (pstream == NULL)
834     return 0;
835
836   if (FAILED(IAVIStream_Info(pstream, &asiw, sizeof(asiw))))
837     return 0;
838
839   return asiw.dwLength;
840 }
841
842 /***********************************************************************
843  *              AVIStreamSampleToTime   (AVIFILE.133)
844  *              AVIStreamSampleToTime   (AVIFIL32.@)
845  */
846 LONG WINAPI AVIStreamSampleToTime(PAVISTREAM pstream, LONG lSample)
847 {
848   AVISTREAMINFOW asiw;
849
850   TRACE("(%p,%ld)\n", pstream, lSample);
851
852   if (pstream == NULL)
853     return -1;
854
855   if (FAILED(IAVIStream_Info(pstream, &asiw, sizeof(asiw))))
856     return -1;
857   if (asiw.dwRate == 0)
858     return -1;
859
860   return (LONG)(((float)lSample * asiw.dwScale * 1000.0) / asiw.dwRate);
861 }
862
863 /***********************************************************************
864  *              AVIStreamTimeToSample   (AVIFILE.132)
865  *              AVIStreamTimeToSample   (AVIFIL32.@)
866  */
867 LONG WINAPI AVIStreamTimeToSample(PAVISTREAM pstream, LONG lTime)
868 {
869   AVISTREAMINFOW asiw;
870
871   TRACE("(%p,%ld)\n", pstream, lTime);
872
873   if (pstream == NULL)
874     return -1;
875
876   if (FAILED(IAVIStream_Info(pstream, &asiw, sizeof(asiw))))
877     return -1;
878   if (asiw.dwScale == 0)
879     return -1;
880
881   return (LONG)(((float)lTime * asiw.dwRate) / asiw.dwScale / 1000.0);
882 }
883
884 /***********************************************************************
885  *              AVIBuildFilterA         (AVIFIL32.@)
886  */
887 HRESULT WINAPI AVIBuildFilterA(LPSTR szFilter, LONG cbFilter, BOOL fSaving)
888 {
889   LPWSTR  wszFilter;
890   HRESULT hr;
891
892   TRACE("(%p,%ld,%d)\n", szFilter, cbFilter, fSaving);
893
894   /* check parameters */
895   if (szFilter == NULL)
896     return AVIERR_BADPARAM;
897   if (cbFilter < 2)
898     return AVIERR_BADSIZE;
899
900   szFilter[0] = 0;
901   szFilter[1] = 0;
902
903   wszFilter = (LPWSTR)GlobalAllocPtr(GHND, cbFilter);
904   if (wszFilter == NULL)
905     return AVIERR_MEMORY;
906
907   hr = AVIBuildFilterW(wszFilter, cbFilter, fSaving);
908   if (SUCCEEDED(hr)) {
909     WideCharToMultiByte(CP_ACP, 0, wszFilter, cbFilter,
910                         szFilter, cbFilter, NULL, NULL);
911   }
912
913   GlobalFreePtr(wszFilter);
914
915   return hr;
916 }
917
918 /***********************************************************************
919  *              AVIBuildFilterW         (AVIFIL32.@)
920  */
921 HRESULT WINAPI AVIBuildFilterW(LPWSTR szFilter, LONG cbFilter, BOOL fSaving)
922 {
923   static const WCHAR szClsid[] = {'C','L','S','I','D',0};
924   static const WCHAR szExtensionFmt[] = {';','*','.','%','s',0};
925   static const WCHAR szAVIFileExtensions[] =
926     {'A','V','I','F','i','l','e','\\','E','x','t','e','n','s','i','o','n','s',0};
927
928   AVIFilter *lp;
929   WCHAR      szAllFiles[40];
930   WCHAR      szFileExt[10];
931   WCHAR      szValue[128];
932   HKEY       hKey;
933   LONG       n, i;
934   LONG       size;
935   LONG       count = 0;
936
937   TRACE("(%p,%ld,%d)\n", szFilter, cbFilter, fSaving);
938
939   /* check parameters */
940   if (szFilter == NULL)
941     return AVIERR_BADPARAM;
942   if (cbFilter < 2)
943     return AVIERR_BADSIZE;
944
945   lp = (AVIFilter*)GlobalAllocPtr(GHND, MAX_FILTERS * sizeof(AVIFilter));
946   if (lp == NULL)
947     return AVIERR_MEMORY;
948
949   /*
950    * 1. iterate over HKEY_CLASSES_ROOT\\AVIFile\\Extensions and collect
951    *    extensions and CLSID's
952    * 2. iterate over collected CLSID's and copy it's description and it's
953    *    extensions to szFilter if it fits
954    *
955    * First filter is named "All multimedia files" and it's filter is a
956    * collection of all possible extensions except "*.*".
957    */
958   if (RegOpenKeyW(HKEY_CLASSES_ROOT, szAVIFileExtensions, &hKey) != S_OK) {
959     GlobalFreePtr(lp);
960     return AVIERR_ERROR;
961   }
962   for (n = 0;RegEnumKeyW(hKey, n, szFileExt, sizeof(szFileExt)) == S_OK;n++) {
963     /* get CLSID to extension */
964     size = sizeof(szValue)/sizeof(szValue[0]);
965     if (RegQueryValueW(hKey, szFileExt, szValue, &size) != S_OK)
966       break;
967
968     /* search if the CLSID is already known */
969     for (i = 1; i <= count; i++) {
970       if (lstrcmpW(lp[i].szClsid, szValue) == 0)
971         break; /* a new one */
972     }
973
974     if (count - i == -1) {
975       /* it's a new CLSID */
976
977       /* FIXME: How do we get info's about read/write capabilities? */
978
979       if (count >= MAX_FILTERS) {
980         /* try to inform user of our full fixed size table */
981         ERR(": More than %d filters found! Adjust MAX_FILTERS in dlls/avifil32/api.c\n", MAX_FILTERS);
982         break;
983       }
984
985       lstrcpyW(lp[i].szClsid, szValue);
986
987       count++;
988     }
989
990     /* append extension to the filter */
991     wsprintfW(szValue, szExtensionFmt, szFileExt);
992     if (lp[i].szExtensions[0] == 0)
993       lstrcatW(lp[i].szExtensions, szValue + 1);
994     else
995       lstrcatW(lp[i].szExtensions, szValue);
996
997     /* also append to the "all multimedia"-filter */
998     if (lp[0].szExtensions[0] == 0)
999       lstrcatW(lp[0].szExtensions, szValue + 1);
1000     else
1001       lstrcatW(lp[0].szExtensions, szValue);
1002   }
1003   RegCloseKey(hKey);
1004
1005   /* 2. get descriptions for the CLSIDs and fill out szFilter */
1006   if (RegOpenKeyW(HKEY_CLASSES_ROOT, szClsid, &hKey) != S_OK) {
1007     GlobalFreePtr(lp);
1008     return AVIERR_ERROR;
1009   }
1010   for (n = 0; n <= count; n++) {
1011     /* first the description */
1012     if (n != 0) {
1013       size = sizeof(szValue)/sizeof(szValue[0]);
1014       if (RegQueryValueW(hKey, lp[n].szClsid, szValue, &size) == S_OK) {
1015         size = lstrlenW(szValue);
1016         lstrcpynW(szFilter, szValue, cbFilter);
1017       }
1018     } else
1019       size = LoadStringW(AVIFILE_hModule,IDS_ALLMULTIMEDIA,szFilter,cbFilter);
1020
1021     /* check for enough space */
1022     size++;
1023     if (cbFilter < size + lstrlenW(lp[n].szExtensions) + 2) {
1024       szFilter[0] = 0;
1025       szFilter[1] = 0;
1026       GlobalFreePtr(lp);
1027       RegCloseKey(hKey);
1028       return AVIERR_BUFFERTOOSMALL;
1029     }
1030     cbFilter -= size;
1031     szFilter += size;
1032
1033     /* and then the filter */
1034     lstrcpynW(szFilter, lp[n].szExtensions, cbFilter);
1035     size = lstrlenW(lp[n].szExtensions) + 1;
1036     cbFilter -= size;
1037     szFilter += size;
1038   }
1039
1040   RegCloseKey(hKey);
1041   GlobalFreePtr(lp);
1042
1043   /* add "All files" "*.*" filter if enough space left */
1044   size = LoadStringW(AVIFILE_hModule, IDS_ALLFILES,
1045                      szAllFiles, sizeof(szAllFiles)) + 1;
1046   if (cbFilter > size) {
1047     int i;
1048
1049     /* replace '@' with \000 to seperate description of filter */
1050     for (i = 0; i < size && szAllFiles[i] != 0; i++) {
1051       if (szAllFiles[i] == '@') {
1052         szAllFiles[i] = 0;
1053         break;
1054       }
1055     }
1056       
1057     memcpy(szFilter, szAllFiles, size * sizeof(szAllFiles[0]));
1058     szFilter += size;
1059     szFilter[0] = 0;
1060
1061     return AVIERR_OK;
1062   } else {
1063     szFilter[0] = 0;
1064     return AVIERR_BUFFERTOOSMALL;
1065   }
1066 }
1067
1068 static BOOL AVISaveOptionsFmtChoose(HWND hWnd)
1069 {
1070   LPAVICOMPRESSOPTIONS pOptions = SaveOpts.ppOptions[SaveOpts.nCurrent];
1071   AVISTREAMINFOW       sInfo;
1072
1073   TRACE("(%p)\n", hWnd);
1074
1075   if (pOptions == NULL || SaveOpts.ppavis[SaveOpts.nCurrent] == NULL) {
1076     ERR(": bad state!\n");
1077     return FALSE;
1078   }
1079
1080   if (FAILED(AVIStreamInfoW(SaveOpts.ppavis[SaveOpts.nCurrent],
1081                             &sInfo, sizeof(sInfo)))) {
1082     ERR(": AVIStreamInfoW failed!\n");
1083     return FALSE;
1084   }
1085
1086   if (sInfo.fccType == streamtypeVIDEO) {
1087     COMPVARS cv;
1088     BOOL     ret;
1089
1090     memset(&cv, 0, sizeof(cv));
1091
1092     if ((pOptions->dwFlags & AVICOMPRESSF_VALID) == 0) {
1093       memset(pOptions, 0, sizeof(AVICOMPRESSOPTIONS));
1094       pOptions->fccType    = streamtypeVIDEO;
1095       pOptions->fccHandler = comptypeDIB;
1096       pOptions->dwQuality  = ICQUALITY_DEFAULT;
1097     }
1098
1099     cv.cbSize     = sizeof(cv);
1100     cv.dwFlags    = ICMF_COMPVARS_VALID;
1101     /*cv.fccType    = pOptions->fccType; */
1102     cv.fccHandler = pOptions->fccHandler;
1103     cv.lQ         = pOptions->dwQuality;
1104     cv.lpState    = pOptions->lpParms;
1105     cv.cbState    = pOptions->cbParms;
1106     if (pOptions->dwFlags & AVICOMPRESSF_KEYFRAMES)
1107       cv.lKey = pOptions->dwKeyFrameEvery;
1108     else
1109       cv.lKey = 0;
1110     if (pOptions->dwFlags & AVICOMPRESSF_DATARATE)
1111       cv.lDataRate = pOptions->dwBytesPerSecond / 1024; /* need kBytes */
1112     else
1113       cv.lDataRate = 0;
1114
1115     ret = ICCompressorChoose(hWnd, SaveOpts.uFlags, NULL,
1116                              SaveOpts.ppavis[SaveOpts.nCurrent], &cv, NULL);
1117
1118     if (ret) {
1119       pOptions->lpParms   = cv.lpState;
1120       pOptions->cbParms   = cv.cbState;
1121       pOptions->dwQuality = cv.lQ;
1122       if (cv.lKey != 0) {
1123         pOptions->dwKeyFrameEvery = cv.lKey;
1124         pOptions->dwFlags |= AVICOMPRESSF_KEYFRAMES;
1125       } else
1126         pOptions->dwFlags &= ~AVICOMPRESSF_KEYFRAMES;
1127       if (cv.lDataRate != 0) {
1128         pOptions->dwBytesPerSecond = cv.lDataRate * 1024; /* need bytes */
1129         pOptions->dwFlags |= AVICOMPRESSF_DATARATE;
1130       } else
1131         pOptions->dwFlags &= ~AVICOMPRESSF_DATARATE;
1132       pOptions->dwFlags  |= AVICOMPRESSF_VALID;
1133     }
1134     ICCompressorFree(&cv);
1135
1136     return ret;
1137   } else if (sInfo.fccType == streamtypeAUDIO) {
1138     ACMFORMATCHOOSEW afmtc;
1139     MMRESULT         ret;
1140     LONG             size;
1141
1142     /* FIXME: check ACM version -- Which version is needed? */
1143
1144     memset(&afmtc, 0, sizeof(afmtc));
1145     afmtc.cbStruct  = sizeof(afmtc);
1146     afmtc.fdwStyle  = 0;
1147     afmtc.hwndOwner = hWnd;
1148
1149     acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT, &size);
1150     if ((pOptions->cbFormat == 0 || pOptions->lpFormat == NULL) && size != 0) {
1151       pOptions->lpFormat = GlobalAllocPtr(GMEM_MOVEABLE, size);
1152       pOptions->cbFormat = size;
1153     } else if (pOptions->cbFormat < size) {
1154       pOptions->lpFormat = GlobalReAllocPtr(pOptions->lpFormat, size, GMEM_MOVEABLE);
1155       pOptions->cbFormat = size;
1156     }
1157     if (pOptions->lpFormat == NULL)
1158       return FALSE;
1159     afmtc.pwfx  = pOptions->lpFormat;
1160     afmtc.cbwfx = pOptions->cbFormat;
1161
1162     size = 0;
1163     AVIStreamFormatSize(SaveOpts.ppavis[SaveOpts.nCurrent],
1164                         sInfo.dwStart, &size);
1165     if (size < sizeof(PCMWAVEFORMAT))
1166       size = sizeof(PCMWAVEFORMAT);
1167     afmtc.pwfxEnum = GlobalAllocPtr(GHND, size);
1168     if (afmtc.pwfxEnum != NULL) {
1169       AVIStreamReadFormat(SaveOpts.ppavis[SaveOpts.nCurrent],
1170                           sInfo.dwStart, afmtc.pwfxEnum, &size);
1171       afmtc.fdwEnum = ACM_FORMATENUMF_CONVERT;
1172     }
1173
1174     ret = acmFormatChooseW(&afmtc);
1175     if (ret == S_OK)
1176       pOptions->dwFlags |= AVICOMPRESSF_VALID;
1177
1178     if (afmtc.pwfxEnum != NULL)
1179       GlobalFreePtr(afmtc.pwfxEnum);
1180
1181     return (ret == S_OK ? TRUE : FALSE);
1182   } else {
1183     ERR(": unknown streamtype 0x%08lX\n", sInfo.fccType);
1184     return FALSE;
1185   }
1186 }
1187
1188 static void AVISaveOptionsUpdate(HWND hWnd)
1189 {
1190   static const WCHAR szVideoFmt[]={'%','l','d','x','%','l','d','x','%','d',0};
1191   static const WCHAR szAudioFmt[]={'%','s',' ','%','s',0};
1192
1193   WCHAR          szFormat[128];
1194   AVISTREAMINFOW sInfo;
1195   LPVOID         lpFormat;
1196   LONG           size;
1197
1198   TRACE("(%p)\n", hWnd);
1199
1200   SaveOpts.nCurrent = SendDlgItemMessageW(hWnd,IDC_STREAM,CB_GETCURSEL,0,0);
1201   if (SaveOpts.nCurrent < 0)
1202     return;
1203
1204   if (FAILED(AVIStreamInfoW(SaveOpts.ppavis[SaveOpts.nCurrent], &sInfo, sizeof(sInfo))))
1205     return;
1206
1207   AVIStreamFormatSize(SaveOpts.ppavis[SaveOpts.nCurrent],sInfo.dwStart,&size);
1208   if (size > 0) {
1209     szFormat[0] = 0;
1210
1211     /* read format to build format descriotion string */
1212     lpFormat = GlobalAllocPtr(GHND, size);
1213     if (lpFormat != NULL) {
1214       if (SUCCEEDED(AVIStreamReadFormat(SaveOpts.ppavis[SaveOpts.nCurrent],sInfo.dwStart,lpFormat, &size))) {
1215         if (sInfo.fccType == streamtypeVIDEO) {
1216           LPBITMAPINFOHEADER lpbi = lpFormat;
1217           ICINFO icinfo;
1218
1219           wsprintfW(szFormat, szVideoFmt, lpbi->biWidth,
1220                     lpbi->biHeight, lpbi->biBitCount);
1221
1222           if (lpbi->biCompression != BI_RGB) {
1223             HIC    hic;
1224
1225             hic = ICLocate(ICTYPE_VIDEO, sInfo.fccHandler, lpFormat,
1226                            NULL, ICMODE_DECOMPRESS);
1227             if (hic != NULL) {
1228               if (ICGetInfo(hic, &icinfo, sizeof(icinfo)) == S_OK)
1229                 lstrcatW(szFormat, icinfo.szDescription);
1230               ICClose(hic);
1231             }
1232           } else {
1233             LoadStringW(AVIFILE_hModule, IDS_UNCOMPRESSED,
1234                         icinfo.szDescription, sizeof(icinfo.szDescription));
1235             lstrcatW(szFormat, icinfo.szDescription);
1236           }
1237         } else if (sInfo.fccType == streamtypeAUDIO) {
1238           ACMFORMATTAGDETAILSW aftd;
1239           ACMFORMATDETAILSW    afd;
1240
1241           memset(&aftd, 0, sizeof(aftd));
1242           memset(&afd, 0, sizeof(afd));
1243
1244           aftd.cbStruct     = sizeof(aftd);
1245           aftd.dwFormatTag  = afd.dwFormatTag =
1246             ((PWAVEFORMATEX)lpFormat)->wFormatTag;
1247           aftd.cbFormatSize = afd.cbwfx = size;
1248
1249           afd.cbStruct      = sizeof(afd);
1250           afd.pwfx          = lpFormat;
1251
1252           if (acmFormatTagDetailsW(NULL, &aftd,
1253                                    ACM_FORMATTAGDETAILSF_FORMATTAG) == S_OK) {
1254             if (acmFormatDetailsW(NULL,&afd,ACM_FORMATDETAILSF_FORMAT) == S_OK)
1255               wsprintfW(szFormat, szAudioFmt, afd.szFormat, aftd.szFormatTag);
1256           }
1257         }
1258       }
1259       GlobalFreePtr(lpFormat);
1260     }
1261
1262     /* set text for format description */
1263     SetDlgItemTextW(hWnd, IDC_FORMATTEXT, szFormat);
1264
1265     /* Disable option button for unsupported streamtypes */
1266     if (sInfo.fccType == streamtypeVIDEO ||
1267         sInfo.fccType == streamtypeAUDIO)
1268       EnableWindow(GetDlgItem(hWnd, IDC_OPTIONS), TRUE);
1269     else
1270       EnableWindow(GetDlgItem(hWnd, IDC_OPTIONS), FALSE);
1271   }
1272
1273 }
1274
1275 INT_PTR CALLBACK AVISaveOptionsDlgProc(HWND hWnd, UINT uMsg,
1276                                     WPARAM wParam, LPARAM lParam)
1277 {
1278   DWORD dwInterleave;
1279   BOOL  bIsInterleaved;
1280   INT   n;
1281
1282   /*TRACE("(%p,%u,0x%04X,0x%08lX)\n", hWnd, uMsg, wParam, lParam);*/
1283
1284   switch (uMsg) {
1285   case WM_INITDIALOG:
1286     SaveOpts.nCurrent = 0;
1287     if (SaveOpts.nStreams == 1) {
1288       EndDialog(hWnd, AVISaveOptionsFmtChoose(hWnd));
1289       return FALSE;
1290     }
1291
1292     /* add streams */
1293     for (n = 0; n < SaveOpts.nStreams; n++) {
1294       AVISTREAMINFOW sInfo;
1295
1296       AVIStreamInfoW(SaveOpts.ppavis[n], &sInfo, sizeof(sInfo));
1297       SendDlgItemMessageW(hWnd, IDC_STREAM, CB_ADDSTRING,
1298                           0L, (LPARAM)sInfo.szName);
1299     }
1300
1301     /* select first stream */
1302     SendDlgItemMessageW(hWnd, IDC_STREAM, CB_SETCURSEL, 0, 0);
1303     SendMessageW(hWnd, WM_COMMAND,
1304                  GET_WM_COMMAND_MPS(IDC_STREAM, hWnd, CBN_SELCHANGE));
1305
1306     /* initialize interleave */
1307     if (SaveOpts.ppOptions[0] != NULL &&
1308         (SaveOpts.ppOptions[0]->dwFlags & AVICOMPRESSF_VALID)) {
1309       bIsInterleaved = (SaveOpts.ppOptions[0]->dwFlags & AVICOMPRESSF_INTERLEAVE);
1310       dwInterleave = SaveOpts.ppOptions[0]->dwInterleaveEvery;
1311     } else {
1312       bIsInterleaved = TRUE;
1313       dwInterleave   = 0;
1314     }
1315     CheckDlgButton(hWnd, IDC_INTERLEAVE, bIsInterleaved);
1316     SetDlgItemInt(hWnd, IDC_INTERLEAVEEVERY, dwInterleave, FALSE);
1317     EnableWindow(GetDlgItem(hWnd, IDC_INTERLEAVEEVERY), bIsInterleaved);
1318     break;
1319   case WM_COMMAND:
1320     switch (GET_WM_COMMAND_CMD(wParam, lParam)) {
1321     case IDOK:
1322       /* get data from controls and save them */
1323       dwInterleave   = GetDlgItemInt(hWnd, IDC_INTERLEAVEEVERY, NULL, 0);
1324       bIsInterleaved = IsDlgButtonChecked(hWnd, IDC_INTERLEAVE);
1325       for (n = 0; n < SaveOpts.nStreams; n++) {
1326         if (SaveOpts.ppOptions[n] != NULL) {
1327           if (bIsInterleaved) {
1328             SaveOpts.ppOptions[n]->dwFlags |= AVICOMPRESSF_INTERLEAVE;
1329             SaveOpts.ppOptions[n]->dwInterleaveEvery = dwInterleave;
1330           } else
1331             SaveOpts.ppOptions[n]->dwFlags &= ~AVICOMPRESSF_INTERLEAVE;
1332         }
1333       }
1334       /* fall through */
1335     case IDCANCEL:
1336       EndDialog(hWnd, GET_WM_COMMAND_CMD(wParam, lParam) == IDOK);
1337       break;
1338     case IDC_INTERLEAVE:
1339       EnableWindow(GetDlgItem(hWnd, IDC_INTERLEAVEEVERY),
1340                    IsDlgButtonChecked(hWnd, IDC_INTERLEAVE));
1341       break;
1342     case IDC_STREAM:
1343       if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE) {
1344         /* update control elements */
1345         AVISaveOptionsUpdate(hWnd);
1346       }
1347       break;
1348     case IDC_OPTIONS:
1349       AVISaveOptionsFmtChoose(hWnd);
1350       break;
1351     };
1352     return FALSE;
1353   };
1354
1355   return TRUE;
1356 }
1357
1358 /***********************************************************************
1359  *              AVISaveOptions          (AVIFIL32.@)
1360  */
1361 BOOL WINAPI AVISaveOptions(HWND hWnd, UINT uFlags, INT nStreams,
1362                            PAVISTREAM *ppavi, LPAVICOMPRESSOPTIONS *ppOptions)
1363 {
1364   LPAVICOMPRESSOPTIONS pSavedOptions = NULL;
1365   INT ret, n;
1366
1367   TRACE("(%p,0x%X,%d,%p,%p)\n", hWnd, uFlags, nStreams,
1368         ppavi, ppOptions);
1369
1370   /* check parameters */
1371   if (nStreams <= 0 || ppavi == NULL || ppOptions == NULL)
1372     return AVIERR_BADPARAM;
1373
1374   /* save options for case user press cancel */
1375   if (ppOptions != NULL && nStreams > 1) {
1376     pSavedOptions = GlobalAllocPtr(GHND,nStreams * sizeof(AVICOMPRESSOPTIONS));
1377     if (pSavedOptions == NULL)
1378       return FALSE;
1379
1380     for (n = 0; n < nStreams; n++) {
1381       if (ppOptions[n] != NULL)
1382         memcpy(pSavedOptions + n, ppOptions[n], sizeof(AVICOMPRESSOPTIONS));
1383     }
1384   }
1385
1386   SaveOpts.uFlags    = uFlags;
1387   SaveOpts.nStreams  = nStreams;
1388   SaveOpts.ppavis    = ppavi;
1389   SaveOpts.ppOptions = ppOptions;
1390
1391   ret = DialogBoxW(AVIFILE_hModule, MAKEINTRESOURCEW(IDD_SAVEOPTIONS),
1392                    hWnd, AVISaveOptionsDlgProc);
1393
1394   if (ret == -1)
1395     ret = FALSE;
1396
1397   /* restore options when user pressed cancel */
1398   if (pSavedOptions != NULL && ret == FALSE) {
1399     for (n = 0; n < nStreams; n++) {
1400       if (ppOptions[n] != NULL)
1401         memcpy(ppOptions[n], pSavedOptions + n, sizeof(AVICOMPRESSOPTIONS));
1402     }
1403     GlobalFreePtr(pSavedOptions);
1404   }
1405
1406   return (BOOL)ret;
1407 }
1408
1409 /***********************************************************************
1410  *              AVISaveOptionsFree      (AVIFIL32.@)
1411  */
1412 HRESULT WINAPI AVISaveOptionsFree(INT nStreams,LPAVICOMPRESSOPTIONS*ppOptions)
1413 {
1414   TRACE("(%d,%p)\n", nStreams, ppOptions);
1415
1416   if (nStreams < 0 || ppOptions == NULL)
1417     return AVIERR_BADPARAM;
1418
1419   for (; nStreams > 0; nStreams--) {
1420     if (ppOptions[nStreams] != NULL) {
1421       ppOptions[nStreams]->dwFlags &= ~AVICOMPRESSF_VALID;
1422
1423       if (ppOptions[nStreams]->lpParms != NULL) {
1424         GlobalFreePtr(ppOptions[nStreams]->lpParms);
1425         ppOptions[nStreams]->lpParms = NULL;
1426         ppOptions[nStreams]->cbParms = 0;
1427       }
1428       if (ppOptions[nStreams]->lpFormat != NULL) {
1429         GlobalFreePtr(ppOptions[nStreams]->lpFormat);
1430         ppOptions[nStreams]->lpFormat = NULL;
1431         ppOptions[nStreams]->cbFormat = 0;
1432       }
1433     }
1434   }
1435
1436   return AVIERR_OK;
1437 }