diff --git a/dlls/dmband/dmobject.c b/dlls/dmband/dmobject.c index 9ea31ab32a6..e6a1ce906a7 100644 --- a/dlls/dmband/dmobject.c +++ b/dlls/dmband/dmobject.c @@ -28,6 +28,7 @@ #include "dmusics.h" #include "dmobject.h" #include "wine/debug.h" +#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(dmobj); WINE_DECLARE_DEBUG_CHANNEL(dmfile); @@ -349,7 +350,7 @@ HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) return S_OK; } -HRESULT stream_skip_chunk(IStream *stream, struct chunk_entry *chunk) +HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) { LARGE_INTEGER end; @@ -371,6 +372,50 @@ HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) return stream_get_chunk(stream, chunk); } +/* Reads chunk data of the form: + DWORD - size of array element + element[] - Array of elements + The caller needs to heap_free() the array. +*/ +HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, + unsigned int *count, DWORD elem_size) +{ + DWORD size; + HRESULT hr; + + *array = NULL; + *count = 0; + + if (chunk->size < sizeof(DWORD)) { + WARN_(dmfile)("%s: Too short to read element size\n", debugstr_chunk(chunk)); + return E_FAIL; + } + if (FAILED(hr = stream_read(stream, &size, sizeof(DWORD)))) + return hr; + if (size != elem_size) { + WARN_(dmfile)("%s: Array element size mismatch: got %u, expected %u\n", + debugstr_chunk(chunk), size, elem_size); + return DMUS_E_UNSUPPORTED_STREAM; + } + + *count = (chunk->size - sizeof(DWORD)) / elem_size; + size = *count * elem_size; + if (!(*array = heap_alloc(size))) + return E_OUTOFMEMORY; + if (FAILED(hr = stream_read(stream, *array, size))) { + heap_free(*array); + *array = NULL; + return hr; + } + + if (chunk->size > size + sizeof(DWORD)) { + WARN_(dmfile)("%s: Extraneous data at end of array\n", debugstr_chunk(chunk)); + stream_skip_chunk(stream, chunk); + return S_FALSE; + } + return S_OK; +} + HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, ULONG size) { @@ -529,15 +574,30 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { switch (chunk.id) { + case DMUS_FOURCC_CATEGORY_CHUNK: + if ((supported & DMUS_OBJ_CATEGORY) && stream_chunk_get_wstr(stream, &chunk, + desc->wszCategory, sizeof(desc->wszCategory)) == S_OK) + desc->dwValidData |= DMUS_OBJ_CATEGORY; + break; + case DMUS_FOURCC_DATE_CHUNK: + if ((supported & DMUS_OBJ_DATE) && stream_chunk_get_data(stream, &chunk, + &desc->ftDate, sizeof(desc->ftDate)) == S_OK) + desc->dwValidData |= DMUS_OBJ_DATE; + break; + case DMUS_FOURCC_FILE_CHUNK: + if ((supported & DMUS_OBJ_FILENAME) && stream_chunk_get_wstr(stream, &chunk, + desc->wszFileName, sizeof(desc->wszFileName)) == S_OK) + desc->dwValidData |= DMUS_OBJ_FILENAME; + break; case DMUS_FOURCC_GUID_CHUNK: if ((supported & DMUS_OBJ_OBJECT) && stream_chunk_get_data(stream, &chunk, &desc->guidObject, sizeof(desc->guidObject)) == S_OK) desc->dwValidData |= DMUS_OBJ_OBJECT; break; - case DMUS_FOURCC_CATEGORY_CHUNK: - if ((supported & DMUS_OBJ_CATEGORY) && stream_chunk_get_wstr(stream, &chunk, - desc->wszCategory, sizeof(desc->wszCategory)) == S_OK) - desc->dwValidData |= DMUS_OBJ_CATEGORY; + case DMUS_FOURCC_NAME_CHUNK: + if ((supported & DMUS_OBJ_NAME) && stream_chunk_get_wstr(stream, &chunk, + desc->wszName, sizeof(desc->wszName)) == S_OK) + desc->dwValidData |= DMUS_OBJ_NAME; break; case DMUS_FOURCC_VERSION_CHUNK: if ((supported & DMUS_OBJ_VERSION) && stream_chunk_get_data(stream, &chunk, @@ -557,6 +617,47 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, return hr; } +HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, + IDirectMusicObject **dmobj) +{ + struct chunk_entry chunk = {.parent = list}; + IDirectMusicGetLoader *getloader; + IDirectMusicLoader *loader; + DMUS_OBJECTDESC desc; + DMUS_IO_REFERENCE reference; + HRESULT hr; + + if (FAILED(hr = stream_next_chunk(stream, &chunk))) + return hr; + if (chunk.id != DMUS_FOURCC_REF_CHUNK) + return DMUS_E_UNSUPPORTED_STREAM; + + if (FAILED(hr = stream_chunk_get_data(stream, &chunk, &reference, sizeof(reference)))) { + WARN("Failed to read data of %s\n", debugstr_chunk(&chunk)); + return hr; + } + TRACE("REFERENCE guidClassID %s, dwValidData %#x\n", debugstr_dmguid(&reference.guidClassID), + reference.dwValidData); + + if (FAILED(hr = dmobj_parsedescriptor(stream, list, &desc, reference.dwValidData))) + return hr; + desc.guidClass = reference.guidClassID; + desc.dwValidData |= DMUS_OBJ_CLASS; + dump_DMUS_OBJECTDESC(&desc); + + if (FAILED(hr = IStream_QueryInterface(stream, &IID_IDirectMusicGetLoader, (void**)&getloader))) + return hr; + hr = IDirectMusicGetLoader_GetLoader(getloader, &loader); + IDirectMusicGetLoader_Release(getloader); + if (FAILED(hr)) + return hr; + + hr = IDirectMusicLoader_GetObject(loader, &desc, &IID_IDirectMusicObject, (void**)dmobj); + IDirectMusicLoader_Release(loader); + + return hr; +} + /* Generic IPersistStream methods */ static inline struct dmobject *impl_from_IPersistStream(IPersistStream *iface) { diff --git a/dlls/dmband/dmobject.h b/dlls/dmband/dmobject.h index d347020691c..afe721dc824 100644 --- a/dlls/dmband/dmobject.h +++ b/dlls/dmband/dmobject.h @@ -33,8 +33,10 @@ struct chunk_entry { HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; -HRESULT stream_skip_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; +HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) DECLSPEC_HIDDEN; +HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, + unsigned int *count, DWORD elem_size) DECLSPEC_HIDDEN; HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, ULONG size) DECLSPEC_HIDDEN; HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, @@ -89,6 +91,10 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, #define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ #define DMUS_OBJ_NAME_INFO 0x2000 /* 'INAM' chunk in INFO list */ +/* 'DMRF' (reference list) helper */ +HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, + IDirectMusicObject **dmobj) DECLSPEC_HIDDEN; + /* Generic IPersistStream methods */ HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; diff --git a/dlls/dmcompos/dmobject.c b/dlls/dmcompos/dmobject.c index 9ea31ab32a6..e6a1ce906a7 100644 --- a/dlls/dmcompos/dmobject.c +++ b/dlls/dmcompos/dmobject.c @@ -28,6 +28,7 @@ #include "dmusics.h" #include "dmobject.h" #include "wine/debug.h" +#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(dmobj); WINE_DECLARE_DEBUG_CHANNEL(dmfile); @@ -349,7 +350,7 @@ HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) return S_OK; } -HRESULT stream_skip_chunk(IStream *stream, struct chunk_entry *chunk) +HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) { LARGE_INTEGER end; @@ -371,6 +372,50 @@ HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) return stream_get_chunk(stream, chunk); } +/* Reads chunk data of the form: + DWORD - size of array element + element[] - Array of elements + The caller needs to heap_free() the array. +*/ +HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, + unsigned int *count, DWORD elem_size) +{ + DWORD size; + HRESULT hr; + + *array = NULL; + *count = 0; + + if (chunk->size < sizeof(DWORD)) { + WARN_(dmfile)("%s: Too short to read element size\n", debugstr_chunk(chunk)); + return E_FAIL; + } + if (FAILED(hr = stream_read(stream, &size, sizeof(DWORD)))) + return hr; + if (size != elem_size) { + WARN_(dmfile)("%s: Array element size mismatch: got %u, expected %u\n", + debugstr_chunk(chunk), size, elem_size); + return DMUS_E_UNSUPPORTED_STREAM; + } + + *count = (chunk->size - sizeof(DWORD)) / elem_size; + size = *count * elem_size; + if (!(*array = heap_alloc(size))) + return E_OUTOFMEMORY; + if (FAILED(hr = stream_read(stream, *array, size))) { + heap_free(*array); + *array = NULL; + return hr; + } + + if (chunk->size > size + sizeof(DWORD)) { + WARN_(dmfile)("%s: Extraneous data at end of array\n", debugstr_chunk(chunk)); + stream_skip_chunk(stream, chunk); + return S_FALSE; + } + return S_OK; +} + HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, ULONG size) { @@ -529,15 +574,30 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { switch (chunk.id) { + case DMUS_FOURCC_CATEGORY_CHUNK: + if ((supported & DMUS_OBJ_CATEGORY) && stream_chunk_get_wstr(stream, &chunk, + desc->wszCategory, sizeof(desc->wszCategory)) == S_OK) + desc->dwValidData |= DMUS_OBJ_CATEGORY; + break; + case DMUS_FOURCC_DATE_CHUNK: + if ((supported & DMUS_OBJ_DATE) && stream_chunk_get_data(stream, &chunk, + &desc->ftDate, sizeof(desc->ftDate)) == S_OK) + desc->dwValidData |= DMUS_OBJ_DATE; + break; + case DMUS_FOURCC_FILE_CHUNK: + if ((supported & DMUS_OBJ_FILENAME) && stream_chunk_get_wstr(stream, &chunk, + desc->wszFileName, sizeof(desc->wszFileName)) == S_OK) + desc->dwValidData |= DMUS_OBJ_FILENAME; + break; case DMUS_FOURCC_GUID_CHUNK: if ((supported & DMUS_OBJ_OBJECT) && stream_chunk_get_data(stream, &chunk, &desc->guidObject, sizeof(desc->guidObject)) == S_OK) desc->dwValidData |= DMUS_OBJ_OBJECT; break; - case DMUS_FOURCC_CATEGORY_CHUNK: - if ((supported & DMUS_OBJ_CATEGORY) && stream_chunk_get_wstr(stream, &chunk, - desc->wszCategory, sizeof(desc->wszCategory)) == S_OK) - desc->dwValidData |= DMUS_OBJ_CATEGORY; + case DMUS_FOURCC_NAME_CHUNK: + if ((supported & DMUS_OBJ_NAME) && stream_chunk_get_wstr(stream, &chunk, + desc->wszName, sizeof(desc->wszName)) == S_OK) + desc->dwValidData |= DMUS_OBJ_NAME; break; case DMUS_FOURCC_VERSION_CHUNK: if ((supported & DMUS_OBJ_VERSION) && stream_chunk_get_data(stream, &chunk, @@ -557,6 +617,47 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, return hr; } +HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, + IDirectMusicObject **dmobj) +{ + struct chunk_entry chunk = {.parent = list}; + IDirectMusicGetLoader *getloader; + IDirectMusicLoader *loader; + DMUS_OBJECTDESC desc; + DMUS_IO_REFERENCE reference; + HRESULT hr; + + if (FAILED(hr = stream_next_chunk(stream, &chunk))) + return hr; + if (chunk.id != DMUS_FOURCC_REF_CHUNK) + return DMUS_E_UNSUPPORTED_STREAM; + + if (FAILED(hr = stream_chunk_get_data(stream, &chunk, &reference, sizeof(reference)))) { + WARN("Failed to read data of %s\n", debugstr_chunk(&chunk)); + return hr; + } + TRACE("REFERENCE guidClassID %s, dwValidData %#x\n", debugstr_dmguid(&reference.guidClassID), + reference.dwValidData); + + if (FAILED(hr = dmobj_parsedescriptor(stream, list, &desc, reference.dwValidData))) + return hr; + desc.guidClass = reference.guidClassID; + desc.dwValidData |= DMUS_OBJ_CLASS; + dump_DMUS_OBJECTDESC(&desc); + + if (FAILED(hr = IStream_QueryInterface(stream, &IID_IDirectMusicGetLoader, (void**)&getloader))) + return hr; + hr = IDirectMusicGetLoader_GetLoader(getloader, &loader); + IDirectMusicGetLoader_Release(getloader); + if (FAILED(hr)) + return hr; + + hr = IDirectMusicLoader_GetObject(loader, &desc, &IID_IDirectMusicObject, (void**)dmobj); + IDirectMusicLoader_Release(loader); + + return hr; +} + /* Generic IPersistStream methods */ static inline struct dmobject *impl_from_IPersistStream(IPersistStream *iface) { diff --git a/dlls/dmcompos/dmobject.h b/dlls/dmcompos/dmobject.h index d347020691c..afe721dc824 100644 --- a/dlls/dmcompos/dmobject.h +++ b/dlls/dmcompos/dmobject.h @@ -33,8 +33,10 @@ struct chunk_entry { HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; -HRESULT stream_skip_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; +HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) DECLSPEC_HIDDEN; +HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, + unsigned int *count, DWORD elem_size) DECLSPEC_HIDDEN; HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, ULONG size) DECLSPEC_HIDDEN; HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, @@ -89,6 +91,10 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, #define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ #define DMUS_OBJ_NAME_INFO 0x2000 /* 'INAM' chunk in INFO list */ +/* 'DMRF' (reference list) helper */ +HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, + IDirectMusicObject **dmobj) DECLSPEC_HIDDEN; + /* Generic IPersistStream methods */ HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; diff --git a/dlls/dmloader/dmobject.c b/dlls/dmloader/dmobject.c index 9ea31ab32a6..e6a1ce906a7 100644 --- a/dlls/dmloader/dmobject.c +++ b/dlls/dmloader/dmobject.c @@ -28,6 +28,7 @@ #include "dmusics.h" #include "dmobject.h" #include "wine/debug.h" +#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(dmobj); WINE_DECLARE_DEBUG_CHANNEL(dmfile); @@ -349,7 +350,7 @@ HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) return S_OK; } -HRESULT stream_skip_chunk(IStream *stream, struct chunk_entry *chunk) +HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) { LARGE_INTEGER end; @@ -371,6 +372,50 @@ HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) return stream_get_chunk(stream, chunk); } +/* Reads chunk data of the form: + DWORD - size of array element + element[] - Array of elements + The caller needs to heap_free() the array. +*/ +HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, + unsigned int *count, DWORD elem_size) +{ + DWORD size; + HRESULT hr; + + *array = NULL; + *count = 0; + + if (chunk->size < sizeof(DWORD)) { + WARN_(dmfile)("%s: Too short to read element size\n", debugstr_chunk(chunk)); + return E_FAIL; + } + if (FAILED(hr = stream_read(stream, &size, sizeof(DWORD)))) + return hr; + if (size != elem_size) { + WARN_(dmfile)("%s: Array element size mismatch: got %u, expected %u\n", + debugstr_chunk(chunk), size, elem_size); + return DMUS_E_UNSUPPORTED_STREAM; + } + + *count = (chunk->size - sizeof(DWORD)) / elem_size; + size = *count * elem_size; + if (!(*array = heap_alloc(size))) + return E_OUTOFMEMORY; + if (FAILED(hr = stream_read(stream, *array, size))) { + heap_free(*array); + *array = NULL; + return hr; + } + + if (chunk->size > size + sizeof(DWORD)) { + WARN_(dmfile)("%s: Extraneous data at end of array\n", debugstr_chunk(chunk)); + stream_skip_chunk(stream, chunk); + return S_FALSE; + } + return S_OK; +} + HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, ULONG size) { @@ -529,15 +574,30 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { switch (chunk.id) { + case DMUS_FOURCC_CATEGORY_CHUNK: + if ((supported & DMUS_OBJ_CATEGORY) && stream_chunk_get_wstr(stream, &chunk, + desc->wszCategory, sizeof(desc->wszCategory)) == S_OK) + desc->dwValidData |= DMUS_OBJ_CATEGORY; + break; + case DMUS_FOURCC_DATE_CHUNK: + if ((supported & DMUS_OBJ_DATE) && stream_chunk_get_data(stream, &chunk, + &desc->ftDate, sizeof(desc->ftDate)) == S_OK) + desc->dwValidData |= DMUS_OBJ_DATE; + break; + case DMUS_FOURCC_FILE_CHUNK: + if ((supported & DMUS_OBJ_FILENAME) && stream_chunk_get_wstr(stream, &chunk, + desc->wszFileName, sizeof(desc->wszFileName)) == S_OK) + desc->dwValidData |= DMUS_OBJ_FILENAME; + break; case DMUS_FOURCC_GUID_CHUNK: if ((supported & DMUS_OBJ_OBJECT) && stream_chunk_get_data(stream, &chunk, &desc->guidObject, sizeof(desc->guidObject)) == S_OK) desc->dwValidData |= DMUS_OBJ_OBJECT; break; - case DMUS_FOURCC_CATEGORY_CHUNK: - if ((supported & DMUS_OBJ_CATEGORY) && stream_chunk_get_wstr(stream, &chunk, - desc->wszCategory, sizeof(desc->wszCategory)) == S_OK) - desc->dwValidData |= DMUS_OBJ_CATEGORY; + case DMUS_FOURCC_NAME_CHUNK: + if ((supported & DMUS_OBJ_NAME) && stream_chunk_get_wstr(stream, &chunk, + desc->wszName, sizeof(desc->wszName)) == S_OK) + desc->dwValidData |= DMUS_OBJ_NAME; break; case DMUS_FOURCC_VERSION_CHUNK: if ((supported & DMUS_OBJ_VERSION) && stream_chunk_get_data(stream, &chunk, @@ -557,6 +617,47 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, return hr; } +HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, + IDirectMusicObject **dmobj) +{ + struct chunk_entry chunk = {.parent = list}; + IDirectMusicGetLoader *getloader; + IDirectMusicLoader *loader; + DMUS_OBJECTDESC desc; + DMUS_IO_REFERENCE reference; + HRESULT hr; + + if (FAILED(hr = stream_next_chunk(stream, &chunk))) + return hr; + if (chunk.id != DMUS_FOURCC_REF_CHUNK) + return DMUS_E_UNSUPPORTED_STREAM; + + if (FAILED(hr = stream_chunk_get_data(stream, &chunk, &reference, sizeof(reference)))) { + WARN("Failed to read data of %s\n", debugstr_chunk(&chunk)); + return hr; + } + TRACE("REFERENCE guidClassID %s, dwValidData %#x\n", debugstr_dmguid(&reference.guidClassID), + reference.dwValidData); + + if (FAILED(hr = dmobj_parsedescriptor(stream, list, &desc, reference.dwValidData))) + return hr; + desc.guidClass = reference.guidClassID; + desc.dwValidData |= DMUS_OBJ_CLASS; + dump_DMUS_OBJECTDESC(&desc); + + if (FAILED(hr = IStream_QueryInterface(stream, &IID_IDirectMusicGetLoader, (void**)&getloader))) + return hr; + hr = IDirectMusicGetLoader_GetLoader(getloader, &loader); + IDirectMusicGetLoader_Release(getloader); + if (FAILED(hr)) + return hr; + + hr = IDirectMusicLoader_GetObject(loader, &desc, &IID_IDirectMusicObject, (void**)dmobj); + IDirectMusicLoader_Release(loader); + + return hr; +} + /* Generic IPersistStream methods */ static inline struct dmobject *impl_from_IPersistStream(IPersistStream *iface) { diff --git a/dlls/dmloader/dmobject.h b/dlls/dmloader/dmobject.h index d347020691c..afe721dc824 100644 --- a/dlls/dmloader/dmobject.h +++ b/dlls/dmloader/dmobject.h @@ -33,8 +33,10 @@ struct chunk_entry { HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; -HRESULT stream_skip_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; +HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) DECLSPEC_HIDDEN; +HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, + unsigned int *count, DWORD elem_size) DECLSPEC_HIDDEN; HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, ULONG size) DECLSPEC_HIDDEN; HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, @@ -89,6 +91,10 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, #define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ #define DMUS_OBJ_NAME_INFO 0x2000 /* 'INAM' chunk in INFO list */ +/* 'DMRF' (reference list) helper */ +HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, + IDirectMusicObject **dmobj) DECLSPEC_HIDDEN; + /* Generic IPersistStream methods */ HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; diff --git a/dlls/dmscript/dmobject.c b/dlls/dmscript/dmobject.c index 9ea31ab32a6..e6a1ce906a7 100644 --- a/dlls/dmscript/dmobject.c +++ b/dlls/dmscript/dmobject.c @@ -28,6 +28,7 @@ #include "dmusics.h" #include "dmobject.h" #include "wine/debug.h" +#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(dmobj); WINE_DECLARE_DEBUG_CHANNEL(dmfile); @@ -349,7 +350,7 @@ HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) return S_OK; } -HRESULT stream_skip_chunk(IStream *stream, struct chunk_entry *chunk) +HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) { LARGE_INTEGER end; @@ -371,6 +372,50 @@ HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) return stream_get_chunk(stream, chunk); } +/* Reads chunk data of the form: + DWORD - size of array element + element[] - Array of elements + The caller needs to heap_free() the array. +*/ +HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, + unsigned int *count, DWORD elem_size) +{ + DWORD size; + HRESULT hr; + + *array = NULL; + *count = 0; + + if (chunk->size < sizeof(DWORD)) { + WARN_(dmfile)("%s: Too short to read element size\n", debugstr_chunk(chunk)); + return E_FAIL; + } + if (FAILED(hr = stream_read(stream, &size, sizeof(DWORD)))) + return hr; + if (size != elem_size) { + WARN_(dmfile)("%s: Array element size mismatch: got %u, expected %u\n", + debugstr_chunk(chunk), size, elem_size); + return DMUS_E_UNSUPPORTED_STREAM; + } + + *count = (chunk->size - sizeof(DWORD)) / elem_size; + size = *count * elem_size; + if (!(*array = heap_alloc(size))) + return E_OUTOFMEMORY; + if (FAILED(hr = stream_read(stream, *array, size))) { + heap_free(*array); + *array = NULL; + return hr; + } + + if (chunk->size > size + sizeof(DWORD)) { + WARN_(dmfile)("%s: Extraneous data at end of array\n", debugstr_chunk(chunk)); + stream_skip_chunk(stream, chunk); + return S_FALSE; + } + return S_OK; +} + HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, ULONG size) { @@ -529,15 +574,30 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { switch (chunk.id) { + case DMUS_FOURCC_CATEGORY_CHUNK: + if ((supported & DMUS_OBJ_CATEGORY) && stream_chunk_get_wstr(stream, &chunk, + desc->wszCategory, sizeof(desc->wszCategory)) == S_OK) + desc->dwValidData |= DMUS_OBJ_CATEGORY; + break; + case DMUS_FOURCC_DATE_CHUNK: + if ((supported & DMUS_OBJ_DATE) && stream_chunk_get_data(stream, &chunk, + &desc->ftDate, sizeof(desc->ftDate)) == S_OK) + desc->dwValidData |= DMUS_OBJ_DATE; + break; + case DMUS_FOURCC_FILE_CHUNK: + if ((supported & DMUS_OBJ_FILENAME) && stream_chunk_get_wstr(stream, &chunk, + desc->wszFileName, sizeof(desc->wszFileName)) == S_OK) + desc->dwValidData |= DMUS_OBJ_FILENAME; + break; case DMUS_FOURCC_GUID_CHUNK: if ((supported & DMUS_OBJ_OBJECT) && stream_chunk_get_data(stream, &chunk, &desc->guidObject, sizeof(desc->guidObject)) == S_OK) desc->dwValidData |= DMUS_OBJ_OBJECT; break; - case DMUS_FOURCC_CATEGORY_CHUNK: - if ((supported & DMUS_OBJ_CATEGORY) && stream_chunk_get_wstr(stream, &chunk, - desc->wszCategory, sizeof(desc->wszCategory)) == S_OK) - desc->dwValidData |= DMUS_OBJ_CATEGORY; + case DMUS_FOURCC_NAME_CHUNK: + if ((supported & DMUS_OBJ_NAME) && stream_chunk_get_wstr(stream, &chunk, + desc->wszName, sizeof(desc->wszName)) == S_OK) + desc->dwValidData |= DMUS_OBJ_NAME; break; case DMUS_FOURCC_VERSION_CHUNK: if ((supported & DMUS_OBJ_VERSION) && stream_chunk_get_data(stream, &chunk, @@ -557,6 +617,47 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, return hr; } +HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, + IDirectMusicObject **dmobj) +{ + struct chunk_entry chunk = {.parent = list}; + IDirectMusicGetLoader *getloader; + IDirectMusicLoader *loader; + DMUS_OBJECTDESC desc; + DMUS_IO_REFERENCE reference; + HRESULT hr; + + if (FAILED(hr = stream_next_chunk(stream, &chunk))) + return hr; + if (chunk.id != DMUS_FOURCC_REF_CHUNK) + return DMUS_E_UNSUPPORTED_STREAM; + + if (FAILED(hr = stream_chunk_get_data(stream, &chunk, &reference, sizeof(reference)))) { + WARN("Failed to read data of %s\n", debugstr_chunk(&chunk)); + return hr; + } + TRACE("REFERENCE guidClassID %s, dwValidData %#x\n", debugstr_dmguid(&reference.guidClassID), + reference.dwValidData); + + if (FAILED(hr = dmobj_parsedescriptor(stream, list, &desc, reference.dwValidData))) + return hr; + desc.guidClass = reference.guidClassID; + desc.dwValidData |= DMUS_OBJ_CLASS; + dump_DMUS_OBJECTDESC(&desc); + + if (FAILED(hr = IStream_QueryInterface(stream, &IID_IDirectMusicGetLoader, (void**)&getloader))) + return hr; + hr = IDirectMusicGetLoader_GetLoader(getloader, &loader); + IDirectMusicGetLoader_Release(getloader); + if (FAILED(hr)) + return hr; + + hr = IDirectMusicLoader_GetObject(loader, &desc, &IID_IDirectMusicObject, (void**)dmobj); + IDirectMusicLoader_Release(loader); + + return hr; +} + /* Generic IPersistStream methods */ static inline struct dmobject *impl_from_IPersistStream(IPersistStream *iface) { diff --git a/dlls/dmscript/dmobject.h b/dlls/dmscript/dmobject.h index d347020691c..afe721dc824 100644 --- a/dlls/dmscript/dmobject.h +++ b/dlls/dmscript/dmobject.h @@ -33,8 +33,10 @@ struct chunk_entry { HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; -HRESULT stream_skip_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; +HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) DECLSPEC_HIDDEN; +HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, + unsigned int *count, DWORD elem_size) DECLSPEC_HIDDEN; HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, ULONG size) DECLSPEC_HIDDEN; HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, @@ -89,6 +91,10 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, #define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ #define DMUS_OBJ_NAME_INFO 0x2000 /* 'INAM' chunk in INFO list */ +/* 'DMRF' (reference list) helper */ +HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, + IDirectMusicObject **dmobj) DECLSPEC_HIDDEN; + /* Generic IPersistStream methods */ HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; diff --git a/dlls/dmstyle/dmobject.c b/dlls/dmstyle/dmobject.c index 9ea31ab32a6..e6a1ce906a7 100644 --- a/dlls/dmstyle/dmobject.c +++ b/dlls/dmstyle/dmobject.c @@ -28,6 +28,7 @@ #include "dmusics.h" #include "dmobject.h" #include "wine/debug.h" +#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(dmobj); WINE_DECLARE_DEBUG_CHANNEL(dmfile); @@ -349,7 +350,7 @@ HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) return S_OK; } -HRESULT stream_skip_chunk(IStream *stream, struct chunk_entry *chunk) +HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) { LARGE_INTEGER end; @@ -371,6 +372,50 @@ HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) return stream_get_chunk(stream, chunk); } +/* Reads chunk data of the form: + DWORD - size of array element + element[] - Array of elements + The caller needs to heap_free() the array. +*/ +HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, + unsigned int *count, DWORD elem_size) +{ + DWORD size; + HRESULT hr; + + *array = NULL; + *count = 0; + + if (chunk->size < sizeof(DWORD)) { + WARN_(dmfile)("%s: Too short to read element size\n", debugstr_chunk(chunk)); + return E_FAIL; + } + if (FAILED(hr = stream_read(stream, &size, sizeof(DWORD)))) + return hr; + if (size != elem_size) { + WARN_(dmfile)("%s: Array element size mismatch: got %u, expected %u\n", + debugstr_chunk(chunk), size, elem_size); + return DMUS_E_UNSUPPORTED_STREAM; + } + + *count = (chunk->size - sizeof(DWORD)) / elem_size; + size = *count * elem_size; + if (!(*array = heap_alloc(size))) + return E_OUTOFMEMORY; + if (FAILED(hr = stream_read(stream, *array, size))) { + heap_free(*array); + *array = NULL; + return hr; + } + + if (chunk->size > size + sizeof(DWORD)) { + WARN_(dmfile)("%s: Extraneous data at end of array\n", debugstr_chunk(chunk)); + stream_skip_chunk(stream, chunk); + return S_FALSE; + } + return S_OK; +} + HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, ULONG size) { @@ -529,15 +574,30 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { switch (chunk.id) { + case DMUS_FOURCC_CATEGORY_CHUNK: + if ((supported & DMUS_OBJ_CATEGORY) && stream_chunk_get_wstr(stream, &chunk, + desc->wszCategory, sizeof(desc->wszCategory)) == S_OK) + desc->dwValidData |= DMUS_OBJ_CATEGORY; + break; + case DMUS_FOURCC_DATE_CHUNK: + if ((supported & DMUS_OBJ_DATE) && stream_chunk_get_data(stream, &chunk, + &desc->ftDate, sizeof(desc->ftDate)) == S_OK) + desc->dwValidData |= DMUS_OBJ_DATE; + break; + case DMUS_FOURCC_FILE_CHUNK: + if ((supported & DMUS_OBJ_FILENAME) && stream_chunk_get_wstr(stream, &chunk, + desc->wszFileName, sizeof(desc->wszFileName)) == S_OK) + desc->dwValidData |= DMUS_OBJ_FILENAME; + break; case DMUS_FOURCC_GUID_CHUNK: if ((supported & DMUS_OBJ_OBJECT) && stream_chunk_get_data(stream, &chunk, &desc->guidObject, sizeof(desc->guidObject)) == S_OK) desc->dwValidData |= DMUS_OBJ_OBJECT; break; - case DMUS_FOURCC_CATEGORY_CHUNK: - if ((supported & DMUS_OBJ_CATEGORY) && stream_chunk_get_wstr(stream, &chunk, - desc->wszCategory, sizeof(desc->wszCategory)) == S_OK) - desc->dwValidData |= DMUS_OBJ_CATEGORY; + case DMUS_FOURCC_NAME_CHUNK: + if ((supported & DMUS_OBJ_NAME) && stream_chunk_get_wstr(stream, &chunk, + desc->wszName, sizeof(desc->wszName)) == S_OK) + desc->dwValidData |= DMUS_OBJ_NAME; break; case DMUS_FOURCC_VERSION_CHUNK: if ((supported & DMUS_OBJ_VERSION) && stream_chunk_get_data(stream, &chunk, @@ -557,6 +617,47 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, return hr; } +HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, + IDirectMusicObject **dmobj) +{ + struct chunk_entry chunk = {.parent = list}; + IDirectMusicGetLoader *getloader; + IDirectMusicLoader *loader; + DMUS_OBJECTDESC desc; + DMUS_IO_REFERENCE reference; + HRESULT hr; + + if (FAILED(hr = stream_next_chunk(stream, &chunk))) + return hr; + if (chunk.id != DMUS_FOURCC_REF_CHUNK) + return DMUS_E_UNSUPPORTED_STREAM; + + if (FAILED(hr = stream_chunk_get_data(stream, &chunk, &reference, sizeof(reference)))) { + WARN("Failed to read data of %s\n", debugstr_chunk(&chunk)); + return hr; + } + TRACE("REFERENCE guidClassID %s, dwValidData %#x\n", debugstr_dmguid(&reference.guidClassID), + reference.dwValidData); + + if (FAILED(hr = dmobj_parsedescriptor(stream, list, &desc, reference.dwValidData))) + return hr; + desc.guidClass = reference.guidClassID; + desc.dwValidData |= DMUS_OBJ_CLASS; + dump_DMUS_OBJECTDESC(&desc); + + if (FAILED(hr = IStream_QueryInterface(stream, &IID_IDirectMusicGetLoader, (void**)&getloader))) + return hr; + hr = IDirectMusicGetLoader_GetLoader(getloader, &loader); + IDirectMusicGetLoader_Release(getloader); + if (FAILED(hr)) + return hr; + + hr = IDirectMusicLoader_GetObject(loader, &desc, &IID_IDirectMusicObject, (void**)dmobj); + IDirectMusicLoader_Release(loader); + + return hr; +} + /* Generic IPersistStream methods */ static inline struct dmobject *impl_from_IPersistStream(IPersistStream *iface) { diff --git a/dlls/dmstyle/dmobject.h b/dlls/dmstyle/dmobject.h index d347020691c..afe721dc824 100644 --- a/dlls/dmstyle/dmobject.h +++ b/dlls/dmstyle/dmobject.h @@ -33,8 +33,10 @@ struct chunk_entry { HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; -HRESULT stream_skip_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; +HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) DECLSPEC_HIDDEN; +HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, + unsigned int *count, DWORD elem_size) DECLSPEC_HIDDEN; HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, ULONG size) DECLSPEC_HIDDEN; HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, @@ -89,6 +91,10 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, #define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ #define DMUS_OBJ_NAME_INFO 0x2000 /* 'INAM' chunk in INFO list */ +/* 'DMRF' (reference list) helper */ +HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, + IDirectMusicObject **dmobj) DECLSPEC_HIDDEN; + /* Generic IPersistStream methods */ HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; diff --git a/dlls/dmusic/dmobject.c b/dlls/dmusic/dmobject.c index 9ea31ab32a6..e6a1ce906a7 100644 --- a/dlls/dmusic/dmobject.c +++ b/dlls/dmusic/dmobject.c @@ -28,6 +28,7 @@ #include "dmusics.h" #include "dmobject.h" #include "wine/debug.h" +#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(dmobj); WINE_DECLARE_DEBUG_CHANNEL(dmfile); @@ -349,7 +350,7 @@ HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) return S_OK; } -HRESULT stream_skip_chunk(IStream *stream, struct chunk_entry *chunk) +HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) { LARGE_INTEGER end; @@ -371,6 +372,50 @@ HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) return stream_get_chunk(stream, chunk); } +/* Reads chunk data of the form: + DWORD - size of array element + element[] - Array of elements + The caller needs to heap_free() the array. +*/ +HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, + unsigned int *count, DWORD elem_size) +{ + DWORD size; + HRESULT hr; + + *array = NULL; + *count = 0; + + if (chunk->size < sizeof(DWORD)) { + WARN_(dmfile)("%s: Too short to read element size\n", debugstr_chunk(chunk)); + return E_FAIL; + } + if (FAILED(hr = stream_read(stream, &size, sizeof(DWORD)))) + return hr; + if (size != elem_size) { + WARN_(dmfile)("%s: Array element size mismatch: got %u, expected %u\n", + debugstr_chunk(chunk), size, elem_size); + return DMUS_E_UNSUPPORTED_STREAM; + } + + *count = (chunk->size - sizeof(DWORD)) / elem_size; + size = *count * elem_size; + if (!(*array = heap_alloc(size))) + return E_OUTOFMEMORY; + if (FAILED(hr = stream_read(stream, *array, size))) { + heap_free(*array); + *array = NULL; + return hr; + } + + if (chunk->size > size + sizeof(DWORD)) { + WARN_(dmfile)("%s: Extraneous data at end of array\n", debugstr_chunk(chunk)); + stream_skip_chunk(stream, chunk); + return S_FALSE; + } + return S_OK; +} + HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, ULONG size) { @@ -529,15 +574,30 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { switch (chunk.id) { + case DMUS_FOURCC_CATEGORY_CHUNK: + if ((supported & DMUS_OBJ_CATEGORY) && stream_chunk_get_wstr(stream, &chunk, + desc->wszCategory, sizeof(desc->wszCategory)) == S_OK) + desc->dwValidData |= DMUS_OBJ_CATEGORY; + break; + case DMUS_FOURCC_DATE_CHUNK: + if ((supported & DMUS_OBJ_DATE) && stream_chunk_get_data(stream, &chunk, + &desc->ftDate, sizeof(desc->ftDate)) == S_OK) + desc->dwValidData |= DMUS_OBJ_DATE; + break; + case DMUS_FOURCC_FILE_CHUNK: + if ((supported & DMUS_OBJ_FILENAME) && stream_chunk_get_wstr(stream, &chunk, + desc->wszFileName, sizeof(desc->wszFileName)) == S_OK) + desc->dwValidData |= DMUS_OBJ_FILENAME; + break; case DMUS_FOURCC_GUID_CHUNK: if ((supported & DMUS_OBJ_OBJECT) && stream_chunk_get_data(stream, &chunk, &desc->guidObject, sizeof(desc->guidObject)) == S_OK) desc->dwValidData |= DMUS_OBJ_OBJECT; break; - case DMUS_FOURCC_CATEGORY_CHUNK: - if ((supported & DMUS_OBJ_CATEGORY) && stream_chunk_get_wstr(stream, &chunk, - desc->wszCategory, sizeof(desc->wszCategory)) == S_OK) - desc->dwValidData |= DMUS_OBJ_CATEGORY; + case DMUS_FOURCC_NAME_CHUNK: + if ((supported & DMUS_OBJ_NAME) && stream_chunk_get_wstr(stream, &chunk, + desc->wszName, sizeof(desc->wszName)) == S_OK) + desc->dwValidData |= DMUS_OBJ_NAME; break; case DMUS_FOURCC_VERSION_CHUNK: if ((supported & DMUS_OBJ_VERSION) && stream_chunk_get_data(stream, &chunk, @@ -557,6 +617,47 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, return hr; } +HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, + IDirectMusicObject **dmobj) +{ + struct chunk_entry chunk = {.parent = list}; + IDirectMusicGetLoader *getloader; + IDirectMusicLoader *loader; + DMUS_OBJECTDESC desc; + DMUS_IO_REFERENCE reference; + HRESULT hr; + + if (FAILED(hr = stream_next_chunk(stream, &chunk))) + return hr; + if (chunk.id != DMUS_FOURCC_REF_CHUNK) + return DMUS_E_UNSUPPORTED_STREAM; + + if (FAILED(hr = stream_chunk_get_data(stream, &chunk, &reference, sizeof(reference)))) { + WARN("Failed to read data of %s\n", debugstr_chunk(&chunk)); + return hr; + } + TRACE("REFERENCE guidClassID %s, dwValidData %#x\n", debugstr_dmguid(&reference.guidClassID), + reference.dwValidData); + + if (FAILED(hr = dmobj_parsedescriptor(stream, list, &desc, reference.dwValidData))) + return hr; + desc.guidClass = reference.guidClassID; + desc.dwValidData |= DMUS_OBJ_CLASS; + dump_DMUS_OBJECTDESC(&desc); + + if (FAILED(hr = IStream_QueryInterface(stream, &IID_IDirectMusicGetLoader, (void**)&getloader))) + return hr; + hr = IDirectMusicGetLoader_GetLoader(getloader, &loader); + IDirectMusicGetLoader_Release(getloader); + if (FAILED(hr)) + return hr; + + hr = IDirectMusicLoader_GetObject(loader, &desc, &IID_IDirectMusicObject, (void**)dmobj); + IDirectMusicLoader_Release(loader); + + return hr; +} + /* Generic IPersistStream methods */ static inline struct dmobject *impl_from_IPersistStream(IPersistStream *iface) { diff --git a/dlls/dmusic/dmobject.h b/dlls/dmusic/dmobject.h index d347020691c..afe721dc824 100644 --- a/dlls/dmusic/dmobject.h +++ b/dlls/dmusic/dmobject.h @@ -33,8 +33,10 @@ struct chunk_entry { HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; -HRESULT stream_skip_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; +HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) DECLSPEC_HIDDEN; +HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, + unsigned int *count, DWORD elem_size) DECLSPEC_HIDDEN; HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, ULONG size) DECLSPEC_HIDDEN; HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, @@ -89,6 +91,10 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, #define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ #define DMUS_OBJ_NAME_INFO 0x2000 /* 'INAM' chunk in INFO list */ +/* 'DMRF' (reference list) helper */ +HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, + IDirectMusicObject **dmobj) DECLSPEC_HIDDEN; + /* Generic IPersistStream methods */ HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; diff --git a/dlls/dswave/dmobject.c b/dlls/dswave/dmobject.c index 9ea31ab32a6..e6a1ce906a7 100644 --- a/dlls/dswave/dmobject.c +++ b/dlls/dswave/dmobject.c @@ -28,6 +28,7 @@ #include "dmusics.h" #include "dmobject.h" #include "wine/debug.h" +#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(dmobj); WINE_DECLARE_DEBUG_CHANNEL(dmfile); @@ -349,7 +350,7 @@ HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) return S_OK; } -HRESULT stream_skip_chunk(IStream *stream, struct chunk_entry *chunk) +HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) { LARGE_INTEGER end; @@ -371,6 +372,50 @@ HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) return stream_get_chunk(stream, chunk); } +/* Reads chunk data of the form: + DWORD - size of array element + element[] - Array of elements + The caller needs to heap_free() the array. +*/ +HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, + unsigned int *count, DWORD elem_size) +{ + DWORD size; + HRESULT hr; + + *array = NULL; + *count = 0; + + if (chunk->size < sizeof(DWORD)) { + WARN_(dmfile)("%s: Too short to read element size\n", debugstr_chunk(chunk)); + return E_FAIL; + } + if (FAILED(hr = stream_read(stream, &size, sizeof(DWORD)))) + return hr; + if (size != elem_size) { + WARN_(dmfile)("%s: Array element size mismatch: got %u, expected %u\n", + debugstr_chunk(chunk), size, elem_size); + return DMUS_E_UNSUPPORTED_STREAM; + } + + *count = (chunk->size - sizeof(DWORD)) / elem_size; + size = *count * elem_size; + if (!(*array = heap_alloc(size))) + return E_OUTOFMEMORY; + if (FAILED(hr = stream_read(stream, *array, size))) { + heap_free(*array); + *array = NULL; + return hr; + } + + if (chunk->size > size + sizeof(DWORD)) { + WARN_(dmfile)("%s: Extraneous data at end of array\n", debugstr_chunk(chunk)); + stream_skip_chunk(stream, chunk); + return S_FALSE; + } + return S_OK; +} + HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, ULONG size) { @@ -529,15 +574,30 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { switch (chunk.id) { + case DMUS_FOURCC_CATEGORY_CHUNK: + if ((supported & DMUS_OBJ_CATEGORY) && stream_chunk_get_wstr(stream, &chunk, + desc->wszCategory, sizeof(desc->wszCategory)) == S_OK) + desc->dwValidData |= DMUS_OBJ_CATEGORY; + break; + case DMUS_FOURCC_DATE_CHUNK: + if ((supported & DMUS_OBJ_DATE) && stream_chunk_get_data(stream, &chunk, + &desc->ftDate, sizeof(desc->ftDate)) == S_OK) + desc->dwValidData |= DMUS_OBJ_DATE; + break; + case DMUS_FOURCC_FILE_CHUNK: + if ((supported & DMUS_OBJ_FILENAME) && stream_chunk_get_wstr(stream, &chunk, + desc->wszFileName, sizeof(desc->wszFileName)) == S_OK) + desc->dwValidData |= DMUS_OBJ_FILENAME; + break; case DMUS_FOURCC_GUID_CHUNK: if ((supported & DMUS_OBJ_OBJECT) && stream_chunk_get_data(stream, &chunk, &desc->guidObject, sizeof(desc->guidObject)) == S_OK) desc->dwValidData |= DMUS_OBJ_OBJECT; break; - case DMUS_FOURCC_CATEGORY_CHUNK: - if ((supported & DMUS_OBJ_CATEGORY) && stream_chunk_get_wstr(stream, &chunk, - desc->wszCategory, sizeof(desc->wszCategory)) == S_OK) - desc->dwValidData |= DMUS_OBJ_CATEGORY; + case DMUS_FOURCC_NAME_CHUNK: + if ((supported & DMUS_OBJ_NAME) && stream_chunk_get_wstr(stream, &chunk, + desc->wszName, sizeof(desc->wszName)) == S_OK) + desc->dwValidData |= DMUS_OBJ_NAME; break; case DMUS_FOURCC_VERSION_CHUNK: if ((supported & DMUS_OBJ_VERSION) && stream_chunk_get_data(stream, &chunk, @@ -557,6 +617,47 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, return hr; } +HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, + IDirectMusicObject **dmobj) +{ + struct chunk_entry chunk = {.parent = list}; + IDirectMusicGetLoader *getloader; + IDirectMusicLoader *loader; + DMUS_OBJECTDESC desc; + DMUS_IO_REFERENCE reference; + HRESULT hr; + + if (FAILED(hr = stream_next_chunk(stream, &chunk))) + return hr; + if (chunk.id != DMUS_FOURCC_REF_CHUNK) + return DMUS_E_UNSUPPORTED_STREAM; + + if (FAILED(hr = stream_chunk_get_data(stream, &chunk, &reference, sizeof(reference)))) { + WARN("Failed to read data of %s\n", debugstr_chunk(&chunk)); + return hr; + } + TRACE("REFERENCE guidClassID %s, dwValidData %#x\n", debugstr_dmguid(&reference.guidClassID), + reference.dwValidData); + + if (FAILED(hr = dmobj_parsedescriptor(stream, list, &desc, reference.dwValidData))) + return hr; + desc.guidClass = reference.guidClassID; + desc.dwValidData |= DMUS_OBJ_CLASS; + dump_DMUS_OBJECTDESC(&desc); + + if (FAILED(hr = IStream_QueryInterface(stream, &IID_IDirectMusicGetLoader, (void**)&getloader))) + return hr; + hr = IDirectMusicGetLoader_GetLoader(getloader, &loader); + IDirectMusicGetLoader_Release(getloader); + if (FAILED(hr)) + return hr; + + hr = IDirectMusicLoader_GetObject(loader, &desc, &IID_IDirectMusicObject, (void**)dmobj); + IDirectMusicLoader_Release(loader); + + return hr; +} + /* Generic IPersistStream methods */ static inline struct dmobject *impl_from_IPersistStream(IPersistStream *iface) { diff --git a/dlls/dswave/dmobject.h b/dlls/dswave/dmobject.h index d347020691c..afe721dc824 100644 --- a/dlls/dswave/dmobject.h +++ b/dlls/dswave/dmobject.h @@ -33,8 +33,10 @@ struct chunk_entry { HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; -HRESULT stream_skip_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; +HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) DECLSPEC_HIDDEN; +HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, + unsigned int *count, DWORD elem_size) DECLSPEC_HIDDEN; HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, ULONG size) DECLSPEC_HIDDEN; HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, @@ -89,6 +91,10 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, #define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ #define DMUS_OBJ_NAME_INFO 0x2000 /* 'INAM' chunk in INFO list */ +/* 'DMRF' (reference list) helper */ +HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, + IDirectMusicObject **dmobj) DECLSPEC_HIDDEN; + /* Generic IPersistStream methods */ HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, void **ret_iface) DECLSPEC_HIDDEN;