blob: 60e452df2390603995252bc06a67b815cc2ea185 [file] [log] [blame]
/*
* ITSS Storage implementation
*
* Copyright 2004 Mike McCormack
*
* see http://bonedaddy.net/pabs3/hhm/#chmspec
*
* 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 "config.h"
#include <stdarg.h>
#include <stdio.h>
#define COBJMACROS
#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "ole2.h"
#include "chm_lib.h"
#include "itsstor.h"
#include "wine/itss.h"
#include "wine/unicode.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(itss);
/************************************************************************/
typedef struct _ITSS_IStorageImpl
{
IStorage IStorage_iface;
LONG ref;
struct chmFile *chmfile;
WCHAR dir[1];
} ITSS_IStorageImpl;
struct enum_info
{
struct enum_info *next, *prev;
struct chmUnitInfo ui;
};
typedef struct _IEnumSTATSTG_Impl
{
IEnumSTATSTG IEnumSTATSTG_iface;
LONG ref;
struct enum_info *first, *last, *current;
} IEnumSTATSTG_Impl;
typedef struct _IStream_Impl
{
IStream IStream_iface;
LONG ref;
ITSS_IStorageImpl *stg;
ULONGLONG addr;
struct chmUnitInfo ui;
} IStream_Impl;
static inline ITSS_IStorageImpl *impl_from_IStorage(IStorage *iface)
{
return CONTAINING_RECORD(iface, ITSS_IStorageImpl, IStorage_iface);
}
static inline IEnumSTATSTG_Impl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
{
return CONTAINING_RECORD(iface, IEnumSTATSTG_Impl, IEnumSTATSTG_iface);
}
static inline IStream_Impl *impl_from_IStream(IStream *iface)
{
return CONTAINING_RECORD(iface, IStream_Impl, IStream_iface);
}
static HRESULT ITSS_create_chm_storage(
struct chmFile *chmfile, const WCHAR *dir, IStorage** ppstgOpen );
static IStream_Impl* ITSS_create_stream(
ITSS_IStorageImpl *stg, struct chmUnitInfo *ui );
/************************************************************************/
static HRESULT WINAPI ITSS_IEnumSTATSTG_QueryInterface(
IEnumSTATSTG* iface,
REFIID riid,
void** ppvObject)
{
IEnumSTATSTG_Impl *This = impl_from_IEnumSTATSTG(iface);
if (IsEqualGUID(riid, &IID_IUnknown)
|| IsEqualGUID(riid, &IID_IEnumSTATSTG))
{
IEnumSTATSTG_AddRef(iface);
*ppvObject = This;
return S_OK;
}
WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
return E_NOINTERFACE;
}
static ULONG WINAPI ITSS_IEnumSTATSTG_AddRef(
IEnumSTATSTG* iface)
{
IEnumSTATSTG_Impl *This = impl_from_IEnumSTATSTG(iface);
return InterlockedIncrement(&This->ref);
}
static ULONG WINAPI ITSS_IEnumSTATSTG_Release(
IEnumSTATSTG* iface)
{
IEnumSTATSTG_Impl *This = impl_from_IEnumSTATSTG(iface);
ULONG ref = InterlockedDecrement(&This->ref);
if (ref == 0)
{
while( This->first )
{
struct enum_info *t = This->first->next;
HeapFree( GetProcessHeap(), 0, This->first );
This->first = t;
}
HeapFree(GetProcessHeap(), 0, This);
ITSS_UnlockModule();
}
return ref;
}
static HRESULT WINAPI ITSS_IEnumSTATSTG_Next(
IEnumSTATSTG* iface,
ULONG celt,
STATSTG* rgelt,
ULONG* pceltFetched)
{
IEnumSTATSTG_Impl *This = impl_from_IEnumSTATSTG(iface);
DWORD len, n;
struct enum_info *cur;
TRACE("%p %u %p %p\n", This, celt, rgelt, pceltFetched );
cur = This->current;
n = 0;
while( (n<celt) && cur)
{
WCHAR *str;
memset( rgelt, 0, sizeof *rgelt );
/* copy the name */
str = cur->ui.path;
if( *str == '/' )
str++;
len = strlenW( str ) + 1;
rgelt->pwcsName = CoTaskMemAlloc( len*sizeof(WCHAR) );
strcpyW( rgelt->pwcsName, str );
/* determine the type */
if( rgelt->pwcsName[len-2] == '/' )
{
rgelt->pwcsName[len-2] = 0;
rgelt->type = STGTY_STORAGE;
}
else
rgelt->type = STGTY_STREAM;
/* copy the size */
rgelt->cbSize.QuadPart = cur->ui.length;
/* advance to the next item if it exists */
n++;
cur = cur->next;
}
This->current = cur;
*pceltFetched = n;
if( n < celt )
return S_FALSE;
return S_OK;
}
static HRESULT WINAPI ITSS_IEnumSTATSTG_Skip(
IEnumSTATSTG* iface,
ULONG celt)
{
IEnumSTATSTG_Impl *This = impl_from_IEnumSTATSTG(iface);
DWORD n;
struct enum_info *cur;
TRACE("%p %u\n", This, celt );
cur = This->current;
n = 0;
while( (n<celt) && cur)
{
n++;
cur = cur->next;
}
This->current = cur;
if( n < celt )
return S_FALSE;
return S_OK;
}
static HRESULT WINAPI ITSS_IEnumSTATSTG_Reset(
IEnumSTATSTG* iface)
{
IEnumSTATSTG_Impl *This = impl_from_IEnumSTATSTG(iface);
TRACE("%p\n", This );
This->current = This->first;
return S_OK;
}
static HRESULT WINAPI ITSS_IEnumSTATSTG_Clone(
IEnumSTATSTG* iface,
IEnumSTATSTG** ppenum)
{
FIXME("\n");
return E_NOTIMPL;
}
static const IEnumSTATSTGVtbl IEnumSTATSTG_vtbl =
{
ITSS_IEnumSTATSTG_QueryInterface,
ITSS_IEnumSTATSTG_AddRef,
ITSS_IEnumSTATSTG_Release,
ITSS_IEnumSTATSTG_Next,
ITSS_IEnumSTATSTG_Skip,
ITSS_IEnumSTATSTG_Reset,
ITSS_IEnumSTATSTG_Clone
};
static IEnumSTATSTG_Impl *ITSS_create_enum( void )
{
IEnumSTATSTG_Impl *stgenum;
stgenum = HeapAlloc( GetProcessHeap(), 0, sizeof (IEnumSTATSTG_Impl) );
stgenum->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTG_vtbl;
stgenum->ref = 1;
stgenum->first = NULL;
stgenum->last = NULL;
stgenum->current = NULL;
ITSS_LockModule();
TRACE(" -> %p\n", stgenum );
return stgenum;
}
/************************************************************************/
static HRESULT WINAPI ITSS_IStorageImpl_QueryInterface(
IStorage* iface,
REFIID riid,
void** ppvObject)
{
ITSS_IStorageImpl *This = impl_from_IStorage(iface);
if (IsEqualGUID(riid, &IID_IUnknown)
|| IsEqualGUID(riid, &IID_IStorage))
{
IStorage_AddRef(iface);
*ppvObject = This;
return S_OK;
}
WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
return E_NOINTERFACE;
}
static ULONG WINAPI ITSS_IStorageImpl_AddRef(
IStorage* iface)
{
ITSS_IStorageImpl *This = impl_from_IStorage(iface);
return InterlockedIncrement(&This->ref);
}
static ULONG WINAPI ITSS_IStorageImpl_Release(
IStorage* iface)
{
ITSS_IStorageImpl *This = impl_from_IStorage(iface);
ULONG ref = InterlockedDecrement(&This->ref);
if (ref == 0)
{
chm_close(This->chmfile);
HeapFree(GetProcessHeap(), 0, This);
ITSS_UnlockModule();
}
return ref;
}
static HRESULT WINAPI ITSS_IStorageImpl_CreateStream(
IStorage* iface,
LPCOLESTR pwcsName,
DWORD grfMode,
DWORD reserved1,
DWORD reserved2,
IStream** ppstm)
{
FIXME("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI ITSS_IStorageImpl_OpenStream(
IStorage* iface,
LPCOLESTR pwcsName,
void* reserved1,
DWORD grfMode,
DWORD reserved2,
IStream** ppstm)
{
ITSS_IStorageImpl *This = impl_from_IStorage(iface);
IStream_Impl *stm;
DWORD len;
struct chmUnitInfo ui;
int r;
WCHAR *path, *p;
TRACE("%p %s %p %u %u %p\n", This, debugstr_w(pwcsName),
reserved1, grfMode, reserved2, ppstm );
len = strlenW( This->dir ) + strlenW( pwcsName ) + 1;
path = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
strcpyW( path, This->dir );
if( pwcsName[0] == '/' || pwcsName[0] == '\\' )
{
p = &path[strlenW( path ) - 1];
while( ( path <= p ) && ( *p == '/' ) )
*p-- = 0;
}
strcatW( path, pwcsName );
for(p=path; *p; p++) {
if(*p == '\\')
*p = '/';
}
if(*--p == '/')
*p = 0;
TRACE("Resolving %s\n", debugstr_w(path));
r = chm_resolve_object(This->chmfile, path, &ui);
HeapFree( GetProcessHeap(), 0, path );
if( r != CHM_RESOLVE_SUCCESS ) {
WARN("Could not resolve object\n");
return STG_E_FILENOTFOUND;
}
stm = ITSS_create_stream( This, &ui );
if( !stm )
return E_FAIL;
*ppstm = &stm->IStream_iface;
return S_OK;
}
static HRESULT WINAPI ITSS_IStorageImpl_CreateStorage(
IStorage* iface,
LPCOLESTR pwcsName,
DWORD grfMode,
DWORD dwStgFmt,
DWORD reserved2,
IStorage** ppstg)
{
FIXME("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI ITSS_IStorageImpl_OpenStorage(
IStorage* iface,
LPCOLESTR pwcsName,
IStorage* pstgPriority,
DWORD grfMode,
SNB snbExclude,
DWORD reserved,
IStorage** ppstg)
{
ITSS_IStorageImpl *This = impl_from_IStorage(iface);
struct chmFile *chmfile;
WCHAR *path, *p;
DWORD len;
TRACE("%p %s %p %u %p %u %p\n", This, debugstr_w(pwcsName),
pstgPriority, grfMode, snbExclude, reserved, ppstg);
chmfile = chm_dup( This->chmfile );
if( !chmfile )
return E_FAIL;
len = strlenW( This->dir ) + strlenW( pwcsName ) + 2; /* need room for a terminating slash */
path = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
strcpyW( path, This->dir );
if( pwcsName[0] == '/' || pwcsName[0] == '\\' )
{
p = &path[strlenW( path ) - 1];
while( ( path <= p ) && ( *p == '/' ) )
*p-- = 0;
}
strcatW( path, pwcsName );
for(p=path; *p; p++) {
if(*p == '\\')
*p = '/';
}
/* add a terminating slash if one does not already exist */
if(*(p-1) != '/')
{
*p++ = '/';
*p = 0;
}
TRACE("Resolving %s\n", debugstr_w(path));
return ITSS_create_chm_storage(chmfile, path, ppstg);
}
static HRESULT WINAPI ITSS_IStorageImpl_CopyTo(
IStorage* iface,
DWORD ciidExclude,
const IID* rgiidExclude,
SNB snbExclude,
IStorage* pstgDest)
{
FIXME("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI ITSS_IStorageImpl_MoveElementTo(
IStorage* iface,
LPCOLESTR pwcsName,
IStorage* pstgDest,
LPCOLESTR pwcsNewName,
DWORD grfFlags)
{
FIXME("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI ITSS_IStorageImpl_Commit(
IStorage* iface,
DWORD grfCommitFlags)
{
FIXME("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI ITSS_IStorageImpl_Revert(
IStorage* iface)
{
FIXME("\n");
return E_NOTIMPL;
}
static int ITSS_chm_enumerator(
struct chmFile *h,
struct chmUnitInfo *ui,
void *context)
{
struct enum_info *info;
IEnumSTATSTG_Impl* stgenum = context;
TRACE("adding %s to enumeration\n", debugstr_w(ui->path) );
info = HeapAlloc( GetProcessHeap(), 0, sizeof (struct enum_info) );
info->ui = *ui;
info->next = NULL;
info->prev = stgenum->last;
if( stgenum->last )
stgenum->last->next = info;
else
stgenum->first = info;
stgenum->last = info;
return CHM_ENUMERATOR_CONTINUE;
}
static HRESULT WINAPI ITSS_IStorageImpl_EnumElements(
IStorage* iface,
DWORD reserved1,
void* reserved2,
DWORD reserved3,
IEnumSTATSTG** ppenum)
{
ITSS_IStorageImpl *This = impl_from_IStorage(iface);
IEnumSTATSTG_Impl* stgenum;
TRACE("%p %d %p %d %p\n", This, reserved1, reserved2, reserved3, ppenum );
stgenum = ITSS_create_enum();
if( !stgenum )
return E_FAIL;
chm_enumerate_dir(This->chmfile,
This->dir,
CHM_ENUMERATE_ALL,
ITSS_chm_enumerator,
stgenum );
stgenum->current = stgenum->first;
*ppenum = &stgenum->IEnumSTATSTG_iface;
return S_OK;
}
static HRESULT WINAPI ITSS_IStorageImpl_DestroyElement(
IStorage* iface,
LPCOLESTR pwcsName)
{
FIXME("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI ITSS_IStorageImpl_RenameElement(
IStorage* iface,
LPCOLESTR pwcsOldName,
LPCOLESTR pwcsNewName)
{
FIXME("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI ITSS_IStorageImpl_SetElementTimes(
IStorage* iface,
LPCOLESTR pwcsName,
const FILETIME* pctime,
const FILETIME* patime,
const FILETIME* pmtime)
{
FIXME("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI ITSS_IStorageImpl_SetClass(
IStorage* iface,
REFCLSID clsid)
{
FIXME("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI ITSS_IStorageImpl_SetStateBits(
IStorage* iface,
DWORD grfStateBits,
DWORD grfMask)
{
FIXME("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI ITSS_IStorageImpl_Stat(
IStorage* iface,
STATSTG* pstatstg,
DWORD grfStatFlag)
{
FIXME("\n");
return E_NOTIMPL;
}
static const IStorageVtbl ITSS_IStorageImpl_Vtbl =
{
ITSS_IStorageImpl_QueryInterface,
ITSS_IStorageImpl_AddRef,
ITSS_IStorageImpl_Release,
ITSS_IStorageImpl_CreateStream,
ITSS_IStorageImpl_OpenStream,
ITSS_IStorageImpl_CreateStorage,
ITSS_IStorageImpl_OpenStorage,
ITSS_IStorageImpl_CopyTo,
ITSS_IStorageImpl_MoveElementTo,
ITSS_IStorageImpl_Commit,
ITSS_IStorageImpl_Revert,
ITSS_IStorageImpl_EnumElements,
ITSS_IStorageImpl_DestroyElement,
ITSS_IStorageImpl_RenameElement,
ITSS_IStorageImpl_SetElementTimes,
ITSS_IStorageImpl_SetClass,
ITSS_IStorageImpl_SetStateBits,
ITSS_IStorageImpl_Stat,
};
static HRESULT ITSS_create_chm_storage(
struct chmFile *chmfile, const WCHAR *dir, IStorage** ppstgOpen )
{
ITSS_IStorageImpl *stg;
TRACE("%p %s\n", chmfile, debugstr_w( dir ) );
stg = HeapAlloc( GetProcessHeap(), 0,
FIELD_OFFSET( ITSS_IStorageImpl, dir[strlenW( dir ) + 1] ));
stg->IStorage_iface.lpVtbl = &ITSS_IStorageImpl_Vtbl;
stg->ref = 1;
stg->chmfile = chmfile;
strcpyW( stg->dir, dir );
*ppstgOpen = &stg->IStorage_iface;
ITSS_LockModule();
return S_OK;
}
HRESULT ITSS_StgOpenStorage(
const WCHAR* pwcsName,
IStorage* pstgPriority,
DWORD grfMode,
SNB snbExclude,
DWORD reserved,
IStorage** ppstgOpen)
{
struct chmFile *chmfile;
static const WCHAR szRoot[] = { '/', 0 };
TRACE("%s\n", debugstr_w(pwcsName) );
chmfile = chm_openW( pwcsName );
if( !chmfile )
return E_FAIL;
return ITSS_create_chm_storage( chmfile, szRoot, ppstgOpen );
}
/************************************************************************/
static HRESULT WINAPI ITSS_IStream_QueryInterface(
IStream* iface,
REFIID riid,
void** ppvObject)
{
IStream_Impl *This = impl_from_IStream(iface);
if (IsEqualGUID(riid, &IID_IUnknown)
|| IsEqualGUID(riid, &IID_ISequentialStream)
|| IsEqualGUID(riid, &IID_IStream))
{
IStream_AddRef(iface);
*ppvObject = This;
return S_OK;
}
WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
return E_NOINTERFACE;
}
static ULONG WINAPI ITSS_IStream_AddRef(
IStream* iface)
{
IStream_Impl *This = impl_from_IStream(iface);
return InterlockedIncrement(&This->ref);
}
static ULONG WINAPI ITSS_IStream_Release(
IStream* iface)
{
IStream_Impl *This = impl_from_IStream(iface);
ULONG ref = InterlockedDecrement(&This->ref);
if (ref == 0)
{
IStorage_Release( &This->stg->IStorage_iface );
HeapFree(GetProcessHeap(), 0, This);
ITSS_UnlockModule();
}
return ref;
}
static HRESULT WINAPI ITSS_IStream_Read(
IStream* iface,
void* pv,
ULONG cb,
ULONG* pcbRead)
{
IStream_Impl *This = impl_from_IStream(iface);
ULONG count;
TRACE("%p %p %u %p\n", This, pv, cb, pcbRead);
count = chm_retrieve_object(This->stg->chmfile,
&This->ui, pv, This->addr, cb);
This->addr += count;
if( pcbRead )
*pcbRead = count;
return count ? S_OK : S_FALSE;
}
static HRESULT WINAPI ITSS_IStream_Write(
IStream* iface,
const void* pv,
ULONG cb,
ULONG* pcbWritten)
{
FIXME("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI ITSS_IStream_Seek(
IStream* iface,
LARGE_INTEGER dlibMove,
DWORD dwOrigin,
ULARGE_INTEGER* plibNewPosition)
{
IStream_Impl *This = impl_from_IStream(iface);
LONGLONG newpos;
TRACE("%p %s %u %p\n", This,
wine_dbgstr_longlong( dlibMove.QuadPart ), dwOrigin, plibNewPosition );
newpos = This->addr;
switch( dwOrigin )
{
case STREAM_SEEK_CUR:
newpos = This->addr + dlibMove.QuadPart;
break;
case STREAM_SEEK_SET:
newpos = dlibMove.QuadPart;
break;
case STREAM_SEEK_END:
newpos = This->ui.length + dlibMove.QuadPart;
break;
}
if( ( newpos < 0 ) || ( newpos > This->ui.length ) )
return STG_E_INVALIDPOINTER;
This->addr = newpos;
if( plibNewPosition )
plibNewPosition->QuadPart = This->addr;
return S_OK;
}
static HRESULT WINAPI ITSS_IStream_SetSize(
IStream* iface,
ULARGE_INTEGER libNewSize)
{
FIXME("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI ITSS_IStream_CopyTo(
IStream* iface,
IStream* pstm,
ULARGE_INTEGER cb,
ULARGE_INTEGER* pcbRead,
ULARGE_INTEGER* pcbWritten)
{
FIXME("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI ITSS_IStream_Commit(
IStream* iface,
DWORD grfCommitFlags)
{
FIXME("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI ITSS_IStream_Revert(
IStream* iface)
{
FIXME("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI ITSS_IStream_LockRegion(
IStream* iface,
ULARGE_INTEGER libOffset,
ULARGE_INTEGER cb,
DWORD dwLockType)
{
FIXME("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI ITSS_IStream_UnlockRegion(
IStream* iface,
ULARGE_INTEGER libOffset,
ULARGE_INTEGER cb,
DWORD dwLockType)
{
FIXME("\n");
return E_NOTIMPL;
}
static HRESULT WINAPI ITSS_IStream_Stat(
IStream* iface,
STATSTG* pstatstg,
DWORD grfStatFlag)
{
IStream_Impl *This = impl_from_IStream(iface);
TRACE("%p %p %d\n", This, pstatstg, grfStatFlag);
memset( pstatstg, 0, sizeof *pstatstg );
if( !( grfStatFlag & STATFLAG_NONAME ) )
{
FIXME("copy the name\n");
}
pstatstg->type = STGTY_STREAM;
pstatstg->cbSize.QuadPart = This->ui.length;
pstatstg->grfMode = STGM_READ;
pstatstg->clsid = CLSID_ITStorage;
return S_OK;
}
static HRESULT WINAPI ITSS_IStream_Clone(
IStream* iface,
IStream** ppstm)
{
FIXME("\n");
return E_NOTIMPL;
}
static const IStreamVtbl ITSS_IStream_vtbl =
{
ITSS_IStream_QueryInterface,
ITSS_IStream_AddRef,
ITSS_IStream_Release,
ITSS_IStream_Read,
ITSS_IStream_Write,
ITSS_IStream_Seek,
ITSS_IStream_SetSize,
ITSS_IStream_CopyTo,
ITSS_IStream_Commit,
ITSS_IStream_Revert,
ITSS_IStream_LockRegion,
ITSS_IStream_UnlockRegion,
ITSS_IStream_Stat,
ITSS_IStream_Clone,
};
static IStream_Impl *ITSS_create_stream(
ITSS_IStorageImpl *stg, struct chmUnitInfo *ui )
{
IStream_Impl *stm;
stm = HeapAlloc( GetProcessHeap(), 0, sizeof (IStream_Impl) );
stm->IStream_iface.lpVtbl = &ITSS_IStream_vtbl;
stm->ref = 1;
stm->addr = 0;
stm->ui = *ui;
stm->stg = stg;
IStorage_AddRef( &stg->IStorage_iface );
ITSS_LockModule();
TRACE(" -> %p\n", stm );
return stm;
}