2 * Base IDirectMusicObject Implementation
3 * Keep in sync with the master in dlls/dmusic/dmobject.c
5 * Copyright (C) 2003-2004 Rok Mandeljc
6 * Copyright (C) 2014 Michael Stefaniuc
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "wine/debug.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(dmobj
);
32 WINE_DECLARE_DEBUG_CHANNEL(dmfile
);
34 /* RIFF format parsing */
35 #define CHUNK_HDR_SIZE (sizeof(FOURCC) + sizeof(DWORD))
37 static inline const char *debugstr_fourcc(DWORD fourcc
)
39 if (!fourcc
) return "''";
40 return wine_dbg_sprintf("'%c%c%c%c'", (char)(fourcc
), (char)(fourcc
>> 8),
41 (char)(fourcc
>> 16), (char)(fourcc
>> 24));
44 const char *debugstr_chunk(const struct chunk_entry
*chunk
)
46 const char *type
= "";
50 if (chunk
->id
== FOURCC_RIFF
|| chunk
->id
== FOURCC_LIST
)
51 type
= wine_dbg_sprintf("type %s, ", debugstr_fourcc(chunk
->type
));
52 return wine_dbg_sprintf("%s chunk, %ssize %u", debugstr_fourcc(chunk
->id
), type
, chunk
->size
);
55 static HRESULT
stream_read(IStream
*stream
, void *data
, ULONG size
)
60 hr
= IStream_Read(stream
, data
, size
, &read
);
62 TRACE_(dmfile
)("IStream_Read failed: %08x\n", hr
);
63 else if (!read
&& read
< size
) {
64 /* All or nothing: Handle a partial read due to end of stream as an error */
65 TRACE_(dmfile
)("Short read: %u < %u\n", read
, size
);
72 HRESULT
stream_get_chunk(IStream
*stream
, struct chunk_entry
*chunk
)
74 static const LARGE_INTEGER zero
;
75 ULONGLONG ck_end
= 0, p_end
= 0;
78 hr
= IStream_Seek(stream
, zero
, STREAM_SEEK_CUR
, &chunk
->offset
);
81 assert(!(chunk
->offset
.QuadPart
& 1));
83 p_end
= chunk
->parent
->offset
.QuadPart
+ CHUNK_HDR_SIZE
+ ((chunk
->parent
->size
+ 1) & ~1);
84 if (chunk
->offset
.QuadPart
== p_end
)
86 ck_end
= chunk
->offset
.QuadPart
+ CHUNK_HDR_SIZE
;
88 WARN_(dmfile
)("No space for sub-chunk header in parent chunk: ends at offset %s > %s\n",
89 wine_dbgstr_longlong(ck_end
), wine_dbgstr_longlong(p_end
));
94 hr
= stream_read(stream
, chunk
, CHUNK_HDR_SIZE
);
98 ck_end
+= (chunk
->size
+ 1) & ~1;
100 WARN_(dmfile
)("No space for sub-chunk data in parent chunk: ends at offset %s > %s\n",
101 wine_dbgstr_longlong(ck_end
), wine_dbgstr_longlong(p_end
));
106 if (chunk
->id
== FOURCC_LIST
|| chunk
->id
== FOURCC_RIFF
) {
107 hr
= stream_read(stream
, &chunk
->type
, sizeof(FOURCC
));
109 return hr
!= S_FALSE
? hr
: E_FAIL
;
112 TRACE_(dmfile
)("Returning %s\n", debugstr_chunk(chunk
));
117 HRESULT
stream_skip_chunk(IStream
*stream
, struct chunk_entry
*chunk
)
121 end
.QuadPart
= (chunk
->offset
.QuadPart
+ CHUNK_HDR_SIZE
+ chunk
->size
+ 1) & ~(ULONGLONG
)1;
123 return IStream_Seek(stream
, end
, STREAM_SEEK_SET
, NULL
);
126 HRESULT
stream_next_chunk(IStream
*stream
, struct chunk_entry
*chunk
)
131 hr
= stream_skip_chunk(stream
, chunk
);
136 return stream_get_chunk(stream
, chunk
);
139 HRESULT
stream_chunk_get_data(IStream
*stream
, const struct chunk_entry
*chunk
, void *data
,
142 if (chunk
->size
!= size
) {
143 WARN_(dmfile
)("Chunk %s (size %u, offset %s) doesn't contains the expected data size %u\n",
144 debugstr_fourcc(chunk
->id
), chunk
->size
,
145 wine_dbgstr_longlong(chunk
->offset
.QuadPart
), size
);
148 return stream_read(stream
, data
, size
);
151 HRESULT
stream_chunk_get_wstr(IStream
*stream
, const struct chunk_entry
*chunk
, WCHAR
*str
,
157 hr
= IStream_Read(stream
, str
, min(chunk
->size
, size
), &len
);
161 /* Don't assume the string is properly zero terminated */
162 str
[min(len
, size
- 1)] = 0;
164 if (len
< chunk
->size
)
171 /* Generic IDirectMusicObject methods */
172 static inline struct dmobject
*impl_from_IDirectMusicObject(IDirectMusicObject
*iface
)
174 return CONTAINING_RECORD(iface
, struct dmobject
, IDirectMusicObject_iface
);
177 HRESULT WINAPI
dmobj_IDirectMusicObject_QueryInterface(IDirectMusicObject
*iface
, REFIID riid
,
180 struct dmobject
*This
= impl_from_IDirectMusicObject(iface
);
181 return IUnknown_QueryInterface(This
->outer_unk
, riid
, ret_iface
);
184 ULONG WINAPI
dmobj_IDirectMusicObject_AddRef(IDirectMusicObject
*iface
)
186 struct dmobject
*This
= impl_from_IDirectMusicObject(iface
);
187 return IUnknown_AddRef(This
->outer_unk
);
190 ULONG WINAPI
dmobj_IDirectMusicObject_Release(IDirectMusicObject
*iface
)
192 struct dmobject
*This
= impl_from_IDirectMusicObject(iface
);
193 return IUnknown_Release(This
->outer_unk
);
196 HRESULT WINAPI
dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject
*iface
,
197 DMUS_OBJECTDESC
*desc
)
199 struct dmobject
*This
= impl_from_IDirectMusicObject(iface
);
201 TRACE("(%p/%p)->(%p)\n", iface
, This
, desc
);
206 memcpy(desc
, &This
->desc
, This
->desc
.dwSize
);
211 HRESULT WINAPI
dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject
*iface
,
212 DMUS_OBJECTDESC
*desc
)
214 struct dmobject
*This
= impl_from_IDirectMusicObject(iface
);
217 TRACE("(%p, %p)\n", iface
, desc
);
222 /* Immutable property */
223 if (desc
->dwValidData
& DMUS_OBJ_CLASS
)
225 desc
->dwValidData
&= ~DMUS_OBJ_CLASS
;
228 /* Set only valid fields */
229 if (desc
->dwValidData
& DMUS_OBJ_OBJECT
)
230 This
->desc
.guidObject
= desc
->guidObject
;
231 if (desc
->dwValidData
& DMUS_OBJ_NAME
)
232 lstrcpynW(This
->desc
.wszName
, desc
->wszName
, DMUS_MAX_NAME
);
233 if (desc
->dwValidData
& DMUS_OBJ_CATEGORY
)
234 lstrcpynW(This
->desc
.wszCategory
, desc
->wszCategory
, DMUS_MAX_CATEGORY
);
235 if (desc
->dwValidData
& DMUS_OBJ_FILENAME
)
236 lstrcpynW(This
->desc
.wszFileName
, desc
->wszFileName
, DMUS_MAX_FILENAME
);
237 if (desc
->dwValidData
& DMUS_OBJ_VERSION
)
238 This
->desc
.vVersion
= desc
->vVersion
;
239 if (desc
->dwValidData
& DMUS_OBJ_DATE
)
240 This
->desc
.ftDate
= desc
->ftDate
;
241 if (desc
->dwValidData
& DMUS_OBJ_MEMORY
) {
242 This
->desc
.llMemLength
= desc
->llMemLength
;
243 memcpy(This
->desc
.pbMemData
, desc
->pbMemData
, desc
->llMemLength
);
245 if (desc
->dwValidData
& DMUS_OBJ_STREAM
)
246 IStream_Clone(desc
->pStream
, &This
->desc
.pStream
);
248 This
->desc
.dwValidData
|= desc
->dwValidData
;
253 /* Helper for IDirectMusicObject::ParseDescriptor */
254 static inline void info_get_name(IStream
*stream
, const struct chunk_entry
*info
,
255 DMUS_OBJECTDESC
*desc
)
257 struct chunk_entry chunk
= {.parent
= info
};
258 char name
[DMUS_MAX_NAME
];
262 while (stream_next_chunk(stream
, &chunk
) == S_OK
)
263 if (chunk
.id
== mmioFOURCC('I','N','A','M'))
264 hr
= IStream_Read(stream
, name
, min(chunk
.size
, sizeof(name
)), &len
);
267 len
= MultiByteToWideChar(CP_ACP
, 0, name
, len
, desc
->wszName
, sizeof(desc
->wszName
));
268 desc
->wszName
[min(len
, sizeof(desc
->wszName
) - 1)] = 0;
269 desc
->dwValidData
|= DMUS_OBJ_NAME
;
273 static inline void unfo_get_name(IStream
*stream
, const struct chunk_entry
*unfo
,
274 DMUS_OBJECTDESC
*desc
, BOOL inam
)
276 struct chunk_entry chunk
= {.parent
= unfo
};
278 while (stream_next_chunk(stream
, &chunk
) == S_OK
)
279 if (chunk
.id
== DMUS_FOURCC_UNAM_CHUNK
|| (inam
&& chunk
.id
== mmioFOURCC('I','N','A','M')))
280 if (stream_chunk_get_wstr(stream
, &chunk
, desc
->wszName
, sizeof(desc
->wszName
)) == S_OK
)
281 desc
->dwValidData
|= DMUS_OBJ_NAME
;
284 HRESULT
dmobj_parsedescriptor(IStream
*stream
, const struct chunk_entry
*riff
,
285 DMUS_OBJECTDESC
*desc
, DWORD supported
)
287 struct chunk_entry chunk
= {.parent
= riff
};
290 TRACE("Looking for %#x in %p: %s\n", supported
, stream
, debugstr_chunk(riff
));
292 desc
->dwValidData
= 0;
293 desc
->dwSize
= sizeof(*desc
);
295 while ((hr
= stream_next_chunk(stream
, &chunk
)) == S_OK
) {
297 case DMUS_FOURCC_GUID_CHUNK
:
298 if ((supported
& DMUS_OBJ_OBJECT
) && stream_chunk_get_data(stream
, &chunk
,
299 &desc
->guidObject
, sizeof(desc
->guidObject
)) == S_OK
)
300 desc
->dwValidData
|= DMUS_OBJ_OBJECT
;
302 case DMUS_FOURCC_CATEGORY_CHUNK
:
303 if ((supported
& DMUS_OBJ_CATEGORY
) && stream_chunk_get_wstr(stream
, &chunk
,
304 desc
->wszCategory
, sizeof(desc
->wszCategory
)) == S_OK
)
305 desc
->dwValidData
|= DMUS_OBJ_CATEGORY
;
307 case DMUS_FOURCC_VERSION_CHUNK
:
308 if ((supported
& DMUS_OBJ_VERSION
) && stream_chunk_get_data(stream
, &chunk
,
309 &desc
->vVersion
, sizeof(desc
->vVersion
)) == S_OK
)
310 desc
->dwValidData
|= DMUS_OBJ_VERSION
;
313 if (chunk
.type
== DMUS_FOURCC_UNFO_LIST
&& (supported
& DMUS_OBJ_NAME
))
314 unfo_get_name(stream
, &chunk
, desc
, supported
& DMUS_OBJ_NAME_INAM
);
315 else if (chunk
.type
== DMUS_FOURCC_INFO_LIST
&& (supported
& DMUS_OBJ_NAME_INFO
))
316 info_get_name(stream
, &chunk
, desc
);
320 TRACE("Found %#x\n", desc
->dwValidData
);
325 /* Generic IPersistStream methods */
326 static inline struct dmobject
*impl_from_IPersistStream(IPersistStream
*iface
)
328 return CONTAINING_RECORD(iface
, struct dmobject
, IPersistStream_iface
);
331 HRESULT WINAPI
dmobj_IPersistStream_QueryInterface(IPersistStream
*iface
, REFIID riid
,
334 struct dmobject
*This
= impl_from_IPersistStream(iface
);
335 return IUnknown_QueryInterface(This
->outer_unk
, riid
, ret_iface
);
338 ULONG WINAPI
dmobj_IPersistStream_AddRef(IPersistStream
*iface
)
340 struct dmobject
*This
= impl_from_IPersistStream(iface
);
341 return IUnknown_AddRef(This
->outer_unk
);
344 ULONG WINAPI
dmobj_IPersistStream_Release(IPersistStream
*iface
)
346 struct dmobject
*This
= impl_from_IPersistStream(iface
);
347 return IUnknown_Release(This
->outer_unk
);
350 HRESULT WINAPI
dmobj_IPersistStream_GetClassID(IPersistStream
*iface
, CLSID
*class)
352 struct dmobject
*This
= impl_from_IPersistStream(iface
);
354 TRACE("(%p, %p)\n", This
, class);
359 *class = This
->desc
.guidClass
;
364 /* IPersistStream methods not implemented in native */
365 HRESULT WINAPI
unimpl_IPersistStream_GetClassID(IPersistStream
*iface
, CLSID
*class)
367 TRACE("(%p, %p): method not implemented\n", iface
, class);
371 HRESULT WINAPI
unimpl_IPersistStream_IsDirty(IPersistStream
*iface
)
373 TRACE("(%p): method not implemented, always returning S_FALSE\n", iface
);
377 HRESULT WINAPI
unimpl_IPersistStream_Save(IPersistStream
*iface
, IStream
*stream
,
380 TRACE("(%p, %p, %d): method not implemented\n", iface
, stream
, clear_dirty
);
384 HRESULT WINAPI
unimpl_IPersistStream_GetSizeMax(IPersistStream
*iface
, ULARGE_INTEGER
*size
)
386 TRACE("(%p, %p): method not implemented\n", iface
, size
);
391 void dmobject_init(struct dmobject
*dmobj
, const GUID
*class, IUnknown
*outer_unk
)
393 dmobj
->outer_unk
= outer_unk
;
394 dmobj
->desc
.dwSize
= sizeof(dmobj
->desc
);
395 dmobj
->desc
.dwValidData
= DMUS_OBJ_CLASS
;
396 dmobj
->desc
.guidClass
= *class;