/*		DirectMusicLoader
 *
 * Copyright 2002 TransGaming Technologies Inc. 
 *
 */

#include "config.h"
#include <assert.h>
#include <string.h>

#include "wine/debug.h"
#include "winbase.h"
#include "winerror.h"
#include "windef.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
#include "mmsystem.h"
#include "ole2.h"
#include "strmif.h"
#include "control.h"
#include "uuids.h"
#include "errors.h"
#include "dsound.h"
#include "dmusici.h"

WINE_DEFAULT_DEBUG_CHANNEL(dmusic);

struct objtype_list {
  REFCLSID rclsid;
  WCHAR ext[5];
} objtypes[]={
  {&CLSID_DirectMusicAudioPathConfig, {'.','a','u','d',0}}, /* audiopath */
  {&CLSID_DirectMusicBand,            {'.','b','n','d',0}}, /* band */
  {&CLSID_DirectMusicContainer,       {'.','c','o','n',0}}, /* container */
//  {&CLSID_DirectMusicCollection,      {'.','d','l','s',0}}, /* collection */
  {&CLSID_DirectMusicChordMap,        {'.','c','d','m',0}}, /* chordmap */
  {&CLSID_DirectMusicSegment,         {'.','s','g','t',0}}, /* segment */
  {&CLSID_DirectMusicScript,          {'.','s','p','t',0}}, /* script */
  {&CLSID_DirectMusicStyle,           {'.','s','t','y',0}}, /* style */
  {&CLSID_DirectMusicSegment,         {'.','t','p','l',0}}, /* template */
  {&CLSID_DirectMusicGraph,           {'.','t','g','r',0}}, /* toolgraph */
//  {&CLSID_DirectMusicWave,            {'.','w','a','v',0}}  /* wave */
};
#define num_objtypes (sizeof(objtypes)/sizeof(struct objtype_list))

static int find_objtype(REFCLSID rclsid)
{
	unsigned u;
	for (u=0; u<num_objtypes; u++)
		if (IsEqualCLSID(objtypes[u].rclsid, rclsid))
			return u;
	return -1;
}

/*****************************************************************************
 * IStream implementation structure
 */
typedef struct IStreamImpl IStreamImpl;
struct IStreamImpl
{
    /* IUnknown fields */
    ICOM_VFIELD(IStream);
    ICOM_VTABLE(IDirectMusicGetLoader) *lpGLVtbl;
    DWORD ref;
    /* IStream fields */
    WCHAR wszName[MAX_PATH];
    HANDLE hFile;
    LPBYTE data;
    LARGE_INTEGER ofs, size;
    /* IDirectMusicGetLoader fields */
    IDirectMusicLoader8* pLoader;
};

static HRESULT StreamCreate(IDirectMusicLoader8* pLoader, IStreamImpl** ppStream);
static HRESULT StreamCreateFile(IDirectMusicLoader8* pLoader, WCHAR* pwszName, IStreamImpl** ppStream);

static HRESULT WINAPI IStreamImpl_QueryInterface(
	IStream* iface,REFIID riid,LPVOID *ppobj
) {
	ICOM_THIS(IStreamImpl,iface);

	TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
	if (IsEqualIID(&IID_IUnknown, riid) ||
	    IsEqualIID(&IID_ISequentialStream, riid) ||
	    IsEqualIID(&IID_IStream, riid)) {
		*ppobj = (LPVOID)This;
		This->ref++;
		return S_OK;
	}
	if (IsEqualIID(&IID_IDirectMusicGetLoader, riid)) {
		*ppobj = (LPVOID)&This->lpGLVtbl;
		This->ref++;
		return S_OK;
	}
	FIXME("(%p): no interface for %s\n", This, debugstr_guid(riid));
	return E_NOINTERFACE;
}

static ULONG WINAPI IStreamImpl_AddRef(IStream* iface) {
	ICOM_THIS(IStreamImpl,iface);
	return ++(This->ref);
}

static ULONG WINAPI IStreamImpl_Release(IStream* iface) {
	ICOM_THIS(IStreamImpl,iface);
	TRACE("(%p), ref was %ld\n",This,This->ref);
	if (!--(This->ref)) {
		if (This->hFile) {
			CloseHandle(This->hFile);
			if (This->data) HeapFree(GetProcessHeap(),0,This->data);
		}
		IDirectMusicLoader_Release(This->pLoader);
		HeapFree(GetProcessHeap(),0,This);
		return 0;
	}
	return This->ref;
}

