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