1 /*
2 OpenLieroX
3
4 INI reader
5
6 18-01-2008, by Albert Zeyer
7 code under LGPL
8 */
9
10
11
12
13 #include "IniReader.h"
14 #include "FindFile.h"
15 #include "Debug.h"
16
17
18 IniReader::KeywordList IniReader::DefaultKeywords;
19
20
IniReader(const std::string & filename,KeywordList & keywords)21 IniReader::IniReader(const std::string& filename, KeywordList& keywords) : m_filename(filename), m_keywords(keywords) {
22 if (IniReader::DefaultKeywords.empty()) {
23 (IniReader::DefaultKeywords)["true"] = true;
24 (IniReader::DefaultKeywords)["false"] = false;
25 }
26 }
27
~IniReader()28 IniReader::~IniReader() {}
29
Parse()30 bool IniReader::Parse() {
31 FILE* f = OpenGameFile(m_filename, "r");
32 if(f == NULL)
33 return false;
34
35 bool res = true;
36 enum ParseState {
37 S_DEFAULT, S_IGNORERESTLINE, S_PROPNAME, S_PROPVALUE, S_SECTION };
38 ParseState state = S_DEFAULT;
39 std::string propname;
40 std::string section;
41 std::string value;
42
43 while(!feof(f) && !ferror(f)) {
44 unsigned char c = 0;
45 if(fread(&c, 1, 1, f) == 0) break;
46
47 if(c == '\r') continue; // ignore this
48
49 switch(state) {
50 case S_DEFAULT:
51 if(c >= 128) break; // just ignore unicode-stuff when we are in this state (UTF8 bytes at beginning are also handled by this)
52 else if(isspace(c)) break; // ignore spaces and newlines
53 else if(c == '#') { state = S_IGNORERESTLINE; /* this is a comment */ break; }
54 else if(c == '[') { state = S_SECTION; section = ""; break; }
55 else if(c == '=') {
56 warnings << "WARNING: \"=\" is not allowed as the first character in a line of " << m_filename << endl;
57 break; /* ignore */ }
58 else { state = S_PROPNAME; propname = c; break; }
59
60 case S_SECTION:
61 if(c == ']') {
62 if( ! OnNewSection(section) ) { res = false; goto parseCleanup; }
63 state = S_DEFAULT; NewSection(section); break; }
64 else if(c == '\n') {
65 warnings << "WARNING: section-name \"" << section << "\" of " << m_filename << " is not closed correctly" << endl;
66 state = S_DEFAULT; break; }
67 else if(isspace(c)) {
68 warnings << "WARNING: section-name \"" << section << "\" of " << m_filename << " contains a space" << endl;
69 break; /* ignore */ }
70 else { section += c; break; }
71
72 case S_PROPNAME:
73 if(c == '\n') {
74 warnings << "WARNING: property \"" << propname << "\" of " << m_filename << " incomplete" << endl;
75 state = S_DEFAULT; break; }
76 else if(isspace(c)) break; // just ignore spaces
77 else if(c == '=') { state = S_PROPVALUE; value = ""; break; }
78 else { propname += c; break; }
79
80 case S_PROPVALUE:
81 if(c == '\n' || c == '#') {
82 if( ! OnEntry(section, propname, value) ) { res = false; goto parseCleanup; }
83 NewEntryInSection(propname, value);
84 if(c == '#') state = S_IGNORERESTLINE; else state = S_DEFAULT;
85 break; }
86 else if(isspace(c) && value == "") break; // ignore heading spaces
87 else { value += c; break; }
88
89 case S_IGNORERESTLINE:
90 if(c == '\n') state = S_DEFAULT;
91 break; // ignore everything
92 }
93 }
94
95 // In case the endline is missing at the end of file, finish the parsing of the last line
96 if (state == S_PROPVALUE) {
97 if( ! OnEntry(section, propname, value) ) { res = false; goto parseCleanup; }
98 NewEntryInSection(propname, value);
99 }
100
101 // DEBUG: dump the file
102 /*notes << "Dump of " << m_filename << endl;
103 for (SectionMap::iterator it = m_sections.begin(); it != m_sections.end(); ++it) {
104 notes << "[" << it->first << "]" << endl;
105 for (Section::iterator k = it->second.begin(); k != it->second.end(); ++k)
106 notes << k->first << "=" << k->second << endl;
107 notes << endl;
108 }
109 notes << endl;*/
110
111 parseCleanup:
112 fclose(f);
113
114 return res;
115 }
116
NewSection(const std::string & name)117 void IniReader::NewSection(const std::string& name)
118 {
119 m_sections[name] = Section();
120 m_curSection = &m_sections[name];
121 }
122
NewEntryInSection(const std::string & name,const std::string & value)123 void IniReader::NewEntryInSection(const std::string& name, const std::string& value)
124 {
125 if (!m_curSection) {
126 warnings << "Cannot add item " << name << " to any section, because the current section is unset" << endl;
127 return;
128 }
129
130 (*m_curSection)[name] = value;
131 }
132
GetString(const std::string & section,const std::string & key,std::string & string) const133 bool IniReader::GetString(const std::string& section, const std::string& key, std::string& string) const
134 {
135 // Get the section
136 SectionMap::const_iterator sect = m_sections.find(section);
137 if (sect == m_sections.end())
138 return false;
139
140 // Get the key=value pair
141 Section::const_iterator item = sect->second.find(key);
142 if (item == sect->second.end())
143 return false;
144
145 string = item->second;
146 return true;
147 }
148
ReadString(const std::string & section,const std::string & key,std::string & value,std::string defaultv) const149 bool IniReader::ReadString(const std::string& section, const std::string& key, std::string& value, std::string defaultv) const
150 {
151 bool res = GetString(section, key, value);
152 if (!res)
153 value = defaultv;
154 return res;
155 }
156
ReadInteger(const std::string & section,const std::string & key,int * value,int defaultv) const157 bool IniReader::ReadInteger(const std::string& section, const std::string& key, int *value, int defaultv) const
158 {
159 std::string string;
160
161 *value = defaultv;
162
163 if(!GetString(section, key, string))
164 return false;
165
166 *value = from_string<int>(string);
167
168 return true;
169 }
170
ReadFloat(const std::string & section,const std::string & key,float * value,float defaultv) const171 bool IniReader::ReadFloat(const std::string §ion, const std::string &key, float *value, float defaultv) const
172 {
173 std::string string;
174
175 *value = defaultv;
176
177 if(!GetString(section, key, string))
178 return false;
179
180 *value = (float)atof(string);
181
182 return true;
183 }
184
ReadColour(const std::string & section,const std::string & key,Color & value,const Color & defaultv) const185 bool IniReader::ReadColour(const std::string §ion, const std::string &key, Color &value, const Color &defaultv) const
186 {
187 std::string string;
188
189 value = defaultv;
190
191 if(!GetString(section, key, string))
192 return false;
193
194 value = StrToCol(string);
195
196 return true;
197 }
198
ReadIntArray(const std::string & section,const std::string & key,int * array,int num_items) const199 bool IniReader::ReadIntArray(const std::string §ion, const std::string &key, int *array, int num_items) const
200 {
201 std::string string;
202
203 if (!GetString(section, key, string))
204 return false;
205
206 std::vector<std::string> arr = explode(string,",");
207 for (int i=0; i < MIN(num_items,(int)arr.size()); i++) {
208 TrimSpaces(arr[i]);
209 array[i] = from_string<int>(arr[i]);
210 }
211
212 return num_items == (int)arr.size();
213 }
214
ReadKeyword(const std::string & section,const std::string & key,int * value,int defaultv) const215 bool IniReader::ReadKeyword(const std::string §ion, const std::string &key, int *value, int defaultv) const
216 {
217 std::string string;
218
219 *value = defaultv;
220
221 if(!GetString(section, key, string))
222 return false;
223
224 // Try and find a keyword with matching keys
225 KeywordList::const_iterator f = m_keywords.find(string);
226 if(f != m_keywords.end()) {
227 //notes << filename << ":" << section << "." << key << ": " << f->first << "(" << string << ") = " << f->second << endl;
228 *value = f->second;
229 return true;
230 }
231
232 warnings << m_filename << ":" << section << "." << key << ": '" << string << "' is an unknown keyword" << endl;
233
234 return false;
235 }
236
ReadKeyword(const std::string & section,const std::string & key,bool * value,bool defaultv) const237 bool IniReader::ReadKeyword(const std::string §ion, const std::string &key, bool *value, bool defaultv) const
238 {
239 int v = defaultv ? 1 : 0;
240 bool ret = ReadKeyword(section, key, &v, defaultv ? 1 : 0);
241 *value = v != 0;
242 return ret;
243 }
244
ReadKeywordList(const std::string & section,const std::string & key,int * value,int defaultv) const245 bool IniReader::ReadKeywordList(const std::string §ion, const std::string &key, int *value, int defaultv) const
246 {
247 std::string string;
248
249 *value = defaultv;
250
251 if (!GetString(section, key, string))
252 return false;
253
254 std::vector<std::string> split = explode(string, ",");
255 for (std::vector<std::string>::iterator it = split.begin(); it != split.end(); it++) {
256 TrimSpaces(*it);
257 KeywordList::const_iterator key = m_keywords.find(*it);
258 if (key != m_keywords.end())
259 *value |= key->second;
260 }
261
262 return true;
263 }
264