vbscript: Added support for title and type arguments of MsgBox.
[wine.git] / dlls / gameux / gameexplorer.c
blobcbe3e8d8c37611b67f9fc4469629176aa31adea7
1 /*
2 * Gameux library coclass GameExplorer implementation
4 * Copyright (C) 2010 Mariusz PluciƄski
6 * This library 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 library 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 library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #define COBJMACROS
22 #include "config.h"
24 #include "ole2.h"
25 #include "sddl.h"
27 #include "gameux.h"
28 #include "gameux_private.h"
30 #include "initguid.h"
31 #include "msxml2.h"
33 #include "wine/debug.h"
34 #include "winreg.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(gameux);
38 /* function from Shell32, not defined in header */
39 extern BOOL WINAPI GUIDFromStringW(LPCWSTR psz, LPGUID pguid);
41 /*******************************************************************************
42 * GameUX helper functions
44 /*******************************************************************************
45 * GAMEUX_initGameData
47 * Internal helper function.
48 * Initializes GAME_DATA structure fields with proper values. Should be
49 * called always before first usage of this structure. Implemented in gameexplorer.c
51 * Parameters:
52 * GameData [I/O] pointer to structure to initialize
54 static void GAMEUX_initGameData(struct GAMEUX_GAME_DATA *GameData)
56 GameData->sGDFBinaryPath = NULL;
57 GameData->sGameInstallDirectory = NULL;
58 GameData->bstrName = NULL;
59 GameData->bstrDescription = NULL;
61 /*******************************************************************************
62 * GAMEUX_uninitGameData
64 * Internal helper function.
65 * Properly frees all data stored or pointed by fields of GAME_DATA structure.
66 * Should be called before freeing this structure. Implemented in gameexplorer.c
68 * Parameters:
69 * GameData [I/O] pointer to structure to uninitialize
71 static void GAMEUX_uninitGameData(struct GAMEUX_GAME_DATA *GameData)
73 HeapFree(GetProcessHeap(), 0, GameData->sGDFBinaryPath);
74 HeapFree(GetProcessHeap(), 0, GameData->sGameInstallDirectory);
75 SysFreeString(GameData->bstrName);
76 SysFreeString(GameData->bstrDescription);
78 /*******************************************************************************
79 * GAMEUX_buildGameRegistryPath
81 * Internal helper function. Description available in gameux_private.h file
83 HRESULT GAMEUX_buildGameRegistryPath(GAME_INSTALL_SCOPE installScope,
84 LPCGUID gameInstanceId,
85 LPWSTR* lpRegistryPath)
87 static const WCHAR sGameUxRegistryPath[] = {'S','O','F','T','W','A','R','E','\\',
88 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
89 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','G','a','m','e','U','X',0};
90 static const WCHAR sGames[] = {'G','a','m','e','s',0};
91 static const WCHAR sBackslash[] = {'\\',0};
93 HRESULT hr = S_OK;
94 HANDLE hToken = NULL;
95 PTOKEN_USER pTokenUser = NULL;
96 DWORD dwLength;
97 LPWSTR lpSID = NULL;
98 WCHAR sInstanceId[40];
99 WCHAR sRegistryPath[8192];
101 TRACE("(0x%x, %s, %p)\n", installScope, debugstr_guid(gameInstanceId), lpRegistryPath);
103 /* this will make freeing it easier for user */
104 *lpRegistryPath = NULL;
106 lstrcpyW(sRegistryPath, sGameUxRegistryPath);
107 lstrcatW(sRegistryPath, sBackslash);
109 if(installScope == GIS_CURRENT_USER)
111 /* build registry path containing user's SID */
112 if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
113 hr = HRESULT_FROM_WIN32(GetLastError());
115 if(SUCCEEDED(hr))
117 if(!GetTokenInformation(hToken, TokenUser, NULL, 0, &dwLength) &&
118 GetLastError()!=ERROR_INSUFFICIENT_BUFFER)
119 hr = HRESULT_FROM_WIN32(GetLastError());
121 if(SUCCEEDED(hr))
123 pTokenUser = HeapAlloc(GetProcessHeap(), 0, dwLength);
124 if(!pTokenUser)
125 hr = E_OUTOFMEMORY;
128 if(SUCCEEDED(hr))
129 if(!GetTokenInformation(hToken, TokenUser, (LPVOID)pTokenUser, dwLength, &dwLength))
130 hr = HRESULT_FROM_WIN32(GetLastError());
132 if(SUCCEEDED(hr))
133 if(!ConvertSidToStringSidW(pTokenUser->User.Sid, &lpSID))
134 hr = HRESULT_FROM_WIN32(GetLastError());
136 if(SUCCEEDED(hr))
138 lstrcatW(sRegistryPath, lpSID);
139 LocalFree(lpSID);
142 HeapFree(GetProcessHeap(), 0, pTokenUser);
143 CloseHandle(hToken);
146 else if(installScope == GIS_ALL_USERS)
147 /* build registry path without SID */
148 lstrcatW(sRegistryPath, sGames);
149 else
150 hr = E_INVALIDARG;
152 /* put game's instance id on the end of path, only if instance id was given */
153 if(gameInstanceId)
155 if(SUCCEEDED(hr))
156 hr = (StringFromGUID2(gameInstanceId, sInstanceId, sizeof(sInstanceId)/sizeof(sInstanceId[0])) ? S_OK : E_FAIL);
158 if(SUCCEEDED(hr))
160 lstrcatW(sRegistryPath, sBackslash);
161 lstrcatW(sRegistryPath, sInstanceId);
165 if(SUCCEEDED(hr))
167 *lpRegistryPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sRegistryPath)+1)*sizeof(WCHAR));
168 if(!*lpRegistryPath)
169 hr = E_OUTOFMEMORY;
172 if(SUCCEEDED(hr))
173 lstrcpyW(*lpRegistryPath, sRegistryPath);
175 TRACE("result: 0x%x, path: %s\n", hr, debugstr_w(*lpRegistryPath));
176 return hr;
178 /*******************************************************************************
179 * GAMEUX_WriteRegistryRecord
181 * Helper function, writes data associated with game (stored in GAMEUX_GAME_DATA
182 * structure) into expected place in registry.
184 * Parameters:
185 * GameData [I] structure with data which will
186 * be written into registry.
187 * Proper values of fields installScope
188 * and guidInstanceId are required
189 * to create registry key.
191 * Schema of naming registry keys associated with games is available in
192 * description of _buildGameRegistryPath internal function.
194 * List of registry keys associated with structure fields:
195 * Key Field in GAMEUX_GAME_DATA structure
196 * ApplicationId guidApplicationId
197 * ConfigApplicationPath sGameInstallDirectory
198 * ConfigGDFBinaryPath sGDFBinaryPath
199 * Title bstrName
202 static HRESULT GAMEUX_WriteRegistryRecord(struct GAMEUX_GAME_DATA *GameData)
204 static const WCHAR sApplicationId[] =
205 {'A','p','p','l','i','c','a','t','i','o','n','I','d',0};
206 static const WCHAR sConfigApplicationPath[] =
207 {'C','o','n','f','i','g','A','p','p','l','i','c','a','t','i','o','n','P','a','t','h',0};
208 static const WCHAR sConfigGDFBinaryPath[] =
209 {'C','o','n','f','i','g','G','D','F','B','i','n','a','r','y','P','a','t','h',0};
210 static const WCHAR sTitle[] =
211 {'T','i','t','l','e',0};
212 static const WCHAR sDescription[] =
213 {'D','e','s','c','r','i','p','t','i','o','n',0};
215 HRESULT hr, hr2;
216 LPWSTR lpRegistryKey;
217 HKEY hKey;
218 WCHAR sGameApplicationId[40];
220 TRACE("(%p)\n", GameData);
222 hr = GAMEUX_buildGameRegistryPath(GameData->installScope, &GameData->guidInstanceId, &lpRegistryKey);
224 if(SUCCEEDED(hr))
225 hr = (StringFromGUID2(&GameData->guidApplicationId, sGameApplicationId, sizeof(sGameApplicationId)/sizeof(sGameApplicationId[0])) ? S_OK : E_FAIL);
227 if(SUCCEEDED(hr))
228 hr = HRESULT_FROM_WIN32(RegCreateKeyExW(HKEY_LOCAL_MACHINE, lpRegistryKey,
229 0, NULL, 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY, NULL,
230 &hKey, NULL));
232 if(SUCCEEDED(hr))
234 /* write game data to registry key */
235 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sConfigApplicationPath, 0,
236 REG_SZ, (LPBYTE)(GameData->sGameInstallDirectory),
237 (lstrlenW(GameData->sGameInstallDirectory)+1)*sizeof(WCHAR)));
239 if(SUCCEEDED(hr))
240 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sConfigGDFBinaryPath, 0,
241 REG_SZ, (LPBYTE)(GameData->sGDFBinaryPath),
242 (lstrlenW(GameData->sGDFBinaryPath)+1)*sizeof(WCHAR)));
244 if(SUCCEEDED(hr))
245 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sApplicationId, 0,
246 REG_SZ, (LPBYTE)(sGameApplicationId),
247 (lstrlenW(sGameApplicationId)+1)*sizeof(WCHAR)));
249 if(SUCCEEDED(hr))
250 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sTitle, 0,
251 REG_SZ, (LPBYTE)(GameData->bstrName),
252 (lstrlenW(GameData->bstrName)+1)*sizeof(WCHAR)));
254 if(SUCCEEDED(hr))
255 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sDescription, 0,
256 REG_SZ, (LPBYTE)(GameData->bstrDescription ? GameData->bstrDescription : GameData->bstrName),
257 (lstrlenW(GameData->bstrDescription ? GameData->bstrDescription : GameData->bstrName)+1)*sizeof(WCHAR)));
259 RegCloseKey(hKey);
261 if(FAILED(hr))
263 /* if something failed, remove whole key */
264 hr2 = RegDeleteKeyExW(HKEY_LOCAL_MACHINE, lpRegistryKey, KEY_WOW64_64KEY, 0);
265 /* do not overwrite old failure code with new success code */
266 if(FAILED(hr2))
267 hr = hr2;
271 HeapFree(GetProcessHeap(), 0, lpRegistryKey);
272 TRACE("returning 0x%x\n", hr);
273 return hr;
275 /*******************************************************************************
276 * GAMEUX_ProcessGameDefinitionElement
278 * Helper function, parses single element from Game Definition
280 * Parameters:
281 * lpXMLElement [I] game definition element
282 * GameData [O] structure, where parsed
283 * data will be stored
285 static HRESULT GAMEUX_ProcessGameDefinitionElement(
286 IXMLDOMElement *element,
287 struct GAMEUX_GAME_DATA *GameData)
289 static const WCHAR sName[] =
290 {'N','a','m','e',0};
291 static const WCHAR sDescription[] =
292 {'D','e','s','c','r','i','p','t','i','o','n',0};
294 HRESULT hr;
295 BSTR bstrElementName;
297 TRACE("(%p, %p)\n", element, GameData);
299 hr = IXMLDOMElement_get_nodeName(element, &bstrElementName);
300 if(SUCCEEDED(hr))
302 /* check element name */
303 if(lstrcmpW(bstrElementName, sName) == 0)
304 hr = IXMLDOMElement_get_text(element, &GameData->bstrName);
306 else if(lstrcmpW(bstrElementName, sDescription) == 0)
307 hr = IXMLDOMElement_get_text(element, &GameData->bstrDescription);
309 else
310 FIXME("entry %s in Game Definition File not yet supported\n", debugstr_w(bstrElementName));
312 SysFreeString(bstrElementName);
315 return hr;
317 /*******************************************************************************
318 * GAMEUX_ParseGameDefinition
320 * Helper function, loads data from given XML element into fields of GAME_DATA
321 * structure
323 * Parameters:
324 * lpXMLGameDefinitionElement [I] Game Definition XML element
325 * GameData [O] structure where data loaded from
326 * XML element will be stored in
328 static HRESULT GAMEUX_ParseGameDefinition(
329 IXMLDOMElement *gdElement,
330 struct GAMEUX_GAME_DATA *GameData)
332 static const WCHAR sGameId[] = {'g','a','m','e','I','D',0};
334 HRESULT hr = S_OK;
335 BSTR bstrAttribute;
336 VARIANT variant;
337 IXMLDOMNodeList *childrenList;
338 IXMLDOMNode *nextNode;
339 IXMLDOMElement *nextElement;
341 TRACE("(%p, %p)\n", gdElement, GameData);
343 bstrAttribute = SysAllocString(sGameId);
344 if(!bstrAttribute)
345 hr = E_OUTOFMEMORY;
347 hr = IXMLDOMElement_getAttribute(gdElement, bstrAttribute, &variant);
349 if(SUCCEEDED(hr))
351 hr = ( GUIDFromStringW(V_BSTR(&variant), &GameData->guidApplicationId)==TRUE ? S_OK : E_FAIL);
353 SysFreeString(V_BSTR(&variant));
356 SysFreeString(bstrAttribute);
358 /* browse subnodes */
359 if(SUCCEEDED(hr))
360 hr = IXMLDOMElement_get_childNodes(gdElement, &childrenList);
362 if(SUCCEEDED(hr))
366 hr = IXMLDOMNodeList_nextNode(childrenList, &nextNode);
368 if(hr == S_OK)
370 hr = IXMLDOMNode_QueryInterface(nextNode, &IID_IXMLDOMElement,
371 (LPVOID*)&nextElement);
373 if(SUCCEEDED(hr))
375 hr = GAMEUX_ProcessGameDefinitionElement(nextElement, GameData);
376 IXMLDOMElement_Release(nextElement);
379 IXMLDOMNode_Release(nextNode);
382 while(hr == S_OK);
383 hr = S_OK;
385 IXMLDOMNodeList_Release(childrenList);
388 return hr;
391 struct parse_gdf_thread_param
393 struct GAMEUX_GAME_DATA *GameData;
394 HRESULT hr;
397 /*******************************************************************************
398 * GAMEUX_ParseGDFBinary
400 * Helper function, loads given binary and parses embed GDF if there's any.
402 * Parameters:
403 * GameData [I/O] Structure with game's data. Content of field
404 * sGDFBinaryPath defines path to binary, from
405 * which embed GDF will be loaded. Data from
406 * GDF will be stored in other fields of this
407 * structure.
409 static DWORD WINAPI GAMEUX_ParseGDFBinary(void *thread_param)
411 struct parse_gdf_thread_param *ctx = thread_param;
412 struct GAMEUX_GAME_DATA *GameData = ctx->GameData;
413 static const WCHAR sRes[] = {'r','e','s',':','/','/',0};
414 static const WCHAR sDATA[] = {'D','A','T','A',0};
415 static const WCHAR sSlash[] = {'/',0};
417 HRESULT hr = S_OK;
418 WCHAR sResourcePath[MAX_PATH];
419 VARIANT variant;
420 VARIANT_BOOL isSuccessful;
421 IXMLDOMDocument *document;
422 IXMLDOMNode *gdNode;
423 IXMLDOMElement *root, *gdElement;
425 TRACE("(%p)->sGDFBinaryPath = %s\n", GameData, debugstr_w(GameData->sGDFBinaryPath));
427 /* prepare path to GDF, using res:// prefix */
428 lstrcpyW(sResourcePath, sRes);
429 lstrcatW(sResourcePath, GameData->sGDFBinaryPath);
430 lstrcatW(sResourcePath, sSlash);
431 lstrcatW(sResourcePath, sDATA);
432 lstrcatW(sResourcePath, sSlash);
433 lstrcatW(sResourcePath, ID_GDF_XML_STR);
435 CoInitialize(NULL);
437 hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
438 &IID_IXMLDOMDocument, (void**)&document);
440 if(SUCCEEDED(hr))
442 /* load GDF into MSXML */
443 V_VT(&variant) = VT_BSTR;
444 V_BSTR(&variant) = SysAllocString(sResourcePath);
445 if(!V_BSTR(&variant))
446 hr = E_OUTOFMEMORY;
448 if(SUCCEEDED(hr))
450 hr = IXMLDOMDocument_load(document, variant, &isSuccessful);
451 if(hr == S_FALSE || isSuccessful == VARIANT_FALSE)
452 hr = E_FAIL;
455 SysFreeString(V_BSTR(&variant));
457 if(SUCCEEDED(hr))
459 hr = IXMLDOMDocument_get_documentElement(document, &root);
460 if(hr == S_FALSE)
461 hr = E_FAIL;
464 if(SUCCEEDED(hr))
466 hr = IXMLDOMElement_get_firstChild(root, &gdNode);
467 if(hr == S_FALSE)
468 hr = E_FAIL;
470 if(SUCCEEDED(hr))
472 hr = IXMLDOMNode_QueryInterface(gdNode, &IID_IXMLDOMElement, (LPVOID*)&gdElement);
473 if(SUCCEEDED(hr))
475 hr = GAMEUX_ParseGameDefinition(gdElement, GameData);
476 IXMLDOMElement_Release(gdElement);
479 IXMLDOMNode_Release(gdNode);
482 IXMLDOMElement_Release(root);
485 IXMLDOMDocument_Release(document);
488 CoUninitialize();
489 ctx->hr = hr;
490 return 0;
493 /*******************************************************************
494 * GAMEUX_RemoveRegistryRecord
496 * Helper function, removes registry key associated with given game instance
498 static HRESULT GAMEUX_RemoveRegistryRecord(GUID* pInstanceID)
500 HRESULT hr;
501 LPWSTR lpRegistryPath = NULL;
502 TRACE("(%s)\n", debugstr_guid(pInstanceID));
504 /* first, check is game installed for all users */
505 hr = GAMEUX_buildGameRegistryPath(GIS_ALL_USERS, pInstanceID, &lpRegistryPath);
506 if(SUCCEEDED(hr))
507 hr = HRESULT_FROM_WIN32(RegDeleteKeyExW(HKEY_LOCAL_MACHINE, lpRegistryPath, KEY_WOW64_64KEY, 0));
509 HeapFree(GetProcessHeap(), 0, lpRegistryPath);
511 /* if not, check current user */
512 if(FAILED(hr))
514 hr = GAMEUX_buildGameRegistryPath(GIS_CURRENT_USER, pInstanceID, &lpRegistryPath);
515 if(SUCCEEDED(hr))
516 hr = HRESULT_FROM_WIN32(RegDeleteKeyExW(HKEY_LOCAL_MACHINE, lpRegistryPath, KEY_WOW64_64KEY, 0));
518 HeapFree(GetProcessHeap(), 0, lpRegistryPath);
521 return hr;
523 /*******************************************************************************
524 * GAMEUX_RegisterGame
526 * Internal helper function. Registers game associated with given GDF binary in
527 * Game Explorer. Implemented in gameexplorer.c
529 * Parameters:
530 * sGDFBinaryPath [I] path to binary containing GDF file in
531 * resources
532 * sGameInstallDirectory [I] path to directory, where game installed
533 * its files.
534 * installScope [I] scope of game installation
535 * pInstanceID [I/O] pointer to game instance identifier.
536 * If pointing to GUID_NULL, then new
537 * identifier will be generated automatically
538 * and returned via this parameter
540 static HRESULT GAMEUX_RegisterGame(LPCWSTR sGDFBinaryPath,
541 LPCWSTR sGameInstallDirectory,
542 GAME_INSTALL_SCOPE installScope,
543 GUID *pInstanceID)
545 HRESULT hr = S_OK;
546 struct GAMEUX_GAME_DATA GameData;
548 TRACE("(%s, %s, 0x%x, %s)\n", debugstr_w(sGDFBinaryPath), debugstr_w(sGameInstallDirectory), installScope, debugstr_guid(pInstanceID));
550 GAMEUX_initGameData(&GameData);
551 GameData.sGDFBinaryPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sGDFBinaryPath)+1)*sizeof(WCHAR));
552 lstrcpyW(GameData.sGDFBinaryPath, sGDFBinaryPath);
553 GameData.sGameInstallDirectory = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sGameInstallDirectory)+1)*sizeof(WCHAR));
554 lstrcpyW(GameData.sGameInstallDirectory, sGameInstallDirectory);
555 GameData.installScope = installScope;
557 /* generate GUID if it was not provided by user */
558 if(IsEqualGUID(pInstanceID, &GUID_NULL))
559 hr = CoCreateGuid(pInstanceID);
561 GameData.guidInstanceId = *pInstanceID;
563 /* load data from GDF binary */
564 if(SUCCEEDED(hr))
566 struct parse_gdf_thread_param thread_param;
567 HANDLE thread;
568 DWORD ret;
570 thread_param.GameData = &GameData;
571 if(!(thread = CreateThread(NULL, 0, GAMEUX_ParseGDFBinary, &thread_param, 0, &ret)))
573 ERR("Failed to create thread.\n");
574 hr = E_FAIL;
575 goto done;
577 ret = WaitForSingleObject(thread, INFINITE);
578 CloseHandle(thread);
579 if(ret != WAIT_OBJECT_0)
581 ERR("Wait failed (%#x).\n", ret);
582 hr = E_FAIL;
583 goto done;
585 hr = thread_param.hr;
588 /* save data to registry */
589 if(SUCCEEDED(hr))
590 hr = GAMEUX_WriteRegistryRecord(&GameData);
592 done:
593 GAMEUX_uninitGameData(&GameData);
594 TRACE("returning 0x%08x\n", hr);
595 return hr;
597 /*******************************************************************************
598 * GAMEUX_IsGameKeyExist
600 * Helper function, checks if game's registry ath exists in given scope
602 * Parameters:
603 * installScope [I] scope to search game in
604 * InstanceID [I] game instance identifier
605 * lpRegistryPath [O] place to store address of registry path to
606 * the game. It is filled only if key exists.
607 * It must be freed by HeapFree(GetProcessHeap(), 0, ...)
609 * Returns:
610 * S_OK key was found properly
611 * S_FALSE key does not exists
614 static HRESULT GAMEUX_IsGameKeyExist(GAME_INSTALL_SCOPE installScope,
615 LPCGUID InstanceID,
616 LPWSTR* lpRegistryPath) {
618 HRESULT hr;
619 HKEY hKey;
621 hr = GAMEUX_buildGameRegistryPath(installScope, InstanceID, lpRegistryPath);
623 if(SUCCEEDED(hr))
624 hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE, *lpRegistryPath,
625 0, KEY_WOW64_64KEY, &hKey));
627 if(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
628 hr = S_FALSE;
630 if(hr == S_OK)
631 RegCloseKey(hKey);
632 else
634 /* if the key does not exist or another error occurred, do not return the path */
635 HeapFree(GetProcessHeap(), 0, *lpRegistryPath);
636 *lpRegistryPath = NULL;
639 return hr;
641 /*******************************************************************************
642 * GAMEUX_LoadRegistryString
644 * Helper function, loads string from registry value and allocates buffer for it
646 static HRESULT GAMEUX_LoadRegistryString(HKEY hRootKey,
647 LPCWSTR lpRegistryKey,
648 LPCWSTR lpRegistryValue,
649 LPWSTR* lpValue)
651 HRESULT hr;
652 DWORD dwSize;
654 *lpValue = NULL;
656 hr = HRESULT_FROM_WIN32(RegGetValueW(hRootKey, lpRegistryKey, lpRegistryValue,
657 RRF_RT_REG_SZ, NULL, NULL, &dwSize));
659 if(SUCCEEDED(hr))
661 *lpValue = HeapAlloc(GetProcessHeap(), 0, dwSize);
662 if(!*lpValue)
663 hr = E_OUTOFMEMORY;
666 if(SUCCEEDED(hr))
667 hr = HRESULT_FROM_WIN32(RegGetValueW(hRootKey, lpRegistryKey, lpRegistryValue,
668 RRF_RT_REG_SZ, NULL, *lpValue, &dwSize));
670 return hr;
672 /*******************************************************************************
673 * GAMEUX_UpdateGame
675 * Helper function, updates stored data about game with given InstanceID
677 static HRESULT GAMEUX_UpdateGame(LPGUID InstanceID) {
678 static const WCHAR sConfigGDFBinaryPath[] = {'C','o','n','f','i','g','G','D','F','B','i','n','a','r','y','P','a','t','h',0};
679 static const WCHAR sConfigApplicationPath[] = {'C','o','n','f','i','g','A','p','p','l','i','c','a','t','i','o','n','P','a','t','h',0};
681 HRESULT hr;
682 GAME_INSTALL_SCOPE installScope;
683 LPWSTR lpRegistryPath;
684 LPWSTR lpGDFBinaryPath;
686 TRACE("(%s)\n", debugstr_guid(InstanceID));
688 /* first, check is game exists in CURRENT_USER scope */
689 installScope = GIS_CURRENT_USER;
690 hr = GAMEUX_IsGameKeyExist(installScope, InstanceID, &lpRegistryPath);
692 if(hr == S_FALSE)
694 /* game not found in CURRENT_USER scope, let's check in ALL_USERS */
695 installScope = GIS_ALL_USERS;
696 hr = GAMEUX_IsGameKeyExist(installScope, InstanceID, &lpRegistryPath);
699 if(hr == S_FALSE)
700 /* still not found? let's inform user that game does not exists */
701 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
703 if(SUCCEEDED(hr))
705 WCHAR *lpGameInstallDirectory = NULL;
707 /* game found, its registry path is in lpRegistryPath and install
708 * scope in installScope */
709 TRACE("game found in registry (path %s), updating\n", debugstr_w(lpRegistryPath));
711 /* first, read required data about game */
712 hr = GAMEUX_LoadRegistryString(HKEY_LOCAL_MACHINE, lpRegistryPath,
713 sConfigGDFBinaryPath, &lpGDFBinaryPath);
715 if(SUCCEEDED(hr))
716 hr = GAMEUX_LoadRegistryString(HKEY_LOCAL_MACHINE, lpRegistryPath,
717 sConfigApplicationPath, &lpGameInstallDirectory);
719 /* now remove currently existing registry key */
720 if(SUCCEEDED(hr))
721 hr = GAMEUX_RemoveRegistryRecord(InstanceID);
723 /* and add it again, it will cause in reparsing of whole GDF */
724 if(SUCCEEDED(hr))
725 hr = GAMEUX_RegisterGame(lpGDFBinaryPath, lpGameInstallDirectory,
726 installScope, InstanceID);
728 HeapFree(GetProcessHeap(), 0, lpGDFBinaryPath);
729 HeapFree(GetProcessHeap(), 0, lpGameInstallDirectory);
732 HeapFree(GetProcessHeap(), 0, lpRegistryPath);
733 TRACE("returning 0x%x\n", hr);
734 return hr;
736 /*******************************************************************************
737 * GAMEUX_FindGameInstanceId
739 * Internal helper function. Description available in gameux_private.h file
741 HRESULT GAMEUX_FindGameInstanceId(
742 LPCWSTR sGDFBinaryPath,
743 GAME_INSTALL_SCOPE installScope,
744 GUID* pInstanceId)
746 static const WCHAR sConfigGDFBinaryPath[] =
747 {'C','o','n','f','i','g','G','D','F','B','i','n','a','r','y','P','a','t','h',0};
749 HRESULT hr;
750 BOOL found = FALSE;
751 LPWSTR lpRegistryPath = NULL;
752 HKEY hRootKey;
753 DWORD dwSubKeys, dwSubKeyLen, dwMaxSubKeyLen, i;
754 LPWSTR lpName = NULL, lpValue = NULL;
756 hr = GAMEUX_buildGameRegistryPath(installScope, NULL, &lpRegistryPath);
758 if(SUCCEEDED(hr))
759 /* enumerate all subkeys of received one and search them for value "ConfigGGDFBinaryPath" */
760 hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE,
761 lpRegistryPath, 0, KEY_READ | KEY_WOW64_64KEY, &hRootKey));
763 if(SUCCEEDED(hr))
765 hr = HRESULT_FROM_WIN32(RegQueryInfoKeyW(hRootKey, NULL, NULL, NULL,
766 &dwSubKeys, &dwMaxSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL));
768 if(SUCCEEDED(hr))
770 ++dwMaxSubKeyLen; /* for string terminator */
771 lpName = CoTaskMemAlloc(dwMaxSubKeyLen*sizeof(WCHAR));
772 if(!lpName) hr = E_OUTOFMEMORY;
775 if(SUCCEEDED(hr))
777 for(i=0; i<dwSubKeys && !found; ++i)
779 dwSubKeyLen = dwMaxSubKeyLen;
780 hr = HRESULT_FROM_WIN32(RegEnumKeyExW(hRootKey, i, lpName, &dwSubKeyLen,
781 NULL, NULL, NULL, NULL));
783 if(SUCCEEDED(hr))
784 hr = GAMEUX_LoadRegistryString(hRootKey, lpName,
785 sConfigGDFBinaryPath, &lpValue);
787 if(SUCCEEDED(hr))
789 if(lstrcmpW(lpValue, sGDFBinaryPath)==0)
791 /* key found, let's copy instance id and exit */
792 hr = (GUIDFromStringW(lpName, pInstanceId) ? S_OK : E_FAIL);
793 found = TRUE;
795 HeapFree(GetProcessHeap(), 0, lpValue);
800 HeapFree(GetProcessHeap(), 0, lpName);
801 RegCloseKey(hRootKey);
804 HeapFree(GetProcessHeap(), 0, lpRegistryPath);
806 if((SUCCEEDED(hr) && !found) || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
807 hr = S_FALSE;
809 return hr;
811 /*******************************************************************************
812 * GameExplorer implementation
815 typedef struct _GameExplorerImpl
817 IGameExplorer IGameExplorer_iface;
818 IGameExplorer2 IGameExplorer2_iface;
819 LONG ref;
820 } GameExplorerImpl;
822 static inline GameExplorerImpl *impl_from_IGameExplorer(IGameExplorer *iface)
824 return CONTAINING_RECORD(iface, GameExplorerImpl, IGameExplorer_iface);
827 static inline GameExplorerImpl *impl_from_IGameExplorer2(IGameExplorer2 *iface)
829 return CONTAINING_RECORD(iface, GameExplorerImpl, IGameExplorer2_iface);
832 static HRESULT WINAPI GameExplorerImpl_QueryInterface(
833 IGameExplorer *iface,
834 REFIID riid,
835 void **ppvObject)
837 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
839 TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
841 *ppvObject = NULL;
843 if(IsEqualGUID(riid, &IID_IUnknown) ||
844 IsEqualGUID(riid, &IID_IGameExplorer))
846 *ppvObject = &This->IGameExplorer_iface;
848 else if(IsEqualGUID(riid, &IID_IGameExplorer2))
850 *ppvObject = &This->IGameExplorer2_iface;
852 else
854 FIXME("interface %s not implemented\n", debugstr_guid(riid));
855 return E_NOINTERFACE;
858 IGameExplorer_AddRef(iface);
859 return S_OK;
862 static ULONG WINAPI GameExplorerImpl_AddRef(IGameExplorer *iface)
864 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
865 LONG ref;
867 ref = InterlockedIncrement(&This->ref);
869 TRACE("(%p): ref=%d\n", This, ref);
870 return ref;
873 static ULONG WINAPI GameExplorerImpl_Release(IGameExplorer *iface)
875 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
876 LONG ref;
878 ref = InterlockedDecrement(&This->ref);
879 TRACE("(%p): ref=%d\n", This, ref);
881 if(ref == 0)
883 TRACE("freeing GameExplorer object\n");
884 HeapFree(GetProcessHeap(), 0, This);
887 return ref;
890 static HRESULT WINAPI GameExplorerImpl_AddGame(
891 IGameExplorer *iface,
892 BSTR bstrGDFBinaryPath,
893 BSTR sGameInstallDirectory,
894 GAME_INSTALL_SCOPE installScope,
895 GUID *pInstanceID)
897 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
898 TRACE("(%p, %s, %s, 0x%x, %s)\n", This, debugstr_w(bstrGDFBinaryPath), debugstr_w(sGameInstallDirectory), installScope, debugstr_guid(pInstanceID));
899 return GAMEUX_RegisterGame(bstrGDFBinaryPath, sGameInstallDirectory, installScope, pInstanceID);
902 static HRESULT WINAPI GameExplorerImpl_RemoveGame(
903 IGameExplorer *iface,
904 GUID instanceID)
906 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
908 TRACE("(%p, %s)\n", This, debugstr_guid(&instanceID));
909 return GAMEUX_RemoveRegistryRecord(&instanceID);
912 static HRESULT WINAPI GameExplorerImpl_UpdateGame(
913 IGameExplorer *iface,
914 GUID instanceID)
916 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
918 TRACE("(%p, %s)\n", This, debugstr_guid(&instanceID));
919 return GAMEUX_UpdateGame(&instanceID);
922 static HRESULT WINAPI GameExplorerImpl_VerifyAccess(
923 IGameExplorer *iface,
924 BSTR sGDFBinaryPath,
925 BOOL *pHasAccess)
927 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
929 FIXME("(%p, %s, %p)\n", This, debugstr_w(sGDFBinaryPath), pHasAccess);
930 *pHasAccess = TRUE;
931 return S_OK;
934 static const struct IGameExplorerVtbl GameExplorerImplVtbl =
936 GameExplorerImpl_QueryInterface,
937 GameExplorerImpl_AddRef,
938 GameExplorerImpl_Release,
939 GameExplorerImpl_AddGame,
940 GameExplorerImpl_RemoveGame,
941 GameExplorerImpl_UpdateGame,
942 GameExplorerImpl_VerifyAccess
946 static HRESULT WINAPI GameExplorer2Impl_QueryInterface(
947 IGameExplorer2 *iface,
948 REFIID riid,
949 void **ppvObject)
951 GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
952 return GameExplorerImpl_QueryInterface(&This->IGameExplorer_iface, riid, ppvObject);
955 static ULONG WINAPI GameExplorer2Impl_AddRef(IGameExplorer2 *iface)
957 GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
958 return GameExplorerImpl_AddRef(&This->IGameExplorer_iface);
961 static ULONG WINAPI GameExplorer2Impl_Release(IGameExplorer2 *iface)
963 GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
964 return GameExplorerImpl_Release(&This->IGameExplorer_iface);
967 static HRESULT WINAPI GameExplorer2Impl_CheckAccess(
968 IGameExplorer2 *iface,
969 LPCWSTR binaryGDFPath,
970 BOOL *pHasAccess)
972 GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
973 FIXME("stub (%p, %s, %p)\n", This, debugstr_w(binaryGDFPath), pHasAccess);
974 return E_NOTIMPL;
977 static HRESULT WINAPI GameExplorer2Impl_InstallGame(
978 IGameExplorer2 *iface,
979 LPCWSTR binaryGDFPath,
980 LPCWSTR installDirectory,
981 GAME_INSTALL_SCOPE installScope)
983 HRESULT hr;
984 GUID instanceId;
985 GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
987 TRACE("(%p, %s, %s, 0x%x)\n", This, debugstr_w(binaryGDFPath), debugstr_w(installDirectory), installScope);
989 if(!binaryGDFPath)
990 return E_INVALIDARG;
992 hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_CURRENT_USER, &instanceId);
994 if(hr == S_FALSE)
995 hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_ALL_USERS, &instanceId);
997 if(hr == S_FALSE)
999 /* if game isn't yet registered, then install it */
1000 instanceId = GUID_NULL;
1001 hr = GAMEUX_RegisterGame(binaryGDFPath, installDirectory, installScope, &instanceId);
1003 else if(hr == S_OK)
1004 /* otherwise, update game */
1005 hr = GAMEUX_UpdateGame(&instanceId);
1007 return hr;
1010 static HRESULT WINAPI GameExplorer2Impl_UninstallGame(
1011 IGameExplorer2 *iface,
1012 LPCWSTR binaryGDFPath)
1014 HRESULT hr;
1015 GUID instanceId;
1016 GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
1017 TRACE("(%p, %s)\n", This, debugstr_w(binaryGDFPath));
1019 if(!binaryGDFPath)
1020 return E_INVALIDARG;
1022 hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_CURRENT_USER, &instanceId);
1024 if(hr == S_FALSE)
1025 hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_ALL_USERS, &instanceId);
1027 if(hr == S_OK)
1028 hr = GAMEUX_RemoveRegistryRecord(&instanceId);
1030 return hr;
1033 static const struct IGameExplorer2Vtbl GameExplorer2ImplVtbl =
1035 GameExplorer2Impl_QueryInterface,
1036 GameExplorer2Impl_AddRef,
1037 GameExplorer2Impl_Release,
1038 GameExplorer2Impl_InstallGame,
1039 GameExplorer2Impl_UninstallGame,
1040 GameExplorer2Impl_CheckAccess
1044 * Construction routine
1046 HRESULT GameExplorer_create(
1047 IUnknown* pUnkOuter,
1048 IUnknown** ppObj)
1050 GameExplorerImpl *pGameExplorer;
1052 TRACE("(%p, %p)\n", pUnkOuter, ppObj);
1054 pGameExplorer = HeapAlloc(GetProcessHeap(), 0, sizeof(*pGameExplorer));
1056 if(!pGameExplorer)
1057 return E_OUTOFMEMORY;
1059 pGameExplorer->IGameExplorer_iface.lpVtbl = &GameExplorerImplVtbl;
1060 pGameExplorer->IGameExplorer2_iface.lpVtbl = &GameExplorer2ImplVtbl;
1061 pGameExplorer->ref = 1;
1063 *ppObj = (IUnknown*)&pGameExplorer->IGameExplorer_iface;
1065 TRACE("returning iface: %p\n", *ppObj);
1066 return S_OK;