static HRESULT WINAPI IStreamImpl_Read(
	IStream* iface,
	void* pv,
	ULONG cb,
	ULONG* pcbRead)
{
	ICOM_THIS(IStreamImpl,iface);
	TRACE("(%p)->(%p,%ld,%p)\n", This, pv, cb, pcbRead);
	if (This->hFile) {
		if (!ReadFile(This->hFile, pv, cb, pcbRead, NULL)) {
			ERR("read failed, err=%ld\n", GetLastError());
			return E_FAIL;
		}
	} else {
		FIXME("read from memory stream\n");
		return E_NOTIMPL;
	}
	return S_OK;
}

static HRESULT WINAPI IStreamImpl_Write(
	IStream* iface,
	const void* pv,
	ULONG cb,
	ULONG* pcbWritten)
{
	ICOM_THIS(IStreamImpl,iface);
	FIXME("(%p)->(%p,%ld,%p): stub\n", This, pv, cb, pcbWritten);
	*pcbWritten = 0;
	return E_NOTIMPL;
}

static HRESULT WINAPI IStreamImpl_Seek(
	IStream* iface,
	LARGE_INTEGER dlibMove,
	DWORD dwOrigin,
	ULARGE_INTEGER* plibNewPosition)
{
	ICOM_THIS(IStreamImpl,iface);
	TRACE("(%p)->(%Ld,%ld,%p)\n", This, dlibMove.QuadPart, dwOrigin, plibNewPosition);
	if (This->hFile) {
		LONG low, high = dlibMove.s.HighPart;
		low = SetFilePointer(This->hFile, dlibMove.s.LowPart, &high, dwOrigin);
		/* SetFilePointer seems to set last error to 0 if successful */
		if (GetLastError()) {
			ERR("seek failed, err=%ld\n", GetLastError());
			return E_FAIL;
		}
		if (plibNewPosition) {
			plibNewPosition->s.LowPart = low;
			plibNewPosition->s.HighPart = high;
		}
	} else {
		FIXME("seek in memory stream\n");
		return E_NOTIMPL;
	}
	return S_OK;
}

static HRESULT WINAPI IStreamImpl_Clone(
	IStream* iface,
	IStream** ppStream)
{
	ICOM_THIS(IStreamImpl,iface);
	IStreamImpl* pStream;
	HRESULT hr;
	TRACE("(%p)->(%p)\n", This, ppStream);
	if (This->hFile) {
		LONG low = 0, high = 0;
		hr = StreamCreateFile(This->pLoader, This->wszName, &pStream);
		if (FAILED(hr)) return hr;
		low = SetFilePointer(This->hFile, low, &high, FILE_CURRENT);
		TRACE("filepos lo=%ld hi=%ld\n", low, high);
		low = SetFilePointer(pStream->hFile, low, &high, FILE_BEGIN);
	} else {
		hr = StreamCreate(This->pLoader, &pStream);
		if (FAILED(hr)) return hr;
		pStream->data = This->data;
		pStream->ofs = This->ofs;
		pStream->size = This->size;
	}
	*ppStream = (IStream*)pStream;
	return S_OK;
}

static ICOM_VTABLE(IStream) dmsvt =
{
	ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
	IStreamImpl_QueryInterface,
	IStreamImpl_AddRef,
	IStreamImpl_Release,
	IStreamImpl_Read,
	IStreamImpl_Write,
	IStreamImpl_Seek,
	(void*)0xdeaf2108, /* SetSize */
	(void*)0xdeaf2109, /* CopyTo */
	(void*)0xdeaf210a, /* Commit */
	(void*)0xdeaf210b, /* Revert */
	(void*)0xdeaf210c, /* LockRegion */
	(void*)0xdeaf210d, /* UnlockRegion */
	(void*)0xdeaf210e, /* Stat */
	IStreamImpl_Clone
};

static HRESULT WINAPI IDirectMusicGetLoaderImpl_QueryInterface(
	IDirectMusicGetLoader* iface,REFIID riid,LPVOID *ppobj
) {
	ICOM_THIS_MULTI(IStreamImpl,lpGLVtbl,iface);
	return IStream_QueryInterface((IStream*)This, riid, ppobj);
}

