dwrite: Implement GetUnicodeRanges().
[wine.git] / dlls / gameux / gameexplorer.c
blob6bdb2e44596e2bd9df369c52be8cc4b65c15c2b7
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 /*******************************************************************************
39 * GameUX helper functions
41 /*******************************************************************************
42 * GAMEUX_initGameData
44 * Internal helper function.
45 * Initializes GAME_DATA structure fields with proper values. Should be
46 * called always before first usage of this structure. Implemented in gameexplorer.c
48 * Parameters:
49 * GameData [I/O] pointer to structure to initialize
51 static void GAMEUX_initGameData(struct GAMEUX_GAME_DATA *GameData)
53 GameData->sGDFBinaryPath = NULL;
54 GameData->sGameInstallDirectory = NULL;
55 GameData->bstrName = NULL;
56 GameData->bstrDescription = NULL;
58 /*******************************************************************************
59 * GAMEUX_uninitGameData
61 * Internal helper function.
62 * Properly frees all data stored or pointed by fields of GAME_DATA structure.
63 * Should be called before freeing this structure. Implemented in gameexplorer.c
65 * Parameters:
66 * GameData [I/O] pointer to structure to uninitialize
68 static void GAMEUX_uninitGameData(struct GAMEUX_GAME_DATA *GameData)
70 HeapFree(GetProcessHeap(), 0, GameData->sGDFBinaryPath);
71 HeapFree(GetProcessHeap(), 0, GameData->sGameInstallDirectory);
72 SysFreeString(GameData->bstrName);
73 SysFreeString(GameData->bstrDescription);
75 /*******************************************************************************
76 * GAMEUX_buildGameRegistryPath
78 * Internal helper function. Description available in gameux_private.h file
80 HRESULT GAMEUX_buildGameRegistryPath(GAME_INSTALL_SCOPE installScope,
81 LPCGUID gameInstanceId,
82 LPWSTR* lpRegistryPath)
84 static const WCHAR sGameUxRegistryPath[] = {'S','O','F','T','W','A','R','E','\\',
85 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
86 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','G','a','m','e','U','X',0};
87 static const WCHAR sGames[] = {'G','a','m','e','s',0};
88 static const WCHAR sBackslash[] = {'\\',0};
90 HRESULT hr = S_OK;
91 HANDLE hToken = NULL;
92 PTOKEN_USER pTokenUser = NULL;
93 DWORD dwLength;
94 LPWSTR lpSID = NULL;
95 WCHAR sInstanceId[40];
96 WCHAR sRegistryPath[8192];
98 TRACE("(0x%x, %s, %p)\n", installScope, debugstr_guid(gameInstanceId), lpRegistryPath);
100 /* this will make freeing it easier for user */
101 *lpRegistryPath = NULL;
103 lstrcpyW(sRegistryPath, sGameUxRegistryPath);
104 lstrcatW(sRegistryPath, sBackslash);
106 if(installScope == GIS_CURRENT_USER)
108 /* build registry path containing user's SID */
109 if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
110 hr = HRESULT_FROM_WIN32(GetLastError());
112 if(SUCCEEDED(hr))
114 if(!GetTokenInformation(hToken, TokenUser, NULL, 0, &dwLength) &&
115 GetLastError()!=ERROR_INSUFFICIENT_BUFFER)
116 hr = HRESULT_FROM_WIN32(GetLastError());
118 if(SUCCEEDED(hr))
120 pTokenUser = HeapAlloc(GetProcessHeap(), 0, dwLength);
121 if(!pTokenUser)
122 hr = E_OUTOFMEMORY;
125 if(SUCCEEDED(hr))
126 if(!GetTokenInformation(hToken, TokenUser, (LPVOID)pTokenUser, dwLength, &dwLength))
127 hr = HRESULT_FROM_WIN32(GetLastError());
129 if(SUCCEEDED(hr))
130 if(!ConvertSidToStringSidW(pTokenUser->User.Sid, &lpSID))
131 hr = HRESULT_FROM_WIN32(GetLastError());
133 if(SUCCEEDED(hr))
135 lstrcatW(sRegistryPath, lpSID);
136 LocalFree(lpSID);
139 HeapFree(GetProcessHeap(), 0, pTokenUser);
140 CloseHandle(hToken);
143 else if(installScope == GIS_ALL_USERS)
144 /* build registry path without SID */
145 lstrcatW(sRegistryPath, sGames);
146 else
147 hr = E_INVALIDARG;
149 /* put game's instance id on the end of path, only if instance id was given */
150 if(gameInstanceId)
152 if(SUCCEEDED(hr))
153 hr = (StringFromGUID2(gameInstanceId, sInstanceId, sizeof(sInstanceId)/sizeof(sInstanceId[0])) ? S_OK : E_FAIL);
155 if(SUCCEEDED(hr))
157 lstrcatW(sRegistryPath, sBackslash);
158 lstrcatW(sRegistryPath, sInstanceId);
162 if(SUCCEEDED(hr))
164 *lpRegistryPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sRegistryPath)+1)*sizeof(WCHAR));
165 if(!*lpRegistryPath)
166 hr = E_OUTOFMEMORY;
169 if(SUCCEEDED(hr))
170 lstrcpyW(*lpRegistryPath, sRegistryPath);
172 TRACE("result: 0x%x, path: %s\n", hr, debugstr_w(*lpRegistryPath));
173 return hr;
175 /*******************************************************************************
176 * GAMEUX_WriteRegistryRecord
178 * Helper function, writes data associated with game (stored in GAMEUX_GAME_DATA
179 * structure) into expected place in registry.
181 * Parameters:
182 * GameData [I] structure with data which will
183 * be written into registry.
184 * Proper values of fields installScope
185 * and guidInstanceId are required
186 * to create registry key.
188 * Schema of naming registry keys associated with games is available in
189 * description of _buildGameRegistryPath internal function.
191 * List of registry keys associated with structure fields:
192 * Key Field in GAMEUX_GAME_DATA structure
193 * ApplicationId guidApplicationId
194 * ConfigApplicationPath sGameInstallDirectory
195 * ConfigGDFBinaryPath sGDFBinaryPath
196 * Title bstrName
199 static HRESULT GAMEUX_WriteRegistryRecord(struct GAMEUX_GAME_DATA *GameData)
201 static const WCHAR sApplicationId[] =
202 {'A','p','p','l','i','c','a','t','i','o','n','I','d',0};
203 static const WCHAR sConfigApplicationPath[] =
204 {'C','o','n','f','i','g','A','p','p','l','i','c','a','t','i','o','n','P','a','t','h',0};
205 static const WCHAR sConfigGDFBinaryPath[] =
206 {'C','o','n','f','i','g','G','D','F','B','i','n','a','r','y','P','a','t','h',0};
207 static const WCHAR sTitle[] =
208 {'T','i','t','l','e',0};
209 static const WCHAR sDescription[] =
210 {'D','e','s','c','r','i','p','t','i','o','n',0};
212 HRESULT hr, hr2;
213 LPWSTR lpRegistryKey;
214 HKEY hKey;
215 WCHAR sGameApplicationId[40];
217 TRACE("(%p)\n", GameData);
219 hr = GAMEUX_buildGameRegistryPath(GameData->installScope, &GameData->guidInstanceId, &lpRegistryKey);
221 if(SUCCEEDED(hr))
222 hr = (StringFromGUID2(&GameData->guidApplicationId, sGameApplicationId, sizeof(sGameApplicationId)/sizeof(sGameApplicationId[0])) ? S_OK : E_FAIL);
224 if(SUCCEEDED(hr))
225 hr = HRESULT_FROM_WIN32(RegCreateKeyExW(HKEY_LOCAL_MACHINE, lpRegistryKey,
226 0, NULL, 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY, NULL,
227 &hKey, NULL));
229 if(SUCCEEDED(hr))
231 /* write game data to registry key */
232 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sConfigApplicationPath, 0,
233 REG_SZ, (LPBYTE)(GameData->sGameInstallDirectory),
234 (lstrlenW(GameData->sGameInstallDirectory)+1)*sizeof(WCHAR)));
236 if(SUCCEEDED(hr))
237 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sConfigGDFBinaryPath, 0,
238 REG_SZ, (LPBYTE)(GameData->sGDFBinaryPath),
239 (lstrlenW(GameData->sGDFBinaryPath)+1)*sizeof(WCHAR)));
241 if(SUCCEEDED(hr))
242 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sApplicationId, 0,
243 REG_SZ, (LPBYTE)(sGameApplicationId),
244 (lstrlenW(sGameApplicationId)+1)*sizeof(WCHAR)));
246 if(SUCCEEDED(hr))
247 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sTitle, 0,
248 REG_SZ, (LPBYTE)(GameData->bstrName),
249 (lstrlenW(GameData->bstrName)+1)*sizeof(WCHAR)));
251 if(SUCCEEDED(hr))
252 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sDescription, 0,
253 REG_SZ, (LPBYTE)(GameData->bstrDescription ? GameData->bstrDescription : GameData->bstrName),
254 (lstrlenW(GameData->bstrDescription ? GameData->bstrDescription : GameData->bstrName)+1)*sizeof(WCHAR)));
256 RegCloseKey(hKey);
258 if(FAILED(hr))
260 /* if something failed, remove whole key */
261 hr2 = RegDeleteKeyExW(HKEY_LOCAL_MACHINE, lpRegistryKey, KEY_WOW64_64KEY, 0);
262 /* do not overwrite old failure code with new success code */
263 if(FAILED(hr2))
264 hr = hr2;
268 HeapFree(GetProcessHeap(), 0, lpRegistryKey);
269 TRACE("returning 0x%x\n", hr);
270 return hr;
272 /*******************************************************************************
273 * GAMEUX_ProcessGameDefinitionElement
275 * Helper function, parses single element from Game Definition
277 * Parameters:
278 * lpXMLElement [I] game definition element
279 * GameData [O] structure, where parsed
280 * data will be stored
282 static HRESULT GAMEUX_ProcessGameDefinitionElement(
283 IXMLDOMElement *element,
284 struct GAMEUX_GAME_DATA *GameData)
286 static const WCHAR sName[] =
287 {'N','a','m','e',0};
288 static const WCHAR sDescription[] =
289 {'D','e','s','c','r','i','p','t','i','o','n',0};
291 HRESULT hr;
292 BSTR bstrElementName;
294 TRACE("(%p, %p)\n", element, GameData);
296 hr = IXMLDOMElement_get_nodeName(element, &bstrElementName);
297 if(SUCCEEDED(hr))
299 /* check element name */
300 if(lstrcmpW(bstrElementName, sName) == 0)
301 hr = IXMLDOMElement_get_text(element, &GameData->bstrName);
303 else if(lstrcmpW(bstrElementName, sDescription) == 0)
304 hr = IXMLDOMElement_get_text(element, &GameData->bstrDescription);
306 else
307 FIXME("entry %s in Game Definition File not yet supported\n", debugstr_w(bstrElementName));
309 SysFreeString(bstrElementName);
312 return hr;
314 /*******************************************************************************
315 * GAMEUX_ParseGameDefinition
317 * Helper function, loads data from given XML element into fields of GAME_DATA
318 * structure
320 * Parameters:
321 * lpXMLGameDefinitionElement [I] Game Definition XML element
322 * GameData [O] structure where data loaded from
323 * XML element will be stored in
325 static HRESULT GAMEUX_ParseGameDefinition(IXMLDOMElement *gamedef, struct GAMEUX_GAME_DATA *game_data)
327 static const WCHAR gameidW[] = {'g','a','m','e','I','D',0};
328 IXMLDOMNodeList *props;
329 VARIANT var;
330 HRESULT hr;
331 BSTR attr;
333 TRACE("(%p, %p)\n", gamedef, game_data);
335 attr = SysAllocString(gameidW);
336 if (!attr)
337 return E_OUTOFMEMORY;
339 hr = IXMLDOMElement_getAttribute(gamedef, attr, &var);
340 SysFreeString(attr);
342 if (SUCCEEDED(hr))
344 hr = CLSIDFromString(V_BSTR(&var), &game_data->guidApplicationId);
345 VariantClear(&var);
348 if (SUCCEEDED(hr))
349 hr = IXMLDOMElement_get_childNodes(gamedef, &props);
351 if (FAILED(hr))
352 return hr;
356 IXMLDOMNode *prop;
358 hr = IXMLDOMNodeList_nextNode(props, &prop);
359 if (hr == S_OK)
361 IXMLDOMElement *element;
363 hr = IXMLDOMNode_QueryInterface(prop, &IID_IXMLDOMElement, (void**)&element);
364 if (hr == S_OK)
366 hr = GAMEUX_ProcessGameDefinitionElement(element, game_data);
367 IXMLDOMElement_Release(element);
370 IXMLDOMNode_Release(prop);
373 while (hr == S_OK);
374 IXMLDOMNodeList_Release(props);
376 return FAILED(hr) ? hr : S_OK;
379 struct parse_gdf_thread_param
381 struct GAMEUX_GAME_DATA *GameData;
382 HRESULT hr;
385 /*******************************************************************************
386 * GAMEUX_ParseGDFBinary
388 * Helper function, loads given binary and parses embed GDF if there's any.
390 * Parameters:
391 * GameData [I/O] Structure with game's data. Content of field
392 * sGDFBinaryPath defines path to binary, from
393 * which embed GDF will be loaded. Data from
394 * GDF will be stored in other fields of this
395 * structure.
397 static DWORD WINAPI GAMEUX_ParseGDFBinary(void *thread_param)
399 struct parse_gdf_thread_param *ctx = thread_param;
400 struct GAMEUX_GAME_DATA *GameData = ctx->GameData;
401 static const WCHAR sRes[] = {'r','e','s',':','/','/',0};
402 static const WCHAR sDATA[] = {'D','A','T','A',0};
403 static const WCHAR sSlash[] = {'/',0};
405 HRESULT hr = S_OK;
406 WCHAR sResourcePath[MAX_PATH];
407 VARIANT variant;
408 VARIANT_BOOL isSuccessful;
409 IXMLDOMDocument *document;
410 IXMLDOMNode *gdNode;
411 IXMLDOMElement *root, *gdElement;
413 TRACE("(%p)->sGDFBinaryPath = %s\n", GameData, debugstr_w(GameData->sGDFBinaryPath));
415 /* prepare path to GDF, using res:// prefix */
416 lstrcpyW(sResourcePath, sRes);
417 lstrcatW(sResourcePath, GameData->sGDFBinaryPath);
418 lstrcatW(sResourcePath, sSlash);
419 lstrcatW(sResourcePath, sDATA);
420 lstrcatW(sResourcePath, sSlash);
421 lstrcatW(sResourcePath, ID_GDF_XML_STR);
423 CoInitialize(NULL);
425 hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
426 &IID_IXMLDOMDocument, (void**)&document);
428 if(SUCCEEDED(hr))
430 /* load GDF into MSXML */
431 V_VT(&variant) = VT_BSTR;
432 V_BSTR(&variant) = SysAllocString(sResourcePath);
433 if(!V_BSTR(&variant))
434 hr = E_OUTOFMEMORY;
436 if(SUCCEEDED(hr))
438 hr = IXMLDOMDocument_load(document, variant, &isSuccessful);
439 if(hr == S_FALSE || isSuccessful == VARIANT_FALSE)
440 hr = E_FAIL;
443 SysFreeString(V_BSTR(&variant));
445 if(SUCCEEDED(hr))
447 hr = IXMLDOMDocument_get_documentElement(document, &root);
448 if(hr == S_FALSE)
449 hr = E_FAIL;
452 if(SUCCEEDED(hr))
454 hr = IXMLDOMElement_get_firstChild(root, &gdNode);
455 if(hr == S_FALSE)
456 hr = E_FAIL;
458 if(SUCCEEDED(hr))
460 hr = IXMLDOMNode_QueryInterface(gdNode, &IID_IXMLDOMElement, (LPVOID*)&gdElement);
461 if(SUCCEEDED(hr))
463 hr = GAMEUX_ParseGameDefinition(gdElement, GameData);
464 IXMLDOMElement_Release(gdElement);
467 IXMLDOMNode_Release(gdNode);
470 IXMLDOMElement_Release(root);
473 IXMLDOMDocument_Release(document);
476 CoUninitialize();
477 ctx->hr = hr;
478 return 0;
481 /*******************************************************************
482 * GAMEUX_RemoveRegistryRecord
484 * Helper function, removes registry key associated with given game instance
486 static HRESULT GAMEUX_RemoveRegistryRecord(GUID* pInstanceID)
488 HRESULT hr;
489 LPWSTR lpRegistryPath = NULL;
490 TRACE("(%s)\n", debugstr_guid(pInstanceID));
492 /* first, check is game installed for all users */
493 hr = GAMEUX_buildGameRegistryPath(GIS_ALL_USERS, pInstanceID, &lpRegistryPath);
494 if(SUCCEEDED(hr))
495 hr = HRESULT_FROM_WIN32(RegDeleteKeyExW(HKEY_LOCAL_MACHINE, lpRegistryPath, KEY_WOW64_64KEY, 0));
497 HeapFree(GetProcessHeap(), 0, lpRegistryPath);
499 /* if not, check current user */
500 if(FAILED(hr))
502 hr = GAMEUX_buildGameRegistryPath(GIS_CURRENT_USER, pInstanceID, &lpRegistryPath);
503 if(SUCCEEDED(hr))
504 hr = HRESULT_FROM_WIN32(RegDeleteKeyExW(HKEY_LOCAL_MACHINE, lpRegistryPath, KEY_WOW64_64KEY, 0));
506 HeapFree(GetProcessHeap(), 0, lpRegistryPath);
509 return hr;
511 /*******************************************************************************
512 * GAMEUX_RegisterGame
514 * Internal helper function. Registers game associated with given GDF binary in
515 * Game Explorer. Implemented in gameexplorer.c
517 * Parameters:
518 * sGDFBinaryPath [I] path to binary containing GDF file in
519 * resources
520 * sGameInstallDirectory [I] path to directory, where game installed
521 * its files.
522 * installScope [I] scope of game installation
523 * pInstanceID [I/O] pointer to game instance identifier.
524 * If pointing to GUID_NULL, then new
525 * identifier will be generated automatically
526 * and returned via this parameter
528 static HRESULT GAMEUX_RegisterGame(LPCWSTR sGDFBinaryPath,
529 LPCWSTR sGameInstallDirectory,
530 GAME_INSTALL_SCOPE installScope,
531 GUID *pInstanceID)
533 HRESULT hr = S_OK;
534 struct GAMEUX_GAME_DATA GameData;
536 TRACE("(%s, %s, 0x%x, %s)\n", debugstr_w(sGDFBinaryPath), debugstr_w(sGameInstallDirectory), installScope, debugstr_guid(pInstanceID));
538 GAMEUX_initGameData(&GameData);
539 GameData.sGDFBinaryPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sGDFBinaryPath)+1)*sizeof(WCHAR));
540 lstrcpyW(GameData.sGDFBinaryPath, sGDFBinaryPath);
541 GameData.sGameInstallDirectory = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sGameInstallDirectory)+1)*sizeof(WCHAR));
542 lstrcpyW(GameData.sGameInstallDirectory, sGameInstallDirectory);
543 GameData.installScope = installScope;
545 /* generate GUID if it was not provided by user */
546 if(IsEqualGUID(pInstanceID, &GUID_NULL))
547 hr = CoCreateGuid(pInstanceID);
549 GameData.guidInstanceId = *pInstanceID;
551 /* load data from GDF binary */
552 if(SUCCEEDED(hr))
554 struct parse_gdf_thread_param thread_param;
555 HANDLE thread;
556 DWORD ret;
558 thread_param.GameData = &GameData;
559 if(!(thread = CreateThread(NULL, 0, GAMEUX_ParseGDFBinary, &thread_param, 0, &ret)))
561 ERR("Failed to create thread.\n");
562 hr = E_FAIL;
563 goto done;
565 ret = WaitForSingleObject(thread, INFINITE);
566 CloseHandle(thread);
567 if(ret != WAIT_OBJECT_0)
569 ERR("Wait failed (%#x).\n", ret);
570 hr = E_FAIL;
571 goto done;
573 hr = thread_param.hr;
576 /* save data to registry */
577 if(SUCCEEDED(hr))
578 hr = GAMEUX_WriteRegistryRecord(&GameData);
580 done:
581 GAMEUX_uninitGameData(&GameData);
582 TRACE("returning 0x%08x\n", hr);
583 return hr;
585 /*******************************************************************************
586 * GAMEUX_IsGameKeyExist
588 * Helper function, checks if game's registry ath exists in given scope
590 * Parameters:
591 * installScope [I] scope to search game in
592 * InstanceID [I] game instance identifier
593 * lpRegistryPath [O] place to store address of registry path to
594 * the game. It is filled only if key exists.
595 * It must be freed by HeapFree(GetProcessHeap(), 0, ...)
597 * Returns:
598 * S_OK key was found properly
599 * S_FALSE key does not exists
602 static HRESULT GAMEUX_IsGameKeyExist(GAME_INSTALL_SCOPE installScope,
603 LPCGUID InstanceID,
604 LPWSTR* lpRegistryPath) {
606 HRESULT hr;
607 HKEY hKey;
609 hr = GAMEUX_buildGameRegistryPath(installScope, InstanceID, lpRegistryPath);
611 if(SUCCEEDED(hr))
612 hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE, *lpRegistryPath,
613 0, KEY_WOW64_64KEY, &hKey));
615 if(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
616 hr = S_FALSE;
618 if(hr == S_OK)
619 RegCloseKey(hKey);
620 else
622 /* if the key does not exist or another error occurred, do not return the path */
623 HeapFree(GetProcessHeap(), 0, *lpRegistryPath);
624 *lpRegistryPath = NULL;
627 return hr;
629 /*******************************************************************************
630 * GAMEUX_LoadRegistryString
632 * Helper function, loads string from registry value and allocates buffer for it
634 static HRESULT GAMEUX_LoadRegistryString(HKEY hRootKey,
635 LPCWSTR lpRegistryKey,
636 LPCWSTR lpRegistryValue,
637 LPWSTR* lpValue)
639 HRESULT hr;
640 DWORD dwSize;
642 *lpValue = NULL;
644 hr = HRESULT_FROM_WIN32(RegGetValueW(hRootKey, lpRegistryKey, lpRegistryValue,
645 RRF_RT_REG_SZ, NULL, NULL, &dwSize));
647 if(SUCCEEDED(hr))
649 *lpValue = HeapAlloc(GetProcessHeap(), 0, dwSize);
650 if(!*lpValue)
651 hr = E_OUTOFMEMORY;
654 if(SUCCEEDED(hr))
655 hr = HRESULT_FROM_WIN32(RegGetValueW(hRootKey, lpRegistryKey, lpRegistryValue,
656 RRF_RT_REG_SZ, NULL, *lpValue, &dwSize));
658 return hr;
660 /*******************************************************************************
661 * GAMEUX_UpdateGame
663 * Helper function, updates stored data about game with given InstanceID
665 static HRESULT GAMEUX_UpdateGame(LPGUID InstanceID) {
666 static const WCHAR sConfigGDFBinaryPath[] = {'C','o','n','f','i','g','G','D','F','B','i','n','a','r','y','P','a','t','h',0};
667 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};
669 HRESULT hr;
670 GAME_INSTALL_SCOPE installScope;
671 LPWSTR lpRegistryPath;
672 LPWSTR lpGDFBinaryPath;
674 TRACE("(%s)\n", debugstr_guid(InstanceID));
676 /* first, check is game exists in CURRENT_USER scope */
677 installScope = GIS_CURRENT_USER;
678 hr = GAMEUX_IsGameKeyExist(installScope, InstanceID, &lpRegistryPath);
680 if(hr == S_FALSE)
682 /* game not found in CURRENT_USER scope, let's check in ALL_USERS */
683 installScope = GIS_ALL_USERS;
684 hr = GAMEUX_IsGameKeyExist(installScope, InstanceID, &lpRegistryPath);
687 if(hr == S_FALSE)
688 /* still not found? let's inform user that game does not exists */
689 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
691 if(SUCCEEDED(hr))
693 WCHAR *lpGameInstallDirectory = NULL;
695 /* game found, its registry path is in lpRegistryPath and install
696 * scope in installScope */
697 TRACE("game found in registry (path %s), updating\n", debugstr_w(lpRegistryPath));
699 /* first, read required data about game */
700 hr = GAMEUX_LoadRegistryString(HKEY_LOCAL_MACHINE, lpRegistryPath,
701 sConfigGDFBinaryPath, &lpGDFBinaryPath);
703 if(SUCCEEDED(hr))
704 hr = GAMEUX_LoadRegistryString(HKEY_LOCAL_MACHINE, lpRegistryPath,
705 sConfigApplicationPath, &lpGameInstallDirectory);
707 /* now remove currently existing registry key */
708 if(SUCCEEDED(hr))
709 hr = GAMEUX_RemoveRegistryRecord(InstanceID);
711 /* and add it again, it will cause in reparsing of whole GDF */
712 if(SUCCEEDED(hr))
713 hr = GAMEUX_RegisterGame(lpGDFBinaryPath, lpGameInstallDirectory,
714 installScope, InstanceID);
716 HeapFree(GetProcessHeap(), 0, lpGDFBinaryPath);
717 HeapFree(GetProcessHeap(), 0, lpGameInstallDirectory);
720 HeapFree(GetProcessHeap(), 0, lpRegistryPath);
721 TRACE("returning 0x%x\n", hr);
722 return hr;
724 /*******************************************************************************
725 * GAMEUX_FindGameInstanceId
727 * Internal helper function. Description available in gameux_private.h file
729 HRESULT GAMEUX_FindGameInstanceId(
730 LPCWSTR sGDFBinaryPath,
731 GAME_INSTALL_SCOPE installScope,
732 GUID* pInstanceId)
734 static const WCHAR sConfigGDFBinaryPath[] =
735 {'C','o','n','f','i','g','G','D','F','B','i','n','a','r','y','P','a','t','h',0};
737 HRESULT hr;
738 BOOL found = FALSE;
739 LPWSTR lpRegistryPath = NULL;
740 HKEY hRootKey;
741 DWORD dwSubKeys, dwSubKeyLen, dwMaxSubKeyLen, i;
742 LPWSTR lpName = NULL, lpValue = NULL;
744 hr = GAMEUX_buildGameRegistryPath(installScope, NULL, &lpRegistryPath);
746 if(SUCCEEDED(hr))
747 /* enumerate all subkeys of received one and search them for value "ConfigGGDFBinaryPath" */
748 hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE,
749 lpRegistryPath, 0, KEY_READ | KEY_WOW64_64KEY, &hRootKey));
751 if(SUCCEEDED(hr))
753 hr = HRESULT_FROM_WIN32(RegQueryInfoKeyW(hRootKey, NULL, NULL, NULL,
754 &dwSubKeys, &dwMaxSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL));
756 if(SUCCEEDED(hr))
758 ++dwMaxSubKeyLen; /* for string terminator */
759 lpName = CoTaskMemAlloc(dwMaxSubKeyLen*sizeof(WCHAR));
760 if(!lpName) hr = E_OUTOFMEMORY;
763 if(SUCCEEDED(hr))
765 for(i=0; i<dwSubKeys && !found; ++i)
767 dwSubKeyLen = dwMaxSubKeyLen;
768 hr = HRESULT_FROM_WIN32(RegEnumKeyExW(hRootKey, i, lpName, &dwSubKeyLen,
769 NULL, NULL, NULL, NULL));
771 if(SUCCEEDED(hr))
772 hr = GAMEUX_LoadRegistryString(hRootKey, lpName,
773 sConfigGDFBinaryPath, &lpValue);
775 if(SUCCEEDED(hr))
777 if(lstrcmpW(lpValue, sGDFBinaryPath)==0)
779 /* key found, let's copy instance id and exit */
780 hr = CLSIDFromString(lpName, pInstanceId);
781 found = TRUE;
783 HeapFree(GetProcessHeap(), 0, lpValue);
788 HeapFree(GetProcessHeap(), 0, lpName);
789 RegCloseKey(hRootKey);
792 HeapFree(GetProcessHeap(), 0, lpRegistryPath);
794 if((SUCCEEDED(hr) && !found) || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
795 hr = S_FALSE;
797 return hr;
799 /*******************************************************************************
800 * GameExplorer implementation
803 typedef struct _GameExplorerImpl
805 IGameExplorer IGameExplorer_iface;
806 IGameExplorer2 IGameExplorer2_iface;
807 LONG ref;
808 } GameExplorerImpl;
810 static inline GameExplorerImpl *impl_from_IGameExplorer(IGameExplorer *iface)
812 return CONTAINING_RECORD(iface, GameExplorerImpl, IGameExplorer_iface);
815 static inline GameExplorerImpl *impl_from_IGameExplorer2(IGameExplorer2 *iface)
817 return CONTAINING_RECORD(iface, GameExplorerImpl, IGameExplorer2_iface);
820 static HRESULT WINAPI GameExplorerImpl_QueryInterface(
821 IGameExplorer *iface,
822 REFIID riid,
823 void **ppvObject)
825 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
827 TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
829 *ppvObject = NULL;
831 if(IsEqualGUID(riid, &IID_IUnknown) ||
832 IsEqualGUID(riid, &IID_IGameExplorer))
834 *ppvObject = &This->IGameExplorer_iface;
836 else if(IsEqualGUID(riid, &IID_IGameExplorer2))
838 *ppvObject = &This->IGameExplorer2_iface;
840 else
842 FIXME("interface %s not implemented\n", debugstr_guid(riid));
843 return E_NOINTERFACE;
846 IGameExplorer_AddRef(iface);
847 return S_OK;
850 static ULONG WINAPI GameExplorerImpl_AddRef(IGameExplorer *iface)
852 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
853 LONG ref;
855 ref = InterlockedIncrement(&This->ref);
857 TRACE("(%p): ref=%d\n", This, ref);
858 return ref;
861 static ULONG WINAPI GameExplorerImpl_Release(IGameExplorer *iface)
863 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
864 LONG ref;
866 ref = InterlockedDecrement(&This->ref);
867 TRACE("(%p): ref=%d\n", This, ref);
869 if(ref == 0)
871 TRACE("freeing GameExplorer object\n");
872 HeapFree(GetProcessHeap(), 0, This);
875 return ref;
878 static HRESULT WINAPI GameExplorerImpl_AddGame(
879 IGameExplorer *iface,
880 BSTR bstrGDFBinaryPath,
881 BSTR sGameInstallDirectory,
882 GAME_INSTALL_SCOPE installScope,
883 GUID *pInstanceID)
885 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
886 TRACE("(%p, %s, %s, 0x%x, %s)\n", This, debugstr_w(bstrGDFBinaryPath), debugstr_w(sGameInstallDirectory), installScope, debugstr_guid(pInstanceID));
887 return GAMEUX_RegisterGame(bstrGDFBinaryPath, sGameInstallDirectory, installScope, pInstanceID);
890 static HRESULT WINAPI GameExplorerImpl_RemoveGame(
891 IGameExplorer *iface,
892 GUID instanceID)
894 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
896 TRACE("(%p, %s)\n", This, debugstr_guid(&instanceID));
897 return GAMEUX_RemoveRegistryRecord(&instanceID);
900 static HRESULT WINAPI GameExplorerImpl_UpdateGame(
901 IGameExplorer *iface,
902 GUID instanceID)
904 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
906 TRACE("(%p, %s)\n", This, debugstr_guid(&instanceID));
907 return GAMEUX_UpdateGame(&instanceID);
910 static HRESULT WINAPI GameExplorerImpl_VerifyAccess(
911 IGameExplorer *iface,
912 BSTR sGDFBinaryPath,
913 BOOL *pHasAccess)
915 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
917 FIXME("(%p, %s, %p)\n", This, debugstr_w(sGDFBinaryPath), pHasAccess);
918 *pHasAccess = TRUE;
919 return S_OK;
922 static const struct IGameExplorerVtbl GameExplorerImplVtbl =
924 GameExplorerImpl_QueryInterface,
925 GameExplorerImpl_AddRef,
926 GameExplorerImpl_Release,
927 GameExplorerImpl_AddGame,
928 GameExplorerImpl_RemoveGame,
929 GameExplorerImpl_UpdateGame,
930 GameExplorerImpl_VerifyAccess
934 static HRESULT WINAPI GameExplorer2Impl_QueryInterface(
935 IGameExplorer2 *iface,
936 REFIID riid,
937 void **ppvObject)
939 GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
940 return GameExplorerImpl_QueryInterface(&This->IGameExplorer_iface, riid, ppvObject);
943 static ULONG WINAPI GameExplorer2Impl_AddRef(IGameExplorer2 *iface)
945 GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
946 return GameExplorerImpl_AddRef(&This->IGameExplorer_iface);
949 static ULONG WINAPI GameExplorer2Impl_Release(IGameExplorer2 *iface)
951 GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
952 return GameExplorerImpl_Release(&This->IGameExplorer_iface);
955 static HRESULT WINAPI GameExplorer2Impl_CheckAccess(
956 IGameExplorer2 *iface,
957 LPCWSTR binaryGDFPath,
958 BOOL *pHasAccess)
960 GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
961 FIXME("stub (%p, %s, %p)\n", This, debugstr_w(binaryGDFPath), pHasAccess);
962 return E_NOTIMPL;
965 static HRESULT WINAPI GameExplorer2Impl_InstallGame(
966 IGameExplorer2 *iface,
967 LPCWSTR binaryGDFPath,
968 LPCWSTR installDirectory,
969 GAME_INSTALL_SCOPE installScope)
971 HRESULT hr;
972 GUID instanceId;
973 GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
975 TRACE("(%p, %s, %s, 0x%x)\n", This, debugstr_w(binaryGDFPath), debugstr_w(installDirectory), installScope);
977 if(!binaryGDFPath)
978 return E_INVALIDARG;
980 hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_CURRENT_USER, &instanceId);
982 if(hr == S_FALSE)
983 hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_ALL_USERS, &instanceId);
985 if(hr == S_FALSE)
987 /* if game isn't yet registered, then install it */
988 instanceId = GUID_NULL;
989 hr = GAMEUX_RegisterGame(binaryGDFPath, installDirectory, installScope, &instanceId);
991 else if(hr == S_OK)
992 /* otherwise, update game */
993 hr = GAMEUX_UpdateGame(&instanceId);
995 return hr;
998 static HRESULT WINAPI GameExplorer2Impl_UninstallGame(
999 IGameExplorer2 *iface,
1000 LPCWSTR binaryGDFPath)
1002 HRESULT hr;
1003 GUID instanceId;
1004 GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
1005 TRACE("(%p, %s)\n", This, debugstr_w(binaryGDFPath));
1007 if(!binaryGDFPath)
1008 return E_INVALIDARG;
1010 hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_CURRENT_USER, &instanceId);
1012 if(hr == S_FALSE)
1013 hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_ALL_USERS, &instanceId);
1015 if(hr == S_OK)
1016 hr = GAMEUX_RemoveRegistryRecord(&instanceId);
1018 return hr;
1021 static const struct IGameExplorer2Vtbl GameExplorer2ImplVtbl =
1023 GameExplorer2Impl_QueryInterface,
1024 GameExplorer2Impl_AddRef,
1025 GameExplorer2Impl_Release,
1026 GameExplorer2Impl_InstallGame,
1027 GameExplorer2Impl_UninstallGame,
1028 GameExplorer2Impl_CheckAccess
1032 * Construction routine
1034 HRESULT GameExplorer_create(
1035 IUnknown* pUnkOuter,
1036 IUnknown** ppObj)
1038 GameExplorerImpl *pGameExplorer;
1040 TRACE("(%p, %p)\n", pUnkOuter, ppObj);
1042 pGameExplorer = HeapAlloc(GetProcessHeap(), 0, sizeof(*pGameExplorer));
1044 if(!pGameExplorer)
1045 return E_OUTOFMEMORY;
1047 pGameExplorer->IGameExplorer_iface.lpVtbl = &GameExplorerImplVtbl;
1048 pGameExplorer->IGameExplorer2_iface.lpVtbl = &GameExplorer2ImplVtbl;
1049 pGameExplorer->ref = 1;
1051 *ppObj = (IUnknown*)&pGameExplorer->IGameExplorer_iface;
1053 TRACE("returning iface: %p\n", *ppObj);
1054 return S_OK;