avifil32 vendor import
authorGé van Geldorp <ge@gse.nl>
Mon, 21 Nov 2005 19:19:13 +0000 (19:19 +0000)
committerGé van Geldorp <ge@gse.nl>
Mon, 21 Nov 2005 19:19:13 +0000 (19:19 +0000)
svn path=/trunk/; revision=19419

31 files changed:
reactos/lib/avifil32/acmstream.c [new file with mode: 0644]
reactos/lib/avifil32/api.c [new file with mode: 0644]
reactos/lib/avifil32/avifil32.spec [new file with mode: 0644]
reactos/lib/avifil32/avifil32.xml [new file with mode: 0644]
reactos/lib/avifil32/avifile.c [new file with mode: 0644]
reactos/lib/avifil32/avifile.spec [new file with mode: 0644]
reactos/lib/avifil32/avifile_Cs.rc [new file with mode: 0644]
reactos/lib/avifil32/avifile_De.rc [new file with mode: 0644]
reactos/lib/avifil32/avifile_En.rc [new file with mode: 0644]
reactos/lib/avifil32/avifile_Es.rc [new file with mode: 0644]
reactos/lib/avifil32/avifile_Fr.rc [new file with mode: 0644]
reactos/lib/avifil32/avifile_It.rc [new file with mode: 0644]
reactos/lib/avifil32/avifile_Ja.rc [new file with mode: 0644]
reactos/lib/avifil32/avifile_Nl.rc [new file with mode: 0644]
reactos/lib/avifil32/avifile_No.rc [new file with mode: 0644]
reactos/lib/avifil32/avifile_Pl.rc [new file with mode: 0644]
reactos/lib/avifil32/avifile_Pt.rc [new file with mode: 0644]
reactos/lib/avifil32/avifile_Ru.rc [new file with mode: 0644]
reactos/lib/avifil32/avifile_Si.rc [new file with mode: 0644]
reactos/lib/avifil32/avifile_private.h [new file with mode: 0644]
reactos/lib/avifil32/editstream.c [new file with mode: 0644]
reactos/lib/avifil32/extrachunk.c [new file with mode: 0644]
reactos/lib/avifil32/extrachunk.h [new file with mode: 0644]
reactos/lib/avifil32/factory.c [new file with mode: 0644]
reactos/lib/avifil32/getframe.c [new file with mode: 0644]
reactos/lib/avifil32/icmstream.c [new file with mode: 0644]
reactos/lib/avifil32/regsvr.c [new file with mode: 0644]
reactos/lib/avifil32/rsrc.rc [new file with mode: 0644]
reactos/lib/avifil32/tmpfile.c [new file with mode: 0644]
reactos/lib/avifil32/wavfile.c [new file with mode: 0644]
reactos/lib/directory.xml

