1 #include "GameSave.h"
2
3 #include <iostream>
4 #include <cmath>
5 #include <climits>
6 #include <memory>
7 #include <set>
8 #include <bzlib.h>
9 #include <cmath>
10
11 #include "Config.h"
12 #include "Format.h"
13 #include "hmap.h"
14
15 #include "simulation/Simulation.h"
16 #include "simulation/ElementClasses.h"
17
GameSave(GameSave & save)18 GameSave::GameSave(GameSave & save):
19 majorVersion(save.majorVersion),
20 waterEEnabled(save.waterEEnabled),
21 legacyEnable(save.legacyEnable),
22 gravityEnable(save.gravityEnable),
23 aheatEnable(save.aheatEnable),
24 paused(save.paused),
25 gravityMode(save.gravityMode),
26 airMode(save.airMode),
27 edgeMode(save.edgeMode),
28 signs(save.signs),
29 stkm(save.stkm),
30 palette(save.palette),
31 pmapbits(save.pmapbits),
32 expanded(save.expanded),
33 hasOriginalData(save.hasOriginalData),
34 originalData(save.originalData)
35 {
36 InitData();
37 hasPressure = save.hasPressure;
38 hasAmbientHeat = save.hasAmbientHeat;
39 if (save.expanded)
40 {
41 setSize(save.blockWidth, save.blockHeight);
42
43 std::copy(save.particles, save.particles+NPART, particles);
44 for (int j = 0; j < blockHeight; j++)
45 {
46 std::copy(save.blockMap[j], save.blockMap[j]+blockWidth, blockMap[j]);
47 std::copy(save.fanVelX[j], save.fanVelX[j]+blockWidth, fanVelX[j]);
48 std::copy(save.fanVelY[j], save.fanVelY[j]+blockWidth, fanVelY[j]);
49 std::copy(save.pressure[j], save.pressure[j]+blockWidth, pressure[j]);
50 std::copy(save.velocityX[j], save.velocityX[j]+blockWidth, velocityX[j]);
51 std::copy(save.velocityY[j], save.velocityY[j]+blockWidth, velocityY[j]);
52 std::copy(save.ambientHeat[j], save.ambientHeat[j]+blockWidth, ambientHeat[j]);
53 }
54 }
55 else
56 {
57 blockWidth = save.blockWidth;
58 blockHeight = save.blockHeight;
59 }
60 particlesCount = save.particlesCount;
61 authors = save.authors;
62 }
63
GameSave(int width,int height)64 GameSave::GameSave(int width, int height)
65 {
66 InitData();
67 InitVars();
68 hasOriginalData = false;
69 expanded = true;
70 setSize(width, height);
71 }
72
GameSave(std::vector<char> data)73 GameSave::GameSave(std::vector<char> data)
74 {
75 blockWidth = 0;
76 blockHeight = 0;
77
78 InitData();
79 InitVars();
80 expanded = false;
81 hasOriginalData = true;
82 originalData = data;
83 try
84 {
85 Expand();
86 }
87 catch(ParseException & e)
88 {
89 std::cout << e.what() << std::endl;
90 dealloc(); //Free any allocated memory
91 throw;
92 }
93 Collapse();
94 }
95
GameSave(std::vector<unsigned char> data)96 GameSave::GameSave(std::vector<unsigned char> data)
97 {
98 blockWidth = 0;
99 blockHeight = 0;
100
101 InitData();
102 InitVars();
103 expanded = false;
104 hasOriginalData = true;
105 originalData = std::vector<char>(data.begin(), data.end());
106 try
107 {
108 Expand();
109 }
110 catch(ParseException & e)
111 {
112 std::cout << e.what() << std::endl;
113 dealloc(); //Free any allocated memory
114 throw;
115 }
116 Collapse();
117 }
118
GameSave(char * data,int dataSize)119 GameSave::GameSave(char * data, int dataSize)
120 {
121 blockWidth = 0;
122 blockHeight = 0;
123
124 InitData();
125 InitVars();
126 expanded = false;
127 hasOriginalData = true;
128 originalData = std::vector<char>(data, data+dataSize);
129 #ifdef DEBUG
130 std::cout << "Creating Expanded save from data" << std::endl;
131 #endif
132 try
133 {
134 Expand();
135 }
136 catch(ParseException & e)
137 {
138 std::cout << e.what() << std::endl;
139 dealloc(); //Free any allocated memory
140 throw;
141 }
142 //Collapse();
143 }
144
145 // Called on every new GameSave, including the copy constructor
InitData()146 void GameSave::InitData()
147 {
148 blockMap = NULL;
149 fanVelX = NULL;
150 fanVelY = NULL;
151 particles = NULL;
152 pressure = NULL;
153 velocityX = NULL;
154 velocityY = NULL;
155 ambientHeat = NULL;
156 fromNewerVersion = false;
157 hasPressure = false;
158 hasAmbientHeat = false;
159 authors.clear();
160 }
161
162 // Called on every new GameSave, except the copy constructor
InitVars()163 void GameSave::InitVars()
164 {
165 majorVersion = 0;
166 minorVersion = 0;
167 waterEEnabled = false;
168 legacyEnable = false;
169 gravityEnable = false;
170 aheatEnable = false;
171 paused = false;
172 gravityMode = 0;
173 airMode = 0;
174 edgeMode = 0;
175 translated.x = translated.y = 0;
176 pmapbits = 8; // default to 8 bits for older saves
177 }
178
Collapsed()179 bool GameSave::Collapsed()
180 {
181 return !expanded;
182 }
183
Expand()184 void GameSave::Expand()
185 {
186 if(hasOriginalData && !expanded)
187 {
188 InitVars();
189 expanded = true;
190 read(&originalData[0], originalData.size());
191 }
192 }
193
Collapse()194 void GameSave::Collapse()
195 {
196 if(expanded && hasOriginalData)
197 {
198 expanded = false;
199 dealloc();
200 signs.clear();
201 }
202 }
203
read(char * data,int dataSize)204 void GameSave::read(char * data, int dataSize)
205 {
206 if(dataSize > 15)
207 {
208 if ((data[0]==0x66 && data[1]==0x75 && data[2]==0x43) || (data[0]==0x50 && data[1]==0x53 && data[2]==0x76))
209 {
210 readPSv(data, dataSize);
211 }
212 else if(data[0] == 'O' && data[1] == 'P' && data[2] == 'S')
213 {
214 if (data[3] != '1')
215 throw ParseException(ParseException::WrongVersion, "Save format from newer version");
216 readOPS(data, dataSize);
217 }
218 else
219 {
220 std::cerr << "Got Magic number '" << data[0] << data[1] << data[2] << "'" << std::endl;
221 throw ParseException(ParseException::Corrupt, "Invalid save format");
222 }
223 }
224 else
225 {
226 throw ParseException(ParseException::Corrupt, "No data");
227 }
228 }
229
230 template <typename T>
Allocate2DArray(int blockWidth,int blockHeight,T defaultVal)231 T ** GameSave::Allocate2DArray(int blockWidth, int blockHeight, T defaultVal)
232 {
233 T ** temp = new T*[blockHeight];
234 for (int y = 0; y < blockHeight; y++)
235 {
236 temp[y] = new T[blockWidth];
237 std::fill(&temp[y][0], &temp[y][0]+blockWidth, defaultVal);
238 }
239 return temp;
240 }
241
setSize(int newWidth,int newHeight)242 void GameSave::setSize(int newWidth, int newHeight)
243 {
244 this->blockWidth = newWidth;
245 this->blockHeight = newHeight;
246
247 particlesCount = 0;
248 particles = new Particle[NPART];
249
250 blockMap = Allocate2DArray<unsigned char>(blockWidth, blockHeight, 0);
251 fanVelX = Allocate2DArray<float>(blockWidth, blockHeight, 0.0f);
252 fanVelY = Allocate2DArray<float>(blockWidth, blockHeight, 0.0f);
253 pressure = Allocate2DArray<float>(blockWidth, blockHeight, 0.0f);
254 velocityX = Allocate2DArray<float>(blockWidth, blockHeight, 0.0f);
255 velocityY = Allocate2DArray<float>(blockWidth, blockHeight, 0.0f);
256 ambientHeat = Allocate2DArray<float>(blockWidth, blockHeight, 0.0f);
257 }
258
Serialise()259 std::vector<char> GameSave::Serialise()
260 {
261 unsigned int dataSize;
262 char * data = Serialise(dataSize);
263 if (data == NULL)
264 return std::vector<char>();
265 std::vector<char> dataVect(data, data+dataSize);
266 delete[] data;
267 return dataVect;
268 }
269
Serialise(unsigned int & dataSize)270 char * GameSave::Serialise(unsigned int & dataSize)
271 {
272 try
273 {
274 return serialiseOPS(dataSize);
275 }
276 catch (BuildException & e)
277 {
278 std::cout << e.what() << std::endl;
279 return NULL;
280 }
281 }
282
Translate(vector2d translate)283 vector2d GameSave::Translate(vector2d translate)
284 {
285 if (Collapsed())
286 Expand();
287 int nx, ny;
288 vector2d pos;
289 vector2d translateReal = translate;
290 float minx = 0, miny = 0, maxx = 0, maxy = 0;
291 // determine minimum and maximum position of all particles / signs
292 for (size_t i = 0; i < signs.size(); i++)
293 {
294 pos = v2d_new(signs[i].x, signs[i].y);
295 pos = v2d_add(pos,translate);
296 nx = floor(pos.x+0.5f);
297 ny = floor(pos.y+0.5f);
298 if (nx < minx)
299 minx = nx;
300 if (ny < miny)
301 miny = ny;
302 if (nx > maxx)
303 maxx = nx;
304 if (ny > maxy)
305 maxy = ny;
306 }
307 for (int i = 0; i < particlesCount; i++)
308 {
309 if (!particles[i].type) continue;
310 pos = v2d_new(particles[i].x, particles[i].y);
311 pos = v2d_add(pos,translate);
312 nx = floor(pos.x+0.5f);
313 ny = floor(pos.y+0.5f);
314 if (nx < minx)
315 minx = nx;
316 if (ny < miny)
317 miny = ny;
318 if (nx > maxx)
319 maxx = nx;
320 if (ny > maxy)
321 maxy = ny;
322 }
323 // determine whether corrections are needed. If moving in this direction would delete stuff, expand the save
324 vector2d backCorrection = v2d_new(
325 (minx < 0) ? (-floor(minx / CELL)) : 0,
326 (miny < 0) ? (-floor(miny / CELL)) : 0
327 );
328 int blockBoundsX = int(maxx / CELL) + 1, blockBoundsY = int(maxy / CELL) + 1;
329 vector2d frontCorrection = v2d_new(
330 (blockBoundsX > blockWidth) ? (blockBoundsX - blockWidth) : 0,
331 (blockBoundsY > blockHeight) ? (blockBoundsY - blockHeight) : 0
332 );
333
334 // get new width based on corrections
335 int newWidth = (blockWidth + backCorrection.x + frontCorrection.x) * CELL;
336 int newHeight = (blockHeight + backCorrection.y + frontCorrection.y) * CELL;
337 if (newWidth > XRES)
338 frontCorrection.x = backCorrection.x = 0;
339 if (newHeight > YRES)
340 frontCorrection.y = backCorrection.y = 0;
341
342 // call Transform to do the transformation we wanted when calling this function
343 translate = v2d_add(translate, v2d_multiply_float(backCorrection, CELL));
344 Transform(m2d_identity, translate, translateReal,
345 (blockWidth + backCorrection.x + frontCorrection.x) * CELL,
346 (blockHeight + backCorrection.y + frontCorrection.y) * CELL
347 );
348
349 // return how much we corrected. This is used to offset the position of the current stamp
350 // otherwise it would attempt to recenter it with the current size
351 return v2d_add(v2d_multiply_float(backCorrection, -CELL), v2d_multiply_float(frontCorrection, CELL));
352 }
353
Transform(matrix2d transform,vector2d translate)354 void GameSave::Transform(matrix2d transform, vector2d translate)
355 {
356 if (Collapsed())
357 Expand();
358
359 int width = blockWidth*CELL, height = blockHeight*CELL, newWidth, newHeight;
360 vector2d tmp, ctl, cbr;
361 vector2d cornerso[4];
362 vector2d translateReal = translate;
363 // undo any translation caused by rotation
364 cornerso[0] = v2d_new(0,0);
365 cornerso[1] = v2d_new(width-1,0);
366 cornerso[2] = v2d_new(0,height-1);
367 cornerso[3] = v2d_new(width-1,height-1);
368 for (int i = 0; i < 4; i++)
369 {
370 tmp = m2d_multiply_v2d(transform,cornerso[i]);
371 if (i==0) ctl = cbr = tmp; // top left, bottom right corner
372 if (tmp.x<ctl.x) ctl.x = tmp.x;
373 if (tmp.y<ctl.y) ctl.y = tmp.y;
374 if (tmp.x>cbr.x) cbr.x = tmp.x;
375 if (tmp.y>cbr.y) cbr.y = tmp.y;
376 }
377 // casting as int doesn't quite do what we want with negative numbers, so use floor()
378 tmp = v2d_new(floor(ctl.x+0.5f),floor(ctl.y+0.5f));
379 translate = v2d_sub(translate,tmp);
380 newWidth = floor(cbr.x+0.5f)-floor(ctl.x+0.5f)+1;
381 newHeight = floor(cbr.y+0.5f)-floor(ctl.y+0.5f)+1;
382 Transform(transform, translate, translateReal, newWidth, newHeight);
383 }
384
385 // transform is a matrix describing how we want to rotate this save
386 // translate can vary depending on whether the save is bring rotated, or if a normal translate caused it to expand
387 // translateReal is the original amount we tried to translate, used to calculate wall shifting
Transform(matrix2d transform,vector2d translate,vector2d translateReal,int newWidth,int newHeight)388 void GameSave::Transform(matrix2d transform, vector2d translate, vector2d translateReal, int newWidth, int newHeight)
389 {
390 if (Collapsed())
391 Expand();
392
393 if (newWidth>XRES) newWidth = XRES;
394 if (newHeight>YRES) newHeight = YRES;
395
396 int x, y, nx, ny, newBlockWidth = newWidth / CELL, newBlockHeight = newHeight / CELL;
397 vector2d pos, vel;
398
399 unsigned char ** blockMapNew;
400 float **fanVelXNew, **fanVelYNew, **pressureNew, **velocityXNew, **velocityYNew, **ambientHeatNew;
401
402 blockMapNew = Allocate2DArray<unsigned char>(newBlockWidth, newBlockHeight, 0);
403 fanVelXNew = Allocate2DArray<float>(newBlockWidth, newBlockHeight, 0.0f);
404 fanVelYNew = Allocate2DArray<float>(newBlockWidth, newBlockHeight, 0.0f);
405 pressureNew = Allocate2DArray<float>(newBlockWidth, newBlockHeight, 0.0f);
406 velocityXNew = Allocate2DArray<float>(newBlockWidth, newBlockHeight, 0.0f);
407 velocityYNew = Allocate2DArray<float>(newBlockWidth, newBlockHeight, 0.0f);
408 ambientHeatNew = Allocate2DArray<float>(newBlockWidth, newBlockHeight, 0.0f);
409
410 // rotate and translate signs, parts, walls
411 for (size_t i = 0; i < signs.size(); i++)
412 {
413 pos = v2d_new(signs[i].x, signs[i].y);
414 pos = v2d_add(m2d_multiply_v2d(transform,pos),translate);
415 nx = floor(pos.x+0.5f);
416 ny = floor(pos.y+0.5f);
417 if (nx<0 || nx>=newWidth || ny<0 || ny>=newHeight)
418 {
419 signs[i].text[0] = 0;
420 continue;
421 }
422 signs[i].x = nx;
423 signs[i].y = ny;
424 }
425 for (int i = 0; i < particlesCount; i++)
426 {
427 if (!particles[i].type) continue;
428 pos = v2d_new(particles[i].x, particles[i].y);
429 pos = v2d_add(m2d_multiply_v2d(transform,pos),translate);
430 nx = floor(pos.x+0.5f);
431 ny = floor(pos.y+0.5f);
432 if (nx<0 || nx>=newWidth || ny<0 || ny>=newHeight)
433 {
434 particles[i].type = PT_NONE;
435 continue;
436 }
437 particles[i].x = nx;
438 particles[i].y = ny;
439 vel = v2d_new(particles[i].vx, particles[i].vy);
440 vel = m2d_multiply_v2d(transform, vel);
441 particles[i].vx = vel.x;
442 particles[i].vy = vel.y;
443 }
444
445 // translate walls and other grid items when the stamp is shifted more than 4 pixels in any direction
446 int translateX = 0, translateY = 0;
447 if (translateReal.x > 0 && ((int)translated.x%CELL == 3
448 || (translated.x < 0 && (int)translated.x%CELL == 0)))
449 translateX = CELL;
450 else if (translateReal.x < 0 && ((int)translated.x%CELL == -3
451 || (translated.x > 0 && (int)translated.x%CELL == 0)))
452 translateX = -CELL;
453 if (translateReal.y > 0 && ((int)translated.y%CELL == 3
454 || (translated.y < 0 && (int)translated.y%CELL == 0)))
455 translateY = CELL;
456 else if (translateReal.y < 0 && ((int)translated.y%CELL == -3
457 || (translated.y > 0 && (int)translated.y%CELL == 0)))
458 translateY = -CELL;
459
460 for (y=0; y<blockHeight; y++)
461 for (x=0; x<blockWidth; x++)
462 {
463 pos = v2d_new(x*CELL+CELL*0.4f+translateX, y*CELL+CELL*0.4f+translateY);
464 pos = v2d_add(m2d_multiply_v2d(transform,pos),translate);
465 nx = pos.x/CELL;
466 ny = pos.y/CELL;
467 if (pos.x<0 || nx>=newBlockWidth || pos.y<0 || ny>=newBlockHeight)
468 continue;
469 if (blockMap[y][x])
470 {
471 blockMapNew[ny][nx] = blockMap[y][x];
472 if (blockMap[y][x]==WL_FAN)
473 {
474 vel = v2d_new(fanVelX[y][x], fanVelY[y][x]);
475 vel = m2d_multiply_v2d(transform, vel);
476 fanVelXNew[ny][nx] = vel.x;
477 fanVelYNew[ny][nx] = vel.y;
478 }
479 }
480 pressureNew[ny][nx] = pressure[y][x];
481 velocityXNew[ny][nx] = velocityX[y][x];
482 velocityYNew[ny][nx] = velocityY[y][x];
483 ambientHeatNew[ny][nx] = ambientHeat[y][x];
484 }
485 translated = v2d_add(m2d_multiply_v2d(transform, translated), translateReal);
486
487 for (int j = 0; j < blockHeight; j++)
488 {
489 delete[] blockMap[j];
490 delete[] fanVelX[j];
491 delete[] fanVelY[j];
492 delete[] pressure[j];
493 delete[] velocityX[j];
494 delete[] velocityY[j];
495 delete[] ambientHeat[j];
496 }
497
498 blockWidth = newBlockWidth;
499 blockHeight = newBlockHeight;
500
501 delete[] blockMap;
502 delete[] fanVelX;
503 delete[] fanVelY;
504 delete[] pressure;
505 delete[] velocityX;
506 delete[] velocityY;
507 delete[] ambientHeat;
508
509 blockMap = blockMapNew;
510 fanVelX = fanVelXNew;
511 fanVelY = fanVelYNew;
512 pressure = pressureNew;
513 velocityX = velocityXNew;
514 velocityY = velocityYNew;
515 ambientHeat = ambientHeatNew;
516 }
517
CheckBsonFieldUser(bson_iterator iter,const char * field,unsigned char ** data,unsigned int * fieldLen)518 void GameSave::CheckBsonFieldUser(bson_iterator iter, const char *field, unsigned char **data, unsigned int *fieldLen)
519 {
520 if (!strcmp(bson_iterator_key(&iter), field))
521 {
522 if (bson_iterator_type(&iter)==BSON_BINDATA && ((unsigned char)bson_iterator_bin_type(&iter))==BSON_BIN_USER && (*fieldLen = bson_iterator_bin_len(&iter)) > 0)
523 {
524 *data = (unsigned char*)bson_iterator_bin_data(&iter);
525 }
526 else
527 {
528 fprintf(stderr, "Invalid datatype for %s: %d[%d] %d[%d] %d[%d]\n", field, bson_iterator_type(&iter), bson_iterator_type(&iter)==BSON_BINDATA, (unsigned char)bson_iterator_bin_type(&iter), ((unsigned char)bson_iterator_bin_type(&iter))==BSON_BIN_USER, bson_iterator_bin_len(&iter), bson_iterator_bin_len(&iter)>0);
529 }
530 }
531 }
532
CheckBsonFieldBool(bson_iterator iter,const char * field,bool * flag)533 void GameSave::CheckBsonFieldBool(bson_iterator iter, const char *field, bool *flag)
534 {
535 if (!strcmp(bson_iterator_key(&iter), field))
536 {
537 if (bson_iterator_type(&iter) == BSON_BOOL)
538 {
539 *flag = bson_iterator_bool(&iter);
540 }
541 else
542 {
543 fprintf(stderr, "Wrong type for %s\n", bson_iterator_key(&iter));
544 }
545 }
546 }
547
CheckBsonFieldInt(bson_iterator iter,const char * field,int * setting)548 void GameSave::CheckBsonFieldInt(bson_iterator iter, const char *field, int *setting)
549 {
550 if (!strcmp(bson_iterator_key(&iter), field))
551 {
552 if (bson_iterator_type(&iter) == BSON_INT)
553 {
554 *setting = bson_iterator_int(&iter);
555 }
556 else
557 {
558 fprintf(stderr, "Wrong type for %s\n", bson_iterator_key(&iter));
559 }
560 }
561 }
562
readOPS(char * data,int dataLength)563 void GameSave::readOPS(char * data, int dataLength)
564 {
565 unsigned char *inputData = (unsigned char*)data, *bsonData = NULL, *partsData = NULL, *partsPosData = NULL, *fanData = NULL, *wallData = NULL, *soapLinkData = NULL;
566 unsigned char *pressData = NULL, *vxData = NULL, *vyData = NULL, *ambientData = NULL;
567 unsigned int inputDataLen = dataLength, bsonDataLen = 0, partsDataLen, partsPosDataLen, fanDataLen, wallDataLen, soapLinkDataLen;
568 unsigned int pressDataLen, vxDataLen, vyDataLen, ambientDataLen;
569 unsigned partsCount = 0;
570 unsigned int blockX, blockY, blockW, blockH, fullX, fullY, fullW, fullH;
571 int savedVersion = inputData[4];
572 majorVersion = savedVersion;
573 minorVersion = 0;
574 bool fakeNewerVersion = false; // used for development builds only
575
576 bson b;
577 b.data = NULL;
578 bson_iterator iter;
579 auto bson_deleter = [](bson * b) { bson_destroy(b); };
580 // Use unique_ptr with a custom deleter to ensure that bson_destroy is called even when an exception is thrown
581 std::unique_ptr<bson, decltype(bson_deleter)> b_ptr(&b, bson_deleter);
582
583 //Block sizes
584 blockX = 0;
585 blockY = 0;
586 blockW = inputData[6];
587 blockH = inputData[7];
588
589 //Full size, normalised
590 fullX = blockX*CELL;
591 fullY = blockY*CELL;
592 fullW = blockW*CELL;
593 fullH = blockH*CELL;
594
595 //From newer version
596 if (savedVersion > SAVE_VERSION)
597 {
598 fromNewerVersion = true;
599 //throw ParseException(ParseException::WrongVersion, "Save from newer version");
600 }
601
602 //Incompatible cell size
603 if (inputData[5] != CELL)
604 throw ParseException(ParseException::InvalidDimensions, "Incorrect CELL size");
605
606 //Too large/off screen
607 if (blockX+blockW > XRES/CELL || blockY+blockH > YRES/CELL)
608 throw ParseException(ParseException::InvalidDimensions, "Save too large");
609
610 setSize(blockW, blockH);
611
612 bsonDataLen = ((unsigned)inputData[8]);
613 bsonDataLen |= ((unsigned)inputData[9]) << 8;
614 bsonDataLen |= ((unsigned)inputData[10]) << 16;
615 bsonDataLen |= ((unsigned)inputData[11]) << 24;
616
617 //Check for overflows, don't load saves larger than 200MB
618 unsigned int toAlloc = bsonDataLen+1;
619 if (toAlloc > 209715200 || !toAlloc)
620 throw ParseException(ParseException::InvalidDimensions, "Save data too large, refusing");
621
622 bsonData = (unsigned char*)malloc(toAlloc);
623 if (!bsonData)
624 throw ParseException(ParseException::InternalError, "Unable to allocate memory");
625
626 //Make sure bsonData is null terminated, since all string functions need null terminated strings
627 //(bson_iterator_key returns a pointer into bsonData, which is then used with strcmp)
628 bsonData[bsonDataLen] = 0;
629
630 int bz2ret;
631 if ((bz2ret = BZ2_bzBuffToBuffDecompress((char*)bsonData, &bsonDataLen, (char*)(inputData+12), inputDataLen-12, 0, 0)) != BZ_OK)
632 {
633 throw ParseException(ParseException::Corrupt, String::Build("Unable to decompress (ret ", bz2ret, ")"));
634 }
635
636 set_bson_err_handler([](const char* err) { throw ParseException(ParseException::Corrupt, "BSON error when parsing save: " + ByteString(err).FromUtf8()); });
637 bson_init_data_size(&b, (char*)bsonData, bsonDataLen);
638 bson_iterator_init(&iter, &b);
639
640 std::vector<sign> tempSigns;
641
642 while (bson_iterator_next(&iter))
643 {
644 CheckBsonFieldUser(iter, "parts", &partsData, &partsDataLen);
645 CheckBsonFieldUser(iter, "partsPos", &partsPosData, &partsPosDataLen);
646 CheckBsonFieldUser(iter, "wallMap", &wallData, &wallDataLen);
647 CheckBsonFieldUser(iter, "pressMap", &pressData, &pressDataLen);
648 CheckBsonFieldUser(iter, "vxMap", &vxData, &vxDataLen);
649 CheckBsonFieldUser(iter, "vyMap", &vyData, &vyDataLen);
650 CheckBsonFieldUser(iter, "ambientMap", &ambientData, &ambientDataLen);
651 CheckBsonFieldUser(iter, "fanMap", &fanData, &fanDataLen);
652 CheckBsonFieldUser(iter, "soapLinks", &soapLinkData, &soapLinkDataLen);
653 CheckBsonFieldBool(iter, "legacyEnable", &legacyEnable);
654 CheckBsonFieldBool(iter, "gravityEnable", &gravityEnable);
655 CheckBsonFieldBool(iter, "aheat_enable", &aheatEnable);
656 CheckBsonFieldBool(iter, "waterEEnabled", &waterEEnabled);
657 CheckBsonFieldBool(iter, "paused", &paused);
658 CheckBsonFieldInt(iter, "gravityMode", &gravityMode);
659 CheckBsonFieldInt(iter, "airMode", &airMode);
660 CheckBsonFieldInt(iter, "edgeMode", &edgeMode);
661 CheckBsonFieldInt(iter, "pmapbits", &pmapbits);
662 if (!strcmp(bson_iterator_key(&iter), "signs"))
663 {
664 if (bson_iterator_type(&iter)==BSON_ARRAY)
665 {
666 bson_iterator subiter;
667 bson_iterator_subiterator(&iter, &subiter);
668 while (bson_iterator_next(&subiter))
669 {
670 if (!strcmp(bson_iterator_key(&subiter), "sign"))
671 {
672 if (bson_iterator_type(&subiter) == BSON_OBJECT)
673 {
674 bson_iterator signiter;
675 bson_iterator_subiterator(&subiter, &signiter);
676
677 sign tempSign("", 0, 0, sign::Left);
678 while (bson_iterator_next(&signiter))
679 {
680 if (!strcmp(bson_iterator_key(&signiter), "text") && bson_iterator_type(&signiter) == BSON_STRING)
681 {
682 tempSign.text = format::CleanString(ByteString(bson_iterator_string(&signiter)).FromUtf8(), true, true, true).Substr(0, 45);
683 if (majorVersion < 94 || (majorVersion == 94 && minorVersion < 2))
684 {
685 if (tempSign.text == "{t}")
686 {
687 tempSign.text = "Temp: {t}";
688 }
689 else if (tempSign.text == "{p}")
690 {
691 tempSign.text = "Pressure: {p}";
692 }
693 }
694 }
695 else if (!strcmp(bson_iterator_key(&signiter), "justification") && bson_iterator_type(&signiter) == BSON_INT)
696 {
697 tempSign.ju = (sign::Justification)bson_iterator_int(&signiter);
698 }
699 else if (!strcmp(bson_iterator_key(&signiter), "x") && bson_iterator_type(&signiter) == BSON_INT)
700 {
701 tempSign.x = bson_iterator_int(&signiter)+fullX;
702 }
703 else if (!strcmp(bson_iterator_key(&signiter), "y") && bson_iterator_type(&signiter) == BSON_INT)
704 {
705 tempSign.y = bson_iterator_int(&signiter)+fullY;
706 }
707 else
708 {
709 fprintf(stderr, "Unknown sign property %s\n", bson_iterator_key(&signiter));
710 }
711 }
712 tempSigns.push_back(tempSign);
713 }
714 else
715 {
716 fprintf(stderr, "Wrong type for %s\n", bson_iterator_key(&subiter));
717 }
718 }
719 }
720 }
721 else
722 {
723 fprintf(stderr, "Wrong type for %s\n", bson_iterator_key(&iter));
724 }
725 }
726 else if (!strcmp(bson_iterator_key(&iter), "stkm"))
727 {
728 if (bson_iterator_type(&iter) == BSON_OBJECT)
729 {
730 bson_iterator stkmiter;
731 bson_iterator_subiterator(&iter, &stkmiter);
732 while (bson_iterator_next(&stkmiter))
733 {
734 CheckBsonFieldBool(stkmiter, "rocketBoots1", &stkm.rocketBoots1);
735 CheckBsonFieldBool(stkmiter, "rocketBoots2", &stkm.rocketBoots2);
736 CheckBsonFieldBool(stkmiter, "fan1", &stkm.fan1);
737 CheckBsonFieldBool(stkmiter, "fan2", &stkm.fan2);
738 if (!strcmp(bson_iterator_key(&stkmiter), "rocketBootsFigh") && bson_iterator_type(&stkmiter) == BSON_ARRAY)
739 {
740 bson_iterator fighiter;
741 bson_iterator_subiterator(&stkmiter, &fighiter);
742 while (bson_iterator_next(&fighiter))
743 {
744 if (bson_iterator_type(&fighiter) == BSON_INT)
745 stkm.rocketBootsFigh.push_back(bson_iterator_int(&fighiter));
746 }
747 }
748 else if (!strcmp(bson_iterator_key(&stkmiter), "fanFigh") && bson_iterator_type(&stkmiter) == BSON_ARRAY)
749 {
750 bson_iterator fighiter;
751 bson_iterator_subiterator(&stkmiter, &fighiter);
752 while (bson_iterator_next(&fighiter))
753 {
754 if (bson_iterator_type(&fighiter) == BSON_INT)
755 stkm.fanFigh.push_back(bson_iterator_int(&fighiter));
756 }
757 }
758 }
759 }
760 else
761 {
762 fprintf(stderr, "Wrong type for %s\n", bson_iterator_key(&iter));
763 }
764 }
765 else if (!strcmp(bson_iterator_key(&iter), "palette"))
766 {
767 palette.clear();
768 if (bson_iterator_type(&iter) == BSON_ARRAY)
769 {
770 bson_iterator subiter;
771 bson_iterator_subiterator(&iter, &subiter);
772 while (bson_iterator_next(&subiter))
773 {
774 if (bson_iterator_type(&subiter) == BSON_INT)
775 {
776 ByteString id = bson_iterator_key(&subiter);
777 int num = bson_iterator_int(&subiter);
778 palette.push_back(PaletteItem(id, num));
779 }
780 }
781 }
782 }
783 else if (!strcmp(bson_iterator_key(&iter), "origin"))
784 {
785 if (bson_iterator_type(&iter) == BSON_OBJECT)
786 {
787 bson_iterator subiter;
788 bson_iterator_subiterator(&iter, &subiter);
789 while (bson_iterator_next(&subiter))
790 {
791 if (bson_iterator_type(&subiter) == BSON_INT)
792 {
793 if (!strcmp(bson_iterator_key(&subiter), "minorVersion"))
794 {
795 minorVersion = bson_iterator_int(&subiter);
796 }
797 }
798 }
799 }
800 else
801 {
802 fprintf(stderr, "Wrong type for %s\n", bson_iterator_key(&iter));
803 }
804 }
805 else if (!strcmp(bson_iterator_key(&iter), "minimumVersion"))
806 {
807 if (bson_iterator_type(&iter) == BSON_OBJECT)
808 {
809 int major = INT_MAX, minor = INT_MAX;
810 bson_iterator subiter;
811 bson_iterator_subiterator(&iter, &subiter);
812 while (bson_iterator_next(&subiter))
813 {
814 if (bson_iterator_type(&subiter) == BSON_INT)
815 {
816 if (!strcmp(bson_iterator_key(&subiter), "major"))
817 major = bson_iterator_int(&subiter);
818 else if (!strcmp(bson_iterator_key(&subiter), "minor"))
819 minor = bson_iterator_int(&subiter);
820 }
821 }
822 #if defined(SNAPSHOT) || defined(DEBUG)
823 if (major > FUTURE_SAVE_VERSION || (major == FUTURE_SAVE_VERSION && minor > FUTURE_MINOR_VERSION))
824 #else
825 if (major > SAVE_VERSION || (major == SAVE_VERSION && minor > MINOR_VERSION))
826 #endif
827 {
828 String errorMessage = String::Build("Save from a newer version: Requires version ", major, ".", minor);
829 throw ParseException(ParseException::WrongVersion, errorMessage);
830 }
831 #if defined(SNAPSHOT) || defined(DEBUG)
832 else if (major > SAVE_VERSION || (major == SAVE_VERSION && minor > MINOR_VERSION))
833 fakeNewerVersion = true;
834 #endif
835 }
836 else
837 {
838 fprintf(stderr, "Wrong type for %s\n", bson_iterator_key(&iter));
839 }
840 }
841 #ifndef RENDERER
842 else if (!strcmp(bson_iterator_key(&iter), "authors"))
843 {
844 if (bson_iterator_type(&iter) == BSON_OBJECT)
845 {
846 // we need to clear authors because the save may be read multiple times in the stamp browser (loading and rendering twice)
847 // seems inefficient ...
848 authors.clear();
849 ConvertBsonToJson(&iter, &authors);
850 }
851 else
852 {
853 fprintf(stderr, "Wrong type for %s\n", bson_iterator_key(&iter));
854 }
855 }
856 #endif
857 }
858
859 //Read wall and fan data
860 if(wallData)
861 {
862 unsigned int j = 0;
863 if (blockW * blockH > wallDataLen)
864 throw ParseException(ParseException::Corrupt, "Not enough wall data");
865 for (unsigned int x = 0; x < blockW; x++)
866 {
867 for (unsigned int y = 0; y < blockH; y++)
868 {
869 if (wallData[y*blockW+x])
870 blockMap[blockY+y][blockX+x] = wallData[y*blockW+x];
871
872 if (blockMap[y][x]==O_WL_WALLELEC)
873 blockMap[y][x]=WL_WALLELEC;
874 if (blockMap[y][x]==O_WL_EWALL)
875 blockMap[y][x]=WL_EWALL;
876 if (blockMap[y][x]==O_WL_DETECT)
877 blockMap[y][x]=WL_DETECT;
878 if (blockMap[y][x]==O_WL_STREAM)
879 blockMap[y][x]=WL_STREAM;
880 if (blockMap[y][x]==O_WL_FAN||blockMap[y][x]==O_WL_FANHELPER)
881 blockMap[y][x]=WL_FAN;
882 if (blockMap[y][x]==O_WL_ALLOWLIQUID)
883 blockMap[y][x]=WL_ALLOWLIQUID;
884 if (blockMap[y][x]==O_WL_DESTROYALL)
885 blockMap[y][x]=WL_DESTROYALL;
886 if (blockMap[y][x]==O_WL_ERASE)
887 blockMap[y][x]=WL_ERASE;
888 if (blockMap[y][x]==O_WL_WALL)
889 blockMap[y][x]=WL_WALL;
890 if (blockMap[y][x]==O_WL_ALLOWAIR)
891 blockMap[y][x]=WL_ALLOWAIR;
892 if (blockMap[y][x]==O_WL_ALLOWSOLID)
893 blockMap[y][x]=WL_ALLOWPOWDER;
894 if (blockMap[y][x]==O_WL_ALLOWALLELEC)
895 blockMap[y][x]=WL_ALLOWALLELEC;
896 if (blockMap[y][x]==O_WL_EHOLE)
897 blockMap[y][x]=WL_EHOLE;
898 if (blockMap[y][x]==O_WL_ALLOWGAS)
899 blockMap[y][x]=WL_ALLOWGAS;
900 if (blockMap[y][x]==O_WL_GRAV)
901 blockMap[y][x]=WL_GRAV;
902 if (blockMap[y][x]==O_WL_ALLOWENERGY)
903 blockMap[y][x]=WL_ALLOWENERGY;
904
905 if (blockMap[y][x] == WL_FAN && fanData)
906 {
907 if(j+1 >= fanDataLen)
908 {
909 fprintf(stderr, "Not enough fan data\n");
910 }
911 fanVelX[blockY+y][blockX+x] = (fanData[j++]-127.0f)/64.0f;
912 fanVelY[blockY+y][blockX+x] = (fanData[j++]-127.0f)/64.0f;
913 }
914
915 if (blockMap[y][x] >= UI_WALLCOUNT)
916 blockMap[y][x] = 0;
917 }
918 }
919 }
920
921 //Read pressure data
922 if (pressData)
923 {
924 unsigned int j = 0;
925 unsigned char i, i2;
926 if (blockW * blockH > pressDataLen)
927 throw ParseException(ParseException::Corrupt, "Not enough pressure data");
928 for (unsigned int x = 0; x < blockW; x++)
929 {
930 for (unsigned int y = 0; y < blockH; y++)
931 {
932 i = pressData[j++];
933 i2 = pressData[j++];
934 pressure[blockY+y][blockX+x] = ((i+(i2<<8))/128.0f)-256;
935 }
936 }
937 hasPressure = true;
938 }
939
940 //Read vx data
941 if (vxData)
942 {
943 unsigned int j = 0;
944 unsigned char i, i2;
945 if (blockW * blockH > vxDataLen)
946 throw ParseException(ParseException::Corrupt, "Not enough vx data");
947 for (unsigned int x = 0; x < blockW; x++)
948 {
949 for (unsigned int y = 0; y < blockH; y++)
950 {
951 i = vxData[j++];
952 i2 = vxData[j++];
953 velocityX[blockY+y][blockX+x] = ((i+(i2<<8))/128.0f)-256;
954 }
955 }
956 }
957
958 //Read vy data
959 if (vyData)
960 {
961 unsigned int j = 0;
962 unsigned char i, i2;
963 if (blockW * blockH > vyDataLen)
964 throw ParseException(ParseException::Corrupt, "Not enough vy data");
965 for (unsigned int x = 0; x < blockW; x++)
966 {
967 for (unsigned int y = 0; y < blockH; y++)
968 {
969 i = vyData[j++];
970 i2 = vyData[j++];
971 velocityY[blockY+y][blockX+x] = ((i+(i2<<8))/128.0f)-256;
972 }
973 }
974 }
975
976 //Read ambient data
977 if (ambientData)
978 {
979 unsigned int i = 0, tempTemp;
980 if (blockW * blockH > ambientDataLen)
981 throw ParseException(ParseException::Corrupt, "Not enough ambient heat data");
982 for (unsigned int x = 0; x < blockW; x++)
983 {
984 for (unsigned int y = 0; y < blockH; y++)
985 {
986 tempTemp = ambientData[i++];
987 tempTemp |= (((unsigned)ambientData[i++]) << 8);
988 ambientHeat[blockY+y][blockX+x] = tempTemp;
989 }
990 }
991 hasAmbientHeat = true;
992 }
993
994 //Read particle data
995 if (partsData && partsPosData)
996 {
997 int newIndex = 0, fieldDescriptor, tempTemp;
998 int posCount, posTotal, partsPosDataIndex = 0;
999 if (fullW * fullH * 3 > partsPosDataLen)
1000 throw ParseException(ParseException::Corrupt, "Not enough particle position data");
1001
1002 partsCount = 0;
1003
1004 unsigned int i = 0;
1005 unsigned int saved_x, saved_y, x, y;
1006 newIndex = 0;
1007 for (saved_y = 0; saved_y < fullH; saved_y++)
1008 {
1009 for (saved_x = 0; saved_x < fullW; saved_x++)
1010 {
1011 //Read total number of particles at this position
1012 posTotal = 0;
1013 posTotal |= partsPosData[partsPosDataIndex++]<<16;
1014 posTotal |= partsPosData[partsPosDataIndex++]<<8;
1015 posTotal |= partsPosData[partsPosDataIndex++];
1016 //Put the next posTotal particles at this position
1017 for (posCount = 0; posCount < posTotal; posCount++)
1018 {
1019 particlesCount = newIndex+1;
1020 //i+3 because we have 4 bytes of required fields (type (1), descriptor (2), temp (1))
1021 if (i+3 >= partsDataLen)
1022 throw ParseException(ParseException::Corrupt, "Ran past particle data buffer");
1023 x = saved_x + fullX;
1024 y = saved_y + fullY;
1025 fieldDescriptor = partsData[i+1];
1026 fieldDescriptor |= partsData[i+2] << 8;
1027 if (x >= fullW || y >= fullH)
1028 throw ParseException(ParseException::Corrupt, "Particle out of range");
1029
1030 if (newIndex < 0 || newIndex >= NPART)
1031 throw ParseException(ParseException::Corrupt, "Too many particles");
1032
1033 //Clear the particle, ready for our new properties
1034 memset(&(particles[newIndex]), 0, sizeof(Particle));
1035
1036 //Required fields
1037 particles[newIndex].type = partsData[i];
1038 particles[newIndex].x = x;
1039 particles[newIndex].y = y;
1040 i+=3;
1041
1042 // Read type (2nd byte)
1043 if (fieldDescriptor & 0x4000)
1044 particles[newIndex].type |= (((unsigned)partsData[i++]) << 8);
1045
1046 //Read temp
1047 if(fieldDescriptor & 0x01)
1048 {
1049 //Full 16bit int
1050 tempTemp = partsData[i++];
1051 tempTemp |= (((unsigned)partsData[i++]) << 8);
1052 particles[newIndex].temp = tempTemp;
1053 }
1054 else
1055 {
1056 //1 Byte room temp offset
1057 tempTemp = (char)partsData[i++];
1058 particles[newIndex].temp = tempTemp+294.15f;
1059 }
1060
1061 //Read life
1062 if(fieldDescriptor & 0x02)
1063 {
1064 if (i >= partsDataLen)
1065 throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading life");
1066 particles[newIndex].life = partsData[i++];
1067 //i++;
1068 //Read 2nd byte
1069 if(fieldDescriptor & 0x04)
1070 {
1071 if (i >= partsDataLen)
1072 throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading life");
1073 particles[newIndex].life |= (((unsigned)partsData[i++]) << 8);
1074 }
1075 }
1076
1077 //Read tmp
1078 if(fieldDescriptor & 0x08)
1079 {
1080 if (i >= partsDataLen)
1081 throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading tmp");
1082 particles[newIndex].tmp = partsData[i++];
1083 //Read 2nd byte
1084 if(fieldDescriptor & 0x10)
1085 {
1086 if (i >= partsDataLen)
1087 throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading tmp");
1088 particles[newIndex].tmp |= (((unsigned)partsData[i++]) << 8);
1089 //Read 3rd and 4th bytes
1090 if(fieldDescriptor & 0x1000)
1091 {
1092 if (i+1 >= partsDataLen)
1093 throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading tmp");
1094 particles[newIndex].tmp |= (((unsigned)partsData[i++]) << 24);
1095 particles[newIndex].tmp |= (((unsigned)partsData[i++]) << 16);
1096 }
1097 }
1098 }
1099
1100 //Read ctype
1101 if(fieldDescriptor & 0x20)
1102 {
1103 if (i >= partsDataLen)
1104 throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading ctype");
1105 particles[newIndex].ctype = partsData[i++];
1106 //Read additional bytes
1107 if(fieldDescriptor & 0x200)
1108 {
1109 if (i+2 >= partsDataLen)
1110 throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading ctype");
1111 particles[newIndex].ctype |= (((unsigned)partsData[i++]) << 24);
1112 particles[newIndex].ctype |= (((unsigned)partsData[i++]) << 16);
1113 particles[newIndex].ctype |= (((unsigned)partsData[i++]) << 8);
1114 }
1115 }
1116
1117 //Read dcolour
1118 if(fieldDescriptor & 0x40)
1119 {
1120 if (i+3 >= partsDataLen)
1121 throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading deco");
1122 particles[newIndex].dcolour = (((unsigned)partsData[i++]) << 24);
1123 particles[newIndex].dcolour |= (((unsigned)partsData[i++]) << 16);
1124 particles[newIndex].dcolour |= (((unsigned)partsData[i++]) << 8);
1125 particles[newIndex].dcolour |= ((unsigned)partsData[i++]);
1126 }
1127
1128 //Read vx
1129 if(fieldDescriptor & 0x80)
1130 {
1131 if (i >= partsDataLen)
1132 throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading vx");
1133 particles[newIndex].vx = (partsData[i++]-127.0f)/16.0f;
1134 }
1135
1136 //Read vy
1137 if(fieldDescriptor & 0x100)
1138 {
1139 if (i >= partsDataLen)
1140 throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading vy");
1141 particles[newIndex].vy = (partsData[i++]-127.0f)/16.0f;
1142 }
1143
1144 //Read tmp2
1145 if(fieldDescriptor & 0x400)
1146 {
1147 if (i >= partsDataLen)
1148 throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading tmp2");
1149 particles[newIndex].tmp2 = partsData[i++];
1150 if(fieldDescriptor & 0x800)
1151 {
1152 if (i >= partsDataLen)
1153 throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading tmp2");
1154 particles[newIndex].tmp2 |= (((unsigned)partsData[i++]) << 8);
1155 }
1156 }
1157
1158 //Read pavg
1159 if(fieldDescriptor & 0x2000)
1160 {
1161 if (i+3 >= partsDataLen)
1162 throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading pavg");
1163 int pavg;
1164 pavg = partsData[i++];
1165 pavg |= (((unsigned)partsData[i++]) << 8);
1166 particles[newIndex].pavg[0] = (float)pavg;
1167 pavg = partsData[i++];
1168 pavg |= (((unsigned)partsData[i++]) << 8);
1169 particles[newIndex].pavg[1] = (float)pavg;
1170
1171 switch (particles[newIndex].type)
1172 {
1173 // List of elements that save pavg with a multiplicative bias of 2**6
1174 // (or not at all if pressure is not saved).
1175 // If you change this list, change it in Simulation::Load and GameSave::serialiseOPS too!
1176 case PT_QRTZ:
1177 case PT_GLAS:
1178 case PT_TUNG:
1179 particles[newIndex].pavg[0] /= 64;
1180 particles[newIndex].pavg[1] /= 64;
1181 break;
1182 }
1183 }
1184
1185 //Particle specific parsing:
1186 switch(particles[newIndex].type)
1187 {
1188 case PT_SOAP:
1189 //Clear soap links, links will be added back in if soapLinkData is present
1190 particles[newIndex].ctype &= ~6;
1191 break;
1192 case PT_BOMB:
1193 if (particles[newIndex].tmp!=0 && savedVersion < 81)
1194 {
1195 particles[newIndex].type = PT_EMBR;
1196 particles[newIndex].ctype = 0;
1197 if (particles[newIndex].tmp==1)
1198 particles[newIndex].tmp = 0;
1199 }
1200 break;
1201 case PT_DUST:
1202 if (particles[newIndex].life>0 && savedVersion < 81)
1203 {
1204 particles[newIndex].type = PT_EMBR;
1205 particles[newIndex].ctype = (particles[newIndex].tmp2<<16) | (particles[newIndex].tmp<<8) | particles[newIndex].ctype;
1206 particles[newIndex].tmp = 1;
1207 }
1208 break;
1209 case PT_FIRW:
1210 if (particles[newIndex].tmp>=2 && savedVersion < 81)
1211 {
1212 int caddress = restrict_flt(restrict_flt((float)(particles[newIndex].tmp-4), 0.0f, 200.0f)*3, 0.0f, (200.0f*3)-3);
1213 particles[newIndex].type = PT_EMBR;
1214 particles[newIndex].tmp = 1;
1215 particles[newIndex].ctype = (((firw_data[caddress]))<<16) | (((firw_data[caddress+1]))<<8) | ((firw_data[caddress+2]));
1216 }
1217 break;
1218 case PT_PSTN:
1219 if (savedVersion < 87 && particles[newIndex].ctype)
1220 particles[newIndex].life = 1;
1221 if (savedVersion < 91)
1222 particles[newIndex].temp = 283.15;
1223 break;
1224 case PT_FILT:
1225 if (savedVersion < 89)
1226 {
1227 if (particles[newIndex].tmp<0 || particles[newIndex].tmp>3)
1228 particles[newIndex].tmp = 6;
1229 particles[newIndex].ctype = 0;
1230 }
1231 break;
1232 case PT_QRTZ:
1233 case PT_PQRT:
1234 if (savedVersion < 89)
1235 {
1236 particles[newIndex].tmp2 = particles[newIndex].tmp;
1237 particles[newIndex].tmp = particles[newIndex].ctype;
1238 particles[newIndex].ctype = 0;
1239 }
1240 break;
1241 case PT_PHOT:
1242 if (savedVersion < 90)
1243 {
1244 particles[newIndex].flags |= FLAG_PHOTDECO;
1245 }
1246 break;
1247 case PT_VINE:
1248 if (savedVersion < 91)
1249 {
1250 particles[newIndex].tmp = 1;
1251 }
1252 break;
1253 case PT_DLAY:
1254 // correct DLAY temperature in older saves
1255 // due to either the +.5f now done in DLAY (higher temps), or rounding errors in the old DLAY code (room temperature temps),
1256 // the delay in all DLAY from older versions will always be one greater than it should
1257 if (savedVersion < 91)
1258 {
1259 particles[newIndex].temp = particles[newIndex].temp - 1.0f;
1260 }
1261 break;
1262 case PT_CRAY:
1263 if (savedVersion < 91)
1264 {
1265 if (particles[newIndex].tmp2)
1266 {
1267 particles[newIndex].ctype |= particles[newIndex].tmp2<<8;
1268 particles[newIndex].tmp2 = 0;
1269 }
1270 }
1271 break;
1272 case PT_CONV:
1273 if (savedVersion < 91)
1274 {
1275 if (particles[newIndex].tmp)
1276 {
1277 particles[newIndex].ctype |= particles[newIndex].tmp<<8;
1278 particles[newIndex].tmp = 0;
1279 }
1280 }
1281 break;
1282 case PT_PIPE:
1283 case PT_PPIP:
1284 if (savedVersion < 93 && !fakeNewerVersion)
1285 {
1286 if (particles[newIndex].ctype == 1)
1287 particles[newIndex].tmp |= 0x00020000; //PFLAG_INITIALIZING
1288 particles[newIndex].tmp |= (particles[newIndex].ctype-1)<<18;
1289 particles[newIndex].ctype = particles[newIndex].tmp&0xFF;
1290 }
1291 break;
1292 case PT_TSNS:
1293 case PT_HSWC:
1294 case PT_PSNS:
1295 case PT_PUMP:
1296 if (savedVersion < 93 && !fakeNewerVersion)
1297 {
1298 particles[newIndex].tmp = 0;
1299 }
1300 break;
1301 }
1302 //note: PSv was used in version 77.0 and every version before, add something in PSv too if the element is that old
1303 newIndex++;
1304 partsCount++;
1305 }
1306 }
1307 }
1308
1309 if (i != partsDataLen)
1310 throw ParseException(ParseException::Corrupt, "Didn't reach end of particle data buffer");
1311 }
1312
1313 if (soapLinkData)
1314 {
1315 unsigned int soapLinkDataPos = 0;
1316 for (unsigned int i = 0; i < partsCount; i++)
1317 {
1318 if (particles[i].type == PT_SOAP)
1319 {
1320 // Get the index of the particle forward linked from this one, if present in the save data
1321 unsigned int linkedIndex = 0;
1322 if (soapLinkDataPos+3 > soapLinkDataLen) break;
1323 linkedIndex |= soapLinkData[soapLinkDataPos++]<<16;
1324 linkedIndex |= soapLinkData[soapLinkDataPos++]<<8;
1325 linkedIndex |= soapLinkData[soapLinkDataPos++];
1326 // All indexes in soapLinkData and partsSimIndex have 1 added to them (0 means not saved/loaded)
1327 if (!linkedIndex || linkedIndex-1 >= partsCount)
1328 continue;
1329 linkedIndex = linkedIndex-1;
1330
1331 //Attach the two particles
1332 particles[i].ctype |= 2;
1333 particles[i].tmp = linkedIndex;
1334 particles[linkedIndex].ctype |= 4;
1335 particles[linkedIndex].tmp2 = i;
1336 }
1337 }
1338 }
1339
1340 if (tempSigns.size())
1341 {
1342 for (size_t i = 0; i < tempSigns.size(); i++)
1343 {
1344 if(signs.size() == MAXSIGNS)
1345 break;
1346 signs.push_back(tempSigns[i]);
1347 }
1348 }
1349 }
1350
readPSv(char * saveDataChar,int dataLength)1351 void GameSave::readPSv(char * saveDataChar, int dataLength)
1352 {
1353 unsigned char * saveData = (unsigned char *)saveDataChar;
1354 int q,j,k,x,y,p=0, ver, pty, ty, legacy_beta=0;
1355 int bx0=0, by0=0, bw, bh, w, h, y0 = 0, x0 = 0;
1356 int new_format = 0, ttv = 0;
1357
1358 std::vector<sign> tempSigns;
1359 char tempSignText[255];
1360 sign tempSign("", 0, 0, sign::Left);
1361
1362 //Gol data used to read older saves
1363 std::vector<int> goltype = LoadGOLTypes();
1364 std::vector<std::array<int, 10> > grule = LoadGOLRules();
1365
1366 std::vector<Element> elements = GetElements();
1367
1368 //New file header uses PSv, replacing fuC. This is to detect if the client uses a new save format for temperatures
1369 //This creates a problem for old clients, that display and "corrupt" error instead of a "newer version" error
1370
1371 if (dataLength<16)
1372 throw ParseException(ParseException::Corrupt, "No save data");
1373 if (!(saveData[2]==0x43 && saveData[1]==0x75 && saveData[0]==0x66) && !(saveData[2]==0x76 && saveData[1]==0x53 && saveData[0]==0x50))
1374 throw ParseException(ParseException::Corrupt, "Unknown format");
1375 if (saveData[2]==0x76 && saveData[1]==0x53 && saveData[0]==0x50) {
1376 new_format = 1;
1377 }
1378 if (saveData[4]>SAVE_VERSION)
1379 throw ParseException(ParseException::WrongVersion, "Save from newer version");
1380 ver = saveData[4];
1381 majorVersion = saveData[4];
1382 minorVersion = 0;
1383
1384 if (ver<34)
1385 {
1386 legacyEnable = 1;
1387 }
1388 else
1389 {
1390 if (ver>=44) {
1391 legacyEnable = saveData[3]&0x01;
1392 paused = (saveData[3]>>1)&0x01;
1393 if (ver>=46) {
1394 gravityMode = ((saveData[3]>>2)&0x03);// | ((c[3]>>2)&0x01);
1395 airMode = ((saveData[3]>>4)&0x07);// | ((c[3]>>4)&0x02) | ((c[3]>>4)&0x01);
1396 }
1397 if (ver>=49) {
1398 gravityEnable = ((saveData[3]>>7)&0x01);
1399 }
1400 } else {
1401 if (saveData[3]==1||saveData[3]==0) {
1402 legacyEnable = saveData[3];
1403 } else {
1404 legacy_beta = 1;
1405 }
1406 }
1407 }
1408
1409 bw = saveData[6];
1410 bh = saveData[7];
1411 if (bx0+bw > XRES/CELL)
1412 bx0 = XRES/CELL - bw;
1413 if (by0+bh > YRES/CELL)
1414 by0 = YRES/CELL - bh;
1415 if (bx0 < 0)
1416 bx0 = 0;
1417 if (by0 < 0)
1418 by0 = 0;
1419
1420 if (saveData[5]!=CELL || bx0+bw>XRES/CELL || by0+bh>YRES/CELL)
1421 throw ParseException(ParseException::InvalidDimensions, "Save too large");
1422 int size = (unsigned)saveData[8];
1423 size |= ((unsigned)saveData[9])<<8;
1424 size |= ((unsigned)saveData[10])<<16;
1425 size |= ((unsigned)saveData[11])<<24;
1426
1427 if (size > 209715200 || !size)
1428 throw ParseException(ParseException::InvalidDimensions, "Save data too large");
1429
1430 auto dataPtr = std::unique_ptr<unsigned char[]>(new unsigned char[size]);
1431 unsigned char *data = dataPtr.get();
1432 if (!data)
1433 throw ParseException(ParseException::Corrupt, "Cannot allocate memory");
1434
1435 setSize(bw, bh);
1436
1437 int bzStatus = 0;
1438 if ((bzStatus = BZ2_bzBuffToBuffDecompress((char *)data, (unsigned *)&size, (char *)(saveData+12), dataLength-12, 0, 0)))
1439 throw ParseException(ParseException::Corrupt, String::Build("Cannot decompress: ", bzStatus));
1440 dataLength = size;
1441
1442 #ifdef DEBUG
1443 std::cout << "Parsing " << dataLength << " bytes of data, version " << ver << std::endl;
1444 #endif
1445
1446 if (dataLength < bw*bh)
1447 throw ParseException(ParseException::Corrupt, "Save data corrupt (missing data)");
1448
1449 // normalize coordinates
1450 x0 = bx0*CELL;
1451 y0 = by0*CELL;
1452 w = bw *CELL;
1453 h = bh *CELL;
1454
1455 if (ver<46) {
1456 gravityMode = 0;
1457 airMode = 0;
1458 }
1459
1460 auto particleIDMapPtr = std::unique_ptr<int[]>(new int[XRES*YRES]);
1461 int *particleIDMap = particleIDMapPtr.get();
1462 std::fill(&particleIDMap[0], &particleIDMap[XRES*YRES], 0);
1463 if (!particleIDMap)
1464 throw ParseException(ParseException::Corrupt, "Cannot allocate memory");
1465
1466 // load the required air state
1467 for (y=by0; y<by0+bh; y++)
1468 for (x=bx0; x<bx0+bw; x++)
1469 {
1470 if (data[p])
1471 {
1472 //In old saves, ignore walls created by sign tool bug
1473 //Not ignoring other invalid walls or invalid walls in new saves, so that any other bugs causing them are easier to notice, find and fix
1474 if (ver>=44 && ver<71 && data[p]==O_WL_SIGN)
1475 {
1476 p++;
1477 continue;
1478 }
1479 blockMap[y][x] = data[p];
1480 if (blockMap[y][x]==1)
1481 blockMap[y][x]=WL_WALL;
1482 else if (blockMap[y][x]==2)
1483 blockMap[y][x]=WL_DESTROYALL;
1484 else if (blockMap[y][x]==3)
1485 blockMap[y][x]=WL_ALLOWLIQUID;
1486 else if (blockMap[y][x]==4)
1487 blockMap[y][x]=WL_FAN;
1488 else if (blockMap[y][x]==5)
1489 blockMap[y][x]=WL_STREAM;
1490 else if (blockMap[y][x]==6)
1491 blockMap[y][x]=WL_DETECT;
1492 else if (blockMap[y][x]==7)
1493 blockMap[y][x]=WL_EWALL;
1494 else if (blockMap[y][x]==8)
1495 blockMap[y][x]=WL_WALLELEC;
1496 else if (blockMap[y][x]==9)
1497 blockMap[y][x]=WL_ALLOWAIR;
1498 else if (blockMap[y][x]==10)
1499 blockMap[y][x]=WL_ALLOWPOWDER;
1500 else if (blockMap[y][x]==11)
1501 blockMap[y][x]=WL_ALLOWALLELEC;
1502 else if (blockMap[y][x]==12)
1503 blockMap[y][x]=WL_EHOLE;
1504 else if (blockMap[y][x]==13)
1505 blockMap[y][x]=WL_ALLOWGAS;
1506
1507 if (ver>=44)
1508 {
1509 /* The numbers used to save walls were changed, starting in v44.
1510 * The new numbers are ignored for older versions due to some corruption of bmap in saves from older versions.
1511 */
1512 if (blockMap[y][x]==O_WL_WALLELEC)
1513 blockMap[y][x]=WL_WALLELEC;
1514 else if (blockMap[y][x]==O_WL_EWALL)
1515 blockMap[y][x]=WL_EWALL;
1516 else if (blockMap[y][x]==O_WL_DETECT)
1517 blockMap[y][x]=WL_DETECT;
1518 else if (blockMap[y][x]==O_WL_STREAM)
1519 blockMap[y][x]=WL_STREAM;
1520 else if (blockMap[y][x]==O_WL_FAN||blockMap[y][x]==O_WL_FANHELPER)
1521 blockMap[y][x]=WL_FAN;
1522 else if (blockMap[y][x]==O_WL_ALLOWLIQUID)
1523 blockMap[y][x]=WL_ALLOWLIQUID;
1524 else if (blockMap[y][x]==O_WL_DESTROYALL)
1525 blockMap[y][x]=WL_DESTROYALL;
1526 else if (blockMap[y][x]==O_WL_ERASE)
1527 blockMap[y][x]=WL_ERASE;
1528 else if (blockMap[y][x]==O_WL_WALL)
1529 blockMap[y][x]=WL_WALL;
1530 else if (blockMap[y][x]==O_WL_ALLOWAIR)
1531 blockMap[y][x]=WL_ALLOWAIR;
1532 else if (blockMap[y][x]==O_WL_ALLOWSOLID)
1533 blockMap[y][x]=WL_ALLOWPOWDER;
1534 else if (blockMap[y][x]==O_WL_ALLOWALLELEC)
1535 blockMap[y][x]=WL_ALLOWALLELEC;
1536 else if (blockMap[y][x]==O_WL_EHOLE)
1537 blockMap[y][x]=WL_EHOLE;
1538 else if (blockMap[y][x]==O_WL_ALLOWGAS)
1539 blockMap[y][x]=WL_ALLOWGAS;
1540 else if (blockMap[y][x]==O_WL_GRAV)
1541 blockMap[y][x]=WL_GRAV;
1542 else if (blockMap[y][x]==O_WL_ALLOWENERGY)
1543 blockMap[y][x]=WL_ALLOWENERGY;
1544 }
1545
1546 if (blockMap[y][x] >= UI_WALLCOUNT)
1547 blockMap[y][x] = 0;
1548 }
1549
1550 p++;
1551 }
1552 for (y=by0; y<by0+bh; y++)
1553 for (x=bx0; x<bx0+bw; x++)
1554 if (data[(y-by0)*bw+(x-bx0)]==4||(ver>=44 && data[(y-by0)*bw+(x-bx0)]==O_WL_FAN))
1555 {
1556 if (p >= dataLength)
1557 throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
1558 fanVelX[y][x] = (data[p++]-127.0f)/64.0f;
1559 }
1560 for (y=by0; y<by0+bh; y++)
1561 for (x=bx0; x<bx0+bw; x++)
1562 if (data[(y-by0)*bw+(x-bx0)]==4||(ver>=44 && data[(y-by0)*bw+(x-bx0)]==O_WL_FAN))
1563 {
1564 if (p >= dataLength)
1565 throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
1566 fanVelY[y][x] = (data[p++]-127.0f)/64.0f;
1567 }
1568
1569 // load the particle map
1570 int i = 0;
1571 k = 0;
1572 pty = p;
1573 for (y=y0; y<y0+h; y++)
1574 for (x=x0; x<x0+w; x++)
1575 {
1576 if (p >= dataLength)
1577 throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
1578 j=data[p++];
1579 if (j >= PT_NUM) {
1580 j = PT_DUST;//throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
1581 }
1582 if (j)
1583 {
1584 memset(particles+k, 0, sizeof(Particle));
1585 particles[k].type = j;
1586 if (j == PT_COAL)
1587 particles[k].tmp = 50;
1588 if (j == PT_FUSE)
1589 particles[k].tmp = 50;
1590 if (j == PT_PHOT)
1591 particles[k].ctype = 0x3fffffff;
1592 if (j == PT_SOAP)
1593 particles[k].ctype = 0;
1594 if (j==PT_BIZR || j==PT_BIZRG || j==PT_BIZRS)
1595 particles[k].ctype = 0x47FFFF;
1596 particles[k].x = (float)x;
1597 particles[k].y = (float)y;
1598 particleIDMap[(x-x0)+(y-y0)*w] = k+1;
1599 particlesCount = ++k;
1600 }
1601 }
1602
1603 // load particle properties
1604 for (j=0; j<w*h; j++)
1605 {
1606 i = particleIDMap[j];
1607 if (i)
1608 {
1609 i--;
1610 if (p+1 >= dataLength)
1611 throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
1612 if (i < NPART)
1613 {
1614 particles[i].vx = (data[p++]-127.0f)/16.0f;
1615 particles[i].vy = (data[p++]-127.0f)/16.0f;
1616 }
1617 else
1618 p += 2;
1619 }
1620 }
1621 for (j=0; j<w*h; j++)
1622 {
1623 i = particleIDMap[j];
1624 if (i)
1625 {
1626 if (ver>=44) {
1627 if (p >= dataLength) {
1628 throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
1629 }
1630 if (i <= NPART) {
1631 ttv = (data[p++])<<8;
1632 ttv |= (data[p++]);
1633 particles[i-1].life = ttv;
1634 } else {
1635 p+=2;
1636 }
1637 } else {
1638 if (p >= dataLength)
1639 throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
1640 if (i <= NPART)
1641 particles[i-1].life = data[p++]*4;
1642 else
1643 p++;
1644 }
1645 }
1646 }
1647 if (ver>=44) {
1648 for (j=0; j<w*h; j++)
1649 {
1650 i = particleIDMap[j];
1651 if (i)
1652 {
1653 if (p >= dataLength) {
1654 throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
1655 }
1656 if (i <= NPART) {
1657 ttv = (data[p++])<<8;
1658 ttv |= (data[p++]);
1659 particles[i-1].tmp = ttv;
1660 if (ver<53 && !particles[i-1].tmp)
1661 for (q = 1; q<=NGOL; q++) {
1662 if (particles[i-1].type==goltype[q-1] && grule[q][9]==2)
1663 particles[i-1].tmp = grule[q][9]-1;
1664 }
1665 if (ver>=51 && ver<53 && particles[i-1].type==PT_PBCN)
1666 {
1667 particles[i-1].tmp2 = particles[i-1].tmp;
1668 particles[i-1].tmp = 0;
1669 }
1670 } else {
1671 p+=2;
1672 }
1673 }
1674 }
1675 }
1676 if (ver>=53) {
1677 for (j=0; j<w*h; j++)
1678 {
1679 i = particleIDMap[j];
1680 ty = data[pty+j];
1681 if (i && (ty==PT_PBCN || (ty==PT_TRON && ver>=77)))
1682 {
1683 if (p >= dataLength)
1684 throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
1685 if (i <= NPART)
1686 particles[i-1].tmp2 = data[p++];
1687 else
1688 p++;
1689 }
1690 }
1691 }
1692 //Read ALPHA component
1693 for (j=0; j<w*h; j++)
1694 {
1695 i = particleIDMap[j];
1696 if (i)
1697 {
1698 if (ver>=49) {
1699 if (p >= dataLength) {
1700 throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
1701 }
1702 if (i <= NPART) {
1703 particles[i-1].dcolour = data[p++]<<24;
1704 } else {
1705 p++;
1706 }
1707 }
1708 }
1709 }
1710 //Read RED component
1711 for (j=0; j<w*h; j++)
1712 {
1713 i = particleIDMap[j];
1714 if (i)
1715 {
1716 if (ver>=49) {
1717 if (p >= dataLength) {
1718 throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
1719 }
1720 if (i <= NPART) {
1721 particles[i-1].dcolour |= data[p++]<<16;
1722 } else {
1723 p++;
1724 }
1725 }
1726 }
1727 }
1728 //Read GREEN component
1729 for (j=0; j<w*h; j++)
1730 {
1731 i = particleIDMap[j];
1732 if (i)
1733 {
1734 if (ver>=49) {
1735 if (p >= dataLength) {
1736 throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
1737 }
1738 if (i <= NPART) {
1739 particles[i-1].dcolour |= data[p++]<<8;
1740 } else {
1741 p++;
1742 }
1743 }
1744 }
1745 }
1746 //Read BLUE component
1747 for (j=0; j<w*h; j++)
1748 {
1749 i = particleIDMap[j];
1750 if (i)
1751 {
1752 if (ver>=49) {
1753 if (p >= dataLength) {
1754 throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
1755 }
1756 if (i <= NPART) {
1757 particles[i-1].dcolour |= data[p++];
1758 } else {
1759 p++;
1760 }
1761 }
1762 }
1763 }
1764 for (j=0; j<w*h; j++)
1765 {
1766 i = particleIDMap[j];
1767 ty = data[pty+j];
1768 if (i)
1769 {
1770 if (ver>=34&&legacy_beta==0)
1771 {
1772 if (p >= dataLength)
1773 {
1774 throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
1775 }
1776 if (i <= NPART)
1777 {
1778 if (ver>=42) {
1779 if (new_format) {
1780 ttv = (data[p++])<<8;
1781 ttv |= (data[p++]);
1782 if (particles[i-1].type==PT_PUMP) {
1783 particles[i-1].temp = ttv + 0.15;//fix PUMP saved at 0, so that it loads at 0.
1784 } else {
1785 particles[i-1].temp = ttv;
1786 }
1787 } else {
1788 particles[i-1].temp = (data[p++]*((MAX_TEMP+(-MIN_TEMP))/255))+MIN_TEMP;
1789 }
1790 } else {
1791 particles[i-1].temp = ((data[p++]*((O_MAX_TEMP+(-O_MIN_TEMP))/255))+O_MIN_TEMP)+273;
1792 }
1793 }
1794 else
1795 {
1796 p++;
1797 if (new_format) {
1798 p++;
1799 }
1800 }
1801 }
1802 else
1803 {
1804 particles[i-1].temp = elements[particles[i-1].type].DefaultProperties.temp;
1805 }
1806 }
1807 }
1808 for (j=0; j<w*h; j++)
1809 {
1810 int gnum = 0;
1811 i = particleIDMap[j];
1812 ty = data[pty+j];
1813 if (i && (ty==PT_CLNE || (ty==PT_PCLN && ver>=43) || (ty==PT_BCLN && ver>=44) || (ty==PT_SPRK && ver>=21) || (ty==PT_LAVA && ver>=34) || (ty==PT_PIPE && ver>=43) || (ty==PT_LIFE && ver>=51) || (ty==PT_PBCN && ver>=52) || (ty==PT_WIRE && ver>=55) || (ty==PT_STOR && ver>=59) || (ty==PT_CONV && ver>=60)))
1814 {
1815 if (p >= dataLength)
1816 throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
1817 if (i <= NPART)
1818 particles[i-1].ctype = data[p++];
1819 else
1820 p++;
1821 }
1822 // no more particle properties to load, so we can change type here without messing up loading
1823 if (i && i<=NPART)
1824 {
1825 if (ver<90 && particles[i-1].type == PT_PHOT)
1826 {
1827 particles[i-1].flags |= FLAG_PHOTDECO;
1828 }
1829 if (ver<79 && particles[i-1].type == PT_SPNG)
1830 {
1831 if (fabs(particles[i-1].vx)>0.0f || fabs(particles[i-1].vy)>0.0f)
1832 particles[i-1].flags |= FLAG_MOVABLE;
1833 }
1834
1835 if (ver<48 && (ty==OLD_PT_WIND || (ty==PT_BRAY&&particles[i-1].life==0)))
1836 {
1837 // Replace invisible particles with something sensible and add decoration to hide it
1838 x = (int)(particles[i-1].x+0.5f);
1839 y = (int)(particles[i-1].y+0.5f);
1840 particles[i-1].dcolour = 0xFF000000;
1841 particles[i-1].type = PT_DMND;
1842 }
1843 if(ver<51 && ((ty>=78 && ty<=89) || (ty>=134 && ty<=146 && ty!=141))){
1844 //Replace old GOL
1845 particles[i-1].type = PT_LIFE;
1846 for (gnum = 0; gnum<NGOL; gnum++){
1847 if (ty==goltype[gnum])
1848 particles[i-1].ctype = gnum;
1849 }
1850 ty = PT_LIFE;
1851 }
1852 if(ver<52 && (ty==PT_CLNE || ty==PT_PCLN || ty==PT_BCLN)){
1853 //Replace old GOL ctypes in clone
1854 for (gnum = 0; gnum<NGOL; gnum++){
1855 if (particles[i-1].ctype==goltype[gnum])
1856 {
1857 particles[i-1].ctype = PT_LIFE;
1858 particles[i-1].tmp = gnum;
1859 }
1860 }
1861 }
1862 if(ty==PT_LCRY){
1863 if(ver<67)
1864 {
1865 //New LCRY uses TMP not life
1866 if(particles[i-1].life>=10)
1867 {
1868 particles[i-1].life = 10;
1869 particles[i-1].tmp2 = 10;
1870 particles[i-1].tmp = 3;
1871 }
1872 else if(particles[i-1].life<=0)
1873 {
1874 particles[i-1].life = 0;
1875 particles[i-1].tmp2 = 0;
1876 particles[i-1].tmp = 0;
1877 }
1878 else if(particles[i-1].life < 10 && particles[i-1].life > 0)
1879 {
1880 particles[i-1].tmp = 1;
1881 }
1882 }
1883 else
1884 {
1885 particles[i-1].tmp2 = particles[i-1].life;
1886 }
1887 }
1888
1889 if (ver<81)
1890 {
1891 if (particles[i-1].type==PT_BOMB && particles[i-1].tmp!=0)
1892 {
1893 particles[i-1].type = PT_EMBR;
1894 particles[i-1].ctype = 0;
1895 if (particles[i-1].tmp==1)
1896 particles[i-1].tmp = 0;
1897 }
1898 if (particles[i-1].type==PT_DUST && particles[i-1].life>0)
1899 {
1900 particles[i-1].type = PT_EMBR;
1901 particles[i-1].ctype = (particles[i-1].tmp2<<16) | (particles[i-1].tmp<<8) | particles[i-1].ctype;
1902 particles[i-1].tmp = 1;
1903 }
1904 if (particles[i-1].type==PT_FIRW && particles[i-1].tmp>=2)
1905 {
1906 int caddress = restrict_flt(restrict_flt((float)(particles[i-1].tmp-4), 0.0f, 200.0f)*3, 0.0f, (200.0f*3)-3);
1907 particles[i-1].type = PT_EMBR;
1908 particles[i-1].tmp = 1;
1909 particles[i-1].ctype = (((firw_data[caddress]))<<16) | (((firw_data[caddress+1]))<<8) | ((firw_data[caddress+2]));
1910 }
1911 }
1912 if (ver < 89)
1913 {
1914 if (particles[i-1].type == PT_FILT)
1915 {
1916 if (particles[i-1].tmp<0 || particles[i-1].tmp>3)
1917 particles[i-1].tmp = 6;
1918 particles[i-1].ctype = 0;
1919 }
1920 else if (particles[i-1].type == PT_QRTZ || particles[i-1].type == PT_PQRT)
1921 {
1922 particles[i-1].tmp2 = particles[i-1].tmp;
1923 particles[i-1].tmp = particles[i-1].ctype;
1924 particles[i-1].ctype = 0;
1925 }
1926 }
1927 if (ver < 91)
1928 {
1929 if (particles[i-1].type == PT_VINE)
1930 particles[i-1].tmp = 1;
1931 else if (particles[i-1].type == PT_CONV)
1932 {
1933 if (particles[i-1].tmp)
1934 {
1935 particles[i-1].ctype |= particles[i-1].tmp<<8;
1936 particles[i-1].tmp = 0;
1937 }
1938 }
1939 }
1940 if (ver < 93)
1941 {
1942 if (particles[i-1].type == PT_PIPE || particles[i-1].type == PT_PPIP)
1943 {
1944 if (particles[i-1].ctype == 1)
1945 particles[i-1].tmp |= 0x00020000; //PFLAG_INITIALIZING
1946 particles[i-1].tmp |= (particles[i-1].ctype-1)<<18;
1947 particles[i-1].ctype = particles[i-1].tmp&0xFF;
1948 }
1949 else if (particles[i-1].type == PT_HSWC || particles[i-1].type == PT_PUMP)
1950 {
1951 particles[i-1].tmp = 0;
1952 }
1953 }
1954 }
1955 }
1956
1957 if (p >= dataLength)
1958 throw ParseException(ParseException::Corrupt, "Ran past data buffer");
1959
1960 j = data[p++];
1961 for (i=0; i<j; i++)
1962 {
1963 if (p+6 > dataLength)
1964 throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
1965 x = data[p++];
1966 x |= ((unsigned)data[p++])<<8;
1967 tempSign.x = x+x0;
1968 x = data[p++];
1969 x |= ((unsigned)data[p++])<<8;
1970 tempSign.y = x+y0;
1971 x = data[p++];
1972 tempSign.ju = (sign::Justification)x;
1973 x = data[p++];
1974 if (p+x > dataLength)
1975 throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
1976 if(x>254)
1977 x = 254;
1978 memcpy(tempSignText, data+p, x);
1979 tempSignText[x] = 0;
1980 tempSign.text = format::CleanString(ByteString(tempSignText).FromUtf8(), true, true, true).Substr(0, 45);
1981 if (tempSign.text == "{t}")
1982 {
1983 tempSign.text = "Temp: {t}";
1984 }
1985 else if (tempSign.text == "{p}")
1986 {
1987 tempSign.text = "Pressure: {p}";
1988 }
1989 tempSigns.push_back(tempSign);
1990 p += x;
1991 }
1992
1993 for (size_t i = 0; i < tempSigns.size(); i++)
1994 {
1995 if(signs.size() == MAXSIGNS)
1996 break;
1997 signs.push_back(tempSigns[i]);
1998 }
1999 }
2000
2001 // restrict the minimum version this save can be opened with
2002 #define RESTRICTVERSION(major, minor) if ((major) > minimumMajorVersion || (((major) == minimumMajorVersion && (minor) > minimumMinorVersion))) {\
2003 minimumMajorVersion = major;\
2004 minimumMinorVersion = minor;\
2005 }
2006
2007 // restrict the minimum version this save can be rendered with
2008 #define RESTRICTRENDERVERSION(major, minor) if ((major) > blameSimon_major || (((major) == blameSimon_major && (minor) > blameSimon_minor))) {\
2009 blameSimon_major = major;\
2010 blameSimon_minor = minor;\
2011 }
2012
serialiseOPS(unsigned int & dataLength)2013 char * GameSave::serialiseOPS(unsigned int & dataLength)
2014 {
2015 int blockX, blockY, blockW, blockH, fullX, fullY, fullW, fullH;
2016 int x, y, i;
2017 // minimum version this save is compatible with
2018 // when building, this number may be increased depending on what elements are used
2019 // or what properties are detected
2020 int minimumMajorVersion = 90, minimumMinorVersion = 2;
2021 // blame simon for always being slow updating the renderer
2022 int blameSimon_major = 92, blameSimon_minor = 0;
2023
2024 //Get coords in blocks
2025 blockX = 0;//orig_x0/CELL;
2026 blockY = 0;//orig_y0/CELL;
2027
2028 //Snap full coords to block size
2029 fullX = blockX*CELL;
2030 fullY = blockY*CELL;
2031
2032 //Original size + offset of original corner from snapped corner, rounded up by adding CELL-1
2033 blockW = blockWidth;//(blockWidth-fullX+CELL-1)/CELL;
2034 blockH = blockHeight;//(blockHeight-fullY+CELL-1)/CELL;
2035 fullW = blockW*CELL;
2036 fullH = blockH*CELL;
2037
2038 // Copy fan and wall data
2039 auto wallData = std::unique_ptr<unsigned char[]>(new unsigned char[blockWidth*blockHeight]);
2040 bool hasWallData = false;
2041 auto fanData = std::unique_ptr<unsigned char[]>(new unsigned char[blockWidth*blockHeight*2]);
2042 auto pressData = std::unique_ptr<unsigned char[]>(new unsigned char[blockWidth*blockHeight*2]);
2043 auto vxData = std::unique_ptr<unsigned char[]>(new unsigned char[blockWidth*blockHeight*2]);
2044 auto vyData = std::unique_ptr<unsigned char[]>(new unsigned char[blockWidth*blockHeight*2]);
2045 auto ambientData = std::unique_ptr<unsigned char[]>(new unsigned char[blockWidth*blockHeight*2]);
2046 std::fill(&ambientData[0], &ambientData[blockWidth*blockHeight*2], 0);
2047 if (!wallData || !fanData || !pressData || !vxData || !vyData || !ambientData)
2048 throw BuildException("Save error, out of memory (blockmaps)");
2049 unsigned int wallDataLen = blockWidth*blockHeight, fanDataLen = 0, pressDataLen = 0, vxDataLen = 0, vyDataLen = 0, ambientDataLen = 0;
2050
2051 for (x = blockX; x < blockX+blockW; x++)
2052 {
2053 for (y = blockY; y < blockY+blockH; y++)
2054 {
2055 wallData[(y-blockY)*blockW+(x-blockX)] = blockMap[y][x];
2056 if (blockMap[y][x])
2057 hasWallData = true;
2058
2059 if (hasPressure)
2060 {
2061 //save pressure and x/y velocity grids
2062 float pres = std::max(-255.0f,std::min(255.0f,pressure[y][x]))+256.0f;
2063 float velX = std::max(-255.0f,std::min(255.0f,velocityX[y][x]))+256.0f;
2064 float velY = std::max(-255.0f,std::min(255.0f,velocityY[y][x]))+256.0f;
2065 pressData[pressDataLen++] = (unsigned char)((int)(pres*128)&0xFF);
2066 pressData[pressDataLen++] = (unsigned char)((int)(pres*128)>>8);
2067
2068 vxData[vxDataLen++] = (unsigned char)((int)(velX*128)&0xFF);
2069 vxData[vxDataLen++] = (unsigned char)((int)(velX*128)>>8);
2070
2071 vyData[vyDataLen++] = (unsigned char)((int)(velY*128)&0xFF);
2072 vyData[vyDataLen++] = (unsigned char)((int)(velY*128)>>8);
2073 }
2074
2075 if (hasAmbientHeat)
2076 {
2077 int tempTemp = (int)(ambientHeat[y][x]+0.5f);
2078 ambientData[ambientDataLen++] = tempTemp;
2079 ambientData[ambientDataLen++] = tempTemp >> 8;
2080 }
2081
2082 if (blockMap[y][x] == WL_FAN)
2083 {
2084 i = (int)(fanVelX[y][x]*64.0f+127.5f);
2085 if (i<0) i=0;
2086 if (i>255) i=255;
2087 fanData[fanDataLen++] = i;
2088 i = (int)(fanVelY[y][x]*64.0f+127.5f);
2089 if (i<0) i=0;
2090 if (i>255) i=255;
2091 fanData[fanDataLen++] = i;
2092 }
2093 else if (blockMap[y][x] == WL_STASIS)
2094 {
2095 RESTRICTVERSION(94, 0);
2096 }
2097 }
2098 }
2099
2100 //Index positions of all particles, using linked lists
2101 //partsPosFirstMap is pmap for the first particle in each position
2102 //partsPosLastMap is pmap for the last particle in each position
2103 //partsPosCount is the number of particles in each position
2104 //partsPosLink contains, for each particle, (i<<8)|1 of the next particle in the same position
2105 auto partsPosFirstMap = std::unique_ptr<unsigned[]>(new unsigned[fullW*fullH]);
2106 auto partsPosLastMap = std::unique_ptr<unsigned[]>(new unsigned[fullW*fullH]);
2107 auto partsPosCount = std::unique_ptr<unsigned[]>(new unsigned[fullW*fullH]);
2108 auto partsPosLink = std::unique_ptr<unsigned[]>(new unsigned[NPART]);
2109 if (!partsPosFirstMap || !partsPosLastMap || !partsPosCount || !partsPosLink)
2110 throw BuildException("Save error, out of memory (partmaps)");
2111 std::fill(&partsPosFirstMap[0], &partsPosFirstMap[fullW*fullH], 0);
2112 std::fill(&partsPosLastMap[0], &partsPosLastMap[fullW*fullH], 0);
2113 std::fill(&partsPosCount[0], &partsPosCount[fullW*fullH], 0);
2114 std::fill(&partsPosLink[0], &partsPosLink[NPART], 0);
2115 unsigned int soapCount = 0;
2116 for(i = 0; i < particlesCount; i++)
2117 {
2118 if(particles[i].type)
2119 {
2120 x = (int)(particles[i].x+0.5f);
2121 y = (int)(particles[i].y+0.5f);
2122 //Coordinates relative to top left corner of saved area
2123 x -= fullX;
2124 y -= fullY;
2125 if (!partsPosFirstMap[y*fullW + x])
2126 {
2127 //First entry in list
2128 partsPosFirstMap[y*fullW + x] = (i<<8)|1;
2129 partsPosLastMap[y*fullW + x] = (i<<8)|1;
2130 }
2131 else
2132 {
2133 //Add to end of list
2134 partsPosLink[partsPosLastMap[y*fullW + x]>>8] = (i<<8)|1;//link to current end of list
2135 partsPosLastMap[y*fullW + x] = (i<<8)|1;//set as new end of list
2136 }
2137 partsPosCount[y*fullW + x]++;
2138 }
2139 }
2140
2141 //Store number of particles in each position
2142 auto partsPosData = std::unique_ptr<unsigned char[]>(new unsigned char[fullW*fullH*3]);
2143 unsigned int partsPosDataLen = 0;
2144 if (!partsPosData)
2145 throw BuildException("Save error, out of memory (partposdata)");
2146 for (y=0;y<fullH;y++)
2147 {
2148 for (x=0;x<fullW;x++)
2149 {
2150 unsigned int posCount = partsPosCount[y*fullW + x];
2151 partsPosData[partsPosDataLen++] = (posCount&0x00FF0000)>>16;
2152 partsPosData[partsPosDataLen++] = (posCount&0x0000FF00)>>8;
2153 partsPosData[partsPosDataLen++] = (posCount&0x000000FF);
2154 }
2155 }
2156
2157 //Copy parts data
2158 /* Field descriptor format:
2159 | 0 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
2160 | RESERVED | type[2] | pavg | tmp[3+4] | tmp2[2] | tmp2 | ctype[2] | vy | vx | decorations | ctype[1] | tmp[2] | tmp[1] | life[2] | life[1] | temp dbl len |
2161 life[2] means a second byte (for a 16 bit field) if life[1] is present
2162 last bit is reserved. If necessary, use it to signify that fieldDescriptor will have another byte
2163 That way, if we ever need a 17th bit, we won't have to change the save format
2164 */
2165 auto partsData = std::unique_ptr<unsigned char[]>(new unsigned char[NPART * (sizeof(Particle)+1)]);
2166 unsigned int partsDataLen = 0;
2167 auto partsSaveIndex = std::unique_ptr<unsigned[]>(new unsigned[NPART]);
2168 unsigned int partsCount = 0;
2169 if (!partsData || !partsSaveIndex)
2170 throw BuildException("Save error, out of memory (partsdata)");
2171 std::fill(&partsSaveIndex[0], &partsSaveIndex[NPART], 0);
2172 for (y=0;y<fullH;y++)
2173 {
2174 for (x=0;x<fullW;x++)
2175 {
2176 //Find the first particle in this position
2177 i = partsPosFirstMap[y*fullW + x];
2178
2179 //Loop while there is a pmap entry
2180 while (i)
2181 {
2182 unsigned short fieldDesc = 0;
2183 int fieldDescLoc = 0, tempTemp, vTemp;
2184
2185 //Turn pmap entry into a particles index
2186 i = i>>8;
2187
2188 //Store saved particle index+1 for this partsptr index (0 means not saved)
2189 partsSaveIndex[i] = (partsCount++) + 1;
2190
2191 //Type (required)
2192 partsData[partsDataLen++] = particles[i].type;
2193
2194 //Location of the field descriptor
2195 fieldDescLoc = partsDataLen++;
2196 partsDataLen++;
2197
2198 // Extra type byte if necessary
2199 if (particles[i].type & 0xFF00)
2200 {
2201 partsData[partsDataLen++] = particles[i].type >> 8;
2202 fieldDesc |= 1 << 14;
2203 RESTRICTVERSION(93, 0);
2204 RESTRICTRENDERVERSION(93, 0);
2205 }
2206
2207 //Extra Temperature (2nd byte optional, 1st required), 1 to 2 bytes
2208 //Store temperature as an offset of 21C(294.15K) or go into a 16byte int and store the whole thing
2209 if(fabs(particles[i].temp-294.15f)<127)
2210 {
2211 tempTemp = floor(particles[i].temp-294.15f+0.5f);
2212 partsData[partsDataLen++] = tempTemp;
2213 }
2214 else
2215 {
2216 fieldDesc |= 1;
2217 tempTemp = (int)(particles[i].temp+0.5f);
2218 partsData[partsDataLen++] = tempTemp;
2219 partsData[partsDataLen++] = tempTemp >> 8;
2220 }
2221
2222 //Life (optional), 1 to 2 bytes
2223 if(particles[i].life)
2224 {
2225 int life = particles[i].life;
2226 if (life > 0xFFFF)
2227 life = 0xFFFF;
2228 else if (life < 0)
2229 life = 0;
2230 fieldDesc |= 1 << 1;
2231 partsData[partsDataLen++] = life;
2232 if (life & 0xFF00)
2233 {
2234 fieldDesc |= 1 << 2;
2235 partsData[partsDataLen++] = life >> 8;
2236 }
2237 }
2238
2239 //Tmp (optional), 1, 2, or 4 bytes
2240 if(particles[i].tmp)
2241 {
2242 fieldDesc |= 1 << 3;
2243 partsData[partsDataLen++] = particles[i].tmp;
2244 if(particles[i].tmp & 0xFFFFFF00)
2245 {
2246 fieldDesc |= 1 << 4;
2247 partsData[partsDataLen++] = particles[i].tmp >> 8;
2248 if(particles[i].tmp & 0xFFFF0000)
2249 {
2250 fieldDesc |= 1 << 12;
2251 partsData[partsDataLen++] = (particles[i].tmp&0xFF000000)>>24;
2252 partsData[partsDataLen++] = (particles[i].tmp&0x00FF0000)>>16;
2253 }
2254 }
2255 }
2256
2257 //Ctype (optional), 1 or 4 bytes
2258 if(particles[i].ctype)
2259 {
2260 fieldDesc |= 1 << 5;
2261 partsData[partsDataLen++] = particles[i].ctype;
2262 if(particles[i].ctype & 0xFFFFFF00)
2263 {
2264 fieldDesc |= 1 << 9;
2265 partsData[partsDataLen++] = (particles[i].ctype&0xFF000000)>>24;
2266 partsData[partsDataLen++] = (particles[i].ctype&0x00FF0000)>>16;
2267 partsData[partsDataLen++] = (particles[i].ctype&0x0000FF00)>>8;
2268 }
2269 }
2270
2271 //Dcolour (optional), 4 bytes
2272 if(particles[i].dcolour && (particles[i].dcolour & 0xFF000000))
2273 {
2274 fieldDesc |= 1 << 6;
2275 partsData[partsDataLen++] = (particles[i].dcolour&0xFF000000)>>24;
2276 partsData[partsDataLen++] = (particles[i].dcolour&0x00FF0000)>>16;
2277 partsData[partsDataLen++] = (particles[i].dcolour&0x0000FF00)>>8;
2278 partsData[partsDataLen++] = (particles[i].dcolour&0x000000FF);
2279 }
2280
2281 //VX (optional), 1 byte
2282 if(fabs(particles[i].vx) > 0.001f)
2283 {
2284 fieldDesc |= 1 << 7;
2285 vTemp = (int)(particles[i].vx*16.0f+127.5f);
2286 if (vTemp<0) vTemp=0;
2287 if (vTemp>255) vTemp=255;
2288 partsData[partsDataLen++] = vTemp;
2289 }
2290
2291 //VY (optional), 1 byte
2292 if(fabs(particles[i].vy) > 0.001f)
2293 {
2294 fieldDesc |= 1 << 8;
2295 vTemp = (int)(particles[i].vy*16.0f+127.5f);
2296 if (vTemp<0) vTemp=0;
2297 if (vTemp>255) vTemp=255;
2298 partsData[partsDataLen++] = vTemp;
2299 }
2300
2301 //Tmp2 (optional), 1 or 2 bytes
2302 if(particles[i].tmp2)
2303 {
2304 fieldDesc |= 1 << 10;
2305 partsData[partsDataLen++] = particles[i].tmp2;
2306 if(particles[i].tmp2 & 0xFF00)
2307 {
2308 fieldDesc |= 1 << 11;
2309 partsData[partsDataLen++] = particles[i].tmp2 >> 8;
2310 }
2311 }
2312
2313 //Pavg, 4 bytes
2314 // save pavg if there's useful pavg to save
2315 // and either we save pressure data too
2316 // or the current particle is not one that cares about pressure
2317 if (particles[i].pavg[0] || particles[i].pavg[1])
2318 {
2319 float pavg0 = particles[i].pavg[0];
2320 float pavg1 = particles[i].pavg[1];
2321 switch (particles[i].type)
2322 {
2323 // List of elements that save pavg with a multiplicative bias of 2**6
2324 // (or not at all if pressure is not saved).
2325 // If you change this list, change it in Simulation::Load and GameSave::readOPS too!
2326 case PT_QRTZ:
2327 case PT_GLAS:
2328 case PT_TUNG:
2329 if (!hasPressure)
2330 break;
2331 pavg0 *= 64;
2332 pavg1 *= 64;
2333 // fallthrough!
2334
2335 default:
2336 fieldDesc |= 1 << 13;
2337 partsData[partsDataLen++] = (int)pavg0;
2338 partsData[partsDataLen++] = ((int)pavg0)>>8;
2339 partsData[partsDataLen++] = (int)pavg1;
2340 partsData[partsDataLen++] = ((int)pavg1)>>8;
2341 break;
2342 }
2343 }
2344
2345 //Write the field descriptor
2346 partsData[fieldDescLoc] = fieldDesc;
2347 partsData[fieldDescLoc+1] = fieldDesc>>8;
2348
2349 if (particles[i].type == PT_SOAP)
2350 soapCount++;
2351
2352 if (particles[i].type == PT_RPEL && particles[i].ctype)
2353 {
2354 RESTRICTVERSION(91, 4);
2355 }
2356 else if (particles[i].type == PT_NWHL && particles[i].tmp)
2357 {
2358 RESTRICTVERSION(91, 5);
2359 }
2360 if (particles[i].type == PT_HEAC || particles[i].type == PT_SAWD || particles[i].type == PT_POLO
2361 || particles[i].type == PT_RFRG || particles[i].type == PT_RFGL || particles[i].type == PT_LSNS)
2362 {
2363 RESTRICTVERSION(92, 0);
2364 }
2365 else if ((particles[i].type == PT_FRAY || particles[i].type == PT_INVIS) && particles[i].tmp)
2366 {
2367 RESTRICTVERSION(92, 0);
2368 }
2369 else if (particles[i].type == PT_PIPE || particles[i].type == PT_PPIP)
2370 {
2371 RESTRICTVERSION(93, 0);
2372 }
2373 if (particles[i].type == PT_TSNS || particles[i].type == PT_PSNS
2374 || particles[i].type == PT_HSWC || particles[i].type == PT_PUMP)
2375 {
2376 if (particles[i].tmp == 1)
2377 {
2378 RESTRICTVERSION(93, 0);
2379 }
2380 }
2381 if (PMAPBITS > 8)
2382 {
2383 if (TypeInCtype(particles[i].type, particles[i].ctype) && particles[i].ctype > 0xFF)
2384 {
2385 RESTRICTVERSION(93, 0);
2386 }
2387 else if (TypeInTmp(particles[i].type) && particles[i].tmp > 0xFF)
2388 {
2389 RESTRICTVERSION(93, 0);
2390 }
2391 else if (TypeInTmp2(particles[i].type, particles[i].tmp2) && particles[i].tmp2 > 0xFF)
2392 {
2393 RESTRICTVERSION(93, 0);
2394 }
2395 }
2396 if (particles[i].type == PT_LDTC)
2397 {
2398 RESTRICTVERSION(94, 0);
2399 }
2400 if (particles[i].type == PT_TSNS || particles[i].type == PT_PSNS)
2401 {
2402 if (particles[i].tmp == 2)
2403 {
2404 RESTRICTVERSION(94, 0);
2405 }
2406 }
2407 if (particles[i].type == PT_LSNS)
2408 {
2409 if (particles[i].tmp >= 1 || particles[i].tmp <= 3)
2410 {
2411 RESTRICTVERSION(95, 0);
2412 }
2413 }
2414
2415 //Get the pmap entry for the next particle in the same position
2416 i = partsPosLink[i];
2417 }
2418 }
2419 }
2420
2421 unsigned char *soapLinkData = NULL;
2422 auto soapLinkDataPtr = std::unique_ptr<unsigned char[]>();
2423 unsigned int soapLinkDataLen = 0;
2424 if (soapCount)
2425 {
2426 soapLinkData = new unsigned char[3*soapCount];
2427 if (!soapLinkData)
2428 throw BuildException("Save error, out of memory (SOAP)");
2429 soapLinkDataPtr = std::unique_ptr<unsigned char[]>(soapLinkData);
2430
2431 //Iterate through particles in the same order that they were saved
2432 for (y=0;y<fullH;y++)
2433 {
2434 for (x=0;x<fullW;x++)
2435 {
2436 //Find the first particle in this position
2437 i = partsPosFirstMap[y*fullW + x];
2438
2439 //Loop while there is a pmap entry
2440 while (i)
2441 {
2442 //Turn pmap entry into a partsptr index
2443 i = i>>8;
2444
2445 if (particles[i].type==PT_SOAP)
2446 {
2447 //Only save forward link for each particle, back links can be deduced from other forward links
2448 //linkedIndex is index within saved particles + 1, 0 means not saved or no link
2449
2450 unsigned linkedIndex = 0;
2451 if ((particles[i].ctype&2) && particles[i].tmp>=0 && particles[i].tmp<NPART)
2452 {
2453 linkedIndex = partsSaveIndex[particles[i].tmp];
2454 }
2455 soapLinkData[soapLinkDataLen++] = (linkedIndex&0xFF0000)>>16;
2456 soapLinkData[soapLinkDataLen++] = (linkedIndex&0x00FF00)>>8;
2457 soapLinkData[soapLinkDataLen++] = (linkedIndex&0x0000FF);
2458 }
2459
2460 //Get the pmap entry for the next particle in the same position
2461 i = partsPosLink[i];
2462 }
2463 }
2464 }
2465 }
2466
2467 for (size_t i = 0; i < signs.size(); i++)
2468 {
2469 if(signs[i].text.length() && signs[i].x>=0 && signs[i].x<=fullW && signs[i].y>=0 && signs[i].y<=fullH)
2470 {
2471 int x, y, w, h;
2472 bool v95 = false;
2473 signs[i].getDisplayText(nullptr, x, y, w, h, true, &v95);
2474 if (v95)
2475 {
2476 RESTRICTVERSION(95, 0);
2477 }
2478 }
2479 }
2480
2481 bson b;
2482 b.data = NULL;
2483 auto bson_deleter = [](bson * b) { bson_destroy(b); };
2484 // Use unique_ptr with a custom deleter to ensure that bson_destroy is called even when an exception is thrown
2485 std::unique_ptr<bson, decltype(bson_deleter)> b_ptr(&b, bson_deleter);
2486
2487 set_bson_err_handler([](const char* err) { throw BuildException("BSON error when parsing save: " + ByteString(err).FromUtf8()); });
2488 bson_init(&b);
2489 bson_append_start_object(&b, "origin");
2490 bson_append_int(&b, "majorVersion", SAVE_VERSION);
2491 bson_append_int(&b, "minorVersion", MINOR_VERSION);
2492 bson_append_int(&b, "buildNum", BUILD_NUM);
2493 bson_append_int(&b, "snapshotId", SNAPSHOT_ID);
2494 bson_append_int(&b, "modId", MOD_ID);
2495 bson_append_string(&b, "releaseType", IDENT_RELTYPE);
2496 bson_append_string(&b, "platform", IDENT_PLATFORM);
2497 bson_append_string(&b, "builtType", IDENT_BUILD);
2498 bson_append_finish_object(&b);
2499 bson_append_start_object(&b, "minimumVersion");
2500 bson_append_int(&b, "major", minimumMajorVersion);
2501 bson_append_int(&b, "minor", minimumMinorVersion);
2502 bson_append_int(&b, "rendermajor", blameSimon_major);
2503 bson_append_int(&b, "renderminor", blameSimon_minor);
2504 bson_append_finish_object(&b);
2505
2506
2507 bson_append_bool(&b, "waterEEnabled", waterEEnabled);
2508 bson_append_bool(&b, "legacyEnable", legacyEnable);
2509 bson_append_bool(&b, "gravityEnable", gravityEnable);
2510 bson_append_bool(&b, "aheat_enable", aheatEnable);
2511 bson_append_bool(&b, "paused", paused);
2512 bson_append_int(&b, "gravityMode", gravityMode);
2513 bson_append_int(&b, "airMode", airMode);
2514 bson_append_int(&b, "edgeMode", edgeMode);
2515
2516 if (stkm.hasData())
2517 {
2518 bson_append_start_object(&b, "stkm");
2519 if (stkm.rocketBoots1)
2520 bson_append_bool(&b, "rocketBoots1", stkm.rocketBoots1);
2521 if (stkm.rocketBoots2)
2522 bson_append_bool(&b, "rocketBoots2", stkm.rocketBoots2);
2523 if (stkm.fan1)
2524 bson_append_bool(&b, "fan1", stkm.fan1);
2525 if (stkm.fan2)
2526 bson_append_bool(&b, "fan2", stkm.fan2);
2527 if (stkm.rocketBootsFigh.size())
2528 {
2529 bson_append_start_array(&b, "rocketBootsFigh");
2530 for (unsigned int fighNum : stkm.rocketBootsFigh)
2531 bson_append_int(&b, "num", fighNum);
2532 bson_append_finish_array(&b);
2533 }
2534 if (stkm.fanFigh.size())
2535 {
2536 bson_append_start_array(&b, "fanFigh");
2537 for (unsigned int fighNum : stkm.fanFigh)
2538 bson_append_int(&b, "num", fighNum);
2539 bson_append_finish_array(&b);
2540 }
2541 bson_append_finish_object(&b);
2542 }
2543
2544 bson_append_int(&b, "pmapbits", pmapbits);
2545 if (partsData && partsDataLen)
2546 {
2547 bson_append_binary(&b, "parts", BSON_BIN_USER, (const char *)partsData.get(), partsDataLen);
2548
2549 if (palette.size())
2550 {
2551 bson_append_start_array(&b, "palette");
2552 for(std::vector<PaletteItem>::iterator iter = palette.begin(), end = palette.end(); iter != end; ++iter)
2553 {
2554 bson_append_int(&b, (*iter).first.c_str(), (*iter).second);
2555 }
2556 bson_append_finish_array(&b);
2557 }
2558
2559 if (partsPosData && partsPosDataLen)
2560 bson_append_binary(&b, "partsPos", BSON_BIN_USER, (const char *)partsPosData.get(), partsPosDataLen);
2561 }
2562 if (wallData && hasWallData)
2563 bson_append_binary(&b, "wallMap", BSON_BIN_USER, (const char *)wallData.get(), wallDataLen);
2564 if (fanData && fanDataLen)
2565 bson_append_binary(&b, "fanMap", BSON_BIN_USER, (const char *)fanData.get(), fanDataLen);
2566 if (pressData && hasPressure && pressDataLen)
2567 bson_append_binary(&b, "pressMap", (char)BSON_BIN_USER, (const char*)pressData.get(), pressDataLen);
2568 if (vxData && hasPressure && vxDataLen)
2569 bson_append_binary(&b, "vxMap", (char)BSON_BIN_USER, (const char*)vxData.get(), vxDataLen);
2570 if (vyData && hasPressure && vyDataLen)
2571 bson_append_binary(&b, "vyMap", (char)BSON_BIN_USER, (const char*)vyData.get(), vyDataLen);
2572 if (ambientData && hasAmbientHeat && this->aheatEnable && ambientDataLen)
2573 bson_append_binary(&b, "ambientMap", (char)BSON_BIN_USER, (const char*)ambientData.get(), ambientDataLen);
2574 if (soapLinkData && soapLinkDataLen)
2575 bson_append_binary(&b, "soapLinks", BSON_BIN_USER, (const char *)soapLinkData, soapLinkDataLen);
2576 unsigned int signsCount = 0;
2577 for (size_t i = 0; i < signs.size(); i++)
2578 {
2579 if(signs[i].text.length() && signs[i].x>=0 && signs[i].x<=fullW && signs[i].y>=0 && signs[i].y<=fullH)
2580 {
2581 signsCount++;
2582 }
2583 }
2584 if (signsCount)
2585 {
2586 bson_append_start_array(&b, "signs");
2587 for (size_t i = 0; i < signs.size(); i++)
2588 {
2589 if(signs[i].text.length() && signs[i].x>=0 && signs[i].x<=fullW && signs[i].y>=0 && signs[i].y<=fullH)
2590 {
2591 bson_append_start_object(&b, "sign");
2592 bson_append_string(&b, "text", signs[i].text.ToUtf8().c_str());
2593 bson_append_int(&b, "justification", signs[i].ju);
2594 bson_append_int(&b, "x", signs[i].x);
2595 bson_append_int(&b, "y", signs[i].y);
2596 bson_append_finish_object(&b);
2597 }
2598 }
2599 bson_append_finish_array(&b);
2600 }
2601 if (authors.size())
2602 {
2603 bson_append_start_object(&b, "authors");
2604 ConvertJsonToBson(&b, authors);
2605 bson_append_finish_object(&b);
2606 }
2607 if (bson_finish(&b) == BSON_ERROR)
2608 throw BuildException("Error building bson data");
2609
2610 unsigned char *finalData = (unsigned char*)bson_data(&b);
2611 unsigned int finalDataLen = bson_size(&b);
2612 auto outputData = std::unique_ptr<unsigned char[]>(new unsigned char[finalDataLen*2+12]);
2613 if (!outputData)
2614 throw BuildException(String::Build("Save error, out of memory (finalData): ", finalDataLen*2+12));
2615
2616 outputData[0] = 'O';
2617 outputData[1] = 'P';
2618 outputData[2] = 'S';
2619 outputData[3] = '1';
2620 outputData[4] = SAVE_VERSION;
2621 outputData[5] = CELL;
2622 outputData[6] = blockW;
2623 outputData[7] = blockH;
2624 outputData[8] = finalDataLen;
2625 outputData[9] = finalDataLen >> 8;
2626 outputData[10] = finalDataLen >> 16;
2627 outputData[11] = finalDataLen >> 24;
2628
2629 unsigned int compressedSize = finalDataLen*2, bz2ret;
2630 if ((bz2ret = BZ2_bzBuffToBuffCompress((char*)(outputData.get()+12), &compressedSize, (char*)finalData, bson_size(&b), 9, 0, 0)) != BZ_OK)
2631 {
2632 throw BuildException(String::Build("Save error, could not compress (ret ", bz2ret, ")"));
2633 }
2634
2635 #ifdef DEBUG
2636 printf("compressed data: %d\n", compressedSize);
2637 #endif
2638 dataLength = compressedSize + 12;
2639
2640 char *saveData = new char[dataLength];
2641 std::copy(&outputData[0], &outputData[dataLength], &saveData[0]);
2642 return saveData;
2643 }
2644
ConvertBsonToJson(bson_iterator * iter,Json::Value * j,int depth)2645 void GameSave::ConvertBsonToJson(bson_iterator *iter, Json::Value *j, int depth)
2646 {
2647 bson_iterator subiter;
2648 bson_iterator_subiterator(iter, &subiter);
2649 while (bson_iterator_next(&subiter))
2650 {
2651 ByteString key = bson_iterator_key(&subiter);
2652 if (bson_iterator_type(&subiter) == BSON_STRING)
2653 (*j)[key] = bson_iterator_string(&subiter);
2654 else if (bson_iterator_type(&subiter) == BSON_BOOL)
2655 (*j)[key] = bson_iterator_bool(&subiter);
2656 else if (bson_iterator_type(&subiter) == BSON_INT)
2657 (*j)[key] = bson_iterator_int(&subiter);
2658 else if (bson_iterator_type(&subiter) == BSON_LONG)
2659 (*j)[key] = (Json::Value::UInt64)bson_iterator_long(&subiter);
2660 else if (bson_iterator_type(&subiter) == BSON_ARRAY && depth < 5)
2661 {
2662 bson_iterator arrayiter;
2663 bson_iterator_subiterator(&subiter, &arrayiter);
2664 int length = 0, length2 = 0;
2665 while (bson_iterator_next(&arrayiter))
2666 {
2667 if (bson_iterator_type(&arrayiter) == BSON_OBJECT && !strcmp(bson_iterator_key(&arrayiter), "part"))
2668 {
2669 Json::Value tempPart;
2670 ConvertBsonToJson(&arrayiter, &tempPart, depth + 1);
2671 (*j)["links"].append(tempPart);
2672 length++;
2673 }
2674 else if (bson_iterator_type(&arrayiter) == BSON_INT && !strcmp(bson_iterator_key(&arrayiter), "saveID"))
2675 {
2676 (*j)["links"].append(bson_iterator_int(&arrayiter));
2677 }
2678 length2++;
2679 if (length > (int)(40 / ((depth+1) * (depth+1))) || length2 > 50)
2680 break;
2681 }
2682 }
2683 }
2684 }
2685
GetNestedSaveIDs(Json::Value j)2686 std::set<int> GetNestedSaveIDs(Json::Value j)
2687 {
2688 Json::Value::Members members = j.getMemberNames();
2689 std::set<int> saveIDs = std::set<int>();
2690 for (Json::Value::Members::iterator iter = members.begin(), end = members.end(); iter != end; ++iter)
2691 {
2692 ByteString member = *iter;
2693 if (member == "id" && j[member].isInt())
2694 saveIDs.insert(j[member].asInt());
2695 else if (j[member].isArray())
2696 {
2697 for (Json::Value::ArrayIndex i = 0; i < j[member].size(); i++)
2698 {
2699 // only supports objects and ints here because that is all we need
2700 if (j[member][i].isInt())
2701 {
2702 saveIDs.insert(j[member][i].asInt());
2703 continue;
2704 }
2705 if (!j[member][i].isObject())
2706 continue;
2707 std::set<int> nestedSaveIDs = GetNestedSaveIDs(j[member][i]);
2708 saveIDs.insert(nestedSaveIDs.begin(), nestedSaveIDs.end());
2709 }
2710 }
2711 }
2712 return saveIDs;
2713 }
2714
2715 // converts a json object to bson
ConvertJsonToBson(bson * b,Json::Value j,int depth)2716 void GameSave::ConvertJsonToBson(bson *b, Json::Value j, int depth)
2717 {
2718 Json::Value::Members members = j.getMemberNames();
2719 for (Json::Value::Members::iterator iter = members.begin(), end = members.end(); iter != end; ++iter)
2720 {
2721 ByteString member = *iter;
2722 if (j[member].isString())
2723 bson_append_string(b, member.c_str(), j[member].asCString());
2724 else if (j[member].isBool())
2725 bson_append_bool(b, member.c_str(), j[member].asBool());
2726 else if (j[member].type() == Json::intValue)
2727 bson_append_int(b, member.c_str(), j[member].asInt());
2728 else if (j[member].type() == Json::uintValue)
2729 bson_append_long(b, member.c_str(), j[member].asInt64());
2730 else if (j[member].isArray())
2731 {
2732 bson_append_start_array(b, member.c_str());
2733 std::set<int> saveIDs = std::set<int>();
2734 int length = 0;
2735 for (Json::Value::ArrayIndex i = 0; i < j[member].size(); i++)
2736 {
2737 // only supports objects and ints here because that is all we need
2738 if (j[member][i].isInt())
2739 {
2740 saveIDs.insert(j[member][i].asInt());
2741 continue;
2742 }
2743 if (!j[member][i].isObject())
2744 continue;
2745 if (depth > 4 || length > (int)(40 / ((depth+1) * (depth+1))))
2746 {
2747 std::set<int> nestedSaveIDs = GetNestedSaveIDs(j[member][i]);
2748 saveIDs.insert(nestedSaveIDs.begin(), nestedSaveIDs.end());
2749 }
2750 else
2751 {
2752 bson_append_start_object(b, "part");
2753 ConvertJsonToBson(b, j[member][i], depth+1);
2754 bson_append_finish_object(b);
2755 }
2756 length++;
2757 }
2758 for (std::set<int>::iterator iter = saveIDs.begin(), end = saveIDs.end(); iter != end; ++iter)
2759 {
2760 bson_append_int(b, "saveID", *iter);
2761 }
2762 bson_append_finish_array(b);
2763 }
2764 }
2765 }
2766
2767 // deallocates a pointer to a 2D array and sets it to NULL
2768 template <typename T>
Deallocate2DArray(T *** array,int blockHeight)2769 void GameSave::Deallocate2DArray(T ***array, int blockHeight)
2770 {
2771 if (*array)
2772 {
2773 for (int y = 0; y < blockHeight; y++)
2774 delete[] (*array)[y];
2775 delete[] (*array);
2776 *array = NULL;
2777 }
2778 }
2779
TypeInCtype(int type,int ctype)2780 bool GameSave::TypeInCtype(int type, int ctype)
2781 {
2782 return ctype >= 0 && ctype < PT_NUM &&
2783 (type == PT_CLNE || type == PT_PCLN || type == PT_BCLN || type == PT_PBCN ||
2784 type == PT_STOR || type == PT_CONV || type == PT_STKM || type == PT_STKM2 ||
2785 type == PT_FIGH || type == PT_LAVA || type == PT_SPRK || type == PT_PSTN ||
2786 type == PT_CRAY || type == PT_DTEC || type == PT_DRAY || type == PT_PIPE ||
2787 type == PT_PPIP || type == PT_LDTC);
2788 }
2789
TypeInTmp(int type)2790 bool GameSave::TypeInTmp(int type)
2791 {
2792 return type == PT_STOR;
2793 }
2794
TypeInTmp2(int type,int tmp2)2795 bool GameSave::TypeInTmp2(int type, int tmp2)
2796 {
2797 return (type == PT_VIRS || type == PT_VRSG || type == PT_VRSS) && (tmp2 >= 0 && tmp2 < PT_NUM);
2798 }
2799
dealloc()2800 void GameSave::dealloc()
2801 {
2802 if (particles)
2803 {
2804 delete[] particles;
2805 particles = NULL;
2806 }
2807 Deallocate2DArray<unsigned char>(&blockMap, blockHeight);
2808 Deallocate2DArray<float>(&fanVelX, blockHeight);
2809 Deallocate2DArray<float>(&fanVelY, blockHeight);
2810 Deallocate2DArray<float>(&pressure, blockHeight);
2811 Deallocate2DArray<float>(&velocityX, blockHeight);
2812 Deallocate2DArray<float>(&velocityY, blockHeight);
2813 Deallocate2DArray<float>(&ambientHeat, blockHeight);
2814 }
2815
~GameSave()2816 GameSave::~GameSave()
2817 {
2818 dealloc();
2819 }
2820
operator <<(Particle & v)2821 GameSave& GameSave::operator << (Particle &v)
2822 {
2823 if(particlesCount<NPART && v.type)
2824 {
2825 particles[particlesCount++] = v;
2826 }
2827 return *this;
2828 }
2829
operator <<(sign & v)2830 GameSave& GameSave::operator << (sign &v)
2831 {
2832 if(signs.size()<MAXSIGNS && v.text.length())
2833 signs.push_back(v);
2834 return *this;
2835 }
2836