1 // Copyright © 2008-2021 Pioneer Developers. See AUTHORS.txt for details
2 // Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
3 
4 #ifndef _USE_MATH_DEFINES
5 #define _USE_MATH_DEFINES
6 #endif
7 
8 #include "JsonUtils.h"
9 #include "FileSystem.h"
10 #include "base64/base64.hpp"
11 #include "core/GZipFormat.h"
12 #include "utils.h"
13 #include <cmath>
14 
15 extern "C" {
16 #include "miniz/miniz.h"
17 }
18 
19 namespace {
20 	// XXX currently unused, uncomment if/when needed again.
21 	// also has some endianness issues,
22 	/*
23 	uint32_t pack4char(const uint8_t a, const uint8_t b, const uint8_t c, const uint8_t d)
24 	{
25 		return ((a << 24) | (b << 16) | (c << 8) | d);
26 	}
27 
28 	void unpack4char(const uint32_t packed, uint8_t &a, uint8_t &b, uint8_t &c, uint8_t &d)
29 	{
30 		a = ((packed >> 24) & 0xff);
31 		b = ((packed >> 16) & 0xff);
32 		c = ((packed >> 8) & 0xff);
33 		d = (packed & 0xff);
34 	}
35 */
36 
37 	static const vector3f zeroVector3f(0.0f);
38 	static const vector3d zeroVector3d(0.0);
39 	static const Quaternionf identityQuaternionf(1.0f, 0.0f, 0.0f, 0.0f);
40 	static const Quaterniond identityQuaterniond(1.0, 0.0, 0.0, 0.0);
41 } // namespace
42 
43 namespace JsonUtils {
LoadJson(RefCountedPtr<FileSystem::FileData> fd)44 	Json LoadJson(RefCountedPtr<FileSystem::FileData> fd)
45 	{
46 		if (!fd) return Json();
47 
48 		Json out;
49 
50 		try {
51 			out = Json::parse(std::string(fd->GetData(), fd->GetSize()));
52 		} catch (Json::parse_error &e) {
53 			Output("error in JSON file '%s': %s\n", fd->GetInfo().GetPath().c_str(), e.what());
54 			return nullptr;
55 		}
56 
57 		return out;
58 	}
59 
LoadJsonFile(const std::string & filename,FileSystem::FileSource & source)60 	Json LoadJsonFile(const std::string &filename, FileSystem::FileSource &source)
61 	{
62 		return LoadJson(source.ReadFile(filename));
63 	}
64 
LoadJsonDataFile(const std::string & filename,bool with_merge)65 	Json LoadJsonDataFile(const std::string &filename, bool with_merge)
66 	{
67 		Json out = LoadJsonFile(filename, FileSystem::gameDataFiles);
68 		if (out.is_null() || !with_merge) return out;
69 
70 		for (auto info : FileSystem::gameDataFiles.LookupAll(filename + ".patch")) {
71 			Json patch = LoadJson(info.Read());
72 			if (!patch.is_null()) out.merge_patch(patch);
73 		}
74 
75 		return out;
76 	}
77 
LoadJsonSaveFile(const std::string & filename,FileSystem::FileSource & source)78 	Json LoadJsonSaveFile(const std::string &filename, FileSystem::FileSource &source)
79 	{
80 		auto file = source.ReadFile(filename);
81 		if (!file) return nullptr;
82 		const auto file_data = std::string(file->GetData(), file->GetSize());
83 		const unsigned char *dataPtr = reinterpret_cast<const unsigned char *>(&file_data[0]);
84 		try {
85 			std::string plain_data;
86 			if (gzip::IsGZipFormat(dataPtr, file_data.size())) {
87 				plain_data = gzip::DecompressDeflateOrGZip(dataPtr, file_data.size());
88 			} else {
89 				plain_data = file_data;
90 			}
91 
92 			Json rootNode;
93 			try {
94 				// Allow loading files in JSON format as well as CBOR
95 				if (plain_data[0] == '{')
96 					return Json::parse(plain_data);
97 				else
98 					return Json::from_cbor(plain_data);
99 			} catch (Json::parse_error &e) {
100 				Output("error in JSON file '%s': %s\n", file->GetInfo().GetPath().c_str(), e.what());
101 				return nullptr;
102 			}
103 		} catch (gzip::DecompressionFailedException) {
104 			return nullptr;
105 		}
106 	}
107 } // namespace JsonUtils
108 
109 #define USE_STRING_VERSIONS
110 
VectorToJson(Json & jsonObj,const vector3f & vec)111 void VectorToJson(Json &jsonObj, const vector3f &vec)
112 {
113 	PROFILE_SCOPED()
114 #ifdef USE_STRING_VERSIONS
115 	if (vec == zeroVector3f)
116 		return; // don't store zero vector
117 
118 	char str[128];
119 	Vector3fToStr(vec, str, 128);
120 	jsonObj = str; // Add vector array to supplied object.
121 #else
122 	// Create JSON array to contain vector data.
123 	jsonObj = Json::array({ vec.x, vec.y, vec.z });
124 #endif
125 }
126 
VectorToJson(Json & jsonObj,const vector3d & vec)127 void VectorToJson(Json &jsonObj, const vector3d &vec)
128 {
129 	PROFILE_SCOPED()
130 #ifdef USE_STRING_VERSIONS
131 	if (vec == zeroVector3d)
132 		return; // don't store zero vector
133 	char str[128];
134 	Vector3dToStr(vec, str, 128);
135 	jsonObj = str; // Add vector array to supplied object.
136 #else
137 	// Create JSON array to contain vector data.
138 	jsonObj = Json::array({ vec.x, vec.y, vec.z });
139 #endif
140 }
141 
QuaternionToJson(Json & jsonObj,const Quaternionf & quat)142 void QuaternionToJson(Json &jsonObj, const Quaternionf &quat)
143 {
144 	PROFILE_SCOPED()
145 
146 	if (memcmp(&quat, &identityQuaternionf, sizeof(Quaternionf)) == 0)
147 		return;
148 	jsonObj = Json::array({ quat.w, quat.x, quat.y, quat.z });
149 }
150 
QuaternionToJson(Json & jsonObj,const Quaterniond & quat)151 void QuaternionToJson(Json &jsonObj, const Quaterniond &quat)
152 {
153 	PROFILE_SCOPED()
154 	if (memcmp(&quat, &identityQuaterniond, sizeof(Quaterniond)) == 0)
155 		return;
156 
157 	jsonObj = Json::array({ quat.w, quat.x, quat.y, quat.z });
158 }
159 
MatrixToJson(Json & jsonObj,const matrix3x3f & mat)160 void MatrixToJson(Json &jsonObj, const matrix3x3f &mat)
161 {
162 	PROFILE_SCOPED()
163 #ifdef USE_STRING_VERSIONS
164 	if (!memcmp(&matrix3x3fIdentity, &mat, sizeof(matrix3x3f))) return;
165 	char str[512];
166 	Matrix3x3fToStr(mat, str, 512);
167 	jsonObj = str;
168 #else
169 	jsonObj = Json::array({ mat[0], mat[1], mat[2],
170 		mat[3], mat[4], mat[5],
171 		mat[6], mat[7], mat[8] });
172 #endif
173 }
174 
MatrixToJson(Json & jsonObj,const matrix3x3d & mat)175 void MatrixToJson(Json &jsonObj, const matrix3x3d &mat)
176 {
177 	PROFILE_SCOPED()
178 #ifdef USE_STRING_VERSIONS
179 	if (!memcmp(&matrix3x3dIdentity, &mat, sizeof(matrix3x3d))) return;
180 	char str[512];
181 	Matrix3x3dToStr(mat, str, 512);
182 	jsonObj = str;
183 #else
184 	jsonObj = Json::array({ mat[0], mat[1], mat[2],
185 		mat[3], mat[4], mat[5],
186 		mat[6], mat[7], mat[8] });
187 #endif
188 }
189 
MatrixToJson(Json & jsonObj,const matrix4x4f & mat)190 void MatrixToJson(Json &jsonObj, const matrix4x4f &mat)
191 {
192 	PROFILE_SCOPED()
193 #ifdef USE_STRING_VERSIONS
194 	if (!memcmp(&matrix4x4fIdentity, &mat, sizeof(matrix4x4f))) return;
195 	char str[512];
196 	Matrix4x4fToStr(mat, str, 512);
197 	jsonObj = str;
198 #else
199 	jsonObj = Json::array({
200 		mat[0],
201 		mat[1],
202 		mat[2],
203 		mat[3],
204 		mat[4],
205 		mat[5],
206 		mat[6],
207 		mat[7],
208 		mat[8],
209 		mat[9],
210 		mat[10],
211 		mat[11],
212 		mat[12],
213 		mat[13],
214 		mat[14],
215 		mat[15],
216 	});
217 #endif
218 }
219 
MatrixToJson(Json & jsonObj,const matrix4x4d & mat)220 void MatrixToJson(Json &jsonObj, const matrix4x4d &mat)
221 {
222 	PROFILE_SCOPED()
223 #ifdef USE_STRING_VERSIONS
224 	if (!memcmp(&matrix4x4dIdentity, &mat, sizeof(matrix4x4d))) return;
225 	char str[512];
226 	Matrix4x4dToStr(mat, str, 512);
227 	jsonObj = str;
228 #else
229 	jsonObj = Json::array({
230 		mat[0],
231 		mat[1],
232 		mat[2],
233 		mat[3],
234 		mat[4],
235 		mat[5],
236 		mat[6],
237 		mat[7],
238 		mat[8],
239 		mat[9],
240 		mat[10],
241 		mat[11],
242 		mat[12],
243 		mat[13],
244 		mat[14],
245 		mat[15],
246 	});
247 #endif
248 }
249 
ColorToJson(Json & jsonObj,const Color3ub & col)250 void ColorToJson(Json &jsonObj, const Color3ub &col)
251 {
252 	PROFILE_SCOPED()
253 
254 	jsonObj[0] = col.r;
255 	jsonObj[1] = col.g;
256 	jsonObj[2] = col.b;
257 }
258 
ColorToJson(Json & jsonObj,const Color4ub & col)259 void ColorToJson(Json &jsonObj, const Color4ub &col)
260 {
261 	PROFILE_SCOPED()
262 
263 	jsonObj[0] = col.r;
264 	jsonObj[1] = col.g;
265 	jsonObj[2] = col.b;
266 	jsonObj[3] = col.a;
267 }
268 
BinStrToJson(Json & jsonObj,const std::string & binStr)269 void BinStrToJson(Json &jsonObj, const std::string &binStr)
270 {
271 	PROFILE_SCOPED()
272 
273 	// compress in memory, write to open file
274 	size_t outSize = 0;
275 	void *pCompressedData = tdefl_compress_mem_to_heap(binStr.data(), binStr.length(), &outSize, 128);
276 	assert(pCompressedData); // can we fail to compress?
277 	if (pCompressedData) {
278 		// We encode as base64 to avoid generating invalid UTF-8 data, which breaks the JSON standard.
279 		// Prealloc a string for the encoded data.
280 		std::string encodedData = std::string(Base64::EncodedLength(outSize), '\0');
281 		// Use C++11's contiguous std::string implementation to great effect.
282 		if (Base64::Encode(static_cast<const char *>(pCompressedData), outSize, &encodedData[0], encodedData.size())) {
283 			// Store everything in a string.
284 			jsonObj = std::move(encodedData);
285 		}
286 		// release the compressed data
287 		mz_free(pCompressedData);
288 	}
289 }
290 
JsonToVector(vector3f * pVec,const Json & jsonObj)291 void JsonToVector(vector3f *pVec, const Json &jsonObj)
292 {
293 	PROFILE_SCOPED()
294 #ifdef USE_STRING_VERSIONS
295 	if (!jsonObj.is_string()) {
296 		*pVec = vector3f(0.0f);
297 		return;
298 	}
299 	std::string vecStr = jsonObj;
300 	StrToVector3f(vecStr.c_str(), *pVec);
301 #else
302 	pVec->x = jsonObj[0];
303 	pVec->y = jsonObj[1];
304 	pVec->z = jsonObj[2];
305 #endif
306 }
307 
JsonToVector(vector3d * pVec,const Json & jsonObj)308 void JsonToVector(vector3d *pVec, const Json &jsonObj)
309 {
310 	PROFILE_SCOPED()
311 #ifdef USE_STRING_VERSIONS
312 	if (!jsonObj.is_string()) {
313 		*pVec = vector3d(0.0);
314 		return;
315 	}
316 	std::string vecStr = jsonObj;
317 	StrToVector3d(vecStr.c_str(), *pVec);
318 #else
319 	pVec->x = jsonObj[0];
320 	pVec->y = jsonObj[1];
321 	pVec->z = jsonObj[2];
322 #endif
323 }
324 
JsonToQuaternion(Quaternionf * pQuat,const Json & jsonObj)325 void JsonToQuaternion(Quaternionf *pQuat, const Json &jsonObj)
326 {
327 	PROFILE_SCOPED()
328 	if (!jsonObj.is_array()) {
329 		*pQuat = identityQuaternionf;
330 		return;
331 	}
332 
333 	pQuat->w = jsonObj[0];
334 	pQuat->x = jsonObj[1];
335 	pQuat->y = jsonObj[2];
336 	pQuat->z = jsonObj[3];
337 }
338 
JsonToQuaternion(Quaterniond * pQuat,const Json & jsonObj)339 void JsonToQuaternion(Quaterniond *pQuat, const Json &jsonObj)
340 {
341 	PROFILE_SCOPED()
342 	if (!jsonObj.is_array()) {
343 		*pQuat = identityQuaterniond;
344 		return;
345 	}
346 
347 	pQuat->w = jsonObj[0];
348 	pQuat->x = jsonObj[1];
349 	pQuat->y = jsonObj[2];
350 	pQuat->z = jsonObj[3];
351 }
352 
JsonToMatrix(matrix3x3f * pMat,const Json & jsonObj)353 void JsonToMatrix(matrix3x3f *pMat, const Json &jsonObj)
354 {
355 	PROFILE_SCOPED()
356 #ifdef USE_STRING_VERSIONS
357 	if (!jsonObj.is_string()) {
358 		*pMat = matrix3x3fIdentity;
359 		return;
360 	}
361 	std::string matStr = jsonObj;
362 	StrToMatrix3x3f(matStr.c_str(), *pMat);
363 #else
364 	(*pMat)[0] = jsonObj[0];
365 	(*pMat)[1] = jsonObj[1];
366 	(*pMat)[2] = jsonObj[2];
367 	(*pMat)[3] = jsonObj[3];
368 	(*pMat)[4] = jsonObj[4];
369 	(*pMat)[5] = jsonObj[5];
370 	(*pMat)[6] = jsonObj[6];
371 	(*pMat)[7] = jsonObj[7];
372 	(*pMat)[8] = jsonObj[8];
373 #endif
374 }
375 
JsonToMatrix(matrix3x3d * pMat,const Json & jsonObj)376 void JsonToMatrix(matrix3x3d *pMat, const Json &jsonObj)
377 {
378 	PROFILE_SCOPED()
379 #ifdef USE_STRING_VERSIONS
380 	if (!jsonObj.is_string()) {
381 		*pMat = matrix3x3dIdentity;
382 		return;
383 	}
384 	std::string matStr = jsonObj;
385 	StrToMatrix3x3d(matStr.c_str(), *pMat);
386 #else
387 	(*pMat)[0] = jsonObj[0];
388 	(*pMat)[1] = jsonObj[1];
389 	(*pMat)[2] = jsonObj[2];
390 	(*pMat)[3] = jsonObj[3];
391 	(*pMat)[4] = jsonObj[4];
392 	(*pMat)[5] = jsonObj[5];
393 	(*pMat)[6] = jsonObj[6];
394 	(*pMat)[7] = jsonObj[7];
395 	(*pMat)[8] = jsonObj[8];
396 #endif
397 }
398 
JsonToMatrix(matrix4x4f * pMat,const Json & jsonObj)399 void JsonToMatrix(matrix4x4f *pMat, const Json &jsonObj)
400 {
401 	PROFILE_SCOPED()
402 #ifdef USE_STRING_VERSIONS
403 	if (!jsonObj.is_string()) {
404 		*pMat = matrix4x4fIdentity;
405 		return;
406 	}
407 	std::string matStr = jsonObj;
408 	StrToMatrix4x4f(matStr.c_str(), *pMat);
409 #else
410 	(*pMat)[0] = jsonObj[0];
411 	(*pMat)[1] = jsonObj[1];
412 	(*pMat)[2] = jsonObj[2];
413 	(*pMat)[3] = jsonObj[3];
414 	(*pMat)[4] = jsonObj[4];
415 	(*pMat)[5] = jsonObj[5];
416 	(*pMat)[6] = jsonObj[6];
417 	(*pMat)[7] = jsonObj[7];
418 	(*pMat)[8] = jsonObj[8];
419 	(*pMat)[9] = jsonObj[9];
420 	(*pMat)[10] = jsonObj[10];
421 	(*pMat)[11] = jsonObj[11];
422 	(*pMat)[12] = jsonObj[12];
423 	(*pMat)[13] = jsonObj[13];
424 	(*pMat)[14] = jsonObj[14];
425 	(*pMat)[15] = jsonObj[15];
426 #endif
427 }
428 
JsonToMatrix(matrix4x4d * pMat,const Json & jsonObj)429 void JsonToMatrix(matrix4x4d *pMat, const Json &jsonObj)
430 {
431 	PROFILE_SCOPED()
432 #ifdef USE_STRING_VERSIONS
433 	if (!jsonObj.is_string()) {
434 		*pMat = matrix4x4dIdentity;
435 		return;
436 	}
437 	std::string matStr = jsonObj;
438 	StrToMatrix4x4d(matStr.c_str(), *pMat);
439 #else
440 	(*pMat)[0] = jsonObj[0];
441 	(*pMat)[1] = jsonObj[1];
442 	(*pMat)[2] = jsonObj[2];
443 	(*pMat)[3] = jsonObj[3];
444 	(*pMat)[4] = jsonObj[4];
445 	(*pMat)[5] = jsonObj[5];
446 	(*pMat)[6] = jsonObj[6];
447 	(*pMat)[7] = jsonObj[7];
448 	(*pMat)[8] = jsonObj[8];
449 	(*pMat)[9] = jsonObj[9];
450 	(*pMat)[10] = jsonObj[10];
451 	(*pMat)[11] = jsonObj[11];
452 	(*pMat)[12] = jsonObj[12];
453 	(*pMat)[13] = jsonObj[13];
454 	(*pMat)[14] = jsonObj[14];
455 	(*pMat)[15] = jsonObj[15];
456 #endif
457 }
458 
JsonToColor(Color3ub * pCol,const Json & jsonObj)459 void JsonToColor(Color3ub *pCol, const Json &jsonObj)
460 {
461 	PROFILE_SCOPED()
462 
463 	pCol->r = jsonObj[0];
464 	pCol->g = jsonObj[1];
465 	pCol->b = jsonObj[2];
466 }
467 
JsonToColor(Color4ub * pCol,const Json & jsonObj)468 void JsonToColor(Color4ub *pCol, const Json &jsonObj)
469 {
470 	PROFILE_SCOPED()
471 
472 	pCol->r = jsonObj[0];
473 	pCol->g = jsonObj[1];
474 	pCol->b = jsonObj[2];
475 	pCol->a = jsonObj[3];
476 }
477 
JsonToBinStr(const Json & jsonObj)478 std::string JsonToBinStr(const Json &jsonObj)
479 {
480 	PROFILE_SCOPED()
481 
482 	// Decode the base64 string into raw binary data.
483 	std::string binStr;
484 	if (!Base64::Decode(jsonObj, &binStr)) return binStr;
485 
486 	size_t outSize = 0;
487 	void *pDecompressedData = tinfl_decompress_mem_to_heap(binStr.c_str(), binStr.size(), &outSize, 0);
488 	binStr.clear();
489 	assert(pDecompressedData);
490 	if (pDecompressedData) {
491 		binStr = std::string((char *)pDecompressedData, outSize);
492 		mz_free(pDecompressedData);
493 	}
494 
495 	return binStr;
496 }
497 
to_json(Json & obj,const fixed & f)498 void to_json(Json &obj, const fixed &f)
499 {
500 	// produces e.g. "f53/100"
501 	obj = std::string("f") + std::to_string((f.v & ~f.MASK) >> f.FRAC) + "/" + std::to_string(f.v & f.MASK);
502 }
503 
from_json(const Json & obj,fixed & f)504 void from_json(const Json &obj, fixed &f)
505 {
506 	if (obj.is_number_integer())
507 		f = fixed(obj.get<int64_t>(), 1);
508 
509 	else if (obj.is_number_float())
510 		f = fixed::FromDouble(obj.get<double>());
511 
512 	else {
513 		std::string str = obj;
514 		// must have at least f1/1, though can be f1234567/135758548 etc.
515 		if (str.size() < 4 || obj[0] != 'f')
516 			throw Json::type_error::create(320, "cannot pickle string to fixed point number");
517 
518 		char *next_str = const_cast<char *>(str.c_str()) + 1;
519 		int64_t numerator = std::strtol(next_str, &next_str, 10);
520 
521 		// handle cases: f/34, f1356, f14+4
522 		if (numerator == 0 || next_str == nullptr || size_t(next_str - str.c_str()) >= str.size() || *next_str++ != '/')
523 			throw Json::type_error::create(320, "cannot pickle string to fixed point number");
524 
525 		int64_t denominator = std::strtol(next_str, &next_str, 10);
526 		// handle cases f1345/7684gfrty; fixed numbers should not have any garbage data involved
527 		if (next_str != str.c_str() + str.size())
528 			throw Json::type_error::create(320, "cannot pickle string to fixed point number");
529 
530 		f = fixed(numerator, denominator);
531 	}
532 }
533