/*		DirectMusic
 *
 * Copyright 2002-2003 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"
#include "dmusicf.h"
#include "dmime_private.h"

WINE_DEFAULT_DEBUG_CHANNEL(dmusic);

typedef struct WaveItem {
    struct WaveItem* next;
    DMUS_IO_WAVE_ITEM_HEADER hdr;
    IUnknown* pWave;
} WaveItem;

static WaveItem* AllocWaveItem(void)
{
	return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WaveItem));
}

static void FreeWaveItem(WaveItem* item)
{
	if (item->pWave) IUnknown_Release(item->pWave);
	HeapFree(GetProcessHeap(), 0, item);
}

static void FreeWaveItems(WaveItem* item)
{
	while (item) {
		WaveItem* next = item->next;
		FreeWaveItem(item);
		item = next;
	}
}

typedef struct WavePart {
    struct WavePart* next;
    DMUS_IO_WAVE_PART_HEADER hdr;
    WaveItem* items;
} WavePart;

static WavePart* AllocWavePart(void)
{
	return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WavePart));
}

static void FreeWavePart(WavePart* part)
{
	FreeWaveItems(part->items);
	HeapFree(GetProcessHeap(), 0, part);
}

static void FreeWaveParts(WavePart* part)
{
	while (part) {
		WavePart* next = part->next;
		FreeWavePart(part);
		part = next;
	}
}

typedef struct {
    DWORD dwGroup;
} WaveState;

/*****************************************************************************
 * IDirectMusicWaveTrack implementation structure
 */
typedef struct IDirectMusicWaveTrackImpl IDirectMusicWaveTrackImpl;
struct IDirectMusicWaveTrackImpl
{
    /* IUnknown fields */
    ICOM_VFIELD(IDirectMusicTrack8);
    ICOM_VTABLE(IPersistStream) *lpPSVtbl;
    DWORD ref;
    /* IDirectMusicWaveTrack fields */
    DMUS_IO_WAVE_TRACK_HEADER hdr;
    WavePart* parts;
};

static HRESULT WINAPI IDirectMusicWaveTrackImpl_ParseStream(
	IDirectMusicWaveTrackImpl* This,
	IStream* pStream);

static HRESULT WINAPI IDirectMusicWaveTrackImpl_QueryInterface(
	IDirectMusicTrack8* iface,REFIID riid,LPVOID *ppobj
) {
	ICOM_THIS(IDirectMusicWaveTrackImpl,iface);

	TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
	if (IsEqualGUID(riid, &IID_IUnknown) ||
	    IsEqualGUID(riid, &IID_IDirectMusicTrack) ||
	    IsEqualGUID(riid, &IID_IDirectMusicTrack8)) {
	    *ppobj = (LPVOID)This;
	    This->ref++;
	    return S_OK;
	}
	if (IsEqualGUID(riid, &IID_IPersist) ||
	    IsEqualGUID(riid, &IID_IPersistStream)) {
	    *ppobj = (LPVOID)&This->lpPSVtbl;
	    This->ref++;
	    return S_OK;
	}
	FIXME("(%p): no interface for %s\n", This, debugstr_guid(riid));
	return E_NOINTERFACE;
}

static ULONG WINAPI IDirectMusicWaveTrackImpl_AddRef(IDirectMusicTrack8* iface) {
	ICOM_THIS(IDirectMusicWaveTrackImpl,iface);
	return ++(This->ref);
}

static ULONG WINAPI IDirectMusicWaveTrackImpl_Release(IDirectMusicTrack8* iface) {
	ICOM_THIS(IDirectMusicWaveTrackImpl,iface);
	TRACE("(%p), ref was %ld\n",This,This->ref);
	if (!--(This->ref)) {
		FreeWaveParts(This->parts);
		HeapFree(GetProcessHeap(),0,This);
		return 0;
	}
	return This->ref;
}

