2  * Copyright 2002 Michael Günnewig
 
   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.
 
   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.
 
  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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 
  32 #include "avifile_private.h"
 
  33 #include "extrachunk.h"
 
  35 #include "wine/unicode.h"
 
  36 #include "wine/debug.h"
 
  38 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
 
  40 /***********************************************************************/
 
  42 #define formtypeWAVE    mmioFOURCC('W','A','V','E')
 
  43 #define ckidWAVEFORMAT  mmioFOURCC('f','m','t',' ')
 
  44 #define ckidWAVEFACT    mmioFOURCC('f','a','c','t')
 
  45 #define ckidWAVEDATA    mmioFOURCC('d','a','t','a')
 
  47 /***********************************************************************/
 
  49 #define ENDIAN_SWAPWORD(x)  ((((x) >> 8) & 0xFF) | (((x) & 0xFF) << 8))
 
  50 #define ENDIAN_SWAPDWORD(x) (ENDIAN_SWAPWORD((x >> 16) & 0xFFFF) | \
 
  51                              ENDIAN_SWAPWORD(x & 0xFFFF) << 16)
 
  53 #ifdef WORDS_BIGENDIAN
 
  54 #define BE2H_WORD(x)  (x)
 
  55 #define BE2H_DWORD(x) (x)
 
  56 #define LE2H_WORD(x)  ENDIAN_SWAPWORD(x)
 
  57 #define LE2H_DWORD(x) ENDIAN_SWAPDWORD(x)
 
  59 #define BE2H_WORD(x)  ENDIAN_SWAPWORD(x)
 
  60 #define BE2H_DWORD(x) ENDIAN_SWAPDWORD(x)
 
  61 #define LE2H_WORD(x)  (x)
 
  62 #define LE2H_DWORD(x) (x)
 
  74 #define AU_ENCODING_ULAW_8                 1
 
  75 #define AU_ENCODING_PCM_8                  2
 
  76 #define AU_ENCODING_PCM_16                 3
 
  77 #define AU_ENCODING_PCM_24                 4
 
  78 #define AU_ENCODING_PCM_32                 5
 
  79 #define AU_ENCODING_FLOAT                  6
 
  80 #define AU_ENCODING_DOUBLE                 7
 
  81 #define AU_ENCODING_ADPCM_G721_32         23
 
  82 #define AU_ENCODING_ADPCM_G722            24
 
  83 #define AU_ENCODING_ADPCM_G723_24         25
 
  84 #define AU_ENCODING_ADPCM_G723_5          26
 
  85 #define AU_ENCODING_ALAW_8                27
 
  87 /***********************************************************************/
 
  89 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile* iface,REFIID refiid,LPVOID *obj);
 
  90 static ULONG   WINAPI IAVIFile_fnAddRef(IAVIFile* iface);
 
  91 static ULONG   WINAPI IAVIFile_fnRelease(IAVIFile* iface);
 
  92 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile*iface,AVIFILEINFOW*afi,LONG size);
 
  93 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile*iface,PAVISTREAM*avis,DWORD fccType,LONG lParam);
 
  94 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile*iface,PAVISTREAM*avis,AVISTREAMINFOW*asi);
 
  95 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG size);
 
  96 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG *size);
 
  97 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile*iface);
 
  98 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile*iface,DWORD fccType,LONG lParam);
 
 100 static const struct IAVIFileVtbl iwavft = {
 
 101   IAVIFile_fnQueryInterface,
 
 105   IAVIFile_fnGetStream,
 
 106   IAVIFile_fnCreateStream,
 
 107   IAVIFile_fnWriteData,
 
 109   IAVIFile_fnEndRecord,
 
 110   IAVIFile_fnDeleteStream
 
 113 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile*iface,REFIID refiid,LPVOID*obj);
 
 114 static ULONG   WINAPI IPersistFile_fnAddRef(IPersistFile*iface);
 
 115 static ULONG   WINAPI IPersistFile_fnRelease(IPersistFile*iface);
 
 116 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile*iface,CLSID*pClassID);
 
 117 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile*iface);
 
 118 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile*iface,LPCOLESTR pszFileName,DWORD dwMode);
 
 119 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile*iface,LPCOLESTR pszFileName,BOOL fRemember);
 
 120 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile*iface,LPCOLESTR pszFileName);
 
 121 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile*iface,LPOLESTR*ppszFileName);
 
 123 static const struct IPersistFileVtbl iwavpft = {
 
 124   IPersistFile_fnQueryInterface,
 
 125   IPersistFile_fnAddRef,
 
 126   IPersistFile_fnRelease,
 
 127   IPersistFile_fnGetClassID,
 
 128   IPersistFile_fnIsDirty,
 
 131   IPersistFile_fnSaveCompleted,
 
 132   IPersistFile_fnGetCurFile
 
 135 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj);
 
 136 static ULONG   WINAPI IAVIStream_fnAddRef(IAVIStream*iface);
 
 137 static ULONG   WINAPI IAVIStream_fnRelease(IAVIStream* iface);
 
 138 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2);
 
 139 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size);
 
 140 static LONG    WINAPI IAVIStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags);
 
 141 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize);
 
 142 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize);
 
 143 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread);
 
 144 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten);
 
 145 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream*iface,LONG start,LONG samples);
 
 146 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread);
 
 147 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size);
 
 148 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen);
 
 150 static const struct IAVIStreamVtbl iwavst = {
 
 151   IAVIStream_fnQueryInterface,
 
 153   IAVIStream_fnRelease,
 
 156   IAVIStream_fnFindSample,
 
 157   IAVIStream_fnReadFormat,
 
 158   IAVIStream_fnSetFormat,
 
 162   IAVIStream_fnReadData,
 
 163   IAVIStream_fnWriteData,
 
 167 typedef struct _IAVIFileImpl IAVIFileImpl;
 
 169 typedef struct _IPersistFileImpl {
 
 171   const IPersistFileVtbl *lpVtbl;
 
 173   /* IPersistFile stuff */
 
 177 typedef struct _IAVIStreamImpl {
 
 179   const IAVIStreamVtbl *lpVtbl;
 
 181   /* IAVIStream stuff */
 
 185 struct _IAVIFileImpl {
 
 187   const IAVIFileVtbl *lpVtbl;
 
 190   /* IAVIFile, IAVIStream stuff... */
 
 191   IPersistFileImpl  iPersistFile;
 
 192   IAVIStreamImpl    iAVIStream;
 
 195   AVISTREAMINFOW    sInfo;
 
 197   LPWAVEFORMATEX    lpFormat;
 
 204   /* IPersistFile stuff ... */
 
 211 /***********************************************************************/
 
 213 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This);
 
 214 static HRESULT AVIFILE_LoadSunFile(IAVIFileImpl *This);
 
 215 static HRESULT AVIFILE_SaveFile(const IAVIFileImpl *This);
 
 217 HRESULT AVIFILE_CreateWAVFile(REFIID riid, LPVOID *ppv)
 
 222   assert(riid != NULL && ppv != NULL);
 
 226   pfile = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIFileImpl));
 
 228     return AVIERR_MEMORY;
 
 230   pfile->lpVtbl                = &iwavft;
 
 231   pfile->iPersistFile.lpVtbl   = &iwavpft;
 
 232   pfile->iAVIStream.lpVtbl     = &iwavst;
 
 234   pfile->iPersistFile.paf = pfile;
 
 235   pfile->iAVIStream.paf   = pfile;
 
 237   hr = IAVIFile_QueryInterface((IAVIFile*)pfile, riid, ppv);
 
 239     HeapFree(GetProcessHeap(), 0, pfile);
 
 244 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID refiid,
 
 247   IAVIFileImpl *This = (IAVIFileImpl *)iface;
 
 249   TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj);
 
 251   if (IsEqualGUID(&IID_IUnknown, refiid) ||
 
 252       IsEqualGUID(&IID_IAVIFile, refiid)) {
 
 254     IAVIFile_AddRef(iface);
 
 256   } else if (This->fInfo.dwStreams == 1 &&
 
 257              IsEqualGUID(&IID_IAVIStream, refiid)) {
 
 258     *obj = &This->iAVIStream;
 
 259     IAVIFile_AddRef(iface);
 
 261   } else if (IsEqualGUID(&IID_IPersistFile, refiid)) {
 
 262     *obj = &This->iPersistFile;
 
 263     IAVIFile_AddRef(iface);
 
 267   return OLE_E_ENUM_NOMORE;
 
 270 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface)
 
 272   IAVIFileImpl *This = (IAVIFileImpl *)iface;
 
 274   TRACE("(%p)\n",iface);
 
 276   return InterlockedIncrement(&This->ref);
 
 279 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface)
 
 281   IAVIFileImpl *This = (IAVIFileImpl *)iface;
 
 282   ULONG ref = InterlockedDecrement(&This->ref);
 
 284   TRACE("(%p)\n",iface);
 
 288       /* need to write headers to file */
 
 289       AVIFILE_SaveFile(This);
 
 292     if (This->lpFormat != NULL) {
 
 293       HeapFree(GetProcessHeap(), 0, This->lpFormat);
 
 294       This->lpFormat = NULL;
 
 297     if (This->extra.lp != NULL) {
 
 298       HeapFree(GetProcessHeap(), 0, This->extra.lp);
 
 299       This->extra.lp = NULL;
 
 302     HeapFree(GetProcessHeap(), 0, This->szFileName);
 
 303     This->szFileName = NULL;
 
 304     if (This->hmmio != NULL) {
 
 305       mmioClose(This->hmmio, 0);
 
 309     HeapFree(GetProcessHeap(), 0, This);
 
 315 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, LPAVIFILEINFOW afi,
 
 318   IAVIFileImpl *This = (IAVIFileImpl *)iface;
 
 320   TRACE("(%p,%p,%d)\n",iface,afi,size);
 
 323     return AVIERR_BADPARAM;
 
 325     return AVIERR_BADSIZE;
 
 327   /* update file info */
 
 328   This->fInfo.dwFlags = 0;
 
 329   This->fInfo.dwCaps  = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
 
 330   if (This->lpFormat != NULL) {
 
 331     assert(This->sInfo.dwScale != 0);
 
 333     This->fInfo.dwStreams             = 1;
 
 334     This->fInfo.dwScale               = This->sInfo.dwScale;
 
 335     This->fInfo.dwRate                = This->sInfo.dwRate;
 
 336     This->fInfo.dwLength              = This->sInfo.dwLength;
 
 337     This->fInfo.dwSuggestedBufferSize = This->ckData.cksize;
 
 338     This->fInfo.dwMaxBytesPerSec =
 
 339       MulDiv(This->sInfo.dwSampleSize,This->sInfo.dwRate,This->sInfo.dwScale);
 
 342   memcpy(afi, &This->fInfo, min((DWORD)size, sizeof(This->fInfo)));
 
 344   if ((DWORD)size < sizeof(This->fInfo))
 
 345     return AVIERR_BUFFERTOOSMALL;
 
 349 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, PAVISTREAM *avis,
 
 350                                            DWORD fccType, LONG lParam)
 
 352   IAVIFileImpl *This = (IAVIFileImpl *)iface;
 
 354   TRACE("(%p,%p,0x%08X,%d)\n", iface, avis, fccType, lParam);
 
 356   /* check parameter */
 
 358     return AVIERR_BADPARAM;
 
 362   /* Does our stream exists? */
 
 363   if (lParam != 0 || This->fInfo.dwStreams == 0)
 
 364     return AVIERR_NODATA;
 
 365   if (fccType != 0 && fccType != streamtypeAUDIO)
 
 366     return AVIERR_NODATA;
 
 368   *avis = (PAVISTREAM)&This->iAVIStream;
 
 369   IAVIFile_AddRef(iface);
 
 374 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface,PAVISTREAM *avis,
 
 375                                               LPAVISTREAMINFOW asi)
 
 377   IAVIFileImpl *This = (IAVIFileImpl *)iface;
 
 379   TRACE("(%p,%p,%p)\n", iface, avis, asi);
 
 381   /* check parameters */
 
 382   if (avis == NULL || asi == NULL)
 
 383     return AVIERR_BADPARAM;
 
 387   /* We only support one audio stream */
 
 388   if (This->fInfo.dwStreams != 0 || This->lpFormat != NULL)
 
 389     return AVIERR_UNSUPPORTED;
 
 390   if (asi->fccType != streamtypeAUDIO)
 
 391     return AVIERR_UNSUPPORTED;
 
 393   /* Does the user have write permission? */
 
 394   if ((This->uMode & MMIO_RWMODE) == 0)
 
 395     return AVIERR_READONLY;
 
 398   This->lpFormat = NULL;
 
 400   memcpy(&This->sInfo, asi, sizeof(This->sInfo));
 
 402   /* make sure streaminfo if okay for us */
 
 403   This->sInfo.fccHandler          = 0;
 
 404   This->sInfo.dwFlags             = 0;
 
 405   This->sInfo.dwCaps              = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
 
 406   This->sInfo.dwStart             = 0;
 
 407   This->sInfo.dwInitialFrames     = 0;
 
 408   This->sInfo.dwFormatChangeCount = 0;
 
 409   memset(&This->sInfo.rcFrame, 0, sizeof(This->sInfo.rcFrame));
 
 411   This->fInfo.dwStreams = 1;
 
 412   This->fInfo.dwScale   = This->sInfo.dwScale;
 
 413   This->fInfo.dwRate    = This->sInfo.dwRate;
 
 414   This->fInfo.dwLength  = This->sInfo.dwLength;
 
 416   This->ckData.dwDataOffset = 0;
 
 417   This->ckData.cksize       = 0;
 
 419   *avis = (PAVISTREAM)&This->iAVIStream;
 
 420   IAVIFile_AddRef(iface);
 
 425 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid,
 
 426                                            LPVOID lpData, LONG size)
 
 428   IAVIFileImpl *This = (IAVIFileImpl *)iface;
 
 430   TRACE("(%p,0x%08X,%p,%d)\n", iface, ckid, lpData, size);
 
 432   /* check parameters */
 
 434     return AVIERR_BADPARAM;
 
 436     return AVIERR_BADSIZE;
 
 438   /* Do we have write permission? */
 
 439   if ((This->uMode & MMIO_RWMODE) == 0)
 
 440     return AVIERR_READONLY;
 
 444   return WriteExtraChunk(&This->extra, ckid, lpData, size);
 
 447 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid,
 
 448                                           LPVOID lpData, LONG *size)
 
 450   IAVIFileImpl *This = (IAVIFileImpl *)iface;
 
 452   TRACE("(%p,0x%08X,%p,%p)\n", iface, ckid, lpData, size);
 
 454   return ReadExtraChunk(&This->extra, ckid, lpData, size);
 
 457 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface)
 
 459   TRACE("(%p)\n",iface);
 
 461   /* This is only needed for interleaved files.
 
 462    * We have only one stream, which can't be interleaved.
 
 467 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType,
 
 470   IAVIFileImpl *This = (IAVIFileImpl *)iface;
 
 472   TRACE("(%p,0x%08X,%d)\n", iface, fccType, lParam);
 
 474   /* check parameter */
 
 476     return AVIERR_BADPARAM;
 
 478   /* Do we have our audio stream? */
 
 479   if (lParam != 0 || This->fInfo.dwStreams == 0 ||
 
 480       (fccType != 0 && fccType != streamtypeAUDIO))
 
 481     return AVIERR_NODATA;
 
 483   /* Have user write permissions? */
 
 484   if ((This->uMode & MMIO_RWMODE) == 0)
 
 485     return AVIERR_READONLY;
 
 487   HeapFree(GetProcessHeap(), 0, This->lpFormat);
 
 488   This->lpFormat = NULL;
 
 492   This->ckData.dwDataOffset = 0;
 
 493   This->ckData.cksize       = 0;
 
 495   This->sInfo.dwScale   = 0;
 
 496   This->sInfo.dwRate    = 0;
 
 497   This->sInfo.dwLength  = 0;
 
 498   This->sInfo.dwSuggestedBufferSize = 0;
 
 500   This->fInfo.dwStreams = 0;
 
 501   This->fInfo.dwEditCount++;
 
 508 /***********************************************************************/
 
 510 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface,
 
 511                                                     REFIID refiid, LPVOID *obj)
 
 513   IPersistFileImpl *This = (IPersistFileImpl *)iface;
 
 515   assert(This->paf != NULL);
 
 517   return IAVIFile_QueryInterface((PAVIFILE)This->paf, refiid, obj);
 
 520 static ULONG   WINAPI IPersistFile_fnAddRef(IPersistFile *iface)
 
 522   IPersistFileImpl *This = (IPersistFileImpl *)iface;
 
 524   assert(This->paf != NULL);
 
 526   return IAVIFile_AddRef((PAVIFILE)This->paf);
 
 529 static ULONG   WINAPI IPersistFile_fnRelease(IPersistFile *iface)
 
 531   IPersistFileImpl *This = (IPersistFileImpl *)iface;
 
 533   assert(This->paf != NULL);
 
 535   return IAVIFile_Release((PAVIFILE)This->paf);
 
 538 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface,
 
 541   TRACE("(%p,%p)\n", iface, pClassID);
 
 543   if (pClassID == NULL)
 
 544     return AVIERR_BADPARAM;
 
 546   *pClassID = CLSID_WAVFile;
 
 551 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface)
 
 553   IPersistFileImpl *This = (IPersistFileImpl *)iface;
 
 555   TRACE("(%p)\n", iface);
 
 557   assert(This->paf != NULL);
 
 559   return (This->paf->fDirty ? S_OK : S_FALSE);
 
 562 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface,
 
 563                                           LPCOLESTR pszFileName, DWORD dwMode)
 
 565   IAVIFileImpl *This = ((IPersistFileImpl*)iface)->paf;
 
 567   WCHAR wszStreamFmt[50];
 
 570   TRACE("(%p,%s,0x%08X)\n", iface, debugstr_w(pszFileName), dwMode);
 
 572   /* check parameter */
 
 573   if (pszFileName == NULL)
 
 574     return AVIERR_BADPARAM;
 
 576   assert(This != NULL);
 
 577   if (This->hmmio != NULL)
 
 578     return AVIERR_ERROR; /* No reuse of this object for another file! */
 
 580   /* remember mode and name */
 
 581   This->uMode = dwMode;
 
 583   len = lstrlenW(pszFileName) + 1;
 
 584   This->szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
 
 585   if (This->szFileName == NULL)
 
 586     return AVIERR_MEMORY;
 
 587   lstrcpyW(This->szFileName, pszFileName);
 
 589   /* try to open the file */
 
 590   This->hmmio = mmioOpenW(This->szFileName, NULL, MMIO_ALLOCBUF | dwMode);
 
 591   if (This->hmmio == NULL) {
 
 592     /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */
 
 594     len = WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1,
 
 595                               NULL, 0, NULL, NULL);
 
 596     szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(CHAR));
 
 597     if (szFileName == NULL)
 
 598       return AVIERR_MEMORY;
 
 600     WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, szFileName,
 
 603     This->hmmio = mmioOpenA(szFileName, NULL, MMIO_ALLOCBUF | dwMode);
 
 604     HeapFree(GetProcessHeap(), 0, szFileName);
 
 605     if (This->hmmio == NULL)
 
 606       return AVIERR_FILEOPEN;
 
 609   memset(& This->fInfo, 0, sizeof(This->fInfo));
 
 610   memset(& This->sInfo, 0, sizeof(This->sInfo));
 
 612   LoadStringW(AVIFILE_hModule, IDS_WAVEFILETYPE, This->fInfo.szFileType,
 
 613               sizeof(This->fInfo.szFileType)/sizeof(This->fInfo.szFileType[0]));
 
 614   if (LoadStringW(AVIFILE_hModule, IDS_WAVESTREAMFORMAT,
 
 615                   wszStreamFmt, sizeof(wszStreamFmt)/sizeof(wszStreamFmt[0])) > 0) {
 
 616     wsprintfW(This->sInfo.szName, wszStreamFmt,
 
 617               AVIFILE_BasenameW(This->szFileName));
 
 620   /* should we create a new file? */
 
 621   if (dwMode & OF_CREATE) {
 
 622     /* nothing more to do */
 
 625     return AVIFILE_LoadFile(This);
 
 628 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface,
 
 629                                           LPCOLESTR pszFileName,BOOL fRemember)
 
 631   TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember);
 
 633   /* We write directly to disk, so nothing to do. */
 
 638 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface,
 
 639                                                    LPCOLESTR pszFileName)
 
 641   TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName));
 
 643   /* We write directly to disk, so nothing to do. */
 
 648 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface,
 
 649                                                 LPOLESTR *ppszFileName)
 
 651   IPersistFileImpl *This = (IPersistFileImpl *)iface;
 
 653   TRACE("(%p,%p)\n", iface, ppszFileName);
 
 655   if (ppszFileName == NULL)
 
 656     return AVIERR_BADPARAM;
 
 658   *ppszFileName = NULL;
 
 660   assert(This->paf != NULL);
 
 662   if (This->paf->szFileName != NULL) {
 
 663     int len = lstrlenW(This->paf->szFileName) + 1;
 
 665     *ppszFileName = CoTaskMemAlloc(len * sizeof(WCHAR));
 
 666     if (*ppszFileName == NULL)
 
 667       return AVIERR_MEMORY;
 
 669     strcpyW(*ppszFileName, This->paf->szFileName);
 
 675 /***********************************************************************/
 
 677 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface,
 
 678                                                   REFIID refiid, LPVOID *obj)
 
 680   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
 
 682   assert(This->paf != NULL);
 
 684   return IAVIFile_QueryInterface((PAVIFILE)This->paf, refiid, obj);
 
 687 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface)
 
 689   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
 
 691   assert(This->paf != NULL);
 
 693   return IAVIFile_AddRef((PAVIFILE)This->paf);
 
 696 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface)
 
 698   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
 
 700   assert(This->paf != NULL);
 
 702   return IAVIFile_Release((PAVIFILE)This->paf);
 
 705 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1,
 
 708   TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2);
 
 710   /* This IAVIStream interface needs an WAVFile */
 
 711   return AVIERR_UNSUPPORTED;
 
 714 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi,
 
 717   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
 
 719   TRACE("(%p,%p,%d)\n", iface, psi, size);
 
 722     return AVIERR_BADPARAM;
 
 724     return AVIERR_BADSIZE;
 
 726   memcpy(psi, &This->paf->sInfo, min((DWORD)size, sizeof(This->paf->sInfo)));
 
 728   if ((DWORD)size < sizeof(This->paf->sInfo))
 
 729     return AVIERR_BUFFERTOOSMALL;
 
 733 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos,
 
 736   IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf;
 
 738   TRACE("(%p,%d,0x%08X)\n",iface,pos,flags);
 
 740   /* Do we have data? */
 
 741   if (This->lpFormat == NULL)
 
 744   /* We don't have an index */
 
 745   if (flags & FIND_INDEX)
 
 748   if (flags & FIND_FROM_START) {
 
 749     pos = This->sInfo.dwStart;
 
 750     flags &= ~(FIND_FROM_START|FIND_PREV);
 
 754   if (flags & FIND_FORMAT) {
 
 755     if ((flags & FIND_NEXT) && pos > 0)
 
 761   if ((flags & FIND_RET) == FIND_LENGTH ||
 
 762       (flags & FIND_RET) == FIND_SIZE)
 
 763     return This->sInfo.dwSampleSize;
 
 764   if ((flags & FIND_RET) == FIND_OFFSET)
 
 765     return This->ckData.dwDataOffset + pos * This->sInfo.dwSampleSize;
 
 770 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos,
 
 771                                               LPVOID format, LONG *formatsize)
 
 773   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
 
 775   TRACE("(%p,%d,%p,%p)\n", iface, pos, format, formatsize);
 
 777   if (formatsize == NULL)
 
 778     return AVIERR_BADPARAM;
 
 780   /* only interested in needed buffersize? */
 
 781   if (format == NULL || *formatsize <= 0) {
 
 782     *formatsize = This->paf->cbFormat;
 
 787   /* copy initial format (only as much as will fit) */
 
 788   memcpy(format, This->paf->lpFormat, min(*formatsize, This->paf->cbFormat));
 
 789   if (*formatsize < This->paf->cbFormat) {
 
 790     *formatsize = This->paf->cbFormat;
 
 791     return AVIERR_BUFFERTOOSMALL;
 
 794   *formatsize = This->paf->cbFormat;
 
 798 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos,
 
 799                                              LPVOID format, LONG formatsize)
 
 801   IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf;
 
 803   TRACE("(%p,%d,%p,%d)\n", iface, pos, format, formatsize);
 
 805   /* check parameters */
 
 806   if (format == NULL || formatsize <= sizeof(PCMWAVEFORMAT))
 
 807     return AVIERR_BADPARAM;
 
 809   /* We can only do this to an empty wave file, but ignore call
 
 810    * if still same format */
 
 811   if (This->lpFormat != NULL) {
 
 812     if (formatsize != This->cbFormat ||
 
 813         memcmp(format, This->lpFormat, formatsize) != 0)
 
 814       return AVIERR_UNSUPPORTED;
 
 819   /* only support start at position 0 */
 
 821     return AVIERR_UNSUPPORTED;
 
 823   /* Do we have write permission? */
 
 824   if ((This->uMode & MMIO_RWMODE) == 0)
 
 825     return AVIERR_READONLY;
 
 827   /* get memory for format and copy it */
 
 828   This->lpFormat = HeapAlloc(GetProcessHeap(), 0, formatsize);
 
 829   if (This->lpFormat == NULL)
 
 830     return AVIERR_MEMORY;
 
 832   This->cbFormat = formatsize;
 
 833   memcpy(This->lpFormat, format, formatsize);
 
 835   /* update info's about 'data' chunk */
 
 836   This->ckData.dwDataOffset = formatsize + 7 * sizeof(DWORD);
 
 837   This->ckData.cksize       = 0;
 
 839   /* for non-pcm format we need also a 'fact' chunk */
 
 840   if (This->lpFormat->wFormatTag != WAVE_FORMAT_PCM)
 
 841     This->ckData.dwDataOffset += 3 * sizeof(DWORD);
 
 843   /* update stream and file info */
 
 844   This->sInfo.dwSampleSize = This->lpFormat->nBlockAlign;
 
 845   This->sInfo.dwScale      = This->lpFormat->nBlockAlign;
 
 846   This->sInfo.dwRate       = This->lpFormat->nAvgBytesPerSec;
 
 847   This->sInfo.dwLength     = 0;
 
 848   This->sInfo.dwSuggestedBufferSize = 0;
 
 853 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start,
 
 854                                         LONG samples, LPVOID buffer,
 
 855                                         LONG buffersize, LPLONG bytesread,
 
 858   IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf;
 
 860   TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", iface, start, samples, buffer,
 
 861         buffersize, bytesread, samplesread);
 
 863   /* clear return parameters if given */
 
 864   if (bytesread != NULL)
 
 866   if (samplesread != NULL)
 
 869   /* positions without data */
 
 870   if (start < 0 || (DWORD)start > This->sInfo.dwLength)
 
 876   if (buffersize > 0) {
 
 878       samples = min((DWORD)samples, buffersize / This->sInfo.dwSampleSize);
 
 880       samples = buffersize / This->sInfo.dwSampleSize;
 
 883   /* limit to end of stream */
 
 884   if ((DWORD)(start + samples) > This->sInfo.dwLength)
 
 885     samples = This->sInfo.dwLength - start;
 
 887   /* request only the sizes? */
 
 888   if (buffer == NULL || buffersize <= 0) {
 
 889     /* then I need at least one parameter for it */
 
 890     if (bytesread == NULL && samplesread == NULL)
 
 891       return AVIERR_BADPARAM;
 
 893     if (bytesread != NULL)
 
 894       *bytesread = samples * This->sInfo.dwSampleSize;
 
 895     if (samplesread != NULL)
 
 896       *samplesread = samples;
 
 901   /* nothing to read? */
 
 905   /* Can I read at least one sample? */
 
 906   if ((DWORD)buffersize < This->sInfo.dwSampleSize)
 
 907     return AVIERR_BUFFERTOOSMALL;
 
 909   buffersize = samples * This->sInfo.dwSampleSize;
 
 911   if (mmioSeek(This->hmmio, This->ckData.dwDataOffset
 
 912                + start * This->sInfo.dwSampleSize, SEEK_SET) == -1)
 
 913     return AVIERR_FILEREAD;
 
 914   if (mmioRead(This->hmmio, buffer, buffersize) != buffersize)
 
 915     return AVIERR_FILEREAD;
 
 917   /* fill out return parameters if given */
 
 918   if (bytesread != NULL)
 
 919     *bytesread = buffersize;
 
 920   if (samplesread != NULL)
 
 921     *samplesread = samples;  
 
 926 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start,
 
 927                                          LONG samples, LPVOID buffer,
 
 928                                          LONG buffersize, DWORD flags,
 
 932   IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf;
 
 934   TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n", iface, start, samples,
 
 935         buffer, buffersize, flags, sampwritten, byteswritten);
 
 937   /* clear return parameters if given */
 
 938   if (sampwritten != NULL)
 
 940   if (byteswritten != NULL)
 
 943   /* check parameters */
 
 944   if (buffer == NULL && (buffersize > 0 || samples > 0))
 
 945     return AVIERR_BADPARAM;
 
 947   /* Do we have write permission? */
 
 948   if ((This->uMode & MMIO_RWMODE) == 0)
 
 949     return AVIERR_READONLY;
 
 951   /* < 0 means "append" */
 
 953     start = This->sInfo.dwStart + This->sInfo.dwLength;
 
 955   /* check buffersize -- must multiple of samplesize */
 
 956   if (buffersize & ~(This->sInfo.dwSampleSize - 1))
 
 957     return AVIERR_BADSIZE;
 
 959   /* do we have anything to write? */
 
 960   if (buffer != NULL && buffersize > 0) {
 
 963     if (mmioSeek(This->hmmio, This->ckData.dwDataOffset +
 
 964                  start * This->sInfo.dwSampleSize, SEEK_SET) == -1)
 
 965       return AVIERR_FILEWRITE;
 
 966     if (mmioWrite(This->hmmio, buffer, buffersize) != buffersize)
 
 967       return AVIERR_FILEWRITE;
 
 969     This->sInfo.dwLength = max(This->sInfo.dwLength, (DWORD)start + samples);
 
 970     This->ckData.cksize  = max(This->ckData.cksize,
 
 971                                start * This->sInfo.dwSampleSize + buffersize);
 
 973     /* fill out return parameters if given */
 
 974     if (sampwritten != NULL)
 
 975       *sampwritten = samples;
 
 976     if (byteswritten != NULL)
 
 977       *byteswritten = buffersize;
 
 983 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start,
 
 986   IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf;
 
 988   TRACE("(%p,%d,%d)\n", iface, start, samples);
 
 990   /* check parameters */
 
 991   if (start < 0 || samples < 0)
 
 992     return AVIERR_BADPARAM;
 
 994   /* Delete before start of stream? */
 
 995   if ((DWORD)(start + samples) < This->sInfo.dwStart)
 
 998   /* Delete after end of stream? */
 
 999   if ((DWORD)start > This->sInfo.dwLength)
 
1002   /* For the rest we need write permissions */
 
1003   if ((This->uMode & MMIO_RWMODE) == 0)
 
1004     return AVIERR_READONLY;
 
1006   if ((DWORD)(start + samples) >= This->sInfo.dwLength) {
 
1007     /* deletion at end */
 
1008     samples = This->sInfo.dwLength - start;
 
1009     This->sInfo.dwLength -= samples;
 
1010     This->ckData.cksize  -= samples * This->sInfo.dwSampleSize;
 
1011   } else if ((DWORD)start <= This->sInfo.dwStart) {
 
1012     /* deletion at start */
 
1013     samples = This->sInfo.dwStart - start;
 
1014     start   = This->sInfo.dwStart;
 
1015     This->ckData.dwDataOffset += samples * This->sInfo.dwSampleSize;
 
1016     This->ckData.cksize       -= samples * This->sInfo.dwSampleSize;
 
1018     /* deletion inside stream -- needs playlist and cue's */
 
1019     FIXME(": deletion inside of stream not supported!\n");
 
1021     return AVIERR_UNSUPPORTED;
 
1029 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc,
 
1030                                             LPVOID lp, LPLONG lpread)
 
1032   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
 
1034   assert(This->paf != NULL);
 
1036   return IAVIFile_ReadData((PAVIFILE)This->paf, fcc, lp, lpread);
 
1039 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc,
 
1040                                              LPVOID lp, LONG size)
 
1042   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
 
1044   return IAVIFile_WriteData((PAVIFILE)This->paf, fcc, lp, size);
 
1047 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream *iface,
 
1048                                            LPAVISTREAMINFOW info, LONG infolen)
 
1050   FIXME("(%p,%p,%d): stub\n", iface, info, infolen);
 
1055 /***********************************************************************/
 
1057 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This)
 
1062   This->sInfo.dwLength = 0; /* just to be sure */
 
1063   This->fDirty = FALSE;
 
1065   /* search for RIFF chunk */
 
1066   ckRIFF.fccType = 0; /* find any */
 
1067   if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) {
 
1068     return AVIFILE_LoadSunFile(This);
 
1071   if (ckRIFF.fccType != formtypeWAVE)
 
1072     return AVIERR_BADFORMAT;
 
1074   /* search WAVE format chunk */
 
1075   ck.ckid = ckidWAVEFORMAT;
 
1076   if (FindChunkAndKeepExtras(&This->extra, This->hmmio, &ck,
 
1077                              &ckRIFF, MMIO_FINDCHUNK) != S_OK)
 
1078     return AVIERR_FILEREAD;
 
1080   /* get memory for format and read it */
 
1081   This->lpFormat = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
 
1082   if (This->lpFormat == NULL)
 
1083     return AVIERR_FILEREAD;
 
1084   This->cbFormat = ck.cksize;
 
1086   if (mmioRead(This->hmmio, (HPSTR)This->lpFormat, ck.cksize) != ck.cksize)
 
1087     return AVIERR_FILEREAD;
 
1088   if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
 
1089     return AVIERR_FILEREAD;
 
1091   /* Non-pcm formats have a fact chunk.
 
1092    * We don't need it, so simply add it to the extra chunks.
 
1095   /* find the big data chunk */
 
1096   This->ckData.ckid = ckidWAVEDATA;
 
1097   if (FindChunkAndKeepExtras(&This->extra, This->hmmio, &This->ckData,
 
1098                              &ckRIFF, MMIO_FINDCHUNK) != S_OK)
 
1099     return AVIERR_FILEREAD;
 
1101   memset(&This->sInfo, 0, sizeof(This->sInfo));
 
1102   This->sInfo.fccType      = streamtypeAUDIO;
 
1103   This->sInfo.dwRate       = This->lpFormat->nAvgBytesPerSec;
 
1104   This->sInfo.dwSampleSize =
 
1105     This->sInfo.dwScale    = This->lpFormat->nBlockAlign;
 
1106   This->sInfo.dwLength     = This->ckData.cksize / This->lpFormat->nBlockAlign;
 
1107   This->sInfo.dwSuggestedBufferSize = This->ckData.cksize;
 
1109   This->fInfo.dwStreams = 1;
 
1111   if (mmioAscend(This->hmmio, &This->ckData, 0) != S_OK) {
 
1112     /* seems to be truncated */
 
1113     WARN(": file seems to be truncated!\n");
 
1114     This->ckData.cksize  = mmioSeek(This->hmmio, 0, SEEK_END) -
 
1115       This->ckData.dwDataOffset;
 
1116     This->sInfo.dwLength = This->ckData.cksize / This->lpFormat->nBlockAlign;
 
1117     This->sInfo.dwSuggestedBufferSize = This->ckData.cksize;
 
1121   FindChunkAndKeepExtras(&This->extra, This->hmmio, &ck, &ckRIFF, 0);
 
1126 static HRESULT AVIFILE_LoadSunFile(IAVIFileImpl *This)
 
1128   SUNAUDIOHEADER auhdr;
 
1130   mmioSeek(This->hmmio, 0, SEEK_SET);
 
1131   if (mmioRead(This->hmmio, (HPSTR)&auhdr, sizeof(auhdr)) != sizeof(auhdr))
 
1132     return AVIERR_FILEREAD;
 
1134   if (auhdr.fccType == 0x0064732E) {
 
1135     /* header in little endian */
 
1136     This->ckData.dwDataOffset = LE2H_DWORD(auhdr.offset);
 
1137     This->ckData.cksize       = LE2H_DWORD(auhdr.size);
 
1139     auhdr.encoding   = LE2H_DWORD(auhdr.encoding);
 
1140     auhdr.sampleRate = LE2H_DWORD(auhdr.sampleRate);
 
1141     auhdr.channels   = LE2H_DWORD(auhdr.channels);
 
1142   } else if (auhdr.fccType == mmioFOURCC('.','s','n','d')) {
 
1143     /* header in big endian */
 
1144     This->ckData.dwDataOffset = BE2H_DWORD(auhdr.offset);
 
1145     This->ckData.cksize       = BE2H_DWORD(auhdr.size);
 
1147     auhdr.encoding   = BE2H_DWORD(auhdr.encoding);
 
1148     auhdr.sampleRate = BE2H_DWORD(auhdr.sampleRate);
 
1149     auhdr.channels   = BE2H_DWORD(auhdr.channels);
 
1151     return AVIERR_FILEREAD;
 
1153   if (auhdr.channels < 1)
 
1154     return AVIERR_BADFORMAT;
 
1156   /* get size of header */
 
1157   switch(auhdr.encoding) {
 
1158   case AU_ENCODING_ADPCM_G721_32:
 
1159     This->cbFormat = sizeof(G721_ADPCMWAVEFORMAT); break;
 
1160   case AU_ENCODING_ADPCM_G723_24:
 
1161     This->cbFormat = sizeof(G723_ADPCMWAVEFORMAT); break;
 
1162   case AU_ENCODING_ADPCM_G722:
 
1163   case AU_ENCODING_ADPCM_G723_5:
 
1164     WARN("unsupported Sun audio format %d\n", auhdr.encoding);
 
1165     return AVIERR_UNSUPPORTED; /* FIXME */
 
1167     This->cbFormat = sizeof(WAVEFORMATEX); break;
 
1170   This->lpFormat = HeapAlloc(GetProcessHeap(), 0, This->cbFormat);
 
1171   if (This->lpFormat == NULL)
 
1172     return AVIERR_MEMORY;
 
1174   This->lpFormat->nChannels      = auhdr.channels;
 
1175   This->lpFormat->nSamplesPerSec = auhdr.sampleRate;
 
1176   switch(auhdr.encoding) {
 
1177   case AU_ENCODING_ULAW_8:
 
1178     This->lpFormat->wFormatTag     = WAVE_FORMAT_MULAW;
 
1179     This->lpFormat->wBitsPerSample = 8;
 
1181   case AU_ENCODING_PCM_8:
 
1182     This->lpFormat->wFormatTag     = WAVE_FORMAT_PCM;
 
1183     This->lpFormat->wBitsPerSample = 8;
 
1185   case AU_ENCODING_PCM_16:
 
1186     This->lpFormat->wFormatTag     = WAVE_FORMAT_PCM;
 
1187     This->lpFormat->wBitsPerSample = 16;
 
1189   case AU_ENCODING_PCM_24:
 
1190     This->lpFormat->wFormatTag     = WAVE_FORMAT_PCM;
 
1191     This->lpFormat->wBitsPerSample = 24;
 
1193   case AU_ENCODING_PCM_32:
 
1194     This->lpFormat->wFormatTag     = WAVE_FORMAT_PCM;
 
1195     This->lpFormat->wBitsPerSample = 32;
 
1197   case AU_ENCODING_ALAW_8:
 
1198     This->lpFormat->wFormatTag     = WAVE_FORMAT_ALAW;
 
1199     This->lpFormat->wBitsPerSample = 8;
 
1201   case AU_ENCODING_ADPCM_G721_32:
 
1202     This->lpFormat->wFormatTag     = WAVE_FORMAT_G721_ADPCM;
 
1203     This->lpFormat->wBitsPerSample = (3*5*8);
 
1204     This->lpFormat->nBlockAlign    = 15*15*8;
 
1205     This->lpFormat->cbSize         = sizeof(WORD);
 
1206     ((LPG721_ADPCMWAVEFORMAT)This->lpFormat)->nAuxBlockSize = 0;
 
1208   case AU_ENCODING_ADPCM_G723_24:
 
1209     This->lpFormat->wFormatTag     = WAVE_FORMAT_G723_ADPCM;
 
1210     This->lpFormat->wBitsPerSample = (3*5*8);
 
1211     This->lpFormat->nBlockAlign    = 15*15*8;
 
1212     This->lpFormat->cbSize         = 2*sizeof(WORD);
 
1213     ((LPG723_ADPCMWAVEFORMAT)This->lpFormat)->cbExtraSize   = 0;
 
1214     ((LPG723_ADPCMWAVEFORMAT)This->lpFormat)->nAuxBlockSize = 0;
 
1217     WARN("unsupported Sun audio format %d\n", auhdr.encoding);
 
1218     return AVIERR_UNSUPPORTED;
 
1221   This->lpFormat->nBlockAlign =
 
1222     (This->lpFormat->nChannels * This->lpFormat->wBitsPerSample) / 8;
 
1223   if (This->lpFormat->nBlockAlign == 0 && This->lpFormat->wBitsPerSample < 8)
 
1224     This->lpFormat->nBlockAlign++;
 
1225   This->lpFormat->nAvgBytesPerSec =
 
1226     This->lpFormat->nBlockAlign * This->lpFormat->nSamplesPerSec;
 
1230   This->sInfo.fccType               = streamtypeAUDIO;
 
1231   This->sInfo.fccHandler            = 0;
 
1232   This->sInfo.dwFlags               = 0;
 
1233   This->sInfo.wPriority             = 0;
 
1234   This->sInfo.wLanguage             = 0;
 
1235   This->sInfo.dwInitialFrames       = 0;
 
1236   This->sInfo.dwScale               = This->lpFormat->nBlockAlign;
 
1237   This->sInfo.dwRate                = This->lpFormat->nAvgBytesPerSec;
 
1238   This->sInfo.dwStart               = 0;
 
1239   This->sInfo.dwLength              =
 
1240     This->ckData.cksize / This->lpFormat->nBlockAlign;
 
1241   This->sInfo.dwSuggestedBufferSize = This->sInfo.dwLength;
 
1242   This->sInfo.dwSampleSize          = This->lpFormat->nBlockAlign;
 
1244   This->fInfo.dwStreams = 1;
 
1245   This->fInfo.dwScale   = 1;
 
1246   This->fInfo.dwRate    = This->lpFormat->nSamplesPerSec;
 
1247   This->fInfo.dwLength  =
 
1248     MulDiv(This->ckData.cksize, This->lpFormat->nSamplesPerSec,
 
1249            This->lpFormat->nAvgBytesPerSec);
 
1254 static HRESULT AVIFILE_SaveFile(const IAVIFileImpl *This)
 
1259   mmioSeek(This->hmmio, 0, SEEK_SET);
 
1261   /* create the RIFF chunk with formtype WAVE */
 
1262   ckRIFF.fccType = formtypeWAVE;
 
1264   if (mmioCreateChunk(This->hmmio, &ckRIFF, MMIO_CREATERIFF) != S_OK)
 
1265     return AVIERR_FILEWRITE;
 
1267   /* the next chunk is the format */
 
1268   ck.ckid   = ckidWAVEFORMAT;
 
1269   ck.cksize = This->cbFormat;
 
1270   if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
 
1271     return AVIERR_FILEWRITE;
 
1272   if (This->lpFormat != NULL && This->cbFormat > 0) {
 
1273     if (mmioWrite(This->hmmio, (HPSTR)This->lpFormat, ck.cksize) != ck.cksize)
 
1274       return AVIERR_FILEWRITE;
 
1276   if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
 
1277     return AVIERR_FILEWRITE;
 
1279   /* fact chunk is needed for non-pcm waveforms */
 
1280   if (This->lpFormat != NULL && This->cbFormat > sizeof(PCMWAVEFORMAT) &&
 
1281       This->lpFormat->wFormatTag != WAVE_FORMAT_PCM) {
 
1286     /* try to open an appropriate audio codec to figure out
 
1287      * data for fact-chunk */
 
1288     wfx.wFormatTag = WAVE_FORMAT_PCM;
 
1289     if (acmFormatSuggest(NULL, This->lpFormat, &wfx,
 
1290                          sizeof(wfx), ACM_FORMATSUGGESTF_WFORMATTAG)) {
 
1291       acmStreamOpen(&has, NULL, This->lpFormat, &wfx, NULL,
 
1292                     0, 0, ACM_STREAMOPENF_NONREALTIME);
 
1293       acmStreamSize(has, This->ckData.cksize, &dwFactLength,
 
1294                     ACM_STREAMSIZEF_SOURCE);
 
1295       dwFactLength /= wfx.nBlockAlign;
 
1296       acmStreamClose(has, 0);
 
1298       /* create the fact chunk */
 
1299       ck.ckid   = ckidWAVEFACT;
 
1300       ck.cksize = sizeof(dwFactLength);
 
1302       /* test for enough space before data chunk */
 
1303       if (mmioSeek(This->hmmio, 0, SEEK_CUR) > This->ckData.dwDataOffset
 
1304           - ck.cksize - 4 * sizeof(DWORD))
 
1305         return AVIERR_FILEWRITE;
 
1306       if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
 
1307         return AVIERR_FILEWRITE;
 
1308       if (mmioWrite(This->hmmio, (HPSTR)&dwFactLength, ck.cksize) != ck.cksize)
 
1309         return AVIERR_FILEWRITE;
 
1310       if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
 
1311         return AVIERR_FILEWRITE;
 
1313       ERR(": fact chunk is needed for non-pcm files -- currently no codec found, so skipped!\n");
 
1316   /* if there was extra stuff, we need to fill it with JUNK */
 
1317   if (mmioSeek(This->hmmio, 0, SEEK_CUR) + 2 * sizeof(DWORD) < This->ckData.dwDataOffset) {
 
1318     ck.ckid   = ckidAVIPADDING;
 
1320     if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
 
1321       return AVIERR_FILEWRITE;
 
1323     if (mmioSeek(This->hmmio, This->ckData.dwDataOffset
 
1324                  - 2 * sizeof(DWORD), SEEK_SET) == -1)
 
1325       return AVIERR_FILEWRITE;
 
1326     if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
 
1327       return AVIERR_FILEWRITE;
 
1330   /* create the data chunk */
 
1331   ck.ckid   = ckidWAVEDATA;
 
1332   ck.cksize = This->ckData.cksize;
 
1333   if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
 
1334     return AVIERR_FILEWRITE;
 
1335   if (mmioSeek(This->hmmio, This->ckData.cksize, SEEK_CUR) == -1)
 
1336     return AVIERR_FILEWRITE;
 
1337   if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
 
1338     return AVIERR_FILEWRITE;
 
1340   /* some optional extra chunks? */
 
1341   if (This->extra.lp != NULL && This->extra.cb > 0) {
 
1342     /* chunk headers are already in structure */
 
1343     if (mmioWrite(This->hmmio, This->extra.lp, This->extra.cb) != This->extra.cb)
 
1344       return AVIERR_FILEWRITE;
 
1347   /* close RIFF chunk */
 
1348   if (mmioAscend(This->hmmio, &ckRIFF, 0) != S_OK)
 
1349     return AVIERR_FILEWRITE;
 
1350   if (mmioFlush(This->hmmio, 0) != S_OK)
 
1351     return AVIERR_FILEWRITE;