wintrust: Add tests for encoding/decoding SPC links.
[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     memcpy(&clsidHandler, lpHandler, sizeof(clsidHandler));
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 HRESULT WINAPI AVIStreamFindSample(PAVISTREAM pstream, LONG pos, DWORD 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     memcpy(&clsidHandler, pclsidHandler, sizeof(clsidHandler));
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)) == S_OK;n++) {
1069     /* get CLSID to extension */
1070     size = sizeof(szValue)/sizeof(szValue[0]);
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 (count - i == -1U) {
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)/sizeof(szValue[0]);
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)) + 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, sizeof(icinfo.szDescription));
1340             lstrcatW(szFormat, icinfo.szDescription);
1341           }
1342         } else if (sInfo.fccType == streamtypeAUDIO) {
1343           ACMFORMATTAGDETAILSW aftd;
1344           ACMFORMATDETAILSW    afd;
1345
1346           memset(&aftd, 0, sizeof(aftd));
1347           memset(&afd, 0, sizeof(afd));
1348
1349           aftd.cbStruct     = sizeof(aftd);
1350           aftd.dwFormatTag  = afd.dwFormatTag =
1351             ((PWAVEFORMATEX)lpFormat)->wFormatTag;
1352           aftd.cbFormatSize = afd.cbwfx = size;
1353
1354           afd.cbStruct      = sizeof(afd);
1355           afd.pwfx          = lpFormat;
1356
1357           if (acmFormatTagDetailsW(NULL, &aftd,
1358                                    ACM_FORMATTAGDETAILSF_FORMATTAG) == S_OK) {
1359             if (acmFormatDetailsW(NULL,&afd,ACM_FORMATDETAILSF_FORMAT) == S_OK)
1360               wsprintfW(szFormat, szAudioFmt, afd.szFormat, aftd.szFormatTag);
1361           }
1362         }
1363       }
1364       HeapFree(GetProcessHeap(), 0, lpFormat);
1365     }
1366
1367     /* set text for format description */
1368     SetDlgItemTextW(hWnd, IDC_FORMATTEXT, szFormat);
1369
1370     /* Disable option button for unsupported streamtypes */
1371     if (sInfo.fccType == streamtypeVIDEO ||
1372         sInfo.fccType == streamtypeAUDIO)
1373       EnableWindow(GetDlgItem(hWnd, IDC_OPTIONS), TRUE);
1374     else
1375       EnableWindow(GetDlgItem(hWnd, IDC_OPTIONS), FALSE);
1376   }
1377
1378 }
1379
1380 static INT_PTR CALLBACK AVISaveOptionsDlgProc(HWND hWnd, UINT uMsg,
1381                                               WPARAM wParam, LPARAM lParam)
1382 {
1383   DWORD dwInterleave;
1384   BOOL  bIsInterleaved;
1385   INT   n;
1386
1387   /*TRACE("(%p,%u,0x%04X,0x%08lX)\n", hWnd, uMsg, wParam, lParam);*/
1388
1389   switch (uMsg) {
1390   case WM_INITDIALOG:
1391     SaveOpts.nCurrent = 0;
1392     if (SaveOpts.nStreams == 1) {
1393       EndDialog(hWnd, AVISaveOptionsFmtChoose(hWnd));
1394       return TRUE;
1395     }
1396
1397     /* add streams */
1398     for (n = 0; n < SaveOpts.nStreams; n++) {
1399       AVISTREAMINFOW sInfo;
1400
1401       AVIStreamInfoW(SaveOpts.ppavis[n], &sInfo, sizeof(sInfo));
1402       SendDlgItemMessageW(hWnd, IDC_STREAM, CB_ADDSTRING,
1403                           0L, (LPARAM)sInfo.szName);
1404     }
1405
1406     /* select first stream */
1407     SendDlgItemMessageW(hWnd, IDC_STREAM, CB_SETCURSEL, 0, 0);
1408     SendMessageW(hWnd, WM_COMMAND, MAKELONG(IDC_STREAM, CBN_SELCHANGE), (LPARAM)hWnd);
1409
1410     /* initialize interleave */
1411     if (SaveOpts.ppOptions[0] != NULL &&
1412         (SaveOpts.ppOptions[0]->dwFlags & AVICOMPRESSF_VALID)) {
1413       bIsInterleaved = (SaveOpts.ppOptions[0]->dwFlags & AVICOMPRESSF_INTERLEAVE);
1414       dwInterleave = SaveOpts.ppOptions[0]->dwInterleaveEvery;
1415     } else {
1416       bIsInterleaved = TRUE;
1417       dwInterleave   = 0;
1418     }
1419     CheckDlgButton(hWnd, IDC_INTERLEAVE, bIsInterleaved);
1420     SetDlgItemInt(hWnd, IDC_INTERLEAVEEVERY, dwInterleave, FALSE);
1421     EnableWindow(GetDlgItem(hWnd, IDC_INTERLEAVEEVERY), bIsInterleaved);
1422     break;
1423   case WM_COMMAND:
1424     switch (LOWORD(wParam)) {
1425     case IDOK:
1426       /* get data from controls and save them */
1427       dwInterleave   = GetDlgItemInt(hWnd, IDC_INTERLEAVEEVERY, NULL, 0);
1428       bIsInterleaved = IsDlgButtonChecked(hWnd, IDC_INTERLEAVE);
1429       for (n = 0; n < SaveOpts.nStreams; n++) {
1430         if (SaveOpts.ppOptions[n] != NULL) {
1431           if (bIsInterleaved) {
1432             SaveOpts.ppOptions[n]->dwFlags |= AVICOMPRESSF_INTERLEAVE;
1433             SaveOpts.ppOptions[n]->dwInterleaveEvery = dwInterleave;
1434           } else
1435             SaveOpts.ppOptions[n]->dwFlags &= ~AVICOMPRESSF_INTERLEAVE;
1436         }
1437       }
1438       /* fall through */
1439     case IDCANCEL:
1440       EndDialog(hWnd, LOWORD(wParam) == IDOK);
1441       break;
1442     case IDC_INTERLEAVE:
1443       EnableWindow(GetDlgItem(hWnd, IDC_INTERLEAVEEVERY),
1444                    IsDlgButtonChecked(hWnd, IDC_INTERLEAVE));
1445       break;
1446     case IDC_STREAM:
1447       if (HIWORD(wParam) == CBN_SELCHANGE) {
1448         /* update control elements */
1449         AVISaveOptionsUpdate(hWnd);
1450       }
1451       break;
1452     case IDC_OPTIONS:
1453       AVISaveOptionsFmtChoose(hWnd);
1454       break;
1455     };
1456     return TRUE;
1457   };
1458
1459   return FALSE;
1460 }
1461
1462 /***********************************************************************
1463  *              AVISaveOptions          (AVIFIL32.@)
1464  */
1465 BOOL WINAPI AVISaveOptions(HWND hWnd, UINT uFlags, INT nStreams,
1466                            PAVISTREAM *ppavi, LPAVICOMPRESSOPTIONS *ppOptions)
1467 {
1468   LPAVICOMPRESSOPTIONS pSavedOptions = NULL;
1469   INT ret, n;
1470
1471   TRACE("(%p,0x%X,%d,%p,%p)\n", hWnd, uFlags, nStreams,
1472         ppavi, ppOptions);
1473
1474   /* check parameters */
1475   if (nStreams <= 0 || ppavi == NULL || ppOptions == NULL)
1476     return AVIERR_BADPARAM;
1477
1478   /* save options in case the user presses cancel */
1479   if (ppOptions != NULL && nStreams > 1) {
1480     pSavedOptions = HeapAlloc(GetProcessHeap(), 0, nStreams * sizeof(AVICOMPRESSOPTIONS));
1481     if (pSavedOptions == NULL)
1482       return FALSE;
1483
1484     for (n = 0; n < nStreams; n++) {
1485       if (ppOptions[n] != NULL)
1486         memcpy(pSavedOptions + n, ppOptions[n], sizeof(AVICOMPRESSOPTIONS));
1487     }
1488   }
1489
1490   SaveOpts.uFlags    = uFlags;
1491   SaveOpts.nStreams  = nStreams;
1492   SaveOpts.ppavis    = ppavi;
1493   SaveOpts.ppOptions = ppOptions;
1494
1495   ret = DialogBoxW(AVIFILE_hModule, MAKEINTRESOURCEW(IDD_SAVEOPTIONS),
1496                    hWnd, AVISaveOptionsDlgProc);
1497
1498   if (ret == -1)
1499     ret = FALSE;
1500
1501   /* restore options when user pressed cancel */
1502   if (pSavedOptions != NULL) {
1503     if (ret == FALSE) {
1504       for (n = 0; n < nStreams; n++) {
1505         if (ppOptions[n] != NULL)
1506           memcpy(ppOptions[n], pSavedOptions + n, sizeof(AVICOMPRESSOPTIONS));
1507       }
1508     }
1509     HeapFree(GetProcessHeap(), 0, pSavedOptions);
1510   }
1511
1512   return (BOOL)ret;
1513 }
1514
1515 /***********************************************************************
1516  *              AVISaveOptionsFree      (AVIFIL32.@)
1517  *              AVISaveOptionsFree      (AVIFILE.124)
1518  */
1519 HRESULT WINAPI AVISaveOptionsFree(INT nStreams,LPAVICOMPRESSOPTIONS*ppOptions)
1520 {
1521   TRACE("(%d,%p)\n", nStreams, ppOptions);
1522
1523   if (nStreams < 0 || ppOptions == NULL)
1524     return AVIERR_BADPARAM;
1525
1526   for (; nStreams > 0; nStreams--) {
1527     if (ppOptions[nStreams] != NULL) {
1528       ppOptions[nStreams]->dwFlags &= ~AVICOMPRESSF_VALID;
1529
1530       if (ppOptions[nStreams]->lpParms != NULL) {
1531         HeapFree(GetProcessHeap(), 0, ppOptions[nStreams]->lpParms);
1532         ppOptions[nStreams]->lpParms = NULL;
1533         ppOptions[nStreams]->cbParms = 0;
1534       }
1535       if (ppOptions[nStreams]->lpFormat != NULL) {
1536         HeapFree(GetProcessHeap(), 0, ppOptions[nStreams]->lpFormat);
1537         ppOptions[nStreams]->lpFormat = NULL;
1538         ppOptions[nStreams]->cbFormat = 0;
1539       }
1540     }
1541   }
1542
1543   return AVIERR_OK;
1544 }
1545
1546 /***********************************************************************
1547  *              AVISaveVA               (AVIFIL32.@)
1548  */
1549 HRESULT WINAPI AVISaveVA(LPCSTR szFile, CLSID *pclsidHandler,
1550                          AVISAVECALLBACK lpfnCallback, int nStream,
1551                          PAVISTREAM *ppavi, LPAVICOMPRESSOPTIONS *plpOptions)
1552 {
1553   LPWSTR  wszFile = NULL;
1554   HRESULT hr;
1555   int     len;
1556
1557   TRACE("%s,%p,%p,%d,%p,%p)\n", debugstr_a(szFile), pclsidHandler,
1558         lpfnCallback, nStream, ppavi, plpOptions);
1559
1560   if (szFile == NULL || ppavi == NULL || plpOptions == NULL)
1561     return AVIERR_BADPARAM;
1562
1563   /* convert ASCII string to Unicode and call Unicode function */
1564   len = MultiByteToWideChar(CP_ACP, 0, szFile, -1, NULL, 0);
1565   if (len <= 0)
1566     return AVIERR_BADPARAM;
1567
1568   wszFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1569   if (wszFile == NULL)
1570     return AVIERR_MEMORY;
1571
1572   MultiByteToWideChar(CP_ACP, 0, szFile, -1, wszFile, len);
1573
1574   hr = AVISaveVW(wszFile, pclsidHandler, lpfnCallback,
1575                  nStream, ppavi, plpOptions);
1576
1577   HeapFree(GetProcessHeap(), 0, wszFile);
1578
1579   return hr;
1580 }
1581
1582 /***********************************************************************
1583  *              AVIFILE_AVISaveDefaultCallback  (internal)
1584  */
1585 static BOOL WINAPI AVIFILE_AVISaveDefaultCallback(INT progress)
1586 {
1587   TRACE("(%d)\n", progress);
1588
1589   return FALSE;
1590 }
1591
1592 /***********************************************************************
1593  *              AVISaveVW               (AVIFIL32.@)
1594  */
1595 HRESULT WINAPI AVISaveVW(LPCWSTR szFile, CLSID *pclsidHandler,
1596                          AVISAVECALLBACK lpfnCallback, int nStreams,
1597                          PAVISTREAM *ppavi, LPAVICOMPRESSOPTIONS *plpOptions)
1598 {
1599   LONG           lStart[MAX_AVISTREAMS];
1600   PAVISTREAM     pOutStreams[MAX_AVISTREAMS];
1601   PAVISTREAM     pInStreams[MAX_AVISTREAMS];
1602   AVIFILEINFOW   fInfo;
1603   AVISTREAMINFOW sInfo;
1604
1605   PAVIFILE       pfile = NULL; /* the output AVI file */
1606   LONG           lFirstVideo = -1;
1607   int            curStream;
1608
1609   /* for interleaving ... */
1610   DWORD          dwInterleave = 0; /* interleave rate */
1611   DWORD          dwFileInitialFrames;
1612   LONG           lFileLength;
1613   LONG           lSampleInc;
1614
1615   /* for reading/writing the data ... */
1616   LPVOID         lpBuffer = NULL;
1617   LONG           cbBuffer;        /* real size of lpBuffer */
1618   LONG           lBufferSize;     /* needed bytes for format(s), etc. */
1619   LONG           lReadBytes;
1620   LONG           lReadSamples;
1621   HRESULT        hres;
1622
1623   TRACE("(%s,%p,%p,%d,%p,%p)\n", debugstr_w(szFile), pclsidHandler,
1624         lpfnCallback, nStreams, ppavi, plpOptions);
1625
1626   if (szFile == NULL || ppavi == NULL || plpOptions == NULL)
1627     return AVIERR_BADPARAM;
1628   if (nStreams >= MAX_AVISTREAMS) {
1629     WARN("Can't write AVI with %d streams only supports %d -- change MAX_AVISTREAMS!\n", nStreams, MAX_AVISTREAMS);
1630     return AVIERR_INTERNAL;
1631   }
1632
1633   if (lpfnCallback == NULL)
1634     lpfnCallback = AVIFILE_AVISaveDefaultCallback;
1635
1636   /* clear local variable(s) */
1637   for (curStream = 0; curStream < nStreams; curStream++) {
1638     pInStreams[curStream]  = NULL;
1639     pOutStreams[curStream] = NULL;
1640   }
1641
1642   /* open output AVI file (create it if it doesn't exist) */
1643   hres = AVIFileOpenW(&pfile, szFile, OF_CREATE|OF_SHARE_EXCLUSIVE|OF_WRITE,
1644                       pclsidHandler);
1645   if (FAILED(hres))
1646     return hres;
1647   AVIFileInfoW(pfile, &fInfo, sizeof(fInfo)); /* for dwCaps */
1648
1649   /* initialize our data structures part 1 */
1650   for (curStream = 0; curStream < nStreams; curStream++) {
1651     PAVISTREAM pCurStream = ppavi[curStream];
1652
1653     hres = AVIStreamInfoW(pCurStream, &sInfo, sizeof(sInfo));
1654     if (FAILED(hres))
1655       goto error;
1656
1657     /* search first video stream and check for interleaving */
1658     if (sInfo.fccType == streamtypeVIDEO) {
1659       /* remember first video stream -- needed for interleaving */
1660       if (lFirstVideo < 0)
1661         lFirstVideo = curStream;
1662     } else if (!dwInterleave && plpOptions != NULL) {
1663       /* check if any non-video stream wants to be interleaved */
1664       WARN("options.flags=0x%X options.dwInterleave=%u\n",plpOptions[curStream]->dwFlags,plpOptions[curStream]->dwInterleaveEvery);
1665       if (plpOptions[curStream] != NULL &&
1666           plpOptions[curStream]->dwFlags & AVICOMPRESSF_INTERLEAVE)
1667         dwInterleave = plpOptions[curStream]->dwInterleaveEvery;
1668     }
1669
1670     /* create de-/compressed stream interface if needed */
1671     pInStreams[curStream] = NULL;
1672     if (plpOptions != NULL && plpOptions[curStream] != NULL) {
1673       if (plpOptions[curStream]->fccHandler ||
1674           plpOptions[curStream]->lpFormat != NULL) {
1675         DWORD dwKeySave = plpOptions[curStream]->dwKeyFrameEvery;
1676
1677         if (fInfo.dwCaps & AVIFILECAPS_ALLKEYFRAMES)
1678           plpOptions[curStream]->dwKeyFrameEvery = 1;
1679
1680         hres = AVIMakeCompressedStream(&pInStreams[curStream], pCurStream,
1681                                        plpOptions[curStream], NULL);
1682         plpOptions[curStream]->dwKeyFrameEvery = dwKeySave;
1683         if (FAILED(hres) || pInStreams[curStream] == NULL) {
1684           pInStreams[curStream] = NULL;
1685           goto error;
1686         }
1687
1688         /* test stream interface and update stream-info */
1689         hres = AVIStreamInfoW(pInStreams[curStream], &sInfo, sizeof(sInfo));
1690         if (FAILED(hres))
1691           goto error;
1692       }
1693     }
1694
1695     /* now handle streams which will only be copied */
1696     if (pInStreams[curStream] == NULL) {
1697       pCurStream = pInStreams[curStream] = ppavi[curStream];
1698       AVIStreamAddRef(pCurStream);
1699     } else
1700       pCurStream = pInStreams[curStream];
1701
1702     lStart[curStream] = sInfo.dwStart;
1703   } /* for all streams */
1704
1705   /* check that first video stream is the first stream */
1706   if (lFirstVideo > 0) {
1707     PAVISTREAM pTmp = pInStreams[lFirstVideo];
1708     LONG lTmp = lStart[lFirstVideo];
1709
1710     pInStreams[lFirstVideo] = pInStreams[0];
1711     pInStreams[0] = pTmp;
1712     lStart[lFirstVideo] = lStart[0];
1713     lStart[0] = lTmp;
1714     lFirstVideo = 0;
1715   }
1716
1717   /* allocate buffer for formats, data, etc. of an initial size of 64 kBytes*/
1718   cbBuffer = 0x00010000;
1719   lpBuffer = HeapAlloc(GetProcessHeap(), 0, cbBuffer);
1720   if (lpBuffer == NULL) {
1721     hres = AVIERR_MEMORY;
1722     goto error;
1723   }
1724
1725   AVIStreamInfoW(pInStreams[0], &sInfo, sizeof(sInfo));
1726   lFileLength = sInfo.dwLength;
1727   dwFileInitialFrames = 0;
1728   if (lFirstVideo >= 0) {
1729     /* check for correct version of the format
1730      *  -- need at least BITMAPINFOHEADER or newer
1731      */
1732     lSampleInc = 1;
1733     lBufferSize = cbBuffer;
1734     hres = AVIStreamReadFormat(pInStreams[lFirstVideo], AVIStreamStart(pInStreams[lFirstVideo]), lpBuffer, &lBufferSize);
1735     if (lBufferSize < (LONG)sizeof(BITMAPINFOHEADER))
1736       hres = AVIERR_INTERNAL;
1737     if (FAILED(hres))
1738       goto error;
1739   } else /* use one second blocks for interleaving if no video present */
1740     lSampleInc = AVIStreamTimeToSample(pInStreams[0], 1000000);
1741
1742   /* create output streams */
1743   for (curStream = 0; curStream < nStreams; curStream++) {
1744     AVIStreamInfoW(pInStreams[curStream], &sInfo, sizeof(sInfo));
1745
1746     sInfo.dwInitialFrames = 0;
1747     if (dwInterleave != 0 && curStream > 0 && sInfo.fccType != streamtypeVIDEO) {
1748       /* 750 ms initial frames for non-video streams */
1749       sInfo.dwInitialFrames = AVIStreamTimeToSample(pInStreams[0], 750);
1750     }
1751
1752     hres = AVIFileCreateStreamW(pfile, &pOutStreams[curStream], &sInfo);
1753     if (pOutStreams[curStream] != NULL && SUCCEEDED(hres)) {
1754       /* copy initial format for this stream */
1755       lBufferSize = cbBuffer;
1756       hres = AVIStreamReadFormat(pInStreams[curStream], sInfo.dwStart,
1757                                  lpBuffer, &lBufferSize);
1758       if (FAILED(hres))
1759         goto error;
1760       hres = AVIStreamSetFormat(pOutStreams[curStream], 0, lpBuffer, lBufferSize);
1761       if (FAILED(hres))
1762         goto error;
1763
1764       /* try to copy stream handler data */
1765       lBufferSize = cbBuffer;
1766       hres = AVIStreamReadData(pInStreams[curStream], ckidSTREAMHANDLERDATA,
1767                                lpBuffer, &lBufferSize);
1768       if (SUCCEEDED(hres) && lBufferSize > 0) {
1769         hres = AVIStreamWriteData(pOutStreams[curStream],ckidSTREAMHANDLERDATA,
1770                                   lpBuffer, lBufferSize);
1771         if (FAILED(hres))
1772           goto error;
1773       }
1774
1775       if (dwFileInitialFrames < sInfo.dwInitialFrames)
1776         dwFileInitialFrames = sInfo.dwInitialFrames;
1777       lReadBytes =
1778         AVIStreamSampleToSample(pOutStreams[0], pInStreams[curStream],
1779                                 sInfo.dwLength);
1780       if (lFileLength < lReadBytes)
1781         lFileLength = lReadBytes;
1782     } else {
1783       /* creation of de-/compression stream interface failed */
1784       WARN("creation of (de-)compression stream failed for stream %d\n",curStream);
1785       AVIStreamRelease(pInStreams[curStream]);
1786       if (curStream + 1 >= nStreams) {
1787         /* move the others one up */
1788         PAVISTREAM *ppas = &pInStreams[curStream];
1789         int            n = nStreams - (curStream + 1);
1790
1791         do {
1792           *ppas = pInStreams[curStream + 1];
1793         } while (--n);
1794       }
1795       nStreams--;
1796       curStream--;
1797     }
1798   } /* create output streams for all input streams */
1799
1800   /* have we still something to write, or lost everything? */
1801   if (nStreams <= 0)
1802     goto error;
1803
1804   if (dwInterleave) {
1805     LONG lCurFrame = -dwFileInitialFrames;
1806
1807     /* interleaved file */
1808     if (dwInterleave == 1)
1809       AVIFileEndRecord(pfile);
1810
1811     for (; lCurFrame < lFileLength; lCurFrame += lSampleInc) {
1812       for (curStream = 0; curStream < nStreams; curStream++) {
1813         LONG lLastSample;
1814
1815         hres = AVIStreamInfoW(pOutStreams[curStream], &sInfo, sizeof(sInfo));
1816         if (FAILED(hres))
1817           goto error;
1818
1819         /* initial frames phase at the end for this stream? */
1820         if (-(LONG)sInfo.dwInitialFrames > lCurFrame)
1821           continue;
1822
1823         if ((lFileLength - lSampleInc) <= lCurFrame) {
1824           lLastSample = AVIStreamLength(pInStreams[curStream]);
1825           lFirstVideo = lLastSample + AVIStreamStart(pInStreams[curStream]);
1826         } else {
1827           if (curStream != 0) {
1828             lFirstVideo =
1829               AVIStreamSampleToSample(pInStreams[curStream], pInStreams[0],
1830                                       (sInfo.fccType == streamtypeVIDEO ? 
1831                                        (LONG)dwInterleave : lSampleInc) +
1832                                       sInfo.dwInitialFrames + lCurFrame);
1833           } else
1834             lFirstVideo = lSampleInc + (sInfo.dwInitialFrames + lCurFrame);
1835
1836           lLastSample = AVIStreamEnd(pInStreams[curStream]);
1837           if (lLastSample <= lFirstVideo)
1838             lFirstVideo = lLastSample;
1839         }
1840
1841         /* copy needed samples now */
1842         WARN("copy from stream %d samples %d to %d...\n",curStream,
1843               lStart[curStream],lFirstVideo);
1844         while (lFirstVideo > lStart[curStream]) {
1845           DWORD flags = 0;
1846
1847           /* copy format in case it can change */
1848           lBufferSize = cbBuffer;
1849           hres = AVIStreamReadFormat(pInStreams[curStream], lStart[curStream],
1850                                      lpBuffer, &lBufferSize);
1851           if (FAILED(hres))
1852             goto error;
1853           AVIStreamSetFormat(pOutStreams[curStream], lStart[curStream],
1854                              lpBuffer, lBufferSize);
1855
1856           /* try to read data until we got it, or error */
1857           do {
1858             hres = AVIStreamRead(pInStreams[curStream], lStart[curStream],
1859                                  lFirstVideo - lStart[curStream], lpBuffer,
1860                                  cbBuffer, &lReadBytes, &lReadSamples);
1861           } while ((hres == AVIERR_BUFFERTOOSMALL) &&
1862                    (lpBuffer = HeapReAlloc(GetProcessHeap(), 0, lpBuffer, cbBuffer *= 2)) != NULL);
1863           if (lpBuffer == NULL)
1864             hres = AVIERR_MEMORY;
1865           if (FAILED(hres))
1866             goto error;
1867
1868           if (AVIStreamIsKeyFrame(pInStreams[curStream], (LONG)sInfo.dwStart))
1869             flags = AVIIF_KEYFRAME;
1870           hres = AVIStreamWrite(pOutStreams[curStream], -1, lReadSamples,
1871                                 lpBuffer, lReadBytes, flags, NULL, NULL);
1872           if (FAILED(hres))
1873             goto error;
1874
1875           lStart[curStream] += lReadSamples;
1876         }
1877         lStart[curStream] = lFirstVideo;
1878       } /* stream by stream */
1879
1880       /* need to close this block? */
1881       if (dwInterleave == 1) {
1882         hres = AVIFileEndRecord(pfile);
1883         if (FAILED(hres))
1884           break;
1885       }
1886
1887       /* show progress */
1888       if (lpfnCallback(MulDiv(dwFileInitialFrames + lCurFrame, 100,
1889                               dwFileInitialFrames + lFileLength))) {
1890         hres = AVIERR_USERABORT;
1891         break;
1892       }
1893     } /* copy frame by frame */
1894   } else {
1895     /* non-interleaved file */
1896
1897     for (curStream = 0; curStream < nStreams; curStream++) {
1898       /* show progress */
1899       if (lpfnCallback(MulDiv(curStream, 100, nStreams))) {
1900         hres = AVIERR_USERABORT;
1901         goto error;
1902       }
1903
1904       AVIStreamInfoW(pInStreams[curStream], &sInfo, sizeof(sInfo));
1905
1906       if (sInfo.dwSampleSize != 0) {
1907         /* sample-based data like audio */
1908         while (sInfo.dwStart < sInfo.dwLength) {
1909           LONG lSamples = cbBuffer / sInfo.dwSampleSize;
1910
1911           /* copy format in case it can change */
1912           lBufferSize = cbBuffer;
1913           hres = AVIStreamReadFormat(pInStreams[curStream], sInfo.dwStart,
1914                                      lpBuffer, &lBufferSize);
1915           if (FAILED(hres))
1916             return hres;
1917           AVIStreamSetFormat(pOutStreams[curStream], sInfo.dwStart,
1918                              lpBuffer, lBufferSize);
1919
1920           /* limit to stream boundaries */
1921           if (lSamples != (LONG)(sInfo.dwLength - sInfo.dwStart))
1922             lSamples = sInfo.dwLength - sInfo.dwStart;
1923
1924           /* now try to read until we get it, or an error occurs */
1925           do {
1926             lReadBytes   = cbBuffer;
1927             lReadSamples = 0;
1928             hres = AVIStreamRead(pInStreams[curStream],sInfo.dwStart,lSamples,
1929                                  lpBuffer,cbBuffer,&lReadBytes,&lReadSamples);
1930           } while ((hres == AVIERR_BUFFERTOOSMALL) &&
1931                    (lpBuffer = HeapReAlloc(GetProcessHeap(), 0, lpBuffer, cbBuffer *= 2)) != NULL);
1932           if (lpBuffer == NULL)
1933             hres = AVIERR_MEMORY;
1934           if (FAILED(hres))
1935             goto error;
1936           if (lReadSamples != 0) {
1937             sInfo.dwStart += lReadSamples;
1938             hres = AVIStreamWrite(pOutStreams[curStream], -1, lReadSamples,
1939                                   lpBuffer, lReadBytes, 0, NULL , NULL);
1940             if (FAILED(hres))
1941               goto error;
1942
1943             /* show progress */
1944             if (lpfnCallback(MulDiv(sInfo.dwStart,100,nStreams*sInfo.dwLength)+
1945                              MulDiv(curStream, 100, nStreams))) {
1946               hres = AVIERR_USERABORT;
1947               goto error;
1948             }
1949           } else {
1950             if ((sInfo.dwLength - sInfo.dwStart) != 1) {
1951               hres = AVIERR_FILEREAD;
1952               goto error;
1953             }
1954           }
1955         }
1956       } else {
1957         /* block-based data like video */
1958         for (; sInfo.dwStart < sInfo.dwLength; sInfo.dwStart++) {
1959           DWORD flags = 0;
1960
1961           /* copy format in case it can change */
1962           lBufferSize = cbBuffer;
1963           hres = AVIStreamReadFormat(pInStreams[curStream], sInfo.dwStart,
1964                                      lpBuffer, &lBufferSize);
1965           if (FAILED(hres))
1966             goto error;
1967           AVIStreamSetFormat(pOutStreams[curStream], sInfo.dwStart,
1968                              lpBuffer, lBufferSize);
1969
1970           /* try to read block and resize buffer if necessary */
1971           do {
1972             lReadSamples = 0;
1973             lReadBytes   = cbBuffer;
1974             hres = AVIStreamRead(pInStreams[curStream], sInfo.dwStart, 1,
1975                                  lpBuffer, cbBuffer,&lReadBytes,&lReadSamples);
1976           } while ((hres == AVIERR_BUFFERTOOSMALL) &&
1977                    (lpBuffer = HeapReAlloc(GetProcessHeap(), 0, lpBuffer, cbBuffer *= 2)) != NULL);
1978           if (lpBuffer == NULL)
1979             hres = AVIERR_MEMORY;
1980           if (FAILED(hres))
1981             goto error;
1982           if (lReadSamples != 1) {
1983             hres = AVIERR_FILEREAD;
1984             goto error;
1985           }
1986
1987           if (AVIStreamIsKeyFrame(pInStreams[curStream], (LONG)sInfo.dwStart))
1988             flags = AVIIF_KEYFRAME;
1989           hres = AVIStreamWrite(pOutStreams[curStream], -1, lReadSamples,
1990                                 lpBuffer, lReadBytes, flags, NULL, NULL);
1991           if (FAILED(hres))
1992             goto error;
1993
1994           /* show progress */
1995           if (lpfnCallback(MulDiv(sInfo.dwStart,100,nStreams*sInfo.dwLength)+
1996                            MulDiv(curStream, 100, nStreams))) {
1997             hres = AVIERR_USERABORT;
1998             goto error;
1999           }
2000         } /* copy all blocks */
2001       }
2002     } /* copy data stream by stream */
2003   }
2004
2005  error:
2006   HeapFree(GetProcessHeap(), 0, lpBuffer);
2007   if (pfile != NULL) {
2008     for (curStream = 0; curStream < nStreams; curStream++) {
2009       if (pOutStreams[curStream] != NULL)
2010         AVIStreamRelease(pOutStreams[curStream]);
2011       if (pInStreams[curStream] != NULL)
2012         AVIStreamRelease(pInStreams[curStream]);
2013     }
2014
2015     AVIFileRelease(pfile);
2016   }
2017
2018   return hres;
2019 }
2020
2021 /***********************************************************************
2022  *              CreateEditableStream    (AVIFIL32.@)
2023  */
2024 HRESULT WINAPI CreateEditableStream(PAVISTREAM *ppEditable, PAVISTREAM pSource)
2025 {
2026   IAVIEditStream *pEdit = NULL;
2027   HRESULT         hr;
2028
2029   TRACE("(%p,%p)\n", ppEditable, pSource);
2030
2031   if (ppEditable == NULL)
2032     return AVIERR_BADPARAM;
2033
2034   *ppEditable = NULL;
2035
2036   if (pSource != NULL) {
2037     hr = IAVIStream_QueryInterface(pSource, &IID_IAVIEditStream,
2038                                    (LPVOID*)&pEdit);
2039     if (SUCCEEDED(hr) && pEdit != NULL) {
2040       hr = IAVIEditStream_Clone(pEdit, ppEditable);
2041       IAVIEditStream_Release(pEdit);
2042
2043       return hr;
2044     }
2045   }
2046
2047   /* need own implementation of IAVIEditStream */
2048   pEdit = AVIFILE_CreateEditStream(pSource);
2049   if (pEdit == NULL)
2050     return AVIERR_MEMORY;
2051
2052   hr = IAVIEditStream_QueryInterface(pEdit, &IID_IAVIStream,
2053                                      (LPVOID*)ppEditable);
2054   IAVIEditStream_Release(pEdit);
2055
2056   return hr;
2057 }
2058
2059 /***********************************************************************
2060  *              EditStreamClone         (AVIFIL32.@)
2061  */
2062 HRESULT WINAPI EditStreamClone(PAVISTREAM pStream, PAVISTREAM *ppResult)
2063 {
2064   PAVIEDITSTREAM pEdit = NULL;
2065   HRESULT        hr;
2066
2067   TRACE("(%p,%p)\n", pStream, ppResult);
2068
2069   if (pStream == NULL)
2070     return AVIERR_BADHANDLE;
2071   if (ppResult == NULL)
2072     return AVIERR_BADPARAM;
2073
2074   *ppResult = NULL;
2075
2076   hr = IAVIStream_QueryInterface(pStream, &IID_IAVIEditStream,(LPVOID*)&pEdit);
2077   if (SUCCEEDED(hr) && pEdit != NULL) {
2078     hr = IAVIEditStream_Clone(pEdit, ppResult);
2079
2080     IAVIEditStream_Release(pEdit);
2081   } else
2082     hr = AVIERR_UNSUPPORTED;
2083
2084   return hr;
2085 }
2086
2087 /***********************************************************************
2088  *              EditStreamCopy          (AVIFIL32.@)
2089  */
2090 HRESULT WINAPI EditStreamCopy(PAVISTREAM pStream, LONG *plStart,
2091                               LONG *plLength, PAVISTREAM *ppResult)
2092 {
2093   PAVIEDITSTREAM pEdit = NULL;
2094   HRESULT        hr;
2095
2096   TRACE("(%p,%p,%p,%p)\n", pStream, plStart, plLength, ppResult);
2097
2098   if (pStream == NULL)
2099     return AVIERR_BADHANDLE;
2100   if (plStart == NULL || plLength == NULL || ppResult == NULL)
2101     return AVIERR_BADPARAM;
2102
2103   *ppResult = NULL;
2104
2105   hr = IAVIStream_QueryInterface(pStream, &IID_IAVIEditStream,(LPVOID*)&pEdit);
2106   if (SUCCEEDED(hr) && pEdit != NULL) {
2107     hr = IAVIEditStream_Copy(pEdit, plStart, plLength, ppResult);
2108
2109     IAVIEditStream_Release(pEdit);
2110   } else
2111     hr = AVIERR_UNSUPPORTED;
2112
2113   return hr;
2114 }
2115
2116 /***********************************************************************
2117  *              EditStreamCut           (AVIFIL32.@)
2118  */
2119 HRESULT WINAPI EditStreamCut(PAVISTREAM pStream, LONG *plStart,
2120                              LONG *plLength, PAVISTREAM *ppResult)
2121 {
2122   PAVIEDITSTREAM pEdit = NULL;
2123   HRESULT        hr;
2124
2125   TRACE("(%p,%p,%p,%p)\n", pStream, plStart, plLength, ppResult);
2126
2127   if (ppResult != NULL)
2128     *ppResult = NULL;
2129   if (pStream == NULL)
2130     return AVIERR_BADHANDLE;
2131   if (plStart == NULL || plLength == NULL)
2132     return AVIERR_BADPARAM;
2133
2134   hr = IAVIStream_QueryInterface(pStream, &IID_IAVIEditStream,(LPVOID*)&pEdit);
2135   if (SUCCEEDED(hr) && pEdit != NULL) {
2136     hr = IAVIEditStream_Cut(pEdit, plStart, plLength, ppResult);
2137
2138     IAVIEditStream_Release(pEdit);
2139   } else
2140     hr = AVIERR_UNSUPPORTED;
2141
2142   return hr;
2143 }
2144
2145 /***********************************************************************
2146  *              EditStreamPaste         (AVIFIL32.@)
2147  */
2148 HRESULT WINAPI EditStreamPaste(PAVISTREAM pDest, LONG *plStart, LONG *plLength,
2149                                PAVISTREAM pSource, LONG lStart, LONG lEnd)
2150 {
2151   PAVIEDITSTREAM pEdit = NULL;
2152   HRESULT        hr;
2153
2154   TRACE("(%p,%p,%p,%p,%d,%d)\n", pDest, plStart, plLength,
2155         pSource, lStart, lEnd);
2156
2157   if (pDest == NULL || pSource == NULL)
2158     return AVIERR_BADHANDLE;
2159   if (plStart == NULL || plLength == NULL || lStart < 0)
2160     return AVIERR_BADPARAM;
2161
2162   hr = IAVIStream_QueryInterface(pDest, &IID_IAVIEditStream,(LPVOID*)&pEdit);
2163   if (SUCCEEDED(hr) && pEdit != NULL) {
2164     hr = IAVIEditStream_Paste(pEdit, plStart, plLength, pSource, lStart, lEnd);
2165
2166     IAVIEditStream_Release(pEdit);
2167   } else
2168     hr = AVIERR_UNSUPPORTED;
2169
2170   return hr;
2171 }
2172
2173 /***********************************************************************
2174  *              EditStreamSetInfoA      (AVIFIL32.@)
2175  */
2176 HRESULT WINAPI EditStreamSetInfoA(PAVISTREAM pstream, LPAVISTREAMINFOA asi,
2177                                   LONG size)
2178 {
2179   AVISTREAMINFOW asiw;
2180
2181   TRACE("(%p,%p,%d)\n", pstream, asi, size);
2182
2183   if (pstream == NULL)
2184     return AVIERR_BADHANDLE;
2185   if ((DWORD)size < sizeof(AVISTREAMINFOA))
2186     return AVIERR_BADSIZE;
2187
2188   memcpy(&asiw, asi, sizeof(asiw) - sizeof(asiw.szName));
2189   MultiByteToWideChar(CP_ACP, 0, asi->szName, -1,
2190                       asiw.szName, sizeof(asiw.szName)/sizeof(WCHAR));
2191
2192   return EditStreamSetInfoW(pstream, &asiw, sizeof(asiw));
2193 }
2194
2195 /***********************************************************************
2196  *              EditStreamSetInfoW      (AVIFIL32.@)
2197  */
2198 HRESULT WINAPI EditStreamSetInfoW(PAVISTREAM pstream, LPAVISTREAMINFOW asi,
2199                                   LONG size)
2200 {
2201   PAVIEDITSTREAM pEdit = NULL;
2202   HRESULT        hr;
2203
2204   TRACE("(%p,%p,%d)\n", pstream, asi, size);
2205
2206   hr = IAVIStream_QueryInterface(pstream, &IID_IAVIEditStream,(LPVOID*)&pEdit);
2207   if (SUCCEEDED(hr) && pEdit != NULL) {
2208     hr = IAVIEditStream_SetInfo(pEdit, asi, size);
2209
2210     IAVIEditStream_Release(pEdit);
2211   } else
2212     hr = AVIERR_UNSUPPORTED;
2213
2214   return hr;
2215 }
2216
2217 /***********************************************************************
2218  *              EditStreamSetNameA      (AVIFIL32.@)
2219  */
2220 HRESULT WINAPI EditStreamSetNameA(PAVISTREAM pstream, LPCSTR szName)
2221 {
2222   AVISTREAMINFOA asia;
2223   HRESULT        hres;
2224
2225   TRACE("(%p,%s)\n", pstream, debugstr_a(szName));
2226
2227   if (pstream == NULL)
2228     return AVIERR_BADHANDLE;
2229   if (szName == NULL)
2230     return AVIERR_BADPARAM;
2231
2232   hres = AVIStreamInfoA(pstream, &asia, sizeof(asia));
2233   if (FAILED(hres))
2234     return hres;
2235
2236   memset(asia.szName, 0, sizeof(asia.szName));
2237   lstrcpynA(asia.szName, szName, sizeof(asia.szName)/sizeof(asia.szName[0]));
2238
2239   return EditStreamSetInfoA(pstream, &asia, sizeof(asia));
2240 }
2241
2242 /***********************************************************************
2243  *              EditStreamSetNameW      (AVIFIL32.@)
2244  */
2245 HRESULT WINAPI EditStreamSetNameW(PAVISTREAM pstream, LPCWSTR szName)
2246 {
2247   AVISTREAMINFOW asiw;
2248   HRESULT        hres;
2249
2250   TRACE("(%p,%s)\n", pstream, debugstr_w(szName));
2251
2252   if (pstream == NULL)
2253     return AVIERR_BADHANDLE;
2254   if (szName == NULL)
2255     return AVIERR_BADPARAM;
2256
2257   hres = IAVIStream_Info(pstream, &asiw, sizeof(asiw));
2258   if (FAILED(hres))
2259     return hres;
2260
2261   memset(asiw.szName, 0, sizeof(asiw.szName));
2262   lstrcpynW(asiw.szName, szName, sizeof(asiw.szName)/sizeof(asiw.szName[0]));
2263
2264   return EditStreamSetInfoW(pstream, &asiw, sizeof(asiw));
2265 }
2266
2267 /***********************************************************************
2268  *              AVIClearClipboard       (AVIFIL32.@)
2269  */
2270 HRESULT WINAPI AVIClearClipboard(void)
2271 {
2272   TRACE("()\n");
2273
2274   return AVIERR_UNSUPPORTED; /* OleSetClipboard(NULL); */
2275 }
2276
2277 /***********************************************************************
2278  *              AVIGetFromClipboard     (AVIFIL32.@)
2279  */
2280 HRESULT WINAPI AVIGetFromClipboard(PAVIFILE *ppfile)
2281 {
2282   FIXME("(%p), stub!\n", ppfile);
2283
2284   *ppfile = NULL;
2285
2286   return AVIERR_UNSUPPORTED;
2287 }
2288
2289 /***********************************************************************
2290  *      AVIMakeStreamFromClipboard (AVIFIL32.@)
2291  */
2292 HRESULT WINAPI AVIMakeStreamFromClipboard(UINT cfFormat, HANDLE hGlobal,
2293                                           PAVISTREAM * ppstream)
2294 {
2295   FIXME("(0x%08x,%p,%p), stub!\n", cfFormat, hGlobal, ppstream);
2296
2297   if (ppstream == NULL)
2298     return AVIERR_BADHANDLE;
2299
2300   return AVIERR_UNSUPPORTED;
2301 }
2302
2303 /***********************************************************************
2304  *              AVIPutFileOnClipboard   (AVIFIL32.@)
2305  */
2306 HRESULT WINAPI AVIPutFileOnClipboard(PAVIFILE pfile)
2307 {
2308   FIXME("(%p), stub!\n", pfile);
2309
2310   if (pfile == NULL)
2311     return AVIERR_BADHANDLE;
2312
2313   return AVIERR_UNSUPPORTED;
2314 }
2315
2316 HRESULT WINAPIV AVISaveA(LPCSTR szFile, CLSID * pclsidHandler, AVISAVECALLBACK lpfnCallback,
2317                         int nStreams, PAVISTREAM pavi, LPAVICOMPRESSOPTIONS lpOptions, ...)
2318 {
2319     FIXME("(%s,%p,%p,0x%08x,%p,%p), stub!\n", debugstr_a(szFile), pclsidHandler, lpfnCallback,
2320           nStreams, pavi, lpOptions);
2321
2322     return AVIERR_UNSUPPORTED;
2323 }
2324
2325 HRESULT WINAPIV AVISaveW(LPCWSTR szFile, CLSID * pclsidHandler, AVISAVECALLBACK lpfnCallback,
2326                         int nStreams, PAVISTREAM pavi, LPAVICOMPRESSOPTIONS lpOptions, ...)
2327 {
2328     FIXME("(%s,%p,%p,0x%08x,%p,%p), stub!\n", debugstr_w(szFile), pclsidHandler, lpfnCallback,
2329           nStreams, pavi, lpOptions);
2330
2331     return AVIERR_UNSUPPORTED;
2332 }