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