static ULONG WINAPI IDirectMusicGetLoaderImpl_AddRef(IDirectMusicGetLoader* iface) {
	ICOM_THIS_MULTI(IStreamImpl,lpGLVtbl,iface);
	return IStream_AddRef((IStream*)This);
}

static ULONG WINAPI IDirectMusicGetLoaderImpl_Release(IDirectMusicGetLoader* iface) {
	ICOM_THIS_MULTI(IStreamImpl,lpGLVtbl,iface);
	return IStream_Release((IStream*)This);
}

static HRESULT WINAPI IDirectMusicGetLoaderImpl_GetLoader(
	IDirectMusicGetLoader* iface,
	IDirectMusicLoader** ppLoader)
{
	ICOM_THIS_MULTI(IStreamImpl,lpGLVtbl,iface);
	IDirectMusicLoader_AddRef(This->pLoader);
	*ppLoader = (IDirectMusicLoader*)This->pLoader;
	return S_OK;
}

static ICOM_VTABLE(IDirectMusicGetLoader) dmglvt =
{
	ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
	IDirectMusicGetLoaderImpl_QueryInterface,
	IDirectMusicGetLoaderImpl_AddRef,
	IDirectMusicGetLoaderImpl_Release,
	IDirectMusicGetLoaderImpl_GetLoader
};

static HRESULT StreamCreate(IDirectMusicLoader8* pLoader, IStreamImpl** ppStream)
{
	IStreamImpl* stream;
	stream = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IStreamImpl));
	if (!stream) return E_OUTOFMEMORY;

	ICOM_VTBL(stream)	= &dmsvt;
	stream->lpGLVtbl	= &dmglvt;
	stream->ref		= 1;
	IDirectMusicLoader8_AddRef(pLoader);
	stream->pLoader		= pLoader;

	*ppStream = stream;

	return S_OK;
}