static HRESULT WINAPI IDirectMusicWaveTrackImpl_Init(
	IDirectMusicTrack8* iface,
	IDirectMusicSegment* pSegment)
{
	ICOM_THIS(IDirectMusicWaveTrackImpl,iface);
	TRACE("(%p)->(%p)\n", This, pSegment);
	/* tempo tracks don't play anything, nothing to do */
	return S_OK;
}

static HRESULT WINAPI IDirectMusicWaveTrackImpl_InitPlay(
	IDirectMusicTrack8* iface,
	IDirectMusicSegmentState* pSegmentState,
	IDirectMusicPerformance* pPerformance,
	void** ppStateData,
	DWORD dwVirtualTrackID,
	DWORD dwFlags)
{
	ICOM_THIS(IDirectMusicWaveTrackImpl,iface);
	IDirectMusicSegment* pSegment;
	WaveState* state;
	HRESULT hr;

	TRACE("(%p)->(%p,%p,%p,%ld,0x%lx)\n", This,
	      pSegmentState, pPerformance, ppStateData, dwVirtualTrackID, dwFlags);
	state = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WaveState));
	state->dwGroup = (DWORD)-1;
	hr = IDirectMusicSegmentState_GetSegment(pSegmentState, &pSegment);
	if (SUCCEEDED(hr)) {
		hr = IDirectMusicSegment_GetTrackGroup(pSegment, (IDirectMusicTrack*)This,
						       &state->dwGroup);
		IDirectMusicSegment_Release(pSegment);
	}
	*ppStateData = state;
	return S_OK;
}

static HRESULT WINAPI IDirectMusicWaveTrackImpl_EndPlay(
	IDirectMusicTrack8* iface,
	void* pStateData)
{
	ICOM_THIS(IDirectMusicWaveTrackImpl,iface);
	WaveState* state;
	TRACE("(%p)->(%p)\n", This, pStateData);
	state = pStateData;
	HeapFree(GetProcessHeap(), 0, state);
	return S_OK;
}

static HRESULT WINAPI IDirectMusicWaveTrackImpl_RealPlay(
	IDirectMusicWaveTrackImpl* This,
	void* pStateData,
	REFERENCE_TIME rtStart,
	REFERENCE_TIME rtEnd,
	REFERENCE_TIME rtOffset,
	DWORD dwFlags,
	IDirectMusicPerformance* pPerf,
	IDirectMusicSegmentState* pSegSt,
	DWORD dwVirtualID,
	DWORD timefmt)
{
	WaveState* state;
	WavePart* part;
	HRESULT hr;

	state = pStateData;
	part = This->parts;
	if (!part) return DMUS_S_END;

	for (; part; part = part->next) {
		WaveItem* item = part->items;
		for (; item; item = item->next) {
			DMUS_WAVE_PMSG *pMsg;
			if (item->hdr.rtTime < rtStart ||
			    item->hdr.rtTime >= rtEnd)
			    continue;
			/* wave item found */
			hr = IDirectMusicPerformance_AllocPMsg(pPerf, sizeof(*pMsg), (DMUS_PMSG**)&pMsg);
			if (FAILED(hr)) continue;
			pMsg->dwType = DMUS_PMSGT_WAVE;
			if (timefmt & DMUS_PMSGF_REFTIME)
				pMsg->rtTime = item->hdr.rtTime + rtOffset;
			if (timefmt & DMUS_PMSGF_MUSICTIME)
				pMsg->mtTime = item->hdr.rtTime + rtOffset;
			pMsg->dwPChannel = part->hdr.dwPChannel;
			pMsg->dwVirtualTrackID = dwVirtualID;
			pMsg->dwFlags = timefmt | DMUS_PMSGF_TOOL_QUEUE;
			pMsg->dwGroupID = state->dwGroup;
			pMsg->rtStartOffset = item->hdr.rtStartOffset;
			pMsg->rtDuration = item->hdr.rtDuration;
			if (timefmt & DMUS_PMSGF_MUSICTIME)
				pMsg->lOffset = item->hdr.mtLogicalTime - item->hdr.rtTime;
			pMsg->lVolume = This->hdr.lVolume + part->hdr.lVolume + item->hdr.lVolume;
			pMsg->lPitch = item->hdr.lPitch;
			pMsg->bFlags = dwFlags;
			IUnknown_AddRef(item->pWave);
			pMsg->punkUser = item->pWave;
			hr = IDirectMusicPerformance_SendPMsg(pPerf, (DMUS_PMSG*)pMsg);
			if (FAILED(hr)) {
				IDirectMusicPerformance_FreePMsg(pPerf, (DMUS_PMSG*)pMsg);
			}
		}
	}
	
	return S_OK;
}

