Fix error reporting in wavemap.c:wodOpen(); this solves the
[wine] / dlls / avifil32 / wavfile.c
1 /*
2  * Copyright 2002 Michael Günnewig
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19 #include <assert.h>
20
21 #include "winbase.h"
22 #include "winuser.h"
23 #include "winnls.h"
24 #include "winerror.h"
25 #include "windowsx.h"
26 #include "mmsystem.h"
27 #include "vfw.h"
28 #include "msacm.h"
29
30 #include "avifile_private.h"
31 #include "extrachunk.h"
32
33 #include "wine/debug.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
36
37 /***********************************************************************/
38
39 #define formtypeWAVE    mmioFOURCC('W','A','V','E')
40 #define ckidWAVEFORMAT  mmioFOURCC('f','m','t',' ')
41 #define ckidWAVEFACT    mmioFOURCC('f','a','c','t')
42 #define ckidWAVEDATA    mmioFOURCC('d','a','t','a')
43
44 /***********************************************************************/
45
46 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile* iface,REFIID refiid,LPVOID *obj);
47 static ULONG   WINAPI IAVIFile_fnAddRef(IAVIFile* iface);
48 static ULONG   WINAPI IAVIFile_fnRelease(IAVIFile* iface);
49 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile*iface,AVIFILEINFOW*afi,LONG size);
50 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile*iface,PAVISTREAM*avis,DWORD fccType,LONG lParam);
51 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile*iface,PAVISTREAM*avis,AVISTREAMINFOW*asi);
52 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG size);
53 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG *size);
54 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile*iface);
55 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile*iface,DWORD fccType,LONG lParam);
56
57 struct ICOM_VTABLE(IAVIFile) iwavft = {
58   ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
59   IAVIFile_fnQueryInterface,
60   IAVIFile_fnAddRef,
61   IAVIFile_fnRelease,
62   IAVIFile_fnInfo,
63   IAVIFile_fnGetStream,
64   IAVIFile_fnCreateStream,
65   IAVIFile_fnWriteData,
66   IAVIFile_fnReadData,
67   IAVIFile_fnEndRecord,
68   IAVIFile_fnDeleteStream
69 };
70
71 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile*iface,REFIID refiid,LPVOID*obj);
72 static ULONG   WINAPI IPersistFile_fnAddRef(IPersistFile*iface);
73 static ULONG   WINAPI IPersistFile_fnRelease(IPersistFile*iface);
74 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile*iface,CLSID*pClassID);
75 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile*iface);
76 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile*iface,LPCOLESTR pszFileName,DWORD dwMode);
77 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile*iface,LPCOLESTR pszFileName,BOOL fRemember);
78 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile*iface,LPCOLESTR pszFileName);
79 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile*iface,LPOLESTR*ppszFileName);
80
81 struct ICOM_VTABLE(IPersistFile) iwavpft = {
82   ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
83   IPersistFile_fnQueryInterface,
84   IPersistFile_fnAddRef,
85   IPersistFile_fnRelease,
86   IPersistFile_fnGetClassID,
87   IPersistFile_fnIsDirty,
88   IPersistFile_fnLoad,
89   IPersistFile_fnSave,
90   IPersistFile_fnSaveCompleted,
91   IPersistFile_fnGetCurFile
92 };
93
94 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj);
95 static ULONG   WINAPI IAVIStream_fnAddRef(IAVIStream*iface);
96 static ULONG   WINAPI IAVIStream_fnRelease(IAVIStream* iface);
97 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2);
98 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size);
99 static LONG    WINAPI IAVIStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags);
100 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize);
101 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize);
102 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread);
103 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten);
104 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream*iface,LONG start,LONG samples);
105 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread);
106 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size);
107 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen);
108
109 struct ICOM_VTABLE(IAVIStream) iwavst = {
110   ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
111   IAVIStream_fnQueryInterface,
112   IAVIStream_fnAddRef,
113   IAVIStream_fnRelease,
114   IAVIStream_fnCreate,
115   IAVIStream_fnInfo,
116   IAVIStream_fnFindSample,
117   IAVIStream_fnReadFormat,
118   IAVIStream_fnSetFormat,
119   IAVIStream_fnRead,
120   IAVIStream_fnWrite,
121   IAVIStream_fnDelete,
122   IAVIStream_fnReadData,
123   IAVIStream_fnWriteData,
124   IAVIStream_fnSetInfo
125 };
126
127 typedef struct _IAVIFileImpl IAVIFileImpl;
128
129 typedef struct _IPersistFileImpl {
130   /* IUnknown stuff */
131   ICOM_VFIELD(IPersistFile);
132
133   /* IPersistFile stuff */
134   IAVIFileImpl     *paf;
135 } IPersistFileImpl;
136
137 typedef struct _IAVIStreamImpl {
138   /* IUnknown stuff */
139   ICOM_VFIELD(IAVIStream);
140
141   /* IAVIStream stuff */
142   IAVIFileImpl     *paf;
143 } IAVIStreamImpl;
144
145 struct _IAVIFileImpl {
146   /* IUnknown stuff */
147   ICOM_VFIELD(IAVIFile);
148   DWORD             ref;
149
150   /* IAVIFile, IAVIStream stuff... */
151   IPersistFileImpl  iPersistFile;
152   IAVIStreamImpl    iAVIStream;
153
154   AVIFILEINFOW      fInfo;
155   AVISTREAMINFOW    sInfo;
156
157   LPWAVEFORMATEX    lpFormat;
158   LONG              cbFormat;
159
160   MMCKINFO          ckData;
161
162   EXTRACHUNKS       extra;
163
164   /* IPersistFile stuff ... */
165   HMMIO             hmmio;
166   LPWSTR            szFileName;
167   UINT              uMode;
168   BOOL              fDirty;
169 };
170
171 /***********************************************************************/
172
173 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This);
174 static HRESULT AVIFILE_LoadSunFile(IAVIFileImpl *This);
175 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This);
176
177 HRESULT AVIFILE_CreateWAVFile(REFIID riid, LPVOID *ppv)
178 {
179   IAVIFileImpl *pfile;
180   HRESULT       hr;
181
182   assert(riid != NULL && ppv != NULL);
183
184   *ppv = NULL;
185
186   pfile = (IAVIFileImpl*)LocalAlloc(LPTR, sizeof(IAVIFileImpl));
187   if (pfile == NULL)
188     return AVIERR_MEMORY;
189
190   ICOM_VTBL(pfile)                = &iwavft;
191   ICOM_VTBL(&pfile->iPersistFile) = &iwavpft;
192   ICOM_VTBL(&pfile->iAVIStream)   = &iwavst;
193   pfile->ref = 0;
194   pfile->iPersistFile.paf = pfile;
195   pfile->iAVIStream.paf   = pfile;
196
197   hr = IUnknown_QueryInterface((IUnknown*)pfile, riid, ppv);
198   if (FAILED(hr))
199     LocalFree((HLOCAL)pfile);
200
201   return hr;
202 }
203
204 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID refiid,
205                                                 LPVOID *obj)
206 {
207   ICOM_THIS(IAVIFileImpl,iface);
208
209   TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj);
210
211   if (IsEqualGUID(&IID_IUnknown, refiid) ||
212       IsEqualGUID(&IID_IAVIFile, refiid)) {
213     *obj = iface;
214     return S_OK;
215   } else if (This->fInfo.dwStreams == 1 &&
216              IsEqualGUID(&IID_IAVIStream, refiid)) {
217     *obj = &This->iAVIStream;
218     return S_OK;
219   } else if (IsEqualGUID(&IID_IPersistFile, refiid)) {
220     *obj = &This->iPersistFile;
221     return S_OK;
222   }
223
224   return OLE_E_ENUM_NOMORE;
225 }
226
227 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface)
228 {
229   ICOM_THIS(IAVIFileImpl,iface);
230
231   TRACE("(%p)\n",iface);
232
233   return ++(This->ref);
234 }
235
236 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface)
237 {
238   ICOM_THIS(IAVIFileImpl,iface);
239
240   TRACE("(%p)\n",iface);
241
242   if (!--(This->ref)) {
243     if (This->fDirty) {
244       /* need to write headers to file */
245       AVIFILE_SaveFile(This);
246     }
247
248     if (This->lpFormat != NULL) {
249       GlobalFreePtr(This->lpFormat);
250       This->lpFormat = NULL;
251       This->cbFormat = 0;
252     }
253     if (This->extra.lp != NULL) {
254       GlobalFreePtr(This->extra.lp);
255       This->extra.lp = NULL;
256       This->extra.cb = 0;
257     }
258     if (This->szFileName != NULL) {
259       LocalFree((HLOCAL)This->szFileName);
260       This->szFileName = NULL;
261     }
262     if (This->hmmio != NULL) {
263       mmioClose(This->hmmio, 0);
264       This->hmmio = NULL;
265     }
266
267     LocalFree((HLOCAL)This);
268     return 0;
269   }
270   return This->ref;
271 }
272
273 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, LPAVIFILEINFOW afi,
274                                       LONG size)
275 {
276   ICOM_THIS(IAVIFileImpl,iface);
277
278   TRACE("(%p,%p,%ld)\n",iface,afi,size);
279
280   if (afi == NULL)
281     return AVIERR_BADPARAM;
282   if (size < 0)
283     return AVIERR_BADSIZE;
284
285   /* update file info */
286   This->fInfo.dwFlags = 0;
287   This->fInfo.dwCaps  = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
288   if (This->lpFormat != NULL) {
289     assert(This->sInfo.dwScale != 0);
290
291     This->fInfo.dwStreams             = 1;
292     This->fInfo.dwScale               = This->sInfo.dwScale;
293     This->fInfo.dwRate                = This->sInfo.dwRate;
294     This->fInfo.dwLength              = This->sInfo.dwLength;
295     This->fInfo.dwSuggestedBufferSize = This->ckData.cksize;
296     This->fInfo.dwMaxBytesPerSec =
297       MulDiv(This->sInfo.dwSampleSize,This->sInfo.dwRate,This->sInfo.dwScale);
298   }
299
300   memcpy(afi, &This->fInfo, min(size, sizeof(This->fInfo)));
301
302   if (size < sizeof(This->fInfo))
303     return AVIERR_BUFFERTOOSMALL;
304   return AVIERR_OK;
305 }
306
307 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, PAVISTREAM *avis,
308                                            DWORD fccType, LONG lParam)
309 {
310   ICOM_THIS(IAVIFileImpl,iface);
311
312   TRACE("(%p,%p,0x%08lX,%ld)\n", iface, avis, fccType, lParam);
313
314   /* check parameter */
315   if (avis == NULL)
316     return AVIERR_BADPARAM;
317
318   *avis = NULL;
319
320   /* Does our stream exists? */
321   if (lParam != 0 || This->fInfo.dwStreams == 0)
322     return AVIERR_NODATA;
323   if (fccType != 0 && fccType != streamtypeAUDIO)
324     return AVIERR_NODATA;
325
326   *avis = (PAVISTREAM)&This->iAVIStream;
327   IAVIFile_AddRef(iface);
328
329   return AVIERR_OK;
330 }
331
332 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface,PAVISTREAM *avis,
333                                               LPAVISTREAMINFOW asi)
334 {
335   ICOM_THIS(IAVIFileImpl,iface);
336
337   TRACE("(%p,%p,%p)\n", iface, avis, asi);
338
339   /* check parameters */
340   if (avis == NULL || asi == NULL)
341     return AVIERR_BADPARAM;
342
343   *avis = NULL;
344
345   /* We only support one audio stream */
346   if (This->fInfo.dwStreams != 0 || This->lpFormat != NULL)
347     return AVIERR_UNSUPPORTED;
348   if (asi->fccType != streamtypeAUDIO)
349     return AVIERR_UNSUPPORTED;
350
351   /* Does the user have write permission? */
352   if ((This->uMode & MMIO_RWMODE) == 0)
353     return AVIERR_READONLY;
354
355   This->cbFormat = 0;
356   This->lpFormat = NULL;
357
358   memcpy(&This->sInfo, asi, sizeof(This->sInfo));
359
360   /* make sure streaminfo if okay for us */
361   This->sInfo.fccHandler          = 0;
362   This->sInfo.dwFlags             = 0;
363   This->sInfo.dwCaps              = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
364   This->sInfo.dwStart             = 0;
365   This->sInfo.dwInitialFrames     = 0;
366   This->sInfo.dwFormatChangeCount = 0;
367   memset(&This->sInfo.rcFrame, 0, sizeof(This->sInfo.rcFrame));
368
369   This->fInfo.dwStreams = 1;
370   This->fInfo.dwScale   = This->sInfo.dwScale;
371   This->fInfo.dwRate    = This->sInfo.dwRate;
372   This->fInfo.dwLength  = This->sInfo.dwLength;
373
374   This->ckData.dwDataOffset = 0;
375   This->ckData.cksize       = 0;
376
377   *avis = (PAVISTREAM)&This->iAVIStream;
378   IAVIFile_AddRef(iface);
379
380   return AVIERR_OK;
381 }
382
383 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid,
384                                            LPVOID lpData, LONG size)
385 {
386   ICOM_THIS(IAVIFileImpl,iface);
387
388   TRACE("(%p,0x%08lX,%p,%ld)\n", iface, ckid, lpData, size);
389
390   /* check parameters */
391   if (lpData == NULL)
392     return AVIERR_BADPARAM;
393   if (size < 0)
394     return AVIERR_BADSIZE;
395
396   /* Do we have write permission? */
397   if ((This->uMode & MMIO_RWMODE) == 0)
398     return AVIERR_READONLY;
399
400   This->fDirty = TRUE;
401
402   return WriteExtraChunk(&This->extra, ckid, lpData, size);
403 }
404
405 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid,
406                                           LPVOID lpData, LONG *size)
407 {
408   ICOM_THIS(IAVIFileImpl,iface);
409
410   TRACE("(%p,0x%08lX,%p,%p)\n", iface, ckid, lpData, size);
411
412   return ReadExtraChunk(&This->extra, ckid, lpData, size);
413 }
414
415 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface)
416 {
417   TRACE("(%p)\n",iface);
418
419   /* This is only needed for interleaved files.
420    * We have only one stream, which can't be interleaved.
421    */
422   return AVIERR_OK;
423 }
424
425 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType,
426                                               LONG lParam)
427 {
428   ICOM_THIS(IAVIFileImpl,iface);
429
430   TRACE("(%p,0x%08lX,%ld)\n", iface, fccType, lParam);
431
432   /* check parameter */
433   if (lParam < 0)
434     return AVIERR_BADPARAM;
435
436   /* Have we our audio stream? */
437   if (lParam != 0 || This->fInfo.dwStreams == 0 ||
438       (fccType != 0 && fccType != streamtypeAUDIO))
439     return AVIERR_NODATA;
440
441   /* Have user write permissions? */
442   if ((This->uMode & MMIO_RWMODE) == 0)
443     return AVIERR_READONLY;
444
445   GlobalFreePtr(This->lpFormat);
446   This->lpFormat = NULL;
447   This->cbFormat = 0;
448
449   /* update infos */
450   This->ckData.dwDataOffset = 0;
451   This->ckData.cksize       = 0;
452
453   This->sInfo.dwScale   = 0;
454   This->sInfo.dwRate    = 0;
455   This->sInfo.dwLength  = 0;
456   This->sInfo.dwSuggestedBufferSize = 0;
457
458   This->fInfo.dwStreams = 0;
459   This->fInfo.dwEditCount++;
460
461   This->fDirty = TRUE;
462
463   return AVIERR_OK;
464 }
465
466 /***********************************************************************/
467
468 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface,
469                                                     REFIID refiid, LPVOID *obj)
470 {
471   ICOM_THIS(IPersistFileImpl,iface);
472
473   assert(This->paf != NULL);
474
475   return IAVIFile_QueryInterface((PAVIFILE)This->paf, refiid, obj);
476 }
477
478 static ULONG   WINAPI IPersistFile_fnAddRef(IPersistFile *iface)
479 {
480   ICOM_THIS(IPersistFileImpl,iface);
481
482   assert(This->paf != NULL);
483
484   return IAVIFile_AddRef((PAVIFILE)This->paf);
485 }
486
487 static ULONG   WINAPI IPersistFile_fnRelease(IPersistFile *iface)
488 {
489   ICOM_THIS(IPersistFileImpl,iface);
490
491   assert(This->paf != NULL);
492
493   return IAVIFile_Release((PAVIFILE)This->paf);
494 }
495
496 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface,
497                                                 LPCLSID pClassID)
498 {
499   TRACE("(%p,%p)\n", iface, pClassID);
500
501   if (pClassID == NULL)
502     return AVIERR_BADPARAM;
503
504   memcpy(pClassID, &CLSID_WAVFile, sizeof(CLSID_WAVFile));
505
506   return AVIERR_OK;
507 }
508
509 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface)
510 {
511   ICOM_THIS(IPersistFileImpl,iface);
512
513   TRACE("(%p)\n", iface);
514
515   assert(This->paf != NULL);
516
517   return (This->paf->fDirty ? S_OK : S_FALSE);
518 }
519
520 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface,
521                                           LPCOLESTR pszFileName, DWORD dwMode)
522 {
523   IAVIFileImpl *This = ((IPersistFileImpl*)iface)->paf;
524
525   WCHAR wszStreamFmt[50];
526   INT   len;
527
528   TRACE("(%p,%s,0x%08lX)\n", iface, debugstr_w(pszFileName), dwMode);
529
530   /* check parameter */
531   if (pszFileName == NULL)
532     return AVIERR_BADPARAM;
533
534   assert(This != NULL);
535   if (This->hmmio != NULL)
536     return AVIERR_ERROR; /* No reuse of this object for another file! */
537
538   /* remeber mode and name */
539   This->uMode = dwMode;
540
541   len = lstrlenW(pszFileName) + 1;
542   This->szFileName = (LPWSTR)LocalAlloc(LPTR, len * sizeof(WCHAR));
543   if (This->szFileName == NULL)
544     return AVIERR_MEMORY;
545   lstrcpyW(This->szFileName, pszFileName);
546
547   /* try to open the file */
548   This->hmmio = mmioOpenW(This->szFileName, NULL, MMIO_ALLOCBUF | dwMode);
549   if (This->hmmio == NULL)
550     return AVIERR_FILEOPEN;
551
552   memset(& This->fInfo, 0, sizeof(This->fInfo));
553   memset(& This->sInfo, 0, sizeof(This->sInfo));
554
555   LoadStringW(AVIFILE_hModule, IDS_WAVEFILETYPE, This->fInfo.szFileType,
556               sizeof(This->fInfo.szFileType));
557   if (LoadStringW(AVIFILE_hModule, IDS_WAVESTREAMFORMAT,
558                   wszStreamFmt, sizeof(wszStreamFmt)) > 0) {
559     wsprintfW(This->sInfo.szName, wszStreamFmt,
560               AVIFILE_BasenameW(This->szFileName));
561   }
562
563   /* should we create a new file? */
564   if (dwMode & OF_CREATE) {
565     /* nothing more to do */
566     return AVIERR_OK;
567   } else
568     return AVIFILE_LoadFile(This);
569 }
570
571 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface,
572                                           LPCOLESTR pszFileName,BOOL fRemember)
573 {
574   TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember);
575
576   /* We write directly to disk, so nothing to do. */
577
578   return AVIERR_OK;
579 }
580
581 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface,
582                                                    LPCOLESTR pszFileName)
583 {
584   TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName));
585
586   /* We write directly to disk, so nothing to do. */
587
588   return AVIERR_OK;
589 }
590
591 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface,
592                                                 LPOLESTR *ppszFileName)
593 {
594   ICOM_THIS(IPersistFileImpl,iface);
595
596   TRACE("(%p,%p)\n", iface, ppszFileName);
597
598   if (ppszFileName == NULL)
599     return AVIERR_BADPARAM;
600
601   *ppszFileName = NULL;
602
603   assert(This->paf != NULL);
604
605   if (This->paf->szFileName != NULL) {
606     int len = lstrlenW(This->paf->szFileName);
607
608     *ppszFileName = (LPOLESTR)GlobalAllocPtr(GHND, len * sizeof(WCHAR));
609     if (*ppszFileName == NULL)
610       return AVIERR_MEMORY;
611
612     memcpy(*ppszFileName, This->paf->szFileName, len * sizeof(WCHAR));
613   }
614
615   return AVIERR_OK;
616 }
617
618 /***********************************************************************/
619
620 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface,
621                                                   REFIID refiid, LPVOID *obj)
622 {
623   ICOM_THIS(IAVIStreamImpl,iface);
624
625   assert(This->paf != NULL);
626
627   return IAVIFile_QueryInterface((PAVIFILE)This->paf, refiid, obj);
628 }
629
630 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface)
631 {
632   ICOM_THIS(IAVIStreamImpl,iface);
633
634   assert(This->paf != NULL);
635
636   return IAVIFile_AddRef((PAVIFILE)This->paf);
637 }
638
639 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface)
640 {
641   ICOM_THIS(IAVIStreamImpl,iface);
642
643   assert(This->paf != NULL);
644
645   return IAVIFile_Release((PAVIFILE)This->paf);
646 }
647
648 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1,
649                                           LPARAM lParam2)
650 {
651   TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2);
652
653   /* This IAVIStream interface needs an WAVFile */
654   return AVIERR_UNSUPPORTED;
655 }
656
657 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi,
658                                         LONG size)
659 {
660   ICOM_THIS(IAVIStreamImpl,iface);
661
662   TRACE("(%p,%p,%ld)\n", iface, psi, size);
663
664   if (psi == NULL)
665     return AVIERR_BADPARAM;
666   if (size < 0)
667     return AVIERR_BADSIZE;
668
669   memcpy(psi, &This->paf->sInfo, min(size, sizeof(This->paf->sInfo)));
670
671   if (size < sizeof(This->paf->sInfo))
672     return AVIERR_BUFFERTOOSMALL;
673   return AVIERR_OK;
674 }
675
676 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos,
677                                            LONG flags)
678 {
679   IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf;
680
681   TRACE("(%p,%ld,0x%08lX)\n",iface,pos,flags);
682
683   /* Do we have data? */
684   if (This->lpFormat == NULL)
685     return -1;
686
687   /* We don't have an index */
688   if (flags & FIND_INDEX)
689     return -1;
690
691   if (flags & FIND_FROM_START) {
692     pos = This->sInfo.dwStart;
693     flags &= ~(FIND_FROM_START|FIND_PREV);
694     flags |= FIND_NEXT;
695   }
696
697   if (flags & FIND_FORMAT) {
698     if ((flags & FIND_NEXT) && pos > 0)
699       pos = -1;
700     else
701       pos = 0;
702   }
703
704   if (flags & (FIND_LENGTH|FIND_SIZE))
705     return This->sInfo.dwSampleSize;
706   if (flags & FIND_OFFSET)
707     return This->ckData.dwDataOffset + pos * This->sInfo.dwSampleSize;
708
709   return pos;
710 }
711
712 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos,
713                                               LPVOID format, LONG *formatsize)
714 {
715   ICOM_THIS(IAVIStreamImpl,iface);
716
717   TRACE("(%p,%ld,%p,%p)\n", iface, pos, format, formatsize);
718
719   if (formatsize == NULL)
720     return AVIERR_BADPARAM;
721
722   /* only interested in needed buffersize? */
723   if (format == NULL || *formatsize <= 0) {
724     *formatsize = This->paf->cbFormat;
725
726     return AVIERR_OK;
727   }
728
729   /* copy initial format (only as much as will fit) */
730   memcpy(format, This->paf->lpFormat, min(*formatsize, This->paf->cbFormat));
731   if (*formatsize < This->paf->cbFormat) {
732     *formatsize = This->paf->cbFormat;
733     return AVIERR_BUFFERTOOSMALL;
734   }
735
736   *formatsize = This->paf->cbFormat;
737   return AVIERR_OK;
738 }
739
740 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos,
741                                              LPVOID format, LONG formatsize)
742 {
743   IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf;
744
745   TRACE("(%p,%ld,%p,%ld)\n", iface, pos, format, formatsize);
746
747   /* check parameters */
748   if (format == NULL || formatsize <= sizeof(PCMWAVEFORMAT))
749     return AVIERR_BADPARAM;
750
751   /* We can only do this to an empty wave file, but ignore call
752    * if still same format */
753   if (This->lpFormat != NULL) {
754     if (formatsize != This->cbFormat ||
755         memcmp(format, This->lpFormat, formatsize) != 0)
756       return AVIERR_UNSUPPORTED;
757
758     return AVIERR_OK;
759   }
760
761   /* only support start at position 0 */
762   if (pos != 0)
763     return AVIERR_UNSUPPORTED;
764
765   /* Do we have write permission? */
766   if ((This->uMode & MMIO_RWMODE) == 0)
767     return AVIERR_READONLY;
768
769   /* get memory for format and copy it */
770   This->lpFormat = (LPWAVEFORMATEX)GlobalAllocPtr(GMEM_MOVEABLE, formatsize);
771   if (This->lpFormat == NULL)
772     return AVIERR_MEMORY;
773
774   This->cbFormat = formatsize;
775   memcpy(This->lpFormat, format, formatsize);
776
777   /* update info's about 'data' chunk */
778   This->ckData.dwDataOffset = formatsize + 7 * sizeof(DWORD);
779   This->ckData.cksize       = 0;
780
781   /* for non-pcm format we need also a 'fact' chunk */
782   if (This->lpFormat->wFormatTag != WAVE_FORMAT_PCM)
783     This->ckData.dwDataOffset += 3 * sizeof(DWORD);
784
785   /* update stream and file info */
786   This->sInfo.dwSampleSize = This->lpFormat->nBlockAlign;
787   This->sInfo.dwScale      = This->lpFormat->nBlockAlign;
788   This->sInfo.dwRate       = This->lpFormat->nAvgBytesPerSec;
789   This->sInfo.dwLength     = 0;
790   This->sInfo.dwSuggestedBufferSize = 0;
791
792   return AVIERR_OK;
793 }
794
795 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start,
796                                         LONG samples, LPVOID buffer,
797                                         LONG buffersize, LPLONG bytesread,
798                                         LPLONG samplesread)
799 {
800   IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf;
801
802   TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface, start, samples, buffer,
803         buffersize, bytesread, samplesread);
804
805   /* clear return parameters if given */
806   if (bytesread != NULL)
807     *bytesread = 0;
808   if (samplesread != NULL)
809     *samplesread = 0;
810
811   /* positions without data */
812   if (start < 0 || start > This->sInfo.dwLength)
813     return AVIERR_OK;
814
815   /* check samples */
816   if (samples < 0)
817     samples = 0;
818   if (buffersize > 0) {
819     if (samples > 0)
820       samples = min(samples, buffersize / This->sInfo.dwSampleSize);
821     else
822       samples = buffersize / This->sInfo.dwSampleSize;
823   }
824
825   /* limit to end of stream */
826   if (start + samples > This->sInfo.dwLength)
827     samples = This->sInfo.dwLength - start;
828
829   /* request only the sizes? */
830   if (buffer == NULL || buffersize <= 0) {
831     /* then I need atleast one parameter for it */
832     if (bytesread == NULL && samplesread == NULL)
833       return AVIERR_BADPARAM;
834
835     if (bytesread != NULL)
836       *bytesread = samples * This->sInfo.dwSampleSize;
837     if (samplesread != NULL)
838       *samplesread = samples;
839
840     return AVIERR_OK;
841   }
842
843   /* nothing to read? */
844   if (samples == 0)
845     return AVIERR_OK;
846
847   /* Can I atleast read one sample? */
848   if (buffersize < This->sInfo.dwSampleSize)
849     return AVIERR_BUFFERTOOSMALL;
850
851   buffersize = samples * This->sInfo.dwSampleSize;
852
853   if (mmioSeek(This->hmmio, This->ckData.dwDataOffset
854                + start * This->sInfo.dwSampleSize, SEEK_SET) == -1)
855     return AVIERR_FILEREAD;
856   if (mmioRead(This->hmmio, (HPSTR)buffer, buffersize) != buffersize)
857     return AVIERR_FILEREAD;
858
859   /* fill out return parameters if given */
860   if (bytesread != NULL)
861     *bytesread = buffersize;
862   if (samplesread != NULL)
863     *samplesread = samples;  
864
865   return AVIERR_OK;
866 }
867
868 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start,
869                                          LONG samples, LPVOID buffer,
870                                          LONG buffersize, DWORD flags,
871                                          LPLONG sampwritten,
872                                          LPLONG byteswritten)
873 {
874   IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf;
875
876   TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n", iface, start, samples,
877         buffer, buffersize, flags, sampwritten, byteswritten);
878
879   /* clear return parameters if given */
880   if (sampwritten != NULL)
881     *sampwritten = 0;
882   if (byteswritten != NULL)
883     *byteswritten = 0;
884
885   /* check parameters */
886   if (buffer == NULL && (buffersize > 0 || samples > 0))
887     return AVIERR_BADPARAM;
888
889   /* Have we write permission? */
890   if ((This->uMode & MMIO_RWMODE) == 0)
891     return AVIERR_READONLY;
892
893   /* < 0 means "append" */
894   if (start < 0)
895     start = This->sInfo.dwStart + This->sInfo.dwLength;
896
897   /* check buffersize -- must multiple of samplesize */
898   if (buffersize & ~(This->sInfo.dwSampleSize - 1))
899     return AVIERR_BADSIZE;
900
901   /* have we anything to write? */
902   if (buffer != NULL && buffersize > 0) {
903     This->fDirty = 1;
904
905     if (mmioSeek(This->hmmio, This->ckData.dwDataOffset +
906                  start * This->sInfo.dwSampleSize, SEEK_SET) == -1)
907       return AVIERR_FILEWRITE;
908     if (mmioWrite(This->hmmio, (HPSTR)buffer, buffersize) != buffersize)
909       return AVIERR_FILEWRITE;
910
911     This->sInfo.dwLength = max(This->sInfo.dwLength, start + samples);
912     This->ckData.cksize  = max(This->ckData.cksize,
913                                start * This->sInfo.dwSampleSize + buffersize);
914
915     /* fill out return parameters if given */
916     if (sampwritten != NULL)
917       *sampwritten = samples;
918     if (byteswritten != NULL)
919       *byteswritten = buffersize;
920   }
921
922   return AVIERR_OK;
923 }
924
925 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start,
926                                           LONG samples)
927 {
928   IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf;
929
930   TRACE("(%p,%ld,%ld)\n", iface, start, samples);
931
932   /* check parameters */
933   if (start < 0 || samples < 0)
934     return AVIERR_BADPARAM;
935
936   /* Delete before start of stream? */
937   if (start + samples < This->sInfo.dwStart)
938     return AVIERR_OK;
939
940   /* Delete after end of stream? */
941   if (start > This->sInfo.dwLength)
942     return AVIERR_OK;
943
944   /* For the rest we need write permissions */
945   if ((This->uMode & MMIO_RWMODE) == 0)
946     return AVIERR_READONLY;
947
948   if (start + samples >= This->sInfo.dwLength) {
949     /* deletion at end */
950     samples = This->sInfo.dwLength - start;
951     This->sInfo.dwLength -= samples;
952     This->ckData.cksize  -= samples * This->sInfo.dwSampleSize;
953   } else if (start <= This->sInfo.dwStart) {
954     /* deletion at start */
955     samples = This->sInfo.dwStart - start;
956     start   = This->sInfo.dwStart;
957     This->ckData.dwDataOffset += samples * This->sInfo.dwSampleSize;
958     This->ckData.cksize       -= samples * This->sInfo.dwSampleSize;
959   } else {
960     /* deletion inside stream -- needs playlist and cue's */
961     FIXME(": deletion inside of stream not supported!\n");
962
963     return AVIERR_UNSUPPORTED;
964   }
965
966   This->fDirty = 1;
967
968   return AVIERR_OK;
969 }
970
971 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc,
972                                             LPVOID lp, LPLONG lpread)
973 {
974   ICOM_THIS(IAVIStreamImpl,iface);
975
976   assert(This->paf != NULL);
977
978   return IAVIFile_ReadData((PAVIFILE)This->paf, fcc, lp, lpread);
979 }
980
981 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc,
982                                              LPVOID lp, LONG size)
983 {
984   ICOM_THIS(IAVIStreamImpl,iface);
985
986   return IAVIFile_WriteData((PAVIFILE)This->paf, fcc, lp, size);
987 }
988
989 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream *iface,
990                                            LPAVISTREAMINFOW info, LONG infolen)
991 {
992   FIXME("(%p,%p,%ld): stub\n", iface, info, infolen);
993
994   return E_FAIL;
995 }
996
997 /***********************************************************************/
998
999 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This)
1000 {
1001   MMCKINFO ckRIFF;
1002   MMCKINFO ck;
1003
1004   This->sInfo.dwLength = 0; /* just to be sure */
1005   This->fDirty = FALSE;
1006
1007   /* search for RIFF chunk */
1008   ckRIFF.fccType = 0; /* find any */
1009   if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) {
1010     return AVIFILE_LoadSunFile(This);
1011   }
1012
1013   if (ckRIFF.fccType != formtypeWAVE)
1014     return AVIERR_BADFORMAT;
1015
1016   /* search WAVE format chunk */
1017   ck.ckid = ckidWAVEFORMAT;
1018   if (FindChunkAndKeepExtras(&This->extra, This->hmmio, &ck,
1019                              &ckRIFF, MMIO_FINDCHUNK) != S_OK)
1020     return AVIERR_FILEREAD;
1021
1022   /* get memory for format and read it */
1023   This->lpFormat = (LPWAVEFORMATEX)GlobalAllocPtr(GMEM_MOVEABLE, ck.cksize);
1024   if (This->lpFormat == NULL)
1025     return AVIERR_FILEREAD;
1026   This->cbFormat = ck.cksize;
1027
1028   if (mmioRead(This->hmmio, (HPSTR)This->lpFormat, ck.cksize) != ck.cksize)
1029     return AVIERR_FILEREAD;
1030   if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1031     return AVIERR_FILEREAD;
1032
1033   /* Non-pcm formats have a fact chunk.
1034    * We don't need it, so simply add it to the extra chunks.
1035    */
1036
1037   /* find the big data chunk */
1038   This->ckData.ckid = ckidWAVEDATA;
1039   if (FindChunkAndKeepExtras(&This->extra, This->hmmio, &This->ckData,
1040                              &ckRIFF, MMIO_FINDCHUNK) != S_OK)
1041     return AVIERR_FILEREAD;
1042
1043   memset(&This->sInfo, 0, sizeof(This->sInfo));
1044   This->sInfo.fccType      = streamtypeAUDIO;
1045   This->sInfo.dwRate       = This->lpFormat->nAvgBytesPerSec;
1046   This->sInfo.dwSampleSize =
1047     This->sInfo.dwScale    = This->lpFormat->nBlockAlign;
1048   This->sInfo.dwLength     = This->ckData.cksize / This->lpFormat->nBlockAlign;
1049   This->sInfo.dwSuggestedBufferSize = This->ckData.cksize;
1050
1051   This->fInfo.dwStreams = 1;
1052
1053   if (mmioAscend(This->hmmio, &This->ckData, 0) != S_OK) {
1054     /* seems to be truncated */
1055     WARN(": file seems to be truncated!\n");
1056     This->ckData.cksize  = mmioSeek(This->hmmio, 0, SEEK_END) -
1057       This->ckData.dwDataOffset;
1058     This->sInfo.dwLength = This->ckData.cksize / This->lpFormat->nBlockAlign;
1059     This->sInfo.dwSuggestedBufferSize = This->ckData.cksize;
1060   }
1061
1062   /* ignore errors */
1063   FindChunkAndKeepExtras(&This->extra, This->hmmio, &ck, &ckRIFF, 0);
1064
1065   return AVIERR_OK;
1066 }
1067
1068 static HRESULT AVIFILE_LoadSunFile(IAVIFileImpl *This)
1069 {
1070   FIXME(": pherhaps sun-audio file -- not implemented !\n");
1071
1072   return AVIERR_UNSUPPORTED;
1073 }
1074
1075 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This)
1076 {
1077   MMCKINFO ckRIFF;
1078   MMCKINFO ck;
1079
1080   mmioSeek(This->hmmio, 0, SEEK_SET);
1081
1082   /* create the RIFF chunk with formtype WAVE */
1083   ckRIFF.fccType = formtypeWAVE;
1084   ckRIFF.cksize  = 0;
1085   if (mmioCreateChunk(This->hmmio, &ckRIFF, MMIO_CREATERIFF) != S_OK)
1086     return AVIERR_FILEWRITE;
1087
1088   /* the next chunk is the format */
1089   ck.ckid   = ckidWAVEFORMAT;
1090   ck.cksize = This->cbFormat;
1091   if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
1092     return AVIERR_FILEWRITE;
1093   if (This->lpFormat != NULL && This->cbFormat > 0) {
1094     if (mmioWrite(This->hmmio, (HPSTR)This->lpFormat, ck.cksize) != ck.cksize)
1095       return AVIERR_FILEWRITE;
1096   }
1097   if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1098     return AVIERR_FILEWRITE;
1099
1100   /* fact chunk is needed for non-pcm waveforms */
1101   if (This->lpFormat != NULL && This->cbFormat > sizeof(PCMWAVEFORMAT) &&
1102       This->lpFormat->wFormatTag != WAVE_FORMAT_PCM) {
1103     WAVEFORMATEX wfx;
1104     DWORD        dwFactLength;
1105     HACMSTREAM   has;
1106
1107     /* try to open an appropriate audio codec to figure out
1108      * data for fact-chunk */
1109     wfx.wFormatTag = WAVE_FORMAT_PCM;
1110     if (acmFormatSuggest(NULL, This->lpFormat, &wfx,
1111                          sizeof(wfx), ACM_FORMATSUGGESTF_WFORMATTAG)) {
1112       acmStreamOpen(&has, NULL, This->lpFormat, &wfx, NULL,
1113                     0, 0, ACM_STREAMOPENF_NONREALTIME);
1114       acmStreamSize(has, This->ckData.cksize, &dwFactLength,
1115                     ACM_STREAMSIZEF_SOURCE);
1116       dwFactLength /= wfx.nBlockAlign;
1117       acmStreamClose(has, 0);
1118
1119       /* crete the fact chunk */
1120       ck.ckid   = ckidWAVEFACT;
1121       ck.cksize = sizeof(dwFactLength);
1122
1123       /* test for enough space before data chunk */
1124       if (mmioSeek(This->hmmio, 0, SEEK_CUR) > This->ckData.dwDataOffset
1125           - ck.cksize - 4 * sizeof(DWORD))
1126         return AVIERR_FILEWRITE;
1127       if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
1128         return AVIERR_FILEWRITE;
1129       if (mmioWrite(This->hmmio, (HPSTR)&dwFactLength, ck.cksize) != ck.cksize)
1130         return AVIERR_FILEWRITE;
1131       if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1132         return AVIERR_FILEWRITE;
1133     } else
1134       ERR(": fact chunk is needed for non-pcm files -- currently no codec found, so skipped!\n");
1135   }
1136
1137   /* if here was extra stuff, we need to fill it with JUNK */
1138   if (mmioSeek(This->hmmio, 0, SEEK_CUR) + 2 * sizeof(DWORD) < This->ckData.dwDataOffset) {
1139     ck.ckid   = ckidAVIPADDING;
1140     ck.cksize = 0;
1141     if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
1142       return AVIERR_FILEWRITE;
1143
1144     if (mmioSeek(This->hmmio, This->ckData.dwDataOffset
1145                  - 2 * sizeof(DWORD), SEEK_SET) == -1)
1146       return AVIERR_FILEWRITE;
1147     if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1148       return AVIERR_FILEWRITE;
1149   }
1150
1151   /* crete the data chunk */
1152   ck.ckid   = ckidWAVEDATA;
1153   ck.cksize = This->ckData.cksize;
1154   if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
1155     return AVIERR_FILEWRITE;
1156   if (mmioSeek(This->hmmio, This->ckData.cksize, SEEK_CUR) == -1)
1157     return AVIERR_FILEWRITE;
1158   if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1159     return AVIERR_FILEWRITE;
1160
1161   /* some optional extra chunks? */
1162   if (This->extra.lp != NULL && This->extra.cb > 0) {
1163     /* chunk headers are already in structure */
1164     if (mmioWrite(This->hmmio, This->extra.lp, This->extra.cb) != This->extra.cb)
1165       return AVIERR_FILEWRITE;
1166   }
1167
1168   /* close RIFF chunk */
1169   if (mmioAscend(This->hmmio, &ckRIFF, 0) != S_OK)
1170     return AVIERR_FILEWRITE;
1171   if (mmioFlush(This->hmmio, 0) != S_OK)
1172     return AVIERR_FILEWRITE;
1173
1174   return AVIERR_OK;
1175 }