static HRESULT StreamCreateFile(IDirectMusicLoader8* pLoader, WCHAR* pwszName, IStreamImpl** ppStream)
{
	IStreamImpl* stream;
	HRESULT hr;
	HFILE hFile;

	hFile = CreateFileW(pwszName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
	if (hFile == INVALID_HANDLE_VALUE) return E_FAIL;

	hr = StreamCreate(pLoader, &stream);
	if (FAILED(hr)) return hr;

	GetFullPathNameW(pwszName, MAX_PATH, stream->wszName, NULL);
	stream->hFile = hFile;
	stream->size.s.LowPart = GetFileSize(hFile, &stream->size.s.HighPart);

	*ppStream = stream;

	return S_OK;
}

/*****************************************************************************
 * IDirectMusicLoader implementation structure
 */
typedef struct IDirectMusicLoaderImpl IDirectMusicLoaderImpl;
struct IDirectMusicLoaderImpl
{
    /* IUnknown fields */
    ICOM_VFIELD(IDirectMusicLoader8);
    DWORD ref;
    /* IDirectMusicLoader fields */
    WCHAR* pwszPath[num_objtypes];
};

static HRESULT WINAPI IDirectMusicLoaderImpl_QueryInterface(
	IDirectMusicLoader8* iface,REFIID riid,LPVOID *ppobj
) {
	ICOM_THIS(IDirectMusicLoaderImpl,iface);

	FIXME("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
	return E_NOINTERFACE;
}

static ULONG WINAPI IDirectMusicLoaderImpl_AddRef(IDirectMusicLoader8* iface) {
	ICOM_THIS(IDirectMusicLoaderImpl,iface);
	return ++(This->ref);
}

static ULONG WINAPI IDirectMusicLoaderImpl_Release(IDirectMusicLoader8* iface) {
	ICOM_THIS(IDirectMusicLoaderImpl,iface);
	TRACE("(%p), ref was %ld\n",This,This->ref);
	if (!--(This->ref)) {
		unsigned u;
		for (u=0; u<num_objtypes; u++)
			if (This->pwszPath[u])
				HeapFree(GetProcessHeap(),0,This->pwszPath[u]);
		HeapFree(GetProcessHeap(),0,This);
		return 0;
	}
	return This->ref;
}

static HRESULT WINAPI IDirectMusicLoaderImpl_SetSearchDirectory(
	IDirectMusicLoader8* iface,
	REFGUID rguidClass,
	WCHAR* pwszPath,
	BOOL fClear)
{
	ICOM_THIS(IDirectMusicLoaderImpl,iface);
	unsigned size;
	TRACE("(%p)->(%s,%s,%d)\n", This,
	      debugstr_guid(rguidClass), debugstr_w(pwszPath), fClear);
	size = (lstrlenW(pwszPath)+1)*sizeof(WCHAR);
	if (IsEqualCLSID(&GUID_DirectMusicAllTypes, rguidClass)) {
		unsigned u;
		for (u=0; u<num_objtypes; u++) {
			if (This->pwszPath[u])
				HeapFree(GetProcessHeap(),0,This->pwszPath[u]);
			This->pwszPath[u] = HeapAlloc(GetProcessHeap(),0,size);
			memcpy(This->pwszPath[u], pwszPath, size);
		}
	} else {
		int t = find_objtype(rguidClass);
		if (t == -1) {
			FIXME("unknown CLSID: %s\n", debugstr_guid(rguidClass));
			return S_OK;
		}
		if (This->pwszPath[t])
			HeapFree(GetProcessHeap(),0,This->pwszPath[t]);
		This->pwszPath[t] = HeapAlloc(GetProcessHeap(),0,size);
		memcpy(This->pwszPath[t], pwszPath, size);
	}
	return S_OK;
}

static HRESULT WINAPI IDirectMusicLoaderImpl_LoadObjectFromFile(
	IDirectMusicLoader8* iface,
	REFGUID rguidClassID,
	REFIID iidInterfaceID,
	WCHAR* pwszFilePath,
	void** ppObject)
{
	ICOM_THIS(IDirectMusicLoaderImpl,iface);
	DMUS_OBJECTDESC desc;
	int t;

	TRACE("(%p)->(%s,%s,%s,%p)\n",This,
	      debugstr_guid(rguidClassID),
	      debugstr_guid(iidInterfaceID),
	      debugstr_w(pwszFilePath), ppObject);
	*ppObject = NULL;
	memset(&desc, 0, sizeof(desc));
	desc.dwSize = sizeof(desc);
	desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FULLPATH;
	desc.guidClass = *rguidClassID;
	t = find_objtype(rguidClassID);
	/* first search Windows search path */
	if (!SearchPathW(NULL, pwszFilePath, NULL, DMUS_MAX_FILENAME,
			 desc.wszFileName, NULL)) {
		if (t == -1 || !This->pwszPath[t]) {
			TRACE("no searchpath known, failing\n");
			return E_FAIL;
		}
		/* then search class search path */
		if (!SearchPathW(This->pwszPath[t], pwszFilePath, NULL, DMUS_MAX_FILENAME,
				 desc.wszFileName, NULL)) {
			TRACE("not found in searchpath, failing\n");
			return E_FAIL;
		}
	}
	/* found it, try to open it */
	return IDirectMusicLoader_GetObject(iface, &desc, iidInterfaceID, ppObject);
}

static HRESULT WINAPI IDirectMusicLoaderImpl_GetObject(
	IDirectMusicLoader8* iface,
	LPDMUS_OBJECTDESC pDesc,
	REFIID riid,
	LPVOID* ppv)
{
	ICOM_THIS(IDirectMusicLoaderImpl,iface);
	IDirectMusicObject* pObj = NULL;
	IPersistStream* pObjStream = NULL;
	IStream* pStream;
	HRESULT hr;

	FIXME("(%p)->(%p,%s,%p): stub\n",This,
	      pDesc, debugstr_guid(riid), ppv);
	*ppv = NULL;

	/* Scan object cache first */
	if (pDesc->dwValidData & DMUS_OBJ_OBJECT) {
		FIXME("load object with GUID\n");
	}
	if (pDesc->dwValidData & DMUS_OBJ_STREAM) {
		FIXME("load object from stream\n");
	}
	if (pDesc->dwValidData & DMUS_OBJ_MEMORY) {
		FIXME("load object from memory\n");
	}
	if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) {
		FIXME("load object with fullpath\n");
	}
	if (pDesc->dwValidData & DMUS_OBJ_NAME) {
		if (pDesc->dwValidData & DMUS_OBJ_CATEGORY) {
			/* check this first */
		}
		FIXME("load object with name/category\n");
	}
	if (pDesc->dwValidData & DMUS_OBJ_FILENAME) {
		FIXME("load object with filename\n");
	}

	/* Object not found in cache, need to load it */
	if (pDesc->dwValidData & DMUS_OBJ_OBJECT) {
		if (IsEqualGUID(&pDesc->guidObject, &GUID_DefaultGMCollection)) {
			FIXME("load default GM collection\n");
			hr = E_FAIL;
			goto fail;
		}
	}

	/* create object of class */
	hr = CoCreateInstance(&pDesc->guidClass, NULL, CLSCTX_INPROC,
			      &IID_IDirectMusicObject, (void**)&pObj);
	if (FAILED(hr)) goto fail;

	/* get its IPersistStream interface */
	hr = IDirectMusicObject_QueryInterface(pObj, &IID_IPersistStream, (void**)&pObjStream);
	if (FAILED(hr)) goto fail;

	if (pDesc->dwValidData & DMUS_OBJ_STREAM) {
		/* create object from app stream */
		hr = IStream_Clone(pDesc->pStream, &pStream);
		if (FAILED(hr)) goto fail;
	}
	else if (pDesc->dwValidData & DMUS_OBJ_MEMORY) {
		IStreamImpl* stream;
		hr = StreamCreate(iface, &stream);
		if (FAILED(hr)) goto fail;
		stream->data = pDesc->pbMemData;
		stream->size.QuadPart = pDesc->llMemLength;
		pStream = (IStream*)stream;
	}
	else if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) {
		IStreamImpl* stream;
		hr = StreamCreateFile(iface, pDesc->wszFileName, &stream);
		if (FAILED(hr)) goto fail;
		pStream = (IStream*)stream;
	}
	else if (pDesc->dwValidData & DMUS_OBJ_FILENAME) {
		FIXME("load file with search path\n");
		hr = E_FAIL;
		goto fail;
	}
	else {
		TRACE("no data source found\n");
		hr = E_FAIL;
		goto fail;
	}

	hr = IPersistStream_Load(pObjStream, pStream);
	IStream_Release(pStream);
	if (FAILED(hr)) goto fail;

	IPersistStream_Release(pObjStream);

	/* get object's interface */
	hr = IDirectMusicObject_QueryInterface(pObj, riid, ppv);

	/* Add object to cache here (instead of releasing it) */
	IDirectMusicObject_Release(pObj);

	return hr;
fail:
	if (pObjStream) IPersistStream_Release(pObjStream);
	if (pObj) IDirectMusicObject_Release(pObj);
	return hr;
}

