msvcrt/tests: Add _strupr tests.
[wine.git] / dlls / dmime / dmobject.c
blob25f3198d2bce981bda71ab28798dbf4248d717fa
1 /*
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
23 #define COBJMACROS
24 #include <assert.h>
25 #include "objbase.h"
26 #include "dmusici.h"
27 #include "dmusicf.h"
28 #include "dmobject.h"
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 = "";
48 if (!chunk)
49 return "(null)";
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)
57 ULONG read;
58 HRESULT hr;
60 hr = IStream_Read(stream, data, size, &read);
61 if (FAILED(hr))
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);
66 return E_FAIL;
69 return hr;
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;
76 HRESULT hr;
78 hr = IStream_Seek(stream, zero, STREAM_SEEK_CUR, &chunk->offset);
79 if (FAILED(hr))
80 return hr;
81 assert(!(chunk->offset.QuadPart & 1));
82 if (chunk->parent) {
83 p_end = chunk->parent->offset.QuadPart + CHUNK_HDR_SIZE + ((chunk->parent->size + 1) & ~1);
84 if (chunk->offset.QuadPart == p_end)
85 return S_FALSE;
86 ck_end = chunk->offset.QuadPart + CHUNK_HDR_SIZE;
87 if (ck_end > p_end) {
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));
90 return E_FAIL;
94 hr = stream_read(stream, chunk, CHUNK_HDR_SIZE);
95 if (hr != S_OK)
96 return hr;
97 if (chunk->parent) {
98 ck_end += (chunk->size + 1) & ~1;
99 if (ck_end > p_end) {
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));
102 return E_FAIL;
106 if (chunk->id == FOURCC_LIST || chunk->id == FOURCC_RIFF) {
107 hr = stream_read(stream, &chunk->type, sizeof(FOURCC));
108 if (hr != S_OK)
109 return hr != S_FALSE ? hr : E_FAIL;
112 TRACE_(dmfile)("Returning %s\n", debugstr_chunk(chunk));
114 return S_OK;
117 HRESULT stream_skip_chunk(IStream *stream, struct chunk_entry *chunk)
119 LARGE_INTEGER end;
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)
128 HRESULT hr;
130 if (chunk->id) {
131 hr = stream_skip_chunk(stream, chunk);
132 if (FAILED(hr))
133 return hr;
136 return stream_get_chunk(stream, chunk);
139 HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data,
140 ULONG size)
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);
146 return E_FAIL;
148 return stream_read(stream, data, size);
151 HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str,
152 ULONG size)
154 ULONG len;
155 HRESULT hr;
157 hr = IStream_Read(stream, str, min(chunk->size, size), &len);
158 if (FAILED(hr))
159 return hr;
161 /* Don't assume the string is properly zero terminated */
162 str[min(len, size - 1)] = 0;
164 if (len < chunk->size)
165 return S_FALSE;
166 return S_OK;
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,
178 void **ret_iface)
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);
203 if (!desc)
204 return E_POINTER;
206 memcpy(desc, &This->desc, This->desc.dwSize);
208 return S_OK;
211 HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface,
212 DMUS_OBJECTDESC *desc)
214 struct dmobject *This = impl_from_IDirectMusicObject(iface);
215 HRESULT ret = S_OK;
217 TRACE("(%p, %p)\n", iface, desc);
219 if (!desc)
220 return E_POINTER;
222 /* Immutable property */
223 if (desc->dwValidData & DMUS_OBJ_CLASS)
225 desc->dwValidData &= ~DMUS_OBJ_CLASS;
226 ret = S_FALSE;
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;
250 return ret;
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];
259 ULONG len;
260 HRESULT hr = E_FAIL;
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);
266 if (SUCCEEDED(hr)) {
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};
288 HRESULT hr;
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) {
296 switch (chunk.id) {
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;
301 break;
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;
306 break;
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;
311 break;
312 case FOURCC_LIST:
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);
317 break;
320 TRACE("Found %#x\n", desc->dwValidData);
322 return hr;
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,
332 void **ret_iface)
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);
356 if (!class)
357 return E_POINTER;
359 *class = This->desc.guidClass;
361 return S_OK;
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);
368 return E_NOTIMPL;
371 HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface)
373 TRACE("(%p): method not implemented, always returning S_FALSE\n", iface);
374 return S_FALSE;
377 HRESULT WINAPI unimpl_IPersistStream_Save(IPersistStream *iface, IStream *stream,
378 BOOL clear_dirty)
380 TRACE("(%p, %p, %d): method not implemented\n", iface, stream, clear_dirty);
381 return E_NOTIMPL;
384 HRESULT WINAPI unimpl_IPersistStream_GetSizeMax(IPersistStream *iface, ULARGE_INTEGER *size)
386 TRACE("(%p, %p): method not implemented\n", iface, size);
387 return E_NOTIMPL;
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;