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