1/*****************************************************************************\
2 Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3 This file is licensed under the Snes9x License.
4 For further information, consult the LICENSE file in the root directory.
5\*****************************************************************************/
6
7#ifndef _CONFIG_H_
8#define _CONFIG_H_
9
10#include <set>
11#include <map>
12#include <vector>
13#include <string>
14
15#ifdef UNZIP_SUPPORT
16# ifdef SYSTEM_ZIP
17# include <minizip/unzip.h>
18# else
19# include "unzip/unzip.h"
20# endif
21#endif
22#include "snes9x.h"
23
24#ifndef MAX
25# define MAX(a,b) ((a) > (b)? (a) : (b))
26# define MIN(a,b) ((a) < (b)? (a) : (b))
27#endif
28
29class ConfigFile {
30 public:
31 ConfigFile(void);
32
33 void Clear(void);
34
35 // return false on failure
36 bool LoadFile(const char *filename);
37 void LoadFile(Stream *r, const char *name=NULL);
38
39 // return false if key does not exist or is empty
40 bool Exists(const char *key);
41
42 // return the value / default
43 std::string GetString(const char *key, std::string def);
44 char *GetString(const char *key, char *out, uint32 outlen); // return NULL if it doesn't exist, out not affected
45 const char *GetString(const char *key, const char *def=NULL); // NOTE: returned pointer becomes invalid when key is deleted/modified, or the ConfigFile is Clear()ed or deleted.
46 char *GetStringDup(const char *key, const char *def=NULL); // Much like "strdup(GetString(key, def))"
47 int32 GetInt(const char *key, int32 def=-1, bool *bad=NULL);
48 uint32 GetUInt(const char *key, uint32 def=0, int base=0, bool *bad=NULL); // base = 0, 8, 10, or 16
49 bool GetBool(const char *key, bool def=false, bool *bad=NULL);
50 const char* GetComment(const char *key); // NOTE: returned pointer becomes invalid when key is deleted/modified, or the ConfigFile is Clear()ed or deleted.
51
52 // return true if the key existed prior to setting
53 bool SetString(const char *key, std::string val, const char *comment="");
54 bool SetInt(const char *key, int32 val, const char *comment="");
55 bool SetUInt(const char *key, uint32 val, int base=10, const char *comment=""); // base = 8, 10, or 16
56 bool SetBool(const char *key, bool val, const char *true_val="TRUE", const char *false_val="FALSE", const char *comment="");
57 bool DeleteKey(const char *key);
58
59 // Operation on entire sections
60 bool DeleteSection(const char *section);
61 typedef std::vector<std::pair<std::string,std::string> > secvec_t;
62 secvec_t GetSection(const char *section);
63 int GetSectionSize(const std::string section);
64
65 // Clears all key-value pairs that didn't receive a Set command, or a Get command with autoAdd on
66 void ClearUnused(void);
67
68 // Clears all stored line numbers
69 void ClearLines(void);
70
71 bool SaveTo(const char *filename);
72
73 static void SetDefaultAutoAdd(bool autoAdd);
74 static void SetNiceAlignment(bool align);
75 static void SetShowComments(bool show);
76 static void SetAlphaSort(bool sort);
77 static void SetTimeSort(bool sort);
78
79 private:
80 std::string Get(const char *key);
81 bool Has(const char *key);
82
83 class ConfigEntry {
84 protected:
85 int line;
86 std::string section;
87 std::string key;
88 std::string val;
89 std::string comment;
90 mutable bool used;
91
92 struct section_then_key_less {
93 bool operator()(const ConfigEntry &a, const ConfigEntry &b);
94 };
95
96 struct key_less {
97 bool operator()(const ConfigEntry &a, const ConfigEntry &b) const{
98 if(a.section!=b.section) return a.section<b.section;
99 return a.key<b.key;
100 }
101 };
102
103 struct line_less {
104 bool operator()(const ConfigEntry &a, const ConfigEntry &b){
105 if(a.line==b.line) return (b.val.empty() && !a.val.empty()) || a.key<b.key;
106 if(b.line<0) return true;
107 if(a.line<0) return false;
108 return a.line<b.line;
109 }
110 };
111
112 static void trim(std::string &s){
113 int i;
114 i=s.find_first_not_of(" \f\n\r\t\v");
115 if(i==-1){
116 s.clear();
117 return;
118 }
119 if(i>0) s.erase(0, i); // erase leading whitespace
120 i=s.find_last_not_of(" \f\n\r\t\v");
121 if(i!=-1) s.erase(i+1); // erase trailing whitespace
122 return;
123 }
124
125 // trims comments and leading/trailing whitespace from s, and returns any trimmed comments
126 // make sure not to call this more than once on the same string
127 static std::string trimCommented(std::string &s){
128 std::string cmt;
129 int i;
130 i=s.find_first_not_of(" \f\n\r\t\v");
131 if(i==-1){
132 s.clear();
133 return cmt;
134 }
135 if(i>0) s.erase(0, i); // erase leading whitespace
136 int off=0;
137 for(;;){
138 i=s.find('#',off); // find trailing comment
139 if(i>=0)
140 {
141 if((int)s.length()>i+1 && s.at(i+1) == '#') {
142 s.erase(i,1); // ignore ## and change to #
143 off = i+1;
144 continue;
145 } else {
146 int j=s.find_first_not_of(" \f\n\r\t\v",i+1);
147 if(j!=-1) cmt = s.substr(j); // store
148 s.erase(i); // erase trailing comment
149 }
150 }
151 break;
152 }
153 i=s.find_last_not_of(" \f\n\r\t\v");
154 if(i!=-1) s.erase(i+1); // erase trailing whitespace
155 return cmt;
156 }
157
158 public:
159 ConfigEntry(int l, const std::string &s, const std::string &k, const std::string &v) :
160 line(l), section(s), key(k), val(v) {
161 trim(section);
162 trim(key);
163 used=false;
164 }
165
166 void parse_key(const std::string &k){
167 int i=k.find("::");
168 if(i==-1){
169 section="Uncategorized"; key=k;
170 } else {
171 section=k.substr(0,i); key=k.substr(i+2);
172 }
173 trim(section);
174 trim(key);
175 used=false;
176 }
177
178 ConfigEntry(const std::string k){
179 parse_key(k);
180 }
181
182 ConfigEntry(const std::string k, const std::string &v) : line(-1), val(v) {
183 parse_key(k);
184 }
185
186 friend class ConfigFile;
187 friend struct key_less;
188 friend struct line_less;
189 };
190 class SectionSizes {
191 protected:
192 std::map<std::string,uint32> sections;
193
194 public:
195 uint32 GetSectionSize(const std::string section) {
196 uint32 count=0;
197 uint32 seclen;
198 std::map<std::string,uint32>::iterator it;
199 for(it=sections.begin(); it!=sections.end(); it++) {
200 seclen = MIN(section.size(),it->first.size());
201 if(it->first==section || !section.compare(0,seclen,it->first,0,seclen)) count+=it->second;
202 }
203 return count;
204 }
205
206 void IncreaseSectionSize(const std::string section) {
207 std::map<std::string,uint32>::iterator it=sections.find(section);
208 if(it!=sections.end())
209 it->second++;
210 else
211 sections.insert(std::pair<std::string,uint32>(section,1));
212 }
213
214 void DecreaseSectionSize(const std::string section) {
215 std::map<std::string,uint32>::iterator it=sections.find(section);
216 if(it!=sections.end())
217 it->second--;
218 }
219
220 void ClearSections() {
221 sections.clear();
222 }
223
224 void DeleteSection(const std::string section) {
225 sections.erase(section);
226 }
227
228 };
229 std::set<ConfigEntry, ConfigEntry::key_less> data;
230 SectionSizes sectionSizes;
231 int linectr;
232 static bool defaultAutoAdd;
233 static bool niceAlignment;
234 static bool showComments;
235 static bool alphaSort;
236 static bool timeSort;
237};
238
239/* Config file format:
240 *
241 * Comments are any lines whose first non-whitespace character is ';' or '#'.
242 * Note that comments can also follow a value, on the same line.
243 * To intentionally have a '#' character in the value, use ##
244 *
245 * All parameters fall into sections. To name a section, the first
246 * non-whitespace character on the line will be '[', and the last will be ']'.
247 *
248 * Parameters are simple key=value pairs. Whitespace around the '=', and at the
249 * beginning or end of the line is ignored. Key names may not contain '=' nor
250 * begin with '[', however values can. If the last character of the value is
251 * '\', the next line (sans leading/trailing whitespace) is considered part of
252 * the value as well. Programmatically, the key "K" in section "S" is referred
253 * to as "S::K", much like C++ namespaces. For example:
254 * [Section1]
255 * # this is a comment
256 * foo = bar \
257 * baz\
258 * quux \
259 * ## this is not a comment! # this IS a comment
260 * means the value of "Section1::foo" is "bar bazquux # this is not a comment!"
261 *
262 * Parameters may be of several types:
263 * String - Bare characters. If the first and last characters are both '"',
264 * they are removed (so just double them if you really want quotes
265 * there)
266 * Int - A decimal number from -2147483648 to 2147483647
267 * UInt - A number in decimal, hex, or octal from 0 to 4294967295 (or
268 * 0xffffffff, or 037777777777)
269 * Bool - true/false, 0/1, on/off, yes/no
270 *
271 * Of course, the actual accepted values for a parameter may be further
272 * restricted ;)
273 */
274
275
276/* You must write this for your port */
277void S9xParsePortConfig(ConfigFile &, int pass);
278
279/* This may or may not be useful to you */
280const char *S9xParseDisplayConfig(ConfigFile &, int pass);
281
282#endif
283