1 /*
2 * Copyright © 2003 Callum McKenzie <callum@physics.otago.ac.nz>
3 * Copyright © 2007 Christian Persch
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <config.h>
20
21 #include <string.h>
22 #include <errno.h>
23
24 #include "ar-string-utils.h"
25
26 #include "util.h"
27 #include "conf.h"
28
29 #ifdef HAVE_GNOME
30 #include <gconf/gconf-client.h>
31 #endif
32
33 #ifdef HAVE_GNOME
34 static const char key_names[] =
35 "card_style\0"
36 "game_file\0"
37 "recent_games_list\0"
38 "show_toolbar\0"
39 "click_to_move\0"
40 "sound\0"
41 "show_statusbar\0"
42 "animations";
43
44 static const guint8 key_name_offsets[] = {
45 0, 11, 21, 39, 52, 66, 72, 87
46 };
47
48 static const char statistics_key[] = "/apps/aisleriot/statistics";
49
50 #else
51
52 static const char key_names[] =
53 "Theme\0"
54 "Variation\0"
55 "Recent\0"
56 "ShowToolbar\0"
57 "ClickToMove\0"
58 "Sound\0"
59 "ShowStatusbar\0"
60 "Animations";
61
62 static const guint8 key_name_offsets[] = {
63 0, 6, 16, 23, 35, 47, 53, 67
64 };
65
66 #endif /* HAVE_GNOME */
67
68 static char *
game_module_to_game_name(const char * game_module)69 game_module_to_game_name (const char *game_module)
70 {
71 char *game_name;
72
73 game_name = g_strdelimit (g_strconcat (game_module, ".scm", NULL), "-", '_');
74
75 return game_name;
76 }
77
78 #ifdef HAVE_GNOME
79
80 static GConfClient *gconf_client;
81 static GHashTable *stats;
82
83 static char *
options_gconf_key(const char * game_module)84 options_gconf_key (const char *game_module)
85 {
86 static const char basekey[] = "/apps/aisleriot/rules/";
87
88 return g_strdelimit (g_strconcat (basekey, game_module, ".scm", NULL), "-", '_');
89 }
90
91 static void
load_statistics(void)92 load_statistics (void)
93 {
94 GSList *raw_list;
95 AisleriotStatistic *new_stats;
96 guint64 value;
97
98 raw_list = gconf_client_get_list (gconf_client, statistics_key,
99 GCONF_VALUE_STRING, NULL);
100
101 while (raw_list) {
102 new_stats = g_hash_table_lookup (stats, raw_list->data);
103
104 if (!new_stats) {
105 new_stats = g_new (AisleriotStatistic, 1);
106 new_stats->wins = 0;
107 new_stats->total = 0;
108 new_stats->best = 0;
109 new_stats->worst = 0;
110 g_hash_table_insert (stats, raw_list->data, new_stats);
111 } else {
112 g_free (raw_list->data);
113 }
114
115 raw_list = g_slist_delete_link (raw_list, raw_list);
116
117 if (!raw_list)
118 break;
119 new_stats->wins = g_ascii_strtoull (raw_list->data, NULL, 10);
120 g_free (raw_list->data);
121 raw_list = g_slist_delete_link (raw_list, raw_list);
122
123 if (!raw_list)
124 break;
125 new_stats->total = g_ascii_strtoull (raw_list->data, NULL, 10);
126 g_free (raw_list->data);
127 raw_list = g_slist_delete_link (raw_list, raw_list);
128
129 if (!raw_list)
130 break;
131 value = g_ascii_strtoull (raw_list->data, NULL, 10);
132 /* Sanitise value to fix statistics from bug #474615 */
133 if (value > 0 && value <= 6000) {
134 new_stats->best = value;
135 } else {
136 new_stats->best = 0;
137 }
138 g_free (raw_list->data);
139 raw_list = g_slist_delete_link (raw_list, raw_list);
140
141 if (!raw_list)
142 break;
143 value = g_ascii_strtoull (raw_list->data, NULL, 10);
144 /* Sanitise value to fix statistics from bug #474615 */
145 if (value > 0 && value <= 6000) {
146 new_stats->worst = value;
147 } else {
148 new_stats->worst = 0;
149 }
150 g_free (raw_list->data);
151 raw_list = g_slist_delete_link (raw_list, raw_list);
152 }
153 }
154
155 static void
save_single_stat(char * name,AisleriotStatistic * entry,GSList ** list)156 save_single_stat (char *name,
157 AisleriotStatistic *entry,
158 GSList **list)
159 {
160 /* Everything is pushed onto the list in reverse order. */
161 *list = g_slist_prepend (*list, g_strdup_printf ("%d", entry->worst));
162 *list = g_slist_prepend (*list, g_strdup_printf ("%d", entry->best));
163 *list = g_slist_prepend (*list, g_strdup_printf ("%d", entry->total));
164 *list = g_slist_prepend (*list, g_strdup_printf ("%d", entry->wins));
165 *list = g_slist_prepend (*list, g_strdup (name));
166 }
167
168 static void
save_statistics(void)169 save_statistics (void)
170 {
171 GSList *stats_list = NULL;
172
173 g_hash_table_foreach (stats, (GHFunc) save_single_stat, &stats_list);
174
175 gconf_client_set_list (gconf_client, statistics_key,
176 GCONF_VALUE_STRING, stats_list, NULL);
177
178 g_slist_foreach (stats_list, (GFunc) g_free, NULL);
179 g_slist_free (stats_list);
180 }
181
182 #endif /* HAVE_GNOME */
183
184 void
aisleriot_conf_init(void)185 aisleriot_conf_init (void)
186 {
187 if (!ar_conf_initialise ("Aisleriot")) {
188 /* Set defaults */
189 ar_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_SHOW_TOOLBAR), TRUE);
190 ar_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_SHOW_STATUSBAR), TRUE);
191 ar_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_SOUND), TRUE);
192 ar_conf_set_boolean (NULL, aisleriot_conf_get_key (CONF_ANIMATIONS), TRUE);
193 }
194
195 #ifdef HAVE_GNOME
196
197 gconf_client = gconf_client_get_default ();
198
199 stats = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
200
201 load_statistics ();
202 gconf_client_notify_add (gconf_client, statistics_key,
203 (GConfClientNotifyFunc) load_statistics,
204 NULL, NULL, NULL);
205
206 #endif /* HAVE_GNOME */
207 }
208
209 void
aisleriot_conf_shutdown(void)210 aisleriot_conf_shutdown (void)
211 {
212 #ifdef HAVE_GNOME
213 g_object_unref (gconf_client);
214 gconf_client = NULL;
215
216 g_hash_table_destroy (stats);
217 stats = NULL;
218 #endif /* HAVE_GNOME */
219
220 ar_conf_shutdown ();
221 }
222
223 const char *
aisleriot_conf_get_key(AisleriotConfKey key)224 aisleriot_conf_get_key (AisleriotConfKey key)
225 {
226 return key_names + key_name_offsets[key];
227 }
228
229 gboolean
aisleriot_conf_get_options(const char * game_module,int * options)230 aisleriot_conf_get_options (const char *game_module,
231 int *options)
232 {
233 #ifdef HAVE_GNOME
234 GConfEntry *entry;
235 GConfValue *value;
236 char *gconf_key;
237
238 gconf_key = options_gconf_key (game_module);
239 entry = gconf_client_get_entry (gconf_client, gconf_key, NULL, TRUE, NULL);
240 g_free (gconf_key);
241 if (!entry)
242 return FALSE;
243
244 value = gconf_entry_get_value (entry);
245 if (!value ||
246 value->type != GCONF_VALUE_INT) {
247 gconf_entry_unref (entry);
248 return FALSE;
249 }
250
251 *options = gconf_value_get_int (value);
252 gconf_entry_unref (entry);
253
254 return TRUE;
255 #else
256 GError *error = NULL;
257 char *game_name;
258
259 game_name = game_module_to_game_name (game_module);
260 *options = ar_conf_get_integer (game_name, "Options", &error);
261 g_free (game_name);
262 if (error) {
263 g_error_free (error);
264 return FALSE;
265 }
266
267 return TRUE;
268 #endif /* HAVE_GNOME */
269 }
270
271 void
aisleriot_conf_set_options(const char * game_module,int value)272 aisleriot_conf_set_options (const char *game_module,
273 int value)
274 {
275 #ifdef HAVE_GNOME
276 GConfSchema *schema;
277 char *gconf_key, *schemas_key;
278
279 gconf_key = options_gconf_key (game_module);
280
281 schemas_key = g_strconcat ("/schemas", gconf_key, NULL);
282
283 /* Check if we have a schema for this key, and make one if we don't */
284 schema = gconf_client_get_schema (gconf_client, schemas_key, NULL);
285 if (!schema) {
286 GConfValue *def;
287
288 schema = gconf_schema_new ();
289 gconf_schema_set_type (schema, GCONF_VALUE_INT);
290 gconf_schema_set_owner (schema, "aisleriot");
291 /* FIXME: Translation - how? */
292 gconf_schema_set_short_desc (schema, "A per-game option");
293 gconf_schema_set_long_desc (schema,
294 "An integer encoding a list of boolean values (LSB = first item) for use as options in a solitaire game.");
295
296 def = gconf_value_new (GCONF_VALUE_INT);
297 /* Not entirely correct, but there's no way to get the default options from
298 * the game, and setting this to 0 makes the game options be incorrect.
299 */
300 gconf_value_set_int (def, value);
301 gconf_schema_set_default_value_nocopy (schema, def);
302
303 gconf_client_set_schema (gconf_client, schemas_key, schema, NULL);
304 gconf_engine_associate_schema (gconf_engine_get_default (), gconf_key, schemas_key, NULL);
305 }
306 gconf_schema_free (schema);
307 g_free (schemas_key);
308
309 gconf_client_set_int (gconf_client, gconf_key, value, NULL);
310 g_free (gconf_key);
311 #else
312 char *game_name;
313
314 game_name = game_module_to_game_name (game_module);
315 ar_conf_set_integer (game_name, "Options", value);
316 g_free (game_name);
317 #endif /* HAVE_GNOME */
318 }
319
320 void
aisleriot_conf_get_statistic(const char * game_module,AisleriotStatistic * statistic)321 aisleriot_conf_get_statistic (const char *game_module,
322 AisleriotStatistic *statistic)
323 {
324 #ifdef HAVE_GNOME
325 AisleriotStatistic *game_stat;
326 char *game_name;
327
328 game_name = game_module_to_game_name (game_module);
329
330 game_stat = g_hash_table_lookup (stats, game_name);
331 if (!game_stat) {
332 char *display_name;
333
334 /* Previous versions used the localised name as key, so try it as fall-back.
335 * See bug #406267 and bug #525177.
336 */
337 display_name = ar_filename_to_display_name (game_module);
338 game_stat = g_hash_table_lookup (stats, display_name);
339 g_free (display_name);
340 }
341
342 if (game_stat) {
343 *statistic = *game_stat;
344 } else {
345 memset (statistic, 0, sizeof (AisleriotStatistic));
346 }
347
348 g_free (game_name);
349
350 #else
351
352 int *values;
353 gsize len = 0;
354 GError *error = NULL;
355 char *game_name;
356
357 game_name = game_module_to_game_name (game_module);
358
359 memset (statistic, 0, sizeof (AisleriotStatistic));
360
361 values = ar_conf_get_integer_list (game_name, "Statistic", &len, &error);
362 if (error) {
363 g_error_free (error);
364 g_free (game_name);
365 return;
366 }
367 if (len != 4) {
368 g_free (values);
369 g_free (game_name);
370 return;
371 }
372
373 statistic->wins = values[0];
374 statistic->total = values[1];
375 /* Sanitise values to fix statistics from bug #474615 */
376 if (values[2] > 0 && values[2] <= 6000) {
377 statistic->best = values[2];
378 } else {
379 statistic->best = 0;
380 }
381 if (values[3] > 0 && values[3] <= 6000) {
382 statistic->worst = values[3];
383 } else {
384 statistic->worst = 0;
385 }
386
387 g_free (values);
388 g_free (game_name);
389 #endif /* HAVE_GNOME */
390 }
391
392 void
aisleriot_conf_set_statistic(const char * game_module,AisleriotStatistic * statistic)393 aisleriot_conf_set_statistic (const char *game_module,
394 AisleriotStatistic *statistic)
395 {
396 #ifdef HAVE_GNOME
397 AisleriotStatistic *game_stat;
398 char *game_name;
399
400 game_name = game_module_to_game_name (game_module);
401
402 game_stat = g_hash_table_lookup (stats, game_name);
403 /* Backward compatibility with buggy old aisleriot versions
404 * which stored the localised game name.
405 */
406 if (!game_stat) {
407 char *localised_name;
408
409 localised_name = ar_filename_to_display_name (game_module);
410 game_stat = g_hash_table_lookup (stats, localised_name);
411 g_free (localised_name);
412 }
413
414 if (!game_stat) {
415 game_stat = g_new0 (AisleriotStatistic, 1);
416 g_hash_table_insert (stats, g_strdup (game_name), game_stat);
417 }
418
419 *game_stat = *statistic;
420
421 save_statistics ();
422
423 g_free (game_name);
424
425 #else
426
427 char *game_name;
428 int values[4];
429
430 game_name = game_module_to_game_name (game_module);
431
432 values[0] = statistic->wins;
433 values[1] = statistic->total;
434 values[2] = statistic->best;
435 values[3] = statistic->worst;
436
437 ar_conf_set_integer_list (game_name, "Statistic", values, G_N_ELEMENTS (values));
438
439 g_free (game_name);
440 #endif /* HAVE_GNOME */
441 }
442