static HRESULT WINAPI IDirectMusicWaveTrackImpl_Play(
	IDirectMusicTrack8* iface,
	void* pStateData,
	MUSIC_TIME mtStart,
	MUSIC_TIME mtEnd,
	MUSIC_TIME mtOffset,
	DWORD dwFlags,
	IDirectMusicPerformance* pPerf,
	IDirectMusicSegmentState* pSegSt,
	DWORD dwVirtualID)
{
	ICOM_THIS(IDirectMusicWaveTrackImpl,iface);
	HRESULT hr;

	TRACE("(%p)->(%p,%ld,%ld,%ld,0x%lx,%p,%p,%ld)\n", This, pStateData,
	      mtStart, mtEnd, mtOffset, dwFlags, pPerf, pSegSt, dwVirtualID);

	/* I can't seem to be able to find a way to tell whether the track
	 * is in reference time or music time from the track headers I've
	 * seen. But the tracks I'm working with now seem to use music time,
	 * so I guess I'll use that for now. */
	if (TRUE) {
		return IDirectMusicWaveTrackImpl_RealPlay(This, pStateData, mtStart, mtEnd, mtOffset,
							  dwFlags, pPerf, pSegSt, dwVirtualID,
							  DMUS_PMSGF_MUSICTIME);
	} else {
		REFERENCE_TIME rtStart, rtEnd, rtOffset;

		/* if the track is in reference time, we have to convert
		 * arguments to that format first (music time has less precision,
		 * so doing it the other way (converting track to music time)
		 * is not such a good idea) */

		hr = IDirectMusicPerformance_MusicToReferenceTime(pPerf, mtStart, &rtStart);
		if (FAILED(hr)) return hr;
		hr = IDirectMusicPerformance_MusicToReferenceTime(pPerf, mtEnd, &rtEnd);
		if (FAILED(hr)) return hr;
		/* not sure if this is right */
		hr = IDirectMusicPerformance_MusicToReferenceTime(pPerf, mtOffset, &rtOffset);
		if (FAILED(hr)) return hr;
		/* go ahead and play */
		return IDirectMusicWaveTrackImpl_RealPlay(This, pStateData, rtStart, rtEnd, rtOffset,
							  dwFlags, pPerf, pSegSt, dwVirtualID,
							  DMUS_PMSGF_REFTIME | DMUS_PMSGF_LOCKTOREFTIME);
	}
}

static HRESULT WINAPI IDirectMusicWaveTrackImpl_GetParam(
	IDirectMusicTrack8* iface,
	REFGUID rguidType,
	MUSIC_TIME mtTime,
	MUSIC_TIME* pmtNext,
	void* pParam)
{
	ICOM_THIS(IDirectMusicWaveTrackImpl,iface);
	FIXME("(%p)->(%s,%ld,%p,%p): stub\n", This, debugstr_guid(rguidType), mtTime, pmtNext, pParam);
	return DMUS_E_GET_UNSUPPORTED;
}

static HRESULT WINAPI IDirectMusicWaveTrackImpl_IsParamSupported(
	IDirectMusicTrack8* iface,
	REFGUID rguidType)
{
	ICOM_THIS(IDirectMusicWaveTrackImpl,iface);
	FIXME("(%p)->(%s): stub\n", This, debugstr_guid(rguidType));
	return DMUS_E_TYPE_UNSUPPORTED;
}

