1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 #include "C4Include.h"
17 #include "lib/StdCompiler.h"
18 
19 // *** StdCompiler
20 
Warn(const char * szWarning,...)21 void StdCompiler::Warn(const char *szWarning, ...)
22 {
23 	// Got warning callback?
24 	if (!pWarnCB) return;
25 	// Format message
26 	va_list args; va_start(args, szWarning);
27 	StdStrBuf Msg; Msg.FormatV(szWarning, args);
28 	// do callback
29 	(*pWarnCB)(pWarnData, getPosition().getData(), Msg.getData());
30 }
31 
SeparatorToChar(Sep eSep)32 char StdCompiler::SeparatorToChar(Sep eSep)
33 {
34 	switch (eSep)
35 	{
36 	case SEP_SEP: return ',';
37 	case SEP_SEP2: return ';';
38 	case SEP_SET: return '=';
39 	case SEP_PART: return '.';
40 	case SEP_PART2: return ':';
41 	case SEP_PLUS: return '+';
42 	case SEP_START: return '(';
43 	case SEP_END: return ')';
44 	case SEP_START2: return '[';
45 	case SEP_END2: return ']';
46 	case SEP_VLINE: return '|';
47 	case SEP_DOLLAR: return '$';
48 	default: assert(!"Unhandled Separator value");
49 	}
50 	return ' ';
51 }
52 
IsStringEnd(char c,RawCompileType eType)53 bool StdCompiler::IsStringEnd(char c, RawCompileType eType)
54 {
55 	switch (eType)
56 	{
57 	case RCT_Escaped: return c == '"' || !c || c == '\n' || c == '\r';
58 	case RCT_All: return !c || c == '\n' || c == '\r';
59 		// '-' is needed for Layers in Scenario.txt (C4NameList) and other Material-Texture combinations
60 	case RCT_Idtf: case RCT_IdtfAllowEmpty: case RCT_ID: return !isalnum((unsigned char)c) && c != '_' && c != '-';
61 	}
62 	// unreachable
63 	return true;
64 }
65 
66 // *** StdCompilerBinWrite
67 
DWord(int32_t & rInt)68 void StdCompilerBinWrite::DWord(int32_t &rInt)   { WriteValue(rInt); }
DWord(uint32_t & rInt)69 void StdCompilerBinWrite::DWord(uint32_t &rInt)  { WriteValue(rInt); }
Word(int16_t & rShort)70 void StdCompilerBinWrite::Word(int16_t &rShort)  { WriteValue(rShort); }
Word(uint16_t & rShort)71 void StdCompilerBinWrite::Word(uint16_t &rShort) { WriteValue(rShort); }
Byte(int8_t & rByte)72 void StdCompilerBinWrite::Byte(int8_t &rByte)    { WriteValue(rByte); }
Byte(uint8_t & rByte)73 void StdCompilerBinWrite::Byte(uint8_t &rByte)   { WriteValue(rByte); }
Boolean(bool & rBool)74 void StdCompilerBinWrite::Boolean(bool &rBool)   { WriteValue(rBool); }
Character(char & rChar)75 void StdCompilerBinWrite::Character(char &rChar) { WriteValue(rChar); }
String(char * szString,size_t iMaxLength,RawCompileType eType)76 void StdCompilerBinWrite::String(char *szString, size_t iMaxLength, RawCompileType eType)
77 {
78 	WriteData(szString, strlen(szString) + 1);
79 }
String(char ** pszString,RawCompileType eType)80 void StdCompilerBinWrite::String(char **pszString, RawCompileType eType)
81 {
82 	if (*pszString)
83 		WriteData(*pszString, strlen(*pszString) + 1);
84 	else
85 		WriteValue('\0');
86 }
87 
String(std::string & str,RawCompileType type)88 void StdCompilerBinWrite::String(std::string &str, RawCompileType type)
89 {
90 	WriteData(str.c_str(), str.size() + 1);
91 }
92 
93 template <class T>
WriteValue(const T & rValue)94 void StdCompilerBinWrite::WriteValue(const T &rValue)
95 {
96 	// Copy data
97 	if (fSecondPass)
98 		*getMBufPtr<T>(Buf, iPos) = rValue;
99 	iPos += sizeof(rValue);
100 }
101 
WriteData(const void * pData,size_t iSize)102 void StdCompilerBinWrite::WriteData(const void *pData, size_t iSize)
103 {
104 	// Copy data
105 	if (fSecondPass)
106 		Buf.Write(pData, iSize, iPos);
107 	iPos += iSize;
108 }
109 
Raw(void * pData,size_t iSize,RawCompileType eType)110 void StdCompilerBinWrite::Raw(void *pData, size_t iSize, RawCompileType eType)
111 {
112 	// Copy data
113 	if (fSecondPass)
114 		Buf.Write(pData, iSize, iPos);
115 	iPos += iSize;
116 }
117 
Begin()118 void StdCompilerBinWrite::Begin()
119 {
120 	fSecondPass = false; iPos = 0;
121 }
122 
BeginSecond()123 void StdCompilerBinWrite::BeginSecond()
124 {
125 	Buf.New(iPos);
126 	fSecondPass = true; iPos = 0;
127 }
128 
129 // *** StdCompilerBinRead
130 
DWord(int32_t & rInt)131 void StdCompilerBinRead::DWord(int32_t &rInt)   { ReadValue(rInt); }
DWord(uint32_t & rInt)132 void StdCompilerBinRead::DWord(uint32_t &rInt)  { ReadValue(rInt); }
Word(int16_t & rShort)133 void StdCompilerBinRead::Word(int16_t &rShort)  { ReadValue(rShort); }
Word(uint16_t & rShort)134 void StdCompilerBinRead::Word(uint16_t &rShort) { ReadValue(rShort); }
Byte(int8_t & rByte)135 void StdCompilerBinRead::Byte(int8_t &rByte)    { ReadValue(rByte); }
Byte(uint8_t & rByte)136 void StdCompilerBinRead::Byte(uint8_t &rByte)   { ReadValue(rByte); }
Boolean(bool & rBool)137 void StdCompilerBinRead::Boolean(bool &rBool)   { ReadValue(rBool); }
Character(char & rChar)138 void StdCompilerBinRead::Character(char &rChar) { ReadValue(rChar); }
String(char * szString,size_t iMaxLength,RawCompileType eType)139 void StdCompilerBinRead::String(char *szString, size_t iMaxLength, RawCompileType eType)
140 {
141 	// At least one byte data needed
142 	if (iPos >= Buf.getSize())
143 		{ excEOF(); return; }
144 	// Copy until no data left
145 	char *pPos = szString;
146 	while ((*pPos++ = *getBufPtr<char>(Buf, iPos++)))
147 		if (iPos >= Buf.getSize())
148 			{ excEOF(); return; }
149 		else if (pPos > szString + iMaxLength)
150 			{ excCorrupt("string too long"); return; }
151 }
152 
String(char ** pszString,RawCompileType eType)153 void StdCompilerBinRead::String(char **pszString, RawCompileType eType)
154 {
155 	// At least one byte data needed
156 	if (iPos >= Buf.getSize())
157 		{ excEOF(); return; }
158 	int iStart = iPos;
159 	// Search string end
160 	while (*getBufPtr<char>(Buf, iPos++))
161 		if (iPos >= Buf.getSize())
162 			{ excEOF(); return; }
163 	// Allocate and copy data
164 	*pszString = (char *) malloc(iPos - iStart);
165 	memcpy(*pszString, Buf.getPtr(iStart), iPos - iStart);
166 }
167 
String(std::string & str,RawCompileType type)168 void StdCompilerBinRead::String(std::string &str, RawCompileType type)
169 {
170 	// At least one byte data needed
171 	if (iPos >= Buf.getSize())
172 	{
173 		excEOF(); return;
174 	}
175 	int iStart = iPos;
176 	// Search string end
177 	while (*getBufPtr<char>(Buf, iPos++))
178 		if (iPos >= Buf.getSize())
179 		{
180 			excEOF(); return;
181 		}
182 	// Copy data
183 	str.assign(getBufPtr<char>(Buf, iStart), getBufPtr<char>(Buf, iPos));
184 }
185 
Raw(void * pData,size_t iSize,RawCompileType eType)186 void StdCompilerBinRead::Raw(void *pData, size_t iSize, RawCompileType eType)
187 {
188 	if (iPos + iSize > Buf.getSize())
189 		{ excEOF(); return; }
190 	// Copy data
191 	memcpy(pData, Buf.getPtr(iPos), iSize);
192 	iPos += iSize;
193 }
194 
getPosition() const195 StdStrBuf StdCompilerBinRead::getPosition() const
196 {
197 	return FormatString("byte %ld", static_cast<unsigned long>(iPos));
198 }
199 
200 template <class T>
ReadValue(T & rValue)201 inline void StdCompilerBinRead::ReadValue(T &rValue)
202 {
203 	// Pufferüberhang prüfen
204 	if (iPos + sizeof(T) > Buf.getSize())
205 		{ excEOF(); return; }
206 	// Kopieren
207 	rValue = *getBufPtr<T>(Buf, iPos);
208 	iPos += sizeof(T);
209 }
210 
Begin()211 void StdCompilerBinRead::Begin()
212 {
213 	iPos = 0;
214 }
215 
216 // *** StdCompilerINIWrite
217 
Name(const char * szName)218 bool StdCompilerINIWrite::Name(const char *szName)
219 {
220 	// Sub-Namesections exist, so it's a section. Write name if not already done so.
221 	if (fPutName) PutName(true);
222 	// Push struct
223 	Naming *pnNaming = new Naming;
224 	pnNaming->Name.Copy(szName);
225 	pnNaming->Parent = pNaming;
226 	pNaming = pnNaming;
227 	iDepth++;
228 	// Done
229 	fPutName = true; fInSection = false;
230 	return true;
231 }
232 
NameEnd(bool fBreak)233 void StdCompilerINIWrite::NameEnd(bool fBreak)
234 {
235 	// Append newline
236 	if (!fPutName && !fInSection)
237 		Buf.Append("\r\n");
238 	fPutName = false;
239 	// Note this makes it impossible to distinguish an empty name section from
240 	// a non-existing name section.
241 
242 	// Pop
243 	assert(iDepth);
244 	Naming *poNaming = pNaming;
245 	pNaming = poNaming->Parent;
246 	delete poNaming;
247 	iDepth--;
248 	// We're inside a section now
249 	fInSection = true;
250 }
251 
Separator(Sep eSep)252 bool StdCompilerINIWrite::Separator(Sep eSep)
253 {
254 	if (fInSection)
255 	{
256 		// Re-put section name
257 		PutName(true);
258 	}
259 	else
260 	{
261 		PrepareForValue();
262 		Buf.AppendChar(SeparatorToChar(eSep));
263 	}
264 	return true;
265 }
266 
DWord(int32_t & rInt)267 void StdCompilerINIWrite::DWord(int32_t &rInt)
268 {
269 	PrepareForValue();
270 	Buf.AppendFormat("%d", rInt);
271 }
DWord(uint32_t & rInt)272 void StdCompilerINIWrite::DWord(uint32_t &rInt)
273 {
274 	PrepareForValue();
275 	Buf.AppendFormat("%u", rInt);
276 }
Word(int16_t & rInt)277 void StdCompilerINIWrite::Word(int16_t &rInt)
278 {
279 	PrepareForValue();
280 	Buf.AppendFormat("%d", rInt);
281 }
Word(uint16_t & rInt)282 void StdCompilerINIWrite::Word(uint16_t &rInt)
283 {
284 	PrepareForValue();
285 	Buf.AppendFormat("%u", rInt);
286 }
Byte(int8_t & rByte)287 void StdCompilerINIWrite::Byte(int8_t &rByte)
288 {
289 	PrepareForValue();
290 	Buf.AppendFormat("%d", rByte);
291 }
Byte(uint8_t & rInt)292 void StdCompilerINIWrite::Byte(uint8_t &rInt)
293 {
294 	PrepareForValue();
295 	Buf.AppendFormat("%u", rInt);
296 }
Boolean(bool & rBool)297 void StdCompilerINIWrite::Boolean(bool &rBool)
298 {
299 	PrepareForValue();
300 	Buf.Append(rBool ? "true" : "false");
301 }
Character(char & rChar)302 void StdCompilerINIWrite::Character(char &rChar)
303 {
304 	PrepareForValue();
305 	Buf.AppendFormat("%c", rChar);
306 }
307 
String(char * szString,size_t iMaxLength,RawCompileType eType)308 void StdCompilerINIWrite::String(char *szString, size_t iMaxLength, RawCompileType eType)
309 {
310 	StringN(szString, strnlen(szString, iMaxLength), eType);
311 }
312 
StringN(const char * szString,size_t iLength,RawCompileType eType)313 void StdCompilerINIWrite::StringN(const char *szString, size_t iLength, RawCompileType eType)
314 {
315 	PrepareForValue();
316 	switch (eType)
317 	{
318 	case RCT_Escaped:
319 		WriteEscaped(szString, szString + iLength);
320 		break;
321 	case RCT_All:
322 	case RCT_Idtf:
323 	case RCT_IdtfAllowEmpty:
324 	case RCT_ID:
325 		Buf.Append(szString);
326 	}
327 }
328 
String(char ** pszString,RawCompileType eType)329 void StdCompilerINIWrite::String(char **pszString, RawCompileType eType)
330 {
331 	assert(pszString);
332 	char cNull = '\0';
333 	char * szString = *pszString ? *pszString : &cNull;
334 	String(szString, strlen(szString), eType);
335 }
336 
Raw(void * pData,size_t iSize,RawCompileType eType)337 void StdCompilerINIWrite::Raw(void *pData, size_t iSize, RawCompileType eType)
338 {
339 	switch (eType)
340 	{
341 	case RCT_Escaped:
342 		WriteEscaped(reinterpret_cast<char *>(pData), reinterpret_cast<char *>(pData) + iSize);
343 		break;
344 	case RCT_All:
345 	case RCT_Idtf:
346 	case RCT_IdtfAllowEmpty:
347 	case RCT_ID:
348 		Buf.Append(reinterpret_cast<char *>(pData), iSize);
349 	}
350 }
351 
String(std::string & str,RawCompileType type)352 void StdCompilerINIWrite::String(std::string &str, RawCompileType type)
353 {
354 	StringN(str.c_str(), str.size(), type);
355 }
356 
Begin()357 void StdCompilerINIWrite::Begin()
358 {
359 	pNaming = nullptr;
360 	fPutName = false;
361 	iDepth = 0;
362 	fInSection = false;
363 	Buf.Clear();
364 }
365 
End()366 void StdCompilerINIWrite::End()
367 {
368 	// Ensure all namings were closed properly
369 	assert(!iDepth);
370 }
371 
PrepareForValue()372 void StdCompilerINIWrite::PrepareForValue()
373 {
374 	// Put name (value-type), if not already done so
375 	if (fPutName) PutName(false);
376 	// No data allowed inside of sections
377 	assert(!fInSection);
378 	// No values allowed on top-level - must be contained in at least one section
379 	assert(iDepth > 1);
380 }
381 
WriteEscaped(const char * szString,const char * pEnd)382 void StdCompilerINIWrite::WriteEscaped(const char *szString, const char *pEnd)
383 {
384 	Buf.AppendChar('"');
385 	// Try to write chunks as huge as possible of "normal" chars.
386 	// Note this excludes '\0', so the standard Append() can be used.
387 	const char *pStart, *pPos; pStart = pPos = szString;
388 	bool fLastNumEscape = false; // catch "\1""1", which must become "\1\61"
389 	for (; pPos < pEnd; pPos++)
390 		if (!isprint((unsigned char)(unsigned char) *pPos) || *pPos == '\\' || *pPos == '"' || (fLastNumEscape && isdigit((unsigned char)*pPos)))
391 		{
392 			// Write everything up to this point
393 			if (pPos - pStart) Buf.Append(pStart, pPos - pStart);
394 			// Escape
395 			fLastNumEscape = false;
396 			switch (*pPos)
397 			{
398 			case '\a': Buf.Append(R"(\a)"); break;
399 			case '\b': Buf.Append(R"(\b)"); break;
400 			case '\f': Buf.Append(R"(\f)"); break;
401 			case '\n': Buf.Append(R"(\n)"); break;
402 			case '\r': Buf.Append(R"(\r)"); break;
403 			case '\t': Buf.Append(R"(\t)"); break;
404 			case '\v': Buf.Append(R"(\v)"); break;
405 			case '\"': Buf.Append(R"(\")"); break;
406 			case '\\': Buf.Append(R"(\\)"); break;
407 			default:
408 				Buf.AppendFormat(R"(\%o)", *reinterpret_cast<const unsigned char *>(pPos));
409 				fLastNumEscape = true;
410 			}
411 			// Set pointer
412 			pStart = pPos + 1;
413 		}
414 		else
415 			fLastNumEscape = false;
416 	// Write the rest
417 	if (pEnd - pStart) Buf.Append(pStart, pEnd - pStart);
418 	Buf.AppendChar('"');
419 }
420 
WriteIndent(bool fSection)421 void StdCompilerINIWrite::WriteIndent(bool fSection)
422 {
423 	// Do not indent level 1 (level 0 values aren't allowed - see above)
424 	int iIndent = iDepth - 1;
425 	// Sections are indented more, even though they belong to this level
426 	if (!fSection) iIndent--;
427 	// Do indention
428 	if (iIndent <= 0) return;
429 	Buf.AppendChars(' ',  iIndent * 2);
430 }
431 
PutName(bool fSection)432 void StdCompilerINIWrite::PutName(bool fSection)
433 {
434 	if (fSection && Buf.getLength())
435 		Buf.Append("\r\n");
436 	WriteIndent(fSection);
437 	// Put name
438 	if (fSection)
439 		Buf.AppendFormat("[%s]\r\n", pNaming->Name.getData());
440 	else
441 		Buf.AppendFormat("%s=", pNaming->Name.getData());
442 	// Set flag
443 	fPutName = false;
444 }
445 
446 // *** StdCompilerINIRead
447 
448 StdCompilerINIRead::StdCompilerINIRead() = default;
449 
~StdCompilerINIRead()450 StdCompilerINIRead::~StdCompilerINIRead()
451 {
452 	FreeNameTree();
453 }
454 
455 // Naming
Name(const char * szName)456 bool StdCompilerINIRead::Name(const char *szName)
457 {
458 	// Increase depth
459 	iDepth++;
460 	// Parent category virtual?
461 	if (iDepth - 1 > iRealDepth)
462 		return false;
463 	// Name must be alphanumerical and non-empty (force it)
464 	if (!isalpha((unsigned char)*szName))
465 		{ assert(false); return false; }
466 	for (const char *p = szName + 1; *p; p++)
467 		// C4Update needs Name**...
468 		if (!isalnum((unsigned char)*p) && *p != ' ' && *p != '_' && *p != '*')
469 			{ assert(false); return false; }
470 	// Search name
471 	NameNode *pNode;
472 	for (pNode = pName->FirstChild; pNode; pNode = pNode->NextChild)
473 		if (pNode->Pos && pNode->Name == szName)
474 			break;
475 	// Not found?
476 	if (!pNode)
477 	{
478 		NotFoundName = szName;
479 		return false;
480 	}
481 	// Save tree position, indicate success
482 	pName = pNode;
483 	pPos = pName->Pos;
484 	pReenter = nullptr;
485 	iRealDepth++;
486 	return true;
487 }
NameEnd(bool fBreak)488 void StdCompilerINIRead::NameEnd(bool fBreak)
489 {
490 	assert(iDepth > 0);
491 	if (iRealDepth == iDepth)
492 	{
493 		// Remove childs
494 		for (NameNode *pNode = pName->FirstChild, *pNext; pNode; pNode = pNext)
495 		{
496 			// Report unused entries
497 			if (pNode->Pos && !fBreak)
498 				Warn(R"(Unexpected %s "%s"!)", pNode->Section ? "section" : "value", pNode->Name.getData());
499 			// delete node
500 			pNext = pNode->NextChild;
501 			delete pNode;
502 		}
503 		// Remove name so it won't be found again
504 		NameNode *pParent = pName->Parent;
505 		(pName->PrevChild ? pName->PrevChild->NextChild : pParent->FirstChild) = pName->NextChild;
506 		(pName->NextChild ? pName->NextChild->PrevChild : pParent->LastChild) = pName->PrevChild;
507 		delete pName;
508 		// Go up
509 		pName = pParent;
510 		iRealDepth--;
511 	}
512 	// Decrease depth
513 	iDepth--;
514 	// This is the middle of nowhere
515 	pPos = nullptr; pReenter = nullptr;
516 }
517 
FollowName(const char * szName)518 bool StdCompilerINIRead::FollowName(const char *szName)
519 {
520 	// Current naming virtual?
521 	if (iDepth > iRealDepth)
522 		return false;
523 	// Next section must be the one
524 	if (!pName->NextChild || pName->NextChild->Name != szName)
525 	{
526 		// End current naming
527 		NameEnd();
528 		// Go into virtual naming
529 		iDepth++;
530 		return false;
531 	}
532 	// End current naming
533 	NameEnd();
534 	// Start new one
535 	Name(szName);
536 	// Done
537 	return true;
538 }
539 
540 // Separators
Separator(Sep eSep)541 bool StdCompilerINIRead::Separator(Sep eSep)
542 {
543 	if (iDepth > iRealDepth) return false;
544 	// In section?
545 	if (pName->Section)
546 	{
547 		// Store current name, search another section with the same name
548 		StdStrBuf CurrName = pName->Name;
549 		NameEnd();
550 		return Name(CurrName.getData());
551 	}
552 	// Position saved back from separator mismatch?
553 	if (pReenter) { pPos = pReenter; pReenter = nullptr; }
554 	// Nothing to read?
555 	if (!pPos) return false;
556 	// Read (while skipping over whitespace)
557 	SkipWhitespace();
558 	// Separator mismatch? Let all read attempts fail until the correct separator is found or the naming ends.
559 	if (*pPos != SeparatorToChar(eSep)) { pReenter = pPos; pPos = nullptr; return false; }
560 	// Go over separator, success
561 	pPos++;
562 	return true;
563 }
564 
NoSeparator()565 void StdCompilerINIRead::NoSeparator()
566 {
567 	// Position saved back from separator mismatch?
568 	if (pReenter) { pPos = pReenter; pReenter = nullptr; }
569 }
570 
NameCount(const char * szName)571 int StdCompilerINIRead::NameCount(const char *szName)
572 {
573 	// not in virtual naming
574 	if (iDepth > iRealDepth || !pName) return 0;
575 	// count within current name
576 	int iCount = 0;
577 	NameNode *pNode;
578 	for (pNode = pName->FirstChild; pNode; pNode = pNode->NextChild)
579 		// if no name is given, all valid subsections are counted
580 		if (pNode->Pos && (!szName || pNode->Name == szName))
581 			++iCount;
582 	return iCount;
583 }
584 
GetNameByIndex(size_t idx) const585 const char *StdCompilerINIRead::GetNameByIndex(size_t idx) const
586 {
587 	// not in virtual naming
588 	if (iDepth > iRealDepth || !pName) return nullptr;
589 	// count within current name
590 	NameNode *pNode;
591 	for (pNode = pName->FirstChild; pNode; pNode = pNode->NextChild)
592 		// all valid subsections are counted
593 		if (pNode->Pos)
594 				if (!idx--)
595 					return pNode->Name.getData();
596 	// index out of range
597 	return nullptr;
598 }
599 
600 // Various data readers
DWord(int32_t & rInt)601 void StdCompilerINIRead::DWord(int32_t &rInt)
602 {
603 	rInt = ReadNum();
604 }
DWord(uint32_t & rInt)605 void StdCompilerINIRead::DWord(uint32_t &rInt)
606 {
607 	rInt = ReadUNum();
608 }
Word(int16_t & rShort)609 void StdCompilerINIRead::Word(int16_t &rShort)
610 {
611 	const int MIN = -(1 << 15), MAX = (1 << 15) - 1;
612 	int iNum = ReadNum();
613 	if (iNum < MIN || iNum > MAX)
614 		Warn("number out of range (%d to %d): %d ", MIN, MAX, iNum);
615 	rShort = Clamp(iNum, MIN, MAX);
616 }
Word(uint16_t & rShort)617 void StdCompilerINIRead::Word(uint16_t &rShort)
618 {
619 	const unsigned int MIN = 0, MAX = (1 << 16) - 1;
620 	unsigned int iNum = ReadUNum();
621 	if (iNum > MAX)
622 		Warn("number out of range (%u to %u): %u ", MIN, MAX, iNum);
623 	rShort = Clamp(iNum, MIN, MAX);
624 }
Byte(int8_t & rByte)625 void StdCompilerINIRead::Byte(int8_t &rByte)
626 {
627 	const int MIN = -(1 << 7), MAX = (1 << 7) - 1;
628 	int iNum = ReadNum();
629 	if (iNum < MIN || iNum > MAX)
630 		Warn("number out of range (%d to %d): %d ", MIN, MAX, iNum);
631 	rByte = Clamp(iNum, MIN, MAX);
632 }
Byte(uint8_t & rByte)633 void StdCompilerINIRead::Byte(uint8_t &rByte)
634 {
635 	const unsigned int MIN = 0, MAX = (1 << 8) - 1;
636 	unsigned int iNum = ReadUNum();
637 	if (iNum > MAX)
638 		Warn("number out of range (%u to %u): %u ", MIN, MAX, iNum);
639 	rByte = Clamp(iNum, MIN, MAX);
640 }
Boolean(bool & rBool)641 void StdCompilerINIRead::Boolean(bool &rBool)
642 {
643 	if (!pPos) { notFound("Boolean"); return; }
644 	if (*pPos == '1' && !isdigit((unsigned char)*(pPos+1)))
645 		{ rBool = true; pPos ++; }
646 	else if (*pPos == '0' && !isdigit((unsigned char)*(pPos+1)))
647 		{ rBool = false; pPos ++; }
648 	else if (SEqual2(pPos, "true"))
649 		{ rBool = true; pPos += 4; }
650 	else if (SEqual2(pPos, "false"))
651 		{ rBool = false; pPos += 5; }
652 	else
653 		{ notFound("Boolean"); return; }
654 }
Character(char & rChar)655 void StdCompilerINIRead::Character(char &rChar)
656 {
657 	if (!pPos || !isalpha((unsigned char)*pPos))
658 		{ notFound("Character"); return; }
659 	rChar = *pPos++;
660 }
String(char * szString,size_t iMaxLength,RawCompileType eType)661 void StdCompilerINIRead::String(char *szString, size_t iMaxLength, RawCompileType eType)
662 {
663 	// Read data
664 	StdBuf Buf = ReadString(iMaxLength, eType, true);
665 	// Copy
666 	SCopy(getBufPtr<char>(Buf), szString, iMaxLength);
667 }
String(char ** pszString,RawCompileType eType)668 void StdCompilerINIRead::String(char **pszString, RawCompileType eType)
669 {
670 	// Get length
671 	size_t iLength = GetStringLength(eType);
672 	// Read data
673 	StdBuf Buf = ReadString(iLength, eType, true);
674 	// Set
675 	*pszString = reinterpret_cast<char *>(Buf.GrabPointer());
676 }
String(std::string & str,RawCompileType type)677 void StdCompilerINIRead::String(std::string &str, RawCompileType type)
678 {
679 	// Get length
680 	size_t iLength = GetStringLength(type);
681 	// Read data
682 	StdBuf Buf = ReadString(iLength, type, true);
683 	str = getBufPtr<char>(Buf);
684 }
Raw(void * pData,size_t iSize,RawCompileType eType)685 void StdCompilerINIRead::Raw(void *pData, size_t iSize, RawCompileType eType)
686 {
687 	// Read data
688 	StdBuf Buf = ReadString(iSize, eType, false);
689 	// Correct size?
690 	if (Buf.getSize() != iSize)
691 		Warn("got %u bytes raw data, but %u bytes expected!", Buf.getSize(), iSize);
692 	// Copy
693 	MemCopy(Buf.getData(), pData, iSize);
694 }
695 
getLineNumberOfPos(const char * pos) const696 uint32_t StdCompilerINIRead::getLineNumberOfPos(const char *pos) const
697 {
698 	// Figure out quickly whether we already know which line this is
699 	auto entry = std::lower_bound(lineBreaks.begin(), lineBreaks.end(), pos);
700 	if (entry != lineBreaks.end())
701 	{
702 		return std::distance(lineBreaks.begin(), entry) + 1;
703 	}
704 	// Otherwise search through the buffer until we find out, filling the
705 	// cache in the process
706 	const char *cursor = Buf.getData();
707 	if (!lineBreaks.empty())
708 		cursor = *(lineBreaks.end() - 1) + 1;
709 	for (;;)
710 	{
711 		if (*cursor == '\0' || *cursor == '\n')
712 		{
713 			lineBreaks.push_back(cursor);
714 
715 			// If we're at the end of the file or have found the line break
716 			// past the requested position, we're done for now
717 			if (*cursor == '\0' || pos < cursor)
718 			{
719 				break;
720 			}
721 		}
722 		++cursor;
723 	}
724 	return std::distance(lineBreaks.begin(),
725 		std::lower_bound(lineBreaks.begin(), lineBreaks.end(), pos)) + 1;
726 }
727 
getPosition() const728 StdStrBuf StdCompilerINIRead::getPosition() const
729 {
730 	if (pPos)
731 		return FormatString("line %d", getLineNumberOfPos(pPos));
732 	else if (iDepth == iRealDepth)
733 		return FormatString(pName->Section ? R"(section "%s", after line %d)" : R"(value "%s", line %d)", pName->Name.getData(), getLineNumberOfPos(pName->Pos));
734 	else if (iRealDepth)
735 		return FormatString(R"(missing value/section "%s" inside section "%s" (line %d))", NotFoundName.getData(), pName->Name.getData(), getLineNumberOfPos(pName->Pos));
736 	else
737 		return FormatString(R"(missing value/section "%s")", NotFoundName.getData());
738 }
739 
Begin()740 void StdCompilerINIRead::Begin()
741 {
742 	// Already running? This may happen if someone confuses Compile with Value.
743 	assert(!iDepth && !iRealDepth && !pNameRoot);
744 	// Create tree
745 	CreateNameTree();
746 	// Start must be inside a section
747 	iDepth = iRealDepth = 0;
748 	pPos = nullptr; pReenter = nullptr;
749 }
End()750 void StdCompilerINIRead::End()
751 {
752 	assert(!iDepth && !iRealDepth);
753 	FreeNameTree();
754 }
755 
CreateNameTree()756 void StdCompilerINIRead::CreateNameTree()
757 {
758 	FreeNameTree();
759 	// Create root node
760 	pName = pNameRoot = new NameNode();
761 	// No input? Stop
762 	if (!Buf) return;
763 	// Start scanning
764 	pPos = Buf.getPtr(0);
765 	while (*pPos)
766 	{
767 		// Go over whitespace
768 		int iIndent = 0;
769 		while (*pPos == ' ' || *pPos == '\t')
770 			{ pPos++; iIndent++; }
771 		// Name/Section?
772 		bool fSection = *pPos == '[' && isalpha((unsigned char)*(pPos+1));
773 		if (fSection || isalpha((unsigned char)*pPos))
774 		{
775 			// Treat values as if they had more indention
776 			// (so they become children of sections on the same level)
777 			if (!fSection) iIndent++; else pPos++;
778 			// Go up in tree structure if there is less indention
779 			while (pName->Parent && pName->Indent >= iIndent)
780 				pName = pName->Parent;
781 			// Copy name
782 			StdStrBuf Name;
783 			while (isalnum((unsigned char)*pPos) || *pPos == ' ' || *pPos == '_')
784 				Name.AppendChar(*pPos++);
785 			while (*pPos == ' ' || *pPos == '\t') pPos++;
786 			if ( *pPos != (fSection ? ']' : '=') )
787 				// Warn, ignore
788 				Warn(isprint((unsigned char)*pPos) ? "Unexpected character ('%c'): %s ignored" : "Unexpected character ('0x%02x'): %s ignored", unsigned(*pPos), fSection ? "section" : "value");
789 			else
790 			{
791 				pPos++;
792 				// Create new node
793 				NameNode *pPrev = pName->LastChild;
794 				pName =
795 				  pName->LastChild =
796 				    (pName->LastChild ? pName->LastChild->NextChild : pName->FirstChild) =
797 				      new NameNode(pName);
798 				pName->PrevChild = pPrev;
799 				pName->Name.Take(std::move(Name));
800 				pName->Pos = pPos;
801 				pName->Indent = iIndent;
802 				pName->Section = fSection;
803 				// Values don't have children (even if the indention looks like it)
804 				if (!fSection)
805 					pName = pName->Parent;
806 			}
807 		}
808 		// Skip line
809 		while (*pPos && (*pPos != '\n' && *pPos != '\r'))
810 			pPos++;
811 		while (*pPos == '\n' || *pPos == '\r')
812 			pPos++;
813 	}
814 	// Set pointer back
815 	pName = pNameRoot;
816 }
817 
FreeNameTree()818 void StdCompilerINIRead::FreeNameTree()
819 {
820 	// free all nodes
821 	FreeNameNode(pNameRoot);
822 	pName = pNameRoot = nullptr;
823 }
824 
FreeNameNode(NameNode * pDelNode)825 void StdCompilerINIRead::FreeNameNode(NameNode *pDelNode)
826 {
827 	NameNode *pNode = pDelNode;
828 	while (pNode)
829 	{
830 		if (pNode->FirstChild)
831 			pNode = pNode->FirstChild;
832 		else
833 		{
834 			NameNode *pDelete = pNode;
835 			if (pDelete == pDelNode) { delete pDelete; break; }
836 			if (pNode->NextChild)
837 				pNode = pNode->NextChild;
838 			else
839 			{
840 				pNode = pNode->Parent;
841 				if (pNode) pNode->FirstChild = nullptr;
842 			}
843 			delete pDelete;
844 		}
845 	}
846 }
847 
SkipWhitespace()848 void StdCompilerINIRead::SkipWhitespace()
849 {
850 	while (*pPos == ' ' || *pPos == '\t')
851 		pPos++;
852 }
853 
SkipNum()854 void StdCompilerINIRead::SkipNum()
855 {
856 	while (*pPos == '+' || *pPos == '-' || isdigit((unsigned char)*pPos))
857 		pPos++;
858 }
859 
ReadNum()860 long StdCompilerINIRead::ReadNum()
861 {
862 	if (!pPos)
863 		{ notFound("Number"); return 0; }
864 	// Skip whitespace
865 	SkipWhitespace();
866 	// Read number. If this breaks, Günther is to blame!
867 	const char *pnPos = pPos;
868 	long iNum = strtol(pPos, const_cast<char **>(&pnPos), 10);
869 	// Could not read?
870 	if (!iNum && pnPos == pPos)
871 		{ notFound("Number"); return 0; }
872 	// Get over it
873 	pPos = pnPos;
874 	return iNum;
875 }
876 
ReadUNum()877 unsigned long StdCompilerINIRead::ReadUNum()
878 {
879 	if (!pPos)
880 		{ notFound("Number"); return 0; }
881 	// Skip whitespace
882 	SkipWhitespace();
883 	// Read number. If this breaks, Günther is to blame!
884 	const char *pnPos = pPos;
885 	unsigned long iNum = strtoul(pPos, const_cast<char **>(&pnPos), 10);
886 	// Could not read?
887 	if (!iNum && pnPos == pPos)
888 		{ notFound("Number"); return 0; }
889 	// Get over it
890 	pPos = pnPos;
891 	return iNum;
892 }
893 
GetStringLength(RawCompileType eRawType)894 size_t StdCompilerINIRead::GetStringLength(RawCompileType eRawType)
895 {
896 	// Excpect valid position
897 	if (!pPos)
898 		{ notFound("String"); return 0; }
899 	// Skip whitespace
900 	SkipWhitespace();
901 	// Save position
902 	const char *pStart = pPos;
903 	// Escaped? Go over '"'
904 	if (eRawType == RCT_Escaped && *pPos++ != '"')
905 		{ notFound("Escaped string"); return 0; }
906 	// Search end of string
907 	size_t iLength = 0;
908 	while (!TestStringEnd(eRawType))
909 	{
910 		// Read a character (we're just counting atm)
911 		if (eRawType == RCT_Escaped)
912 			ReadEscapedChar();
913 		else
914 			pPos++;
915 		// Count it
916 		iLength++;
917 	}
918 	// Reset position, return the length
919 	pPos = pStart;
920 	return iLength;
921 }
922 
ReadString(size_t iLength,RawCompileType eRawType,bool fAppendNull)923 StdBuf StdCompilerINIRead::ReadString(size_t iLength, RawCompileType eRawType, bool fAppendNull)
924 {
925 	// Excpect valid position
926 	if (!pPos)
927 		{ notFound("String"); return StdBuf(); }
928 	// Skip whitespace
929 	SkipWhitespace();
930 	// Escaped? Go over '"'
931 	if (eRawType == RCT_Escaped && *pPos++ != '"')
932 		{ notFound("Escaped string"); return StdBuf(); }
933 	// Create buffer
934 	StdBuf OutBuf; OutBuf.New(iLength + (fAppendNull ? sizeof('\0') : 0));
935 	// Read
936 	char *pOut = getMBufPtr<char>(OutBuf);
937 	while (iLength && !TestStringEnd(eRawType))
938 	{
939 		// Read a character
940 		if (eRawType == RCT_Escaped)
941 			*pOut++ = ReadEscapedChar();
942 		else
943 			*pOut++ = *pPos++;
944 		// Count it
945 		iLength--;
946 	}
947 	// Escaped: Go over '"'
948 	if (eRawType == RCT_Escaped)
949 	{
950 		while (*pPos != '"')
951 		{
952 			if (!*pPos || *pPos == '\n' || *pPos == '\r')
953 			{
954 				Warn("string not terminated!");
955 				pPos--;
956 				break;
957 			}
958 			pPos++;
959 		}
960 		pPos++;
961 	}
962 	// Nothing read? Identifiers need to be non-empty
963 	if (pOut == OutBuf.getData() && (eRawType == RCT_Idtf || eRawType == RCT_ID))
964 		{ notFound("String"); return StdBuf(); }
965 	// Append null
966 	if (fAppendNull)
967 		*pOut = '\0';
968 	// Shrink, if less characters were read
969 	OutBuf.Shrink(iLength);
970 	// Done
971 	return OutBuf;
972 }
973 
ReadEscapedChar()974 char StdCompilerINIRead::ReadEscapedChar()
975 {
976 	// Catch some no-noes like \0, \n etc.
977 	if (*pPos >= 0 && iscntrl((unsigned char)*pPos))
978 	{
979 		Warn("Nonprintable character found in string: %02x", static_cast<unsigned char>(*pPos));
980 		return *pPos;
981 	}
982 	// Not escaped? Just return it
983 	if (*pPos != '\\') return *pPos++;
984 	// What type of escape?
985 	switch (*++pPos)
986 	{
987 	case 'a': pPos++; return '\a';
988 	case 'b': pPos++; return '\b';
989 	case 'f': pPos++; return '\f';
990 	case 'n': pPos++; return '\n';
991 	case 'r': pPos++; return '\r';
992 	case 't': pPos++; return '\t';
993 	case 'v': pPos++; return '\v';
994 	case '\'': pPos++; return '\'';
995 	case '"': pPos++; return '"';
996 	case '\\': pPos++; return '\\';
997 	case '?': pPos++; return '?';
998 	case 'x':
999 		// Treat '\x' as 'x' - damn special cases
1000 		if (!isxdigit((unsigned char)*++pPos))
1001 			return 'x';
1002 		else
1003 		{
1004 			// Read everything that looks like it might be hexadecimal - MSVC does it this way, so do not sue me.
1005 			int iCode = 0;
1006 			do
1007 				{ iCode = iCode * 16 + (isdigit((unsigned char)*pPos) ? *pPos - '0' : *pPos - 'a' + 10); pPos++; }
1008 			while (isxdigit((unsigned char)*pPos));
1009 			// Done. Don't bother to check the range (we aren't doing anything mission-critical here, are we?)
1010 			return char(iCode);
1011 		}
1012 	default:
1013 		// Not octal? Let it pass through.
1014 		if (!isdigit((unsigned char)*pPos) || *pPos >= '8')
1015 			return *pPos++;
1016 		else
1017 		{
1018 			// Read it the octal way.
1019 			int iCode = 0;
1020 			do
1021 				{ iCode = iCode * 8 + (*pPos - '0'); pPos++;}
1022 			while (isdigit((unsigned char)*pPos) && *pPos < '8');
1023 			// Done. See above.
1024 			return char(iCode);
1025 		}
1026 	}
1027 	// unreachable
1028 	assert (false);
1029 }
1030 
notFound(const char * szWhat)1031 void StdCompilerINIRead::notFound(const char *szWhat)
1032 {
1033 	excNotFound("%s expected", szWhat);
1034 }
1035 
StdCompilerWarnCallback(void * pData,const char * szPosition,const char * szError)1036 void StdCompilerWarnCallback(void *pData, const char *szPosition, const char *szError)
1037 {
1038 	const char *szName = reinterpret_cast<const char *>(pData);
1039 	if (!szPosition || !*szPosition)
1040 		DebugLogF("WARNING: %s (in %s)", szError, szName);
1041 	else
1042 		DebugLogF("WARNING: %s (in %s, %s)", szError, szPosition, szName);
1043 }
1044