Removed the FailReadOnly option, this is now the default behavior.
[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 = lstrlenA(szFile);
210   if (len <= 0)
211     return AVIERR_BADPARAM;
212
213   wszFile = (LPWSTR)LocalAlloc(LPTR, (len + 1) * sizeof(WCHAR));
214   if (wszFile == NULL)
215     return AVIERR_MEMORY;
216
217   MultiByteToWideChar(CP_ACP, 0, szFile, -1, wszFile, len + 1);
218   wszFile[len + 1] = 0;
219
220   hr = AVIFileOpenW(ppfile, wszFile, uMode, lpHandler);
221
222   LocalFree((HLOCAL)wszFile);
223
224   return hr;
225 }
226
227 /***********************************************************************
228  *              AVIFileOpenW            (AVIFIL32.@)
229  */
230 HRESULT WINAPI AVIFileOpenW(PAVIFILE *ppfile, LPCWSTR szFile, UINT uMode,
231                             LPCLSID lpHandler)
232 {
233   IPersistFile *ppersist = NULL;
234   CLSID         clsidHandler;
235   HRESULT       hr;
236
237   TRACE("(%p,%s,0x%X,%s)\n", ppfile, debugstr_w(szFile), uMode,
238         debugstr_guid(lpHandler));
239
240   /* check parameters */
241   if (ppfile == NULL || szFile == NULL)
242     return AVIERR_BADPARAM;
243
244   *ppfile = NULL;
245
246   /* if no handler then try guessing it by extension */
247   if (lpHandler == NULL) {
248     if (! AVIFILE_GetFileHandlerByExtension(szFile, &clsidHandler))
249       return AVIERR_UNSUPPORTED;
250   } else
251     memcpy(&clsidHandler, lpHandler, sizeof(clsidHandler));
252
253   /* crete instance of handler */
254   hr = SHCoCreateInstance(NULL, &clsidHandler, NULL,
255                           &IID_IAVIFile, (LPVOID*)ppfile);
256   if (FAILED(hr) || *ppfile == NULL)
257     return hr;
258
259   /* ask for IPersistFile interface for loading/creating the file */
260   hr = IAVIFile_QueryInterface(*ppfile, &IID_IPersistFile, (LPVOID*)&ppersist);
261   if (FAILED(hr) || ppersist == NULL) {
262     IAVIFile_Release(*ppfile);
263     *ppfile = NULL;
264     return hr;
265   }
266
267   hr = IPersistFile_Load(ppersist, szFile, uMode);
268   IPersistFile_Release(ppersist);
269   if (FAILED(hr)) {
270     IAVIFile_Release(*ppfile);
271     *ppfile = NULL;
272   }
273
274   return hr;
275 }
276
277 /***********************************************************************
278  *              AVIFileAddRef           (AVIFIL32.@)
279  *              AVIFileAddRef           (AVIFILE.140)
280  */
281 ULONG WINAPI AVIFileAddRef(PAVIFILE pfile)
282 {
283   TRACE("(%p)\n", pfile);
284
285   if (pfile == NULL) {
286     ERR(": bad handle passed!\n");
287     return 0;
288   }
289
290   return IAVIFile_AddRef(pfile);
291 }
292
293 /***********************************************************************
294  *              AVIFileRelease          (AVIFIL32.@)
295  *              AVIFileRelease          (AVIFILE.141)
296  */
297 ULONG WINAPI AVIFileRelease(PAVIFILE pfile)
298 {
299   TRACE("(%p)\n", pfile);
300
301   if (pfile == NULL) {
302     ERR(": bad handle passed!\n");
303     return 0;
304   }
305
306   return IAVIFile_Release(pfile);
307 }
308
309 /***********************************************************************
310  *              AVIFileInfo             (AVIFIL32.@)
311  *              AVIFileInfoA            (AVIFIL32.@)
312  *              AVIFileInfo             (AVIFILE.142)
313  */
314 HRESULT WINAPI AVIFileInfoA(PAVIFILE pfile, LPAVIFILEINFOA afi, LONG size)
315 {
316   AVIFILEINFOW afiw;
317   HRESULT      hres;
318
319   TRACE("(%p,%p,%ld)\n", pfile, afi, size);
320
321   if (pfile == NULL)
322     return AVIERR_BADHANDLE;
323   if ((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,%ld)\n", pfile, afiw, size);
342
343   if (pfile == NULL)
344     return AVIERR_BADHANDLE;
345
346   return IAVIFile_Info(pfile, afiw, size);
347 }
348
349 /***********************************************************************
350  *              AVIFileGetStream        (AVIFIL32.@)
351  *              AVIFileGetStream        (AVIFILE.143)
352  */
353 HRESULT WINAPI AVIFileGetStream(PAVIFILE pfile, PAVISTREAM *avis,
354                                 DWORD fccType, LONG lParam)
355 {
356   TRACE("(%p,%p,'%4.4s',%ld)\n", pfile, avis, (char*)&fccType, lParam);
357
358   if (pfile == NULL)
359     return AVIERR_BADHANDLE;
360
361   return IAVIFile_GetStream(pfile, avis, fccType, lParam);
362 }
363
364 /***********************************************************************
365  *              AVIFileCreateStreamA    (AVIFIL32.@)
366  *              AVIFileCreateStream     (AVIFILE.144)
367  */
368 HRESULT WINAPI AVIFileCreateStreamA(PAVIFILE pfile, PAVISTREAM *ppavi,
369                                     LPAVISTREAMINFOA psi)
370 {
371   AVISTREAMINFOW        psiw;
372
373   TRACE("(%p,%p,%p)\n", pfile, ppavi, psi);
374
375   if (pfile == NULL)
376     return AVIERR_BADHANDLE;
377
378   /* Only the szName at the end is different */
379   memcpy(&psiw, psi, sizeof(*psi) - sizeof(psi->szName));
380   MultiByteToWideChar(CP_ACP, 0, psi->szName, -1, psiw.szName,
381                       sizeof(psiw.szName) / sizeof(psiw.szName[0]));
382
383   return IAVIFile_CreateStream(pfile, ppavi, &psiw);
384 }
385
386 /***********************************************************************
387  *              AVIFileCreateStreamW    (AVIFIL32.@)
388  */
389 HRESULT WINAPI AVIFileCreateStreamW(PAVIFILE pfile, PAVISTREAM *avis,
390                                     LPAVISTREAMINFOW asi)
391 {
392   TRACE("(%p,%p,%p)\n", pfile, avis, asi);
393
394   if (pfile == NULL)
395     return AVIERR_BADHANDLE;
396
397   return IAVIFile_CreateStream(pfile, avis, asi);
398 }
399
400 /***********************************************************************
401  *              AVIFileWriteData        (AVIFIL32.@)
402  *              AVIFileWriteData        (AVIFILE.146)
403  */
404 HRESULT WINAPI AVIFileWriteData(PAVIFILE pfile,DWORD fcc,LPVOID lp,LONG size)
405 {
406   TRACE("(%p,'%4.4s',%p,%ld)\n", pfile, (char*)&fcc, lp, size);
407
408   if (pfile == NULL)
409     return AVIERR_BADHANDLE;
410
411   return IAVIFile_WriteData(pfile, fcc, lp, size);
412 }
413
414 /***********************************************************************
415  *              AVIFileReadData         (AVIFIL32.@)
416  *              AVIFileReadData         (AVIFILE.147)
417  */
418 HRESULT WINAPI AVIFileReadData(PAVIFILE pfile,DWORD fcc,LPVOID lp,LPLONG size)
419 {
420   TRACE("(%p,'%4.4s',%p,%p)\n", pfile, (char*)&fcc, lp, size);
421
422   if (pfile == NULL)
423     return AVIERR_BADHANDLE;
424
425   return IAVIFile_ReadData(pfile, fcc, lp, size);
426 }
427
428 /***********************************************************************
429  *              AVIFileEndRecord        (AVIFIL32.@)
430  *              AVIFileEndRecord        (AVIFILE.148)
431  */
432 HRESULT WINAPI AVIFileEndRecord(PAVIFILE pfile)
433 {
434   TRACE("(%p)\n", pfile);
435
436   if (pfile == NULL)
437     return AVIERR_BADHANDLE;
438
439   return IAVIFile_EndRecord(pfile);
440 }
441
442 /***********************************************************************
443  *              AVIStreamAddRef         (AVIFIL32.@)
444  *              AVIStreamAddRef         (AVIFILE.160)
445  */
446 ULONG WINAPI AVIStreamAddRef(PAVISTREAM pstream)
447 {
448   TRACE("(%p)\n", pstream);
449
450   if (pstream == NULL) {
451     ERR(": bad handle passed!\n");
452     return 0;
453   }
454
455   return IAVIStream_AddRef(pstream);
456 }
457
458 /***********************************************************************
459  *              AVIStreamRelease        (AVIFIL32.@)
460  *              AVIStreamRelease        (AVIFILE.161)
461  */
462 ULONG WINAPI AVIStreamRelease(PAVISTREAM pstream)
463 {
464   TRACE("(%p)\n", pstream);
465
466   if (pstream == NULL) {
467     ERR(": bad handle passed!\n");
468     return 0;
469   }
470
471   return IAVIStream_Release(pstream);
472 }
473
474 /***********************************************************************
475  *              AVIStreamCreate         (AVIFIL32.@)
476  *              AVIStreamCreate         (AVIFILE.104)
477  */
478 HRESULT WINAPI AVIStreamCreate(PAVISTREAM *ppavi, LONG lParam1, LONG lParam2,
479                                LPCLSID pclsidHandler)
480 {
481   HRESULT hr;
482
483   TRACE("(%p,0x%08lX,0x%08lX,%s)\n", ppavi, lParam1, lParam2,
484         debugstr_guid(pclsidHandler));
485
486   if (ppavi == NULL)
487     return AVIERR_BADPARAM;
488
489   *ppavi = NULL;
490   if (pclsidHandler == NULL)
491     return AVIERR_UNSUPPORTED;
492
493   hr = SHCoCreateInstance(NULL, pclsidHandler, NULL,
494                           &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,%ld)\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,%ld)\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,%ld,0x%lX)\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,%ld,%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,%ld,%p,%ld)\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,%ld,%ld,%p,%ld,%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,%ld,%ld,%p,%ld,0x%lX,%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,%ld)\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,%ld)\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 = SHCoCreateInstance(NULL, &clsidHandler, NULL,
752                           &IID_IAVIStream, (LPVOID*)ppsCompressed);
753   if (FAILED(hr) || *ppsCompressed == NULL)
754     return hr;
755
756   hr = IAVIStream_Create(*ppsCompressed, (LPARAM)psSource, (LPARAM)aco);
757   if (FAILED(hr)) {
758     IAVIStream_Release(*ppsCompressed);
759     *ppsCompressed = NULL;
760   }
761
762   return hr;
763 }
764
765 /***********************************************************************
766  *              AVIMakeFileFromStreams  (AVIFIL32.@)
767  */
768 HRESULT WINAPI AVIMakeFileFromStreams(PAVIFILE *ppfile, int nStreams,
769                                       PAVISTREAM *ppStreams)
770 {
771   TRACE("(%p,%d,%p)\n", ppfile, nStreams, ppStreams);
772
773   if (nStreams < 0 || ppfile == NULL || ppStreams == NULL)
774     return AVIERR_BADPARAM;
775
776   *ppfile = AVIFILE_CreateAVITempFile(nStreams, ppStreams);
777   if (*ppfile == NULL)
778     return AVIERR_MEMORY;
779
780   return AVIERR_OK;
781 }
782
783 /***********************************************************************
784  *              AVIStreamOpenFromFile   (AVIFILE.103)
785  *              AVIStreamOpenFromFileA  (AVIFIL32.@)
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',%ld,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',%ld,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,%ld,%ld,%ld)\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,%ld)\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(" -> %ld\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   LONG sample;
962
963   TRACE("(%p,%ld)\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(" -> %ld\n", sample);
985   return sample;
986 }
987
988 /***********************************************************************
989  *              AVIBuildFilterA         (AVIFIL32.@)
990  *              AVIBuildFilter          (AVIFILE.123)
991  */
992 HRESULT WINAPI AVIBuildFilterA(LPSTR szFilter, LONG cbFilter, BOOL fSaving)
993 {
994   LPWSTR  wszFilter;
995   HRESULT hr;
996
997   TRACE("(%p,%ld,%d)\n", szFilter, cbFilter, fSaving);
998
999   /* check parameters */
1000   if (szFilter == NULL)
1001     return AVIERR_BADPARAM;
1002   if (cbFilter < 2)
1003     return AVIERR_BADSIZE;
1004
1005   szFilter[0] = 0;
1006   szFilter[1] = 0;
1007
1008   wszFilter = (LPWSTR)GlobalAllocPtr(GHND, cbFilter * sizeof(WCHAR));
1009   if (wszFilter == NULL)
1010     return AVIERR_MEMORY;
1011
1012   hr = AVIBuildFilterW(wszFilter, cbFilter, fSaving);
1013   if (SUCCEEDED(hr)) {
1014     WideCharToMultiByte(CP_ACP, 0, wszFilter, cbFilter,
1015                         szFilter, cbFilter, NULL, NULL);
1016   }
1017
1018   GlobalFreePtr(wszFilter);
1019
1020   return hr;
1021 }
1022
1023 /***********************************************************************
1024  *              AVIBuildFilterW         (AVIFIL32.@)
1025  */
1026 HRESULT WINAPI AVIBuildFilterW(LPWSTR szFilter, LONG cbFilter, BOOL fSaving)
1027 {
1028   static const WCHAR szClsid[] = {'C','L','S','I','D',0};
1029   static const WCHAR szExtensionFmt[] = {';','*','.','%','s',0};
1030   static const WCHAR szAVIFileExtensions[] =
1031     {'A','V','I','F','i','l','e','\\','E','x','t','e','n','s','i','o','n','s',0};
1032
1033   AVIFilter *lp;
1034   WCHAR      szAllFiles[40];
1035   WCHAR      szFileExt[10];
1036   WCHAR      szValue[128];
1037   HKEY       hKey;
1038   DWORD      n, i;
1039   LONG       size;
1040   DWORD      count = 0;
1041
1042   TRACE("(%p,%ld,%d)\n", szFilter, cbFilter, fSaving);
1043
1044   /* check parameters */
1045   if (szFilter == NULL)
1046     return AVIERR_BADPARAM;
1047   if (cbFilter < 2)
1048     return AVIERR_BADSIZE;
1049
1050   lp = (AVIFilter*)GlobalAllocPtr(GHND, MAX_FILTERS * sizeof(AVIFilter));
1051   if (lp == NULL)
1052     return AVIERR_MEMORY;
1053
1054   /*
1055    * 1. iterate over HKEY_CLASSES_ROOT\\AVIFile\\Extensions and collect
1056    *    extensions and CLSID's
1057    * 2. iterate over collected CLSID's and copy it's description and it's
1058    *    extensions to szFilter if it fits
1059    *
1060    * First filter is named "All multimedia files" and it's filter is a
1061    * collection of all possible extensions except "*.*".
1062    */
1063   if (RegOpenKeyW(HKEY_CLASSES_ROOT, szAVIFileExtensions, &hKey) != S_OK) {
1064     GlobalFreePtr(lp);
1065     return AVIERR_ERROR;
1066   }
1067   for (n = 0;RegEnumKeyW(hKey, n, szFileExt, sizeof(szFileExt)) == S_OK;n++) {
1068     /* get CLSID to extension */
1069     size = sizeof(szValue)/sizeof(szValue[0]);
1070     if (RegQueryValueW(hKey, szFileExt, szValue, &size) != S_OK)
1071       break;
1072
1073     /* search if the CLSID is already known */
1074     for (i = 1; i <= count; i++) {
1075       if (lstrcmpW(lp[i].szClsid, szValue) == 0)
1076         break; /* a new one */
1077     }
1078
1079     if (count - i == -1U) {
1080       /* it's a new CLSID */
1081
1082       /* FIXME: How do we get info's about read/write capabilities? */
1083
1084       if (count >= MAX_FILTERS) {
1085         /* try to inform user of our full fixed size table */
1086         ERR(": More than %d filters found! Adjust MAX_FILTERS in dlls/avifil32/api.c\n", MAX_FILTERS);
1087         break;
1088       }
1089
1090       lstrcpyW(lp[i].szClsid, szValue);
1091
1092       count++;
1093     }
1094
1095     /* append extension to the filter */
1096     wsprintfW(szValue, szExtensionFmt, szFileExt);
1097     if (lp[i].szExtensions[0] == 0)
1098       lstrcatW(lp[i].szExtensions, szValue + 1);
1099     else
1100       lstrcatW(lp[i].szExtensions, szValue);
1101
1102     /* also append to the "all multimedia"-filter */
1103     if (lp[0].szExtensions[0] == 0)
1104       lstrcatW(lp[0].szExtensions, szValue + 1);
1105     else
1106       lstrcatW(lp[0].szExtensions, szValue);
1107   }
1108   RegCloseKey(hKey);
1109
1110   /* 2. get descriptions for the CLSIDs and fill out szFilter */
1111   if (RegOpenKeyW(HKEY_CLASSES_ROOT, szClsid, &hKey) != S_OK) {
1112     GlobalFreePtr(lp);
1113     return AVIERR_ERROR;
1114   }
1115   for (n = 0; n <= count; n++) {
1116     /* first the description */
1117     if (n != 0) {
1118       size = sizeof(szValue)/sizeof(szValue[0]);
1119       if (RegQueryValueW(hKey, lp[n].szClsid, szValue, &size) == S_OK) {
1120         size = lstrlenW(szValue);
1121         lstrcpynW(szFilter, szValue, cbFilter);
1122       }
1123     } else
1124       size = LoadStringW(AVIFILE_hModule,IDS_ALLMULTIMEDIA,szFilter,cbFilter);
1125
1126     /* check for enough space */
1127     size++;
1128     if (cbFilter < size + lstrlenW(lp[n].szExtensions) + 2) {
1129       szFilter[0] = 0;
1130       szFilter[1] = 0;
1131       GlobalFreePtr(lp);
1132       RegCloseKey(hKey);
1133       return AVIERR_BUFFERTOOSMALL;
1134     }
1135     cbFilter -= size;
1136     szFilter += size;
1137
1138     /* and then the filter */
1139     lstrcpynW(szFilter, lp[n].szExtensions, cbFilter);
1140     size = lstrlenW(lp[n].szExtensions) + 1;
1141     cbFilter -= size;
1142     szFilter += size;
1143   }
1144
1145   RegCloseKey(hKey);
1146   GlobalFreePtr(lp);
1147
1148   /* add "All files" "*.*" filter if enough space left */
1149   size = LoadStringW(AVIFILE_hModule, IDS_ALLFILES,
1150                      szAllFiles, sizeof(szAllFiles)) + 1;
1151   if (cbFilter > size) {
1152     int i;
1153
1154     /* replace '@' with \000 to separate description of filter */
1155     for (i = 0; i < size && szAllFiles[i] != 0; i++) {
1156       if (szAllFiles[i] == '@') {
1157         szAllFiles[i] = 0;
1158         break;
1159       }
1160     }
1161       
1162     memcpy(szFilter, szAllFiles, size * sizeof(szAllFiles[0]));
1163     szFilter += size;
1164     szFilter[0] = 0;
1165
1166     return AVIERR_OK;
1167   } else {
1168     szFilter[0] = 0;
1169     return AVIERR_BUFFERTOOSMALL;
1170   }
1171 }
1172
1173 static BOOL AVISaveOptionsFmtChoose(HWND hWnd)
1174 {
1175   LPAVICOMPRESSOPTIONS pOptions = SaveOpts.ppOptions[SaveOpts.nCurrent];
1176   AVISTREAMINFOW       sInfo;
1177
1178   TRACE("(%p)\n", hWnd);
1179
1180   if (pOptions == NULL || SaveOpts.ppavis[SaveOpts.nCurrent] == NULL) {
1181     ERR(": bad state!\n");
1182     return FALSE;
1183   }
1184
1185   if (FAILED(AVIStreamInfoW(SaveOpts.ppavis[SaveOpts.nCurrent],
1186                             &sInfo, sizeof(sInfo)))) {
1187     ERR(": AVIStreamInfoW failed!\n");
1188     return FALSE;
1189   }
1190
1191   if (sInfo.fccType == streamtypeVIDEO) {
1192     COMPVARS cv;
1193     BOOL     ret;
1194
1195     memset(&cv, 0, sizeof(cv));
1196
1197     if ((pOptions->dwFlags & AVICOMPRESSF_VALID) == 0) {
1198       memset(pOptions, 0, sizeof(AVICOMPRESSOPTIONS));
1199       pOptions->fccType    = streamtypeVIDEO;
1200       pOptions->fccHandler = comptypeDIB;
1201       pOptions->dwQuality  = (DWORD)ICQUALITY_DEFAULT;
1202     }
1203
1204     cv.cbSize     = sizeof(cv);
1205     cv.dwFlags    = ICMF_COMPVARS_VALID;
1206     /*cv.fccType    = pOptions->fccType; */
1207     cv.fccHandler = pOptions->fccHandler;
1208     cv.lQ         = pOptions->dwQuality;
1209     cv.lpState    = pOptions->lpParms;
1210     cv.cbState    = pOptions->cbParms;
1211     if (pOptions->dwFlags & AVICOMPRESSF_KEYFRAMES)
1212       cv.lKey = pOptions->dwKeyFrameEvery;
1213     else
1214       cv.lKey = 0;
1215     if (pOptions->dwFlags & AVICOMPRESSF_DATARATE)
1216       cv.lDataRate = pOptions->dwBytesPerSecond / 1024; /* need kBytes */
1217     else
1218       cv.lDataRate = 0;
1219
1220     ret = ICCompressorChoose(hWnd, SaveOpts.uFlags, NULL,
1221                              SaveOpts.ppavis[SaveOpts.nCurrent], &cv, NULL);
1222
1223     if (ret) {
1224       pOptions->fccHandler = cv.fccHandler;
1225       pOptions->lpParms   = cv.lpState;
1226       pOptions->cbParms   = cv.cbState;
1227       pOptions->dwQuality = cv.lQ;
1228       if (cv.lKey != 0) {
1229         pOptions->dwKeyFrameEvery = cv.lKey;
1230         pOptions->dwFlags |= AVICOMPRESSF_KEYFRAMES;
1231       } else
1232         pOptions->dwFlags &= ~AVICOMPRESSF_KEYFRAMES;
1233       if (cv.lDataRate != 0) {
1234         pOptions->dwBytesPerSecond = cv.lDataRate * 1024; /* need bytes */
1235         pOptions->dwFlags |= AVICOMPRESSF_DATARATE;
1236       } else
1237         pOptions->dwFlags &= ~AVICOMPRESSF_DATARATE;
1238       pOptions->dwFlags  |= AVICOMPRESSF_VALID;
1239     }
1240     ICCompressorFree(&cv);
1241
1242     return ret;
1243   } else if (sInfo.fccType == streamtypeAUDIO) {
1244     ACMFORMATCHOOSEW afmtc;
1245     MMRESULT         ret;
1246     LONG             size;
1247
1248     /* FIXME: check ACM version -- Which version is needed? */
1249
1250     memset(&afmtc, 0, sizeof(afmtc));
1251     afmtc.cbStruct  = sizeof(afmtc);
1252     afmtc.fdwStyle  = 0;
1253     afmtc.hwndOwner = hWnd;
1254
1255     acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT, &size);
1256     if ((pOptions->cbFormat == 0 || pOptions->lpFormat == NULL) && size != 0) {
1257       pOptions->lpFormat = GlobalAllocPtr(GMEM_MOVEABLE, size);
1258       pOptions->cbFormat = size;
1259     } else if (pOptions->cbFormat < (DWORD)size) {
1260       pOptions->lpFormat = GlobalReAllocPtr(pOptions->lpFormat, size, GMEM_MOVEABLE);
1261       pOptions->cbFormat = size;
1262     }
1263     if (pOptions->lpFormat == NULL)
1264       return FALSE;
1265     afmtc.pwfx  = pOptions->lpFormat;
1266     afmtc.cbwfx = pOptions->cbFormat;
1267
1268     size = 0;
1269     AVIStreamFormatSize(SaveOpts.ppavis[SaveOpts.nCurrent],
1270                         sInfo.dwStart, &size);
1271     if (size < (LONG)sizeof(PCMWAVEFORMAT))
1272       size = sizeof(PCMWAVEFORMAT);
1273     afmtc.pwfxEnum = GlobalAllocPtr(GHND, size);
1274     if (afmtc.pwfxEnum != NULL) {
1275       AVIStreamReadFormat(SaveOpts.ppavis[SaveOpts.nCurrent],
1276                           sInfo.dwStart, afmtc.pwfxEnum, &size);
1277       afmtc.fdwEnum = ACM_FORMATENUMF_CONVERT;
1278     }
1279
1280     ret = acmFormatChooseW(&afmtc);
1281     if (ret == S_OK)
1282       pOptions->dwFlags |= AVICOMPRESSF_VALID;
1283
1284     if (afmtc.pwfxEnum != NULL)
1285       GlobalFreePtr(afmtc.pwfxEnum);
1286
1287     return (ret == S_OK ? TRUE : FALSE);
1288   } else {
1289     ERR(": unknown streamtype 0x%08lX\n", sInfo.fccType);
1290     return FALSE;
1291   }
1292 }
1293
1294 static void AVISaveOptionsUpdate(HWND hWnd)
1295 {
1296   static const WCHAR szVideoFmt[]={'%','l','d','x','%','l','d','x','%','d',0};
1297   static const WCHAR szAudioFmt[]={'%','s',' ','%','s',0};
1298
1299   WCHAR          szFormat[128];
1300   AVISTREAMINFOW sInfo;
1301   LPVOID         lpFormat;
1302   LONG           size;
1303
1304   TRACE("(%p)\n", hWnd);
1305
1306   SaveOpts.nCurrent = SendDlgItemMessageW(hWnd,IDC_STREAM,CB_GETCURSEL,0,0);
1307   if (SaveOpts.nCurrent < 0)
1308     return;
1309
1310   if (FAILED(AVIStreamInfoW(SaveOpts.ppavis[SaveOpts.nCurrent], &sInfo, sizeof(sInfo))))
1311     return;
1312
1313   AVIStreamFormatSize(SaveOpts.ppavis[SaveOpts.nCurrent],sInfo.dwStart,&size);
1314   if (size > 0) {
1315     szFormat[0] = 0;
1316
1317     /* read format to build format descriotion string */
1318     lpFormat = GlobalAllocPtr(GHND, size);
1319     if (lpFormat != NULL) {
1320       if (SUCCEEDED(AVIStreamReadFormat(SaveOpts.ppavis[SaveOpts.nCurrent],sInfo.dwStart,lpFormat, &size))) {
1321         if (sInfo.fccType == streamtypeVIDEO) {
1322           LPBITMAPINFOHEADER lpbi = lpFormat;
1323           ICINFO icinfo;
1324
1325           wsprintfW(szFormat, szVideoFmt, lpbi->biWidth,
1326                     lpbi->biHeight, lpbi->biBitCount);
1327
1328           if (lpbi->biCompression != BI_RGB) {
1329             HIC    hic;
1330
1331             hic = ICLocate(ICTYPE_VIDEO, sInfo.fccHandler, lpFormat,
1332                            NULL, ICMODE_DECOMPRESS);
1333             if (hic != NULL) {
1334               if (ICGetInfo(hic, &icinfo, sizeof(icinfo)) == S_OK)
1335                 lstrcatW(szFormat, icinfo.szDescription);
1336               ICClose(hic);
1337             }
1338           } else {
1339             LoadStringW(AVIFILE_hModule, IDS_UNCOMPRESSED,
1340                         icinfo.szDescription, sizeof(icinfo.szDescription));
1341             lstrcatW(szFormat, icinfo.szDescription);
1342           }
1343         } else if (sInfo.fccType == streamtypeAUDIO) {
1344           ACMFORMATTAGDETAILSW aftd;
1345           ACMFORMATDETAILSW    afd;
1346
1347           memset(&aftd, 0, sizeof(aftd));
1348           memset(&afd, 0, sizeof(afd));
1349
1350           aftd.cbStruct     = sizeof(aftd);
1351           aftd.dwFormatTag  = afd.dwFormatTag =
1352             ((PWAVEFORMATEX)lpFormat)->wFormatTag;
1353           aftd.cbFormatSize = afd.cbwfx = size;
1354
1355           afd.cbStruct      = sizeof(afd);
1356           afd.pwfx          = lpFormat;
1357
1358           if (acmFormatTagDetailsW(NULL, &aftd,
1359                                    ACM_FORMATTAGDETAILSF_FORMATTAG) == S_OK) {
1360             if (acmFormatDetailsW(NULL,&afd,ACM_FORMATDETAILSF_FORMAT) == S_OK)
1361               wsprintfW(szFormat, szAudioFmt, afd.szFormat, aftd.szFormatTag);
1362           }
1363         }
1364       }
1365       GlobalFreePtr(lpFormat);
1366     }
1367
1368     /* set text for format description */
1369     SetDlgItemTextW(hWnd, IDC_FORMATTEXT, szFormat);
1370
1371     /* Disable option button for unsupported streamtypes */
1372     if (sInfo.fccType == streamtypeVIDEO ||
1373         sInfo.fccType == streamtypeAUDIO)
1374       EnableWindow(GetDlgItem(hWnd, IDC_OPTIONS), TRUE);
1375     else
1376       EnableWindow(GetDlgItem(hWnd, IDC_OPTIONS), FALSE);
1377   }
1378
1379 }
1380
1381 INT_PTR CALLBACK AVISaveOptionsDlgProc(HWND hWnd, UINT uMsg,
1382                                     WPARAM wParam, LPARAM lParam)
1383 {
1384   DWORD dwInterleave;
1385   BOOL  bIsInterleaved;
1386   INT   n;
1387
1388   /*TRACE("(%p,%u,0x%04X,0x%08lX)\n", hWnd, uMsg, wParam, lParam);*/
1389
1390   switch (uMsg) {
1391   case WM_INITDIALOG:
1392     SaveOpts.nCurrent = 0;
1393     if (SaveOpts.nStreams == 1) {
1394       EndDialog(hWnd, AVISaveOptionsFmtChoose(hWnd));
1395       return TRUE;
1396     }
1397
1398     /* add streams */
1399     for (n = 0; n < SaveOpts.nStreams; n++) {
1400       AVISTREAMINFOW sInfo;
1401
1402       AVIStreamInfoW(SaveOpts.ppavis[n], &sInfo, sizeof(sInfo));
1403       SendDlgItemMessageW(hWnd, IDC_STREAM, CB_ADDSTRING,
1404                           0L, (LPARAM)sInfo.szName);
1405     }
1406
1407     /* select first stream */
1408     SendDlgItemMessageW(hWnd, IDC_STREAM, CB_SETCURSEL, 0, 0);
1409     SendMessageW(hWnd, WM_COMMAND,
1410                  GET_WM_COMMAND_MPS(IDC_STREAM, hWnd, CBN_SELCHANGE));
1411
1412     /* initialize interleave */
1413     if (SaveOpts.ppOptions[0] != NULL &&
1414         (SaveOpts.ppOptions[0]->dwFlags & AVICOMPRESSF_VALID)) {
1415       bIsInterleaved = (SaveOpts.ppOptions[0]->dwFlags & AVICOMPRESSF_INTERLEAVE);
1416       dwInterleave = SaveOpts.ppOptions[0]->dwInterleaveEvery;
1417     } else {
1418       bIsInterleaved = TRUE;
1419       dwInterleave   = 0;
1420     }
1421     CheckDlgButton(hWnd, IDC_INTERLEAVE, bIsInterleaved);
1422     SetDlgItemInt(hWnd, IDC_INTERLEAVEEVERY, dwInterleave, FALSE);
1423     EnableWindow(GetDlgItem(hWnd, IDC_INTERLEAVEEVERY), bIsInterleaved);
1424     break;
1425   case WM_COMMAND:
1426     switch (GET_WM_COMMAND_ID(wParam, lParam)) {
1427     case IDOK:
1428       /* get data from controls and save them */
1429       dwInterleave   = GetDlgItemInt(hWnd, IDC_INTERLEAVEEVERY, NULL, 0);
1430       bIsInterleaved = IsDlgButtonChecked(hWnd, IDC_INTERLEAVE);
1431       for (n = 0; n < SaveOpts.nStreams; n++) {
1432         if (SaveOpts.ppOptions[n] != NULL) {
1433           if (bIsInterleaved) {
1434             SaveOpts.ppOptions[n]->dwFlags |= AVICOMPRESSF_INTERLEAVE;
1435             SaveOpts.ppOptions[n]->dwInterleaveEvery = dwInterleave;
1436           } else
1437             SaveOpts.ppOptions[n]->dwFlags &= ~AVICOMPRESSF_INTERLEAVE;
1438         }
1439       }
1440       /* fall through */
1441     case IDCANCEL:
1442       EndDialog(hWnd, GET_WM_COMMAND_ID(wParam, lParam) == IDOK);
1443       break;
1444     case IDC_INTERLEAVE:
1445       EnableWindow(GetDlgItem(hWnd, IDC_INTERLEAVEEVERY),
1446                    IsDlgButtonChecked(hWnd, IDC_INTERLEAVE));
1447       break;
1448     case IDC_STREAM:
1449       if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE) {
1450         /* update control elements */
1451         AVISaveOptionsUpdate(hWnd);
1452       }
1453       break;
1454     case IDC_OPTIONS:
1455       AVISaveOptionsFmtChoose(hWnd);
1456       break;
1457     };
1458     return TRUE;
1459   };
1460
1461   return FALSE;
1462 }
1463
1464 /***********************************************************************
1465  *              AVISaveOptions          (AVIFIL32.@)
1466  */
1467 BOOL WINAPI AVISaveOptions(HWND hWnd, UINT uFlags, INT nStreams,
1468                            PAVISTREAM *ppavi, LPAVICOMPRESSOPTIONS *ppOptions)
1469 {
1470   LPAVICOMPRESSOPTIONS pSavedOptions = NULL;
1471   INT ret, n;
1472
1473   TRACE("(%p,0x%X,%d,%p,%p)\n", hWnd, uFlags, nStreams,
1474         ppavi, ppOptions);
1475
1476   /* check parameters */
1477   if (nStreams <= 0 || ppavi == NULL || ppOptions == NULL)
1478     return AVIERR_BADPARAM;
1479
1480   /* save options for case user press cancel */
1481   if (ppOptions != NULL && nStreams > 1) {
1482     pSavedOptions = GlobalAllocPtr(GHND,nStreams * sizeof(AVICOMPRESSOPTIONS));
1483     if (pSavedOptions == NULL)
1484       return FALSE;
1485
1486     for (n = 0; n < nStreams; n++) {
1487       if (ppOptions[n] != NULL)
1488         memcpy(pSavedOptions + n, ppOptions[n], sizeof(AVICOMPRESSOPTIONS));
1489     }
1490   }
1491
1492   SaveOpts.uFlags    = uFlags;
1493   SaveOpts.nStreams  = nStreams;
1494   SaveOpts.ppavis    = ppavi;
1495   SaveOpts.ppOptions = ppOptions;
1496
1497   ret = DialogBoxW(AVIFILE_hModule, MAKEINTRESOURCEW(IDD_SAVEOPTIONS),
1498                    hWnd, AVISaveOptionsDlgProc);
1499
1500   if (ret == -1)
1501     ret = FALSE;
1502
1503   /* restore options when user pressed cancel */
1504   if (pSavedOptions != NULL) {
1505     if (ret == FALSE) {
1506       for (n = 0; n < nStreams; n++) {
1507         if (ppOptions[n] != NULL)
1508           memcpy(ppOptions[n], pSavedOptions + n, sizeof(AVICOMPRESSOPTIONS));
1509       }
1510     }
1511     GlobalFreePtr(pSavedOptions);
1512   }
1513
1514   return (BOOL)ret;
1515 }
1516
1517 /***********************************************************************
1518  *              AVISaveOptionsFree      (AVIFIL32.@)
1519  *              AVISaveOptionsFree      (AVIFILE.124)
1520  */
1521 HRESULT WINAPI AVISaveOptionsFree(INT nStreams,LPAVICOMPRESSOPTIONS*ppOptions)
1522 {
1523   TRACE("(%d,%p)\n", nStreams, ppOptions);
1524
1525   if (nStreams < 0 || ppOptions == NULL)
1526     return AVIERR_BADPARAM;
1527
1528   for (; nStreams > 0; nStreams--) {
1529     if (ppOptions[nStreams] != NULL) {
1530       ppOptions[nStreams]->dwFlags &= ~AVICOMPRESSF_VALID;
1531
1532       if (ppOptions[nStreams]->lpParms != NULL) {
1533         GlobalFreePtr(ppOptions[nStreams]->lpParms);
1534         ppOptions[nStreams]->lpParms = NULL;
1535         ppOptions[nStreams]->cbParms = 0;
1536       }
1537       if (ppOptions[nStreams]->lpFormat != NULL) {
1538         GlobalFreePtr(ppOptions[nStreams]->lpFormat);
1539         ppOptions[nStreams]->lpFormat = NULL;
1540         ppOptions[nStreams]->cbFormat = 0;
1541       }
1542     }
1543   }
1544
1545   return AVIERR_OK;
1546 }
1547
1548 /***********************************************************************
1549  *              AVISaveVA               (AVIFIL32.@)
1550  */
1551 HRESULT WINAPI AVISaveVA(LPCSTR szFile, CLSID *pclsidHandler,
1552                          AVISAVECALLBACK lpfnCallback, int nStream,
1553                          PAVISTREAM *ppavi, LPAVICOMPRESSOPTIONS *plpOptions)
1554 {
1555   LPWSTR  wszFile = NULL;
1556   HRESULT hr;
1557   int     len;
1558
1559   TRACE("%s,%p,%p,%d,%p,%p)\n", debugstr_a(szFile), pclsidHandler,
1560         lpfnCallback, nStream, ppavi, plpOptions);
1561
1562   if (szFile == NULL || ppavi == NULL || plpOptions == NULL)
1563     return AVIERR_BADPARAM;
1564
1565   /* convert ASCII string to Unicode and call Unicode function */
1566   len = lstrlenA(szFile);
1567   if (len <= 0)
1568     return AVIERR_BADPARAM;
1569
1570   wszFile = (LPWSTR)LocalAlloc(LPTR, (len + 1) * sizeof(WCHAR));
1571   if (wszFile == NULL)
1572     return AVIERR_MEMORY;
1573
1574   MultiByteToWideChar(CP_ACP, 0, szFile, -1, wszFile, len + 1);
1575   wszFile[len + 1] = 0;
1576
1577   hr = AVISaveVW(wszFile, pclsidHandler, lpfnCallback,
1578                  nStream, ppavi, plpOptions);
1579
1580   LocalFree((HLOCAL)wszFile);
1581
1582   return hr;
1583 }
1584
1585 /***********************************************************************
1586  *              AVIFILE_AVISaveDefaultCallback  (internal)
1587  */
1588 static BOOL WINAPI AVIFILE_AVISaveDefaultCallback(INT progress)
1589 {
1590   TRACE("(%d)\n", progress);
1591
1592   return FALSE;
1593 }
1594
1595 /***********************************************************************
1596  *              AVISaveVW               (AVIFIL32.@)
1597  */
1598 HRESULT WINAPI AVISaveVW(LPCWSTR szFile, CLSID *pclsidHandler,
1599                          AVISAVECALLBACK lpfnCallback, int nStreams,
1600                          PAVISTREAM *ppavi, LPAVICOMPRESSOPTIONS *plpOptions)
1601 {
1602   LONG           lStart[MAX_AVISTREAMS];
1603   PAVISTREAM     pOutStreams[MAX_AVISTREAMS];
1604   PAVISTREAM     pInStreams[MAX_AVISTREAMS];
1605   AVIFILEINFOW   fInfo;
1606   AVISTREAMINFOW sInfo;
1607
1608   PAVIFILE       pfile = NULL; /* the output AVI file */
1609   LONG           lFirstVideo = -1;
1610   int            curStream;
1611
1612   /* for interleaving ... */
1613   DWORD          dwInterleave = 0; /* interleave rate */
1614   DWORD          dwFileInitialFrames;
1615   LONG           lFileLength;
1616   LONG           lSampleInc;
1617
1618   /* for reading/writing the data ... */
1619   LPVOID         lpBuffer = NULL;
1620   LONG           cbBuffer;        /* real size of lpBuffer */
1621   LONG           lBufferSize;     /* needed bytes for format(s), etc. */
1622   LONG           lReadBytes;
1623   LONG           lReadSamples;
1624   HRESULT        hres;
1625
1626   TRACE("(%s,%p,%p,%d,%p,%p)\n", debugstr_w(szFile), pclsidHandler,
1627         lpfnCallback, nStreams, ppavi, plpOptions);
1628
1629   if (szFile == NULL || ppavi == NULL || plpOptions == NULL)
1630     return AVIERR_BADPARAM;
1631   if (nStreams >= MAX_AVISTREAMS) {
1632     WARN("Can't write AVI with %d streams only supports %d -- change MAX_AVISTREAMS!\n", nStreams, MAX_AVISTREAMS);
1633     return AVIERR_INTERNAL;
1634   }
1635
1636   if (lpfnCallback == NULL)
1637     lpfnCallback = AVIFILE_AVISaveDefaultCallback;
1638
1639   /* clear local variable(s) */
1640   for (curStream = 0; curStream < nStreams; curStream++) {
1641     pInStreams[curStream]  = NULL;
1642     pOutStreams[curStream] = NULL;
1643   }
1644
1645   /* open output AVI file (create it if it doesn't exist) */
1646   hres = AVIFileOpenW(&pfile, szFile, OF_CREATE|OF_SHARE_EXCLUSIVE|OF_WRITE,
1647                       pclsidHandler);
1648   if (FAILED(hres))
1649     return hres;
1650   AVIFileInfoW(pfile, &fInfo, sizeof(fInfo)); /* for dwCaps */
1651
1652   /* initialize our data structures part 1 */
1653   for (curStream = 0; curStream < nStreams; curStream++) {
1654     PAVISTREAM pCurStream = ppavi[curStream];
1655
1656     hres = AVIStreamInfoW(pCurStream, &sInfo, sizeof(sInfo));
1657     if (FAILED(hres))
1658       goto error;
1659
1660     /* search first video stream and check for interleaving */
1661     if (sInfo.fccType == streamtypeVIDEO) {
1662       /* remember first video stream -- needed for interleaving */
1663       if (lFirstVideo < 0)
1664         lFirstVideo = curStream;
1665     } else if (!dwInterleave && plpOptions != NULL) {
1666       /* check if any non-video stream wants to be interleaved */
1667       WARN("options.flags=0x%lX options.dwInterleave=%lu\n",plpOptions[curStream]->dwFlags,plpOptions[curStream]->dwInterleaveEvery);
1668       if (plpOptions[curStream] != NULL &&
1669           plpOptions[curStream]->dwFlags & AVICOMPRESSF_INTERLEAVE)
1670         dwInterleave = plpOptions[curStream]->dwInterleaveEvery;
1671     }
1672
1673     /* create de-/compressed stream interface if needed */
1674     pInStreams[curStream] = NULL;
1675     if (plpOptions != NULL && plpOptions[curStream] != NULL) {
1676       if (plpOptions[curStream]->fccHandler ||
1677           plpOptions[curStream]->lpFormat != NULL) {
1678         DWORD dwKeySave = plpOptions[curStream]->dwKeyFrameEvery;
1679
1680         if (fInfo.dwCaps & AVIFILECAPS_ALLKEYFRAMES)
1681           plpOptions[curStream]->dwKeyFrameEvery = 1;
1682
1683         hres = AVIMakeCompressedStream(&pInStreams[curStream], pCurStream,
1684                                        plpOptions[curStream], NULL);
1685         plpOptions[curStream]->dwKeyFrameEvery = dwKeySave;
1686         if (FAILED(hres) || pInStreams[curStream] == NULL) {
1687           pInStreams[curStream] = NULL;
1688           goto error;
1689         }
1690
1691         /* test stream interface and update stream-info */
1692         hres = AVIStreamInfoW(pInStreams[curStream], &sInfo, sizeof(sInfo));
1693         if (FAILED(hres))
1694           goto error;
1695       }
1696     }
1697
1698     /* now handle streams which will only be copied */
1699     if (pInStreams[curStream] == NULL) {
1700       pCurStream = pInStreams[curStream] = ppavi[curStream];
1701       AVIStreamAddRef(pCurStream);
1702     } else
1703       pCurStream = pInStreams[curStream];
1704
1705     lStart[curStream] = sInfo.dwStart;
1706   } /* for all streams */
1707
1708   /* check that first video stream is the first stream */
1709   if (lFirstVideo > 0) {
1710     PAVISTREAM pTmp = pInStreams[lFirstVideo];
1711     LONG lTmp = lStart[lFirstVideo];
1712
1713     pInStreams[lFirstVideo] = pInStreams[0];
1714     pInStreams[0] = pTmp;
1715     lStart[lFirstVideo] = lStart[0];
1716     lStart[0] = lTmp;
1717     lFirstVideo = 0;
1718   }
1719
1720   /* allocate buffer for formats, data, etc. of an initiale size of 64 kByte */
1721   lpBuffer = GlobalAllocPtr(GPTR, cbBuffer = 0x00010000);
1722   if (lpBuffer == NULL) {
1723     hres = AVIERR_MEMORY;
1724     goto error;
1725   }
1726
1727   AVIStreamInfoW(pInStreams[0], &sInfo, sizeof(sInfo));
1728   lFileLength = sInfo.dwLength;
1729   dwFileInitialFrames = 0;
1730   if (lFirstVideo >= 0) {
1731     /* check for correct version of the format
1732      *  -- need atleast BITMAPINFOHEADER or newer
1733      */
1734     lSampleInc = 1;
1735     lBufferSize = cbBuffer;
1736     hres = AVIStreamReadFormat(pInStreams[lFirstVideo], AVIStreamStart(pInStreams[lFirstVideo]), lpBuffer, &lBufferSize);
1737     if (lBufferSize < (LONG)sizeof(BITMAPINFOHEADER))
1738       hres = AVIERR_INTERNAL;
1739     if (FAILED(hres))
1740       goto error;
1741   } else /* use one second blocks for interleaving if no video present */
1742     lSampleInc = AVIStreamTimeToSample(pInStreams[0], 1000000);
1743
1744   /* create output streams */
1745   for (curStream = 0; curStream < nStreams; curStream++) {
1746     AVIStreamInfoW(pInStreams[curStream], &sInfo, sizeof(sInfo));
1747
1748     sInfo.dwInitialFrames = 0;
1749     if (dwInterleave != 0 && curStream > 0 && sInfo.fccType != streamtypeVIDEO) {
1750       /* 750 ms initial frames for non-video streams */
1751       sInfo.dwInitialFrames = AVIStreamTimeToSample(pInStreams[0], 750);
1752     }
1753
1754     hres = AVIFileCreateStreamW(pfile, &pOutStreams[curStream], &sInfo);
1755     if (pOutStreams[curStream] != NULL && SUCCEEDED(hres)) {
1756       /* copy initial format for this stream */
1757       lBufferSize = cbBuffer;
1758       hres = AVIStreamReadFormat(pInStreams[curStream], sInfo.dwStart,
1759                                  lpBuffer, &lBufferSize);
1760       if (FAILED(hres))
1761         goto error;
1762       hres = AVIStreamSetFormat(pOutStreams[curStream], 0, lpBuffer, lBufferSize);
1763       if (FAILED(hres))
1764         goto error;
1765
1766       /* try to copy stream handler data */
1767       lBufferSize = cbBuffer;
1768       hres = AVIStreamReadData(pInStreams[curStream], ckidSTREAMHANDLERDATA,
1769                                lpBuffer, &lBufferSize);
1770       if (SUCCEEDED(hres) && lBufferSize > 0) {
1771         hres = AVIStreamWriteData(pOutStreams[curStream],ckidSTREAMHANDLERDATA,
1772                                   lpBuffer, lBufferSize);
1773         if (FAILED(hres))
1774           goto error;
1775       }
1776
1777       if (dwFileInitialFrames < sInfo.dwInitialFrames)
1778         dwFileInitialFrames = sInfo.dwInitialFrames;
1779       lReadBytes =
1780         AVIStreamSampleToSample(pOutStreams[0], pInStreams[curStream],
1781                                 sInfo.dwLength);
1782       if (lFileLength < lReadBytes)
1783         lFileLength = lReadBytes;
1784     } else {
1785       /* creation of de-/compression stream interface failed */
1786       WARN("creation of (de-)compression stream failed for stream %d\n",curStream);
1787       AVIStreamRelease(pInStreams[curStream]);
1788       if (curStream + 1 >= nStreams) {
1789         /* move the others one up */
1790         PAVISTREAM *ppas = &pInStreams[curStream];
1791         int            n = nStreams - (curStream + 1);
1792
1793         do {
1794           *ppas = pInStreams[curStream + 1];
1795         } while (--n);
1796       }
1797       nStreams--;
1798       curStream--;
1799     }
1800   } /* create output streams for all input streams */
1801
1802   /* have we still something to write, or lost everything? */
1803   if (nStreams <= 0)
1804     goto error;
1805
1806   if (dwInterleave) {
1807     LONG lCurFrame = -dwFileInitialFrames;
1808
1809     /* interleaved file */
1810     if (dwInterleave == 1)
1811       AVIFileEndRecord(pfile);
1812
1813     for (; lCurFrame < lFileLength; lCurFrame += lSampleInc) {
1814       for (curStream = 0; curStream < nStreams; curStream++) {
1815         LONG lLastSample;
1816
1817         hres = AVIStreamInfoW(pOutStreams[curStream], &sInfo, sizeof(sInfo));
1818         if (FAILED(hres))
1819           goto error;
1820
1821         /* initial frames phase at the end for this stream? */
1822         if (-(LONG)sInfo.dwInitialFrames > lCurFrame)
1823           continue;
1824
1825         if ((lFileLength - lSampleInc) <= lCurFrame) {
1826           lLastSample = AVIStreamLength(pInStreams[curStream]);
1827           lFirstVideo = lLastSample + AVIStreamStart(pInStreams[curStream]);
1828         } else {
1829           if (curStream != 0) {
1830             lFirstVideo =
1831               AVIStreamSampleToSample(pInStreams[curStream], pInStreams[0],
1832                                       (sInfo.fccType == streamtypeVIDEO ? 
1833                                        (LONG)dwInterleave : lSampleInc) +
1834                                       sInfo.dwInitialFrames + lCurFrame);
1835           } else
1836             lFirstVideo = lSampleInc + (sInfo.dwInitialFrames + lCurFrame);
1837
1838           lLastSample = AVIStreamEnd(pInStreams[curStream]);
1839           if (lLastSample <= lFirstVideo)
1840             lFirstVideo = lLastSample;
1841         }
1842
1843         /* copy needed samples now */
1844         WARN("copy from stream %d samples %ld to %ld...\n",curStream,
1845               lStart[curStream],lFirstVideo);
1846         while (lFirstVideo > lStart[curStream]) {
1847           DWORD flags = 0;
1848
1849           /* copy format for case it can change */
1850           lBufferSize = cbBuffer;
1851           hres = AVIStreamReadFormat(pInStreams[curStream], lStart[curStream],
1852                                      lpBuffer, &lBufferSize);
1853           if (FAILED(hres))
1854             goto error;
1855           AVIStreamSetFormat(pOutStreams[curStream], lStart[curStream],
1856                              lpBuffer, lBufferSize);
1857
1858           /* try to read data until we got it, or error */
1859           do {
1860             hres = AVIStreamRead(pInStreams[curStream], lStart[curStream],
1861                                  lFirstVideo - lStart[curStream], lpBuffer,
1862                                  cbBuffer, &lReadBytes, &lReadSamples);
1863           } while ((hres == AVIERR_BUFFERTOOSMALL) &&
1864                    (lpBuffer = GlobalReAllocPtr(lpBuffer, cbBuffer *= 2, GPTR)) != NULL);
1865           if (lpBuffer == NULL)
1866             hres = AVIERR_MEMORY;
1867           if (FAILED(hres))
1868             goto error;
1869
1870           if (AVIStreamIsKeyFrame(pInStreams[curStream], (LONG)sInfo.dwStart))
1871             flags = AVIIF_KEYFRAME;
1872           hres = AVIStreamWrite(pOutStreams[curStream], -1, lReadSamples,
1873                                 lpBuffer, lReadBytes, flags, NULL, NULL);
1874           if (FAILED(hres))
1875             goto error;
1876
1877           lStart[curStream] += lReadSamples;
1878         }
1879         lStart[curStream] = lFirstVideo;
1880       } /* stream by stream */
1881
1882       /* need to close this block? */
1883       if (dwInterleave == 1) {
1884         hres = AVIFileEndRecord(pfile);
1885         if (FAILED(hres))
1886           break;
1887       }
1888
1889       /* show progress */
1890       if (lpfnCallback(MulDiv(dwFileInitialFrames + lCurFrame, 100,
1891                               dwFileInitialFrames + lFileLength))) {
1892         hres = AVIERR_USERABORT;
1893         break;
1894       }
1895     } /* copy frame by frame */
1896   } else {
1897     /* non-interleaved file */
1898
1899     for (curStream = 0; curStream < nStreams; curStream++) {
1900       /* show progress */
1901       if (lpfnCallback(MulDiv(curStream, 100, nStreams))) {
1902         hres = AVIERR_USERABORT;
1903         goto error;
1904       }
1905
1906       AVIStreamInfoW(pInStreams[curStream], &sInfo, sizeof(sInfo));
1907
1908       if (sInfo.dwSampleSize != 0) {
1909         /* sample-based data like audio */
1910         while (sInfo.dwStart < sInfo.dwLength) {
1911           LONG lSamples = cbBuffer / sInfo.dwSampleSize;
1912
1913           /* copy format for case it can change */
1914           lBufferSize = cbBuffer;
1915           hres = AVIStreamReadFormat(pInStreams[curStream], sInfo.dwStart,
1916                                      lpBuffer, &lBufferSize);
1917           if (FAILED(hres))
1918             return hres;
1919           AVIStreamSetFormat(pOutStreams[curStream], sInfo.dwStart,
1920                              lpBuffer, lBufferSize);
1921
1922           /* limit to stream boundaries */
1923           if (lSamples != (LONG)(sInfo.dwLength - sInfo.dwStart))
1924             lSamples = sInfo.dwLength - sInfo.dwStart;
1925
1926           /* now try to read until we got it, or error occures */
1927           do {
1928             lReadBytes   = cbBuffer;
1929             lReadSamples = 0;
1930             hres = AVIStreamRead(pInStreams[curStream],sInfo.dwStart,lSamples,
1931                                  lpBuffer,cbBuffer,&lReadBytes,&lReadSamples);
1932           } while ((hres == AVIERR_BUFFERTOOSMALL) &&
1933                    (lpBuffer = GlobalReAllocPtr(lpBuffer, cbBuffer *= 2, GPTR)) != NULL);
1934           if (lpBuffer == NULL)
1935             hres = AVIERR_MEMORY;
1936           if (FAILED(hres))
1937             goto error;
1938           if (lReadSamples != 0) {
1939             sInfo.dwStart += lReadSamples;
1940             hres = AVIStreamWrite(pOutStreams[curStream], -1, lReadSamples,
1941                                   lpBuffer, lReadBytes, 0, NULL , NULL);
1942             if (FAILED(hres))
1943               goto error;
1944
1945             /* show progress */
1946             if (lpfnCallback(MulDiv(sInfo.dwStart,100,nStreams*sInfo.dwLength)+
1947                              MulDiv(curStream, 100, nStreams))) {
1948               hres = AVIERR_USERABORT;
1949               goto error;
1950             }
1951           } else {
1952             if ((sInfo.dwLength - sInfo.dwStart) != 1) {
1953               hres = AVIERR_FILEREAD;
1954               goto error;
1955             }
1956           }
1957         }
1958       } else {
1959         /* block-based data like video */
1960         for (; sInfo.dwStart < sInfo.dwLength; sInfo.dwStart++) {
1961           DWORD flags = 0;
1962
1963           /* copy format for case it can change */
1964           lBufferSize = cbBuffer;
1965           hres = AVIStreamReadFormat(pInStreams[curStream], sInfo.dwStart,
1966                                      lpBuffer, &lBufferSize);
1967           if (FAILED(hres))
1968             goto error;
1969           AVIStreamSetFormat(pOutStreams[curStream], sInfo.dwStart,
1970                              lpBuffer, lBufferSize);
1971
1972           /* try to read block and resize buffer if necessary */
1973           do {
1974             lReadSamples = 0;
1975             lReadBytes   = cbBuffer;
1976             hres = AVIStreamRead(pInStreams[curStream], sInfo.dwStart, 1,
1977                                  lpBuffer, cbBuffer,&lReadBytes,&lReadSamples);
1978           } while ((hres == AVIERR_BUFFERTOOSMALL) &&
1979                    (lpBuffer = GlobalReAllocPtr(lpBuffer, cbBuffer *= 2, GPTR)) != NULL);
1980           if (lpBuffer == NULL)
1981             hres = AVIERR_MEMORY;
1982           if (FAILED(hres))
1983             goto error;
1984           if (lReadSamples != 1) {
1985             hres = AVIERR_FILEREAD;
1986             goto error;
1987           }
1988
1989           if (AVIStreamIsKeyFrame(pInStreams[curStream], (LONG)sInfo.dwStart))
1990             flags = AVIIF_KEYFRAME;
1991           hres = AVIStreamWrite(pOutStreams[curStream], -1, lReadSamples,
1992                                 lpBuffer, lReadBytes, flags, NULL, NULL);
1993           if (FAILED(hres))
1994             goto error;
1995
1996           /* show progress */
1997           if (lpfnCallback(MulDiv(sInfo.dwStart,100,nStreams*sInfo.dwLength)+
1998                            MulDiv(curStream, 100, nStreams))) {
1999             hres = AVIERR_USERABORT;
2000             goto error;
2001           }
2002         } /* copy all blocks */
2003       }
2004     } /* copy data stream by stream */
2005   }
2006
2007  error:
2008   if (lpBuffer != NULL)
2009     GlobalFreePtr(lpBuffer);
2010   if (pfile != NULL) {
2011     for (curStream = 0; curStream < nStreams; curStream++) {
2012       if (pOutStreams[curStream] != NULL)
2013         AVIStreamRelease(pOutStreams[curStream]);
2014       if (pInStreams[curStream] != NULL)
2015         AVIStreamRelease(pInStreams[curStream]);
2016     }
2017
2018     AVIFileRelease(pfile);
2019   }
2020
2021   return hres;
2022 }
2023
2024 /***********************************************************************
2025  *              CreateEditableStream    (AVIFIL32.@)
2026  */
2027 HRESULT WINAPI CreateEditableStream(PAVISTREAM *ppEditable, PAVISTREAM pSource)
2028 {
2029   IAVIEditStream *pEdit = NULL;
2030   HRESULT         hr;
2031
2032   TRACE("(%p,%p)\n", ppEditable, pSource);
2033
2034   if (ppEditable == NULL)
2035     return AVIERR_BADPARAM;
2036
2037   *ppEditable = NULL;
2038
2039   if (pSource != NULL) {
2040     hr = IAVIStream_QueryInterface(pSource, &IID_IAVIEditStream,
2041                                    (LPVOID*)&pEdit);
2042     if (SUCCEEDED(hr) && pEdit != NULL) {
2043       hr = IAVIEditStream_Clone(pEdit, ppEditable);
2044       IAVIEditStream_Release(pEdit);
2045
2046       return hr;
2047     }
2048   }
2049
2050   /* need own implementation of IAVIEditStream */
2051   pEdit = AVIFILE_CreateEditStream(pSource);
2052   if (pEdit == NULL)
2053     return AVIERR_MEMORY;
2054
2055   hr = IAVIEditStream_QueryInterface(pEdit, &IID_IAVIStream,
2056                                      (LPVOID*)ppEditable);
2057   IAVIEditStream_Release(pEdit);
2058
2059   return hr;
2060 }
2061
2062 /***********************************************************************
2063  *              EditStreamClone         (AVIFIL32.@)
2064  */
2065 HRESULT WINAPI EditStreamClone(PAVISTREAM pStream, PAVISTREAM *ppResult)
2066 {
2067   PAVIEDITSTREAM pEdit = NULL;
2068   HRESULT        hr;
2069
2070   TRACE("(%p,%p)\n", pStream, ppResult);
2071
2072   if (pStream == NULL)
2073     return AVIERR_BADHANDLE;
2074   if (ppResult == NULL)
2075     return AVIERR_BADPARAM;
2076
2077   *ppResult = NULL;
2078
2079   hr = IAVIStream_QueryInterface(pStream, &IID_IAVIEditStream,(LPVOID*)&pEdit);
2080   if (SUCCEEDED(hr) && pEdit != NULL) {
2081     hr = IAVIEditStream_Clone(pEdit, ppResult);
2082
2083     IAVIEditStream_Release(pEdit);
2084   } else
2085     hr = AVIERR_UNSUPPORTED;
2086
2087   return hr;
2088 }
2089
2090 /***********************************************************************
2091  *              EditStreamCopy          (AVIFIL32.@)
2092  */
2093 HRESULT WINAPI EditStreamCopy(PAVISTREAM pStream, LONG *plStart,
2094                               LONG *plLength, PAVISTREAM *ppResult)
2095 {
2096   PAVIEDITSTREAM pEdit = NULL;
2097   HRESULT        hr;
2098
2099   TRACE("(%p,%p,%p,%p)\n", pStream, plStart, plLength, ppResult);
2100
2101   if (pStream == NULL)
2102     return AVIERR_BADHANDLE;
2103   if (plStart == NULL || plLength == NULL || ppResult == NULL)
2104     return AVIERR_BADPARAM;
2105
2106   *ppResult = NULL;
2107
2108   hr = IAVIStream_QueryInterface(pStream, &IID_IAVIEditStream,(LPVOID*)&pEdit);
2109   if (SUCCEEDED(hr) && pEdit != NULL) {
2110     hr = IAVIEditStream_Copy(pEdit, plStart, plLength, ppResult);
2111
2112     IAVIEditStream_Release(pEdit);
2113   } else
2114     hr = AVIERR_UNSUPPORTED;
2115
2116   return hr;
2117 }
2118
2119 /***********************************************************************
2120  *              EditStreamCut           (AVIFIL32.@)
2121  */
2122 HRESULT WINAPI EditStreamCut(PAVISTREAM pStream, LONG *plStart,
2123                              LONG *plLength, PAVISTREAM *ppResult)
2124 {
2125   PAVIEDITSTREAM pEdit = NULL;
2126   HRESULT        hr;
2127
2128   TRACE("(%p,%p,%p,%p)\n", pStream, plStart, plLength, ppResult);
2129
2130   if (ppResult != NULL)
2131     *ppResult = NULL;
2132   if (pStream == NULL)
2133     return AVIERR_BADHANDLE;
2134   if (plStart == NULL || plLength == NULL)
2135     return AVIERR_BADPARAM;
2136
2137   hr = IAVIStream_QueryInterface(pStream, &IID_IAVIEditStream,(LPVOID*)&pEdit);
2138   if (SUCCEEDED(hr) && pEdit != NULL) {
2139     hr = IAVIEditStream_Cut(pEdit, plStart, plLength, ppResult);
2140
2141     IAVIEditStream_Release(pEdit);
2142   } else
2143     hr = AVIERR_UNSUPPORTED;
2144
2145   return hr;
2146 }
2147
2148 /***********************************************************************
2149  *              EditStreamPaste         (AVIFIL32.@)
2150  */
2151 HRESULT WINAPI EditStreamPaste(PAVISTREAM pDest, LONG *plStart, LONG *plLength,
2152                                PAVISTREAM pSource, LONG lStart, LONG lEnd)
2153 {
2154   PAVIEDITSTREAM pEdit = NULL;
2155   HRESULT        hr;
2156
2157   TRACE("(%p,%p,%p,%p,%ld,%ld)\n", pDest, plStart, plLength,
2158         pSource, lStart, lEnd);
2159
2160   if (pDest == NULL || pSource == NULL)
2161     return AVIERR_BADHANDLE;
2162   if (plStart == NULL || plLength == NULL || lStart < 0)
2163     return AVIERR_BADPARAM;
2164
2165   hr = IAVIStream_QueryInterface(pDest, &IID_IAVIEditStream,(LPVOID*)&pEdit);
2166   if (SUCCEEDED(hr) && pEdit != NULL) {
2167     hr = IAVIEditStream_Paste(pEdit, plStart, plLength, pSource, lStart, lEnd);
2168
2169     IAVIEditStream_Release(pEdit);
2170   } else
2171     hr = AVIERR_UNSUPPORTED;
2172
2173   return hr;
2174 }
2175
2176 /***********************************************************************
2177  *              EditStreamSetInfoA      (AVIFIL32.@)
2178  */
2179 HRESULT WINAPI EditStreamSetInfoA(PAVISTREAM pstream, LPAVISTREAMINFOA asi,
2180                                   LONG size)
2181 {
2182   AVISTREAMINFOW asiw;
2183
2184   TRACE("(%p,%p,%ld)\n", pstream, asi, size);
2185
2186   if (pstream == NULL)
2187     return AVIERR_BADHANDLE;
2188   if ((DWORD)size < sizeof(AVISTREAMINFOA))
2189     return AVIERR_BADSIZE;
2190
2191   memcpy(&asiw, asi, sizeof(asiw) - sizeof(asiw.szName));
2192   MultiByteToWideChar(CP_ACP, 0, asi->szName, -1,
2193                       asiw.szName, sizeof(asiw.szName));
2194
2195   return EditStreamSetInfoW(pstream, &asiw, sizeof(asiw));
2196 }
2197
2198 /***********************************************************************
2199  *              EditStreamSetInfoW      (AVIFIL32.@)
2200  */
2201 HRESULT WINAPI EditStreamSetInfoW(PAVISTREAM pstream, LPAVISTREAMINFOW asi,
2202                                   LONG size)
2203 {
2204   PAVIEDITSTREAM pEdit = NULL;
2205   HRESULT        hr;
2206
2207   TRACE("(%p,%p,%ld)\n", pstream, asi, size);
2208
2209   hr = IAVIStream_QueryInterface(pstream, &IID_IAVIEditStream,(LPVOID*)&pEdit);
2210   if (SUCCEEDED(hr) && pEdit != NULL) {
2211     hr = IAVIEditStream_SetInfo(pEdit, asi, size);
2212
2213     IAVIEditStream_Release(pEdit);
2214   } else
2215     hr = AVIERR_UNSUPPORTED;
2216
2217   return hr;
2218 }
2219
2220 /***********************************************************************
2221  *              EditStreamSetNameA      (AVIFIL32.@)
2222  */
2223 HRESULT WINAPI EditStreamSetNameA(PAVISTREAM pstream, LPCSTR szName)
2224 {
2225   AVISTREAMINFOA asia;
2226   HRESULT        hres;
2227
2228   TRACE("(%p,%s)\n", pstream, debugstr_a(szName));
2229
2230   if (pstream == NULL)
2231     return AVIERR_BADHANDLE;
2232   if (szName == NULL)
2233     return AVIERR_BADPARAM;
2234
2235   hres = AVIStreamInfoA(pstream, &asia, sizeof(asia));
2236   if (FAILED(hres))
2237     return hres;
2238
2239   memset(asia.szName, 0, sizeof(asia.szName));
2240   lstrcpynA(asia.szName, szName, sizeof(asia.szName)/sizeof(asia.szName[0]));
2241
2242   return EditStreamSetInfoA(pstream, &asia, sizeof(asia));
2243 }
2244
2245 /***********************************************************************
2246  *              EditStreamSetNameW      (AVIFIL32.@)
2247  */
2248 HRESULT WINAPI EditStreamSetNameW(PAVISTREAM pstream, LPCWSTR szName)
2249 {
2250   AVISTREAMINFOW asiw;
2251   HRESULT        hres;
2252
2253   TRACE("(%p,%s)\n", pstream, debugstr_w(szName));
2254
2255   if (pstream == NULL)
2256     return AVIERR_BADHANDLE;
2257   if (szName == NULL)
2258     return AVIERR_BADPARAM;
2259
2260   hres = IAVIStream_Info(pstream, &asiw, sizeof(asiw));
2261   if (FAILED(hres))
2262     return hres;
2263
2264   memset(asiw.szName, 0, sizeof(asiw.szName));
2265   lstrcpynW(asiw.szName, szName, sizeof(asiw.szName)/sizeof(asiw.szName[0]));
2266
2267   return EditStreamSetInfoW(pstream, &asiw, sizeof(asiw));
2268 }
2269
2270 /***********************************************************************
2271  *              AVIClearClipboard       (AVIFIL32.@)
2272  */
2273 HRESULT WINAPI AVIClearClipboard(void)
2274 {
2275   TRACE("()\n");
2276
2277   return AVIERR_UNSUPPORTED; /* OleSetClipboard(NULL); */
2278 }
2279
2280 /***********************************************************************
2281  *              AVIGetFromClipboard     (AVIFIL32.@)
2282  */
2283 HRESULT WINAPI AVIGetFromClipboard(PAVIFILE *ppfile)
2284 {
2285   FIXME("(%p), stub!\n", ppfile);
2286
2287   *ppfile = NULL;
2288
2289   return AVIERR_UNSUPPORTED;
2290 }
2291
2292 /***********************************************************************
2293  *              AVIPutFileOnClipboard   (AVIFIL32.@)
2294  */
2295 HRESULT WINAPI AVIPutFileOnClipboard(PAVIFILE pfile)
2296 {
2297   FIXME("(%p), stub!\n", pfile);
2298
2299   if (pfile == NULL)
2300     return AVIERR_BADHANDLE;
2301
2302   return AVIERR_UNSUPPORTED;
2303 }