static ICOM_VTABLE(IDirectMusicTrack8) dmttvt = 
{
	ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
	IDirectMusicWaveTrackImpl_QueryInterface,
	IDirectMusicWaveTrackImpl_AddRef,
	IDirectMusicWaveTrackImpl_Release,
	IDirectMusicWaveTrackImpl_Init,
	IDirectMusicWaveTrackImpl_InitPlay,
	IDirectMusicWaveTrackImpl_EndPlay,
	IDirectMusicWaveTrackImpl_Play,
	IDirectMusicWaveTrackImpl_GetParam,
	(void*)0xdeaf510a, /* SetParam */
	IDirectMusicWaveTrackImpl_IsParamSupported,
	(void*)0xdeaf510c, /* AddNotificationType */
	(void*)0xdeaf510d, /* RemoveNotificationType */
	(void*)0xdeaf510e, /* Clone */
	(void*)0xdeaf510f, /* PlayEx */
	(void*)0xdeaf5110, /* GetParamEx */
	(void*)0xdeaf5111, /* SetParamEx */
	(void*)0xdeaf5112, /* Compose */
	(void*)0xdeaf5113  /* Join */
};

static HRESULT WINAPI IDirectMusicWaveTrackStream_QueryInterface(
	IPersistStream* iface,REFIID riid,LPVOID *ppobj
) {
	ICOM_THIS_MULTI(IDirectMusicWaveTrackImpl,lpPSVtbl,iface);
	return IDirectMusicTrack8_QueryInterface((IDirectMusicTrack8*)This, riid, ppobj);
}

static ULONG WINAPI IDirectMusicWaveTrackStream_AddRef(IPersistStream* iface) {
	ICOM_THIS_MULTI(IDirectMusicWaveTrackImpl,lpPSVtbl,iface);
	return IDirectMusicTrack8_AddRef((IDirectMusicTrack8*)This);
}

static ULONG WINAPI IDirectMusicWaveTrackStream_Release(IPersistStream* iface) {
	ICOM_THIS_MULTI(IDirectMusicWaveTrackImpl,lpPSVtbl,iface);
	return IDirectMusicTrack8_Release((IDirectMusicTrack8*)This);
}

static HRESULT WINAPI IDirectMusicWaveTrackStream_GetClassID(
	IPersistStream* iface,
	CLSID* pClassID)
{
	ICOM_THIS_MULTI(IDirectMusicWaveTrackImpl,lpPSVtbl,iface);
	TRACE("(%p)->(%p)\n", This, pClassID);
	memcpy(pClassID, &CLSID_DirectMusicWaveTrack, sizeof(CLSID));
	return S_OK;
}

static HRESULT WINAPI IDirectMusicWaveTrackStream_IsDirty(
	IPersistStream* iface)
{
	ICOM_THIS_MULTI(IDirectMusicWaveTrackImpl,lpPSVtbl,iface);
	TRACE("(%p)->()\n", This);
	return S_FALSE; /* who cares */
}

static HRESULT WINAPI IDirectMusicWaveTrackStream_Load(
	IPersistStream* iface,
	IStream* pStream)
{
	ICOM_THIS_MULTI(IDirectMusicWaveTrackImpl,lpPSVtbl,iface);
	TRACE("(%p)->(%p)\n", This, pStream);
	return IDirectMusicWaveTrackImpl_ParseStream(This, pStream);
}

static HRESULT WINAPI IDirectMusicWaveTrackStream_Save(
	IPersistStream* iface,
	IStream* pStream,
	BOOL fClearDirty)
{
	ICOM_THIS_MULTI(IDirectMusicWaveTrackImpl,lpPSVtbl,iface);
	FIXME("(%p)->(%p,%d): stub\n", This, pStream, fClearDirty);
	return E_NOTIMPL;
}

