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