dmusic: Avoid swallowing collection Load failures.
[wine.git] / dlls / dmusic / collection.c
blob5d6b533d6490831bb0bd79c9d320560008c237fb
1 /*
2 * IDirectMusicCollection Implementation
4 * Copyright (C) 2003-2004 Rok Mandeljc
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "dmusic_private.h"
23 WINE_DEFAULT_DEBUG_CHANNEL(dmusic);
25 struct instrument_entry
27 struct list entry;
28 DMUS_OBJECTDESC desc;
29 IDirectMusicInstrument *instrument;
32 struct pool
34 POOLTABLE table;
35 POOLCUE cues[];
38 C_ASSERT(sizeof(struct pool) == offsetof(struct pool, cues[0]));
40 struct collection
42 IDirectMusicCollection IDirectMusicCollection_iface;
43 struct dmobject dmobj;
44 LONG ref;
46 DLSHEADER header;
47 struct pool *pool;
48 struct list instruments;
51 static inline struct collection *impl_from_IDirectMusicCollection(IDirectMusicCollection *iface)
53 return CONTAINING_RECORD(iface, struct collection, IDirectMusicCollection_iface);
56 static inline struct collection *impl_from_IPersistStream(IPersistStream *iface)
58 return CONTAINING_RECORD(iface, struct collection, dmobj.IPersistStream_iface);
61 static HRESULT WINAPI collection_QueryInterface(IDirectMusicCollection *iface,
62 REFIID riid, void **ret_iface)
64 struct collection *This = impl_from_IDirectMusicCollection(iface);
66 TRACE("(%p, %s, %p)\n", iface, debugstr_dmguid(riid), ret_iface);
68 *ret_iface = NULL;
70 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDirectMusicCollection))
71 *ret_iface = iface;
72 else if (IsEqualIID(riid, &IID_IDirectMusicObject))
73 *ret_iface = &This->dmobj.IDirectMusicObject_iface;
74 else if (IsEqualIID(riid, &IID_IPersistStream))
75 *ret_iface = &This->dmobj.IPersistStream_iface;
76 else
78 WARN("(%p, %s, %p): not found\n", iface, debugstr_dmguid(riid), ret_iface);
79 return E_NOINTERFACE;
82 IUnknown_AddRef((IUnknown*)*ret_iface);
83 return S_OK;
86 static ULONG WINAPI collection_AddRef(IDirectMusicCollection *iface)
88 struct collection *This = impl_from_IDirectMusicCollection(iface);
89 ULONG ref = InterlockedIncrement(&This->ref);
91 TRACE("(%p): new ref = %lu\n", iface, ref);
93 return ref;
96 static ULONG WINAPI collection_Release(IDirectMusicCollection *iface)
98 struct collection *This = impl_from_IDirectMusicCollection(iface);
99 ULONG ref = InterlockedDecrement(&This->ref);
101 TRACE("(%p): new ref = %lu\n", iface, ref);
103 if (!ref)
105 struct instrument_entry *instrument_entry;
106 void *next;
108 LIST_FOR_EACH_ENTRY_SAFE(instrument_entry, next, &This->instruments, struct instrument_entry, entry)
110 list_remove(&instrument_entry->entry);
111 IDirectMusicInstrument_Release(instrument_entry->instrument);
112 free(instrument_entry);
115 free(This);
118 return ref;
121 static HRESULT WINAPI collection_GetInstrument(IDirectMusicCollection *iface,
122 DWORD patch, IDirectMusicInstrument **instrument)
124 struct collection *This = impl_from_IDirectMusicCollection(iface);
125 struct instrument_entry *entry;
126 DWORD inst_patch;
127 HRESULT hr;
129 TRACE("(%p, %lu, %p)\n", iface, patch, instrument);
131 LIST_FOR_EACH_ENTRY(entry, &This->instruments, struct instrument_entry, entry)
133 if (FAILED(hr = IDirectMusicInstrument_GetPatch(entry->instrument, &inst_patch))) return hr;
134 if (patch == inst_patch)
136 *instrument = entry->instrument;
137 IDirectMusicInstrument_AddRef(entry->instrument);
138 TRACE(": returning instrument %p\n", entry->instrument);
139 return S_OK;
143 TRACE(": instrument not found\n");
144 return DMUS_E_INVALIDPATCH;
147 static HRESULT WINAPI collection_EnumInstrument(IDirectMusicCollection *iface,
148 DWORD index, DWORD *patch, LPWSTR name, DWORD name_length)
150 struct collection *This = impl_from_IDirectMusicCollection(iface);
151 struct instrument_entry *entry;
152 HRESULT hr;
154 TRACE("(%p, %ld, %p, %p, %ld)\n", iface, index, patch, name, name_length);
156 LIST_FOR_EACH_ENTRY(entry, &This->instruments, struct instrument_entry, entry)
158 if (index--) continue;
159 if (FAILED(hr = IDirectMusicInstrument_GetPatch(entry->instrument, patch))) return hr;
160 if (name) lstrcpynW(name, entry->desc.wszName, name_length);
161 return S_OK;
164 return S_FALSE;
167 static const IDirectMusicCollectionVtbl collection_vtbl =
169 collection_QueryInterface,
170 collection_AddRef,
171 collection_Release,
172 collection_GetInstrument,
173 collection_EnumInstrument,
176 static HRESULT parse_lins_list(struct collection *This, IStream *stream, struct chunk_entry *parent)
178 struct chunk_entry chunk = {.parent = parent};
179 struct instrument_entry *entry;
180 HRESULT hr;
182 while ((hr = stream_next_chunk(stream, &chunk)) == S_OK)
184 switch (MAKE_IDTYPE(chunk.id, chunk.type))
186 case MAKE_IDTYPE(FOURCC_LIST, FOURCC_INS):
187 if (!(entry = malloc(sizeof(*entry)))) return E_OUTOFMEMORY;
188 hr = instrument_create_from_chunk(stream, &chunk, &entry->desc, &entry->instrument);
189 if (SUCCEEDED(hr)) list_add_tail(&This->instruments, &entry->entry);
190 else free(entry);
191 break;
193 default:
194 FIXME("Ignoring chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type));
195 break;
198 if (FAILED(hr)) break;
201 return hr;
204 static HRESULT parse_ptbl_chunk(struct collection *This, IStream *stream, struct chunk_entry *chunk)
206 struct pool *pool;
207 POOLTABLE table;
208 HRESULT hr;
209 UINT size;
211 if (chunk->size < sizeof(table)) return E_INVALIDARG;
212 if (FAILED(hr = stream_read(stream, &table, sizeof(table)))) return hr;
213 if (chunk->size != table.cbSize + sizeof(POOLCUE) * table.cCues) return E_INVALIDARG;
214 if (table.cbSize != sizeof(table)) return E_INVALIDARG;
216 size = offsetof(struct pool, cues[table.cCues]);
217 if (!(pool = malloc(size))) return E_OUTOFMEMORY;
218 pool->table = table;
220 size = sizeof(POOLCUE) * table.cCues;
221 if (FAILED(hr = stream_read(stream, pool->cues, size))) free(pool);
222 else This->pool = pool;
224 return hr;
227 static HRESULT parse_dls_chunk(struct collection *This, IStream *stream, struct chunk_entry *parent)
229 struct chunk_entry chunk = {.parent = parent};
230 HRESULT hr;
232 if (FAILED(hr = dmobj_parsedescriptor(stream, parent, &This->dmobj.desc,
233 DMUS_OBJ_NAME_INFO|DMUS_OBJ_VERSION|DMUS_OBJ_OBJECT|DMUS_OBJ_GUID_DLID))
234 || FAILED(hr = stream_reset_chunk_data(stream, parent)))
235 return hr;
237 while ((hr = stream_next_chunk(stream, &chunk)) == S_OK)
239 switch (MAKE_IDTYPE(chunk.id, chunk.type))
241 case FOURCC_DLID:
242 case FOURCC_VERS:
243 case MAKE_IDTYPE(FOURCC_LIST, DMUS_FOURCC_INFO_LIST):
244 /* already parsed by dmobj_parsedescriptor */
245 break;
247 case FOURCC_COLH:
248 hr = stream_chunk_get_data(stream, &chunk, &This->header, sizeof(This->header));
249 break;
251 case FOURCC_PTBL:
252 hr = parse_ptbl_chunk(This, stream, &chunk);
253 break;
255 case MAKE_IDTYPE(FOURCC_LIST, FOURCC_LINS):
256 hr = parse_lins_list(This, stream, &chunk);
257 break;
259 default:
260 FIXME("Ignoring chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type));
261 break;
264 if (FAILED(hr)) break;
267 return hr;
270 static HRESULT WINAPI collection_object_ParseDescriptor(IDirectMusicObject *iface,
271 IStream *stream, DMUS_OBJECTDESC *desc)
273 struct chunk_entry riff = {0};
274 HRESULT hr;
276 TRACE("(%p, %p, %p)\n", iface, stream, desc);
278 if (!stream || !desc)
279 return E_POINTER;
281 if ((hr = stream_get_chunk(stream, &riff)) != S_OK)
282 return hr;
283 if (riff.id != FOURCC_RIFF || riff.type != FOURCC_DLS) {
284 TRACE("loading failed: unexpected %s\n", debugstr_chunk(&riff));
285 stream_skip_chunk(stream, &riff);
286 return DMUS_E_NOTADLSCOL;
289 hr = dmobj_parsedescriptor(stream, &riff, desc, DMUS_OBJ_NAME_INFO|DMUS_OBJ_VERSION);
290 if (FAILED(hr))
291 return hr;
293 desc->guidClass = CLSID_DirectMusicCollection;
294 desc->dwValidData |= DMUS_OBJ_CLASS;
296 TRACE("returning descriptor:\n");
297 dump_DMUS_OBJECTDESC(desc);
298 return S_OK;
301 static const IDirectMusicObjectVtbl collection_object_vtbl =
303 dmobj_IDirectMusicObject_QueryInterface,
304 dmobj_IDirectMusicObject_AddRef,
305 dmobj_IDirectMusicObject_Release,
306 dmobj_IDirectMusicObject_GetDescriptor,
307 dmobj_IDirectMusicObject_SetDescriptor,
308 collection_object_ParseDescriptor,
311 static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, IStream *stream)
313 struct collection *This = impl_from_IPersistStream(iface);
314 struct chunk_entry chunk = {0};
315 HRESULT hr;
317 TRACE("(%p, %p)\n", This, stream);
319 if ((hr = stream_next_chunk(stream, &chunk)) == S_OK)
321 switch (MAKE_IDTYPE(chunk.id, chunk.type))
323 case MAKE_IDTYPE(FOURCC_RIFF, FOURCC_DLS):
324 hr = parse_dls_chunk(This, stream, &chunk);
325 break;
327 default:
328 WARN("Invalid collection chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type));
329 hr = DMUS_E_UNSUPPORTED_STREAM;
330 break;
334 if (FAILED(hr)) return hr;
336 if (TRACE_ON(dmusic))
338 struct instrument_entry *entry;
339 int i = 0;
341 TRACE("*** IDirectMusicCollection (%p) ***\n", &This->IDirectMusicCollection_iface);
342 dump_DMUS_OBJECTDESC(&This->dmobj.desc);
344 TRACE(" - Collection header:\n");
345 TRACE(" - cInstruments: %ld\n", This->header.cInstruments);
346 TRACE(" - Instruments:\n");
348 LIST_FOR_EACH_ENTRY(entry, &This->instruments, struct instrument_entry, entry)
349 TRACE(" - Instrument[%i]: %p\n", i++, entry->instrument);
352 stream_skip_chunk(stream, &chunk);
353 return S_OK;
356 static const IPersistStreamVtbl collection_stream_vtbl =
358 dmobj_IPersistStream_QueryInterface,
359 dmobj_IPersistStream_AddRef,
360 dmobj_IPersistStream_Release,
361 unimpl_IPersistStream_GetClassID,
362 unimpl_IPersistStream_IsDirty,
363 collection_stream_Load,
364 unimpl_IPersistStream_Save,
365 unimpl_IPersistStream_GetSizeMax,
368 HRESULT collection_create(IUnknown **ret_iface)
370 struct collection *collection;
372 *ret_iface = NULL;
373 if (!(collection = calloc(1, sizeof(*collection)))) return E_OUTOFMEMORY;
374 collection->IDirectMusicCollection_iface.lpVtbl = &collection_vtbl;
375 collection->ref = 1;
376 dmobject_init(&collection->dmobj, &CLSID_DirectMusicCollection,
377 (IUnknown *)&collection->IDirectMusicCollection_iface);
378 collection->dmobj.IDirectMusicObject_iface.lpVtbl = &collection_object_vtbl;
379 collection->dmobj.IPersistStream_iface.lpVtbl = &collection_stream_vtbl;
380 list_init(&collection->instruments);
382 TRACE("Created DirectMusicCollection %p\n", collection);
383 *ret_iface = (IUnknown *)&collection->IDirectMusicCollection_iface;
384 return S_OK;