webservices: Don't allocate an XML buffer until it is needed.
[wine.git] / dlls / gameux / gamestatistics.c
blobb9e913e4376f963a3a9138fb8645002d7c504101
1 /*
2 * Gameux library coclass GameStatistics 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 "winreg.h"
26 #include "msxml2.h"
27 #include "shlwapi.h"
28 #include "shlobj.h"
30 #include "gameux.h"
31 #include "gameux_private.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(gameux);
38 * constant definitions
40 #define MAX_CATEGORY_LENGTH 60
41 #define MAX_NAME_LENGTH 30
42 #define MAX_VALUE_LENGTH 30
43 #define MAX_CATEGORIES 10
44 #define MAX_STATS_PER_CATEGORY 10
45 /*******************************************************************************
46 * Game statistics helper components
48 /*******************************************************************************
49 * struct GAMEUX_STATS
51 * set of structures for containing game's data
53 struct GAMEUX_STATS_STAT
55 WCHAR sName[MAX_NAME_LENGTH+1];
56 WCHAR sValue[MAX_VALUE_LENGTH+1];
58 struct GAMEUX_STATS_CATEGORY
60 WCHAR sName[MAX_CATEGORY_LENGTH+1];
61 struct GAMEUX_STATS_STAT stats[MAX_STATS_PER_CATEGORY];
63 struct GAMEUX_STATS
65 WCHAR sStatsFile[MAX_PATH];
66 struct GAMEUX_STATS_CATEGORY categories[MAX_CATEGORIES];
68 /*******************************************************************************
69 * GAMEUX_createStatsDirectory
71 * Helper function, creates directory to store game statistics
73 * Parameters
74 * path [I] path to game statistics file.
75 * base directory of this file will
76 * be created if it doesn't exists
78 static HRESULT GAMEUX_createStatsDirectory(LPCWSTR lpFilePath)
80 HRESULT hr;
81 WCHAR lpDirectoryPath[MAX_PATH];
82 LPCWSTR lpEnd;
84 lpEnd = StrRChrW(lpFilePath, NULL, '\\');
85 lstrcpynW(lpDirectoryPath, lpFilePath, lpEnd-lpFilePath+1);
87 hr = HRESULT_FROM_WIN32(SHCreateDirectoryExW(NULL, lpDirectoryPath, NULL));
89 if(hr == HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) ||
90 hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS))
91 hr = S_FALSE;
93 return hr;
95 /*******************************************************************
96 * GAMEUX_updateStatisticsFile
98 * Helper function updating data stored in statistics file
100 * Parameters:
101 * data [I] pointer to struct containing
102 * statistics data
104 static HRESULT GAMEUX_updateStatisticsFile(struct GAMEUX_STATS *stats)
106 static const WCHAR sStatistics[] = {'S','t','a','t','i','s','t','i','c','s',0};
107 static const WCHAR sCategory[] = {'C','a','t','e','g','o','r','y',0};
108 static const WCHAR sIndex[] = {'I','n','d','e','x',0};
109 static const WCHAR sStatistic[] = {'S','t','a','t','i','s','t','i','c',0};
110 static const WCHAR sName[] = {'N','a','m','e',0};
111 static const WCHAR sValue[] = {'V','a','l','u','e',0};
113 HRESULT hr = S_OK;
114 IXMLDOMDocument *document;
115 IXMLDOMElement *root, *statisticsElement;
116 IXMLDOMNode *categoryNode, *statisticsNode;
117 VARIANT vStatsFilePath, vValue;
118 BSTR bstrStatistics = NULL, bstrCategory = NULL, bstrIndex = NULL,
119 bstrStatistic = NULL, bstrName = NULL, bstrValue = NULL;
120 int i, j;
122 TRACE("(%p)\n", stats);
124 V_VT(&vStatsFilePath) = VT_BSTR;
125 V_BSTR(&vStatsFilePath) = SysAllocString(stats->sStatsFile);
126 if(!V_BSTR(&vStatsFilePath))
127 hr = E_OUTOFMEMORY;
129 if(SUCCEEDED(hr))
130 hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
131 &IID_IXMLDOMDocument, (void**)&document);
133 if(SUCCEEDED(hr))
135 bstrStatistics = SysAllocString(sStatistics);
136 if(!bstrStatistics)
137 hr = E_OUTOFMEMORY;
140 if(SUCCEEDED(hr))
141 hr = IXMLDOMDocument_createElement(document, bstrStatistics, &root);
143 if(SUCCEEDED(hr))
145 bstrCategory = SysAllocString(sCategory);
146 if(!bstrCategory)
147 hr = E_OUTOFMEMORY;
150 if(SUCCEEDED(hr))
152 bstrIndex = SysAllocString(sIndex);
153 if(!bstrIndex)
154 hr = E_OUTOFMEMORY;
157 if(SUCCEEDED(hr))
159 bstrStatistic = SysAllocString(sStatistic);
160 if(!bstrStatistic)
161 hr = E_OUTOFMEMORY;
164 if(SUCCEEDED(hr))
166 bstrName = SysAllocString(sName);
167 if(!bstrName)
168 hr = E_OUTOFMEMORY;
171 if(SUCCEEDED(hr))
173 bstrValue = SysAllocString(sValue);
174 if(!bstrValue)
175 hr = E_OUTOFMEMORY;
178 if(SUCCEEDED(hr))
179 for(i=0; i<MAX_CATEGORIES; ++i)
181 IXMLDOMElement *categoryElement = NULL;
183 if(!stats->categories[i].sName[0])
184 continue;
186 V_VT(&vValue) = VT_INT;
187 V_INT(&vValue) = NODE_ELEMENT;
189 hr = IXMLDOMDocument_createNode(document, vValue, bstrCategory, NULL, &categoryNode);
191 if(SUCCEEDED(hr))
192 hr = IXMLDOMNode_QueryInterface(categoryNode, &IID_IXMLDOMElement, (void**)&categoryElement);
194 V_INT(&vValue) = i;
195 if(SUCCEEDED(hr))
196 hr = IXMLDOMElement_setAttribute(categoryElement, bstrIndex, vValue);
198 if(SUCCEEDED(hr))
200 V_VT(&vValue) = VT_BSTR;
201 V_BSTR(&vValue) = SysAllocString(stats->categories[i].sName);
202 if(!V_BSTR(&vValue))
203 hr = E_OUTOFMEMORY;
206 if(SUCCEEDED(hr))
208 TRACE("storing category %d: %s\n", i, debugstr_w(V_BSTR(&vValue)));
209 hr = IXMLDOMElement_setAttribute(categoryElement, bstrName, vValue);
212 if (categoryElement)
213 IXMLDOMElement_Release(categoryElement);
215 SysFreeString(V_BSTR(&vValue));
217 if(SUCCEEDED(hr))
219 for(j=0; j<MAX_STATS_PER_CATEGORY; ++j)
221 if(!stats->categories[i].stats[j].sName[0])
222 continue;
224 V_VT(&vValue) = VT_INT;
225 V_INT(&vValue) = NODE_ELEMENT;
227 hr = IXMLDOMDocument_createNode(document, vValue, bstrStatistic, NULL, &statisticsNode);
229 if(SUCCEEDED(hr))
230 hr = IXMLDOMNode_QueryInterface(statisticsNode, &IID_IXMLDOMElement, (LPVOID*)&statisticsElement);
232 V_INT(&vValue) = j;
233 if(SUCCEEDED(hr))
234 hr = IXMLDOMElement_setAttribute(statisticsElement, bstrIndex, vValue);
236 if(SUCCEEDED(hr))
238 V_VT(&vValue) = VT_BSTR;
239 V_BSTR(&vValue) = SysAllocString(stats->categories[i].stats[j].sName);
240 if(!V_BSTR(&vValue))
241 hr = E_OUTOFMEMORY;
244 if(SUCCEEDED(hr))
246 TRACE(" storing statistic %d: name: %s\n", j, debugstr_w(V_BSTR(&vValue)));
247 hr = IXMLDOMElement_setAttribute(statisticsElement, bstrName, vValue);
248 SysFreeString(V_BSTR(&vValue));
251 if(SUCCEEDED(hr))
253 V_VT(&vValue) = VT_BSTR;
254 V_BSTR(&vValue) = SysAllocString(stats->categories[i].stats[j].sValue);
255 if(!V_BSTR(&vValue))
256 hr = E_OUTOFMEMORY;
259 if(SUCCEEDED(hr))
261 TRACE(" storing statistic %d: name: %s\n", j, debugstr_w(V_BSTR(&vValue)));
262 hr = IXMLDOMElement_setAttribute(statisticsElement, bstrValue, vValue);
263 SysFreeString(V_BSTR(&vValue));
266 if(SUCCEEDED(hr))
267 hr = IXMLDOMNode_appendChild(categoryNode, statisticsNode, NULL);
269 IXMLDOMElement_Release(statisticsElement);
270 IXMLDOMNode_Release(statisticsNode);
274 if(SUCCEEDED(hr))
275 hr = IXMLDOMElement_appendChild(root, categoryNode, &categoryNode);
277 IXMLDOMNode_Release(categoryNode);
279 if(FAILED(hr))
280 break;
283 if(SUCCEEDED(hr))
284 hr = IXMLDOMDocument_putref_documentElement(document, root);
286 IXMLDOMElement_Release(root);
288 TRACE("saving game statistics in %s file\n", debugstr_w(stats->sStatsFile));
289 if(SUCCEEDED(hr))
290 hr = GAMEUX_createStatsDirectory(stats->sStatsFile);
292 if(SUCCEEDED(hr))
293 hr = IXMLDOMDocument_save(document, vStatsFilePath);
295 IXMLDOMDocument_Release(document);
297 SysFreeString(bstrValue);
298 SysFreeString(bstrName);
299 SysFreeString(bstrStatistic);
300 SysFreeString(bstrIndex);
301 SysFreeString(bstrCategory);
302 SysFreeString(bstrStatistics);
303 SysFreeString(V_BSTR(&vStatsFilePath));
304 TRACE("ret=0x%x\n", hr);
305 return hr;
307 /*******************************************************************************
308 * GAMEUX_buildStatisticsFilePath
309 * Creates path to file containing statistics of game with given id.
311 * Parameters:
312 * lpApplicationId [I] application id of game,
313 * as string
314 * lpStatisticsFile [O] array where path will be
315 * stored. Its size must be
316 * at least MAX_PATH
318 static HRESULT GAMEUX_buildStatisticsFilePath(
319 LPCWSTR lpApplicationId,
320 LPWSTR lpStatisticsFile)
322 static const WCHAR sBackslash[] = {'\\',0};
323 static const WCHAR sStatisticsDir[] = {'\\','M','i','c','r','o','s','o','f','t',
324 '\\','W','i','n','d','o','w','s','\\','G','a','m','e','E','x','p',
325 'l','o','r','e','r','\\','G','a','m','e','S','t','a','t','i','s',
326 't','i','c','s','\\',0};
327 static const WCHAR sDotGamestats[] = {'.','g','a','m','e','s','t','a','t','s',0};
329 HRESULT hr;
331 hr = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, lpStatisticsFile);
333 if(SUCCEEDED(hr))
335 lstrcatW(lpStatisticsFile, sStatisticsDir);
336 lstrcatW(lpStatisticsFile, lpApplicationId);
337 lstrcatW(lpStatisticsFile, sBackslash);
338 lstrcatW(lpStatisticsFile, lpApplicationId);
339 lstrcatW(lpStatisticsFile, sDotGamestats);
342 return hr;
344 /*******************************************************************************
345 * GAMEUX_getAppIdFromGDFPath
347 * Loads application identifier associated with given GDF binary.
348 * Routine reads identifier from registry, so will fail if game
349 * is not registered.
351 * Parameters:
352 * GDFBinaryPath [I] path to gdf binary
353 * lpApplicationId [O] place to store application id.
354 * must be at least 49 characters
355 * to store guid and termination 0
357 static HRESULT GAMEUX_getAppIdFromGDFPath(
358 LPCWSTR GDFBinaryPath,
359 LPWSTR lpApplicationId)
361 static const WCHAR sApplicationId[] =
362 {'A','p','p','l','i','c','a','t','i','o','n','I','d',0};
364 HRESULT hr;
365 GAME_INSTALL_SCOPE installScope;
366 GUID instanceId;
367 LPWSTR lpRegistryPath = NULL;
368 HKEY hKey;
369 DWORD dwLength = 49*sizeof(WCHAR);/* place for GUID */
371 TRACE("(%s, %p)\n", debugstr_w(GDFBinaryPath), lpApplicationId);
373 if(!GDFBinaryPath)
374 return E_INVALIDARG;
376 installScope = GIS_CURRENT_USER;
377 hr = GAMEUX_FindGameInstanceId(GDFBinaryPath, installScope, &instanceId);
379 if(hr == S_FALSE)
381 installScope = GIS_ALL_USERS;
382 hr = GAMEUX_FindGameInstanceId(GDFBinaryPath, installScope, &instanceId);
385 if(hr == S_FALSE)
386 /* game not registered, so statistics cannot be used */
387 hr = E_FAIL;
389 if(SUCCEEDED(hr))
390 /* game is registered, let's read its application id from registry */
391 hr = GAMEUX_buildGameRegistryPath(installScope, &instanceId, &lpRegistryPath);
393 if(SUCCEEDED(hr)) {
394 hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE,
395 lpRegistryPath, 0, KEY_READ | KEY_WOW64_64KEY, &hKey));
396 if(SUCCEEDED(hr)) {
397 hr = HRESULT_FROM_WIN32(RegGetValueW(hKey,
398 NULL, sApplicationId, RRF_RT_REG_SZ,
399 NULL, lpApplicationId, &dwLength));
400 RegCloseKey(hKey);
404 HeapFree(GetProcessHeap(), 0, lpRegistryPath);
406 TRACE("found app id: %s, return: %#x\n", debugstr_w(lpApplicationId), hr);
407 return hr;
409 /*******************************************************************
410 * GAMEUX_loadGameStatisticsFromFile
411 * Helper function, loads game statistics from file and stores them
412 * in the structure.
414 * Parameters:
415 * data [I/O] structure containing file name to
416 * load and data fields to store data in
418 static HRESULT GAMEUX_loadStatisticsFromFile(struct GAMEUX_STATS *data)
420 static const WCHAR sStatistics[] = {'S','t','a','t','i','s','t','i','c','s',0};
421 static const WCHAR sCategory[] = {'C','a','t','e','g','o','r','y',0};
422 static const WCHAR sIndex[] = {'I','n','d','e','x',0};
423 static const WCHAR sStatistic[] = {'S','t','a','t','i','s','t','i','c',0};
424 static const WCHAR sName[] = {'N','a','m','e',0};
425 static const WCHAR sValue[] = {'V','a','l','u','e',0};
427 HRESULT hr = S_OK;
428 IXMLDOMDocument *document = NULL;
429 IXMLDOMElement *root = NULL, *categoryElement, *statisticElement;
430 IXMLDOMNode *categoryNode, *statisticNode;
431 IXMLDOMNodeList *rootChildren = NULL, *categoryChildren;
432 VARIANT vStatsFilePath, vValue;
433 BSTR bstrStatistics = NULL, bstrCategory = NULL, bstrIndex = NULL,
434 bstrStatistic = NULL, bstrName = NULL, bstrValue = NULL;
435 VARIANT_BOOL isSuccessful = VARIANT_FALSE;
436 int i, j;
438 TRACE("(%p)\n", data);
440 V_VT(&vStatsFilePath) = VT_BSTR;
441 V_BSTR(&vStatsFilePath) = SysAllocString(data->sStatsFile);
442 if(!V_BSTR(&vStatsFilePath))
443 hr = E_OUTOFMEMORY;
445 if(SUCCEEDED(hr))
446 hr = CoCreateInstance(&CLSID_DOMDocument30, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, (void**)&document);
448 if(SUCCEEDED(hr))
450 bstrStatistics = SysAllocString(sStatistics);
451 if(!bstrStatistics)
452 hr = E_OUTOFMEMORY;
455 if(SUCCEEDED(hr))
457 bstrCategory = SysAllocString(sCategory);
458 if(!bstrCategory)
459 hr = E_OUTOFMEMORY;
462 if(SUCCEEDED(hr))
464 bstrIndex = SysAllocString(sIndex);
465 if(!bstrIndex)
466 hr = E_OUTOFMEMORY;
469 if(SUCCEEDED(hr))
471 bstrStatistic = SysAllocString(sStatistic);
472 if(!bstrStatistic)
473 hr = E_OUTOFMEMORY;
476 if(SUCCEEDED(hr))
478 bstrName = SysAllocString(sName);
479 if(!bstrName)
480 hr = E_OUTOFMEMORY;
483 if(SUCCEEDED(hr))
485 bstrValue = SysAllocString(sValue);
486 if(!bstrValue)
487 hr = E_OUTOFMEMORY;
490 if(SUCCEEDED(hr))
491 hr = IXMLDOMDocument_load(document, vStatsFilePath, &isSuccessful);
493 if(hr == S_OK && isSuccessful != VARIANT_TRUE)
494 hr = S_FALSE;
496 if( hr == S_OK )
497 hr = IXMLDOMDocument_get_documentElement(document, &root);
499 if(hr == S_OK)
500 hr = IXMLDOMElement_get_childNodes(root, &rootChildren);
502 if(hr == S_OK)
504 hr = S_OK;
505 while(hr == S_OK)
507 hr = IXMLDOMNodeList_nextNode(rootChildren, &categoryNode);
509 if(hr == S_OK)
511 hr = IXMLDOMNode_QueryInterface(categoryNode, &IID_IXMLDOMElement, (LPVOID*)&categoryElement);
513 if(SUCCEEDED(hr))
515 hr = IXMLDOMElement_getAttribute(categoryElement, bstrIndex, &vValue);
516 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
517 hr = E_FAIL;
519 if(SUCCEEDED(hr))
521 i = StrToIntW(V_BSTR(&vValue));
522 hr = IXMLDOMElement_getAttribute(categoryElement, bstrName, &vValue);
523 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
524 hr = E_FAIL;
527 if(SUCCEEDED(hr))
529 lstrcpynW(data->categories[i].sName, V_BSTR(&vValue), MAX_CATEGORY_LENGTH);
530 TRACE("category %d name %s\n", i, debugstr_w(data->categories[i].sName));
531 hr = IXMLDOMElement_get_childNodes(categoryElement, &categoryChildren);
534 if(SUCCEEDED(hr))
536 hr = S_OK;
537 while(hr == S_OK)
539 hr = IXMLDOMNodeList_nextNode(categoryChildren, &statisticNode);
541 if(hr == S_OK)
543 hr = IXMLDOMNode_QueryInterface(statisticNode, &IID_IXMLDOMElement, (LPVOID*)&statisticElement);
545 if(SUCCEEDED(hr))
547 hr = IXMLDOMElement_getAttribute(statisticElement, bstrIndex, &vValue);
548 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
549 hr = E_FAIL;
551 if(SUCCEEDED(hr))
553 j = StrToIntW(V_BSTR(&vValue));
554 hr = IXMLDOMElement_getAttribute(statisticElement, bstrName, &vValue);
555 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
556 hr = E_FAIL;
559 if(SUCCEEDED(hr))
561 lstrcpynW(data->categories[i].stats[j].sName, V_BSTR(&vValue), MAX_NAME_LENGTH);
562 hr = IXMLDOMElement_getAttribute(statisticElement, bstrValue, &vValue);
563 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
564 hr = E_FAIL;
567 if(SUCCEEDED(hr))
569 lstrcpynW(data->categories[i].stats[j].sValue, V_BSTR(&vValue), MAX_VALUE_LENGTH);
570 TRACE("statistic %d name %s value %s\n", j,
571 debugstr_w(data->categories[i].stats[j].sName),
572 debugstr_w(data->categories[i].stats[j].sValue));
574 IXMLDOMElement_Release(statisticElement);
577 IXMLDOMNode_Release(statisticNode);
581 IXMLDOMNodeList_Release(categoryChildren);
583 if(SUCCEEDED(hr))
584 hr = S_OK;
586 IXMLDOMElement_Release(categoryElement);
589 IXMLDOMNode_Release(categoryNode);
592 if(SUCCEEDED(hr))
593 hr = S_OK;
596 if(rootChildren) IXMLDOMNodeList_Release(rootChildren);
597 if(root) IXMLDOMElement_Release(root);
598 if(document) IXMLDOMDocument_Release(document);
600 SysFreeString(bstrValue);
601 SysFreeString(bstrName);
602 SysFreeString(bstrStatistic);
603 SysFreeString(bstrIndex);
604 SysFreeString(bstrCategory);
605 SysFreeString(bstrStatistics);
606 SysFreeString(V_BSTR(&vStatsFilePath));
607 return hr;
609 /*******************************************************************
610 * GAMEUX_loadGameStatistics
612 * Helper function which loads game statistics associated with game
613 * into interface's internal structures
615 * Parameters:
616 * pStats [O] structure which will receive data
617 * sGameId [I] application instance Id, stored as string
618 * to avoid additional conversions
619 * openType [I] allowed ways of opening statistics
620 * pOpenResult [O] way used to open statistics
623 static HRESULT GAMEUX_loadGameStatistics(struct GAMEUX_STATS *pStats,
624 LPWSTR sGameId,
625 GAMESTATS_OPEN_TYPE openType,
626 GAMESTATS_OPEN_RESULT* pOpenResult)
628 HRESULT hr;
629 TRACE("(%p, %s, %d, %p)\n", pStats, debugstr_w(sGameId), openType, pOpenResult);
631 hr = GAMEUX_buildStatisticsFilePath(sGameId, pStats->sStatsFile);
632 if (FAILED(hr)) return hr;
634 hr = GAMEUX_loadStatisticsFromFile(pStats);
635 TRACE("ldstats finished, res: %#x\n", hr);
636 if(hr == S_OK)
638 *pOpenResult = GAMESTATS_OPEN_OPENED;
640 else if(hr == S_FALSE && openType == GAMESTATS_OPEN_OPENORCREATE) /* file does not exist */
642 /* create new statistics, not yet connected with file */
643 ZeroMemory(pStats->categories, sizeof(pStats->categories));
644 *pOpenResult = GAMESTATS_OPEN_CREATED;
645 hr = S_OK;
647 else
648 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
650 TRACE("openResult=%#x ret=%#x\n", *pOpenResult, hr);
651 return hr;
653 /*******************************************************************
654 * IGameStatistics implementation
656 typedef struct _GameStatisticsImpl
658 IGameStatistics IGameStatistics_iface;
659 LONG ref;
660 struct GAMEUX_STATS stats;
661 } GameStatisticsImpl;
663 static inline GameStatisticsImpl *impl_from_IGameStatistics( IGameStatistics *iface )
665 return CONTAINING_RECORD(iface, GameStatisticsImpl, IGameStatistics_iface);
668 static HRESULT WINAPI GameStatisticsImpl_QueryInterface(
669 IGameStatistics *iface,
670 REFIID riid,
671 void **ppvObject)
673 GameStatisticsImpl *This = impl_from_IGameStatistics( iface );
675 TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppvObject );
677 *ppvObject = NULL;
679 if ( IsEqualGUID( riid, &IID_IUnknown ) ||
680 IsEqualGUID( riid, &IID_IGameStatistics ) )
682 *ppvObject = iface;
684 else
686 FIXME("interface %s not implemented\n", debugstr_guid(riid));
687 return E_NOINTERFACE;
690 IGameStatistics_AddRef( iface );
691 return S_OK;
694 static ULONG WINAPI GameStatisticsImpl_AddRef(IGameStatistics *iface)
696 GameStatisticsImpl *This = impl_from_IGameStatistics( iface );
697 LONG ref;
699 ref = InterlockedIncrement(&This->ref);
701 TRACE("(%p): ref=%d\n", This, ref);
702 return ref;
705 static ULONG WINAPI GameStatisticsImpl_Release(IGameStatistics *iface)
707 GameStatisticsImpl *This = impl_from_IGameStatistics( iface );
708 LONG ref;
710 ref = InterlockedDecrement( &This->ref );
711 TRACE("(%p): ref=%d\n", This, ref);
713 if ( ref == 0 )
715 TRACE("freeing IGameStatistics\n");
716 HeapFree( GetProcessHeap(), 0, This );
719 return ref;
722 static HRESULT WINAPI GameStatisticsImpl_GetMaxCategoryLength(
723 IGameStatistics *iface,
724 UINT *cch)
726 TRACE("(%p, %p)\n", iface, cch);
727 if(!cch)
728 return E_INVALIDARG;
730 *cch = MAX_CATEGORY_LENGTH;
731 return S_OK;
734 static HRESULT WINAPI GameStatisticsImpl_GetMaxNameLength(
735 IGameStatistics *iface,
736 UINT *cch)
738 TRACE("(%p, %p)\n", iface, cch);
739 if(!cch)
740 return E_INVALIDARG;
742 *cch = MAX_NAME_LENGTH;
743 return S_OK;
746 static HRESULT WINAPI GameStatisticsImpl_GetMaxValueLength(
747 IGameStatistics *iface,
748 UINT *cch)
750 TRACE("(%p, %p)\n", iface, cch);
751 if(!cch)
752 return E_INVALIDARG;
754 *cch = MAX_VALUE_LENGTH;
755 return S_OK;
758 static HRESULT WINAPI GameStatisticsImpl_GetMaxCategories(
759 IGameStatistics *iface,
760 WORD *pMax)
762 TRACE("(%p, %p)\n", iface, pMax);
763 if(!pMax)
764 return E_INVALIDARG;
766 *pMax = MAX_CATEGORIES;
767 return S_OK;
770 static HRESULT WINAPI GameStatisticsImpl_GetMaxStatsPerCategory(
771 IGameStatistics *iface,
772 WORD *pMax)
774 TRACE("(%p, %p)\n", iface, pMax);
775 if(!pMax)
776 return E_INVALIDARG;
778 *pMax = MAX_STATS_PER_CATEGORY;
779 return S_OK;
782 static HRESULT WINAPI GameStatisticsImpl_SetCategoryTitle(
783 IGameStatistics *iface,
784 WORD categoryIndex,
785 LPCWSTR title)
787 HRESULT hr = S_OK;
788 DWORD dwLength;
789 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
791 TRACE("(%p, %d, %s)\n", This, categoryIndex, debugstr_w(title));
793 if(!title || categoryIndex >= MAX_CATEGORIES)
794 return E_INVALIDARG;
796 dwLength = lstrlenW(title);
798 if(dwLength > MAX_CATEGORY_LENGTH)
800 hr = S_FALSE;
801 dwLength = MAX_CATEGORY_LENGTH;
804 lstrcpynW(This->stats.categories[categoryIndex].sName,
805 title, dwLength+1);
807 return hr;
810 static HRESULT WINAPI GameStatisticsImpl_GetCategoryTitle(
811 IGameStatistics *iface,
812 WORD categoryIndex,
813 LPWSTR *pTitle)
815 HRESULT hr = S_OK;
816 LONG nLength;
817 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
819 TRACE("%p, %d, %p\n", This, categoryIndex, pTitle);
821 if(!pTitle)
822 return E_INVALIDARG;
823 *pTitle = NULL;
825 if (categoryIndex >= MAX_CATEGORIES)
826 hr = E_INVALIDARG;
828 if(SUCCEEDED(hr))
830 nLength = lstrlenW(This->stats.categories[categoryIndex].sName);
831 if(nLength != 0)
833 *pTitle = CoTaskMemAlloc(sizeof(WCHAR)*(nLength+1));
834 lstrcpyW(*pTitle, This->stats.categories[categoryIndex].sName);
838 return hr;
841 static HRESULT WINAPI GameStatisticsImpl_GetStatistic(
842 IGameStatistics *iface,
843 WORD categoryIndex,
844 WORD statIndex,
845 LPWSTR *pName,
846 LPWSTR *pValue)
848 HRESULT hr = S_OK;
849 LONG nLength;
850 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
852 TRACE("%p, %d,%d, %p, %p\n", This, categoryIndex, statIndex, pName, pValue);
854 if(!pName || !pValue)
855 return E_INVALIDARG;
857 *pName = NULL;
858 *pValue = NULL;
860 if(categoryIndex >= MAX_CATEGORIES || statIndex >= MAX_STATS_PER_CATEGORY)
861 hr = E_INVALIDARG;
863 if(SUCCEEDED(hr))
865 nLength = lstrlenW(This->stats.categories[categoryIndex].stats[statIndex].sName);
866 if(nLength != 0)
868 *pName = CoTaskMemAlloc(sizeof(WCHAR)*(nLength+1));
869 if(!(*pName))
870 hr = E_OUTOFMEMORY;
871 else
872 lstrcpyW(*pName, This->stats.categories[categoryIndex].stats[statIndex].sName);
876 if(SUCCEEDED(hr))
878 nLength = lstrlenW(This->stats.categories[categoryIndex].stats[statIndex].sValue);
879 if(nLength != 0)
881 *pValue = CoTaskMemAlloc(sizeof(WCHAR)*(nLength+1));
882 if(!(*pValue))
883 hr = E_OUTOFMEMORY;
884 else
885 lstrcpyW(*pValue, This->stats.categories[categoryIndex].stats[statIndex].sValue);
889 TRACE("returning pair; %s => %s\n", debugstr_w(*pName), debugstr_w(*pValue));
890 return hr;
893 static HRESULT WINAPI GameStatisticsImpl_SetStatistic(
894 IGameStatistics *iface,
895 WORD categoryIndex,
896 WORD statIndex,
897 LPCWSTR name,
898 LPCWSTR value)
900 HRESULT hr = S_OK;
901 DWORD dwNameLen, dwValueLen;
902 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
904 TRACE("(%p, %d, %d, %s, %s)\n", This, categoryIndex, statIndex,
905 debugstr_w(name), debugstr_w(value));
907 if(!name)
908 return S_FALSE;
910 if(categoryIndex >= MAX_CATEGORIES || statIndex >= MAX_STATS_PER_CATEGORY)
911 return E_INVALIDARG;
913 dwNameLen = lstrlenW(name);
915 if(dwNameLen > MAX_NAME_LENGTH)
917 hr = S_FALSE;
918 dwNameLen = MAX_NAME_LENGTH;
921 lstrcpynW(This->stats.categories[categoryIndex].stats[statIndex].sName,
922 name, dwNameLen+1);
924 if(value)
926 dwValueLen = lstrlenW(value);
928 if(dwValueLen > MAX_VALUE_LENGTH)
930 hr = S_FALSE;
931 dwValueLen = MAX_VALUE_LENGTH;
934 lstrcpynW(This->stats.categories[categoryIndex].stats[statIndex].sValue,
935 value, dwValueLen+1);
937 else
938 /* Windows allows passing NULL as value */
939 This->stats.categories[categoryIndex].stats[statIndex].sValue[0] = 0;
941 return hr;
944 static HRESULT WINAPI GameStatisticsImpl_Save(
945 IGameStatistics *iface,
946 BOOL trackChanges)
948 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
950 TRACE("(%p, %d)\n", This, trackChanges);
952 if(trackChanges)
953 FIXME("tracking changes not yet implemented\n");
955 return GAMEUX_updateStatisticsFile(&This->stats);
958 static HRESULT WINAPI GameStatisticsImpl_SetLastPlayedCategory(
959 IGameStatistics *iface,
960 UINT categoryIndex)
962 FIXME("stub\n");
963 return E_NOTIMPL;
966 static HRESULT WINAPI GameStatisticsImpl_GetLastPlayedCategory(
967 IGameStatistics *iface,
968 UINT *pCategoryIndex)
970 FIXME("stub\n");
971 return E_NOTIMPL;
974 static const struct IGameStatisticsVtbl GameStatisticsImplVtbl =
976 GameStatisticsImpl_QueryInterface,
977 GameStatisticsImpl_AddRef,
978 GameStatisticsImpl_Release,
979 GameStatisticsImpl_GetMaxCategoryLength,
980 GameStatisticsImpl_GetMaxNameLength,
981 GameStatisticsImpl_GetMaxValueLength,
982 GameStatisticsImpl_GetMaxCategories,
983 GameStatisticsImpl_GetMaxStatsPerCategory,
984 GameStatisticsImpl_SetCategoryTitle,
985 GameStatisticsImpl_GetCategoryTitle,
986 GameStatisticsImpl_GetStatistic,
987 GameStatisticsImpl_SetStatistic,
988 GameStatisticsImpl_Save,
989 GameStatisticsImpl_SetLastPlayedCategory,
990 GameStatisticsImpl_GetLastPlayedCategory
994 static HRESULT create_IGameStatistics(GameStatisticsImpl** ppStats)
996 TRACE("(%p)\n", ppStats);
998 *ppStats = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(**ppStats));
999 if(!(*ppStats))
1000 return E_OUTOFMEMORY;
1002 (*ppStats)->IGameStatistics_iface.lpVtbl = &GameStatisticsImplVtbl;
1003 (*ppStats)->ref = 1;
1005 TRACE("returning coclass: %p\n", *ppStats);
1006 return S_OK;
1009 /*******************************************************************************
1010 * IGameStatisticsMgr implementation
1012 typedef struct _GameStatisticsMgrImpl
1014 IGameStatisticsMgr IGameStatisticsMgr_iface;
1015 LONG ref;
1016 } GameStatisticsMgrImpl;
1018 static inline GameStatisticsMgrImpl *impl_from_IGameStatisticsMgr( IGameStatisticsMgr *iface )
1020 return CONTAINING_RECORD(iface, GameStatisticsMgrImpl, IGameStatisticsMgr_iface);
1024 static HRESULT WINAPI GameStatisticsMgrImpl_QueryInterface(
1025 IGameStatisticsMgr *iface,
1026 REFIID riid,
1027 void **ppvObject)
1029 GameStatisticsMgrImpl *This = impl_from_IGameStatisticsMgr( iface );
1031 TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppvObject );
1033 *ppvObject = NULL;
1035 if(IsEqualGUID(riid, &IID_IUnknown) ||
1036 IsEqualGUID(riid, &IID_IGameStatisticsMgr) )
1038 *ppvObject = iface;
1040 else
1042 FIXME("interface %s not implemented\n", debugstr_guid(riid));
1043 return E_NOINTERFACE;
1046 IGameStatisticsMgr_AddRef( iface );
1047 return S_OK;
1050 static ULONG WINAPI GameStatisticsMgrImpl_AddRef(IGameStatisticsMgr *iface)
1052 GameStatisticsMgrImpl *This = impl_from_IGameStatisticsMgr( iface );
1053 LONG ref;
1055 ref = InterlockedIncrement(&This->ref);
1057 TRACE("(%p): ref=%d\n", This, ref);
1058 return ref;
1061 static ULONG WINAPI GameStatisticsMgrImpl_Release(IGameStatisticsMgr *iface)
1063 GameStatisticsMgrImpl *This = impl_from_IGameStatisticsMgr( iface );
1064 LONG ref;
1066 ref = InterlockedDecrement(&This->ref);
1067 TRACE("(%p): ref=%d\n", This, ref);
1069 if ( ref == 0 )
1071 TRACE("freeing GameStatistics object\n");
1072 HeapFree( GetProcessHeap(), 0, This);
1075 return ref;
1078 static HRESULT STDMETHODCALLTYPE GameStatisticsMgrImpl_GetGameStatistics(
1079 IGameStatisticsMgr* iface,
1080 LPCWSTR GDFBinaryPath,
1081 GAMESTATS_OPEN_TYPE openType,
1082 GAMESTATS_OPEN_RESULT *pOpenResult,
1083 IGameStatistics **ppiStats)
1085 HRESULT hr;
1086 WCHAR lpApplicationId[49];
1087 GameStatisticsImpl *statisticsImpl = NULL;
1088 IGameStatistics *output_iface;
1090 TRACE("(%p, %s, 0x%x, %p, %p)\n", iface, debugstr_w(GDFBinaryPath), openType, pOpenResult, ppiStats);
1092 hr = GAMEUX_getAppIdFromGDFPath(GDFBinaryPath, lpApplicationId);
1094 if(SUCCEEDED(hr))
1095 hr = create_IGameStatistics(&statisticsImpl);
1097 if(SUCCEEDED(hr))
1099 output_iface = &statisticsImpl->IGameStatistics_iface;
1100 hr = GAMEUX_buildStatisticsFilePath(lpApplicationId, statisticsImpl->stats.sStatsFile);
1103 if(SUCCEEDED(hr))
1104 hr = GAMEUX_loadGameStatistics(&statisticsImpl->stats, lpApplicationId, openType, pOpenResult);
1106 if(SUCCEEDED(hr))
1107 *ppiStats = output_iface;
1108 else
1110 HeapFree(GetProcessHeap(), 0, statisticsImpl);
1111 *ppiStats = NULL;
1114 return hr;
1117 static HRESULT STDMETHODCALLTYPE GameStatisticsMgrImpl_RemoveGameStatistics(
1118 IGameStatisticsMgr* iface,
1119 LPCWSTR GDFBinaryPath)
1121 HRESULT hr;
1122 WCHAR lpApplicationId[49];
1123 WCHAR sStatsFile[MAX_PATH];
1125 TRACE("(%p, %s)\n", iface, debugstr_w(GDFBinaryPath));
1127 hr = GAMEUX_getAppIdFromGDFPath(GDFBinaryPath, lpApplicationId);
1129 if(SUCCEEDED(hr))
1130 hr = GAMEUX_buildStatisticsFilePath(lpApplicationId, sStatsFile);
1132 if(SUCCEEDED(hr))
1133 hr = DeleteFileW(sStatsFile) ? S_OK : HRESULT_FROM_WIN32(GetLastError());
1135 return hr;
1138 static const struct IGameStatisticsMgrVtbl GameStatisticsMgrImplVtbl =
1140 GameStatisticsMgrImpl_QueryInterface,
1141 GameStatisticsMgrImpl_AddRef,
1142 GameStatisticsMgrImpl_Release,
1143 GameStatisticsMgrImpl_GetGameStatistics,
1144 GameStatisticsMgrImpl_RemoveGameStatistics,
1147 HRESULT GameStatistics_create(
1148 IUnknown *pUnkOuter,
1149 IUnknown **ppObj)
1151 GameStatisticsMgrImpl *pGameStatistics;
1153 TRACE("(%p, %p)\n", pUnkOuter, ppObj);
1155 pGameStatistics = HeapAlloc( GetProcessHeap(), 0, sizeof (*pGameStatistics) );
1157 if( !pGameStatistics )
1158 return E_OUTOFMEMORY;
1160 pGameStatistics->IGameStatisticsMgr_iface.lpVtbl = &GameStatisticsMgrImplVtbl;
1161 pGameStatistics->ref = 1;
1163 *ppObj = (IUnknown*)&pGameStatistics->IGameStatisticsMgr_iface;
1165 TRACE("returning iface %p\n", *ppObj);
1166 return S_OK;