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