1 /***************************************************************************
2                           IniConfig.cpp  -  Sidplay2 config file reader.
3                              -------------------
4     begin                : Sun Mar 25 2001
5     copyright            : (C) 2000 by Simon White
6     email                : s_a_white@email.com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 /***************************************************************************
18  *  $Log: IniConfig.cpp,v $
19  *  Revision 1.12  2004/02/26 18:17:27  s_a_white
20  *  Use ini_readBool for boolean parameters rather than ini_readInt.
21  *
22  *  Revision 1.11  2002/02/18 20:05:52  s_a_white
23  *  Fixed for new libini ini_open call.
24  *
25  *  Revision 1.10  2001/11/16 19:30:45  s_a_white
26  *  Libsidutils support update.
27  *
28  *  Revision 1.9  2001/08/30 21:40:51  s_a_white
29  *  libini-1.1.7 update.
30  *
31  *  Revision 1.8  2001/08/20 18:28:55  s_a_white
32  *  MOS8580 fixed so that nothing means correct revision, 0 is 6581 and
33  *  1 is 8580.
34  *
35  *  Revision 1.7  2001/07/14 16:52:56  s_a_white
36  *  Removed warning.
37  *
38  *  Revision 1.6  2001/07/03 17:50:14  s_a_white
39  *  External filter no longer supported.  This filter is needed internally by the
40  *  library.
41  *
42  *  Revision 1.5  2001/04/09 17:11:03  s_a_white
43  *  Added INI file version number so theres a possibility for automated updates
44  *  should the keys/sections change names (or meaning).
45  *
46  *  Revision 1.4  2001/03/27 19:35:33  s_a_white
47  *  Moved default record length for wav files from main.cpp to IniConfig.cpp.
48  *
49  *  Revision 1.3  2001/03/27 19:00:49  s_a_white
50  *  Default record and play lengths can now be set in the sidplay2.ini file.
51  *
52  *  Revision 1.2  2001/03/26 18:13:07  s_a_white
53  *  Support individual filters for 6581 and 8580.
54  *
55  ***************************************************************************/
56 
57 #include <stdlib.h>
58 #include <string.h>
59 #include <sidplay/sidplay2.h>
60 #include "config.h"
61 #include "IniConfig.h"
62 
63 #ifdef HAVE_UNIX
64 #   include <sys/types.h>
65 #   include <sys/stat.h>  /* mkdir */
66 #   include <dirent.h>    /* opendir */
67 #endif
68 
69 #define SAFE_FREE(p) { if(p) { free (p); (p)=NULL; } }
70 const char *IniConfig::DIR_NAME  = ".sidplay";
71 const char *IniConfig::FILE_NAME = "sidplay2.ini";
72 
73 
IniConfig()74 IniConfig::IniConfig ()
75 :status(true)
76 {   // Initialise everything else
77     sidplay2_s.database    = NULL;
78     emulation_s.filter6581 = NULL;
79     emulation_s.filter8580 = NULL;
80     clear ();
81 }
82 
83 
~IniConfig()84 IniConfig::~IniConfig ()
85 {
86     clear ();
87 }
88 
89 
clear()90 void IniConfig::clear ()
91 {
92     sidplay2_s.version      = 1;           // INI File Version
93     SAFE_FREE (sidplay2_s.database);
94     sidplay2_s.playLength   = 0;           // INFINITE
95     sidplay2_s.recordLength = 3 * 60 + 30; // 3.5 minutes
96 
97     console_s.ansi          = false;
98     console_s.topLeft       = '+';
99     console_s.topRight      = '+';
100     console_s.bottomLeft    = '+';
101     console_s.bottomRight   = '+';
102     console_s.vertical      = '|';
103     console_s.horizontal    = '-';
104     console_s.junctionLeft  = '+';
105     console_s.junctionRight = '+';
106 
107     audio_s.frequency = SID2_DEFAULT_SAMPLING_FREQ;
108     audio_s.playback  = sid2_mono;
109     audio_s.precision = SID2_DEFAULT_PRECISION;
110 
111     emulation_s.clockSpeed    = SID2_CLOCK_CORRECT;
112     emulation_s.clockForced   = false;
113     emulation_s.sidModel      = SID2_MODEL_CORRECT;
114     emulation_s.filter        = true;
115     emulation_s.optimiseLevel = SID2_DEFAULT_OPTIMISATION;
116     emulation_s.sidSamples    = true;
117 
118     SAFE_FREE (emulation_s.filter6581);
119     SAFE_FREE (emulation_s.filter8580);
120 }
121 
122 
readInt(ini_fd_t ini,char * key,int & value)123 bool IniConfig::readInt (ini_fd_t ini, char *key, int &value)
124 {
125     int i = value;
126     if (ini_locateKey (ini, key) < 0)
127     {   // Dosen't exist, add it
128         (void) ini_writeString (ini, "");
129     }
130     if (ini_readInt (ini, &i) < 0)
131         return false;
132     value = i;
133     return true;
134 }
135 
136 
readString(ini_fd_t ini,char * key,char * & str)137 bool IniConfig::readString (ini_fd_t ini, char *key, char *&str)
138 {
139     char  *ret;
140     size_t length;
141 
142     if (ini_locateKey (ini, key) < 0)
143     {   // Dosen't exist, add it
144         (void) ini_writeString (ini, "");
145     }
146 
147     length = (size_t) ini_dataLength (ini);
148     if (!length)
149         return 0;
150 
151     ret = (char *) malloc (++length);
152     if (!ret)
153         return false;
154 
155     if (ini_readString (ini, ret, (uint) length) < 0)
156         goto IniCofig_readString_error;
157 
158     str = ret;
159 return true;
160 
161 IniCofig_readString_error:
162     if (str)
163         free (str);
164     return false;
165 }
166 
167 
readBool(ini_fd_t ini,char * key,bool & boolean)168 bool IniConfig::readBool (ini_fd_t ini, char *key, bool &boolean)
169 {
170     int b = boolean;
171     if (ini_locateKey (ini, key) < 0)
172     {   // Dosen't exist, add it
173         (void) ini_writeString (ini, "");
174     }
175     if (ini_readBool (ini, &b) < 0)
176         return false;
177     boolean = (b != 0);
178     return true;
179 }
180 
181 
readChar(ini_fd_t ini,char * key,char & ch)182 bool IniConfig::readChar (ini_fd_t ini, char *key, char &ch)
183 {
184     char *str, c = 0;
185     bool  ret = readString (ini, key, str);
186     if (!ret)
187         return false;
188 
189     // Check if we have an actual chanracter
190     if (str[0] == '\'')
191     {
192         if (str[2] != '\'')
193             ret = false;
194         else
195             c = str[1];
196     } // Nope is number
197     else
198         c = (char) atoi (str);
199 
200     // Clip off special characters
201     if ((unsigned) c >= 32)
202         ch = c;
203 
204     free (str);
205     return ret;
206 }
207 
208 
readTime(ini_fd_t ini,char * key,int & value)209 bool IniConfig::readTime (ini_fd_t ini, char *key, int &value)
210 {
211     char *str, *sep;
212     int   time;
213     bool  ret = readString (ini, key, str);
214     if (!ret)
215         return false;
216 
217     if (!*str)
218         return false;
219 
220     sep = strstr (str, ":");
221     if (!sep)
222     {   // User gave seconds
223         time = atoi (str);
224     }
225     else
226     {   // Read in MM:SS format
227         int val;
228         *sep = '\0';
229         val  = atoi (str);
230         if (val < 0 || val > 99)
231             goto IniCofig_readTime_error;
232         time = val * 60;
233         val  = atoi (sep + 1);
234         if (val < 0 || val > 59)
235             goto IniCofig_readTime_error;
236         time += val;
237     }
238 
239     value = time;
240     free (str);
241 return ret;
242 
243 IniCofig_readTime_error:
244     free (str);
245     return false;
246 }
247 
248 
readSidplay2(ini_fd_t ini)249 bool IniConfig::readSidplay2 (ini_fd_t ini)
250 {
251     bool ret = true;
252     int  time, version = sidplay2_s.version;
253 
254     (void) ini_locateHeading (ini, "SIDPlay2");
255     ret &= readInt (ini, "Version", version);
256     if (version > 0)
257         sidplay2_s.version = version;
258 
259     ret &= readString (ini, "Songlength Database", sidplay2_s.database);
260     if (readTime (ini, "Default Play Length", time))
261         sidplay2_s.playLength   = (uint_least32_t) time;
262     if (readTime (ini, "Default Record Length", time))
263         sidplay2_s.recordLength = (uint_least32_t) time;
264 
265     return ret;
266 }
267 
268 
readConsole(ini_fd_t ini)269 bool IniConfig::readConsole (ini_fd_t ini)
270 {
271     bool ret = true;
272     (void) ini_locateHeading (ini, "Console");
273     ret &= readBool (ini, "Ansi",                console_s.ansi);
274     ret &= readChar (ini, "Char Top Left",       console_s.topLeft);
275     ret &= readChar (ini, "Char Top Right",      console_s.topRight);
276     ret &= readChar (ini, "Char Bottom Left",    console_s.bottomLeft);
277     ret &= readChar (ini, "Char Bottom Right",   console_s.bottomRight);
278     ret &= readChar (ini, "Char Vertical",       console_s.vertical);
279     ret &= readChar (ini, "Char Horizontal",     console_s.horizontal);
280     ret &= readChar (ini, "Char Junction Left",  console_s.junctionLeft);
281     ret &= readChar (ini, "Char Junction Right", console_s.junctionRight);
282     return ret;
283 }
284 
285 
readAudio(ini_fd_t ini)286 bool IniConfig::readAudio (ini_fd_t ini)
287 {
288     bool ret = true;
289     (void) ini_locateHeading (ini, "Audio");
290 
291     {
292         int frequency = (int) audio_s.frequency;
293         ret &= readInt (ini, "Frequency", frequency);
294         audio_s.frequency = (unsigned long) frequency;
295     }
296 
297     {
298         int channels = 0;
299         ret &= readInt (ini, "Channels",  channels);
300         if (channels)
301         {
302             audio_s.playback = sid2_mono;
303             if (channels != 1)
304                 audio_s.playback = sid2_stereo;
305         }
306     }
307 
308     ret &= readInt (ini, "BitsPerSample", audio_s.precision);
309     return ret;
310 }
311 
312 
readEmulation(ini_fd_t ini)313 bool IniConfig::readEmulation (ini_fd_t ini)
314 {
315     bool ret = true;
316     (void) ini_locateHeading (ini, "Emulation");
317 
318     {
319         int clockSpeed = -1;
320         ret &= readInt (ini, "ClockSpeed", clockSpeed);
321         if (clockSpeed != -1)
322         {
323             if (clockSpeed < 0 || clockSpeed > 1)
324                 ret = false;
325             emulation_s.clockSpeed = SID2_CLOCK_PAL;
326             if (clockSpeed)
327                 emulation_s.clockSpeed = SID2_CLOCK_NTSC;
328         }
329     }
330 
331     ret &= readBool (ini, "ForceSongSpeed", emulation_s.clockForced);
332 
333     {
334         int mos8580 = -1;
335         ret &= readInt (ini, "MOS8580", mos8580);
336         if (mos8580 != -1)
337         {
338             if (mos8580 < 0 || mos8580 > 1)
339                 ret = false;
340             emulation_s.sidModel = SID2_MOS6581;
341             if (mos8580)
342                 emulation_s.sidModel = SID2_MOS8580;
343         }
344     }
345 
346     ret &= readBool (ini, "UseFilter", emulation_s.filter);
347 
348     {
349         int optimiseLevel = -1;
350         ret &= readInt  (ini, "OptimiseLevel", optimiseLevel);
351         if (optimiseLevel != -1)
352             emulation_s.optimiseLevel = optimiseLevel;
353     }
354 
355     ret &= readString (ini, "Filter6581", emulation_s.filter6581);
356     ret &= readString (ini, "Filter8580", emulation_s.filter8580);
357     ret &= readBool   (ini, "SidSamples", emulation_s.sidSamples);
358 
359     // These next two change the ini section!
360     if (emulation_s.filter6581)
361     {   // Try to load the filter
362         filter6581.read (ini, emulation_s.filter6581);
363         if (!filter6581)
364         {
365             filter6581.read (emulation_s.filter6581);
366             if (!filter6581)
367                 ret = false;
368         }
369     }
370 
371     if (emulation_s.filter8580)
372     {   // Try to load the filter
373         filter8580.read (ini, emulation_s.filter8580);
374         if (!filter8580)
375         {
376             filter8580.read (emulation_s.filter8580);
377             if (!filter8580)
378                 ret = false;
379         }
380     }
381 
382     return ret;
383 }
384 
385 
read()386 void IniConfig::read ()
387 {
388     char   *path = (char *) getenv ("HOME");
389     ini_fd_t ini  = 0;
390     char   *configPath;
391     size_t  length;
392 
393     if (!path)
394         path = (char *) getenv ("windir");
395 
396     if (!path)
397         path = "";
398 
399     length     = strlen (path) + strlen (DIR_NAME) + strlen (FILE_NAME) + 3;
400     configPath = (char *) malloc (length);
401     if (!configPath)
402         goto IniConfig_read_error;
403 
404     {   // Format path from system
405         char *s = path;
406         while (*s != '\0')
407         {
408             if (*s == '\\')
409                 *s = '/';
410             s++;
411         }
412     }
413 
414 #ifdef HAVE_UNIX
415     sprintf (configPath, "%s/%s", path, DIR_NAME);
416 
417     // Make sure the config path exists
418     if (!opendir (configPath))
419         mkdir (configPath, 0755);
420 
421     sprintf (configPath, "%s/%s", configPath, FILE_NAME);
422 #else
423     sprintf (configPath, "%s/%s", path, FILE_NAME);
424 #endif
425 
426     // Opens an existing file or creates a new one
427     ini = ini_open (configPath, "w", ";");
428 
429     // Unable to open file?
430     if (!ini)
431         goto IniConfig_read_error;
432 
433     clear ();
434 
435     // This may not exist here...
436     status &= readSidplay2  (ini);
437     status &= readConsole   (ini);
438     status &= readAudio     (ini);
439     status &= readEmulation (ini);
440     ini_close (ini);
441 return;
442 
443 IniConfig_read_error:
444     if (ini)
445         ini_close (ini);
446     clear ();
447     status = false;
448 }
449 
filter(sid2_model_t model)450 const sid_filter_t* IniConfig::filter (sid2_model_t model)
451 {
452     if (model == SID2_MOS8580)
453         return filter8580.provide ();
454     return filter6581.provide ();
455 }
456