| /* | |
| * IDirectMusicInstrument Implementation | |
| * | |
| * Copyright (C) 2003-2004 Rok Mandeljc | |
| * | |
| * This program 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 program 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 program; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #include "dmusic_private.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(dmusic); | |
| static const GUID IID_IDirectMusicInstrumentPRIVATE = { 0xbcb20080, 0xa40c, 0x11d1, { 0x86, 0xbc, 0x00, 0xc0, 0x4f, 0xbf, 0x8f, 0xef } }; | |
| /* IDirectMusicInstrument IUnknown part: */ | |
| static HRESULT WINAPI IDirectMusicInstrumentImpl_QueryInterface(LPDIRECTMUSICINSTRUMENT iface, REFIID riid, LPVOID *ret_iface) | |
| { | |
| TRACE("(%p)->(%s, %p)\n", iface, debugstr_dmguid(riid), ret_iface); | |
| if (IsEqualIID(riid, &IID_IUnknown) || | |
| IsEqualIID(riid, &IID_IDirectMusicInstrument)) | |
| { | |
| *ret_iface = iface; | |
| IDirectMusicInstrument_AddRef(iface); | |
| return S_OK; | |
| } | |
| else if (IsEqualIID(riid, &IID_IDirectMusicInstrumentPRIVATE)) | |
| { | |
| /* it seems to me that this interface is only basic IUnknown, without any | |
| * other inherited functions... *sigh* this is the worst scenario, since it means | |
| * that whoever calls it knows the layout of original implementation table and therefore | |
| * tries to get data by direct access... expect crashes | |
| */ | |
| FIXME("*sigh*... requested private/unspecified interface\n"); | |
| *ret_iface = iface; | |
| IDirectMusicInstrument_AddRef(iface); | |
| return S_OK; | |
| } | |
| WARN("(%p)->(%s, %p): not found\n", iface, debugstr_dmguid(riid), ret_iface); | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI IDirectMusicInstrumentImpl_AddRef(LPDIRECTMUSICINSTRUMENT iface) | |
| { | |
| IDirectMusicInstrumentImpl *This = impl_from_IDirectMusicInstrument(iface); | |
| ULONG ref = InterlockedIncrement(&This->ref); | |
| TRACE("(%p)->(): new ref = %u\n", iface, ref); | |
| return ref; | |
| } | |
| static ULONG WINAPI IDirectMusicInstrumentImpl_Release(LPDIRECTMUSICINSTRUMENT iface) | |
| { | |
| IDirectMusicInstrumentImpl *This = impl_from_IDirectMusicInstrument(iface); | |
| ULONG ref = InterlockedDecrement(&This->ref); | |
| TRACE("(%p)->(): new ref = %u\n", iface, ref); | |
| if (!ref) | |
| { | |
| ULONG i; | |
| HeapFree(GetProcessHeap(), 0, This->regions); | |
| for (i = 0; i < This->nb_articulations; i++) | |
| HeapFree(GetProcessHeap(), 0, This->articulations->connections); | |
| HeapFree(GetProcessHeap(), 0, This->articulations); | |
| HeapFree(GetProcessHeap(), 0, This); | |
| DMUSIC_UnlockModule(); | |
| } | |
| return ref; | |
| } | |
| /* IDirectMusicInstrumentImpl IDirectMusicInstrument part: */ | |
| static HRESULT WINAPI IDirectMusicInstrumentImpl_GetPatch(LPDIRECTMUSICINSTRUMENT iface, DWORD* pdwPatch) | |
| { | |
| IDirectMusicInstrumentImpl *This = impl_from_IDirectMusicInstrument(iface); | |
| TRACE("(%p)->(%p)\n", This, pdwPatch); | |
| *pdwPatch = MIDILOCALE2Patch(&This->header.Locale); | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI IDirectMusicInstrumentImpl_SetPatch(LPDIRECTMUSICINSTRUMENT iface, DWORD dwPatch) | |
| { | |
| IDirectMusicInstrumentImpl *This = impl_from_IDirectMusicInstrument(iface); | |
| TRACE("(%p)->(%d): stub\n", This, dwPatch); | |
| Patch2MIDILOCALE(dwPatch, &This->header.Locale); | |
| return S_OK; | |
| } | |
| static const IDirectMusicInstrumentVtbl DirectMusicInstrument_Vtbl = | |
| { | |
| IDirectMusicInstrumentImpl_QueryInterface, | |
| IDirectMusicInstrumentImpl_AddRef, | |
| IDirectMusicInstrumentImpl_Release, | |
| IDirectMusicInstrumentImpl_GetPatch, | |
| IDirectMusicInstrumentImpl_SetPatch | |
| }; | |
| /* for ClassFactory */ | |
| HRESULT DMUSIC_CreateDirectMusicInstrumentImpl (LPCGUID lpcGUID, LPVOID* ppobj, LPUNKNOWN pUnkOuter) { | |
| IDirectMusicInstrumentImpl* dminst; | |
| HRESULT hr; | |
| dminst = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicInstrumentImpl)); | |
| if (NULL == dminst) { | |
| *ppobj = NULL; | |
| return E_OUTOFMEMORY; | |
| } | |
| dminst->IDirectMusicInstrument_iface.lpVtbl = &DirectMusicInstrument_Vtbl; | |
| dminst->ref = 1; | |
| DMUSIC_LockModule(); | |
| hr = IDirectMusicInstrument_QueryInterface(&dminst->IDirectMusicInstrument_iface, lpcGUID, | |
| ppobj); | |
| IDirectMusicInstrument_Release(&dminst->IDirectMusicInstrument_iface); | |
| return hr; | |
| } | |
| static HRESULT read_from_stream(IStream *stream, void *data, ULONG size) | |
| { | |
| ULONG bytes_read; | |
| HRESULT hr; | |
| hr = IStream_Read(stream, data, size, &bytes_read); | |
| if(FAILED(hr)){ | |
| TRACE("IStream_Read failed: %08x\n", hr); | |
| return hr; | |
| } | |
| if (bytes_read < size) { | |
| TRACE("Didn't read full chunk: %u < %u\n", bytes_read, size); | |
| return E_FAIL; | |
| } | |
| return S_OK; | |
| } | |
| static inline DWORD subtract_bytes(DWORD len, DWORD bytes) | |
| { | |
| if(bytes > len){ | |
| TRACE("Apparent mismatch in chunk lengths? %u bytes remaining, %u bytes read\n", len, bytes); | |
| return 0; | |
| } | |
| return len - bytes; | |
| } | |
| static inline HRESULT advance_stream(IStream *stream, ULONG bytes) | |
| { | |
| LARGE_INTEGER move; | |
| HRESULT ret; | |
| move.QuadPart = bytes; | |
| ret = IStream_Seek(stream, move, STREAM_SEEK_CUR, NULL); | |
| if (FAILED(ret)) | |
| WARN("IStream_Seek failed: %08x\n", ret); | |
| return ret; | |
| } | |
| static HRESULT load_region(IDirectMusicInstrumentImpl *This, IStream *stream, instrument_region *region, ULONG length) | |
| { | |
| HRESULT ret; | |
| DMUS_PRIVATE_CHUNK chunk; | |
| TRACE("(%p, %p, %p, %u)\n", This, stream, region, length); | |
| while (length) | |
| { | |
| ret = read_from_stream(stream, &chunk, sizeof(chunk)); | |
| if (FAILED(ret)) | |
| return ret; | |
| length = subtract_bytes(length, sizeof(chunk)); | |
| switch (chunk.fccID) | |
| { | |
| case FOURCC_RGNH: | |
| TRACE("RGNH chunk (region header): %u bytes\n", chunk.dwSize); | |
| ret = read_from_stream(stream, ®ion->header, sizeof(region->header)); | |
| if (FAILED(ret)) | |
| return ret; | |
| length = subtract_bytes(length, sizeof(region->header)); | |
| break; | |
| case FOURCC_WSMP: | |
| TRACE("WSMP chunk (wave sample): %u bytes\n", chunk.dwSize); | |
| ret = read_from_stream(stream, ®ion->wave_sample, sizeof(region->wave_sample)); | |
| if (FAILED(ret)) | |
| return ret; | |
| length = subtract_bytes(length, sizeof(region->wave_sample)); | |
| if (!(region->loop_present = (chunk.dwSize != sizeof(region->wave_sample)))) | |
| break; | |
| ret = read_from_stream(stream, ®ion->wave_loop, sizeof(region->wave_loop)); | |
| if (FAILED(ret)) | |
| return ret; | |
| length = subtract_bytes(length, sizeof(region->wave_loop)); | |
| break; | |
| case FOURCC_WLNK: | |
| TRACE("WLNK chunk (wave link): %u bytes\n", chunk.dwSize); | |
| ret = read_from_stream(stream, ®ion->wave_link, sizeof(region->wave_link)); | |
| if (FAILED(ret)) | |
| return ret; | |
| length = subtract_bytes(length, sizeof(region->wave_link)); | |
| break; | |
| default: | |
| TRACE("Unknown chunk %s (skipping): %u bytes\n", debugstr_fourcc(chunk.fccID), chunk.dwSize); | |
| ret = advance_stream(stream, chunk.dwSize); | |
| if (FAILED(ret)) | |
| return ret; | |
| length = subtract_bytes(length, chunk.dwSize); | |
| break; | |
| } | |
| } | |
| return S_OK; | |
| } | |
| static HRESULT load_articulation(IDirectMusicInstrumentImpl *This, IStream *stream, ULONG length) | |
| { | |
| HRESULT ret; | |
| instrument_articulation *articulation; | |
| if (!This->articulations) | |
| This->articulations = HeapAlloc(GetProcessHeap(), 0, sizeof(*This->articulations)); | |
| else | |
| This->articulations = HeapReAlloc(GetProcessHeap(), 0, This->articulations, sizeof(*This->articulations) * (This->nb_articulations + 1)); | |
| if (!This->articulations) | |
| return E_OUTOFMEMORY; | |
| articulation = &This->articulations[This->nb_articulations]; | |
| ret = read_from_stream(stream, &articulation->connections_list, sizeof(CONNECTIONLIST)); | |
| if (FAILED(ret)) | |
| return ret; | |
| articulation->connections = HeapAlloc(GetProcessHeap(), 0, sizeof(CONNECTION) * articulation->connections_list.cConnections); | |
| if (!articulation->connections) | |
| return E_OUTOFMEMORY; | |
| ret = read_from_stream(stream, articulation->connections, sizeof(CONNECTION) * articulation->connections_list.cConnections); | |
| if (FAILED(ret)) | |
| { | |
| HeapFree(GetProcessHeap(), 0, articulation->connections); | |
| return ret; | |
| } | |
| subtract_bytes(length, sizeof(CONNECTIONLIST) + sizeof(CONNECTION) * articulation->connections_list.cConnections); | |
| This->nb_articulations++; | |
| return S_OK; | |
| } | |
| /* Function that loads all instrument data and which is called from IDirectMusicCollection_GetInstrument as in native */ | |
| HRESULT IDirectMusicInstrumentImpl_CustomLoad(IDirectMusicInstrument *iface, IStream *stream) | |
| { | |
| IDirectMusicInstrumentImpl *This = impl_from_IDirectMusicInstrument(iface); | |
| HRESULT hr; | |
| DMUS_PRIVATE_CHUNK chunk; | |
| ULONG i = 0; | |
| ULONG length = This->length; | |
| TRACE("(%p, %p): offset = 0x%s, length = %u)\n", This, stream, wine_dbgstr_longlong(This->liInstrumentPosition.QuadPart), This->length); | |
| if (This->loaded) | |
| return S_OK; | |
| hr = IStream_Seek(stream, This->liInstrumentPosition, STREAM_SEEK_SET, NULL); | |
| if (FAILED(hr)) | |
| { | |
| WARN("IStream_Seek failed: %08x\n", hr); | |
| return DMUS_E_UNSUPPORTED_STREAM; | |
| } | |
| This->regions = HeapAlloc(GetProcessHeap(), 0, sizeof(*This->regions) * This->header.cRegions); | |
| if (!This->regions) | |
| return E_OUTOFMEMORY; | |
| while (length) | |
| { | |
| hr = read_from_stream(stream, &chunk, sizeof(chunk)); | |
| if (FAILED(hr)) | |
| goto error; | |
| length = subtract_bytes(length, sizeof(chunk) + chunk.dwSize); | |
| switch (chunk.fccID) | |
| { | |
| case FOURCC_INSH: | |
| case FOURCC_DLID: | |
| TRACE("Chunk %s: %u bytes\n", debugstr_fourcc(chunk.fccID), chunk.dwSize); | |
| /* Instrument header and id are already set so just skip */ | |
| hr = advance_stream(stream, chunk.dwSize); | |
| if (FAILED(hr)) | |
| goto error; | |
| break; | |
| case FOURCC_LIST: { | |
| DWORD size = chunk.dwSize; | |
| TRACE("LIST chunk: %u bytes\n", chunk.dwSize); | |
| hr = read_from_stream(stream, &chunk.fccID, sizeof(chunk.fccID)); | |
| if (FAILED(hr)) | |
| goto error; | |
| size = subtract_bytes(size, sizeof(chunk.fccID)); | |
| switch (chunk.fccID) | |
| { | |
| case FOURCC_LRGN: | |
| TRACE("LRGN chunk (regions list): %u bytes\n", size); | |
| while (size) | |
| { | |
| hr = read_from_stream(stream, &chunk, sizeof(chunk)); | |
| if (FAILED(hr)) | |
| goto error; | |
| if (chunk.fccID != FOURCC_LIST) | |
| { | |
| TRACE("Unknown chunk %s: %u bytes\n", debugstr_fourcc(chunk.fccID), chunk.dwSize); | |
| goto error; | |
| } | |
| hr = read_from_stream(stream, &chunk.fccID, sizeof(chunk.fccID)); | |
| if (FAILED(hr)) | |
| goto error; | |
| if (chunk.fccID == FOURCC_RGN) | |
| { | |
| TRACE("RGN chunk (region): %u bytes\n", chunk.dwSize); | |
| hr = load_region(This, stream, &This->regions[i++], chunk.dwSize - sizeof(chunk.fccID)); | |
| } | |
| else | |
| { | |
| TRACE("Unknown chunk %s: %u bytes\n", debugstr_fourcc(chunk.fccID), chunk.dwSize); | |
| hr = advance_stream(stream, chunk.dwSize - sizeof(chunk.fccID)); | |
| } | |
| if (FAILED(hr)) | |
| goto error; | |
| size = subtract_bytes(size, chunk.dwSize + sizeof(chunk)); | |
| } | |
| break; | |
| case FOURCC_LART: | |
| TRACE("LART chunk (articulations list): %u bytes\n", size); | |
| while (size) | |
| { | |
| hr = read_from_stream(stream, &chunk, sizeof(chunk)); | |
| if (FAILED(hr)) | |
| goto error; | |
| if (chunk.fccID == FOURCC_ART1) | |
| { | |
| TRACE("ART1 chunk (level 1 articulation): %u bytes\n", chunk.dwSize); | |
| hr = load_articulation(This, stream, chunk.dwSize); | |
| } | |
| else | |
| { | |
| TRACE("Unknown chunk %s: %u bytes\n", debugstr_fourcc(chunk.fccID), chunk.dwSize); | |
| hr = advance_stream(stream, chunk.dwSize); | |
| } | |
| if (FAILED(hr)) | |
| goto error; | |
| size = subtract_bytes(size, chunk.dwSize + sizeof(chunk)); | |
| } | |
| break; | |
| default: | |
| TRACE("Unknown chunk %s: %u bytes\n", debugstr_fourcc(chunk.fccID), chunk.dwSize); | |
| hr = advance_stream(stream, chunk.dwSize - sizeof(chunk.fccID)); | |
| if (FAILED(hr)) | |
| goto error; | |
| size = subtract_bytes(size, chunk.dwSize - sizeof(chunk.fccID)); | |
| break; | |
| } | |
| break; | |
| } | |
| default: | |
| TRACE("Unknown chunk %s: %u bytes\n", debugstr_fourcc(chunk.fccID), chunk.dwSize); | |
| hr = advance_stream(stream, chunk.dwSize); | |
| if (FAILED(hr)) | |
| goto error; | |
| break; | |
| } | |
| } | |
| This->loaded = TRUE; | |
| return S_OK; | |
| error: | |
| HeapFree(GetProcessHeap(), 0, This->regions); | |
| This->regions = NULL; | |
| return DMUS_E_UNSUPPORTED_STREAM; | |
| } |