Signed-off-by: Michael Stefaniuc mstefani@winehq.org --- dlls/dmband/dmobject.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++ dlls/dmband/dmobject.h | 53 ++++++++++++ dlls/dmcompos/dmobject.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++ dlls/dmcompos/dmobject.h | 53 ++++++++++++ dlls/dmloader/dmobject.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++ dlls/dmloader/dmobject.h | 53 ++++++++++++ dlls/dmscript/dmobject.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++ dlls/dmscript/dmobject.h | 53 ++++++++++++ dlls/dmstyle/dmobject.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++ dlls/dmstyle/dmobject.h | 53 ++++++++++++ dlls/dmusic/dmobject.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++ dlls/dmusic/dmobject.h | 53 ++++++++++++ dlls/dswave/dmobject.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++ dlls/dswave/dmobject.h | 53 ++++++++++++ 14 files changed, 1855 insertions(+)
diff --git a/dlls/dmband/dmobject.c b/dlls/dmband/dmobject.c index cabb6f74db..25f3198d2b 100644 --- a/dlls/dmband/dmobject.c +++ b/dlls/dmband/dmobject.c @@ -21,12 +21,152 @@ */
#define COBJMACROS +#include <assert.h> #include "objbase.h" #include "dmusici.h" +#include "dmusicf.h" #include "dmobject.h" #include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(dmobj); +WINE_DECLARE_DEBUG_CHANNEL(dmfile); + +/* RIFF format parsing */ +#define CHUNK_HDR_SIZE (sizeof(FOURCC) + sizeof(DWORD)) + +static inline const char *debugstr_fourcc(DWORD fourcc) +{ + if (!fourcc) return "''"; + return wine_dbg_sprintf("'%c%c%c%c'", (char)(fourcc), (char)(fourcc >> 8), + (char)(fourcc >> 16), (char)(fourcc >> 24)); +} + +const char *debugstr_chunk(const struct chunk_entry *chunk) +{ + const char *type = ""; + + if (!chunk) + return "(null)"; + if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) + type = wine_dbg_sprintf("type %s, ", debugstr_fourcc(chunk->type)); + return wine_dbg_sprintf("%s chunk, %ssize %u", debugstr_fourcc(chunk->id), type, chunk->size); +} + +static HRESULT stream_read(IStream *stream, void *data, ULONG size) +{ + ULONG read; + HRESULT hr; + + hr = IStream_Read(stream, data, size, &read); + if (FAILED(hr)) + TRACE_(dmfile)("IStream_Read failed: %08x\n", hr); + else if (!read && read < size) { + /* All or nothing: Handle a partial read due to end of stream as an error */ + TRACE_(dmfile)("Short read: %u < %u\n", read, size); + return E_FAIL; + } + + return hr; +} + +HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) +{ + static const LARGE_INTEGER zero; + ULONGLONG ck_end = 0, p_end = 0; + HRESULT hr; + + hr = IStream_Seek(stream, zero, STREAM_SEEK_CUR, &chunk->offset); + if (FAILED(hr)) + return hr; + assert(!(chunk->offset.QuadPart & 1)); + if (chunk->parent) { + p_end = chunk->parent->offset.QuadPart + CHUNK_HDR_SIZE + ((chunk->parent->size + 1) & ~1); + if (chunk->offset.QuadPart == p_end) + return S_FALSE; + ck_end = chunk->offset.QuadPart + CHUNK_HDR_SIZE; + if (ck_end > p_end) { + WARN_(dmfile)("No space for sub-chunk header in parent chunk: ends at offset %s > %s\n", + wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); + return E_FAIL; + } + } + + hr = stream_read(stream, chunk, CHUNK_HDR_SIZE); + if (hr != S_OK) + return hr; + if (chunk->parent) { + ck_end += (chunk->size + 1) & ~1; + if (ck_end > p_end) { + WARN_(dmfile)("No space for sub-chunk data in parent chunk: ends at offset %s > %s\n", + wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); + return E_FAIL; + } + } + + if (chunk->id == FOURCC_LIST || chunk->id == FOURCC_RIFF) { + hr = stream_read(stream, &chunk->type, sizeof(FOURCC)); + if (hr != S_OK) + return hr != S_FALSE ? hr : E_FAIL; + } + + TRACE_(dmfile)("Returning %s\n", debugstr_chunk(chunk)); + + return S_OK; +} + +HRESULT stream_skip_chunk(IStream *stream, struct chunk_entry *chunk) +{ + LARGE_INTEGER end; + + end.QuadPart = (chunk->offset.QuadPart + CHUNK_HDR_SIZE + chunk->size + 1) & ~(ULONGLONG)1; + + return IStream_Seek(stream, end, STREAM_SEEK_SET, NULL); +} + +HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) +{ + HRESULT hr; + + if (chunk->id) { + hr = stream_skip_chunk(stream, chunk); + if (FAILED(hr)) + return hr; + } + + return stream_get_chunk(stream, chunk); +} + +HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, + ULONG size) +{ + if (chunk->size != size) { + WARN_(dmfile)("Chunk %s (size %u, offset %s) doesn't contains the expected data size %u\n", + debugstr_fourcc(chunk->id), chunk->size, + wine_dbgstr_longlong(chunk->offset.QuadPart), size); + return E_FAIL; + } + return stream_read(stream, data, size); +} + +HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, + ULONG size) +{ + ULONG len; + HRESULT hr; + + hr = IStream_Read(stream, str, min(chunk->size, size), &len); + if (FAILED(hr)) + return hr; + + /* Don't assume the string is properly zero terminated */ + str[min(len, size - 1)] = 0; + + if (len < chunk->size) + return S_FALSE; + return S_OK; +} + +
/* Generic IDirectMusicObject methods */ static inline struct dmobject *impl_from_IDirectMusicObject(IDirectMusicObject *iface) @@ -110,6 +250,78 @@ HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, return ret; }
+/* Helper for IDirectMusicObject::ParseDescriptor */ +static inline void info_get_name(IStream *stream, const struct chunk_entry *info, + DMUS_OBJECTDESC *desc) +{ + struct chunk_entry chunk = {.parent = info}; + char name[DMUS_MAX_NAME]; + ULONG len; + HRESULT hr = E_FAIL; + + while (stream_next_chunk(stream, &chunk) == S_OK) + if (chunk.id == mmioFOURCC('I','N','A','M')) + hr = IStream_Read(stream, name, min(chunk.size, sizeof(name)), &len); + + if (SUCCEEDED(hr)) { + len = MultiByteToWideChar(CP_ACP, 0, name, len, desc->wszName, sizeof(desc->wszName)); + desc->wszName[min(len, sizeof(desc->wszName) - 1)] = 0; + desc->dwValidData |= DMUS_OBJ_NAME; + } +} + +static inline void unfo_get_name(IStream *stream, const struct chunk_entry *unfo, + DMUS_OBJECTDESC *desc, BOOL inam) +{ + struct chunk_entry chunk = {.parent = unfo}; + + while (stream_next_chunk(stream, &chunk) == S_OK) + if (chunk.id == DMUS_FOURCC_UNAM_CHUNK || (inam && chunk.id == mmioFOURCC('I','N','A','M'))) + if (stream_chunk_get_wstr(stream, &chunk, desc->wszName, sizeof(desc->wszName)) == S_OK) + desc->dwValidData |= DMUS_OBJ_NAME; +} + +HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, + DMUS_OBJECTDESC *desc, DWORD supported) +{ + struct chunk_entry chunk = {.parent = riff}; + HRESULT hr; + + TRACE("Looking for %#x in %p: %s\n", supported, stream, debugstr_chunk(riff)); + + desc->dwValidData = 0; + desc->dwSize = sizeof(*desc); + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { + switch (chunk.id) { + 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; + break; + case DMUS_FOURCC_VERSION_CHUNK: + if ((supported & DMUS_OBJ_VERSION) && stream_chunk_get_data(stream, &chunk, + &desc->vVersion, sizeof(desc->vVersion)) == S_OK) + desc->dwValidData |= DMUS_OBJ_VERSION; + break; + case FOURCC_LIST: + if (chunk.type == DMUS_FOURCC_UNFO_LIST && (supported & DMUS_OBJ_NAME)) + unfo_get_name(stream, &chunk, desc, supported & DMUS_OBJ_NAME_INAM); + else if (chunk.type == DMUS_FOURCC_INFO_LIST && (supported & DMUS_OBJ_NAME_INFO)) + info_get_name(stream, &chunk, desc); + break; + } + } + TRACE("Found %#x\n", desc->dwValidData); + + 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 ad6bf6d14c..4a721cc152 100644 --- a/dlls/dmband/dmobject.h +++ b/dlls/dmband/dmobject.h @@ -19,6 +19,51 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include "wine/debug.h" + +/* RIFF stream parsing */ +struct chunk_entry; +struct chunk_entry { + FOURCC id; + DWORD size; + FOURCC type; /* valid only for LIST and RIFF chunks */ + ULARGE_INTEGER offset; /* chunk offset from start of stream */ + const struct chunk_entry *parent; /* enclosing RIFF or LIST chunk */ +}; + +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_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, + ULONG size) DECLSPEC_HIDDEN; + +static inline HRESULT stream_reset_chunk_data(IStream *stream, const struct chunk_entry *chunk) +{ + LARGE_INTEGER offset; + + offset.QuadPart = chunk->offset.QuadPart + sizeof(FOURCC) + sizeof(DWORD); + if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) + offset.QuadPart += sizeof(FOURCC); + + return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); +} + +static inline HRESULT stream_reset_chunk_start(IStream *stream, const struct chunk_entry *chunk) +{ + LARGE_INTEGER offset; + + offset.QuadPart = chunk->offset.QuadPart; + + return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); +} + +const char *debugstr_chunk(const struct chunk_entry *chunk) DECLSPEC_HIDDEN; + + +/* IDirectMusicObject base object */ struct dmobject { IDirectMusicObject IDirectMusicObject_iface; IPersistStream IPersistStream_iface; @@ -38,6 +83,14 @@ HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN;
+/* Helper for IDirectMusicObject::ParseDescriptor */ +HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, + DMUS_OBJECTDESC *desc, DWORD supported) DECLSPEC_HIDDEN; +/* Additional supported flags for dmobj_parsedescriptor. + DMUS_OBJ_NAME is 'UNAM' chunk in UNFO list */ +#define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ +#define DMUS_OBJ_NAME_INFO 0x2000 /* 'INAM' chunk in INFO list */ + /* 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 cabb6f74db..25f3198d2b 100644 --- a/dlls/dmcompos/dmobject.c +++ b/dlls/dmcompos/dmobject.c @@ -21,12 +21,152 @@ */
#define COBJMACROS +#include <assert.h> #include "objbase.h" #include "dmusici.h" +#include "dmusicf.h" #include "dmobject.h" #include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(dmobj); +WINE_DECLARE_DEBUG_CHANNEL(dmfile); + +/* RIFF format parsing */ +#define CHUNK_HDR_SIZE (sizeof(FOURCC) + sizeof(DWORD)) + +static inline const char *debugstr_fourcc(DWORD fourcc) +{ + if (!fourcc) return "''"; + return wine_dbg_sprintf("'%c%c%c%c'", (char)(fourcc), (char)(fourcc >> 8), + (char)(fourcc >> 16), (char)(fourcc >> 24)); +} + +const char *debugstr_chunk(const struct chunk_entry *chunk) +{ + const char *type = ""; + + if (!chunk) + return "(null)"; + if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) + type = wine_dbg_sprintf("type %s, ", debugstr_fourcc(chunk->type)); + return wine_dbg_sprintf("%s chunk, %ssize %u", debugstr_fourcc(chunk->id), type, chunk->size); +} + +static HRESULT stream_read(IStream *stream, void *data, ULONG size) +{ + ULONG read; + HRESULT hr; + + hr = IStream_Read(stream, data, size, &read); + if (FAILED(hr)) + TRACE_(dmfile)("IStream_Read failed: %08x\n", hr); + else if (!read && read < size) { + /* All or nothing: Handle a partial read due to end of stream as an error */ + TRACE_(dmfile)("Short read: %u < %u\n", read, size); + return E_FAIL; + } + + return hr; +} + +HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) +{ + static const LARGE_INTEGER zero; + ULONGLONG ck_end = 0, p_end = 0; + HRESULT hr; + + hr = IStream_Seek(stream, zero, STREAM_SEEK_CUR, &chunk->offset); + if (FAILED(hr)) + return hr; + assert(!(chunk->offset.QuadPart & 1)); + if (chunk->parent) { + p_end = chunk->parent->offset.QuadPart + CHUNK_HDR_SIZE + ((chunk->parent->size + 1) & ~1); + if (chunk->offset.QuadPart == p_end) + return S_FALSE; + ck_end = chunk->offset.QuadPart + CHUNK_HDR_SIZE; + if (ck_end > p_end) { + WARN_(dmfile)("No space for sub-chunk header in parent chunk: ends at offset %s > %s\n", + wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); + return E_FAIL; + } + } + + hr = stream_read(stream, chunk, CHUNK_HDR_SIZE); + if (hr != S_OK) + return hr; + if (chunk->parent) { + ck_end += (chunk->size + 1) & ~1; + if (ck_end > p_end) { + WARN_(dmfile)("No space for sub-chunk data in parent chunk: ends at offset %s > %s\n", + wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); + return E_FAIL; + } + } + + if (chunk->id == FOURCC_LIST || chunk->id == FOURCC_RIFF) { + hr = stream_read(stream, &chunk->type, sizeof(FOURCC)); + if (hr != S_OK) + return hr != S_FALSE ? hr : E_FAIL; + } + + TRACE_(dmfile)("Returning %s\n", debugstr_chunk(chunk)); + + return S_OK; +} + +HRESULT stream_skip_chunk(IStream *stream, struct chunk_entry *chunk) +{ + LARGE_INTEGER end; + + end.QuadPart = (chunk->offset.QuadPart + CHUNK_HDR_SIZE + chunk->size + 1) & ~(ULONGLONG)1; + + return IStream_Seek(stream, end, STREAM_SEEK_SET, NULL); +} + +HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) +{ + HRESULT hr; + + if (chunk->id) { + hr = stream_skip_chunk(stream, chunk); + if (FAILED(hr)) + return hr; + } + + return stream_get_chunk(stream, chunk); +} + +HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, + ULONG size) +{ + if (chunk->size != size) { + WARN_(dmfile)("Chunk %s (size %u, offset %s) doesn't contains the expected data size %u\n", + debugstr_fourcc(chunk->id), chunk->size, + wine_dbgstr_longlong(chunk->offset.QuadPart), size); + return E_FAIL; + } + return stream_read(stream, data, size); +} + +HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, + ULONG size) +{ + ULONG len; + HRESULT hr; + + hr = IStream_Read(stream, str, min(chunk->size, size), &len); + if (FAILED(hr)) + return hr; + + /* Don't assume the string is properly zero terminated */ + str[min(len, size - 1)] = 0; + + if (len < chunk->size) + return S_FALSE; + return S_OK; +} + +
/* Generic IDirectMusicObject methods */ static inline struct dmobject *impl_from_IDirectMusicObject(IDirectMusicObject *iface) @@ -110,6 +250,78 @@ HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, return ret; }
+/* Helper for IDirectMusicObject::ParseDescriptor */ +static inline void info_get_name(IStream *stream, const struct chunk_entry *info, + DMUS_OBJECTDESC *desc) +{ + struct chunk_entry chunk = {.parent = info}; + char name[DMUS_MAX_NAME]; + ULONG len; + HRESULT hr = E_FAIL; + + while (stream_next_chunk(stream, &chunk) == S_OK) + if (chunk.id == mmioFOURCC('I','N','A','M')) + hr = IStream_Read(stream, name, min(chunk.size, sizeof(name)), &len); + + if (SUCCEEDED(hr)) { + len = MultiByteToWideChar(CP_ACP, 0, name, len, desc->wszName, sizeof(desc->wszName)); + desc->wszName[min(len, sizeof(desc->wszName) - 1)] = 0; + desc->dwValidData |= DMUS_OBJ_NAME; + } +} + +static inline void unfo_get_name(IStream *stream, const struct chunk_entry *unfo, + DMUS_OBJECTDESC *desc, BOOL inam) +{ + struct chunk_entry chunk = {.parent = unfo}; + + while (stream_next_chunk(stream, &chunk) == S_OK) + if (chunk.id == DMUS_FOURCC_UNAM_CHUNK || (inam && chunk.id == mmioFOURCC('I','N','A','M'))) + if (stream_chunk_get_wstr(stream, &chunk, desc->wszName, sizeof(desc->wszName)) == S_OK) + desc->dwValidData |= DMUS_OBJ_NAME; +} + +HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, + DMUS_OBJECTDESC *desc, DWORD supported) +{ + struct chunk_entry chunk = {.parent = riff}; + HRESULT hr; + + TRACE("Looking for %#x in %p: %s\n", supported, stream, debugstr_chunk(riff)); + + desc->dwValidData = 0; + desc->dwSize = sizeof(*desc); + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { + switch (chunk.id) { + 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; + break; + case DMUS_FOURCC_VERSION_CHUNK: + if ((supported & DMUS_OBJ_VERSION) && stream_chunk_get_data(stream, &chunk, + &desc->vVersion, sizeof(desc->vVersion)) == S_OK) + desc->dwValidData |= DMUS_OBJ_VERSION; + break; + case FOURCC_LIST: + if (chunk.type == DMUS_FOURCC_UNFO_LIST && (supported & DMUS_OBJ_NAME)) + unfo_get_name(stream, &chunk, desc, supported & DMUS_OBJ_NAME_INAM); + else if (chunk.type == DMUS_FOURCC_INFO_LIST && (supported & DMUS_OBJ_NAME_INFO)) + info_get_name(stream, &chunk, desc); + break; + } + } + TRACE("Found %#x\n", desc->dwValidData); + + 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 ad6bf6d14c..4a721cc152 100644 --- a/dlls/dmcompos/dmobject.h +++ b/dlls/dmcompos/dmobject.h @@ -19,6 +19,51 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include "wine/debug.h" + +/* RIFF stream parsing */ +struct chunk_entry; +struct chunk_entry { + FOURCC id; + DWORD size; + FOURCC type; /* valid only for LIST and RIFF chunks */ + ULARGE_INTEGER offset; /* chunk offset from start of stream */ + const struct chunk_entry *parent; /* enclosing RIFF or LIST chunk */ +}; + +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_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, + ULONG size) DECLSPEC_HIDDEN; + +static inline HRESULT stream_reset_chunk_data(IStream *stream, const struct chunk_entry *chunk) +{ + LARGE_INTEGER offset; + + offset.QuadPart = chunk->offset.QuadPart + sizeof(FOURCC) + sizeof(DWORD); + if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) + offset.QuadPart += sizeof(FOURCC); + + return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); +} + +static inline HRESULT stream_reset_chunk_start(IStream *stream, const struct chunk_entry *chunk) +{ + LARGE_INTEGER offset; + + offset.QuadPart = chunk->offset.QuadPart; + + return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); +} + +const char *debugstr_chunk(const struct chunk_entry *chunk) DECLSPEC_HIDDEN; + + +/* IDirectMusicObject base object */ struct dmobject { IDirectMusicObject IDirectMusicObject_iface; IPersistStream IPersistStream_iface; @@ -38,6 +83,14 @@ HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN;
+/* Helper for IDirectMusicObject::ParseDescriptor */ +HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, + DMUS_OBJECTDESC *desc, DWORD supported) DECLSPEC_HIDDEN; +/* Additional supported flags for dmobj_parsedescriptor. + DMUS_OBJ_NAME is 'UNAM' chunk in UNFO list */ +#define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ +#define DMUS_OBJ_NAME_INFO 0x2000 /* 'INAM' chunk in INFO list */ + /* 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 cabb6f74db..25f3198d2b 100644 --- a/dlls/dmloader/dmobject.c +++ b/dlls/dmloader/dmobject.c @@ -21,12 +21,152 @@ */
#define COBJMACROS +#include <assert.h> #include "objbase.h" #include "dmusici.h" +#include "dmusicf.h" #include "dmobject.h" #include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(dmobj); +WINE_DECLARE_DEBUG_CHANNEL(dmfile); + +/* RIFF format parsing */ +#define CHUNK_HDR_SIZE (sizeof(FOURCC) + sizeof(DWORD)) + +static inline const char *debugstr_fourcc(DWORD fourcc) +{ + if (!fourcc) return "''"; + return wine_dbg_sprintf("'%c%c%c%c'", (char)(fourcc), (char)(fourcc >> 8), + (char)(fourcc >> 16), (char)(fourcc >> 24)); +} + +const char *debugstr_chunk(const struct chunk_entry *chunk) +{ + const char *type = ""; + + if (!chunk) + return "(null)"; + if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) + type = wine_dbg_sprintf("type %s, ", debugstr_fourcc(chunk->type)); + return wine_dbg_sprintf("%s chunk, %ssize %u", debugstr_fourcc(chunk->id), type, chunk->size); +} + +static HRESULT stream_read(IStream *stream, void *data, ULONG size) +{ + ULONG read; + HRESULT hr; + + hr = IStream_Read(stream, data, size, &read); + if (FAILED(hr)) + TRACE_(dmfile)("IStream_Read failed: %08x\n", hr); + else if (!read && read < size) { + /* All or nothing: Handle a partial read due to end of stream as an error */ + TRACE_(dmfile)("Short read: %u < %u\n", read, size); + return E_FAIL; + } + + return hr; +} + +HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) +{ + static const LARGE_INTEGER zero; + ULONGLONG ck_end = 0, p_end = 0; + HRESULT hr; + + hr = IStream_Seek(stream, zero, STREAM_SEEK_CUR, &chunk->offset); + if (FAILED(hr)) + return hr; + assert(!(chunk->offset.QuadPart & 1)); + if (chunk->parent) { + p_end = chunk->parent->offset.QuadPart + CHUNK_HDR_SIZE + ((chunk->parent->size + 1) & ~1); + if (chunk->offset.QuadPart == p_end) + return S_FALSE; + ck_end = chunk->offset.QuadPart + CHUNK_HDR_SIZE; + if (ck_end > p_end) { + WARN_(dmfile)("No space for sub-chunk header in parent chunk: ends at offset %s > %s\n", + wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); + return E_FAIL; + } + } + + hr = stream_read(stream, chunk, CHUNK_HDR_SIZE); + if (hr != S_OK) + return hr; + if (chunk->parent) { + ck_end += (chunk->size + 1) & ~1; + if (ck_end > p_end) { + WARN_(dmfile)("No space for sub-chunk data in parent chunk: ends at offset %s > %s\n", + wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); + return E_FAIL; + } + } + + if (chunk->id == FOURCC_LIST || chunk->id == FOURCC_RIFF) { + hr = stream_read(stream, &chunk->type, sizeof(FOURCC)); + if (hr != S_OK) + return hr != S_FALSE ? hr : E_FAIL; + } + + TRACE_(dmfile)("Returning %s\n", debugstr_chunk(chunk)); + + return S_OK; +} + +HRESULT stream_skip_chunk(IStream *stream, struct chunk_entry *chunk) +{ + LARGE_INTEGER end; + + end.QuadPart = (chunk->offset.QuadPart + CHUNK_HDR_SIZE + chunk->size + 1) & ~(ULONGLONG)1; + + return IStream_Seek(stream, end, STREAM_SEEK_SET, NULL); +} + +HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) +{ + HRESULT hr; + + if (chunk->id) { + hr = stream_skip_chunk(stream, chunk); + if (FAILED(hr)) + return hr; + } + + return stream_get_chunk(stream, chunk); +} + +HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, + ULONG size) +{ + if (chunk->size != size) { + WARN_(dmfile)("Chunk %s (size %u, offset %s) doesn't contains the expected data size %u\n", + debugstr_fourcc(chunk->id), chunk->size, + wine_dbgstr_longlong(chunk->offset.QuadPart), size); + return E_FAIL; + } + return stream_read(stream, data, size); +} + +HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, + ULONG size) +{ + ULONG len; + HRESULT hr; + + hr = IStream_Read(stream, str, min(chunk->size, size), &len); + if (FAILED(hr)) + return hr; + + /* Don't assume the string is properly zero terminated */ + str[min(len, size - 1)] = 0; + + if (len < chunk->size) + return S_FALSE; + return S_OK; +} + +
/* Generic IDirectMusicObject methods */ static inline struct dmobject *impl_from_IDirectMusicObject(IDirectMusicObject *iface) @@ -110,6 +250,78 @@ HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, return ret; }
+/* Helper for IDirectMusicObject::ParseDescriptor */ +static inline void info_get_name(IStream *stream, const struct chunk_entry *info, + DMUS_OBJECTDESC *desc) +{ + struct chunk_entry chunk = {.parent = info}; + char name[DMUS_MAX_NAME]; + ULONG len; + HRESULT hr = E_FAIL; + + while (stream_next_chunk(stream, &chunk) == S_OK) + if (chunk.id == mmioFOURCC('I','N','A','M')) + hr = IStream_Read(stream, name, min(chunk.size, sizeof(name)), &len); + + if (SUCCEEDED(hr)) { + len = MultiByteToWideChar(CP_ACP, 0, name, len, desc->wszName, sizeof(desc->wszName)); + desc->wszName[min(len, sizeof(desc->wszName) - 1)] = 0; + desc->dwValidData |= DMUS_OBJ_NAME; + } +} + +static inline void unfo_get_name(IStream *stream, const struct chunk_entry *unfo, + DMUS_OBJECTDESC *desc, BOOL inam) +{ + struct chunk_entry chunk = {.parent = unfo}; + + while (stream_next_chunk(stream, &chunk) == S_OK) + if (chunk.id == DMUS_FOURCC_UNAM_CHUNK || (inam && chunk.id == mmioFOURCC('I','N','A','M'))) + if (stream_chunk_get_wstr(stream, &chunk, desc->wszName, sizeof(desc->wszName)) == S_OK) + desc->dwValidData |= DMUS_OBJ_NAME; +} + +HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, + DMUS_OBJECTDESC *desc, DWORD supported) +{ + struct chunk_entry chunk = {.parent = riff}; + HRESULT hr; + + TRACE("Looking for %#x in %p: %s\n", supported, stream, debugstr_chunk(riff)); + + desc->dwValidData = 0; + desc->dwSize = sizeof(*desc); + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { + switch (chunk.id) { + 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; + break; + case DMUS_FOURCC_VERSION_CHUNK: + if ((supported & DMUS_OBJ_VERSION) && stream_chunk_get_data(stream, &chunk, + &desc->vVersion, sizeof(desc->vVersion)) == S_OK) + desc->dwValidData |= DMUS_OBJ_VERSION; + break; + case FOURCC_LIST: + if (chunk.type == DMUS_FOURCC_UNFO_LIST && (supported & DMUS_OBJ_NAME)) + unfo_get_name(stream, &chunk, desc, supported & DMUS_OBJ_NAME_INAM); + else if (chunk.type == DMUS_FOURCC_INFO_LIST && (supported & DMUS_OBJ_NAME_INFO)) + info_get_name(stream, &chunk, desc); + break; + } + } + TRACE("Found %#x\n", desc->dwValidData); + + 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 ad6bf6d14c..4a721cc152 100644 --- a/dlls/dmloader/dmobject.h +++ b/dlls/dmloader/dmobject.h @@ -19,6 +19,51 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include "wine/debug.h" + +/* RIFF stream parsing */ +struct chunk_entry; +struct chunk_entry { + FOURCC id; + DWORD size; + FOURCC type; /* valid only for LIST and RIFF chunks */ + ULARGE_INTEGER offset; /* chunk offset from start of stream */ + const struct chunk_entry *parent; /* enclosing RIFF or LIST chunk */ +}; + +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_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, + ULONG size) DECLSPEC_HIDDEN; + +static inline HRESULT stream_reset_chunk_data(IStream *stream, const struct chunk_entry *chunk) +{ + LARGE_INTEGER offset; + + offset.QuadPart = chunk->offset.QuadPart + sizeof(FOURCC) + sizeof(DWORD); + if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) + offset.QuadPart += sizeof(FOURCC); + + return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); +} + +static inline HRESULT stream_reset_chunk_start(IStream *stream, const struct chunk_entry *chunk) +{ + LARGE_INTEGER offset; + + offset.QuadPart = chunk->offset.QuadPart; + + return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); +} + +const char *debugstr_chunk(const struct chunk_entry *chunk) DECLSPEC_HIDDEN; + + +/* IDirectMusicObject base object */ struct dmobject { IDirectMusicObject IDirectMusicObject_iface; IPersistStream IPersistStream_iface; @@ -38,6 +83,14 @@ HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN;
+/* Helper for IDirectMusicObject::ParseDescriptor */ +HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, + DMUS_OBJECTDESC *desc, DWORD supported) DECLSPEC_HIDDEN; +/* Additional supported flags for dmobj_parsedescriptor. + DMUS_OBJ_NAME is 'UNAM' chunk in UNFO list */ +#define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ +#define DMUS_OBJ_NAME_INFO 0x2000 /* 'INAM' chunk in INFO list */ + /* 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 cabb6f74db..25f3198d2b 100644 --- a/dlls/dmscript/dmobject.c +++ b/dlls/dmscript/dmobject.c @@ -21,12 +21,152 @@ */
#define COBJMACROS +#include <assert.h> #include "objbase.h" #include "dmusici.h" +#include "dmusicf.h" #include "dmobject.h" #include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(dmobj); +WINE_DECLARE_DEBUG_CHANNEL(dmfile); + +/* RIFF format parsing */ +#define CHUNK_HDR_SIZE (sizeof(FOURCC) + sizeof(DWORD)) + +static inline const char *debugstr_fourcc(DWORD fourcc) +{ + if (!fourcc) return "''"; + return wine_dbg_sprintf("'%c%c%c%c'", (char)(fourcc), (char)(fourcc >> 8), + (char)(fourcc >> 16), (char)(fourcc >> 24)); +} + +const char *debugstr_chunk(const struct chunk_entry *chunk) +{ + const char *type = ""; + + if (!chunk) + return "(null)"; + if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) + type = wine_dbg_sprintf("type %s, ", debugstr_fourcc(chunk->type)); + return wine_dbg_sprintf("%s chunk, %ssize %u", debugstr_fourcc(chunk->id), type, chunk->size); +} + +static HRESULT stream_read(IStream *stream, void *data, ULONG size) +{ + ULONG read; + HRESULT hr; + + hr = IStream_Read(stream, data, size, &read); + if (FAILED(hr)) + TRACE_(dmfile)("IStream_Read failed: %08x\n", hr); + else if (!read && read < size) { + /* All or nothing: Handle a partial read due to end of stream as an error */ + TRACE_(dmfile)("Short read: %u < %u\n", read, size); + return E_FAIL; + } + + return hr; +} + +HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) +{ + static const LARGE_INTEGER zero; + ULONGLONG ck_end = 0, p_end = 0; + HRESULT hr; + + hr = IStream_Seek(stream, zero, STREAM_SEEK_CUR, &chunk->offset); + if (FAILED(hr)) + return hr; + assert(!(chunk->offset.QuadPart & 1)); + if (chunk->parent) { + p_end = chunk->parent->offset.QuadPart + CHUNK_HDR_SIZE + ((chunk->parent->size + 1) & ~1); + if (chunk->offset.QuadPart == p_end) + return S_FALSE; + ck_end = chunk->offset.QuadPart + CHUNK_HDR_SIZE; + if (ck_end > p_end) { + WARN_(dmfile)("No space for sub-chunk header in parent chunk: ends at offset %s > %s\n", + wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); + return E_FAIL; + } + } + + hr = stream_read(stream, chunk, CHUNK_HDR_SIZE); + if (hr != S_OK) + return hr; + if (chunk->parent) { + ck_end += (chunk->size + 1) & ~1; + if (ck_end > p_end) { + WARN_(dmfile)("No space for sub-chunk data in parent chunk: ends at offset %s > %s\n", + wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); + return E_FAIL; + } + } + + if (chunk->id == FOURCC_LIST || chunk->id == FOURCC_RIFF) { + hr = stream_read(stream, &chunk->type, sizeof(FOURCC)); + if (hr != S_OK) + return hr != S_FALSE ? hr : E_FAIL; + } + + TRACE_(dmfile)("Returning %s\n", debugstr_chunk(chunk)); + + return S_OK; +} + +HRESULT stream_skip_chunk(IStream *stream, struct chunk_entry *chunk) +{ + LARGE_INTEGER end; + + end.QuadPart = (chunk->offset.QuadPart + CHUNK_HDR_SIZE + chunk->size + 1) & ~(ULONGLONG)1; + + return IStream_Seek(stream, end, STREAM_SEEK_SET, NULL); +} + +HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) +{ + HRESULT hr; + + if (chunk->id) { + hr = stream_skip_chunk(stream, chunk); + if (FAILED(hr)) + return hr; + } + + return stream_get_chunk(stream, chunk); +} + +HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, + ULONG size) +{ + if (chunk->size != size) { + WARN_(dmfile)("Chunk %s (size %u, offset %s) doesn't contains the expected data size %u\n", + debugstr_fourcc(chunk->id), chunk->size, + wine_dbgstr_longlong(chunk->offset.QuadPart), size); + return E_FAIL; + } + return stream_read(stream, data, size); +} + +HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, + ULONG size) +{ + ULONG len; + HRESULT hr; + + hr = IStream_Read(stream, str, min(chunk->size, size), &len); + if (FAILED(hr)) + return hr; + + /* Don't assume the string is properly zero terminated */ + str[min(len, size - 1)] = 0; + + if (len < chunk->size) + return S_FALSE; + return S_OK; +} + +
/* Generic IDirectMusicObject methods */ static inline struct dmobject *impl_from_IDirectMusicObject(IDirectMusicObject *iface) @@ -110,6 +250,78 @@ HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, return ret; }
+/* Helper for IDirectMusicObject::ParseDescriptor */ +static inline void info_get_name(IStream *stream, const struct chunk_entry *info, + DMUS_OBJECTDESC *desc) +{ + struct chunk_entry chunk = {.parent = info}; + char name[DMUS_MAX_NAME]; + ULONG len; + HRESULT hr = E_FAIL; + + while (stream_next_chunk(stream, &chunk) == S_OK) + if (chunk.id == mmioFOURCC('I','N','A','M')) + hr = IStream_Read(stream, name, min(chunk.size, sizeof(name)), &len); + + if (SUCCEEDED(hr)) { + len = MultiByteToWideChar(CP_ACP, 0, name, len, desc->wszName, sizeof(desc->wszName)); + desc->wszName[min(len, sizeof(desc->wszName) - 1)] = 0; + desc->dwValidData |= DMUS_OBJ_NAME; + } +} + +static inline void unfo_get_name(IStream *stream, const struct chunk_entry *unfo, + DMUS_OBJECTDESC *desc, BOOL inam) +{ + struct chunk_entry chunk = {.parent = unfo}; + + while (stream_next_chunk(stream, &chunk) == S_OK) + if (chunk.id == DMUS_FOURCC_UNAM_CHUNK || (inam && chunk.id == mmioFOURCC('I','N','A','M'))) + if (stream_chunk_get_wstr(stream, &chunk, desc->wszName, sizeof(desc->wszName)) == S_OK) + desc->dwValidData |= DMUS_OBJ_NAME; +} + +HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, + DMUS_OBJECTDESC *desc, DWORD supported) +{ + struct chunk_entry chunk = {.parent = riff}; + HRESULT hr; + + TRACE("Looking for %#x in %p: %s\n", supported, stream, debugstr_chunk(riff)); + + desc->dwValidData = 0; + desc->dwSize = sizeof(*desc); + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { + switch (chunk.id) { + 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; + break; + case DMUS_FOURCC_VERSION_CHUNK: + if ((supported & DMUS_OBJ_VERSION) && stream_chunk_get_data(stream, &chunk, + &desc->vVersion, sizeof(desc->vVersion)) == S_OK) + desc->dwValidData |= DMUS_OBJ_VERSION; + break; + case FOURCC_LIST: + if (chunk.type == DMUS_FOURCC_UNFO_LIST && (supported & DMUS_OBJ_NAME)) + unfo_get_name(stream, &chunk, desc, supported & DMUS_OBJ_NAME_INAM); + else if (chunk.type == DMUS_FOURCC_INFO_LIST && (supported & DMUS_OBJ_NAME_INFO)) + info_get_name(stream, &chunk, desc); + break; + } + } + TRACE("Found %#x\n", desc->dwValidData); + + 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 ad6bf6d14c..4a721cc152 100644 --- a/dlls/dmscript/dmobject.h +++ b/dlls/dmscript/dmobject.h @@ -19,6 +19,51 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include "wine/debug.h" + +/* RIFF stream parsing */ +struct chunk_entry; +struct chunk_entry { + FOURCC id; + DWORD size; + FOURCC type; /* valid only for LIST and RIFF chunks */ + ULARGE_INTEGER offset; /* chunk offset from start of stream */ + const struct chunk_entry *parent; /* enclosing RIFF or LIST chunk */ +}; + +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_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, + ULONG size) DECLSPEC_HIDDEN; + +static inline HRESULT stream_reset_chunk_data(IStream *stream, const struct chunk_entry *chunk) +{ + LARGE_INTEGER offset; + + offset.QuadPart = chunk->offset.QuadPart + sizeof(FOURCC) + sizeof(DWORD); + if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) + offset.QuadPart += sizeof(FOURCC); + + return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); +} + +static inline HRESULT stream_reset_chunk_start(IStream *stream, const struct chunk_entry *chunk) +{ + LARGE_INTEGER offset; + + offset.QuadPart = chunk->offset.QuadPart; + + return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); +} + +const char *debugstr_chunk(const struct chunk_entry *chunk) DECLSPEC_HIDDEN; + + +/* IDirectMusicObject base object */ struct dmobject { IDirectMusicObject IDirectMusicObject_iface; IPersistStream IPersistStream_iface; @@ -38,6 +83,14 @@ HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN;
+/* Helper for IDirectMusicObject::ParseDescriptor */ +HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, + DMUS_OBJECTDESC *desc, DWORD supported) DECLSPEC_HIDDEN; +/* Additional supported flags for dmobj_parsedescriptor. + DMUS_OBJ_NAME is 'UNAM' chunk in UNFO list */ +#define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ +#define DMUS_OBJ_NAME_INFO 0x2000 /* 'INAM' chunk in INFO list */ + /* 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 cabb6f74db..25f3198d2b 100644 --- a/dlls/dmstyle/dmobject.c +++ b/dlls/dmstyle/dmobject.c @@ -21,12 +21,152 @@ */
#define COBJMACROS +#include <assert.h> #include "objbase.h" #include "dmusici.h" +#include "dmusicf.h" #include "dmobject.h" #include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(dmobj); +WINE_DECLARE_DEBUG_CHANNEL(dmfile); + +/* RIFF format parsing */ +#define CHUNK_HDR_SIZE (sizeof(FOURCC) + sizeof(DWORD)) + +static inline const char *debugstr_fourcc(DWORD fourcc) +{ + if (!fourcc) return "''"; + return wine_dbg_sprintf("'%c%c%c%c'", (char)(fourcc), (char)(fourcc >> 8), + (char)(fourcc >> 16), (char)(fourcc >> 24)); +} + +const char *debugstr_chunk(const struct chunk_entry *chunk) +{ + const char *type = ""; + + if (!chunk) + return "(null)"; + if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) + type = wine_dbg_sprintf("type %s, ", debugstr_fourcc(chunk->type)); + return wine_dbg_sprintf("%s chunk, %ssize %u", debugstr_fourcc(chunk->id), type, chunk->size); +} + +static HRESULT stream_read(IStream *stream, void *data, ULONG size) +{ + ULONG read; + HRESULT hr; + + hr = IStream_Read(stream, data, size, &read); + if (FAILED(hr)) + TRACE_(dmfile)("IStream_Read failed: %08x\n", hr); + else if (!read && read < size) { + /* All or nothing: Handle a partial read due to end of stream as an error */ + TRACE_(dmfile)("Short read: %u < %u\n", read, size); + return E_FAIL; + } + + return hr; +} + +HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) +{ + static const LARGE_INTEGER zero; + ULONGLONG ck_end = 0, p_end = 0; + HRESULT hr; + + hr = IStream_Seek(stream, zero, STREAM_SEEK_CUR, &chunk->offset); + if (FAILED(hr)) + return hr; + assert(!(chunk->offset.QuadPart & 1)); + if (chunk->parent) { + p_end = chunk->parent->offset.QuadPart + CHUNK_HDR_SIZE + ((chunk->parent->size + 1) & ~1); + if (chunk->offset.QuadPart == p_end) + return S_FALSE; + ck_end = chunk->offset.QuadPart + CHUNK_HDR_SIZE; + if (ck_end > p_end) { + WARN_(dmfile)("No space for sub-chunk header in parent chunk: ends at offset %s > %s\n", + wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); + return E_FAIL; + } + } + + hr = stream_read(stream, chunk, CHUNK_HDR_SIZE); + if (hr != S_OK) + return hr; + if (chunk->parent) { + ck_end += (chunk->size + 1) & ~1; + if (ck_end > p_end) { + WARN_(dmfile)("No space for sub-chunk data in parent chunk: ends at offset %s > %s\n", + wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); + return E_FAIL; + } + } + + if (chunk->id == FOURCC_LIST || chunk->id == FOURCC_RIFF) { + hr = stream_read(stream, &chunk->type, sizeof(FOURCC)); + if (hr != S_OK) + return hr != S_FALSE ? hr : E_FAIL; + } + + TRACE_(dmfile)("Returning %s\n", debugstr_chunk(chunk)); + + return S_OK; +} + +HRESULT stream_skip_chunk(IStream *stream, struct chunk_entry *chunk) +{ + LARGE_INTEGER end; + + end.QuadPart = (chunk->offset.QuadPart + CHUNK_HDR_SIZE + chunk->size + 1) & ~(ULONGLONG)1; + + return IStream_Seek(stream, end, STREAM_SEEK_SET, NULL); +} + +HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) +{ + HRESULT hr; + + if (chunk->id) { + hr = stream_skip_chunk(stream, chunk); + if (FAILED(hr)) + return hr; + } + + return stream_get_chunk(stream, chunk); +} + +HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, + ULONG size) +{ + if (chunk->size != size) { + WARN_(dmfile)("Chunk %s (size %u, offset %s) doesn't contains the expected data size %u\n", + debugstr_fourcc(chunk->id), chunk->size, + wine_dbgstr_longlong(chunk->offset.QuadPart), size); + return E_FAIL; + } + return stream_read(stream, data, size); +} + +HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, + ULONG size) +{ + ULONG len; + HRESULT hr; + + hr = IStream_Read(stream, str, min(chunk->size, size), &len); + if (FAILED(hr)) + return hr; + + /* Don't assume the string is properly zero terminated */ + str[min(len, size - 1)] = 0; + + if (len < chunk->size) + return S_FALSE; + return S_OK; +} + +
/* Generic IDirectMusicObject methods */ static inline struct dmobject *impl_from_IDirectMusicObject(IDirectMusicObject *iface) @@ -110,6 +250,78 @@ HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, return ret; }
+/* Helper for IDirectMusicObject::ParseDescriptor */ +static inline void info_get_name(IStream *stream, const struct chunk_entry *info, + DMUS_OBJECTDESC *desc) +{ + struct chunk_entry chunk = {.parent = info}; + char name[DMUS_MAX_NAME]; + ULONG len; + HRESULT hr = E_FAIL; + + while (stream_next_chunk(stream, &chunk) == S_OK) + if (chunk.id == mmioFOURCC('I','N','A','M')) + hr = IStream_Read(stream, name, min(chunk.size, sizeof(name)), &len); + + if (SUCCEEDED(hr)) { + len = MultiByteToWideChar(CP_ACP, 0, name, len, desc->wszName, sizeof(desc->wszName)); + desc->wszName[min(len, sizeof(desc->wszName) - 1)] = 0; + desc->dwValidData |= DMUS_OBJ_NAME; + } +} + +static inline void unfo_get_name(IStream *stream, const struct chunk_entry *unfo, + DMUS_OBJECTDESC *desc, BOOL inam) +{ + struct chunk_entry chunk = {.parent = unfo}; + + while (stream_next_chunk(stream, &chunk) == S_OK) + if (chunk.id == DMUS_FOURCC_UNAM_CHUNK || (inam && chunk.id == mmioFOURCC('I','N','A','M'))) + if (stream_chunk_get_wstr(stream, &chunk, desc->wszName, sizeof(desc->wszName)) == S_OK) + desc->dwValidData |= DMUS_OBJ_NAME; +} + +HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, + DMUS_OBJECTDESC *desc, DWORD supported) +{ + struct chunk_entry chunk = {.parent = riff}; + HRESULT hr; + + TRACE("Looking for %#x in %p: %s\n", supported, stream, debugstr_chunk(riff)); + + desc->dwValidData = 0; + desc->dwSize = sizeof(*desc); + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { + switch (chunk.id) { + 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; + break; + case DMUS_FOURCC_VERSION_CHUNK: + if ((supported & DMUS_OBJ_VERSION) && stream_chunk_get_data(stream, &chunk, + &desc->vVersion, sizeof(desc->vVersion)) == S_OK) + desc->dwValidData |= DMUS_OBJ_VERSION; + break; + case FOURCC_LIST: + if (chunk.type == DMUS_FOURCC_UNFO_LIST && (supported & DMUS_OBJ_NAME)) + unfo_get_name(stream, &chunk, desc, supported & DMUS_OBJ_NAME_INAM); + else if (chunk.type == DMUS_FOURCC_INFO_LIST && (supported & DMUS_OBJ_NAME_INFO)) + info_get_name(stream, &chunk, desc); + break; + } + } + TRACE("Found %#x\n", desc->dwValidData); + + 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 ad6bf6d14c..4a721cc152 100644 --- a/dlls/dmstyle/dmobject.h +++ b/dlls/dmstyle/dmobject.h @@ -19,6 +19,51 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include "wine/debug.h" + +/* RIFF stream parsing */ +struct chunk_entry; +struct chunk_entry { + FOURCC id; + DWORD size; + FOURCC type; /* valid only for LIST and RIFF chunks */ + ULARGE_INTEGER offset; /* chunk offset from start of stream */ + const struct chunk_entry *parent; /* enclosing RIFF or LIST chunk */ +}; + +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_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, + ULONG size) DECLSPEC_HIDDEN; + +static inline HRESULT stream_reset_chunk_data(IStream *stream, const struct chunk_entry *chunk) +{ + LARGE_INTEGER offset; + + offset.QuadPart = chunk->offset.QuadPart + sizeof(FOURCC) + sizeof(DWORD); + if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) + offset.QuadPart += sizeof(FOURCC); + + return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); +} + +static inline HRESULT stream_reset_chunk_start(IStream *stream, const struct chunk_entry *chunk) +{ + LARGE_INTEGER offset; + + offset.QuadPart = chunk->offset.QuadPart; + + return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); +} + +const char *debugstr_chunk(const struct chunk_entry *chunk) DECLSPEC_HIDDEN; + + +/* IDirectMusicObject base object */ struct dmobject { IDirectMusicObject IDirectMusicObject_iface; IPersistStream IPersistStream_iface; @@ -38,6 +83,14 @@ HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN;
+/* Helper for IDirectMusicObject::ParseDescriptor */ +HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, + DMUS_OBJECTDESC *desc, DWORD supported) DECLSPEC_HIDDEN; +/* Additional supported flags for dmobj_parsedescriptor. + DMUS_OBJ_NAME is 'UNAM' chunk in UNFO list */ +#define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ +#define DMUS_OBJ_NAME_INFO 0x2000 /* 'INAM' chunk in INFO list */ + /* 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 cabb6f74db..25f3198d2b 100644 --- a/dlls/dmusic/dmobject.c +++ b/dlls/dmusic/dmobject.c @@ -21,12 +21,152 @@ */
#define COBJMACROS +#include <assert.h> #include "objbase.h" #include "dmusici.h" +#include "dmusicf.h" #include "dmobject.h" #include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(dmobj); +WINE_DECLARE_DEBUG_CHANNEL(dmfile); + +/* RIFF format parsing */ +#define CHUNK_HDR_SIZE (sizeof(FOURCC) + sizeof(DWORD)) + +static inline const char *debugstr_fourcc(DWORD fourcc) +{ + if (!fourcc) return "''"; + return wine_dbg_sprintf("'%c%c%c%c'", (char)(fourcc), (char)(fourcc >> 8), + (char)(fourcc >> 16), (char)(fourcc >> 24)); +} + +const char *debugstr_chunk(const struct chunk_entry *chunk) +{ + const char *type = ""; + + if (!chunk) + return "(null)"; + if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) + type = wine_dbg_sprintf("type %s, ", debugstr_fourcc(chunk->type)); + return wine_dbg_sprintf("%s chunk, %ssize %u", debugstr_fourcc(chunk->id), type, chunk->size); +} + +static HRESULT stream_read(IStream *stream, void *data, ULONG size) +{ + ULONG read; + HRESULT hr; + + hr = IStream_Read(stream, data, size, &read); + if (FAILED(hr)) + TRACE_(dmfile)("IStream_Read failed: %08x\n", hr); + else if (!read && read < size) { + /* All or nothing: Handle a partial read due to end of stream as an error */ + TRACE_(dmfile)("Short read: %u < %u\n", read, size); + return E_FAIL; + } + + return hr; +} + +HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) +{ + static const LARGE_INTEGER zero; + ULONGLONG ck_end = 0, p_end = 0; + HRESULT hr; + + hr = IStream_Seek(stream, zero, STREAM_SEEK_CUR, &chunk->offset); + if (FAILED(hr)) + return hr; + assert(!(chunk->offset.QuadPart & 1)); + if (chunk->parent) { + p_end = chunk->parent->offset.QuadPart + CHUNK_HDR_SIZE + ((chunk->parent->size + 1) & ~1); + if (chunk->offset.QuadPart == p_end) + return S_FALSE; + ck_end = chunk->offset.QuadPart + CHUNK_HDR_SIZE; + if (ck_end > p_end) { + WARN_(dmfile)("No space for sub-chunk header in parent chunk: ends at offset %s > %s\n", + wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); + return E_FAIL; + } + } + + hr = stream_read(stream, chunk, CHUNK_HDR_SIZE); + if (hr != S_OK) + return hr; + if (chunk->parent) { + ck_end += (chunk->size + 1) & ~1; + if (ck_end > p_end) { + WARN_(dmfile)("No space for sub-chunk data in parent chunk: ends at offset %s > %s\n", + wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); + return E_FAIL; + } + } + + if (chunk->id == FOURCC_LIST || chunk->id == FOURCC_RIFF) { + hr = stream_read(stream, &chunk->type, sizeof(FOURCC)); + if (hr != S_OK) + return hr != S_FALSE ? hr : E_FAIL; + } + + TRACE_(dmfile)("Returning %s\n", debugstr_chunk(chunk)); + + return S_OK; +} + +HRESULT stream_skip_chunk(IStream *stream, struct chunk_entry *chunk) +{ + LARGE_INTEGER end; + + end.QuadPart = (chunk->offset.QuadPart + CHUNK_HDR_SIZE + chunk->size + 1) & ~(ULONGLONG)1; + + return IStream_Seek(stream, end, STREAM_SEEK_SET, NULL); +} + +HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) +{ + HRESULT hr; + + if (chunk->id) { + hr = stream_skip_chunk(stream, chunk); + if (FAILED(hr)) + return hr; + } + + return stream_get_chunk(stream, chunk); +} + +HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, + ULONG size) +{ + if (chunk->size != size) { + WARN_(dmfile)("Chunk %s (size %u, offset %s) doesn't contains the expected data size %u\n", + debugstr_fourcc(chunk->id), chunk->size, + wine_dbgstr_longlong(chunk->offset.QuadPart), size); + return E_FAIL; + } + return stream_read(stream, data, size); +} + +HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, + ULONG size) +{ + ULONG len; + HRESULT hr; + + hr = IStream_Read(stream, str, min(chunk->size, size), &len); + if (FAILED(hr)) + return hr; + + /* Don't assume the string is properly zero terminated */ + str[min(len, size - 1)] = 0; + + if (len < chunk->size) + return S_FALSE; + return S_OK; +} + +
/* Generic IDirectMusicObject methods */ static inline struct dmobject *impl_from_IDirectMusicObject(IDirectMusicObject *iface) @@ -110,6 +250,78 @@ HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, return ret; }
+/* Helper for IDirectMusicObject::ParseDescriptor */ +static inline void info_get_name(IStream *stream, const struct chunk_entry *info, + DMUS_OBJECTDESC *desc) +{ + struct chunk_entry chunk = {.parent = info}; + char name[DMUS_MAX_NAME]; + ULONG len; + HRESULT hr = E_FAIL; + + while (stream_next_chunk(stream, &chunk) == S_OK) + if (chunk.id == mmioFOURCC('I','N','A','M')) + hr = IStream_Read(stream, name, min(chunk.size, sizeof(name)), &len); + + if (SUCCEEDED(hr)) { + len = MultiByteToWideChar(CP_ACP, 0, name, len, desc->wszName, sizeof(desc->wszName)); + desc->wszName[min(len, sizeof(desc->wszName) - 1)] = 0; + desc->dwValidData |= DMUS_OBJ_NAME; + } +} + +static inline void unfo_get_name(IStream *stream, const struct chunk_entry *unfo, + DMUS_OBJECTDESC *desc, BOOL inam) +{ + struct chunk_entry chunk = {.parent = unfo}; + + while (stream_next_chunk(stream, &chunk) == S_OK) + if (chunk.id == DMUS_FOURCC_UNAM_CHUNK || (inam && chunk.id == mmioFOURCC('I','N','A','M'))) + if (stream_chunk_get_wstr(stream, &chunk, desc->wszName, sizeof(desc->wszName)) == S_OK) + desc->dwValidData |= DMUS_OBJ_NAME; +} + +HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, + DMUS_OBJECTDESC *desc, DWORD supported) +{ + struct chunk_entry chunk = {.parent = riff}; + HRESULT hr; + + TRACE("Looking for %#x in %p: %s\n", supported, stream, debugstr_chunk(riff)); + + desc->dwValidData = 0; + desc->dwSize = sizeof(*desc); + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { + switch (chunk.id) { + 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; + break; + case DMUS_FOURCC_VERSION_CHUNK: + if ((supported & DMUS_OBJ_VERSION) && stream_chunk_get_data(stream, &chunk, + &desc->vVersion, sizeof(desc->vVersion)) == S_OK) + desc->dwValidData |= DMUS_OBJ_VERSION; + break; + case FOURCC_LIST: + if (chunk.type == DMUS_FOURCC_UNFO_LIST && (supported & DMUS_OBJ_NAME)) + unfo_get_name(stream, &chunk, desc, supported & DMUS_OBJ_NAME_INAM); + else if (chunk.type == DMUS_FOURCC_INFO_LIST && (supported & DMUS_OBJ_NAME_INFO)) + info_get_name(stream, &chunk, desc); + break; + } + } + TRACE("Found %#x\n", desc->dwValidData); + + 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 ad6bf6d14c..4a721cc152 100644 --- a/dlls/dmusic/dmobject.h +++ b/dlls/dmusic/dmobject.h @@ -19,6 +19,51 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include "wine/debug.h" + +/* RIFF stream parsing */ +struct chunk_entry; +struct chunk_entry { + FOURCC id; + DWORD size; + FOURCC type; /* valid only for LIST and RIFF chunks */ + ULARGE_INTEGER offset; /* chunk offset from start of stream */ + const struct chunk_entry *parent; /* enclosing RIFF or LIST chunk */ +}; + +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_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, + ULONG size) DECLSPEC_HIDDEN; + +static inline HRESULT stream_reset_chunk_data(IStream *stream, const struct chunk_entry *chunk) +{ + LARGE_INTEGER offset; + + offset.QuadPart = chunk->offset.QuadPart + sizeof(FOURCC) + sizeof(DWORD); + if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) + offset.QuadPart += sizeof(FOURCC); + + return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); +} + +static inline HRESULT stream_reset_chunk_start(IStream *stream, const struct chunk_entry *chunk) +{ + LARGE_INTEGER offset; + + offset.QuadPart = chunk->offset.QuadPart; + + return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); +} + +const char *debugstr_chunk(const struct chunk_entry *chunk) DECLSPEC_HIDDEN; + + +/* IDirectMusicObject base object */ struct dmobject { IDirectMusicObject IDirectMusicObject_iface; IPersistStream IPersistStream_iface; @@ -38,6 +83,14 @@ HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN;
+/* Helper for IDirectMusicObject::ParseDescriptor */ +HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, + DMUS_OBJECTDESC *desc, DWORD supported) DECLSPEC_HIDDEN; +/* Additional supported flags for dmobj_parsedescriptor. + DMUS_OBJ_NAME is 'UNAM' chunk in UNFO list */ +#define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ +#define DMUS_OBJ_NAME_INFO 0x2000 /* 'INAM' chunk in INFO list */ + /* 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 cabb6f74db..25f3198d2b 100644 --- a/dlls/dswave/dmobject.c +++ b/dlls/dswave/dmobject.c @@ -21,12 +21,152 @@ */
#define COBJMACROS +#include <assert.h> #include "objbase.h" #include "dmusici.h" +#include "dmusicf.h" #include "dmobject.h" #include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(dmobj); +WINE_DECLARE_DEBUG_CHANNEL(dmfile); + +/* RIFF format parsing */ +#define CHUNK_HDR_SIZE (sizeof(FOURCC) + sizeof(DWORD)) + +static inline const char *debugstr_fourcc(DWORD fourcc) +{ + if (!fourcc) return "''"; + return wine_dbg_sprintf("'%c%c%c%c'", (char)(fourcc), (char)(fourcc >> 8), + (char)(fourcc >> 16), (char)(fourcc >> 24)); +} + +const char *debugstr_chunk(const struct chunk_entry *chunk) +{ + const char *type = ""; + + if (!chunk) + return "(null)"; + if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) + type = wine_dbg_sprintf("type %s, ", debugstr_fourcc(chunk->type)); + return wine_dbg_sprintf("%s chunk, %ssize %u", debugstr_fourcc(chunk->id), type, chunk->size); +} + +static HRESULT stream_read(IStream *stream, void *data, ULONG size) +{ + ULONG read; + HRESULT hr; + + hr = IStream_Read(stream, data, size, &read); + if (FAILED(hr)) + TRACE_(dmfile)("IStream_Read failed: %08x\n", hr); + else if (!read && read < size) { + /* All or nothing: Handle a partial read due to end of stream as an error */ + TRACE_(dmfile)("Short read: %u < %u\n", read, size); + return E_FAIL; + } + + return hr; +} + +HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) +{ + static const LARGE_INTEGER zero; + ULONGLONG ck_end = 0, p_end = 0; + HRESULT hr; + + hr = IStream_Seek(stream, zero, STREAM_SEEK_CUR, &chunk->offset); + if (FAILED(hr)) + return hr; + assert(!(chunk->offset.QuadPart & 1)); + if (chunk->parent) { + p_end = chunk->parent->offset.QuadPart + CHUNK_HDR_SIZE + ((chunk->parent->size + 1) & ~1); + if (chunk->offset.QuadPart == p_end) + return S_FALSE; + ck_end = chunk->offset.QuadPart + CHUNK_HDR_SIZE; + if (ck_end > p_end) { + WARN_(dmfile)("No space for sub-chunk header in parent chunk: ends at offset %s > %s\n", + wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); + return E_FAIL; + } + } + + hr = stream_read(stream, chunk, CHUNK_HDR_SIZE); + if (hr != S_OK) + return hr; + if (chunk->parent) { + ck_end += (chunk->size + 1) & ~1; + if (ck_end > p_end) { + WARN_(dmfile)("No space for sub-chunk data in parent chunk: ends at offset %s > %s\n", + wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); + return E_FAIL; + } + } + + if (chunk->id == FOURCC_LIST || chunk->id == FOURCC_RIFF) { + hr = stream_read(stream, &chunk->type, sizeof(FOURCC)); + if (hr != S_OK) + return hr != S_FALSE ? hr : E_FAIL; + } + + TRACE_(dmfile)("Returning %s\n", debugstr_chunk(chunk)); + + return S_OK; +} + +HRESULT stream_skip_chunk(IStream *stream, struct chunk_entry *chunk) +{ + LARGE_INTEGER end; + + end.QuadPart = (chunk->offset.QuadPart + CHUNK_HDR_SIZE + chunk->size + 1) & ~(ULONGLONG)1; + + return IStream_Seek(stream, end, STREAM_SEEK_SET, NULL); +} + +HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) +{ + HRESULT hr; + + if (chunk->id) { + hr = stream_skip_chunk(stream, chunk); + if (FAILED(hr)) + return hr; + } + + return stream_get_chunk(stream, chunk); +} + +HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, + ULONG size) +{ + if (chunk->size != size) { + WARN_(dmfile)("Chunk %s (size %u, offset %s) doesn't contains the expected data size %u\n", + debugstr_fourcc(chunk->id), chunk->size, + wine_dbgstr_longlong(chunk->offset.QuadPart), size); + return E_FAIL; + } + return stream_read(stream, data, size); +} + +HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, + ULONG size) +{ + ULONG len; + HRESULT hr; + + hr = IStream_Read(stream, str, min(chunk->size, size), &len); + if (FAILED(hr)) + return hr; + + /* Don't assume the string is properly zero terminated */ + str[min(len, size - 1)] = 0; + + if (len < chunk->size) + return S_FALSE; + return S_OK; +} + +
/* Generic IDirectMusicObject methods */ static inline struct dmobject *impl_from_IDirectMusicObject(IDirectMusicObject *iface) @@ -110,6 +250,78 @@ HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, return ret; }
+/* Helper for IDirectMusicObject::ParseDescriptor */ +static inline void info_get_name(IStream *stream, const struct chunk_entry *info, + DMUS_OBJECTDESC *desc) +{ + struct chunk_entry chunk = {.parent = info}; + char name[DMUS_MAX_NAME]; + ULONG len; + HRESULT hr = E_FAIL; + + while (stream_next_chunk(stream, &chunk) == S_OK) + if (chunk.id == mmioFOURCC('I','N','A','M')) + hr = IStream_Read(stream, name, min(chunk.size, sizeof(name)), &len); + + if (SUCCEEDED(hr)) { + len = MultiByteToWideChar(CP_ACP, 0, name, len, desc->wszName, sizeof(desc->wszName)); + desc->wszName[min(len, sizeof(desc->wszName) - 1)] = 0; + desc->dwValidData |= DMUS_OBJ_NAME; + } +} + +static inline void unfo_get_name(IStream *stream, const struct chunk_entry *unfo, + DMUS_OBJECTDESC *desc, BOOL inam) +{ + struct chunk_entry chunk = {.parent = unfo}; + + while (stream_next_chunk(stream, &chunk) == S_OK) + if (chunk.id == DMUS_FOURCC_UNAM_CHUNK || (inam && chunk.id == mmioFOURCC('I','N','A','M'))) + if (stream_chunk_get_wstr(stream, &chunk, desc->wszName, sizeof(desc->wszName)) == S_OK) + desc->dwValidData |= DMUS_OBJ_NAME; +} + +HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, + DMUS_OBJECTDESC *desc, DWORD supported) +{ + struct chunk_entry chunk = {.parent = riff}; + HRESULT hr; + + TRACE("Looking for %#x in %p: %s\n", supported, stream, debugstr_chunk(riff)); + + desc->dwValidData = 0; + desc->dwSize = sizeof(*desc); + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { + switch (chunk.id) { + 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; + break; + case DMUS_FOURCC_VERSION_CHUNK: + if ((supported & DMUS_OBJ_VERSION) && stream_chunk_get_data(stream, &chunk, + &desc->vVersion, sizeof(desc->vVersion)) == S_OK) + desc->dwValidData |= DMUS_OBJ_VERSION; + break; + case FOURCC_LIST: + if (chunk.type == DMUS_FOURCC_UNFO_LIST && (supported & DMUS_OBJ_NAME)) + unfo_get_name(stream, &chunk, desc, supported & DMUS_OBJ_NAME_INAM); + else if (chunk.type == DMUS_FOURCC_INFO_LIST && (supported & DMUS_OBJ_NAME_INFO)) + info_get_name(stream, &chunk, desc); + break; + } + } + TRACE("Found %#x\n", desc->dwValidData); + + 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 ad6bf6d14c..4a721cc152 100644 --- a/dlls/dswave/dmobject.h +++ b/dlls/dswave/dmobject.h @@ -19,6 +19,51 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include "wine/debug.h" + +/* RIFF stream parsing */ +struct chunk_entry; +struct chunk_entry { + FOURCC id; + DWORD size; + FOURCC type; /* valid only for LIST and RIFF chunks */ + ULARGE_INTEGER offset; /* chunk offset from start of stream */ + const struct chunk_entry *parent; /* enclosing RIFF or LIST chunk */ +}; + +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_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, + ULONG size) DECLSPEC_HIDDEN; + +static inline HRESULT stream_reset_chunk_data(IStream *stream, const struct chunk_entry *chunk) +{ + LARGE_INTEGER offset; + + offset.QuadPart = chunk->offset.QuadPart + sizeof(FOURCC) + sizeof(DWORD); + if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) + offset.QuadPart += sizeof(FOURCC); + + return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); +} + +static inline HRESULT stream_reset_chunk_start(IStream *stream, const struct chunk_entry *chunk) +{ + LARGE_INTEGER offset; + + offset.QuadPart = chunk->offset.QuadPart; + + return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); +} + +const char *debugstr_chunk(const struct chunk_entry *chunk) DECLSPEC_HIDDEN; + + +/* IDirectMusicObject base object */ struct dmobject { IDirectMusicObject IDirectMusicObject_iface; IPersistStream IPersistStream_iface; @@ -38,6 +83,14 @@ HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN;
+/* Helper for IDirectMusicObject::ParseDescriptor */ +HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, + DMUS_OBJECTDESC *desc, DWORD supported) DECLSPEC_HIDDEN; +/* Additional supported flags for dmobj_parsedescriptor. + DMUS_OBJ_NAME is 'UNAM' chunk in UNFO list */ +#define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ +#define DMUS_OBJ_NAME_INFO 0x2000 /* 'INAM' chunk in INFO list */ + /* Generic IPersistStream methods */ HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, void **ret_iface) DECLSPEC_HIDDEN;
On 15/07/18 21:08, Michael Stefaniuc wrote:
Signed-off-by: Michael Stefaniuc mstefani@winehq.org
dlls/dmband/dmobject.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++ dlls/dmband/dmobject.h | 53 ++++++++++++ dlls/dmcompos/dmobject.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++ dlls/dmcompos/dmobject.h | 53 ++++++++++++ dlls/dmloader/dmobject.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++ dlls/dmloader/dmobject.h | 53 ++++++++++++ dlls/dmscript/dmobject.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++ dlls/dmscript/dmobject.h | 53 ++++++++++++ dlls/dmstyle/dmobject.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++ dlls/dmstyle/dmobject.h | 53 ++++++++++++ dlls/dmusic/dmobject.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++ dlls/dmusic/dmobject.h | 53 ++++++++++++ dlls/dswave/dmobject.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++ dlls/dswave/dmobject.h | 53 ++++++++++++ 14 files changed, 1855 insertions(+)
I haven't actually read through this patch in detail, so I'm not immediately sure if this proposal would make sense, but it looks like there's at least some code duplication going on here across DLLs. Would it make sense to have a common import library for dmusic?
On 07/16/2018 08:30 AM, Zebediah Figura wrote:
On 15/07/18 21:08, Michael Stefaniuc wrote:
Signed-off-by: Michael Stefaniuc mstefani@winehq.org
dlls/dmband/dmobject.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++ dlls/dmband/dmobject.h | 53 ++++++++++++ dlls/dmcompos/dmobject.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++ dlls/dmcompos/dmobject.h | 53 ++++++++++++ dlls/dmloader/dmobject.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++ dlls/dmloader/dmobject.h | 53 ++++++++++++ dlls/dmscript/dmobject.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++ dlls/dmscript/dmobject.h | 53 ++++++++++++ dlls/dmstyle/dmobject.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++ dlls/dmstyle/dmobject.h | 53 ++++++++++++ dlls/dmusic/dmobject.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++ dlls/dmusic/dmobject.h | 53 ++++++++++++ dlls/dswave/dmobject.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++ dlls/dswave/dmobject.h | 53 ++++++++++++ 14 files changed, 1855 insertions(+)
I haven't actually read through this patch in detail, so I'm not immediately sure if this proposal would make sense, but it looks like there's at least some code duplication going on here across DLLs. Would it make sense to have a common import library for dmusic?
Those files are identical after the patch is applied, as they should be.
Alexandre wanted those files duplicated for now. I just keep adding code to it until he changes his mind ;)
bye michael