gameux: Move SysFreeString to success branches (Coverity).
[wine.git] / dlls / gameux / gamestatistics.c
blob619c6161dc5cade6da45b07b1f30fc150b675e0a
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, *categoryElement, *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 if(lstrlenW(stats->categories[i].sName)==0)
182 continue;
184 V_VT(&vValue) = VT_INT;
185 V_INT(&vValue) = NODE_ELEMENT;
187 hr = IXMLDOMDocument_createNode(document, vValue, bstrCategory, NULL, &categoryNode);
189 if(SUCCEEDED(hr))
190 hr = IXMLDOMNode_QueryInterface(categoryNode, &IID_IXMLDOMElement, (LPVOID*)&categoryElement);
192 V_INT(&vValue) = i;
193 if(SUCCEEDED(hr))
194 hr = IXMLDOMElement_setAttribute(categoryElement, bstrIndex, vValue);
196 if(SUCCEEDED(hr))
198 V_VT(&vValue) = VT_BSTR;
199 V_BSTR(&vValue) = SysAllocString(stats->categories[i].sName);
200 if(!V_BSTR(&vValue))
201 hr = E_OUTOFMEMORY;
204 if(SUCCEEDED(hr))
206 TRACE("storing category %d: %s\n", i, debugstr_w(V_BSTR(&vValue)));
207 hr = IXMLDOMElement_setAttribute(categoryElement, bstrName, vValue);
210 SysFreeString(V_BSTR(&vValue));
212 if(SUCCEEDED(hr))
214 for(j=0; j<MAX_STATS_PER_CATEGORY; ++j)
216 if(lstrlenW(stats->categories[i].stats[j].sName)==0)
217 continue;
219 V_VT(&vValue) = VT_INT;
220 V_INT(&vValue) = NODE_ELEMENT;
222 hr = IXMLDOMDocument_createNode(document, vValue, bstrStatistic, NULL, &statisticsNode);
224 if(SUCCEEDED(hr))
225 hr = IXMLDOMNode_QueryInterface(statisticsNode, &IID_IXMLDOMElement, (LPVOID*)&statisticsElement);
227 V_INT(&vValue) = j;
228 if(SUCCEEDED(hr))
229 hr = IXMLDOMElement_setAttribute(statisticsElement, bstrIndex, vValue);
231 if(SUCCEEDED(hr))
233 V_VT(&vValue) = VT_BSTR;
234 V_BSTR(&vValue) = SysAllocString(stats->categories[i].stats[j].sName);
235 if(!V_BSTR(&vValue))
236 hr = E_OUTOFMEMORY;
239 if(SUCCEEDED(hr))
241 TRACE(" storing statistic %d: name: %s\n", j, debugstr_w(V_BSTR(&vValue)));
242 hr = IXMLDOMElement_setAttribute(statisticsElement, bstrName, vValue);
243 SysFreeString(V_BSTR(&vValue));
246 if(SUCCEEDED(hr))
248 V_VT(&vValue) = VT_BSTR;
249 V_BSTR(&vValue) = SysAllocString(stats->categories[i].stats[j].sValue);
250 if(!V_BSTR(&vValue))
251 hr = E_OUTOFMEMORY;
254 if(SUCCEEDED(hr))
256 TRACE(" storing statistic %d: name: %s\n", j, debugstr_w(V_BSTR(&vValue)));
257 hr = IXMLDOMElement_setAttribute(statisticsElement, bstrValue, vValue);
258 SysFreeString(V_BSTR(&vValue));
261 if(SUCCEEDED(hr))
262 hr = IXMLDOMNode_appendChild(categoryNode, statisticsNode, &statisticsNode);
264 IXMLDOMElement_Release(statisticsElement);
265 IXMLDOMNode_Release(statisticsNode);
269 if(SUCCEEDED(hr))
270 hr = IXMLDOMElement_appendChild(root, categoryNode, &categoryNode);
272 IXMLDOMElement_Release(categoryElement);
273 IXMLDOMNode_Release(categoryNode);
275 if(FAILED(hr))
276 break;
279 if(SUCCEEDED(hr))
280 hr = IXMLDOMDocument_putref_documentElement(document, root);
282 IXMLDOMElement_Release(root);
284 TRACE("saving game statistics in %s file\n", debugstr_w(stats->sStatsFile));
285 if(SUCCEEDED(hr))
286 hr = GAMEUX_createStatsDirectory(stats->sStatsFile);
288 if(SUCCEEDED(hr))
289 hr = IXMLDOMDocument_save(document, vStatsFilePath);
291 IXMLDOMDocument_Release(document);
293 SysFreeString(bstrValue);
294 SysFreeString(bstrName);
295 SysFreeString(bstrStatistic);
296 SysFreeString(bstrIndex);
297 SysFreeString(bstrCategory);
298 SysFreeString(bstrStatistics);
299 SysFreeString(V_BSTR(&vStatsFilePath));
300 TRACE("ret=0x%x\n", hr);
301 return hr;
303 /*******************************************************************************
304 * GAMEUX_buildStatisticsFilePath
305 * Creates path to file containing statistics of game with given id.
307 * Parameters:
308 * lpApplicationId [I] application id of game,
309 * as string
310 * lpStatisticsFile [O] array where path will be
311 * stored. Its size must be
312 * at least MAX_PATH
314 static HRESULT GAMEUX_buildStatisticsFilePath(
315 LPCWSTR lpApplicationId,
316 LPWSTR lpStatisticsFile)
318 static const WCHAR sBackslash[] = {'\\',0};
319 static const WCHAR sStatisticsDir[] = {'\\','M','i','c','r','o','s','o','f','t',
320 '\\','W','i','n','d','o','w','s','\\','G','a','m','e','E','x','p',
321 'l','o','r','e','r','\\','G','a','m','e','S','t','a','t','i','s',
322 't','i','c','s','\\',0};
323 static const WCHAR sDotGamestats[] = {'.','g','a','m','e','s','t','a','t','s',0};
325 HRESULT hr;
327 hr = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, lpStatisticsFile);
329 if(SUCCEEDED(hr))
331 lstrcatW(lpStatisticsFile, sStatisticsDir);
332 lstrcatW(lpStatisticsFile, lpApplicationId);
333 lstrcatW(lpStatisticsFile, sBackslash);
334 lstrcatW(lpStatisticsFile, lpApplicationId);
335 lstrcatW(lpStatisticsFile, sDotGamestats);
338 return hr;
340 /*******************************************************************************
341 * GAMEUX_getAppIdFromGDFPath
343 * Loads application identifier associated with given GDF binary.
344 * Routine reads identifier from registry, so will fail if game
345 * is not registered.
347 * Parameters:
348 * GDFBinaryPath [I] path to gdf binary
349 * lpApplicationId [O] place to store application id.
350 * must be at least 49 characters
351 * to store guid and termination 0
353 static HRESULT GAMEUX_getAppIdFromGDFPath(
354 LPCWSTR GDFBinaryPath,
355 LPWSTR lpApplicationId)
357 static const WCHAR sApplicationId[] =
358 {'A','p','p','l','i','c','a','t','i','o','n','I','d',0};
360 HRESULT hr;
361 GAME_INSTALL_SCOPE installScope;
362 GUID instanceId;
363 LPWSTR lpRegistryPath = NULL;
364 HKEY hKey;
365 DWORD dwLength = 49*sizeof(WCHAR);/* place for GUID */
367 TRACE("(%s, %p)\n", debugstr_w(GDFBinaryPath), lpApplicationId);
369 if(!GDFBinaryPath)
370 return E_INVALIDARG;
372 installScope = GIS_CURRENT_USER;
373 hr = GAMEUX_FindGameInstanceId(GDFBinaryPath, installScope, &instanceId);
375 if(hr == S_FALSE)
377 installScope = GIS_ALL_USERS;
378 hr = GAMEUX_FindGameInstanceId(GDFBinaryPath, installScope, &instanceId);
381 if(hr == S_FALSE)
382 /* game not registered, so statistics cannot be used */
383 hr = E_FAIL;
385 if(SUCCEEDED(hr))
386 /* game is registered, let's read it's application id from registry */
387 hr = GAMEUX_buildGameRegistryPath(installScope, &instanceId, &lpRegistryPath);
389 if(SUCCEEDED(hr)) {
390 hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE,
391 lpRegistryPath, 0, KEY_READ | KEY_WOW64_64KEY, &hKey));
392 if(SUCCEEDED(hr)) {
393 hr = HRESULT_FROM_WIN32(RegGetValueW(hKey,
394 NULL, sApplicationId, RRF_RT_REG_SZ,
395 NULL, lpApplicationId, &dwLength));
396 RegCloseKey(hKey);
400 HeapFree(GetProcessHeap(), 0, lpRegistryPath);
402 TRACE("found app id: %s, return: %#x\n", debugstr_w(lpApplicationId), hr);
403 return hr;
405 /*******************************************************************
406 * GAMEUX_loadGameStatisticsFromFile
407 * Helper function, loads game statistics from file and stores them
408 * in the structure.
410 * Parameters:
411 * data [I/O] structure containing file name to
412 * load and data fields to store data in
414 static HRESULT GAMEUX_loadStatisticsFromFile(struct GAMEUX_STATS *data)
416 static const WCHAR sStatistics[] = {'S','t','a','t','i','s','t','i','c','s',0};
417 static const WCHAR sCategory[] = {'C','a','t','e','g','o','r','y',0};
418 static const WCHAR sIndex[] = {'I','n','d','e','x',0};
419 static const WCHAR sStatistic[] = {'S','t','a','t','i','s','t','i','c',0};
420 static const WCHAR sName[] = {'N','a','m','e',0};
421 static const WCHAR sValue[] = {'V','a','l','u','e',0};
423 HRESULT hr = S_OK;
424 IXMLDOMDocument *document = NULL;
425 IXMLDOMElement *root = NULL, *categoryElement, *statisticElement;
426 IXMLDOMNode *categoryNode, *statisticNode;
427 IXMLDOMNodeList *rootChildren = NULL, *categoryChildren;
428 VARIANT vStatsFilePath, vValue;
429 BSTR bstrStatistics = NULL, bstrCategory = NULL, bstrIndex = NULL,
430 bstrStatistic = NULL, bstrName = NULL, bstrValue = NULL;
431 VARIANT_BOOL isSuccessful = VARIANT_FALSE;
432 int i, j;
434 TRACE("(%p)\n", data);
436 V_VT(&vStatsFilePath) = VT_BSTR;
437 V_BSTR(&vStatsFilePath) = SysAllocString(data->sStatsFile);
438 if(!V_BSTR(&vStatsFilePath))
439 hr = E_OUTOFMEMORY;
441 if(SUCCEEDED(hr))
442 hr = CoCreateInstance(&CLSID_DOMDocument30, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, (void**)&document);
444 if(SUCCEEDED(hr))
446 bstrStatistics = SysAllocString(sStatistics);
447 if(!bstrStatistics)
448 hr = E_OUTOFMEMORY;
451 if(SUCCEEDED(hr))
453 bstrCategory = SysAllocString(sCategory);
454 if(!bstrCategory)
455 hr = E_OUTOFMEMORY;
458 if(SUCCEEDED(hr))
460 bstrIndex = SysAllocString(sIndex);
461 if(!bstrIndex)
462 hr = E_OUTOFMEMORY;
465 if(SUCCEEDED(hr))
467 bstrStatistic = SysAllocString(sStatistic);
468 if(!bstrStatistic)
469 hr = E_OUTOFMEMORY;
472 if(SUCCEEDED(hr))
474 bstrName = SysAllocString(sName);
475 if(!bstrName)
476 hr = E_OUTOFMEMORY;
479 if(SUCCEEDED(hr))
481 bstrValue = SysAllocString(sValue);
482 if(!bstrValue)
483 hr = E_OUTOFMEMORY;
486 if(SUCCEEDED(hr))
487 hr = IXMLDOMDocument_load(document, vStatsFilePath, &isSuccessful);
489 if(hr == S_OK && isSuccessful != VARIANT_TRUE)
490 hr = S_FALSE;
492 if( hr == S_OK )
493 hr = IXMLDOMDocument_get_documentElement(document, &root);
495 if(hr == S_OK)
496 hr = IXMLDOMElement_get_childNodes(root, &rootChildren);
498 if(hr == S_OK)
500 hr = S_OK;
501 while(hr == S_OK)
503 hr = IXMLDOMNodeList_nextNode(rootChildren, &categoryNode);
505 if(hr == S_OK)
507 hr = IXMLDOMNode_QueryInterface(categoryNode, &IID_IXMLDOMElement, (LPVOID*)&categoryElement);
509 if(SUCCEEDED(hr))
511 hr = IXMLDOMElement_getAttribute(categoryElement, bstrIndex, &vValue);
512 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
513 hr = E_FAIL;
515 if(SUCCEEDED(hr))
517 i = StrToIntW(V_BSTR(&vValue));
518 hr = IXMLDOMElement_getAttribute(categoryElement, bstrName, &vValue);
519 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
520 hr = E_FAIL;
523 if(SUCCEEDED(hr))
525 lstrcpynW(data->categories[i].sName, V_BSTR(&vValue), MAX_CATEGORY_LENGTH);
526 TRACE("category %d name %s\n", i, debugstr_w(data->categories[i].sName));
527 hr = IXMLDOMElement_get_childNodes(categoryElement, &categoryChildren);
530 if(SUCCEEDED(hr))
532 hr = S_OK;
533 while(hr == S_OK)
535 hr = IXMLDOMNodeList_nextNode(categoryChildren, &statisticNode);
537 if(hr == S_OK)
539 hr = IXMLDOMNode_QueryInterface(statisticNode, &IID_IXMLDOMElement, (LPVOID*)&statisticElement);
541 if(SUCCEEDED(hr))
543 hr = IXMLDOMElement_getAttribute(statisticElement, bstrIndex, &vValue);
544 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
545 hr = E_FAIL;
547 if(SUCCEEDED(hr))
549 j = StrToIntW(V_BSTR(&vValue));
550 hr = IXMLDOMElement_getAttribute(statisticElement, bstrName, &vValue);
551 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
552 hr = E_FAIL;
555 if(SUCCEEDED(hr))
557 lstrcpynW(data->categories[i].stats[j].sName, V_BSTR(&vValue), MAX_NAME_LENGTH);
558 hr = IXMLDOMElement_getAttribute(statisticElement, bstrValue, &vValue);
559 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
560 hr = E_FAIL;
563 if(SUCCEEDED(hr))
565 lstrcpynW(data->categories[i].stats[j].sValue, V_BSTR(&vValue), MAX_VALUE_LENGTH);
566 TRACE("statistic %d name %s value %s\n", j,
567 debugstr_w(data->categories[i].stats[j].sName),
568 debugstr_w(data->categories[i].stats[j].sValue));
570 IXMLDOMElement_Release(statisticElement);
573 IXMLDOMNode_Release(statisticNode);
577 if(SUCCEEDED(hr))
578 hr = S_OK;
580 IXMLDOMElement_Release(categoryElement);
583 IXMLDOMNode_Release(categoryNode);
586 if(SUCCEEDED(hr))
587 hr = S_OK;
590 if(rootChildren) IXMLDOMNodeList_Release(rootChildren);
591 if(root) IXMLDOMElement_Release(root);
592 if(document) IXMLDOMDocument_Release(document);
594 SysFreeString(bstrValue);
595 SysFreeString(bstrName);
596 SysFreeString(bstrStatistic);
597 SysFreeString(bstrIndex);
598 SysFreeString(bstrCategory);
599 SysFreeString(bstrStatistics);
600 SysFreeString(V_BSTR(&vStatsFilePath));
601 return hr;
603 /*******************************************************************
604 * GAMEUX_loadGameStatistics
606 * Helper function which loads game statistics associated with game
607 * into interface's internal structures
609 * Parameters:
610 * pStats [O] structure which will receive data
611 * sGameId [I] application instance Id, stored as string
612 * to avoid additional conversions
613 * openType [I] allowed ways of opening statistics
614 * pOpenResult [O] way used to open statistics
617 static HRESULT GAMEUX_loadGameStatistics(struct GAMEUX_STATS *pStats,
618 LPWSTR sGameId,
619 GAMESTATS_OPEN_TYPE openType,
620 GAMESTATS_OPEN_RESULT* pOpenResult)
622 HRESULT hr;
623 TRACE("(%p, %s, %d, %p)\n", pStats, debugstr_w(sGameId), openType, pOpenResult);
625 hr = GAMEUX_buildStatisticsFilePath(sGameId, pStats->sStatsFile);
627 hr = GAMEUX_loadStatisticsFromFile(pStats);
628 TRACE("ldstats finished, res: %#x\n", hr);
629 if(hr == S_OK)
631 *pOpenResult = GAMESTATS_OPEN_OPENED;
633 else if(hr == S_FALSE && openType == GAMESTATS_OPEN_OPENORCREATE) /* file does not exist */
635 /* create new statistics, not yet connected with file */
636 ZeroMemory(pStats->categories, sizeof(pStats->categories));
637 *pOpenResult = GAMESTATS_OPEN_CREATED;
638 hr = S_OK;
640 else
641 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
643 TRACE("openResult=%#x ret=%#x\n", *pOpenResult, hr);
644 return hr;
646 /*******************************************************************
647 * IGameStatistics implementation
649 typedef struct _GameStatisticsImpl
651 IGameStatistics IGameStatistics_iface;
652 LONG ref;
653 struct GAMEUX_STATS stats;
654 } GameStatisticsImpl;
656 static inline GameStatisticsImpl *impl_from_IGameStatistics( IGameStatistics *iface )
658 return CONTAINING_RECORD(iface, GameStatisticsImpl, IGameStatistics_iface);
661 static HRESULT WINAPI GameStatisticsImpl_QueryInterface(
662 IGameStatistics *iface,
663 REFIID riid,
664 void **ppvObject)
666 GameStatisticsImpl *This = impl_from_IGameStatistics( iface );
668 TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppvObject );
670 *ppvObject = NULL;
672 if ( IsEqualGUID( riid, &IID_IUnknown ) ||
673 IsEqualGUID( riid, &IID_IGameStatistics ) )
675 *ppvObject = iface;
677 else
679 FIXME("interface %s not implemented\n", debugstr_guid(riid));
680 return E_NOINTERFACE;
683 IGameStatistics_AddRef( iface );
684 return S_OK;
687 static ULONG WINAPI GameStatisticsImpl_AddRef(IGameStatistics *iface)
689 GameStatisticsImpl *This = impl_from_IGameStatistics( iface );
690 LONG ref;
692 ref = InterlockedIncrement(&This->ref);
694 TRACE("(%p): ref=%d\n", This, ref);
695 return ref;
698 static ULONG WINAPI GameStatisticsImpl_Release(IGameStatistics *iface)
700 GameStatisticsImpl *This = impl_from_IGameStatistics( iface );
701 LONG ref;
703 ref = InterlockedDecrement( &This->ref );
704 TRACE("(%p): ref=%d\n", This, ref);
706 if ( ref == 0 )
708 TRACE("freeing IGameStatistics\n");
709 HeapFree( GetProcessHeap(), 0, This );
712 return ref;
715 static HRESULT WINAPI GameStatisticsImpl_GetMaxCategoryLength(
716 IGameStatistics *iface,
717 UINT *cch)
719 TRACE("(%p, %p)\n", iface, cch);
720 if(!cch)
721 return E_INVALIDARG;
723 *cch = MAX_CATEGORY_LENGTH;
724 return S_OK;
727 static HRESULT WINAPI GameStatisticsImpl_GetMaxNameLength(
728 IGameStatistics *iface,
729 UINT *cch)
731 TRACE("(%p, %p)\n", iface, cch);
732 if(!cch)
733 return E_INVALIDARG;
735 *cch = MAX_NAME_LENGTH;
736 return S_OK;
739 static HRESULT WINAPI GameStatisticsImpl_GetMaxValueLength(
740 IGameStatistics *iface,
741 UINT *cch)
743 TRACE("(%p, %p)\n", iface, cch);
744 if(!cch)
745 return E_INVALIDARG;
747 *cch = MAX_VALUE_LENGTH;
748 return S_OK;
751 static HRESULT WINAPI GameStatisticsImpl_GetMaxCategories(
752 IGameStatistics *iface,
753 WORD *pMax)
755 TRACE("(%p, %p)\n", iface, pMax);
756 if(!pMax)
757 return E_INVALIDARG;
759 *pMax = MAX_CATEGORIES;
760 return S_OK;
763 static HRESULT WINAPI GameStatisticsImpl_GetMaxStatsPerCategory(
764 IGameStatistics *iface,
765 WORD *pMax)
767 TRACE("(%p, %p)\n", iface, pMax);
768 if(!pMax)
769 return E_INVALIDARG;
771 *pMax = MAX_STATS_PER_CATEGORY;
772 return S_OK;
775 static HRESULT WINAPI GameStatisticsImpl_SetCategoryTitle(
776 IGameStatistics *iface,
777 WORD categoryIndex,
778 LPCWSTR title)
780 HRESULT hr = S_OK;
781 DWORD dwLength;
782 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
784 TRACE("(%p, %d, %s)\n", This, categoryIndex, debugstr_w(title));
786 if(!title || categoryIndex >= MAX_CATEGORIES)
787 return E_INVALIDARG;
789 dwLength = lstrlenW(title);
791 if(dwLength > MAX_CATEGORY_LENGTH)
793 hr = S_FALSE;
794 dwLength = MAX_CATEGORY_LENGTH;
797 lstrcpynW(This->stats.categories[categoryIndex].sName,
798 title, dwLength+1);
800 return hr;
803 static HRESULT WINAPI GameStatisticsImpl_GetCategoryTitle(
804 IGameStatistics *iface,
805 WORD categoryIndex,
806 LPWSTR *pTitle)
808 HRESULT hr = S_OK;
809 LONG nLength;
810 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
812 TRACE("%p, %d, %p\n", This, categoryIndex, pTitle);
814 if(!pTitle)
815 return E_INVALIDARG;
816 *pTitle = NULL;
818 if (categoryIndex >= MAX_CATEGORIES)
819 hr = E_INVALIDARG;
821 if(SUCCEEDED(hr))
823 nLength = lstrlenW(This->stats.categories[categoryIndex].sName);
824 if(nLength != 0)
826 *pTitle = CoTaskMemAlloc(sizeof(WCHAR)*(nLength+1));
827 lstrcpyW(*pTitle, This->stats.categories[categoryIndex].sName);
831 return hr;
834 static HRESULT WINAPI GameStatisticsImpl_GetStatistic(
835 IGameStatistics *iface,
836 WORD categoryIndex,
837 WORD statIndex,
838 LPWSTR *pName,
839 LPWSTR *pValue)
841 HRESULT hr = S_OK;
842 LONG nLength;
843 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
845 TRACE("%p, %d,%d, %p, %p\n", This, categoryIndex, statIndex, pName, pValue);
847 if(!pName || !pValue)
848 return E_INVALIDARG;
850 *pName = NULL;
851 *pValue = NULL;
853 if(categoryIndex >= MAX_CATEGORIES || statIndex >= MAX_STATS_PER_CATEGORY)
854 hr = E_INVALIDARG;
856 if(SUCCEEDED(hr))
858 nLength = lstrlenW(This->stats.categories[categoryIndex].stats[statIndex].sName);
859 if(nLength != 0)
861 *pName = CoTaskMemAlloc(sizeof(WCHAR)*(nLength+1));
862 if(!(*pName))
863 hr = E_OUTOFMEMORY;
864 else
865 lstrcpyW(*pName, This->stats.categories[categoryIndex].stats[statIndex].sName);
869 if(SUCCEEDED(hr))
871 nLength = lstrlenW(This->stats.categories[categoryIndex].stats[statIndex].sValue);
872 if(nLength != 0)
874 *pValue = CoTaskMemAlloc(sizeof(WCHAR)*(nLength+1));
875 if(!(*pValue))
876 hr = E_OUTOFMEMORY;
877 else
878 lstrcpyW(*pValue, This->stats.categories[categoryIndex].stats[statIndex].sValue);
882 TRACE("returning pair; %s => %s\n", debugstr_w(*pName), debugstr_w(*pValue));
883 return hr;
886 static HRESULT WINAPI GameStatisticsImpl_SetStatistic(
887 IGameStatistics *iface,
888 WORD categoryIndex,
889 WORD statIndex,
890 LPCWSTR name,
891 LPCWSTR value)
893 HRESULT hr = S_OK;
894 DWORD dwNameLen, dwValueLen;
895 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
897 TRACE("(%p, %d, %d, %s, %s)\n", This, categoryIndex, statIndex,
898 debugstr_w(name), debugstr_w(value));
900 if(!name)
901 return S_FALSE;
903 if(categoryIndex >= MAX_CATEGORIES || statIndex >= MAX_STATS_PER_CATEGORY)
904 return E_INVALIDARG;
906 dwNameLen = lstrlenW(name);
908 if(dwNameLen > MAX_NAME_LENGTH)
910 hr = S_FALSE;
911 dwNameLen = MAX_NAME_LENGTH;
914 lstrcpynW(This->stats.categories[categoryIndex].stats[statIndex].sName,
915 name, dwNameLen+1);
917 if(value)
919 dwValueLen = lstrlenW(value);
921 if(dwValueLen > MAX_VALUE_LENGTH)
923 hr = S_FALSE;
924 dwValueLen = MAX_VALUE_LENGTH;
927 lstrcpynW(This->stats.categories[categoryIndex].stats[statIndex].sValue,
928 value, dwValueLen+1);
930 else
931 /* Windows allows passing NULL as value */
932 This->stats.categories[categoryIndex].stats[statIndex].sValue[0] = 0;
934 return hr;
937 static HRESULT WINAPI GameStatisticsImpl_Save(
938 IGameStatistics *iface,
939 BOOL trackChanges)
941 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
942 HRESULT hr = S_OK;
944 TRACE("(%p, %d)\n", This, trackChanges);
946 if(trackChanges)
947 FIXME("tracking changes not yet implemented\n");
949 hr = GAMEUX_updateStatisticsFile(&This->stats);
951 return hr;
954 static HRESULT WINAPI GameStatisticsImpl_SetLastPlayedCategory(
955 IGameStatistics *iface,
956 UINT categoryIndex)
958 FIXME("stub\n");
959 return E_NOTIMPL;
962 static HRESULT WINAPI GameStatisticsImpl_GetLastPlayedCategory(
963 IGameStatistics *iface,
964 UINT *pCategoryIndex)
966 FIXME("stub\n");
967 return E_NOTIMPL;
970 static const struct IGameStatisticsVtbl GameStatisticsImplVtbl =
972 GameStatisticsImpl_QueryInterface,
973 GameStatisticsImpl_AddRef,
974 GameStatisticsImpl_Release,
975 GameStatisticsImpl_GetMaxCategoryLength,
976 GameStatisticsImpl_GetMaxNameLength,
977 GameStatisticsImpl_GetMaxValueLength,
978 GameStatisticsImpl_GetMaxCategories,
979 GameStatisticsImpl_GetMaxStatsPerCategory,
980 GameStatisticsImpl_SetCategoryTitle,
981 GameStatisticsImpl_GetCategoryTitle,
982 GameStatisticsImpl_GetStatistic,
983 GameStatisticsImpl_SetStatistic,
984 GameStatisticsImpl_Save,
985 GameStatisticsImpl_SetLastPlayedCategory,
986 GameStatisticsImpl_GetLastPlayedCategory
990 static HRESULT create_IGameStatistics(GameStatisticsImpl** ppStats)
992 TRACE("(%p)\n", ppStats);
994 *ppStats = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(**ppStats));
995 if(!(*ppStats))
996 return E_OUTOFMEMORY;
998 (*ppStats)->IGameStatistics_iface.lpVtbl = &GameStatisticsImplVtbl;
999 (*ppStats)->ref = 1;
1001 TRACE("returning coclass: %p\n", *ppStats);
1002 return S_OK;
1005 /*******************************************************************************
1006 * IGameStatisticsMgr implementation
1008 typedef struct _GameStatisticsMgrImpl
1010 IGameStatisticsMgr IGameStatisticsMgr_iface;
1011 LONG ref;
1012 } GameStatisticsMgrImpl;
1014 static inline GameStatisticsMgrImpl *impl_from_IGameStatisticsMgr( IGameStatisticsMgr *iface )
1016 return CONTAINING_RECORD(iface, GameStatisticsMgrImpl, IGameStatisticsMgr_iface);
1020 static HRESULT WINAPI GameStatisticsMgrImpl_QueryInterface(
1021 IGameStatisticsMgr *iface,
1022 REFIID riid,
1023 void **ppvObject)
1025 GameStatisticsMgrImpl *This = impl_from_IGameStatisticsMgr( iface );
1027 TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppvObject );
1029 *ppvObject = NULL;
1031 if(IsEqualGUID(riid, &IID_IUnknown) ||
1032 IsEqualGUID(riid, &IID_IGameStatisticsMgr) )
1034 *ppvObject = iface;
1036 else
1038 FIXME("interface %s not implemented\n", debugstr_guid(riid));
1039 return E_NOINTERFACE;
1042 IGameStatisticsMgr_AddRef( iface );
1043 return S_OK;
1046 static ULONG WINAPI GameStatisticsMgrImpl_AddRef(IGameStatisticsMgr *iface)
1048 GameStatisticsMgrImpl *This = impl_from_IGameStatisticsMgr( iface );
1049 LONG ref;
1051 ref = InterlockedIncrement(&This->ref);
1053 TRACE("(%p): ref=%d\n", This, ref);
1054 return ref;
1057 static ULONG WINAPI GameStatisticsMgrImpl_Release(IGameStatisticsMgr *iface)
1059 GameStatisticsMgrImpl *This = impl_from_IGameStatisticsMgr( iface );
1060 LONG ref;
1062 ref = InterlockedDecrement(&This->ref);
1063 TRACE("(%p): ref=%d\n", This, ref);
1065 if ( ref == 0 )
1067 TRACE("freeing GameStatistics object\n");
1068 HeapFree( GetProcessHeap(), 0, This);
1071 return ref;
1074 static HRESULT STDMETHODCALLTYPE GameStatisticsMgrImpl_GetGameStatistics(
1075 IGameStatisticsMgr* iface,
1076 LPCWSTR GDFBinaryPath,
1077 GAMESTATS_OPEN_TYPE openType,
1078 GAMESTATS_OPEN_RESULT *pOpenResult,
1079 IGameStatistics **ppiStats)
1081 HRESULT hr;
1082 WCHAR lpApplicationId[49];
1083 GameStatisticsImpl *statisticsImpl = NULL;
1084 IGameStatistics *output_iface;
1086 TRACE("(%p, %s, 0x%x, %p, %p)\n", iface, debugstr_w(GDFBinaryPath), openType, pOpenResult, ppiStats);
1088 hr = GAMEUX_getAppIdFromGDFPath(GDFBinaryPath, lpApplicationId);
1090 if(SUCCEEDED(hr))
1091 hr = create_IGameStatistics(&statisticsImpl);
1093 if(SUCCEEDED(hr))
1095 output_iface = &statisticsImpl->IGameStatistics_iface;
1096 hr = GAMEUX_buildStatisticsFilePath(lpApplicationId, statisticsImpl->stats.sStatsFile);
1099 if(SUCCEEDED(hr))
1100 hr = GAMEUX_loadGameStatistics(&statisticsImpl->stats, lpApplicationId, openType, pOpenResult);
1102 if(SUCCEEDED(hr))
1103 *ppiStats = output_iface;
1104 else
1106 HeapFree(GetProcessHeap(), 0, statisticsImpl);
1107 *ppiStats = NULL;
1110 return hr;
1113 static HRESULT STDMETHODCALLTYPE GameStatisticsMgrImpl_RemoveGameStatistics(
1114 IGameStatisticsMgr* iface,
1115 LPCWSTR GDFBinaryPath)
1117 HRESULT hr;
1118 WCHAR lpApplicationId[49];
1119 WCHAR sStatsFile[MAX_PATH];
1121 TRACE("(%p, %s)\n", iface, debugstr_w(GDFBinaryPath));
1123 hr = GAMEUX_getAppIdFromGDFPath(GDFBinaryPath, lpApplicationId);
1125 if(SUCCEEDED(hr))
1126 hr = GAMEUX_buildStatisticsFilePath(lpApplicationId, sStatsFile);
1128 if(SUCCEEDED(hr))
1129 hr = (DeleteFileW(sStatsFile)==TRUE ? S_OK : HRESULT_FROM_WIN32(GetLastError()));
1131 return hr;
1134 static const struct IGameStatisticsMgrVtbl GameStatisticsMgrImplVtbl =
1136 GameStatisticsMgrImpl_QueryInterface,
1137 GameStatisticsMgrImpl_AddRef,
1138 GameStatisticsMgrImpl_Release,
1139 GameStatisticsMgrImpl_GetGameStatistics,
1140 GameStatisticsMgrImpl_RemoveGameStatistics,
1143 HRESULT GameStatistics_create(
1144 IUnknown *pUnkOuter,
1145 IUnknown **ppObj)
1147 GameStatisticsMgrImpl *pGameStatistics;
1149 TRACE("(%p, %p)\n", pUnkOuter, ppObj);
1151 pGameStatistics = HeapAlloc( GetProcessHeap(), 0, sizeof (*pGameStatistics) );
1153 if( !pGameStatistics )
1154 return E_OUTOFMEMORY;
1156 pGameStatistics->IGameStatisticsMgr_iface.lpVtbl = &GameStatisticsMgrImplVtbl;
1157 pGameStatistics->ref = 1;
1159 *ppObj = (IUnknown*)&pGameStatistics->IGameStatisticsMgr_iface;
1161 TRACE("returning iface %p\n", *ppObj);
1162 return S_OK;