static void WINAPI IDirectMusicLoaderImpl_SetObject(
		        IDirectMusicLoader8* iface, LPDMUS_OBJECTDESC pDesc)
{
	ICOM_THIS(IDirectMusicLoaderImpl,iface);
	FIXME("(%p): stub\n",This);
}


static void WINAPI IDirectMusicLoaderImpl_CollectGarbage(
	IDirectMusicLoader8* iface)
{
	ICOM_THIS(IDirectMusicLoaderImpl,iface);
	FIXME("(%p): stub\n",This);
}

static ICOM_VTABLE(IDirectMusicLoader8) dmlvt =
{
	ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
	IDirectMusicLoaderImpl_QueryInterface,
	IDirectMusicLoaderImpl_AddRef,
	IDirectMusicLoaderImpl_Release,
	IDirectMusicLoaderImpl_GetObject,
	IDirectMusicLoaderImpl_SetObject,
	IDirectMusicLoaderImpl_SetSearchDirectory,
	(void*)0xdeaf2008, /* ScanDirectory */
	(void*)0xdeaf2009, /* CacheObject */
	(void*)0xdeaf200a, /* ReleaseObject */
	(void*)0xdeaf200b, /* ClearCache */
	(void*)0xdeaf200c, /* EnableCache */
	(void*)0xdeaf200d, /* EnumObject */
	IDirectMusicLoaderImpl_CollectGarbage,
	(void*)0xdeaf200f, /* ReleaseObjectByUnknown */
	IDirectMusicLoaderImpl_LoadObjectFromFile
};



/***********************************************************************
 *		DirectMusicLoaderCreate 
 */
