Avoid excessive heap memory reallocation when generating EMF
[wine] / dlls / dmloader / loader.c
1 /* IDirectMusicLoader8 Implementation
2  *
3  * Copyright (C) 2003 Rok Mandeljc
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #include <stdarg.h>
21
22 #include "windef.h"
23 #include "winbase.h"
24 #include "winuser.h"
25 #include "wingdi.h"
26 #include "wine/debug.h"
27 #include "wine/unicode.h"
28 #include "winreg.h"
29
30 #include "dmloader_private.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(dmloader);
33
34 HRESULT WINAPI DMUSIC_GetDefaultGMPath (WCHAR wszPath[MAX_PATH]);
35
36 /* IDirectMusicLoader8 IUnknown part: */
37 HRESULT WINAPI IDirectMusicLoader8Impl_QueryInterface (LPDIRECTMUSICLOADER8 iface, REFIID riid, LPVOID *ppobj)
38 {
39         ICOM_THIS(IDirectMusicLoader8Impl,iface);
40
41         if (IsEqualIID (riid, &IID_IUnknown) || 
42             IsEqualIID (riid, &IID_IDirectMusicLoader) ||
43             IsEqualIID (riid, &IID_IDirectMusicLoader8)) {
44                 IDirectMusicLoader8Impl_AddRef(iface);
45                 *ppobj = This;
46                 return S_OK;
47         }
48         
49         WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppobj);
50         return E_NOINTERFACE;
51 }
52
53 ULONG WINAPI IDirectMusicLoader8Impl_AddRef (LPDIRECTMUSICLOADER8 iface)
54 {
55         ICOM_THIS(IDirectMusicLoader8Impl,iface);
56         TRACE("(%p) : AddRef from %ld\n", This, This->ref);
57         return ++(This->ref);
58 }
59
60 ULONG WINAPI IDirectMusicLoader8Impl_Release (LPDIRECTMUSICLOADER8 iface)
61 {
62         ICOM_THIS(IDirectMusicLoader8Impl,iface);
63         ULONG ref = --This->ref;
64         TRACE("(%p) : ReleaseRef to %ld\n", This, This->ref);
65         if (ref == 0) {
66                 HeapFree(GetProcessHeap(), 0, This);
67         }
68         return ref;
69 }
70
71 /* IDirectMusicLoader8 IDirectMusicLoader part: */
72 HRESULT WINAPI IDirectMusicLoader8Impl_GetObject (LPDIRECTMUSICLOADER8 iface, LPDMUS_OBJECTDESC pDesc, REFIID riid, LPVOID* ppv)
73 {
74         IDirectMusicObject* pObject;
75         ICOM_THIS(IDirectMusicLoader8Impl,iface);
76         HRESULT result;
77         struct list *listEntry;
78         DMUS_PRIVATE_CACHE_ENTRY *cacheEntry;
79         LPDMUS_PRIVATE_CACHE_ENTRY newEntry;
80
81         TRACE("(%p, %p, %s, %p)\n", This, pDesc, debugstr_guid(riid), ppv);
82
83         TRACE("looking up cache...\n");
84
85         LIST_FOR_EACH (listEntry, &This->CacheList) {
86                 cacheEntry = LIST_ENTRY( listEntry, DMUS_PRIVATE_CACHE_ENTRY, entry );
87                 
88                 if ((pDesc->dwValidData & DMUS_OBJ_OBJECT) || (pDesc->dwValidData & DMUS_OBJ_FILENAME)) {
89                         if (pDesc->dwValidData & DMUS_OBJ_OBJECT) {
90                                 if (IsEqualGUID (&cacheEntry->guidObject, &pDesc->guidObject)) {
91                                         TRACE(": found it by GUID\n");
92                                         if (cacheEntry->pObject)
93                                                 return IDirectMusicObject_QueryInterface ((LPDIRECTMUSICOBJECT)cacheEntry->pObject, riid, ppv);
94                                 }
95                         }
96                         if (pDesc->dwValidData & DMUS_OBJ_FILENAME) {
97                                 if (cacheEntry->wzFileName && !strncmpW (pDesc->wszFileName, cacheEntry->wzFileName, MAX_PATH)) {
98                                         TRACE(": found it by FileName\n");
99                                         if (cacheEntry->pObject)
100                                                 return IDirectMusicObject_QueryInterface ((LPDIRECTMUSICOBJECT)cacheEntry->pObject, riid, ppv);
101                                 }
102                         }
103                 }
104         }
105         
106         /* object doesn't exist in cache... guess we'll have to load it */
107         TRACE(": object does not exist in cache\n");
108         result = CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*)&pObject);
109         if (FAILED(result)) return result;
110         if (pDesc->dwValidData & DMUS_OBJ_FILENAME) {
111                 /* load object from file */
112                 WCHAR wzFileName[MAX_PATH];
113                 ILoaderStream* pStream;
114                 IPersistStream *pPersistStream = NULL;
115                 /* if it's full path, don't add search directory path, otherwise do */
116                 if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) {
117                         lstrcpyW( wzFileName, pDesc->wszFileName );
118                 } else {
119                         WCHAR *p;
120                         lstrcpyW( wzFileName, This->wzSearchPath );
121                         p = wzFileName + lstrlenW(wzFileName);
122                         if (p > wzFileName && p[-1] != '\\') *p++ = '\\';
123                         strcpyW( p, pDesc->wszFileName );
124                 }
125                 TRACE(": loading from file (%s)\n", debugstr_w(wzFileName));
126          
127                 result = DMUSIC_CreateLoaderStream ((LPSTREAM*)&pStream);
128                 if (FAILED(result)) return result;
129                 
130                 result = ILoaderStream_Attach (pStream, wzFileName, (LPDIRECTMUSICLOADER)iface);
131                 if (FAILED(result)) return result;
132                         
133                 result = IDirectMusicObject_QueryInterface (pObject, &IID_IPersistStream, (LPVOID*)&pPersistStream);
134                 if (FAILED(result)) return result;
135                         
136                 result = IPersistStream_Load (pPersistStream, (LPSTREAM)pStream);
137                 if (FAILED(result)) return result;
138                         
139                 ILoaderStream_IStream_Release ((LPSTREAM)pStream);
140                 IPersistStream_Release (pPersistStream);
141         } else if (pDesc->dwValidData & DMUS_OBJ_STREAM) {
142                 /* load object from stream */
143                 IStream* pClonedStream = NULL;
144                 IPersistStream* pPersistStream = NULL;
145
146                 TRACE(": loading from stream\n");
147                 result = IDirectMusicObject_QueryInterface (pObject, &IID_IPersistStream, (LPVOID*)&pPersistStream);
148                 if (FAILED(result)) return result;
149                         
150                 result = IStream_Clone (pDesc->pStream, &pClonedStream);
151                 if (FAILED(result)) return result;
152
153                 result = IPersistStream_Load (pPersistStream, pClonedStream);
154                 if (FAILED(result))     return result;
155
156                 IPersistStream_Release (pPersistStream);
157                 IStream_Release (pClonedStream);
158         } else if (pDesc->dwValidData & DMUS_OBJ_OBJECT) {
159                 /* load object by GUID */
160                 TRACE(": loading by GUID (only default DLS supported)\n");
161                 if (IsEqualGUID (&pDesc->guidObject, &GUID_DefaultGMCollection)) {
162                         WCHAR wzFileName[MAX_PATH];
163                         IPersistStream *pPersistStream = NULL;
164                         ILoaderStream* pStream;
165                         if (FAILED(DMUSIC_GetDefaultGMPath (wzFileName)))
166                                 return E_FAIL;
167                         /* load object from file */
168                         result = DMUSIC_CreateLoaderStream ((LPSTREAM*)&pStream);
169                         if (FAILED(result)) return result;
170
171                         result = ILoaderStream_Attach (pStream, wzFileName, (LPDIRECTMUSICLOADER)iface);
172                         if (FAILED(result)) return result;
173
174                         result = IDirectMusicObject_QueryInterface (pObject, &IID_IPersistStream, (LPVOID*)&pPersistStream);
175                         if (FAILED(result)) return result;
176
177                         result = IPersistStream_Load (pPersistStream, (LPSTREAM)pStream);
178                         if (FAILED(result)) return result;
179
180                         ILoaderStream_IStream_Release ((LPSTREAM)pStream);
181                         IPersistStream_Release (pPersistStream);
182                 } else {
183                         return E_FAIL;
184                 }
185         } else {
186                 /* nowhere to load from */
187                 FIXME(": unknown/unsupported way of loading\n");
188                 return E_FAIL;
189         }
190         /* add object to cache */
191         newEntry = (LPDMUS_PRIVATE_CACHE_ENTRY) HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(DMUS_PRIVATE_CACHE_ENTRY));
192         if (pDesc->dwValidData & DMUS_OBJ_OBJECT)
193                 memcpy (&newEntry->guidObject, &pDesc->guidObject, sizeof (pDesc->guidObject));
194         if (pDesc->dwValidData & DMUS_OBJ_FILENAME)
195                 strncpyW (newEntry->wzFileName, pDesc->wszFileName, MAX_PATH);
196         if (pObject)
197                 newEntry->pObject = pObject;
198         list_add_tail (&This->CacheList, &newEntry->entry);
199         TRACE(": filled in cache entry\n");
200         
201         /* for debug purposes (e.g. to check if all files are cached) */
202 #if 0
203         int i = 0;
204         LIST_FOR_EACH (listEntry, &This->CacheList) {
205                 i++;
206                 cacheEntry = LIST_ENTRY( listEntry, DMUS_PRIVATE_CACHE_ENTRY, entry );
207                 TRACE("Entry nr. %i: GUID = %s, FileName = %s\n", i, debugstr_guid (&cacheEntry->guidObject), debugstr_w (cacheEntry->wzFileName));
208         }
209 #endif
210         
211         return IDirectMusicObject_QueryInterface (pObject, riid, ppv);
212 }
213
214 HRESULT WINAPI IDirectMusicLoader8Impl_SetObject (LPDIRECTMUSICLOADER8 iface, LPDMUS_OBJECTDESC pDesc)
215 {
216         ICOM_THIS(IDirectMusicLoader8Impl,iface);
217
218         FIXME("(%p, %p): stub\n", This, pDesc);
219
220         return S_OK;
221 }
222
223 HRESULT WINAPI IDirectMusicLoader8Impl_SetSearchDirectory (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, WCHAR* pwzPath, BOOL fClear)
224 {
225         ICOM_THIS(IDirectMusicLoader8Impl,iface);
226
227         TRACE("(%p, %s, %p, %d)\n", This, debugstr_guid(rguidClass), pwzPath, fClear);
228         if (0 == strncmpW(This->wzSearchPath, pwzPath, MAX_PATH)) {
229           return S_FALSE;
230         } 
231         strncpyW(This->wzSearchPath, pwzPath, MAX_PATH);
232         
233         return S_OK;
234 }
235
236 HRESULT WINAPI IDirectMusicLoader8Impl_ScanDirectory (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, WCHAR* pwzFileExtension, WCHAR* pwzScanFileName)
237 {
238         ICOM_THIS(IDirectMusicLoader8Impl,iface);
239
240         FIXME("(%p, %s, %p, %p): stub\n", This, debugstr_guid(rguidClass), pwzFileExtension, pwzScanFileName);
241
242         return S_OK;
243 }
244
245 HRESULT WINAPI IDirectMusicLoader8Impl_CacheObject (LPDIRECTMUSICLOADER8 iface, IDirectMusicObject* pObject)
246 {
247         ICOM_THIS(IDirectMusicLoader8Impl,iface);
248
249         FIXME("(%p, %p): stub\n", This, pObject);
250
251         return S_OK;
252 }
253
254 HRESULT WINAPI IDirectMusicLoader8Impl_ReleaseObject (LPDIRECTMUSICLOADER8 iface, IDirectMusicObject* pObject)
255 {
256         ICOM_THIS(IDirectMusicLoader8Impl,iface);
257
258         FIXME("(%p, %p): stub\n", This, pObject);
259
260         return S_OK;
261 }
262
263 HRESULT WINAPI IDirectMusicLoader8Impl_ClearCache (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass)
264 {
265         ICOM_THIS(IDirectMusicLoader8Impl,iface);
266
267         FIXME("(%p, %s): stub\n", This, debugstr_guid(rguidClass));
268
269         return S_OK;
270 }
271
272 HRESULT WINAPI IDirectMusicLoader8Impl_EnableCache (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, BOOL fEnable)
273 {
274         ICOM_THIS(IDirectMusicLoader8Impl,iface);
275
276         FIXME("(%p, %s, %d): stub\n", This, debugstr_guid(rguidClass), fEnable);
277
278         return S_OK;
279 }
280
281 HRESULT WINAPI IDirectMusicLoader8Impl_EnumObject (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, DWORD dwIndex, LPDMUS_OBJECTDESC pDesc)
282 {
283         ICOM_THIS(IDirectMusicLoader8Impl,iface);
284
285         FIXME("(%p, %s, %ld, %p): stub\n", This, debugstr_guid(rguidClass), dwIndex, pDesc);
286
287         return S_OK;
288 }
289
290 /* IDirectMusicLoader8 Interface part follow: */
291 void WINAPI IDirectMusicLoader8Impl_CollectGarbage (LPDIRECTMUSICLOADER8 iface)
292 {
293         ICOM_THIS(IDirectMusicLoader8Impl,iface);
294
295         FIXME("(%p): stub\n", This);
296 }
297
298 HRESULT WINAPI IDirectMusicLoader8Impl_ReleaseObjectByUnknown (LPDIRECTMUSICLOADER8 iface, IUnknown* pObject)
299 {
300         ICOM_THIS(IDirectMusicLoader8Impl,iface);
301
302         FIXME("(%p, %p): stub\n", This, pObject);
303         
304         return S_OK;
305 }
306
307 HRESULT WINAPI IDirectMusicLoader8Impl_LoadObjectFromFile (LPDIRECTMUSICLOADER8 iface, 
308                                                            REFGUID rguidClassID, 
309                                                            REFIID iidInterfaceID, 
310                                                            WCHAR* pwzFilePath, 
311                                                            void** ppObject)
312 {
313         ICOM_THIS(IDirectMusicLoader8Impl,iface);
314         DMUS_OBJECTDESC ObjDesc;
315         
316         TRACE("(%p, %s, %s, %s, %p): wrapping to IDirectMusicLoader8Impl_GetObject\n", This, debugstr_guid(rguidClassID), debugstr_guid(iidInterfaceID), debugstr_w(pwzFilePath), ppObject);
317         
318         ObjDesc.dwSize = sizeof(DMUS_OBJECTDESC);
319         ObjDesc.dwValidData = DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_CLASS; /* I believe I've read somewhere in MSDN that this function requires either full path or relative path */
320         ObjDesc.guidClass = *rguidClassID;
321         strncpyW (ObjDesc.wszFileName, pwzFilePath, MAX_PATH);
322
323         return IDirectMusicLoader8Impl_GetObject (iface, &ObjDesc, iidInterfaceID, ppObject);
324 }
325
326 ICOM_VTABLE(IDirectMusicLoader8) DirectMusicLoader8_Vtbl =
327 {
328     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
329         IDirectMusicLoader8Impl_QueryInterface,
330         IDirectMusicLoader8Impl_AddRef,
331         IDirectMusicLoader8Impl_Release,
332         IDirectMusicLoader8Impl_GetObject,
333         IDirectMusicLoader8Impl_SetObject,
334         IDirectMusicLoader8Impl_SetSearchDirectory,
335         IDirectMusicLoader8Impl_ScanDirectory,
336         IDirectMusicLoader8Impl_CacheObject,
337         IDirectMusicLoader8Impl_ReleaseObject,
338         IDirectMusicLoader8Impl_ClearCache,
339         IDirectMusicLoader8Impl_EnableCache,
340         IDirectMusicLoader8Impl_EnumObject,
341         IDirectMusicLoader8Impl_CollectGarbage,
342         IDirectMusicLoader8Impl_ReleaseObjectByUnknown,
343         IDirectMusicLoader8Impl_LoadObjectFromFile
344 };
345
346 /* for ClassFactory */
347 HRESULT WINAPI DMUSIC_CreateDirectMusicLoader (LPCGUID lpcGUID, LPDIRECTMUSICLOADER8 *ppDMLoad, LPUNKNOWN pUnkOuter)
348 {
349         IDirectMusicLoader8Impl *dmloader;
350
351         TRACE("(%p,%p,%p)\n",lpcGUID, ppDMLoad, pUnkOuter);
352         if (IsEqualIID (lpcGUID, &IID_IDirectMusicLoader) || 
353             IsEqualIID (lpcGUID, &IID_IDirectMusicLoader8)) {
354                 dmloader = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicLoader8Impl));
355                 if (NULL == dmloader) {
356                         *ppDMLoad = (LPDIRECTMUSICLOADER8)NULL;
357                         return E_OUTOFMEMORY;
358                 }
359                 dmloader->lpVtbl = &DirectMusicLoader8_Vtbl;
360                 dmloader->ref = 1;
361                 list_init (&dmloader->CacheList);
362                 *ppDMLoad = (LPDIRECTMUSICLOADER8)dmloader;
363                 return S_OK;
364         }
365         
366         WARN("No interface found\n");
367         return E_NOINTERFACE;
368 }
369
370 /* help function for IDirectMusicLoader8Impl_GetObject */
371 HRESULT WINAPI DMUSIC_GetDefaultGMPath (WCHAR wszPath[MAX_PATH])
372 {
373         HKEY hkDM;
374         DWORD returnType, sizeOfReturnBuffer = MAX_PATH;
375         char szPath[MAX_PATH];
376
377         if ((RegOpenKeyExA (HKEY_LOCAL_MACHINE, "Software\\Microsoft\\DirectMusic" , 0, KEY_READ, &hkDM) != ERROR_SUCCESS) || 
378             (RegQueryValueExA (hkDM, "GMFilePath", NULL, &returnType, szPath, &sizeOfReturnBuffer) != ERROR_SUCCESS)) {
379                 WARN(": registry entry missing\n" );
380                 return E_FAIL;
381         }
382         /* FIXME: Check return types to ensure we're interpreting data right */
383         MultiByteToWideChar (CP_ACP, 0, szPath, -1, wszPath, MAX_PATH);
384         
385         return S_OK;
386 }