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