static HRESULT WINAPI IDirectMusicWaveTrackStream_GetSizeMax(
	IPersistStream* iface,
	ULARGE_INTEGER* pcbSize)
{
	ICOM_THIS_MULTI(IDirectMusicWaveTrackImpl,lpPSVtbl,iface);
	FIXME("(%p)->(%p): stub\n", This, pcbSize);
	pcbSize->QuadPart = 0;
	return E_NOTIMPL;
}

static ICOM_VTABLE(IPersistStream) dmpsvt =
{
	ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
	IDirectMusicWaveTrackStream_QueryInterface,
	IDirectMusicWaveTrackStream_AddRef,
	IDirectMusicWaveTrackStream_Release,
	IDirectMusicWaveTrackStream_GetClassID,
	IDirectMusicWaveTrackStream_IsDirty,
	IDirectMusicWaveTrackStream_Load,
	IDirectMusicWaveTrackStream_Save,
	IDirectMusicWaveTrackStream_GetSizeMax
};

static HRESULT WINAPI IDirectMusicWaveTrackImpl_ParseWaveList(
	IDirectMusicWaveTrackImpl* This,
	HMMIO hmmio,
	LPMMCKINFO pck,
	WaveItem* item)
{
	MMCKINFO ck;
	MMRESULT mmres;

	mmres = mmioDescend(hmmio, &ck, pck, 0);
	if (mmres) {
		ERR("mmio error %d\n", mmres);
		return E_FAIL;
	}
	/* first chunk should be wave item header */
	if (ck.ckid != DMUS_FOURCC_WAVEITEM_CHUNK) {
		ERR("Wave Item chunk not found\n");
		return E_FAIL;
	}
	STRUCT_READ_START(item->hdr)
	STRUCT_READ(item->hdr, lVolume)
	STRUCT_READ(item->hdr, lPitch)
	STRUCT_READ(item->hdr, dwVariations)
	STRUCT_READ(item->hdr, _padding_)
	STRUCT_READ(item->hdr, rtTime)
	STRUCT_READ(item->hdr, rtStartOffset)
	STRUCT_READ(item->hdr, rtReserved)
	STRUCT_READ(item->hdr, rtDuration)
	STRUCT_READ(item->hdr, mtLogicalTime)
	STRUCT_READ(item->hdr, dwLoopStart)
	STRUCT_READ(item->hdr, dwLoopEnd)
	STRUCT_READ(item->hdr, dwFlags)
	STRUCT_READ_END(item->hdr)
	mmioAscend(hmmio, &ck, 0);
	TRACE("  Item Volume: %ld\n", item->hdr.lVolume);
	TRACE("        Pitch: %ld\n", item->hdr.lPitch);
	TRACE("         Time: %Ld\n", item->hdr.rtTime);
	TRACE("     Duration: %Ld\n", item->hdr.rtDuration);
	TRACE("    Log. time: %ld\n", item->hdr.mtLogicalTime);
	TRACE("        Flags: 0x%lx\n", item->hdr.dwFlags);
	/* read remaining chunks */
	while (!(mmres = mmioDescend(hmmio, &ck, pck, 0))) {
		HRESULT hr;
		if (ck.ckid == FOURCC_LIST) {
			switch (ck.fccType) {
			case DMUS_FOURCC_REF_LIST:
				TRACE("Reference list found\n");
				if (item->pWave) {
					ERR("More than one reference list found\n");
					IUnknown_Release(item->pWave);
					item->pWave = NULL;
				}
				hr = DMIME_LoadReference(hmmio, &ck, &IID_IUnknown, (void**)&item->pWave);
				if (FAILED(hr)) return hr;
				break;
			default:
				FIXME("unknown list %08lx\n", ck.ckid);
				break;
			}
		}
		else {
			FIXME("unknown chunk %08lx\n", ck.ckid);
			break;
		}
		mmioAscend(hmmio, &ck, 0);
	}
	if (mmres != MMIOERR_CHUNKNOTFOUND) {
		ERR("mmio error %d\n", mmres);
		return E_FAIL;
	}
	return S_OK;
}

