Hallo. This patch adds many functionality to the avifil32.dll. It's now possible to use the wine/programs/avitools/aviinfo programm. - added DllCanUnloadNow, DllGetClassObject to avifil32.spec - implemented AVIFileOpenW - implemented many functions of the AVI filehandler. - added resources to avifil32 - added FIND_* consts to include/vfw.h - added registry entries for avifile.dll and avifil32.dll
Index: winedefault.reg =================================================================== RCS file: /home/wine/wine/winedefault.reg,v retrieving revision 1.54 diff -u -r1.54 winedefault.reg --- winedefault.reg 20 Sep 2002 19:38:35 -0000 1.54 +++ winedefault.reg 11 Oct 2002 21:49:09 -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 11 Oct 2002 21:49:10 -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 11 Oct 2002 21:49:10 -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 11 Oct 2002 21:49:10 -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,11 @@ C_SRCS = \ api.c \ avifile.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 11 Oct 2002 21:49:10 -0000 @@ -125,6 +125,25 @@ return S_OK; } +static BOOL AVIFILE_GetFileHandlerByExtension(LPCWSTR szFile, LPCLSID lpclsid) +{ + CHAR szRegKey[25]; + CHAR szValue[100]; + LPWSTR szExt = strrchrW(szFile, L'.'); + 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 +209,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 +220,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)); @@ -820,3 +840,4 @@ return (LONG)(((float)lTime * asiw.dwRate) / asiw.dwScale / 1000.0); } + 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 11 Oct 2002 21:49:10 -0000 @@ -1,3 +1,5 @@ +init AVIFILE_DllMain + @ stub AVIBuildFilter @ stub AVIBuildFilterA @ stub AVIBuildFilterW @@ -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 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 11 Oct 2002 21:49:12 -0000 @@ -20,6 +20,7 @@ #include <assert.h> #include "winbase.h" +#include "winuser.h" #include "winnls.h" #include "winerror.h" #include "windowsx.h" @@ -27,6 +28,7 @@ #include "vfw.h" #include "avifile_private.h" +#include "extrachunk.h" #include "wine/debug.h" @@ -132,7 +134,22 @@ /* IAVIStream stuff */ IAVIFileImpl *paf; + DWORD nStream; /* the n-th stream in file */ AVISTREAMINFOW sInfo; + + LPVOID lpFormat; + DWORD cbFormat; + + LPVOID lpHandlerData; + DWORD cbHandlerData; + + EXTRACHUNKS extra; + + 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 +163,13 @@ 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 */ + /* IPersistFile stuff ... */ HMMIO hmmio; LPWSTR szFileName; @@ -155,6 +179,17 @@ /***********************************************************************/ +static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, + DWORD offset, DWORD flags); +static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, + LPAVISTREAMINFOW asi); +static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This); +static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This); +static void AVIFILE_SamplesToBlock(IAVIStreamImpl *This, LPLONG pos, + LPLONG offset); +static ULONG AVIFILE_SearchStream(IAVIFileImpl *This, DWORD fccType, + LONG lSkip); + HRESULT AVIFILE_CreateAVIFile(REFIID riid, LPVOID *ppv) { IAVIFileImpl *pfile; @@ -210,14 +245,40 @@ static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface) { ICOM_THIS(IAVIFileImpl,iface); + UINT i; + + TRACE("(%p)\n",iface); - FIXME("(%p): partial stub!\n",iface); if (!--(This->ref)) { if (This->fDirty) { - /* FIXME: write headers to disk */ + FIXME(": need to write headers to file -- not impemented!\n"); + } + + for (i = 0; i < This->fInfo.dwStreams; i++) { + if (This->ppStreams[i] != NULL) { + AVIFILE_DestructAVIStream(This->ppStreams[i]); + LocalFree((HLOCAL)This->ppStreams[i]); + This->ppStreams[i] = NULL; + } + } + + if (This->idxRecords != NULL) { + GlobalFreePtr(This->idxRecords); + This->idxRecords = NULL; } - HeapFree(GetProcessHeap(),0,iface); + 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; + } + + LocalFree((HLOCAL)This); return 0; } return This->ref; @@ -245,50 +306,160 @@ 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; + + /* FIXME: here we could update our AVIFILEINFO structure -- should we? */ + + 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; + + 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 +521,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, @@ -477,7 +678,88 @@ 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 +767,147 @@ 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); + + return AVIFILE_AddFrame(This, cktypePALchange, n, ck.dwDataOffset, 0); + } } static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start, @@ -551,17 +961,55 @@ 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; + + 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 +1018,512 @@ 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 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 = 1; + pstream->paf = paf; + pstream->nStream = nr; + 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) { + /* FIXME: pre-allocate mem for formatchange-index structure */ + } + + pstream->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; + + /* 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); + + /* FIXME? */ + } +} + +static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This) +{ + /* pre-conditions */ + assert(This != NULL); + + This->dwLastFrame = -1; + 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->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)) { + ERR(": no AVI header list found!\n"); + return hr; + } + + /* get "avih" chunk */ + ck.ckid = ckidAVIMAINHDR; + hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, MMIO_FINDCHUNK); + if (FAILED(hr)) { + ERR(": no AVI mainheader found!\n"); + return hr; + } + + if (ck.cksize != sizeof(MainAVIHdr)) { + FIXME(": 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; + case 0: + ERR(": Oops, internal error at line %d\n", __LINE__); + return AVIERR_INTERNAL; + 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) { + FIXME(": parse 'idx1' chunk -- not implemented\n"); + + 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 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 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; } 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 11 Oct 2002 21:49:12 -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,7 @@ 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 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 11 Oct 2002 21:49:12 -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; @@ -153,6 +155,23 @@ return S_OK; } +#define SLASH(w) ((w) == L'/' || (w) == L'\\') + +LPCWSTR AVIFILE_BasenameW(LPCWSTR szPath) +{ + LPCWSTR szCur; + + for (szCur = szPath + lstrlenW(szPath); + szCur > szPath && !SLASH(*szCur) && *szCur != L':';) + 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 11 Oct 2002 21:49:16 -0000 @@ -907,6 +907,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 @@ -967,6 +968,23 @@ DWORD cbParms; 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 /* seacrh 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" --- /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/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