ALL: Huge upstream synch + PerRom DelaySI & CountPerOp parameters
[mupen64plus-pandora.git] / source / gles2glide64 / src / Glide64 / Ini.cpp
1 /*
2 *   Glide64 - Glide video plugin for Nintendo 64 emulators.
3 *   Copyright (c) 2002  Dave2001
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 2 of the License, or
8 *   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
16 *   Licence along with this program; if not, write to the Free
17 *   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 
18 *   Boston, MA  02110-1301, USA
19 */
20
21 //****************************************************************
22 //
23 // Glide64 - Glide Plugin for Nintendo 64 emulators (tested mostly with Project64)
24 // Project started on December 29th, 2001
25 //
26 // To modify Glide64:
27 // * Write your name and (optional)email, commented by your work, so I know who did it, and so that you can find which parts you modified when it comes time to send it to me.
28 // * Do NOT send me the whole project or file that you modified.  Take out your modified code sections, and tell me where to put them.  If people sent the whole thing, I would have many different versions, but no idea how to combine them all.
29 //
30 // Official Glide64 development channel: #Glide64 on EFnet
31 //
32 // Original author: Dave2001 (Dave2999@hotmail.com)
33 // Other authors: Gonetz, Gugaman
34 //
35 //****************************************************************
36
37 // INI code v1.1
38
39 #include "m64p.h"
40
41 #include "Ini.h"
42 #include "Gfx_1.3.h"
43 #include <limits.h>
44 #ifndef _WIN32
45 #include <unistd.h>
46 #include <string.h>
47 #include <dirent.h>
48 #include <stdlib.h>
49 #else
50 #include <io.h>
51 #endif // _WIN32
52
53 #include <errno.h>
54 #ifndef _WIN32
55 #include <sys/resource.h>
56 #endif
57
58 #ifdef _WIN32
59   #define PATH_MAX _MAX_PATH
60   #define stricmp _stricmp
61 #endif
62 #ifndef PATH_MAX
63   #define PATH_MAX 4096
64 #endif
65
66 FILE *ini;
67 int sectionstart;
68 int last_line;      // last good line
69 int last_line_ret;  // last line ended in return?
70 wxUint16 cr = 0x0A0D;
71 static char configdir[PATH_MAX] = {0};
72
73
74 //TODO: move INI_xxxx() function code into class and clean up
75 Ini *Ini::singleton = 0;
76
77 Ini::Ini()
78 {
79         if (!INI_Open())
80         {
81                 printf("Could not find INI file!");
82                 exit(1);
83         }
84 }
85
86 Ini *Ini::OpenIni()
87 {
88         if (!singleton)
89                 singleton = new Ini();
90         return singleton;
91 }
92
93 void Ini::SetPath(const char *path)
94 {
95         if (!INI_FindSection(path, false))
96         {
97                 printf("Could not find [%s] section in INI file!", path);
98         }
99 }
100
101
102 bool Ini::Read(const char *key, int *l)
103 {
104         int undef = 0xDEADBEEF;
105         int tmpVal = INI_ReadInt(key, undef, false);
106         if (tmpVal == undef)
107         {
108                 return false;
109         }
110         else
111         {
112                 *l = tmpVal;
113                 return true;
114         }
115 }
116
117 bool Ini::Read(const char *key, int *l, int defaultVal)
118 {
119         *l = INI_ReadInt(key, defaultVal, false);
120         return true;
121 }
122
123 int Ini::Read(const char *key, int defaultVal)
124 {
125         return INI_ReadInt(key, defaultVal, false);
126 }
127
128
129 BOOL INI_Open ()
130 {
131         //TODO: use ConfigGetSharedDataFilepath
132         
133     // Get the path of the dll, ex: C:\Games\Project64\Plugin\Glide64.dll
134     char path[PATH_MAX];
135     if(strlen(configdir) > 0)
136     {
137         strncpy(path, configdir, PATH_MAX);
138         // make sure there's a trailing '/'
139         //if(path[strlen(path)-1] != '/')
140         //    strncat(path, "/", PATH_MAX - strlen(path));
141     }
142     else
143     {
144 #ifdef _WIN32
145     GetModuleFileName (NULL, path, PATH_MAX);
146 #else // _WIN32
147 # ifdef __FreeBSD__
148    int n = readlink("/proc/curproc/files", path, PATH_MAX);
149 #else
150    int n = readlink("/proc/self/exe", path, PATH_MAX);
151 #endif
152    if (n == -1) strcpy(path, "./");
153    else
154      {
155     char path2[PATH_MAX];
156     int i;
157     
158     path[n] = '\0';
159     strcpy(path2, path);
160     for (i=strlen(path2)-1; i>0; i--)
161       {
162          if(path2[i] == '/') break;
163       }
164     if(i == 0) strcpy(path, "./");
165     else
166       {
167          DIR *dir;
168          struct dirent *entry;
169          int gooddir = 0;
170          
171          path2[i+1] = '\0';
172          dir = opendir(path2);
173          while((entry = readdir(dir)) != NULL)
174            {
175           if(!strcmp(entry->d_name, "plugins"))
176             gooddir = 1;
177            }
178          closedir(dir);
179          if(!gooddir) strcpy(path, "./");
180       }
181      }
182
183 #endif // _WIN32
184
185     // Find the previous backslash
186     int i;
187     for (i=strlen(path)-1; i>0; i--)
188     {
189 #ifdef _WIN32
190         if (path[i] == '\\')
191 #else // _WIN32
192             if (path[i] == '/')
193 #endif // _WIN32
194             break;
195     }
196     if (path == 0) return FALSE;
197     path[i+1] = 0;
198
199 #ifndef _WIN32
200    strcat(path, "plugins/");
201 #endif // _WIN32
202     }
203    
204     //strncat (path, "Glide64mk2.ini", PATH_MAX - strlen(path));
205     LOG("opening %s\n", path);
206     // Open the file
207     ini = fopen (path, "rb");
208     if (ini == NULL)
209     {
210         ERRLOG("Could not find Glide64mk2.ini!");
211         return FALSE;
212         /*
213         ini = fopen (path, "w+b");
214         if (ini == NULL)
215         {
216             return FALSE;
217         }
218         */
219     }
220
221     sectionstart = 0;
222     last_line = 0;
223     last_line_ret = 1;
224
225     return TRUE;
226 }
227
228 void INI_Close ()
229 {
230     //if (ini)
231       //  fclose(ini);
232 }
233
234 void INI_InsertSpace(int space)
235 {
236   printf("Inserting space, space to insert is %d\n", space);
237     // Since there is no good way to normally insert to or delete from a certain location in
238     //  a file, this function was added.  It will insert (or delete) space bytes at the
239     //  current location.
240
241     // note: negative count means delete
242     char chunk[2048];
243     int len, file, start_pos, cur_pos;
244
245 #ifdef _WIN32
246     file = _fileno(ini);
247 #else // _WIN32
248    file = fileno(ini);
249 #endif // _WIN32
250
251     start_pos = ftell(ini);
252     fseek(ini,0,SEEK_END);
253
254     // if adding, extend the file
255     if (space > 0)
256 #ifdef _WIN32
257         _chsize (file, _filelength(file)+space);
258 #else // _WIN32
259      {
260     int t1 = ftell(ini);
261     fseek(ini, 0L, SEEK_END);
262     int t2 = ftell(ini);
263     fseek(ini, t1, SEEK_SET);
264     if (ftruncate(file, t2+space) != 0)
265         ERRLOG("Failed to truncate .ini file to %i bytes", t2+space);
266     }
267 #endif // _WIN32
268
269     while (1) {
270         cur_pos = ftell(ini);
271         len = cur_pos - start_pos;
272         if (len == 0) break;
273         if (len > 2048) len = 2048;
274
275         fseek (ini,-len,SEEK_CUR);
276         if (fread(chunk,1,len,ini) != (size_t) len)
277             ERRLOG("Failed to read %i bytes from .ini file", len);
278         fseek (ini,-len+space,SEEK_CUR);
279         if (fwrite(chunk,1,len,ini) != (size_t) len)
280             ERRLOG("Failed to write %i bytes to .ini file", len);
281         fseek (ini,-len-space,SEEK_CUR);
282     }
283
284     // if deleted, make the file shorter
285     if (space < 0)
286 #ifdef _WIN32
287         _chsize (file, _filelength(file)+space);
288 #else // _WIN32
289      {
290     int t1 = ftell(ini);
291     fseek(ini, 0L, SEEK_END);
292     int t2 = ftell(ini);
293     fseek(ini, t1, SEEK_SET);
294     if (ftruncate(file, t2+space) != 0)
295         ERRLOG("Failed to truncate .ini file to %i bytes", t2+space);
296      }
297 #endif // _WIN32
298 }
299
300 BOOL INI_FindSection (const char *sectionname, BOOL create)
301 {
302     if (ini == NULL)
303         return FALSE;
304 /*    static char cached_secion[200]="\0";
305     static BOOL cached_result;
306     if (!strcasecmp(section,sectionname))
307         return cached_result;*/
308
309
310 //    printf("INI_FindSection trying to find name for %s\n", sectionname);
311
312     char line[256], section[64];
313     char *p;
314     int  i, sectionfound, ret;
315
316     rewind (ini);
317
318     last_line = 0;
319     sectionfound = 0;
320
321     while(!feof(ini)) {
322         ret = 0;
323         *line=0;
324         if (fgets(line,255,ini) == NULL)
325             break;
326
327         // remove enter
328         i=strlen(line);
329     // ZIGGY there was a bug here if EOL was unix like on a short line (i.e. a line
330     // with just EOL), it would write into line[-1]
331         if(i>=1 && line[i-1]==0xa) {
332       ret=1;
333       line[i-1]=0;
334       if (i>=2 && line[i-2]==0xd) line[i-2]=0;
335     }
336
337         // remove comments
338         p=line;
339         while(*p)
340         {
341             if (p[0]=='/' && p[1]=='/')
342             {
343                 p[0]=0;
344                 break;
345             }
346             p++;
347         }
348
349         // skip starting space
350         p=line;
351         while(*p<=' ' && *p) p++;
352
353         // empty line
354         if(!*p) continue;
355
356         last_line=ftell(ini);   // where to add if not found
357         last_line_ret = ret;
358
359         if(*p!='[') continue;
360
361         p++;
362         for (i=0;i<63;i++)
363         {
364             if(*p==']' || !*p) break;
365             section[i]=*p++;
366         }
367         section[i]=0;
368
369 #ifdef _WIN32
370         if(!stricmp(section,sectionname))
371 #else // _WIN32
372          if (!strcasecmp(section,sectionname))
373 #endif // _WIN32
374         {
375             sectionstart=ftell(ini);
376             sectionfound=1;
377             return TRUE;
378         }
379     }
380
381     if (!sectionfound && create)
382     {
383         // create the section
384         fseek(ini,last_line,SEEK_SET);
385         INI_InsertSpace ((!last_line_ret) * 2 + 6 + strlen(sectionname));
386         if (!last_line_ret)
387             if (fwrite(&cr, 1, 2, ini) != 2)
388                 ERRLOG("Failed to write <CR><LF> to .ini file");
389         sprintf (section, "[%s]", sectionname);
390         if (fwrite(&cr, 1, 2, ini) != 2 ||
391             fwrite(section, 1, strlen(section), ini) != strlen(section) ||
392             fwrite(&cr, 1, 2, ini) != 2)
393             ERRLOG("Failed to write Section line to .ini file");
394         sectionstart = ftell(ini);
395         last_line = sectionstart;
396         last_line_ret = 1;
397         return TRUE;
398     }
399
400     return FALSE;
401 }
402
403 // Reads the value of item 'itemname' as a string.
404 const char *INI_ReadString (const char *itemname, char *value, const char *def_value, BOOL create)
405 {
406     char line[256], name[64];
407     char *p, *n;
408     int ret, i;
409     *value = 0;
410
411     fseek(ini,sectionstart,SEEK_SET);
412
413     while(!feof(ini)) {
414         ret = 0;
415         *line=0;
416         if (fgets(line,255,ini) == NULL)
417             break;
418
419         // remove enter
420         i=strlen(line);
421     // ZIGGY there was a bug here if EOL was unix like on a short line (i.e. a line
422     // with just EOL), it would write into line[-1]
423         // OLD CODE : if(line[i-1]=='\n') ret=1, line[i-2]=0;
424         if(i>=1 && line[i-1]==0xa) {
425       ret=1;
426       line[i-1]=0;
427       if (i>=2 && line[i-2]==0xd) line[i-2]=0;
428     }
429
430         // remove comments
431         p=line;
432         while(*p)
433         {
434             if (p[0]==';')
435             {
436                 p[0]=0;
437                 break;
438             }
439             p++;
440         }
441
442         // skip starting space
443         p=line;
444         while(*p<=' ' && *p) p++;
445
446         // empty line
447         if(!*p) continue;
448
449         // new section
450         if(*p=='[') break;
451
452         last_line=ftell(ini);   // where to add if not found
453         last_line_ret = ret;
454
455         // read name
456         n = name;
457         while(*p && *p!='=' && *p>' ') *n++ = *p++;
458         *n = 0;
459
460 #ifdef _WIN32
461         if(!stricmp(name,itemname))
462 #else // _WIN32
463          if(!strcasecmp(name,itemname))
464 #endif // _WIN32
465         {
466             // skip spaces/equal sign
467             while(*p<=' ' || *p=='=') p++;
468
469             // read value
470             n = value;
471             while(*p) *n++ = *p++;
472
473             // remove trailing spaces
474             while (*(n-1) == ' ') n--;
475
476             *n=0;
477
478             return value;
479         }
480     }
481
482     // uh-oh, not found.  we need to create
483     if (create)
484     {
485         fseek(ini,last_line,SEEK_SET);
486         INI_InsertSpace ((!last_line_ret) * 2 + strlen(itemname) + strlen(def_value) + 5);
487         if (!last_line_ret)
488             if (fwrite(&cr, 1, 2, ini) != 2)
489                 ERRLOG("Failed to write <CR><LF> to .ini file");
490         sprintf (line, "%s = %s", itemname, def_value);
491         if (fwrite(line, 1, strlen(line), ini) != strlen(line) ||
492             fwrite(&cr, 1, 2, ini) != 2)
493             ERRLOG("Failed to write key,value line to .ini file");
494         last_line = ftell(ini);
495         last_line_ret = 1;
496     }
497
498     strcpy (value, def_value);
499     return value;
500 }
501
502 // Reads the value of item 'itemname' as a string.
503 void INI_WriteString (const char *itemname, const char *value)
504 {
505     char line[256], name[64];
506     char *p, *n;
507     int ret, i;
508
509     fseek(ini,sectionstart,SEEK_SET);
510
511     while(!feof(ini)) {
512         ret = 0;
513         *line=0;
514         if (fgets(line,255,ini) == NULL) break;
515
516         // remove enter
517         i=strlen(line);
518     // ZIGGY there was a bug here if EOL was unix like on a short line (i.e. a line
519     // with just EOL), it would write into line[-1]
520         // OLD CODE : if(line[i-1]=='\n') ret=1, line[i-2]=0;
521         if(i>=1 && line[i-1]==0xa) {
522       ret=1;
523       line[i-1]=0;
524       if (i>=2 && line[i-2]==0xd) line[i-2]=0;
525     }
526
527         // remove comments
528         p=line;
529         while(*p)
530         {
531             if (p[0]=='/' && p[1]=='/')
532             {
533                 p[0]=0;
534                 break;
535             }
536             p++;
537         }
538
539         // skip starting space
540         p=line;
541         while(*p<=' ' && *p) p++;
542
543         // empty line
544         if(!*p) continue;
545
546         // new section
547         if(*p=='[') break;
548
549         last_line=ftell(ini);   // where to add if not found
550         last_line_ret = ret;
551
552         // read name
553         n = name;
554         while(*p && *p!='=' && *p>' ') *n++ = *p++;
555         *n = 0;
556
557 #ifdef _WIN32
558         if(!stricmp(name,itemname))
559 #else // _WIN32
560          if(!strcasecmp(name,itemname))
561 #endif // _WIN32
562         {
563             INI_InsertSpace (-i + (strlen(itemname) + strlen(value) + 5));
564             sprintf (line, "%s = %s", itemname, value);
565             fseek (ini, -i, SEEK_CUR);
566             if (fwrite(line, 1, strlen(line), ini) != strlen(line) ||
567                 fwrite(&cr, 1, 2, ini) != 2)
568             {
569                 ERRLOG("Failed to write line '%s' to .ini file", line);
570             }
571             last_line = ftell(ini);
572             last_line_ret = 1;
573             return;
574         }
575     }
576
577     // uh-oh, not found.  we need to create
578     fseek(ini,last_line,SEEK_SET);
579     INI_InsertSpace ((!last_line_ret) * 2 + strlen(itemname) + strlen(value) + 5);
580     sprintf (line, "%s = %s", itemname, value);
581     if (!last_line_ret)
582         if (fwrite(&cr, 1, 2, ini) != 2)
583             ERRLOG("Failed to write <CR> to .ini file");
584     if (fwrite(line, 1, strlen(line), ini) != strlen(line) ||
585         fwrite(&cr, 1, 2, ini) != 2)
586     {
587         ERRLOG("Failed to write line '%s' to .ini file", line);
588     }
589     last_line = ftell(ini);
590     last_line_ret = 1;
591     return;
592 }
593
594 int INI_ReadInt (const char *itemname, int def_value, BOOL create)
595 {
596     if (ini == NULL)
597         return def_value;
598
599     char value[64], def[64];
600 #ifdef _WIN32
601     _itoa (def_value, def, 10);
602 #else // _WIN32
603    sprintf(def, "%d", def_value);
604 #endif // _WIN32
605     INI_ReadString (itemname, value, def, create);
606     return atoi (value);
607 }
608
609 void INI_WriteInt (const char *itemname, int value)
610 {
611     char valstr[64];
612 #ifdef _WIN32
613     _itoa (value, valstr, 10);
614 #else // _WIN32
615    sprintf(valstr, "%d", value);
616 #endif // _WIN32
617     INI_WriteString (itemname, valstr);
618 }
619
620 void SetConfigDir( const char *configDir )
621 {
622     strncpy(configdir, configDir, PATH_MAX);
623 }
624