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