blob: 308ef8e02b414f95eeb23058c4702d1c44db2018 [file] [log] [blame]
/*
* 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)\n", This);
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)\n", This);
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)\n", This);
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)\n", iface);
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)\n", iface);
return E_NOTIMPL;
}
/* Commit isn't implemented in the native windowscodecs DLL either */
static HRESULT WINAPI StreamOnMemory_Commit(IStream *iface,
DWORD grfCommitFlags)
{
TRACE("(%p)\n", iface);
return E_NOTIMPL;
}
/* 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)\n", iface);
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)\n", iface);
return E_NOTIMPL;
}
static HRESULT WINAPI StreamOnMemory_Stat(IStream *iface,
STATSTG *pstatstg, DWORD grfStatFlag)
{
StreamOnMemory *This = StreamOnMemory_from_IStream(iface);
TRACE("(%p)\n", This);
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)\n", iface);
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)\n", This);
return IWICStream_Read(This->stream, pv, cb, pcbRead);
}
static HRESULT WINAPI StreamOnFileHandle_Write(IStream *iface,
void const *pv, ULONG cb, ULONG *pcbWritten)
{
ERR("(%p)\n", iface);
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)\n", This);
return IWICStream_Seek(This->stream, dlibMove, dwOrigin, plibNewPosition);
}
static HRESULT WINAPI StreamOnFileHandle_SetSize(IStream *iface,
ULARGE_INTEGER libNewSize)
{
TRACE("(%p)\n", iface);
return E_NOTIMPL;
}
static HRESULT WINAPI StreamOnFileHandle_CopyTo(IStream *iface,
IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
{
TRACE("(%p)\n", iface);
return E_NOTIMPL;
}
static HRESULT WINAPI StreamOnFileHandle_Commit(IStream *iface,
DWORD grfCommitFlags)
{
TRACE("(%p)\n", iface);
return E_NOTIMPL;
}
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)\n", iface);
return E_NOTIMPL;
}
static HRESULT WINAPI StreamOnFileHandle_UnlockRegion(IStream *iface,
ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
{
TRACE("(%p)\n", iface);
return E_NOTIMPL;
}
static HRESULT WINAPI StreamOnFileHandle_Stat(IStream *iface,
STATSTG *pstatstg, DWORD grfStatFlag)
{
StreamOnFileHandle *This = StreamOnFileHandle_from_IStream(iface);
TRACE("(%p)\n", This);
return IWICStream_Stat(This->stream, pstatstg, grfStatFlag);
}
static HRESULT WINAPI StreamOnFileHandle_Clone(IStream *iface,
IStream **ppstm)
{
TRACE("(%p)\n", iface);
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)\n", This);
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)\n", This);
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)\n", This);
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)\n", iface);
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)\n", iface);
return E_NOTIMPL;
}
/* Commit isn't implemented in the native windowscodecs DLL either */
static HRESULT WINAPI StreamOnStreamRange_Commit(IStream *iface,
DWORD grfCommitFlags)
{
TRACE("(%p)\n", iface);
return E_NOTIMPL;
}
/* 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)\n", iface);
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)\n", iface);
return E_NOTIMPL;
}
static HRESULT WINAPI StreamOnStreamRange_Stat(IStream *iface,
STATSTG *pstatstg, DWORD grfStatFlag)
{
StreamOnStreamRange *This = StreamOnStreamRange_from_IStream(iface);
HRESULT hr;
TRACE("(%p)\n", This);
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)\n", iface);
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): relay\n", This);
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): relay\n", This);
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): relay\n", This);
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): relay\n", This);
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): relay\n", This);
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): relay\n", This);
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): relay\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): relay\n", This);
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): relay\n", This);
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): relay\n", This);
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): relay\n", This);
if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
return IStream_Clone(This->pStream, ppstm);
}
static HRESULT WINAPI IWICStreamImpl_InitializeFromIStream(IWICStream *iface,
IStream *pIStream)
{
ULARGE_INTEGER offset, size;
TRACE("(%p): relay\n", iface);
offset.QuadPart = 0;
size.u.LowPart = 0xffffffff;
size.u.HighPart = 0xffffffff;
return IWICStream_InitializeFromIStreamRegion(iface, pIStream, offset, size);
}
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)\n", iface, pbBuffer);
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)\n", iface, pIStream);
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;
}