From 3dd0788855c76cd9163d5bf594b7ad05e8ec67cf Mon Sep 17 00:00:00 2001 From: =?utf8?q?G=C3=A9=20van=20Geldorp?= Date: Mon, 21 Nov 2005 19:19:13 +0000 Subject: [PATCH] avifil32 vendor import svn path=/trunk/; revision=19419 --- reactos/lib/avifil32/acmstream.c | 747 +++++++ reactos/lib/avifil32/api.c | 2338 +++++++++++++++++++++ reactos/lib/avifil32/avifil32.spec | 79 + reactos/lib/avifil32/avifil32.xml | 28 + reactos/lib/avifil32/avifile.c | 2587 ++++++++++++++++++++++++ reactos/lib/avifil32/avifile.spec | 64 + reactos/lib/avifil32/avifile_Cs.rc | 53 + reactos/lib/avifil32/avifile_De.rc | 50 + reactos/lib/avifil32/avifile_En.rc | 50 + reactos/lib/avifil32/avifile_Es.rc | 50 + reactos/lib/avifil32/avifile_Fr.rc | 54 + reactos/lib/avifil32/avifile_It.rc | 51 + reactos/lib/avifil32/avifile_Ja.rc | 50 + reactos/lib/avifil32/avifile_Nl.rc | 52 + reactos/lib/avifil32/avifile_No.rc | 50 + reactos/lib/avifil32/avifile_Pl.rc | 51 + reactos/lib/avifil32/avifile_Pt.rc | 50 + reactos/lib/avifil32/avifile_Ru.rc | 52 + reactos/lib/avifil32/avifile_Si.rc | 50 + reactos/lib/avifil32/avifile_private.h | 74 + reactos/lib/avifil32/editstream.c | 1171 +++++++++++ reactos/lib/avifil32/extrachunk.c | 200 ++ reactos/lib/avifil32/extrachunk.h | 55 + reactos/lib/avifil32/factory.c | 220 ++ reactos/lib/avifil32/getframe.c | 527 +++++ reactos/lib/avifil32/icmstream.c | 976 +++++++++ reactos/lib/avifil32/regsvr.c | 580 ++++++ reactos/lib/avifil32/rsrc.rc | 53 + reactos/lib/avifil32/tmpfile.c | 290 +++ reactos/lib/avifil32/wavfile.c | 1356 +++++++++++++ reactos/lib/directory.xml | 3 + 31 files changed, 12011 insertions(+) create mode 100644 reactos/lib/avifil32/acmstream.c create mode 100644 reactos/lib/avifil32/api.c create mode 100644 reactos/lib/avifil32/avifil32.spec create mode 100644 reactos/lib/avifil32/avifil32.xml create mode 100644 reactos/lib/avifil32/avifile.c create mode 100644 reactos/lib/avifil32/avifile.spec create mode 100644 reactos/lib/avifil32/avifile_Cs.rc create mode 100644 reactos/lib/avifil32/avifile_De.rc create mode 100644 reactos/lib/avifil32/avifile_En.rc create mode 100644 reactos/lib/avifil32/avifile_Es.rc create mode 100644 reactos/lib/avifil32/avifile_Fr.rc create mode 100644 reactos/lib/avifil32/avifile_It.rc create mode 100644 reactos/lib/avifil32/avifile_Ja.rc create mode 100644 reactos/lib/avifil32/avifile_Nl.rc create mode 100644 reactos/lib/avifil32/avifile_No.rc create mode 100644 reactos/lib/avifil32/avifile_Pl.rc create mode 100644 reactos/lib/avifil32/avifile_Pt.rc create mode 100644 reactos/lib/avifil32/avifile_Ru.rc create mode 100644 reactos/lib/avifil32/avifile_Si.rc create mode 100644 reactos/lib/avifil32/avifile_private.h create mode 100644 reactos/lib/avifil32/editstream.c create mode 100644 reactos/lib/avifil32/extrachunk.c create mode 100644 reactos/lib/avifil32/extrachunk.h create mode 100644 reactos/lib/avifil32/factory.c create mode 100644 reactos/lib/avifil32/getframe.c create mode 100644 reactos/lib/avifil32/icmstream.c create mode 100644 reactos/lib/avifil32/regsvr.c create mode 100644 reactos/lib/avifil32/rsrc.rc create mode 100644 reactos/lib/avifil32/tmpfile.c create mode 100644 reactos/lib/avifil32/wavfile.c diff --git a/reactos/lib/avifil32/acmstream.c b/reactos/lib/avifil32/acmstream.c new file mode 100644 index 00000000000..0dc068f195e --- /dev/null +++ b/reactos/lib/avifil32/acmstream.c @@ -0,0 +1,747 @@ +/* + * Copyright 2002 Michael Günnewig + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define COM_NO_WINDOWS_H +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "winerror.h" +#include "windowsx.h" +#include "mmsystem.h" +#include "vfw.h" +#include "msacm.h" + +#include "avifile_private.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(avifile); + +/***********************************************************************/ + +static HRESULT WINAPI ACMStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj); +static ULONG WINAPI ACMStream_fnAddRef(IAVIStream*iface); +static ULONG WINAPI ACMStream_fnRelease(IAVIStream* iface); +static HRESULT WINAPI ACMStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2); +static HRESULT WINAPI ACMStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size); +static LONG WINAPI ACMStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags); +static HRESULT WINAPI ACMStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize); +static HRESULT WINAPI ACMStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize); +static HRESULT WINAPI ACMStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread); +static HRESULT WINAPI ACMStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten); +static HRESULT WINAPI ACMStream_fnDelete(IAVIStream*iface,LONG start,LONG samples); +static HRESULT WINAPI ACMStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread); +static HRESULT WINAPI ACMStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size); +static HRESULT WINAPI ACMStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen); + +static const struct IAVIStreamVtbl iacmst = { + ACMStream_fnQueryInterface, + ACMStream_fnAddRef, + ACMStream_fnRelease, + ACMStream_fnCreate, + ACMStream_fnInfo, + ACMStream_fnFindSample, + ACMStream_fnReadFormat, + ACMStream_fnSetFormat, + ACMStream_fnRead, + ACMStream_fnWrite, + ACMStream_fnDelete, + ACMStream_fnReadData, + ACMStream_fnWriteData, + ACMStream_fnSetInfo +}; + +typedef struct _IAVIStreamImpl { + /* IUnknown stuff */ + const IAVIStreamVtbl *lpVtbl; + LONG ref; + + /* IAVIStream stuff */ + PAVISTREAM pStream; + AVISTREAMINFOW sInfo; + + HACMSTREAM has; + + LPWAVEFORMATEX lpInFormat; + LONG cbInFormat; + + LPWAVEFORMATEX lpOutFormat; + LONG cbOutFormat; + + ACMSTREAMHEADER acmStreamHdr; +} IAVIStreamImpl; + +/***********************************************************************/ + +#define CONVERT_STREAM_to_THIS(a) do { \ + DWORD __bytes; \ + acmStreamSize(This->has,*(a) * This->lpInFormat->nBlockAlign,\ + &__bytes, ACM_STREAMSIZEF_SOURCE); \ + *(a) = __bytes / This->lpOutFormat->nBlockAlign; } while(0) + +#define CONVERT_THIS_to_STREAM(a) do { \ + DWORD __bytes; \ + acmStreamSize(This->has,*(a) * This->lpOutFormat->nBlockAlign,\ + &__bytes, ACM_STREAMSIZEF_DESTINATION); \ + *(a) = __bytes / This->lpInFormat->nBlockAlign; } while(0) + +static HRESULT AVIFILE_OpenCompressor(IAVIStreamImpl *This); + +HRESULT AVIFILE_CreateACMStream(REFIID riid, LPVOID *ppv) +{ + IAVIStreamImpl *pstream; + HRESULT hr; + + assert(riid != NULL && ppv != NULL); + + *ppv = NULL; + + pstream = (IAVIStreamImpl*)LocalAlloc(LPTR, sizeof(IAVIStreamImpl)); + if (pstream == NULL) + return AVIERR_MEMORY; + + pstream->lpVtbl = &iacmst; + + hr = IAVIStream_QueryInterface((IAVIStream*)pstream, riid, ppv); + if (FAILED(hr)) + LocalFree((HLOCAL)pstream); + + return hr; +} + +static HRESULT WINAPI ACMStream_fnQueryInterface(IAVIStream *iface, + REFIID refiid, LPVOID *obj) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj); + + if (IsEqualGUID(&IID_IUnknown, refiid) || + IsEqualGUID(&IID_IAVIStream, refiid)) { + *obj = This; + IAVIStream_AddRef(iface); + + return S_OK; + } + + return OLE_E_ENUM_NOMORE; +} + +static ULONG WINAPI ACMStream_fnAddRef(IAVIStream *iface) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) -> %ld\n", iface, ref); + + /* also add reference to the nested stream */ + if (This->pStream != NULL) + IAVIStream_AddRef(This->pStream); + + return ref; +} + +static ULONG WINAPI ACMStream_fnRelease(IAVIStream* iface) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) -> %ld\n", iface, ref); + + if (ref == 0) { + /* destruct */ + if (This->has != NULL) { + if (This->acmStreamHdr.fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED) + acmStreamUnprepareHeader(This->has, &This->acmStreamHdr, 0); + acmStreamClose(This->has, 0); + This->has = NULL; + } + if (This->acmStreamHdr.pbSrc != NULL) { + GlobalFreePtr(This->acmStreamHdr.pbSrc); + This->acmStreamHdr.pbSrc = NULL; + } + if (This->acmStreamHdr.pbDst != NULL) { + GlobalFreePtr(This->acmStreamHdr.pbDst); + This->acmStreamHdr.pbDst = NULL; + } + if (This->lpInFormat != NULL) { + GlobalFreePtr(This->lpInFormat); + This->lpInFormat = NULL; + This->cbInFormat = 0; + } + if (This->lpOutFormat != NULL) { + GlobalFreePtr(This->lpOutFormat); + This->lpOutFormat = NULL; + This->cbOutFormat = 0; + } + if (This->pStream != NULL) { + IAVIStream_Release(This->pStream); + This->pStream = NULL; + } + LocalFree((HLOCAL)This); + + return 0; + } + + /* also release reference to the nested stream */ + if (This->pStream != NULL) + IAVIStream_Release(This->pStream); + + return ref; +} + +/* lParam1: PAVISTREAM + * lParam2: LPAVICOMPRESSOPTIONS -- even if doc's say LPWAVEFORMAT + */ +static HRESULT WINAPI ACMStream_fnCreate(IAVIStream *iface, LPARAM lParam1, + LPARAM lParam2) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2); + + /* check for swapped parameters */ + if ((LPVOID)lParam1 != NULL && + ((LPAVICOMPRESSOPTIONS)lParam1)->fccType == streamtypeAUDIO) { + register LPARAM tmp = lParam1; + + lParam1 = lParam2; + lParam2 = tmp; + } + + if ((LPVOID)lParam1 == NULL) + return AVIERR_BADPARAM; + + IAVIStream_Info((PAVISTREAM)lParam1, &This->sInfo, sizeof(This->sInfo)); + if (This->sInfo.fccType != streamtypeAUDIO) + return AVIERR_ERROR; /* error in registry or AVIMakeCompressedStream */ + + This->sInfo.fccHandler = 0; /* be paranoid */ + + /* FIXME: check ACM version? Which version does we need? */ + + if ((LPVOID)lParam2 != NULL) { + /* We only need the format from the compress-options */ + if (((LPAVICOMPRESSOPTIONS)lParam2)->fccType == streamtypeAUDIO) + lParam2 = (LPARAM)((LPAVICOMPRESSOPTIONS)lParam2)->lpFormat; + + if (((LPWAVEFORMATEX)lParam2)->wFormatTag != WAVE_FORMAT_PCM) + This->cbOutFormat = sizeof(WAVEFORMATEX) + ((LPWAVEFORMATEX)lParam2)->cbSize; + else + This->cbOutFormat = sizeof(PCMWAVEFORMAT); + + This->lpOutFormat = (LPWAVEFORMATEX)GlobalAllocPtr(GHND, This->cbOutFormat); + if (This->lpOutFormat == NULL) + return AVIERR_MEMORY; + + memcpy(This->lpOutFormat, (LPVOID)lParam2, This->cbOutFormat); + } else { + This->lpOutFormat = NULL; + This->cbOutFormat = 0; + } + + This->pStream = (PAVISTREAM)lParam1; + IAVIStream_AddRef(This->pStream); + + return AVIERR_OK; +} + +static HRESULT WINAPI ACMStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi, + LONG size) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + TRACE("(%p,%p,%ld)\n", iface, psi, size); + + if (psi == NULL) + return AVIERR_BADPARAM; + if (size < 0) + return AVIERR_BADSIZE; + + /* Need codec to correct some values in structure */ + if (This->has == NULL) { + HRESULT hr = AVIFILE_OpenCompressor(This); + + if (FAILED(hr)) + return hr; + } + + memcpy(psi, &This->sInfo, min(size, (LONG)sizeof(This->sInfo))); + + if (size < (LONG)sizeof(This->sInfo)) + return AVIERR_BUFFERTOOSMALL; + return AVIERR_OK; +} + +static LONG WINAPI ACMStream_fnFindSample(IAVIStream *iface, LONG pos, + LONG flags) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + TRACE("(%p,%ld,0x%08lX)\n",iface,pos,flags); + + if (flags & FIND_FROM_START) { + pos = This->sInfo.dwStart; + flags &= ~(FIND_FROM_START|FIND_PREV); + flags |= FIND_NEXT; + } + + /* convert pos from our 'space' to This->pStream's one */ + CONVERT_THIS_to_STREAM(&pos); + + /* ask stream */ + pos = IAVIStream_FindSample(This->pStream, pos, flags); + + if (pos != -1) { + /* convert pos back to our 'space' if it's no size or physical pos */ + if ((flags & FIND_RET) == 0) + CONVERT_STREAM_to_THIS(&pos); + } + + return pos; +} + +static HRESULT WINAPI ACMStream_fnReadFormat(IAVIStream *iface, LONG pos, + LPVOID format, LONG *formatsize) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + TRACE("(%p,%ld,%p,%p)\n", iface, pos, format, formatsize); + + if (formatsize == NULL) + return AVIERR_BADPARAM; + + if (This->has == NULL) { + HRESULT hr = AVIFILE_OpenCompressor(This); + + if (FAILED(hr)) + return hr; + } + + /* only interested in needed buffersize? */ + if (format == NULL || *formatsize <= 0) { + *formatsize = This->cbOutFormat; + + return AVIERR_OK; + } + + /* copy initial format (only as much as will fit) */ + memcpy(format, This->lpOutFormat, min(*formatsize, This->cbOutFormat)); + if (*formatsize < This->cbOutFormat) { + *formatsize = This->cbOutFormat; + return AVIERR_BUFFERTOOSMALL; + } + + *formatsize = This->cbOutFormat; + return AVIERR_OK; +} + +static HRESULT WINAPI ACMStream_fnSetFormat(IAVIStream *iface, LONG pos, + LPVOID format, LONG formatsize) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + HRESULT hr; + + TRACE("(%p,%ld,%p,%ld)\n", iface, pos, format, formatsize); + + /* check parameters */ + if (format == NULL || formatsize <= 0) + return AVIERR_BADPARAM; + + /* Input format already known? + * Changing is unsupported, but be quiet if it's the same */ + if (This->lpInFormat != NULL) { + if (This->cbInFormat != formatsize || + memcmp(format, This->lpInFormat, formatsize) != 0) + return AVIERR_UNSUPPORTED; + + return AVIERR_OK; + } + + /* Does the nested stream support writing? */ + if ((This->sInfo.dwCaps & AVIFILECAPS_CANWRITE) == 0) + return AVIERR_READONLY; + + This->lpInFormat = (LPWAVEFORMATEX)GlobalAllocPtr(GMEM_MOVEABLE, formatsize); + if (This->lpInFormat == NULL) + return AVIERR_MEMORY; + This->cbInFormat = formatsize; + memcpy(This->lpInFormat, format, formatsize); + + /* initialize formats and get compressor */ + hr = AVIFILE_OpenCompressor(This); + if (FAILED(hr)) + return hr; + + CONVERT_THIS_to_STREAM(&pos); + + /* tell the nested stream the new format */ + return IAVIStream_SetFormat(This->pStream, pos, This->lpOutFormat, + This->cbOutFormat); +} + +static HRESULT WINAPI ACMStream_fnRead(IAVIStream *iface, LONG start, + LONG samples, LPVOID buffer, + LONG buffersize, LPLONG bytesread, + LPLONG samplesread) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + HRESULT hr; + DWORD size; + + TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface, start, samples, buffer, + buffersize, bytesread, samplesread); + + /* clear return parameters if given */ + if (bytesread != NULL) + *bytesread = 0; + if (samplesread != NULL) + *samplesread = 0; + + /* Do we have our compressor? */ + if (This->has == NULL) { + hr = AVIFILE_OpenCompressor(This); + + if (FAILED(hr)) + return hr; + } + + /* only need to pass through? */ + if (This->cbInFormat == This->cbOutFormat && + memcmp(This->lpInFormat, This->lpOutFormat, This->cbInFormat) == 0) { + return IAVIStream_Read(This->pStream, start, samples, buffer, buffersize, + bytesread, samplesread); + } + + /* read as much as fit? */ + if (samples == -1) + samples = buffersize / This->lpOutFormat->nBlockAlign; + /* limit to buffersize */ + if (samples * This->lpOutFormat->nBlockAlign > buffersize) + samples = buffersize / This->lpOutFormat->nBlockAlign; + + /* only return needed size? */ + if (buffer == NULL || buffersize <= 0 || samples == 0) { + if (bytesread == NULL && samplesread == NULL) + return AVIERR_BADPARAM; + + if (bytesread != NULL) + *bytesread = samples * This->lpOutFormat->nBlockAlign; + if (samplesread != NULL) + *samplesread = samples; + + return AVIERR_OK; + } + + /* map our positions to pStream positions */ + CONVERT_THIS_to_STREAM(&start); + + /* our needed internal buffersize */ + size = samples * This->lpInFormat->nBlockAlign; + + /* Need to free destination buffer used for writing? */ + if (This->acmStreamHdr.pbDst != NULL) { + GlobalFreePtr(This->acmStreamHdr.pbDst); + This->acmStreamHdr.pbDst = NULL; + This->acmStreamHdr.dwDstUser = 0; + } + + /* need bigger source buffer? */ + if (This->acmStreamHdr.pbSrc == NULL || + This->acmStreamHdr.dwSrcUser < size) { + if (This->acmStreamHdr.pbSrc == NULL) + This->acmStreamHdr.pbSrc = GlobalAllocPtr(GMEM_MOVEABLE, size); + else + This->acmStreamHdr.pbSrc = GlobalReAllocPtr(This->acmStreamHdr.pbSrc, + size, GMEM_MOVEABLE); + if (This->acmStreamHdr.pbSrc == NULL) + return AVIERR_MEMORY; + This->acmStreamHdr.dwSrcUser = size; + } + + This->acmStreamHdr.cbStruct = sizeof(This->acmStreamHdr); + This->acmStreamHdr.cbSrcLengthUsed = 0; + This->acmStreamHdr.cbDstLengthUsed = 0; + This->acmStreamHdr.cbSrcLength = size; + + /* read source data */ + hr = IAVIStream_Read(This->pStream, start, -1, This->acmStreamHdr.pbSrc, + This->acmStreamHdr.cbSrcLength, + (LONG *)&This->acmStreamHdr.cbSrcLength, NULL); + if (FAILED(hr) || This->acmStreamHdr.cbSrcLength == 0) + return hr; + + /* need to prepare stream? */ + This->acmStreamHdr.pbDst = buffer; + This->acmStreamHdr.cbDstLength = buffersize; + if ((This->acmStreamHdr.fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED) == 0) { + if (acmStreamPrepareHeader(This->has, &This->acmStreamHdr, 0) != S_OK) { + This->acmStreamHdr.pbDst = NULL; + This->acmStreamHdr.cbDstLength = 0; + return AVIERR_COMPRESSOR; + } + } + + /* now do the conversion */ + /* FIXME: use ACM_CONVERTF_* flags */ + if (acmStreamConvert(This->has, &This->acmStreamHdr, 0) != S_OK) + hr = AVIERR_COMPRESSOR; + + This->acmStreamHdr.pbDst = NULL; + This->acmStreamHdr.cbDstLength = 0; + + /* fill out return parameters if given */ + if (bytesread != NULL) + *bytesread = This->acmStreamHdr.cbDstLengthUsed; + if (samplesread != NULL) + *samplesread = + This->acmStreamHdr.cbDstLengthUsed / This->lpOutFormat->nBlockAlign; + + return hr; +} + +static HRESULT WINAPI ACMStream_fnWrite(IAVIStream *iface, LONG start, + LONG samples, LPVOID buffer, + LONG buffersize, DWORD flags, + LPLONG sampwritten, + LPLONG byteswritten) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + HRESULT hr; + ULONG size; + + TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n", iface, start, samples, + buffer, buffersize, flags, sampwritten, byteswritten); + + /* clear return parameters if given */ + if (sampwritten != NULL) + *sampwritten = 0; + if (byteswritten != NULL) + *byteswritten = 0; + + /* check parameters */ + if (buffer == NULL && (buffersize > 0 || samples > 0)) + return AVIERR_BADPARAM; + + /* Have we write capability? */ + if ((This->sInfo.dwCaps & AVIFILECAPS_CANWRITE) == 0) + return AVIERR_READONLY; + + /* also need a compressor */ + if (This->has == NULL) + return AVIERR_NOCOMPRESSOR; + + /* map our sizes to pStream sizes */ + size = buffersize; + CONVERT_THIS_to_STREAM(&size); + CONVERT_THIS_to_STREAM(&start); + + /* no bytes to write? -- short circuit */ + if (size == 0) { + return IAVIStream_Write(This->pStream, -1, samples, buffer, size, + flags, sampwritten, byteswritten); + } + + /* Need to free source buffer used for reading? */ + if (This->acmStreamHdr.pbSrc != NULL) { + GlobalFreePtr(This->acmStreamHdr.pbSrc); + This->acmStreamHdr.pbSrc = NULL; + This->acmStreamHdr.dwSrcUser = 0; + } + + /* Need bigger destination buffer? */ + if (This->acmStreamHdr.pbDst == NULL || + This->acmStreamHdr.dwDstUser < size) { + if (This->acmStreamHdr.pbDst == NULL) + This->acmStreamHdr.pbDst = GlobalAllocPtr(GMEM_MOVEABLE, size); + else + This->acmStreamHdr.pbDst = GlobalReAllocPtr(This->acmStreamHdr.pbDst, + size, GMEM_MOVEABLE); + if (This->acmStreamHdr.pbDst == NULL) + return AVIERR_MEMORY; + This->acmStreamHdr.dwDstUser = size; + } + This->acmStreamHdr.cbStruct = sizeof(This->acmStreamHdr); + This->acmStreamHdr.cbSrcLengthUsed = 0; + This->acmStreamHdr.cbDstLengthUsed = 0; + This->acmStreamHdr.cbDstLength = This->acmStreamHdr.dwDstUser; + + /* need to prepare stream? */ + This->acmStreamHdr.pbSrc = buffer; + This->acmStreamHdr.cbSrcLength = buffersize; + if ((This->acmStreamHdr.fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED) == 0) { + if (acmStreamPrepareHeader(This->has, &This->acmStreamHdr, 0) != S_OK) { + This->acmStreamHdr.pbSrc = NULL; + This->acmStreamHdr.cbSrcLength = 0; + return AVIERR_COMPRESSOR; + } + } + + /* now do the conversion */ + /* FIXME: use ACM_CONVERTF_* flags */ + if (acmStreamConvert(This->has, &This->acmStreamHdr, 0) != S_OK) + hr = AVIERR_COMPRESSOR; + else + hr = AVIERR_OK; + + This->acmStreamHdr.pbSrc = NULL; + This->acmStreamHdr.cbSrcLength = 0; + + if (FAILED(hr)) + return hr; + + return IAVIStream_Write(This->pStream,-1,This->acmStreamHdr.cbDstLengthUsed / + This->lpOutFormat->nBlockAlign,This->acmStreamHdr.pbDst, + This->acmStreamHdr.cbDstLengthUsed,flags,sampwritten, + byteswritten); +} + +static HRESULT WINAPI ACMStream_fnDelete(IAVIStream *iface, LONG start, + LONG samples) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + TRACE("(%p,%ld,%ld)\n", iface, start, samples); + + /* check parameters */ + if (start < 0 || samples < 0) + return AVIERR_BADPARAM; + + /* Delete before start of stream? */ + if ((DWORD)(start + samples) < This->sInfo.dwStart) + return AVIERR_OK; + + /* Delete after end of stream? */ + if ((DWORD)start > This->sInfo.dwLength) + return AVIERR_OK; + + /* For the rest we need write capability */ + if ((This->sInfo.dwCaps & AVIFILECAPS_CANWRITE) == 0) + return AVIERR_READONLY; + + /* A compressor is also necessary */ + if (This->has == NULL) + return AVIERR_NOCOMPRESSOR; + + /* map our positions to pStream positions */ + CONVERT_THIS_to_STREAM(&start); + CONVERT_THIS_to_STREAM(&samples); + + return IAVIStream_Delete(This->pStream, start, samples); +} + +static HRESULT WINAPI ACMStream_fnReadData(IAVIStream *iface, DWORD fcc, + LPVOID lp, LPLONG lpread) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + TRACE("(%p,0x%08lX,%p,%p)\n", iface, fcc, lp, lpread); + + assert(This->pStream != NULL); + + return IAVIStream_ReadData(This->pStream, fcc, lp, lpread); +} + +static HRESULT WINAPI ACMStream_fnWriteData(IAVIStream *iface, DWORD fcc, + LPVOID lp, LONG size) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + TRACE("(%p,0x%08lx,%p,%ld)\n", iface, fcc, lp, size); + + assert(This->pStream != NULL); + + return IAVIStream_WriteData(This->pStream, fcc, lp, size); +} + +static HRESULT WINAPI ACMStream_fnSetInfo(IAVIStream *iface, + LPAVISTREAMINFOW info, LONG infolen) +{ + FIXME("(%p,%p,%ld): stub\n", iface, info, infolen); + + return E_FAIL; +} + +/***********************************************************************/ + +static HRESULT AVIFILE_OpenCompressor(IAVIStreamImpl *This) +{ + HRESULT hr; + + /* pre-conditions */ + assert(This != NULL); + assert(This->pStream != NULL); + + if (This->has != NULL) + return AVIERR_OK; + + if (This->lpInFormat == NULL) { + /* decode or encode the data from pStream */ + hr = AVIStreamFormatSize(This->pStream, This->sInfo.dwStart, &This->cbInFormat); + if (FAILED(hr)) + return hr; + This->lpInFormat = (LPWAVEFORMATEX)GlobalAllocPtr(GMEM_MOVEABLE, This->cbInFormat); + if (This->lpInFormat == NULL) + return AVIERR_MEMORY; + + hr = IAVIStream_ReadFormat(This->pStream, This->sInfo.dwStart, + This->lpInFormat, &This->cbInFormat); + if (FAILED(hr)) + return hr; + + if (This->lpOutFormat == NULL) { + /* we must decode to default format */ + This->cbOutFormat = sizeof(PCMWAVEFORMAT); + This->lpOutFormat = (LPWAVEFORMATEX)GlobalAllocPtr(GHND, This->cbOutFormat); + if (This->lpOutFormat == NULL) + return AVIERR_MEMORY; + + This->lpOutFormat->wFormatTag = WAVE_FORMAT_PCM; + if (acmFormatSuggest(NULL, This->lpInFormat, This->lpOutFormat, + This->cbOutFormat, ACM_FORMATSUGGESTF_WFORMATTAG) != S_OK) + return AVIERR_NOCOMPRESSOR; + } + } else if (This->lpOutFormat == NULL) + return AVIERR_ERROR; /* To what should I encode? */ + + if (acmStreamOpen(&This->has, NULL, This->lpInFormat, This->lpOutFormat, + NULL, 0, 0, ACM_STREAMOPENF_NONREALTIME) != S_OK) + return AVIERR_NOCOMPRESSOR; + + /* update AVISTREAMINFO structure */ + This->sInfo.dwSampleSize = This->lpOutFormat->nBlockAlign; + This->sInfo.dwScale = This->lpOutFormat->nBlockAlign; + This->sInfo.dwRate = This->lpOutFormat->nAvgBytesPerSec; + This->sInfo.dwQuality = (DWORD)ICQUALITY_DEFAULT; + SetRectEmpty(&This->sInfo.rcFrame); + + /* convert positions ansd sizes to output format */ + CONVERT_STREAM_to_THIS(&This->sInfo.dwStart); + CONVERT_STREAM_to_THIS(&This->sInfo.dwLength); + CONVERT_STREAM_to_THIS(&This->sInfo.dwSuggestedBufferSize); + + return AVIERR_OK; +} diff --git a/reactos/lib/avifil32/api.c b/reactos/lib/avifil32/api.c new file mode 100644 index 00000000000..300be27d2aa --- /dev/null +++ b/reactos/lib/avifil32/api.c @@ -0,0 +1,2338 @@ +/* + * Copyright 1999 Marcus Meissner + * Copyright 2002-2003 Michael Günnewig + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#define COBJMACROS +#define COM_NO_WINDOWS_H + +#include "windef.h" +#include "winbase.h" +#include "winnls.h" +#include "wingdi.h" +#include "winuser.h" +#include "winreg.h" +#include "winerror.h" +#include "windowsx.h" + +#include "ole2.h" +#include "shellapi.h" +#include "shlobj.h" +#include "vfw.h" +#include "msacm.h" + +#include "avifile_private.h" + +#include "wine/debug.h" +#include "wine/unicode.h" + +WINE_DEFAULT_DEBUG_CHANNEL(avifile); + + +/*********************************************************************** + * for AVIBuildFilterW -- uses fixed size table + */ +#define MAX_FILTERS 30 /* 30 => 7kB */ + +typedef struct _AVIFilter { + WCHAR szClsid[40]; + WCHAR szExtensions[MAX_FILTERS * 7]; +} AVIFilter; + +/*********************************************************************** + * for AVISaveOptions + */ +static struct { + UINT uFlags; + INT nStreams; + PAVISTREAM *ppavis; + LPAVICOMPRESSOPTIONS *ppOptions; + INT nCurrent; +} SaveOpts; + +/*********************************************************************** + * copied from dlls/ole32/compobj.c + */ +static HRESULT AVIFILE_CLSIDFromString(LPCSTR idstr, LPCLSID id) +{ + BYTE const *s; + BYTE *p; + INT i; + BYTE table[256]; + + if (!idstr) { + memset(id, 0, sizeof(CLSID)); + return S_OK; + } + + /* validate the CLSID string */ + if (lstrlenA(idstr) != 38) + return CO_E_CLASSSTRING; + + s = (BYTE const*)idstr; + if ((s[0]!='{') || (s[9]!='-') || (s[14]!='-') || (s[19]!='-') || + (s[24]!='-') || (s[37]!='}')) + return CO_E_CLASSSTRING; + + for (i = 1; i < 37; i++) { + if ((i == 9) || (i == 14) || (i == 19) || (i == 24)) + continue; + if (!(((s[i] >= '0') && (s[i] <= '9')) || + ((s[i] >= 'a') && (s[i] <= 'f')) || + ((s[i] >= 'A') && (s[i] <= 'F'))) + ) + return CO_E_CLASSSTRING; + } + + TRACE("%s -> %p\n", s, id); + + /* quick lookup table */ + memset(table, 0, 256); + + for (i = 0; i < 10; i++) + table['0' + i] = i; + + for (i = 0; i < 6; i++) { + table['A' + i] = i+10; + table['a' + i] = i+10; + } + + /* in form {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} */ + p = (BYTE *) id; + + s++; /* skip leading brace */ + for (i = 0; i < 4; i++) { + p[3 - i] = table[*s]<<4 | table[*(s+1)]; + s += 2; + } + p += 4; + s++; /* skip - */ + + for (i = 0; i < 2; i++) { + p[1-i] = table[*s]<<4 | table[*(s+1)]; + s += 2; + } + p += 2; + s++; /* skip - */ + + for (i = 0; i < 2; i++) { + p[1-i] = table[*s]<<4 | table[*(s+1)]; + s += 2; + } + p += 2; + s++; /* skip - */ + + /* these are just sequential bytes */ + for (i = 0; i < 2; i++) { + *p++ = table[*s]<<4 | table[*(s+1)]; + s += 2; + } + s++; /* skip - */ + + for (i = 0; i < 6; i++) { + *p++ = table[*s]<<4 | table[*(s+1)]; + s += 2; + } + + return S_OK; +} + +static BOOL AVIFILE_GetFileHandlerByExtension(LPCWSTR szFile, LPCLSID lpclsid) +{ + CHAR szRegKey[25]; + CHAR szValue[100]; + LPWSTR szExt = strrchrW(szFile, '.'); + LONG len = sizeof(szValue) / sizeof(szValue[0]); + + if (szExt == NULL) + return FALSE; + + szExt++; + + wsprintfA(szRegKey, "AVIFile\\Extensions\\%.3ls", szExt); + if (RegQueryValueA(HKEY_CLASSES_ROOT, szRegKey, szValue, &len) != ERROR_SUCCESS) + return FALSE; + + return (AVIFILE_CLSIDFromString(szValue, lpclsid) == S_OK); +} + +/*********************************************************************** + * AVIFileInit (AVIFIL32.@) + * AVIFileInit (AVIFILE.100) + */ +void WINAPI AVIFileInit(void) { + OleInitialize(NULL); +} + +/*********************************************************************** + * AVIFileExit (AVIFIL32.@) + * AVIFileExit (AVIFILE.101) + */ +void WINAPI AVIFileExit(void) { + /* need to free ole32.dll if we are the last exit call */ + /* OleUnitialize() */ + FIXME("(): stub!\n"); +} + +/*********************************************************************** + * AVIFileOpen (AVIFIL32.@) + * AVIFileOpenA (AVIFIL32.@) + * AVIFileOpen (AVIFILE.102) + */ +HRESULT WINAPI AVIFileOpenA(PAVIFILE *ppfile, LPCSTR szFile, UINT uMode, + LPCLSID lpHandler) +{ + LPWSTR wszFile = NULL; + HRESULT hr; + int len; + + TRACE("(%p,%s,0x%08X,%s)\n", ppfile, debugstr_a(szFile), uMode, + debugstr_guid(lpHandler)); + + /* check parameters */ + if (ppfile == NULL || szFile == NULL) + return AVIERR_BADPARAM; + + /* convert ASCII string to Unicode and call unicode function */ + len = MultiByteToWideChar(CP_ACP, 0, szFile, -1, NULL, 0); + if (len <= 0) + return AVIERR_BADPARAM; + + wszFile = (LPWSTR)LocalAlloc(LPTR, len * sizeof(WCHAR)); + if (wszFile == NULL) + return AVIERR_MEMORY; + + MultiByteToWideChar(CP_ACP, 0, szFile, -1, wszFile, len); + + hr = AVIFileOpenW(ppfile, wszFile, uMode, lpHandler); + + LocalFree((HLOCAL)wszFile); + + return hr; +} + +/*********************************************************************** + * AVIFileOpenW (AVIFIL32.@) + */ +HRESULT WINAPI AVIFileOpenW(PAVIFILE *ppfile, LPCWSTR szFile, UINT uMode, + LPCLSID lpHandler) +{ + IPersistFile *ppersist = NULL; + CLSID clsidHandler; + HRESULT hr; + + TRACE("(%p,%s,0x%X,%s)\n", ppfile, debugstr_w(szFile), uMode, + debugstr_guid(lpHandler)); + + /* check parameters */ + if (ppfile == NULL || szFile == NULL) + return AVIERR_BADPARAM; + + *ppfile = NULL; + + /* if no handler then try guessing it by extension */ + if (lpHandler == NULL) { + if (! AVIFILE_GetFileHandlerByExtension(szFile, &clsidHandler)) + return AVIERR_UNSUPPORTED; + } else + memcpy(&clsidHandler, lpHandler, sizeof(clsidHandler)); + + /* create instance of handler */ + hr = CoCreateInstance(&clsidHandler, NULL, CLSCTX_INPROC, &IID_IAVIFile, (LPVOID*)ppfile); + if (FAILED(hr) || *ppfile == NULL) + return hr; + + /* ask for IPersistFile interface for loading/creating the file */ + hr = IAVIFile_QueryInterface(*ppfile, &IID_IPersistFile, (LPVOID*)&ppersist); + if (FAILED(hr) || ppersist == NULL) { + IAVIFile_Release(*ppfile); + *ppfile = NULL; + return hr; + } + + hr = IPersistFile_Load(ppersist, szFile, uMode); + IPersistFile_Release(ppersist); + if (FAILED(hr)) { + IAVIFile_Release(*ppfile); + *ppfile = NULL; + } + + return hr; +} + +/*********************************************************************** + * AVIFileAddRef (AVIFIL32.@) + * AVIFileAddRef (AVIFILE.140) + */ +ULONG WINAPI AVIFileAddRef(PAVIFILE pfile) +{ + TRACE("(%p)\n", pfile); + + if (pfile == NULL) { + ERR(": bad handle passed!\n"); + return 0; + } + + return IAVIFile_AddRef(pfile); +} + +/*********************************************************************** + * AVIFileRelease (AVIFIL32.@) + * AVIFileRelease (AVIFILE.141) + */ +ULONG WINAPI AVIFileRelease(PAVIFILE pfile) +{ + TRACE("(%p)\n", pfile); + + if (pfile == NULL) { + ERR(": bad handle passed!\n"); + return 0; + } + + return IAVIFile_Release(pfile); +} + +/*********************************************************************** + * AVIFileInfo (AVIFIL32.@) + * AVIFileInfoA (AVIFIL32.@) + * AVIFileInfo (AVIFILE.142) + */ +HRESULT WINAPI AVIFileInfoA(PAVIFILE pfile, LPAVIFILEINFOA afi, LONG size) +{ + AVIFILEINFOW afiw; + HRESULT hres; + + TRACE("(%p,%p,%ld)\n", pfile, afi, size); + + if (pfile == NULL) + return AVIERR_BADHANDLE; + if ((DWORD)size < sizeof(AVIFILEINFOA)) + return AVIERR_BADSIZE; + + hres = IAVIFile_Info(pfile, &afiw, sizeof(afiw)); + + memcpy(afi, &afiw, sizeof(*afi) - sizeof(afi->szFileType)); + WideCharToMultiByte(CP_ACP, 0, afiw.szFileType, -1, afi->szFileType, + sizeof(afi->szFileType), NULL, NULL); + afi->szFileType[sizeof(afi->szFileType) - 1] = 0; + + return hres; +} + +/*********************************************************************** + * AVIFileInfoW (AVIFIL32.@) + */ +HRESULT WINAPI AVIFileInfoW(PAVIFILE pfile, LPAVIFILEINFOW afiw, LONG size) +{ + TRACE("(%p,%p,%ld)\n", pfile, afiw, size); + + if (pfile == NULL) + return AVIERR_BADHANDLE; + + return IAVIFile_Info(pfile, afiw, size); +} + +/*********************************************************************** + * AVIFileGetStream (AVIFIL32.@) + * AVIFileGetStream (AVIFILE.143) + */ +HRESULT WINAPI AVIFileGetStream(PAVIFILE pfile, PAVISTREAM *avis, + DWORD fccType, LONG lParam) +{ + TRACE("(%p,%p,'%4.4s',%ld)\n", pfile, avis, (char*)&fccType, lParam); + + if (pfile == NULL) + return AVIERR_BADHANDLE; + + return IAVIFile_GetStream(pfile, avis, fccType, lParam); +} + +/*********************************************************************** + * AVIFileCreateStream (AVIFIL32.@) + * AVIFileCreateStreamA (AVIFIL32.@) + * AVIFileCreateStream (AVIFILE.144) + */ +HRESULT WINAPI AVIFileCreateStreamA(PAVIFILE pfile, PAVISTREAM *ppavi, + LPAVISTREAMINFOA psi) +{ + AVISTREAMINFOW psiw; + + TRACE("(%p,%p,%p)\n", pfile, ppavi, psi); + + if (pfile == NULL) + return AVIERR_BADHANDLE; + + /* Only the szName at the end is different */ + memcpy(&psiw, psi, sizeof(*psi) - sizeof(psi->szName)); + MultiByteToWideChar(CP_ACP, 0, psi->szName, -1, psiw.szName, + sizeof(psiw.szName) / sizeof(psiw.szName[0])); + + return IAVIFile_CreateStream(pfile, ppavi, &psiw); +} + +/*********************************************************************** + * AVIFileCreateStreamW (AVIFIL32.@) + */ +HRESULT WINAPI AVIFileCreateStreamW(PAVIFILE pfile, PAVISTREAM *avis, + LPAVISTREAMINFOW asi) +{ + TRACE("(%p,%p,%p)\n", pfile, avis, asi); + + if (pfile == NULL) + return AVIERR_BADHANDLE; + + return IAVIFile_CreateStream(pfile, avis, asi); +} + +/*********************************************************************** + * AVIFileWriteData (AVIFIL32.@) + * AVIFileWriteData (AVIFILE.146) + */ +HRESULT WINAPI AVIFileWriteData(PAVIFILE pfile,DWORD fcc,LPVOID lp,LONG size) +{ + TRACE("(%p,'%4.4s',%p,%ld)\n", pfile, (char*)&fcc, lp, size); + + if (pfile == NULL) + return AVIERR_BADHANDLE; + + return IAVIFile_WriteData(pfile, fcc, lp, size); +} + +/*********************************************************************** + * AVIFileReadData (AVIFIL32.@) + * AVIFileReadData (AVIFILE.147) + */ +HRESULT WINAPI AVIFileReadData(PAVIFILE pfile,DWORD fcc,LPVOID lp,LPLONG size) +{ + TRACE("(%p,'%4.4s',%p,%p)\n", pfile, (char*)&fcc, lp, size); + + if (pfile == NULL) + return AVIERR_BADHANDLE; + + return IAVIFile_ReadData(pfile, fcc, lp, size); +} + +/*********************************************************************** + * AVIFileEndRecord (AVIFIL32.@) + * AVIFileEndRecord (AVIFILE.148) + */ +HRESULT WINAPI AVIFileEndRecord(PAVIFILE pfile) +{ + TRACE("(%p)\n", pfile); + + if (pfile == NULL) + return AVIERR_BADHANDLE; + + return IAVIFile_EndRecord(pfile); +} + +/*********************************************************************** + * AVIStreamAddRef (AVIFIL32.@) + * AVIStreamAddRef (AVIFILE.160) + */ +ULONG WINAPI AVIStreamAddRef(PAVISTREAM pstream) +{ + TRACE("(%p)\n", pstream); + + if (pstream == NULL) { + ERR(": bad handle passed!\n"); + return 0; + } + + return IAVIStream_AddRef(pstream); +} + +/*********************************************************************** + * AVIStreamRelease (AVIFIL32.@) + * AVIStreamRelease (AVIFILE.161) + */ +ULONG WINAPI AVIStreamRelease(PAVISTREAM pstream) +{ + TRACE("(%p)\n", pstream); + + if (pstream == NULL) { + ERR(": bad handle passed!\n"); + return 0; + } + + return IAVIStream_Release(pstream); +} + +/*********************************************************************** + * AVIStreamCreate (AVIFIL32.@) + * AVIStreamCreate (AVIFILE.104) + */ +HRESULT WINAPI AVIStreamCreate(PAVISTREAM *ppavi, LONG lParam1, LONG lParam2, + LPCLSID pclsidHandler) +{ + HRESULT hr; + + TRACE("(%p,0x%08lX,0x%08lX,%s)\n", ppavi, lParam1, lParam2, + debugstr_guid(pclsidHandler)); + + if (ppavi == NULL) + return AVIERR_BADPARAM; + + *ppavi = NULL; + if (pclsidHandler == NULL) + return AVIERR_UNSUPPORTED; + + hr = CoCreateInstance(pclsidHandler, NULL, CLSCTX_INPROC, &IID_IAVIStream, (LPVOID*)ppavi); + if (FAILED(hr) || *ppavi == NULL) + return hr; + + hr = IAVIStream_Create(*ppavi, lParam1, lParam2); + if (FAILED(hr)) { + IAVIStream_Release(*ppavi); + *ppavi = NULL; + } + + return hr; +} + +/*********************************************************************** + * AVIStreamInfo (AVIFIL32.@) + * AVIStreamInfoA (AVIFIL32.@) + * AVIStreamInfo (AVIFILE.162) + */ +HRESULT WINAPI AVIStreamInfoA(PAVISTREAM pstream, LPAVISTREAMINFOA asi, + LONG size) +{ + AVISTREAMINFOW asiw; + HRESULT hres; + + TRACE("(%p,%p,%ld)\n", pstream, asi, size); + + if (pstream == NULL) + return AVIERR_BADHANDLE; + if ((DWORD)size < sizeof(AVISTREAMINFOA)) + return AVIERR_BADSIZE; + + hres = IAVIStream_Info(pstream, &asiw, sizeof(asiw)); + + memcpy(asi, &asiw, sizeof(asiw) - sizeof(asiw.szName)); + WideCharToMultiByte(CP_ACP, 0, asiw.szName, -1, asi->szName, + sizeof(asi->szName), NULL, NULL); + asi->szName[sizeof(asi->szName) - 1] = 0; + + return hres; +} + +/*********************************************************************** + * AVIStreamInfoW (AVIFIL32.@) + */ +HRESULT WINAPI AVIStreamInfoW(PAVISTREAM pstream, LPAVISTREAMINFOW asi, + LONG size) +{ + TRACE("(%p,%p,%ld)\n", pstream, asi, size); + + if (pstream == NULL) + return AVIERR_BADHANDLE; + + return IAVIStream_Info(pstream, asi, size); +} + +/*********************************************************************** + * AVIStreamFindSample (AVIFIL32.@) + * AVIStreamFindSample (AVIFILE.163) + */ +HRESULT WINAPI AVIStreamFindSample(PAVISTREAM pstream, LONG pos, DWORD flags) +{ + TRACE("(%p,%ld,0x%lX)\n", pstream, pos, flags); + + if (pstream == NULL) + return -1; + + return IAVIStream_FindSample(pstream, pos, flags); +} + +/*********************************************************************** + * AVIStreamReadFormat (AVIFIL32.@) + * AVIStreamReadFormat (AVIFILE.164) + */ +HRESULT WINAPI AVIStreamReadFormat(PAVISTREAM pstream, LONG pos, + LPVOID format, LPLONG formatsize) +{ + TRACE("(%p,%ld,%p,%p)\n", pstream, pos, format, formatsize); + + if (pstream == NULL) + return AVIERR_BADHANDLE; + + return IAVIStream_ReadFormat(pstream, pos, format, formatsize); +} + +/*********************************************************************** + * AVIStreamSetFormat (AVIFIL32.@) + * AVIStreamSetFormat (AVIFILE.169) + */ +HRESULT WINAPI AVIStreamSetFormat(PAVISTREAM pstream, LONG pos, + LPVOID format, LONG formatsize) +{ + TRACE("(%p,%ld,%p,%ld)\n", pstream, pos, format, formatsize); + + if (pstream == NULL) + return AVIERR_BADHANDLE; + + return IAVIStream_SetFormat(pstream, pos, format, formatsize); +} + +/*********************************************************************** + * AVIStreamRead (AVIFIL32.@) + * AVIStreamRead (AVIFILE.167) + */ +HRESULT WINAPI AVIStreamRead(PAVISTREAM pstream, LONG start, LONG samples, + LPVOID buffer, LONG buffersize, + LPLONG bytesread, LPLONG samplesread) +{ + TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", pstream, start, samples, buffer, + buffersize, bytesread, samplesread); + + if (pstream == NULL) + return AVIERR_BADHANDLE; + + return IAVIStream_Read(pstream, start, samples, buffer, buffersize, + bytesread, samplesread); +} + +/*********************************************************************** + * AVIStreamWrite (AVIFIL32.@) + * AVIStreamWrite (AVIFILE.168) + */ +HRESULT WINAPI AVIStreamWrite(PAVISTREAM pstream, LONG start, LONG samples, + LPVOID buffer, LONG buffersize, DWORD flags, + LPLONG sampwritten, LPLONG byteswritten) +{ + TRACE("(%p,%ld,%ld,%p,%ld,0x%lX,%p,%p)\n", pstream, start, samples, buffer, + buffersize, flags, sampwritten, byteswritten); + + if (pstream == NULL) + return AVIERR_BADHANDLE; + + return IAVIStream_Write(pstream, start, samples, buffer, buffersize, + flags, sampwritten, byteswritten); +} + +/*********************************************************************** + * AVIStreamReadData (AVIFIL32.@) + * AVIStreamReadData (AVIFILE.165) + */ +HRESULT WINAPI AVIStreamReadData(PAVISTREAM pstream, DWORD fcc, LPVOID lp, + LPLONG lpread) +{ + TRACE("(%p,'%4.4s',%p,%p)\n", pstream, (char*)&fcc, lp, lpread); + + if (pstream == NULL) + return AVIERR_BADHANDLE; + + return IAVIStream_ReadData(pstream, fcc, lp, lpread); +} + +/*********************************************************************** + * AVIStreamWriteData (AVIFIL32.@) + * AVIStreamWriteData (AVIFILE.166) + */ +HRESULT WINAPI AVIStreamWriteData(PAVISTREAM pstream, DWORD fcc, LPVOID lp, + LONG size) +{ + TRACE("(%p,'%4.4s',%p,%ld)\n", pstream, (char*)&fcc, lp, size); + + if (pstream == NULL) + return AVIERR_BADHANDLE; + + return IAVIStream_WriteData(pstream, fcc, lp, size); +} + +/*********************************************************************** + * AVIStreamGetFrameOpen (AVIFIL32.@) + * AVIStreamGetFrameOpen (AVIFILE.112) + */ +PGETFRAME WINAPI AVIStreamGetFrameOpen(PAVISTREAM pstream, + LPBITMAPINFOHEADER lpbiWanted) +{ + PGETFRAME pg = NULL; + + TRACE("(%p,%p)\n", pstream, lpbiWanted); + + if (FAILED(IAVIStream_QueryInterface(pstream, &IID_IGetFrame, (LPVOID*)&pg)) || + pg == NULL) { + pg = AVIFILE_CreateGetFrame(pstream); + if (pg == NULL) + return NULL; + } + + if (FAILED(IGetFrame_SetFormat(pg, lpbiWanted, NULL, 0, 0, -1, -1))) { + IGetFrame_Release(pg); + return NULL; + } + + return pg; +} + +/*********************************************************************** + * AVIStreamGetFrame (AVIFIL32.@) + * AVIStreamGetFrame (AVIFILE.110) + */ +LPVOID WINAPI AVIStreamGetFrame(PGETFRAME pg, LONG pos) +{ + TRACE("(%p,%ld)\n", pg, pos); + + if (pg == NULL) + return NULL; + + return IGetFrame_GetFrame(pg, pos); +} + +/*********************************************************************** + * AVIStreamGetFrameClose (AVIFIL32.@) + * AVIStreamGetFrameClose (AVIFILE.111) + */ +HRESULT WINAPI AVIStreamGetFrameClose(PGETFRAME pg) +{ + TRACE("(%p)\n", pg); + + if (pg != NULL) + return IGetFrame_Release(pg); + return 0; +} + +/*********************************************************************** + * AVIMakeCompressedStream (AVIFIL32.@) + */ +HRESULT WINAPI AVIMakeCompressedStream(PAVISTREAM *ppsCompressed, + PAVISTREAM psSource, + LPAVICOMPRESSOPTIONS aco, + LPCLSID pclsidHandler) +{ + AVISTREAMINFOW asiw; + CHAR szRegKey[25]; + CHAR szValue[100]; + CLSID clsidHandler; + HRESULT hr; + LONG size = sizeof(szValue); + + TRACE("(%p,%p,%p,%s)\n", ppsCompressed, psSource, aco, + debugstr_guid(pclsidHandler)); + + if (ppsCompressed == NULL) + return AVIERR_BADPARAM; + if (psSource == NULL) + return AVIERR_BADHANDLE; + + *ppsCompressed = NULL; + + /* if no handler given get default ones based on streamtype */ + if (pclsidHandler == NULL) { + hr = IAVIStream_Info(psSource, &asiw, sizeof(asiw)); + if (FAILED(hr)) + return hr; + + wsprintfA(szRegKey, "AVIFile\\Compressors\\%4.4s", (char*)&asiw.fccType); + if (RegQueryValueA(HKEY_CLASSES_ROOT, szRegKey, szValue, &size) != ERROR_SUCCESS) + return AVIERR_UNSUPPORTED; + if (AVIFILE_CLSIDFromString(szValue, &clsidHandler) != S_OK) + return AVIERR_UNSUPPORTED; + } else + memcpy(&clsidHandler, pclsidHandler, sizeof(clsidHandler)); + + hr = CoCreateInstance(&clsidHandler, NULL, CLSCTX_INPROC, &IID_IAVIStream, (LPVOID*)ppsCompressed); + if (FAILED(hr) || *ppsCompressed == NULL) + return hr; + + hr = IAVIStream_Create(*ppsCompressed, (LPARAM)psSource, (LPARAM)aco); + if (FAILED(hr)) { + IAVIStream_Release(*ppsCompressed); + *ppsCompressed = NULL; + } + + return hr; +} + +/*********************************************************************** + * AVIMakeFileFromStreams (AVIFIL32.@) + */ +HRESULT WINAPI AVIMakeFileFromStreams(PAVIFILE *ppfile, int nStreams, + PAVISTREAM *ppStreams) +{ + TRACE("(%p,%d,%p)\n", ppfile, nStreams, ppStreams); + + if (nStreams < 0 || ppfile == NULL || ppStreams == NULL) + return AVIERR_BADPARAM; + + *ppfile = AVIFILE_CreateAVITempFile(nStreams, ppStreams); + if (*ppfile == NULL) + return AVIERR_MEMORY; + + return AVIERR_OK; +} + +/*********************************************************************** + * AVIStreamOpenFromFile (AVIFIL32.@) + * AVIStreamOpenFromFileA (AVIFIL32.@) + * AVIStreamOpenFromFile (AVIFILE.103) + */ +HRESULT WINAPI AVIStreamOpenFromFileA(PAVISTREAM *ppavi, LPCSTR szFile, + DWORD fccType, LONG lParam, + UINT mode, LPCLSID pclsidHandler) +{ + PAVIFILE pfile = NULL; + HRESULT hr; + + TRACE("(%p,%s,'%4.4s',%ld,0x%X,%s)\n", ppavi, debugstr_a(szFile), + (char*)&fccType, lParam, mode, debugstr_guid(pclsidHandler)); + + if (ppavi == NULL || szFile == NULL) + return AVIERR_BADPARAM; + + *ppavi = NULL; + + hr = AVIFileOpenA(&pfile, szFile, mode, pclsidHandler); + if (FAILED(hr) || pfile == NULL) + return hr; + + hr = IAVIFile_GetStream(pfile, ppavi, fccType, lParam); + IAVIFile_Release(pfile); + + return hr; +} + +/*********************************************************************** + * AVIStreamOpenFromFileW (AVIFIL32.@) + */ +HRESULT WINAPI AVIStreamOpenFromFileW(PAVISTREAM *ppavi, LPCWSTR szFile, + DWORD fccType, LONG lParam, + UINT mode, LPCLSID pclsidHandler) +{ + PAVIFILE pfile = NULL; + HRESULT hr; + + TRACE("(%p,%s,'%4.4s',%ld,0x%X,%s)\n", ppavi, debugstr_w(szFile), + (char*)&fccType, lParam, mode, debugstr_guid(pclsidHandler)); + + if (ppavi == NULL || szFile == NULL) + return AVIERR_BADPARAM; + + *ppavi = NULL; + + hr = AVIFileOpenW(&pfile, szFile, mode, pclsidHandler); + if (FAILED(hr) || pfile == NULL) + return hr; + + hr = IAVIFile_GetStream(pfile, ppavi, fccType, lParam); + IAVIFile_Release(pfile); + + return hr; +} + +/*********************************************************************** + * AVIStreamBeginStreaming (AVIFIL32.@) + */ +LONG WINAPI AVIStreamBeginStreaming(PAVISTREAM pavi, LONG lStart, LONG lEnd, LONG lRate) +{ + IAVIStreaming* pstream = NULL; + HRESULT hr; + + TRACE("(%p,%ld,%ld,%ld)\n", pavi, lStart, lEnd, lRate); + + if (pavi == NULL) + return AVIERR_BADHANDLE; + + hr = IAVIStream_QueryInterface(pavi, &IID_IAVIStreaming, (LPVOID*)&pstream); + if (SUCCEEDED(hr) && pstream != NULL) { + hr = IAVIStreaming_Begin(pstream, lStart, lEnd, lRate); + IAVIStreaming_Release(pstream); + } else + hr = AVIERR_OK; + + return hr; +} + +/*********************************************************************** + * AVIStreamEndStreaming (AVIFIL32.@) + */ +LONG WINAPI AVIStreamEndStreaming(PAVISTREAM pavi) +{ + IAVIStreaming* pstream = NULL; + HRESULT hr; + + TRACE("(%p)\n", pavi); + + hr = IAVIStream_QueryInterface(pavi, &IID_IAVIStreaming, (LPVOID*)&pstream); + if (SUCCEEDED(hr) && pstream != NULL) { + IAVIStreaming_End(pstream); + IAVIStreaming_Release(pstream); + } + + return AVIERR_OK; +} + +/*********************************************************************** + * AVIStreamStart (AVIFILE.130) + * AVIStreamStart (AVIFIL32.@) + */ +LONG WINAPI AVIStreamStart(PAVISTREAM pstream) +{ + AVISTREAMINFOW asiw; + + TRACE("(%p)\n", pstream); + + if (pstream == NULL) + return 0; + + if (FAILED(IAVIStream_Info(pstream, &asiw, sizeof(asiw)))) + return 0; + + return asiw.dwStart; +} + +/*********************************************************************** + * AVIStreamLength (AVIFILE.131) + * AVIStreamLength (AVIFIL32.@) + */ +LONG WINAPI AVIStreamLength(PAVISTREAM pstream) +{ + AVISTREAMINFOW asiw; + + TRACE("(%p)\n", pstream); + + if (pstream == NULL) + return 0; + + if (FAILED(IAVIStream_Info(pstream, &asiw, sizeof(asiw)))) + return 0; + + return asiw.dwLength; +} + +/*********************************************************************** + * AVIStreamSampleToTime (AVIFILE.133) + * AVIStreamSampleToTime (AVIFIL32.@) + */ +LONG WINAPI AVIStreamSampleToTime(PAVISTREAM pstream, LONG lSample) +{ + AVISTREAMINFOW asiw; + LONG time; + + TRACE("(%p,%ld)\n", pstream, lSample); + + if (pstream == NULL) + return -1; + + if (FAILED(IAVIStream_Info(pstream, &asiw, sizeof(asiw)))) + return -1; + if (asiw.dwRate == 0) + return -1; + + /* limit to stream bounds */ + if (lSample < asiw.dwStart) + lSample = asiw.dwStart; + if (lSample > asiw.dwStart + asiw.dwLength) + lSample = asiw.dwStart + asiw.dwLength; + + if (asiw.dwRate / asiw.dwScale < 1000) + time = (LONG)(((float)lSample * asiw.dwScale * 1000) / asiw.dwRate); + else + time = (LONG)(((float)lSample * asiw.dwScale * 1000 + (asiw.dwRate - 1)) / asiw.dwRate); + + TRACE(" -> %ld\n",time); + return time; +} + +/*********************************************************************** + * AVIStreamTimeToSample (AVIFILE.132) + * AVIStreamTimeToSample (AVIFIL32.@) + */ +LONG WINAPI AVIStreamTimeToSample(PAVISTREAM pstream, LONG lTime) +{ + AVISTREAMINFOW asiw; + ULONG sample; + + TRACE("(%p,%ld)\n", pstream, lTime); + + if (pstream == NULL || lTime < 0) + return -1; + + if (FAILED(IAVIStream_Info(pstream, &asiw, sizeof(asiw)))) + return -1; + if (asiw.dwScale == 0) + return -1; + + if (asiw.dwRate / asiw.dwScale < 1000) + sample = (LONG)((((float)asiw.dwRate * lTime) / (asiw.dwScale * 1000))); + else + sample = (LONG)(((float)asiw.dwRate * lTime + (asiw.dwScale * 1000 - 1)) / (asiw.dwScale * 1000)); + + /* limit to stream bounds */ + if (sample < asiw.dwStart) + sample = asiw.dwStart; + if (sample > asiw.dwStart + asiw.dwLength) + sample = asiw.dwStart + asiw.dwLength; + + TRACE(" -> %ld\n", sample); + return sample; +} + +/*********************************************************************** + * AVIBuildFilter (AVIFIL32.@) + * AVIBuildFilterA (AVIFIL32.@) + * AVIBuildFilter (AVIFILE.123) + */ +HRESULT WINAPI AVIBuildFilterA(LPSTR szFilter, LONG cbFilter, BOOL fSaving) +{ + LPWSTR wszFilter; + HRESULT hr; + + TRACE("(%p,%ld,%d)\n", szFilter, cbFilter, fSaving); + + /* check parameters */ + if (szFilter == NULL) + return AVIERR_BADPARAM; + if (cbFilter < 2) + return AVIERR_BADSIZE; + + szFilter[0] = 0; + szFilter[1] = 0; + + wszFilter = (LPWSTR)GlobalAllocPtr(GHND, cbFilter * sizeof(WCHAR)); + if (wszFilter == NULL) + return AVIERR_MEMORY; + + hr = AVIBuildFilterW(wszFilter, cbFilter, fSaving); + if (SUCCEEDED(hr)) { + WideCharToMultiByte(CP_ACP, 0, wszFilter, cbFilter, + szFilter, cbFilter, NULL, NULL); + } + + GlobalFreePtr(wszFilter); + + return hr; +} + +/*********************************************************************** + * AVIBuildFilterW (AVIFIL32.@) + */ +HRESULT WINAPI AVIBuildFilterW(LPWSTR szFilter, LONG cbFilter, BOOL fSaving) +{ + static const WCHAR szClsid[] = {'C','L','S','I','D',0}; + static const WCHAR szExtensionFmt[] = {';','*','.','%','s',0}; + static const WCHAR szAVIFileExtensions[] = + {'A','V','I','F','i','l','e','\\','E','x','t','e','n','s','i','o','n','s',0}; + + AVIFilter *lp; + WCHAR szAllFiles[40]; + WCHAR szFileExt[10]; + WCHAR szValue[128]; + HKEY hKey; + DWORD n, i; + LONG size; + DWORD count = 0; + + TRACE("(%p,%ld,%d)\n", szFilter, cbFilter, fSaving); + + /* check parameters */ + if (szFilter == NULL) + return AVIERR_BADPARAM; + if (cbFilter < 2) + return AVIERR_BADSIZE; + + lp = (AVIFilter*)GlobalAllocPtr(GHND, MAX_FILTERS * sizeof(AVIFilter)); + if (lp == NULL) + return AVIERR_MEMORY; + + /* + * 1. iterate over HKEY_CLASSES_ROOT\\AVIFile\\Extensions and collect + * extensions and CLSID's + * 2. iterate over collected CLSID's and copy its description and its + * extensions to szFilter if it fits + * + * First filter is named "All multimedia files" and its filter is a + * collection of all possible extensions except "*.*". + */ + if (RegOpenKeyW(HKEY_CLASSES_ROOT, szAVIFileExtensions, &hKey) != S_OK) { + GlobalFreePtr(lp); + return AVIERR_ERROR; + } + for (n = 0;RegEnumKeyW(hKey, n, szFileExt, sizeof(szFileExt)) == S_OK;n++) { + /* get CLSID to extension */ + size = sizeof(szValue)/sizeof(szValue[0]); + if (RegQueryValueW(hKey, szFileExt, szValue, &size) != S_OK) + break; + + /* search if the CLSID is already known */ + for (i = 1; i <= count; i++) { + if (lstrcmpW(lp[i].szClsid, szValue) == 0) + break; /* a new one */ + } + + if (count - i == -1U) { + /* it's a new CLSID */ + + /* FIXME: How do we get info's about read/write capabilities? */ + + if (count >= MAX_FILTERS) { + /* try to inform user of our full fixed size table */ + ERR(": More than %d filters found! Adjust MAX_FILTERS in dlls/avifil32/api.c\n", MAX_FILTERS); + break; + } + + lstrcpyW(lp[i].szClsid, szValue); + + count++; + } + + /* append extension to the filter */ + wsprintfW(szValue, szExtensionFmt, szFileExt); + if (lp[i].szExtensions[0] == 0) + lstrcatW(lp[i].szExtensions, szValue + 1); + else + lstrcatW(lp[i].szExtensions, szValue); + + /* also append to the "all multimedia"-filter */ + if (lp[0].szExtensions[0] == 0) + lstrcatW(lp[0].szExtensions, szValue + 1); + else + lstrcatW(lp[0].szExtensions, szValue); + } + RegCloseKey(hKey); + + /* 2. get descriptions for the CLSIDs and fill out szFilter */ + if (RegOpenKeyW(HKEY_CLASSES_ROOT, szClsid, &hKey) != S_OK) { + GlobalFreePtr(lp); + return AVIERR_ERROR; + } + for (n = 0; n <= count; n++) { + /* first the description */ + if (n != 0) { + size = sizeof(szValue)/sizeof(szValue[0]); + if (RegQueryValueW(hKey, lp[n].szClsid, szValue, &size) == S_OK) { + size = lstrlenW(szValue); + lstrcpynW(szFilter, szValue, cbFilter); + } + } else + size = LoadStringW(AVIFILE_hModule,IDS_ALLMULTIMEDIA,szFilter,cbFilter); + + /* check for enough space */ + size++; + if (cbFilter < size + lstrlenW(lp[n].szExtensions) + 2) { + szFilter[0] = 0; + szFilter[1] = 0; + GlobalFreePtr(lp); + RegCloseKey(hKey); + return AVIERR_BUFFERTOOSMALL; + } + cbFilter -= size; + szFilter += size; + + /* and then the filter */ + lstrcpynW(szFilter, lp[n].szExtensions, cbFilter); + size = lstrlenW(lp[n].szExtensions) + 1; + cbFilter -= size; + szFilter += size; + } + + RegCloseKey(hKey); + GlobalFreePtr(lp); + + /* add "All files" "*.*" filter if enough space left */ + size = LoadStringW(AVIFILE_hModule, IDS_ALLFILES, + szAllFiles, sizeof(szAllFiles)) + 1; + if (cbFilter > size) { + int i; + + /* replace '@' with \000 to separate description of filter */ + for (i = 0; i < size && szAllFiles[i] != 0; i++) { + if (szAllFiles[i] == '@') { + szAllFiles[i] = 0; + break; + } + } + + memcpy(szFilter, szAllFiles, size * sizeof(szAllFiles[0])); + szFilter += size; + szFilter[0] = 0; + + return AVIERR_OK; + } else { + szFilter[0] = 0; + return AVIERR_BUFFERTOOSMALL; + } +} + +static BOOL AVISaveOptionsFmtChoose(HWND hWnd) +{ + LPAVICOMPRESSOPTIONS pOptions = SaveOpts.ppOptions[SaveOpts.nCurrent]; + AVISTREAMINFOW sInfo; + + TRACE("(%p)\n", hWnd); + + if (pOptions == NULL || SaveOpts.ppavis[SaveOpts.nCurrent] == NULL) { + ERR(": bad state!\n"); + return FALSE; + } + + if (FAILED(AVIStreamInfoW(SaveOpts.ppavis[SaveOpts.nCurrent], + &sInfo, sizeof(sInfo)))) { + ERR(": AVIStreamInfoW failed!\n"); + return FALSE; + } + + if (sInfo.fccType == streamtypeVIDEO) { + COMPVARS cv; + BOOL ret; + + memset(&cv, 0, sizeof(cv)); + + if ((pOptions->dwFlags & AVICOMPRESSF_VALID) == 0) { + memset(pOptions, 0, sizeof(AVICOMPRESSOPTIONS)); + pOptions->fccType = streamtypeVIDEO; + pOptions->fccHandler = comptypeDIB; + pOptions->dwQuality = (DWORD)ICQUALITY_DEFAULT; + } + + cv.cbSize = sizeof(cv); + cv.dwFlags = ICMF_COMPVARS_VALID; + /*cv.fccType = pOptions->fccType; */ + cv.fccHandler = pOptions->fccHandler; + cv.lQ = pOptions->dwQuality; + cv.lpState = pOptions->lpParms; + cv.cbState = pOptions->cbParms; + if (pOptions->dwFlags & AVICOMPRESSF_KEYFRAMES) + cv.lKey = pOptions->dwKeyFrameEvery; + else + cv.lKey = 0; + if (pOptions->dwFlags & AVICOMPRESSF_DATARATE) + cv.lDataRate = pOptions->dwBytesPerSecond / 1024; /* need kBytes */ + else + cv.lDataRate = 0; + + ret = ICCompressorChoose(hWnd, SaveOpts.uFlags, NULL, + SaveOpts.ppavis[SaveOpts.nCurrent], &cv, NULL); + + if (ret) { + pOptions->fccHandler = cv.fccHandler; + pOptions->lpParms = cv.lpState; + pOptions->cbParms = cv.cbState; + pOptions->dwQuality = cv.lQ; + if (cv.lKey != 0) { + pOptions->dwKeyFrameEvery = cv.lKey; + pOptions->dwFlags |= AVICOMPRESSF_KEYFRAMES; + } else + pOptions->dwFlags &= ~AVICOMPRESSF_KEYFRAMES; + if (cv.lDataRate != 0) { + pOptions->dwBytesPerSecond = cv.lDataRate * 1024; /* need bytes */ + pOptions->dwFlags |= AVICOMPRESSF_DATARATE; + } else + pOptions->dwFlags &= ~AVICOMPRESSF_DATARATE; + pOptions->dwFlags |= AVICOMPRESSF_VALID; + } + ICCompressorFree(&cv); + + return ret; + } else if (sInfo.fccType == streamtypeAUDIO) { + ACMFORMATCHOOSEW afmtc; + MMRESULT ret; + LONG size; + + /* FIXME: check ACM version -- Which version is needed? */ + + memset(&afmtc, 0, sizeof(afmtc)); + afmtc.cbStruct = sizeof(afmtc); + afmtc.fdwStyle = 0; + afmtc.hwndOwner = hWnd; + + acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT, &size); + if ((pOptions->cbFormat == 0 || pOptions->lpFormat == NULL) && size != 0) { + pOptions->lpFormat = GlobalAllocPtr(GMEM_MOVEABLE, size); + pOptions->cbFormat = size; + } else if (pOptions->cbFormat < (DWORD)size) { + pOptions->lpFormat = GlobalReAllocPtr(pOptions->lpFormat, size, GMEM_MOVEABLE); + pOptions->cbFormat = size; + } + if (pOptions->lpFormat == NULL) + return FALSE; + afmtc.pwfx = pOptions->lpFormat; + afmtc.cbwfx = pOptions->cbFormat; + + size = 0; + AVIStreamFormatSize(SaveOpts.ppavis[SaveOpts.nCurrent], + sInfo.dwStart, &size); + if (size < (LONG)sizeof(PCMWAVEFORMAT)) + size = sizeof(PCMWAVEFORMAT); + afmtc.pwfxEnum = GlobalAllocPtr(GHND, size); + if (afmtc.pwfxEnum != NULL) { + AVIStreamReadFormat(SaveOpts.ppavis[SaveOpts.nCurrent], + sInfo.dwStart, afmtc.pwfxEnum, &size); + afmtc.fdwEnum = ACM_FORMATENUMF_CONVERT; + } + + ret = acmFormatChooseW(&afmtc); + if (ret == S_OK) + pOptions->dwFlags |= AVICOMPRESSF_VALID; + + if (afmtc.pwfxEnum != NULL) + GlobalFreePtr(afmtc.pwfxEnum); + + return (ret == S_OK ? TRUE : FALSE); + } else { + ERR(": unknown streamtype 0x%08lX\n", sInfo.fccType); + return FALSE; + } +} + +static void AVISaveOptionsUpdate(HWND hWnd) +{ + static const WCHAR szVideoFmt[]={'%','l','d','x','%','l','d','x','%','d',0}; + static const WCHAR szAudioFmt[]={'%','s',' ','%','s',0}; + + WCHAR szFormat[128]; + AVISTREAMINFOW sInfo; + LPVOID lpFormat; + LONG size; + + TRACE("(%p)\n", hWnd); + + SaveOpts.nCurrent = SendDlgItemMessageW(hWnd,IDC_STREAM,CB_GETCURSEL,0,0); + if (SaveOpts.nCurrent < 0) + return; + + if (FAILED(AVIStreamInfoW(SaveOpts.ppavis[SaveOpts.nCurrent], &sInfo, sizeof(sInfo)))) + return; + + AVIStreamFormatSize(SaveOpts.ppavis[SaveOpts.nCurrent],sInfo.dwStart,&size); + if (size > 0) { + szFormat[0] = 0; + + /* read format to build format description string */ + lpFormat = GlobalAllocPtr(GHND, size); + if (lpFormat != NULL) { + if (SUCCEEDED(AVIStreamReadFormat(SaveOpts.ppavis[SaveOpts.nCurrent],sInfo.dwStart,lpFormat, &size))) { + if (sInfo.fccType == streamtypeVIDEO) { + LPBITMAPINFOHEADER lpbi = lpFormat; + ICINFO icinfo; + + wsprintfW(szFormat, szVideoFmt, lpbi->biWidth, + lpbi->biHeight, lpbi->biBitCount); + + if (lpbi->biCompression != BI_RGB) { + HIC hic; + + hic = ICLocate(ICTYPE_VIDEO, sInfo.fccHandler, lpFormat, + NULL, ICMODE_DECOMPRESS); + if (hic != NULL) { + if (ICGetInfo(hic, &icinfo, sizeof(icinfo)) == S_OK) + lstrcatW(szFormat, icinfo.szDescription); + ICClose(hic); + } + } else { + LoadStringW(AVIFILE_hModule, IDS_UNCOMPRESSED, + icinfo.szDescription, sizeof(icinfo.szDescription)); + lstrcatW(szFormat, icinfo.szDescription); + } + } else if (sInfo.fccType == streamtypeAUDIO) { + ACMFORMATTAGDETAILSW aftd; + ACMFORMATDETAILSW afd; + + memset(&aftd, 0, sizeof(aftd)); + memset(&afd, 0, sizeof(afd)); + + aftd.cbStruct = sizeof(aftd); + aftd.dwFormatTag = afd.dwFormatTag = + ((PWAVEFORMATEX)lpFormat)->wFormatTag; + aftd.cbFormatSize = afd.cbwfx = size; + + afd.cbStruct = sizeof(afd); + afd.pwfx = lpFormat; + + if (acmFormatTagDetailsW(NULL, &aftd, + ACM_FORMATTAGDETAILSF_FORMATTAG) == S_OK) { + if (acmFormatDetailsW(NULL,&afd,ACM_FORMATDETAILSF_FORMAT) == S_OK) + wsprintfW(szFormat, szAudioFmt, afd.szFormat, aftd.szFormatTag); + } + } + } + GlobalFreePtr(lpFormat); + } + + /* set text for format description */ + SetDlgItemTextW(hWnd, IDC_FORMATTEXT, szFormat); + + /* Disable option button for unsupported streamtypes */ + if (sInfo.fccType == streamtypeVIDEO || + sInfo.fccType == streamtypeAUDIO) + EnableWindow(GetDlgItem(hWnd, IDC_OPTIONS), TRUE); + else + EnableWindow(GetDlgItem(hWnd, IDC_OPTIONS), FALSE); + } + +} + +static INT_PTR CALLBACK AVISaveOptionsDlgProc(HWND hWnd, UINT uMsg, + WPARAM wParam, LPARAM lParam) +{ + DWORD dwInterleave; + BOOL bIsInterleaved; + INT n; + + /*TRACE("(%p,%u,0x%04X,0x%08lX)\n", hWnd, uMsg, wParam, lParam);*/ + + switch (uMsg) { + case WM_INITDIALOG: + SaveOpts.nCurrent = 0; + if (SaveOpts.nStreams == 1) { + EndDialog(hWnd, AVISaveOptionsFmtChoose(hWnd)); + return TRUE; + } + + /* add streams */ + for (n = 0; n < SaveOpts.nStreams; n++) { + AVISTREAMINFOW sInfo; + + AVIStreamInfoW(SaveOpts.ppavis[n], &sInfo, sizeof(sInfo)); + SendDlgItemMessageW(hWnd, IDC_STREAM, CB_ADDSTRING, + 0L, (LPARAM)sInfo.szName); + } + + /* select first stream */ + SendDlgItemMessageW(hWnd, IDC_STREAM, CB_SETCURSEL, 0, 0); + SendMessageW(hWnd, WM_COMMAND, + GET_WM_COMMAND_MPS(IDC_STREAM, hWnd, CBN_SELCHANGE)); + + /* initialize interleave */ + if (SaveOpts.ppOptions[0] != NULL && + (SaveOpts.ppOptions[0]->dwFlags & AVICOMPRESSF_VALID)) { + bIsInterleaved = (SaveOpts.ppOptions[0]->dwFlags & AVICOMPRESSF_INTERLEAVE); + dwInterleave = SaveOpts.ppOptions[0]->dwInterleaveEvery; + } else { + bIsInterleaved = TRUE; + dwInterleave = 0; + } + CheckDlgButton(hWnd, IDC_INTERLEAVE, bIsInterleaved); + SetDlgItemInt(hWnd, IDC_INTERLEAVEEVERY, dwInterleave, FALSE); + EnableWindow(GetDlgItem(hWnd, IDC_INTERLEAVEEVERY), bIsInterleaved); + break; + case WM_COMMAND: + switch (GET_WM_COMMAND_ID(wParam, lParam)) { + case IDOK: + /* get data from controls and save them */ + dwInterleave = GetDlgItemInt(hWnd, IDC_INTERLEAVEEVERY, NULL, 0); + bIsInterleaved = IsDlgButtonChecked(hWnd, IDC_INTERLEAVE); + for (n = 0; n < SaveOpts.nStreams; n++) { + if (SaveOpts.ppOptions[n] != NULL) { + if (bIsInterleaved) { + SaveOpts.ppOptions[n]->dwFlags |= AVICOMPRESSF_INTERLEAVE; + SaveOpts.ppOptions[n]->dwInterleaveEvery = dwInterleave; + } else + SaveOpts.ppOptions[n]->dwFlags &= ~AVICOMPRESSF_INTERLEAVE; + } + } + /* fall through */ + case IDCANCEL: + EndDialog(hWnd, GET_WM_COMMAND_ID(wParam, lParam) == IDOK); + break; + case IDC_INTERLEAVE: + EnableWindow(GetDlgItem(hWnd, IDC_INTERLEAVEEVERY), + IsDlgButtonChecked(hWnd, IDC_INTERLEAVE)); + break; + case IDC_STREAM: + if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE) { + /* update control elements */ + AVISaveOptionsUpdate(hWnd); + } + break; + case IDC_OPTIONS: + AVISaveOptionsFmtChoose(hWnd); + break; + }; + return TRUE; + }; + + return FALSE; +} + +/*********************************************************************** + * AVISaveOptions (AVIFIL32.@) + */ +BOOL WINAPI AVISaveOptions(HWND hWnd, UINT uFlags, INT nStreams, + PAVISTREAM *ppavi, LPAVICOMPRESSOPTIONS *ppOptions) +{ + LPAVICOMPRESSOPTIONS pSavedOptions = NULL; + INT ret, n; + + TRACE("(%p,0x%X,%d,%p,%p)\n", hWnd, uFlags, nStreams, + ppavi, ppOptions); + + /* check parameters */ + if (nStreams <= 0 || ppavi == NULL || ppOptions == NULL) + return AVIERR_BADPARAM; + + /* save options in case the user presses cancel */ + if (ppOptions != NULL && nStreams > 1) { + pSavedOptions = GlobalAllocPtr(GHND,nStreams * sizeof(AVICOMPRESSOPTIONS)); + if (pSavedOptions == NULL) + return FALSE; + + for (n = 0; n < nStreams; n++) { + if (ppOptions[n] != NULL) + memcpy(pSavedOptions + n, ppOptions[n], sizeof(AVICOMPRESSOPTIONS)); + } + } + + SaveOpts.uFlags = uFlags; + SaveOpts.nStreams = nStreams; + SaveOpts.ppavis = ppavi; + SaveOpts.ppOptions = ppOptions; + + ret = DialogBoxW(AVIFILE_hModule, MAKEINTRESOURCEW(IDD_SAVEOPTIONS), + hWnd, AVISaveOptionsDlgProc); + + if (ret == -1) + ret = FALSE; + + /* restore options when user pressed cancel */ + if (pSavedOptions != NULL) { + if (ret == FALSE) { + for (n = 0; n < nStreams; n++) { + if (ppOptions[n] != NULL) + memcpy(ppOptions[n], pSavedOptions + n, sizeof(AVICOMPRESSOPTIONS)); + } + } + GlobalFreePtr(pSavedOptions); + } + + return (BOOL)ret; +} + +/*********************************************************************** + * AVISaveOptionsFree (AVIFIL32.@) + * AVISaveOptionsFree (AVIFILE.124) + */ +HRESULT WINAPI AVISaveOptionsFree(INT nStreams,LPAVICOMPRESSOPTIONS*ppOptions) +{ + TRACE("(%d,%p)\n", nStreams, ppOptions); + + if (nStreams < 0 || ppOptions == NULL) + return AVIERR_BADPARAM; + + for (; nStreams > 0; nStreams--) { + if (ppOptions[nStreams] != NULL) { + ppOptions[nStreams]->dwFlags &= ~AVICOMPRESSF_VALID; + + if (ppOptions[nStreams]->lpParms != NULL) { + GlobalFreePtr(ppOptions[nStreams]->lpParms); + ppOptions[nStreams]->lpParms = NULL; + ppOptions[nStreams]->cbParms = 0; + } + if (ppOptions[nStreams]->lpFormat != NULL) { + GlobalFreePtr(ppOptions[nStreams]->lpFormat); + ppOptions[nStreams]->lpFormat = NULL; + ppOptions[nStreams]->cbFormat = 0; + } + } + } + + return AVIERR_OK; +} + +/*********************************************************************** + * AVISaveVA (AVIFIL32.@) + */ +HRESULT WINAPI AVISaveVA(LPCSTR szFile, CLSID *pclsidHandler, + AVISAVECALLBACK lpfnCallback, int nStream, + PAVISTREAM *ppavi, LPAVICOMPRESSOPTIONS *plpOptions) +{ + LPWSTR wszFile = NULL; + HRESULT hr; + int len; + + TRACE("%s,%p,%p,%d,%p,%p)\n", debugstr_a(szFile), pclsidHandler, + lpfnCallback, nStream, ppavi, plpOptions); + + if (szFile == NULL || ppavi == NULL || plpOptions == NULL) + return AVIERR_BADPARAM; + + /* convert ASCII string to Unicode and call Unicode function */ + len = MultiByteToWideChar(CP_ACP, 0, szFile, -1, NULL, 0); + if (len <= 0) + return AVIERR_BADPARAM; + + wszFile = LocalAlloc(LPTR, len * sizeof(WCHAR)); + if (wszFile == NULL) + return AVIERR_MEMORY; + + MultiByteToWideChar(CP_ACP, 0, szFile, -1, wszFile, len); + + hr = AVISaveVW(wszFile, pclsidHandler, lpfnCallback, + nStream, ppavi, plpOptions); + + LocalFree((HLOCAL)wszFile); + + return hr; +} + +/*********************************************************************** + * AVIFILE_AVISaveDefaultCallback (internal) + */ +static BOOL WINAPI AVIFILE_AVISaveDefaultCallback(INT progress) +{ + TRACE("(%d)\n", progress); + + return FALSE; +} + +/*********************************************************************** + * AVISaveVW (AVIFIL32.@) + */ +HRESULT WINAPI AVISaveVW(LPCWSTR szFile, CLSID *pclsidHandler, + AVISAVECALLBACK lpfnCallback, int nStreams, + PAVISTREAM *ppavi, LPAVICOMPRESSOPTIONS *plpOptions) +{ + LONG lStart[MAX_AVISTREAMS]; + PAVISTREAM pOutStreams[MAX_AVISTREAMS]; + PAVISTREAM pInStreams[MAX_AVISTREAMS]; + AVIFILEINFOW fInfo; + AVISTREAMINFOW sInfo; + + PAVIFILE pfile = NULL; /* the output AVI file */ + LONG lFirstVideo = -1; + int curStream; + + /* for interleaving ... */ + DWORD dwInterleave = 0; /* interleave rate */ + DWORD dwFileInitialFrames; + LONG lFileLength; + LONG lSampleInc; + + /* for reading/writing the data ... */ + LPVOID lpBuffer = NULL; + LONG cbBuffer; /* real size of lpBuffer */ + LONG lBufferSize; /* needed bytes for format(s), etc. */ + LONG lReadBytes; + LONG lReadSamples; + HRESULT hres; + + TRACE("(%s,%p,%p,%d,%p,%p)\n", debugstr_w(szFile), pclsidHandler, + lpfnCallback, nStreams, ppavi, plpOptions); + + if (szFile == NULL || ppavi == NULL || plpOptions == NULL) + return AVIERR_BADPARAM; + if (nStreams >= MAX_AVISTREAMS) { + WARN("Can't write AVI with %d streams only supports %d -- change MAX_AVISTREAMS!\n", nStreams, MAX_AVISTREAMS); + return AVIERR_INTERNAL; + } + + if (lpfnCallback == NULL) + lpfnCallback = AVIFILE_AVISaveDefaultCallback; + + /* clear local variable(s) */ + for (curStream = 0; curStream < nStreams; curStream++) { + pInStreams[curStream] = NULL; + pOutStreams[curStream] = NULL; + } + + /* open output AVI file (create it if it doesn't exist) */ + hres = AVIFileOpenW(&pfile, szFile, OF_CREATE|OF_SHARE_EXCLUSIVE|OF_WRITE, + pclsidHandler); + if (FAILED(hres)) + return hres; + AVIFileInfoW(pfile, &fInfo, sizeof(fInfo)); /* for dwCaps */ + + /* initialize our data structures part 1 */ + for (curStream = 0; curStream < nStreams; curStream++) { + PAVISTREAM pCurStream = ppavi[curStream]; + + hres = AVIStreamInfoW(pCurStream, &sInfo, sizeof(sInfo)); + if (FAILED(hres)) + goto error; + + /* search first video stream and check for interleaving */ + if (sInfo.fccType == streamtypeVIDEO) { + /* remember first video stream -- needed for interleaving */ + if (lFirstVideo < 0) + lFirstVideo = curStream; + } else if (!dwInterleave && plpOptions != NULL) { + /* check if any non-video stream wants to be interleaved */ + WARN("options.flags=0x%lX options.dwInterleave=%lu\n",plpOptions[curStream]->dwFlags,plpOptions[curStream]->dwInterleaveEvery); + if (plpOptions[curStream] != NULL && + plpOptions[curStream]->dwFlags & AVICOMPRESSF_INTERLEAVE) + dwInterleave = plpOptions[curStream]->dwInterleaveEvery; + } + + /* create de-/compressed stream interface if needed */ + pInStreams[curStream] = NULL; + if (plpOptions != NULL && plpOptions[curStream] != NULL) { + if (plpOptions[curStream]->fccHandler || + plpOptions[curStream]->lpFormat != NULL) { + DWORD dwKeySave = plpOptions[curStream]->dwKeyFrameEvery; + + if (fInfo.dwCaps & AVIFILECAPS_ALLKEYFRAMES) + plpOptions[curStream]->dwKeyFrameEvery = 1; + + hres = AVIMakeCompressedStream(&pInStreams[curStream], pCurStream, + plpOptions[curStream], NULL); + plpOptions[curStream]->dwKeyFrameEvery = dwKeySave; + if (FAILED(hres) || pInStreams[curStream] == NULL) { + pInStreams[curStream] = NULL; + goto error; + } + + /* test stream interface and update stream-info */ + hres = AVIStreamInfoW(pInStreams[curStream], &sInfo, sizeof(sInfo)); + if (FAILED(hres)) + goto error; + } + } + + /* now handle streams which will only be copied */ + if (pInStreams[curStream] == NULL) { + pCurStream = pInStreams[curStream] = ppavi[curStream]; + AVIStreamAddRef(pCurStream); + } else + pCurStream = pInStreams[curStream]; + + lStart[curStream] = sInfo.dwStart; + } /* for all streams */ + + /* check that first video stream is the first stream */ + if (lFirstVideo > 0) { + PAVISTREAM pTmp = pInStreams[lFirstVideo]; + LONG lTmp = lStart[lFirstVideo]; + + pInStreams[lFirstVideo] = pInStreams[0]; + pInStreams[0] = pTmp; + lStart[lFirstVideo] = lStart[0]; + lStart[0] = lTmp; + lFirstVideo = 0; + } + + /* allocate buffer for formats, data, etc. of an initial size of 64 kBytes*/ + lpBuffer = GlobalAllocPtr(GPTR, cbBuffer = 0x00010000); + if (lpBuffer == NULL) { + hres = AVIERR_MEMORY; + goto error; + } + + AVIStreamInfoW(pInStreams[0], &sInfo, sizeof(sInfo)); + lFileLength = sInfo.dwLength; + dwFileInitialFrames = 0; + if (lFirstVideo >= 0) { + /* check for correct version of the format + * -- need at least BITMAPINFOHEADER or newer + */ + lSampleInc = 1; + lBufferSize = cbBuffer; + hres = AVIStreamReadFormat(pInStreams[lFirstVideo], AVIStreamStart(pInStreams[lFirstVideo]), lpBuffer, &lBufferSize); + if (lBufferSize < (LONG)sizeof(BITMAPINFOHEADER)) + hres = AVIERR_INTERNAL; + if (FAILED(hres)) + goto error; + } else /* use one second blocks for interleaving if no video present */ + lSampleInc = AVIStreamTimeToSample(pInStreams[0], 1000000); + + /* create output streams */ + for (curStream = 0; curStream < nStreams; curStream++) { + AVIStreamInfoW(pInStreams[curStream], &sInfo, sizeof(sInfo)); + + sInfo.dwInitialFrames = 0; + if (dwInterleave != 0 && curStream > 0 && sInfo.fccType != streamtypeVIDEO) { + /* 750 ms initial frames for non-video streams */ + sInfo.dwInitialFrames = AVIStreamTimeToSample(pInStreams[0], 750); + } + + hres = AVIFileCreateStreamW(pfile, &pOutStreams[curStream], &sInfo); + if (pOutStreams[curStream] != NULL && SUCCEEDED(hres)) { + /* copy initial format for this stream */ + lBufferSize = cbBuffer; + hres = AVIStreamReadFormat(pInStreams[curStream], sInfo.dwStart, + lpBuffer, &lBufferSize); + if (FAILED(hres)) + goto error; + hres = AVIStreamSetFormat(pOutStreams[curStream], 0, lpBuffer, lBufferSize); + if (FAILED(hres)) + goto error; + + /* try to copy stream handler data */ + lBufferSize = cbBuffer; + hres = AVIStreamReadData(pInStreams[curStream], ckidSTREAMHANDLERDATA, + lpBuffer, &lBufferSize); + if (SUCCEEDED(hres) && lBufferSize > 0) { + hres = AVIStreamWriteData(pOutStreams[curStream],ckidSTREAMHANDLERDATA, + lpBuffer, lBufferSize); + if (FAILED(hres)) + goto error; + } + + if (dwFileInitialFrames < sInfo.dwInitialFrames) + dwFileInitialFrames = sInfo.dwInitialFrames; + lReadBytes = + AVIStreamSampleToSample(pOutStreams[0], pInStreams[curStream], + sInfo.dwLength); + if (lFileLength < lReadBytes) + lFileLength = lReadBytes; + } else { + /* creation of de-/compression stream interface failed */ + WARN("creation of (de-)compression stream failed for stream %d\n",curStream); + AVIStreamRelease(pInStreams[curStream]); + if (curStream + 1 >= nStreams) { + /* move the others one up */ + PAVISTREAM *ppas = &pInStreams[curStream]; + int n = nStreams - (curStream + 1); + + do { + *ppas = pInStreams[curStream + 1]; + } while (--n); + } + nStreams--; + curStream--; + } + } /* create output streams for all input streams */ + + /* have we still something to write, or lost everything? */ + if (nStreams <= 0) + goto error; + + if (dwInterleave) { + LONG lCurFrame = -dwFileInitialFrames; + + /* interleaved file */ + if (dwInterleave == 1) + AVIFileEndRecord(pfile); + + for (; lCurFrame < lFileLength; lCurFrame += lSampleInc) { + for (curStream = 0; curStream < nStreams; curStream++) { + LONG lLastSample; + + hres = AVIStreamInfoW(pOutStreams[curStream], &sInfo, sizeof(sInfo)); + if (FAILED(hres)) + goto error; + + /* initial frames phase at the end for this stream? */ + if (-(LONG)sInfo.dwInitialFrames > lCurFrame) + continue; + + if ((lFileLength - lSampleInc) <= lCurFrame) { + lLastSample = AVIStreamLength(pInStreams[curStream]); + lFirstVideo = lLastSample + AVIStreamStart(pInStreams[curStream]); + } else { + if (curStream != 0) { + lFirstVideo = + AVIStreamSampleToSample(pInStreams[curStream], pInStreams[0], + (sInfo.fccType == streamtypeVIDEO ? + (LONG)dwInterleave : lSampleInc) + + sInfo.dwInitialFrames + lCurFrame); + } else + lFirstVideo = lSampleInc + (sInfo.dwInitialFrames + lCurFrame); + + lLastSample = AVIStreamEnd(pInStreams[curStream]); + if (lLastSample <= lFirstVideo) + lFirstVideo = lLastSample; + } + + /* copy needed samples now */ + WARN("copy from stream %d samples %ld to %ld...\n",curStream, + lStart[curStream],lFirstVideo); + while (lFirstVideo > lStart[curStream]) { + DWORD flags = 0; + + /* copy format in case it can change */ + lBufferSize = cbBuffer; + hres = AVIStreamReadFormat(pInStreams[curStream], lStart[curStream], + lpBuffer, &lBufferSize); + if (FAILED(hres)) + goto error; + AVIStreamSetFormat(pOutStreams[curStream], lStart[curStream], + lpBuffer, lBufferSize); + + /* try to read data until we got it, or error */ + do { + hres = AVIStreamRead(pInStreams[curStream], lStart[curStream], + lFirstVideo - lStart[curStream], lpBuffer, + cbBuffer, &lReadBytes, &lReadSamples); + } while ((hres == AVIERR_BUFFERTOOSMALL) && + (lpBuffer = GlobalReAllocPtr(lpBuffer, cbBuffer *= 2, GPTR)) != NULL); + if (lpBuffer == NULL) + hres = AVIERR_MEMORY; + if (FAILED(hres)) + goto error; + + if (AVIStreamIsKeyFrame(pInStreams[curStream], (LONG)sInfo.dwStart)) + flags = AVIIF_KEYFRAME; + hres = AVIStreamWrite(pOutStreams[curStream], -1, lReadSamples, + lpBuffer, lReadBytes, flags, NULL, NULL); + if (FAILED(hres)) + goto error; + + lStart[curStream] += lReadSamples; + } + lStart[curStream] = lFirstVideo; + } /* stream by stream */ + + /* need to close this block? */ + if (dwInterleave == 1) { + hres = AVIFileEndRecord(pfile); + if (FAILED(hres)) + break; + } + + /* show progress */ + if (lpfnCallback(MulDiv(dwFileInitialFrames + lCurFrame, 100, + dwFileInitialFrames + lFileLength))) { + hres = AVIERR_USERABORT; + break; + } + } /* copy frame by frame */ + } else { + /* non-interleaved file */ + + for (curStream = 0; curStream < nStreams; curStream++) { + /* show progress */ + if (lpfnCallback(MulDiv(curStream, 100, nStreams))) { + hres = AVIERR_USERABORT; + goto error; + } + + AVIStreamInfoW(pInStreams[curStream], &sInfo, sizeof(sInfo)); + + if (sInfo.dwSampleSize != 0) { + /* sample-based data like audio */ + while (sInfo.dwStart < sInfo.dwLength) { + LONG lSamples = cbBuffer / sInfo.dwSampleSize; + + /* copy format in case it can change */ + lBufferSize = cbBuffer; + hres = AVIStreamReadFormat(pInStreams[curStream], sInfo.dwStart, + lpBuffer, &lBufferSize); + if (FAILED(hres)) + return hres; + AVIStreamSetFormat(pOutStreams[curStream], sInfo.dwStart, + lpBuffer, lBufferSize); + + /* limit to stream boundaries */ + if (lSamples != (LONG)(sInfo.dwLength - sInfo.dwStart)) + lSamples = sInfo.dwLength - sInfo.dwStart; + + /* now try to read until we get it, or an error occurs */ + do { + lReadBytes = cbBuffer; + lReadSamples = 0; + hres = AVIStreamRead(pInStreams[curStream],sInfo.dwStart,lSamples, + lpBuffer,cbBuffer,&lReadBytes,&lReadSamples); + } while ((hres == AVIERR_BUFFERTOOSMALL) && + (lpBuffer = GlobalReAllocPtr(lpBuffer, cbBuffer *= 2, GPTR)) != NULL); + if (lpBuffer == NULL) + hres = AVIERR_MEMORY; + if (FAILED(hres)) + goto error; + if (lReadSamples != 0) { + sInfo.dwStart += lReadSamples; + hres = AVIStreamWrite(pOutStreams[curStream], -1, lReadSamples, + lpBuffer, lReadBytes, 0, NULL , NULL); + if (FAILED(hres)) + goto error; + + /* show progress */ + if (lpfnCallback(MulDiv(sInfo.dwStart,100,nStreams*sInfo.dwLength)+ + MulDiv(curStream, 100, nStreams))) { + hres = AVIERR_USERABORT; + goto error; + } + } else { + if ((sInfo.dwLength - sInfo.dwStart) != 1) { + hres = AVIERR_FILEREAD; + goto error; + } + } + } + } else { + /* block-based data like video */ + for (; sInfo.dwStart < sInfo.dwLength; sInfo.dwStart++) { + DWORD flags = 0; + + /* copy format in case it can change */ + lBufferSize = cbBuffer; + hres = AVIStreamReadFormat(pInStreams[curStream], sInfo.dwStart, + lpBuffer, &lBufferSize); + if (FAILED(hres)) + goto error; + AVIStreamSetFormat(pOutStreams[curStream], sInfo.dwStart, + lpBuffer, lBufferSize); + + /* try to read block and resize buffer if necessary */ + do { + lReadSamples = 0; + lReadBytes = cbBuffer; + hres = AVIStreamRead(pInStreams[curStream], sInfo.dwStart, 1, + lpBuffer, cbBuffer,&lReadBytes,&lReadSamples); + } while ((hres == AVIERR_BUFFERTOOSMALL) && + (lpBuffer = GlobalReAllocPtr(lpBuffer, cbBuffer *= 2, GPTR)) != NULL); + if (lpBuffer == NULL) + hres = AVIERR_MEMORY; + if (FAILED(hres)) + goto error; + if (lReadSamples != 1) { + hres = AVIERR_FILEREAD; + goto error; + } + + if (AVIStreamIsKeyFrame(pInStreams[curStream], (LONG)sInfo.dwStart)) + flags = AVIIF_KEYFRAME; + hres = AVIStreamWrite(pOutStreams[curStream], -1, lReadSamples, + lpBuffer, lReadBytes, flags, NULL, NULL); + if (FAILED(hres)) + goto error; + + /* show progress */ + if (lpfnCallback(MulDiv(sInfo.dwStart,100,nStreams*sInfo.dwLength)+ + MulDiv(curStream, 100, nStreams))) { + hres = AVIERR_USERABORT; + goto error; + } + } /* copy all blocks */ + } + } /* copy data stream by stream */ + } + + error: + if (lpBuffer != NULL) + GlobalFreePtr(lpBuffer); + if (pfile != NULL) { + for (curStream = 0; curStream < nStreams; curStream++) { + if (pOutStreams[curStream] != NULL) + AVIStreamRelease(pOutStreams[curStream]); + if (pInStreams[curStream] != NULL) + AVIStreamRelease(pInStreams[curStream]); + } + + AVIFileRelease(pfile); + } + + return hres; +} + +/*********************************************************************** + * CreateEditableStream (AVIFIL32.@) + */ +HRESULT WINAPI CreateEditableStream(PAVISTREAM *ppEditable, PAVISTREAM pSource) +{ + IAVIEditStream *pEdit = NULL; + HRESULT hr; + + TRACE("(%p,%p)\n", ppEditable, pSource); + + if (ppEditable == NULL) + return AVIERR_BADPARAM; + + *ppEditable = NULL; + + if (pSource != NULL) { + hr = IAVIStream_QueryInterface(pSource, &IID_IAVIEditStream, + (LPVOID*)&pEdit); + if (SUCCEEDED(hr) && pEdit != NULL) { + hr = IAVIEditStream_Clone(pEdit, ppEditable); + IAVIEditStream_Release(pEdit); + + return hr; + } + } + + /* need own implementation of IAVIEditStream */ + pEdit = AVIFILE_CreateEditStream(pSource); + if (pEdit == NULL) + return AVIERR_MEMORY; + + hr = IAVIEditStream_QueryInterface(pEdit, &IID_IAVIStream, + (LPVOID*)ppEditable); + IAVIEditStream_Release(pEdit); + + return hr; +} + +/*********************************************************************** + * EditStreamClone (AVIFIL32.@) + */ +HRESULT WINAPI EditStreamClone(PAVISTREAM pStream, PAVISTREAM *ppResult) +{ + PAVIEDITSTREAM pEdit = NULL; + HRESULT hr; + + TRACE("(%p,%p)\n", pStream, ppResult); + + if (pStream == NULL) + return AVIERR_BADHANDLE; + if (ppResult == NULL) + return AVIERR_BADPARAM; + + *ppResult = NULL; + + hr = IAVIStream_QueryInterface(pStream, &IID_IAVIEditStream,(LPVOID*)&pEdit); + if (SUCCEEDED(hr) && pEdit != NULL) { + hr = IAVIEditStream_Clone(pEdit, ppResult); + + IAVIEditStream_Release(pEdit); + } else + hr = AVIERR_UNSUPPORTED; + + return hr; +} + +/*********************************************************************** + * EditStreamCopy (AVIFIL32.@) + */ +HRESULT WINAPI EditStreamCopy(PAVISTREAM pStream, LONG *plStart, + LONG *plLength, PAVISTREAM *ppResult) +{ + PAVIEDITSTREAM pEdit = NULL; + HRESULT hr; + + TRACE("(%p,%p,%p,%p)\n", pStream, plStart, plLength, ppResult); + + if (pStream == NULL) + return AVIERR_BADHANDLE; + if (plStart == NULL || plLength == NULL || ppResult == NULL) + return AVIERR_BADPARAM; + + *ppResult = NULL; + + hr = IAVIStream_QueryInterface(pStream, &IID_IAVIEditStream,(LPVOID*)&pEdit); + if (SUCCEEDED(hr) && pEdit != NULL) { + hr = IAVIEditStream_Copy(pEdit, plStart, plLength, ppResult); + + IAVIEditStream_Release(pEdit); + } else + hr = AVIERR_UNSUPPORTED; + + return hr; +} + +/*********************************************************************** + * EditStreamCut (AVIFIL32.@) + */ +HRESULT WINAPI EditStreamCut(PAVISTREAM pStream, LONG *plStart, + LONG *plLength, PAVISTREAM *ppResult) +{ + PAVIEDITSTREAM pEdit = NULL; + HRESULT hr; + + TRACE("(%p,%p,%p,%p)\n", pStream, plStart, plLength, ppResult); + + if (ppResult != NULL) + *ppResult = NULL; + if (pStream == NULL) + return AVIERR_BADHANDLE; + if (plStart == NULL || plLength == NULL) + return AVIERR_BADPARAM; + + hr = IAVIStream_QueryInterface(pStream, &IID_IAVIEditStream,(LPVOID*)&pEdit); + if (SUCCEEDED(hr) && pEdit != NULL) { + hr = IAVIEditStream_Cut(pEdit, plStart, plLength, ppResult); + + IAVIEditStream_Release(pEdit); + } else + hr = AVIERR_UNSUPPORTED; + + return hr; +} + +/*********************************************************************** + * EditStreamPaste (AVIFIL32.@) + */ +HRESULT WINAPI EditStreamPaste(PAVISTREAM pDest, LONG *plStart, LONG *plLength, + PAVISTREAM pSource, LONG lStart, LONG lEnd) +{ + PAVIEDITSTREAM pEdit = NULL; + HRESULT hr; + + TRACE("(%p,%p,%p,%p,%ld,%ld)\n", pDest, plStart, plLength, + pSource, lStart, lEnd); + + if (pDest == NULL || pSource == NULL) + return AVIERR_BADHANDLE; + if (plStart == NULL || plLength == NULL || lStart < 0) + return AVIERR_BADPARAM; + + hr = IAVIStream_QueryInterface(pDest, &IID_IAVIEditStream,(LPVOID*)&pEdit); + if (SUCCEEDED(hr) && pEdit != NULL) { + hr = IAVIEditStream_Paste(pEdit, plStart, plLength, pSource, lStart, lEnd); + + IAVIEditStream_Release(pEdit); + } else + hr = AVIERR_UNSUPPORTED; + + return hr; +} + +/*********************************************************************** + * EditStreamSetInfoA (AVIFIL32.@) + */ +HRESULT WINAPI EditStreamSetInfoA(PAVISTREAM pstream, LPAVISTREAMINFOA asi, + LONG size) +{ + AVISTREAMINFOW asiw; + + TRACE("(%p,%p,%ld)\n", pstream, asi, size); + + if (pstream == NULL) + return AVIERR_BADHANDLE; + if ((DWORD)size < sizeof(AVISTREAMINFOA)) + return AVIERR_BADSIZE; + + memcpy(&asiw, asi, sizeof(asiw) - sizeof(asiw.szName)); + MultiByteToWideChar(CP_ACP, 0, asi->szName, -1, + asiw.szName, sizeof(asiw.szName)); + + return EditStreamSetInfoW(pstream, &asiw, sizeof(asiw)); +} + +/*********************************************************************** + * EditStreamSetInfoW (AVIFIL32.@) + */ +HRESULT WINAPI EditStreamSetInfoW(PAVISTREAM pstream, LPAVISTREAMINFOW asi, + LONG size) +{ + PAVIEDITSTREAM pEdit = NULL; + HRESULT hr; + + TRACE("(%p,%p,%ld)\n", pstream, asi, size); + + hr = IAVIStream_QueryInterface(pstream, &IID_IAVIEditStream,(LPVOID*)&pEdit); + if (SUCCEEDED(hr) && pEdit != NULL) { + hr = IAVIEditStream_SetInfo(pEdit, asi, size); + + IAVIEditStream_Release(pEdit); + } else + hr = AVIERR_UNSUPPORTED; + + return hr; +} + +/*********************************************************************** + * EditStreamSetNameA (AVIFIL32.@) + */ +HRESULT WINAPI EditStreamSetNameA(PAVISTREAM pstream, LPCSTR szName) +{ + AVISTREAMINFOA asia; + HRESULT hres; + + TRACE("(%p,%s)\n", pstream, debugstr_a(szName)); + + if (pstream == NULL) + return AVIERR_BADHANDLE; + if (szName == NULL) + return AVIERR_BADPARAM; + + hres = AVIStreamInfoA(pstream, &asia, sizeof(asia)); + if (FAILED(hres)) + return hres; + + memset(asia.szName, 0, sizeof(asia.szName)); + lstrcpynA(asia.szName, szName, sizeof(asia.szName)/sizeof(asia.szName[0])); + + return EditStreamSetInfoA(pstream, &asia, sizeof(asia)); +} + +/*********************************************************************** + * EditStreamSetNameW (AVIFIL32.@) + */ +HRESULT WINAPI EditStreamSetNameW(PAVISTREAM pstream, LPCWSTR szName) +{ + AVISTREAMINFOW asiw; + HRESULT hres; + + TRACE("(%p,%s)\n", pstream, debugstr_w(szName)); + + if (pstream == NULL) + return AVIERR_BADHANDLE; + if (szName == NULL) + return AVIERR_BADPARAM; + + hres = IAVIStream_Info(pstream, &asiw, sizeof(asiw)); + if (FAILED(hres)) + return hres; + + memset(asiw.szName, 0, sizeof(asiw.szName)); + lstrcpynW(asiw.szName, szName, sizeof(asiw.szName)/sizeof(asiw.szName[0])); + + return EditStreamSetInfoW(pstream, &asiw, sizeof(asiw)); +} + +/*********************************************************************** + * AVIClearClipboard (AVIFIL32.@) + */ +HRESULT WINAPI AVIClearClipboard(void) +{ + TRACE("()\n"); + + return AVIERR_UNSUPPORTED; /* OleSetClipboard(NULL); */ +} + +/*********************************************************************** + * AVIGetFromClipboard (AVIFIL32.@) + */ +HRESULT WINAPI AVIGetFromClipboard(PAVIFILE *ppfile) +{ + FIXME("(%p), stub!\n", ppfile); + + *ppfile = NULL; + + return AVIERR_UNSUPPORTED; +} + +/*********************************************************************** + * AVIMakeStreamFromClipboard (AVIFIL32.@) + */ +HRESULT WINAPI AVIMakeStreamFromClipboard(UINT cfFormat, HANDLE hGlobal, + PAVISTREAM * ppstream) +{ + FIXME("(0x%08x,%p,%p), stub!\n", cfFormat, hGlobal, ppstream); + + if (ppstream == NULL) + return AVIERR_BADHANDLE; + + return AVIERR_UNSUPPORTED; +} + +/*********************************************************************** + * AVIPutFileOnClipboard (AVIFIL32.@) + */ +HRESULT WINAPI AVIPutFileOnClipboard(PAVIFILE pfile) +{ + FIXME("(%p), stub!\n", pfile); + + if (pfile == NULL) + return AVIERR_BADHANDLE; + + return AVIERR_UNSUPPORTED; +} + +HRESULT CDECL AVISaveA(LPCSTR szFile, CLSID * pclsidHandler, AVISAVECALLBACK lpfnCallback, + int nStreams, PAVISTREAM pavi, LPAVICOMPRESSOPTIONS lpOptions, ...) +{ + FIXME("(%s,%p,%p,0x%08x,%p,%p), stub!\n", debugstr_a(szFile), pclsidHandler, lpfnCallback, + nStreams, pavi, lpOptions); + + return AVIERR_UNSUPPORTED; +} + +HRESULT CDECL AVISaveW(LPCWSTR szFile, CLSID * pclsidHandler, AVISAVECALLBACK lpfnCallback, + int nStreams, PAVISTREAM pavi, LPAVICOMPRESSOPTIONS lpOptions, ...) +{ + FIXME("(%s,%p,%p,0x%08x,%p,%p), stub!\n", debugstr_w(szFile), pclsidHandler, lpfnCallback, + nStreams, pavi, lpOptions); + + return AVIERR_UNSUPPORTED; +} diff --git a/reactos/lib/avifil32/avifil32.spec b/reactos/lib/avifil32/avifil32.spec new file mode 100644 index 00000000000..9eae7495fc9 --- /dev/null +++ b/reactos/lib/avifil32/avifil32.spec @@ -0,0 +1,79 @@ +@ stdcall AVIBuildFilter(str long long) AVIBuildFilterA +@ stdcall AVIBuildFilterA(str long long) +@ stdcall AVIBuildFilterW(wstr long long) +@ stdcall AVIClearClipboard() +@ stdcall AVIFileAddRef(ptr) +@ stdcall AVIFileCreateStream(ptr ptr ptr) AVIFileCreateStreamA +@ stdcall AVIFileCreateStreamA(ptr ptr ptr) +@ stdcall AVIFileCreateStreamW(ptr ptr ptr) +@ stdcall AVIFileEndRecord(ptr) +@ stdcall AVIFileExit() +@ stdcall AVIFileGetStream(ptr ptr long long) +@ stdcall AVIFileInfo (ptr ptr long) AVIFileInfoA # A in both Win95 and NT +@ stdcall AVIFileInfoA(ptr ptr long) +@ stdcall AVIFileInfoW(ptr ptr long) +@ stdcall AVIFileInit() +@ stdcall AVIFileOpen(ptr str long ptr) AVIFileOpenA +@ stdcall AVIFileOpenA(ptr str long ptr) +@ stdcall AVIFileOpenW(ptr wstr long ptr) +@ stdcall AVIFileReadData(ptr long ptr ptr) +@ stdcall AVIFileRelease(ptr) +@ stdcall AVIFileWriteData(ptr long ptr long) +@ stdcall AVIGetFromClipboard(ptr) +@ stdcall AVIMakeCompressedStream(ptr ptr ptr ptr) +@ stdcall AVIMakeFileFromStreams(ptr long ptr) +@ stdcall AVIMakeStreamFromClipboard(long long ptr) +@ stdcall AVIPutFileOnClipboard(ptr) +@ varargs AVISave(str ptr ptr long ptr ptr) AVISaveA +@ varargs AVISaveA(str ptr ptr long ptr ptr) +@ stdcall AVISaveOptions(long long long ptr ptr) +@ stdcall AVISaveOptionsFree(long ptr) +@ stdcall AVISaveV(str ptr ptr long ptr ptr) AVISaveVA +@ stdcall AVISaveVA(str ptr ptr long ptr ptr) +@ stdcall AVISaveVW(wstr ptr ptr long ptr ptr) +@ varargs AVISaveW(wstr ptr ptr long ptr ptr) +@ stdcall AVIStreamAddRef(ptr) +@ stdcall AVIStreamBeginStreaming(ptr long long long) +@ stdcall AVIStreamCreate(ptr long long ptr) +@ stdcall AVIStreamEndStreaming(ptr) +@ stdcall AVIStreamFindSample(ptr long long) +@ stdcall AVIStreamGetFrame(ptr long) +@ stdcall AVIStreamGetFrameClose(ptr) +@ stdcall AVIStreamGetFrameOpen(ptr ptr) +@ stdcall AVIStreamInfo (ptr ptr long) AVIStreamInfoA +@ stdcall AVIStreamInfoA(ptr ptr long) +@ stdcall AVIStreamInfoW(ptr ptr long) +@ stdcall AVIStreamLength(ptr) +@ stdcall AVIStreamOpenFromFile (ptr str long long long ptr) AVIStreamOpenFromFileA +@ stdcall AVIStreamOpenFromFileA(ptr str long long long ptr) +@ stdcall AVIStreamOpenFromFileW(ptr wstr long long long ptr) +@ stdcall AVIStreamRead(ptr long long ptr long ptr ptr) +@ stdcall AVIStreamReadData(ptr long ptr ptr) +@ stdcall AVIStreamReadFormat(ptr long ptr long) +@ stdcall AVIStreamRelease(ptr) +@ stdcall AVIStreamSampleToTime(ptr long) +@ stdcall AVIStreamSetFormat(ptr long ptr long) +@ stdcall AVIStreamStart(ptr) +@ stdcall AVIStreamTimeToSample(ptr long) +@ stdcall AVIStreamWrite(ptr long long ptr long long ptr ptr) +@ stdcall AVIStreamWriteData(ptr long ptr long) +@ extern CLSID_AVISimpleUnMarshal +@ stdcall CreateEditableStream(ptr ptr) +@ stdcall -private DllCanUnloadNow() +@ stdcall -private DllGetClassObject(ptr ptr ptr) +@ stdcall -private DllRegisterServer() +@ stdcall -private DllUnregisterServer() +@ stdcall EditStreamClone(ptr ptr) +@ stdcall EditStreamCopy(ptr ptr ptr ptr) +@ stdcall EditStreamCut(ptr ptr ptr ptr) +@ stdcall EditStreamPaste(ptr ptr ptr ptr long long) +@ stdcall EditStreamSetInfo(ptr ptr long) EditStreamSetInfoA +@ stdcall EditStreamSetInfoA(ptr ptr long) +@ stdcall EditStreamSetInfoW(ptr ptr long) +@ stdcall EditStreamSetName(ptr str) EditStreamSetNameA +@ stdcall EditStreamSetNameA(ptr str) +@ stdcall EditStreamSetNameW(ptr wstr) +@ extern IID_IAVIEditStream +@ extern IID_IAVIFile +@ extern IID_IAVIStream +@ extern IID_IGetFrame diff --git a/reactos/lib/avifil32/avifil32.xml b/reactos/lib/avifil32/avifil32.xml new file mode 100644 index 00000000000..7c4c9e4d3bc --- /dev/null +++ b/reactos/lib/avifil32/avifil32.xml @@ -0,0 +1,28 @@ + + + . + include/wine + + 0x501 + uuid + ntdll + winmm + ole32 + msvfw32 + msacm32 + kernel32 + wine + acmstream.c + api.c + avifile.c + editstream.c + extrachunk.c + factory.c + getframe.c + icmstream.c + regsvr.c + tmpfile.c + wavfile.c + rsrc.rc + avifil32.spec + diff --git a/reactos/lib/avifil32/avifile.c b/reactos/lib/avifil32/avifile.c new file mode 100644 index 00000000000..901010d59d9 --- /dev/null +++ b/reactos/lib/avifil32/avifile.c @@ -0,0 +1,2587 @@ +/* + * Copyright 1999 Marcus Meissner + * Copyright 2002-2003 Michael Günnewig + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* TODO: + * - IAVIStreaming interface is missing for the IAVIStreamImpl + * - IAVIStream_fnFindSample: FIND_INDEX isn't supported. + * - IAVIStream_fnReadFormat: formatchanges aren't read in. + * - IAVIStream_fnDelete: a stub. + * - IAVIStream_fnSetInfo: a stub. + * - make thread safe + * + * KNOWN Bugs: + * - native version can hangup when reading a file generated with this DLL. + * When index is missing it works, but index seems to be okay. + */ + +#define COM_NO_WINDOWS_H +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "winerror.h" +#include "windowsx.h" +#include "mmsystem.h" +#include "vfw.h" + +#include "avifile_private.h" +#include "extrachunk.h" + +#include "wine/unicode.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(avifile); + +#ifndef IDX_PER_BLOCK +#define IDX_PER_BLOCK 2730 +#endif + +/***********************************************************************/ + +static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile* iface,REFIID refiid,LPVOID *obj); +static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile* iface); +static ULONG WINAPI IAVIFile_fnRelease(IAVIFile* iface); +static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile*iface,AVIFILEINFOW*afi,LONG size); +static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile*iface,PAVISTREAM*avis,DWORD fccType,LONG lParam); +static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile*iface,PAVISTREAM*avis,AVISTREAMINFOW*asi); +static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG size); +static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG *size); +static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile*iface); +static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile*iface,DWORD fccType,LONG lParam); + +static const struct IAVIFileVtbl iavift = { + IAVIFile_fnQueryInterface, + IAVIFile_fnAddRef, + IAVIFile_fnRelease, + IAVIFile_fnInfo, + IAVIFile_fnGetStream, + IAVIFile_fnCreateStream, + IAVIFile_fnWriteData, + IAVIFile_fnReadData, + IAVIFile_fnEndRecord, + IAVIFile_fnDeleteStream +}; + +static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile*iface,REFIID refiid,LPVOID*obj); +static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile*iface); +static ULONG WINAPI IPersistFile_fnRelease(IPersistFile*iface); +static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile*iface,CLSID*pClassID); +static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile*iface); +static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile*iface,LPCOLESTR pszFileName,DWORD dwMode); +static HRESULT WINAPI IPersistFile_fnSave(IPersistFile*iface,LPCOLESTR pszFileName,BOOL fRemember); +static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile*iface,LPCOLESTR pszFileName); +static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile*iface,LPOLESTR*ppszFileName); + +static const struct IPersistFileVtbl ipersistft = { + IPersistFile_fnQueryInterface, + IPersistFile_fnAddRef, + IPersistFile_fnRelease, + IPersistFile_fnGetClassID, + IPersistFile_fnIsDirty, + IPersistFile_fnLoad, + IPersistFile_fnSave, + IPersistFile_fnSaveCompleted, + IPersistFile_fnGetCurFile +}; + +static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj); +static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream*iface); +static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface); +static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2); +static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size); +static LONG WINAPI IAVIStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags); +static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize); +static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize); +static HRESULT WINAPI IAVIStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread); +static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten); +static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream*iface,LONG start,LONG samples); +static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread); +static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size); +static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen); + +static const struct IAVIStreamVtbl iavist = { + IAVIStream_fnQueryInterface, + IAVIStream_fnAddRef, + IAVIStream_fnRelease, + IAVIStream_fnCreate, + IAVIStream_fnInfo, + IAVIStream_fnFindSample, + IAVIStream_fnReadFormat, + IAVIStream_fnSetFormat, + IAVIStream_fnRead, + IAVIStream_fnWrite, + IAVIStream_fnDelete, + IAVIStream_fnReadData, + IAVIStream_fnWriteData, + IAVIStream_fnSetInfo +}; + +typedef struct _IAVIFileImpl IAVIFileImpl; + +typedef struct _IPersistFileImpl { + /* IUnknown stuff */ + const IPersistFileVtbl *lpVtbl; + + /* IPersistFile stuff */ + IAVIFileImpl *paf; +} IPersistFileImpl; + +typedef struct _IAVIStreamImpl { + /* IUnknown stuff */ + const IAVIStreamVtbl *lpVtbl; + LONG ref; + + /* IAVIStream stuff */ + IAVIFileImpl *paf; + DWORD nStream; /* the n-th stream in file */ + AVISTREAMINFOW sInfo; + + LPVOID lpFormat; + DWORD cbFormat; + + LPVOID lpHandlerData; + DWORD cbHandlerData; + + EXTRACHUNKS extra; + + LPDWORD lpBuffer; + DWORD cbBuffer; /* size of lpBuffer */ + DWORD dwCurrentFrame; /* frame/block currently in lpBuffer */ + + LONG lLastFrame; /* last correct index in idxFrames */ + AVIINDEXENTRY *idxFrames; + DWORD nIdxFrames; /* upper index limit of idxFrames */ + AVIINDEXENTRY *idxFmtChanges; + DWORD nIdxFmtChanges; /* upper index limit of idxFmtChanges */ +} IAVIStreamImpl; + +struct _IAVIFileImpl { + /* IUnknown stuff */ + const IAVIFileVtbl *lpVtbl; + LONG ref; + + /* IAVIFile stuff... */ + IPersistFileImpl iPersistFile; + + AVIFILEINFOW fInfo; + IAVIStreamImpl *ppStreams[MAX_AVISTREAMS]; + + EXTRACHUNKS fileextra; + + DWORD dwMoviChunkPos; /* some stuff for saving ... */ + DWORD dwIdxChunkPos; + DWORD dwNextFramePos; + DWORD dwInitialFrames; + + MMCKINFO ckLastRecord; + AVIINDEXENTRY *idxRecords; /* won't be updated while loading */ + DWORD nIdxRecords; /* current fill level */ + DWORD cbIdxRecords; /* size of idxRecords */ + + /* IPersistFile stuff ... */ + HMMIO hmmio; + LPWSTR szFileName; + UINT uMode; + BOOL fDirty; +}; + +/***********************************************************************/ + +static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, + DWORD offset, DWORD flags); +static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This); +static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This); +static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, + LPAVISTREAMINFOW asi); +static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This); +static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This); +static HRESULT AVIFILE_LoadIndex(IAVIFileImpl *This, DWORD size, DWORD offset); +static HRESULT AVIFILE_ParseIndex(IAVIFileImpl *This, AVIINDEXENTRY *lp, + LONG count, DWORD pos, BOOL *bAbsolute); +static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD start, + LPVOID buffer, LONG size); +static void AVIFILE_SamplesToBlock(IAVIStreamImpl *This, LPLONG pos, + LPLONG offset); +static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This); +static HRESULT AVIFILE_SaveIndex(IAVIFileImpl *This); +static ULONG AVIFILE_SearchStream(IAVIFileImpl *This, DWORD fccType, + LONG lSkip); +static void AVIFILE_UpdateInfo(IAVIFileImpl *This); +static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block, + FOURCC ckid, DWORD flags, LPVOID buffer, + LONG size); + +HRESULT AVIFILE_CreateAVIFile(REFIID riid, LPVOID *ppv) +{ + IAVIFileImpl *pfile; + HRESULT hr; + + assert(riid != NULL && ppv != NULL); + + *ppv = NULL; + + pfile = (IAVIFileImpl*)LocalAlloc(LPTR, sizeof(IAVIFileImpl)); + if (pfile == NULL) + return AVIERR_MEMORY; + + pfile->lpVtbl = &iavift; + pfile->ref = 0; + pfile->iPersistFile.lpVtbl = &ipersistft; + pfile->iPersistFile.paf = pfile; + + hr = IAVIFile_QueryInterface((IAVIFile*)pfile, riid, ppv); + if (FAILED(hr)) + LocalFree((HLOCAL)pfile); + + return hr; +} + +static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID refiid, + LPVOID *obj) +{ + IAVIFileImpl *This = (IAVIFileImpl *)iface; + + TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj); + + if (IsEqualGUID(&IID_IUnknown, refiid) || + IsEqualGUID(&IID_IAVIFile, refiid)) { + *obj = iface; + IAVIFile_AddRef(iface); + + return S_OK; + } else if (IsEqualGUID(&IID_IPersistFile, refiid)) { + *obj = &This->iPersistFile; + IAVIFile_AddRef(iface); + + return S_OK; + } + + return OLE_E_ENUM_NOMORE; +} + +static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface) +{ + IAVIFileImpl *This = (IAVIFileImpl *)iface; + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) -> %ld\n", iface, ref); + + return ref; +} + +static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface) +{ + IAVIFileImpl *This = (IAVIFileImpl *)iface; + UINT i; + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) -> %ld\n", iface, ref); + + if (!ref) { + if (This->fDirty) { + /* need to write headers to file */ + AVIFILE_SaveFile(This); + } + + for (i = 0; i < This->fInfo.dwStreams; i++) { + if (This->ppStreams[i] != NULL) { + if (This->ppStreams[i]->ref != 0) { + ERR(": someone has still %lu reference to stream %u (%p)!\n", + This->ppStreams[i]->ref, i, This->ppStreams[i]); + } + AVIFILE_DestructAVIStream(This->ppStreams[i]); + LocalFree((HLOCAL)This->ppStreams[i]); + This->ppStreams[i] = NULL; + } + } + + if (This->idxRecords != NULL) { + GlobalFreePtr(This->idxRecords); + This->idxRecords = NULL; + This->nIdxRecords = 0; + } + + if (This->fileextra.lp != NULL) { + GlobalFreePtr(This->fileextra.lp); + This->fileextra.lp = NULL; + This->fileextra.cb = 0; + } + + if (This->szFileName != NULL) { + LocalFree((HLOCAL)This->szFileName); + This->szFileName = NULL; + } + if (This->hmmio != NULL) { + mmioClose(This->hmmio, 0); + This->hmmio = NULL; + } + + LocalFree((HLOCAL)This); + } + return ref; +} + +static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, LPAVIFILEINFOW afi, + LONG size) +{ + IAVIFileImpl *This = (IAVIFileImpl *)iface; + + TRACE("(%p,%p,%ld)\n",iface,afi,size); + + if (afi == NULL) + return AVIERR_BADPARAM; + if (size < 0) + return AVIERR_BADSIZE; + + AVIFILE_UpdateInfo(This); + + memcpy(afi, &This->fInfo, min((DWORD)size, sizeof(This->fInfo))); + + if ((DWORD)size < sizeof(This->fInfo)) + return AVIERR_BUFFERTOOSMALL; + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, PAVISTREAM *avis, + DWORD fccType, LONG lParam) +{ + IAVIFileImpl *This = (IAVIFileImpl *)iface; + + ULONG nStream; + + TRACE("(%p,%p,0x%08lX,%ld)\n", iface, avis, fccType, lParam); + + if (avis == NULL || lParam < 0) + return AVIERR_BADPARAM; + + nStream = AVIFILE_SearchStream(This, fccType, lParam); + + /* Does the requested stream exist? */ + if (nStream < This->fInfo.dwStreams && + This->ppStreams[nStream] != NULL) { + *avis = (PAVISTREAM)This->ppStreams[nStream]; + IAVIStream_AddRef(*avis); + + return AVIERR_OK; + } + + /* Sorry, but the specified stream doesn't exist */ + return AVIERR_NODATA; +} + +static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface,PAVISTREAM *avis, + LPAVISTREAMINFOW asi) +{ + IAVIFileImpl *This = (IAVIFileImpl *)iface; + + DWORD n; + + TRACE("(%p,%p,%p)\n", iface, avis, asi); + + /* check parameters */ + if (avis == NULL || asi == NULL) + return AVIERR_BADPARAM; + + *avis = NULL; + + /* Does the user have write permission? */ + if ((This->uMode & MMIO_RWMODE) == 0) + return AVIERR_READONLY; + + /* Can we add another stream? */ + n = This->fInfo.dwStreams; + if (n >= MAX_AVISTREAMS || This->dwMoviChunkPos != 0) { + /* already reached max nr of streams + * or have already written frames to disk */ + return AVIERR_UNSUPPORTED; + } + + /* check AVISTREAMINFO for some really needed things */ + if (asi->fccType == 0 || asi->dwScale == 0 || asi->dwRate == 0) + return AVIERR_BADFORMAT; + + /* now it seems to be save to add the stream */ + assert(This->ppStreams[n] == NULL); + This->ppStreams[n] = (IAVIStreamImpl*)LocalAlloc(LPTR, + sizeof(IAVIStreamImpl)); + if (This->ppStreams[n] == NULL) + return AVIERR_MEMORY; + + /* initialize the new allocated stream */ + AVIFILE_ConstructAVIStream(This, n, asi); + + This->fInfo.dwStreams++; + This->fDirty = TRUE; + + /* update our AVIFILEINFO structure */ + AVIFILE_UpdateInfo(This); + + /* return it */ + *avis = (PAVISTREAM)This->ppStreams[n]; + IAVIStream_AddRef(*avis); + + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid, + LPVOID lpData, LONG size) +{ + IAVIFileImpl *This = (IAVIFileImpl *)iface; + + TRACE("(%p,0x%08lX,%p,%ld)\n", iface, ckid, lpData, size); + + /* check parameters */ + if (lpData == NULL) + return AVIERR_BADPARAM; + if (size < 0) + return AVIERR_BADSIZE; + + /* Do we have write permission? */ + if ((This->uMode & MMIO_RWMODE) == 0) + return AVIERR_READONLY; + + This->fDirty = TRUE; + + return WriteExtraChunk(&This->fileextra, ckid, lpData, size); +} + +static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid, + LPVOID lpData, LONG *size) +{ + IAVIFileImpl *This = (IAVIFileImpl *)iface; + + TRACE("(%p,0x%08lX,%p,%p)\n", iface, ckid, lpData, size); + + return ReadExtraChunk(&This->fileextra, ckid, lpData, size); +} + +static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface) +{ + IAVIFileImpl *This = (IAVIFileImpl *)iface; + + TRACE("(%p)\n",iface); + + if ((This->uMode & MMIO_RWMODE) == 0) + return AVIERR_READONLY; + + This->fDirty = TRUE; + + /* no frames written to any stream? -- compute start of 'movi'-chunk */ + if (This->dwMoviChunkPos == 0) + AVIFILE_ComputeMoviStart(This); + + This->fInfo.dwFlags |= AVIFILEINFO_ISINTERLEAVED; + + /* already written frames to any stream, ... */ + if (This->ckLastRecord.dwFlags & MMIO_DIRTY) { + /* close last record */ + if (mmioAscend(This->hmmio, &This->ckLastRecord, 0) != 0) + return AVIERR_FILEWRITE; + + AVIFILE_AddRecord(This); + + if (This->fInfo.dwSuggestedBufferSize < This->ckLastRecord.cksize + 3 * sizeof(DWORD)) + This->fInfo.dwSuggestedBufferSize = This->ckLastRecord.cksize + 3 * sizeof(DWORD); + } + + /* write out a new record into file, but don't close it */ + This->ckLastRecord.cksize = 0; + This->ckLastRecord.fccType = listtypeAVIRECORD; + if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1) + return AVIERR_FILEWRITE; + if (mmioCreateChunk(This->hmmio, &This->ckLastRecord, MMIO_CREATELIST) != 0) + return AVIERR_FILEWRITE; + This->dwNextFramePos += 3 * sizeof(DWORD); + + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType, + LONG lParam) +{ + IAVIFileImpl *This = (IAVIFileImpl *)iface; + + ULONG nStream; + + TRACE("(%p,0x%08lX,%ld)\n", iface, fccType, lParam); + + /* check parameter */ + if (lParam < 0) + return AVIERR_BADPARAM; + + /* Have user write permissions? */ + if ((This->uMode & MMIO_RWMODE) == 0) + return AVIERR_READONLY; + + nStream = AVIFILE_SearchStream(This, fccType, lParam); + + /* Does the requested stream exist? */ + if (nStream < This->fInfo.dwStreams && + This->ppStreams[nStream] != NULL) { + /* ... so delete it now */ + LocalFree((HLOCAL)This->ppStreams[nStream]); + + if (This->fInfo.dwStreams - nStream > 0) + memcpy(This->ppStreams + nStream, This->ppStreams + nStream + 1, + (This->fInfo.dwStreams - nStream) * sizeof(IAVIStreamImpl*)); + + This->ppStreams[This->fInfo.dwStreams] = NULL; + This->fInfo.dwStreams--; + This->fDirty = TRUE; + + /* This->fInfo will be updated further when asked for */ + return AVIERR_OK; + } else + return AVIERR_NODATA; +} + +/***********************************************************************/ + +static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface, + REFIID refiid, LPVOID *obj) +{ + IPersistFileImpl *This = (IPersistFileImpl *)iface; + + assert(This->paf != NULL); + + return IAVIFile_QueryInterface((PAVIFILE)This->paf, refiid, obj); +} + +static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile *iface) +{ + IPersistFileImpl *This = (IPersistFileImpl *)iface; + + assert(This->paf != NULL); + + return IAVIFile_AddRef((PAVIFILE)This->paf); +} + +static ULONG WINAPI IPersistFile_fnRelease(IPersistFile *iface) +{ + IPersistFileImpl *This = (IPersistFileImpl *)iface; + + assert(This->paf != NULL); + + return IAVIFile_Release((PAVIFILE)This->paf); +} + +static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface, + LPCLSID pClassID) +{ + TRACE("(%p,%p)\n", iface, pClassID); + + if (pClassID == NULL) + return AVIERR_BADPARAM; + + memcpy(pClassID, &CLSID_AVIFile, sizeof(CLSID_AVIFile)); + + return AVIERR_OK; +} + +static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface) +{ + IPersistFileImpl *This = (IPersistFileImpl *)iface; + + TRACE("(%p)\n", iface); + + assert(This->paf != NULL); + + return (This->paf->fDirty ? S_OK : S_FALSE); +} + +static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface, + LPCOLESTR pszFileName, DWORD dwMode) +{ + IPersistFileImpl *This = (IPersistFileImpl *)iface; + + int len; + + TRACE("(%p,%s,0x%08lX)\n", iface, debugstr_w(pszFileName), dwMode); + + /* check parameter */ + if (pszFileName == NULL) + return AVIERR_BADPARAM; + + assert(This->paf != NULL); + if (This->paf->hmmio != NULL) + return AVIERR_ERROR; /* No reuse of this object for another file! */ + + /* remeber mode and name */ + This->paf->uMode = dwMode; + + len = lstrlenW(pszFileName) + 1; + This->paf->szFileName = (LPWSTR)LocalAlloc(LPTR, len * sizeof(WCHAR)); + if (This->paf->szFileName == NULL) + return AVIERR_MEMORY; + lstrcpyW(This->paf->szFileName, pszFileName); + + /* try to open the file */ + This->paf->hmmio = mmioOpenW(This->paf->szFileName, NULL, + MMIO_ALLOCBUF | dwMode); + if (This->paf->hmmio == NULL) { + /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */ + LPSTR szFileName; + + len = WideCharToMultiByte(CP_ACP, 0, This->paf->szFileName, -1, + NULL, 0, NULL, NULL); + szFileName = LocalAlloc(LPTR, len * sizeof(CHAR)); + if (szFileName == NULL) + return AVIERR_MEMORY; + + WideCharToMultiByte(CP_ACP, 0, This->paf->szFileName, -1, szFileName, + len, NULL, NULL); + + This->paf->hmmio = mmioOpenA(szFileName, NULL, MMIO_ALLOCBUF | dwMode); + LocalFree((HLOCAL)szFileName); + if (This->paf->hmmio == NULL) + return AVIERR_FILEOPEN; + } + + /* should we create a new file? */ + if (dwMode & OF_CREATE) { + memset(& This->paf->fInfo, 0, sizeof(This->paf->fInfo)); + This->paf->fInfo.dwFlags = AVIFILEINFO_HASINDEX | AVIFILEINFO_TRUSTCKTYPE; + + return AVIERR_OK; + } else + return AVIFILE_LoadFile(This->paf); +} + +static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface, + LPCOLESTR pszFileName,BOOL fRemember) +{ + TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember); + + /* We write directly to disk, so nothing to do. */ + + return AVIERR_OK; +} + +static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface, + LPCOLESTR pszFileName) +{ + TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName)); + + /* We write directly to disk, so nothing to do. */ + + return AVIERR_OK; +} + +static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface, + LPOLESTR *ppszFileName) +{ + IPersistFileImpl *This = (IPersistFileImpl *)iface; + + TRACE("(%p,%p)\n", iface, ppszFileName); + + if (ppszFileName == NULL) + return AVIERR_BADPARAM; + + *ppszFileName = NULL; + + assert(This->paf != NULL); + + if (This->paf->szFileName != NULL) { + int len = lstrlenW(This->paf->szFileName) + 1; + + *ppszFileName = (LPOLESTR)GlobalAllocPtr(GHND, len * sizeof(WCHAR)); + if (*ppszFileName == NULL) + return AVIERR_MEMORY; + + strcpyW(*ppszFileName, This->paf->szFileName); + } + + return AVIERR_OK; +} + +/***********************************************************************/ + +static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface, + REFIID refiid, LPVOID *obj) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj); + + if (IsEqualGUID(&IID_IUnknown, refiid) || + IsEqualGUID(&IID_IAVIStream, refiid)) { + *obj = This; + IAVIStream_AddRef(iface); + + return S_OK; + } + /* FIXME: IAVIStreaming interface */ + + return OLE_E_ENUM_NOMORE; +} + +static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) -> %ld\n", iface, ref); + + /* also add ref to parent, so that it doesn't kill us */ + if (This->paf != NULL) + IAVIFile_AddRef((PAVIFILE)This->paf); + + return ref; +} + +static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) -> %ld\n", iface, ref); + + if (This->paf != NULL) + IAVIFile_Release((PAVIFILE)This->paf); + + return ref; +} + +static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1, + LPARAM lParam2) +{ + TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2); + + /* This IAVIStream interface needs an AVIFile */ + return AVIERR_UNSUPPORTED; +} + +static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi, + LONG size) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + TRACE("(%p,%p,%ld)\n", iface, psi, size); + + if (psi == NULL) + return AVIERR_BADPARAM; + if (size < 0) + return AVIERR_BADSIZE; + + memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo))); + + if ((DWORD)size < sizeof(This->sInfo)) + return AVIERR_BUFFERTOOSMALL; + return AVIERR_OK; +} + +static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos, + LONG flags) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + LONG offset = 0; + + TRACE("(%p,%ld,0x%08lX)\n",iface,pos,flags); + + if (flags & FIND_FROM_START) { + pos = This->sInfo.dwStart; + flags &= ~(FIND_FROM_START|FIND_PREV); + flags |= FIND_NEXT; + } + + if (This->sInfo.dwSampleSize != 0) { + /* convert samples into block number with offset */ + AVIFILE_SamplesToBlock(This, &pos, &offset); + } + + if (flags & FIND_TYPE) { + if (flags & FIND_KEY) { + while (0 <= pos && pos <= This->lLastFrame) { + if (This->idxFrames[pos].dwFlags & AVIIF_KEYFRAME) + goto RETURN_FOUND; + + if (flags & FIND_NEXT) + pos++; + else + pos--; + }; + } else if (flags & FIND_ANY) { + while (0 <= pos && pos <= This->lLastFrame) { + if (This->idxFrames[pos].dwChunkLength > 0) + goto RETURN_FOUND; + + if (flags & FIND_NEXT) + pos++; + else + pos--; + + }; + } else if ((flags & FIND_FORMAT) && This->idxFmtChanges != NULL && + This->sInfo.fccType == streamtypeVIDEO) { + if (flags & FIND_NEXT) { + ULONG n; + + for (n = 0; n < This->sInfo.dwFormatChangeCount; n++) + if (This->idxFmtChanges[n].ckid >= pos) { + pos = This->idxFmtChanges[n].ckid; + goto RETURN_FOUND; + } + } else { + LONG n; + + for (n = (LONG)This->sInfo.dwFormatChangeCount; n >= 0; n--) { + if (This->idxFmtChanges[n].ckid <= pos) { + pos = This->idxFmtChanges[n].ckid; + goto RETURN_FOUND; + } + } + + if (pos > (LONG)This->sInfo.dwStart) + return 0; /* format changes always for first frame */ + } + } + + return -1; + } + + RETURN_FOUND: + if (pos < (LONG)This->sInfo.dwStart) + return -1; + + switch (flags & FIND_RET) { + case FIND_LENGTH: + /* physical size */ + pos = This->idxFrames[pos].dwChunkLength; + break; + case FIND_OFFSET: + /* physical position */ + pos = This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD) + + offset * This->sInfo.dwSampleSize; + break; + case FIND_SIZE: + /* logical size */ + if (This->sInfo.dwSampleSize) + pos = This->sInfo.dwSampleSize; + else + pos = 1; + break; + case FIND_INDEX: + FIXME(": FIND_INDEX flag is not supported!\n"); + /* This is an index in the index-table on disc. */ + break; + }; /* else logical position */ + + return pos; +} + +static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos, + LPVOID format, LONG *formatsize) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + TRACE("(%p,%ld,%p,%p)\n", iface, pos, format, formatsize); + + if (formatsize == NULL) + return AVIERR_BADPARAM; + + /* only interested in needed buffersize? */ + if (format == NULL || *formatsize <= 0) { + *formatsize = This->cbFormat; + + return AVIERR_OK; + } + + /* copy initial format (only as much as will fit) */ + memcpy(format, This->lpFormat, min(*(DWORD*)formatsize, This->cbFormat)); + if (*(DWORD*)formatsize < This->cbFormat) { + *formatsize = This->cbFormat; + return AVIERR_BUFFERTOOSMALL; + } + + /* Could format change? When yes will it change? */ + if ((This->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) && + pos > This->sInfo.dwStart) { + LONG lLastFmt; + + lLastFmt = IAVIStream_fnFindSample(iface, pos, FIND_FORMAT|FIND_PREV); + if (lLastFmt > 0) { + FIXME(": need to read formatchange for %ld -- unimplemented!\n",lLastFmt); + } + } + + *formatsize = This->cbFormat; + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos, + LPVOID format, LONG formatsize) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + LPBITMAPINFOHEADER lpbiNew = (LPBITMAPINFOHEADER)format; + + TRACE("(%p,%ld,%p,%ld)\n", iface, pos, format, formatsize); + + /* check parameters */ + if (format == NULL || formatsize <= 0) + return AVIERR_BADPARAM; + + /* Do we have write permission? */ + if ((This->paf->uMode & MMIO_RWMODE) == 0) + return AVIERR_READONLY; + + /* can only set format before frame is written! */ + if (This->lLastFrame > pos) + return AVIERR_UNSUPPORTED; + + /* initial format or a formatchange? */ + if (This->lpFormat == NULL) { + /* initial format */ + if (This->paf->dwMoviChunkPos != 0) + return AVIERR_ERROR; /* user has used API in wrong sequnece! */ + + This->lpFormat = GlobalAllocPtr(GMEM_MOVEABLE, formatsize); + if (This->lpFormat == NULL) + return AVIERR_MEMORY; + This->cbFormat = formatsize; + + memcpy(This->lpFormat, format, formatsize); + + /* update some infos about stream */ + if (This->sInfo.fccType == streamtypeVIDEO) { + LONG lDim; + + lDim = This->sInfo.rcFrame.right - This->sInfo.rcFrame.left; + if (lDim < lpbiNew->biWidth) + This->sInfo.rcFrame.right = This->sInfo.rcFrame.left + lpbiNew->biWidth; + lDim = This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top; + if (lDim < lpbiNew->biHeight) + This->sInfo.rcFrame.bottom = This->sInfo.rcFrame.top + lpbiNew->biHeight; + } else if (This->sInfo.fccType == streamtypeAUDIO) + This->sInfo.dwSampleSize = ((LPWAVEFORMATEX)This->lpFormat)->nBlockAlign; + + return AVIERR_OK; + } else { + MMCKINFO ck; + LPBITMAPINFOHEADER lpbiOld = (LPBITMAPINFOHEADER)This->lpFormat; + RGBQUAD *rgbNew = (RGBQUAD*)((LPBYTE)lpbiNew + lpbiNew->biSize); + AVIPALCHANGE *lppc = NULL; + UINT n; + + /* perhaps format change, check it ... */ + if (This->cbFormat != formatsize) + return AVIERR_UNSUPPORTED; + + /* no format change, only the initial one */ + if (memcmp(This->lpFormat, format, formatsize) == 0) + return AVIERR_OK; + + /* check that's only the palette, which changes */ + if (lpbiOld->biSize != lpbiNew->biSize || + lpbiOld->biWidth != lpbiNew->biWidth || + lpbiOld->biHeight != lpbiNew->biHeight || + lpbiOld->biPlanes != lpbiNew->biPlanes || + lpbiOld->biBitCount != lpbiNew->biBitCount || + lpbiOld->biCompression != lpbiNew->biCompression || + lpbiOld->biClrUsed != lpbiNew->biClrUsed) + return AVIERR_UNSUPPORTED; + + This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES; + + /* simply say all colors have changed */ + ck.ckid = MAKEAVICKID(cktypePALchange, This->nStream); + ck.cksize = 2 * sizeof(WORD) + lpbiOld->biClrUsed * sizeof(PALETTEENTRY); + lppc = (AVIPALCHANGE*)GlobalAllocPtr(GMEM_MOVEABLE, ck.cksize); + if (lppc == NULL) + return AVIERR_MEMORY; + + lppc->bFirstEntry = 0; + lppc->bNumEntries = (lpbiOld->biClrUsed < 256 ? lpbiOld->biClrUsed : 0); + lppc->wFlags = 0; + for (n = 0; n < lpbiOld->biClrUsed; n++) { + lppc->peNew[n].peRed = rgbNew[n].rgbRed; + lppc->peNew[n].peGreen = rgbNew[n].rgbGreen; + lppc->peNew[n].peBlue = rgbNew[n].rgbBlue; + lppc->peNew[n].peFlags = 0; + } + + if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1) + return AVIERR_FILEWRITE; + if (mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + if (mmioWrite(This->paf->hmmio, (HPSTR)lppc, ck.cksize) != ck.cksize) + return AVIERR_FILEWRITE; + if (mmioAscend(This->paf->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + This->paf->dwNextFramePos += ck.cksize + 2 * sizeof(DWORD); + + GlobalFreePtr(lppc); + + return AVIFILE_AddFrame(This, cktypePALchange, n, ck.dwDataOffset, 0); + } +} + +static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start, + LONG samples, LPVOID buffer, + LONG buffersize, LPLONG bytesread, + LPLONG samplesread) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + DWORD size; + HRESULT hr; + + TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface, start, samples, buffer, + buffersize, bytesread, samplesread); + + /* clear return parameters if given */ + if (bytesread != NULL) + *bytesread = 0; + if (samplesread != NULL) + *samplesread = 0; + + /* check parameters */ + if ((LONG)This->sInfo.dwStart > start) + return AVIERR_NODATA; /* couldn't read before start of stream */ + if (This->sInfo.dwStart + This->sInfo.dwLength < (DWORD)start) + return AVIERR_NODATA; /* start is past end of stream */ + + /* should we read as much as possible? */ + if (samples == -1) { + /* User should know how much we have read */ + if (bytesread == NULL && samplesread == NULL) + return AVIERR_BADPARAM; + + if (This->sInfo.dwSampleSize != 0) + samples = buffersize / This->sInfo.dwSampleSize; + else + samples = 1; + } + + /* limit to end of stream */ + if ((LONG)This->sInfo.dwLength < samples) + samples = This->sInfo.dwLength; + if ((start - This->sInfo.dwStart) > (This->sInfo.dwLength - samples)) + samples = This->sInfo.dwLength - (start - This->sInfo.dwStart); + + /* nothing to read? Then leave ... */ + if (samples == 0) + return AVIERR_OK; + + if (This->sInfo.dwSampleSize != 0) { + /* fixed samplesize -- we can read over frame/block boundaries */ + LONG block = start; + LONG offset = 0; + + /* convert start sample to block,offset pair */ + AVIFILE_SamplesToBlock(This, &block, &offset); + + /* convert samples to bytes */ + samples *= This->sInfo.dwSampleSize; + + while (samples > 0 && buffersize > 0) { + if (block != This->dwCurrentFrame) { + hr = AVIFILE_ReadBlock(This, block, NULL, 0); + if (FAILED(hr)) + return hr; + } + + size = min((DWORD)samples, (DWORD)buffersize); + size = min(size, This->cbBuffer - offset); + memcpy(buffer, ((BYTE*)&This->lpBuffer[2]) + offset, size); + + block++; + offset = 0; + buffer = ((LPBYTE)buffer)+size; + samples -= size; + buffersize -= size; + + /* fill out return parameters if given */ + if (bytesread != NULL) + *bytesread += size; + if (samplesread != NULL) + *samplesread += size / This->sInfo.dwSampleSize; + } + + if (samples == 0) + return AVIERR_OK; + else + return AVIERR_BUFFERTOOSMALL; + } else { + /* variable samplesize -- we can only read one full frame/block */ + if (samples > 1) + samples = 1; + + assert(start <= This->lLastFrame); + size = This->idxFrames[start].dwChunkLength; + if (buffer != NULL && buffersize >= size) { + hr = AVIFILE_ReadBlock(This, start, buffer, size); + if (FAILED(hr)) + return hr; + } else if (buffer != NULL) + return AVIERR_BUFFERTOOSMALL; + + /* fill out return parameters if given */ + if (bytesread != NULL) + *bytesread = size; + if (samplesread != NULL) + *samplesread = samples; + + return AVIERR_OK; + } +} + +static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start, + LONG samples, LPVOID buffer, + LONG buffersize, DWORD flags, + LPLONG sampwritten, + LPLONG byteswritten) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + FOURCC ckid; + HRESULT hr; + + TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n", iface, start, samples, + buffer, buffersize, flags, sampwritten, byteswritten); + + /* clear return parameters if given */ + if (sampwritten != NULL) + *sampwritten = 0; + if (byteswritten != NULL) + *byteswritten = 0; + + /* check parameters */ + if (buffer == NULL && (buffersize > 0 || samples > 0)) + return AVIERR_BADPARAM; + + /* Have we write permission? */ + if ((This->paf->uMode & MMIO_RWMODE) == 0) + return AVIERR_READONLY; + + switch (This->sInfo.fccType) { + case streamtypeAUDIO: + ckid = MAKEAVICKID(cktypeWAVEbytes, This->nStream); + break; + default: + if ((flags & AVIIF_KEYFRAME) && buffersize != 0) + ckid = MAKEAVICKID(cktypeDIBbits, This->nStream); + else + ckid = MAKEAVICKID(cktypeDIBcompressed, This->nStream); + break; + }; + + /* append to end of stream? */ + if (start == -1) { + if (This->lLastFrame == -1) + start = This->sInfo.dwStart; + else + start = This->sInfo.dwLength; + } else if (This->lLastFrame == -1) + This->sInfo.dwStart = start; + + if (This->sInfo.dwSampleSize != 0) { + /* fixed sample size -- audio like */ + if (samples * This->sInfo.dwSampleSize != buffersize) + return AVIERR_BADPARAM; + + /* Couldn't skip audio-like data -- User must supply appropriate silence */ + if (This->sInfo.dwLength != start) + return AVIERR_UNSUPPORTED; + + /* Convert position to frame/block */ + start = This->lLastFrame + 1; + + if ((This->paf->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) == 0) { + FIXME(": not interleaved, could collect audio data!\n"); + } + } else { + /* variable sample size -- video like */ + if (samples > 1) + return AVIERR_UNSUPPORTED; + + /* must we fill up with empty frames? */ + if (This->lLastFrame != -1) { + FOURCC ckid2 = MAKEAVICKID(cktypeDIBcompressed, This->nStream); + + while (start > This->lLastFrame + 1) { + hr = AVIFILE_WriteBlock(This, This->lLastFrame + 1, ckid2, 0, NULL, 0); + if (FAILED(hr)) + return hr; + } + } + } + + /* write the block now */ + hr = AVIFILE_WriteBlock(This, start, ckid, flags, buffer, buffersize); + if (SUCCEEDED(hr)) { + /* fill out return parameters if given */ + if (sampwritten != NULL) + *sampwritten = samples; + if (byteswritten != NULL) + *byteswritten = buffersize; + } + + return hr; +} + +static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start, + LONG samples) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + FIXME("(%p,%ld,%ld): stub\n", iface, start, samples); + + /* check parameters */ + if (start < 0 || samples < 0) + return AVIERR_BADPARAM; + + /* Delete before start of stream? */ + if (start + samples < This->sInfo.dwStart) + return AVIERR_OK; + + /* Delete after end of stream? */ + if (start > This->sInfo.dwLength) + return AVIERR_OK; + + /* For the rest we need write permissions */ + if ((This->paf->uMode & MMIO_RWMODE) == 0) + return AVIERR_READONLY; + + /* 1. overwrite the data with JUNK + * + * if ISINTERLEAVED { + * 2. concat all neighboured JUNK-blocks in this record to one + * 3. if this record only contains JUNK and is at end set dwNextFramePos + * to start of this record, repeat this. + * } else { + * 2. concat all neighboured JUNK-blocks. + * 3. if the JUNK block is at the end, then set dwNextFramePos to + * start of this block. + * } + */ + + return AVIERR_UNSUPPORTED; +} + +static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc, + LPVOID lp, LPLONG lpread) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + TRACE("(%p,0x%08lX,%p,%p)\n", iface, fcc, lp, lpread); + + if (fcc == ckidSTREAMHANDLERDATA) { + if (This->lpHandlerData != NULL && This->cbHandlerData > 0) { + if (lp == NULL || *lpread <= 0) { + *lpread = This->cbHandlerData; + return AVIERR_OK; + } + + memcpy(lp, This->lpHandlerData, min(This->cbHandlerData, *lpread)); + if (*lpread < This->cbHandlerData) + return AVIERR_BUFFERTOOSMALL; + return AVIERR_OK; + } else + return AVIERR_NODATA; + } else + return ReadExtraChunk(&This->extra, fcc, lp, lpread); +} + +static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc, + LPVOID lp, LONG size) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + TRACE("(%p,0x%08lx,%p,%ld)\n", iface, fcc, lp, size); + + /* check parameters */ + if (lp == NULL) + return AVIERR_BADPARAM; + if (size <= 0) + return AVIERR_BADSIZE; + + /* need write permission */ + if ((This->paf->uMode & MMIO_RWMODE) == 0) + return AVIERR_READONLY; + + /* already written something to this file? */ + if (This->paf->dwMoviChunkPos != 0) { + /* the data will be inserted before the 'movi' chunk, so check for + * enough space */ + DWORD dwPos = AVIFILE_ComputeMoviStart(This->paf); + + /* ckid,size => 2 * sizeof(DWORD) */ + dwPos += 2 * sizeof(DWORD) + size; + if (size >= This->paf->dwMoviChunkPos - 2 * sizeof(DWORD)) + return AVIERR_UNSUPPORTED; /* not enough space left */ + } + + This->paf->fDirty = TRUE; + + if (fcc == ckidSTREAMHANDLERDATA) { + if (This->lpHandlerData != NULL) { + FIXME(": handler data already set -- overwirte?\n"); + return AVIERR_UNSUPPORTED; + } + + This->lpHandlerData = GlobalAllocPtr(GMEM_MOVEABLE, size); + if (This->lpHandlerData == NULL) + return AVIERR_MEMORY; + This->cbHandlerData = size; + memcpy(This->lpHandlerData, lp, size); + + return AVIERR_OK; + } else + return WriteExtraChunk(&This->extra, fcc, lp, size); +} + +static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream *iface, + LPAVISTREAMINFOW info, LONG infolen) +{ + FIXME("(%p,%p,%ld): stub\n", iface, info, infolen); + + return E_FAIL; +} + +/***********************************************************************/ + +static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, DWORD offset, DWORD flags) +{ + /* pre-conditions */ + assert(This != NULL); + + switch (TWOCCFromFOURCC(ckid)) { + case cktypeDIBbits: + if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE) + flags |= AVIIF_KEYFRAME; + break; + case cktypeDIBcompressed: + if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE) + flags &= ~AVIIF_KEYFRAME; + break; + case cktypePALchange: + if (This->sInfo.fccType != streamtypeVIDEO) { + ERR(": found palette change in non-video stream!\n"); + return AVIERR_BADFORMAT; + } + This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES; + This->sInfo.dwFormatChangeCount++; + + if (This->idxFmtChanges == NULL || This->sInfo.dwFormatChangeCount < This->nIdxFmtChanges) { + UINT n = This->sInfo.dwFormatChangeCount; + + This->nIdxFmtChanges += 16; + if (This->idxFmtChanges == NULL) + This->idxFmtChanges = + GlobalAllocPtr(GHND, This->nIdxFmtChanges * sizeof(AVIINDEXENTRY)); + else + This->idxFmtChanges = + GlobalReAllocPtr(This->idxFmtChanges, + This->nIdxFmtChanges * sizeof(AVIINDEXENTRY), GHND); + if (This->idxFmtChanges == NULL) + return AVIERR_MEMORY; + + This->idxFmtChanges[n].ckid = This->lLastFrame; + This->idxFmtChanges[n].dwFlags = 0; + This->idxFmtChanges[n].dwChunkOffset = offset; + This->idxFmtChanges[n].dwChunkLength = size; + + return AVIERR_OK; + } + break; + case cktypeWAVEbytes: + if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE) + flags |= AVIIF_KEYFRAME; + break; + default: + WARN(": unknown TWOCC 0x%04X found\n", TWOCCFromFOURCC(ckid)); + break; + }; + + /* first frame is alwasy a keyframe */ + if (This->lLastFrame == -1) + flags |= AVIIF_KEYFRAME; + + if (This->sInfo.dwSuggestedBufferSize < size) + This->sInfo.dwSuggestedBufferSize = size; + + /* get memory for index */ + if (This->idxFrames == NULL || This->lLastFrame + 1 >= This->nIdxFrames) { + This->nIdxFrames += 512; + if (This->idxFrames == NULL) + This->idxFrames = + GlobalAllocPtr(GHND, This->nIdxFrames * sizeof(AVIINDEXENTRY)); + else + This->idxFrames = + GlobalReAllocPtr(This->idxFrames, + This->nIdxFrames * sizeof(AVIINDEXENTRY), GHND); + if (This->idxFrames == NULL) + return AVIERR_MEMORY; + } + + This->lLastFrame++; + This->idxFrames[This->lLastFrame].ckid = ckid; + This->idxFrames[This->lLastFrame].dwFlags = flags; + This->idxFrames[This->lLastFrame].dwChunkOffset = offset; + This->idxFrames[This->lLastFrame].dwChunkLength = size; + + /* update AVISTREAMINFO structure if necessary */ + if (This->sInfo.dwLength <= This->lLastFrame) + This->sInfo.dwLength = This->lLastFrame + 1; + + return AVIERR_OK; +} + +static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This) +{ + /* pre-conditions */ + assert(This != NULL && This->ppStreams[0] != NULL); + + if (This->idxRecords == NULL || This->cbIdxRecords == 0) { + This->cbIdxRecords += 1024 * sizeof(AVIINDEXENTRY); + This->idxRecords = GlobalAllocPtr(GHND, This->cbIdxRecords); + if (This->idxRecords == NULL) + return AVIERR_MEMORY; + } + + assert(This->nIdxRecords < This->cbIdxRecords/sizeof(AVIINDEXENTRY)); + + This->idxRecords[This->nIdxRecords].ckid = listtypeAVIRECORD; + This->idxRecords[This->nIdxRecords].dwFlags = AVIIF_LIST; + This->idxRecords[This->nIdxRecords].dwChunkOffset = + This->ckLastRecord.dwDataOffset - 2 * sizeof(DWORD); + This->idxRecords[This->nIdxRecords].dwChunkLength = + This->ckLastRecord.cksize; + This->nIdxRecords++; + + return AVIERR_OK; +} + +static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This) +{ + DWORD dwPos; + DWORD nStream; + + /* RIFF,hdrl,movi,avih => (3 * 3 + 2) * sizeof(DWORD) = 11 * sizeof(DWORD) */ + dwPos = 11 * sizeof(DWORD) + sizeof(MainAVIHeader); + + for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { + IAVIStreamImpl *pStream = This->ppStreams[nStream]; + + /* strl,strh,strf => (3 + 2 * 2) * sizeof(DWORD) = 7 * sizeof(DWORD) */ + dwPos += 7 * sizeof(DWORD) + sizeof(AVIStreamHeader); + dwPos += ((pStream->cbFormat + 1) & ~1U); + if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0) + dwPos += 2 * sizeof(DWORD) + ((pStream->cbHandlerData + 1) & ~1U); + if (lstrlenW(pStream->sInfo.szName) > 0) + dwPos += 2 * sizeof(DWORD) + ((lstrlenW(pStream->sInfo.szName) + 1) & ~1U); + } + + if (This->dwMoviChunkPos == 0) { + This->dwNextFramePos = dwPos; + + /* pad to multiple of AVI_HEADERSIZE only if we are more than 8 bytes away from it */ + if (((dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1)) - dwPos > 2 * sizeof(DWORD)) + This->dwNextFramePos = (dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1); + + This->dwMoviChunkPos = This->dwNextFramePos - sizeof(DWORD); + } + + return dwPos; +} + +static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, LPAVISTREAMINFOW asi) +{ + IAVIStreamImpl *pstream; + + /* pre-conditions */ + assert(paf != NULL); + assert(nr < MAX_AVISTREAMS); + assert(paf->ppStreams[nr] != NULL); + + pstream = paf->ppStreams[nr]; + + pstream->lpVtbl = &iavist; + pstream->ref = 0; + pstream->paf = paf; + pstream->nStream = nr; + pstream->dwCurrentFrame = (DWORD)-1; + pstream->lLastFrame = -1; + + if (asi != NULL) { + memcpy(&pstream->sInfo, asi, sizeof(pstream->sInfo)); + + if (asi->dwLength > 0) { + /* pre-allocate mem for frame-index structure */ + pstream->idxFrames = + (AVIINDEXENTRY*)GlobalAllocPtr(GHND, asi->dwLength * sizeof(AVIINDEXENTRY)); + if (pstream->idxFrames != NULL) + pstream->nIdxFrames = asi->dwLength; + } + if (asi->dwFormatChangeCount > 0) { + /* pre-allocate mem for formatchange-index structure */ + pstream->idxFmtChanges = + (AVIINDEXENTRY*)GlobalAllocPtr(GHND, asi->dwFormatChangeCount * sizeof(AVIINDEXENTRY)); + if (pstream->idxFmtChanges != NULL) + pstream->nIdxFmtChanges = asi->dwFormatChangeCount; + } + + /* These values will be computed */ + pstream->sInfo.dwLength = 0; + pstream->sInfo.dwSuggestedBufferSize = 0; + pstream->sInfo.dwFormatChangeCount = 0; + pstream->sInfo.dwEditCount = 1; + if (pstream->sInfo.dwSampleSize > 0) + SetRectEmpty(&pstream->sInfo.rcFrame); + } + + pstream->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; +} + +static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This) +{ + /* pre-conditions */ + assert(This != NULL); + + This->dwCurrentFrame = (DWORD)-1; + This->lLastFrame = -1; + This->paf = NULL; + if (This->idxFrames != NULL) { + GlobalFreePtr(This->idxFrames); + This->idxFrames = NULL; + This->nIdxFrames = 0; + } + if (This->idxFmtChanges != NULL) { + GlobalFreePtr(This->idxFmtChanges); + This->idxFmtChanges = NULL; + } + if (This->lpBuffer != NULL) { + GlobalFreePtr(This->lpBuffer); + This->lpBuffer = NULL; + This->cbBuffer = 0; + } + if (This->lpHandlerData != NULL) { + GlobalFreePtr(This->lpHandlerData); + This->lpHandlerData = NULL; + This->cbHandlerData = 0; + } + if (This->extra.lp != NULL) { + GlobalFreePtr(This->extra.lp); + This->extra.lp = NULL; + This->extra.cb = 0; + } + if (This->lpFormat != NULL) { + GlobalFreePtr(This->lpFormat); + This->lpFormat = NULL; + This->cbFormat = 0; + } +} + +static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This) +{ + MainAVIHeader MainAVIHdr; + MMCKINFO ckRIFF; + MMCKINFO ckLIST1; + MMCKINFO ckLIST2; + MMCKINFO ck; + IAVIStreamImpl *pStream; + DWORD nStream; + HRESULT hr; + + if (This->hmmio == NULL) + return AVIERR_FILEOPEN; + + /* initialize stream ptr's */ + memset(This->ppStreams, 0, sizeof(This->ppStreams)); + + /* try to get "RIFF" chunk -- must not be at beginning of file! */ + ckRIFF.fccType = formtypeAVI; + if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) { + ERR(": not an AVI!\n"); + return AVIERR_FILEREAD; + } + + /* get "LIST" "hdrl" */ + ckLIST1.fccType = listtypeAVIHEADER; + hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF, MMIO_FINDLIST); + if (FAILED(hr)) + return hr; + + /* get "avih" chunk */ + ck.ckid = ckidAVIMAINHDR; + hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, MMIO_FINDCHUNK); + if (FAILED(hr)) + return hr; + + if (ck.cksize != sizeof(MainAVIHdr)) { + ERR(": invalid size of %ld for MainAVIHeader!\n", ck.cksize); + return AVIERR_BADFORMAT; + } + if (mmioRead(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize) + return AVIERR_FILEREAD; + + /* check for MAX_AVISTREAMS limit */ + if (MainAVIHdr.dwStreams > MAX_AVISTREAMS) { + WARN("file contains %lu streams, but only supports %d -- change MAX_AVISTREAMS!\n", MainAVIHdr.dwStreams, MAX_AVISTREAMS); + return AVIERR_UNSUPPORTED; + } + + /* adjust permissions if copyrighted material in file */ + if (MainAVIHdr.dwFlags & AVIFILEINFO_COPYRIGHTED) { + This->uMode &= ~MMIO_RWMODE; + This->uMode |= MMIO_READ; + } + + /* convert MainAVIHeader into AVIFILINFOW */ + memset(&This->fInfo, 0, sizeof(This->fInfo)); + This->fInfo.dwRate = MainAVIHdr.dwMicroSecPerFrame; + This->fInfo.dwScale = 1000000; + This->fInfo.dwMaxBytesPerSec = MainAVIHdr.dwMaxBytesPerSec; + This->fInfo.dwFlags = MainAVIHdr.dwFlags; + This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; + This->fInfo.dwLength = MainAVIHdr.dwTotalFrames; + This->fInfo.dwStreams = MainAVIHdr.dwStreams; + This->fInfo.dwSuggestedBufferSize = MainAVIHdr.dwSuggestedBufferSize; + This->fInfo.dwWidth = MainAVIHdr.dwWidth; + This->fInfo.dwHeight = MainAVIHdr.dwHeight; + LoadStringW(AVIFILE_hModule, IDS_AVIFILETYPE, This->fInfo.szFileType, + sizeof(This->fInfo.szFileType)); + + /* go back to into header list */ + if (mmioAscend(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEREAD; + + /* foreach stream exists a "LIST","strl" chunk */ + for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { + /* get next nested chunk in this "LIST","strl" */ + if (mmioDescend(This->hmmio, &ckLIST2, &ckLIST1, 0) != S_OK) + return AVIERR_FILEREAD; + + /* nested chunk must be of type "LIST","strl" -- when not normally JUNK */ + if (ckLIST2.ckid == FOURCC_LIST && + ckLIST2.fccType == listtypeSTREAMHEADER) { + pStream = This->ppStreams[nStream] = + (IAVIStreamImpl*)LocalAlloc(LPTR, sizeof(IAVIStreamImpl)); + if (pStream == NULL) + return AVIERR_MEMORY; + AVIFILE_ConstructAVIStream(This, nStream, NULL); + + ck.ckid = 0; + while (mmioDescend(This->hmmio, &ck, &ckLIST2, 0) == S_OK) { + switch (ck.ckid) { + case ckidSTREAMHANDLERDATA: + if (pStream->lpHandlerData != NULL) + return AVIERR_BADFORMAT; + pStream->lpHandlerData = GlobalAllocPtr(GMEM_DDESHARE|GMEM_MOVEABLE, + ck.cksize); + if (pStream->lpHandlerData == NULL) + return AVIERR_MEMORY; + pStream->cbHandlerData = ck.cksize; + + if (mmioRead(This->hmmio, (HPSTR)pStream->lpHandlerData, ck.cksize) != ck.cksize) + return AVIERR_FILEREAD; + break; + case ckidSTREAMFORMAT: + if (pStream->lpFormat != NULL) + return AVIERR_BADFORMAT; + if (ck.cksize == 0) + break; + + pStream->lpFormat = GlobalAllocPtr(GMEM_DDESHARE|GMEM_MOVEABLE, + ck.cksize); + if (pStream->lpFormat == NULL) + return AVIERR_MEMORY; + pStream->cbFormat = ck.cksize; + + if (mmioRead(This->hmmio, (HPSTR)pStream->lpFormat, ck.cksize) != ck.cksize) + return AVIERR_FILEREAD; + + if (pStream->sInfo.fccType == streamtypeVIDEO) { + LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)pStream->lpFormat; + + /* some corrections to the video format */ + if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8) + lpbi->biClrUsed = 1u << lpbi->biBitCount; + if (lpbi->biCompression == BI_RGB && lpbi->biSizeImage == 0) + lpbi->biSizeImage = DIBWIDTHBYTES(*lpbi) * lpbi->biHeight; + if (lpbi->biCompression != BI_RGB && lpbi->biBitCount == 8) { + if (pStream->sInfo.fccHandler == mmioFOURCC('R','L','E','0') || + pStream->sInfo.fccHandler == mmioFOURCC('R','L','E',' ')) + lpbi->biCompression = BI_RLE8; + } + if (lpbi->biCompression == BI_RGB && + (pStream->sInfo.fccHandler == 0 || + pStream->sInfo.fccHandler == mmioFOURCC('N','O','N','E'))) + pStream->sInfo.fccHandler = comptypeDIB; + + /* init rcFrame if it's empty */ + if (IsRectEmpty(&pStream->sInfo.rcFrame)) + SetRect(&pStream->sInfo.rcFrame, 0, 0, lpbi->biWidth, lpbi->biHeight); + } + break; + case ckidSTREAMHEADER: + { + static const WCHAR streamTypeFmt[] = {'%','4','.','4','h','s',0}; + + AVIStreamHeader streamHdr; + WCHAR szType[25]; + WCHAR streamNameFmt[25]; + UINT count; + LONG n = ck.cksize; + + if (ck.cksize > sizeof(streamHdr)) + n = sizeof(streamHdr); + + if (mmioRead(This->hmmio, (HPSTR)&streamHdr, n) != n) + return AVIERR_FILEREAD; + + pStream->sInfo.fccType = streamHdr.fccType; + pStream->sInfo.fccHandler = streamHdr.fccHandler; + pStream->sInfo.dwFlags = streamHdr.dwFlags; + pStream->sInfo.wPriority = streamHdr.wPriority; + pStream->sInfo.wLanguage = streamHdr.wLanguage; + pStream->sInfo.dwInitialFrames = streamHdr.dwInitialFrames; + pStream->sInfo.dwScale = streamHdr.dwScale; + pStream->sInfo.dwRate = streamHdr.dwRate; + pStream->sInfo.dwStart = streamHdr.dwStart; + pStream->sInfo.dwLength = streamHdr.dwLength; + pStream->sInfo.dwSuggestedBufferSize = + streamHdr.dwSuggestedBufferSize; + pStream->sInfo.dwQuality = streamHdr.dwQuality; + pStream->sInfo.dwSampleSize = streamHdr.dwSampleSize; + pStream->sInfo.rcFrame.left = streamHdr.rcFrame.left; + pStream->sInfo.rcFrame.top = streamHdr.rcFrame.top; + pStream->sInfo.rcFrame.right = streamHdr.rcFrame.right; + pStream->sInfo.rcFrame.bottom = streamHdr.rcFrame.bottom; + pStream->sInfo.dwEditCount = 0; + pStream->sInfo.dwFormatChangeCount = 0; + + /* generate description for stream like "filename.avi Type #n" */ + if (streamHdr.fccType == streamtypeVIDEO) + LoadStringW(AVIFILE_hModule, IDS_VIDEO, szType, sizeof(szType)); + else if (streamHdr.fccType == streamtypeAUDIO) + LoadStringW(AVIFILE_hModule, IDS_AUDIO, szType, sizeof(szType)); + else + wsprintfW(szType, streamTypeFmt, (char*)&streamHdr.fccType); + + /* get count of this streamtype up to this stream */ + count = 0; + for (n = nStream; 0 <= n; n--) { + if (This->ppStreams[n]->sInfo.fccHandler == streamHdr.fccType) + count++; + } + + memset(pStream->sInfo.szName, 0, sizeof(pStream->sInfo.szName)); + + LoadStringW(AVIFILE_hModule, IDS_AVISTREAMFORMAT, streamNameFmt, sizeof(streamNameFmt)); + + /* FIXME: avoid overflow -- better use wsnprintfW, which doesn't exists ! */ + wsprintfW(pStream->sInfo.szName, streamNameFmt, + AVIFILE_BasenameW(This->szFileName), szType, count); + } + break; + case ckidSTREAMNAME: + { /* streamname will be saved as ASCII string */ + LPSTR str = (LPSTR)LocalAlloc(LMEM_FIXED, ck.cksize); + if (str == NULL) + return AVIERR_MEMORY; + + if (mmioRead(This->hmmio, (HPSTR)str, ck.cksize) != ck.cksize) + { + LocalFree((HLOCAL)str); + return AVIERR_FILEREAD; + } + + MultiByteToWideChar(CP_ACP, 0, str, -1, pStream->sInfo.szName, + sizeof(pStream->sInfo.szName)/sizeof(pStream->sInfo.szName[0])); + + LocalFree((HLOCAL)str); + } + break; + case ckidAVIPADDING: + case mmioFOURCC('p','a','d','d'): + break; + default: + WARN(": found extra chunk 0x%08lX\n", ck.ckid); + hr = ReadChunkIntoExtra(&pStream->extra, This->hmmio, &ck); + if (FAILED(hr)) + return hr; + }; + + if (mmioAscend(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEREAD; + } + } else { + /* nested chunks in "LIST","hdrl" which are not of type "LIST","strl" */ + hr = ReadChunkIntoExtra(&This->fileextra, This->hmmio, &ckLIST2); + if (FAILED(hr)) + return hr; + } + if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK) + return AVIERR_FILEREAD; + } + + /* read any extra headers in "LIST","hdrl" */ + FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, 0); + if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK) + return AVIERR_FILEREAD; + + /* search "LIST","movi" chunk in "RIFF","AVI " */ + ckLIST1.fccType = listtypeAVIMOVIE; + hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF, + MMIO_FINDLIST); + if (FAILED(hr)) + return hr; + + This->dwMoviChunkPos = ckLIST1.dwDataOffset; + This->dwIdxChunkPos = ckLIST1.cksize + ckLIST1.dwDataOffset; + if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK) + return AVIERR_FILEREAD; + + /* try to find an index */ + ck.ckid = ckidAVINEWINDEX; + hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, + &ck, &ckRIFF, MMIO_FINDCHUNK); + if (SUCCEEDED(hr) && ck.cksize > 0) { + if (FAILED(AVIFILE_LoadIndex(This, ck.cksize, ckLIST1.dwDataOffset))) + This->fInfo.dwFlags &= ~AVIFILEINFO_HASINDEX; + } + + /* when we haven't found an index or it's bad, then build one + * by parsing 'movi' chunk */ + if ((This->fInfo.dwFlags & AVIFILEINFO_HASINDEX) == 0) { + for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) + This->ppStreams[nStream]->lLastFrame = -1; + + if (mmioSeek(This->hmmio, ckLIST1.dwDataOffset + sizeof(DWORD), SEEK_SET) == -1) { + ERR(": Oops, can't seek back to 'movi' chunk!\n"); + return AVIERR_FILEREAD; + } + + /* seek through the 'movi' list until end */ + while (mmioDescend(This->hmmio, &ck, &ckLIST1, 0) == S_OK) { + if (ck.ckid != FOURCC_LIST) { + if (mmioAscend(This->hmmio, &ck, 0) == S_OK) { + nStream = StreamFromFOURCC(ck.ckid); + + if (nStream > This->fInfo.dwStreams) + return AVIERR_BADFORMAT; + + AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize, + ck.dwDataOffset - 2 * sizeof(DWORD), 0); + } else { + nStream = StreamFromFOURCC(ck.ckid); + WARN(": file seems to be truncated!\n"); + if (nStream <= This->fInfo.dwStreams && + This->ppStreams[nStream]->sInfo.dwSampleSize > 0) { + ck.cksize = mmioSeek(This->hmmio, 0, SEEK_END); + if (ck.cksize != -1) { + ck.cksize -= ck.dwDataOffset; + ck.cksize &= ~(This->ppStreams[nStream]->sInfo.dwSampleSize - 1); + + AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize, + ck.dwDataOffset - 2 * sizeof(DWORD), 0); + } + } + } + } + } + } + + /* find other chunks */ + FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckRIFF, 0); + + return AVIERR_OK; +} + +static HRESULT AVIFILE_LoadIndex(IAVIFileImpl *This, DWORD size, DWORD offset) +{ + AVIINDEXENTRY *lp; + DWORD pos, n; + HRESULT hr = AVIERR_OK; + BOOL bAbsolute = TRUE; + + lp = (AVIINDEXENTRY*)GlobalAllocPtr(GMEM_MOVEABLE, + IDX_PER_BLOCK * sizeof(AVIINDEXENTRY)); + if (lp == NULL) + return AVIERR_MEMORY; + + /* adjust limits for index tables, so that inserting will be faster */ + for (n = 0; n < This->fInfo.dwStreams; n++) { + IAVIStreamImpl *pStream = This->ppStreams[n]; + + pStream->lLastFrame = -1; + + if (pStream->idxFrames != NULL) { + GlobalFreePtr(pStream->idxFrames); + pStream->idxFrames = NULL; + pStream->nIdxFrames = 0; + } + + if (pStream->sInfo.dwSampleSize != 0) { + if (n > 0 && This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) { + pStream->nIdxFrames = This->ppStreams[0]->nIdxFrames; + } else if (pStream->sInfo.dwSuggestedBufferSize) { + pStream->nIdxFrames = + pStream->sInfo.dwLength / pStream->sInfo.dwSuggestedBufferSize; + } + } else + pStream->nIdxFrames = pStream->sInfo.dwLength; + + pStream->idxFrames = + (AVIINDEXENTRY*)GlobalAllocPtr(GHND, pStream->nIdxFrames * sizeof(AVIINDEXENTRY)); + if (pStream->idxFrames == NULL && pStream->nIdxFrames > 0) { + pStream->nIdxFrames = 0; + return AVIERR_MEMORY; + } + } + + pos = (DWORD)-1; + while (size != 0) { + LONG read = min(IDX_PER_BLOCK * sizeof(AVIINDEXENTRY), size); + + if (mmioRead(This->hmmio, (HPSTR)lp, read) != read) { + hr = AVIERR_FILEREAD; + break; + } + size -= read; + + if (pos == (DWORD)-1) + pos = offset - lp->dwChunkOffset + sizeof(DWORD); + + AVIFILE_ParseIndex(This, lp, read / sizeof(AVIINDEXENTRY), + pos, &bAbsolute); + } + + if (lp != NULL) + GlobalFreePtr(lp); + + /* checking ... */ + for (n = 0; n < This->fInfo.dwStreams; n++) { + IAVIStreamImpl *pStream = This->ppStreams[n]; + + if (pStream->sInfo.dwSampleSize == 0 && + pStream->sInfo.dwLength != pStream->lLastFrame+1) + ERR("stream %lu length mismatch: dwLength=%lu found=%ld\n", + n, pStream->sInfo.dwLength, pStream->lLastFrame); + } + + return hr; +} + +static HRESULT AVIFILE_ParseIndex(IAVIFileImpl *This, AVIINDEXENTRY *lp, + LONG count, DWORD pos, BOOL *bAbsolute) +{ + if (lp == NULL) + return AVIERR_BADPARAM; + + for (; count > 0; count--, lp++) { + WORD nStream = StreamFromFOURCC(lp->ckid); + + if (lp->ckid == listtypeAVIRECORD || nStream == 0x7F) + continue; /* skip these */ + + if (nStream > This->fInfo.dwStreams) + return AVIERR_BADFORMAT; + + if (*bAbsolute && lp->dwChunkOffset < This->dwMoviChunkPos) + *bAbsolute = FALSE; + + if (*bAbsolute) + lp->dwChunkOffset += sizeof(DWORD); + else + lp->dwChunkOffset += pos; + + if (FAILED(AVIFILE_AddFrame(This->ppStreams[nStream], lp->ckid, lp->dwChunkLength, lp->dwChunkOffset, lp->dwFlags))) + return AVIERR_MEMORY; + } + + return AVIERR_OK; +} + +static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD pos, + LPVOID buffer, LONG size) +{ + /* pre-conditions */ + assert(This != NULL); + assert(This->paf != NULL); + assert(This->paf->hmmio != NULL); + assert(This->sInfo.dwStart <= pos && pos < This->sInfo.dwLength); + assert(pos <= This->lLastFrame); + + /* should we read as much as block gives us? */ + if (size == 0 || size > This->idxFrames[pos].dwChunkLength) + size = This->idxFrames[pos].dwChunkLength; + + /* read into out own buffer or given one? */ + if (buffer == NULL) { + /* we also read the chunk */ + size += 2 * sizeof(DWORD); + + /* check that buffer is big enough -- don't trust dwSuggestedBufferSize */ + if (This->lpBuffer == NULL || size < This->cbBuffer) { + DWORD maxSize = max(size, This->sInfo.dwSuggestedBufferSize); + + if (This->lpBuffer == NULL) + This->lpBuffer = (LPDWORD)GlobalAllocPtr(GMEM_MOVEABLE, maxSize); + else + This->lpBuffer = + (LPDWORD)GlobalReAllocPtr(This->lpBuffer, maxSize, GMEM_MOVEABLE); + if (This->lpBuffer == NULL) + return AVIERR_MEMORY; + This->cbBuffer = max(size, This->sInfo.dwSuggestedBufferSize); + } + + /* now read the complete chunk into our buffer */ + if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset, SEEK_SET) == -1) + return AVIERR_FILEREAD; + if (mmioRead(This->paf->hmmio, (HPSTR)This->lpBuffer, size) != size) + return AVIERR_FILEREAD; + + /* check if it was the correct block which we have read */ + if (This->lpBuffer[0] != This->idxFrames[pos].ckid || + This->lpBuffer[1] != This->idxFrames[pos].dwChunkLength) { + ERR(": block %ld not found at 0x%08lX\n", pos, This->idxFrames[pos].dwChunkOffset); + ERR(": Index says: '%4.4s'(0x%08lX) size 0x%08lX\n", + (char*)&This->idxFrames[pos].ckid, This->idxFrames[pos].ckid, + This->idxFrames[pos].dwChunkLength); + ERR(": Data says: '%4.4s'(0x%08lX) size 0x%08lX\n", + (char*)&This->lpBuffer[0], This->lpBuffer[0], This->lpBuffer[1]); + return AVIERR_FILEREAD; + } + } else { + if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD), SEEK_SET) == -1) + return AVIERR_FILEREAD; + if (mmioRead(This->paf->hmmio, (HPSTR)buffer, size) != size) + return AVIERR_FILEREAD; + } + + return AVIERR_OK; +} + +static void AVIFILE_SamplesToBlock(IAVIStreamImpl *This, LPLONG pos, + LPLONG offset) +{ + LONG block; + + /* pre-conditions */ + assert(This != NULL); + assert(pos != NULL); + assert(offset != NULL); + assert(This->sInfo.dwSampleSize != 0); + assert(*pos >= This->sInfo.dwStart); + + /* convert start sample to start bytes */ + (*offset) = (*pos) - This->sInfo.dwStart; + (*offset) *= This->sInfo.dwSampleSize; + + /* convert bytes to block number */ + for (block = 0; block <= This->lLastFrame; block++) { + if (This->idxFrames[block].dwChunkLength <= *offset) + (*offset) -= This->idxFrames[block].dwChunkLength; + else + break; + } + + *pos = block; +} + +static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This) +{ + MainAVIHeader MainAVIHdr; + IAVIStreamImpl* pStream; + MMCKINFO ckRIFF; + MMCKINFO ckLIST1; + MMCKINFO ckLIST2; + MMCKINFO ck; + DWORD nStream; + DWORD dwPos; + HRESULT hr; + + /* initialize some things */ + if (This->dwMoviChunkPos == 0) + AVIFILE_ComputeMoviStart(This); + + /* written one record to much? */ + if (This->ckLastRecord.dwFlags & MMIO_DIRTY) { + This->dwNextFramePos -= 3 * sizeof(DWORD); + if (This->nIdxRecords > 0) + This->nIdxRecords--; + } + + AVIFILE_UpdateInfo(This); + + assert(This->fInfo.dwScale != 0); + + memset(&MainAVIHdr, 0, sizeof(MainAVIHdr)); + MainAVIHdr.dwMicroSecPerFrame = MulDiv(This->fInfo.dwRate, 1000000, + This->fInfo.dwScale); + MainAVIHdr.dwMaxBytesPerSec = This->fInfo.dwMaxBytesPerSec; + MainAVIHdr.dwPaddingGranularity = AVI_HEADERSIZE; + MainAVIHdr.dwFlags = This->fInfo.dwFlags; + MainAVIHdr.dwTotalFrames = This->fInfo.dwLength; + MainAVIHdr.dwInitialFrames = 0; + MainAVIHdr.dwStreams = This->fInfo.dwStreams; + MainAVIHdr.dwSuggestedBufferSize = This->fInfo.dwSuggestedBufferSize; + MainAVIHdr.dwWidth = This->fInfo.dwWidth; + MainAVIHdr.dwHeight = This->fInfo.dwHeight; + MainAVIHdr.dwInitialFrames = This->dwInitialFrames; + + /* now begin writing ... */ + mmioSeek(This->hmmio, 0, SEEK_SET); + + /* RIFF chunk */ + ckRIFF.cksize = 0; + ckRIFF.fccType = formtypeAVI; + if (mmioCreateChunk(This->hmmio, &ckRIFF, MMIO_CREATERIFF) != S_OK) + return AVIERR_FILEWRITE; + + /* AVI headerlist */ + ckLIST1.cksize = 0; + ckLIST1.fccType = listtypeAVIHEADER; + if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK) + return AVIERR_FILEWRITE; + + /* MainAVIHeader */ + ck.ckid = ckidAVIMAINHDR; + ck.cksize = sizeof(MainAVIHdr); + ck.fccType = 0; + if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + if (mmioWrite(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize) + return AVIERR_FILEWRITE; + if (mmioAscend(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + + /* write the headers of each stream into a separate streamheader list */ + for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { + AVIStreamHeader strHdr; + + pStream = This->ppStreams[nStream]; + + /* begin the new streamheader list */ + ckLIST2.cksize = 0; + ckLIST2.fccType = listtypeSTREAMHEADER; + if (mmioCreateChunk(This->hmmio, &ckLIST2, MMIO_CREATELIST) != S_OK) + return AVIERR_FILEWRITE; + + /* create an AVIStreamHeader from the AVSTREAMINFO */ + strHdr.fccType = pStream->sInfo.fccType; + strHdr.fccHandler = pStream->sInfo.fccHandler; + strHdr.dwFlags = pStream->sInfo.dwFlags; + strHdr.wPriority = pStream->sInfo.wPriority; + strHdr.wLanguage = pStream->sInfo.wLanguage; + strHdr.dwInitialFrames = pStream->sInfo.dwInitialFrames; + strHdr.dwScale = pStream->sInfo.dwScale; + strHdr.dwRate = pStream->sInfo.dwRate; + strHdr.dwStart = pStream->sInfo.dwStart; + strHdr.dwLength = pStream->sInfo.dwLength; + strHdr.dwSuggestedBufferSize = pStream->sInfo.dwSuggestedBufferSize; + strHdr.dwQuality = pStream->sInfo.dwQuality; + strHdr.dwSampleSize = pStream->sInfo.dwSampleSize; + strHdr.rcFrame.left = pStream->sInfo.rcFrame.left; + strHdr.rcFrame.top = pStream->sInfo.rcFrame.top; + strHdr.rcFrame.right = pStream->sInfo.rcFrame.right; + strHdr.rcFrame.bottom = pStream->sInfo.rcFrame.bottom; + + /* now write the AVIStreamHeader */ + ck.ckid = ckidSTREAMHEADER; + ck.cksize = sizeof(strHdr); + if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + if (mmioWrite(This->hmmio, (HPSTR)&strHdr, ck.cksize) != ck.cksize) + return AVIERR_FILEWRITE; + if (mmioAscend(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + + /* ... the hopefully ever present streamformat ... */ + ck.ckid = ckidSTREAMFORMAT; + ck.cksize = pStream->cbFormat; + if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + if (pStream->lpFormat != NULL && ck.cksize > 0) { + if (mmioWrite(This->hmmio, (HPSTR)pStream->lpFormat, ck.cksize) != ck.cksize) + return AVIERR_FILEWRITE; + } + if (mmioAscend(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + + /* ... some optional existing handler data ... */ + if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0) { + ck.ckid = ckidSTREAMHANDLERDATA; + ck.cksize = pStream->cbHandlerData; + if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + if (mmioWrite(This->hmmio, (HPSTR)pStream->lpHandlerData, ck.cksize) != ck.cksize) + return AVIERR_FILEWRITE; + if (mmioAscend(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + } + + /* ... some optional additional extra chunk for this stream ... */ + if (pStream->extra.lp != NULL && pStream->extra.cb > 0) { + /* the chunk header(s) are already in the strucuture */ + if (mmioWrite(This->hmmio, (HPSTR)pStream->extra.lp, pStream->extra.cb) != pStream->extra.cb) + return AVIERR_FILEWRITE; + } + + /* ... an optional name for this stream ... */ + if (lstrlenW(pStream->sInfo.szName) > 0) { + LPSTR str; + + ck.ckid = ckidSTREAMNAME; + ck.cksize = lstrlenW(pStream->sInfo.szName) + 1; + if (ck.cksize & 1) /* align */ + ck.cksize++; + if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + + /* the streamname must be saved in ASCII not Unicode */ + str = (LPSTR)LocalAlloc(LPTR, ck.cksize); + if (str == NULL) + return AVIERR_MEMORY; + WideCharToMultiByte(CP_ACP, 0, pStream->sInfo.szName, -1, str, + ck.cksize, NULL, NULL); + + if (mmioWrite(This->hmmio, (HPSTR)str, ck.cksize) != ck.cksize) { + LocalFree((HLOCAL)str); + return AVIERR_FILEWRITE; + } + + LocalFree((HLOCAL)str); + if (mmioAscend(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + } + + /* close streamheader list for this stream */ + if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK) + return AVIERR_FILEWRITE; + } /* for (0 <= nStream < MainAVIHdr.dwStreams) */ + + /* close the aviheader list */ + if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK) + return AVIERR_FILEWRITE; + + /* check for padding to pre-guessed 'movi'-chunk position */ + dwPos = ckLIST1.dwDataOffset + ckLIST1.cksize; + if (This->dwMoviChunkPos - 2 * sizeof(DWORD) > dwPos) { + ck.ckid = ckidAVIPADDING; + ck.cksize = This->dwMoviChunkPos - dwPos - 4 * sizeof(DWORD); + assert((LONG)ck.cksize >= 0); + + if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + if (mmioSeek(This->hmmio, ck.cksize, SEEK_CUR) == -1) + return AVIERR_FILEWRITE; + if (mmioAscend(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + } + + /* now write the 'movi' chunk */ + mmioSeek(This->hmmio, This->dwMoviChunkPos - 2 * sizeof(DWORD), SEEK_SET); + ckLIST1.cksize = 0; + ckLIST1.fccType = listtypeAVIMOVIE; + if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK) + return AVIERR_FILEWRITE; + if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1) + return AVIERR_FILEWRITE; + if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK) + return AVIERR_FILEWRITE; + + /* write 'idx1' chunk */ + hr = AVIFILE_SaveIndex(This); + if (FAILED(hr)) + return hr; + + /* write optional extra file chunks */ + if (This->fileextra.lp != NULL && This->fileextra.cb > 0) { + /* as for the streams, are the chunk header(s) in the structure */ + if (mmioWrite(This->hmmio, (HPSTR)This->fileextra.lp, This->fileextra.cb) != This->fileextra.cb) + return AVIERR_FILEWRITE; + } + + /* close RIFF chunk */ + if (mmioAscend(This->hmmio, &ckRIFF, 0) != S_OK) + return AVIERR_FILEWRITE; + + /* add some JUNK at end for bad parsers */ + memset(&ckRIFF, 0, sizeof(ckRIFF)); + mmioWrite(This->hmmio, (HPSTR)&ckRIFF, sizeof(ckRIFF)); + mmioFlush(This->hmmio, 0); + + return AVIERR_OK; +} + +static HRESULT AVIFILE_SaveIndex(IAVIFileImpl *This) +{ + IAVIStreamImpl *pStream; + AVIINDEXENTRY idx; + MMCKINFO ck; + DWORD nStream; + LONG n; + + ck.ckid = ckidAVINEWINDEX; + ck.cksize = 0; + if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + + if (This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) { + /* is interleaved -- write block of coresponding frames */ + LONG lInitialFrames = 0; + LONG stepsize; + LONG i; + + if (This->ppStreams[0]->sInfo.dwSampleSize == 0) + stepsize = 1; + else + stepsize = AVIStreamTimeToSample((PAVISTREAM)This->ppStreams[0], 1000000); + + assert(stepsize > 0); + + for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { + if (lInitialFrames < This->ppStreams[nStream]->sInfo.dwInitialFrames) + lInitialFrames = This->ppStreams[nStream]->sInfo.dwInitialFrames; + } + + for (i = -lInitialFrames; i < (LONG)This->fInfo.dwLength - lInitialFrames; + i += stepsize) { + DWORD nFrame = lInitialFrames + i; + + assert(nFrame < This->nIdxRecords); + + idx.ckid = listtypeAVIRECORD; + idx.dwFlags = AVIIF_LIST; + idx.dwChunkLength = This->idxRecords[nFrame].dwChunkLength; + idx.dwChunkOffset = This->idxRecords[nFrame].dwChunkOffset + - This->dwMoviChunkPos; + if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) + return AVIERR_FILEWRITE; + + for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { + pStream = This->ppStreams[nStream]; + + /* heave we reached start of this stream? */ + if (-(LONG)pStream->sInfo.dwInitialFrames > i) + continue; + + if (pStream->sInfo.dwInitialFrames < lInitialFrames) + nFrame -= (lInitialFrames - pStream->sInfo.dwInitialFrames); + + /* reached end of this stream? */ + if (pStream->lLastFrame <= nFrame) + continue; + + if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) && + pStream->sInfo.dwFormatChangeCount != 0 && + pStream->idxFmtChanges != NULL) { + DWORD pos; + + for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) { + if (pStream->idxFmtChanges[pos].ckid == nFrame) { + idx.dwFlags = AVIIF_NOTIME; + idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream); + idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength; + idx.dwChunkOffset = pStream->idxFmtChanges[pos].dwChunkOffset + - This->dwMoviChunkPos; + + if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) + return AVIERR_FILEWRITE; + break; + } + } + } /* if have formatchanges */ + + idx.ckid = pStream->idxFrames[nFrame].ckid; + idx.dwFlags = pStream->idxFrames[nFrame].dwFlags; + idx.dwChunkLength = pStream->idxFrames[nFrame].dwChunkLength; + idx.dwChunkOffset = pStream->idxFrames[nFrame].dwChunkOffset + - This->dwMoviChunkPos; + if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) + return AVIERR_FILEWRITE; + } + } + } else { + /* not interleaved -- write index for each stream at once */ + for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { + pStream = This->ppStreams[nStream]; + + for (n = 0; n <= pStream->lLastFrame; n++) { + if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) && + (pStream->sInfo.dwFormatChangeCount != 0)) { + DWORD pos; + + for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) { + if (pStream->idxFmtChanges[pos].ckid == n) { + idx.dwFlags = AVIIF_NOTIME; + idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream); + idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength; + idx.dwChunkOffset = + pStream->idxFmtChanges[pos].dwChunkOffset - This->dwMoviChunkPos; + if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) + return AVIERR_FILEWRITE; + break; + } + } + } /* if have formatchanges */ + + idx.ckid = pStream->idxFrames[n].ckid; + idx.dwFlags = pStream->idxFrames[n].dwFlags; + idx.dwChunkLength = pStream->idxFrames[n].dwChunkLength; + idx.dwChunkOffset = pStream->idxFrames[n].dwChunkOffset + - This->dwMoviChunkPos; + + if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) + return AVIERR_FILEWRITE; + } + } + } /* if not interleaved */ + + if (mmioAscend(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + + return AVIERR_OK; +} + +static ULONG AVIFILE_SearchStream(IAVIFileImpl *This, DWORD fcc, LONG lSkip) +{ + UINT i; + UINT nStream; + + /* pre-condition */ + assert(lSkip >= 0); + + if (fcc != 0) { + /* search the number of the specified stream */ + nStream = (ULONG)-1; + for (i = 0; i < This->fInfo.dwStreams; i++) { + assert(This->ppStreams[i] != NULL); + + if (This->ppStreams[i]->sInfo.fccType == fcc) { + if (lSkip == 0) { + nStream = i; + break; + } else + lSkip--; + } + } + } else + nStream = lSkip; + + return nStream; +} + +static void AVIFILE_UpdateInfo(IAVIFileImpl *This) +{ + UINT i; + + /* pre-conditions */ + assert(This != NULL); + + This->fInfo.dwMaxBytesPerSec = 0; + This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; + This->fInfo.dwSuggestedBufferSize = 0; + This->fInfo.dwWidth = 0; + This->fInfo.dwHeight = 0; + This->fInfo.dwScale = 0; + This->fInfo.dwRate = 0; + This->fInfo.dwLength = 0; + This->dwInitialFrames = 0; + + for (i = 0; i < This->fInfo.dwStreams; i++) { + AVISTREAMINFOW *psi; + DWORD n; + + /* pre-conditions */ + assert(This->ppStreams[i] != NULL); + + psi = &This->ppStreams[i]->sInfo; + assert(psi->dwScale != 0); + assert(psi->dwRate != 0); + + if (i == 0) { + /* use first stream timings as base */ + This->fInfo.dwScale = psi->dwScale; + This->fInfo.dwRate = psi->dwRate; + This->fInfo.dwLength = psi->dwLength; + } else { + n = AVIStreamSampleToSample((PAVISTREAM)This->ppStreams[0], + (PAVISTREAM)This->ppStreams[i],psi->dwLength); + if (This->fInfo.dwLength < n) + This->fInfo.dwLength = n; + } + + if (This->dwInitialFrames < psi->dwInitialFrames) + This->dwInitialFrames = psi->dwInitialFrames; + + if (This->fInfo.dwSuggestedBufferSize < psi->dwSuggestedBufferSize) + This->fInfo.dwSuggestedBufferSize = psi->dwSuggestedBufferSize; + + if (psi->dwSampleSize != 0) { + /* fixed sample size -- exact computation */ + This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSampleSize, psi->dwRate, + psi->dwScale); + } else { + /* variable sample size -- only upper limit */ + This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSuggestedBufferSize, + psi->dwRate, psi->dwScale); + + /* update dimensions */ + n = psi->rcFrame.right - psi->rcFrame.left; + if (This->fInfo.dwWidth < n) + This->fInfo.dwWidth = n; + n = psi->rcFrame.bottom - psi->rcFrame.top; + if (This->fInfo.dwHeight < n) + This->fInfo.dwHeight = n; + } + } +} + +static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block, + FOURCC ckid, DWORD flags, LPVOID buffer, + LONG size) +{ + MMCKINFO ck; + + ck.ckid = ckid; + ck.cksize = size; + ck.fccType = 0; + + /* if no frame/block is already written, we must compute start of movi chunk */ + if (This->paf->dwMoviChunkPos == 0) + AVIFILE_ComputeMoviStart(This->paf); + + if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1) + return AVIERR_FILEWRITE; + + if (mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + if (buffer != NULL && size > 0) { + if (mmioWrite(This->paf->hmmio, (HPSTR)buffer, size) != size) + return AVIERR_FILEWRITE; + } + if (mmioAscend(This->paf->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + + This->paf->fDirty = TRUE; + This->paf->dwNextFramePos = mmioSeek(This->paf->hmmio, 0, SEEK_CUR); + + return AVIFILE_AddFrame(This, ckid, size, + ck.dwDataOffset - 2 * sizeof(DWORD), flags); +} diff --git a/reactos/lib/avifil32/avifile.spec b/reactos/lib/avifil32/avifile.spec new file mode 100644 index 00000000000..71a6c74db41 --- /dev/null +++ b/reactos/lib/avifil32/avifile.spec @@ -0,0 +1,64 @@ +# I'm just using "long" instead of "ptr" for the interface pointers, +# because they are 32-bit pointers, not converted to 16-bit format, +# but the app doesn't really need to know, it should never need to +# dereference the interface pointer itself (if we're lucky)... + +#1 stub WEP +2 stub DLLGETCLASSOBJECT +3 stub DLLCANUNLOADNOW +4 stub ___EXPORTEDSTUB +10 stub _IID_IAVISTREAM +11 stub _IID_IAVIFILE +12 stub _IID_IAVIEDITSTREAM +13 stub _IID_IGETFRAME +14 stub _CLSID_AVISIMPLEUNMARSHAL +100 pascal AVIFileInit() AVIFileInit +101 pascal AVIFileExit() AVIFileExit +102 pascal AVIFileOpen(ptr str word ptr) AVIFileOpenA +103 pascal AVIStreamOpenFromFile(ptr str long long word ptr) AVIStreamOpenFromFileA +104 pascal AVIStreamCreate(ptr long long ptr) AVIStreamCreate +105 stub AVIMAKECOMPRESSEDSTREAM +106 stub AVIMAKEFILEFROMSTREAMS +107 stub AVIMAKESTREAMFROMCLIPBOARD +110 pascal AVIStreamGetFrame(long long) AVIStreamGetFrame +111 pascal AVIStreamGetFrameClose(long) AVIStreamGetFrameClose +112 pascal AVIStreamGetFrameOpen(long ptr) AVIStreamGetFrameOpen +120 stub _AVISAVE +121 stub AVISAVEV +122 stub AVISAVEOPTIONS +123 pascal AVIBuildFilter(str long word) AVIBuildFilterA +124 pascal AVISaveOptionsFree(word ptr) AVISaveOptionsFree +130 pascal AVIStreamStart(long) AVIStreamStart +131 pascal AVIStreamLength(long) AVIStreamLength +132 pascal AVIStreamTimeToSample(long long) AVIStreamTimeToSample +133 pascal AVIStreamSampleToTime(long long) AVIStreamSampleToTime +140 pascal AVIFileAddRef(long) AVIFileAddRef +141 pascal AVIFileRelease(long) AVIFileRelease +142 pascal AVIFileInfo(long ptr long) AVIFileInfoA +143 pascal AVIFileGetStream(long ptr long long) AVIFileGetStream +144 pascal AVIFileCreateStream(long ptr ptr) AVIFileCreateStreamA +146 pascal AVIFileWriteData(long long ptr long) AVIFileWriteData +147 pascal AVIFileReadData(long long ptr ptr) AVIFileReadData +148 pascal AVIFileEndRecord(long) AVIFileEndRecord +160 pascal AVIStreamAddRef(long) AVIStreamAddRef +161 pascal AVIStreamRelease(long) AVIStreamRelease +162 pascal AVIStreamInfo(long ptr long) AVIStreamInfoA +163 pascal AVIStreamFindSample(long long long) AVIStreamFindSample +164 pascal AVIStreamReadFormat(long long ptr ptr) AVIStreamReadFormat +165 pascal AVIStreamReadData(long long ptr ptr) AVIStreamReadData +166 pascal AVIStreamWriteData(long long ptr long) AVIStreamWriteData +167 pascal AVIStreamRead(long long long ptr long ptr ptr) AVIStreamRead +168 pascal AVIStreamWrite(long long long ptr long long ptr ptr) AVIStreamWrite +169 pascal AVIStreamSetFormat(long long ptr long) AVIStreamSetFormat +180 stub EDITSTREAMCOPY +181 stub EDITSTREAMCUT +182 stub EDITSTREAMPASTE +184 stub CREATEEDITABLESTREAM +185 stub AVIPUTFILEONCLIPBOARD +187 stub AVIGETFROMCLIPBOARD +188 stub AVICLEARCLIPBOARD +190 stub EDITSTREAMCLONE +191 stub EDITSTREAMSETNAME +192 stub EDITSTREAMSETINFO +200 stub AVISTREAMBEGINSTREAMING +201 stub AVISTREAMENDSTREAMING diff --git a/reactos/lib/avifil32/avifile_Cs.rc b/reactos/lib/avifil32/avifile_Cs.rc new file mode 100644 index 00000000000..e8da6f12232 --- /dev/null +++ b/reactos/lib/avifil32/avifile_Cs.rc @@ -0,0 +1,53 @@ +/* + * Copyright 2002 Michael Günnewig + * + * Czech resources for avifile + * Copyright 2004 David Kredba + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_CZECH, SUBLANG_DEFAULT + +IDD_SAVEOPTIONS DIALOG FIXED IMPURE 43, 37, 196, 82 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Nastavení komprese" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "&Vyber datový proud:",-1,2,5,114,10 + COMBOBOX IDC_STREAM,2,18,134,61,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "V&olby...",IDC_OPTIONS,145,17,45,14 + AUTOCHECKBOX "Prolo¾&it ka¾dých",IDC_INTERLEAVE,3,42,60,11,WS_TABSTOP + EDITTEXT IDC_INTERLEAVEEVERY,66,41,32,12,ES_AUTOHSCROLL + LTEXT "rámcù",-1,104,43,36,9 + LTEXT "Souèasný formát:",-1,3,56,53,9 + LTEXT "Zbývající místo",IDC_FORMATTEXT,55,56,90,26 + DEFPUSHBUTTON "Budi¾",IDOK,145,42,45,14 + PUSHBUTTON "Zru¹it",IDCANCEL,145,61,45,14 +END + +STRINGTABLE DISCARDABLE +{ + IDS_WAVESTREAMFORMAT "Waveform: %s" + IDS_WAVEFILETYPE "Waveform" + IDS_ALLMULTIMEDIA "V¹echny soubory multimédií" + IDS_ALLFILES "V¹echny soubory (*.*)@*.*" + IDS_VIDEO "video" + IDS_AUDIO "audio" + IDS_AVISTREAMFORMAT "%s %s #%d" + IDS_AVIFILETYPE "Wine AVI-default-filehandler" + IDS_UNCOMPRESSED "nekomprimovaný" +} diff --git a/reactos/lib/avifil32/avifile_De.rc b/reactos/lib/avifil32/avifile_De.rc new file mode 100644 index 00000000000..f6550227c9d --- /dev/null +++ b/reactos/lib/avifil32/avifile_De.rc @@ -0,0 +1,50 @@ +/* + * Copyright 2002 Michael Günnewig + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_GERMAN, SUBLANG_GERMAN + +IDD_SAVEOPTIONS DIALOG FIXED IMPURE 43, 37, 196, 82 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Komprimierungsoptionen" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "&Wählen Sie die Eingangsdaten aus:",-1,2,5,114,10 + COMBOBOX IDC_STREAM,2,18,134,61,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "&Optionen...",IDC_OPTIONS,145,17,45,14 + AUTOCHECKBOX "&Interleave alle",IDC_INTERLEAVE,3,42,60,11,WS_TABSTOP + EDITTEXT IDC_INTERLEAVEEVERY,66,41,32,12,ES_AUTOHSCROLL + LTEXT "Einzelbilder",-1,104,43,36,9 + LTEXT "Akuelles Format:",-1,3,56,53,9 + LTEXT "This space for rent",IDC_FORMATTEXT,55,56,90,26 + DEFPUSHBUTTON "OK",IDOK,145,42,45,14 + PUSHBUTTON "Abbrechen",IDCANCEL,145,61,45,14 +END + +STRINGTABLE DISCARDABLE +{ + IDS_WAVESTREAMFORMAT "Waveform: %s" + IDS_WAVEFILETYPE "Waveform" + IDS_ALLMULTIMEDIA "Alle Multimedia-Dateien" + IDS_ALLFILES "Alle Dateien (*.*)@*.*" + IDS_VIDEO "Video" + IDS_AUDIO "Audio" + IDS_AVISTREAMFORMAT "%s %s #%d" + IDS_AVIFILETYPE "Wine AVI-Standard-Dateibehandlungsroutine" + IDS_UNCOMPRESSED "Unkomprimiert" +} diff --git a/reactos/lib/avifil32/avifile_En.rc b/reactos/lib/avifil32/avifile_En.rc new file mode 100644 index 00000000000..2fa2fac366d --- /dev/null +++ b/reactos/lib/avifil32/avifile_En.rc @@ -0,0 +1,50 @@ +/* + * Copyright 2002 Michael Günnewig + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT + +IDD_SAVEOPTIONS DIALOG FIXED IMPURE 43, 37, 196, 82 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Compress options" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "&Choose a stream:",-1,2,5,114,10 + COMBOBOX IDC_STREAM,2,18,134,61,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "&Options...",IDC_OPTIONS,145,17,45,14 + AUTOCHECKBOX "&Interleave every",IDC_INTERLEAVE,3,42,60,11,WS_TABSTOP + EDITTEXT IDC_INTERLEAVEEVERY,66,41,32,12,ES_AUTOHSCROLL + LTEXT "frames",-1,104,43,36,9 + LTEXT "Current format:",-1,3,56,53,9 + LTEXT "This space for rent",IDC_FORMATTEXT,55,56,90,26 + DEFPUSHBUTTON "OK",IDOK,145,42,45,14 + PUSHBUTTON "Cancel",IDCANCEL,145,61,45,14 +END + +STRINGTABLE DISCARDABLE +{ + IDS_WAVESTREAMFORMAT "Waveform: %s" + IDS_WAVEFILETYPE "Waveform" + IDS_ALLMULTIMEDIA "All multimedia files" + IDS_ALLFILES "All files (*.*)@*.*" + IDS_VIDEO "video" + IDS_AUDIO "audio" + IDS_AVISTREAMFORMAT "%s %s #%d" + IDS_AVIFILETYPE "Wine AVI-default-filehandler" + IDS_UNCOMPRESSED "uncompressed" +} diff --git a/reactos/lib/avifil32/avifile_Es.rc b/reactos/lib/avifil32/avifile_Es.rc new file mode 100644 index 00000000000..c2b51c3962b --- /dev/null +++ b/reactos/lib/avifil32/avifile_Es.rc @@ -0,0 +1,50 @@ +/* + * Copyright 2003 José Manuel Ferrer Ortiz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL + +IDD_SAVEOPTIONS DIALOG FIXED IMPURE 43, 37, 196, 82 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Opciones de compresión" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "&Elija un stream:",-1,2,5,114,10 + COMBOBOX IDC_STREAM,2,18,134,61,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "&Opciones...",IDC_OPTIONS,145,17,45,14 + AUTOCHECKBOX "&Interleave cada",IDC_INTERLEAVE,3,42,60,11,WS_TABSTOP + EDITTEXT IDC_INTERLEAVEEVERY,66,41,32,12,ES_AUTOHSCROLL + LTEXT "cuadros",-1,104,43,36,9 + LTEXT "Formato actual:",-1,3,56,53,9 + LTEXT "Espacio en alquiler",IDC_FORMATTEXT,55,56,90,26 + DEFPUSHBUTTON "Aceptar",IDOK,145,42,45,14 + PUSHBUTTON "Cancelar",IDCANCEL,145,61,45,14 +END + +STRINGTABLE DISCARDABLE +{ + IDS_WAVESTREAMFORMAT "Formato de ondas: %s" + IDS_WAVEFILETYPE "Formato de ondas" + IDS_ALLMULTIMEDIA "Todos los archivos multimedia" + IDS_ALLFILES "Todos los archivos (*.*)@*.*" + IDS_VIDEO "vídeo" + IDS_AUDIO "audio" + IDS_AVISTREAMFORMAT "%s %s #%d" + IDS_AVIFILETYPE "Manejador de archivo AVI por defecto de Wine" + IDS_UNCOMPRESSED "sin compresión" +} diff --git a/reactos/lib/avifil32/avifile_Fr.rc b/reactos/lib/avifil32/avifile_Fr.rc new file mode 100644 index 00000000000..b9ee5d91ee7 --- /dev/null +++ b/reactos/lib/avifil32/avifile_Fr.rc @@ -0,0 +1,54 @@ +/* + * Avifil32 + * French language support + * + * Copyright 2002 Michael Günnewig + * Copyright 2003 Vincent Béron + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL + +IDD_SAVEOPTIONS DIALOG FIXED IMPURE 43, 37, 196, 82 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Options de compression" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "&Sélectionnez un flux :",-1,2,5,114,10 + COMBOBOX IDC_STREAM,2,18,134,61,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "&Options...",IDC_OPTIONS,145,17,45,14 + AUTOCHECKBOX "&Imbriquer à chaque",IDC_INTERLEAVE,3,42,60,11,WS_TABSTOP + EDITTEXT IDC_INTERLEAVEEVERY,66,41,32,12,ES_AUTOHSCROLL + LTEXT "images",-1,104,43,36,9 + LTEXT "Format actuel :",-1,3,56,53,9 + LTEXT "Cet espace est à louer",IDC_FORMATTEXT,55,56,90,26 + DEFPUSHBUTTON "OK",IDOK,145,42,45,14 + PUSHBUTTON "Annuler",IDCANCEL,145,61,45,14 +END + +STRINGTABLE DISCARDABLE +{ + IDS_WAVESTREAMFORMAT "Waveform: %s" + IDS_WAVEFILETYPE "Waveform" + IDS_ALLMULTIMEDIA "Tous les fichiers multimédias" + IDS_ALLFILES "Tous les fichier (*.*)@*.*" + IDS_VIDEO "vidéo" + IDS_AUDIO "audio" + IDS_AVISTREAMFORMAT "%s %s #%d" + IDS_AVIFILETYPE "Wine AVI-default-filehandler" + IDS_UNCOMPRESSED "non compressé" +} diff --git a/reactos/lib/avifil32/avifile_It.rc b/reactos/lib/avifil32/avifile_It.rc new file mode 100644 index 00000000000..e0873194ba3 --- /dev/null +++ b/reactos/lib/avifil32/avifile_It.rc @@ -0,0 +1,51 @@ +/* + * Copyright 2002 Michael Günnewig + * Copyright 2003 Ivan Leo Puoti + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_ITALIAN, SUBLANG_DEFAULT + +IDD_SAVEOPTIONS DIALOG FIXED IMPURE 43, 37, 196, 82 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Opzioni di compressione" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "&Scegliere un flusso:",-1,2,5,114,10 + COMBOBOX IDC_STREAM,2,18,134,61,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "&Opzioni...",IDC_OPTIONS,145,17,45,14 + AUTOCHECKBOX "&Interfoliazione ogni",IDC_INTERLEAVE,3,42,60,11,WS_TABSTOP + EDITTEXT IDC_INTERLEAVEEVERY,66,41,32,12,ES_AUTOHSCROLL + LTEXT "fotogrammi",-1,104,43,36,9 + LTEXT "Formato attuale:",-1,3,56,53,9 + LTEXT "This space for rent",IDC_FORMATTEXT,55,56,90,26 + DEFPUSHBUTTON "OK",IDOK,145,42,45,14 + PUSHBUTTON "Annulla",IDCANCEL,145,61,45,14 +END + +STRINGTABLE DISCARDABLE +{ + IDS_WAVESTREAMFORMAT "Formato wave: %s" + IDS_WAVEFILETYPE "Formato wave" + IDS_ALLMULTIMEDIA "Tutti i file multimediali" + IDS_ALLFILES "Tutti i file (*.*)@*.*" + IDS_VIDEO "Video" + IDS_AUDIO "Audio" + IDS_AVISTREAMFORMAT "%s %s #%d" + IDS_AVIFILETYPE "Gestore predefinito di Wine dei file AVI" + IDS_UNCOMPRESSED "Non compresso" +} diff --git a/reactos/lib/avifil32/avifile_Ja.rc b/reactos/lib/avifil32/avifile_Ja.rc new file mode 100644 index 00000000000..3bb780ef44c --- /dev/null +++ b/reactos/lib/avifil32/avifile_Ja.rc @@ -0,0 +1,50 @@ +/* + * Copyright 2004 Hajime Segawa + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT + +IDD_SAVEOPTIONS DIALOG FIXED IMPURE 43, 37, 196, 82 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "ˆ³kƒIƒvƒVƒ‡ƒ“" +FONT 9, "MS UI Gothic" +BEGIN + LTEXT "ƒXƒgƒŠ[ƒ€‚ð‘I‘ð(&C):",-1,2,5,114,10 + COMBOBOX IDC_STREAM,2,18,134,61,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "ƒIƒvƒVƒ‡ƒ“(&O)...",IDC_OPTIONS,145,17,45,14 + AUTOCHECKBOX "ƒCƒ“ƒ^[ƒŠ[ƒu(&I)",IDC_INTERLEAVE,3,42,60,11,WS_TABSTOP + EDITTEXT IDC_INTERLEAVEEVERY,66,41,32,12,ES_AUTOHSCROLL + LTEXT "ƒtƒŒ[ƒ€",-1,104,43,36,9 + LTEXT "Œ»Ý‚̃tƒH[ƒ}ƒbƒg:",-1,3,56,53,9 + LTEXT "This space for rent",IDC_FORMATTEXT,55,56,90,26 + DEFPUSHBUTTON "OK",IDOK,145,42,45,14 + PUSHBUTTON "ƒLƒƒƒ“ƒZƒ‹",IDCANCEL,145,61,45,14 +END + +STRINGTABLE DISCARDABLE +{ + IDS_WAVESTREAMFORMAT "Waveƒtƒ@ƒCƒ‹: %s" + IDS_WAVEFILETYPE "Waveƒtƒ@ƒCƒ‹" + IDS_ALLMULTIMEDIA "‘S‚Ẵ}ƒ‹ƒ`ƒƒfƒBƒAƒtƒ@ƒCƒ‹" + IDS_ALLFILES "‘S‚Ẵtƒ@ƒCƒ‹ (*.*)@*.*" + IDS_VIDEO "ƒrƒfƒI" + IDS_AUDIO "‰¹º" + IDS_AVISTREAMFORMAT "%s %s #%d" + IDS_AVIFILETYPE "Wine AVI-default-filehandler" + IDS_UNCOMPRESSED "–¢ˆ³k" +} diff --git a/reactos/lib/avifil32/avifile_Nl.rc b/reactos/lib/avifil32/avifile_Nl.rc new file mode 100644 index 00000000000..ecabf2d2216 --- /dev/null +++ b/reactos/lib/avifil32/avifile_Nl.rc @@ -0,0 +1,52 @@ +/* + * avifile (Dutch resources) + * + * Copyright 2003 Hans Leidekker + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_DUTCH, SUBLANG_DEFAULT + +IDD_SAVEOPTIONS DIALOG FIXED IMPURE 43, 37, 196, 82 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Compressie-instellingen" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "&Kies een invoerbestand:",-1,2,5,114,10 + COMBOBOX IDC_STREAM,2,18,134,61,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "&Opties...",IDC_OPTIONS,145,17,45,14 + AUTOCHECKBOX "&Interleave alle",IDC_INTERLEAVE,3,42,60,11,WS_TABSTOP + EDITTEXT IDC_INTERLEAVEEVERY,66,41,32,12,ES_AUTOHSCROLL + LTEXT "Losse frames",-1,104,43,36,9 + LTEXT "Huidig formaat:",-1,3,56,53,9 + LTEXT "This space for rent",IDC_FORMATTEXT,55,56,90,26 + DEFPUSHBUTTON "OK",IDOK,145,42,45,14 + PUSHBUTTON "Annuleren",IDCANCEL,145,61,45,14 +END + +STRINGTABLE DISCARDABLE +{ + IDS_WAVESTREAMFORMAT "Waveform: %s" + IDS_WAVEFILETYPE "Waveform" + IDS_ALLMULTIMEDIA "Alle multimediabestanden" + IDS_ALLFILES "Alle bestanden (*.*)@*.*" + IDS_VIDEO "video" + IDS_AUDIO "audio" + IDS_AVISTREAMFORMAT "%s %s #%d" + IDS_AVIFILETYPE "Wine AVI-standaard-bestandskoppeling" + IDS_UNCOMPRESSED "ongecomprimeerd" +} diff --git a/reactos/lib/avifil32/avifile_No.rc b/reactos/lib/avifil32/avifile_No.rc new file mode 100644 index 00000000000..d75e042155f --- /dev/null +++ b/reactos/lib/avifil32/avifile_No.rc @@ -0,0 +1,50 @@ +/* + * Copyright 2005 Alexander N. Sørnes + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_NORWEGIAN, SUBLANG_NORWEGIAN_BOKMAL + +IDD_SAVEOPTIONS DIALOG FIXED IMPURE 43, 37, 196, 82 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Komprimeringsinnstillinger" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "&Velg en strøm:",-1,2,5,114,10 + COMBOBOX IDC_STREAM,2,18,134,61,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "I&nnstillinger . . .",IDC_OPTIONS,145,17,45,14 + AUTOCHECKBOX "Sett &inn for hver",IDC_INTERLEAVE,3,42,60,11,WS_TABSTOP + EDITTEXT IDC_INTERLEAVEEVERY,66,41,32,12,ES_AUTOHSCROLL + LTEXT "bilde",-1,104,43,36,9 + LTEXT "Gjeldende format:",-1,3,56,53,9 + LTEXT "Denne plassen er til leie",IDC_FORMATTEXT,55,56,90,26 + DEFPUSHBUTTON "OK",IDOK,145,42,45,14 + PUSHBUTTON "Avbryt",IDCANCEL,145,61,45,14 +END + +STRINGTABLE DISCARDABLE +{ + IDS_WAVESTREAMFORMAT "Lydformat: %s" + IDS_WAVEFILETYPE "Lydformat" + IDS_ALLMULTIMEDIA "Alle multimedia-filer" + IDS_ALLFILES "Alle filer (*.*)@*.*" + IDS_VIDEO "video" + IDS_AUDIO "lyd" + IDS_AVISTREAMFORMAT "%s %s #%d" + IDS_AVIFILETYPE "Wine standardfilhåndterer for AVI" + IDS_UNCOMPRESSED "ukomprimert" +} diff --git a/reactos/lib/avifil32/avifile_Pl.rc b/reactos/lib/avifil32/avifile_Pl.rc new file mode 100644 index 00000000000..052718012f1 --- /dev/null +++ b/reactos/lib/avifil32/avifile_Pl.rc @@ -0,0 +1,51 @@ +/* + * Copyright 2002 Michael Günnewig + * Copyright 2004 Piotr Caban + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_POLISH, SUBLANG_DEFAULT + +IDD_SAVEOPTIONS DIALOG FIXED IMPURE 43, 37, 196, 82 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Opcje kompresji" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "&Wybierz strumieñ:",-1,2,5,114,10 + COMBOBOX IDC_STREAM,2,18,134,61,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "&Opcje...",IDC_OPTIONS,145,17,45,14 + AUTOCHECKBOX "&Przeplot co",IDC_INTERLEAVE,3,42,60,11,WS_TABSTOP + EDITTEXT IDC_INTERLEAVEEVERY,66,41,32,12,ES_AUTOHSCROLL + LTEXT "klatek",-1,104,43,36,9 + LTEXT "Wybrany format:",-1,2,56,53,9 + LTEXT "Zarezerwownae miejsce",IDC_FORMATTEXT,55,56,90,26 + DEFPUSHBUTTON "OK",IDOK,145,42,45,14 + PUSHBUTTON "Anuluj",IDCANCEL,145,61,45,14 +END + +STRINGTABLE DISCARDABLE +{ + IDS_WAVESTREAMFORMAT "Waveform: %s" + IDS_WAVEFILETYPE "Waveform" + IDS_ALLMULTIMEDIA "Wszystkie pliki multimedialne" + IDS_ALLFILES "Wszystkie pliki (*.*)@*.*" + IDS_VIDEO "obraz" + IDS_AUDIO "dŸwiêk" + IDS_AVISTREAMFORMAT "%s %s #%d" + IDS_AVIFILETYPE "Wine AVI-domyœlna-obs³uga-pliku" + IDS_UNCOMPRESSED "nie skompresowany" +} diff --git a/reactos/lib/avifil32/avifile_Pt.rc b/reactos/lib/avifil32/avifile_Pt.rc new file mode 100644 index 00000000000..9ca53c9c6fd --- /dev/null +++ b/reactos/lib/avifil32/avifile_Pt.rc @@ -0,0 +1,50 @@ +/* + * Copyright 2003 Marcelo Duarte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_PORTUGUESE, SUBLANG_DEFAULT + +IDD_SAVEOPTIONS DIALOG FIXED IMPURE 43, 37, 196, 82 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Opções de compressão" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "&Escolha a stream:",-1,2,5,114,10 + COMBOBOX IDC_STREAM,2,18,134,61,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "&Opções...",IDC_OPTIONS,145,17,45,14 + AUTOCHECKBOX "&Interleave every",IDC_INTERLEAVE,3,42,60,11,WS_TABSTOP + EDITTEXT IDC_INTERLEAVEEVERY,66,41,32,12,ES_AUTOHSCROLL + LTEXT "frames",-1,104,43,36,9 + LTEXT "Formato atual:",-1,3,56,53,9 + LTEXT "This space for rent",IDC_FORMATTEXT,55,56,90,26 + DEFPUSHBUTTON "OK",IDOK,145,42,45,14 + PUSHBUTTON "Cancelar",IDCANCEL,145,61,45,14 +END + +STRINGTABLE DISCARDABLE +{ + IDS_WAVESTREAMFORMAT "Formato wave: %s" + IDS_WAVEFILETYPE "Formato wave" + IDS_ALLMULTIMEDIA "Todos arquivos multimídia" + IDS_ALLFILES "Todos os arquivos (*.*)@*.*" + IDS_VIDEO "vídeo" + IDS_AUDIO "áudio" + IDS_AVISTREAMFORMAT "%s %s #%d" + IDS_AVIFILETYPE "Wine AVI-manipulador padrão de arquivo" + IDS_UNCOMPRESSED "sem compressão" +} diff --git a/reactos/lib/avifil32/avifile_Ru.rc b/reactos/lib/avifil32/avifile_Ru.rc new file mode 100644 index 00000000000..1949d397eea --- /dev/null +++ b/reactos/lib/avifil32/avifile_Ru.rc @@ -0,0 +1,52 @@ +/* + * avifil32.dll (Russian resources) + * + * Copyright 2003 Igor Stepin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT + +IDD_SAVEOPTIONS DIALOG FIXED IMPURE 43, 37, 196, 82 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Íàñòðîéêè ñæàòèÿ" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "&Âûáåðèòå ïîòîê:",-1,2,5,114,10 + COMBOBOX IDC_STREAM,2,18,134,61,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "&Îïöèè...",IDC_OPTIONS,145,17,45,14 + AUTOCHECKBOX "&Ïðîñëàèâàòü êàæäûå",IDC_INTERLEAVE,3,42,60,11,WS_TABSTOP + EDITTEXT IDC_INTERLEAVEEVERY,66,41,32,12,ES_AUTOHSCROLL + LTEXT "ôðåéìà",-1,104,43,36,9 + LTEXT "Òåêóùèé ôîðìàò:",-1,3,56,53,9 + LTEXT "Ýòî ìåñòî ñäà¸òñÿ â àðåíäó",IDC_FORMATTEXT,55,56,90,26 + DEFPUSHBUTTON "OK",IDOK,145,42,45,14 + PUSHBUTTON "Îòìåíà",IDCANCEL,145,61,45,14 +END + +STRINGTABLE DISCARDABLE +{ + IDS_WAVESTREAMFORMAT "Çâóêîâîé ïîòîê: %s" + IDS_WAVEFILETYPE "Çâóêîâîé ïîòîê" + IDS_ALLMULTIMEDIA "Âñå ôàéëû ìóëüòèìåäèà" + IDS_ALLFILES "Âñå ôàéëû (*.*)@*.*" + IDS_VIDEO "âèäåî" + IDS_AUDIO "àóäèî" + IDS_AVISTREAMFORMAT "%s %s #%d" + IDS_AVIFILETYPE "Îáðàáîò÷èê ïî óìîë÷àíèþ avi-ôàéëîâ â Wine" + IDS_UNCOMPRESSED "áåç ñæàòèÿ" +} diff --git a/reactos/lib/avifil32/avifile_Si.rc b/reactos/lib/avifil32/avifile_Si.rc new file mode 100644 index 00000000000..b5714dea634 --- /dev/null +++ b/reactos/lib/avifil32/avifile_Si.rc @@ -0,0 +1,50 @@ +/* + * Copyright 2003 Rok Mandeljc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_SLOVENIAN, SUBLANG_DEFAULT + +IDD_SAVEOPTIONS DIALOG FIXED IMPURE 43, 37, 196, 82 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Možnosti za stiskanje" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "&Izbira toka:",-1,2,5,114,10 + COMBOBOX IDC_STREAM,2,18,134,61,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "&Možnosti ...",IDC_OPTIONS,145,17,45,14 + AUTOCHECKBOX "&Preplet vsake",IDC_INTERLEAVE,3,42,60,11,WS_TABSTOP + EDITTEXT IDC_INTERLEAVEEVERY,66,41,32,12,ES_AUTOHSCROLL + LTEXT "slike",-1,104,43,36,9 + LTEXT "Trenutna oblika zapisa:",-1,3,56,53,9 + LTEXT "This space for rent",IDC_FORMATTEXT,55,56,90,26 + DEFPUSHBUTTON "V redu",IDOK,145,42,45,14 + PUSHBUTTON "Preklièi",IDCANCEL,145,61,45,14 +END + +STRINGTABLE DISCARDABLE +{ + IDS_WAVESTREAMFORMAT "Waveform: %s" + IDS_WAVEFILETYPE "Waveform" + IDS_ALLMULTIMEDIA "Vse veèpredstavnostne datoteke" + IDS_ALLFILES "Vse datoteke (*.*)@*.*" + IDS_VIDEO "video" + IDS_AUDIO "avdio" + IDS_AVISTREAMFORMAT "%s %s #%d" + IDS_AVIFILETYPE "Privzeti program za obravnavo AVI datotek" + IDS_UNCOMPRESSED "nestisnjeno" +} diff --git a/reactos/lib/avifil32/avifile_private.h b/reactos/lib/avifil32/avifile_private.h new file mode 100644 index 00000000000..d29c00c07af --- /dev/null +++ b/reactos/lib/avifil32/avifile_private.h @@ -0,0 +1,74 @@ +/* + * Copyright 2002 Michael Günnewig + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __AVIFILE_PRIVATE_H +#define __AVIFILE_PRIVATE_H + +#ifndef MAX_AVISTREAMS +#define MAX_AVISTREAMS 8 +#endif + +#ifndef comptypeDIB +#define comptypeDIB mmioFOURCC('D','I','B',' ') +#endif + +#ifndef DIBWIDTHBYTES +#define WIDTHBYTES(i) (((i+31)&(~31))/8) +#define DIBWIDTHBYTES(bi) WIDTHBYTES((bi).biWidth * (bi).biBitCount) +#endif + +#ifndef DIBPTR +#define DIBPTR(lp) ((LPBYTE)(lp) + (lp)->biSize + \ + (lp)->biClrUsed * sizeof(RGBQUAD)) +#endif + +#define IDD_SAVEOPTIONS 0x0100 +#define IDC_INTERLEAVE 0x0110 +#define IDC_INTERLEAVEEVERY 0x0111 +#define IDC_STREAM 0x0112 +#define IDC_OPTIONS 0x0113 +#define IDC_FORMATTEXT 0x0114 + +#define IDS_WAVESTREAMFORMAT 0x0100 +#define IDS_WAVEFILETYPE 0x0101 +#define IDS_ALLMULTIMEDIA 0x0184 +#define IDS_ALLFILES 0x0185 +#define IDS_VIDEO 0x0189 +#define IDS_AUDIO 0x0190 +#define IDS_AVISTREAMFORMAT 0x0191 +#define IDS_AVIFILETYPE 0x0192 +#define IDS_UNCOMPRESSED 0x0193 + +DEFINE_AVIGUID(CLSID_ICMStream, 0x00020001, 0, 0); +DEFINE_AVIGUID(CLSID_WAVFile, 0x00020003, 0, 0); +DEFINE_AVIGUID(CLSID_ACMStream, 0x0002000F, 0, 0); +DEFINE_AVIGUID(IID_IEditStreamInternal, 0x0002000A,0,0); + +extern HMODULE AVIFILE_hModule; + +extern HRESULT AVIFILE_CreateAVIFile(REFIID riid, LPVOID *ppobj); +extern HRESULT AVIFILE_CreateWAVFile(REFIID riid, LPVOID *ppobj); +extern HRESULT AVIFILE_CreateACMStream(REFIID riid, LPVOID *ppobj); +extern HRESULT AVIFILE_CreateICMStream(REFIID riid, LPVOID *ppobj); +extern PAVIEDITSTREAM AVIFILE_CreateEditStream(PAVISTREAM pstream); +extern PGETFRAME AVIFILE_CreateGetFrame(PAVISTREAM pstream); +extern PAVIFILE AVIFILE_CreateAVITempFile(int nStreams,PAVISTREAM *ppStreams); + +extern LPCWSTR AVIFILE_BasenameW(LPCWSTR szFileName); + +#endif diff --git a/reactos/lib/avifil32/editstream.c b/reactos/lib/avifil32/editstream.c new file mode 100644 index 00000000000..eba563a29a1 --- /dev/null +++ b/reactos/lib/avifil32/editstream.c @@ -0,0 +1,1171 @@ +/* + * Copyright 2003 Michael Günnewig + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define COM_NO_WINDOWS_H +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "wingdi.h" +#include "winnls.h" +#include "winerror.h" +#include "windowsx.h" +#include "mmsystem.h" +#include "vfw.h" + +#include "avifile_private.h" +#include "extrachunk.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(avifile); + +/***********************************************************************/ + +/* internal interface to get access to table of stream in an editable stream */ + +typedef struct _EditStreamTable { + PAVISTREAM pStream; /* stream which contains the data */ + DWORD dwStart; /* where starts the part which is also our */ + DWORD dwLength; /* how many is also in this stream */ +} EditStreamTable; + +#define INTERFACE IEditStreamInternal +DECLARE_INTERFACE_(IEditStreamInternal,IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + /*** IEditStreamInternal methods ***/ + STDMETHOD(GetEditStreamImpl)(THIS_ LPVOID*) PURE; +}; +#undef INTERFACE + +#define EditStreamEnd(This,streamNr) ((This)->pStreams[streamNr].dwStart + \ + (This)->pStreams[streamNr].dwLength) + +/***********************************************************************/ + +static HRESULT WINAPI IAVIEditStream_fnQueryInterface(IAVIEditStream*iface,REFIID refiid,LPVOID *obj); +static ULONG WINAPI IAVIEditStream_fnAddRef(IAVIEditStream*iface); +static ULONG WINAPI IAVIEditStream_fnRelease(IAVIEditStream*iface); +static HRESULT WINAPI IAVIEditStream_fnCut(IAVIEditStream*iface,LONG*plStart, + LONG*plLength,PAVISTREAM*ppResult); +static HRESULT WINAPI IAVIEditStream_fnCopy(IAVIEditStream*iface,LONG*plStart, + LONG*plLength,PAVISTREAM*ppResult); +static HRESULT WINAPI IAVIEditStream_fnPaste(IAVIEditStream*iface,LONG*plStart, + LONG*plLength,PAVISTREAM pSource, + LONG lStart,LONG lEnd); +static HRESULT WINAPI IAVIEditStream_fnClone(IAVIEditStream*iface, + PAVISTREAM*ppResult); +static HRESULT WINAPI IAVIEditStream_fnSetInfo(IAVIEditStream*iface, + LPAVISTREAMINFOW asi,LONG size); + +static const struct IAVIEditStreamVtbl ieditstream = { + IAVIEditStream_fnQueryInterface, + IAVIEditStream_fnAddRef, + IAVIEditStream_fnRelease, + IAVIEditStream_fnCut, + IAVIEditStream_fnCopy, + IAVIEditStream_fnPaste, + IAVIEditStream_fnClone, + IAVIEditStream_fnSetInfo +}; + +static HRESULT WINAPI IEditAVIStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID*obj); +static ULONG WINAPI IEditAVIStream_fnAddRef(IAVIStream*iface); +static ULONG WINAPI IEditAVIStream_fnRelease(IAVIStream*iface); +static HRESULT WINAPI IEditAVIStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2); +static HRESULT WINAPI IEditAVIStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size); +static LONG WINAPI IEditAVIStream_fnFindSample(IAVIStream*iface,LONG pos, + LONG flags); +static HRESULT WINAPI IEditAVIStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG*formatsize); +static HRESULT WINAPI IEditAVIStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize); +static HRESULT WINAPI IEditAVIStream_fnRead(IAVIStream*iface,LONG start, + LONG samples,LPVOID buffer, + LONG buffersize,LONG*bytesread, + LONG*samplesread); +static HRESULT WINAPI IEditAVIStream_fnWrite(IAVIStream*iface,LONG start, + LONG samples,LPVOID buffer, + LONG buffersize,DWORD flags, + LONG*sampwritten,LONG*byteswritten); +static HRESULT WINAPI IEditAVIStream_fnDelete(IAVIStream*iface,LONG start,LONG samples); +static HRESULT WINAPI IEditAVIStream_fnReadData(IAVIStream*iface,DWORD fcc, + LPVOID lp,LONG *lpread); +static HRESULT WINAPI IEditAVIStream_fnWriteData(IAVIStream*iface,DWORD fcc, + LPVOID lp,LONG size); +static HRESULT WINAPI IEditAVIStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen); + +static const struct IAVIStreamVtbl ieditstast = { + IEditAVIStream_fnQueryInterface, + IEditAVIStream_fnAddRef, + IEditAVIStream_fnRelease, + IEditAVIStream_fnCreate, + IEditAVIStream_fnInfo, + IEditAVIStream_fnFindSample, + IEditAVIStream_fnReadFormat, + IEditAVIStream_fnSetFormat, + IEditAVIStream_fnRead, + IEditAVIStream_fnWrite, + IEditAVIStream_fnDelete, + IEditAVIStream_fnReadData, + IEditAVIStream_fnWriteData, + IEditAVIStream_fnSetInfo +}; + +static HRESULT WINAPI IEditStreamInternal_fnQueryInterface(IEditStreamInternal*iface,REFIID refiid,LPVOID*obj); +static ULONG WINAPI IEditStreamInternal_fnAddRef(IEditStreamInternal*iface); +static ULONG WINAPI IEditStreamInternal_fnRelease(IEditStreamInternal*iface); +static HRESULT WINAPI IEditStreamInternal_fnGetEditStreamImpl(IEditStreamInternal*iface,LPVOID*ppimpl); + +static const struct IEditStreamInternalVtbl ieditstreaminternal = { + IEditStreamInternal_fnQueryInterface, + IEditStreamInternal_fnAddRef, + IEditStreamInternal_fnRelease, + IEditStreamInternal_fnGetEditStreamImpl +}; + +typedef struct _IAVIEditStreamImpl IAVIEditStreamImpl; + +typedef struct _IEditAVIStreamImpl { + /* IUnknown stuff */ + const IAVIStreamVtbl *lpVtbl; + + /* IAVIStream stuff */ + IAVIEditStreamImpl *pae; +} IEditAVIStreamImpl; + +typedef struct _IEditStreamInternalImpl { + /* IUnknown stuff */ + const IEditStreamInternalVtbl *lpVtbl; + + /* IEditStreamInternal stuff */ + IAVIEditStreamImpl *pae; +} IEditStreamInternalImpl; + +struct _IAVIEditStreamImpl { + /* IUnknown stuff */ + const IAVIEditStreamVtbl *lpVtbl; + LONG ref; + + /* IAVIEditStream stuff */ + IEditAVIStreamImpl iAVIStream; + IEditStreamInternalImpl iEditStreamInternal; + + AVISTREAMINFOW sInfo; + + EditStreamTable *pStreams; + DWORD nStreams; /* current fill level of pStreams table */ + DWORD nTableSize; /* size of pStreams table */ + + BOOL bDecompress; + PAVISTREAM pCurStream; + PGETFRAME pg; /* IGetFrame for pCurStream */ + LPBITMAPINFOHEADER lpFrame; /* frame of pCurStream */ +}; + +/***********************************************************************/ + +PAVIEDITSTREAM AVIFILE_CreateEditStream(PAVISTREAM pstream) +{ + IAVIEditStreamImpl *pedit = NULL; + + pedit = (IAVIEditStreamImpl*)LocalAlloc(LPTR,sizeof(IAVIEditStreamImpl)); + if (pedit == NULL) + return NULL; + + pedit->lpVtbl = &ieditstream; + pedit->iAVIStream.lpVtbl = &ieditstast; + pedit->iAVIStream.pae = pedit; + pedit->iEditStreamInternal.lpVtbl = &ieditstreaminternal; + pedit->iEditStreamInternal.pae = pedit; + pedit->ref = 1; + + IAVIStream_Create((PAVISTREAM)&pedit->iAVIStream,(LPARAM)pstream,0); + + return (PAVIEDITSTREAM)pedit; +} + +static HRESULT AVIFILE_FindStreamInTable(IAVIEditStreamImpl* const This, + DWORD pos,PAVISTREAM *ppStream, + DWORD* streamPos, + DWORD* streamNr,BOOL bFindSample) +{ + DWORD n; + + TRACE("(%p,%lu,%p,%p,%p,%d)\n",This,pos,ppStream,streamPos, + streamNr,bFindSample); + + if (pos < This->sInfo.dwStart) + return AVIERR_BADPARAM; + + pos -= This->sInfo.dwStart; + for (n = 0; n < This->nStreams; n++) { + if (pos < This->pStreams[n].dwLength) { + *ppStream = This->pStreams[n].pStream; + *streamPos = This->pStreams[n].dwStart + pos; + if (streamNr != NULL) + *streamNr = n; + + return AVIERR_OK; + } + pos -= This->pStreams[n].dwLength; + } + if (pos == 0 && bFindSample) { + *ppStream = This->pStreams[--n].pStream; + *streamPos = EditStreamEnd(This, n); + if (streamNr != NULL) + *streamNr = n; + + TRACE(" -- pos=0 && b=1 -> (%p,%lu,%lu)\n",*ppStream, *streamPos, n); + return AVIERR_OK; + } else { + *ppStream = NULL; + *streamPos = 0; + if (streamNr != NULL) + *streamNr = 0; + + TRACE(" -> ERROR (NULL,0,0)\n"); + return AVIERR_BADPARAM; + } +} + +static LPVOID AVIFILE_ReadFrame(IAVIEditStreamImpl* const This, + PAVISTREAM pstream, LONG pos) +{ + PGETFRAME pg; + + TRACE("(%p,%p,%ld)\n",This,pstream,pos); + + if (pstream == NULL) + return NULL; + + /* if stream changes make sure that only palette changes */ + if (This->pCurStream != pstream) { + pg = AVIStreamGetFrameOpen(pstream, NULL); + if (pg == NULL) + return NULL; + if (This->pg != NULL) { + if (IGetFrame_SetFormat(pg, This->lpFrame, NULL, 0, 0, -1, -1)) { + AVIStreamGetFrameClose(pg); + ERR(": IGetFrame_SetFormat failed\n"); + return NULL; + } + AVIStreamGetFrameClose(This->pg); + } + This->pg = pg; + This->pCurStream = pstream; + } + + /* now get the decompressed frame */ + This->lpFrame = AVIStreamGetFrame(This->pg, pos); + if (This->lpFrame != NULL) + This->sInfo.dwSuggestedBufferSize = This->lpFrame->biSizeImage; + + return This->lpFrame; +} + +static HRESULT AVIFILE_RemoveStream(IAVIEditStreamImpl* const This, DWORD nr) +{ + assert(This != NULL); + assert(nr < This->nStreams); + + /* remove part nr */ + IAVIStream_Release(This->pStreams[nr].pStream); + This->nStreams--; + if (This->nStreams - nr > 0) { + memmove(This->pStreams + nr, This->pStreams + nr + 1, + (This->nStreams - nr) * sizeof(EditStreamTable)); + } + This->pStreams[This->nStreams].pStream = NULL; + This->pStreams[This->nStreams].dwStart = 0; + This->pStreams[This->nStreams].dwLength = 0; + + /* try to merge the part before the deleted one and the one after it */ + if (0 < nr && 0 < This->nStreams && + This->pStreams[nr - 1].pStream == This->pStreams[nr].pStream) { + if (EditStreamEnd(This, nr - 1) == This->pStreams[nr].dwStart) { + This->pStreams[nr - 1].dwLength += This->pStreams[nr].dwLength; + return AVIFILE_RemoveStream(This, nr); + } + } + + return AVIERR_OK; +} + +static BOOL AVIFILE_FormatsEqual(PAVISTREAM avi1, PAVISTREAM avi2) +{ + LPVOID fmt1 = NULL, fmt2 = NULL; + LONG size1, size2, start1, start2; + BOOL status = FALSE; + + assert(avi1 != NULL && avi2 != NULL); + + /* get stream starts and check format sizes */ + start1 = AVIStreamStart(avi1); + start2 = AVIStreamStart(avi2); + if (FAILED(AVIStreamFormatSize(avi1, start1, &size1))) + return FALSE; + if (FAILED(AVIStreamFormatSize(avi2, start2, &size2))) + return FALSE; + if (size1 != size2) + return FALSE; + + /* sizes match, now get formats and compare them */ + fmt1 = GlobalAllocPtr(GHND, size1); + if (fmt1 == NULL) + return FALSE; + if (SUCCEEDED(AVIStreamReadFormat(avi1, start1, fmt1, &size1))) { + fmt2 = GlobalAllocPtr(GHND, size1); + if (fmt2 != NULL) { + if (SUCCEEDED(AVIStreamReadFormat(avi2, start2, fmt2, &size1))) + status = (memcmp(fmt1, fmt2, size1) == 0); + } + } + + if (fmt2 != NULL) + GlobalFreePtr(fmt2); + GlobalFreePtr(fmt1); + + return status; +} + +/***********************************************************************/ + +static HRESULT WINAPI IAVIEditStream_fnQueryInterface(IAVIEditStream*iface,REFIID refiid,LPVOID *obj) +{ + IAVIEditStreamImpl *This = (IAVIEditStreamImpl *)iface; + + TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj); + + if (IsEqualGUID(&IID_IUnknown, refiid) || + IsEqualGUID(&IID_IAVIEditStream, refiid)) { + *obj = iface; + IAVIEditStream_AddRef(iface); + + return S_OK; + } else if (IsEqualGUID(&IID_IAVIStream, refiid)) { + *obj = &This->iAVIStream; + IAVIEditStream_AddRef(iface); + + return S_OK; + } else if (IsEqualGUID(&IID_IEditStreamInternal, refiid)) { + *obj = &This->iEditStreamInternal; + IAVIEditStream_AddRef(iface); + + return S_OK; + } + + return OLE_E_ENUM_NOMORE; +} + +static ULONG WINAPI IAVIEditStream_fnAddRef(IAVIEditStream*iface) +{ + IAVIEditStreamImpl *This = (IAVIEditStreamImpl *)iface; + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) -> %ld\n", iface, ref); + + return ref; +} + +static ULONG WINAPI IAVIEditStream_fnRelease(IAVIEditStream*iface) +{ + IAVIEditStreamImpl *This = (IAVIEditStreamImpl *)iface; + DWORD i; + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) -> %ld\n", iface, ref); + + if (!ref) { + /* release memory */ + if (This->pg != NULL) + AVIStreamGetFrameClose(This->pg); + if (This->pStreams != NULL) { + for (i = 0; i < This->nStreams; i++) { + if (This->pStreams[i].pStream != NULL) + IAVIStream_Release(This->pStreams[i].pStream); + } + GlobalFreePtr(This->pStreams); + } + + LocalFree((HLOCAL)This); + return 0; + } + return ref; +} + +static HRESULT WINAPI IAVIEditStream_fnCut(IAVIEditStream*iface,LONG*plStart, + LONG*plLength,PAVISTREAM*ppResult) +{ + IAVIEditStreamImpl *This = (IAVIEditStreamImpl *)iface; + PAVISTREAM stream; + DWORD start, len, streamPos, streamNr; + HRESULT hr; + + TRACE("(%p,%p,%p,%p)\n",iface,plStart,plLength,ppResult); + + if (ppResult != NULL) + *ppResult = NULL; + if (plStart == NULL || plLength == NULL || *plStart < 0) + return AVIERR_BADPARAM; + + /* if asked for cutted part copy it before deleting */ + if (ppResult != NULL) { + hr = IAVIEditStream_Copy(iface, plStart, plLength, ppResult); + if (FAILED(hr)) + return hr; + } + + start = *plStart; + len = *plLength; + + /* now delete the requested part */ + while (len > 0) { + hr = AVIFILE_FindStreamInTable(This, start, &stream, + &streamPos, &streamNr, FALSE); + if (FAILED(hr)) + return hr; + if (This->pStreams[streamNr].dwStart == streamPos) { + /* deleting from start of part */ + if (len < This->pStreams[streamNr].dwLength) { + start += len; + This->pStreams[streamNr].dwStart += len; + This->pStreams[streamNr].dwLength -= len; + This->sInfo.dwLength -= len; + len = 0; + + /* we must return decompressed data now */ + This->bDecompress = TRUE; + } else { + /* deleting hole part */ + len -= This->pStreams[streamNr].dwLength; + AVIFILE_RemoveStream(This,streamNr); + } + } else if (EditStreamEnd(This, streamNr) <= streamPos + len) { + /* deleting at end of a part */ + DWORD count = EditStreamEnd(This, streamNr) - streamPos; + This->sInfo.dwLength -= count; + len -= count; + This->pStreams[streamNr].dwLength = + streamPos - This->pStreams[streamNr].dwStart; + } else { + /* splitting */ + if (This->nStreams + 1 >= This->nTableSize) { + This->pStreams = + GlobalReAllocPtr(This->pStreams, (This->nTableSize + 32) * sizeof(EditStreamTable), GMEM_SHARE|GHND); + if (This->pStreams == NULL) + return AVIERR_MEMORY; + This->nTableSize += 32; + } + memmove(This->pStreams + streamNr + 1, This->pStreams + streamNr, + (This->nStreams - streamNr) * sizeof(EditStreamTable)); + This->nStreams++; + + IAVIStream_AddRef(This->pStreams[streamNr + 1].pStream); + This->pStreams[streamNr + 1].dwStart = streamPos + len; + This->pStreams[streamNr + 1].dwLength = + EditStreamEnd(This, streamNr) - This->pStreams[streamNr + 1].dwStart; + + This->pStreams[streamNr].dwLength = + streamPos - This->pStreams[streamNr].dwStart; + This->sInfo.dwLength -= len; + len = 0; + } + } + + This->sInfo.dwEditCount++; + + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIEditStream_fnCopy(IAVIEditStream*iface,LONG*plStart, + LONG*plLength,PAVISTREAM*ppResult) +{ + IAVIEditStreamImpl *This = (IAVIEditStreamImpl *)iface; + IAVIEditStreamImpl* pEdit; + HRESULT hr; + LONG start = 0; + + TRACE("(%p,%p,%p,%p)\n",iface,plStart,plLength,ppResult); + + if (ppResult == NULL) + return AVIERR_BADPARAM; + *ppResult = NULL; + if (plStart == NULL || plLength == NULL || *plStart < 0 || *plLength < 0) + return AVIERR_BADPARAM; + + /* check bounds */ + if (*(LPDWORD)plLength > This->sInfo.dwLength) + *(LPDWORD)plLength = This->sInfo.dwLength; + if (*(LPDWORD)plStart < This->sInfo.dwStart) { + *(LPDWORD)plLength -= This->sInfo.dwStart - *(LPDWORD)plStart; + *(LPDWORD)plStart = This->sInfo.dwStart; + if (*plLength < 0) + return AVIERR_BADPARAM; + } + if (*(LPDWORD)plStart + *(LPDWORD)plLength > This->sInfo.dwStart + This->sInfo.dwLength) + *(LPDWORD)plLength = This->sInfo.dwStart + This->sInfo.dwLength - + *(LPDWORD)plStart; + + pEdit = (IAVIEditStreamImpl*)AVIFILE_CreateEditStream(NULL); + if (pEdit == NULL) + return AVIERR_MEMORY; + + hr = IAVIEditStream_Paste((PAVIEDITSTREAM)pEdit,&start,plLength, + (PAVISTREAM)&This->iAVIStream,*plStart, + *plStart + *plLength); + *plStart = start; + if (FAILED(hr)) + IAVIEditStream_Release((PAVIEDITSTREAM)pEdit); + else + *ppResult = (PAVISTREAM)&pEdit->iAVIStream; + + return hr; +} + +static HRESULT WINAPI IAVIEditStream_fnPaste(IAVIEditStream*iface,LONG*plStart, + LONG*plLength,PAVISTREAM pSource, + LONG lStart,LONG lLength) +{ + IAVIEditStreamImpl *This = (IAVIEditStreamImpl *)iface; + AVISTREAMINFOW srcInfo; + IEditStreamInternal*pInternal = NULL; + IAVIEditStreamImpl *pEdit = NULL; + PAVISTREAM pStream; + DWORD startPos, endPos, streamNr, nStreams; + ULONG n; + + TRACE("(%p,%p,%p,%p,%ld,%ld)\n",iface,plStart,plLength, + pSource,lStart,lLength); + + if (pSource == NULL) + return AVIERR_BADHANDLE; + if (plStart == NULL || *plStart < 0) + return AVIERR_BADPARAM; + if (This->sInfo.dwStart + This->sInfo.dwLength < *plStart) + return AVIERR_BADPARAM; /* Can't paste with holes */ + if (FAILED(IAVIStream_Info(pSource, &srcInfo, sizeof(srcInfo)))) + return AVIERR_ERROR; + if (lStart < srcInfo.dwStart || lStart >= srcInfo.dwStart + srcInfo.dwLength) + return AVIERR_BADPARAM; + if (This->sInfo.fccType == 0) { + /* This stream is empty */ + IAVIStream_Info(pSource, &This->sInfo, sizeof(This->sInfo)); + This->sInfo.dwStart = *plStart; + This->sInfo.dwLength = 0; + } + if (This->sInfo.fccType != srcInfo.fccType) + return AVIERR_UNSUPPORTED; /* different stream types */ + if (lLength == -1) /* Copy the hole stream */ + lLength = srcInfo.dwLength; + if (lStart + lLength > srcInfo.dwStart + srcInfo.dwLength) + lLength = srcInfo.dwStart + srcInfo.dwLength - lStart; + if (lLength + *plStart >= 0x80000000) + return AVIERR_MEMORY; + + /* streamtype specific tests */ + if (srcInfo.fccType == streamtypeVIDEO) { + LONG size; + + size = srcInfo.rcFrame.right - srcInfo.rcFrame.left; + if (size != This->sInfo.rcFrame.right - This->sInfo.rcFrame.left) + return AVIERR_UNSUPPORTED; /* FIXME: Can't GetFrame convert it? */ + size = srcInfo.rcFrame.bottom - srcInfo.rcFrame.top; + if (size != This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top) + return AVIERR_UNSUPPORTED; /* FIXME: Can't GetFrame convert it? */ + } else if (srcInfo.fccType == streamtypeAUDIO) { + if (! AVIFILE_FormatsEqual((PAVISTREAM)&This->iAVIStream, pSource)) + return AVIERR_UNSUPPORTED; + } else { + /* FIXME: streamtypeMIDI and streamtypeTEXT */ + return AVIERR_UNSUPPORTED; + } + + /* try to get an IEditStreamInternal interface */ + if (SUCCEEDED(IAVIStream_QueryInterface(pSource, &IID_IEditStreamInternal, + (LPVOID*)&pInternal))) { + pInternal->lpVtbl->GetEditStreamImpl(pInternal, (LPVOID*)&pEdit); + pInternal->lpVtbl->Release(pInternal); + } + + /* for video must check for change of format */ + if (This->sInfo.fccType == streamtypeVIDEO) { + if (! This->bDecompress) { + /* Need to decompress if any of the following conditions matches: + * - pSource is an editable stream which decompresses + * - the nearest keyframe of pSource isn't lStart + * - the nearest keyframe of this stream isn't *plStart + * - the format of pSource doesn't match this one + */ + if ((pEdit != NULL && pEdit->bDecompress) || + AVIStreamNearestKeyFrame(pSource, lStart) != lStart || + AVIStreamNearestKeyFrame((PAVISTREAM)&This->iAVIStream, *plStart) != *plStart || + (This->nStreams > 0 && !AVIFILE_FormatsEqual((PAVISTREAM)&This->iAVIStream, pSource))) { + /* Use first stream part to get format to convert everything to */ + AVIFILE_ReadFrame(This, This->pStreams[0].pStream, + This->pStreams[0].dwStart); + + /* Check if we could convert the source streams to the disired format... */ + if (pEdit != NULL) { + if (FAILED(AVIFILE_FindStreamInTable(pEdit, lStart, &pStream, + &startPos, &streamNr, TRUE))) + return AVIERR_INTERNAL; + for (n = lStart; n < lStart + lLength; streamNr++) { + if (AVIFILE_ReadFrame(This, pEdit->pStreams[streamNr].pStream, startPos) == NULL) + return AVIERR_BADFORMAT; + startPos = pEdit->pStreams[streamNr].dwStart; + n += pEdit->pStreams[streamNr].dwLength; + } + } else if (AVIFILE_ReadFrame(This, pSource, lStart) == NULL) + return AVIERR_BADFORMAT; + + This->bDecompress = TRUE; + This->sInfo.fccHandler = 0; + } + } else if (AVIFILE_ReadFrame(This, pSource, lStart) == NULL) + return AVIERR_BADFORMAT; /* Can't convert source to own format */ + } /* FIXME: something special for the other formats? */ + + /* Make sure we have enough memory for parts */ + if (pEdit != NULL) { + DWORD nLastStream; + + AVIFILE_FindStreamInTable(pEdit, lStart + lLength, &pStream, + &endPos, &nLastStream, TRUE); + AVIFILE_FindStreamInTable(pEdit, lStart, &pStream, + &startPos, &streamNr, FALSE); + if (nLastStream == streamNr) + nLastStream++; + + nStreams = nLastStream - streamNr; + } else + nStreams = 1; + if (This->nStreams + nStreams + 1 > This->nTableSize) { + n = This->nStreams + nStreams + 33; + + This->pStreams = + GlobalReAllocPtr(This->pStreams, n * sizeof(EditStreamTable), GMEM_SHARE|GHND); + if (This->pStreams == NULL) + return AVIERR_MEMORY; + This->nTableSize = n; + } + + if (plLength != NULL) + *plLength = lLength; + + /* now do the real work */ + if (This->sInfo.dwStart + This->sInfo.dwLength > *plStart) { + AVIFILE_FindStreamInTable(This, *plStart, &pStream, + &startPos, &streamNr, FALSE); + if (startPos != This->pStreams[streamNr].dwStart) { + /* split stream streamNr at startPos */ + memmove(This->pStreams + streamNr + nStreams + 1, + This->pStreams + streamNr, + (This->nStreams + nStreams - streamNr + 1) * sizeof(EditStreamTable)); + + This->pStreams[streamNr + 2].dwLength = + EditStreamEnd(This, streamNr + 2) - startPos; + This->pStreams[streamNr + 2].dwStart = startPos; + This->pStreams[streamNr].dwLength = + startPos - This->pStreams[streamNr].dwStart; + IAVIStream_AddRef(This->pStreams[streamNr].pStream); + streamNr++; + } else { + /* insert before stream at streamNr */ + memmove(This->pStreams + streamNr + nStreams, This->pStreams + streamNr, + (This->nStreams + nStreams - streamNr) * sizeof(EditStreamTable)); + } + } else /* append the streams */ + streamNr = This->nStreams; + + if (pEdit != NULL) { + /* insert the parts of the editable stream instead of itself */ + AVIFILE_FindStreamInTable(pEdit, lStart + lLength, &pStream, + &endPos, NULL, FALSE); + AVIFILE_FindStreamInTable(pEdit, lStart, &pStream, &startPos, &n, FALSE); + + memcpy(This->pStreams + streamNr, pEdit->pStreams + n, + nStreams * sizeof(EditStreamTable)); + if (This->pStreams[streamNr].dwStart < startPos) { + This->pStreams[streamNr].dwLength = + EditStreamEnd(This, streamNr) - startPos; + This->pStreams[streamNr].dwStart = startPos; + } + if (endPos < EditStreamEnd(This, streamNr + nStreams)) + This->pStreams[streamNr + nStreams].dwLength = + endPos - This->pStreams[streamNr + nStreams].dwStart; + } else { + /* a simple stream */ + This->pStreams[streamNr].pStream = pSource; + This->pStreams[streamNr].dwStart = lStart; + This->pStreams[streamNr].dwLength = lLength; + } + + for (n = 0; n < nStreams; n++) { + IAVIStream_AddRef(This->pStreams[streamNr + n].pStream); + if (0 < streamNr + n && + This->pStreams[streamNr + n - 1].pStream != This->pStreams[streamNr + n].pStream) { + This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES; + This->sInfo.dwFormatChangeCount++; + } + } + This->sInfo.dwEditCount++; + This->sInfo.dwLength += lLength; + This->nStreams += nStreams; + + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIEditStream_fnClone(IAVIEditStream*iface, + PAVISTREAM*ppResult) +{ + IAVIEditStreamImpl *This = (IAVIEditStreamImpl *)iface; + IAVIEditStreamImpl* pEdit; + DWORD i; + + TRACE("(%p,%p)\n",iface,ppResult); + + if (ppResult == NULL) + return AVIERR_BADPARAM; + *ppResult = NULL; + + pEdit = (IAVIEditStreamImpl*)AVIFILE_CreateEditStream(NULL); + if (pEdit == NULL) + return AVIERR_MEMORY; + if (This->nStreams > pEdit->nTableSize) { + pEdit->pStreams = GlobalReAllocPtr(pEdit->pStreams, This->nStreams * sizeof(EditStreamTable),GMEM_SHARE|GHND); + if (pEdit->pStreams == NULL) + return AVIERR_MEMORY; + pEdit->nTableSize = This->nStreams; + } + pEdit->nStreams = This->nStreams; + memcpy(pEdit->pStreams, This->pStreams, + This->nStreams * sizeof(EditStreamTable)); + memcpy(&pEdit->sInfo,&This->sInfo,sizeof(This->sInfo)); + for (i = 0; i < This->nStreams; i++) { + if (pEdit->pStreams[i].pStream != NULL) + IAVIStream_AddRef(pEdit->pStreams[i].pStream); + } + + *ppResult = (PAVISTREAM)&pEdit->iAVIStream; + + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIEditStream_fnSetInfo(IAVIEditStream*iface, + LPAVISTREAMINFOW asi,LONG size) +{ + IAVIEditStreamImpl *This = (IAVIEditStreamImpl *)iface; + + TRACE("(%p,%p,%ld)\n",iface,asi,size); + + /* check parameters */ + if (asi == NULL) + return AVIERR_BADPARAM; + if (size != sizeof(AVISTREAMINFOW)) + return AVIERR_BADSIZE; + if (asi->dwScale == 0 || asi->dwRate == 0 || (LONG)asi->dwQuality < -1 || + asi->dwQuality > ICQUALITY_HIGH) + return AVIERR_ERROR; + + This->sInfo.wLanguage = asi->wLanguage; + This->sInfo.wPriority = asi->wPriority; + This->sInfo.dwStart = asi->dwStart; + if (asi->dwRate != 0) + This->sInfo.dwRate = asi->dwRate; + if (asi->dwScale != 0) + This->sInfo.dwScale = asi->dwScale; + if (asi->dwQuality <= ICQUALITY_HIGH) + This->sInfo.dwQuality = ICQUALITY_HIGH; + CopyRect(&This->sInfo.rcFrame, &asi->rcFrame); + memcpy(&This->sInfo.szName, &asi->szName, sizeof(asi->szName)); + This->sInfo.dwEditCount++; + + return AVIERR_OK; +} + +static HRESULT WINAPI IEditAVIStream_fnQueryInterface(IAVIStream*iface, + REFIID refiid,LPVOID*obj) +{ + IEditAVIStreamImpl *This = (IEditAVIStreamImpl *)iface; + + assert(This->pae != NULL); + + return IAVIEditStream_QueryInterface((IAVIEditStream*)This->pae,refiid,obj); +} + +static ULONG WINAPI IEditAVIStream_fnAddRef(IAVIStream*iface) +{ + IEditAVIStreamImpl *This = (IEditAVIStreamImpl *)iface; + + assert(This->pae != NULL); + + return IAVIEditStream_AddRef((IAVIEditStream*)This->pae); +} + +static ULONG WINAPI IEditAVIStream_fnRelease(IAVIStream*iface) +{ + IEditAVIStreamImpl *This = (IEditAVIStreamImpl *)iface; + + assert(This->pae != NULL); + + return IAVIEditStream_Release((IAVIEditStream*)This->pae); +} + +static HRESULT WINAPI IEditAVIStream_fnCreate(IAVIStream*iface, + LPARAM lParam1,LPARAM lParam2) +{ + IAVIEditStreamImpl *This = ((IEditAVIStreamImpl*)iface)->pae; + + if (lParam2 != 0) + return AVIERR_ERROR; + + if (This->pStreams == NULL) { + This->pStreams = + GlobalAllocPtr(GMEM_SHARE|GHND, 256 * sizeof(EditStreamTable)); + if (This->pStreams == NULL) + return AVIERR_MEMORY; + This->nTableSize = 256; + } + + if (lParam1 != 0) { + IAVIStream_Info((PAVISTREAM)lParam1, &This->sInfo, sizeof(This->sInfo)); + IAVIStream_AddRef((PAVISTREAM)lParam1); + This->pStreams[0].pStream = (PAVISTREAM)lParam1; + This->pStreams[0].dwStart = This->sInfo.dwStart; + This->pStreams[0].dwLength = This->sInfo.dwLength; + This->nStreams = 1; + } + return AVIERR_OK; +} + +static HRESULT WINAPI IEditAVIStream_fnInfo(IAVIStream*iface, + AVISTREAMINFOW *psi,LONG size) +{ + IEditAVIStreamImpl *This = (IEditAVIStreamImpl *)iface; + + TRACE("(%p,%p,%ld)\n",iface,psi,size); + + assert(This->pae != NULL); + + if (psi == NULL) + return AVIERR_BADPARAM; + if (size < 0) + return AVIERR_BADSIZE; + + if (This->pae->bDecompress) + This->pae->sInfo.fccHandler = 0; + + memcpy(psi, &This->pae->sInfo, min((DWORD)size, sizeof(This->pae->sInfo))); + + if ((DWORD)size < sizeof(This->pae->sInfo)) + return AVIERR_BUFFERTOOSMALL; + return AVIERR_OK; +} + +static LONG WINAPI IEditAVIStream_fnFindSample(IAVIStream*iface,LONG pos, + LONG flags) +{ + IAVIEditStreamImpl* const This = ((IEditAVIStreamImpl* const)iface)->pae; + PAVISTREAM stream; + DWORD streamPos, streamNr; + + TRACE("(%p,%ld,0x%08lX)\n",iface,pos,flags); + + if (flags & FIND_FROM_START) + pos = (LONG)This->sInfo.dwStart; + + /* outside of stream? */ + if (pos < (LONG)This->sInfo.dwStart || + (LONG)This->sInfo.dwStart + (LONG)This->sInfo.dwLength <= pos) + return -1; + + /* map our position to a stream and position in it */ + if (AVIFILE_FindStreamInTable(This, pos, &stream, &streamPos, + &streamNr, TRUE)) + return -1; /* doesn't exist */ + + if (This->bDecompress) { + /* only one stream -- format changes only at start */ + if (flags & FIND_FORMAT) + return (flags & FIND_NEXT ? -1 : 0); + + /* FIXME: map positions back to us */ + return IAVIStream_FindSample(stream, streamPos, flags); + } else { + /* assume change of format every frame */ + return pos; + } +} + +static HRESULT WINAPI IEditAVIStream_fnReadFormat(IAVIStream*iface,LONG pos, + LPVOID format,LONG*fmtsize) +{ + IAVIEditStreamImpl* const This = ((IEditAVIStreamImpl* const)iface)->pae; + LPBITMAPINFOHEADER lp; + PAVISTREAM stream; + DWORD n; + HRESULT hr; + + TRACE("(%p,%ld,%p,%p)\n",iface,pos,format,fmtsize); + + if (fmtsize == NULL || pos < This->sInfo.dwStart || + This->sInfo.dwStart + This->sInfo.dwLength <= pos) + return AVIERR_BADPARAM; + + /* find stream corresponding to position */ + hr = AVIFILE_FindStreamInTable(This, pos, &stream, &n, NULL, FALSE); + if (FAILED(hr)) + return hr; + + if (! This->bDecompress) + return IAVIStream_ReadFormat(stream, n, format, fmtsize); + + lp = (LPBITMAPINFOHEADER)AVIFILE_ReadFrame(This, stream, n); + if (lp == NULL) + return AVIERR_ERROR; + if (lp->biBitCount <= 8) { + n = (lp->biClrUsed > 0 ? lp->biClrUsed : 1 << lp->biBitCount); + n *= sizeof(RGBQUAD); + } else + n = 0; + n += lp->biSize; + + memcpy(format, lp, min((LONG)n, *fmtsize)); + hr = ((LONG)n > *fmtsize ? AVIERR_BUFFERTOOSMALL : AVIERR_OK); + *fmtsize = n; + + return hr; +} + +static HRESULT WINAPI IEditAVIStream_fnSetFormat(IAVIStream*iface,LONG pos, + LPVOID format,LONG formatsize) +{ + TRACE("(%p,%ld,%p,%ld)\n",iface,pos,format,formatsize); + + return AVIERR_UNSUPPORTED; +} + +static HRESULT WINAPI IEditAVIStream_fnRead(IAVIStream*iface,LONG start, + LONG samples,LPVOID buffer, + LONG buffersize,LONG*bytesread, + LONG*samplesread) +{ + IAVIEditStreamImpl* const This = ((IEditAVIStreamImpl* const)iface)->pae; + PAVISTREAM stream; + DWORD streamPos, streamNr; + LONG readBytes, readSamples, count; + HRESULT hr; + + TRACE("(%p,%ld,%ld,%p,%ld,%p,%p) -- 0x%08lX\n",iface,start,samples, + buffer,buffersize,bytesread,samplesread,This->sInfo.fccType); + + /* check parameters */ + if (bytesread != NULL) + *bytesread = 0; + if (samplesread != NULL) + *samplesread = 0; + if (buffersize < 0) + return AVIERR_BADSIZE; + if ((DWORD)start < This->sInfo.dwStart || + This->sInfo.dwStart + This->sInfo.dwLength < (DWORD)start) + return AVIERR_BADPARAM; + + if (! This->bDecompress) { + /* audio like data -- sample-based */ + do { + if (samples == 0) + return AVIERR_OK; /* nothing at all or already done */ + + if (FAILED(AVIFILE_FindStreamInTable(This, start, &stream, + &streamPos, &streamNr, FALSE))) + return AVIERR_ERROR; + + /* limit to end of the stream */ + count = samples; + if (streamPos + count > EditStreamEnd(This, streamNr)) + count = EditStreamEnd(This, streamNr) - streamPos; + + hr = IAVIStream_Read(stream, streamPos, count, buffer, buffersize, + &readBytes, &readSamples); + if (FAILED(hr)) + return hr; + if (readBytes == 0 && readSamples == 0 && count != 0) + return AVIERR_FILEREAD; /* for bad stream implementations */ + + if (samplesread != NULL) + *samplesread += readSamples; + if (bytesread != NULL) + *bytesread += readBytes; + if (buffer != NULL) { + buffer = ((LPBYTE)buffer)+readBytes; + buffersize -= readBytes; + } + start += count; + samples -= count; + } while (This->sInfo.dwStart + This->sInfo.dwLength > start); + } else { + /* video like data -- frame-based */ + LPBITMAPINFOHEADER lp; + + if (samples == 0) + return AVIERR_OK; + + if (FAILED(AVIFILE_FindStreamInTable(This, start, &stream, + &streamPos, &streamNr, FALSE))) + return AVIERR_ERROR; + + lp = AVIFILE_ReadFrame(This, stream, streamPos); + if (lp == NULL) + return AVIERR_ERROR; + + if (buffer != NULL) { + /* need size of format to skip */ + if (lp->biBitCount <= 8) { + count = lp->biClrUsed > 0 ? lp->biClrUsed : 1 << lp->biBitCount; + count *= sizeof(RGBQUAD); + } else + count = 0; + count += lp->biSize; + + if (buffersize < lp->biSizeImage) + return AVIERR_BUFFERTOOSMALL; + memcpy(buffer, (LPBYTE)lp + count, lp->biSizeImage); + } + + if (bytesread != NULL) + *bytesread = lp->biSizeImage; + if (samplesread != NULL) + *samplesread = 1; + } + + return AVIERR_OK; +} + +static HRESULT WINAPI IEditAVIStream_fnWrite(IAVIStream*iface,LONG start, + LONG samples,LPVOID buffer, + LONG buffersize,DWORD flags, + LONG*sampwritten,LONG*byteswritten) +{ + TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n",iface,start,samples,buffer, + buffersize,flags,sampwritten,byteswritten); + + /* be sure return parameters have correct values */ + if (sampwritten != NULL) + *sampwritten = 0; + if (byteswritten != NULL) + *byteswritten = 0; + + return AVIERR_UNSUPPORTED; +} + +static HRESULT WINAPI IEditAVIStream_fnDelete(IAVIStream*iface,LONG start, + LONG samples) +{ + IEditAVIStreamImpl *This = (IEditAVIStreamImpl *)iface; + + TRACE("(%p,%ld,%ld)\n",iface,start,samples); + + return IAVIEditStream_Cut((IAVIEditStream*)This->pae,&start,&samples,NULL); +} + +static HRESULT WINAPI IEditAVIStream_fnReadData(IAVIStream*iface,DWORD fcc, + LPVOID lp,LONG *lpread) +{ + IAVIEditStreamImpl* const This = ((IEditAVIStreamImpl* const)iface)->pae; + DWORD n; + + TRACE("(%p,0x%08lX,%p,%p)\n",iface,fcc,lp,lpread); + + /* check parameters */ + if (lp == NULL || lpread == NULL) + return AVIERR_BADPARAM; + + /* simply ask every stream and return the first block found */ + for (n = 0; n < This->nStreams; n++) { + HRESULT hr = IAVIStream_ReadData(This->pStreams[n].pStream,fcc,lp,lpread); + + if (SUCCEEDED(hr)) + return hr; + } + + *lpread = 0; + return AVIERR_NODATA; +} + +static HRESULT WINAPI IEditAVIStream_fnWriteData(IAVIStream*iface,DWORD fcc, + LPVOID lp,LONG size) +{ + TRACE("(%p,0x%08lX,%p,%ld)\n",iface,fcc,lp,size); + + return AVIERR_UNSUPPORTED; +} + +static HRESULT WINAPI IEditAVIStream_fnSetInfo(IAVIStream*iface, + AVISTREAMINFOW*info,LONG len) +{ + IEditAVIStreamImpl *This = (IEditAVIStreamImpl *)iface; + + TRACE("(%p,%p,%ld)\n",iface,info,len); + + return IAVIEditStream_SetInfo((IAVIEditStream*)This->pae,info,len); +} + +static HRESULT WINAPI IEditStreamInternal_fnQueryInterface(IEditStreamInternal*iface,REFIID refiid,LPVOID*obj) +{ + IEditStreamInternalImpl *This = (IEditStreamInternalImpl *)iface; + + assert(This->pae != NULL); + + return IAVIEditStream_QueryInterface((IAVIEditStream*)This->pae, refiid, obj); +} + +static ULONG WINAPI IEditStreamInternal_fnAddRef(IEditStreamInternal*iface) +{ + IEditStreamInternalImpl *This = (IEditStreamInternalImpl *)iface; + + assert(This->pae != NULL); + + return IAVIEditStream_AddRef((IAVIEditStream*)This->pae); +} + +static ULONG WINAPI IEditStreamInternal_fnRelease(IEditStreamInternal*iface) +{ + IEditStreamInternalImpl *This = (IEditStreamInternalImpl *)iface; + + assert(This->pae != NULL); + + return IAVIEditStream_Release((IAVIEditStream*)This->pae); +} + +static HRESULT WINAPI IEditStreamInternal_fnGetEditStreamImpl(IEditStreamInternal*iface,LPVOID*ppimpl) +{ + IEditStreamInternalImpl *This = (IEditStreamInternalImpl *)iface; + + TRACE("(%p,%p) -> %p\n", iface, ppimpl, This->pae); + + assert(This->pae != NULL); + assert(ppimpl != NULL); + + *ppimpl = This->pae; + return AVIERR_OK; +} diff --git a/reactos/lib/avifil32/extrachunk.c b/reactos/lib/avifil32/extrachunk.c new file mode 100644 index 00000000000..d66fd3032b0 --- /dev/null +++ b/reactos/lib/avifil32/extrachunk.c @@ -0,0 +1,200 @@ +/* + * Copyright 2002 Michael Günnewig + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define COM_NO_WINDOWS_H +#include + +#include "extrachunk.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "windowsx.h" +#include "vfw.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(avifile); + +/* reads a chunk outof the extrachunk-structure */ +HRESULT ReadExtraChunk(LPEXTRACHUNKS extra,FOURCC ckid,LPVOID lpData, + LPLONG size) +{ + LPBYTE lp; + DWORD cb; + + /* pre-conditions */ + assert(extra != NULL); + assert(size != NULL); + + lp = extra->lp; + cb = extra->cb; + + if (lp != NULL) { + while (cb > 0) { + if (((FOURCC*)lp)[0] == ckid) { + /* found correct chunk */ + if (lpData != NULL && *size > 0) + memcpy(lpData, lp + 2 * sizeof(DWORD), + min(((LPDWORD)lp)[1], *(LPDWORD)size)); + + *(LPDWORD)size = ((LPDWORD)lp)[1]; + + return AVIERR_OK; + } else { + /* skip to next chunk */ + cb -= ((LPDWORD)lp)[1] + 2 * sizeof(DWORD); + lp += ((LPDWORD)lp)[1] + 2 * sizeof(DWORD); + } + } + } + + /* wanted chunk doesn't exist */ + *size = 0; + + return AVIERR_NODATA; +} + +/* writes a chunk into the extrachunk-structure */ +HRESULT WriteExtraChunk(LPEXTRACHUNKS extra,FOURCC ckid,LPVOID lpData, + LONG size) +{ + LPDWORD lp; + + /* pre-conditions */ + assert(extra != NULL); + assert(lpData != NULL); + assert(size > 0); + + if (extra->lp) + lp = (LPDWORD)GlobalReAllocPtr(extra->lp, extra->cb + size + 2 * sizeof(DWORD), GHND); + else + lp = (LPDWORD)GlobalAllocPtr(GHND, size + 2 * sizeof(DWORD)); + + if (lp == NULL) + return AVIERR_MEMORY; + + extra->lp = lp; + lp = (LPDWORD) ((LPBYTE)lp + extra->cb); + extra->cb += size + 2 * sizeof(DWORD); + + /* insert chunk-header in block */ + lp[0] = ckid; + lp[1] = size; + + if (lpData != NULL && size > 0) + memcpy(lp + 2, lpData, size); + + return AVIERR_OK; +} + +/* reads a chunk fomr the HMMIO into the extrachunk-structure */ +HRESULT ReadChunkIntoExtra(LPEXTRACHUNKS extra,HMMIO hmmio,MMCKINFO *lpck) +{ + LPDWORD lp; + DWORD cb; + + /* pre-conditions */ + assert(extra != NULL); + assert(hmmio != NULL); + assert(lpck != NULL); + + cb = lpck->cksize + 2 * sizeof(DWORD); + cb += (cb & 1); + + if (extra->lp != NULL) { + lp = (LPDWORD)GlobalReAllocPtr(extra->lp, extra->cb + cb, GHND); + } else + lp = (LPDWORD)GlobalAllocPtr(GHND, cb); + + if (lp == NULL) + return AVIERR_MEMORY; + + extra->lp = lp; + lp = (LPDWORD) ((LPBYTE)lp + extra->cb); + extra->cb += cb; + + /* insert chunk-header in block */ + lp[0] = lpck->ckid; + lp[1] = lpck->cksize; + + if (lpck->cksize > 0) { + if (mmioSeek(hmmio, lpck->dwDataOffset, SEEK_SET) == -1) + return AVIERR_FILEREAD; + if (mmioRead(hmmio, (HPSTR)&lp[2], lpck->cksize) != (LONG)lpck->cksize) + return AVIERR_FILEREAD; + } + + return AVIERR_OK; +} + +/* reads all non-junk chunks into the extrachunk-structure until it finds + * the given chunk or the optional parent-chunk is at the end */ +HRESULT FindChunkAndKeepExtras(LPEXTRACHUNKS extra,HMMIO hmmio,MMCKINFO *lpck, + MMCKINFO *lpckParent,UINT flags) +{ + FOURCC ckid; + FOURCC fccType; + HRESULT hr; + + /* pre-conditions */ + assert(extra != NULL); + assert(hmmio != NULL); + assert(lpck != NULL); + + TRACE("({%p,%lu},%p,%p,%p,0x%X)\n", extra->lp, extra->cb, hmmio, lpck, + lpckParent, flags); + + /* what chunk id and form/list type should we search? */ + if (flags & MMIO_FINDCHUNK) { + ckid = lpck->ckid; + fccType = 0; + } else if (flags & MMIO_FINDLIST) { + ckid = FOURCC_LIST; + fccType = lpck->fccType; + } else if (flags & MMIO_FINDRIFF) { + ckid = FOURCC_RIFF; + fccType = lpck->fccType; + } else + ckid = fccType = (FOURCC)-1; /* collect everything into extra! */ + + TRACE(": find ckid=0x%08lX fccType=0x%08lX\n", ckid, fccType); + + for (;;) { + hr = mmioDescend(hmmio, lpck, lpckParent, 0); + if (hr != S_OK) { + /* No extra chunks in front of desired chunk? */ + if (flags == 0 && hr == MMIOERR_CHUNKNOTFOUND) + hr = AVIERR_OK; + return hr; + } + + /* Have we found what we search for? */ + if ((lpck->ckid == ckid) && + (fccType == (FOURCC)0 || lpck->fccType == fccType)) + return AVIERR_OK; + + /* Skip padding chunks, the others put into the extrachunk-structure */ + if (lpck->ckid == ckidAVIPADDING || + lpck->ckid == mmioFOURCC('p','a','d','d')) + hr = mmioAscend(hmmio, lpck, 0); + else + hr = ReadChunkIntoExtra(extra, hmmio, lpck); + if (FAILED(hr)) + return hr; + } +} diff --git a/reactos/lib/avifil32/extrachunk.h b/reactos/lib/avifil32/extrachunk.h new file mode 100644 index 00000000000..35f4bc01239 --- /dev/null +++ b/reactos/lib/avifil32/extrachunk.h @@ -0,0 +1,55 @@ +/* + * Copyright 2002 Michael Günnewig + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __WINE_EXTRACHUNK_H +#define __WINE_EXTRACHUNK_H + +#include + +#include "windef.h" +#include "winbase.h" +#include "mmsystem.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _EXTRACHUNKS { + LPVOID lp; + DWORD cb; +} EXTRACHUNKS, *LPEXTRACHUNKS; + +/* reads a chunk outof the extrachunk-structure */ +HRESULT ReadExtraChunk(LPEXTRACHUNKS extra,FOURCC ckid,LPVOID lp,LPLONG size); + +/* writes a chunk into the extrachunk-structure */ +HRESULT WriteExtraChunk(LPEXTRACHUNKS extra,FOURCC ckid,LPVOID lp,LONG size); + +/* reads a chunk fomr the HMMIO into the extrachunk-structure */ +HRESULT ReadChunkIntoExtra(LPEXTRACHUNKS extra,HMMIO hmmio,MMCKINFO *lpck); + +/* reads all non-junk chunks into the extrachunk-structure until it finds + * the given chunk or the optional parent-chunk is at the end */ +HRESULT FindChunkAndKeepExtras(LPEXTRACHUNKS extra,HMMIO hmmio, + MMCKINFO *lpck,MMCKINFO *lpckParent,UINT flags); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/reactos/lib/avifil32/factory.c b/reactos/lib/avifil32/factory.c new file mode 100644 index 00000000000..b1f95ab4a8b --- /dev/null +++ b/reactos/lib/avifil32/factory.c @@ -0,0 +1,220 @@ +/* + * Copyright 2002 Michael Günnewig + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#define COBJMACROS +#define COM_NO_WINDOWS_H + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "winerror.h" + +#include "ole2.h" +#include "vfw.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(avifile); + +#include "initguid.h" +#include "avifile_private.h" + +HMODULE AVIFILE_hModule = NULL; + +BOOL AVIFILE_bLocked = FALSE; +UINT AVIFILE_uUseCount = 0; + +static HRESULT WINAPI IClassFactory_fnQueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj); +static ULONG WINAPI IClassFactory_fnAddRef(LPCLASSFACTORY iface); +static ULONG WINAPI IClassFactory_fnRelease(LPCLASSFACTORY iface); +static HRESULT WINAPI IClassFactory_fnCreateInstance(LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj); +static HRESULT WINAPI IClassFactory_fnLockServer(LPCLASSFACTORY iface,BOOL dolock); + +static const IClassFactoryVtbl iclassfact = { + IClassFactory_fnQueryInterface, + IClassFactory_fnAddRef, + IClassFactory_fnRelease, + IClassFactory_fnCreateInstance, + IClassFactory_fnLockServer +}; + +typedef struct +{ + /* IUnknown fields */ + const IClassFactoryVtbl *lpVtbl; + DWORD dwRef; + + CLSID clsid; +} IClassFactoryImpl; + +static HRESULT AVIFILE_CreateClassFactory(const CLSID *pclsid, const IID *riid, + LPVOID *ppv) +{ + IClassFactoryImpl *pClassFactory = NULL; + HRESULT hr; + + *ppv = NULL; + + pClassFactory = (IClassFactoryImpl*)LocalAlloc(LPTR, sizeof(*pClassFactory)); + if (pClassFactory == NULL) + return E_OUTOFMEMORY; + + pClassFactory->lpVtbl = &iclassfact; + pClassFactory->dwRef = 0; + memcpy(&pClassFactory->clsid, pclsid, sizeof(pClassFactory->clsid)); + + hr = IClassFactory_QueryInterface((IClassFactory*)pClassFactory, riid, ppv); + if (FAILED(hr)) { + LocalFree((HLOCAL)pClassFactory); + *ppv = NULL; + } + + return hr; +} + +static HRESULT WINAPI IClassFactory_fnQueryInterface(LPCLASSFACTORY iface, + REFIID riid,LPVOID *ppobj) +{ + TRACE("(%p,%p,%p)\n", iface, riid, ppobj); + + if ((IsEqualGUID(&IID_IUnknown, riid)) || + (IsEqualGUID(&IID_IClassFactory, riid))) { + *ppobj = iface; + IClassFactory_AddRef(iface); + return S_OK; + } + + return E_NOINTERFACE; +} + +static ULONG WINAPI IClassFactory_fnAddRef(LPCLASSFACTORY iface) +{ + IClassFactoryImpl *This = (IClassFactoryImpl *)iface; + + TRACE("(%p)\n", iface); + + return ++(This->dwRef); +} + +static ULONG WINAPI IClassFactory_fnRelease(LPCLASSFACTORY iface) +{ + IClassFactoryImpl *This = (IClassFactoryImpl *)iface; + + TRACE("(%p)\n", iface); + if ((--(This->dwRef)) > 0) + return This->dwRef; + + return 0; +} + +static HRESULT WINAPI IClassFactory_fnCreateInstance(LPCLASSFACTORY iface, + LPUNKNOWN pOuter, + REFIID riid,LPVOID *ppobj) +{ + IClassFactoryImpl *This = (IClassFactoryImpl *)iface; + + TRACE("(%p,%p,%s,%p)\n", iface, pOuter, debugstr_guid(riid), + ppobj); + + if (ppobj == NULL || pOuter != NULL) + return E_FAIL; + *ppobj = NULL; + + if (IsEqualGUID(&CLSID_AVIFile, &This->clsid)) + return AVIFILE_CreateAVIFile(riid,ppobj); + if (IsEqualGUID(&CLSID_ICMStream, &This->clsid)) + return AVIFILE_CreateICMStream(riid,ppobj); + if (IsEqualGUID(&CLSID_WAVFile, &This->clsid)) + return AVIFILE_CreateWAVFile(riid,ppobj); + if (IsEqualGUID(&CLSID_ACMStream, &This->clsid)) + return AVIFILE_CreateACMStream(riid,ppobj); + + return E_NOINTERFACE; +} + +static HRESULT WINAPI IClassFactory_fnLockServer(LPCLASSFACTORY iface,BOOL dolock) +{ + TRACE("(%p,%d)\n",iface,dolock); + + AVIFILE_bLocked = dolock; + + return S_OK; +} + +LPCWSTR AVIFILE_BasenameW(LPCWSTR szPath) +{ +#define SLASH(w) ((w) == '/' || (w) == '\\') + + LPCWSTR szCur; + + for (szCur = szPath + lstrlenW(szPath); + szCur > szPath && !SLASH(*szCur) && *szCur != ':';) + szCur--; + + if (szCur == szPath) + return szCur; + else + return szCur + 1; + +#undef SLASH +} + +/*********************************************************************** + * DllGetClassObject (AVIFIL32.@) + */ +HRESULT WINAPI DllGetClassObject(REFCLSID pclsid, REFIID piid, LPVOID *ppv) +{ + TRACE("(%s,%s,%p)\n", debugstr_guid(pclsid), debugstr_guid(piid), ppv); + + if (pclsid == NULL || piid == NULL || ppv == NULL) + return E_FAIL; + + return AVIFILE_CreateClassFactory(pclsid,piid,ppv); +} + +/***************************************************************************** + * DllCanUnloadNow (AVIFIL32.@) + */ +HRESULT WINAPI DllCanUnloadNow(void) +{ + return ((AVIFILE_bLocked || AVIFILE_uUseCount) ? S_FALSE : S_OK); +} + +/***************************************************************************** + * DllMain [AVIFIL32.init] + */ +BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpvReserved) +{ + TRACE("(%p,%ld,%p)\n", hInstDll, fdwReason, lpvReserved); + + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hInstDll); + AVIFILE_hModule = (HMODULE)hInstDll; + break; + case DLL_PROCESS_DETACH: + break; + }; + + return TRUE; +} diff --git a/reactos/lib/avifil32/getframe.c b/reactos/lib/avifil32/getframe.c new file mode 100644 index 00000000000..874d78fda5a --- /dev/null +++ b/reactos/lib/avifil32/getframe.c @@ -0,0 +1,527 @@ +/* + * Copyright 2002-2003 Michael Günnewig + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define COM_NO_WINDOWS_H +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winnls.h" +#include "windowsx.h" +#include "wingdi.h" +#include "winuser.h" +#include "vfw.h" + +#include "avifile_private.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(avifile); + +#ifndef DIBPTR +#define DIBPTR(lp) ((LPBYTE)(lp) + (lp)->biSize + \ + (lp)->biClrUsed * sizeof(RGBQUAD)) +#endif + +/***********************************************************************/ + +static HRESULT WINAPI IGetFrame_fnQueryInterface(IGetFrame *iface, + REFIID refiid, LPVOID *obj); +static ULONG WINAPI IGetFrame_fnAddRef(IGetFrame *iface); +static ULONG WINAPI IGetFrame_fnRelease(IGetFrame *iface); +static LPVOID WINAPI IGetFrame_fnGetFrame(IGetFrame *iface, LONG lPos); +static HRESULT WINAPI IGetFrame_fnBegin(IGetFrame *iface, LONG lStart, + LONG lEnd, LONG lRate); +static HRESULT WINAPI IGetFrame_fnEnd(IGetFrame *iface); +static HRESULT WINAPI IGetFrame_fnSetFormat(IGetFrame *iface, + LPBITMAPINFOHEADER lpbi, + LPVOID lpBits, INT x, INT y, + INT dx, INT dy); + +static const struct IGetFrameVtbl igetframeVtbl = { + IGetFrame_fnQueryInterface, + IGetFrame_fnAddRef, + IGetFrame_fnRelease, + IGetFrame_fnGetFrame, + IGetFrame_fnBegin, + IGetFrame_fnEnd, + IGetFrame_fnSetFormat +}; + +typedef struct _IGetFrameImpl { + /* IUnknown stuff */ + const IGetFrameVtbl *lpVtbl; + LONG ref; + + /* IGetFrame stuff */ + BOOL bFixedStream; + PAVISTREAM pStream; + + LPVOID lpInBuffer; + LONG cbInBuffer; + LPBITMAPINFOHEADER lpInFormat; + LONG cbInFormat; + + LONG lCurrentFrame; + LPBITMAPINFOHEADER lpOutFormat; + LPVOID lpOutBuffer; + + HIC hic; + BOOL bResize; + DWORD x; + DWORD y; + DWORD dx; + DWORD dy; + + BOOL bFormatChanges; + DWORD dwFormatChangeCount; + DWORD dwEditCount; +} IGetFrameImpl; + +/***********************************************************************/ + +static void AVIFILE_CloseCompressor(IGetFrameImpl *This) +{ + if (This->lpOutFormat != NULL && This->lpInFormat != This->lpOutFormat) { + GlobalFreePtr(This->lpOutFormat); + This->lpOutFormat = NULL; + } + if (This->lpInFormat != NULL) { + GlobalFreePtr(This->lpInFormat); + This->lpInFormat = NULL; + } + if (This->hic != NULL) { + if (This->bResize) + ICDecompressExEnd(This->hic); + else + ICDecompressEnd(This->hic); + ICClose(This->hic); + This->hic = NULL; + } +} + +PGETFRAME AVIFILE_CreateGetFrame(PAVISTREAM pStream) +{ + IGetFrameImpl *pg; + + /* check parameter */ + if (pStream == NULL) + return NULL; + + pg = (IGetFrameImpl*)LocalAlloc(LPTR, sizeof(IGetFrameImpl)); + if (pg != NULL) { + pg->lpVtbl = &igetframeVtbl; + pg->ref = 1; + pg->lCurrentFrame = -1; + pg->pStream = pStream; + IAVIStream_AddRef(pStream); + } + + return (PGETFRAME)pg; +} + +static HRESULT WINAPI IGetFrame_fnQueryInterface(IGetFrame *iface, + REFIID refiid, LPVOID *obj) +{ + IGetFrameImpl *This = (IGetFrameImpl *)iface; + + TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj); + + if (IsEqualGUID(&IID_IUnknown, refiid) || + IsEqualGUID(&IID_IGetFrame, refiid)) { + *obj = iface; + return S_OK; + } + + return OLE_E_ENUM_NOMORE; +} + +static ULONG WINAPI IGetFrame_fnAddRef(IGetFrame *iface) +{ + IGetFrameImpl *This = (IGetFrameImpl *)iface; + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p)\n", iface); + + return ref; +} + +static ULONG WINAPI IGetFrame_fnRelease(IGetFrame *iface) +{ + IGetFrameImpl *This = (IGetFrameImpl *)iface; + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p)\n", iface); + + if (!ref) { + AVIFILE_CloseCompressor(This); + if (This->pStream != NULL) { + IAVIStream_Release(This->pStream); + This->pStream = NULL; + } + + LocalFree((HLOCAL)iface); + return 0; + } + + return ref; +} + +static LPVOID WINAPI IGetFrame_fnGetFrame(IGetFrame *iface, LONG lPos) +{ + IGetFrameImpl *This = (IGetFrameImpl *)iface; + + LONG readBytes; + LONG readSamples; + + TRACE("(%p,%ld)\n", iface, lPos); + + /* We don't want negative start values! -- marks invalid buffer content */ + if (lPos < 0) + return NULL; + + /* check state */ + if (This->pStream == NULL) + return NULL; + if (This->lpInFormat == NULL) + return NULL; + + /* Could stream have changed? */ + if (! This->bFixedStream) { + AVISTREAMINFOW sInfo; + + IAVIStream_Info(This->pStream, &sInfo, sizeof(sInfo)); + + if (sInfo.dwEditCount != This->dwEditCount) { + This->dwEditCount = sInfo.dwEditCount; + This->lCurrentFrame = -1; + } + + if (sInfo.dwFormatChangeCount != This->dwFormatChangeCount) { + /* stream has changed */ + if (This->lpOutFormat != NULL) { + BITMAPINFOHEADER bi; + + memcpy(&bi, This->lpOutFormat, sizeof(bi)); + AVIFILE_CloseCompressor(This); + + if (FAILED(IGetFrame_SetFormat(iface, &bi, NULL, 0, 0, -1, -1))) { + if (FAILED(IGetFrame_SetFormat(iface, NULL, NULL, 0, 0, -1, -1))) + return NULL; + } + } else if (FAILED(IGetFrame_SetFormat(iface, NULL, NULL, 0, 0, -1, -1))) + return NULL; + } + } + + if (lPos != This->lCurrentFrame) { + LONG lNext = IAVIStream_FindSample(This->pStream,lPos,FIND_KEY|FIND_PREV); + + if (lNext == -1) + return NULL; /* frame doesn't exist */ + if (lNext <= This->lCurrentFrame && This->lCurrentFrame < lPos) + lNext = This->lCurrentFrame + 1; + + for (; lNext <= lPos; lNext++) { + /* new format for this frame? */ + if (This->bFormatChanges) { + IAVIStream_ReadFormat(This->pStream, lNext, + This->lpInFormat, &This->cbInFormat); + if (This->lpOutFormat != NULL) { + if (This->lpOutFormat->biBitCount <= 8) + ICDecompressGetPalette(This->hic, This->lpInFormat, + This->lpOutFormat); + } + } + + /* read input frame */ + while (FAILED(AVIStreamRead(This->pStream, lNext, 1, This->lpInBuffer, + This->cbInBuffer, &readBytes, &readSamples))) { + /* not enough memory for input buffer? */ + readBytes = 0; + if (FAILED(AVIStreamSampleSize(This->pStream, lNext, &readBytes))) + return NULL; /* bad thing, but bad things will happen */ + if (readBytes <= 0) { + ERR(": IAVIStream::REad doesn't return needed bytes!\n"); + return NULL; + } + + /* IAVIStream::Read failed because of other reasons not buffersize? */ + if (This->cbInBuffer >= readBytes) + break; + This->cbInBuffer = This->cbInFormat + readBytes; + This->lpInFormat = GlobalReAllocPtr(This->lpInFormat, This->cbInBuffer, 0); + if (This->lpInFormat == NULL) + return NULL; /* out of memory */ + This->lpInBuffer = (BYTE*)This->lpInFormat + This->cbInFormat; + } + + if (readSamples != 1) { + ERR(": no frames read\n"); + return NULL; + } + if (readBytes != 0) { + This->lpInFormat->biSizeImage = readBytes; + + /* nothing to decompress? */ + if (This->hic == NULL) { + This->lCurrentFrame = lPos; + return This->lpInFormat; + } + + if (This->bResize) { + ICDecompressEx(This->hic,0,This->lpInFormat,This->lpInBuffer,0,0, + This->lpInFormat->biWidth,This->lpInFormat->biHeight, + This->lpOutFormat,This->lpOutBuffer,This->x,This->y, + This->dx,This->dy); + } else { + ICDecompress(This->hic, 0, This->lpInFormat, This->lpInBuffer, + This->lpOutFormat, This->lpOutBuffer); + } + } + } /* for (lNext < lPos) */ + } /* if (This->lCurrentFrame != lPos) */ + + return (This->hic == NULL ? This->lpInFormat : This->lpOutFormat); +} + +static HRESULT WINAPI IGetFrame_fnBegin(IGetFrame *iface, LONG lStart, + LONG lEnd, LONG lRate) +{ + IGetFrameImpl *This = (IGetFrameImpl *)iface; + + TRACE("(%p,%ld,%ld,%ld)\n", iface, lStart, lEnd, lRate); + + This->bFixedStream = TRUE; + + return (IGetFrame_GetFrame(iface, lStart) ? AVIERR_OK : AVIERR_ERROR); +} + +static HRESULT WINAPI IGetFrame_fnEnd(IGetFrame *iface) +{ + IGetFrameImpl *This = (IGetFrameImpl *)iface; + + TRACE("(%p)\n", iface); + + This->bFixedStream = FALSE; + + return AVIERR_OK; +} + +static HRESULT WINAPI IGetFrame_fnSetFormat(IGetFrame *iface, + LPBITMAPINFOHEADER lpbiWanted, + LPVOID lpBits, INT x, INT y, + INT dx, INT dy) +{ + IGetFrameImpl *This = (IGetFrameImpl *)iface; + + AVISTREAMINFOW sInfo; + LPBITMAPINFOHEADER lpbi = lpbiWanted; + BOOL bBestDisplay = FALSE; + + TRACE("(%p,%p,%p,%d,%d,%d,%d)\n", iface, lpbiWanted, lpBits, + x, y, dx, dy); + + if (This->pStream == NULL) + return AVIERR_ERROR; + + if (lpbiWanted == (LPBITMAPINFOHEADER)AVIGETFRAMEF_BESTDISPLAYFMT) { + lpbi = NULL; + bBestDisplay = TRUE; + } + + IAVIStream_Info(This->pStream, &sInfo, sizeof(sInfo)); + if (sInfo.fccType != streamtypeVIDEO) + return AVIERR_UNSUPPORTED; + + This->bFormatChanges = + (sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES ? TRUE : FALSE ); + This->dwFormatChangeCount = sInfo.dwFormatChangeCount; + This->dwEditCount = sInfo.dwEditCount; + This->lCurrentFrame = -1; + + /* get input format from stream */ + if (This->lpInFormat == NULL) { + HRESULT hr; + + This->cbInBuffer = (LONG)sInfo.dwSuggestedBufferSize; + if (This->cbInBuffer == 0) + This->cbInBuffer = 1024; + + IAVIStream_ReadFormat(This->pStream, sInfo.dwStart, + NULL, &This->cbInFormat); + + This->lpInFormat = + (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, This->cbInFormat + This->cbInBuffer); + if (This->lpInFormat == NULL) { + AVIFILE_CloseCompressor(This); + return AVIERR_MEMORY; + } + + hr = IAVIStream_ReadFormat(This->pStream, sInfo.dwStart, This->lpInFormat, &This->cbInFormat); + if (FAILED(hr)) { + AVIFILE_CloseCompressor(This); + return hr; + } + + This->lpInBuffer = ((LPBYTE)This->lpInFormat) + This->cbInFormat; + } + + /* check input format */ + if (This->lpInFormat->biClrUsed == 0 && This->lpInFormat->biBitCount <= 8) + This->lpInFormat->biClrUsed = 1u << This->lpInFormat->biBitCount; + if (This->lpInFormat->biSizeImage == 0 && + This->lpInFormat->biCompression == BI_RGB) { + This->lpInFormat->biSizeImage = + DIBWIDTHBYTES(*This->lpInFormat) * This->lpInFormat->biHeight; + } + + /* only to pass through? */ + if (This->lpInFormat->biCompression == BI_RGB && lpBits == NULL) { + if (lpbi == NULL || + (lpbi->biCompression == BI_RGB && + lpbi->biWidth == This->lpInFormat->biWidth && + lpbi->biHeight == This->lpInFormat->biHeight && + lpbi->biBitCount == This->lpInFormat->biBitCount)) { + This->lpOutFormat = This->lpInFormat; + This->lpOutBuffer = DIBPTR(This->lpInFormat); + return AVIERR_OK; + } + } + + /* need memory for output format? */ + if (This->lpOutFormat == NULL) { + This->lpOutFormat = + (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, sizeof(BITMAPINFOHEADER) + + 256 * sizeof(RGBQUAD)); + if (This->lpOutFormat == NULL) { + AVIFILE_CloseCompressor(This); + return AVIERR_MEMORY; + } + } + + /* need handle to video compressor */ + if (This->hic == NULL) { + FOURCC fccHandler; + + if (This->lpInFormat->biCompression == BI_RGB) + fccHandler = comptypeDIB; + else if (This->lpInFormat->biCompression == BI_RLE8) + fccHandler = mmioFOURCC('R','L','E',' '); + else + fccHandler = sInfo.fccHandler; + + if (lpbi != NULL) { + if (lpbi->biWidth == 0) + lpbi->biWidth = This->lpInFormat->biWidth; + if (lpbi->biHeight == 0) + lpbi->biHeight = This->lpInFormat->biHeight; + } + + This->hic = ICLocate(ICTYPE_VIDEO, fccHandler, This->lpInFormat, lpbi, ICMODE_DECOMPRESS); + if (This->hic == NULL) { + AVIFILE_CloseCompressor(This); + return AVIERR_NOCOMPRESSOR; + } + } + + /* output format given? */ + if (lpbi != NULL) { + /* check the given output format ... */ + if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8) + lpbi->biClrUsed = 1u << lpbi->biBitCount; + + /* ... and remember it */ + memcpy(This->lpOutFormat, lpbi, + lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD)); + if (lpbi->biBitCount <= 8) + ICDecompressGetPalette(This->hic, This->lpInFormat, This->lpOutFormat); + + return AVIERR_OK; + } else { + if (bBestDisplay) { + ICGetDisplayFormat(This->hic, This->lpInFormat, + This->lpOutFormat, 0, dx, dy); + } else if (ICDecompressGetFormat(This->hic, This->lpInFormat, + This->lpOutFormat) < 0) { + AVIFILE_CloseCompressor(This); + return AVIERR_NOCOMPRESSOR; + } + + /* check output format */ + if (This->lpOutFormat->biClrUsed == 0 && + This->lpOutFormat->biBitCount <= 8) + This->lpOutFormat->biClrUsed = 1u << This->lpOutFormat->biBitCount; + if (This->lpOutFormat->biSizeImage == 0 && + This->lpOutFormat->biCompression == BI_RGB) { + This->lpOutFormat->biSizeImage = + DIBWIDTHBYTES(*This->lpOutFormat) * This->lpOutFormat->biHeight; + } + + if (lpBits == NULL) { + register DWORD size = This->lpOutFormat->biClrUsed * sizeof(RGBQUAD); + + size += This->lpOutFormat->biSize + This->lpOutFormat->biSizeImage; + This->lpOutFormat = + (LPBITMAPINFOHEADER)GlobalReAllocPtr(This->lpOutFormat, size, GMEM_MOVEABLE); + if (This->lpOutFormat == NULL) { + AVIFILE_CloseCompressor(This); + return AVIERR_MEMORY; + } + This->lpOutBuffer = DIBPTR(This->lpOutFormat); + } else + This->lpOutBuffer = lpBits; + + /* for user size was irrelevant */ + if (dx == -1) + dx = This->lpOutFormat->biWidth; + if (dy == -1) + dy = This->lpOutFormat->biHeight; + + /* need to resize? */ + if (x != 0 || y != 0) { + if (dy == This->lpOutFormat->biHeight && + dx == This->lpOutFormat->biWidth) + This->bResize = FALSE; + else + This->bResize = TRUE; + } + + if (This->bResize) { + This->x = x; + This->y = y; + This->dx = dx; + This->dy = dy; + + if (ICDecompressExBegin(This->hic,0,This->lpInFormat,This->lpInBuffer,0, + 0,This->lpInFormat->biWidth, + This->lpInFormat->biHeight,This->lpOutFormat, + This->lpOutBuffer, x, y, dx, dy) == ICERR_OK) + return AVIERR_OK; + } else if (ICDecompressBegin(This->hic, This->lpInFormat, + This->lpOutFormat) == ICERR_OK) + return AVIERR_OK; + + AVIFILE_CloseCompressor(This); + + return AVIERR_COMPRESSOR; + } +} + +/***********************************************************************/ diff --git a/reactos/lib/avifil32/icmstream.c b/reactos/lib/avifil32/icmstream.c new file mode 100644 index 00000000000..fd1234bbfb7 --- /dev/null +++ b/reactos/lib/avifil32/icmstream.c @@ -0,0 +1,976 @@ +/* + * Copyright 2002 Michael Günnewig + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define COM_NO_WINDOWS_H +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "winerror.h" +#include "windowsx.h" +#include "mmsystem.h" +#include "vfw.h" +#include "msacm.h" + +#include "avifile_private.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(avifile); + +#define MAX_FRAMESIZE (16 * 1024 * 1024) +#define MAX_FRAMESIZE_DIFF 512 + +/***********************************************************************/ + +static HRESULT WINAPI ICMStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj); +static ULONG WINAPI ICMStream_fnAddRef(IAVIStream*iface); +static ULONG WINAPI ICMStream_fnRelease(IAVIStream* iface); +static HRESULT WINAPI ICMStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2); +static HRESULT WINAPI ICMStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size); +static LONG WINAPI ICMStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags); +static HRESULT WINAPI ICMStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize); +static HRESULT WINAPI ICMStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize); +static HRESULT WINAPI ICMStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread); +static HRESULT WINAPI ICMStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten); +static HRESULT WINAPI ICMStream_fnDelete(IAVIStream*iface,LONG start,LONG samples); +static HRESULT WINAPI ICMStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread); +static HRESULT WINAPI ICMStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size); +static HRESULT WINAPI ICMStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen); + +static const struct IAVIStreamVtbl iicmst = { + ICMStream_fnQueryInterface, + ICMStream_fnAddRef, + ICMStream_fnRelease, + ICMStream_fnCreate, + ICMStream_fnInfo, + ICMStream_fnFindSample, + ICMStream_fnReadFormat, + ICMStream_fnSetFormat, + ICMStream_fnRead, + ICMStream_fnWrite, + ICMStream_fnDelete, + ICMStream_fnReadData, + ICMStream_fnWriteData, + ICMStream_fnSetInfo +}; + +typedef struct _IAVIStreamImpl { + /* IUnknown stuff */ + const IAVIStreamVtbl *lpVtbl; + LONG ref; + + /* IAVIStream stuff */ + PAVISTREAM pStream; + AVISTREAMINFOW sInfo; + + PGETFRAME pg; + HIC hic; + DWORD dwICMFlags; + + LONG lCurrent; + LONG lLastKey; + LONG lKeyFrameEvery; + DWORD dwLastQuality; + DWORD dwBytesPerFrame; + DWORD dwUnusedBytes; + + LPBITMAPINFOHEADER lpbiCur; /* current frame */ + LPVOID lpCur; + LPBITMAPINFOHEADER lpbiPrev; /* previous frame */ + LPVOID lpPrev; + + LPBITMAPINFOHEADER lpbiOutput; /* output format of codec */ + LONG cbOutput; + LPBITMAPINFOHEADER lpbiInput; /* input format for codec */ + LONG cbInput; +} IAVIStreamImpl; + +/***********************************************************************/ + +static HRESULT AVIFILE_EncodeFrame(IAVIStreamImpl *This, + LPBITMAPINFOHEADER lpbi, LPVOID lpBits); +static HRESULT AVIFILE_OpenGetFrame(IAVIStreamImpl *This); + +static inline void AVIFILE_Reset(IAVIStreamImpl *This) +{ + This->lCurrent = -1; + This->lLastKey = 0; + This->dwLastQuality = ICQUALITY_HIGH; + This->dwUnusedBytes = 0; +} + +HRESULT AVIFILE_CreateICMStream(REFIID riid, LPVOID *ppv) +{ + IAVIStreamImpl *pstream; + HRESULT hr; + + assert(riid != NULL && ppv != NULL); + + *ppv = NULL; + + pstream = (IAVIStreamImpl*)LocalAlloc(LPTR, sizeof(IAVIStreamImpl)); + if (pstream == NULL) + return AVIERR_MEMORY; + + pstream->lpVtbl = &iicmst; + AVIFILE_Reset(pstream); + + hr = IAVIStream_QueryInterface((IAVIStream*)pstream, riid, ppv); + if (FAILED(hr)) + LocalFree((HLOCAL)pstream); + + return hr; +} + +static HRESULT WINAPI ICMStream_fnQueryInterface(IAVIStream *iface, + REFIID refiid, LPVOID *obj) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj); + + if (IsEqualGUID(&IID_IUnknown, refiid) || + IsEqualGUID(&IID_IAVIStream, refiid)) { + *obj = This; + IAVIStream_AddRef(iface); + + return S_OK; + } + + return OLE_E_ENUM_NOMORE; +} + +static ULONG WINAPI ICMStream_fnAddRef(IAVIStream *iface) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) -> %ld\n", iface, ref); + + /* also add reference to the nested stream */ + if (This->pStream != NULL) + IAVIStream_AddRef(This->pStream); + + return ref; +} + +static ULONG WINAPI ICMStream_fnRelease(IAVIStream* iface) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) -> %ld\n", iface, ref); + + if (ref == 0) { + /* destruct */ + if (This->pg != NULL) { + AVIStreamGetFrameClose(This->pg); + This->pg = NULL; + } + if (This->pStream != NULL) { + IAVIStream_Release(This->pStream); + This->pStream = NULL; + } + if (This->hic != NULL) { + if (This->lpbiPrev != NULL) { + ICDecompressEnd(This->hic); + GlobalFreePtr(This->lpbiPrev); + This->lpbiPrev = NULL; + This->lpPrev = NULL; + } + ICCompressEnd(This->hic); + This->hic = NULL; + } + if (This->lpbiCur != NULL) { + GlobalFreePtr(This->lpbiCur); + This->lpbiCur = NULL; + This->lpCur = NULL; + } + if (This->lpbiOutput != NULL) { + GlobalFreePtr(This->lpbiOutput); + This->lpbiOutput = NULL; + This->cbOutput = 0; + } + if (This->lpbiInput != NULL) { + GlobalFreePtr(This->lpbiInput); + This->lpbiInput = NULL; + This->cbInput = 0; + } + + LocalFree((HLOCAL)This); + + return 0; + } + + /* also release reference to the nested stream */ + if (This->pStream != NULL) + IAVIStream_Release(This->pStream); + + return ref; +} + +/* lParam1: PAVISTREAM + * lParam2: LPAVICOMPRESSOPTIONS + */ +static HRESULT WINAPI ICMStream_fnCreate(IAVIStream *iface, LPARAM lParam1, + LPARAM lParam2) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + ICINFO icinfo; + ICCOMPRESSFRAMES icFrames; + LPAVICOMPRESSOPTIONS pco = (LPAVICOMPRESSOPTIONS)lParam2; + + TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2); + + /* check parameter */ + if ((LPVOID)lParam1 == NULL) + return AVIERR_BADPARAM; + + /* get infos from stream */ + IAVIStream_Info((PAVISTREAM)lParam1, &This->sInfo, sizeof(This->sInfo)); + if (This->sInfo.fccType != streamtypeVIDEO) + return AVIERR_ERROR; /* error in registry or AVIMakeCompressedStream */ + + /* add reference to the stream */ + This->pStream = (PAVISTREAM)lParam1; + IAVIStream_AddRef(This->pStream); + + AVIFILE_Reset(This); + + if (pco != NULL && pco->fccHandler != comptypeDIB) { + /* we should compress */ + This->sInfo.fccHandler = pco->fccHandler; + + This->hic = ICOpen(ICTYPE_VIDEO, pco->fccHandler, ICMODE_COMPRESS); + if (This->hic == NULL) + return AVIERR_NOCOMPRESSOR; + + /* restore saved state of codec */ + if (pco->cbParms > 0 && pco->lpParms != NULL) { + ICSetState(This->hic, pco->lpParms, pco->cbParms); + } + + /* set quality -- resolve default quality */ + This->sInfo.dwQuality = pco->dwQuality; + if (pco->dwQuality == ICQUALITY_DEFAULT) + This->sInfo.dwQuality = ICGetDefaultQuality(This->hic); + + /* get capabilities of codec */ + ICGetInfo(This->hic, &icinfo, sizeof(icinfo)); + This->dwICMFlags = icinfo.dwFlags; + + /* use keyframes? */ + if ((pco->dwFlags & AVICOMPRESSF_KEYFRAMES) && + (icinfo.dwFlags & (VIDCF_TEMPORAL|VIDCF_FASTTEMPORALC))) { + This->lKeyFrameEvery = pco->dwKeyFrameEvery; + } else + This->lKeyFrameEvery = 1; + + /* use datarate? */ + if ((pco->dwFlags & AVICOMPRESSF_DATARATE)) { + /* Do we have a chance to reduce size to desired one? */ + if ((icinfo.dwFlags & (VIDCF_CRUNCH|VIDCF_QUALITY)) == 0) + return AVIERR_NOCOMPRESSOR; + + assert(This->sInfo.dwRate != 0); + + This->dwBytesPerFrame = MulDiv(pco->dwBytesPerSecond, + This->sInfo.dwScale, This->sInfo.dwRate); + } else { + pco->dwBytesPerSecond = 0; + This->dwBytesPerFrame = 0; + } + + if (icinfo.dwFlags & VIDCF_COMPRESSFRAMES) { + memset(&icFrames, 0, sizeof(icFrames)); + icFrames.lpbiOutput = This->lpbiOutput; + icFrames.lpbiInput = This->lpbiInput; + icFrames.lFrameCount = This->sInfo.dwLength; + icFrames.lQuality = This->sInfo.dwQuality; + icFrames.lDataRate = pco->dwBytesPerSecond; + icFrames.lKeyRate = This->lKeyFrameEvery; + icFrames.dwRate = This->sInfo.dwRate; + icFrames.dwScale = This->sInfo.dwScale; + ICSendMessage(This->hic, ICM_COMPRESS_FRAMES_INFO, + (LPARAM)&icFrames, (LPARAM)sizeof(icFrames)); + } + } else + This->sInfo.fccHandler = comptypeDIB; + + return AVIERR_OK; +} + +static HRESULT WINAPI ICMStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi, + LONG size) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + TRACE("(%p,%p,%ld)\n", iface, psi, size); + + if (psi == NULL) + return AVIERR_BADPARAM; + if (size < 0) + return AVIERR_BADSIZE; + + memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo))); + + if ((DWORD)size < sizeof(This->sInfo)) + return AVIERR_BUFFERTOOSMALL; + return AVIERR_OK; +} + +static LONG WINAPI ICMStream_fnFindSample(IAVIStream *iface, LONG pos, + LONG flags) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + TRACE("(%p,%ld,0x%08lX)\n",iface,pos,flags); + + if (flags & FIND_FROM_START) { + pos = This->sInfo.dwStart; + flags &= ~(FIND_FROM_START|FIND_PREV); + flags |= FIND_NEXT; + } + + if (flags & FIND_RET) + WARN(": FIND_RET flags will be ignored!\n"); + + if (flags & FIND_KEY) { + if (This->hic == NULL) + return pos; /* we decompress so every frame is a keyframe */ + + if (flags & FIND_PREV) { + /* need to read old or new frames? */ + if (This->lLastKey <= pos || pos < This->lCurrent) + IAVIStream_Read(iface, pos, 1, NULL, 0, NULL, NULL); + + return This->lLastKey; + } + } else if (flags & FIND_ANY) { + return pos; /* We really don't know, reread is to expensive, so guess. */ + } else if (flags & FIND_FORMAT) { + if (flags & FIND_PREV) + return 0; + } + + return -1; +} + +static HRESULT WINAPI ICMStream_fnReadFormat(IAVIStream *iface, LONG pos, + LPVOID format, LONG *formatsize) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + LPBITMAPINFOHEADER lpbi; + HRESULT hr; + + TRACE("(%p,%ld,%p,%p)\n", iface, pos, format, formatsize); + + if (formatsize == NULL) + return AVIERR_BADPARAM; + + if (This->pg == NULL) { + hr = AVIFILE_OpenGetFrame(This); + + if (FAILED(hr)) + return hr; + } + + lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, pos); + if (lpbi == NULL) + return AVIERR_MEMORY; + + if (This->hic == NULL) { + LONG size = lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD); + + if (size > 0) { + if (This->sInfo.dwSuggestedBufferSize < lpbi->biSizeImage) + This->sInfo.dwSuggestedBufferSize = lpbi->biSizeImage; + + This->cbOutput = size; + if (format != NULL) { + if (This->lpbiOutput != NULL) + memcpy(format, This->lpbiOutput, min(*formatsize, This->cbOutput)); + else + memcpy(format, lpbi, min(*formatsize, size)); + } + } + } else if (format != NULL) + memcpy(format, This->lpbiOutput, min(*formatsize, This->cbOutput)); + + if (*formatsize < This->cbOutput) + hr = AVIERR_BUFFERTOOSMALL; + else + hr = AVIERR_OK; + + *formatsize = This->cbOutput; + return hr; +} + +static HRESULT WINAPI ICMStream_fnSetFormat(IAVIStream *iface, LONG pos, + LPVOID format, LONG formatsize) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + TRACE("(%p,%ld,%p,%ld)\n", iface, pos, format, formatsize); + + /* check parameters */ + if (format == NULL || formatsize <= 0) + return AVIERR_BADPARAM; + + /* We can only accept RGB data for writing */ + if (((LPBITMAPINFOHEADER)format)->biCompression != BI_RGB) { + WARN(": need RGB data as input\n"); + return AVIERR_UNSUPPORTED; + } + + /* Input format already known? + * Changing of palette is supported, but be quiet if it's the same */ + if (This->lpbiInput != NULL) { + if (This->cbInput != formatsize) + return AVIERR_UNSUPPORTED; + + if (memcmp(format, This->lpbiInput, formatsize) == 0) + return AVIERR_OK; + } + + /* Does the nested stream support writing? */ + if ((This->sInfo.dwCaps & AVIFILECAPS_CANWRITE) == 0) + return AVIERR_READONLY; + + /* check if frame is already written */ + if (This->sInfo.dwLength + This->sInfo.dwStart > pos) + return AVIERR_UNSUPPORTED; + + /* check if we should compress */ + if (This->sInfo.fccHandler == 0 || + This->sInfo.fccHandler == mmioFOURCC('N','O','N','E')) + This->sInfo.fccHandler = comptypeDIB; + + /* only pass through? */ + if (This->sInfo.fccHandler == comptypeDIB) + return IAVIStream_SetFormat(This->pStream, pos, format, formatsize); + + /* initial format setting? */ + if (This->lpbiInput == NULL) { + ULONG size; + + assert(This->hic != NULL); + + /* get memory for input format */ + This->lpbiInput = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, formatsize); + if (This->lpbiInput == NULL) + return AVIERR_MEMORY; + This->cbInput = formatsize; + memcpy(This->lpbiInput, format, formatsize); + + /* get output format */ + size = ICCompressGetFormatSize(This->hic, This->lpbiInput); + if (size < sizeof(BITMAPINFOHEADER)) + return AVIERR_COMPRESSOR; + This->lpbiOutput = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size); + if (This->lpbiOutput == NULL) + return AVIERR_MEMORY; + This->cbOutput = size; + if (ICCompressGetFormat(This->hic,This->lpbiInput,This->lpbiOutput) < S_OK) + return AVIERR_COMPRESSOR; + + /* update AVISTREAMINFO structure */ + This->sInfo.rcFrame.right = + This->sInfo.rcFrame.left + This->lpbiOutput->biWidth; + This->sInfo.rcFrame.bottom = + This->sInfo.rcFrame.top + This->lpbiOutput->biHeight; + + /* prepare codec for compression */ + if (ICCompressBegin(This->hic, This->lpbiInput, This->lpbiOutput) != S_OK) + return AVIERR_COMPRESSOR; + + /* allocate memory for compressed frame */ + size = ICCompressGetSize(This->hic, This->lpbiInput, This->lpbiOutput); + This->lpbiCur = + (LPBITMAPINFOHEADER)GlobalAllocPtr(GMEM_MOVEABLE, This->cbOutput + size); + if (This->lpbiCur == NULL) + return AVIERR_MEMORY; + memcpy(This->lpbiCur, This->lpbiOutput, This->cbOutput); + This->lpCur = DIBPTR(This->lpbiCur); + + /* allocate memory for last frame if needed */ + if (This->lKeyFrameEvery != 1 && + (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) { + size = ICDecompressGetFormatSize(This->hic, This->lpbiOutput); + This->lpbiPrev = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size); + if (This->lpbiPrev == NULL) + return AVIERR_MEMORY; + if (ICDecompressGetFormat(This->hic, This->lpbiOutput, This->lpbiPrev) < S_OK) + return AVIERR_COMPRESSOR; + + if (This->lpbiPrev->biSizeImage == 0) { + This->lpbiPrev->biSizeImage = + DIBWIDTHBYTES(*This->lpbiPrev) * This->lpbiPrev->biHeight; + } + + /* get memory for format and picture */ + size += This->lpbiPrev->biSizeImage; + This->lpbiPrev = + (LPBITMAPINFOHEADER)GlobalReAllocPtr(This->lpbiPrev,size,GMEM_MOVEABLE); + if (This->lpbiPrev == NULL) + return AVIERR_MEMORY; + This->lpPrev = DIBPTR(This->lpbiPrev); + + /* prepare codec also for decompression */ + if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK) + return AVIERR_COMPRESSOR; + } + } else { + /* format change -- check that's only the palette */ + LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)format; + + if (lpbi->biSize != This->lpbiInput->biSize || + lpbi->biWidth != This->lpbiInput->biWidth || + lpbi->biHeight != This->lpbiInput->biHeight || + lpbi->biBitCount != This->lpbiInput->biBitCount || + lpbi->biPlanes != This->lpbiInput->biPlanes || + lpbi->biCompression != This->lpbiInput->biCompression || + lpbi->biClrUsed != This->lpbiInput->biClrUsed) + return AVIERR_UNSUPPORTED; + + /* get new output format */ + if (ICCompressGetFormat(This->hic, lpbi, This->lpbiOutput) < S_OK) + return AVIERR_BADFORMAT; + + /* restart compression */ + ICCompressEnd(This->hic); + if (ICCompressBegin(This->hic, lpbi, This->lpbiOutput) != S_OK) + return AVIERR_COMPRESSOR; + + /* check if we need to restart decompresion also */ + if (This->lKeyFrameEvery != 1 && + (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) { + ICDecompressEnd(This->hic); + if (ICDecompressGetFormat(This->hic,This->lpbiOutput,This->lpbiPrev) < S_OK) + return AVIERR_COMPRESSOR; + if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK) + return AVIERR_COMPRESSOR; + } + } + + /* tell nested stream the new format */ + return IAVIStream_SetFormat(This->pStream, pos, + This->lpbiOutput, This->cbOutput); +} + +static HRESULT WINAPI ICMStream_fnRead(IAVIStream *iface, LONG start, + LONG samples, LPVOID buffer, + LONG buffersize, LPLONG bytesread, + LPLONG samplesread) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + LPBITMAPINFOHEADER lpbi; + + TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface, start, samples, buffer, + buffersize, bytesread, samplesread); + + /* clear return parameters if given */ + if (bytesread != NULL) + *bytesread = 0; + if (samplesread != NULL) + *samplesread = 0; + + if (samples == 0) + return AVIERR_OK; + + /* check parameters */ + if (samples != 1 && (bytesread == NULL && samplesread == NULL)) + return AVIERR_BADPARAM; + if (samples == -1) /* read as much as we could */ + samples = 1; + + if (This->pg == NULL) { + HRESULT hr = AVIFILE_OpenGetFrame(This); + + if (FAILED(hr)) + return hr; + } + + /* compress or decompress? */ + if (This->hic == NULL) { + /* decompress */ + lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, start); + if (lpbi == NULL) + return AVIERR_MEMORY; + + if (buffer != NULL && buffersize > 0) { + /* check buffersize */ + if (buffersize < lpbi->biSizeImage) + return AVIERR_BUFFERTOOSMALL; + + memcpy(buffer, DIBPTR(lpbi), lpbi->biSizeImage); + } + + /* fill out return parameters if given */ + if (bytesread != NULL) + *bytesread = lpbi->biSizeImage; + } else { + /* compress */ + if (This->lCurrent > start) + AVIFILE_Reset(This); + + while (start > This->lCurrent) { + HRESULT hr; + + lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, ++This->lCurrent); + if (lpbi == NULL) { + AVIFILE_Reset(This); + return AVIERR_MEMORY; + } + + hr = AVIFILE_EncodeFrame(This, lpbi, DIBPTR(lpbi)); + if (FAILED(hr)) { + AVIFILE_Reset(This); + return hr; + } + } + + if (buffer != NULL && buffersize > 0) { + /* check buffersize */ + if (This->lpbiCur->biSizeImage > buffersize) + return AVIERR_BUFFERTOOSMALL; + + memcpy(buffer, This->lpCur, This->lpbiCur->biSizeImage); + } + + /* fill out return parameters if given */ + if (bytesread != NULL) + *bytesread = This->lpbiCur->biSizeImage; + } + + /* fill out return parameters if given */ + if (samplesread != NULL) + *samplesread = 1; + + return AVIERR_OK; +} + +static HRESULT WINAPI ICMStream_fnWrite(IAVIStream *iface, LONG start, + LONG samples, LPVOID buffer, + LONG buffersize, DWORD flags, + LPLONG sampwritten, + LPLONG byteswritten) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + HRESULT hr; + + TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n", iface, start, samples, + buffer, buffersize, flags, sampwritten, byteswritten); + + /* clear return parameters if given */ + if (sampwritten != NULL) + *sampwritten = 0; + if (byteswritten != NULL) + *byteswritten = 0; + + /* check parameters */ + if (buffer == NULL && (buffersize > 0 || samples > 0)) + return AVIERR_BADPARAM; + + if (This->sInfo.fccHandler == comptypeDIB) { + /* only pass through */ + flags |= AVIIF_KEYFRAME; + + return IAVIStream_Write(This->pStream, start, samples, buffer, buffersize, + flags, sampwritten, byteswritten); + } else { + /* compress data before writing to pStream */ + if (samples != 1 && (sampwritten == NULL && byteswritten == NULL)) + return AVIERR_UNSUPPORTED; + + This->lCurrent = start; + hr = AVIFILE_EncodeFrame(This, This->lpbiInput, buffer); + if (FAILED(hr)) + return hr; + + if (This->lLastKey == start) + flags |= AVIIF_KEYFRAME; + + return IAVIStream_Write(This->pStream, start, samples, This->lpCur, + This->lpbiCur->biSizeImage, flags, byteswritten, + sampwritten); + } +} + +static HRESULT WINAPI ICMStream_fnDelete(IAVIStream *iface, LONG start, + LONG samples) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + TRACE("(%p,%ld,%ld)\n", iface, start, samples); + + return IAVIStream_Delete(This->pStream, start, samples); +} + +static HRESULT WINAPI ICMStream_fnReadData(IAVIStream *iface, DWORD fcc, + LPVOID lp, LPLONG lpread) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + TRACE("(%p,0x%08lX,%p,%p)\n", iface, fcc, lp, lpread); + + assert(This->pStream != NULL); + + return IAVIStream_ReadData(This->pStream, fcc, lp, lpread); +} + +static HRESULT WINAPI ICMStream_fnWriteData(IAVIStream *iface, DWORD fcc, + LPVOID lp, LONG size) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + TRACE("(%p,0x%08lx,%p,%ld)\n", iface, fcc, lp, size); + + assert(This->pStream != NULL); + + return IAVIStream_WriteData(This->pStream, fcc, lp, size); +} + +static HRESULT WINAPI ICMStream_fnSetInfo(IAVIStream *iface, + LPAVISTREAMINFOW info, LONG infolen) +{ + FIXME("(%p,%p,%ld): stub\n", iface, info, infolen); + + return E_FAIL; +} + +/***********************************************************************/ + +static HRESULT AVIFILE_EncodeFrame(IAVIStreamImpl *This, + LPBITMAPINFOHEADER lpbi, LPVOID lpBits) +{ + DWORD dwMinQual, dwMaxQual, dwCurQual; + DWORD dwRequest; + DWORD icmFlags = 0; + DWORD idxFlags = 0; + BOOL bDecreasedQual = FALSE; + BOOL doSizeCheck; + BOOL noPrev; + + /* make lKeyFrameEvery and at start a keyframe */ + if ((This->lKeyFrameEvery != 0 && + (This->lCurrent - This->lLastKey) >= This->lKeyFrameEvery) || + This->lCurrent == This->sInfo.dwStart) { + idxFlags = AVIIF_KEYFRAME; + icmFlags = ICCOMPRESS_KEYFRAME; + } + + if (This->lKeyFrameEvery != 0) { + if (This->lCurrent == This->sInfo.dwStart) { + if (idxFlags & AVIIF_KEYFRAME) { + /* for keyframes allow to consume all unused bytes */ + dwRequest = This->dwBytesPerFrame + This->dwUnusedBytes; + This->dwUnusedBytes = 0; + } else { + /* for non-keyframes only allow something of the unused bytes to be consumed */ + DWORD tmp1 = 0; + DWORD tmp2; + + if (This->dwBytesPerFrame >= This->dwUnusedBytes) + tmp1 = This->dwBytesPerFrame / This->lKeyFrameEvery; + tmp2 = (This->dwUnusedBytes + tmp1) / This->lKeyFrameEvery; + + dwRequest = This->dwBytesPerFrame - tmp1 + tmp2; + This->dwUnusedBytes -= tmp2; + } + } else + dwRequest = MAX_FRAMESIZE; + } else { + /* only one keyframe at start desired */ + if (This->lCurrent == This->sInfo.dwStart) { + dwRequest = This->dwBytesPerFrame + This->dwUnusedBytes; + This->dwUnusedBytes = 0; + } else + dwRequest = MAX_FRAMESIZE; + } + + /* must we check for framesize to gain requested + * datarate or could we trust codec? */ + doSizeCheck = (dwRequest != 0 && ((This->dwICMFlags & (VIDCF_CRUNCH|VIDCF_QUALITY)) == 0)); + + dwMaxQual = dwCurQual = This->sInfo.dwQuality; + dwMinQual = ICQUALITY_LOW; + + noPrev = TRUE; + if ((icmFlags & ICCOMPRESS_KEYFRAME) == 0 && + (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) + noPrev = FALSE; + + do { + DWORD idxCkid = 0; + HRESULT hr; + + hr = ICCompress(This->hic,icmFlags,This->lpbiCur,This->lpCur,lpbi,lpBits, + &idxCkid, &idxFlags, This->lCurrent, dwRequest, dwCurQual, + noPrev ? NULL:This->lpbiPrev, noPrev ? NULL:This->lpPrev); + if (hr == ICERR_NEWPALETTE) { + FIXME(": codec has changed palette -- unhandled!\n"); + } else if (hr != ICERR_OK) + return AVIERR_COMPRESSOR; + + /* need to check for framesize */ + if (! doSizeCheck) + break; + + if (dwRequest >= This->lpbiCur->biSizeImage) { + /* frame is smaller -- try to maximize quality */ + if (dwMaxQual - dwCurQual > 10) { + DWORD tmp = dwRequest / 8; + + if (tmp < MAX_FRAMESIZE_DIFF) + tmp = MAX_FRAMESIZE_DIFF; + + if (tmp < dwRequest - This->lpbiCur->biSizeImage && bDecreasedQual) { + tmp = dwCurQual; + dwCurQual = (dwMinQual + dwMaxQual) / 2; + dwMinQual = tmp; + continue; + } + } else + break; + } else if (dwMaxQual - dwMinQual <= 1) { + break; + } else { + dwMaxQual = dwCurQual; + + if (bDecreasedQual || dwCurQual == This->dwLastQuality) + dwCurQual = (dwMinQual + dwMaxQual) / 2; + else + FIXME(": no new quality computed min=%lu cur=%lu max=%lu last=%lu\n", + dwMinQual, dwCurQual, dwMaxQual, This->dwLastQuality); + + bDecreasedQual = TRUE; + } + } while (TRUE); + + /* remember some values */ + This->dwLastQuality = dwCurQual; + This->dwUnusedBytes = dwRequest - This->lpbiCur->biSizeImage; + if (icmFlags & ICCOMPRESS_KEYFRAME) + This->lLastKey = This->lCurrent; + + /* Does we manage previous frame? */ + if (This->lpPrev != NULL && This->lKeyFrameEvery != 1) + ICDecompress(This->hic, 0, This->lpbiCur, This->lpCur, + This->lpbiPrev, This->lpPrev); + + return AVIERR_OK; +} + +static HRESULT AVIFILE_OpenGetFrame(IAVIStreamImpl *This) +{ + LPBITMAPINFOHEADER lpbi; + DWORD size; + + /* pre-conditions */ + assert(This != NULL); + assert(This->pStream != NULL); + assert(This->pg == NULL); + + This->pg = AVIStreamGetFrameOpen(This->pStream, NULL); + if (This->pg == NULL) + return AVIERR_ERROR; + + /* When we only decompress this is enough */ + if (This->sInfo.fccHandler == comptypeDIB) + return AVIERR_OK; + + assert(This->hic != NULL); + assert(This->lpbiOutput == NULL); + + /* get input format */ + lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, This->sInfo.dwStart); + if (lpbi == NULL) + return AVIERR_MEMORY; + + /* get memory for output format */ + size = ICCompressGetFormatSize(This->hic, lpbi); + if ((LONG)size < (LONG)sizeof(BITMAPINFOHEADER)) + return AVIERR_COMPRESSOR; + This->lpbiOutput = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size); + if (This->lpbiOutput == NULL) + return AVIERR_MEMORY; + This->cbOutput = size; + + if (ICCompressGetFormat(This->hic, lpbi, This->lpbiOutput) < S_OK) + return AVIERR_BADFORMAT; + + /* update AVISTREAMINFO structure */ + This->sInfo.rcFrame.right = + This->sInfo.rcFrame.left + This->lpbiOutput->biWidth; + This->sInfo.rcFrame.bottom = + This->sInfo.rcFrame.top + This->lpbiOutput->biHeight; + This->sInfo.dwSuggestedBufferSize = + ICCompressGetSize(This->hic, lpbi, This->lpbiOutput); + + /* prepare codec for compression */ + if (ICCompressBegin(This->hic, lpbi, This->lpbiOutput) != S_OK) + return AVIERR_COMPRESSOR; + + /* allocate memory for current frame */ + size += This->sInfo.dwSuggestedBufferSize; + This->lpbiCur = (LPBITMAPINFOHEADER)GlobalAllocPtr(GMEM_MOVEABLE, size); + if (This->lpbiCur == NULL) + return AVIERR_MEMORY; + memcpy(This->lpbiCur, This->lpbiOutput, This->cbOutput); + This->lpCur = DIBPTR(This->lpbiCur); + + /* allocate memory for last frame if needed */ + if (This->lKeyFrameEvery != 1 && + (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) { + size = ICDecompressGetFormatSize(This->hic, This->lpbiOutput); + This->lpbiPrev = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size); + if (This->lpbiPrev == NULL) + return AVIERR_MEMORY; + if (ICDecompressGetFormat(This->hic, This->lpbiOutput, This->lpbiPrev) < S_OK) + return AVIERR_COMPRESSOR; + + if (This->lpbiPrev->biSizeImage == 0) { + This->lpbiPrev->biSizeImage = + DIBWIDTHBYTES(*This->lpbiPrev) * This->lpbiPrev->biHeight; + } + + /* get memory for format and picture */ + size += This->lpbiPrev->biSizeImage; + This->lpbiPrev = + (LPBITMAPINFOHEADER)GlobalReAllocPtr(This->lpbiPrev,size,GMEM_MOVEABLE); + if (This->lpbiPrev == NULL) + return AVIERR_MEMORY; + This->lpPrev = DIBPTR(This->lpbiPrev); + + /* prepare codec also for decompression */ + if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK) + return AVIERR_COMPRESSOR; + } + + return AVIERR_OK; +} diff --git a/reactos/lib/avifil32/regsvr.c b/reactos/lib/avifil32/regsvr.c new file mode 100644 index 00000000000..bc3d3aa4553 --- /dev/null +++ b/reactos/lib/avifil32/regsvr.c @@ -0,0 +1,580 @@ +/* + * self-registerable dll functions for avifile.dll and avifil32.dll + * + * Copyright (C) 2003 John K. Hohm + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define COM_NO_WINDOWS_H +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winreg.h" +#include "winerror.h" + +#include "mmsystem.h" +#include "vfw.h" +#include "avifile_private.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(avifile); + +/* + * Near the bottom of this file are the exported DllRegisterServer and + * DllUnregisterServer, which make all this worthwhile. + */ + +/*********************************************************************** + * interface for self-registering + */ +struct regsvr_interface +{ + IID const *iid; /* NULL for end of list */ + LPCSTR name; /* can be NULL to omit */ + IID const *base_iid; /* can be NULL to omit */ + int num_methods; /* can be <0 to omit */ + CLSID const *ps_clsid; /* can be NULL to omit */ + CLSID const *ps_clsid32; /* can be NULL to omit */ +}; + +static HRESULT register_interfaces(struct regsvr_interface const *list); +static HRESULT unregister_interfaces(struct regsvr_interface const *list); + +struct regsvr_coclass +{ + CLSID const *clsid; /* NULL for end of list */ + LPCSTR name; /* can be NULL to omit */ + LPCSTR ips; /* can be NULL to omit */ + LPCSTR ips32; /* can be NULL to omit */ + LPCSTR ips32_tmodel; /* can be NULL to omit */ + LPCSTR progid; /* can be NULL to omit */ + LPCSTR viprogid; /* can be NULL to omit */ + LPCSTR progid_extra; /* can be NULL to omit */ +}; + +static HRESULT register_coclasses(struct regsvr_coclass const *list); +static HRESULT unregister_coclasses(struct regsvr_coclass const *list); + +/*********************************************************************** + * static string constants + */ +static WCHAR const interface_keyname[10] = { + 'I', 'n', 't', 'e', 'r', 'f', 'a', 'c', 'e', 0 }; +static WCHAR const base_ifa_keyname[14] = { + 'B', 'a', 's', 'e', 'I', 'n', 't', 'e', 'r', 'f', 'a', 'c', + 'e', 0 }; +static WCHAR const num_methods_keyname[11] = { + 'N', 'u', 'm', 'M', 'e', 't', 'h', 'o', 'd', 's', 0 }; +static WCHAR const ps_clsid_keyname[15] = { + 'P', 'r', 'o', 'x', 'y', 'S', 't', 'u', 'b', 'C', 'l', 's', + 'i', 'd', 0 }; +static WCHAR const ps_clsid32_keyname[17] = { + 'P', 'r', 'o', 'x', 'y', 'S', 't', 'u', 'b', 'C', 'l', 's', + 'i', 'd', '3', '2', 0 }; +static WCHAR const clsid_keyname[6] = { + 'C', 'L', 'S', 'I', 'D', 0 }; +static WCHAR const curver_keyname[7] = { + 'C', 'u', 'r', 'V', 'e', 'r', 0 }; +static WCHAR const ips_keyname[13] = { + 'I', 'n', 'P', 'r', 'o', 'c', 'S', 'e', 'r', 'v', 'e', 'r', + 0 }; +static WCHAR const ips32_keyname[15] = { + 'I', 'n', 'P', 'r', 'o', 'c', 'S', 'e', 'r', 'v', 'e', 'r', + '3', '2', 0 }; +static WCHAR const progid_keyname[7] = { + 'P', 'r', 'o', 'g', 'I', 'D', 0 }; +static WCHAR const viprogid_keyname[25] = { + 'V', 'e', 'r', 's', 'i', 'o', 'n', 'I', 'n', 'd', 'e', 'p', + 'e', 'n', 'd', 'e', 'n', 't', 'P', 'r', 'o', 'g', 'I', 'D', + 0 }; +static char const tmodel_valuename[] = "ThreadingModel"; + +/*********************************************************************** + * static helper functions + */ +static LONG register_key_guid(HKEY base, WCHAR const *name, GUID const *guid); +static LONG register_key_defvalueW(HKEY base, WCHAR const *name, + WCHAR const *value); +static LONG register_key_defvalueA(HKEY base, WCHAR const *name, + char const *value); +static LONG register_progid(WCHAR const *clsid, + char const *progid, char const *curver_progid, + char const *name, char const *extra); +static LONG recursive_delete_key(HKEY key); +static LONG recursive_delete_keyA(HKEY base, char const *name); +static LONG recursive_delete_keyW(HKEY base, WCHAR const *name); + +/*********************************************************************** + * register_interfaces + */ +static HRESULT register_interfaces(struct regsvr_interface const *list) +{ + LONG res = ERROR_SUCCESS; + HKEY interface_key; + + res = RegCreateKeyExW(HKEY_CLASSES_ROOT, interface_keyname, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, &interface_key, NULL); + if (res != ERROR_SUCCESS) goto error_return; + + for (; res == ERROR_SUCCESS && list->iid; ++list) { + WCHAR buf[39]; + HKEY iid_key; + + StringFromGUID2(list->iid, buf, 39); + res = RegCreateKeyExW(interface_key, buf, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, &iid_key, NULL); + if (res != ERROR_SUCCESS) goto error_close_interface_key; + + if (list->name) { + res = RegSetValueExA(iid_key, NULL, 0, REG_SZ, + (CONST BYTE*)(list->name), + strlen(list->name) + 1); + if (res != ERROR_SUCCESS) goto error_close_iid_key; + } + + if (list->base_iid) { + register_key_guid(iid_key, base_ifa_keyname, list->base_iid); + if (res != ERROR_SUCCESS) goto error_close_iid_key; + } + + if (0 <= list->num_methods) { + static WCHAR const fmt[3] = { '%', 'd', 0 }; + HKEY key; + + res = RegCreateKeyExW(iid_key, num_methods_keyname, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, &key, NULL); + if (res != ERROR_SUCCESS) goto error_close_iid_key; + + wsprintfW(buf, fmt, list->num_methods); + res = RegSetValueExW(key, NULL, 0, REG_SZ, + (CONST BYTE*)buf, + (lstrlenW(buf) + 1) * sizeof(WCHAR)); + RegCloseKey(key); + + if (res != ERROR_SUCCESS) goto error_close_iid_key; + } + + if (list->ps_clsid) { + register_key_guid(iid_key, ps_clsid_keyname, list->ps_clsid); + if (res != ERROR_SUCCESS) goto error_close_iid_key; + } + + if (list->ps_clsid32) { + register_key_guid(iid_key, ps_clsid32_keyname, list->ps_clsid32); + if (res != ERROR_SUCCESS) goto error_close_iid_key; + } + + error_close_iid_key: + RegCloseKey(iid_key); + } + +error_close_interface_key: + RegCloseKey(interface_key); +error_return: + return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK; +} + +/*********************************************************************** + * unregister_interfaces + */ +static HRESULT unregister_interfaces(struct regsvr_interface const *list) +{ + LONG res = ERROR_SUCCESS; + HKEY interface_key; + + res = RegOpenKeyExW(HKEY_CLASSES_ROOT, interface_keyname, 0, + KEY_READ | KEY_WRITE, &interface_key); + if (res == ERROR_FILE_NOT_FOUND) return S_OK; + if (res != ERROR_SUCCESS) goto error_return; + + for (; res == ERROR_SUCCESS && list->iid; ++list) { + WCHAR buf[39]; + + StringFromGUID2(list->iid, buf, 39); + res = recursive_delete_keyW(interface_key, buf); + } + + RegCloseKey(interface_key); +error_return: + return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK; +} + +/*********************************************************************** + * register_coclasses + */ +static HRESULT register_coclasses(struct regsvr_coclass const *list) +{ + LONG res = ERROR_SUCCESS; + HKEY coclass_key; + + res = RegCreateKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, &coclass_key, NULL); + if (res != ERROR_SUCCESS) goto error_return; + + for (; res == ERROR_SUCCESS && list->clsid; ++list) { + WCHAR buf[39]; + HKEY clsid_key; + + StringFromGUID2(list->clsid, buf, 39); + res = RegCreateKeyExW(coclass_key, buf, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, &clsid_key, NULL); + if (res != ERROR_SUCCESS) goto error_close_coclass_key; + + if (list->name) { + res = RegSetValueExA(clsid_key, NULL, 0, REG_SZ, + (CONST BYTE*)(list->name), + strlen(list->name) + 1); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + } + + if (list->ips) { + res = register_key_defvalueA(clsid_key, ips_keyname, list->ips); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + } + + if (list->ips32) { + HKEY ips32_key; + + res = RegCreateKeyExW(clsid_key, ips32_keyname, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, + &ips32_key, NULL); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + + res = RegSetValueExA(ips32_key, NULL, 0, REG_SZ, + (CONST BYTE*)list->ips32, + lstrlenA(list->ips32) + 1); + if (res == ERROR_SUCCESS && list->ips32_tmodel) + res = RegSetValueExA(ips32_key, tmodel_valuename, 0, REG_SZ, + (CONST BYTE*)list->ips32_tmodel, + strlen(list->ips32_tmodel) + 1); + RegCloseKey(ips32_key); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + } + + if (list->progid) { + res = register_key_defvalueA(clsid_key, progid_keyname, + list->progid); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + + res = register_progid(buf, list->progid, NULL, + list->name, list->progid_extra); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + } + + if (list->viprogid) { + res = register_key_defvalueA(clsid_key, viprogid_keyname, + list->viprogid); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + + res = register_progid(buf, list->viprogid, list->progid, + list->name, list->progid_extra); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + } + + error_close_clsid_key: + RegCloseKey(clsid_key); + } + +error_close_coclass_key: + RegCloseKey(coclass_key); +error_return: + return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK; +} + +/*********************************************************************** + * unregister_coclasses + */ +static HRESULT unregister_coclasses(struct regsvr_coclass const *list) +{ + LONG res = ERROR_SUCCESS; + HKEY coclass_key; + + res = RegOpenKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0, + KEY_READ | KEY_WRITE, &coclass_key); + if (res == ERROR_FILE_NOT_FOUND) return S_OK; + if (res != ERROR_SUCCESS) goto error_return; + + for (; res == ERROR_SUCCESS && list->clsid; ++list) { + WCHAR buf[39]; + + StringFromGUID2(list->clsid, buf, 39); + res = recursive_delete_keyW(coclass_key, buf); + if (res != ERROR_SUCCESS) goto error_close_coclass_key; + + if (list->progid) { + res = recursive_delete_keyA(HKEY_CLASSES_ROOT, list->progid); + if (res != ERROR_SUCCESS) goto error_close_coclass_key; + } + + if (list->viprogid) { + res = recursive_delete_keyA(HKEY_CLASSES_ROOT, list->viprogid); + if (res != ERROR_SUCCESS) goto error_close_coclass_key; + } + } + +error_close_coclass_key: + RegCloseKey(coclass_key); +error_return: + return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK; +} + +/*********************************************************************** + * regsvr_key_guid + */ +static LONG register_key_guid(HKEY base, WCHAR const *name, GUID const *guid) +{ + WCHAR buf[39]; + + StringFromGUID2(guid, buf, 39); + return register_key_defvalueW(base, name, buf); +} + +/*********************************************************************** + * regsvr_key_defvalueW + */ +static LONG register_key_defvalueW( + HKEY base, + WCHAR const *name, + WCHAR const *value) +{ + LONG res; + HKEY key; + + res = RegCreateKeyExW(base, name, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, &key, NULL); + if (res != ERROR_SUCCESS) return res; + res = RegSetValueExW(key, NULL, 0, REG_SZ, (CONST BYTE*)value, + (lstrlenW(value) + 1) * sizeof(WCHAR)); + RegCloseKey(key); + return res; +} + +/*********************************************************************** + * regsvr_key_defvalueA + */ +static LONG register_key_defvalueA( + HKEY base, + WCHAR const *name, + char const *value) +{ + LONG res; + HKEY key; + + res = RegCreateKeyExW(base, name, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, &key, NULL); + if (res != ERROR_SUCCESS) return res; + res = RegSetValueExA(key, NULL, 0, REG_SZ, (CONST BYTE*)value, + lstrlenA(value) + 1); + RegCloseKey(key); + return res; +} + +/*********************************************************************** + * regsvr_progid + */ +static LONG register_progid( + WCHAR const *clsid, + char const *progid, + char const *curver_progid, + char const *name, + char const *extra) +{ + LONG res; + HKEY progid_key; + + res = RegCreateKeyExA(HKEY_CLASSES_ROOT, progid, 0, + NULL, 0, KEY_READ | KEY_WRITE, NULL, + &progid_key, NULL); + if (res != ERROR_SUCCESS) return res; + + if (name) { + res = RegSetValueExA(progid_key, NULL, 0, REG_SZ, + (CONST BYTE*)name, strlen(name) + 1); + if (res != ERROR_SUCCESS) goto error_close_progid_key; + } + + if (clsid) { + res = register_key_defvalueW(progid_key, clsid_keyname, clsid); + if (res != ERROR_SUCCESS) goto error_close_progid_key; + } + + if (curver_progid) { + res = register_key_defvalueA(progid_key, curver_keyname, + curver_progid); + if (res != ERROR_SUCCESS) goto error_close_progid_key; + } + + if (extra) { + HKEY extra_key; + + res = RegCreateKeyExA(progid_key, extra, 0, + NULL, 0, KEY_READ | KEY_WRITE, NULL, + &extra_key, NULL); + if (res == ERROR_SUCCESS) + RegCloseKey(extra_key); + } + +error_close_progid_key: + RegCloseKey(progid_key); + return res; +} + +/*********************************************************************** + * recursive_delete_key + */ +static LONG recursive_delete_key(HKEY key) +{ + LONG res; + WCHAR subkey_name[MAX_PATH]; + DWORD cName; + HKEY subkey; + + for (;;) { + cName = sizeof(subkey_name) / sizeof(WCHAR); + res = RegEnumKeyExW(key, 0, subkey_name, &cName, + NULL, NULL, NULL, NULL); + if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) { + res = ERROR_SUCCESS; /* presumably we're done enumerating */ + break; + } + res = RegOpenKeyExW(key, subkey_name, 0, + KEY_READ | KEY_WRITE, &subkey); + if (res == ERROR_FILE_NOT_FOUND) continue; + if (res != ERROR_SUCCESS) break; + + res = recursive_delete_key(subkey); + RegCloseKey(subkey); + if (res != ERROR_SUCCESS) break; + } + + if (res == ERROR_SUCCESS) res = RegDeleteKeyW(key, 0); + return res; +} + +/*********************************************************************** + * recursive_delete_keyA + */ +static LONG recursive_delete_keyA(HKEY base, char const *name) +{ + LONG res; + HKEY key; + + res = RegOpenKeyExA(base, name, 0, KEY_READ | KEY_WRITE, &key); + if (res == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS; + if (res != ERROR_SUCCESS) return res; + res = recursive_delete_key(key); + RegCloseKey(key); + return res; +} + +/*********************************************************************** + * recursive_delete_keyW + */ +static LONG recursive_delete_keyW(HKEY base, WCHAR const *name) +{ + LONG res; + HKEY key; + + res = RegOpenKeyExW(base, name, 0, KEY_READ | KEY_WRITE, &key); + if (res == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS; + if (res != ERROR_SUCCESS) return res; + res = recursive_delete_key(key); + RegCloseKey(key); + return res; +} + +/*********************************************************************** + * coclass list + */ +static GUID const CLSID_AVIProxy = { + 0x0002000D, 0x0000, 0x0000, {0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46} }; + +static struct regsvr_coclass const coclass_list[] = { + { &CLSID_AVIFile, + "Microsoft AVI Files", + "avifile.dll", + "avifil32.dll", + "Apartment" + }, + { &CLSID_ICMStream, + "AVI Compressed Stream", + "avifile.dll", + "avifil32.dll", + "Apartment" + }, + { &CLSID_WAVFile, + "Microsoft Wave File", + "avifile.dll", + "avifil32.dll", + "Apartment" + }, + { &CLSID_AVIProxy, + "IAVIStream & IAVIFile Proxy", + "avifile.dll", + "avifil32.dll", + "Apartment" + }, + { &CLSID_ACMStream, + "ACM Compressed Audio Stream", + "avifile.dll", + "avifil32.dll", + "Apartment" + }, + { NULL } /* list terminator */ +}; + +/*********************************************************************** + * interface list + */ + +static struct regsvr_interface const interface_list[] = { + { NULL } /* list terminator */ +}; + +/*********************************************************************** + * DllRegisterServer (AVIFILE.@) + */ +HRESULT WINAPI DllRegisterServer(void) +{ + HRESULT hr; + + TRACE("\n"); + + hr = register_coclasses(coclass_list); + if (SUCCEEDED(hr)) + hr = register_interfaces(interface_list); + return hr; +} + +/*********************************************************************** + * DllUnregisterServer (AVIFILE.@) + */ +HRESULT WINAPI DllUnregisterServer(void) +{ + HRESULT hr; + + TRACE("\n"); + + hr = unregister_coclasses(coclass_list); + if (SUCCEEDED(hr)) + hr = unregister_interfaces(interface_list); + return hr; +} diff --git a/reactos/lib/avifil32/rsrc.rc b/reactos/lib/avifil32/rsrc.rc new file mode 100644 index 00000000000..5f74b33c387 --- /dev/null +++ b/reactos/lib/avifil32/rsrc.rc @@ -0,0 +1,53 @@ +/* + * Top level resource file for avifil32.dll + * + * Copyright 2002 Michael Günnewig + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winver.h" +#include "avifile_private.h" + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL + +#define WINE_OLESELFREGISTER +#define WINE_FILEDESCRIPTION_STR "Wine AVI file support library" +#define WINE_FILENAME_STR "avifil32.dll" +#define WINE_FILEVERSION 4,0,3,1998 +#define WINE_FILEVERSION_STR "4.03.1998" + +#include "wine/wine_common_ver.rc" + +/* + * Everything specific to any language goes + * in one of the specific files. + */ +#include "avifile_Cs.rc" +#include "avifile_De.rc" +#include "avifile_En.rc" +#include "avifile_Es.rc" +#include "avifile_Fr.rc" +#include "avifile_It.rc" +#include "avifile_Ja.rc" +#include "avifile_Nl.rc" +#include "avifile_No.rc" +#include "avifile_Pl.rc" +#include "avifile_Pt.rc" +#include "avifile_Ru.rc" +#include "avifile_Si.rc" diff --git a/reactos/lib/avifil32/tmpfile.c b/reactos/lib/avifil32/tmpfile.c new file mode 100644 index 00000000000..5968103c867 --- /dev/null +++ b/reactos/lib/avifil32/tmpfile.c @@ -0,0 +1,290 @@ +/* + * Copyright 2003 Michael Günnewig + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define COM_NO_WINDOWS_H +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "winerror.h" +#include "windowsx.h" +#include "mmsystem.h" +#include "vfw.h" + +#include "avifile_private.h" +#include "extrachunk.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(avifile); + +/***********************************************************************/ + +static HRESULT WINAPI ITmpFile_fnQueryInterface(IAVIFile* iface,REFIID refiid,LPVOID *obj); +static ULONG WINAPI ITmpFile_fnAddRef(IAVIFile* iface); +static ULONG WINAPI ITmpFile_fnRelease(IAVIFile* iface); +static HRESULT WINAPI ITmpFile_fnInfo(IAVIFile*iface,AVIFILEINFOW*afi,LONG size); +static HRESULT WINAPI ITmpFile_fnGetStream(IAVIFile*iface,PAVISTREAM*avis,DWORD fccType,LONG lParam); +static HRESULT WINAPI ITmpFile_fnCreateStream(IAVIFile*iface,PAVISTREAM*avis,AVISTREAMINFOW*asi); +static HRESULT WINAPI ITmpFile_fnWriteData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG size); +static HRESULT WINAPI ITmpFile_fnReadData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG *size); +static HRESULT WINAPI ITmpFile_fnEndRecord(IAVIFile*iface); +static HRESULT WINAPI ITmpFile_fnDeleteStream(IAVIFile*iface,DWORD fccType,LONG lParam); + +static const struct IAVIFileVtbl itmpft = { + ITmpFile_fnQueryInterface, + ITmpFile_fnAddRef, + ITmpFile_fnRelease, + ITmpFile_fnInfo, + ITmpFile_fnGetStream, + ITmpFile_fnCreateStream, + ITmpFile_fnWriteData, + ITmpFile_fnReadData, + ITmpFile_fnEndRecord, + ITmpFile_fnDeleteStream +}; + +typedef struct _ITmpFileImpl { + /* IUnknown stuff */ + const IAVIFileVtbl *lpVtbl; + LONG ref; + + /* IAVIFile stuff */ + AVIFILEINFOW fInfo; + PAVISTREAM *ppStreams; +} ITmpFileImpl; + +PAVIFILE AVIFILE_CreateAVITempFile(int nStreams, PAVISTREAM *ppStreams) { + ITmpFileImpl *tmpFile; + int i; + + tmpFile = LocalAlloc(LPTR, sizeof(ITmpFileImpl)); + if (tmpFile == NULL) + return NULL; + + tmpFile->lpVtbl = &itmpft; + tmpFile->ref = 1; + memset(&tmpFile->fInfo, 0, sizeof(tmpFile->fInfo)); + + tmpFile->fInfo.dwStreams = nStreams; + tmpFile->ppStreams = LocalAlloc(LPTR, nStreams * sizeof(PAVISTREAM)); + if (tmpFile->ppStreams == NULL) { + LocalFree((HLOCAL)tmpFile); + return NULL; + } + + for (i = 0; i < nStreams; i++) { + AVISTREAMINFOW sInfo; + + tmpFile->ppStreams[i] = ppStreams[i]; + + AVIStreamAddRef(ppStreams[i]); + AVIStreamInfoW(ppStreams[i], &sInfo, sizeof(sInfo)); + if (i == 0) { + tmpFile->fInfo.dwScale = sInfo.dwScale; + tmpFile->fInfo.dwRate = sInfo.dwRate; + if (!sInfo.dwScale || !sInfo.dwRate) { + tmpFile->fInfo.dwScale = 1; + tmpFile->fInfo.dwRate = 100; + } + } + + if (tmpFile->fInfo.dwSuggestedBufferSize < sInfo.dwSuggestedBufferSize) + tmpFile->fInfo.dwSuggestedBufferSize = sInfo.dwSuggestedBufferSize; + + { + register DWORD tmp; + + tmp = MulDiv(AVIStreamSampleToTime(ppStreams[i], sInfo.dwLength), \ + tmpFile->fInfo.dwScale, tmpFile->fInfo.dwRate * 1000); + if (tmpFile->fInfo.dwLength < tmp) + tmpFile->fInfo.dwLength = tmp; + + tmp = sInfo.rcFrame.right - sInfo.rcFrame.left; + if (tmpFile->fInfo.dwWidth < tmp) + tmpFile->fInfo.dwWidth = tmp; + tmp = sInfo.rcFrame.bottom - sInfo.rcFrame.top; + if (tmpFile->fInfo.dwHeight < tmp) + tmpFile->fInfo.dwHeight = tmp; + } + } + + return (PAVIFILE)tmpFile; +} + +static HRESULT WINAPI ITmpFile_fnQueryInterface(IAVIFile *iface, REFIID refiid, + LPVOID *obj) +{ + ITmpFileImpl *This = (ITmpFileImpl *)iface; + + TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj); + + if (IsEqualGUID(&IID_IUnknown, refiid) || + IsEqualGUID(&IID_IAVIFile, refiid)) { + *obj = iface; + IAVIFile_AddRef(iface); + + return S_OK; + } + + return OLE_E_ENUM_NOMORE; +} + +static ULONG WINAPI ITmpFile_fnAddRef(IAVIFile *iface) +{ + ITmpFileImpl *This = (ITmpFileImpl *)iface; + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) -> %ld\n", iface, ref); + + return ref; +} + +static ULONG WINAPI ITmpFile_fnRelease(IAVIFile *iface) +{ + ITmpFileImpl *This = (ITmpFileImpl *)iface; + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) -> %ld\n", iface, ref); + + if (!ref) { + unsigned int i; + + for (i = 0; i < This->fInfo.dwStreams; i++) { + if (This->ppStreams[i] != NULL) { + AVIStreamRelease(This->ppStreams[i]); + + This->ppStreams[i] = NULL; + } + } + + LocalFree((HLOCAL)This); + return 0; + } + + return ref; +} + +static HRESULT WINAPI ITmpFile_fnInfo(IAVIFile *iface, + AVIFILEINFOW *afi, LONG size) +{ + ITmpFileImpl *This = (ITmpFileImpl *)iface; + + TRACE("(%p,%p,%ld)\n",iface,afi,size); + + if (afi == NULL) + return AVIERR_BADPARAM; + if (size < 0) + return AVIERR_BADSIZE; + + memcpy(afi, &This->fInfo, min((DWORD)size, sizeof(This->fInfo))); + + if ((DWORD)size < sizeof(This->fInfo)) + return AVIERR_BUFFERTOOSMALL; + return AVIERR_OK; +} + +static HRESULT WINAPI ITmpFile_fnGetStream(IAVIFile *iface, PAVISTREAM *avis, + DWORD fccType, LONG lParam) +{ + ITmpFileImpl *This = (ITmpFileImpl *)iface; + + ULONG nStream = (ULONG)-1; + + TRACE("(%p,%p,0x%08lX,%ld)\n", iface, avis, fccType, lParam); + + if (avis == NULL || lParam < 0) + return AVIERR_BADPARAM; + + if (fccType != streamtypeANY) { + /* search the number of the specified stream */ + ULONG i; + + for (i = 0; i < This->fInfo.dwStreams; i++) { + AVISTREAMINFOW sInfo; + HRESULT hr; + + hr = AVIStreamInfoW(This->ppStreams[i], &sInfo, sizeof(sInfo)); + if (FAILED(hr)) + return hr; + + if (sInfo.fccType == fccType) { + if (lParam == 0) { + nStream = i; + break; + } else + lParam--; + } + } + } else + nStream = lParam; + + /* Does the requested stream exist ? */ + if (nStream < This->fInfo.dwStreams && This->ppStreams[nStream] != NULL) { + *avis = This->ppStreams[nStream]; + AVIStreamAddRef(*avis); + + return AVIERR_OK; + } + + /* Sorry, but the specified stream doesn't exist */ + return AVIERR_NODATA; +} + +static HRESULT WINAPI ITmpFile_fnCreateStream(IAVIFile *iface,PAVISTREAM *avis, + AVISTREAMINFOW *asi) +{ + TRACE("(%p,%p,%p)\n",iface,avis,asi); + + return AVIERR_UNSUPPORTED; +} + +static HRESULT WINAPI ITmpFile_fnWriteData(IAVIFile *iface, DWORD ckid, + LPVOID lpData, LONG size) +{ + TRACE("(%p,0x%08lX,%p,%ld)\n", iface, ckid, lpData, size); + + return AVIERR_UNSUPPORTED; +} + +static HRESULT WINAPI ITmpFile_fnReadData(IAVIFile *iface, DWORD ckid, + LPVOID lpData, LONG *size) +{ + TRACE("(%p,0x%08lX,%p,%p)\n", iface, ckid, lpData, size); + + return AVIERR_UNSUPPORTED; +} + +static HRESULT WINAPI ITmpFile_fnEndRecord(IAVIFile *iface) +{ + TRACE("(%p)\n",iface); + + return AVIERR_OK; +} + +static HRESULT WINAPI ITmpFile_fnDeleteStream(IAVIFile *iface, DWORD fccType, + LONG lParam) +{ + TRACE("(%p,0x%08lX,%ld)\n", iface, fccType, lParam); + + return AVIERR_UNSUPPORTED; +} diff --git a/reactos/lib/avifil32/wavfile.c b/reactos/lib/avifil32/wavfile.c new file mode 100644 index 00000000000..c3c2e23a580 --- /dev/null +++ b/reactos/lib/avifil32/wavfile.c @@ -0,0 +1,1356 @@ +/* + * Copyright 2002 Michael Günnewig + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define COM_NO_WINDOWS_H +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "winerror.h" +#include "windowsx.h" +#include "mmsystem.h" +#include "vfw.h" +#include "msacm.h" + +#include "avifile_private.h" +#include "extrachunk.h" + +#include "wine/unicode.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(avifile); + +/***********************************************************************/ + +#define formtypeWAVE mmioFOURCC('W','A','V','E') +#define ckidWAVEFORMAT mmioFOURCC('f','m','t',' ') +#define ckidWAVEFACT mmioFOURCC('f','a','c','t') +#define ckidWAVEDATA mmioFOURCC('d','a','t','a') + +/***********************************************************************/ + +#define ENDIAN_SWAPWORD(x) ((((x) >> 8) & 0xFF) | (((x) & 0xFF) << 8)) +#define ENDIAN_SWAPDWORD(x) (ENDIAN_SWAPWORD((x >> 16) & 0xFFFF) | \ + ENDIAN_SWAPWORD(x & 0xFFFF) << 16) + +#ifdef WORDS_BIGENDIAN +#define BE2H_WORD(x) (x) +#define BE2H_DWORD(x) (x) +#define LE2H_WORD(x) ENDIAN_SWAPWORD(x) +#define LE2H_DWORD(x) ENDIAN_SWAPDWORD(x) +#else +#define BE2H_WORD(x) ENDIAN_SWAPWORD(x) +#define BE2H_DWORD(x) ENDIAN_SWAPDWORD(x) +#define LE2H_WORD(x) (x) +#define LE2H_DWORD(x) (x) +#endif + +typedef struct { + FOURCC fccType; + DWORD offset; + DWORD size; + INT encoding; + DWORD sampleRate; + DWORD channels; +} SUNAUDIOHEADER; + +#define AU_ENCODING_ULAW_8 1 +#define AU_ENCODING_PCM_8 2 +#define AU_ENCODING_PCM_16 3 +#define AU_ENCODING_PCM_24 4 +#define AU_ENCODING_PCM_32 5 +#define AU_ENCODING_FLOAT 6 +#define AU_ENCODING_DOUBLE 7 +#define AU_ENCODING_ADPCM_G721_32 23 +#define AU_ENCODING_ADPCM_G722 24 +#define AU_ENCODING_ADPCM_G723_24 25 +#define AU_ENCODING_ADPCM_G723_5 26 +#define AU_ENCODING_ALAW_8 27 + +/***********************************************************************/ + +static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile* iface,REFIID refiid,LPVOID *obj); +static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile* iface); +static ULONG WINAPI IAVIFile_fnRelease(IAVIFile* iface); +static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile*iface,AVIFILEINFOW*afi,LONG size); +static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile*iface,PAVISTREAM*avis,DWORD fccType,LONG lParam); +static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile*iface,PAVISTREAM*avis,AVISTREAMINFOW*asi); +static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG size); +static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG *size); +static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile*iface); +static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile*iface,DWORD fccType,LONG lParam); + +static const struct IAVIFileVtbl iwavft = { + IAVIFile_fnQueryInterface, + IAVIFile_fnAddRef, + IAVIFile_fnRelease, + IAVIFile_fnInfo, + IAVIFile_fnGetStream, + IAVIFile_fnCreateStream, + IAVIFile_fnWriteData, + IAVIFile_fnReadData, + IAVIFile_fnEndRecord, + IAVIFile_fnDeleteStream +}; + +static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile*iface,REFIID refiid,LPVOID*obj); +static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile*iface); +static ULONG WINAPI IPersistFile_fnRelease(IPersistFile*iface); +static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile*iface,CLSID*pClassID); +static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile*iface); +static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile*iface,LPCOLESTR pszFileName,DWORD dwMode); +static HRESULT WINAPI IPersistFile_fnSave(IPersistFile*iface,LPCOLESTR pszFileName,BOOL fRemember); +static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile*iface,LPCOLESTR pszFileName); +static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile*iface,LPOLESTR*ppszFileName); + +static const struct IPersistFileVtbl iwavpft = { + IPersistFile_fnQueryInterface, + IPersistFile_fnAddRef, + IPersistFile_fnRelease, + IPersistFile_fnGetClassID, + IPersistFile_fnIsDirty, + IPersistFile_fnLoad, + IPersistFile_fnSave, + IPersistFile_fnSaveCompleted, + IPersistFile_fnGetCurFile +}; + +static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj); +static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream*iface); +static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface); +static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2); +static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size); +static LONG WINAPI IAVIStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags); +static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize); +static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize); +static HRESULT WINAPI IAVIStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread); +static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten); +static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream*iface,LONG start,LONG samples); +static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread); +static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size); +static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen); + +static const struct IAVIStreamVtbl iwavst = { + IAVIStream_fnQueryInterface, + IAVIStream_fnAddRef, + IAVIStream_fnRelease, + IAVIStream_fnCreate, + IAVIStream_fnInfo, + IAVIStream_fnFindSample, + IAVIStream_fnReadFormat, + IAVIStream_fnSetFormat, + IAVIStream_fnRead, + IAVIStream_fnWrite, + IAVIStream_fnDelete, + IAVIStream_fnReadData, + IAVIStream_fnWriteData, + IAVIStream_fnSetInfo +}; + +typedef struct _IAVIFileImpl IAVIFileImpl; + +typedef struct _IPersistFileImpl { + /* IUnknown stuff */ + const IPersistFileVtbl *lpVtbl; + + /* IPersistFile stuff */ + IAVIFileImpl *paf; +} IPersistFileImpl; + +typedef struct _IAVIStreamImpl { + /* IUnknown stuff */ + const IAVIStreamVtbl *lpVtbl; + + /* IAVIStream stuff */ + IAVIFileImpl *paf; +} IAVIStreamImpl; + +struct _IAVIFileImpl { + /* IUnknown stuff */ + const IAVIFileVtbl *lpVtbl; + LONG ref; + + /* IAVIFile, IAVIStream stuff... */ + IPersistFileImpl iPersistFile; + IAVIStreamImpl iAVIStream; + + AVIFILEINFOW fInfo; + AVISTREAMINFOW sInfo; + + LPWAVEFORMATEX lpFormat; + LONG cbFormat; + + MMCKINFO ckData; + + EXTRACHUNKS extra; + + /* IPersistFile stuff ... */ + HMMIO hmmio; + LPWSTR szFileName; + UINT uMode; + BOOL fDirty; +}; + +/***********************************************************************/ + +static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This); +static HRESULT AVIFILE_LoadSunFile(IAVIFileImpl *This); +static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This); + +HRESULT AVIFILE_CreateWAVFile(REFIID riid, LPVOID *ppv) +{ + IAVIFileImpl *pfile; + HRESULT hr; + + assert(riid != NULL && ppv != NULL); + + *ppv = NULL; + + pfile = (IAVIFileImpl*)LocalAlloc(LPTR, sizeof(IAVIFileImpl)); + if (pfile == NULL) + return AVIERR_MEMORY; + + pfile->lpVtbl = &iwavft; + pfile->iPersistFile.lpVtbl = &iwavpft; + pfile->iAVIStream.lpVtbl = &iwavst; + pfile->ref = 0; + pfile->iPersistFile.paf = pfile; + pfile->iAVIStream.paf = pfile; + + hr = IAVIFile_QueryInterface((IAVIFile*)pfile, riid, ppv); + if (FAILED(hr)) + LocalFree((HLOCAL)pfile); + + return hr; +} + +static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID refiid, + LPVOID *obj) +{ + IAVIFileImpl *This = (IAVIFileImpl *)iface; + + TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj); + + if (IsEqualGUID(&IID_IUnknown, refiid) || + IsEqualGUID(&IID_IAVIFile, refiid)) { + *obj = iface; + return S_OK; + } else if (This->fInfo.dwStreams == 1 && + IsEqualGUID(&IID_IAVIStream, refiid)) { + *obj = &This->iAVIStream; + return S_OK; + } else if (IsEqualGUID(&IID_IPersistFile, refiid)) { + *obj = &This->iPersistFile; + return S_OK; + } + + return OLE_E_ENUM_NOMORE; +} + +static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface) +{ + IAVIFileImpl *This = (IAVIFileImpl *)iface; + + TRACE("(%p)\n",iface); + + return InterlockedIncrement(&This->ref); +} + +static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface) +{ + IAVIFileImpl *This = (IAVIFileImpl *)iface; + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p)\n",iface); + + if (!ref) { + if (This->fDirty) { + /* need to write headers to file */ + AVIFILE_SaveFile(This); + } + + if (This->lpFormat != NULL) { + GlobalFreePtr(This->lpFormat); + This->lpFormat = NULL; + This->cbFormat = 0; + } + if (This->extra.lp != NULL) { + GlobalFreePtr(This->extra.lp); + This->extra.lp = NULL; + This->extra.cb = 0; + } + if (This->szFileName != NULL) { + LocalFree((HLOCAL)This->szFileName); + This->szFileName = NULL; + } + if (This->hmmio != NULL) { + mmioClose(This->hmmio, 0); + This->hmmio = NULL; + } + + LocalFree((HLOCAL)This); + return 0; + } + return ref; +} + +static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, LPAVIFILEINFOW afi, + LONG size) +{ + IAVIFileImpl *This = (IAVIFileImpl *)iface; + + TRACE("(%p,%p,%ld)\n",iface,afi,size); + + if (afi == NULL) + return AVIERR_BADPARAM; + if (size < 0) + return AVIERR_BADSIZE; + + /* update file info */ + This->fInfo.dwFlags = 0; + This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; + if (This->lpFormat != NULL) { + assert(This->sInfo.dwScale != 0); + + This->fInfo.dwStreams = 1; + This->fInfo.dwScale = This->sInfo.dwScale; + This->fInfo.dwRate = This->sInfo.dwRate; + This->fInfo.dwLength = This->sInfo.dwLength; + This->fInfo.dwSuggestedBufferSize = This->ckData.cksize; + This->fInfo.dwMaxBytesPerSec = + MulDiv(This->sInfo.dwSampleSize,This->sInfo.dwRate,This->sInfo.dwScale); + } + + memcpy(afi, &This->fInfo, min((DWORD)size, sizeof(This->fInfo))); + + if ((DWORD)size < sizeof(This->fInfo)) + return AVIERR_BUFFERTOOSMALL; + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, PAVISTREAM *avis, + DWORD fccType, LONG lParam) +{ + IAVIFileImpl *This = (IAVIFileImpl *)iface; + + TRACE("(%p,%p,0x%08lX,%ld)\n", iface, avis, fccType, lParam); + + /* check parameter */ + if (avis == NULL) + return AVIERR_BADPARAM; + + *avis = NULL; + + /* Does our stream exists? */ + if (lParam != 0 || This->fInfo.dwStreams == 0) + return AVIERR_NODATA; + if (fccType != 0 && fccType != streamtypeAUDIO) + return AVIERR_NODATA; + + *avis = (PAVISTREAM)&This->iAVIStream; + IAVIFile_AddRef(iface); + + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface,PAVISTREAM *avis, + LPAVISTREAMINFOW asi) +{ + IAVIFileImpl *This = (IAVIFileImpl *)iface; + + TRACE("(%p,%p,%p)\n", iface, avis, asi); + + /* check parameters */ + if (avis == NULL || asi == NULL) + return AVIERR_BADPARAM; + + *avis = NULL; + + /* We only support one audio stream */ + if (This->fInfo.dwStreams != 0 || This->lpFormat != NULL) + return AVIERR_UNSUPPORTED; + if (asi->fccType != streamtypeAUDIO) + return AVIERR_UNSUPPORTED; + + /* Does the user have write permission? */ + if ((This->uMode & MMIO_RWMODE) == 0) + return AVIERR_READONLY; + + This->cbFormat = 0; + This->lpFormat = NULL; + + memcpy(&This->sInfo, asi, sizeof(This->sInfo)); + + /* make sure streaminfo if okay for us */ + This->sInfo.fccHandler = 0; + This->sInfo.dwFlags = 0; + This->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; + This->sInfo.dwStart = 0; + This->sInfo.dwInitialFrames = 0; + This->sInfo.dwFormatChangeCount = 0; + memset(&This->sInfo.rcFrame, 0, sizeof(This->sInfo.rcFrame)); + + This->fInfo.dwStreams = 1; + This->fInfo.dwScale = This->sInfo.dwScale; + This->fInfo.dwRate = This->sInfo.dwRate; + This->fInfo.dwLength = This->sInfo.dwLength; + + This->ckData.dwDataOffset = 0; + This->ckData.cksize = 0; + + *avis = (PAVISTREAM)&This->iAVIStream; + IAVIFile_AddRef(iface); + + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid, + LPVOID lpData, LONG size) +{ + IAVIFileImpl *This = (IAVIFileImpl *)iface; + + TRACE("(%p,0x%08lX,%p,%ld)\n", iface, ckid, lpData, size); + + /* check parameters */ + if (lpData == NULL) + return AVIERR_BADPARAM; + if (size < 0) + return AVIERR_BADSIZE; + + /* Do we have write permission? */ + if ((This->uMode & MMIO_RWMODE) == 0) + return AVIERR_READONLY; + + This->fDirty = TRUE; + + return WriteExtraChunk(&This->extra, ckid, lpData, size); +} + +static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid, + LPVOID lpData, LONG *size) +{ + IAVIFileImpl *This = (IAVIFileImpl *)iface; + + TRACE("(%p,0x%08lX,%p,%p)\n", iface, ckid, lpData, size); + + return ReadExtraChunk(&This->extra, ckid, lpData, size); +} + +static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface) +{ + TRACE("(%p)\n",iface); + + /* This is only needed for interleaved files. + * We have only one stream, which can't be interleaved. + */ + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType, + LONG lParam) +{ + IAVIFileImpl *This = (IAVIFileImpl *)iface; + + TRACE("(%p,0x%08lX,%ld)\n", iface, fccType, lParam); + + /* check parameter */ + if (lParam < 0) + return AVIERR_BADPARAM; + + /* Do we have our audio stream? */ + if (lParam != 0 || This->fInfo.dwStreams == 0 || + (fccType != 0 && fccType != streamtypeAUDIO)) + return AVIERR_NODATA; + + /* Have user write permissions? */ + if ((This->uMode & MMIO_RWMODE) == 0) + return AVIERR_READONLY; + + GlobalFreePtr(This->lpFormat); + This->lpFormat = NULL; + This->cbFormat = 0; + + /* update infos */ + This->ckData.dwDataOffset = 0; + This->ckData.cksize = 0; + + This->sInfo.dwScale = 0; + This->sInfo.dwRate = 0; + This->sInfo.dwLength = 0; + This->sInfo.dwSuggestedBufferSize = 0; + + This->fInfo.dwStreams = 0; + This->fInfo.dwEditCount++; + + This->fDirty = TRUE; + + return AVIERR_OK; +} + +/***********************************************************************/ + +static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface, + REFIID refiid, LPVOID *obj) +{ + IPersistFileImpl *This = (IPersistFileImpl *)iface; + + assert(This->paf != NULL); + + return IAVIFile_QueryInterface((PAVIFILE)This->paf, refiid, obj); +} + +static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile *iface) +{ + IPersistFileImpl *This = (IPersistFileImpl *)iface; + + assert(This->paf != NULL); + + return IAVIFile_AddRef((PAVIFILE)This->paf); +} + +static ULONG WINAPI IPersistFile_fnRelease(IPersistFile *iface) +{ + IPersistFileImpl *This = (IPersistFileImpl *)iface; + + assert(This->paf != NULL); + + return IAVIFile_Release((PAVIFILE)This->paf); +} + +static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface, + LPCLSID pClassID) +{ + TRACE("(%p,%p)\n", iface, pClassID); + + if (pClassID == NULL) + return AVIERR_BADPARAM; + + memcpy(pClassID, &CLSID_WAVFile, sizeof(CLSID_WAVFile)); + + return AVIERR_OK; +} + +static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface) +{ + IPersistFileImpl *This = (IPersistFileImpl *)iface; + + TRACE("(%p)\n", iface); + + assert(This->paf != NULL); + + return (This->paf->fDirty ? S_OK : S_FALSE); +} + +static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface, + LPCOLESTR pszFileName, DWORD dwMode) +{ + IAVIFileImpl *This = ((IPersistFileImpl*)iface)->paf; + + WCHAR wszStreamFmt[50]; + INT len; + + TRACE("(%p,%s,0x%08lX)\n", iface, debugstr_w(pszFileName), dwMode); + + /* check parameter */ + if (pszFileName == NULL) + return AVIERR_BADPARAM; + + assert(This != NULL); + if (This->hmmio != NULL) + return AVIERR_ERROR; /* No reuse of this object for another file! */ + + /* remeber mode and name */ + This->uMode = dwMode; + + len = lstrlenW(pszFileName) + 1; + This->szFileName = LocalAlloc(LPTR, len * sizeof(WCHAR)); + if (This->szFileName == NULL) + return AVIERR_MEMORY; + lstrcpyW(This->szFileName, pszFileName); + + /* try to open the file */ + This->hmmio = mmioOpenW(This->szFileName, NULL, MMIO_ALLOCBUF | dwMode); + if (This->hmmio == NULL) { + /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */ + LPSTR szFileName; + len = WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, + NULL, 0, NULL, NULL); + szFileName = LocalAlloc(LPTR, len * sizeof(CHAR)); + if (szFileName == NULL) + return AVIERR_MEMORY; + + WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, szFileName, + len, NULL, NULL); + + This->hmmio = mmioOpenA(szFileName, NULL, MMIO_ALLOCBUF | dwMode); + LocalFree((HLOCAL)szFileName); + if (This->hmmio == NULL) + return AVIERR_FILEOPEN; + } + + memset(& This->fInfo, 0, sizeof(This->fInfo)); + memset(& This->sInfo, 0, sizeof(This->sInfo)); + + LoadStringW(AVIFILE_hModule, IDS_WAVEFILETYPE, This->fInfo.szFileType, + sizeof(This->fInfo.szFileType)); + if (LoadStringW(AVIFILE_hModule, IDS_WAVESTREAMFORMAT, + wszStreamFmt, sizeof(wszStreamFmt)) > 0) { + wsprintfW(This->sInfo.szName, wszStreamFmt, + AVIFILE_BasenameW(This->szFileName)); + } + + /* should we create a new file? */ + if (dwMode & OF_CREATE) { + /* nothing more to do */ + return AVIERR_OK; + } else + return AVIFILE_LoadFile(This); +} + +static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface, + LPCOLESTR pszFileName,BOOL fRemember) +{ + TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember); + + /* We write directly to disk, so nothing to do. */ + + return AVIERR_OK; +} + +static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface, + LPCOLESTR pszFileName) +{ + TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName)); + + /* We write directly to disk, so nothing to do. */ + + return AVIERR_OK; +} + +static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface, + LPOLESTR *ppszFileName) +{ + IPersistFileImpl *This = (IPersistFileImpl *)iface; + + TRACE("(%p,%p)\n", iface, ppszFileName); + + if (ppszFileName == NULL) + return AVIERR_BADPARAM; + + *ppszFileName = NULL; + + assert(This->paf != NULL); + + if (This->paf->szFileName != NULL) { + int len = lstrlenW(This->paf->szFileName) + 1; + + *ppszFileName = GlobalAllocPtr(GHND, len * sizeof(WCHAR)); + if (*ppszFileName == NULL) + return AVIERR_MEMORY; + + strcpyW(*ppszFileName, This->paf->szFileName); + } + + return AVIERR_OK; +} + +/***********************************************************************/ + +static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface, + REFIID refiid, LPVOID *obj) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + assert(This->paf != NULL); + + return IAVIFile_QueryInterface((PAVIFILE)This->paf, refiid, obj); +} + +static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + assert(This->paf != NULL); + + return IAVIFile_AddRef((PAVIFILE)This->paf); +} + +static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + assert(This->paf != NULL); + + return IAVIFile_Release((PAVIFILE)This->paf); +} + +static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1, + LPARAM lParam2) +{ + TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2); + + /* This IAVIStream interface needs an WAVFile */ + return AVIERR_UNSUPPORTED; +} + +static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi, + LONG size) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + TRACE("(%p,%p,%ld)\n", iface, psi, size); + + if (psi == NULL) + return AVIERR_BADPARAM; + if (size < 0) + return AVIERR_BADSIZE; + + memcpy(psi, &This->paf->sInfo, min((DWORD)size, sizeof(This->paf->sInfo))); + + if ((DWORD)size < sizeof(This->paf->sInfo)) + return AVIERR_BUFFERTOOSMALL; + return AVIERR_OK; +} + +static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos, + LONG flags) +{ + IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf; + + TRACE("(%p,%ld,0x%08lX)\n",iface,pos,flags); + + /* Do we have data? */ + if (This->lpFormat == NULL) + return -1; + + /* We don't have an index */ + if (flags & FIND_INDEX) + return -1; + + if (flags & FIND_FROM_START) { + pos = This->sInfo.dwStart; + flags &= ~(FIND_FROM_START|FIND_PREV); + flags |= FIND_NEXT; + } + + if (flags & FIND_FORMAT) { + if ((flags & FIND_NEXT) && pos > 0) + pos = -1; + else + pos = 0; + } + + if ((flags & FIND_RET) == FIND_LENGTH || + (flags & FIND_RET) == FIND_SIZE) + return This->sInfo.dwSampleSize; + if ((flags & FIND_RET) == FIND_OFFSET) + return This->ckData.dwDataOffset + pos * This->sInfo.dwSampleSize; + + return pos; +} + +static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos, + LPVOID format, LONG *formatsize) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + TRACE("(%p,%ld,%p,%p)\n", iface, pos, format, formatsize); + + if (formatsize == NULL) + return AVIERR_BADPARAM; + + /* only interested in needed buffersize? */ + if (format == NULL || *formatsize <= 0) { + *formatsize = This->paf->cbFormat; + + return AVIERR_OK; + } + + /* copy initial format (only as much as will fit) */ + memcpy(format, This->paf->lpFormat, min(*formatsize, This->paf->cbFormat)); + if (*formatsize < This->paf->cbFormat) { + *formatsize = This->paf->cbFormat; + return AVIERR_BUFFERTOOSMALL; + } + + *formatsize = This->paf->cbFormat; + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos, + LPVOID format, LONG formatsize) +{ + IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf; + + TRACE("(%p,%ld,%p,%ld)\n", iface, pos, format, formatsize); + + /* check parameters */ + if (format == NULL || formatsize <= sizeof(PCMWAVEFORMAT)) + return AVIERR_BADPARAM; + + /* We can only do this to an empty wave file, but ignore call + * if still same format */ + if (This->lpFormat != NULL) { + if (formatsize != This->cbFormat || + memcmp(format, This->lpFormat, formatsize) != 0) + return AVIERR_UNSUPPORTED; + + return AVIERR_OK; + } + + /* only support start at position 0 */ + if (pos != 0) + return AVIERR_UNSUPPORTED; + + /* Do we have write permission? */ + if ((This->uMode & MMIO_RWMODE) == 0) + return AVIERR_READONLY; + + /* get memory for format and copy it */ + This->lpFormat = (LPWAVEFORMATEX)GlobalAllocPtr(GMEM_MOVEABLE, formatsize); + if (This->lpFormat == NULL) + return AVIERR_MEMORY; + + This->cbFormat = formatsize; + memcpy(This->lpFormat, format, formatsize); + + /* update info's about 'data' chunk */ + This->ckData.dwDataOffset = formatsize + 7 * sizeof(DWORD); + This->ckData.cksize = 0; + + /* for non-pcm format we need also a 'fact' chunk */ + if (This->lpFormat->wFormatTag != WAVE_FORMAT_PCM) + This->ckData.dwDataOffset += 3 * sizeof(DWORD); + + /* update stream and file info */ + This->sInfo.dwSampleSize = This->lpFormat->nBlockAlign; + This->sInfo.dwScale = This->lpFormat->nBlockAlign; + This->sInfo.dwRate = This->lpFormat->nAvgBytesPerSec; + This->sInfo.dwLength = 0; + This->sInfo.dwSuggestedBufferSize = 0; + + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start, + LONG samples, LPVOID buffer, + LONG buffersize, LPLONG bytesread, + LPLONG samplesread) +{ + IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf; + + TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface, start, samples, buffer, + buffersize, bytesread, samplesread); + + /* clear return parameters if given */ + if (bytesread != NULL) + *bytesread = 0; + if (samplesread != NULL) + *samplesread = 0; + + /* positions without data */ + if (start < 0 || (DWORD)start > This->sInfo.dwLength) + return AVIERR_OK; + + /* check samples */ + if (samples < 0) + samples = 0; + if (buffersize > 0) { + if (samples > 0) + samples = min((DWORD)samples, buffersize / This->sInfo.dwSampleSize); + else + samples = buffersize / This->sInfo.dwSampleSize; + } + + /* limit to end of stream */ + if ((DWORD)(start + samples) > This->sInfo.dwLength) + samples = This->sInfo.dwLength - start; + + /* request only the sizes? */ + if (buffer == NULL || buffersize <= 0) { + /* then I need at least one parameter for it */ + if (bytesread == NULL && samplesread == NULL) + return AVIERR_BADPARAM; + + if (bytesread != NULL) + *bytesread = samples * This->sInfo.dwSampleSize; + if (samplesread != NULL) + *samplesread = samples; + + return AVIERR_OK; + } + + /* nothing to read? */ + if (samples == 0) + return AVIERR_OK; + + /* Can I read at least one sample? */ + if ((DWORD)buffersize < This->sInfo.dwSampleSize) + return AVIERR_BUFFERTOOSMALL; + + buffersize = samples * This->sInfo.dwSampleSize; + + if (mmioSeek(This->hmmio, This->ckData.dwDataOffset + + start * This->sInfo.dwSampleSize, SEEK_SET) == -1) + return AVIERR_FILEREAD; + if (mmioRead(This->hmmio, (HPSTR)buffer, buffersize) != buffersize) + return AVIERR_FILEREAD; + + /* fill out return parameters if given */ + if (bytesread != NULL) + *bytesread = buffersize; + if (samplesread != NULL) + *samplesread = samples; + + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start, + LONG samples, LPVOID buffer, + LONG buffersize, DWORD flags, + LPLONG sampwritten, + LPLONG byteswritten) +{ + IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf; + + TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n", iface, start, samples, + buffer, buffersize, flags, sampwritten, byteswritten); + + /* clear return parameters if given */ + if (sampwritten != NULL) + *sampwritten = 0; + if (byteswritten != NULL) + *byteswritten = 0; + + /* check parameters */ + if (buffer == NULL && (buffersize > 0 || samples > 0)) + return AVIERR_BADPARAM; + + /* Do we have write permission? */ + if ((This->uMode & MMIO_RWMODE) == 0) + return AVIERR_READONLY; + + /* < 0 means "append" */ + if (start < 0) + start = This->sInfo.dwStart + This->sInfo.dwLength; + + /* check buffersize -- must multiple of samplesize */ + if (buffersize & ~(This->sInfo.dwSampleSize - 1)) + return AVIERR_BADSIZE; + + /* do we have anything to write? */ + if (buffer != NULL && buffersize > 0) { + This->fDirty = 1; + + if (mmioSeek(This->hmmio, This->ckData.dwDataOffset + + start * This->sInfo.dwSampleSize, SEEK_SET) == -1) + return AVIERR_FILEWRITE; + if (mmioWrite(This->hmmio, (HPSTR)buffer, buffersize) != buffersize) + return AVIERR_FILEWRITE; + + This->sInfo.dwLength = max(This->sInfo.dwLength, (DWORD)start + samples); + This->ckData.cksize = max(This->ckData.cksize, + start * This->sInfo.dwSampleSize + buffersize); + + /* fill out return parameters if given */ + if (sampwritten != NULL) + *sampwritten = samples; + if (byteswritten != NULL) + *byteswritten = buffersize; + } + + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start, + LONG samples) +{ + IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf; + + TRACE("(%p,%ld,%ld)\n", iface, start, samples); + + /* check parameters */ + if (start < 0 || samples < 0) + return AVIERR_BADPARAM; + + /* Delete before start of stream? */ + if ((DWORD)(start + samples) < This->sInfo.dwStart) + return AVIERR_OK; + + /* Delete after end of stream? */ + if ((DWORD)start > This->sInfo.dwLength) + return AVIERR_OK; + + /* For the rest we need write permissions */ + if ((This->uMode & MMIO_RWMODE) == 0) + return AVIERR_READONLY; + + if ((DWORD)(start + samples) >= This->sInfo.dwLength) { + /* deletion at end */ + samples = This->sInfo.dwLength - start; + This->sInfo.dwLength -= samples; + This->ckData.cksize -= samples * This->sInfo.dwSampleSize; + } else if ((DWORD)start <= This->sInfo.dwStart) { + /* deletion at start */ + samples = This->sInfo.dwStart - start; + start = This->sInfo.dwStart; + This->ckData.dwDataOffset += samples * This->sInfo.dwSampleSize; + This->ckData.cksize -= samples * This->sInfo.dwSampleSize; + } else { + /* deletion inside stream -- needs playlist and cue's */ + FIXME(": deletion inside of stream not supported!\n"); + + return AVIERR_UNSUPPORTED; + } + + This->fDirty = 1; + + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc, + LPVOID lp, LPLONG lpread) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + assert(This->paf != NULL); + + return IAVIFile_ReadData((PAVIFILE)This->paf, fcc, lp, lpread); +} + +static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc, + LPVOID lp, LONG size) +{ + IAVIStreamImpl *This = (IAVIStreamImpl *)iface; + + return IAVIFile_WriteData((PAVIFILE)This->paf, fcc, lp, size); +} + +static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream *iface, + LPAVISTREAMINFOW info, LONG infolen) +{ + FIXME("(%p,%p,%ld): stub\n", iface, info, infolen); + + return E_FAIL; +} + +/***********************************************************************/ + +static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This) +{ + MMCKINFO ckRIFF; + MMCKINFO ck; + + This->sInfo.dwLength = 0; /* just to be sure */ + This->fDirty = FALSE; + + /* search for RIFF chunk */ + ckRIFF.fccType = 0; /* find any */ + if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) { + return AVIFILE_LoadSunFile(This); + } + + if (ckRIFF.fccType != formtypeWAVE) + return AVIERR_BADFORMAT; + + /* search WAVE format chunk */ + ck.ckid = ckidWAVEFORMAT; + if (FindChunkAndKeepExtras(&This->extra, This->hmmio, &ck, + &ckRIFF, MMIO_FINDCHUNK) != S_OK) + return AVIERR_FILEREAD; + + /* get memory for format and read it */ + This->lpFormat = (LPWAVEFORMATEX)GlobalAllocPtr(GMEM_MOVEABLE, ck.cksize); + if (This->lpFormat == NULL) + return AVIERR_FILEREAD; + This->cbFormat = ck.cksize; + + if (mmioRead(This->hmmio, (HPSTR)This->lpFormat, ck.cksize) != ck.cksize) + return AVIERR_FILEREAD; + if (mmioAscend(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEREAD; + + /* Non-pcm formats have a fact chunk. + * We don't need it, so simply add it to the extra chunks. + */ + + /* find the big data chunk */ + This->ckData.ckid = ckidWAVEDATA; + if (FindChunkAndKeepExtras(&This->extra, This->hmmio, &This->ckData, + &ckRIFF, MMIO_FINDCHUNK) != S_OK) + return AVIERR_FILEREAD; + + memset(&This->sInfo, 0, sizeof(This->sInfo)); + This->sInfo.fccType = streamtypeAUDIO; + This->sInfo.dwRate = This->lpFormat->nAvgBytesPerSec; + This->sInfo.dwSampleSize = + This->sInfo.dwScale = This->lpFormat->nBlockAlign; + This->sInfo.dwLength = This->ckData.cksize / This->lpFormat->nBlockAlign; + This->sInfo.dwSuggestedBufferSize = This->ckData.cksize; + + This->fInfo.dwStreams = 1; + + if (mmioAscend(This->hmmio, &This->ckData, 0) != S_OK) { + /* seems to be truncated */ + WARN(": file seems to be truncated!\n"); + This->ckData.cksize = mmioSeek(This->hmmio, 0, SEEK_END) - + This->ckData.dwDataOffset; + This->sInfo.dwLength = This->ckData.cksize / This->lpFormat->nBlockAlign; + This->sInfo.dwSuggestedBufferSize = This->ckData.cksize; + } + + /* ignore errors */ + FindChunkAndKeepExtras(&This->extra, This->hmmio, &ck, &ckRIFF, 0); + + return AVIERR_OK; +} + +static HRESULT AVIFILE_LoadSunFile(IAVIFileImpl *This) +{ + SUNAUDIOHEADER auhdr; + + mmioSeek(This->hmmio, 0, SEEK_SET); + if (mmioRead(This->hmmio, (HPSTR)&auhdr, sizeof(auhdr)) != sizeof(auhdr)) + return AVIERR_FILEREAD; + + if (auhdr.fccType == 0x0064732E) { + /* header in little endian */ + This->ckData.dwDataOffset = LE2H_DWORD(auhdr.offset); + This->ckData.cksize = LE2H_DWORD(auhdr.size); + + auhdr.encoding = LE2H_DWORD(auhdr.encoding); + auhdr.sampleRate = LE2H_DWORD(auhdr.sampleRate); + auhdr.channels = LE2H_DWORD(auhdr.channels); + } else if (auhdr.fccType == mmioFOURCC('.','s','n','d')) { + /* header in big endian */ + This->ckData.dwDataOffset = BE2H_DWORD(auhdr.offset); + This->ckData.cksize = BE2H_DWORD(auhdr.size); + + auhdr.encoding = BE2H_DWORD(auhdr.encoding); + auhdr.sampleRate = BE2H_DWORD(auhdr.sampleRate); + auhdr.channels = BE2H_DWORD(auhdr.channels); + } else + return AVIERR_FILEREAD; + + if (auhdr.channels < 1) + return AVIERR_BADFORMAT; + + /* get size of header */ + switch(auhdr.encoding) { + case AU_ENCODING_ADPCM_G721_32: + This->cbFormat = sizeof(G721_ADPCMWAVEFORMAT); break; + case AU_ENCODING_ADPCM_G723_24: + This->cbFormat = sizeof(G723_ADPCMWAVEFORMAT); break; + case AU_ENCODING_ADPCM_G722: + case AU_ENCODING_ADPCM_G723_5: + WARN("unsupported Sun audio format %d\n", auhdr.encoding); + return AVIERR_UNSUPPORTED; /* FIXME */ + default: + This->cbFormat = sizeof(WAVEFORMATEX); break; + }; + + This->lpFormat = + (LPWAVEFORMATEX)GlobalAllocPtr(GMEM_MOVEABLE, This->cbFormat); + if (This->lpFormat == NULL) + return AVIERR_MEMORY; + + This->lpFormat->nChannels = auhdr.channels; + This->lpFormat->nSamplesPerSec = auhdr.sampleRate; + switch(auhdr.encoding) { + case AU_ENCODING_ULAW_8: + This->lpFormat->wFormatTag = WAVE_FORMAT_MULAW; + This->lpFormat->wBitsPerSample = 8; + break; + case AU_ENCODING_PCM_8: + This->lpFormat->wFormatTag = WAVE_FORMAT_PCM; + This->lpFormat->wBitsPerSample = 8; + break; + case AU_ENCODING_PCM_16: + This->lpFormat->wFormatTag = WAVE_FORMAT_PCM; + This->lpFormat->wBitsPerSample = 16; + break; + case AU_ENCODING_PCM_24: + This->lpFormat->wFormatTag = WAVE_FORMAT_PCM; + This->lpFormat->wBitsPerSample = 24; + break; + case AU_ENCODING_PCM_32: + This->lpFormat->wFormatTag = WAVE_FORMAT_PCM; + This->lpFormat->wBitsPerSample = 32; + break; + case AU_ENCODING_ALAW_8: + This->lpFormat->wFormatTag = WAVE_FORMAT_ALAW; + This->lpFormat->wBitsPerSample = 8; + break; + case AU_ENCODING_ADPCM_G721_32: + This->lpFormat->wFormatTag = WAVE_FORMAT_G721_ADPCM; + This->lpFormat->wBitsPerSample = (3*5*8); + This->lpFormat->nBlockAlign = 15*15*8; + This->lpFormat->cbSize = sizeof(WORD); + ((LPG721_ADPCMWAVEFORMAT)This->lpFormat)->nAuxBlockSize = 0; + break; + case AU_ENCODING_ADPCM_G723_24: + This->lpFormat->wFormatTag = WAVE_FORMAT_G723_ADPCM; + This->lpFormat->wBitsPerSample = (3*5*8); + This->lpFormat->nBlockAlign = 15*15*8; + This->lpFormat->cbSize = 2*sizeof(WORD); + ((LPG723_ADPCMWAVEFORMAT)This->lpFormat)->cbExtraSize = 0; + ((LPG723_ADPCMWAVEFORMAT)This->lpFormat)->nAuxBlockSize = 0; + break; + default: + WARN("unsupported Sun audio format %d\n", auhdr.encoding); + return AVIERR_UNSUPPORTED; + }; + + This->lpFormat->nBlockAlign = + (This->lpFormat->nChannels * This->lpFormat->wBitsPerSample) / 8; + if (This->lpFormat->nBlockAlign == 0 && This->lpFormat->wBitsPerSample < 8) + This->lpFormat->nBlockAlign++; + This->lpFormat->nAvgBytesPerSec = + This->lpFormat->nBlockAlign * This->lpFormat->nSamplesPerSec; + + This->fDirty = 0; + + This->sInfo.fccType = streamtypeAUDIO; + This->sInfo.fccHandler = 0; + This->sInfo.dwFlags = 0; + This->sInfo.wPriority = 0; + This->sInfo.wLanguage = 0; + This->sInfo.dwInitialFrames = 0; + This->sInfo.dwScale = This->lpFormat->nBlockAlign; + This->sInfo.dwRate = This->lpFormat->nAvgBytesPerSec; + This->sInfo.dwStart = 0; + This->sInfo.dwLength = + This->ckData.cksize / This->lpFormat->nBlockAlign; + This->sInfo.dwSuggestedBufferSize = This->sInfo.dwLength; + This->sInfo.dwSampleSize = This->lpFormat->nBlockAlign; + + This->fInfo.dwStreams = 1; + This->fInfo.dwScale = 1; + This->fInfo.dwRate = This->lpFormat->nSamplesPerSec; + This->fInfo.dwLength = + MulDiv(This->ckData.cksize, This->lpFormat->nSamplesPerSec, + This->lpFormat->nAvgBytesPerSec); + + return AVIERR_OK; +} + +static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This) +{ + MMCKINFO ckRIFF; + MMCKINFO ck; + + mmioSeek(This->hmmio, 0, SEEK_SET); + + /* create the RIFF chunk with formtype WAVE */ + ckRIFF.fccType = formtypeWAVE; + ckRIFF.cksize = 0; + if (mmioCreateChunk(This->hmmio, &ckRIFF, MMIO_CREATERIFF) != S_OK) + return AVIERR_FILEWRITE; + + /* the next chunk is the format */ + ck.ckid = ckidWAVEFORMAT; + ck.cksize = This->cbFormat; + if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + if (This->lpFormat != NULL && This->cbFormat > 0) { + if (mmioWrite(This->hmmio, (HPSTR)This->lpFormat, ck.cksize) != ck.cksize) + return AVIERR_FILEWRITE; + } + if (mmioAscend(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + + /* fact chunk is needed for non-pcm waveforms */ + if (This->lpFormat != NULL && This->cbFormat > sizeof(PCMWAVEFORMAT) && + This->lpFormat->wFormatTag != WAVE_FORMAT_PCM) { + WAVEFORMATEX wfx; + DWORD dwFactLength; + HACMSTREAM has; + + /* try to open an appropriate audio codec to figure out + * data for fact-chunk */ + wfx.wFormatTag = WAVE_FORMAT_PCM; + if (acmFormatSuggest(NULL, This->lpFormat, &wfx, + sizeof(wfx), ACM_FORMATSUGGESTF_WFORMATTAG)) { + acmStreamOpen(&has, NULL, This->lpFormat, &wfx, NULL, + 0, 0, ACM_STREAMOPENF_NONREALTIME); + acmStreamSize(has, This->ckData.cksize, &dwFactLength, + ACM_STREAMSIZEF_SOURCE); + dwFactLength /= wfx.nBlockAlign; + acmStreamClose(has, 0); + + /* create the fact chunk */ + ck.ckid = ckidWAVEFACT; + ck.cksize = sizeof(dwFactLength); + + /* test for enough space before data chunk */ + if (mmioSeek(This->hmmio, 0, SEEK_CUR) > This->ckData.dwDataOffset + - ck.cksize - 4 * sizeof(DWORD)) + return AVIERR_FILEWRITE; + if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + if (mmioWrite(This->hmmio, (HPSTR)&dwFactLength, ck.cksize) != ck.cksize) + return AVIERR_FILEWRITE; + if (mmioAscend(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + } else + ERR(": fact chunk is needed for non-pcm files -- currently no codec found, so skipped!\n"); + } + + /* if there was extra stuff, we need to fill it with JUNK */ + if (mmioSeek(This->hmmio, 0, SEEK_CUR) + 2 * sizeof(DWORD) < This->ckData.dwDataOffset) { + ck.ckid = ckidAVIPADDING; + ck.cksize = 0; + if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + + if (mmioSeek(This->hmmio, This->ckData.dwDataOffset + - 2 * sizeof(DWORD), SEEK_SET) == -1) + return AVIERR_FILEWRITE; + if (mmioAscend(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + } + + /* create the data chunk */ + ck.ckid = ckidWAVEDATA; + ck.cksize = This->ckData.cksize; + if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + if (mmioSeek(This->hmmio, This->ckData.cksize, SEEK_CUR) == -1) + return AVIERR_FILEWRITE; + if (mmioAscend(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEWRITE; + + /* some optional extra chunks? */ + if (This->extra.lp != NULL && This->extra.cb > 0) { + /* chunk headers are already in structure */ + if (mmioWrite(This->hmmio, This->extra.lp, This->extra.cb) != This->extra.cb) + return AVIERR_FILEWRITE; + } + + /* close RIFF chunk */ + if (mmioAscend(This->hmmio, &ckRIFF, 0) != S_OK) + return AVIERR_FILEWRITE; + if (mmioFlush(This->hmmio, 0) != S_OK) + return AVIERR_FILEWRITE; + + return AVIERR_OK; +} diff --git a/reactos/lib/directory.xml b/reactos/lib/directory.xml index 4ce3571dab0..f114d10d493 100644 --- a/reactos/lib/directory.xml +++ b/reactos/lib/directory.xml @@ -17,6 +17,9 @@ + + + -- 2.17.1