static HRESULT WINAPI IDirectMusicWaveTrackImpl_ParseItemList(
	IDirectMusicWaveTrackImpl* This,
	HMMIO hmmio,
	LPMMCKINFO pck,
	WavePart* part)
{
	MMCKINFO ck;
	MMRESULT mmres;
	WaveItem* new_item;
	WaveItem* last_item = part->items;

	if (last_item) {
		while (last_item->next)
			last_item = last_item->next;
	}

	while (!(mmres = mmioDescend(hmmio, &ck, pck, 0))) {
		HRESULT hr;
		if (ck.ckid != FOURCC_LIST || ck.fccType != DMUS_FOURCC_WAVE_LIST) {
			ERR("Wave list not found\n");
			return E_FAIL;
		}
		new_item = AllocWaveItem();
		hr = IDirectMusicWaveTrackImpl_ParseWaveList(This, hmmio, &ck, new_item);
		if (FAILED(hr)) {
			FreeWaveItem(new_item);
			return hr;
		}
		if (last_item) last_item->next = new_item;
		else part->items = new_item;
		last_item = new_item;
		mmioAscend(hmmio, &ck, 0);
	}
	if (mmres != MMIOERR_CHUNKNOTFOUND) {
		ERR("mmio error %d\n", mmres);
		return E_FAIL;
	}
	return S_OK;
}

static HRESULT WINAPI IDirectMusicWaveTrackImpl_ParsePartList(
	IDirectMusicWaveTrackImpl* This,
	HMMIO hmmio,
	LPMMCKINFO pck,
	WavePart* part)
{
	MMCKINFO ck;
	MMRESULT mmres;

	mmres = mmioDescend(hmmio, &ck, pck, 0);
	if (mmres) {
		ERR("mmio error %d\n", mmres);
		return E_FAIL;
	}
	/* first chunk should be wave part header */
	if (ck.ckid != DMUS_FOURCC_WAVEPART_CHUNK) {
		ERR("Wave Part chunk not found\n");
		return E_FAIL;
	}
	STRUCT_READ_START(part->hdr)
	STRUCT_READ(part->hdr, lVolume)
	STRUCT_READ(part->hdr, dwVariations)
	STRUCT_READ(part->hdr, dwPChannel)
	STRUCT_READ(part->hdr, dwLockToPart)
	STRUCT_READ(part->hdr, dwFlags)
	STRUCT_READ(part->hdr, dwIndex)
	STRUCT_READ_END(part->hdr)
	mmioAscend(hmmio, &ck, 0);
	TRACE(" Part volume: %ld\n", part->hdr.lVolume);
	TRACE("       Flags: 0x%lx\n", part->hdr.dwFlags);
	/* read remaining chunks */
	while (!(mmres = mmioDescend(hmmio, &ck, pck, 0))) {
		HRESULT hr;
		if (ck.ckid == FOURCC_LIST) {
			switch (ck.fccType) {
			case DMUS_FOURCC_WAVEITEM_LIST:
				TRACE("Wave Item list found\n");
				hr = IDirectMusicWaveTrackImpl_ParseItemList(This, hmmio, &ck, part);
				if (FAILED(hr)) return hr;
				break;
			default:
				FIXME("unknown list %08lx\n", ck.ckid);
				break;
			}
		}
		else {
			FIXME("unknown chunk %08lx\n", ck.ckid);
			break;
		}
		mmioAscend(hmmio, &ck, 0);
	}
	if (mmres != MMIOERR_CHUNKNOTFOUND) {
		ERR("mmio error %d\n", mmres);
		return E_FAIL;
	}
	return S_OK;
}

