msvcp90: Fixed EOF detection in basic_streambuf_char__Xsgetn_s function.
[wine/multimedia.git] / dlls / gameux / gamestatistics.c
blob0116304b0efb989bf72d849a1b864d35ba5d016a
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);
245 SysFreeString(V_BSTR(&vValue));
247 if(SUCCEEDED(hr))
249 V_VT(&vValue) = VT_BSTR;
250 V_BSTR(&vValue) = SysAllocString(stats->categories[i].stats[j].sValue);
251 if(!V_BSTR(&vValue))
252 hr = E_OUTOFMEMORY;
255 if(SUCCEEDED(hr))
257 TRACE(" storing statistic %d: name: %s\n", j, debugstr_w(V_BSTR(&vValue)));
258 hr = IXMLDOMElement_setAttribute(statisticsElement, bstrValue, vValue);
261 SysFreeString(V_BSTR(&vValue));
263 if(SUCCEEDED(hr))
264 hr = IXMLDOMElement_appendChild(categoryNode, statisticsNode, &statisticsNode);
266 IXMLDOMElement_Release(statisticsElement);
267 IXMLDOMNode_Release(statisticsNode);
271 if(SUCCEEDED(hr))
272 hr = IXMLDOMElement_appendChild(root, categoryNode, &categoryNode);
274 IXMLDOMElement_Release(categoryElement);
275 IXMLDOMNode_Release(categoryNode);
277 if(FAILED(hr))
278 break;
281 if(SUCCEEDED(hr))
282 hr = IXMLDOMDocument_putref_documentElement(document, root);
284 IXMLDOMElement_Release(root);
286 TRACE("saving game statistics in %s file\n", debugstr_w(stats->sStatsFile));
287 if(SUCCEEDED(hr))
288 hr = GAMEUX_createStatsDirectory(stats->sStatsFile);
290 if(SUCCEEDED(hr))
291 hr = IXMLDOMDocument_save(document, vStatsFilePath);
293 IXMLDOMDocument_Release(document);
295 SysFreeString(bstrValue);
296 SysFreeString(bstrName);
297 SysFreeString(bstrStatistic);
298 SysFreeString(bstrIndex);
299 SysFreeString(bstrCategory);
300 SysFreeString(bstrStatistics);
301 SysFreeString(V_BSTR(&vStatsFilePath));
302 TRACE("ret=0x%x\n", hr);
303 return hr;
305 /*******************************************************************************
306 * GAMEUX_buildStatisticsFilePath
307 * Creates path to file containing statistics of game with given id.
309 * Parameters:
310 * lpApplicationId [I] application id of game,
311 * as string
312 * lpStatisticsFile [O] array where path will be
313 * stored. Its size must be
314 * at least MAX_PATH
316 static HRESULT GAMEUX_buildStatisticsFilePath(
317 LPCWSTR lpApplicationId,
318 LPWSTR lpStatisticsFile)
320 static const WCHAR sBackslash[] = {'\\',0};
321 static const WCHAR sStatisticsDir[] = {'\\','M','i','c','r','o','s','o','f','t',
322 '\\','W','i','n','d','o','w','s','\\','G','a','m','e','E','x','p',
323 'l','o','r','e','r','\\','G','a','m','e','S','t','a','t','i','s',
324 't','i','c','s','\\',0};
325 static const WCHAR sDotGamestats[] = {'.','g','a','m','e','s','t','a','t','s',0};
327 HRESULT hr;
329 hr = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, lpStatisticsFile);
331 if(SUCCEEDED(hr))
333 lstrcatW(lpStatisticsFile, sStatisticsDir);
334 lstrcatW(lpStatisticsFile, lpApplicationId);
335 lstrcatW(lpStatisticsFile, sBackslash);
336 lstrcatW(lpStatisticsFile, lpApplicationId);
337 lstrcatW(lpStatisticsFile, sDotGamestats);
340 return hr;
342 /*******************************************************************************
343 * GAMEUX_getAppIdFromGDFPath
345 * Loads application identifier associated with given GDF binary.
346 * Routine reads identifier from registry, so will fail if game
347 * is not registered.
349 * Parameters:
350 * GDFBinaryPath [I] path to gdf binary
351 * lpApplicationId [O] place to store application id.
352 * must be at least 49 characters
353 * to store guid and termination 0
355 static HRESULT GAMEUX_getAppIdFromGDFPath(
356 LPCWSTR GDFBinaryPath,
357 LPWSTR lpApplicationId)
359 static const WCHAR sApplicationId[] =
360 {'A','p','p','l','i','c','a','t','i','o','n','I','d',0};
362 HRESULT hr;
363 GAME_INSTALL_SCOPE installScope;
364 GUID instanceId;
365 LPWSTR lpRegistryPath = NULL;
366 HKEY hKey;
367 DWORD dwLength = 49*sizeof(WCHAR);/* place for GUID */
369 TRACE("(%s, %p)\n", debugstr_w(GDFBinaryPath), lpApplicationId);
371 if(!GDFBinaryPath)
372 return E_INVALIDARG;
374 installScope = GIS_CURRENT_USER;
375 hr = GAMEUX_FindGameInstanceId(GDFBinaryPath, installScope, &instanceId);
377 if(hr == S_FALSE)
379 installScope = GIS_ALL_USERS;
380 hr = GAMEUX_FindGameInstanceId(GDFBinaryPath, installScope, &instanceId);
383 if(hr == S_FALSE)
384 /* game not registered, so statistics cannot be used */
385 hr = E_FAIL;
387 if(SUCCEEDED(hr))
388 /* game is registered, let's read it's application id from registry */
389 hr = GAMEUX_buildGameRegistryPath(installScope, &instanceId, &lpRegistryPath);
391 if(SUCCEEDED(hr)) {
392 hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE,
393 lpRegistryPath, 0, KEY_READ | KEY_WOW64_64KEY, &hKey));
394 if(SUCCEEDED(hr)) {
395 hr = HRESULT_FROM_WIN32(RegGetValueW(hKey,
396 NULL, sApplicationId, RRF_RT_REG_SZ,
397 NULL, lpApplicationId, &dwLength));
398 RegCloseKey(hKey);
402 HeapFree(GetProcessHeap(), 0, lpRegistryPath);
404 TRACE("found app id: %s, return: %#x\n", debugstr_w(lpApplicationId), hr);
405 return hr;
407 /*******************************************************************
408 * GAMEUX_loadGameStatisticsFromFile
409 * Helper function, loads game statistics from file and stores them
410 * in the structure.
412 * Parameters:
413 * data [I/O] structure containing file name to
414 * load and data fields to store data in
416 static HRESULT GAMEUX_loadStatisticsFromFile(struct GAMEUX_STATS *data)
418 static const WCHAR sStatistics[] = {'S','t','a','t','i','s','t','i','c','s',0};
419 static const WCHAR sCategory[] = {'C','a','t','e','g','o','r','y',0};
420 static const WCHAR sIndex[] = {'I','n','d','e','x',0};
421 static const WCHAR sStatistic[] = {'S','t','a','t','i','s','t','i','c',0};
422 static const WCHAR sName[] = {'N','a','m','e',0};
423 static const WCHAR sValue[] = {'V','a','l','u','e',0};
425 HRESULT hr = S_OK;
426 IXMLDOMDocument *document = NULL;
427 IXMLDOMElement *root = NULL, *categoryElement, *statisticElement;
428 IXMLDOMNode *categoryNode, *statisticNode;
429 IXMLDOMNodeList *rootChildren = NULL, *categoryChildren;
430 VARIANT vStatsFilePath, vValue;
431 BSTR bstrStatistics = NULL, bstrCategory = NULL, bstrIndex = NULL,
432 bstrStatistic = NULL, bstrName = NULL, bstrValue = NULL;
433 VARIANT_BOOL isSuccessful = VARIANT_FALSE;
434 int i, j;
436 TRACE("(%p)\n", data);
438 V_VT(&vStatsFilePath) = VT_BSTR;
439 V_BSTR(&vStatsFilePath) = SysAllocString(data->sStatsFile);
440 if(!V_BSTR(&vStatsFilePath))
441 hr = E_OUTOFMEMORY;
443 if(SUCCEEDED(hr))
444 hr = CoCreateInstance(&CLSID_DOMDocument30, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, (void**)&document);
446 if(SUCCEEDED(hr))
448 bstrStatistics = SysAllocString(sStatistics);
449 if(!bstrStatistics)
450 hr = E_OUTOFMEMORY;
453 if(SUCCEEDED(hr))
455 bstrCategory = SysAllocString(sCategory);
456 if(!bstrCategory)
457 hr = E_OUTOFMEMORY;
460 if(SUCCEEDED(hr))
462 bstrIndex = SysAllocString(sIndex);
463 if(!bstrIndex)
464 hr = E_OUTOFMEMORY;
467 if(SUCCEEDED(hr))
469 bstrStatistic = SysAllocString(sStatistic);
470 if(!bstrStatistic)
471 hr = E_OUTOFMEMORY;
474 if(SUCCEEDED(hr))
476 bstrName = SysAllocString(sName);
477 if(!bstrName)
478 hr = E_OUTOFMEMORY;
481 if(SUCCEEDED(hr))
483 bstrValue = SysAllocString(sValue);
484 if(!bstrValue)
485 hr = E_OUTOFMEMORY;
488 if(SUCCEEDED(hr))
489 hr = IXMLDOMDocument_load(document, vStatsFilePath, &isSuccessful);
491 if(hr == S_OK && isSuccessful != VARIANT_TRUE)
492 hr = S_FALSE;
494 if( hr == S_OK )
495 hr = IXMLDOMDocument_get_documentElement(document, &root);
497 if(hr == S_OK)
498 hr = IXMLDOMElement_get_childNodes(root, &rootChildren);
500 if(hr == S_OK)
502 hr = S_OK;
503 while(hr == S_OK)
505 hr = IXMLDOMNodeList_nextNode(rootChildren, &categoryNode);
507 if(hr == S_OK)
509 hr = IXMLDOMNode_QueryInterface(categoryNode, &IID_IXMLDOMElement, (LPVOID*)&categoryElement);
511 if(SUCCEEDED(hr))
513 hr = IXMLDOMElement_getAttribute(categoryElement, bstrIndex, &vValue);
514 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
515 hr = E_FAIL;
517 if(SUCCEEDED(hr))
519 i = StrToIntW(V_BSTR(&vValue));
520 hr = IXMLDOMElement_getAttribute(categoryElement, bstrName, &vValue);
521 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
522 hr = E_FAIL;
525 if(SUCCEEDED(hr))
527 lstrcpynW(data->categories[i].sName, V_BSTR(&vValue), MAX_CATEGORY_LENGTH);
528 TRACE("category %d name %s\n", i, debugstr_w(data->categories[i].sName));
529 hr = IXMLDOMElement_get_childNodes(categoryElement, &categoryChildren);
532 if(SUCCEEDED(hr))
534 hr = S_OK;
535 while(hr == S_OK)
537 hr = IXMLDOMNodeList_nextNode(categoryChildren, &statisticNode);
539 if(hr == S_OK)
541 hr = IXMLDOMNode_QueryInterface(statisticNode, &IID_IXMLDOMElement, (LPVOID*)&statisticElement);
543 if(SUCCEEDED(hr))
545 hr = IXMLDOMElement_getAttribute(statisticElement, bstrIndex, &vValue);
546 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
547 hr = E_FAIL;
549 if(SUCCEEDED(hr))
551 j = StrToIntW(V_BSTR(&vValue));
552 hr = IXMLDOMElement_getAttribute(statisticElement, bstrName, &vValue);
553 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
554 hr = E_FAIL;
557 if(SUCCEEDED(hr))
559 lstrcpynW(data->categories[i].stats[j].sName, V_BSTR(&vValue), MAX_NAME_LENGTH);
560 hr = IXMLDOMElement_getAttribute(statisticElement, bstrValue, &vValue);
561 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
562 hr = E_FAIL;
565 if(SUCCEEDED(hr))
567 lstrcpynW(data->categories[i].stats[j].sValue, V_BSTR(&vValue), MAX_VALUE_LENGTH);
568 TRACE("statistic %d name %s value %s\n", j,
569 debugstr_w(data->categories[i].stats[j].sName),
570 debugstr_w(data->categories[i].stats[j].sValue));
572 IXMLDOMElement_Release(statisticElement);
575 IXMLDOMNode_Release(statisticNode);
579 if(SUCCEEDED(hr))
580 hr = S_OK;
582 IXMLDOMElement_Release(categoryElement);
585 IXMLDOMNode_Release(categoryNode);
588 if(SUCCEEDED(hr))
589 hr = S_OK;
592 if(rootChildren) IXMLDOMNodeList_Release(rootChildren);
593 if(root) IXMLDOMElement_Release(root);
594 if(document) IXMLDOMDocument_Release(document);
596 SysFreeString(bstrValue);
597 SysFreeString(bstrName);
598 SysFreeString(bstrStatistic);
599 SysFreeString(bstrIndex);
600 SysFreeString(bstrCategory);
601 SysFreeString(bstrStatistics);
602 SysFreeString(V_BSTR(&vStatsFilePath));
603 return hr;
605 /*******************************************************************
606 * GAMEUX_loadGameStatistics
608 * Helper function which loads game statistics associated with game
609 * into interface's internal structures
611 * Parameters:
612 * pStats [O] structure which will receive data
613 * sGameId [I] application instance Id, stored as string
614 * to avoid additional conversions
615 * openType [I] allowed ways of opening statistics
616 * pOpenResult [O] way used to open statistics
619 static HRESULT GAMEUX_loadGameStatistics(struct GAMEUX_STATS *pStats,
620 LPWSTR sGameId,
621 GAMESTATS_OPEN_TYPE openType,
622 GAMESTATS_OPEN_RESULT* pOpenResult)
624 HRESULT hr;
625 TRACE("(%p, %s, %d, %p)\n", pStats, debugstr_w(sGameId), openType, pOpenResult);
627 hr = GAMEUX_buildStatisticsFilePath(sGameId, pStats->sStatsFile);
629 hr = GAMEUX_loadStatisticsFromFile(pStats);
630 TRACE("ldstats finished, res: %#x\n", hr);
631 if(hr == S_OK)
633 *pOpenResult = GAMESTATS_OPEN_OPENED;
635 else if(hr == S_FALSE && openType == GAMESTATS_OPEN_OPENORCREATE) /* file does not exist */
637 /* create new statistics, not yet connected with file */
638 ZeroMemory(pStats->categories, sizeof(pStats->categories));
639 *pOpenResult = GAMESTATS_OPEN_CREATED;
640 hr = S_OK;
642 else
643 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
645 TRACE("openResult=%#x ret=%#x\n", *pOpenResult, hr);
646 return hr;
648 /*******************************************************************
649 * IGameStatistics implementation
651 typedef struct _GameStatisticsImpl
653 IGameStatistics IGameStatistics_iface;
654 LONG ref;
655 struct GAMEUX_STATS stats;
656 } GameStatisticsImpl;
658 static inline GameStatisticsImpl *impl_from_IGameStatistics( IGameStatistics *iface )
660 return CONTAINING_RECORD(iface, GameStatisticsImpl, IGameStatistics_iface);
663 static HRESULT WINAPI GameStatisticsImpl_QueryInterface(
664 IGameStatistics *iface,
665 REFIID riid,
666 void **ppvObject)
668 GameStatisticsImpl *This = impl_from_IGameStatistics( iface );
670 TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppvObject );
672 *ppvObject = NULL;
674 if ( IsEqualGUID( riid, &IID_IUnknown ) ||
675 IsEqualGUID( riid, &IID_IGameStatistics ) )
677 *ppvObject = iface;
679 else
681 FIXME("interface %s not implemented\n", debugstr_guid(riid));
682 return E_NOINTERFACE;
685 IGameStatistics_AddRef( iface );
686 return S_OK;
689 static ULONG WINAPI GameStatisticsImpl_AddRef(IGameStatistics *iface)
691 GameStatisticsImpl *This = impl_from_IGameStatistics( iface );
692 LONG ref;
694 ref = InterlockedIncrement(&This->ref);
696 TRACE("(%p): ref=%d\n", This, ref);
697 return ref;
700 static ULONG WINAPI GameStatisticsImpl_Release(IGameStatistics *iface)
702 GameStatisticsImpl *This = impl_from_IGameStatistics( iface );
703 LONG ref;
705 ref = InterlockedDecrement( &This->ref );
706 TRACE("(%p): ref=%d\n", This, ref);
708 if ( ref == 0 )
710 TRACE("freeing IGameStatistics\n");
711 HeapFree( GetProcessHeap(), 0, This );
714 return ref;
717 static HRESULT WINAPI GameStatisticsImpl_GetMaxCategoryLength(
718 IGameStatistics *iface,
719 UINT *cch)
721 TRACE("(%p, %p)\n", iface, cch);
722 if(!cch)
723 return E_INVALIDARG;
725 *cch = MAX_CATEGORY_LENGTH;
726 return S_OK;
729 static HRESULT WINAPI GameStatisticsImpl_GetMaxNameLength(
730 IGameStatistics *iface,
731 UINT *cch)
733 TRACE("(%p, %p)\n", iface, cch);
734 if(!cch)
735 return E_INVALIDARG;
737 *cch = MAX_NAME_LENGTH;
738 return S_OK;
741 static HRESULT WINAPI GameStatisticsImpl_GetMaxValueLength(
742 IGameStatistics *iface,
743 UINT *cch)
745 TRACE("(%p, %p)\n", iface, cch);
746 if(!cch)
747 return E_INVALIDARG;
749 *cch = MAX_VALUE_LENGTH;
750 return S_OK;
753 static HRESULT WINAPI GameStatisticsImpl_GetMaxCategories(
754 IGameStatistics *iface,
755 WORD *pMax)
757 TRACE("(%p, %p)\n", iface, pMax);
758 if(!pMax)
759 return E_INVALIDARG;
761 *pMax = MAX_CATEGORIES;
762 return S_OK;
765 static HRESULT WINAPI GameStatisticsImpl_GetMaxStatsPerCategory(
766 IGameStatistics *iface,
767 WORD *pMax)
769 TRACE("(%p, %p)\n", iface, pMax);
770 if(!pMax)
771 return E_INVALIDARG;
773 *pMax = MAX_STATS_PER_CATEGORY;
774 return S_OK;
777 static HRESULT WINAPI GameStatisticsImpl_SetCategoryTitle(
778 IGameStatistics *iface,
779 WORD categoryIndex,
780 LPCWSTR title)
782 HRESULT hr = S_OK;
783 DWORD dwLength;
784 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
786 TRACE("(%p, %d, %s)\n", This, categoryIndex, debugstr_w(title));
788 if(!title || categoryIndex >= MAX_CATEGORIES)
789 return E_INVALIDARG;
791 dwLength = lstrlenW(title);
793 if(dwLength > MAX_CATEGORY_LENGTH)
795 hr = S_FALSE;
796 dwLength = MAX_CATEGORY_LENGTH;
799 lstrcpynW(This->stats.categories[categoryIndex].sName,
800 title, dwLength+1);
802 return hr;
805 static HRESULT WINAPI GameStatisticsImpl_GetCategoryTitle(
806 IGameStatistics *iface,
807 WORD categoryIndex,
808 LPWSTR *pTitle)
810 HRESULT hr = S_OK;
811 LONG nLength;
812 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
814 TRACE("%p, %d, %p\n", This, categoryIndex, pTitle);
816 if(!pTitle)
817 return E_INVALIDARG;
818 *pTitle = NULL;
820 if (categoryIndex >= MAX_CATEGORIES)
821 hr = E_INVALIDARG;
823 if(SUCCEEDED(hr))
825 nLength = lstrlenW(This->stats.categories[categoryIndex].sName);
826 if(nLength != 0)
828 *pTitle = CoTaskMemAlloc(sizeof(WCHAR)*(nLength+1));
829 lstrcpyW(*pTitle, This->stats.categories[categoryIndex].sName);
833 return hr;
836 static HRESULT WINAPI GameStatisticsImpl_GetStatistic(
837 IGameStatistics *iface,
838 WORD categoryIndex,
839 WORD statIndex,
840 LPWSTR *pName,
841 LPWSTR *pValue)
843 HRESULT hr = S_OK;
844 LONG nLength;
845 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
847 TRACE("%p, %d,%d, %p, %p\n", This, categoryIndex, statIndex, pName, pValue);
849 if(!pName || !pValue)
850 return E_INVALIDARG;
852 *pName = NULL;
853 *pValue = NULL;
855 if(categoryIndex >= MAX_CATEGORIES || statIndex >= MAX_STATS_PER_CATEGORY)
856 hr = E_INVALIDARG;
858 if(SUCCEEDED(hr))
860 nLength = lstrlenW(This->stats.categories[categoryIndex].stats[statIndex].sName);
861 if(nLength != 0)
863 *pName = CoTaskMemAlloc(sizeof(WCHAR)*(nLength+1));
864 if(!(*pName))
865 hr = E_OUTOFMEMORY;
866 else
867 lstrcpyW(*pName, This->stats.categories[categoryIndex].stats[statIndex].sName);
871 if(SUCCEEDED(hr))
873 nLength = lstrlenW(This->stats.categories[categoryIndex].stats[statIndex].sValue);
874 if(nLength != 0)
876 *pValue = CoTaskMemAlloc(sizeof(WCHAR)*(nLength+1));
877 if(!(*pValue))
878 hr = E_OUTOFMEMORY;
879 else
880 lstrcpyW(*pValue, This->stats.categories[categoryIndex].stats[statIndex].sValue);
884 TRACE("returning pair; %s => %s\n", debugstr_w(*pName), debugstr_w(*pValue));
885 return hr;
888 static HRESULT WINAPI GameStatisticsImpl_SetStatistic(
889 IGameStatistics *iface,
890 WORD categoryIndex,
891 WORD statIndex,
892 LPCWSTR name,
893 LPCWSTR value)
895 HRESULT hr = S_OK;
896 DWORD dwNameLen, dwValueLen;
897 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
899 TRACE("(%p, %d, %d, %s, %s)\n", This, categoryIndex, statIndex,
900 debugstr_w(name), debugstr_w(value));
902 if(!name)
903 return S_FALSE;
905 if(categoryIndex >= MAX_CATEGORIES || statIndex >= MAX_STATS_PER_CATEGORY)
906 return E_INVALIDARG;
908 dwNameLen = lstrlenW(name);
910 if(dwNameLen > MAX_NAME_LENGTH)
912 hr = S_FALSE;
913 dwNameLen = MAX_NAME_LENGTH;
916 lstrcpynW(This->stats.categories[categoryIndex].stats[statIndex].sName,
917 name, dwNameLen+1);
919 if(value)
921 dwValueLen = lstrlenW(value);
923 if(dwValueLen > MAX_VALUE_LENGTH)
925 hr = S_FALSE;
926 dwValueLen = MAX_VALUE_LENGTH;
929 lstrcpynW(This->stats.categories[categoryIndex].stats[statIndex].sValue,
930 value, dwValueLen+1);
932 else
933 /* Windows allows passing NULL as value */
934 This->stats.categories[categoryIndex].stats[statIndex].sValue[0] = 0;
936 return hr;
939 static HRESULT WINAPI GameStatisticsImpl_Save(
940 IGameStatistics *iface,
941 BOOL trackChanges)
943 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
944 HRESULT hr = S_OK;
946 TRACE("(%p, %d)\n", This, trackChanges);
948 if(trackChanges)
949 FIXME("tracking changes not yet implemented\n");
951 hr = GAMEUX_updateStatisticsFile(&This->stats);
953 return hr;
956 static HRESULT WINAPI GameStatisticsImpl_SetLastPlayedCategory(
957 IGameStatistics *iface,
958 UINT categoryIndex)
960 FIXME("stub\n");
961 return E_NOTIMPL;
964 static HRESULT WINAPI GameStatisticsImpl_GetLastPlayedCategory(
965 IGameStatistics *iface,
966 UINT *pCategoryIndex)
968 FIXME("stub\n");
969 return E_NOTIMPL;
972 static const struct IGameStatisticsVtbl GameStatisticsImplVtbl =
974 GameStatisticsImpl_QueryInterface,
975 GameStatisticsImpl_AddRef,
976 GameStatisticsImpl_Release,
977 GameStatisticsImpl_GetMaxCategoryLength,
978 GameStatisticsImpl_GetMaxNameLength,
979 GameStatisticsImpl_GetMaxValueLength,
980 GameStatisticsImpl_GetMaxCategories,
981 GameStatisticsImpl_GetMaxStatsPerCategory,
982 GameStatisticsImpl_SetCategoryTitle,
983 GameStatisticsImpl_GetCategoryTitle,
984 GameStatisticsImpl_GetStatistic,
985 GameStatisticsImpl_SetStatistic,
986 GameStatisticsImpl_Save,
987 GameStatisticsImpl_SetLastPlayedCategory,
988 GameStatisticsImpl_GetLastPlayedCategory
992 static HRESULT create_IGameStatistics(GameStatisticsImpl** ppStats)
994 TRACE("(%p)\n", ppStats);
996 *ppStats = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(**ppStats));
997 if(!(*ppStats))
998 return E_OUTOFMEMORY;
1000 (*ppStats)->IGameStatistics_iface.lpVtbl = &GameStatisticsImplVtbl;
1001 (*ppStats)->ref = 1;
1003 TRACE("returning coclass: %p\n", *ppStats);
1004 return S_OK;
1007 /*******************************************************************************
1008 * IGameStatisticsMgr implementation
1010 typedef struct _GameStatisticsMgrImpl
1012 IGameStatisticsMgr IGameStatisticsMgr_iface;
1013 LONG ref;
1014 } GameStatisticsMgrImpl;
1016 static inline GameStatisticsMgrImpl *impl_from_IGameStatisticsMgr( IGameStatisticsMgr *iface )
1018 return CONTAINING_RECORD(iface, GameStatisticsMgrImpl, IGameStatisticsMgr_iface);
1022 static HRESULT WINAPI GameStatisticsMgrImpl_QueryInterface(
1023 IGameStatisticsMgr *iface,
1024 REFIID riid,
1025 void **ppvObject)
1027 GameStatisticsMgrImpl *This = impl_from_IGameStatisticsMgr( iface );
1029 TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppvObject );
1031 *ppvObject = NULL;
1033 if(IsEqualGUID(riid, &IID_IUnknown) ||
1034 IsEqualGUID(riid, &IID_IGameStatisticsMgr) )
1036 *ppvObject = iface;
1038 else
1040 FIXME("interface %s not implemented\n", debugstr_guid(riid));
1041 return E_NOINTERFACE;
1044 IGameStatisticsMgr_AddRef( iface );
1045 return S_OK;
1048 static ULONG WINAPI GameStatisticsMgrImpl_AddRef(IGameStatisticsMgr *iface)
1050 GameStatisticsMgrImpl *This = impl_from_IGameStatisticsMgr( iface );
1051 LONG ref;
1053 ref = InterlockedIncrement(&This->ref);
1055 TRACE("(%p): ref=%d\n", This, ref);
1056 return ref;
1059 static ULONG WINAPI GameStatisticsMgrImpl_Release(IGameStatisticsMgr *iface)
1061 GameStatisticsMgrImpl *This = impl_from_IGameStatisticsMgr( iface );
1062 LONG ref;
1064 ref = InterlockedDecrement(&This->ref);
1065 TRACE("(%p): ref=%d\n", This, ref);
1067 if ( ref == 0 )
1069 TRACE("freeing GameStatistics object\n");
1070 HeapFree( GetProcessHeap(), 0, This);
1073 return ref;
1076 static HRESULT STDMETHODCALLTYPE GameStatisticsMgrImpl_GetGameStatistics(
1077 IGameStatisticsMgr* iface,
1078 LPCWSTR GDFBinaryPath,
1079 GAMESTATS_OPEN_TYPE openType,
1080 GAMESTATS_OPEN_RESULT *pOpenResult,
1081 IGameStatistics **ppiStats)
1083 HRESULT hr;
1084 WCHAR lpApplicationId[49];
1085 GameStatisticsImpl *statisticsImpl = NULL;
1086 IGameStatistics *output_iface;
1088 TRACE("(%p, %s, 0x%x, %p, %p)\n", iface, debugstr_w(GDFBinaryPath), openType, pOpenResult, ppiStats);
1090 hr = GAMEUX_getAppIdFromGDFPath(GDFBinaryPath, lpApplicationId);
1092 if(SUCCEEDED(hr))
1093 hr = create_IGameStatistics(&statisticsImpl);
1095 if(SUCCEEDED(hr))
1097 output_iface = &statisticsImpl->IGameStatistics_iface;
1098 hr = GAMEUX_buildStatisticsFilePath(lpApplicationId, statisticsImpl->stats.sStatsFile);
1101 if(SUCCEEDED(hr))
1102 hr = GAMEUX_loadGameStatistics(&statisticsImpl->stats, lpApplicationId, openType, pOpenResult);
1104 if(SUCCEEDED(hr))
1105 *ppiStats = output_iface;
1106 else
1108 HeapFree(GetProcessHeap(), 0, statisticsImpl);
1109 *ppiStats = NULL;
1112 return hr;
1115 static HRESULT STDMETHODCALLTYPE GameStatisticsMgrImpl_RemoveGameStatistics(
1116 IGameStatisticsMgr* iface,
1117 LPCWSTR GDFBinaryPath)
1119 HRESULT hr;
1120 WCHAR lpApplicationId[49];
1121 WCHAR sStatsFile[MAX_PATH];
1123 TRACE("(%p, %s)\n", iface, debugstr_w(GDFBinaryPath));
1125 hr = GAMEUX_getAppIdFromGDFPath(GDFBinaryPath, lpApplicationId);
1127 if(SUCCEEDED(hr))
1128 hr = GAMEUX_buildStatisticsFilePath(lpApplicationId, sStatsFile);
1130 if(SUCCEEDED(hr))
1131 hr = (DeleteFileW(sStatsFile)==TRUE ? S_OK : HRESULT_FROM_WIN32(GetLastError()));
1133 return hr;
1136 static const struct IGameStatisticsMgrVtbl GameStatisticsMgrImplVtbl =
1138 GameStatisticsMgrImpl_QueryInterface,
1139 GameStatisticsMgrImpl_AddRef,
1140 GameStatisticsMgrImpl_Release,
1141 GameStatisticsMgrImpl_GetGameStatistics,
1142 GameStatisticsMgrImpl_RemoveGameStatistics,
1145 HRESULT GameStatistics_create(
1146 IUnknown *pUnkOuter,
1147 IUnknown **ppObj)
1149 GameStatisticsMgrImpl *pGameStatistics;
1151 TRACE("(%p, %p)\n", pUnkOuter, ppObj);
1153 pGameStatistics = HeapAlloc( GetProcessHeap(), 0, sizeof (*pGameStatistics) );
1155 if( !pGameStatistics )
1156 return E_OUTOFMEMORY;
1158 pGameStatistics->IGameStatisticsMgr_iface.lpVtbl = &GameStatisticsMgrImplVtbl;
1159 pGameStatistics->ref = 1;
1161 *ppObj = (IUnknown*)&pGameStatistics->IGameStatisticsMgr_iface;
1163 TRACE("returning iface %p\n", *ppObj);
1164 return S_OK;