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 #ifndef STDCOMPILER_H
17 #define STDCOMPILER_H
18 
19 // Try to avoid casting NotFoundExceptions for trivial cases (MSVC log flood workaround)
20 #if defined(_MSC_VER)
21 #define STDCOMPILER_EXCEPTION_WORKAROUND
22 #endif
23 
24 // Provides an interface of generalized compiling/decompiling
25 // (serialization/deserialization - note that the term "compile" is used for both directions)
26 
27 // The interface is designed to allow both text-type (INI) and binary
28 // compilation. Structures that want to support StdCompiler must provide
29 // a function "void CompileFunc(StdCompiler *)" and therein issue calls
30 // to the data, naming and separation functions as appropriate. If the structure
31 // in question cannot be changed, it is equally valid to define a function
32 // void CompileFunc(StdCompiler *, T *) where T is the type of the structure.
33 
34 // Most details can be hidden inside adaptors (see StdAdaptors.h), so
35 // the structure can re-use common compiling patterns (namings, arrays...).
36 
37 class StdCompiler
38 {
39 
40 public:
41 
42 	StdCompiler() = default;
43 
44 	// *** Overridables (Interface)
45 	virtual ~StdCompiler() = default;
46 
47 	// * Properties
48 
49 	// Needs two passes? Binary compiler uses this for calculating the size.
isDoublePass()50 	virtual bool isDoublePass()                   { return false; }
51 
52 	// Changes the target?
isDeserializer()53 	virtual bool isDeserializer()                 { return false; }
isSerializer()54 	inline  bool isSerializer()                   { return !isDeserializer(); }
55 
56 	// Does the compiler support naming, so values can be omitted without harm to
57 	// the data structure? Is separation implemented?
hasNaming()58 	virtual bool hasNaming()                      { return false; }
59 
60 	// Does the compiler encourage verbosity (like producing more text instead of
61 	// just a numerical value)?
isVerbose()62 	virtual bool isVerbose()                      { return hasNaming(); }
63 
64 	// Is it a registry compiler with special handling for arrays?
isRegistry()65 	virtual bool isRegistry()                     { return false; }
66 
67 	// callback by runtime-write-allowed adaptor used by compilers that may set runtime values only
setRuntimeWritesAllowed(int32_t iChange)68 	virtual void setRuntimeWritesAllowed(int32_t iChange) { }
69 
70 	// * Naming
71 	// Provides extra data for the compiler so he can deal with reordered data.
72 	// Note that sections stack and each section will get compiled only once.
73 	// StartSection won't fail if the naming isn't found while compiling. Name and
74 	// all value compiling functions will fail, though.
75 	// Set the NameEnd parameter to true if you are stopping to parse the structure
76 	// for whatever reason (suppress warning messages).
Name(const char * szName)77 	virtual bool Name(const char *szName)         { return true; }
78 	virtual void NameEnd(bool fBreak = false)     { }
GetNameByIndex(size_t idx)79 	virtual const char *GetNameByIndex(size_t idx) const { return nullptr; }
80 
81 	// Special: A naming that follows to the currently active naming (on the same level).
82 	// Note this will end the current naming, so no additional NameEnd() is needed.
83 	// Only used to maintain backwards compatibility, should not be used in new code.
FollowName(const char * szName)84 	virtual bool FollowName(const char *szName)   { NameEnd(); return Name(szName); }
85 
86 	// Called when a named value omitted because of defaulting (compiler only)
87 	// Returns whether the value has been handled
Default(const char * szName)88 	virtual bool Default(const char *szName)      { return true; }
89 
90 	// Return count of sub-namings. May be unimplemented.
91 	virtual int NameCount(const char *szName = nullptr) { assert(false); return 0; }
92 
93 
94 	// * Separation
95 	// Some data types need separation (note that naming makes this unnecessary).
96 	// Compilers that implement naming must implement separation. Others may just
97 	// always return success.
98 	// If a separator wasn't found, some compilers might react by throwing a
99 	// NotFound exception for all attempts to read a value. This behaviour will
100 	// stop when NoSeparator() is called (which just resets this state) or
101 	// Separator() is called successfully. This behaviour will reset after
102 	// ending the naming, too.
103 	enum Sep
104 	{
105 		SEP_NONE=0, // No separator ("")
106 		SEP_SEP, // Array separation (",")
107 		SEP_SEP2, // Array separation 2 (";")
108 		SEP_SET, // Map pair separation ("=")
109 		SEP_PART, // Value part separation (".")
110 		SEP_PART2, // Value part separation 2 (":")
111 		SEP_PLUS, // Value separation with a '+' char ("+")
112 		SEP_START, // Start some sort of list ('(')
113 		SEP_END, // End some sort of list ('(')
114 		SEP_START2, // Start some sort of list ('[')
115 		SEP_END2, // End some sort of list (']')
116 		SEP_VLINE, // Vertical line separator ('|')
117 		SEP_DOLLAR // Dollar sign ('$')
118 	};
119 	virtual bool Separator(Sep eSep = SEP_SEP)    { return true; }
NoSeparator()120 	virtual void NoSeparator()                    { }
121 
122 	// * Data
123 	// Compiling functions for different data types
124 	virtual void DWord(int32_t &rInt)             = 0; // Needs separator!
125 	virtual void DWord(uint32_t &rInt)            = 0; // Needs separator!
126 	virtual void Word(int16_t &rShort)            = 0; // Needs separator!
127 	virtual void Word(uint16_t &rShort)           = 0; // Needs separator!
128 	virtual void Byte(int8_t &rByte)              = 0; // Needs separator!
129 	virtual void Byte(uint8_t &rByte)             = 0; // Needs separator!
130 	virtual void Boolean(bool &rBool)             = 0;
131 	virtual void Character(char &rChar)           = 0; // Alphanumerical only!
132 
133 
134 	// Compile raw data (strings)
135 	enum RawCompileType
136 	{
137 		RCT_Escaped=0,// Any data allowed, no separator needed (default)
138 		RCT_All,      // Printable characters only, must be last element in naming.
139 		RCT_Idtf,     // Alphanumerical characters or '_', separator needed.
140 		RCT_IdtfAllowEmpty, // Like RCT_Idtf, but empty strings are also allowed
141 		RCT_ID        // Like RCT_Idtf (only used for special compilers that treat IDs differently)
142 	};
143 	// Note that string won't allow '\0' inside the buffer, even with escaped compiling!
144 	virtual void String(char *szString, size_t iMaxLength, RawCompileType eType = RCT_Escaped) = 0;
145 	virtual void String(char **pszString, RawCompileType eType = RCT_Escaped) = 0;
146 	virtual void String(std::string &str, RawCompileType type = RCT_Escaped) = 0;
147 	virtual void Raw(void *pData, size_t iSize, RawCompileType eType = RCT_Escaped) = 0;
148 
149 	// * Position
150 	// May return information about the current position of compilation (used for errors and warnings)
getPosition()151 	virtual StdStrBuf getPosition()         const { return StdStrBuf(); }
152 
153 	// * Passes
Begin()154 	virtual void Begin()                          { }
BeginSecond()155 	virtual void BeginSecond()                    { }
End()156 	virtual void End()                            { }
157 
158 	// *** Composed
159 
160 	// Generic compiler function (plus specializations)
Value(const T & rStruct)161 	template <class T> void Value(const T &rStruct)   { rStruct.CompileFunc(this); }
Value(T & rStruct)162 	template <class T> void Value(T &rStruct)     { CompileFunc(rStruct, this); }
163 
Value(int32_t & rInt)164 	void Value(int32_t &rInt)  { DWord(rInt); }
Value(uint32_t & rInt)165 	void Value(uint32_t &rInt) { DWord(rInt); }
Value(int16_t & rInt)166 	void Value(int16_t &rInt)  { Word(rInt); }
Value(uint16_t & rInt)167 	void Value(uint16_t &rInt) { Word(rInt); }
Value(int8_t & rInt)168 	void Value(int8_t &rInt)   { Byte(rInt); }
Value(uint8_t & rInt)169 	void Value(uint8_t &rInt)  { Byte(rInt); }
Value(bool & rBool)170 	void Value(bool &rBool)    { Boolean(rBool); }
171 
172 	// Compiling/Decompiling (may throw a data format exception!)
Compile(T && rStruct)173 	template <class T> inline void Compile(T &&rStruct)
174 	{
175 		assert(isDeserializer());
176 		DoCompilation(rStruct);
177 	}
Decompile(const T & rStruct)178 	template <class T> inline void Decompile(const T &rStruct)
179 	{
180 		assert(!isDeserializer());
181 		DoCompilation(const_cast<T &>(rStruct));
182 	}
183 
184 protected:
185 
186 	// Compilation process
187 	template <class T>
DoCompilation(T & rStruct)188 	inline void DoCompilation(T &rStruct)
189 	{
190 		// Start compilation, do first pass
191 		Begin();
192 		Value(rStruct);
193 		// Second pass needed?
194 		if (isDoublePass())
195 		{
196 			BeginSecond();
197 			Value(rStruct);
198 		}
199 		// Finish
200 		End();
201 	}
202 
203 public:
204 
205 	// Compiler exception - thrown when something is wrong with the data to compile
206 	struct Exception
207 	{
208 		StdStrBuf Pos;
209 		StdStrBuf Msg;
210 protected:
ExceptionException211 		Exception(StdStrBuf Pos, StdStrBuf Msg) : Pos(std::move(Pos)), Msg(std::move(Msg)) { }
212 private:
213 		// do not copy
ExceptionException214 		Exception(const Exception &Exc) { }
215 	};
216 	class NotFoundException : public Exception
217 	{
218 		friend class StdCompiler;
NotFoundException(StdStrBuf Pos,StdStrBuf Msg)219 		NotFoundException(StdStrBuf Pos, StdStrBuf Msg) : Exception(Pos, Msg) { }
220 	};
221 	class EOFException : public Exception
222 	{
223 		friend class StdCompiler;
EOFException(StdStrBuf Pos,StdStrBuf Msg)224 		EOFException(StdStrBuf Pos, StdStrBuf Msg) : Exception(Pos, Msg)  { }
225 	};
226 	class CorruptException : public Exception
227 	{
228 		friend class StdCompiler;
CorruptException(StdStrBuf Pos,StdStrBuf Msg)229 		CorruptException(StdStrBuf Pos, StdStrBuf Msg) : Exception(Pos, Msg) { }
230 	};
231 
232 	// Throw helpers (might redirect)
excNotFound(const char * szMessage,...)233 	void excNotFound(const char *szMessage, ...)
234 	{
235 #ifdef STDCOMPILER_EXCEPTION_WORKAROUND
236 		// Exception workaround: Just set a flag in failesafe mode.
237 		if (fFailSafe) { fFail = true; return; }
238 #endif
239 		// Throw the appropriate exception
240 		va_list args; va_start(args, szMessage);
241 		throw new NotFoundException(getPosition(), FormatStringV(szMessage, args));
242 	}
243 	void excEOF(const char *szMessage = "EOF", ...)
244 	{
245 		// Throw the appropriate exception
246 		va_list args; va_start(args, szMessage);
247 		throw new EOFException(getPosition(), FormatStringV(szMessage, args));
248 	}
excCorrupt(const char * szMessage,...)249 	void excCorrupt(const char *szMessage, ...)
250 	{
251 		// Throw the appropriate exception
252 		va_list args; va_start(args, szMessage);
253 		throw new CorruptException(getPosition(), FormatStringV(szMessage, args));
254 	}
255 
256 protected:
257 
258 	// Exception workaround
259 #ifdef STDCOMPILER_EXCEPTION_WORKAROUND
260 	bool fFailSafe{false}, fFail{false};
261 
beginFailSafe()262 	void beginFailSafe() { fFailSafe = true; fFail = false; }
endFailSafe()263 	bool endFailSafe() { fFailSafe = false; return !fFail; }
264 
265 public:
ValueSafe(const T & rStruct)266 	template <class T> bool ValueSafe(const T &rStruct) { rStruct.CompileFunc(this); return true; }
ValueSafe(T & rStruct)267 	template <class T> bool ValueSafe(T &rStruct)       { CompileFunc(rStruct, this); return true; }
268 
ValueSafe(int32_t & rInt)269 	bool ValueSafe(int32_t &rInt)  { beginFailSafe(); DWord(rInt);    return endFailSafe(); }
ValueSafe(uint32_t & rInt)270 	bool ValueSafe(uint32_t &rInt) { beginFailSafe(); DWord(rInt);    return endFailSafe(); }
ValueSafe(int16_t & rInt)271 	bool ValueSafe(int16_t &rInt)  { beginFailSafe(); Word(rInt);     return endFailSafe(); }
ValueSafe(uint16_t & rInt)272 	bool ValueSafe(uint16_t &rInt) { beginFailSafe(); Word(rInt);     return endFailSafe(); }
ValueSafe(int8_t & rInt)273 	bool ValueSafe(int8_t &rInt)   { beginFailSafe(); Byte(rInt);     return endFailSafe(); }
ValueSafe(uint8_t & rInt)274 	bool ValueSafe(uint8_t &rInt)  { beginFailSafe(); Byte(rInt);     return endFailSafe(); }
ValueSafe(bool & rBool)275 	bool ValueSafe(bool &rBool)    { beginFailSafe(); Boolean(rBool); return endFailSafe(); }
276 #endif
277 
278 public:
279 
280 	// * Warnings
281 	typedef void (*WarnCBT)(void *, const char *, const char *);
setWarnCallback(WarnCBT pnWarnCB,void * pData)282 	void setWarnCallback(WarnCBT pnWarnCB, void *pData) { pWarnCB = pnWarnCB; pWarnData = pData; }
283 	void Warn(const char *szWarning, ...);
284 
285 private:
286 
287 	// Warnings
288 	WarnCBT pWarnCB{nullptr};
289 	void *pWarnData{nullptr};
290 
291 protected:
292 
293 	// Standard separator character
294 	static char SeparatorToChar(Sep eSep);
295 	// String end test depending on encoding type
296 	static bool IsStringEnd(char c, RawCompileType eType);
297 };
298 
299 // Standard compile funcs
300 template <class T>
CompileFunc(T & rStruct,StdCompiler * pComp)301 inline void CompileFunc(T &rStruct, StdCompiler *pComp)
302 {
303 	// If the compiler doesn't like this line, you tried to compile
304 	// something the compiler doesn't know how to handle.
305 	// Possible reasons:
306 	// a) You are compiling a class/structure without a CompileFunc
307 	//    (you may add a specialization of this function, too)
308 	// b) You are trying to compile a pointer. Use a PtrAdapt instead.
309 	// c) You are trying to compile a simple value that has no
310 	//    fixed representation (float, int). Use safe types instead.
311 	rStruct.CompileFunc(pComp);
312 }
313 
CompileFunc(std::string & s,StdCompiler * comp)314 inline void CompileFunc(std::string &s, StdCompiler *comp)
315 {
316 	comp->String(s);
317 }
318 
319 template <class T>
CompileNewFunc(T * & pStruct,StdCompiler * pComp)320 void CompileNewFunc(T *&pStruct, StdCompiler *pComp)
321 {
322 	// Create new object.
323 	// If this line doesn't compile, you either have to
324 	// a) Define a standard constructor for T
325 	// b) Specialize this function to do whatever the correct
326 	//    behaviour is to construct the object from compiler data
327 	std::unique_ptr<T> temp(new T); // exception-safety
328 	// Compile
329 	pComp->Value(*temp);
330 	pStruct = temp.release();
331 }
332 
333 template <class T, typename ... P>
CompileNewFunc(T * & pStruct,StdCompiler * pComp,P &&...pars)334 void CompileNewFunc(T *&pStruct, StdCompiler *pComp, P && ... pars)
335 {
336 	// Create new object.
337 	// If this line doesn't compile, you either have to
338 	// a) Define a standard constructor for T
339 	// b) Specialize this function to do whatever the correct
340 	//    behaviour is to construct the object from compiler data
341 	std::unique_ptr<T> temp(new T); // exception-safety
342 	// Compile
343 	pComp->Value(mkParAdapt(*temp, std::forward<P>(pars)...));
344 	pStruct = temp.release();
345 }
346 
347 template <class T, class ContextT>
CompileNewFuncCtx(T * & pStruct,StdCompiler * pComp,const ContextT & rCtx)348 void CompileNewFuncCtx(T *&pStruct, StdCompiler *pComp, const ContextT& rCtx)
349 {
350 	// Create new object.
351 	// If this line doesn't compile, you either have to
352 	// a) Define an appropriate constructor for T
353 	// b) Specialize this function to do whatever the correct
354 	//    behaviour is to construct the object from compiler data
355 	//    and context
356 	std::unique_ptr<T> temp(new T(rCtx)); // exception-safety
357 	// Compile
358 	pComp->Value(*temp);
359 	pStruct = temp.release();
360 }
361 
362 template <class T, class ContextT, class P>
CompileNewFuncCtx(T * & pStruct,StdCompiler * pComp,const ContextT & rCtx,const P & rPar)363 void CompileNewFuncCtx(T *&pStruct, StdCompiler *pComp, const ContextT& rCtx, const P& rPar)
364 {
365 	// Create new object.
366 	// If this line doesn't compile, you either have to
367 	// a) Define an appropriate constructor for T
368 	// b) Specialize this function to do whatever the correct
369 	//    behaviour is to construct the object from compiler data
370 	//    and context
371 	std::unique_ptr<T> temp(new T(rCtx));  // exception-safety
372 	// Compile
373 	pComp->Value(mkParAdapt(*temp, rPar));
374 	pStruct = temp.release();
375 }
376 
377 // Helpers for buffer-based compiling (may throw a data format exception!)
378 template <class CompT, class StructT>
CompileFromBuf(StructT && TargetStruct,const typename CompT::InT & SrcBuf)379 void CompileFromBuf(StructT &&TargetStruct, const typename CompT::InT &SrcBuf)
380 {
381 	CompT Compiler;
382 	Compiler.setInput(SrcBuf.getRef());
383 	Compiler.Compile(TargetStruct);
384 }
385 template <class CompT, class StructT>
CompileFromBufToNew(const typename CompT::InT & SrcBuf)386 StructT * CompileFromBufToNew(const typename CompT::InT &SrcBuf)
387 {
388 	StructT *pStruct = nullptr;
389 	CompileFromBuf<CompT>(mkPtrAdaptNoNull(pStruct), SrcBuf);
390 	return pStruct;
391 }
392 template <class CompT, class StructT>
CompileFromBufToNewNamed(const typename CompT::InT & SrcBuf,const char * szName)393 StructT * CompileFromBufToNewNamed(const typename CompT::InT &SrcBuf, const char *szName)
394 {
395 	StructT *pStruct = nullptr;
396 	CompileFromBuf<CompT>(mkNamingAdapt(mkPtrAdaptNoNull(pStruct), szName), SrcBuf);
397 	return pStruct;
398 }
399 template <class CompT, class StructT>
DecompileToBuf(const StructT & SrcStruct)400 typename CompT::OutT DecompileToBuf(const StructT &SrcStruct)
401 {
402 	CompT Compiler;
403 	Compiler.Decompile(SrcStruct);
404 	return Compiler.getOutput();
405 }
406 
407 // *** Null compiler
408 
409 // Naming supported, nothing is returned. Used for setting default values.
410 
411 class StdCompilerNull : public StdCompiler
412 {
413 public:
414 
415 	// Properties
isDeserializer()416 	bool isDeserializer() override { return true; }
hasNaming()417 	bool hasNaming() override { return true; }
418 
419 	// Naming
Name(const char * szName)420 	bool Name(const char *szName) override { return false; }
421 	int NameCount(const char *szName = nullptr) override { return 0; }
422 
423 	// Data readers
DWord(int32_t & rInt)424 	void DWord(int32_t &rInt) override { }
DWord(uint32_t & rInt)425 	void DWord(uint32_t &rInt) override { }
Word(int16_t & rShort)426 	void Word(int16_t &rShort) override { }
Word(uint16_t & rShort)427 	void Word(uint16_t &rShort) override { }
Byte(int8_t & rByte)428 	void Byte(int8_t &rByte) override { }
Byte(uint8_t & rByte)429 	void Byte(uint8_t &rByte) override { }
Boolean(bool & rBool)430 	void Boolean(bool &rBool) override { }
Character(char & rChar)431 	void Character(char &rChar) override { }
432 	void String(char *szString, size_t iMaxLength, RawCompileType eType = RCT_Escaped) override { }
433 	void String(char **pszString, RawCompileType eType = RCT_Escaped) override { }
434 	void String(std::string &str, RawCompileType eType = RCT_Escaped) override {}
435 	void Raw(void *pData, size_t iSize, RawCompileType eType = RCT_Escaped) override { }
436 };
437 
438 // *** Binary compiler
439 
440 // No naming supported, everything is read/written binary.
441 
442 
443 // binary writer
444 class StdCompilerBinWrite : public StdCompiler
445 {
446 public:
447 
448 	// Result
449 	typedef StdBuf OutT;
getOutput()450 	inline OutT getOutput() { return Buf; }
451 
452 	// Properties
isDoublePass()453 	bool isDoublePass() override { return true; }
454 
455 	// Data writers
456 	void DWord(int32_t &rInt) override;
457 	void DWord(uint32_t &rInt) override;
458 	void Word(int16_t &rShort) override;
459 	void Word(uint16_t &rShort) override;
460 	void Byte(int8_t &rByte) override;
461 	void Byte(uint8_t &rByte) override;
462 	void Boolean(bool &rBool) override;
463 	void Character(char &rChar) override;
464 	void String(char *szString, size_t iMaxLength, RawCompileType eType = RCT_Escaped) override;
465 	void String(char **pszString, RawCompileType eType = RCT_Escaped) override;
466 	void String(std::string &str, RawCompileType eType = RCT_Escaped) override;
467 	void Raw(void *pData, size_t iSize, RawCompileType eType = RCT_Escaped) override;
468 
469 	// Passes
470 	void Begin() override;
471 	void BeginSecond() override;
472 
473 protected:
474 	// Process data
475 	bool fSecondPass;
476 	int iPos;
477 	StdBuf Buf;
478 
479 	// Helpers
480 	template <class T> void WriteValue(const T &rValue);
481 	void WriteData(const void *pData, size_t iSize);
482 };
483 
484 // binary read
485 class StdCompilerBinRead : public StdCompiler
486 {
487 public:
488 
489 	// Input
490 	typedef StdBuf InT;
setInput(InT && In)491 	void setInput(InT &&In) { Buf = std::move(In); }
492 
493 	// Properties
isDeserializer()494 	bool isDeserializer() override { return true; }
495 
496 	// Data readers
497 	void DWord(int32_t &rInt) override;
498 	void DWord(uint32_t &rInt) override;
499 	void Word(int16_t &rShort) override;
500 	void Word(uint16_t &rShort) override;
501 	void Byte(int8_t &rByte) override;
502 	void Byte(uint8_t &rByte) override;
503 	void Boolean(bool &rBool) override;
504 	void Character(char &rChar) override;
505 	void String(char *szString, size_t iMaxLength, RawCompileType eType = RCT_Escaped) override;
506 	void String(char **pszString, RawCompileType eType = RCT_Escaped) override;
507 	void String(std::string &str, RawCompileType eType = RCT_Escaped) override;
508 	void Raw(void *pData, size_t iSize, RawCompileType eType = RCT_Escaped) override;
509 
510 	// Position
511 	StdStrBuf getPosition() const override;
512 
513 	// Passes
514 	void Begin() override;
515 
516 	// Data
getPosition()517 	size_t getPosition() { return iPos; }
getRemainingBytes()518 	size_t getRemainingBytes() { return Buf.getSize() - iPos; }
519 
520 protected:
521 	// Process data
522 	size_t iPos;
523 	StdBuf Buf;
524 
525 	// Helper
526 	template <class T> void ReadValue(T &rValue);
527 };
528 
529 // *** INI compiler
530 
531 // Naming and separators supported, so defaulting can be used through
532 // the appropriate adaptors.
533 
534 // Example:
535 
536 // [Sect1]
537 //   [Sect1a]
538 //   Val1=4
539 //   Val2=5
540 // Val4=3,5
541 
542 // will result from:
543 
544 // int v1=4, v2=5, v3=0, v4[3] = { 3, 5, 0 };
545 // DecompileToBuf<StdCompilerINIWrite>(
546 //   mkNamingAdapt(
547 //     mkNamingAdapt(
548 //       mkNamingAdapt(v1, "Val1", 0) +
549 //       mkNamingAdapt(v2, "Val2", 0) +
550 //       mkNamingAdapt(v3, "Val3", 0),
551 //     "Sect1a") +
552 //     mkNamingAdapt(mkArrayAdapt(v4, 3, 0), "Val4", 0),
553 //   "Sect1")
554 // )
555 
556 
557 // text writer
558 class StdCompilerINIWrite : public StdCompiler
559 {
560 public:
561 	// Input
562 	typedef StdStrBuf OutT;
getOutput()563 	inline OutT getOutput() { return Buf; }
564 
565 	// Properties
hasNaming()566 	bool hasNaming() override { return true; }
567 
568 	// Naming
569 	bool Name(const char *szName) override;
570 	void NameEnd(bool fBreak = false) override;
571 
572 	// Separators
573 	bool Separator(Sep eSep) override;
574 
575 	// Data writers
576 	void DWord(int32_t &rInt) override;
577 	void DWord(uint32_t &rInt) override;
578 	void Word(int16_t &rShort) override;
579 	void Word(uint16_t &rShort) override;
580 	void Byte(int8_t &rByte) override;
581 	void Byte(uint8_t &rByte) override;
582 	void Boolean(bool &rBool) override;
583 	void Character(char &rChar) override;
584 	void StringN(const char *szString, size_t iMaxLength, RawCompileType eType = RCT_Escaped);
585 	void String(char *szString, size_t iMaxLength, RawCompileType eType = RCT_Escaped) override;
586 	void String(char **pszString, RawCompileType eType = RCT_Escaped) override;
587 	void String(std::string &str, RawCompileType eType = RCT_Escaped) override;
588 	void Raw(void *pData, size_t iSize, RawCompileType eType = RCT_Escaped) override;
589 
590 	// Passes
591 	void Begin() override;
592 	void End() override;
593 
594 protected:
595 
596 	// Result
597 	StdStrBuf Buf;
598 
599 	// Naming stack
600 	struct Naming
601 	{
602 		StdStrBuf Name;
603 		Naming *Parent;
604 	};
605 	Naming *pNaming;
606 	// Recursion depth
607 	int iDepth;
608 
609 	// Name not put yet (it's not clear wether it is a value or a section)
610 	bool fPutName,
611 	// Currently inside a section, so raw data can't be printed
612 	fInSection;
613 
614 	void PrepareForValue();
615 	void WriteEscaped(const char *szString, const char *pEnd);
616 	void WriteIndent(bool fSectionName);
617 	void PutName(bool fSection);
618 };
619 
620 // text reader
621 class StdCompilerINIRead : public StdCompiler
622 {
623 public:
624 
625 	StdCompilerINIRead();
626 	~StdCompilerINIRead() override;
627 
628 	// Input
629 	typedef StdStrBuf InT;
setInput(const InT & In)630 	void setInput(const InT &In) { Buf.Ref(In); lineBreaks.clear(); }
631 
632 	// Properties
isDeserializer()633 	bool isDeserializer() override { return true; }
hasNaming()634 	bool hasNaming() override { return true; }
635 
636 	// Naming
637 	bool Name(const char *szName) override;
638 	void NameEnd(bool fBreak = false) override;
639 	bool FollowName(const char *szName) override;
640 	const char *GetNameByIndex(size_t idx) const override;
641 
642 	// Separators
643 	bool Separator(Sep eSep) override;
644 	void NoSeparator() override;
645 
646 	// Counters
647 	int NameCount(const char *szName = nullptr) override;
648 
649 	// Data writers
650 	void DWord(int32_t &rInt) override;
651 	void DWord(uint32_t &rInt) override;
652 	void Word(int16_t &rShort) override;
653 	void Word(uint16_t &rShort) override;
654 	void Byte(int8_t &rByte) override;
655 	void Byte(uint8_t &rByte) override;
656 	void Boolean(bool &rBool) override;
657 	void Character(char &rChar) override;
658 	void String(char *szString, size_t iMaxLength, RawCompileType eType = RCT_Escaped) override;
659 	void String(char **pszString, RawCompileType eType = RCT_Escaped) override;
660 	void String(std::string &str, RawCompileType eType = RCT_Escaped) override;
661 	void Raw(void *pData, size_t iSize, RawCompileType eType = RCT_Escaped) override;
662 
663 	// Position
664 	StdStrBuf getPosition() const override;
665 
666 	// Passes
667 	void Begin() override;
668 	void End() override;
669 
670 protected:
671 
672 	// * Data
673 
674 	// Name tree
675 	struct NameNode
676 	{
677 		// Name
678 		StdStrBuf Name;
679 		// Section?
680 		bool Section{false};
681 		// Tree structure
682 		NameNode *Parent,
683 		*FirstChild{nullptr}, *PrevChild{nullptr}, *NextChild{nullptr}, *LastChild{nullptr};
684 		// Indent level
685 		int Indent{-1};
686 		// Name number in parent map
687 		const char *Pos{nullptr};
688 		// Constructor
689 		NameNode(NameNode *pParent = nullptr) :
ParentNameNode690 			Parent(pParent)
691 		{ }
692 	};
693 	NameNode *pNameRoot{nullptr}, *pName;
694 	// Current depth
695 	int iDepth{0};
696 	// Real depth (depth of recursive Name()-calls - if iDepth != iRealDepth, we are in a nonexistant namespace)
697 	int iRealDepth{0};
698 
699 	// Data
700 	StdStrBuf Buf;
701 	// Position
702 	const char *pPos;
703 
704 	// Reenter position (if an nonexistant separator was specified)
705 	const char *pReenter;
706 
707 	// Uppermost name that wasn't found
708 	StdCopyStrBuf NotFoundName;
709 
710 	// * Implementation
711 
712 	// Name tree
713 	void CreateNameTree();
714 	void FreeNameTree();
715 	void FreeNameNode(NameNode *pNode);
716 
717 	// Navigation
718 	void SkipWhitespace();
719 	void SkipNum();
720 	long ReadNum();
721 	size_t GetStringLength(RawCompileType eTyped);
722 	StdBuf ReadString(size_t iLength, RawCompileType eTyped, bool fAppendNull = true);
TestStringEnd(RawCompileType eType)723 	bool TestStringEnd(RawCompileType eType) { return IsStringEnd(*pPos, eType); }
724 	char ReadEscapedChar();
725 	unsigned long ReadUNum();
726 
727 	void notFound(const char *szWhat);
728 
729 private:
730 	uint32_t getLineNumberOfPos(const char *pos) const;
731 	mutable std::vector<const char *> lineBreaks;
732 };
733 
734 void StdCompilerWarnCallback(void *pData, const char *szPosition, const char *szError);
735 
736 template <class CompT, class StructT>
CompileFromBuf_Log(StructT && TargetStruct,const typename CompT::InT & SrcBuf,const char * szName)737 bool CompileFromBuf_Log(StructT &&TargetStruct, const typename CompT::InT &SrcBuf, const char *szName)
738 {
739 	try
740 	{
741 		CompileFromBuf<CompT>(TargetStruct, SrcBuf);
742 		return true;
743 	}
744 	catch (StdCompiler::Exception *pExc)
745 	{
746 		if (!pExc->Pos.getLength())
747 			LogF("ERROR: %s (in %s)", pExc->Msg.getData(), szName);
748 		else
749 			LogF("ERROR: %s (in %s, %s)", pExc->Msg.getData(), pExc->Pos.getData(), szName);
750 		delete pExc;
751 		return false;
752 	}
753 }
754 template <class CompT, class StructT>
CompileFromBuf_LogWarn(StructT && TargetStruct,const typename CompT::InT & SrcBuf,const char * szName)755 bool CompileFromBuf_LogWarn(StructT &&TargetStruct, const typename CompT::InT &SrcBuf, const char *szName)
756 {
757 	try
758 	{
759 		CompT Compiler;
760 		Compiler.setInput(SrcBuf.getRef());
761 		Compiler.setWarnCallback(StdCompilerWarnCallback, reinterpret_cast<void *>(const_cast<char *>(szName)));
762 		Compiler.Compile(TargetStruct);
763 		return true;
764 	}
765 	catch (StdCompiler::Exception *pExc)
766 	{
767 		if (!pExc->Pos.getLength())
768 			LogF("ERROR: %s (in %s)", pExc->Msg.getData(), szName);
769 		else
770 			LogF("ERROR: %s (in %s, %s)", pExc->Msg.getData(), pExc->Pos.getData(), szName);
771 		delete pExc;
772 		return false;
773 	}
774 }
775 template <class CompT, class StructT>
DecompileToBuf_Log(StructT && TargetStruct,typename CompT::OutT * pOut,const char * szName)776 bool DecompileToBuf_Log(StructT &&TargetStruct, typename CompT::OutT *pOut, const char *szName)
777 {
778 	if (!pOut) return false;
779 	try
780 	{
781 		pOut->Take(DecompileToBuf<CompT>(TargetStruct));
782 		return true;
783 	}
784 	catch (StdCompiler::Exception *pExc)
785 	{
786 		LogF("ERROR: %s (in %s)", pExc->Msg.getData(), szName);
787 		delete pExc;
788 		return false;
789 	}
790 }
791 
792 #endif // STDCOMPILER_H
793