blob: 63bf69d7366f31e2a2f9eba2f29d2006e950e62e [file] [log] [blame]
/*
* Gameux library coclass GameExplorer implementation
*
* Copyright (C) 2010 Mariusz PluciƄski
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define COBJMACROS
#include "config.h"
#include "ole2.h"
#include "sddl.h"
#include "gameux.h"
#include "gameux_private.h"
#include "initguid.h"
#include "msxml2.h"
#include "wine/debug.h"
#include "winreg.h"
WINE_DEFAULT_DEBUG_CHANNEL(gameux);
/*******************************************************************************
* GameUX helper functions
*/
/*******************************************************************************
* GAMEUX_initGameData
*
* Internal helper function.
* Initializes GAME_DATA structure fields with proper values. Should be
* called always before first usage of this structure. Implemented in gameexplorer.c
*
* Parameters:
* GameData [I/O] pointer to structure to initialize
*/
static void GAMEUX_initGameData(struct GAMEUX_GAME_DATA *GameData)
{
GameData->sGDFBinaryPath = NULL;
GameData->sGameInstallDirectory = NULL;
GameData->bstrName = NULL;
GameData->bstrDescription = NULL;
}
/*******************************************************************************
* GAMEUX_uninitGameData
*
* Internal helper function.
* Properly frees all data stored or pointed by fields of GAME_DATA structure.
* Should be called before freeing this structure. Implemented in gameexplorer.c
*
* Parameters:
* GameData [I/O] pointer to structure to uninitialize
*/
static void GAMEUX_uninitGameData(struct GAMEUX_GAME_DATA *GameData)
{
HeapFree(GetProcessHeap(), 0, GameData->sGDFBinaryPath);
HeapFree(GetProcessHeap(), 0, GameData->sGameInstallDirectory);
SysFreeString(GameData->bstrName);
SysFreeString(GameData->bstrDescription);
}
/*******************************************************************************
* GAMEUX_buildGameRegistryPath
*
* Internal helper function. Description available in gameux_private.h file
*/
HRESULT GAMEUX_buildGameRegistryPath(GAME_INSTALL_SCOPE installScope,
LPCGUID gameInstanceId,
LPWSTR* lpRegistryPath)
{
static const WCHAR sGameUxRegistryPath[] = {'S','O','F','T','W','A','R','E','\\',
'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','G','a','m','e','U','X',0};
static const WCHAR sGames[] = {'G','a','m','e','s',0};
static const WCHAR sBackslash[] = {'\\',0};
HRESULT hr = S_OK;
HANDLE hToken = NULL;
PTOKEN_USER pTokenUser = NULL;
DWORD dwLength;
LPWSTR lpSID = NULL;
WCHAR sInstanceId[40];
WCHAR sRegistryPath[8192];
TRACE("(0x%x, %s, %p)\n", installScope, debugstr_guid(gameInstanceId), lpRegistryPath);
/* this will make freeing it easier for user */
*lpRegistryPath = NULL;
lstrcpyW(sRegistryPath, sGameUxRegistryPath);
lstrcatW(sRegistryPath, sBackslash);
if(installScope == GIS_CURRENT_USER)
{
/* build registry path containing user's SID */
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
hr = HRESULT_FROM_WIN32(GetLastError());
if(SUCCEEDED(hr))
{
if(!GetTokenInformation(hToken, TokenUser, NULL, 0, &dwLength) &&
GetLastError()!=ERROR_INSUFFICIENT_BUFFER)
hr = HRESULT_FROM_WIN32(GetLastError());
if(SUCCEEDED(hr))
{
pTokenUser = HeapAlloc(GetProcessHeap(), 0, dwLength);
if(!pTokenUser)
hr = E_OUTOFMEMORY;
}
if(SUCCEEDED(hr))
if(!GetTokenInformation(hToken, TokenUser, (LPVOID)pTokenUser, dwLength, &dwLength))
hr = HRESULT_FROM_WIN32(GetLastError());
if(SUCCEEDED(hr))
if(!ConvertSidToStringSidW(pTokenUser->User.Sid, &lpSID))
hr = HRESULT_FROM_WIN32(GetLastError());
if(SUCCEEDED(hr))
{
lstrcatW(sRegistryPath, lpSID);
LocalFree(lpSID);
}
HeapFree(GetProcessHeap(), 0, pTokenUser);
CloseHandle(hToken);
}
}
else if(installScope == GIS_ALL_USERS)
/* build registry path without SID */
lstrcatW(sRegistryPath, sGames);
else
hr = E_INVALIDARG;
/* put game's instance id on the end of path, only if instance id was given */
if(gameInstanceId)
{
if(SUCCEEDED(hr))
hr = (StringFromGUID2(gameInstanceId, sInstanceId, sizeof(sInstanceId)/sizeof(sInstanceId[0])) ? S_OK : E_FAIL);
if(SUCCEEDED(hr))
{
lstrcatW(sRegistryPath, sBackslash);
lstrcatW(sRegistryPath, sInstanceId);
}
}
if(SUCCEEDED(hr))
{
*lpRegistryPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sRegistryPath)+1)*sizeof(WCHAR));
if(!*lpRegistryPath)
hr = E_OUTOFMEMORY;
}
if(SUCCEEDED(hr))
lstrcpyW(*lpRegistryPath, sRegistryPath);
TRACE("result: 0x%x, path: %s\n", hr, debugstr_w(*lpRegistryPath));
return hr;
}
/*******************************************************************************
* GAMEUX_WriteRegistryRecord
*
* Helper function, writes data associated with game (stored in GAMEUX_GAME_DATA
* structure) into expected place in registry.
*
* Parameters:
* GameData [I] structure with data which will
* be written into registry.
* Proper values of fields installScope
* and guidInstanceId are required
* to create registry key.
*
* Schema of naming registry keys associated with games is available in
* description of _buildGameRegistryPath internal function.
*
* List of registry keys associated with structure fields:
* Key Field in GAMEUX_GAME_DATA structure
* ApplicationId guidApplicationId
* ConfigApplicationPath sGameInstallDirectory
* ConfigGDFBinaryPath sGDFBinaryPath
* Title bstrName
*
*/
static HRESULT GAMEUX_WriteRegistryRecord(struct GAMEUX_GAME_DATA *GameData)
{
static const WCHAR sApplicationId[] =
{'A','p','p','l','i','c','a','t','i','o','n','I','d',0};
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};
static const WCHAR sConfigGDFBinaryPath[] =
{'C','o','n','f','i','g','G','D','F','B','i','n','a','r','y','P','a','t','h',0};
static const WCHAR sTitle[] =
{'T','i','t','l','e',0};
static const WCHAR sDescription[] =
{'D','e','s','c','r','i','p','t','i','o','n',0};
HRESULT hr, hr2;
LPWSTR lpRegistryKey;
HKEY hKey;
WCHAR sGameApplicationId[40];
TRACE("(%p)\n", GameData);
hr = GAMEUX_buildGameRegistryPath(GameData->installScope, &GameData->guidInstanceId, &lpRegistryKey);
if(SUCCEEDED(hr))
hr = (StringFromGUID2(&GameData->guidApplicationId, sGameApplicationId, sizeof(sGameApplicationId)/sizeof(sGameApplicationId[0])) ? S_OK : E_FAIL);
if(SUCCEEDED(hr))
hr = HRESULT_FROM_WIN32(RegCreateKeyExW(HKEY_LOCAL_MACHINE, lpRegistryKey,
0, NULL, 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY, NULL,
&hKey, NULL));
if(SUCCEEDED(hr))
{
/* write game data to registry key */
hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sConfigApplicationPath, 0,
REG_SZ, (LPBYTE)(GameData->sGameInstallDirectory),
(lstrlenW(GameData->sGameInstallDirectory)+1)*sizeof(WCHAR)));
if(SUCCEEDED(hr))
hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sConfigGDFBinaryPath, 0,
REG_SZ, (LPBYTE)(GameData->sGDFBinaryPath),
(lstrlenW(GameData->sGDFBinaryPath)+1)*sizeof(WCHAR)));
if(SUCCEEDED(hr))
hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sApplicationId, 0,
REG_SZ, (LPBYTE)(sGameApplicationId),
(lstrlenW(sGameApplicationId)+1)*sizeof(WCHAR)));
if(SUCCEEDED(hr))
hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sTitle, 0,
REG_SZ, (LPBYTE)(GameData->bstrName),
(lstrlenW(GameData->bstrName)+1)*sizeof(WCHAR)));
if(SUCCEEDED(hr))
hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sDescription, 0,
REG_SZ, (LPBYTE)(GameData->bstrDescription ? GameData->bstrDescription : GameData->bstrName),
(lstrlenW(GameData->bstrDescription ? GameData->bstrDescription : GameData->bstrName)+1)*sizeof(WCHAR)));
RegCloseKey(hKey);
if(FAILED(hr))
{
/* if something failed, remove whole key */
hr2 = RegDeleteKeyExW(HKEY_LOCAL_MACHINE, lpRegistryKey, KEY_WOW64_64KEY, 0);
/* do not overwrite old failure code with new success code */
if(FAILED(hr2))
hr = hr2;
}
}
HeapFree(GetProcessHeap(), 0, lpRegistryKey);
TRACE("returning 0x%x\n", hr);
return hr;
}
/*******************************************************************************
* GAMEUX_ProcessGameDefinitionElement
*
* Helper function, parses single element from Game Definition
*
* Parameters:
* lpXMLElement [I] game definition element
* GameData [O] structure, where parsed
* data will be stored
*/
static HRESULT GAMEUX_ProcessGameDefinitionElement(
IXMLDOMElement *element,
struct GAMEUX_GAME_DATA *GameData)
{
static const WCHAR sName[] =
{'N','a','m','e',0};
static const WCHAR sDescription[] =
{'D','e','s','c','r','i','p','t','i','o','n',0};
HRESULT hr;
BSTR bstrElementName;
TRACE("(%p, %p)\n", element, GameData);
hr = IXMLDOMElement_get_nodeName(element, &bstrElementName);
if(SUCCEEDED(hr))
{
/* check element name */
if(lstrcmpW(bstrElementName, sName) == 0)
hr = IXMLDOMElement_get_text(element, &GameData->bstrName);
else if(lstrcmpW(bstrElementName, sDescription) == 0)
hr = IXMLDOMElement_get_text(element, &GameData->bstrDescription);
else
FIXME("entry %s in Game Definition File not yet supported\n", debugstr_w(bstrElementName));
SysFreeString(bstrElementName);
}
return hr;
}
/*******************************************************************************
* GAMEUX_ParseGameDefinition
*
* Helper function, loads data from given XML element into fields of GAME_DATA
* structure
*
* Parameters:
* lpXMLGameDefinitionElement [I] Game Definition XML element
* GameData [O] structure where data loaded from
* XML element will be stored in
*/
static HRESULT GAMEUX_ParseGameDefinition(IXMLDOMElement *gamedef, struct GAMEUX_GAME_DATA *game_data)
{
static const WCHAR gameidW[] = {'g','a','m','e','I','D',0};
IXMLDOMNodeList *props;
VARIANT var;
HRESULT hr;
BSTR attr;
TRACE("(%p, %p)\n", gamedef, game_data);
attr = SysAllocString(gameidW);
if (!attr)
return E_OUTOFMEMORY;
hr = IXMLDOMElement_getAttribute(gamedef, attr, &var);
SysFreeString(attr);
if (SUCCEEDED(hr))
{
hr = CLSIDFromString(V_BSTR(&var), &game_data->guidApplicationId);
VariantClear(&var);
}
if (SUCCEEDED(hr))
hr = IXMLDOMElement_get_childNodes(gamedef, &props);
if (FAILED(hr))
return hr;
do
{
IXMLDOMNode *prop;
hr = IXMLDOMNodeList_nextNode(props, &prop);
if (hr == S_OK)
{
IXMLDOMElement *element;
hr = IXMLDOMNode_QueryInterface(prop, &IID_IXMLDOMElement, (void**)&element);
if (hr == S_OK)
{
hr = GAMEUX_ProcessGameDefinitionElement(element, game_data);
IXMLDOMElement_Release(element);
}
IXMLDOMNode_Release(prop);
}
}
while (hr == S_OK);
IXMLDOMNodeList_Release(props);
return FAILED(hr) ? hr : S_OK;
}
struct parse_gdf_thread_param
{
struct GAMEUX_GAME_DATA *GameData;
HRESULT hr;
};
/*******************************************************************************
* GAMEUX_ParseGDFBinary
*
* Helper function, loads given binary and parses embed GDF if there's any.
*
* Parameters:
* GameData [I/O] Structure with game's data. Content of field
* sGDFBinaryPath defines path to binary, from
* which embed GDF will be loaded. Data from
* GDF will be stored in other fields of this
* structure.
*/
static DWORD WINAPI GAMEUX_ParseGDFBinary(void *thread_param)
{
struct parse_gdf_thread_param *ctx = thread_param;
struct GAMEUX_GAME_DATA *GameData = ctx->GameData;
static const WCHAR sRes[] = {'r','e','s',':','/','/',0};
static const WCHAR sDATA[] = {'D','A','T','A',0};
static const WCHAR sSlash[] = {'/',0};
HRESULT hr = S_OK;
WCHAR sResourcePath[MAX_PATH];
VARIANT variant;
VARIANT_BOOL isSuccessful;
IXMLDOMDocument *document;
IXMLDOMNode *gdNode;
IXMLDOMElement *root, *gdElement;
TRACE("(%p)->sGDFBinaryPath = %s\n", GameData, debugstr_w(GameData->sGDFBinaryPath));
/* prepare path to GDF, using res:// prefix */
lstrcpyW(sResourcePath, sRes);
lstrcatW(sResourcePath, GameData->sGDFBinaryPath);
lstrcatW(sResourcePath, sSlash);
lstrcatW(sResourcePath, sDATA);
lstrcatW(sResourcePath, sSlash);
lstrcatW(sResourcePath, ID_GDF_XML_STR);
CoInitialize(NULL);
hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
&IID_IXMLDOMDocument, (void**)&document);
if(SUCCEEDED(hr))
{
/* load GDF into MSXML */
V_VT(&variant) = VT_BSTR;
V_BSTR(&variant) = SysAllocString(sResourcePath);
if(!V_BSTR(&variant))
hr = E_OUTOFMEMORY;
if(SUCCEEDED(hr))
{
hr = IXMLDOMDocument_load(document, variant, &isSuccessful);
if(hr == S_FALSE || isSuccessful == VARIANT_FALSE)
hr = E_FAIL;
}
SysFreeString(V_BSTR(&variant));
if(SUCCEEDED(hr))
{
hr = IXMLDOMDocument_get_documentElement(document, &root);
if(hr == S_FALSE)
hr = E_FAIL;
}
if(SUCCEEDED(hr))
{
hr = IXMLDOMElement_get_firstChild(root, &gdNode);
if(hr == S_FALSE)
hr = E_FAIL;
if(SUCCEEDED(hr))
{
hr = IXMLDOMNode_QueryInterface(gdNode, &IID_IXMLDOMElement, (LPVOID*)&gdElement);
if(SUCCEEDED(hr))
{
hr = GAMEUX_ParseGameDefinition(gdElement, GameData);
IXMLDOMElement_Release(gdElement);
}
IXMLDOMNode_Release(gdNode);
}
IXMLDOMElement_Release(root);
}
IXMLDOMDocument_Release(document);
}
CoUninitialize();
ctx->hr = hr;
return 0;
}
/*******************************************************************
* GAMEUX_RemoveRegistryRecord
*
* Helper function, removes registry key associated with given game instance
*/
static HRESULT GAMEUX_RemoveRegistryRecord(GUID* pInstanceID)
{
HRESULT hr;
LPWSTR lpRegistryPath = NULL;
TRACE("(%s)\n", debugstr_guid(pInstanceID));
/* first, check is game installed for all users */
hr = GAMEUX_buildGameRegistryPath(GIS_ALL_USERS, pInstanceID, &lpRegistryPath);
if(SUCCEEDED(hr))
hr = HRESULT_FROM_WIN32(RegDeleteKeyExW(HKEY_LOCAL_MACHINE, lpRegistryPath, KEY_WOW64_64KEY, 0));
HeapFree(GetProcessHeap(), 0, lpRegistryPath);
/* if not, check current user */
if(FAILED(hr))
{
hr = GAMEUX_buildGameRegistryPath(GIS_CURRENT_USER, pInstanceID, &lpRegistryPath);
if(SUCCEEDED(hr))
hr = HRESULT_FROM_WIN32(RegDeleteKeyExW(HKEY_LOCAL_MACHINE, lpRegistryPath, KEY_WOW64_64KEY, 0));
HeapFree(GetProcessHeap(), 0, lpRegistryPath);
}
return hr;
}
/*******************************************************************************
* GAMEUX_RegisterGame
*
* Internal helper function. Registers game associated with given GDF binary in
* Game Explorer. Implemented in gameexplorer.c
*
* Parameters:
* sGDFBinaryPath [I] path to binary containing GDF file in
* resources
* sGameInstallDirectory [I] path to directory, where game installed
* its files.
* installScope [I] scope of game installation
* pInstanceID [I/O] pointer to game instance identifier.
* If pointing to GUID_NULL, then new
* identifier will be generated automatically
* and returned via this parameter
*/
static HRESULT GAMEUX_RegisterGame(LPCWSTR sGDFBinaryPath,
LPCWSTR sGameInstallDirectory,
GAME_INSTALL_SCOPE installScope,
GUID *pInstanceID)
{
HRESULT hr = S_OK;
struct GAMEUX_GAME_DATA GameData;
TRACE("(%s, %s, 0x%x, %s)\n", debugstr_w(sGDFBinaryPath), debugstr_w(sGameInstallDirectory), installScope, debugstr_guid(pInstanceID));
GAMEUX_initGameData(&GameData);
GameData.sGDFBinaryPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sGDFBinaryPath)+1)*sizeof(WCHAR));
lstrcpyW(GameData.sGDFBinaryPath, sGDFBinaryPath);
GameData.sGameInstallDirectory = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sGameInstallDirectory)+1)*sizeof(WCHAR));
lstrcpyW(GameData.sGameInstallDirectory, sGameInstallDirectory);
GameData.installScope = installScope;
/* generate GUID if it was not provided by user */
if(IsEqualGUID(pInstanceID, &GUID_NULL))
hr = CoCreateGuid(pInstanceID);
GameData.guidInstanceId = *pInstanceID;
/* load data from GDF binary */
if(SUCCEEDED(hr))
{
struct parse_gdf_thread_param thread_param;
HANDLE thread;
DWORD ret;
thread_param.GameData = &GameData;
if(!(thread = CreateThread(NULL, 0, GAMEUX_ParseGDFBinary, &thread_param, 0, &ret)))
{
ERR("Failed to create thread.\n");
hr = E_FAIL;
goto done;
}
ret = WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
if(ret != WAIT_OBJECT_0)
{
ERR("Wait failed (%#x).\n", ret);
hr = E_FAIL;
goto done;
}
hr = thread_param.hr;
}
/* save data to registry */
if(SUCCEEDED(hr))
hr = GAMEUX_WriteRegistryRecord(&GameData);
done:
GAMEUX_uninitGameData(&GameData);
TRACE("returning 0x%08x\n", hr);
return hr;
}
/*******************************************************************************
* GAMEUX_IsGameKeyExist
*
* Helper function, checks if game's registry ath exists in given scope
*
* Parameters:
* installScope [I] scope to search game in
* InstanceID [I] game instance identifier
* lpRegistryPath [O] place to store address of registry path to
* the game. It is filled only if key exists.
* It must be freed by HeapFree(GetProcessHeap(), 0, ...)
*
* Returns:
* S_OK key was found properly
* S_FALSE key does not exists
*
*/
static HRESULT GAMEUX_IsGameKeyExist(GAME_INSTALL_SCOPE installScope,
LPCGUID InstanceID,
LPWSTR* lpRegistryPath) {
HRESULT hr;
HKEY hKey;
hr = GAMEUX_buildGameRegistryPath(installScope, InstanceID, lpRegistryPath);
if(SUCCEEDED(hr))
hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE, *lpRegistryPath,
0, KEY_WOW64_64KEY, &hKey));
if(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
hr = S_FALSE;
if(hr == S_OK)
RegCloseKey(hKey);
else
{
/* if the key does not exist or another error occurred, do not return the path */
HeapFree(GetProcessHeap(), 0, *lpRegistryPath);
*lpRegistryPath = NULL;
}
return hr;
}
/*******************************************************************************
* GAMEUX_LoadRegistryString
*
* Helper function, loads string from registry value and allocates buffer for it
*/
static HRESULT GAMEUX_LoadRegistryString(HKEY hRootKey,
LPCWSTR lpRegistryKey,
LPCWSTR lpRegistryValue,
LPWSTR* lpValue)
{
HRESULT hr;
DWORD dwSize;
*lpValue = NULL;
hr = HRESULT_FROM_WIN32(RegGetValueW(hRootKey, lpRegistryKey, lpRegistryValue,
RRF_RT_REG_SZ, NULL, NULL, &dwSize));
if(SUCCEEDED(hr))
{
*lpValue = HeapAlloc(GetProcessHeap(), 0, dwSize);
if(!*lpValue)
hr = E_OUTOFMEMORY;
}
if(SUCCEEDED(hr))
hr = HRESULT_FROM_WIN32(RegGetValueW(hRootKey, lpRegistryKey, lpRegistryValue,
RRF_RT_REG_SZ, NULL, *lpValue, &dwSize));
return hr;
}
/*******************************************************************************
* GAMEUX_UpdateGame
*
* Helper function, updates stored data about game with given InstanceID
*/
static HRESULT GAMEUX_UpdateGame(LPGUID InstanceID) {
static const WCHAR sConfigGDFBinaryPath[] = {'C','o','n','f','i','g','G','D','F','B','i','n','a','r','y','P','a','t','h',0};
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};
HRESULT hr;
GAME_INSTALL_SCOPE installScope;
LPWSTR lpRegistryPath;
LPWSTR lpGDFBinaryPath;
TRACE("(%s)\n", debugstr_guid(InstanceID));
/* first, check is game exists in CURRENT_USER scope */
installScope = GIS_CURRENT_USER;
hr = GAMEUX_IsGameKeyExist(installScope, InstanceID, &lpRegistryPath);
if(hr == S_FALSE)
{
/* game not found in CURRENT_USER scope, let's check in ALL_USERS */
installScope = GIS_ALL_USERS;
hr = GAMEUX_IsGameKeyExist(installScope, InstanceID, &lpRegistryPath);
}
if(hr == S_FALSE)
/* still not found? let's inform user that game does not exists */
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
if(SUCCEEDED(hr))
{
WCHAR *lpGameInstallDirectory = NULL;
/* game found, its registry path is in lpRegistryPath and install
* scope in installScope */
TRACE("game found in registry (path %s), updating\n", debugstr_w(lpRegistryPath));
/* first, read required data about game */
hr = GAMEUX_LoadRegistryString(HKEY_LOCAL_MACHINE, lpRegistryPath,
sConfigGDFBinaryPath, &lpGDFBinaryPath);
if(SUCCEEDED(hr))
hr = GAMEUX_LoadRegistryString(HKEY_LOCAL_MACHINE, lpRegistryPath,
sConfigApplicationPath, &lpGameInstallDirectory);
/* now remove currently existing registry key */
if(SUCCEEDED(hr))
hr = GAMEUX_RemoveRegistryRecord(InstanceID);
/* and add it again, it will cause in reparsing of whole GDF */
if(SUCCEEDED(hr))
hr = GAMEUX_RegisterGame(lpGDFBinaryPath, lpGameInstallDirectory,
installScope, InstanceID);
HeapFree(GetProcessHeap(), 0, lpGDFBinaryPath);
HeapFree(GetProcessHeap(), 0, lpGameInstallDirectory);
}
HeapFree(GetProcessHeap(), 0, lpRegistryPath);
TRACE("returning 0x%x\n", hr);
return hr;
}
/*******************************************************************************
* GAMEUX_FindGameInstanceId
*
* Internal helper function. Description available in gameux_private.h file
*/
HRESULT GAMEUX_FindGameInstanceId(
LPCWSTR sGDFBinaryPath,
GAME_INSTALL_SCOPE installScope,
GUID* pInstanceId)
{
static const WCHAR sConfigGDFBinaryPath[] =
{'C','o','n','f','i','g','G','D','F','B','i','n','a','r','y','P','a','t','h',0};
HRESULT hr;
BOOL found = FALSE;
LPWSTR lpRegistryPath = NULL;
HKEY hRootKey;
DWORD dwSubKeys, dwSubKeyLen, dwMaxSubKeyLen, i;
LPWSTR lpName = NULL, lpValue = NULL;
hr = GAMEUX_buildGameRegistryPath(installScope, NULL, &lpRegistryPath);
if(SUCCEEDED(hr))
/* enumerate all subkeys of received one and search them for value "ConfigGGDFBinaryPath" */
hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE,
lpRegistryPath, 0, KEY_READ | KEY_WOW64_64KEY, &hRootKey));
if(SUCCEEDED(hr))
{
hr = HRESULT_FROM_WIN32(RegQueryInfoKeyW(hRootKey, NULL, NULL, NULL,
&dwSubKeys, &dwMaxSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL));
if(SUCCEEDED(hr))
{
++dwMaxSubKeyLen; /* for string terminator */
lpName = HeapAlloc(GetProcessHeap(), 0, dwMaxSubKeyLen*sizeof(WCHAR));
if(!lpName) hr = E_OUTOFMEMORY;
}
if(SUCCEEDED(hr))
{
for(i=0; i<dwSubKeys && !found; ++i)
{
dwSubKeyLen = dwMaxSubKeyLen;
hr = HRESULT_FROM_WIN32(RegEnumKeyExW(hRootKey, i, lpName, &dwSubKeyLen,
NULL, NULL, NULL, NULL));
if(SUCCEEDED(hr))
hr = GAMEUX_LoadRegistryString(hRootKey, lpName,
sConfigGDFBinaryPath, &lpValue);
if(SUCCEEDED(hr))
{
if(lstrcmpW(lpValue, sGDFBinaryPath)==0)
{
/* key found, let's copy instance id and exit */
hr = CLSIDFromString(lpName, pInstanceId);
found = TRUE;
}
HeapFree(GetProcessHeap(), 0, lpValue);
}
}
}
HeapFree(GetProcessHeap(), 0, lpName);
RegCloseKey(hRootKey);
}
HeapFree(GetProcessHeap(), 0, lpRegistryPath);
if((SUCCEEDED(hr) && !found) || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
hr = S_FALSE;
return hr;
}
/*******************************************************************************
* GameExplorer implementation
*/
typedef struct _GameExplorerImpl
{
IGameExplorer IGameExplorer_iface;
IGameExplorer2 IGameExplorer2_iface;
LONG ref;
} GameExplorerImpl;
static inline GameExplorerImpl *impl_from_IGameExplorer(IGameExplorer *iface)
{
return CONTAINING_RECORD(iface, GameExplorerImpl, IGameExplorer_iface);
}
static inline GameExplorerImpl *impl_from_IGameExplorer2(IGameExplorer2 *iface)
{
return CONTAINING_RECORD(iface, GameExplorerImpl, IGameExplorer2_iface);
}
static HRESULT WINAPI GameExplorerImpl_QueryInterface(
IGameExplorer *iface,
REFIID riid,
void **ppvObject)
{
GameExplorerImpl *This = impl_from_IGameExplorer(iface);
TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
*ppvObject = NULL;
if(IsEqualGUID(riid, &IID_IUnknown) ||
IsEqualGUID(riid, &IID_IGameExplorer))
{
*ppvObject = &This->IGameExplorer_iface;
}
else if(IsEqualGUID(riid, &IID_IGameExplorer2))
{
*ppvObject = &This->IGameExplorer2_iface;
}
else
{
FIXME("interface %s not implemented\n", debugstr_guid(riid));
return E_NOINTERFACE;
}
IGameExplorer_AddRef(iface);
return S_OK;
}
static ULONG WINAPI GameExplorerImpl_AddRef(IGameExplorer *iface)
{
GameExplorerImpl *This = impl_from_IGameExplorer(iface);
LONG ref;
ref = InterlockedIncrement(&This->ref);
TRACE("(%p): ref=%d\n", This, ref);
return ref;
}
static ULONG WINAPI GameExplorerImpl_Release(IGameExplorer *iface)
{
GameExplorerImpl *This = impl_from_IGameExplorer(iface);
LONG ref;
ref = InterlockedDecrement(&This->ref);
TRACE("(%p): ref=%d\n", This, ref);
if(ref == 0)
{
TRACE("freeing GameExplorer object\n");
HeapFree(GetProcessHeap(), 0, This);
}
return ref;
}
static HRESULT WINAPI GameExplorerImpl_AddGame(
IGameExplorer *iface,
BSTR bstrGDFBinaryPath,
BSTR sGameInstallDirectory,
GAME_INSTALL_SCOPE installScope,
GUID *pInstanceID)
{
GameExplorerImpl *This = impl_from_IGameExplorer(iface);
TRACE("(%p, %s, %s, 0x%x, %s)\n", This, debugstr_w(bstrGDFBinaryPath), debugstr_w(sGameInstallDirectory), installScope, debugstr_guid(pInstanceID));
return GAMEUX_RegisterGame(bstrGDFBinaryPath, sGameInstallDirectory, installScope, pInstanceID);
}
static HRESULT WINAPI GameExplorerImpl_RemoveGame(
IGameExplorer *iface,
GUID instanceID)
{
GameExplorerImpl *This = impl_from_IGameExplorer(iface);
TRACE("(%p, %s)\n", This, debugstr_guid(&instanceID));
return GAMEUX_RemoveRegistryRecord(&instanceID);
}
static HRESULT WINAPI GameExplorerImpl_UpdateGame(
IGameExplorer *iface,
GUID instanceID)
{
GameExplorerImpl *This = impl_from_IGameExplorer(iface);
TRACE("(%p, %s)\n", This, debugstr_guid(&instanceID));
return GAMEUX_UpdateGame(&instanceID);
}
static HRESULT WINAPI GameExplorerImpl_VerifyAccess(
IGameExplorer *iface,
BSTR sGDFBinaryPath,
BOOL *pHasAccess)
{
GameExplorerImpl *This = impl_from_IGameExplorer(iface);
FIXME("(%p, %s, %p)\n", This, debugstr_w(sGDFBinaryPath), pHasAccess);
*pHasAccess = TRUE;
return S_OK;
}
static const struct IGameExplorerVtbl GameExplorerImplVtbl =
{
GameExplorerImpl_QueryInterface,
GameExplorerImpl_AddRef,
GameExplorerImpl_Release,
GameExplorerImpl_AddGame,
GameExplorerImpl_RemoveGame,
GameExplorerImpl_UpdateGame,
GameExplorerImpl_VerifyAccess
};
static HRESULT WINAPI GameExplorer2Impl_QueryInterface(
IGameExplorer2 *iface,
REFIID riid,
void **ppvObject)
{
GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
return GameExplorerImpl_QueryInterface(&This->IGameExplorer_iface, riid, ppvObject);
}
static ULONG WINAPI GameExplorer2Impl_AddRef(IGameExplorer2 *iface)
{
GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
return GameExplorerImpl_AddRef(&This->IGameExplorer_iface);
}
static ULONG WINAPI GameExplorer2Impl_Release(IGameExplorer2 *iface)
{
GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
return GameExplorerImpl_Release(&This->IGameExplorer_iface);
}
static HRESULT WINAPI GameExplorer2Impl_CheckAccess(
IGameExplorer2 *iface,
LPCWSTR binaryGDFPath,
BOOL *pHasAccess)
{
GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
FIXME("stub (%p, %s, %p)\n", This, debugstr_w(binaryGDFPath), pHasAccess);
return E_NOTIMPL;
}
static HRESULT WINAPI GameExplorer2Impl_InstallGame(
IGameExplorer2 *iface,
LPCWSTR binaryGDFPath,
LPCWSTR installDirectory,
GAME_INSTALL_SCOPE installScope)
{
HRESULT hr;
GUID instanceId;
GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
TRACE("(%p, %s, %s, 0x%x)\n", This, debugstr_w(binaryGDFPath), debugstr_w(installDirectory), installScope);
if(!binaryGDFPath)
return E_INVALIDARG;
hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_CURRENT_USER, &instanceId);
if(hr == S_FALSE)
hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_ALL_USERS, &instanceId);
if(hr == S_FALSE)
{
/* if game isn't yet registered, then install it */
instanceId = GUID_NULL;
hr = GAMEUX_RegisterGame(binaryGDFPath, installDirectory, installScope, &instanceId);
}
else if(hr == S_OK)
/* otherwise, update game */
hr = GAMEUX_UpdateGame(&instanceId);
return hr;
}
static HRESULT WINAPI GameExplorer2Impl_UninstallGame(
IGameExplorer2 *iface,
LPCWSTR binaryGDFPath)
{
HRESULT hr;
GUID instanceId;
GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
TRACE("(%p, %s)\n", This, debugstr_w(binaryGDFPath));
if(!binaryGDFPath)
return E_INVALIDARG;
hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_CURRENT_USER, &instanceId);
if(hr == S_FALSE)
hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_ALL_USERS, &instanceId);
if(hr == S_OK)
hr = GAMEUX_RemoveRegistryRecord(&instanceId);
return hr;
}
static const struct IGameExplorer2Vtbl GameExplorer2ImplVtbl =
{
GameExplorer2Impl_QueryInterface,
GameExplorer2Impl_AddRef,
GameExplorer2Impl_Release,
GameExplorer2Impl_InstallGame,
GameExplorer2Impl_UninstallGame,
GameExplorer2Impl_CheckAccess
};
/*
* Construction routine
*/
HRESULT GameExplorer_create(
IUnknown* pUnkOuter,
IUnknown** ppObj)
{
GameExplorerImpl *pGameExplorer;
TRACE("(%p, %p)\n", pUnkOuter, ppObj);
pGameExplorer = HeapAlloc(GetProcessHeap(), 0, sizeof(*pGameExplorer));
if(!pGameExplorer)
return E_OUTOFMEMORY;
pGameExplorer->IGameExplorer_iface.lpVtbl = &GameExplorerImplVtbl;
pGameExplorer->IGameExplorer2_iface.lpVtbl = &GameExplorer2ImplVtbl;
pGameExplorer->ref = 1;
*ppObj = (IUnknown*)&pGameExplorer->IGameExplorer_iface;
TRACE("returning iface: %p\n", *ppObj);
return S_OK;
}