static HRESULT WINAPI IDirectMusicWaveTrackImpl_ParseList(
	IDirectMusicWaveTrackImpl* This,
	HMMIO hmmio,
	LPMMCKINFO pck)
{
	MMCKINFO ck;
	MMRESULT mmres;
	WavePart* new_part;
	WavePart* last_part = This->parts;

	if (last_part) {
		while (last_part->next)
			last_part = last_part->next;
	}

	mmres = mmioDescend(hmmio, &ck, pck, 0);
	if (mmres) {
		ERR("mmio error %d\n", mmres);
		return E_FAIL;
	}
	/* first chunk should be wave track header */
	if (ck.ckid != DMUS_FOURCC_WAVETRACK_CHUNK) {
		ERR("Wave Track chunk not found\n");
		return E_FAIL;
	}
	STRUCT_READ_START(This->hdr)
	STRUCT_READ(This->hdr, lVolume)
	STRUCT_READ(This->hdr, dwFlags)
	STRUCT_READ_END(This->hdr)
	mmioAscend(hmmio, &ck, 0);
	TRACE("Track volume: %ld\n", This->hdr.lVolume);
	TRACE("Track flags : 0x%lx\n", This->hdr.dwFlags);
	/* read remaining chunks */
	while (!(mmres = mmioDescend(hmmio, &ck, pck, 0))) {
		HRESULT hr;
		if (ck.ckid == FOURCC_LIST) {
			switch (ck.fccType) {
			case DMUS_FOURCC_WAVEPART_LIST:
				TRACE("Wave Part list found\n");
				new_part = AllocWavePart();
				hr = IDirectMusicWaveTrackImpl_ParsePartList(This, hmmio, &ck, new_part);
				if (FAILED(hr)) {
					FreeWavePart(new_part);
					return hr;
				}
				if (last_part) last_part->next = new_part;
				else This->parts = new_part;
				last_part = new_part;
				break;
			default:
				FIXME("unknown list %08lx\n", ck.ckid);
				break;
			}
		}
		else {
			FIXME("unknown chunk %08lx\n", ck.ckid);
			break;
		}
		mmioAscend(hmmio, &ck, 0);
	}
	if (mmres != MMIOERR_CHUNKNOTFOUND) {
		ERR("mmio error %d\n", mmres);
		return E_FAIL;
	}
	return S_OK;
}

static HRESULT WINAPI IDirectMusicWaveTrackImpl_ParseStream(
	IDirectMusicWaveTrackImpl* This,
	IStream* pStream)
{
	HMMIO hmmio;
	MMCKINFO ck;
	MMRESULT mmres;
	HRESULT hr = E_FAIL;

	hmmio = DMIME_mmioOpen(pStream);
	if (!hmmio) return E_FAIL;
	/* descend into data chunk */
	mmres = mmioDescend(hmmio, &ck, NULL, 0);
	if (mmres) {
		ERR("mmio error %d\n", mmres);
		DMIME_mmioClose(hmmio);
		return E_FAIL;
	}
	if (ck.ckid == FOURCC_LIST && ck.fccType == DMUS_FOURCC_WAVETRACK_LIST) {
		TRACE("Wave Track list found\n");
		hr = IDirectMusicWaveTrackImpl_ParseList(This, hmmio, &ck);
	} else {
		ERR("Wave Track not found (found %08lx)\n", ck.ckid);
		hr = E_FAIL;
	}
	mmioAscend(hmmio, &ck, 0);
	DMIME_mmioClose(hmmio);
	return hr;
}


/***********************************************************************
 *		DirectMusicWaveTrackCreate 
 */
HRESULT DirectMusicWaveTrackCreate(REFGUID lpGUID, IUnknown **ppObj, IUnknown *pUnkOuter)
{
	IDirectMusicWaveTrackImpl** ippDM=(IDirectMusicWaveTrackImpl**)ppObj;

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

	if (ippDM == NULL)
		return E_INVALIDARG;

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

	ICOM_VTBL(*ippDM)	= &dmttvt;
	(*ippDM)->lpPSVtbl	= &dmpsvt;
	(*ippDM)->ref		= 1;

	return DS_OK;
}
