ntdll: Load .so builtin modules without using libwine.
[wine/zf.git] / dlls / gameux / gamestatistics.c
blob7189261a4f63e0c2ed7f897b4629eb43e6ba378a
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
23 #include "ole2.h"
24 #include "winreg.h"
25 #include "msxml2.h"
26 #include "shlwapi.h"
27 #include "shlobj.h"
29 #include "gameux.h"
30 #include "gameux_private.h"
32 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(gameux);
37 * constant definitions
39 #define MAX_CATEGORY_LENGTH 60
40 #define MAX_NAME_LENGTH 30
41 #define MAX_VALUE_LENGTH 30
42 #define MAX_CATEGORIES 10
43 #define MAX_STATS_PER_CATEGORY 10
44 /*******************************************************************************
45 * Game statistics helper components
47 /*******************************************************************************
48 * struct GAMEUX_STATS
50 * set of structures for containing game's data
52 struct GAMEUX_STATS_STAT
54 WCHAR sName[MAX_NAME_LENGTH+1];
55 WCHAR sValue[MAX_VALUE_LENGTH+1];
57 struct GAMEUX_STATS_CATEGORY
59 WCHAR sName[MAX_CATEGORY_LENGTH+1];
60 struct GAMEUX_STATS_STAT stats[MAX_STATS_PER_CATEGORY];
62 struct GAMEUX_STATS
64 WCHAR sStatsFile[MAX_PATH];
65 struct GAMEUX_STATS_CATEGORY categories[MAX_CATEGORIES];
67 /*******************************************************************************
68 * GAMEUX_createStatsDirectory
70 * Helper function, creates directory to store game statistics
72 * Parameters
73 * path [I] path to game statistics file.
74 * base directory of this file will
75 * be created if it doesn't exists
77 static HRESULT GAMEUX_createStatsDirectory(LPCWSTR lpFilePath)
79 HRESULT hr;
80 WCHAR lpDirectoryPath[MAX_PATH];
81 LPCWSTR lpEnd;
83 lpEnd = StrRChrW(lpFilePath, NULL, '\\');
84 lstrcpynW(lpDirectoryPath, lpFilePath, lpEnd-lpFilePath+1);
86 hr = HRESULT_FROM_WIN32(SHCreateDirectoryExW(NULL, lpDirectoryPath, NULL));
88 if(hr == HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) ||
89 hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS))
90 hr = S_FALSE;
92 return hr;
94 /*******************************************************************
95 * GAMEUX_updateStatisticsFile
97 * Helper function updating data stored in statistics file
99 * Parameters:
100 * data [I] pointer to struct containing
101 * statistics data
103 static HRESULT GAMEUX_updateStatisticsFile(struct GAMEUX_STATS *stats)
105 static const WCHAR sStatistics[] = {'S','t','a','t','i','s','t','i','c','s',0};
106 static const WCHAR sCategory[] = {'C','a','t','e','g','o','r','y',0};
107 static const WCHAR sIndex[] = {'I','n','d','e','x',0};
108 static const WCHAR sStatistic[] = {'S','t','a','t','i','s','t','i','c',0};
109 static const WCHAR sName[] = {'N','a','m','e',0};
110 static const WCHAR sValue[] = {'V','a','l','u','e',0};
112 HRESULT hr = S_OK;
113 IXMLDOMDocument *document = NULL;
114 IXMLDOMElement *root = NULL, *statisticsElement = NULL;
115 IXMLDOMNode *categoryNode = NULL, *statisticsNode = NULL;
116 VARIANT vStatsFilePath, vValue;
117 BSTR bstrStatistics = NULL, bstrCategory = NULL, bstrIndex = NULL,
118 bstrStatistic = NULL, bstrName = NULL, bstrValue = NULL;
119 int i, j;
121 TRACE("(%p)\n", stats);
123 V_VT(&vStatsFilePath) = VT_BSTR;
124 V_BSTR(&vStatsFilePath) = SysAllocString(stats->sStatsFile);
125 if(!V_BSTR(&vStatsFilePath))
126 hr = E_OUTOFMEMORY;
128 if(SUCCEEDED(hr))
129 hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
130 &IID_IXMLDOMDocument, (void**)&document);
132 if(SUCCEEDED(hr))
134 bstrStatistics = SysAllocString(sStatistics);
135 if(!bstrStatistics)
136 hr = E_OUTOFMEMORY;
139 if(SUCCEEDED(hr))
140 hr = IXMLDOMDocument_createElement(document, bstrStatistics, &root);
142 if(SUCCEEDED(hr))
144 bstrCategory = SysAllocString(sCategory);
145 if(!bstrCategory)
146 hr = E_OUTOFMEMORY;
149 if(SUCCEEDED(hr))
151 bstrIndex = SysAllocString(sIndex);
152 if(!bstrIndex)
153 hr = E_OUTOFMEMORY;
156 if(SUCCEEDED(hr))
158 bstrStatistic = SysAllocString(sStatistic);
159 if(!bstrStatistic)
160 hr = E_OUTOFMEMORY;
163 if(SUCCEEDED(hr))
165 bstrName = SysAllocString(sName);
166 if(!bstrName)
167 hr = E_OUTOFMEMORY;
170 if(SUCCEEDED(hr))
172 bstrValue = SysAllocString(sValue);
173 if(!bstrValue)
174 hr = E_OUTOFMEMORY;
177 if(SUCCEEDED(hr))
178 for(i=0; i<MAX_CATEGORIES; ++i)
180 IXMLDOMElement *categoryElement = NULL;
182 if(!stats->categories[i].sName[0])
183 continue;
185 V_VT(&vValue) = VT_INT;
186 V_INT(&vValue) = NODE_ELEMENT;
188 hr = IXMLDOMDocument_createNode(document, vValue, bstrCategory, NULL, &categoryNode);
190 if(SUCCEEDED(hr))
191 hr = IXMLDOMNode_QueryInterface(categoryNode, &IID_IXMLDOMElement, (void**)&categoryElement);
193 V_INT(&vValue) = i;
194 if(SUCCEEDED(hr))
195 hr = IXMLDOMElement_setAttribute(categoryElement, bstrIndex, vValue);
197 if(SUCCEEDED(hr))
199 V_VT(&vValue) = VT_BSTR;
200 V_BSTR(&vValue) = SysAllocString(stats->categories[i].sName);
201 if(!V_BSTR(&vValue))
202 hr = E_OUTOFMEMORY;
205 if(SUCCEEDED(hr))
207 TRACE("storing category %d: %s\n", i, debugstr_w(V_BSTR(&vValue)));
208 hr = IXMLDOMElement_setAttribute(categoryElement, bstrName, vValue);
211 if (categoryElement) IXMLDOMElement_Release(categoryElement);
213 SysFreeString(V_BSTR(&vValue));
215 if(SUCCEEDED(hr))
217 for(j=0; j<MAX_STATS_PER_CATEGORY; ++j)
219 if(!stats->categories[i].stats[j].sName[0])
220 continue;
222 V_VT(&vValue) = VT_INT;
223 V_INT(&vValue) = NODE_ELEMENT;
225 hr = IXMLDOMDocument_createNode(document, vValue, bstrStatistic, NULL, &statisticsNode);
227 if(SUCCEEDED(hr))
228 hr = IXMLDOMNode_QueryInterface(statisticsNode, &IID_IXMLDOMElement, (LPVOID*)&statisticsElement);
230 V_INT(&vValue) = j;
231 if(SUCCEEDED(hr))
232 hr = IXMLDOMElement_setAttribute(statisticsElement, bstrIndex, vValue);
234 if(SUCCEEDED(hr))
236 V_VT(&vValue) = VT_BSTR;
237 V_BSTR(&vValue) = SysAllocString(stats->categories[i].stats[j].sName);
238 if(!V_BSTR(&vValue))
239 hr = E_OUTOFMEMORY;
242 if(SUCCEEDED(hr))
244 TRACE(" storing statistic %d: name: %s\n", j, debugstr_w(V_BSTR(&vValue)));
245 hr = IXMLDOMElement_setAttribute(statisticsElement, bstrName, vValue);
246 SysFreeString(V_BSTR(&vValue));
249 if(SUCCEEDED(hr))
251 V_VT(&vValue) = VT_BSTR;
252 V_BSTR(&vValue) = SysAllocString(stats->categories[i].stats[j].sValue);
253 if(!V_BSTR(&vValue))
254 hr = E_OUTOFMEMORY;
257 if(SUCCEEDED(hr))
259 TRACE(" storing statistic %d: name: %s\n", j, debugstr_w(V_BSTR(&vValue)));
260 hr = IXMLDOMElement_setAttribute(statisticsElement, bstrValue, vValue);
261 SysFreeString(V_BSTR(&vValue));
264 if(SUCCEEDED(hr))
265 hr = IXMLDOMNode_appendChild(categoryNode, statisticsNode, NULL);
267 if (statisticsElement) IXMLDOMElement_Release(statisticsElement);
268 if (statisticsNode) IXMLDOMNode_Release(statisticsNode);
272 if(SUCCEEDED(hr))
273 hr = IXMLDOMElement_appendChild(root, categoryNode, &categoryNode);
275 if (categoryNode)
276 IXMLDOMNode_Release(categoryNode);
278 if(FAILED(hr))
279 break;
282 if(SUCCEEDED(hr))
283 hr = IXMLDOMDocument_putref_documentElement(document, root);
285 if (root) IXMLDOMElement_Release(root);
287 TRACE("saving game statistics in %s file\n", debugstr_w(stats->sStatsFile));
288 if(SUCCEEDED(hr))
289 hr = GAMEUX_createStatsDirectory(stats->sStatsFile);
291 if(SUCCEEDED(hr))
292 hr = IXMLDOMDocument_save(document, vStatsFilePath);
294 if (document) IXMLDOMDocument_Release(document);
296 SysFreeString(bstrValue);
297 SysFreeString(bstrName);
298 SysFreeString(bstrStatistic);
299 SysFreeString(bstrIndex);
300 SysFreeString(bstrCategory);
301 SysFreeString(bstrStatistics);
302 SysFreeString(V_BSTR(&vStatsFilePath));
303 TRACE("ret=0x%x\n", hr);
304 return hr;
306 /*******************************************************************************
307 * GAMEUX_buildStatisticsFilePath
308 * Creates path to file containing statistics of game with given id.
310 * Parameters:
311 * lpApplicationId [I] application id of game,
312 * as string
313 * lpStatisticsFile [O] array where path will be
314 * stored. Its size must be
315 * at least MAX_PATH
317 static HRESULT GAMEUX_buildStatisticsFilePath(
318 LPCWSTR lpApplicationId,
319 LPWSTR lpStatisticsFile)
321 static const WCHAR sBackslash[] = {'\\',0};
322 static const WCHAR sStatisticsDir[] = {'\\','M','i','c','r','o','s','o','f','t',
323 '\\','W','i','n','d','o','w','s','\\','G','a','m','e','E','x','p',
324 'l','o','r','e','r','\\','G','a','m','e','S','t','a','t','i','s',
325 't','i','c','s','\\',0};
326 static const WCHAR sDotGamestats[] = {'.','g','a','m','e','s','t','a','t','s',0};
328 HRESULT hr;
330 hr = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, lpStatisticsFile);
332 if(SUCCEEDED(hr))
334 lstrcatW(lpStatisticsFile, sStatisticsDir);
335 lstrcatW(lpStatisticsFile, lpApplicationId);
336 lstrcatW(lpStatisticsFile, sBackslash);
337 lstrcatW(lpStatisticsFile, lpApplicationId);
338 lstrcatW(lpStatisticsFile, sDotGamestats);
341 return hr;
343 /*******************************************************************************
344 * GAMEUX_getAppIdFromGDFPath
346 * Loads application identifier associated with given GDF binary.
347 * Routine reads identifier from registry, so will fail if game
348 * is not registered.
350 * Parameters:
351 * GDFBinaryPath [I] path to gdf binary
352 * lpApplicationId [O] place to store application id.
353 * must be at least 49 characters
354 * to store guid and termination 0
356 static HRESULT GAMEUX_getAppIdFromGDFPath(
357 LPCWSTR GDFBinaryPath,
358 LPWSTR lpApplicationId)
360 static const WCHAR sApplicationId[] =
361 {'A','p','p','l','i','c','a','t','i','o','n','I','d',0};
363 HRESULT hr;
364 GAME_INSTALL_SCOPE installScope;
365 GUID instanceId;
366 LPWSTR lpRegistryPath = NULL;
367 HKEY hKey;
368 DWORD dwLength = 49*sizeof(WCHAR);/* place for GUID */
370 TRACE("(%s, %p)\n", debugstr_w(GDFBinaryPath), lpApplicationId);
372 if(!GDFBinaryPath)
373 return E_INVALIDARG;
375 installScope = GIS_CURRENT_USER;
376 hr = GAMEUX_FindGameInstanceId(GDFBinaryPath, installScope, &instanceId);
378 if(hr == S_FALSE)
380 installScope = GIS_ALL_USERS;
381 hr = GAMEUX_FindGameInstanceId(GDFBinaryPath, installScope, &instanceId);
384 if(hr == S_FALSE)
385 /* game not registered, so statistics cannot be used */
386 hr = E_FAIL;
388 if(SUCCEEDED(hr))
389 /* game is registered, let's read its application id from registry */
390 hr = GAMEUX_buildGameRegistryPath(installScope, &instanceId, &lpRegistryPath);
392 if(SUCCEEDED(hr)) {
393 hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE,
394 lpRegistryPath, 0, KEY_READ | KEY_WOW64_64KEY, &hKey));
395 if(SUCCEEDED(hr)) {
396 hr = HRESULT_FROM_WIN32(RegGetValueW(hKey,
397 NULL, sApplicationId, RRF_RT_REG_SZ,
398 NULL, lpApplicationId, &dwLength));
399 RegCloseKey(hKey);
403 HeapFree(GetProcessHeap(), 0, lpRegistryPath);
405 TRACE("found app id: %s, return: %#x\n", debugstr_w(lpApplicationId), hr);
406 return hr;
408 /*******************************************************************
409 * GAMEUX_loadGameStatisticsFromFile
410 * Helper function, loads game statistics from file and stores them
411 * in the structure.
413 * Parameters:
414 * data [I/O] structure containing file name to
415 * load and data fields to store data in
417 static HRESULT GAMEUX_loadStatisticsFromFile(struct GAMEUX_STATS *data)
419 static const WCHAR sStatistics[] = {'S','t','a','t','i','s','t','i','c','s',0};
420 static const WCHAR sCategory[] = {'C','a','t','e','g','o','r','y',0};
421 static const WCHAR sIndex[] = {'I','n','d','e','x',0};
422 static const WCHAR sStatistic[] = {'S','t','a','t','i','s','t','i','c',0};
423 static const WCHAR sName[] = {'N','a','m','e',0};
424 static const WCHAR sValue[] = {'V','a','l','u','e',0};
426 HRESULT hr = S_OK;
427 IXMLDOMDocument *document = NULL;
428 IXMLDOMElement *root = NULL, *categoryElement = NULL, *statisticElement = NULL;
429 IXMLDOMNode *categoryNode = NULL, *statisticNode = NULL;
430 IXMLDOMNodeList *rootChildren = NULL, *categoryChildren = NULL;
431 VARIANT vStatsFilePath, vValue;
432 BSTR bstrStatistics = NULL, bstrCategory = NULL, bstrIndex = NULL,
433 bstrStatistic = NULL, bstrName = NULL, bstrValue = NULL;
434 VARIANT_BOOL isSuccessful = VARIANT_FALSE;
435 int i, j;
437 TRACE("(%p)\n", data);
439 V_VT(&vStatsFilePath) = VT_BSTR;
440 V_BSTR(&vStatsFilePath) = SysAllocString(data->sStatsFile);
441 if(!V_BSTR(&vStatsFilePath))
442 hr = E_OUTOFMEMORY;
444 if(SUCCEEDED(hr))
445 hr = CoCreateInstance(&CLSID_DOMDocument30, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, (void**)&document);
447 if(SUCCEEDED(hr))
449 bstrStatistics = SysAllocString(sStatistics);
450 if(!bstrStatistics)
451 hr = E_OUTOFMEMORY;
454 if(SUCCEEDED(hr))
456 bstrCategory = SysAllocString(sCategory);
457 if(!bstrCategory)
458 hr = E_OUTOFMEMORY;
461 if(SUCCEEDED(hr))
463 bstrIndex = SysAllocString(sIndex);
464 if(!bstrIndex)
465 hr = E_OUTOFMEMORY;
468 if(SUCCEEDED(hr))
470 bstrStatistic = SysAllocString(sStatistic);
471 if(!bstrStatistic)
472 hr = E_OUTOFMEMORY;
475 if(SUCCEEDED(hr))
477 bstrName = SysAllocString(sName);
478 if(!bstrName)
479 hr = E_OUTOFMEMORY;
482 if(SUCCEEDED(hr))
484 bstrValue = SysAllocString(sValue);
485 if(!bstrValue)
486 hr = E_OUTOFMEMORY;
489 if(SUCCEEDED(hr))
490 hr = IXMLDOMDocument_load(document, vStatsFilePath, &isSuccessful);
492 if(hr == S_OK && isSuccessful != VARIANT_TRUE)
493 hr = S_FALSE;
495 if( hr == S_OK )
496 hr = IXMLDOMDocument_get_documentElement(document, &root);
498 if(hr == S_OK)
499 hr = IXMLDOMElement_get_childNodes(root, &rootChildren);
501 if(hr == S_OK)
503 hr = S_OK;
504 while(hr == S_OK)
506 hr = IXMLDOMNodeList_nextNode(rootChildren, &categoryNode);
508 if(hr == S_OK)
510 hr = IXMLDOMNode_QueryInterface(categoryNode, &IID_IXMLDOMElement, (LPVOID*)&categoryElement);
512 if(SUCCEEDED(hr))
514 hr = IXMLDOMElement_getAttribute(categoryElement, bstrIndex, &vValue);
515 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
516 hr = E_FAIL;
518 if(SUCCEEDED(hr))
520 i = StrToIntW(V_BSTR(&vValue));
521 hr = IXMLDOMElement_getAttribute(categoryElement, bstrName, &vValue);
522 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
523 hr = E_FAIL;
526 if(SUCCEEDED(hr))
528 lstrcpynW(data->categories[i].sName, V_BSTR(&vValue), MAX_CATEGORY_LENGTH);
529 TRACE("category %d name %s\n", i, debugstr_w(data->categories[i].sName));
530 hr = IXMLDOMElement_get_childNodes(categoryElement, &categoryChildren);
533 if(SUCCEEDED(hr))
535 hr = S_OK;
536 while(hr == S_OK)
538 hr = IXMLDOMNodeList_nextNode(categoryChildren, &statisticNode);
540 if(hr == S_OK)
542 hr = IXMLDOMNode_QueryInterface(statisticNode, &IID_IXMLDOMElement, (LPVOID*)&statisticElement);
544 if(SUCCEEDED(hr))
546 hr = IXMLDOMElement_getAttribute(statisticElement, bstrIndex, &vValue);
547 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
548 hr = E_FAIL;
550 if(SUCCEEDED(hr))
552 j = StrToIntW(V_BSTR(&vValue));
553 hr = IXMLDOMElement_getAttribute(statisticElement, bstrName, &vValue);
554 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
555 hr = E_FAIL;
558 if(SUCCEEDED(hr))
560 lstrcpynW(data->categories[i].stats[j].sName, V_BSTR(&vValue), MAX_NAME_LENGTH);
561 hr = IXMLDOMElement_getAttribute(statisticElement, bstrValue, &vValue);
562 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
563 hr = E_FAIL;
566 if(SUCCEEDED(hr))
568 lstrcpynW(data->categories[i].stats[j].sValue, V_BSTR(&vValue), MAX_VALUE_LENGTH);
569 TRACE("statistic %d name %s value %s\n", j,
570 debugstr_w(data->categories[i].stats[j].sName),
571 debugstr_w(data->categories[i].stats[j].sValue));
573 if (statisticElement) IXMLDOMElement_Release(statisticElement);
576 if (statisticNode) IXMLDOMNode_Release(statisticNode);
580 if (categoryChildren) IXMLDOMNodeList_Release(categoryChildren);
582 if(SUCCEEDED(hr))
583 hr = S_OK;
585 if (categoryElement) IXMLDOMElement_Release(categoryElement);
588 if (categoryNode) IXMLDOMNode_Release(categoryNode);
591 if(SUCCEEDED(hr))
592 hr = S_OK;
595 if(rootChildren) IXMLDOMNodeList_Release(rootChildren);
596 if(root) IXMLDOMElement_Release(root);
597 if(document) IXMLDOMDocument_Release(document);
599 SysFreeString(bstrValue);
600 SysFreeString(bstrName);
601 SysFreeString(bstrStatistic);
602 SysFreeString(bstrIndex);
603 SysFreeString(bstrCategory);
604 SysFreeString(bstrStatistics);
605 SysFreeString(V_BSTR(&vStatsFilePath));
606 return hr;
608 /*******************************************************************
609 * GAMEUX_loadGameStatistics
611 * Helper function which loads game statistics associated with game
612 * into interface's internal structures
614 * Parameters:
615 * pStats [O] structure which will receive data
616 * sGameId [I] application instance Id, stored as string
617 * to avoid additional conversions
618 * openType [I] allowed ways of opening statistics
619 * pOpenResult [O] way used to open statistics
622 static HRESULT GAMEUX_loadGameStatistics(struct GAMEUX_STATS *pStats,
623 LPWSTR sGameId,
624 GAMESTATS_OPEN_TYPE openType,
625 GAMESTATS_OPEN_RESULT* pOpenResult)
627 HRESULT hr;
628 TRACE("(%p, %s, %d, %p)\n", pStats, debugstr_w(sGameId), openType, pOpenResult);
630 hr = GAMEUX_buildStatisticsFilePath(sGameId, pStats->sStatsFile);
631 if (FAILED(hr)) return hr;
633 hr = GAMEUX_loadStatisticsFromFile(pStats);
634 TRACE("ldstats finished, res: %#x\n", hr);
635 if(hr == S_OK)
637 *pOpenResult = GAMESTATS_OPEN_OPENED;
639 else if(hr == S_FALSE && openType == GAMESTATS_OPEN_OPENORCREATE) /* file does not exist */
641 /* create new statistics, not yet connected with file */
642 ZeroMemory(pStats->categories, sizeof(pStats->categories));
643 *pOpenResult = GAMESTATS_OPEN_CREATED;
644 hr = S_OK;
646 else
647 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
649 TRACE("openResult=%#x ret=%#x\n", *pOpenResult, hr);
650 return hr;
652 /*******************************************************************
653 * IGameStatistics implementation
655 typedef struct _GameStatisticsImpl
657 IGameStatistics IGameStatistics_iface;
658 LONG ref;
659 struct GAMEUX_STATS stats;
660 } GameStatisticsImpl;
662 static inline GameStatisticsImpl *impl_from_IGameStatistics( IGameStatistics *iface )
664 return CONTAINING_RECORD(iface, GameStatisticsImpl, IGameStatistics_iface);
667 static HRESULT WINAPI GameStatisticsImpl_QueryInterface(
668 IGameStatistics *iface,
669 REFIID riid,
670 void **ppvObject)
672 GameStatisticsImpl *This = impl_from_IGameStatistics( iface );
674 TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppvObject );
676 *ppvObject = NULL;
678 if ( IsEqualGUID( riid, &IID_IUnknown ) ||
679 IsEqualGUID( riid, &IID_IGameStatistics ) )
681 *ppvObject = iface;
683 else
685 FIXME("interface %s not implemented\n", debugstr_guid(riid));
686 return E_NOINTERFACE;
689 IGameStatistics_AddRef( iface );
690 return S_OK;
693 static ULONG WINAPI GameStatisticsImpl_AddRef(IGameStatistics *iface)
695 GameStatisticsImpl *This = impl_from_IGameStatistics( iface );
696 LONG ref;
698 ref = InterlockedIncrement(&This->ref);
700 TRACE("(%p): ref=%d\n", This, ref);
701 return ref;
704 static ULONG WINAPI GameStatisticsImpl_Release(IGameStatistics *iface)
706 GameStatisticsImpl *This = impl_from_IGameStatistics( iface );
707 LONG ref;
709 ref = InterlockedDecrement( &This->ref );
710 TRACE("(%p): ref=%d\n", This, ref);
712 if ( ref == 0 )
714 TRACE("freeing IGameStatistics\n");
715 HeapFree( GetProcessHeap(), 0, This );
718 return ref;
721 static HRESULT WINAPI GameStatisticsImpl_GetMaxCategoryLength(
722 IGameStatistics *iface,
723 UINT *cch)
725 TRACE("(%p, %p)\n", iface, cch);
726 if(!cch)
727 return E_INVALIDARG;
729 *cch = MAX_CATEGORY_LENGTH;
730 return S_OK;
733 static HRESULT WINAPI GameStatisticsImpl_GetMaxNameLength(
734 IGameStatistics *iface,
735 UINT *cch)
737 TRACE("(%p, %p)\n", iface, cch);
738 if(!cch)
739 return E_INVALIDARG;
741 *cch = MAX_NAME_LENGTH;
742 return S_OK;
745 static HRESULT WINAPI GameStatisticsImpl_GetMaxValueLength(
746 IGameStatistics *iface,
747 UINT *cch)
749 TRACE("(%p, %p)\n", iface, cch);
750 if(!cch)
751 return E_INVALIDARG;
753 *cch = MAX_VALUE_LENGTH;
754 return S_OK;
757 static HRESULT WINAPI GameStatisticsImpl_GetMaxCategories(
758 IGameStatistics *iface,
759 WORD *pMax)
761 TRACE("(%p, %p)\n", iface, pMax);
762 if(!pMax)
763 return E_INVALIDARG;
765 *pMax = MAX_CATEGORIES;
766 return S_OK;
769 static HRESULT WINAPI GameStatisticsImpl_GetMaxStatsPerCategory(
770 IGameStatistics *iface,
771 WORD *pMax)
773 TRACE("(%p, %p)\n", iface, pMax);
774 if(!pMax)
775 return E_INVALIDARG;
777 *pMax = MAX_STATS_PER_CATEGORY;
778 return S_OK;
781 static HRESULT WINAPI GameStatisticsImpl_SetCategoryTitle(
782 IGameStatistics *iface,
783 WORD categoryIndex,
784 LPCWSTR title)
786 HRESULT hr = S_OK;
787 DWORD dwLength;
788 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
790 TRACE("(%p, %d, %s)\n", This, categoryIndex, debugstr_w(title));
792 if(!title || categoryIndex >= MAX_CATEGORIES)
793 return E_INVALIDARG;
795 dwLength = lstrlenW(title);
797 if(dwLength > MAX_CATEGORY_LENGTH)
799 hr = S_FALSE;
800 dwLength = MAX_CATEGORY_LENGTH;
803 lstrcpynW(This->stats.categories[categoryIndex].sName,
804 title, dwLength+1);
806 return hr;
809 static HRESULT WINAPI GameStatisticsImpl_GetCategoryTitle(
810 IGameStatistics *iface,
811 WORD categoryIndex,
812 LPWSTR *pTitle)
814 HRESULT hr = S_OK;
815 LONG nLength;
816 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
818 TRACE("%p, %d, %p\n", This, categoryIndex, pTitle);
820 if(!pTitle)
821 return E_INVALIDARG;
822 *pTitle = NULL;
824 if (categoryIndex >= MAX_CATEGORIES)
825 hr = E_INVALIDARG;
827 if(SUCCEEDED(hr))
829 nLength = lstrlenW(This->stats.categories[categoryIndex].sName);
830 if(nLength != 0)
832 *pTitle = CoTaskMemAlloc(sizeof(WCHAR)*(nLength+1));
833 lstrcpyW(*pTitle, This->stats.categories[categoryIndex].sName);
837 return hr;
840 static HRESULT WINAPI GameStatisticsImpl_GetStatistic(
841 IGameStatistics *iface,
842 WORD categoryIndex,
843 WORD statIndex,
844 LPWSTR *pName,
845 LPWSTR *pValue)
847 HRESULT hr = S_OK;
848 LONG nLength;
849 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
851 TRACE("%p, %d,%d, %p, %p\n", This, categoryIndex, statIndex, pName, pValue);
853 if(!pName || !pValue)
854 return E_INVALIDARG;
856 *pName = NULL;
857 *pValue = NULL;
859 if(categoryIndex >= MAX_CATEGORIES || statIndex >= MAX_STATS_PER_CATEGORY)
860 hr = E_INVALIDARG;
862 if(SUCCEEDED(hr))
864 nLength = lstrlenW(This->stats.categories[categoryIndex].stats[statIndex].sName);
865 if(nLength != 0)
867 *pName = CoTaskMemAlloc(sizeof(WCHAR)*(nLength+1));
868 if(!(*pName))
869 hr = E_OUTOFMEMORY;
870 else
871 lstrcpyW(*pName, This->stats.categories[categoryIndex].stats[statIndex].sName);
875 if(SUCCEEDED(hr))
877 nLength = lstrlenW(This->stats.categories[categoryIndex].stats[statIndex].sValue);
878 if(nLength != 0)
880 *pValue = CoTaskMemAlloc(sizeof(WCHAR)*(nLength+1));
881 if(!(*pValue))
882 hr = E_OUTOFMEMORY;
883 else
884 lstrcpyW(*pValue, This->stats.categories[categoryIndex].stats[statIndex].sValue);
888 TRACE("returning pair; %s => %s\n", debugstr_w(*pName), debugstr_w(*pValue));
889 return hr;
892 static HRESULT WINAPI GameStatisticsImpl_SetStatistic(
893 IGameStatistics *iface,
894 WORD categoryIndex,
895 WORD statIndex,
896 LPCWSTR name,
897 LPCWSTR value)
899 HRESULT hr = S_OK;
900 DWORD dwNameLen, dwValueLen;
901 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
903 TRACE("(%p, %d, %d, %s, %s)\n", This, categoryIndex, statIndex,
904 debugstr_w(name), debugstr_w(value));
906 if(!name)
907 return S_FALSE;
909 if(categoryIndex >= MAX_CATEGORIES || statIndex >= MAX_STATS_PER_CATEGORY)
910 return E_INVALIDARG;
912 dwNameLen = lstrlenW(name);
914 if(dwNameLen > MAX_NAME_LENGTH)
916 hr = S_FALSE;
917 dwNameLen = MAX_NAME_LENGTH;
920 lstrcpynW(This->stats.categories[categoryIndex].stats[statIndex].sName,
921 name, dwNameLen+1);
923 if(value)
925 dwValueLen = lstrlenW(value);
927 if(dwValueLen > MAX_VALUE_LENGTH)
929 hr = S_FALSE;
930 dwValueLen = MAX_VALUE_LENGTH;
933 lstrcpynW(This->stats.categories[categoryIndex].stats[statIndex].sValue,
934 value, dwValueLen+1);
936 else
937 /* Windows allows passing NULL as value */
938 This->stats.categories[categoryIndex].stats[statIndex].sValue[0] = 0;
940 return hr;
943 static HRESULT WINAPI GameStatisticsImpl_Save(
944 IGameStatistics *iface,
945 BOOL trackChanges)
947 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
949 TRACE("(%p, %d)\n", This, trackChanges);
951 if(trackChanges)
952 FIXME("tracking changes not yet implemented\n");
954 return GAMEUX_updateStatisticsFile(&This->stats);
957 static HRESULT WINAPI GameStatisticsImpl_SetLastPlayedCategory(
958 IGameStatistics *iface,
959 UINT categoryIndex)
961 FIXME("stub\n");
962 return E_NOTIMPL;
965 static HRESULT WINAPI GameStatisticsImpl_GetLastPlayedCategory(
966 IGameStatistics *iface,
967 UINT *pCategoryIndex)
969 FIXME("stub\n");
970 return E_NOTIMPL;
973 static const struct IGameStatisticsVtbl GameStatisticsImplVtbl =
975 GameStatisticsImpl_QueryInterface,
976 GameStatisticsImpl_AddRef,
977 GameStatisticsImpl_Release,
978 GameStatisticsImpl_GetMaxCategoryLength,
979 GameStatisticsImpl_GetMaxNameLength,
980 GameStatisticsImpl_GetMaxValueLength,
981 GameStatisticsImpl_GetMaxCategories,
982 GameStatisticsImpl_GetMaxStatsPerCategory,
983 GameStatisticsImpl_SetCategoryTitle,
984 GameStatisticsImpl_GetCategoryTitle,
985 GameStatisticsImpl_GetStatistic,
986 GameStatisticsImpl_SetStatistic,
987 GameStatisticsImpl_Save,
988 GameStatisticsImpl_SetLastPlayedCategory,
989 GameStatisticsImpl_GetLastPlayedCategory
993 static HRESULT create_IGameStatistics(GameStatisticsImpl** ppStats)
995 TRACE("(%p)\n", ppStats);
997 *ppStats = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(**ppStats));
998 if(!(*ppStats))
999 return E_OUTOFMEMORY;
1001 (*ppStats)->IGameStatistics_iface.lpVtbl = &GameStatisticsImplVtbl;
1002 (*ppStats)->ref = 1;
1004 TRACE("returning coclass: %p\n", *ppStats);
1005 return S_OK;
1008 /*******************************************************************************
1009 * IGameStatisticsMgr implementation
1011 typedef struct _GameStatisticsMgrImpl
1013 IGameStatisticsMgr IGameStatisticsMgr_iface;
1014 LONG ref;
1015 } GameStatisticsMgrImpl;
1017 static inline GameStatisticsMgrImpl *impl_from_IGameStatisticsMgr( IGameStatisticsMgr *iface )
1019 return CONTAINING_RECORD(iface, GameStatisticsMgrImpl, IGameStatisticsMgr_iface);
1023 static HRESULT WINAPI GameStatisticsMgrImpl_QueryInterface(
1024 IGameStatisticsMgr *iface,
1025 REFIID riid,
1026 void **ppvObject)
1028 GameStatisticsMgrImpl *This = impl_from_IGameStatisticsMgr( iface );
1030 TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppvObject );
1032 *ppvObject = NULL;
1034 if(IsEqualGUID(riid, &IID_IUnknown) ||
1035 IsEqualGUID(riid, &IID_IGameStatisticsMgr) )
1037 *ppvObject = iface;
1039 else
1041 FIXME("interface %s not implemented\n", debugstr_guid(riid));
1042 return E_NOINTERFACE;
1045 IGameStatisticsMgr_AddRef( iface );
1046 return S_OK;
1049 static ULONG WINAPI GameStatisticsMgrImpl_AddRef(IGameStatisticsMgr *iface)
1051 GameStatisticsMgrImpl *This = impl_from_IGameStatisticsMgr( iface );
1052 LONG ref;
1054 ref = InterlockedIncrement(&This->ref);
1056 TRACE("(%p): ref=%d\n", This, ref);
1057 return ref;
1060 static ULONG WINAPI GameStatisticsMgrImpl_Release(IGameStatisticsMgr *iface)
1062 GameStatisticsMgrImpl *This = impl_from_IGameStatisticsMgr( iface );
1063 LONG ref;
1065 ref = InterlockedDecrement(&This->ref);
1066 TRACE("(%p): ref=%d\n", This, ref);
1068 if ( ref == 0 )
1070 TRACE("freeing GameStatistics object\n");
1071 HeapFree( GetProcessHeap(), 0, This);
1074 return ref;
1077 static HRESULT STDMETHODCALLTYPE GameStatisticsMgrImpl_GetGameStatistics(
1078 IGameStatisticsMgr* iface,
1079 LPCWSTR GDFBinaryPath,
1080 GAMESTATS_OPEN_TYPE openType,
1081 GAMESTATS_OPEN_RESULT *pOpenResult,
1082 IGameStatistics **ppiStats)
1084 HRESULT hr;
1085 WCHAR lpApplicationId[49];
1086 GameStatisticsImpl *statisticsImpl = NULL;
1087 IGameStatistics *output_iface;
1089 TRACE("(%p, %s, 0x%x, %p, %p)\n", iface, debugstr_w(GDFBinaryPath), openType, pOpenResult, ppiStats);
1091 hr = GAMEUX_getAppIdFromGDFPath(GDFBinaryPath, lpApplicationId);
1093 if(SUCCEEDED(hr))
1094 hr = create_IGameStatistics(&statisticsImpl);
1096 if(SUCCEEDED(hr))
1098 output_iface = &statisticsImpl->IGameStatistics_iface;
1099 hr = GAMEUX_buildStatisticsFilePath(lpApplicationId, statisticsImpl->stats.sStatsFile);
1102 if(SUCCEEDED(hr))
1103 hr = GAMEUX_loadGameStatistics(&statisticsImpl->stats, lpApplicationId, openType, pOpenResult);
1105 if(SUCCEEDED(hr))
1106 *ppiStats = output_iface;
1107 else
1109 HeapFree(GetProcessHeap(), 0, statisticsImpl);
1110 *ppiStats = NULL;
1113 return hr;
1116 static HRESULT STDMETHODCALLTYPE GameStatisticsMgrImpl_RemoveGameStatistics(
1117 IGameStatisticsMgr* iface,
1118 LPCWSTR GDFBinaryPath)
1120 HRESULT hr;
1121 WCHAR lpApplicationId[49];
1122 WCHAR sStatsFile[MAX_PATH];
1124 TRACE("(%p, %s)\n", iface, debugstr_w(GDFBinaryPath));
1126 hr = GAMEUX_getAppIdFromGDFPath(GDFBinaryPath, lpApplicationId);
1128 if(SUCCEEDED(hr))
1129 hr = GAMEUX_buildStatisticsFilePath(lpApplicationId, sStatsFile);
1131 if(SUCCEEDED(hr))
1132 hr = DeleteFileW(sStatsFile) ? S_OK : HRESULT_FROM_WIN32(GetLastError());
1134 return hr;
1137 static const struct IGameStatisticsMgrVtbl GameStatisticsMgrImplVtbl =
1139 GameStatisticsMgrImpl_QueryInterface,
1140 GameStatisticsMgrImpl_AddRef,
1141 GameStatisticsMgrImpl_Release,
1142 GameStatisticsMgrImpl_GetGameStatistics,
1143 GameStatisticsMgrImpl_RemoveGameStatistics,
1146 HRESULT GameStatistics_create(
1147 IUnknown *pUnkOuter,
1148 IUnknown **ppObj)
1150 GameStatisticsMgrImpl *pGameStatistics;
1152 TRACE("(%p, %p)\n", pUnkOuter, ppObj);
1154 pGameStatistics = HeapAlloc( GetProcessHeap(), 0, sizeof (*pGameStatistics) );
1156 if( !pGameStatistics )
1157 return E_OUTOFMEMORY;
1159 pGameStatistics->IGameStatisticsMgr_iface.lpVtbl = &GameStatisticsMgrImplVtbl;
1160 pGameStatistics->ref = 1;
1162 *ppObj = (IUnknown*)&pGameStatistics->IGameStatisticsMgr_iface;
1164 TRACE("returning iface %p\n", *ppObj);
1165 return S_OK;