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 | |
29 | class 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* (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 *="" ); |
54 | bool SetInt(const char *key, int32 val, const char *="" ); |
55 | bool SetUInt(const char *key, uint32 val, int base=10, const char *="" ); // 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 *="" ); |
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 (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 ; |
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 (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 ; |
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 */ |
277 | void S9xParsePortConfig(ConfigFile &, int pass); |
278 | |
279 | /* This may or may not be useful to you */ |
280 | const char *S9xParseDisplayConfig(ConfigFile &, int pass); |
281 | |
282 | #endif |
283 | |