diff --git a/reactos/lib/avifil32/acmstream.c b/reactos/lib/avifil32/acmstream.c
new file mode 100644 (file)
index 0000000..0dc068f
--- /dev/null
@@ -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 <assert.h>
+#include <stdarg.h>
+
+#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 (file)
index 0000000..300be27
--- /dev/null
@@ -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 <assert.h>
+#include <stdarg.h>
+
+#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 (file)
index 0000000..9eae749
--- /dev/null
@@ -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 (file)
index 0000000..7c4c9e4
--- /dev/null
@@ -0,0 +1,28 @@
+<module name="avifil32" type="win32dll" baseaddress="${BASEADDRESS_AVIFIL32}" installbase="system32" installname="avifil32.dll">
+       <importlibrary definition="avifil32.spec.def" />
+       <include base="avifil32">.</include>
+       <include base="ReactOS">include/wine</include>
+       <define name="__USE_W32API" />
+       <define name="_WIN32_WINNT">0x501</define>
+       <library>uuid</library>
+       <library>ntdll</library>
+       <library>winmm</library>
+       <library>ole32</library>
+       <library>msvfw32</library>
+       <library>msacm32</library>
+       <library>kernel32</library>
+       <library>wine</library>
+       <file>acmstream.c</file>
+       <file>api.c</file>
+       <file>avifile.c</file>
+       <file>editstream.c</file>
+       <file>extrachunk.c</file>
+       <file>factory.c</file>
+       <file>getframe.c</file>
+       <file>icmstream.c</file>
+       <file>regsvr.c</file>
+       <file>tmpfile.c</file>
+       <file>wavfile.c</file>
+       <file>rsrc.rc</file>
+       <file>avifil32.spec</file>
+</module>
diff --git a/reactos/lib/avifil32/avifile.c b/reactos/lib/avifil32/avifile.c
new file mode 100644 (file)
index 0000000..901010d
--- /dev/null
@@ -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 <assert.h>
+#include <stdarg.h>
+
+#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 (file)
index 0000000..71a6c74
--- /dev/null
@@ -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 (file)
index 0000000..e8da6f1
--- /dev/null
@@ -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 (file)
index 0000000..f655022
--- /dev/null
@@ -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 (file)
index 0000000..2fa2fac
--- /dev/null
@@ -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 (file)
index 0000000..c2b51c3
--- /dev/null
@@ -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 (file)
index 0000000..b9ee5d9
--- /dev/null
@@ -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 (file)
index 0000000..e087319
--- /dev/null
@@ -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 (file)
index 0000000..3bb780e
--- /dev/null
@@ -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 "\88³\8fk\83I\83v\83V\83\87\83\93"
+FONT 9, "MS UI Gothic"
+BEGIN
+    LTEXT           "\83X\83g\83\8a\81[\83\80\82ð\91I\91ð(&C):",-1,2,5,114,10
+    COMBOBOX        IDC_STREAM,2,18,134,61,CBS_DROPDOWNLIST | WS_VSCROLL | 
+                    WS_TABSTOP
+    PUSHBUTTON      "\83I\83v\83V\83\87\83\93(&O)...",IDC_OPTIONS,145,17,45,14
+    AUTOCHECKBOX    "\83C\83\93\83^\81[\83\8a\81[\83u(&I)",IDC_INTERLEAVE,3,42,60,11,WS_TABSTOP
+    EDITTEXT        IDC_INTERLEAVEEVERY,66,41,32,12,ES_AUTOHSCROLL
+    LTEXT           "\83t\83\8c\81[\83\80",-1,104,43,36,9
+    LTEXT           "\8c»\8dÝ\82Ì\83t\83H\81[\83}\83b\83g:",-1,3,56,53,9
+    LTEXT           "This space for rent",IDC_FORMATTEXT,55,56,90,26
+    DEFPUSHBUTTON   "OK",IDOK,145,42,45,14
+    PUSHBUTTON      "\83L\83\83\83\93\83Z\83\8b",IDCANCEL,145,61,45,14
+END
+
+STRINGTABLE DISCARDABLE
+{
+  IDS_WAVESTREAMFORMAT "Wave\83t\83@\83C\83\8b: %s"
+  IDS_WAVEFILETYPE     "Wave\83t\83@\83C\83\8b"
+  IDS_ALLMULTIMEDIA    "\91S\82Ä\82Ì\83}\83\8b\83`\83\81\83f\83B\83A\83t\83@\83C\83\8b"
+  IDS_ALLFILES         "\91S\82Ä\82Ì\83t\83@\83C\83\8b (*.*)@*.*"
+  IDS_VIDEO            "\83r\83f\83I"
+  IDS_AUDIO            "\89¹\90º"
+  IDS_AVISTREAMFORMAT  "%s %s #%d"
+  IDS_AVIFILETYPE      "Wine AVI-default-filehandler"
+  IDS_UNCOMPRESSED      "\96¢\88³\8fk"
+}
diff --git a/reactos/lib/avifil32/avifile_Nl.rc b/reactos/lib/avifil32/avifile_Nl.rc
new file mode 100644 (file)
index 0000000..ecabf2d
--- /dev/null
@@ -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 (file)
index 0000000..d75e042
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2005 Alexander N. Sørnes <alex@thehandofagony.com>
+ *
+ * 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 (file)
index 0000000..0527180
--- /dev/null
@@ -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\9fwiêk"
+  IDS_AVISTREAMFORMAT  "%s %s #%d"
+  IDS_AVIFILETYPE      "Wine AVI-domy\9clna-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 (file)
index 0000000..9ca53c9
--- /dev/null
@@ -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 (file)
index 0000000..1949d39
--- /dev/null
@@ -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 (file)
index 0000000..b5714de
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2003 Rok Mandeljc <rok.mandeljc@gimb.org>
+ *
+ * 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\9enosti 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\9enosti ...",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 (file)
index 0000000..d29c00c
--- /dev/null
@@ -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 (file)
index 0000000..eba563a
--- /dev/null
@@ -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 <assert.h>
+#include <stdarg.h>
+
+#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 (file)
index 0000000..d66fd30
--- /dev/null
@@ -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 <assert.h>
+
+#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 (file)
index 0000000..35f4bc0
--- /dev/null
@@ -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 <stdarg.h>
+
+#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 (file)
index 0000000..b1f95ab
--- /dev/null
@@ -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 <assert.h>
+#include <stdarg.h>
+
+#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 (file)
index 0000000..874d78f
--- /dev/null
@@ -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 <assert.h>
+#include <stdarg.h>
+
+#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 (file)
index 0000000..fd1234b
--- /dev/null
@@ -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 <assert.h>
+#include <stdarg.h>
+
+#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 (file)
index 0000000..bc3d3aa
--- /dev/null
@@ -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 <stdarg.h>
+#include <string.h>
+
+#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;