[PATCH] dmusic: Sync up the dmobject.[ch] files

Michael Stefaniuc mstefani at winehq.org
Mon Nov 30 14:41:01 CST 2020


Signed-off-by: Michael Stefaniuc <mstefani at winehq.org>
---
 dlls/dmband/dmobject.c   | 111 +++++++++++++++++++++++++++++++++++++--
 dlls/dmband/dmobject.h   |   8 ++-
 dlls/dmcompos/dmobject.c | 111 +++++++++++++++++++++++++++++++++++++--
 dlls/dmcompos/dmobject.h |   8 ++-
 dlls/dmloader/dmobject.c | 111 +++++++++++++++++++++++++++++++++++++--
 dlls/dmloader/dmobject.h |   8 ++-
 dlls/dmscript/dmobject.c | 111 +++++++++++++++++++++++++++++++++++++--
 dlls/dmscript/dmobject.h |   8 ++-
 dlls/dmstyle/dmobject.c  | 111 +++++++++++++++++++++++++++++++++++++--
 dlls/dmstyle/dmobject.h  |   8 ++-
 dlls/dmusic/dmobject.c   | 111 +++++++++++++++++++++++++++++++++++++--
 dlls/dmusic/dmobject.h   |   8 ++-
 dlls/dswave/dmobject.c   | 111 +++++++++++++++++++++++++++++++++++++--
 dlls/dswave/dmobject.h   |   8 ++-
 14 files changed, 791 insertions(+), 42 deletions(-)

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;
-- 
2.26.2




More information about the wine-devel mailing list