/* * Copyright 2009 Tony Wasserka * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "wine/debug.h" #define COBJMACROS #include "windef.h" #include "winbase.h" #include "winreg.h" #include "objbase.h" #include "shlwapi.h" #include "wincodecs_private.h" WINE_DEFAULT_DEBUG_CHANNEL(wincodecs); /****************************************** * StreamOnMemory implementation * * Used by IWICStream_InitializeFromMemory * */ typedef struct StreamOnMemory { IStream IStream_iface; LONG ref; BYTE *pbMemory; DWORD dwMemsize; DWORD dwCurPos; CRITICAL_SECTION lock; /* must be held when pbMemory or dwCurPos is accessed */ } StreamOnMemory; static inline StreamOnMemory *StreamOnMemory_from_IStream(IStream *iface) { return CONTAINING_RECORD(iface, StreamOnMemory, IStream_iface); } static HRESULT WINAPI StreamOnMemory_QueryInterface(IStream *iface, REFIID iid, void **ppv) { TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv); if (!ppv) return E_INVALIDARG; if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IStream, iid) || IsEqualIID(&IID_ISequentialStream, iid)) { *ppv = iface; IUnknown_AddRef((IUnknown*)*ppv); return S_OK; } else { *ppv = NULL; return E_NOINTERFACE; } } static ULONG WINAPI StreamOnMemory_AddRef(IStream *iface) { StreamOnMemory *This = StreamOnMemory_from_IStream(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p) refcount=%u\n", iface, ref); return ref; } static ULONG WINAPI StreamOnMemory_Release(IStream *iface) { StreamOnMemory *This = StreamOnMemory_from_IStream(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p) refcount=%u\n", iface, ref); if (ref == 0) { This->lock.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&This->lock); HeapFree(GetProcessHeap(), 0, This); } return ref; } static HRESULT WINAPI StreamOnMemory_Read(IStream *iface, void *pv, ULONG cb, ULONG *pcbRead) { StreamOnMemory *This = StreamOnMemory_from_IStream(iface); ULONG uBytesRead; TRACE("(%p, %p, %u, %p)\n", This, pv, cb, pcbRead); if (!pv) return E_INVALIDARG; EnterCriticalSection(&This->lock); uBytesRead = min(cb, This->dwMemsize - This->dwCurPos); memmove(pv, This->pbMemory + This->dwCurPos, uBytesRead); This->dwCurPos += uBytesRead; LeaveCriticalSection(&This->lock); if (pcbRead) *pcbRead = uBytesRead; return S_OK; } static HRESULT WINAPI StreamOnMemory_Write(IStream *iface, void const *pv, ULONG cb, ULONG *pcbWritten) { StreamOnMemory *This = StreamOnMemory_from_IStream(iface); HRESULT hr; TRACE("(%p, %p, %u, %p)\n", This, pv, cb, pcbWritten); if (!pv) return E_INVALIDARG; EnterCriticalSection(&This->lock); if (cb > This->dwMemsize - This->dwCurPos) { hr = STG_E_MEDIUMFULL; } else { memmove(This->pbMemory + This->dwCurPos, pv, cb); This->dwCurPos += cb; hr = S_OK; if (pcbWritten) *pcbWritten = cb; } LeaveCriticalSection(&This->lock); return hr; } static HRESULT WINAPI StreamOnMemory_Seek(IStream *iface, LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition) { StreamOnMemory *This = StreamOnMemory_from_IStream(iface); LARGE_INTEGER NewPosition; HRESULT hr=S_OK; TRACE("(%p, %s, %d, %p)\n", This, wine_dbgstr_longlong(dlibMove.QuadPart), dwOrigin, plibNewPosition); EnterCriticalSection(&This->lock); if (dwOrigin == STREAM_SEEK_SET) NewPosition.QuadPart = dlibMove.QuadPart; else if (dwOrigin == STREAM_SEEK_CUR) NewPosition.QuadPart = This->dwCurPos + dlibMove.QuadPart; else if (dwOrigin == STREAM_SEEK_END) NewPosition.QuadPart = This->dwMemsize + dlibMove.QuadPart; else hr = E_INVALIDARG; if (SUCCEEDED(hr)) { if (NewPosition.u.HighPart) hr = HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); else if (NewPosition.QuadPart > This->dwMemsize) hr = E_INVALIDARG; else if (NewPosition.QuadPart < 0) hr = E_INVALIDARG; } if (SUCCEEDED(hr)) { This->dwCurPos = NewPosition.u.LowPart; if(plibNewPosition) plibNewPosition->QuadPart = This->dwCurPos; } LeaveCriticalSection(&This->lock); return hr; } /* SetSize isn't implemented in the native windowscodecs DLL either */ static HRESULT WINAPI StreamOnMemory_SetSize(IStream *iface, ULARGE_INTEGER libNewSize) { TRACE("(%p, %s)\n", iface, wine_dbgstr_longlong(libNewSize.QuadPart)); return E_NOTIMPL; } /* CopyTo isn't implemented in the native windowscodecs DLL either */ static HRESULT WINAPI StreamOnMemory_CopyTo(IStream *iface, IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten) { TRACE("(%p, %p, %s, %p, %p)\n", iface, pstm, wine_dbgstr_longlong(cb.QuadPart), pcbRead, pcbWritten); return E_NOTIMPL; } static HRESULT WINAPI StreamOnMemory_Commit(IStream *iface, DWORD grfCommitFlags) { TRACE("(%p, %#x)\n", iface, grfCommitFlags); return S_OK; } /* Revert isn't implemented in the native windowscodecs DLL either */ static HRESULT WINAPI StreamOnMemory_Revert(IStream *iface) { TRACE("(%p)\n", iface); return E_NOTIMPL; } /* LockRegion isn't implemented in the native windowscodecs DLL either */ static HRESULT WINAPI StreamOnMemory_LockRegion(IStream *iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { TRACE("(%p, %s, %s, %d)\n", iface, wine_dbgstr_longlong(libOffset.QuadPart), wine_dbgstr_longlong(cb.QuadPart), dwLockType); return E_NOTIMPL; } /* UnlockRegion isn't implemented in the native windowscodecs DLL either */ static HRESULT WINAPI StreamOnMemory_UnlockRegion(IStream *iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { TRACE("(%p, %s, %s, %d)\n", iface, wine_dbgstr_longlong(libOffset.QuadPart), wine_dbgstr_longlong(cb.QuadPart), dwLockType); return E_NOTIMPL; } static HRESULT WINAPI StreamOnMemory_Stat(IStream *iface, STATSTG *pstatstg, DWORD grfStatFlag) { StreamOnMemory *This = StreamOnMemory_from_IStream(iface); TRACE("(%p, %p, %#x)\n", This, pstatstg, grfStatFlag); if (!pstatstg) return E_INVALIDARG; ZeroMemory(pstatstg, sizeof(STATSTG)); pstatstg->type = STGTY_STREAM; pstatstg->cbSize.QuadPart = This->dwMemsize; return S_OK; } /* Clone isn't implemented in the native windowscodecs DLL either */ static HRESULT WINAPI StreamOnMemory_Clone(IStream *iface, IStream **ppstm) { TRACE("(%p, %p)\n", iface, ppstm); return E_NOTIMPL; } static const IStreamVtbl StreamOnMemory_Vtbl = { /*** IUnknown methods ***/ StreamOnMemory_QueryInterface, StreamOnMemory_AddRef, StreamOnMemory_Release, /*** ISequentialStream methods ***/ StreamOnMemory_Read, StreamOnMemory_Write, /*** IStream methods ***/ StreamOnMemory_Seek, StreamOnMemory_SetSize, StreamOnMemory_CopyTo, StreamOnMemory_Commit, StreamOnMemory_Revert, StreamOnMemory_LockRegion, StreamOnMemory_UnlockRegion, StreamOnMemory_Stat, StreamOnMemory_Clone, }; /****************************************** * StreamOnFileHandle implementation (internal) * */ typedef struct StreamOnFileHandle { IStream IStream_iface; LONG ref; HANDLE map; void *mem; IWICStream *stream; } StreamOnFileHandle; static inline StreamOnFileHandle *StreamOnFileHandle_from_IStream(IStream *iface) { return CONTAINING_RECORD(iface, StreamOnFileHandle, IStream_iface); } static HRESULT WINAPI StreamOnFileHandle_QueryInterface(IStream *iface, REFIID iid, void **ppv) { TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv); if (!ppv) return E_INVALIDARG; if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IStream, iid) || IsEqualIID(&IID_ISequentialStream, iid)) { *ppv = iface; IUnknown_AddRef((IUnknown*)*ppv); return S_OK; } else { *ppv = NULL; return E_NOINTERFACE; } } static ULONG WINAPI StreamOnFileHandle_AddRef(IStream *iface) { StreamOnFileHandle *This = StreamOnFileHandle_from_IStream(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p) refcount=%u\n", iface, ref); return ref; } static ULONG WINAPI StreamOnFileHandle_Release(IStream *iface) { StreamOnFileHandle *This = StreamOnFileHandle_from_IStream(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p) refcount=%u\n", iface, ref); if (ref == 0) { IWICStream_Release(This->stream); UnmapViewOfFile(This->mem); CloseHandle(This->map); HeapFree(GetProcessHeap(), 0, This); } return ref; } static HRESULT WINAPI StreamOnFileHandle_Read(IStream *iface, void *pv, ULONG cb, ULONG *pcbRead) { StreamOnFileHandle *This = StreamOnFileHandle_from_IStream(iface); TRACE("(%p, %p, %u, %p)\n", This, pv, cb, pcbRead); return IWICStream_Read(This->stream, pv, cb, pcbRead); } static HRESULT WINAPI StreamOnFileHandle_Write(IStream *iface, void const *pv, ULONG cb, ULONG *pcbWritten) { ERR("(%p, %p, %u, %p)\n", iface, pv, cb, pcbWritten); return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); } static HRESULT WINAPI StreamOnFileHandle_Seek(IStream *iface, LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition) { StreamOnFileHandle *This = StreamOnFileHandle_from_IStream(iface); TRACE("(%p, %s, %d, %p)\n", This, wine_dbgstr_longlong(dlibMove.QuadPart), dwOrigin, plibNewPosition); return IWICStream_Seek(This->stream, dlibMove, dwOrigin, plibNewPosition); } static HRESULT WINAPI StreamOnFileHandle_SetSize(IStream *iface, ULARGE_INTEGER libNewSize) { TRACE("(%p, %s)\n", iface, wine_dbgstr_longlong(libNewSize.QuadPart)); return E_NOTIMPL; } static HRESULT WINAPI StreamOnFileHandle_CopyTo(IStream *iface, IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten) { TRACE("(%p, %p, %s, %p, %p)\n", iface, pstm, wine_dbgstr_longlong(cb.QuadPart), pcbRead, pcbWritten); return E_NOTIMPL; } static HRESULT WINAPI StreamOnFileHandle_Commit(IStream *iface, DWORD grfCommitFlags) { TRACE("(%p, %#x)\n", iface, grfCommitFlags); return S_OK; } static HRESULT WINAPI StreamOnFileHandle_Revert(IStream *iface) { TRACE("(%p)\n", iface); return E_NOTIMPL; } static HRESULT WINAPI StreamOnFileHandle_LockRegion(IStream *iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { TRACE("(%p, %s, %s, %d)\n", iface, wine_dbgstr_longlong(libOffset.QuadPart), wine_dbgstr_longlong(cb.QuadPart), dwLockType); return E_NOTIMPL; } static HRESULT WINAPI StreamOnFileHandle_UnlockRegion(IStream *iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { TRACE("(%p, %s, %s, %d)\n", iface, wine_dbgstr_longlong(libOffset.QuadPart), wine_dbgstr_longlong(cb.QuadPart), dwLockType); return E_NOTIMPL; } static HRESULT WINAPI StreamOnFileHandle_Stat(IStream *iface, STATSTG *pstatstg, DWORD grfStatFlag) { StreamOnFileHandle *This = StreamOnFileHandle_from_IStream(iface); TRACE("(%p, %p, %#x)\n", This, pstatstg, grfStatFlag); return IWICStream_Stat(This->stream, pstatstg, grfStatFlag); } static HRESULT WINAPI StreamOnFileHandle_Clone(IStream *iface, IStream **ppstm) { TRACE("(%p, %p)\n", iface, ppstm); return E_NOTIMPL; } static const IStreamVtbl StreamOnFileHandle_Vtbl = { /*** IUnknown methods ***/ StreamOnFileHandle_QueryInterface, StreamOnFileHandle_AddRef, StreamOnFileHandle_Release, /*** ISequentialStream methods ***/ StreamOnFileHandle_Read, StreamOnFileHandle_Write, /*** IStream methods ***/ StreamOnFileHandle_Seek, StreamOnFileHandle_SetSize, StreamOnFileHandle_CopyTo, StreamOnFileHandle_Commit, StreamOnFileHandle_Revert, StreamOnFileHandle_LockRegion, StreamOnFileHandle_UnlockRegion, StreamOnFileHandle_Stat, StreamOnFileHandle_Clone, }; /****************************************** * StreamOnStreamRange implementation * * Used by IWICStream_InitializeFromIStreamRegion * */ typedef struct StreamOnStreamRange { IStream IStream_iface; LONG ref; IStream *stream; ULARGE_INTEGER pos; ULARGE_INTEGER offset; ULARGE_INTEGER max_size; CRITICAL_SECTION lock; } StreamOnStreamRange; static inline StreamOnStreamRange *StreamOnStreamRange_from_IStream(IStream *iface) { return CONTAINING_RECORD(iface, StreamOnStreamRange, IStream_iface); } static HRESULT WINAPI StreamOnStreamRange_QueryInterface(IStream *iface, REFIID iid, void **ppv) { TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv); if (!ppv) return E_INVALIDARG; if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IStream, iid) || IsEqualIID(&IID_ISequentialStream, iid)) { *ppv = iface; IUnknown_AddRef((IUnknown*)*ppv); return S_OK; } else { *ppv = NULL; return E_NOINTERFACE; } } static ULONG WINAPI StreamOnStreamRange_AddRef(IStream *iface) { StreamOnStreamRange *This = StreamOnStreamRange_from_IStream(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p) refcount=%u\n", iface, ref); return ref; } static ULONG WINAPI StreamOnStreamRange_Release(IStream *iface) { StreamOnStreamRange *This = StreamOnStreamRange_from_IStream(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p) refcount=%u\n", iface, ref); if (ref == 0) { This->lock.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&This->lock); IStream_Release(This->stream); HeapFree(GetProcessHeap(), 0, This); } return ref; } static HRESULT WINAPI StreamOnStreamRange_Read(IStream *iface, void *pv, ULONG cb, ULONG *pcbRead) { StreamOnStreamRange *This = StreamOnStreamRange_from_IStream(iface); ULONG uBytesRead=0; HRESULT hr; ULARGE_INTEGER OldPosition; LARGE_INTEGER SetPosition; TRACE("(%p, %p, %u, %p)\n", This, pv, cb, pcbRead); if (!pv) return E_INVALIDARG; EnterCriticalSection(&This->lock); SetPosition.QuadPart = 0; hr = IStream_Seek(This->stream, SetPosition, STREAM_SEEK_CUR, &OldPosition); if (SUCCEEDED(hr)) { SetPosition.QuadPart = This->pos.QuadPart + This->offset.QuadPart; hr = IStream_Seek(This->stream, SetPosition, STREAM_SEEK_SET, NULL); } if (SUCCEEDED(hr)) { if (This->pos.QuadPart + cb > This->max_size.QuadPart) { /* This would read past the end of the stream. */ if (This->pos.QuadPart > This->max_size.QuadPart) cb = 0; else cb = This->max_size.QuadPart - This->pos.QuadPart; } hr = IStream_Read(This->stream, pv, cb, &uBytesRead); SetPosition.QuadPart = OldPosition.QuadPart; IStream_Seek(This->stream, SetPosition, STREAM_SEEK_SET, NULL); } if (SUCCEEDED(hr)) This->pos.QuadPart += uBytesRead; LeaveCriticalSection(&This->lock); if (SUCCEEDED(hr) && pcbRead) *pcbRead = uBytesRead; return hr; } static HRESULT WINAPI StreamOnStreamRange_Write(IStream *iface, void const *pv, ULONG cb, ULONG *pcbWritten) { StreamOnStreamRange *This = StreamOnStreamRange_from_IStream(iface); HRESULT hr; ULARGE_INTEGER OldPosition; LARGE_INTEGER SetPosition; ULONG uBytesWritten=0; TRACE("(%p, %p, %u, %p)\n", This, pv, cb, pcbWritten); if (!pv) return E_INVALIDARG; EnterCriticalSection(&This->lock); SetPosition.QuadPart = 0; hr = IStream_Seek(This->stream, SetPosition, STREAM_SEEK_CUR, &OldPosition); if (SUCCEEDED(hr)) { SetPosition.QuadPart = This->pos.QuadPart + This->offset.QuadPart; hr = IStream_Seek(This->stream, SetPosition, STREAM_SEEK_SET, NULL); } if (SUCCEEDED(hr)) { if (This->pos.QuadPart + cb > This->max_size.QuadPart) { /* This would read past the end of the stream. */ if (This->pos.QuadPart > This->max_size.QuadPart) cb = 0; else cb = This->max_size.QuadPart - This->pos.QuadPart; } hr = IStream_Write(This->stream, pv, cb, &uBytesWritten); SetPosition.QuadPart = OldPosition.QuadPart; IStream_Seek(This->stream, SetPosition, STREAM_SEEK_SET, NULL); } if (SUCCEEDED(hr)) This->pos.QuadPart += uBytesWritten; LeaveCriticalSection(&This->lock); if (SUCCEEDED(hr) && pcbWritten) *pcbWritten = uBytesWritten; return hr; } static HRESULT WINAPI StreamOnStreamRange_Seek(IStream *iface, LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition) { StreamOnStreamRange *This = StreamOnStreamRange_from_IStream(iface); ULARGE_INTEGER NewPosition, actual_size; HRESULT hr=S_OK; STATSTG statstg; TRACE("(%p, %s, %d, %p)\n", This, wine_dbgstr_longlong(dlibMove.QuadPart), dwOrigin, plibNewPosition); EnterCriticalSection(&This->lock); actual_size = This->max_size; if (dwOrigin == STREAM_SEEK_SET) NewPosition.QuadPart = dlibMove.QuadPart; else if (dwOrigin == STREAM_SEEK_CUR) NewPosition.QuadPart = This->pos.QuadPart + dlibMove.QuadPart; else if (dwOrigin == STREAM_SEEK_END) { hr = IStream_Stat(This->stream, &statstg, STATFLAG_NONAME); if (SUCCEEDED(hr)) { if (This->max_size.QuadPart + This->offset.QuadPart > statstg.cbSize.QuadPart) actual_size.QuadPart = statstg.cbSize.QuadPart - This->offset.QuadPart; NewPosition.QuadPart = dlibMove.QuadPart + actual_size.QuadPart; } } else hr = E_INVALIDARG; if (SUCCEEDED(hr) && (NewPosition.u.HighPart != 0 || NewPosition.QuadPart > actual_size.QuadPart)) hr = WINCODEC_ERR_VALUEOUTOFRANGE; if (SUCCEEDED(hr)) { This->pos.QuadPart = NewPosition.QuadPart; if(plibNewPosition) plibNewPosition->QuadPart = This->pos.QuadPart; } LeaveCriticalSection(&This->lock); return hr; } /* SetSize isn't implemented in the native windowscodecs DLL either */ static HRESULT WINAPI StreamOnStreamRange_SetSize(IStream *iface, ULARGE_INTEGER libNewSize) { TRACE("(%p, %s)\n", iface, wine_dbgstr_longlong(libNewSize.QuadPart)); return E_NOTIMPL; } /* CopyTo isn't implemented in the native windowscodecs DLL either */ static HRESULT WINAPI StreamOnStreamRange_CopyTo(IStream *iface, IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten) { TRACE("(%p, %p, %s, %p, %p)\n", iface, pstm, wine_dbgstr_longlong(cb.QuadPart), pcbRead, pcbWritten); return E_NOTIMPL; } /* Commit isn't implemented in the native windowscodecs DLL either */ static HRESULT WINAPI StreamOnStreamRange_Commit(IStream *iface, DWORD grfCommitFlags) { StreamOnStreamRange *This = StreamOnStreamRange_from_IStream(iface); TRACE("(%p, %#x)\n", This, grfCommitFlags); return IStream_Commit(This->stream, grfCommitFlags); } /* Revert isn't implemented in the native windowscodecs DLL either */ static HRESULT WINAPI StreamOnStreamRange_Revert(IStream *iface) { TRACE("(%p)\n", iface); return E_NOTIMPL; } /* LockRegion isn't implemented in the native windowscodecs DLL either */ static HRESULT WINAPI StreamOnStreamRange_LockRegion(IStream *iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { TRACE("(%p, %s, %s, %d)\n", iface, wine_dbgstr_longlong(libOffset.QuadPart), wine_dbgstr_longlong(cb.QuadPart), dwLockType); return E_NOTIMPL; } /* UnlockRegion isn't implemented in the native windowscodecs DLL either */ static HRESULT WINAPI StreamOnStreamRange_UnlockRegion(IStream *iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { TRACE("(%p, %s, %s, %d)\n", iface, wine_dbgstr_longlong(libOffset.QuadPart), wine_dbgstr_longlong(cb.QuadPart), dwLockType); return E_NOTIMPL; } static HRESULT WINAPI StreamOnStreamRange_Stat(IStream *iface, STATSTG *pstatstg, DWORD grfStatFlag) { StreamOnStreamRange *This = StreamOnStreamRange_from_IStream(iface); HRESULT hr; TRACE("(%p, %p, %#x)\n", This, pstatstg, grfStatFlag); if (!pstatstg) return E_INVALIDARG; EnterCriticalSection(&This->lock); hr = IStream_Stat(This->stream, pstatstg, grfStatFlag); if (SUCCEEDED(hr)) { pstatstg->cbSize.QuadPart -= This->offset.QuadPart; if (This->max_size.QuadPart < pstatstg->cbSize.QuadPart) pstatstg->cbSize.QuadPart = This->max_size.QuadPart; } LeaveCriticalSection(&This->lock); return hr; } /* Clone isn't implemented in the native windowscodecs DLL either */ static HRESULT WINAPI StreamOnStreamRange_Clone(IStream *iface, IStream **ppstm) { TRACE("(%p, %p)\n", iface, ppstm); return E_NOTIMPL; } static const IStreamVtbl StreamOnStreamRange_Vtbl = { /*** IUnknown methods ***/ StreamOnStreamRange_QueryInterface, StreamOnStreamRange_AddRef, StreamOnStreamRange_Release, /*** ISequentialStream methods ***/ StreamOnStreamRange_Read, StreamOnStreamRange_Write, /*** IStream methods ***/ StreamOnStreamRange_Seek, StreamOnStreamRange_SetSize, StreamOnStreamRange_CopyTo, StreamOnStreamRange_Commit, StreamOnStreamRange_Revert, StreamOnStreamRange_LockRegion, StreamOnStreamRange_UnlockRegion, StreamOnStreamRange_Stat, StreamOnStreamRange_Clone, }; /****************************************** * IWICStream implementation * */ typedef struct IWICStreamImpl { IWICStream IWICStream_iface; LONG ref; IStream *pStream; } IWICStreamImpl; static inline IWICStreamImpl *impl_from_IWICStream(IWICStream *iface) { return CONTAINING_RECORD(iface, IWICStreamImpl, IWICStream_iface); } static HRESULT WINAPI IWICStreamImpl_QueryInterface(IWICStream *iface, REFIID iid, void **ppv) { IWICStreamImpl *This = impl_from_IWICStream(iface); TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv); if (!ppv) return E_INVALIDARG; if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IStream, iid) || IsEqualIID(&IID_ISequentialStream, iid) || IsEqualIID(&IID_IWICStream, iid)) { *ppv = &This->IWICStream_iface; IUnknown_AddRef((IUnknown*)*ppv); return S_OK; } else { *ppv = NULL; return E_NOINTERFACE; } } static ULONG WINAPI IWICStreamImpl_AddRef(IWICStream *iface) { IWICStreamImpl *This = impl_from_IWICStream(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p) refcount=%u\n", iface, ref); return ref; } static ULONG WINAPI IWICStreamImpl_Release(IWICStream *iface) { IWICStreamImpl *This = impl_from_IWICStream(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p) refcount=%u\n", iface, ref); if (ref == 0) { if (This->pStream) IStream_Release(This->pStream); HeapFree(GetProcessHeap(), 0, This); } return ref; } static HRESULT WINAPI IWICStreamImpl_Read(IWICStream *iface, void *pv, ULONG cb, ULONG *pcbRead) { IWICStreamImpl *This = impl_from_IWICStream(iface); TRACE("(%p, %p, %u, %p)\n", This, pv, cb, pcbRead); if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED; return IStream_Read(This->pStream, pv, cb, pcbRead); } static HRESULT WINAPI IWICStreamImpl_Write(IWICStream *iface, void const *pv, ULONG cb, ULONG *pcbWritten) { IWICStreamImpl *This = impl_from_IWICStream(iface); TRACE("(%p, %p, %u, %p)\n", This, pv, cb, pcbWritten); if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED; return IStream_Write(This->pStream, pv, cb, pcbWritten); } static HRESULT WINAPI IWICStreamImpl_Seek(IWICStream *iface, LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition) { IWICStreamImpl *This = impl_from_IWICStream(iface); TRACE("(%p, %s, %d, %p)\n", This, wine_dbgstr_longlong(dlibMove.QuadPart), dwOrigin, plibNewPosition); if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED; return IStream_Seek(This->pStream, dlibMove, dwOrigin, plibNewPosition); } static HRESULT WINAPI IWICStreamImpl_SetSize(IWICStream *iface, ULARGE_INTEGER libNewSize) { IWICStreamImpl *This = impl_from_IWICStream(iface); TRACE("(%p, %s)\n", This, wine_dbgstr_longlong(libNewSize.QuadPart)); if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED; return IStream_SetSize(This->pStream, libNewSize); } static HRESULT WINAPI IWICStreamImpl_CopyTo(IWICStream *iface, IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten) { IWICStreamImpl *This = impl_from_IWICStream(iface); TRACE("(%p, %p, %s, %p, %p)\n", This, pstm, wine_dbgstr_longlong(cb.QuadPart), pcbRead, pcbWritten); if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED; return IStream_CopyTo(This->pStream, pstm, cb, pcbRead, pcbWritten); } static HRESULT WINAPI IWICStreamImpl_Commit(IWICStream *iface, DWORD grfCommitFlags) { IWICStreamImpl *This = impl_from_IWICStream(iface); TRACE("(%p, %#x)\n", This, grfCommitFlags); if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED; return IStream_Commit(This->pStream, grfCommitFlags); } static HRESULT WINAPI IWICStreamImpl_Revert(IWICStream *iface) { IWICStreamImpl *This = impl_from_IWICStream(iface); TRACE("(%p)\n", This); if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED; return IStream_Revert(This->pStream); } static HRESULT WINAPI IWICStreamImpl_LockRegion(IWICStream *iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { IWICStreamImpl *This = impl_from_IWICStream(iface); TRACE("(%p, %s, %s, %d)\n", This, wine_dbgstr_longlong(libOffset.QuadPart), wine_dbgstr_longlong(cb.QuadPart), dwLockType); if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED; return IStream_LockRegion(This->pStream, libOffset, cb, dwLockType); } static HRESULT WINAPI IWICStreamImpl_UnlockRegion(IWICStream *iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { IWICStreamImpl *This = impl_from_IWICStream(iface); TRACE("(%p, %s, %s, %d)\n", This, wine_dbgstr_longlong(libOffset.QuadPart), wine_dbgstr_longlong(cb.QuadPart), dwLockType); if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED; return IStream_UnlockRegion(This->pStream, libOffset, cb, dwLockType); } static HRESULT WINAPI IWICStreamImpl_Stat(IWICStream *iface, STATSTG *pstatstg, DWORD grfStatFlag) { IWICStreamImpl *This = impl_from_IWICStream(iface); TRACE("(%p, %p, %#x)\n", This, pstatstg, grfStatFlag); if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED; return IStream_Stat(This->pStream, pstatstg, grfStatFlag); } static HRESULT WINAPI IWICStreamImpl_Clone(IWICStream *iface, IStream **ppstm) { IWICStreamImpl *This = impl_from_IWICStream(iface); TRACE("(%p, %p)\n", This, ppstm); if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED; return IStream_Clone(This->pStream, ppstm); } static HRESULT WINAPI IWICStreamImpl_InitializeFromIStream(IWICStream *iface, IStream *stream) { IWICStreamImpl *This = impl_from_IWICStream(iface); HRESULT hr = S_OK; TRACE("(%p, %p)\n", iface, stream); if (!stream) return E_INVALIDARG; if (This->pStream) return WINCODEC_ERR_WRONGSTATE; IStream_AddRef(stream); if (InterlockedCompareExchangePointer((void **)&This->pStream, stream, NULL)) { /* Some other thread set the stream first. */ IStream_Release(stream); hr = WINCODEC_ERR_WRONGSTATE; } return hr; } static HRESULT WINAPI IWICStreamImpl_InitializeFromFilename(IWICStream *iface, LPCWSTR wzFileName, DWORD dwDesiredAccess) { IWICStreamImpl *This = impl_from_IWICStream(iface); HRESULT hr; DWORD dwMode; IStream *stream; TRACE("(%p, %s, %u)\n", iface, debugstr_w(wzFileName), dwDesiredAccess); if (This->pStream) return WINCODEC_ERR_WRONGSTATE; if(dwDesiredAccess & GENERIC_WRITE) dwMode = STGM_SHARE_DENY_WRITE | STGM_WRITE | STGM_CREATE; else if(dwDesiredAccess & GENERIC_READ) dwMode = STGM_SHARE_DENY_WRITE | STGM_READ | STGM_FAILIFTHERE; else return E_INVALIDARG; hr = SHCreateStreamOnFileW(wzFileName, dwMode, &stream); if (SUCCEEDED(hr)) { if (InterlockedCompareExchangePointer((void**)&This->pStream, stream, NULL)) { /* Some other thread set the stream first. */ IStream_Release(stream); hr = WINCODEC_ERR_WRONGSTATE; } } return hr; } /****************************************** * IWICStream_InitializeFromMemory * * Initializes the internal IStream object to retrieve its data from a memory chunk. * * PARAMS * pbBuffer [I] pointer to the memory chunk * cbBufferSize [I] number of bytes to use from the memory chunk * * RETURNS * SUCCESS: S_OK * FAILURE: E_INVALIDARG, if pbBuffer is NULL * E_OUTOFMEMORY, if we run out of memory * WINCODEC_ERR_WRONGSTATE, if the IStream object has already been initialized before * */ static HRESULT WINAPI IWICStreamImpl_InitializeFromMemory(IWICStream *iface, BYTE *pbBuffer, DWORD cbBufferSize) { IWICStreamImpl *This = impl_from_IWICStream(iface); StreamOnMemory *pObject; TRACE("(%p, %p, %u)\n", iface, pbBuffer, cbBufferSize); if (!pbBuffer) return E_INVALIDARG; if (This->pStream) return WINCODEC_ERR_WRONGSTATE; pObject = HeapAlloc(GetProcessHeap(), 0, sizeof(StreamOnMemory)); if (!pObject) return E_OUTOFMEMORY; pObject->IStream_iface.lpVtbl = &StreamOnMemory_Vtbl; pObject->ref = 1; pObject->pbMemory = pbBuffer; pObject->dwMemsize = cbBufferSize; pObject->dwCurPos = 0; InitializeCriticalSection(&pObject->lock); pObject->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": StreamOnMemory.lock"); if (InterlockedCompareExchangePointer((void**)&This->pStream, pObject, NULL)) { /* Some other thread set the stream first. */ IStream_Release(&pObject->IStream_iface); return WINCODEC_ERR_WRONGSTATE; } return S_OK; } static HRESULT map_file(HANDLE file, HANDLE *map, void **mem, LARGE_INTEGER *size) { *map = NULL; if (!GetFileSizeEx(file, size)) return HRESULT_FROM_WIN32(GetLastError()); if (size->u.HighPart) { WARN("file too large\n"); return E_FAIL; } if (!(*map = CreateFileMappingW(file, NULL, PAGE_READONLY, 0, size->u.LowPart, NULL))) { return HRESULT_FROM_WIN32(GetLastError()); } if (!(*mem = MapViewOfFile(*map, FILE_MAP_READ, 0, 0, size->u.LowPart))) { CloseHandle(*map); return HRESULT_FROM_WIN32(GetLastError()); } return S_OK; } HRESULT stream_initialize_from_filehandle(IWICStream *iface, HANDLE file) { IWICStreamImpl *This = impl_from_IWICStream(iface); StreamOnFileHandle *pObject; IWICStream *stream = NULL; HANDLE map; void *mem; LARGE_INTEGER size; HRESULT hr; TRACE("(%p,%p)\n", iface, file); if (This->pStream) return WINCODEC_ERR_WRONGSTATE; hr = map_file(file, &map, &mem, &size); if (FAILED(hr)) return hr; hr = StreamImpl_Create(&stream); if (FAILED(hr)) goto error; hr = IWICStreamImpl_InitializeFromMemory(stream, mem, size.u.LowPart); if (FAILED(hr)) goto error; pObject = HeapAlloc(GetProcessHeap(), 0, sizeof(StreamOnFileHandle)); if (!pObject) { hr = E_OUTOFMEMORY; goto error; } pObject->IStream_iface.lpVtbl = &StreamOnFileHandle_Vtbl; pObject->ref = 1; pObject->map = map; pObject->mem = mem; pObject->stream = stream; if (InterlockedCompareExchangePointer((void**)&This->pStream, pObject, NULL)) { /* Some other thread set the stream first. */ IStream_Release(&pObject->IStream_iface); return WINCODEC_ERR_WRONGSTATE; } return S_OK; error: if (stream) IWICStream_Release(stream); UnmapViewOfFile(mem); CloseHandle(map); return hr; } static HRESULT WINAPI IWICStreamImpl_InitializeFromIStreamRegion(IWICStream *iface, IStream *pIStream, ULARGE_INTEGER ulOffset, ULARGE_INTEGER ulMaxSize) { IWICStreamImpl *This = impl_from_IWICStream(iface); StreamOnStreamRange *pObject; TRACE("(%p,%p,%s,%s)\n", iface, pIStream, wine_dbgstr_longlong(ulOffset.QuadPart), wine_dbgstr_longlong(ulMaxSize.QuadPart)); if (!pIStream) return E_INVALIDARG; if (This->pStream) return WINCODEC_ERR_WRONGSTATE; pObject = HeapAlloc(GetProcessHeap(), 0, sizeof(StreamOnStreamRange)); if (!pObject) return E_OUTOFMEMORY; pObject->IStream_iface.lpVtbl = &StreamOnStreamRange_Vtbl; pObject->ref = 1; IStream_AddRef(pIStream); pObject->stream = pIStream; pObject->pos.QuadPart = 0; pObject->offset = ulOffset; pObject->max_size = ulMaxSize; InitializeCriticalSection(&pObject->lock); pObject->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": StreamOnStreamRange.lock"); if (InterlockedCompareExchangePointer((void**)&This->pStream, pObject, NULL)) { /* Some other thread set the stream first. */ IStream_Release(&pObject->IStream_iface); return WINCODEC_ERR_WRONGSTATE; } return S_OK; } static const IWICStreamVtbl WICStream_Vtbl = { /*** IUnknown methods ***/ IWICStreamImpl_QueryInterface, IWICStreamImpl_AddRef, IWICStreamImpl_Release, /*** ISequentialStream methods ***/ IWICStreamImpl_Read, IWICStreamImpl_Write, /*** IStream methods ***/ IWICStreamImpl_Seek, IWICStreamImpl_SetSize, IWICStreamImpl_CopyTo, IWICStreamImpl_Commit, IWICStreamImpl_Revert, IWICStreamImpl_LockRegion, IWICStreamImpl_UnlockRegion, IWICStreamImpl_Stat, IWICStreamImpl_Clone, /*** IWICStream methods ***/ IWICStreamImpl_InitializeFromIStream, IWICStreamImpl_InitializeFromFilename, IWICStreamImpl_InitializeFromMemory, IWICStreamImpl_InitializeFromIStreamRegion, }; HRESULT StreamImpl_Create(IWICStream **stream) { IWICStreamImpl *pObject; if( !stream ) return E_INVALIDARG; pObject = HeapAlloc(GetProcessHeap(), 0, sizeof(IWICStreamImpl)); if( !pObject ) { *stream = NULL; return E_OUTOFMEMORY; } pObject->IWICStream_iface.lpVtbl = &WICStream_Vtbl; pObject->ref = 1; pObject->pStream = NULL; *stream = &pObject->IWICStream_iface; return S_OK; }