1 /* vim:expandtab:ts=2 sw=2:
2 */
3 /*  Grafx2 - The Ultimate 256-color bitmap paint program
4 
5 	Copyright owned by various GrafX2 authors, see COPYRIGHT.txt for details.
6 
7     Grafx2 is free software; you can redistribute it and/or
8     modify it under the terms of the GNU General Public License
9     as published by the Free Software Foundation; version 2
10     of the License.
11 
12     Grafx2 is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License
18     along with Grafx2; if not, see <http://www.gnu.org/licenses/>
19 */
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <ctype.h>
24 #if defined(_MSC_VER)
25 #define strdup _strdup
26 #endif
27 
28 #include "const.h"
29 #include "errors.h"
30 #include "global.h"
31 #include "misc.h"
32 #include "readini.h"
33 #include "setup.h"
34 #include "realpath.h"
35 #include "io.h"
36 #include "windows.h"
37 #include "gfx2log.h"
38 #include "gfx2mem.h"
39 
40 
41 /**
42  * Clean a line of the ini file.
43  *
44  * - Suppress all leading and trailing spaces
45  * - convert the key to uppercase
46  *
47  * Comments start with ";". The result will be in the following format :
48  * <tt>KEY=value</tt>
49  *
50  * @param[in,out] str
51  * @param[in] keep_comments if set, the comments are kept
52  */
Load_INI_clear_string(char * str,byte keep_comments)53 void Load_INI_clear_string(char * str, byte keep_comments)
54 {
55   int index;
56   int equal_found=0;
57 
58   for (index=0;str[index]!='\0';)
59   {
60     if (str[index]=='=')
61     {
62       equal_found=1;
63       index++;
64       // On enleve les espaces après le '='
65       while (str[index]==' ' || str[index]=='\t')
66         memmove(str+index,str+index+1,strlen(str+index));
67     }
68     else if ((str[index]==' ' && !equal_found) || (str[index]=='\t'))
69     {
70       // Suppression d'un espace ou d'un tab:
71       memmove(str+index,str+index+1,strlen(str+index));
72     }
73     else if (!keep_comments && ((str[index]==';') || (str[index]=='#')))
74     {
75       // Comment
76       str[index]='\0';
77     }
78     else if ((str[index]=='\r') || (str[index]=='\n'))
79     {
80       // Line break
81       str[index]='\0';
82     }
83     else
84     {
85       if (!equal_found)
86       {
87         // Passage en majuscule d'un caractère:
88 #ifndef GCWZERO  //this causes gcw to crash
89         str[index]=toupper((int)str[index]);
90 #endif
91       }
92       index++;
93     }
94   }
95   // On enlève les espaces avant la fin de chaine
96   while (index>0 && (str[index-1]==' ' || str[index-1]=='\t'))
97   {
98     index--;
99     str[index]='\0';
100   }
101 }
102 
103 /**
104  * Search for a substring in a string
105  *
106  * @param buffer the string to search into
107  * @param pattern the string to search for
108  * @return 0 if the pattern was not found
109  * @return >0 the position (+1) where the pattern was found
110  */
Load_INI_seek_pattern(const char * buffer,const char * pattern)111 int Load_INI_seek_pattern(const char * buffer,const char * pattern)
112 {
113   int buffer_index;
114   int pattern_index;
115 
116   // A partir de chaque lettre de la chaîne buffer
117   for (buffer_index=0;buffer[buffer_index]!='\0';buffer_index++)
118   {
119     // On regarde si la chaîne pattern est équivalente à la position courante
120     // de la chaîne buffer:
121     for (pattern_index=0;(pattern[pattern_index]!='\0') && (buffer[buffer_index+pattern_index]==pattern[pattern_index]);pattern_index++);
122 
123     // Si on a trouvé la chaîne pattern dans la chaîne buffer, on renvoie la
124     // position à laquelle on l'a trouvée (+1 pour que si on la trouve au
125     // début ça ne renvoie pas la même chose que si on ne l'avait pas
126     // trouvée):
127     if (pattern[pattern_index]=='\0')
128       return (buffer_index+1);
129   }
130 
131   // Si on ne l'a pas trouvée, on renvoie 0:
132   return 0;
133 }
134 
135 /**
136  * Read lines until a group is found
137  *
138  * @return 0 when the group is reached
139  * @return @ref ERROR_INI_CORRUPTED if the group is not found
140  */
Load_INI_reach_group(FILE * file,char * buffer,const char * group)141 static int Load_INI_reach_group(FILE * file,char * buffer,const char * group)
142 {
143   int    stop_seek;
144   char * group_upper;
145   char * upper_buffer;
146 
147   // On alloue les zones de mémoire:
148   group_upper=(char *)malloc(1024);
149   upper_buffer=(char *)malloc(1024);
150 
151   // On commence par se faire une version majuscule du groupe à rechercher:
152   strcpy(group_upper,group);
153   Load_INI_clear_string(group_upper, 0);
154 
155   stop_seek=0;
156   do
157   {
158     // On lit une ligne dans le fichier:
159     if (fgets(buffer,1024,file)==0)
160     {
161       free(upper_buffer);
162       free(group_upper);
163       return ERROR_INI_CORRUPTED;
164     }
165 
166     Line_number_in_INI_file++;
167 
168     // On s'en fait une version en majuscule:
169     strcpy(upper_buffer,buffer);
170     Load_INI_clear_string(upper_buffer, 0);
171 
172     // On compare la chaîne avec le groupe recherché:
173     stop_seek=Load_INI_seek_pattern(upper_buffer,group_upper);
174   }
175   while (!stop_seek);
176 
177   free(upper_buffer);
178   free(group_upper);
179 
180   return 0;
181 }
182 
183 ///
184 /// Find the next string in the .INI file.
185 /// @param file INI file currently opened
186 /// @param buffer Current text buffer, preserved from one call to the next
187 /// @param option_name string to search
188 /// @param return_code the found value will be copied there. (must be allocaed)
189 /// @param raw_text Boolean: true to return the raw value (up to end-of-line), false to strip comments.
190 /// @return 0 when OK
191 /// @return @ref ERROR_INI_CORRUPTED if the option is not found
Load_INI_get_string(FILE * file,char * buffer,const char * option_name,char * return_code,byte raw_text)192 static int Load_INI_get_string(FILE * file,char * buffer,const char * option_name,char * return_code, byte raw_text)
193 {
194   int    stop_seek;
195   char * option_upper;
196   char * upper_buffer;
197   int    buffer_index;
198 
199   // On alloue les zones de mémoire:
200   option_upper=(char *)malloc(1024);
201   upper_buffer=(char *)malloc(1024);
202 
203   // On commence par se faire une version majuscule de l'option à rechercher:
204   strcpy(option_upper,option_name);
205   Load_INI_clear_string(option_upper, 0);
206 
207   stop_seek=0;
208   do
209   {
210     // On lit une ligne dans le fichier:
211     if (fgets(buffer,1024,file)==NULL)
212     {
213       free(upper_buffer);
214       free(option_upper);
215       return ERROR_INI_CORRUPTED;
216     }
217 
218     Line_number_in_INI_file++;
219 
220     // On s'en fait une version en majuscule:
221     strcpy(upper_buffer,buffer);
222     Load_INI_clear_string(upper_buffer, raw_text);
223 
224     // On compare la chaîne avec l'option recherchée:
225     stop_seek=Load_INI_seek_pattern(upper_buffer,option_upper);
226 
227     // Si on l'a trouvée:
228     if (stop_seek)
229     {
230       // On se positionne juste après la chaîne "="
231       buffer_index=Load_INI_seek_pattern(upper_buffer,"=");
232 
233       strcpy(return_code, upper_buffer + buffer_index);
234     }
235   }
236   while (!stop_seek);
237 
238   free(upper_buffer);
239   free(option_upper);
240 
241   return 0;
242 }
243 
244 /**
245  * Read a value from the string
246  *
247  * @ref index is updated according to the number of characters read
248  * from the source string.
249  *
250  * @param str the source string (option "values")
251  * @param index a pointer to the current index in the source string
252  * @param value the read value will be put there
253  * @return 0
254  * @return @ref ERROR_INI_CORRUPTED
255  */
Load_INI_get_value(const char * str,int * index,int * value)256 static int Load_INI_get_value(const char * str,int * index,int * value)
257 {
258   int negative = 0;
259 
260   // On teste si la valeur actuelle est YES (ou Y):
261 
262   if (Load_INI_seek_pattern(str+(*index),"yes,")==1)
263   {
264     (*value)=1;
265     (*index)+=4;
266     return 0;
267   }
268   if (strcmp(str+(*index),"yes")==0)
269   {
270     (*value)=1;
271     (*index)+=3;
272     return 0;
273   }
274   if (Load_INI_seek_pattern(str+(*index),"y,")==1)
275   {
276     (*value)=1;
277     (*index)+=2;
278     return 0;
279   }
280   if (strcmp(str+(*index),"y")==0)
281   {
282     (*value)=1;
283     (*index)+=1;
284     return 0;
285   }
286 
287   // On teste si la valeur actuelle est NO (ou N):
288 
289   if (Load_INI_seek_pattern(str+(*index),"no,")==1)
290   {
291     (*value)=0;
292     (*index)+=3;
293     return 0;
294   }
295   if (strcmp(str+(*index),"no")==0)
296   {
297     (*value)=0;
298     (*index)+=2;
299     return 0;
300   }
301   if (Load_INI_seek_pattern(str+(*index),"n,")==1)
302   {
303     (*value)=0;
304     (*index)+=2;
305     return 0;
306   }
307   if (strcmp(str+(*index),"n")==0)
308   {
309     (*value)=0;
310     (*index)+=1;
311     return 0;
312   }
313   if (str[*index]=='$')
314   {
315     (*value)=0;
316 
317     for (;;)
318     {
319       (*index)++;
320 
321       if ((str[*index]>='0') && (str[*index]<='9'))
322         (*value)=((*value)*16)+str[*index]-'0';
323       else
324       if ((str[*index]>='A') && (str[*index]<='F'))
325         (*value)=((*value)*16)+str[*index]-'A'+10;
326       else
327       if (str[*index]==',')
328       {
329         (*index)++;
330         return 0;
331       }
332       else
333       if (str[*index]=='\0')
334         return 0;
335       else
336         return ERROR_INI_CORRUPTED;
337     }
338   }
339   if (str[*index]=='-')
340   {
341     negative = 1;
342     // next character
343     (*index)++;
344     // Fall thru
345   }
346   if ((str[*index]>='0') && (str[*index]<='9'))
347   {
348     (*value)=0;
349 
350     for (;;)
351     {
352       if ((str[*index]>='0') && (str[*index]<='9'))
353       {
354         (*value)=((*value)*10)+str[*index]-'0';
355         if (negative)
356         {
357           (*value)*= -1;
358           // This is to do it once per number.
359           negative = 0;
360         }
361       }
362       else
363       if (str[*index]==',')
364       {
365         (*index)++;
366         return 0;
367       }
368       else
369       if (str[*index]=='\0')
370         return 0;
371       else
372         return ERROR_INI_CORRUPTED;
373 
374       (*index)++;
375     }
376   }
377   else
378     return ERROR_INI_CORRUPTED;
379 }
380 
381 
382 /**
383  * Read all values from a string
384  *
385  * The values are comma separated
386  *
387  * @param file handle to gfx2.ini
388  * @param buffer Current text buffer, preserved from one call to the next
389  * @param option_name name of the option to read
390  * @param nb_expected_values number of values to read from the line
391  * @param[out] values the values will be put there
392  * @return 0 when OK
393  * @return @ref ERROR_INI_CORRUPTED if no value was found, or not enough
394  */
Load_INI_get_values(FILE * file,char * buffer,const char * option_name,int nb_expected_values,int * values)395 static int Load_INI_get_values(FILE * file,char * buffer,const char * option_name,int nb_expected_values,int * values)
396 {
397   int    stop_seek;
398   char * option_upper;
399   char * upper_buffer;
400   int    buffer_index;
401   int    nb_values;
402 
403   // On alloue les zones de mémoire:
404   option_upper=(char *)malloc(1024);
405   upper_buffer=(char *)malloc(1024);
406 
407   // On commence par se faire une version majuscule de l'option à rechercher:
408   strcpy(option_upper,option_name);
409   Load_INI_clear_string(option_upper, 0);
410 
411   stop_seek=0;
412   do
413   {
414     // On lit une ligne dans le fichier:
415     if (fgets(buffer,1024,file)==0)
416     {
417       free(upper_buffer);
418       free(option_upper);
419       return ERROR_INI_CORRUPTED;
420     }
421 
422     Line_number_in_INI_file++;
423 
424     // On s'en fait une version en majuscule:
425     strcpy(upper_buffer,buffer);
426     Load_INI_clear_string(upper_buffer, 0);
427 
428     // On compare la chaîne avec l'option recherchée:
429     stop_seek=Load_INI_seek_pattern(upper_buffer,option_upper);
430 
431     // Si on l'a trouvée:
432     if (stop_seek)
433     {
434       nb_values=0;
435 
436       // On se positionne juste après la chaîne "="
437       buffer_index=Load_INI_seek_pattern(upper_buffer,"=");
438 
439       // Tant qu'on a pas atteint la fin de la ligne
440       while (upper_buffer[buffer_index]!='\0')
441       {
442         if (Load_INI_get_value(upper_buffer,&buffer_index,values+nb_values))
443         {
444           free(upper_buffer);
445           free(option_upper);
446           return ERROR_INI_CORRUPTED;
447         }
448 
449         if ( ((++nb_values) == nb_expected_values) &&
450              (upper_buffer[buffer_index]!='\0') )
451         {
452           // Too many values !
453           free(upper_buffer);
454           free(option_upper);
455           return ERROR_INI_CORRUPTED;
456         }
457       }
458 
459       if (nb_values<nb_expected_values)
460       {
461         // Not enough values !
462         free(upper_buffer);
463         free(option_upper);
464         return ERROR_INI_CORRUPTED;
465       }
466     }
467   }
468   while (!stop_seek);
469 
470   free(upper_buffer);
471   free(option_upper);
472 
473   return 0;
474 }
475 
476 
477 /**
478  * Load config from the gfx2.ini file
479  *
480  * @param[out] conf the configuration to read to
481  * @return 0 when OK
482  * @return @ref ERROR_INI_CORRUPTED for any error
483  */
Load_INI(T_Config * conf)484 int Load_INI(T_Config * conf)
485 {
486   FILE * file;
487   char * buffer;
488   int    values[3];
489   int    index;
490   char * filename;
491   int    return_code;
492   char   value_label[1024];
493 
494   Line_number_in_INI_file=0;
495 
496 #if defined(__WIZ__) || defined(__CAANOO__)
497   conf->Stylus_mode = 1;
498 #else
499   conf->Stylus_mode = 0;
500 #endif
501 
502   // allocate buffer
503   buffer = (char *)GFX2_malloc(1024);
504 
505   filename = Filepath_append_to_dir(Config_directory, INI_FILENAME);
506   file = fopen(filename, "r");
507   if (file == NULL)
508   {
509     free(filename);
510     // Si le fichier ini est absent on le relit depuis gfx2def.ini
511     filename = Filepath_append_to_dir(Data_directory, INIDEF_FILENAME);
512     file = fopen(filename, "r");
513     if (file == NULL)
514     {
515       GFX2_Log(GFX2_ERROR, "Load_INI() cannot open %s\n", filename);
516       free(filename);
517       free(buffer);
518       return ERROR_INI_MISSING;
519     }
520   }
521   GFX2_Log(GFX2_DEBUG, "Load_INI() loading %s\n", filename);
522   free(filename);
523 
524   if ((return_code=Load_INI_reach_group(file,buffer,"[MOUSE]")))
525     goto Erreur_Retour;
526 
527   if ((return_code=Load_INI_get_values (file,buffer,"X_sensitivity",1,values)))
528     goto Erreur_Retour;
529   if ((values[0]<1) || (values[0]>4))
530     conf->Mouse_sensitivity_index_x=1;
531   else
532     conf->Mouse_sensitivity_index_x=values[0];
533 
534   if ((return_code=Load_INI_get_values (file,buffer,"Y_sensitivity",1,values)))
535     goto Erreur_Retour;
536   if ((values[0]<1) || (values[0]>4))
537     conf->Mouse_sensitivity_index_y=1;
538   else
539     conf->Mouse_sensitivity_index_y=values[0];
540 
541   if ((return_code=Load_INI_get_values (file,buffer,"X_correction_factor",1,values)))
542     goto Erreur_Retour;
543   if ((values[0]<0) || (values[0]>4))
544     goto Erreur_ERREUR_INI_CORROMPU;
545   // Deprecated setting, unused
546 
547   if ((return_code=Load_INI_get_values (file,buffer,"Y_correction_factor",1,values)))
548     goto Erreur_Retour;
549   if ((values[0]<0) || (values[0]>4))
550     goto Erreur_ERREUR_INI_CORROMPU;
551   // Deprecated setting, unused
552 
553   if ((return_code=Load_INI_get_values (file,buffer,"Cursor_aspect",1,values)))
554     goto Erreur_Retour;
555   if ((values[0]<1) || (values[0]>3))
556     goto Erreur_ERREUR_INI_CORROMPU;
557   conf->Cursor=values[0]-1;
558 
559   if ((return_code=Load_INI_reach_group(file,buffer,"[MENU]")))
560     goto Erreur_Retour;
561 
562   conf->Fav_menu_colors[0].R=0;
563   conf->Fav_menu_colors[0].G=0;
564   conf->Fav_menu_colors[0].B=0;
565   conf->Fav_menu_colors[3].R=255;
566   conf->Fav_menu_colors[3].G=255;
567   conf->Fav_menu_colors[3].B=255;
568 
569   if ((return_code=Load_INI_get_values (file,buffer,"Light_color",3,values)))
570     goto Erreur_Retour;
571   if ((values[0]<0) || (values[0]>63))
572     goto Erreur_ERREUR_INI_CORROMPU;
573   if ((values[1]<0) || (values[1]>63))
574     goto Erreur_ERREUR_INI_CORROMPU;
575   if ((values[2]<0) || (values[2]>63))
576     goto Erreur_ERREUR_INI_CORROMPU;
577   conf->Fav_menu_colors[2].R=(values[0]<<2)|(values[0]>>4);
578   conf->Fav_menu_colors[2].G=(values[1]<<2)|(values[1]>>4);
579   conf->Fav_menu_colors[2].B=(values[2]<<2)|(values[2]>>4);
580 
581   if ((return_code=Load_INI_get_values (file,buffer,"Dark_color",3,values)))
582     goto Erreur_Retour;
583   if ((values[0]<0) || (values[0]>63))
584     goto Erreur_ERREUR_INI_CORROMPU;
585   if ((values[1]<0) || (values[1]>63))
586     goto Erreur_ERREUR_INI_CORROMPU;
587   if ((values[2]<0) || (values[2]>63))
588     goto Erreur_ERREUR_INI_CORROMPU;
589   conf->Fav_menu_colors[1].R=(values[0]<<2)|(values[0]>>4);
590   conf->Fav_menu_colors[1].G=(values[1]<<2)|(values[1]>>4);
591   conf->Fav_menu_colors[1].B=(values[2]<<2)|(values[2]>>4);
592 
593   if ((return_code=Load_INI_get_values (file,buffer,"Menu_ratio",1,values)))
594     goto Erreur_Retour;
595   if ((values[0]<-4) || (values[0]>2))
596     goto Erreur_ERREUR_INI_CORROMPU;
597   conf->Ratio=values[0];
598 
599   if ((return_code=Load_INI_reach_group(file,buffer,"[FILE_SELECTOR]")))
600     goto Erreur_Retour;
601 
602   if ((return_code=Load_INI_get_values (file,buffer,"Show_hidden_files",1,values)))
603     goto Erreur_Retour;
604   if ((values[0]<0) || (values[0]>1))
605     goto Erreur_ERREUR_INI_CORROMPU;
606   conf->Show_hidden_files=values[0]?1:0;
607 
608   if ((return_code=Load_INI_get_values (file,buffer,"Show_hidden_directories",1,values)))
609     goto Erreur_Retour;
610   if ((values[0]<0) || (values[0]>1))
611     goto Erreur_ERREUR_INI_CORROMPU;
612   conf->Show_hidden_directories=values[0]?1:0;
613 
614 /*  if ((return_code=Load_INI_get_values (file,buffer,"Show_system_directories",1,values)))
615     goto Erreur_Retour;
616   if ((values[0]<0) || (values[0]>1))
617     goto Erreur_ERREUR_INI_CORROMPU;
618   conf->Show_system_directories=values[0]?1:0;
619 */
620   if ((return_code=Load_INI_get_values (file,buffer,"Preview_delay",1,values)))
621     goto Erreur_Retour;
622   if ((values[0]<1) || (values[0]>256))
623     goto Erreur_ERREUR_INI_CORROMPU;
624   conf->Timer_delay=values[0];
625 
626   if ((return_code=Load_INI_get_values (file,buffer,"Maximize_preview",1,values)))
627     goto Erreur_Retour;
628   if ((values[0]<0) || (values[0]>1))
629     goto Erreur_ERREUR_INI_CORROMPU;
630   conf->Maximize_preview=values[0];
631 
632   if ((return_code=Load_INI_get_values (file,buffer,"Find_file_fast",1,values)))
633     goto Erreur_Retour;
634   if ((values[0]<0) || (values[0]>2))
635     goto Erreur_ERREUR_INI_CORROMPU;
636   conf->Find_file_fast=values[0];
637 
638 
639   if ((return_code=Load_INI_reach_group(file,buffer,"[LOADING]")))
640     goto Erreur_Retour;
641 
642   if ((return_code=Load_INI_get_values (file,buffer,"Auto_set_resolution",1,values)))
643     goto Erreur_Retour;
644   if ((values[0]<0) || (values[0]>1))
645     goto Erreur_ERREUR_INI_CORROMPU;
646   conf->Auto_set_res=values[0];
647 
648   if ((return_code=Load_INI_get_values (file,buffer,"Set_resolution_according_to",1,values)))
649     goto Erreur_Retour;
650   if ((values[0]<1) || (values[0]>2))
651     goto Erreur_ERREUR_INI_CORROMPU;
652   conf->Set_resolution_according_to=values[0];
653 
654   if ((return_code=Load_INI_get_values (file,buffer,"Clear_palette",1,values)))
655     goto Erreur_Retour;
656   if ((values[0]<0) || (values[0]>1))
657     goto Erreur_ERREUR_INI_CORROMPU;
658   conf->Clear_palette=values[0];
659 
660 
661   if ((return_code=Load_INI_reach_group(file,buffer,"[MISCELLANEOUS]")))
662     goto Erreur_Retour;
663 
664   if ((return_code=Load_INI_get_values (file,buffer,"Draw_limits",1,values)))
665     goto Erreur_Retour;
666   if ((values[0]<0) || (values[0]>1))
667     goto Erreur_ERREUR_INI_CORROMPU;
668   conf->Display_image_limits=values[0];
669 
670   if ((return_code=Load_INI_get_values (file,buffer,"Adjust_brush_pick",1,values)))
671     goto Erreur_Retour;
672   if ((values[0]<0) || (values[0]>1))
673     goto Erreur_ERREUR_INI_CORROMPU;
674   conf->Adjust_brush_pick=values[0];
675 
676   if ((return_code=Load_INI_get_values (file,buffer,"Coordinates",1,values)))
677     goto Erreur_Retour;
678   if ((values[0]<1) || (values[0]>2))
679     goto Erreur_ERREUR_INI_CORROMPU;
680   conf->Coords_rel=2-values[0];
681 
682   if ((return_code=Load_INI_get_values (file,buffer,"Backup",1,values)))
683     goto Erreur_Retour;
684   if ((values[0]<0) || (values[0]>1))
685     goto Erreur_ERREUR_INI_CORROMPU;
686   conf->Backup=values[0];
687 
688   if ((return_code=Load_INI_get_values (file,buffer,"Undo_pages",1,values)))
689     goto Erreur_Retour;
690   if ((values[0]<1) || (values[0]>99))
691     goto Erreur_ERREUR_INI_CORROMPU;
692   conf->Max_undo_pages=values[0];
693 
694   if ((return_code=Load_INI_get_values (file,buffer,"Gauges_scrolling_speed_Left",1,values)))
695     goto Erreur_Retour;
696   if ((values[0]<1) || (values[0]>255))
697     goto Erreur_ERREUR_INI_CORROMPU;
698   conf->Delay_left_click_on_slider=values[0];
699 
700   if ((return_code=Load_INI_get_values (file,buffer,"Gauges_scrolling_speed_Right",1,values)))
701     goto Erreur_Retour;
702   if ((values[0]<1) || (values[0]>255))
703     goto Erreur_ERREUR_INI_CORROMPU;
704   conf->Delay_right_click_on_slider=values[0];
705 
706   if ((return_code=Load_INI_get_values (file,buffer,"Auto_save",1,values)))
707     goto Erreur_Retour;
708   if ((values[0]<0) || (values[0]>1))
709     goto Erreur_ERREUR_INI_CORROMPU;
710   conf->Auto_save=values[0];
711 
712   if ((return_code=Load_INI_get_values (file,buffer,"Vertices_per_polygon",1,values)))
713     goto Erreur_Retour;
714   if ((values[0]<2) || (values[0]>16384))
715     goto Erreur_ERREUR_INI_CORROMPU;
716   conf->Nb_max_vertices_per_polygon=values[0];
717 
718   if ((return_code=Load_INI_get_values (file,buffer,"Fast_zoom",1,values)))
719     goto Erreur_Retour;
720   if ((values[0]<0) || (values[0]>1))
721     goto Erreur_ERREUR_INI_CORROMPU;
722   conf->Fast_zoom=values[0];
723 
724   if ((return_code=Load_INI_get_values (file,buffer,"Separate_colors",1,values)))
725     goto Erreur_Retour;
726   if ((values[0]<0) || (values[0]>1))
727     goto Erreur_ERREUR_INI_CORROMPU;
728   conf->Separate_colors=values[0];
729 
730   if ((return_code=Load_INI_get_values (file,buffer,"FX_feedback",1,values)))
731     goto Erreur_Retour;
732   if ((values[0]<0) || (values[0]>1))
733     goto Erreur_ERREUR_INI_CORROMPU;
734   conf->FX_Feedback=values[0];
735 
736   if ((return_code=Load_INI_get_values (file,buffer,"Safety_colors",1,values)))
737     goto Erreur_Retour;
738   if ((values[0]<0) || (values[0]>1))
739     goto Erreur_ERREUR_INI_CORROMPU;
740   conf->Safety_colors=values[0];
741 
742   if ((return_code=Load_INI_get_values (file,buffer,"Opening_message",1,values)))
743     goto Erreur_Retour;
744   if ((values[0]<0) || (values[0]>1))
745     goto Erreur_ERREUR_INI_CORROMPU;
746   conf->Opening_message=values[0];
747 
748   if ((return_code=Load_INI_get_values (file,buffer,"Clear_with_stencil",1,values)))
749     goto Erreur_Retour;
750   if ((values[0]<0) || (values[0]>1))
751     goto Erreur_ERREUR_INI_CORROMPU;
752   conf->Clear_with_stencil=values[0];
753 
754   if ((return_code=Load_INI_get_values (file,buffer,"Auto_discontinuous",1,values)))
755     goto Erreur_Retour;
756   if ((values[0]<0) || (values[0]>1))
757     goto Erreur_ERREUR_INI_CORROMPU;
758   conf->Auto_discontinuous=values[0];
759 
760   if ((return_code=Load_INI_get_values (file,buffer,"Save_screen_size_in_GIF",1,values)))
761     goto Erreur_Retour;
762   if ((values[0]<0) || (values[0]>1))
763     goto Erreur_ERREUR_INI_CORROMPU;
764   conf->Screen_size_in_GIF=values[0];
765 
766   if ((return_code=Load_INI_get_values (file,buffer,"Auto_nb_colors_used",1,values)))
767     goto Erreur_Retour;
768   if ((values[0]<0) || (values[0]>1))
769     goto Erreur_ERREUR_INI_CORROMPU;
770   conf->Auto_nb_used=values[0];
771 
772   // Optionnel, le mode video par défaut (à partir de beta 97.0%)
773   conf->Default_resolution=0;
774   if (!Load_INI_get_string (file,buffer,"Default_video_mode",value_label, 0))
775   {
776     int mode = Convert_videomode_arg(value_label);
777     if (mode>=0)
778       conf->Default_resolution=mode;
779   }
780 
781   // Optionnel, les dimensions de la fenêtre (à partir de beta 97.0%)
782   // Do that only if the first mode is actually windowed (not the case on gp2x for example)
783   if(Video_mode[0].Fullscreen==0)
784   {
785     Video_mode[0].Width = 640;
786     Video_mode[0].Height = 480;
787     if (!Load_INI_get_values (file,buffer,"Default_window_size",2,values))
788     {
789       if ((values[0]>=320))
790         Default_window_width = Video_mode[0].Width = values[0];
791       if ((values[1]>=200))
792         Default_window_height = Video_mode[0].Height = values[1];
793     }
794   }
795 
796   conf->Mouse_merge_movement=100;
797   // Optionnel, paramètre pour grouper les mouvements souris (>98.0%)
798   if (!Load_INI_get_values (file,buffer,"Merge_movement",1,values))
799   {
800     if ((values[0]<0) || (values[0]>1000))
801       goto Erreur_ERREUR_INI_CORROMPU;
802     conf->Mouse_merge_movement=values[0];
803   }
804 
805   conf->Palette_cells_X=16;
806   // Optionnel, nombre de colonnes dans la palette (>98.0%)
807   if (!Load_INI_get_values (file,buffer,"Palette_cells_X",1,values))
808   {
809     if ((values[0]<1) || (values[0]>256))
810       goto Erreur_ERREUR_INI_CORROMPU;
811     conf->Palette_cells_X=values[0];
812   }
813   conf->Palette_cells_Y=4;
814   // Optionnel, nombre de lignes dans la palette (>98.0%)
815   if (!Load_INI_get_values (file,buffer,"Palette_cells_Y",1,values))
816   {
817     if (values[0]<1 || values[0]>16)
818       goto Erreur_ERREUR_INI_CORROMPU;
819     conf->Palette_cells_Y=values[0];
820   }
821   // Optionnel, bookmarks (>98.0%)
822   for (index=0;index<NB_BOOKMARKS;index++)
823   {
824     conf->Bookmark_directory[index]=NULL;
825     conf->Bookmark_label[index][0]='\0';
826   }
827   for (index=0;index<NB_BOOKMARKS;index++)
828   {
829     if (!Load_INI_get_string (file,buffer,"Bookmark_label",value_label, 1))
830     {
831       size_t size = strlen(value_label);
832       if (size!=0)
833       {
834         if (size >= sizeof(conf->Bookmark_label[0]))
835         {
836           memcpy(conf->Bookmark_label[index], value_label, sizeof(conf->Bookmark_label[0]) - 1);
837           conf->Bookmark_label[index][sizeof(conf->Bookmark_label[0]) - 1] = '\0';
838         }
839         else
840           memcpy(conf->Bookmark_label[index], value_label, size + 1);
841       }
842     }
843     else
844       break;
845     if (!Load_INI_get_string (file,buffer,"Bookmark_directory",value_label, 1))
846     {
847       size_t size = strlen(value_label);
848       if (size!=0)
849       {
850         conf->Bookmark_directory[index]=(char *)malloc(size+1);
851         strcpy(conf->Bookmark_directory[index],value_label);
852       }
853     }
854     else
855       break;
856   }
857   conf->Palette_vertical=1;
858   // Optional, vertical palette option (>98.0%)
859   if (!Load_INI_get_values (file,buffer,"Palette_vertical",1,values))
860   {
861     if ((values[0]<0) || (values[0]>1))
862       goto Erreur_ERREUR_INI_CORROMPU;
863     conf->Palette_vertical=values[0];
864   }
865 
866   // Optional, the window position (>98.0%)
867   conf->Window_pos_x=9999;
868   conf->Window_pos_y=9999;
869   if (!Load_INI_get_values (file,buffer,"Window_position",2,values))
870   {
871     conf->Window_pos_x = values[0];
872     conf->Window_pos_y = values[1];
873   }
874 
875   conf->Double_click_speed=500;
876   // Optional, speed of double-click (>2.0)
877   if (!Load_INI_get_values (file,buffer,"Double_click_speed",1,values))
878   {
879     if ((values[0]>0) || (values[0]<=2000))
880       conf->Double_click_speed=values[0];
881   }
882 
883   conf->Double_key_speed=500;
884   // Optional, speed of double-keypress (>2.0)
885   if (!Load_INI_get_values (file,buffer,"Double_key_speed",1,values))
886   {
887     if ((values[0]>0) || (values[0]<=2000))
888       conf->Double_key_speed=values[0];
889   }
890 
891   // Optional, name of skin file. (>2.0)
892   if(!Load_INI_get_string(file,buffer,"Skin_file",value_label,1))
893   {
894     conf->Skin_file = strdup(value_label);
895   }
896   else
897     conf->Skin_file = strdup(DEFAULT_SKIN_FILENAME);
898 
899   // Optional, name of font file. (>2.0)
900   if(!Load_INI_get_string(file,buffer,"Font_file",value_label,1))
901     conf->Font_file = strdup(value_label);
902   else
903     conf->Font_file = strdup(DEFAULT_FONT_FILENAME);
904 
905   // Optional, "fake hardware zoom" factor (>2.1)
906   if (!Load_INI_get_values (file, buffer,"Pixel_ratio",1,values))
907   {
908     Pixel_ratio = values[0];
909     switch(Pixel_ratio) {
910       case PIXEL_WIDE:
911         if(Video_mode[0].Width < 640)
912           Pixel_ratio = PIXEL_SIMPLE;
913         break;
914       case PIXEL_TALL:
915         if(Video_mode[0].Height < 400)
916           Pixel_ratio = PIXEL_SIMPLE;
917         break;
918       case PIXEL_DOUBLE:
919         if(Video_mode[0].Width < 640 || Video_mode[0].Height < 400)
920           Pixel_ratio = PIXEL_SIMPLE;
921         break;
922       case PIXEL_TRIPLE:
923         if(Video_mode[0].Width < 3*320 || Video_mode[0].Height < 3*200)
924           Pixel_ratio = PIXEL_SIMPLE;
925         break;
926       case PIXEL_WIDE2:
927         if(Video_mode[0].Width < 4*320 || Video_mode[0].Height < 2*200)
928           Pixel_ratio = PIXEL_SIMPLE;
929         break;
930       case PIXEL_TALL2:
931         if(Video_mode[0].Width < 2*320 || Video_mode[0].Height < 4*200)
932           Pixel_ratio = PIXEL_SIMPLE;
933         break;
934       case PIXEL_TALL3:
935         if(Video_mode[0].Width < 3*320 || Video_mode[0].Height < 4*200)
936           Pixel_ratio = PIXEL_SIMPLE;
937         break;
938       case PIXEL_QUAD:
939         if(Video_mode[0].Width < 4*320 || Video_mode[0].Height < 4*200)
940           Pixel_ratio = PIXEL_SIMPLE;
941         break;
942       default:
943         // Convert back unknown values to PIXEL_SIMPLE
944         Pixel_ratio = PIXEL_SIMPLE;
945         break;
946     }
947   }
948 
949   // Optional, Menu bars visibility (> 2.1)
950   if (!Load_INI_get_values (file, buffer,"Menubars_visible",1,values))
951   {
952     byte anim_visible = (values[0] & 2)!=0;
953     byte tools_visible = (values[0] & 4)!=0;
954 
955     // Skip status bar, always enabled.
956     Menu_bars[MENUBAR_LAYERS].Visible = anim_visible;
957     Menu_bars[MENUBAR_ANIMATION].Visible = 0;
958     Menu_bars[MENUBAR_TOOLS].Visible = tools_visible;
959   }
960 
961   conf->Right_click_colorpick=0;
962   // Optional, right mouse button to pick colors (>=2.3)
963   if (!Load_INI_get_values (file,buffer,"Right_click_colorpick",1,values))
964   {
965     conf->Right_click_colorpick=(values[0]!=0);
966   }
967 
968   conf->Sync_views=1;
969   // Optional, synced view of main and spare (>=2.3)
970   if (!Load_INI_get_values (file,buffer,"Sync_views",1,values))
971   {
972     conf->Sync_views=(values[0]!=0);
973   }
974 
975   conf->Swap_buttons=0;
976   // Optional, key for swap buttons (>=2.3)
977   if (!Load_INI_get_values (file,buffer,"Swap_buttons",1,values))
978   {
979     switch(values[0])
980     {
981       case 1:
982         conf->Swap_buttons=GFX2_MOD_CTRL;
983         break;
984       case 2:
985         conf->Swap_buttons=GFX2_MOD_ALT;
986         break;
987     }
988   }
989 
990   // Optional, Location of last directory used for Lua scripts browsing (>=2.3)
991   free(conf->Scripts_directory);
992   conf->Scripts_directory = NULL;
993   if (!Load_INI_get_string (file,buffer,"Scripts_directory",value_label, 1))
994   {
995     if (value_label[0] != '\0')
996       conf->Scripts_directory = strdup(value_label);
997   }
998   if (conf->Scripts_directory == NULL)
999   {
1000     // Default when empty:
1001     char * path = Realpath(Data_directory);
1002     conf->Scripts_directory = Filepath_append_to_dir(path, SCRIPTS_SUBDIRECTORY);
1003     free(path);
1004   }
1005 
1006   conf->Allow_multi_shortcuts=0;
1007   // Optional, allow or disallow multiple shortcuts on same key (>=2.3)
1008   if (!Load_INI_get_values (file,buffer,"Allow_multi_shortcuts",1,values))
1009   {
1010     conf->Allow_multi_shortcuts=(values[0]!=0);
1011   }
1012 
1013   conf->Tilemap_allow_flipped_x=0;
1014   // Optional, makes tilemap effect detect x-flipped tiles (>=2.4)
1015   if (!Load_INI_get_values (file,buffer,"Tilemap_detect_mirrored_x",1,values))
1016   {
1017     conf->Tilemap_allow_flipped_x=(values[0]!=0);
1018   }
1019 
1020   conf->Tilemap_allow_flipped_y=0;
1021   // Optional, makes tilemap effect detect y-flipped tiles (>=2.4)
1022   if (!Load_INI_get_values (file,buffer,"Tilemap_detect_mirrored_y",1,values))
1023   {
1024     conf->Tilemap_allow_flipped_y=(values[0]!=0);
1025   }
1026 
1027   conf->Tilemap_show_count=0;
1028   // Optional, makes tilemap effect display tile count (>=2.4)
1029   if (!Load_INI_get_values (file,buffer,"Tilemap_count",1,values))
1030   {
1031     conf->Tilemap_show_count=(values[0]!=0);
1032   }
1033 
1034   conf->Use_virtual_keyboard=0;
1035   // Optional, enables virtual keyboard (>=2.4)
1036   if (!Load_INI_get_values (file,buffer,"Use_virtual_keyboard",1,values))
1037   {
1038     if (values[0]>=0 && values[0]<=2)
1039       conf->Use_virtual_keyboard=values[0];
1040   }
1041 
1042   conf->Default_mode_layers=0;
1043   // Optional, remembers if the user last chose layers or anim (>=2.4)
1044   if (!Load_INI_get_values (file,buffer,"Default_mode_layers",1,values))
1045   {
1046     conf->Default_mode_layers=(values[0]!=0);
1047   }
1048 
1049   conf->MOTO_gamma=28;
1050   // Optional, gamma value used for palette of load/save Thomson MO/TO pictures (>=2.6)
1051   if (!Load_INI_get_values (file,buffer,"MOTO_gamma",1,values))
1052   {
1053     conf->MOTO_gamma=(byte)values[0];
1054   }
1055 
1056   // Insert new values here
1057 
1058   fclose(file);
1059 
1060   free(buffer);
1061   return 0;
1062 
1063   // Gestion des erreurs:
1064 
1065   Erreur_Retour:
1066     fclose(file);
1067     free(buffer);
1068     return return_code;
1069 
1070   Erreur_ERREUR_INI_CORROMPU:
1071 
1072     fclose(file);
1073     free(buffer);
1074     return ERROR_INI_CORRUPTED;
1075 }
1076