Hallo. This patch is a replacement of the one from Fri Oct 11 2002 - 16:49:14 CDT License: X11 What this patch does: winedefault.reg: - add the neccessary avifile.dll and avifil32.dll registry entries include/vfw.h: - add FIND_* constants - add AVISAVECALLBACK declaration - add declaration for some AVI functions - add some AVI macros dlls/avifil32: - add wavefile handler to avifil32 - add implementation for IGetFrame to avifil32.dll - implemented loading and writing of AVIs. - add some more stubs for some API functions. - add resources to avifil32.dll - implemented AVISaveOptionsFree - declared IID_* as extern in avifil32.spec (avoids linker problem). programs/avitools/aviinfo.c: - fixed memory leak (release AVI stream)
Index: winedefault.reg =================================================================== RCS file: /home/wine/wine/winedefault.reg,v retrieving revision 1.55 diff -u -r1.55 winedefault.reg --- winedefault.reg 13 Oct 2002 17:56:34 -0000 1.55 +++ winedefault.reg 13 Oct 2002 20:21:20 -0000 @@ -1057,3 +1057,60 @@ "Times New Roman Greek,161"="Times New Roman,161" "Times New Roman TUR,162"="Times New Roman,162" "Tms Rmn"="Times New Roman" + +[HKEY_CLASSES_ROOT\AVIFile\Compressors\auds] +@="{0002000F-0000-0000-C000-000000000046}" + +[HKEY_CLASSES_ROOT\AVIFile\Compressors\vids] +@="{00020001-0000-0000-C000-000000000046}" + +[HKEY_CLASSES_ROOT\AVIFile\Extensions\AU] +@="{00020003-0000-0000-C000-000000000046}" + +[HKEY_CLASSES_ROOT\AVIFile\Extensions\AVI] +@="{00020000-0000-0000-C000-000000000046}" + +[HKEY_CLASSES_ROOT\AVIFile\Extensions\WAV] +@="{00020003-0000-0000-C000-000000000046}" + +[HKEY_CLASSES_ROOT\AVIFile\RIFFHandlers\AVI] +@="{00020000-0000-0000-C000-000000000046}" + +[HKEY_CLASSES_ROOT\AVIFile\RIFFHandlers\WAVE] +@="{00020003-0000-0000-C000-000000000046}" + +[HKEY_CLASSES_ROOT\CLSID\{00020000-0000-0000-C000-000000000046}\InProcServer] +@="avifile.dll" + +[HKEY_CLASSES_ROOT\CLSID\{00020000-0000-0000-C000-000000000046}\InProcServer32] +@="avifil32.dll" +"ThreadingModel"="Apartment" + +[HKEY_CLASSES_ROOT\CLSID\{00020001-0000-0000-C000-000000000046}\InProcServer] +@="avifile.dll" + +[HKEY_CLASSES_ROOT\CLSID\{00020001-0000-0000-C000-000000000046}\InProcServer32] +@="avifil32.dll" +"ThreadingModel"="Apartment" + +[HKEY_CLASSES_ROOT\CLSID\{00020003-0000-0000-C000-000000000046}\InProcServer] +@="avifile.dll" + +[HKEY_CLASSES_ROOT\CLSID\{00020003-0000-0000-C000-000000000046}\InProcServer32] +@="avifil32.dll" +"ThreadingModel"="Apartment" + +[HKEY_CLASSES_ROOT\CLSID\{0002000D-0000-0000-C000-000000000046}\InProcServer] +@="avifile.dll" + +[HKEY_CLASSES_ROOT\CLSID\{0002000D-0000-0000-C000-000000000046}\InProcServer32] +@="avifil32.dll" +"ThreadingModel"="Apartment" + +[HKEY_CLASSES_ROOT\CLSID\{0002000F-0000-0000-C000-000000000046}\InProcServer] +@="avifile.dll" + +[HKEY_CLASSES_ROOT\CLSID\{0002000F-0000-0000-C000-000000000046}\InProcServer32] +@="avifil32.dll" +"ThreadingModel"="Apartment" + Index: dlls/Makefile.in =================================================================== RCS file: /home/wine/wine/dlls/Makefile.in,v retrieving revision 1.156 diff -u -r1.156 Makefile.in --- dlls/Makefile.in 10 Oct 2002 23:36:52 -0000 1.156 +++ dlls/Makefile.in 13 Oct 2002 20:21:20 -0000 @@ -764,7 +764,7 @@ advapi32: kernel32.dll$(DLLEXT) ntdll.dll$(DLLEXT) avicap32: ntdll.dll$(DLLEXT) avifil32: msvfw32.dll$(DLLEXT) shell32.dll$(DLLEXT) user32.dll$(DLLEXT) advapi32.dll$(DLLEXT) \ - kernel32.dll$(DLLEXT) + winmm.dll$(DLLEXT) kernel32.dll$(DLLEXT) comcat: ole32.dll$(DLLEXT) user32.dll$(DLLEXT) advapi32.dll$(DLLEXT) kernel32.dll$(DLLEXT) comctl32: user32.dll$(DLLEXT) gdi32.dll$(DLLEXT) advapi32.dll$(DLLEXT) kernel32.dll$(DLLEXT) \ winmm.dll$(DLLEXT) Index: dlls/avifil32/.cvsignore =================================================================== RCS file: /home/wine/wine/dlls/avifil32/.cvsignore,v retrieving revision 1.9 diff -u -r1.9 .cvsignore --- dlls/avifil32/.cvsignore 14 May 2002 20:54:58 -0000 1.9 +++ dlls/avifil32/.cvsignore 13 Oct 2002 20:21:20 -0000 @@ -2,3 +2,4 @@ avifil32.dll.dbg.c avifil32.spec.c avifile.spec.c +rsrc.res Index: dlls/avifil32/Makefile.in =================================================================== RCS file: /home/wine/wine/dlls/avifil32/Makefile.in,v retrieving revision 1.21 diff -u -r1.21 Makefile.in --- dlls/avifil32/Makefile.in 10 Oct 2002 23:31:13 -0000 1.21 +++ dlls/avifil32/Makefile.in 13 Oct 2002 20:21:20 -0000 @@ -3,7 +3,7 @@ SRCDIR = @srcdir@ VPATH = @srcdir@ MODULE = avifil32.dll -IMPORTS = msvfw32 shell32 user32 advapi32 kernel32 +IMPORTS = msvfw32 advapi32 shell32 winmm user32 kernel32 ALTNAMES = avifile.dll EXTRALIBS = $(LIBUUID) @@ -13,7 +13,13 @@ C_SRCS = \ api.c \ avifile.c \ + getframe.c \ + wavfile.c \ + extrachunk.c \ factory.c + +RC_SRCS = \ + rsrc.rc @MAKE_DLL_RULES@ Index: dlls/avifil32/api.c =================================================================== RCS file: /home/wine/wine/dlls/avifil32/api.c,v retrieving revision 1.6 diff -u -r1.6 api.c --- dlls/avifil32/api.c 10 Oct 2002 23:31:13 -0000 1.6 +++ dlls/avifil32/api.c 13 Oct 2002 20:21:21 -0000 @@ -24,11 +24,14 @@ #include "winuser.h" #include "winreg.h" #include "winerror.h" +#include "windowsx.h" #include "ole2.h" #include "shellapi.h" #include "vfw.h" +#include "avifile_private.h" + #include "wine/debug.h" #include "wine/unicode.h" @@ -125,6 +128,25 @@ 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) @@ -190,7 +212,7 @@ CLSID clsidHandler; HRESULT hr; - FIXME("(%p,%s,0x%X,%s): stub!\n", ppfile, debugstr_w(szFile), uMode, + TRACE("(%p,%s,0x%X,%s)\n", ppfile, debugstr_w(szFile), uMode, debugstr_guid(lpHandler)); /* check parameters */ @@ -201,7 +223,8 @@ /* if no handler then try guessing it by extension */ if (lpHandler == NULL) { - FIXME(": must read HKEY_CLASSES_ROOT\\AVIFile\\Extensions\\%s\n", debugstr_w(strrchrW(szFile, L'.'))); + if (! AVIFILE_GetFileHandlerByExtension(szFile, &clsidHandler)) + return AVIERR_UNSUPPORTED; } else memcpy(&clsidHandler, lpHandler, sizeof(clsidHandler)); @@ -599,8 +622,9 @@ if (FAILED(IAVIStream_QueryInterface(pstream, &IID_IGetFrame, (LPVOID*)&pg)) || pg == NULL) { - FIXME(": need internal class for IGetFrame!\n"); - return NULL; + pg = AVIFILE_CreateGetFrame(pstream); + if (pg == NULL) + return NULL; } if (FAILED(IGetFrame_SetFormat(pg, lpbiWanted, NULL, 0, 0, -1, -1))) { @@ -760,7 +784,7 @@ if (FAILED(IAVIStream_Info(pstream, &asiw, sizeof(asiw)))) return 0; - return asiw.dwLength; + return asiw.dwStart; } /*********************************************************************** @@ -819,4 +843,84 @@ return -1; return (LONG)(((float)lTime * asiw.dwRate) / asiw.dwScale / 1000.0); +} + +/*********************************************************************** + * AVIBuildFilterA (AVIFIL32.@) + */ +HRESULT WINAPI AVIBuildFilterA(LPSTR szFilter, LONG cbFilter, BOOL fSaving) +{ + FIXME("(%p,%ld,%d): stub\n", szFilter, cbFilter, fSaving); + + /* check parameters */ + if (szFilter == NULL) + return AVIERR_BADPARAM; + if (cbFilter < 2) + return AVIERR_BADSIZE; + + szFilter[0] = 0; + szFilter[1] = 0; + + return AVIERR_UNSUPPORTED; +} + +/*********************************************************************** + * AVIBuildFilterW (AVIFIL32.@) + */ +HRESULT WINAPI AVIBuildFilterW(LPWSTR szFilter, LONG cbFilter, BOOL fSaving) +{ + FIXME("(%p,%ld,%d): stub\n", szFilter, cbFilter, fSaving); + + /* check parameters */ + if (szFilter == NULL) + return AVIERR_BADPARAM; + if (cbFilter < 2) + return AVIERR_BADSIZE; + + szFilter[0] = 0; + szFilter[1] = 0; + + return AVIERR_UNSUPPORTED; +} + +/*********************************************************************** + * AVISaveOptions (AVIFIL32.@) + */ +BOOL WINAPI AVISaveOptions(HWND hWnd, UINT uFlags, INT nStream, + PAVISTREAM *ppavi, LPAVICOMPRESSOPTIONS *ppOptions) +{ + FIXME("(0x%X,0x%X,%d,%p,%p): stub\n", hWnd, uFlags, nStream, + ppavi, ppOptions); + + return FALSE; +} + +/*********************************************************************** + * AVISaveOptionsFree (AVIFIL32.@) + */ +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; } Index: dlls/avifil32/avifil32.spec =================================================================== RCS file: /home/wine/wine/dlls/avifil32/avifil32.spec,v retrieving revision 1.13 diff -u -r1.13 avifil32.spec --- dlls/avifil32/avifil32.spec 10 Oct 2002 23:31:13 -0000 1.13 +++ dlls/avifil32/avifil32.spec 13 Oct 2002 20:21:21 -0000 @@ -1,6 +1,8 @@ +init AVIFILE_DllMain + @ stub AVIBuildFilter -@ stub AVIBuildFilterA -@ stub AVIBuildFilterW +@ stdcall AVIBuildFilterA(str long long) AVIBuildFilterA +@ stdcall AVIBuildFilterW(wstr long long) AVIBuildFilterW @ stub AVIClearClipboard @ stdcall AVIFileAddRef(ptr) AVIFileAddRef @ stub AVIFileCreateStream @@ -26,8 +28,8 @@ @ stub AVIPutFileOnClipboard @ stub AVISave @ stub AVISaveA -@ stub AVISaveOptions -@ stub AVISaveOptionsFree +@ stdcall AVISaveOptions(long long long ptr ptr) AVISaveOptions +@ stdcall AVISaveOptionsFree(long ptr) AVISaveOptionsFree @ stub AVISaveV @ stub AVISaveVA @ stub AVISaveVW @@ -59,8 +61,8 @@ @ stdcall AVIStreamWriteData(ptr long ptr long) AVIStreamWriteData @ stub CLSID_AVISimpleUnMarshal @ stub CreateEditableStream -@ stub DllCanUnloadNow -@ stub DllGetClassObject +@ stdcall DllCanUnloadNow() AVIFILE_DllCanUnloadNow +@ stdcall DllGetClassObject(ptr ptr ptr) AVIFILE_DllGetClassObject @ stub EditStreamClone @ stub EditStreamCopy @ stub EditStreamCut @@ -71,7 +73,7 @@ @ stub EditStreamSetName @ stub EditStreamSetNameA @ stub EditStreamSetNameW -@ stub IID_IAVIEditStream -@ stub IID_IAVIFile -@ stub IID_IAVIStream -@ stub IID_IGetFrame +@ extern IID_IAVIEditStream IID_IAVIEditStream +@ extern IID_IAVIFile IID_IAVIFile +@ extern IID_IAVIStream IID_IAVIStream +@ extern IID_IGetFrame IID_IGetFrame Index: dlls/avifil32/avifile.c =================================================================== RCS file: /home/wine/wine/dlls/avifil32/avifile.c,v retrieving revision 1.26 diff -u -r1.26 avifile.c --- dlls/avifil32/avifile.c 10 Oct 2002 23:31:13 -0000 1.26 +++ dlls/avifil32/avifile.c 13 Oct 2002 20:21:24 -0000 @@ -17,9 +17,19 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* TODO: + * - IAVIFile_fnEndRecord: a stub -- needed for creating interleaved AVIs. + * - 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. + */ + #include <assert.h> #include "winbase.h" +#include "winuser.h" #include "winnls.h" #include "winerror.h" #include "windowsx.h" @@ -27,11 +37,16 @@ #include "vfw.h" #include "avifile_private.h" +#include "extrachunk.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); @@ -132,7 +147,26 @@ /* 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 */ + + DWORD dwLastFrame; /* 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 { @@ -146,6 +180,15 @@ AVIFILEINFOW fInfo; IAVIStreamImpl *ppStreams[MAX_AVISTREAMS]; + EXTRACHUNKS fileextra; + + DWORD dwMoviChunkPos; /* some stuff for saving ... */ + DWORD dwIdxChunkPos; + DWORD dwNextFramePos; + + AVIINDEXENTRY *idxRecords; /* won't be updated while loading */ + DWORD nIdxRecords; + /* IPersistFile stuff ... */ HMMIO hmmio; LPWSTR szFileName; @@ -155,6 +198,29 @@ /***********************************************************************/ +static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, + DWORD offset, DWORD flags); +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; @@ -190,9 +256,13 @@ 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; } @@ -203,21 +273,57 @@ { ICOM_THIS(IAVIFileImpl,iface); - TRACE("(%p)\n",iface); + TRACE("(%p) -> %ld\n", iface, This->ref + 1); return ++(This->ref); } static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface) { ICOM_THIS(IAVIFileImpl,iface); + UINT i; + + TRACE("(%p) -> %ld\n", iface, This->ref - 1); - FIXME("(%p): partial stub!\n",iface); if (!--(This->ref)) { if (This->fDirty) { - /* FIXME: write headers to disk */ + /* 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 a reference to stream %u (%p)!\n", + i, This->ppStreams[i]); + } + AVIFILE_DestructAVIStream(This->ppStreams[i]); + LocalFree((HLOCAL)This->ppStreams[i]); + This->ppStreams[i] = NULL; + } } - HeapFree(GetProcessHeap(),0,iface); + 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 != (HMMIO)NULL) { + mmioClose(This->hmmio, 0); + This->hmmio = (HMMIO)NULL; + } + + LocalFree((HLOCAL)This); return 0; } return This->ref; @@ -235,6 +341,8 @@ if (size < 0) return AVIERR_BADSIZE; + AVIFILE_UpdateInfo(This); + memcpy(afi, &This->fInfo, min(size, sizeof(This->fInfo))); if (size < sizeof(This->fInfo)) @@ -245,50 +353,163 @@ static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, PAVISTREAM *avis, DWORD fccType, LONG lParam) { - FIXME("(%p,%p,0x%08lX,%ld): stub\n", iface, avis, fccType, lParam); - /* FIXME: create interface etc. */ - return E_FAIL; + ICOM_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) { -/* ICOM_THIS(IAVIStreamImpl,iface); */ + ICOM_THIS(IAVIFileImpl,iface); - FIXME("(%p,%p,%p): stub\n", iface, avis, asi); + DWORD n; - return AVIERR_UNSUPPORTED; + TRACE("(%p,%p,%p)\n", iface, avis, asi); + + /* check parameters */ + if (avis == NULL || asi == NULL) + return AVIERR_BADPARAM; + + /* 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 AVIERR_OK; } static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid, LPVOID lpData, LONG size) { - FIXME("(%p,0x%08lX,%p,%ld): stub\n", iface, ckid, lpData, size); - /* FIXME: write data to file */ - return E_FAIL; + ICOM_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) { - FIXME("(%p,0x%08lX,%p,%p): stub\n", iface, ckid, lpData, size); - /* FIXME: read at most size bytes from file */ - return E_FAIL; + ICOM_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) { + ICOM_THIS(IAVIFileImpl,iface); + FIXME("(%p): stub\n",iface); + + if ((This->uMode & MMIO_RWMODE) == 0) + return AVIERR_READONLY; + + This->fDirty = TRUE; + /* FIXME: end record -- for interleaved files */ + return E_FAIL; } static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType, LONG lParam) { - FIXME("(%p,0x%08lX,%ld): stub\n", iface, fccType, lParam); - /* FIXME: delete stream */ - return E_FAIL; + ICOM_THIS(IAVIFileImpl,iface); + + ULONG nStream; + + TRACE("(%p,0x%08lX,%ld)\n", iface, fccType, lParam); + + /* check parameter */ + if (lParam < 0) + return AVIERR_BADPARAM; + + /* Habe 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; } /***********************************************************************/ @@ -350,11 +571,41 @@ { ICOM_THIS(IPersistFileImpl,iface); - FIXME("(%p,%s,0x%08lX): stub\n", iface, debugstr_w(pszFileName), dwMode); + 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 != (HMMIO)NULL) + return AVIERR_ERROR; /* No reuse of this object for another file! */ + + /* remeber mode and name */ + This->paf->uMode = dwMode; - return AVIERR_ERROR; + 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 == (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, @@ -416,6 +667,8 @@ if (IsEqualGUID(&IID_IUnknown, refiid) || IsEqualGUID(&IID_IAVIStream, refiid)) { *obj = This; + IAVIStream_AddRef(iface); + return S_OK; } /* FIXME: IAVIStreaming interface */ @@ -427,7 +680,12 @@ { ICOM_THIS(IAVIStreamImpl,iface); - TRACE("(%p)\n",iface); + TRACE("(%p) -> %ld\n", iface, This->ref + 1); + + /* also add ref to parent, so that it doesn't kill us */ + if (This->paf != NULL) + IAVIFile_AddRef((PAVIFILE)This->paf); + return ++(This->ref); } @@ -435,7 +693,7 @@ { ICOM_THIS(IAVIStreamImpl,iface); - TRACE("(%p)\n",iface); + TRACE("(%p) -> %ld\n", iface, This->ref - 1); /* we belong to the AVIFile, which must free us! */ if (This->ref == 0) { @@ -443,6 +701,9 @@ return 0; } + if (This->paf != NULL) + IAVIFile_Release((PAVIFILE)This->paf); + return --This->ref; } @@ -477,7 +738,89 @@ static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos, LONG flags) { - FIXME("(%p,%ld,0x%08lX): stub\n",iface,pos,flags); + ICOM_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->dwLastFrame) { + 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->dwLastFrame) { + 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) { + UINT n; + + if (flags & FIND_NEXT) { + for (n = 0; n < This->sInfo.dwFormatChangeCount; n++) + if (This->idxFmtChanges[n].ckid >= pos) + goto RETURN_FOUND; + } else { + for (n = This->sInfo.dwFormatChangeCount; n >= 0; n--) { + if (This->idxFmtChanges[n].ckid <= pos) + goto RETURN_FOUND; + } + + if (pos > This->sInfo.dwStart) + return 0; /* format changes always for first frame */ + } + } + + return -1; + } + + if (flags & FIND_RET) { + RETURN_FOUND: + if (flags & FIND_LENGTH) { + /* logical size */ + if (This->sInfo.dwSampleSize) + pos = This->sInfo.dwSampleSize; + else + pos = 1; + } else if (flags & FIND_OFFSET) { + /* physical position */ + pos = This->idxFrames[pos].dwChunkOffset + offset * This->sInfo.dwSampleSize; + } else if (flags & FIND_SIZE) { + /* physical size */ + pos = This->idxFrames[pos].dwChunkLength; + } else if (flags & FIND_INDEX) { + FIXME(": FIND_INDEX flag is not supported!"); + + pos = This->paf->dwIdxChunkPos; + } /* else logical position */ + + return pos; + } return -1; } @@ -485,19 +828,149 @@ static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos, LPVOID format, LONG *formatsize) { - FIXME("(%p,%ld,%p,%p): stub\n", iface, pos, format, formatsize); + ICOM_THIS(IAVIStreamImpl,iface); - return E_FAIL; + 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(*formatsize, This->cbFormat)); + if (*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) { + LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)This->lpFormat; + 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) { -/* ICOM_THIS(IAVIStreamImpl,iface); */ + ICOM_THIS(IAVIStreamImpl,iface); - FIXME("(%p,%ld,%p,%ld): stub\n", iface, pos, format, formatsize); + LPBITMAPINFOHEADER lpbiNew = (LPBITMAPINFOHEADER)format; - return E_FAIL; + 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->dwLastFrame > 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; + INT n; + + /* pherhaps formatchange, check it ... */ + if (This->cbFormat != formatsize) + return AVIERR_UNSUPPORTED; + + /* no formatchange, 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, @@ -505,17 +978,109 @@ LONG buffersize, LPLONG bytesread, LPLONG samplesread) { -/* ICOM_THIS(IAVIStreamImpl,iface); */ + ICOM_THIS(IAVIStreamImpl,iface); - FIXME("(%p,%ld,%ld,%p,%ld,%p,%p): stub\n", iface, start, samples, buffer, - buffersize, bytesread, samplesread); + 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; - return E_FAIL; + /* check parameters */ + if (This->sInfo.dwStart > start) + return AVIERR_NODATA; /* couldn't read before start of stream */ + if (This->sInfo.dwStart + This->sInfo.dwLength < 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 (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; + ((BYTE*)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->dwLastFrame); + 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, @@ -524,44 +1089,202 @@ LPLONG sampwritten, LPLONG byteswritten) { -/* ICOM_THIS(IAVIStreamImpl,iface); */ + ICOM_THIS(IAVIStreamImpl,iface); + + FOURCC ckid; + HRESULT hr; - FIXME("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p): stub\n", iface, start, samples, + 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; - if (buffer == NULL && buffersize > 0) + /* check parameters */ + if (buffer == NULL && (buffersize > 0 || samples > 0)) return AVIERR_BADPARAM; - return E_FAIL; + /* 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->dwLastFrame == -1) + start = This->sInfo.dwStart; + else + start = This->sInfo.dwLength; + } + + 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->dwLastFrame + 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->dwLastFrame != -1) { + FOURCC ckid2 = MAKEAVICKID(cktypeDIBcompressed, This->nStream); + + while (start > This->dwLastFrame + 1) { + hr = AVIFILE_WriteBlock(This, This->dwLastFrame + 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) { + ICOM_THIS(IAVIStreamImpl,iface); + FIXME("(%p,%ld,%ld): stub\n", iface, start, samples); - return E_FAIL; + /* 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) { - FIXME("(%p,0x%08lX,%p,%p): stub\n", iface, fcc, lp, lpread); + ICOM_THIS(IAVIStreamImpl,iface); - return E_FAIL; + 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) { - FIXME("(%p,0x%08lx,%p,%ld): stub\n", iface, fcc, lp, size); + ICOM_THIS(IAVIStreamImpl,iface); - return E_FAIL; + 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) + 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, @@ -570,4 +1293,1146 @@ 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) +{ + 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; + This->idxFmtChanges = GlobalReAllocPtr(This->idxFmtChanges, This->nIdxFmtChanges * sizeof(AVIINDEXENTRY), GHND); + if (This->idxFmtChanges == NULL) + return AVIERR_MEMORY; + + This->idxFmtChanges[n].ckid = This->dwLastFrame; + 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->dwLastFrame == -1) + flags |= AVIIF_KEYFRAME; + + if (This->sInfo.dwSuggestedBufferSize < size) + This->sInfo.dwSuggestedBufferSize = size; + + /* get memory for index */ + if (This->idxFrames == NULL || This->dwLastFrame + 1 < This->nIdxFrames) { + This->nIdxFrames += 512; + This->idxFrames = GlobalReAllocPtr(This->idxFrames, This->nIdxFrames * sizeof(AVIINDEXENTRY), GHND); + if (This->idxFrames == NULL) + return AVIERR_MEMORY; + } + + This->dwLastFrame++; + This->idxFrames[This->dwLastFrame].ckid = ckid; + This->idxFrames[This->dwLastFrame].dwFlags = flags; + This->idxFrames[This->dwLastFrame].dwChunkOffset = offset; + This->idxFrames[This->dwLastFrame].dwChunkLength = size; + + 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) & ~1); + if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0) + dwPos += 2 * sizeof(DWORD) + ((pStream->cbHandlerData + 1) & ~1); + if (lstrlenW(pStream->sInfo.szName) > 0) + dwPos += 2 * sizeof(DWORD) + ((lstrlenW(pStream->sInfo.szName) + 1) & ~1); + } + + 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]; + + ICOM_VTBL(pstream) = &iavist; + pstream->ref = 0; + pstream->paf = paf; + pstream->nStream = nr; + pstream->dwCurrentFrame = -1; + pstream->dwLastFrame = -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 = -1; + This->dwLastFrame = -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 == (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; + + /* 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; + 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 WCHAR streamTypeFmt[] = {'%','4','.','4','h','s'}; + + 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) + 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 - 2 * sizeof(DWORD); + 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]->dwLastFrame = -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); + + 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->dwLastFrame = -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 = pStream->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; + 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); + + 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 == TRUE && 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 != (HMMIO)NULL); + assert(This->sInfo.dwStart <= pos && pos < This->sInfo.dwLength); + assert(pos <= This->dwLastFrame); + + /* 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) { + This->lpBuffer = + (LPDWORD)GlobalReAllocPtr(This->lpBuffer, max(size, This->sInfo.dwSuggestedBufferSize), 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) +{ + DWORD 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->dwLastFrame; 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); + + 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; + for (nStream = 0; nStream < MainAVIHdr.dwStreams; nStream++) { + pStream = This->ppStreams[nStream]; + + if (MainAVIHdr.dwInitialFrames < pStream->sInfo.dwInitialFrames) + MainAVIHdr.dwInitialFrames = pStream->sInfo.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 seperate 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.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 hopefull 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); + + 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; n++) { + 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->dwLastFrame <= 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; n++) { + pStream = This->ppStreams[nStream]; + + for (n = 0; n < pStream->dwLastFrame; 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; + + 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->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 = ck.dwDataOffset + ck.cksize; + + return AVIFILE_AddFrame(This, ckid, size, ck.dwDataOffset, flags); } Index: dlls/avifil32/avifile_private.h =================================================================== RCS file: /home/wine/wine/dlls/avifil32/avifile_private.h,v retrieving revision 1.4 diff -u -r1.4 avifile_private.h --- dlls/avifil32/avifile_private.h 10 Oct 2002 23:31:13 -0000 1.4 +++ dlls/avifil32/avifile_private.h 13 Oct 2002 20:21:24 -0000 @@ -23,6 +23,23 @@ #define MAX_AVISTREAMS 4 #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 + +#define IDS_WAVESTREAMFORMAT 0x0100 +#define IDS_WAVEFILETYPE 0x0101 +#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); @@ -33,5 +50,8 @@ 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 PGETFRAME AVIFILE_CreateGetFrame(PAVISTREAM pstream); + +extern LPCWSTR AVIFILE_BasenameW(LPCWSTR szFileName); #endif Index: dlls/avifil32/factory.c =================================================================== RCS file: /home/wine/wine/dlls/avifil32/factory.c,v retrieving revision 1.1 diff -u -r1.1 factory.c --- dlls/avifil32/factory.c 10 Oct 2002 23:31:13 -0000 1.1 +++ dlls/avifil32/factory.c 13 Oct 2002 20:21:24 -0000 @@ -19,6 +19,7 @@ #include <assert.h> #include "winbase.h" +#include "winnls.h" #include "winerror.h" #include "ole2.h" @@ -126,7 +127,8 @@ { ICOM_THIS(IClassFactoryImpl,iface); - FIXME("(%p,%p,%s,%p): stub!\n", iface, pOuter, debugstr_guid(riid), ppobj); + FIXME("(%p,%p,%s,%p): partial stub!\n", iface, pOuter, debugstr_guid(riid), + ppobj); if (ppobj == NULL || pOuter != NULL) return E_FAIL; @@ -134,11 +136,11 @@ if (IsEqualGUID(&CLSID_AVIFile, &This->clsid)) return AVIFILE_CreateAVIFile(riid,ppobj); -/* if (IsEqualGUID(&CLSID_ICMStream, This->clsid)) */ +/* 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)) */ + 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; @@ -153,6 +155,23 @@ 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.@) @@ -181,7 +200,7 @@ BOOL WINAPI AVIFILE_DllMain(HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpvReserved) { - TRACE("(%d,%lu,%p)\n", hInstDll, fdwReason, lpvReserved); + TRACE("(0x%X,%lu,%p)\n", hInstDll, fdwReason, lpvReserved); switch (fdwReason) { case DLL_PROCESS_ATTACH: Index: include/vfw.h =================================================================== RCS file: /home/wine/wine/include/vfw.h,v retrieving revision 1.28 diff -u -r1.28 vfw.h --- include/vfw.h 10 Oct 2002 23:31:13 -0000 1.28 +++ include/vfw.h 13 Oct 2002 20:21:29 -0000 @@ -793,6 +793,8 @@ #define AVI_HEADERSIZE 2048 +typedef BOOL (CALLBACK *AVISAVECALLBACK)(INT); + typedef struct _MainAVIHeader { DWORD dwMicroSecPerFrame; @@ -907,6 +909,7 @@ #define AVIFILEINFO_HASINDEX 0x00000010 #define AVIFILEINFO_MUSTUSEINDEX 0x00000020 #define AVIFILEINFO_ISINTERLEAVED 0x00000100 +#define AVIFILEINFO_TRUSTCKTYPE 0x00000800 #define AVIFILEINFO_WASCAPTUREFILE 0x00010000 #define AVIFILEINFO_COPYRIGHTED 0x00020000 @@ -968,6 +971,23 @@ DWORD dwInterleaveEvery; /* for non-video streams only */ } AVICOMPRESSOPTIONS, *LPAVICOMPRESSOPTIONS,*PAVICOMPRESSOPTIONS; +#define FIND_DIR 0x0000000FL /* direction mask */ +#define FIND_NEXT 0x00000001L /* search forward */ +#define FIND_PREV 0x00000004L /* search backward */ +#define FIND_FROM_START 0x00000008L /* start at the logical beginning */ + +#define FIND_TYPE 0x000000F0L /* type mask */ +#define FIND_KEY 0x00000010L /* find a key frame */ +#define FIND_ANY 0x00000020L /* find any (non-empty) sample */ +#define FIND_FORMAT 0x00000040L /* find a formatchange */ + +#define FIND_RET 0x0000F000L /* return mask */ +#define FIND_POS 0x00000000L /* return logical position */ +#define FIND_LENGTH 0x00001000L /* return logical size */ +#define FIND_OFFSET 0x00002000L /* return physical position */ +#define FIND_SIZE 0x00003000L /* return physical size */ +#define FIND_INDEX 0x00004000L /* return physical index position */ + #include "ole2.h" #define DEFINE_AVIGUID(name, l, w1, w2) \ @@ -1048,13 +1068,33 @@ UINT mode, CLSID *pclsidHandler); #define AVIStreamOpenFromFile WINELIB_NAME_AW(AVIStreamOpenFromFile) +HRESULT WINAPI AVIBuildFilterA(LPSTR szFilter, LONG cbFilter, BOOL fSaving); +HRESULT WINAPI AVIBuildFilterW(LPWSTR szFilter, LONG cbFilter, BOOL fSaving); +#define AVIBuildFilter WINELIB_NAME_AW(AVIBuildFilter) + +BOOL WINAPI AVISaveOptions(HWND hWnd,UINT uFlags,INT nStream, + PAVISTREAM *ppavi,LPAVICOMPRESSOPTIONS *ppOptions); +HRESULT WINAPI AVISaveOptionsFree(INT nStreams,LPAVICOMPRESSOPTIONS*ppOptions); + LONG WINAPI AVIStreamStart(PAVISTREAM iface); LONG WINAPI AVIStreamLength(PAVISTREAM iface); LONG WINAPI AVIStreamSampleToTime(PAVISTREAM pstream, LONG lSample); LONG WINAPI AVIStreamTimeToSample(PAVISTREAM pstream, LONG lTime); +#define AVIStreamEnd(pavi) \ + (AVIStreamStart(pavi) + AVIStreamLength(pavi)) +#define AVIStreamEndTime(pavi) \ + AVIStreamSampleToTime(pavi, AVIStreamEnd(pavi)) #define AVIStreamFormatSize(pavi, lPos, plSize) \ AVIStreamReadFormat(pavi, lPos, NULL, plSize) +#define AVIStreamLengthTime(pavi) \ + AVIStreamSampleToTime(pavi, AVIStreamLength(pavi)) +#define AVIStreamSampleSize(pavi,pos,psize) \ + AVIStreamRead(pavi,pos,1,NULL,0,psize,NULL) +#define AVIStreamSampleToSample(pavi1, pavi2, samp2) \ + AVIStreamTimeToSample(pavi1, AVIStreamSampleToTime(pavi2, samp2)) +#define AVIStreamStartTime(pavi) \ + AVIStreamSampleToTime(pavi, AVIStreamStart(pavi)) /***************************************************************************** * IAVIFile interface Index: programs/avitools/aviinfo.c =================================================================== RCS file: /home/wine/wine/programs/avitools/aviinfo.c,v retrieving revision 1.10 diff -u -r1.10 aviinfo.c --- programs/avitools/aviinfo.c 31 May 2002 23:40:58 -0000 1.10 +++ programs/avitools/aviinfo.c 13 Oct 2002 20:21:30 -0000 @@ -147,10 +147,10 @@ type[4]='\0';memcpy(type,&(asi.fccType),4); fprintf(stderr,"Unhandled streamtype %s\n",type); - fnAVIStreamRelease(ast); break; } } + fnAVIStreamRelease(ast); } fnAVIFileRelease(avif); fnAVIFileExit(); --- /dev/null Sat Feb 23 16:05:24 2002 +++ dlls/avifil32/extrachunk.c Fri Oct 11 23:36:39 2002 @@ -0,0 +1,196 @@ +/* + * 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 "extrachunk.h" +#include "winbase.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; + LONG 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],*size)); + + *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; + ((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; + LONG cb; + + /* pre-conditions */ + assert(extra != NULL); + assert(hmmio != (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; + ((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) != lpck->cksize) + return AVIERR_FILEREAD; + } + + return AVIERR_OK; +} + +/* reads all non-junk chunks into the extrachunk-structure until it founds + * 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 != (HMMIO)NULL); + assert(lpck != NULL); + + TRACE("({%p,%lu},%d,%p,%p,0x%X)\n", extra->lp, extra->cb, hmmio, lpck, + lpckParent, flags); + + /* what chunk id and form/list type shoiuld 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 infront 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; + } +} --- /dev/null Sat Feb 23 16:05:24 2002 +++ dlls/avifil32/extrachunk.h Fri Oct 11 23:36:55 2002 @@ -0,0 +1,52 @@ +/* + * 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 "windef.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 founds + * 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 --- /dev/null Sat Feb 23 16:05:24 2002 +++ dlls/avifil32/getframe.c Sun Oct 13 21:01:11 2002 @@ -0,0 +1,495 @@ +/* + * Copyright 2002 Michael Günnewig + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <assert.h> + +#include "winbase.h" +#include "winnls.h" +#include "windowsx.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); + +struct ICOM_VTABLE(IGetFrame) igetframeVtbl = { + ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE + IGetFrame_fnQueryInterface, + IGetFrame_fnAddRef, + IGetFrame_fnRelease, + IGetFrame_fnGetFrame, + IGetFrame_fnBegin, + IGetFrame_fnEnd, + IGetFrame_fnSetFormat +}; + +typedef struct _IGetFrameImpl { + /* IUnknown stuff */ + ICOM_VFIELD(IGetFrame); + DWORD ref; + + /* IGetFrame stuff */ + BOOL bFixedStream; + PAVISTREAM pStream; + + LPVOID lpInBuffer; + DWORD cbInBuffer; + LPBITMAPINFOHEADER lpInFormat; + DWORD 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 != (HIC)NULL) { + if (This->bResize) + ICDecompressExEnd(This->hic); + else + ICDecompressEnd(This->hic); + ICClose(This->hic); + This->hic = (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) { + ICOM_VTBL(pg) = &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) +{ + ICOM_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) +{ + ICOM_THIS(IGetFrameImpl,iface); + + TRACE("(%p)\n", iface); + + return ++(This->ref); +} + +static ULONG WINAPI IGetFrame_fnRelease(IGetFrame *iface) +{ + ICOM_THIS(IGetFrameImpl,iface); + + TRACE("(%p)\n", iface); + + if (!--(This->ref)) { + AVIFILE_CloseCompressor(This); + if (This->pStream != NULL) { + AVIStreamRelease(This->pStream); + This->pStream = NULL; + } + + LocalFree((HLOCAL)iface); + return 0; + } + + return This->ref; +} + +static LPVOID WINAPI IGetFrame_fnGetFrame(IGetFrame *iface, LONG lPos) +{ + ICOM_THIS(IGetFrameImpl,iface); + + LONG readBytes; + LONG readSamples; + + TRACE("(%p,%ld)\n", iface, lPos); + + /* 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 = AVIStreamFindSample(This->pStream, lPos, FIND_KEY|FIND_PREV); + + if (lNext == -1) + lNext = 0; /* first frame is always a keyframe */ + if (lNext <= This->lCurrentFrame && This->lCurrentFrame < lPos) + lNext++; + + for (; lNext < lPos; lNext++) { + /* new format for this frame? */ + if (This->bFormatChanges) { + AVIStreamReadFormat(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; + if (This->cbInBuffer >= readBytes) + break; + This->lpInFormat = GlobalReAllocPtr(This->lpInFormat, This->cbInFormat + readBytes, 0); + if (This->lpInFormat == NULL) + return NULL; + This->lpInBuffer = (BYTE*)This->lpInFormat + This->cbInFormat; + } + + if (readSamples != 1) + return NULL; + if (readBytes != 0) { + This->lpInFormat->biSizeImage = readBytes; + + /* nothing to decompress? */ + if (This->hic == (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 == (HIC)NULL ? This->lpInFormat : This->lpOutFormat); +} + +static HRESULT WINAPI IGetFrame_fnBegin(IGetFrame *iface, LONG lStart, + LONG lEnd, LONG lRate) +{ + ICOM_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) +{ + ICOM_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) +{ + ICOM_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 ((LONG)lpbiWanted == 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) { + This->cbInBuffer = sInfo.dwSuggestedBufferSize; + if (This->cbInBuffer == 0) + This->cbInBuffer = 1024; + + AVIStreamFormatSize(This->pStream, sInfo.dwStart, &This->cbInFormat); + + This->lpInFormat = + (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, This->cbInFormat + This->cbInBuffer); + if (This->lpInFormat == NULL) { + AVIFILE_CloseCompressor(This); + return AVIERR_MEMORY; + } + + AVIStreamReadFormat(This->pStream, sInfo.dwStart, This->lpInFormat, &This->cbInFormat); + + 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 == (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 == (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; + } +} + +/***********************************************************************/ + --- /dev/null Sat Feb 23 16:05:24 2002 +++ dlls/avifil32/wavfile.c Sun Oct 13 18:41:46 2002 @@ -0,0 +1,1070 @@ +/* + * Copyright 2002 Michael Günnewig + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <assert.h> + +#include "winbase.h" +#include "winuser.h" +#include "winnls.h" +#include "winerror.h" +#include "windowsx.h" +#include "mmsystem.h" +#include "vfw.h" + +#include "avifile_private.h" +#include "extrachunk.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(avifile); + +/***********************************************************************/ + +#define formtypeWAVE mmioFOURCC('W','A','V','E') +#define ckidWAVEFORMAT mmioFOURCC('f','m','t',' ') +#define ckidWAVEFACT mmioFOURCC('f','a','c','t') +#define ckidWAVEDATA mmioFOURCC('d','a','t','a') + +/***********************************************************************/ + +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); + +struct ICOM_VTABLE(IAVIFile) iwavft = { + ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE + 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); + +struct ICOM_VTABLE(IPersistFile) iwavpft = { + ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE + 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); + +struct ICOM_VTABLE(IAVIStream) iwavst = { + ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE + 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 */ + ICOM_VFIELD(IPersistFile); + + /* IPersistFile stuff */ + IAVIFileImpl *paf; +} IPersistFileImpl; + +typedef struct _IAVIStreamImpl { + /* IUnknown stuff */ + ICOM_VFIELD(IAVIStream); + + /* IAVIStream stuff */ + IAVIFileImpl *paf; +} IAVIStreamImpl; + +struct _IAVIFileImpl { + /* IUnknown stuff */ + ICOM_VFIELD(IAVIFile); + DWORD ref; + + /* IAVIFile, IAVIStream stuff... */ + IPersistFileImpl iPersistFile; + IAVIStreamImpl iAVIStream; + + AVIFILEINFOW fInfo; + AVISTREAMINFOW sInfo; + + LPWAVEFORMATEX lpFormat; + LONG cbFormat; + + MMCKINFO ckData; + + EXTRACHUNKS extra; + + /* IPersistFile stuff ... */ + HMMIO hmmio; + LPWSTR szFileName; + UINT uMode; + BOOL fDirty; +}; + +/***********************************************************************/ + +static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This); +static HRESULT AVIFILE_LoadSunFile(IAVIFileImpl *This); +static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This); + +HRESULT AVIFILE_CreateWAVFile(REFIID riid, LPVOID *ppv) +{ + IAVIFileImpl *pfile; + HRESULT hr; + + assert(riid != NULL && ppv != NULL); + + *ppv = NULL; + + pfile = (IAVIFileImpl*)LocalAlloc(LPTR, sizeof(IAVIFileImpl)); + if (pfile == NULL) + return AVIERR_MEMORY; + + ICOM_VTBL(pfile) = &iwavft; + ICOM_VTBL(&pfile->iPersistFile) = &iwavpft; + ICOM_VTBL(&pfile->iAVIStream) = &iwavst; + pfile->ref = 0; + pfile->iPersistFile.paf = pfile; + pfile->iAVIStream.paf = pfile; + + hr = IUnknown_QueryInterface((IUnknown*)pfile, riid, ppv); + if (FAILED(hr)) + LocalFree((HLOCAL)pfile); + + return hr; +} + +static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID refiid, + LPVOID *obj) +{ + ICOM_THIS(IAVIFileImpl,iface); + + TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj); + + if (IsEqualGUID(&IID_IUnknown, refiid) || + IsEqualGUID(&IID_IAVIFile, refiid)) { + *obj = iface; + return S_OK; + } else if (This->fInfo.dwStreams == 1 && + IsEqualGUID(&IID_IAVIStream, refiid)) { + *obj = &This->iAVIStream; + return S_OK; + } else if (IsEqualGUID(&IID_IPersistFile, refiid)) { + *obj = &This->iPersistFile; + return S_OK; + } + + return OLE_E_ENUM_NOMORE; +} + +static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface) +{ + ICOM_THIS(IAVIFileImpl,iface); + + TRACE("(%p)\n",iface); + + return ++(This->ref); +} + +static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface) +{ + ICOM_THIS(IAVIFileImpl,iface); + + TRACE("(%p)\n",iface); + + if (!--(This->ref)) { + if (This->fDirty) { + /* need to write headers to file */ + AVIFILE_SaveFile(This); + } + + if (This->lpFormat != NULL) { + GlobalFreePtr(This->lpFormat); + This->lpFormat = NULL; + This->cbFormat = 0; + } + if (This->extra.lp != NULL) { + GlobalFreePtr(This->extra.lp); + This->extra.lp = NULL; + This->extra.cb = 0; + } + if (This->szFileName != NULL) { + LocalFree((HLOCAL)This->szFileName); + This->szFileName = NULL; + } + if (This->hmmio != (HMMIO)NULL) { + mmioClose(This->hmmio, 0); + This->hmmio = (HMMIO)NULL; + } + + LocalFree((HLOCAL)This); + return 0; + } + return This->ref; +} + +static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, LPAVIFILEINFOW afi, + LONG size) +{ + ICOM_THIS(IAVIFileImpl,iface); + + TRACE("(%p,%p,%ld)\n",iface,afi,size); + + if (afi == NULL) + return AVIERR_BADPARAM; + if (size < 0) + return AVIERR_BADSIZE; + + /* update file info */ + This->fInfo.dwFlags = 0; + This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; + if (This->lpFormat != NULL) { + This->fInfo.dwStreams = 1; + This->fInfo.dwScale = This->sInfo.dwScale; + This->fInfo.dwRate = This->sInfo.dwRate; + This->fInfo.dwLength = This->sInfo.dwLength; + This->fInfo.dwSuggestedBufferSize = This->ckData.cksize; + } + + memcpy(afi, &This->fInfo, min(size, sizeof(This->fInfo))); + + if (size < sizeof(This->fInfo)) + return AVIERR_BUFFERTOOSMALL; + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, PAVISTREAM *avis, + DWORD fccType, LONG lParam) +{ + ICOM_THIS(IAVIFileImpl,iface); + + TRACE("(%p,%p,0x%08lX,%ld)\n", iface, avis, fccType, lParam); + + /* check parameter */ + if (avis == NULL) + return AVIERR_BADPARAM; + + *avis = NULL; + + /* Does our stream exists? */ + if (lParam != 0 || This->fInfo.dwStreams == 0) + return AVIERR_NODATA; + if (fccType != 0 && fccType != streamtypeAUDIO) + return AVIERR_NODATA; + + *avis = (PAVISTREAM)&This->iAVIStream; + IAVIFile_AddRef(iface); + + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface,PAVISTREAM *avis, + LPAVISTREAMINFOW asi) +{ + ICOM_THIS(IAVIFileImpl,iface); + + TRACE("(%p,%p,%p)\n", iface, avis, asi); + + /* check parameters */ + if (avis == NULL || asi == NULL) + return AVIERR_BADPARAM; + + *avis = NULL; + + /* We only support one audio stream */ + if (This->fInfo.dwStreams != 0 || This->lpFormat != NULL) + return AVIERR_UNSUPPORTED; + if (asi->fccType != streamtypeAUDIO) + return AVIERR_UNSUPPORTED; + + /* Does the user have write permission? */ + if ((This->uMode & MMIO_RWMODE) == 0) + return AVIERR_READONLY; + + This->cbFormat = 0; + This->lpFormat = NULL; + + memcpy(&This->sInfo, asi, sizeof(This->sInfo)); + + /* make sure streaminfo if okay for us */ + This->sInfo.fccHandler = 0; + This->sInfo.dwFlags = 0; + This->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; + This->sInfo.dwStart = 0; + This->sInfo.dwInitialFrames = 0; + This->sInfo.dwFormatChangeCount = 0; + memset(&This->sInfo.rcFrame, 0, sizeof(This->sInfo.rcFrame)); + + This->fInfo.dwStreams = 1; + This->fInfo.dwScale = This->sInfo.dwScale; + This->fInfo.dwRate = This->sInfo.dwRate; + This->fInfo.dwLength = This->sInfo.dwLength; + + This->ckData.dwDataOffset = 0; + This->ckData.cksize = 0; + + *avis = (PAVISTREAM)&This->iAVIStream; + IAVIFile_AddRef(iface); + + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid, + LPVOID lpData, LONG size) +{ + ICOM_THIS(IAVIFileImpl,iface); + + TRACE("(%p,0x%08lX,%p,%ld)\n", iface, ckid, lpData, size); + + /* check parameters */ + if (lpData == NULL) + return AVIERR_BADPARAM; + if (size < 0) + return AVIERR_BADSIZE; + + /* Do we have write permission? */ + if ((This->uMode & MMIO_RWMODE) == 0) + return AVIERR_READONLY; + + This->fDirty = TRUE; + + return WriteExtraChunk(&This->extra, ckid, lpData, size); +} + +static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid, + LPVOID lpData, LONG *size) +{ + ICOM_THIS(IAVIFileImpl,iface); + + TRACE("(%p,0x%08lX,%p,%p)\n", iface, ckid, lpData, size); + + return ReadExtraChunk(&This->extra, ckid, lpData, size); +} + +static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface) +{ + TRACE("(%p)\n",iface); + + /* This is only needed for interleaved files. + * We have only one stream, which can't be interleaved. + */ + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType, + LONG lParam) +{ + ICOM_THIS(IAVIFileImpl,iface); + + TRACE("(%p,0x%08lX,%ld)\n", iface, fccType, lParam); + + /* check parameter */ + if (lParam < 0) + return AVIERR_BADPARAM; + + /* Have we our audio stream? */ + if (lParam != 0 || This->fInfo.dwStreams == 0 || + (fccType != 0 && fccType != streamtypeAUDIO)) + return AVIERR_NODATA; + + /* Have user write permissions? */ + if ((This->uMode & MMIO_RWMODE) == 0) + return AVIERR_READONLY; + + GlobalFreePtr(This->lpFormat); + This->lpFormat = NULL; + This->cbFormat = 0; + + /* update infos */ + This->ckData.dwDataOffset = 0; + This->ckData.cksize = 0; + + This->sInfo.dwScale = 0; + This->sInfo.dwRate = 0; + This->sInfo.dwLength = 0; + This->sInfo.dwSuggestedBufferSize = 0; + + This->fInfo.dwStreams = 0; + This->fInfo.dwEditCount++; + + This->fDirty = TRUE; + + return AVIERR_OK; +} + +/***********************************************************************/ + +static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface, + REFIID refiid, LPVOID *obj) +{ + ICOM_THIS(IPersistFileImpl,iface); + + assert(This->paf != NULL); + + return IAVIFile_QueryInterface((PAVIFILE)This->paf, refiid, obj); +} + +static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile *iface) +{ + ICOM_THIS(IPersistFileImpl,iface); + + assert(This->paf != NULL); + + return IAVIFile_AddRef((PAVIFILE)This->paf); +} + +static ULONG WINAPI IPersistFile_fnRelease(IPersistFile *iface) +{ + ICOM_THIS(IPersistFileImpl,iface); + + assert(This->paf != NULL); + + return IAVIFile_Release((PAVIFILE)This->paf); +} + +static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface, + LPCLSID pClassID) +{ + TRACE("(%p,%p)\n", iface, pClassID); + + if (pClassID == NULL) + return AVIERR_BADPARAM; + + memcpy(pClassID, &CLSID_WAVFile, sizeof(CLSID_WAVFile)); + + return AVIERR_OK; +} + +static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface) +{ + ICOM_THIS(IPersistFileImpl,iface); + + TRACE("(%p)\n", iface); + + assert(This->paf != NULL); + + return (This->paf->fDirty ? S_OK : S_FALSE); +} + +static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface, + LPCOLESTR pszFileName, DWORD dwMode) +{ + IAVIFileImpl *This = ((IPersistFileImpl*)iface)->paf; + + WCHAR wszStreamFmt[50]; + INT len; + + TRACE("(%p,%s,0x%08lX)\n", iface, debugstr_w(pszFileName), dwMode); + + /* check parameter */ + if (pszFileName == NULL) + return AVIERR_BADPARAM; + + assert(This != NULL); + if (This->hmmio != (HMMIO)NULL) + return AVIERR_ERROR; /* No reuse of this object for another file! */ + + /* remeber mode and name */ + This->uMode = dwMode; + + len = lstrlenW(pszFileName) + 1; + This->szFileName = (LPWSTR)LocalAlloc(LPTR, len * sizeof(WCHAR)); + if (This->szFileName == NULL) + return AVIERR_MEMORY; + lstrcpyW(This->szFileName, pszFileName); + + /* try to open the file */ + This->hmmio = mmioOpenW(This->szFileName, NULL, MMIO_ALLOCBUF | dwMode); + if (This->hmmio == (HMMIO)NULL) + return AVIERR_FILEOPEN; + + memset(& This->fInfo, 0, sizeof(This->fInfo)); + memset(& This->sInfo, 0, sizeof(This->sInfo)); + + LoadStringW(AVIFILE_hModule, IDS_WAVEFILETYPE, This->fInfo.szFileType, + sizeof(This->fInfo.szFileType)); + if (LoadStringW(AVIFILE_hModule, IDS_WAVESTREAMFORMAT, + wszStreamFmt, sizeof(wszStreamFmt)) > 0) { + wsprintfW(This->sInfo.szName, wszStreamFmt, + AVIFILE_BasenameW(This->szFileName)); + } + + /* should we create a new file? */ + if (dwMode & OF_CREATE) { + /* nothing more to do */ + return AVIERR_OK; + } else + return AVIFILE_LoadFile(This); +} + +static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface, + LPCOLESTR pszFileName,BOOL fRemember) +{ + TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember); + + /* We write directly to disk, so nothing to do. */ + + return AVIERR_OK; +} + +static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface, + LPCOLESTR pszFileName) +{ + TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName)); + + /* We write directly to disk, so nothing to do. */ + + return AVIERR_OK; +} + +static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface, + LPOLESTR *ppszFileName) +{ + ICOM_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); + + *ppszFileName = (LPOLESTR)GlobalAllocPtr(GHND, len * sizeof(WCHAR)); + if (*ppszFileName == NULL) + return AVIERR_MEMORY; + + memcpy(*ppszFileName, This->paf->szFileName, len * sizeof(WCHAR)); + } + + return AVIERR_OK; +} + +/***********************************************************************/ + +static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface, + REFIID refiid, LPVOID *obj) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + assert(This->paf != NULL); + + return IAVIFile_QueryInterface((PAVIFILE)This->paf, refiid, obj); +} + +static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + assert(This->paf != NULL); + + return IAVIFile_AddRef((PAVIFILE)This->paf); +} + +static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + assert(This->paf != NULL); + + return IAVIFile_Release((PAVIFILE)This->paf); +} + +static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1, + LPARAM lParam2) +{ + TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2); + + /* This IAVIStream interface needs an WAVFile */ + return AVIERR_UNSUPPORTED; +} + +static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi, + LONG size) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + TRACE("(%p,%p,%ld)\n", iface, psi, size); + + if (psi == NULL) + return AVIERR_BADPARAM; + if (size < 0) + return AVIERR_BADSIZE; + + memcpy(psi, &This->paf->sInfo, min(size, sizeof(This->paf->sInfo))); + + if (size < sizeof(This->paf->sInfo)) + return AVIERR_BUFFERTOOSMALL; + return AVIERR_OK; +} + +static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos, + LONG flags) +{ + IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf; + + TRACE("(%p,%ld,0x%08lX)\n",iface,pos,flags); + + /* Do we have data? */ + if (This->lpFormat == NULL) + return -1; + + /* We don't have an index */ + if (flags & FIND_INDEX) + return -1; + + if (flags & FIND_FROM_START) { + pos = This->sInfo.dwStart; + flags &= ~(FIND_FROM_START|FIND_PREV); + flags |= FIND_NEXT; + } + + if (flags & FIND_FORMAT) { + if ((flags & FIND_NEXT) && pos > 0) + pos = -1; + else + pos = 0; + } + + if (flags & (FIND_LENGTH|FIND_SIZE)) + return This->sInfo.dwSampleSize; + if (flags & FIND_OFFSET) + return This->ckData.dwDataOffset + pos * This->sInfo.dwSampleSize; + + return pos; +} + +static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos, + LPVOID format, LONG *formatsize) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + TRACE("(%p,%ld,%p,%p)\n", iface, pos, format, formatsize); + + if (formatsize == NULL) + return AVIERR_BADPARAM; + + /* only interested in needed buffersize? */ + if (format == NULL || *formatsize <= 0) { + *formatsize = This->paf->cbFormat; + + return AVIERR_OK; + } + + /* copy initial format (only as much as will fit) */ + memcpy(format, This->paf->lpFormat, min(*formatsize, This->paf->cbFormat)); + if (*formatsize < This->paf->cbFormat) { + *formatsize = This->paf->cbFormat; + return AVIERR_BUFFERTOOSMALL; + } + + *formatsize = This->paf->cbFormat; + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos, + LPVOID format, LONG formatsize) +{ + IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf; + + TRACE("(%p,%ld,%p,%ld)\n", iface, pos, format, formatsize); + + /* check parameters */ + if (format == NULL || formatsize <= sizeof(PCMWAVEFORMAT)) + return AVIERR_BADPARAM; + + /* We can only do this to an empty wave file, but ignore call + * if still same format */ + if (This->lpFormat != NULL) { + if (formatsize != This->cbFormat || + memcmp(format, This->lpFormat, formatsize) != 0) + return AVIERR_UNSUPPORTED; + + return AVIERR_OK; + } + + /* only support start at position 0 */ + if (pos != 0) + return AVIERR_UNSUPPORTED; + + /* Do we have write permission? */ + if ((This->uMode & MMIO_RWMODE) == 0) + return AVIERR_READONLY; + + /* get memory for format and copy it */ + This->lpFormat = (LPWAVEFORMATEX)GlobalAllocPtr(GMEM_MOVEABLE, formatsize); + if (This->lpFormat == NULL) + return AVIERR_MEMORY; + + This->cbFormat = formatsize; + memcpy(This->lpFormat, format, formatsize); + + /* update info's about 'data' chunk */ + This->ckData.dwDataOffset = formatsize + 7 * sizeof(DWORD); + This->ckData.cksize = 0; + + /* for non-pcm format we need also a 'fact' chunk */ + if (This->lpFormat->wFormatTag != WAVE_FORMAT_PCM) + This->ckData.dwDataOffset += 3 * sizeof(DWORD); + + /* update stream and file info */ + This->sInfo.dwSampleSize = This->lpFormat->nBlockAlign; + This->sInfo.dwScale = This->lpFormat->nBlockAlign; + This->sInfo.dwRate = This->lpFormat->nAvgBytesPerSec; + This->sInfo.dwLength = 0; + This->sInfo.dwSuggestedBufferSize = 0; + + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start, + LONG samples, LPVOID buffer, + LONG buffersize, LPLONG bytesread, + LPLONG samplesread) +{ + IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf; + + TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface, start, samples, buffer, + buffersize, bytesread, samplesread); + + /* clear return parameters if given */ + if (bytesread != NULL) + *bytesread = 0; + if (samplesread != NULL) + *samplesread = 0; + + /* positions without data */ + if (start < 0 || start > This->sInfo.dwLength) + return AVIERR_OK; + + /* check samples */ + if (samples < 0) + samples = 0; + if (buffersize > 0) { + if (samples > 0) + samples = min(samples, buffersize / This->sInfo.dwSampleSize); + else + samples = buffersize / This->sInfo.dwSampleSize; + } + + /* limit to end of stream */ + if (start + samples > This->sInfo.dwLength) + samples = This->sInfo.dwLength - start; + + /* request only the sizes? */ + if (buffer == NULL || buffersize <= 0) { + /* then I need atleast one parameter for it */ + if (bytesread == NULL && samplesread == NULL) + return AVIERR_BADPARAM; + + if (bytesread != NULL) + *bytesread = samples * This->sInfo.dwSampleSize; + if (samplesread != NULL) + *samplesread = samples; + + return AVIERR_OK; + } + + /* nothing to read? */ + if (samples == 0) + return AVIERR_OK; + + /* Can I atleast read one sample? */ + if (buffersize < This->sInfo.dwSampleSize) + return AVIERR_BUFFERTOOSMALL; + + buffersize = samples * This->sInfo.dwSampleSize; + + if (mmioSeek(This->hmmio, This->ckData.dwDataOffset + + start * This->sInfo.dwSampleSize, SEEK_SET) == -1) + return AVIERR_FILEREAD; + if (mmioRead(This->hmmio, (HPSTR)buffer, buffersize) != buffersize) + return AVIERR_FILEREAD; + + /* fill out return parameters if given */ + if (bytesread != NULL) + *bytesread = buffersize; + if (samplesread != NULL) + *samplesread = samples; + + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start, + LONG samples, LPVOID buffer, + LONG buffersize, DWORD flags, + LPLONG sampwritten, + LPLONG byteswritten) +{ + IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf; + + TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n", iface, start, samples, + buffer, buffersize, flags, sampwritten, byteswritten); + + /* clear return parameters if given */ + if (sampwritten != NULL) + *sampwritten = 0; + if (byteswritten != NULL) + *byteswritten = 0; + + /* check parameters */ + if (buffer == NULL && (buffersize > 0 || samples > 0)) + return AVIERR_BADPARAM; + + /* Have we write permission? */ + if ((This->uMode & MMIO_RWMODE) == 0) + return AVIERR_READONLY; + + /* < 0 means "append" */ + if (start < 0) + start = This->sInfo.dwStart + This->sInfo.dwLength; + + /* check buffersize -- must multiple of samplesize */ + if (buffersize & ~(This->sInfo.dwSampleSize - 1)) + return AVIERR_BADSIZE; + + /* have we anything to write? */ + if (buffer != NULL && buffersize > 0) { + This->fDirty = 1; + + if (mmioSeek(This->hmmio, This->ckData.dwDataOffset + + start * This->sInfo.dwSampleSize, SEEK_SET) == -1) + return AVIERR_FILEWRITE; + if (mmioWrite(This->hmmio, (HPSTR)buffer, buffersize) != buffersize) + return AVIERR_FILEWRITE; + + This->sInfo.dwLength = max(This->sInfo.dwLength, start + samples); + This->ckData.cksize = max(This->ckData.cksize, + start * This->sInfo.dwSampleSize + buffersize); + + /* fill out return parameters if given */ + if (sampwritten != NULL) + *sampwritten = samples; + if (byteswritten != NULL) + *byteswritten = buffersize; + } + + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start, + LONG samples) +{ + IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf; + + TRACE("(%p,%ld,%ld)\n", iface, start, samples); + + /* check parameters */ + if (start < 0 || samples < 0) + return AVIERR_BADPARAM; + + /* Delete before start of stream? */ + if (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->uMode & MMIO_RWMODE) == 0) + return AVIERR_READONLY; + + if (start + samples >= This->sInfo.dwLength) { + /* deletion at end */ + samples = This->sInfo.dwLength - start; + This->sInfo.dwLength -= samples; + This->ckData.cksize -= samples * This->sInfo.dwSampleSize; + } else if (start <= This->sInfo.dwStart) { + /* deletion at start */ + samples = This->sInfo.dwStart - start; + start = This->sInfo.dwStart; + This->ckData.dwDataOffset += samples * This->sInfo.dwSampleSize; + This->ckData.cksize -= samples * This->sInfo.dwSampleSize; + } else { + /* deletion inside stream -- needs playlist and cue's */ + FIXME(": deletion inside of stream not supported!\n"); + + return AVIERR_UNSUPPORTED; + } + + This->fDirty = 1; + + return AVIERR_OK; +} + +static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc, + LPVOID lp, LPLONG lpread) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + assert(This->paf != NULL); + + return IAVIFile_ReadData((PAVIFILE)This->paf, fcc, lp, lpread); +} + +static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc, + LPVOID lp, LONG size) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + return IAVIFile_WriteData((PAVIFILE)This->paf, fcc, lp, size); +} + +static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream *iface, + LPAVISTREAMINFOW info, LONG infolen) +{ + FIXME("(%p,%p,%ld): stub\n", iface, info, infolen); + + return E_FAIL; +} + +/***********************************************************************/ + +static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This) +{ + MMCKINFO ckRIFF; + MMCKINFO ck; + + This->sInfo.dwLength = 0; /* just to be sure */ + This->fDirty = FALSE; + + /* search for RIFF chunk */ + if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) { + return AVIFILE_LoadSunFile(This); + } + + if (ckRIFF.fccType != formtypeWAVE) + return AVIERR_BADFORMAT; + + /* search WAVE format chunk */ + ck.ckid = ckidWAVEFORMAT; + if (FindChunkAndKeepExtras(&This->extra, This->hmmio, &ck, + &ckRIFF, MMIO_FINDCHUNK != S_OK)) + return AVIERR_FILEREAD; + + /* get memory for format and read it */ + This->lpFormat = (LPWAVEFORMATEX)GlobalAllocPtr(GMEM_MOVEABLE, ck.cksize); + if (This->lpFormat == NULL) + return AVIERR_FILEREAD; + This->cbFormat = ck.cksize; + + if (mmioRead(This->hmmio, (HPSTR)This->lpFormat, ck.cksize) != ck.cksize) + return AVIERR_FILEREAD; + if (mmioAscend(This->hmmio, &ck, 0) != S_OK) + return AVIERR_FILEREAD; + + /* non-pcm formats have a fact chunk */ + if (This->lpFormat->wFormatTag != WAVE_FORMAT_PCM) { + ck.ckid = ckidWAVEFACT; + if (mmioDescend(This->hmmio, &ck, NULL, MMIO_FINDCHUNK) != S_OK) { + /* FIXME: if codec is installed could ask him */ + return AVIERR_BADFORMAT; + } + } + + /* find the big data chunk */ + This->ckData.ckid = ckidWAVEDATA; + if (FindChunkAndKeepExtras(&This->extra, This->hmmio, &This->ckData, + &ckRIFF, MMIO_FINDCHUNK) != S_OK) + return AVIERR_FILEREAD; + + memset(&This->sInfo, 0, sizeof(This->sInfo)); + This->sInfo.fccType = streamtypeAUDIO; + This->sInfo.dwRate = This->lpFormat->nAvgBytesPerSec; + This->sInfo.dwSampleSize = + This->sInfo.dwScale = This->lpFormat->nBlockAlign; + This->sInfo.dwLength = This->ckData.cksize / This->lpFormat->nBlockAlign; + This->sInfo.dwSuggestedBufferSize = This->ckData.cksize; + + if (mmioAscend(This->hmmio, &This->ckData, 0) != S_OK) { + /* seems to be truncated */ + WARN(": file seems to be truncated!\n"); + This->ckData.cksize = mmioSeek(This->hmmio, 0, SEEK_END) - + This->ckData.dwDataOffset; + This->sInfo.dwLength = This->ckData.cksize / This->lpFormat->nBlockAlign; + This->sInfo.dwSuggestedBufferSize = This->ckData.cksize; + } + + /* ignore errors */ + FindChunkAndKeepExtras(&This->extra, This->hmmio, &ck, &ckRIFF, 0); + + return AVIERR_OK; +} + +static HRESULT AVIFILE_LoadSunFile(IAVIFileImpl *This) +{ + FIXME(": pherhpas sun-audio file -- not implemented !\n"); + + return AVIERR_UNSUPPORTED; +} + +static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This) +{ + return AVIERR_UNSUPPORTED; +} + --- /dev/null Sat Feb 23 16:05:24 2002 +++ dlls/avifil32/rsrc.rc Fri Oct 11 21:13:40 2002 @@ -0,0 +1,46 @@ +/* + * Top level resource file for avifil32.dll + * + */ + +#include "windef.h" +#include "winuser.h" +#include "winver.h" +#include "avifile_private.h" + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL + +1 VERSIONINFO +FILEVERSION 4, 3, 0, 1998 +PRODUCTVERSION 4, 3, 0, 1998 +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef NDEBUG +FILEFLAGS 0 +#else +FILEFLAGS VS_FF_DEBUG +#endif +FILEOS VOS_DOS_WINDOWS32 +FILETYPE VFT_DLL +{ + BLOCK "StringFileInfo" + { + BLOCK "040604B0" /* Deutschland (Standard) */ + { + VALUE "CompanyName", "Wine Developer Team\000" + VALUE "FileDescription", "Wine Bibliothek zur Unterstützung von AVI-Dateien\000" + VALUE "FileVersion", "4.03.1998\000" + VALUE "InternalName", "AVIFIL32\000" + VALUE "LegalCopyright", "Copyright \251 Michael Günnewig 2002\000" + VALUE "OriginalFileName", "AVIFIL32.DLL\000" + VALUE "ProductName", "Wine\000" + VALUE "ProductVersion", "1.00\000" + } + } +} + +/* + * Everything specific to any language goes + * in one of the specific files. + */ +#include "avifile_De.rc" +#include "avifile_En.rc" --- /dev/null Sat Feb 23 16:05:24 2002 +++ dlls/avifil32/avifile_De.rc Fri Oct 11 21:13:49 2002 @@ -0,0 +1,12 @@ +LANGUAGE LANG_GERMAN, SUBLANG_GERMAN + +STRINGTABLE DISCARDABLE +{ + IDS_WAVESTREAMFORMAT "Waveform: %s" + IDS_WAVEFILETYPE "Waveform" + IDS_VIDEO "Video" + IDS_AUDIO "Audio" + IDS_AVISTREAMFORMAT "%s %s #%d" + IDS_AVIFILETYPE "Wine AVI-Standard-Dateibehandlungsroutine" + IDS_UNCOMPRESSED "Unkomprimiert" +} --- /dev/null Sat Feb 23 16:05:24 2002 +++ dlls/avifil32/avifile_En.rc Fri Oct 11 21:30:16 2002 @@ -0,0 +1,12 @@ +LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT + +STRINGTABLE DISCARDABLE +{ + IDS_WAVESTREAMFORMAT "Waveform: %s" + IDS_WAVEFILETYPE "Waveform" + IDS_VIDEO "video" + IDS_AUDIO "audio" + IDS_AVISTREAMFORMAT "%s %s #%d" + IDS_AVIFILETYPE "Wine AVI-default-filehandler" + IDS_UNCOMPRESSED "uncompressed" +}
Michael Günnewig