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 &section, 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 &section, 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 &section, 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 &section, 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 &section, 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 &section, 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