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