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 
17 #include "C4Include.h"
18 #include "script/C4Value.h"
19 
20 #include "game/C4GameScript.h"
21 #include "object/C4Def.h"
22 #include "object/C4DefList.h"
23 #include "object/C4GameObjects.h"
24 #include "object/C4Object.h"
25 #include "script/C4AulExec.h"
26 #include "script/C4Effect.h"
27 #include "script/C4StringTable.h"
28 #include "script/C4ValueArray.h"
29 
30 const C4Value C4VNull;
31 
GetC4VName(const C4V_Type Type)32 const char* GetC4VName(const C4V_Type Type)
33 {
34 	switch (Type)
35 	{
36 	case C4V_Nil:
37 		return "nil";
38 	case C4V_Int:
39 		return "int";
40 	case C4V_Bool:
41 		return "bool";
42 	case C4V_String:
43 		return "string";
44 	case C4V_Array:
45 		return "array";
46 	case C4V_PropList:
47 		return "proplist";
48 	case C4V_Any:
49 		return "any";
50 	case C4V_Object:
51 		return "object";
52 	case C4V_Def:
53 		return "def";
54 	case C4V_Effect:
55 		return "effect";
56 	case C4V_Function:
57 		return "function";
58 	default:
59 		return "!Fehler!";
60 	}
61 }
62 
C4Value(C4PropListStatic * p)63 C4Value::C4Value(C4PropListStatic * p): C4Value(static_cast<C4PropList *>(p)) {}
C4Value(C4Def * p)64 C4Value::C4Value(C4Def * p): C4Value(static_cast<C4PropList *>(p)) {}
C4Value(C4Object * p)65 C4Value::C4Value(C4Object * p): C4Value(static_cast<C4PropList *>(p)) {}
C4Value(C4Effect * p)66 C4Value::C4Value(C4Effect * p): C4Value(static_cast<C4PropList *>(p)) {}
67 
getObj() const68 C4Object * C4Value::getObj() const
69 {
70 	return CheckConversion(C4V_Object) ? Data.PropList->GetObject() : nullptr;
71 }
72 
_getObj() const73 C4Object * C4Value::_getObj() const
74 {
75 	return Data.PropList ? Data.PropList->GetObject() : nullptr;
76 }
77 
getDef() const78 C4Def * C4Value::getDef() const
79 {
80 	return CheckConversion(C4V_Def) ? Data.PropList->GetDef() : nullptr;
81 }
82 
_getDef() const83 C4Def * C4Value::_getDef() const
84 {
85 	return Data.PropList ? Data.PropList->GetDef() : nullptr;
86 }
87 
C4VObj(C4Object * pObj)88 C4Value C4VObj(C4Object *pObj) { return C4Value(static_cast<C4PropList*>(pObj)); }
89 
FnCnvObject() const90 bool C4Value::FnCnvObject() const
91 {
92 	// try casting
93 	if (Data.PropList->GetObject()) return true;
94 	return false;
95 }
96 
FnCnvDef() const97 bool C4Value::FnCnvDef() const
98 {
99 	// try casting
100 	if (Data.PropList->GetDef()) return true;
101 	return false;
102 }
103 
FnCnvEffect() const104 bool C4Value::FnCnvEffect() const
105 {
106 	// try casting
107 	if (Data.PropList->GetEffect()) return true;
108 	return false;
109 }
110 
WarnAboutConversion(C4V_Type Type,C4V_Type vtToType)111 bool C4Value::WarnAboutConversion(C4V_Type Type, C4V_Type vtToType)
112 {
113 	switch (vtToType)
114 	{
115 	case C4V_Nil:      return Type != C4V_Nil && Type != C4V_Any;
116 	case C4V_Int:      return Type != C4V_Int && Type != C4V_Nil && Type != C4V_Bool && Type != C4V_Any;
117 	case C4V_Bool:     return false;
118 	case C4V_PropList: return Type != C4V_PropList && Type != C4V_Effect && Type != C4V_Def && Type != C4V_Object && Type != C4V_Nil && Type != C4V_Any;
119 	case C4V_String:   return Type != C4V_String && Type != C4V_Nil && Type != C4V_Any;
120 	case C4V_Array:    return Type != C4V_Array && Type != C4V_Nil && Type != C4V_Any;
121 	case C4V_Function: return Type != C4V_Function && Type != C4V_Nil && Type != C4V_Any;
122 	case C4V_Any:      return false;
123 	case C4V_Def:      return Type != C4V_Def && Type != C4V_Object && Type != C4V_PropList && Type != C4V_Nil && Type != C4V_Any;
124 	case C4V_Object:   return Type != C4V_Object && Type != C4V_PropList && Type != C4V_Nil && Type != C4V_Any;
125 	case C4V_Effect:   return Type != C4V_Effect && Type != C4V_PropList && Type != C4V_Nil && Type != C4V_Any;
126 	default: assert(!"C4Value::ConvertTo: impossible conversion target"); return false;
127 	}
128 }
129 
130 // Humanreadable debug output
GetDataString(int depth,const C4PropListStatic * ignore_reference_parent) const131 StdStrBuf C4Value::GetDataString(int depth, const C4PropListStatic *ignore_reference_parent) const
132 {
133 	// ouput by type info
134 	switch (GetType())
135 	{
136 	case C4V_Int:
137 		return FormatString("%ld", static_cast<long>(Data.Int));
138 	case C4V_Bool:
139 		return StdStrBuf(Data ? "true" : "false");
140 	case C4V_PropList:
141 	{
142 		if (Data.PropList == ScriptEngine.GetPropList())
143 			return StdStrBuf("Global");
144 		C4Object * Obj = Data.PropList->GetObject();
145 		if (Obj == Data.PropList)
146 			return FormatString("Object(%d)", Obj->Number);
147 		const C4PropListStatic * Def = Data.PropList->IsStatic();
148 		if (Def)
149 			if (!ignore_reference_parent || Def->GetParent() != ignore_reference_parent)
150 				return Def->GetDataString();
151 		C4Effect * fx = Data.PropList->GetEffect();
152 		StdStrBuf DataString;
153 		DataString = (fx ? "effect {" : "{");
154 		Data.PropList->AppendDataString(&DataString, ", ", depth, Def && ignore_reference_parent);
155 		DataString.AppendChar('}');
156 		return DataString;
157 	}
158 	case C4V_String:
159 		return (Data.Str && Data.Str->GetCStr()) ? FormatString(R"("%s")", Data.Str->GetCStr()) : StdStrBuf("(nullstring)");
160 	case C4V_Array:
161 	{
162 		if (depth <= 0 && Data.Array->GetSize())
163 		{
164 			return StdStrBuf("[...]");
165 		}
166 		StdStrBuf DataString;
167 		DataString = "[";
168 		for (int32_t i = 0; i < Data.Array->GetSize(); i++)
169 		{
170 			if (i) DataString.Append(", ");
171 			DataString.Append(std::move(Data.Array->GetItem(i).GetDataString(depth - 1)));
172 		}
173 		DataString.AppendChar(']');
174 		return DataString;
175 	}
176 	case C4V_Function:
177 		return Data.Fn->GetFullName();
178 	case C4V_Nil:
179 		return StdStrBuf("nil");
180 	default:
181 		return StdStrBuf("-unknown type- ");
182 	}
183 }
184 
185 // JSON serialization.
186 // Only plain data values can be serialized. Throws a C4JSONSerializationError
187 // when encountering values that cannot be represented in JSON or when the
188 // maximum depth is reached.
ToJSON(int depth,const C4PropListStatic * ignore_reference_parent) const189 StdStrBuf C4Value::ToJSON(int depth, const C4PropListStatic *ignore_reference_parent) const
190 {
191 	// ouput by type info
192 	switch (GetType())
193 	{
194 	case C4V_Int:
195 		return FormatString("%ld", static_cast<long>(Data.Int));
196 	case C4V_Bool:
197 		return StdStrBuf(Data ? "true" : "false");
198 	case C4V_PropList:
199 	{
200 		const C4PropListStatic * Def = Data.PropList->IsStatic();
201 		if (Def)
202 			if (!ignore_reference_parent || Def->GetParent() != ignore_reference_parent)
203 				return Def->ToJSON();
204 		return Data.PropList->ToJSON(depth, Def && ignore_reference_parent);
205 	}
206 	case C4V_String:
207 		if (Data.Str && Data.Str->GetCStr())
208 		{
209 			StdStrBuf str = Data.Str->GetData();
210 			str.EscapeString();
211 			str.Replace("\n", R"(\n)");
212 			return FormatString(R"("%s")", str.getData());
213 		}
214 		else
215 		{
216 			return StdStrBuf("null");
217 		}
218 	case C4V_Array:
219 	{
220 		if (depth <= 0 && Data.Array->GetSize())
221 		{
222 			throw C4JSONSerializationError("maximum depth reached");
223 		}
224 		StdStrBuf DataString;
225 		DataString = "[";
226 		for (int32_t i = 0; i < Data.Array->GetSize(); i++)
227 		{
228 			if (i) DataString.Append(",");
229 			DataString.Append(std::move(Data.Array->GetItem(i).ToJSON(depth - 1)));
230 		}
231 		DataString.AppendChar(']');
232 		return DataString;
233 	}
234 	case C4V_Function:
235 		throw C4JSONSerializationError("cannot serialize function");
236 	case C4V_Nil:
237 		return StdStrBuf("null");
238 	default:
239 		throw C4JSONSerializationError("unknown type");
240 	}
241 }
242 
GetValue(uint32_t n)243 const C4Value & C4ValueNumbers::GetValue(uint32_t n)
244 {
245 	if (n <= LoadedValues.size())
246 		return LoadedValues[n - 1];
247 	LogF("ERROR: Value number %d is missing.", n);
248 	return C4VNull;
249 }
250 
Denumerate(class C4ValueNumbers * numbers)251 void C4Value::Denumerate(class C4ValueNumbers * numbers)
252 {
253 	switch (Type)
254 	{
255 	case C4V_Enum:
256 		Set(numbers->GetValue(Data.Int)); break;
257 	case C4V_Array:
258 		Data.Array->Denumerate(numbers); break;
259 	case C4V_PropList:
260 		// objects and effects are denumerated via the main object list
261 		if (!Data.PropList->IsNumbered() && !Data.PropList->IsStatic())
262 			Data.PropList->Denumerate(numbers);
263 		break;
264 	case C4V_C4ObjectEnum:
265 		{
266 			C4PropList *pObj = C4PropListNumbered::GetByNumber(Data.Int);
267 			if (pObj)
268 				// set
269 				SetPropList(pObj);
270 			else
271 			{
272 				// object: invalid value - set to zero
273 				LogF("ERROR: Object number %d is missing.", int(Data.Int));
274 				Set0();
275 			}
276 		}
277 	default: break;
278 	}
279 }
280 
Denumerate()281 void C4ValueNumbers::Denumerate()
282 {
283 	for (auto & LoadedValue : LoadedValues)
284 		LoadedValue.Denumerate(this);
285 }
286 
GetNumberForValue(C4Value * v)287 uint32_t C4ValueNumbers::GetNumberForValue(C4Value * v)
288 {
289 	// This is only used for C4Values containing pointers
290 	// Assume that all pointers have the same size
291 	if (ValueNumbers.find(v->GetData()) == ValueNumbers.end())
292 	{
293 		ValuesToSave.push_back(v);
294 		ValueNumbers[v->GetData()] = ValuesToSave.size();
295 		return ValuesToSave.size();
296 	}
297 	return ValueNumbers[v->GetData()];
298 }
299 
CompileFunc(StdCompiler * pComp,C4ValueNumbers * numbers)300 void C4Value::CompileFunc(StdCompiler *pComp, C4ValueNumbers * numbers)
301 {
302 	// Type
303 	bool deserializing = pComp->isDeserializer();
304 	char cC4VID;
305 	if (!deserializing)
306 	{
307 		assert(Type != C4V_Nil || !Data);
308 		switch (Type)
309 		{
310 		case C4V_Nil:
311 			cC4VID = 'n'; break;
312 		case C4V_Int:
313 			cC4VID = 'i'; break;
314 		case C4V_Bool:
315 			cC4VID = 'b'; break;
316 		case C4V_PropList:
317 			if (getPropList()->IsStatic())
318 				cC4VID = 'D';
319 			else if (getPropList()->IsNumbered())
320 				cC4VID = 'O';
321 			else
322 				cC4VID = 'E';
323 			break;
324 		case C4V_Array:
325 			cC4VID = 'E'; break;
326 		case C4V_Function:
327 			cC4VID = 'D'; break;
328 		case C4V_String:
329 			cC4VID = 's'; break;
330 		default:
331 			assert(false);
332 		}
333 	}
334 	pComp->Character(cC4VID);
335 	// Data
336 	int32_t iTmp;
337 	switch (cC4VID)
338 	{
339 	case 'i':
340 		iTmp = Data.Int;
341 		pComp->Value(iTmp);
342 		SetInt(iTmp);
343 		break;
344 
345 	case 'b':
346 		iTmp = Data.Int;
347 		pComp->Value(iTmp);
348 		SetBool(!!iTmp);
349 		break;
350 
351 	case 'E':
352 		if (!deserializing)
353 			iTmp = numbers->GetNumberForValue(this);
354 		pComp->Value(iTmp);
355 		if (deserializing)
356 		{
357 			Data.Int = iTmp; // must be denumerated later
358 			Type = C4V_Enum;
359 		}
360 		break;
361 
362 	case 'O':
363 		if (!deserializing)
364 			iTmp = getPropList()->GetPropListNumbered()->Number;
365 		pComp->Value(iTmp);
366 		if (deserializing)
367 		{
368 			Data.Int = iTmp; // must be denumerated later
369 			Type = C4V_C4ObjectEnum;
370 		}
371 		break;
372 
373 	case 'D':
374 	{
375 		if (!pComp->isDeserializer())
376 		{
377 			const C4PropList * p = getPropList();
378 			if (getFunction())
379 			{
380 				p = Data.Fn->Parent;
381 				assert(p);
382 				assert(p->GetFunc(Data.Fn->GetName()) == Data.Fn);
383 				assert(p->IsStatic());
384 			}
385 			p->IsStatic()->RefCompileFunc(pComp, numbers);
386 			if (getFunction())
387 			{
388 				pComp->Separator(StdCompiler::SEP_PART);
389 				StdStrBuf s; s.Ref(Data.Fn->GetName());
390 				pComp->Value(mkParAdapt(s, StdCompiler::RCT_ID));
391 			}
392 		}
393 		else
394 		{
395 			StdStrBuf s;
396 			C4Value temp;
397 			pComp->Value(mkParAdapt(s, StdCompiler::RCT_ID));
398 			if (!::ScriptEngine.GetGlobalConstant(s.getData(), &temp))
399 				pComp->excCorrupt("Cannot find global constant %s", s.getData());
400 			while(pComp->Separator(StdCompiler::SEP_PART))
401 			{
402 				C4PropList * p = temp.getPropList();
403 				if (!p)
404 					pComp->excCorrupt("static proplist %s is not a proplist anymore", s.getData());
405 				pComp->Value(mkParAdapt(s, StdCompiler::RCT_ID));
406 				C4String * c4s = ::Strings.FindString(s.getData());
407 				if (!c4s || !p->GetPropertyByS(c4s, &temp))
408 					pComp->excCorrupt("Cannot find property %s in %s", s.getData(), GetDataString().getData());
409 			}
410 			Set(temp);
411 		}
412 		break;
413 	}
414 
415 	case 's':
416 	{
417 		StdStrBuf s;
418 		if (!deserializing)
419 			s = Data.Str->GetData();
420 		pComp->Value(s);
421 		if (deserializing)
422 			SetString(::Strings.RegString(s));
423 		break;
424 	}
425 
426 	// FIXME: remove these three once Game.txt were re-saved with current version
427 	case 'c':
428 		if (deserializing)
429 			Set(GameScript.ScenPropList);
430 		break;
431 
432 	case 't':
433 		if (deserializing)
434 			Set(GameScript.ScenPrototype);
435 		break;
436 
437 	case 'g':
438 		if (deserializing)
439 			SetPropList(ScriptEngine.GetPropList());
440 		break;
441 
442 	case 'n':
443 	case 'A': // compat with OC 5.1
444 		if (deserializing)
445 			Set0();
446 		// doesn't have a value, so nothing to store
447 		break;
448 
449 	default:
450 		// shouldn't happen
451 		pComp->excCorrupt("unknown C4Value type tag '%c'", cC4VID);
452 		break;
453 	}
454 }
455 
CompileValue(StdCompiler * pComp,C4Value * v)456 void C4ValueNumbers::CompileValue(StdCompiler * pComp, C4Value * v)
457 {
458 	// Type
459 	bool deserializing = pComp->isDeserializer();
460 	char cC4VID;
461 	switch(v->GetType())
462 	{
463 	case C4V_PropList: cC4VID = 'p'; break;
464 	case C4V_Array:    cC4VID = 'a'; break;
465 	default: assert(deserializing); break;
466 	}
467 	pComp->Character(cC4VID);
468 	pComp->Separator(StdCompiler::SEP_START);
469 	switch(cC4VID)
470 	{
471 	case 'p':
472 		{
473 			C4PropList * p = v->_getPropList();
474 			pComp->Value(mkParAdapt(mkPtrAdaptNoNull(p), this));
475 			if (deserializing) v->SetPropList(p);
476 		}
477 		break;
478 	case 'a':
479 		{
480 			C4ValueArray * a = v->_getArray();
481 			pComp->Value(mkParAdapt(mkPtrAdaptNoNull(a), this));
482 			if (deserializing) v->SetArray(a);
483 		}
484 		break;
485 	default:
486 		pComp->excCorrupt("Unexpected character '%c'", cC4VID);
487 		break;
488 	}
489 	pComp->Separator(StdCompiler::SEP_END);
490 }
491 
CompileFunc(StdCompiler * pComp)492 void C4ValueNumbers::CompileFunc(StdCompiler * pComp)
493 {
494 	bool deserializing = pComp->isDeserializer();
495 	bool fNaming = pComp->hasNaming();
496 	if (deserializing)
497 	{
498 		uint32_t iSize;
499 		if (!fNaming) pComp->Value(iSize);
500 		// Read new
501 		do
502 		{
503 			// No entries left to read?
504 			if (!fNaming && !iSize--)
505 				break;
506 			// Read entries
507 			try
508 			{
509 				LoadedValues.emplace_back();
510 				CompileValue(pComp, &LoadedValues.back());
511 			}
512 			catch (StdCompiler::NotFoundException *pEx)
513 			{
514 				// No value found: Stop reading loop
515 				delete pEx;
516 				break;
517 			}
518 		}
519 		while (pComp->Separator(StdCompiler::SEP_SEP));
520 	}
521 	else
522 	{
523 		// Note: the list grows during this loop due to nested data structures.
524 		// Data structures with loops are fine because the beginning of the loop
525 		// will be found in the map and not saved again.
526 		// This may still work with the binary compilers due to double-compiling
527 		if (!fNaming)
528 		{
529 			int32_t iSize = ValuesToSave.size();
530 			pComp->Value(iSize);
531 		}
532 		for(std::list<C4Value *>::iterator i = ValuesToSave.begin(); i != ValuesToSave.end(); ++i)
533 		{
534 			CompileValue(pComp, *i);
535 			if (i != ValuesToSave.end()) pComp->Separator(StdCompiler::SEP_SEP);
536 		}
537 	}
538 }
539 
ComparisonImpl(const C4Value & Value1,const C4Value & Value2)540 inline bool ComparisonImpl(const C4Value &Value1, const C4Value &Value2)
541 {
542 	C4V_Type Type1 = Value1.GetType();
543 	C4V_Data Data1 = Value1.GetData();
544 	C4V_Type Type2 = Value2.GetType();
545 	C4V_Data Data2 = Value2.GetData();
546 	switch (Type1)
547 	{
548 	case C4V_Nil:
549 		assert(!Data1);
550 		return Type1 == Type2;
551 	case C4V_Int:
552 	case C4V_Bool:
553 		return (Type2 == C4V_Int || Type2 == C4V_Bool) &&
554 		       Data1.Int == Data2.Int;
555 	case C4V_PropList:
556 		return Type1 == Type2 && *Data1.PropList == *Data2.PropList;
557 	case C4V_String:
558 		return Type1 == Type2 && Data1.Str == Data2.Str;
559 	case C4V_Array:
560 		return Type1 == Type2 &&
561 		       (Data1.Array == Data2.Array || *(Data1.Array) == *(Data2.Array));
562 	case C4V_Function:
563 		return Type1 == Type2 && Data1.Fn == Data2.Fn;
564 	default:
565 		assert(!"Unexpected C4Value type (denumeration missing?)");
566 		return Data1 == Data2;
567 	}
568 }
569 
operator ==(const C4Value & Value2) const570 bool C4Value::operator == (const C4Value& Value2) const
571 {
572 	// recursion guard using a linked list of Seen structures on the stack
573 	// NOT thread-safe
574 	struct Seen
575 	{
576 		Seen *prev;
577 		const C4Value *left;
578 		const C4Value *right;
579 		inline Seen(Seen *prev, const C4Value *left, const C4Value *right):
580 			prev(prev), left(left), right(right) {}
581 		inline bool operator == (const Seen& other)
582 		{
583 			return left == other.left && right == other.right;
584 		}
585 		inline bool recursion(Seen *new_top)
586 		{
587 			for (Seen *s = this; s; s = s->prev)
588 				if (*s == *new_top)
589 					return true;
590 			return false;
591 		}
592 		inline Seen *first()
593 		{
594 			Seen *s = this;
595 			while (s->prev) s = s->prev;
596 			return s;
597 		}
598 	};
599 	static Seen *top = nullptr;
600 	Seen here(top, this, &Value2);
601 
602 	bool recursion = top && top->recursion(&here);
603 	if (recursion)
604 	{
605 		Seen *first = top->first();
606 		// GetDataString is fine for circular values
607 		LogF("Caught infinite recursion comparing %s and %s",
608 			first->left->GetDataString().getData(),
609 			first->right->GetDataString().getData());
610 		return false;
611 	}
612 	top = &here;
613 	bool result = ComparisonImpl(*this, Value2);
614 	top = here.prev;
615 	return result;
616 }
617 
operator !=(const C4Value & Value2) const618 bool C4Value::operator != (const C4Value& Value2) const
619 {
620 	return !(*this == Value2);
621 }
622 
GetTypeEx() const623 C4V_Type C4Value::GetTypeEx() const
624 {
625 	// Return type including types derived from prop list types (such as C4V_Def)
626 	if (Type == C4V_PropList)
627 	{
628 		if (FnCnvEffect()) return C4V_Effect;
629 		if (FnCnvObject()) return C4V_Object;
630 		if (FnCnvDef()) return C4V_Def;
631 	}
632 	return Type;
633 }
634 
LogDeletedObjectWarning(C4PropList * p)635 void C4Value::LogDeletedObjectWarning(C4PropList * p)
636 {
637 	if (p->GetPropListNumbered())
638 		LogF("Warning: using deleted object (#%d) (%s)!", p->GetPropListNumbered()->Number, p->GetName());
639 	else
640 		LogF("Warning: using deleted proplist %p (%s)!", static_cast<void*>(p), p->GetName());
641 	AulExec.LogCallStack();
642 }
643