static HRESULT DirectMusicLoaderCreate(REFGUID lpGUID, IDirectMusicLoader8 **ppDM, IUnknown *pUnkOuter )
{
	IDirectMusicLoaderImpl** ippDM=(IDirectMusicLoaderImpl**)ppDM;

	if (lpGUID)
		TRACE("(%p,%p,%p)\n",lpGUID,ippDM,pUnkOuter);
	else
		TRACE("DirectMusicLoaderCreate (%p)\n", ippDM);

	if (ippDM == NULL)
		return E_INVALIDARG;

	/* Allocate memory */
	*ippDM = (IDirectMusicLoaderImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectMusicLoaderImpl));
	if (*ippDM == NULL)
		return E_OUTOFMEMORY;

	ICOM_VTBL(*ippDM)	= &dmlvt;
	(*ippDM)->ref		= 1;

	return DS_OK;
}


/***********************************************************************
 *		DllCanUnloadNow (DMLOADER.@)
 */
HRESULT WINAPI DMLOADER_DllCanUnloadNow(void)
{
    FIXME("(void): stub\n");

    return S_FALSE;
}


/*******************************************************************************
 * DirectMusic ClassFactory
 */
typedef struct
{
    /* IUnknown fields */
    ICOM_VFIELD(IClassFactory);
    DWORD                       ref;
} IClassFactoryImpl;

static HRESULT WINAPI 
DMCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
	ICOM_THIS(IClassFactoryImpl,iface);

	FIXME("(%p)->(%s,%p),stub!\n",This,debugstr_guid(riid),ppobj);
	return E_NOINTERFACE;
}

static ULONG WINAPI
DMCF_AddRef(LPCLASSFACTORY iface) {
	ICOM_THIS(IClassFactoryImpl,iface);
	return ++(This->ref);
}

static ULONG WINAPI DMCF_Release(LPCLASSFACTORY iface) {
	ICOM_THIS(IClassFactoryImpl,iface);
	/* static class, won't be  freed */
	return --(This->ref);
}

static HRESULT WINAPI DMCF_CreateInstance(
	LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
) {
	ICOM_THIS(IClassFactoryImpl,iface);

	TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
	if ( IsEqualGUID( &IID_IDirectMusicLoader, riid ) ||
	     IsEqualGUID( &IID_IDirectMusicLoader8, riid ) ) {
		return DirectMusicLoaderCreate(riid,(IDirectMusicLoader8**)ppobj,pOuter);
	}
	return E_NOINTERFACE;
}

static HRESULT WINAPI DMCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
	ICOM_THIS(IClassFactoryImpl,iface);
	FIXME("(%p)->(%d),stub!\n",This,dolock);
	return S_OK;
}

static ICOM_VTABLE(IClassFactory) DMCF_Vtbl = {
	ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
	DMCF_QueryInterface,
	DMCF_AddRef,
	DMCF_Release,
	DMCF_CreateInstance,
	DMCF_LockServer
};
static IClassFactoryImpl DMLOADER_CF = {&DMCF_Vtbl, 1 };

/*******************************************************************************
 * DllGetClassObject [DMLOADER.@]
 * Retrieves class object from a DLL object
 *
 * PARAMS
 *    rclsid [I] CLSID for the class object
 *    riid   [I] Reference to identifier of interface for class object
 *    ppv    [O] Address of variable to receive interface pointer for riid
 *
 * RETURNS
 *    Success: S_OK
 *    Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
 *             E_UNEXPECTED
 */
DWORD WINAPI DMLOADER_DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID *ppv)
{
    TRACE("(%p,%p,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
    if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {
    	*ppv = (LPVOID)&DMLOADER_CF;
        IClassFactory_AddRef((IClassFactory*)*ppv);
        return S_OK;
    }

    FIXME("(%p,%p,%p): no interface found.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
    return CLASS_E_CLASSNOTAVAILABLE;
}



/***********************************************************************
 *		DllRegisterServer (DMLOADER.@)
 */
HRESULT WINAPI DMLOADER_DllRegisterServer(void)
{
    FIXME("(void): stub\n");

    return S_OK;
}

/***********************************************************************
 *		DllUnregisterServer (DMLOADER.@)
 */
HRESULT WINAPI DMLOADER_DllUnregisterServer(void)
{
    FIXME("(void): stub\n");

    return S_OK;
}
