diff options
| author | Simon Robertshaw <simon@hardwired.org.uk> | 2011-12-30 02:06:31 (GMT) |
|---|---|---|
| committer | Simon Robertshaw <simon@hardwired.org.uk> | 2011-12-30 02:06:31 (GMT) |
| commit | 86369670aaa11b8cd2ddcbd46413fb9482e4e58f (patch) | |
| tree | 07be282e1e08003153ee5c000f3e50336cfa2eb0 /src/save.c | |
| parent | ad2b000a8af84f299e7c7c43a6a272a60ffcd36d (diff) | |
| download | powder-86369670aaa11b8cd2ddcbd46413fb9482e4e58f.zip powder-86369670aaa11b8cd2ddcbd46413fb9482e4e58f.tar.gz | |
Add checks for prerender_save in save_filename_ui, pass vx, vy and pv air data into parse and build save methods. New save format: OPS, basic reading and writing complete, uses BSON, BSON.c/h (Binary JSON) implementation from MongoDB. (Is License OK?)
Diffstat (limited to 'src/save.c')
| -rw-r--r-- | src/save.c | 1266 |
1 files changed, 1266 insertions, 0 deletions
diff --git a/src/save.c b/src/save.c new file mode 100644 index 0000000..6882979 --- /dev/null +++ b/src/save.c @@ -0,0 +1,1266 @@ +#include <bzlib.h> +#include "defines.h" +#include "powder.h" +#include "save.h" +#include "gravity.h" +#include "BSON.h" + +void *build_save(int *size, int orig_x0, int orig_y0, int orig_w, int orig_h, unsigned char bmap[YRES/CELL][XRES/CELL], float vx[YRES/CELL][XRES/CELL], float vy[YRES/CELL][XRES/CELL], float pv[YRES/CELL][XRES/CELL], float fvx[YRES/CELL][XRES/CELL], float fvy[YRES/CELL][XRES/CELL], sign signs[MAXSIGNS], void* partsptr) +{ + return build_save_OPS(size, orig_x0, orig_y0, orig_w, orig_h, bmap, vx, vy, pv, fvx, fvy, signs, partsptr); + //return build_save_PSv(size, orig_x0, orig_y0, orig_w, orig_h, bmap, fvx, fvy, signs, partsptr); +} + +int parse_save(void *save, int size, int replace, int x0, int y0, unsigned char bmap[YRES/CELL][XRES/CELL], float vx[YRES/CELL][XRES/CELL], float vy[YRES/CELL][XRES/CELL], float pv[YRES/CELL][XRES/CELL], float fvx[YRES/CELL][XRES/CELL], float fvy[YRES/CELL][XRES/CELL], sign signs[MAXSIGNS], void* partsptr, unsigned pmap[YRES][XRES]) +{ + unsigned char * saveData = save; + if(saveData[0] == 'O' && saveData[1] == 'P' && saveData[2] == 'S') + { + return parse_save_OPS(save, size, replace, x0, y0, bmap, vx, vy, pv, fvx, fvy, signs, partsptr, pmap); + } + else if((saveData[0]==0x66 && saveData[1]==0x75 && saveData[2]==0x43) || (saveData[0]==0x50 && saveData[1]==0x53 && saveData[2]==0x76)) + { + return parse_save_PSv(save, size, replace, x0, y0, bmap, fvx, fvy, signs, partsptr, pmap); + } +} + +void *build_save_OPS(int *size, int orig_x0, int orig_y0, int orig_w, int orig_h, unsigned char bmap[YRES/CELL][XRES/CELL], float vx[YRES/CELL][XRES/CELL], float vy[YRES/CELL][XRES/CELL], float pv[YRES/CELL][XRES/CELL], float fvx[YRES/CELL][XRES/CELL], float fvy[YRES/CELL][XRES/CELL], sign signs[MAXSIGNS], void* o_partsptr) +{ + particle *partsptr = o_partsptr; + unsigned char *partsData = NULL, *fanData = NULL, *wallData = NULL, *finalData = NULL, *outputData = NULL; + int partsDataLen, fanDataLen, wallDataLen, finalDataLen, outputDataLen; + int blockX, blockY, blockW, blockH, fullX, fullY, fullW, fullH; + int x, y, i, wallDataFound = 0; + + //Get coords in blocks + blockX = orig_x0/CELL; + blockY = orig_y0/CELL; + blockW = orig_w/CELL; + blockH = orig_h/CELL; + + //Snap full coords to block size + fullX = blockX*CELL; + fullY = blockY*CELL; + fullW = blockW*CELL; + fullH = blockH*CELL; + + //Copy fan and wall data + wallData = malloc(blockW*blockH); + wallDataLen = blockW*blockH; + fanData = malloc(blockW*blockH); + fanDataLen = 0; + for(x = blockX; x < blockX+blockW; x++) + { + for(y = blockY; y < blockY+blockH; y++) + { + wallData[y*blockW+x] = bmap[y][x]; + if(bmap[y][x] && !wallDataFound) + wallDataFound = 1; + if(bmap[y][x]==WL_FAN || bmap[y][x]==4) + { + i = (int)(fvy[y][x]*64.0f+127.5f); + if (i<0) i=0; + if (i>255) i=255; + fanData[fanDataLen++] = i; + } + } + } + if(!fanDataLen) + { + free(fanData); + fanData = NULL; + } + if(!wallDataFound) + { + free(wallData); + wallData = NULL; + } + + //Copy parts data + /* Field descriptor format: + | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | + | vy | vx | dcololour | ctype | tmp[2] | tmp[1] | life[2] | life[1] | + life[2] means a second byte (for a 16 bit field) if life[1] is present + */ + partsData = malloc(NPART * (sizeof(particle)+1)); + partsDataLen = 0; + for(i = 0; i < NPART; i++) + { + if(parts[i].type) + { + x = (int)(parts[i].x+0.5f); + y = (int)(parts[i].y+0.5f); + if(x >= fullX && x <= fullX+fullW && y >= fullY && y <= fullY+fullH) + { + unsigned char fieldDesc = 0; + int fieldDescLoc = 0, tempTemp, vTemp; + + //Type (required) + partsData[partsDataLen++] = partsptr[i].type; + + //X and Y coord (required), 2 bytes each + partsData[partsDataLen++] = x; + partsData[partsDataLen++] = x >> 8; + printf("Saved: %d, %d", (char)x, (char)(x>>8)); + partsData[partsDataLen++] = y; + partsData[partsDataLen++] = y >> 8; + + //Temperature (required), 2 bytes + tempTemp = partsptr[i].temp; + partsData[partsDataLen++] = tempTemp; + partsData[partsDataLen++] = tempTemp >> 8; + + //Location of the field descriptor + fieldDescLoc = partsDataLen++; + + //Life (optional), 1 to 2 bytes + if(partsptr[i].life) + { + fieldDesc |= 1; + partsData[partsDataLen++] = partsptr[i].life; + if(partsptr[i].life > 255) + { + fieldDesc |= 1 << 1; + partsData[partsDataLen++] = partsptr[i].life >> 8; + } + } + + //Tmp (optional), 1 to 2 bytes + if(partsptr[i].tmp) + { + fieldDesc |= 1 << 2; + partsData[partsDataLen++] = partsptr[i].tmp; + if(partsptr[i].tmp > 255) + { + fieldDesc |= 1 << 3; + partsData[partsDataLen++] = partsptr[i].tmp >> 8; + } + } + + //Ctype (optional), 1 byte + if(partsptr[i].ctype) + { + fieldDesc |= 1 << 4; + partsData[partsDataLen++] = partsptr[i].ctype; + } + + //Dcolour (optional), 4 bytes + if(partsptr[i].dcolour && (partsptr[i].dcolour & 0xFF000000)) + { + fieldDesc |= 1 << 5; + partsData[partsDataLen++] = (partsptr[i].dcolour&0xFF000000)>>24; + partsData[partsDataLen++] = (partsptr[i].dcolour&0x00FF0000)>>16; + partsData[partsDataLen++] = (partsptr[i].dcolour&0x0000FF00)>>8; + partsData[partsDataLen++] = (partsptr[i].dcolour&0x000000FF); + } + + //VX (optional), 1 byte + if(fabs(partsptr[i].vx) > 0.001f) + { + fieldDesc |= 1 << 6; + vTemp = (int)(partsptr[i].vx*16.0f+127.5f); + if (vTemp<0) vTemp=0; + if (vTemp>255) vTemp=255; + partsData[partsDataLen++] = vTemp; + } + + //VY (optional), 1 byte + if(fabs(partsptr[i].vy) > 0.001f) + { + fieldDesc |= 1 << 7; + vTemp = (int)(partsptr[i].vy*16.0f+127.5f); + if (vTemp<0) vTemp=0; + if (vTemp>255) vTemp=255; + partsData[partsDataLen++] = vTemp; + } + + //Write the field descriptor; + partsData[fieldDescLoc] = fieldDesc; + } + } + } + + bson b; + bson_init(&b); + /* These fields are in the "outer" header, don't bother saving here + bson_append_int(&b, "majorVersion", SAVE_VERSION); + bson_append_int(&b, "xRes", fullW); + bson_append_int(&b, "yRes", fullH); + bson_append_int(&b, "cellSize", CELL);*/ + //Save stuff like gravity, heat, blah states + bson_append_int(&b, "partsDataBytes", partsDataLen); //For debugging, remove eventually + if(partsData) + bson_append_binary(&b, "parts", BSON_BIN_USER, partsData, partsDataLen); + if(wallData) + bson_append_binary(&b, "wallMap", BSON_BIN_USER, wallData, wallDataLen); + if(fanData) + bson_append_binary(&b, "fanMap", BSON_BIN_USER, fanData, fanDataLen); + bson_finish(&b); + bson_print(&b); + + finalData = bson_data(&b); + finalDataLen = bson_size(&b); + outputDataLen = finalDataLen*2+12; + outputData = malloc(outputDataLen); + + outputData[0] = 'O'; + outputData[1] = 'P'; + outputData[2] = 'S'; + outputData[3] = '1'; + outputData[4] = SAVE_VERSION; + outputData[5] = CELL; + outputData[6] = blockW; + outputData[7] = blockH; + outputData[8] = finalDataLen; + outputData[9] = finalDataLen >> 8; + outputData[10] = finalDataLen >> 16; + outputData[11] = finalDataLen >> 24; + + if (BZ2_bzBuffToBuffCompress(outputData+12, &outputDataLen, finalData, bson_size(&b), 9, 0, 0) != BZ_OK) + { + puts("Save Error\n"); + free(outputData); + *size = 0; + outputData = NULL; + goto fin; + } + + *size = outputDataLen + 12; + +fin: + bson_destroy(&b); + if(partsData) + free(partsData); + if(wallData) + free(wallData); + if(fanData) + free(fanData); + + return outputData; +} + +int parse_save_OPS(void *save, int size, int replace, int x0, int y0, unsigned char bmap[YRES/CELL][XRES/CELL], float vx[YRES/CELL][XRES/CELL], float vy[YRES/CELL][XRES/CELL], float pv[YRES/CELL][XRES/CELL], float fvx[YRES/CELL][XRES/CELL], float fvy[YRES/CELL][XRES/CELL], sign signs[MAXSIGNS], void* o_partsptr, unsigned pmap[YRES][XRES]) +{ + particle *partsptr = o_partsptr; + unsigned char * inputData = save, *bsonData = NULL, *partsData = NULL, *fanData = NULL, *wallData = NULL; + int inputDataLen = size, bsonDataLen = 0, partsDataLen, fanDataLen, wallDataLen; + int i, freeIndicesCount, x, y, returnCode = 0; + int *freeIndices = NULL; + + bsonDataLen = ((unsigned)inputData[8]); + bsonDataLen |= ((unsigned)inputData[9]) << 8; + bsonDataLen |= ((unsigned)inputData[10]) << 16; + bsonDataLen |= ((unsigned)inputData[11]) << 24; + + bsonData = malloc(bsonDataLen); + if(!bsonData) + { + fprintf(stderr, "Internal error while parsing save: could not allocate buffer\n"); + return 3; + } + + if (BZ2_bzBuffToBuffDecompress(bsonData, &bsonDataLen, inputData+12, inputDataLen-12, 0, 0)) + return 1; + + bson b; + bson_iterator iter; + bson_init_data(&b, bsonData); + bson_iterator_init(&iter, &b); + while(bson_iterator_more(&iter)) + { + bson_iterator_next(&iter); + if(strcmp(bson_iterator_key(&iter), "parts")==0) + { + if(bson_iterator_type(&iter)==BSON_BINDATA && ((unsigned char)bson_iterator_bin_type(&iter))==BSON_BIN_USER && (partsDataLen = bson_iterator_bin_len(&iter)) > 0) + { + partsData = bson_iterator_bin_data(&iter); + } + else + { + fprintf(stderr, "Invalid datatype of particle data: %d[%d] %d[%d] %d[%d]\n", 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); + } + } + } + + //Read particle data + if(partsData) + { + int newIndex = 0, fieldDescriptor, tempTemp; + puts("Have particle data"); + parts_lastActiveIndex = NPART-1; + freeIndicesCount = 0; + freeIndices = calloc(sizeof(int), NPART); + for (i = 0; i<NPART; i++) + { + //Ensure ALL parts (even photons) are in the pmap so we can overwrite, keep a track of indices we can use + if (partsptr[i].type) + { + x = (int)(partsptr[i].x+0.5f); + y = (int)(partsptr[i].y+0.5f); + pmap[y][x] = (i<<8)|1; + } + else + freeIndices[freeIndicesCount++] = i; + } + i = 0; + //i+7 because we have 8 bytes of required fields (type (1), x (2), y (2), temp (2), descriptor (1)) + while(i+7 < partsDataLen) + { + x = partsData[i+1] | (((unsigned)partsData[i+2])<<8); + y = partsData[i+3] | (((unsigned)partsData[i+4])<<8); + fieldDescriptor = partsData[i+7]; + if(x >= XRES || x < 0 || y >= YRES || y < 0) + { + fprintf(stderr, "Out of range [%d]: %d %d, [%d, %d], [%d, %d]\n", i, x, y, (unsigned)partsData[i+1], (unsigned)partsData[i+2], (unsigned)partsData[i+3], (unsigned)partsData[i+4]); + goto fail; + } + if(partsData[i] > NPART) + partsData[i+1] = PT_DMND; //Replace all invalid powders with diamond + if(pmap[y][x]) + { + //Replace existing particle or allocated block + newIndex = pmap[y][x]>>8; + } + else if(freeIndicesCount) + { + //Create new particle + newIndex = freeIndices[--freeIndicesCount]; + } + else + { + //Nowhere to put new particle, tpt is sad :( + break; + } + if(newIndex < 0 || newIndex >= NPART) + goto fail; + + //Clear the particle, ready for our new properties + memset(&(partsptr[newIndex]), 0, sizeof(particle)); + + //Required fields + partsptr[newIndex].type = partsData[i]; + partsptr[newIndex].x = x; + partsptr[newIndex].y = y; + partsptr[newIndex].temp = (partsData[i+5] | (partsData[i+6]<<8)); + i+=8; + + //Read life + if(fieldDescriptor & 0x01) + { + if(i >= partsDataLen) goto fail; + partsptr[newIndex].life = partsData[i++]; + //Read 2nd byte + if(fieldDescriptor & 0x02) + { + if(i >= partsDataLen) goto fail; + partsptr[newIndex].life |= partsData[i++]; + } + } + + //Read tmp + if(fieldDescriptor & 0x04) + { + if(i >= partsDataLen) goto fail; + partsptr[newIndex].tmp = partsData[i++]; + //Read 2nd byte + if(fieldDescriptor & 0x08) + { + if(i >= partsDataLen) goto fail; + partsptr[newIndex].tmp |= partsData[i++]; + } + } + + //Read ctype + if(fieldDescriptor & 0x10) + { + if(i >= partsDataLen) goto fail; + partsptr[newIndex].ctype = partsData[i++]; + } + + //Read dcolour + if(fieldDescriptor & 0x20) + { + if(i+3 >= partsDataLen) goto fail; + partsptr[newIndex].dcolour = partsData[i++]; + partsptr[newIndex].dcolour = partsData[i++]; + partsptr[newIndex].dcolour = partsData[i++]; + partsptr[newIndex].dcolour = partsData[i++]; + } + + //Read vx + if(fieldDescriptor & 0x40) + { + if(i >= partsDataLen) goto fail; + partsptr[newIndex].vx = (partsData[i++]-127.0f)/16.0f; + } + + //Read vy + if(fieldDescriptor & 0x80) + { + if(i >= partsDataLen) goto fail; + partsptr[newIndex].vy = (partsData[i++]-127.0f)/16.0f; + } + } + } + goto fin; +fail: + //Clean up everything + returnCode = 1; +fin: + bson_destroy(&b); + if(freeIndices) + free(freeIndices); + return returnCode; +} + +//the old saving function +void *build_save_PSv(int *size, int orig_x0, int orig_y0, int orig_w, int orig_h, unsigned char bmap[YRES/CELL][XRES/CELL], float fvx[YRES/CELL][XRES/CELL], float fvy[YRES/CELL][XRES/CELL], sign signs[MAXSIGNS], void* partsptr) +{ + unsigned char *d=calloc(1,3*(XRES/CELL)*(YRES/CELL)+(XRES*YRES)*15+MAXSIGNS*262), *c; + int i,j,x,y,p=0,*m=calloc(XRES*YRES, sizeof(int)); + int x0, y0, w, h, bx0=orig_x0/CELL, by0=orig_y0/CELL, bw, bh; + particle *parts = partsptr; + bw=(orig_w+orig_x0-bx0*CELL+CELL-1)/CELL; + bh=(orig_h+orig_y0-by0*CELL+CELL-1)/CELL; + + // normalize coordinates + x0 = bx0*CELL; + y0 = by0*CELL; + w = bw *CELL; + h = bh *CELL; + + // save the required air state + for (y=by0; y<by0+bh; y++) + for (x=bx0; x<bx0+bw; x++) + d[p++] = bmap[y][x]; + for (y=by0; y<by0+bh; y++) + for (x=bx0; x<bx0+bw; x++) + if (bmap[y][x]==WL_FAN||bmap[y][x]==4) + { + i = (int)(fvx[y][x]*64.0f+127.5f); + if (i<0) i=0; + if (i>255) i=255; + d[p++] = i; + } + for (y=by0; y<by0+bh; y++) + for (x=bx0; x<bx0+bw; x++) + if (bmap[y][x]==WL_FAN||bmap[y][x]==4) + { + i = (int)(fvy[y][x]*64.0f+127.5f); + if (i<0) i=0; + if (i>255) i=255; + d[p++] = i; + } + + // save the particle map + for (i=0; i<NPART; i++) + if (parts[i].type) + { + x = (int)(parts[i].x+0.5f); + y = (int)(parts[i].y+0.5f); + if (x>=orig_x0 && x<orig_x0+orig_w && y>=orig_y0 && y<orig_y0+orig_h) { + if (!m[(x-x0)+(y-y0)*w] || + parts[m[(x-x0)+(y-y0)*w]-1].type == PT_PHOT || + parts[m[(x-x0)+(y-y0)*w]-1].type == PT_NEUT) + m[(x-x0)+(y-y0)*w] = i+1; + } + } + for (j=0; j<w*h; j++) + { + i = m[j]; + if (i) + d[p++] = parts[i-1].type; + else + d[p++] = 0; + } + + // save particle properties + for (j=0; j<w*h; j++) + { + i = m[j]; + if (i) + { + i--; + x = (int)(parts[i].vx*16.0f+127.5f); + y = (int)(parts[i].vy*16.0f+127.5f); + if (x<0) x=0; + if (x>255) x=255; + if (y<0) y=0; + if (y>255) y=255; + d[p++] = x; + d[p++] = y; + } + } + for (j=0; j<w*h; j++) + { + i = m[j]; + if (i) { + //Everybody loves a 16bit int + //d[p++] = (parts[i-1].life+3)/4; + int ttlife = (int)parts[i-1].life; + d[p++] = ((ttlife&0xFF00)>>8); + d[p++] = (ttlife&0x00FF); + } + } + for (j=0; j<w*h; j++) + { + i = m[j]; + if (i) { + //Now saving tmp! + //d[p++] = (parts[i-1].life+3)/4; + int tttmp = (int)parts[i-1].tmp; + d[p++] = ((tttmp&0xFF00)>>8); + d[p++] = (tttmp&0x00FF); + } + } + for (j=0; j<w*h; j++) + { + i = m[j]; + if (i && (parts[i-1].type==PT_PBCN)) { + //Save tmp2 + d[p++] = parts[i-1].tmp2; + } + } + for (j=0; j<w*h; j++) + { + i = m[j]; + if (i) { + //Save colour (ALPHA) + d[p++] = (parts[i-1].dcolour&0xFF000000)>>24; + } + } + for (j=0; j<w*h; j++) + { + i = m[j]; + if (i) { + //Save colour (RED) + d[p++] = (parts[i-1].dcolour&0x00FF0000)>>16; + } + } + for (j=0; j<w*h; j++) + { + i = m[j]; + if (i) { + //Save colour (GREEN) + d[p++] = (parts[i-1].dcolour&0x0000FF00)>>8; + } + } + for (j=0; j<w*h; j++) + { + i = m[j]; + if (i) { + //Save colour (BLUE) + d[p++] = (parts[i-1].dcolour&0x000000FF); + } + } + for (j=0; j<w*h; j++) + { + i = m[j]; + if (i) + { + //New Temperature saving uses a 16bit unsigned int for temperatures, giving a precision of 1 degree versus 36 for the old format + int tttemp = (int)parts[i-1].temp; + d[p++] = ((tttemp&0xFF00)>>8); + d[p++] = (tttemp&0x00FF); + } + } + for (j=0; j<w*h; j++) + { + i = m[j]; + if (i && (parts[i-1].type==PT_CLNE || parts[i-1].type==PT_PCLN || parts[i-1].type==PT_BCLN || parts[i-1].type==PT_SPRK || parts[i-1].type==PT_LAVA || parts[i-1].type==PT_PIPE || parts[i-1].type==PT_LIFE || parts[i-1].type==PT_PBCN || parts[i-1].type==PT_WIRE || parts[i-1].type==PT_STOR || parts[i-1].type==PT_CONV)) + d[p++] = parts[i-1].ctype; + } + + j = 0; + for (i=0; i<MAXSIGNS; i++) + if (signs[i].text[0] && + signs[i].x>=x0 && signs[i].x<x0+w && + signs[i].y>=y0 && signs[i].y<y0+h) + j++; + d[p++] = j; + for (i=0; i<MAXSIGNS; i++) + if (signs[i].text[0] && + signs[i].x>=x0 && signs[i].x<x0+w && + signs[i].y>=y0 && signs[i].y<y0+h) + { + d[p++] = (signs[i].x-x0); + d[p++] = (signs[i].x-x0)>>8; + d[p++] = (signs[i].y-y0); + d[p++] = (signs[i].y-y0)>>8; + d[p++] = signs[i].ju; + x = strlen(signs[i].text); + d[p++] = x; + memcpy(d+p, signs[i].text, x); + p+=x; + } + + i = (p*101+99)/100 + 612; + c = malloc(i); + + //New file header uses PSv, replacing fuC. This is to detect if the client uses a new save format for temperatures + //This creates a problem for old clients, that display and "corrupt" error instead of a "newer version" error + + c[0] = 0x50; //0x66; + c[1] = 0x53; //0x75; + c[2] = 0x76; //0x43; + c[3] = legacy_enable|((sys_pause<<1)&0x02)|((gravityMode<<2)&0x0C)|((airMode<<4)&0x70)|((ngrav_enable<<7)&0x80); + c[4] = SAVE_VERSION; + c[5] = CELL; + c[6] = bw; + c[7] = bh; + c[8] = p; + c[9] = p >> 8; + c[10] = p >> 16; + c[11] = p >> 24; + + i -= 12; + + if (BZ2_bzBuffToBuffCompress((char *)(c+12), (unsigned *)&i, (char *)d, p, 9, 0, 0) != BZ_OK) + { + free(d); + free(c); + free(m); + return NULL; + } + free(d); + free(m); + + *size = i+12; + return c; +} + +int parse_save_PSv(void *save, int size, int replace, int x0, int y0, unsigned char bmap[YRES/CELL][XRES/CELL], float fvx[YRES/CELL][XRES/CELL], float fvy[YRES/CELL][XRES/CELL], sign signs[MAXSIGNS], void* partsptr, unsigned pmap[YRES][XRES]) +{ + unsigned char *d=NULL,*c=save; + int q,i,j,k,x,y,p=0,*m=NULL, ver, pty, ty, legacy_beta=0, tempGrav = 0; + int bx0=x0/CELL, by0=y0/CELL, bw, bh, w, h; + int nf=0, new_format = 0, ttv = 0; + particle *parts = partsptr; + int *fp = malloc(NPART*sizeof(int)); + + //New file header uses PSv, replacing fuC. This is to detect if the client uses a new save format for temperatures + //This creates a problem for old clients, that display and "corrupt" error instead of a "newer version" error + + if (size<16) + return 1; + if (!(c[2]==0x43 && c[1]==0x75 && c[0]==0x66) && !(c[2]==0x76 && c[1]==0x53 && c[0]==0x50)) + return 1; + if (c[2]==0x76 && c[1]==0x53 && c[0]==0x50) { + new_format = 1; + } + if (c[4]>SAVE_VERSION) + return 2; + ver = c[4]; + + if (ver<34) + { + legacy_enable = 1; + } + else + { + if (ver>=44) { + legacy_enable = c[3]&0x01; + if (!sys_pause) { + sys_pause = (c[3]>>1)&0x01; + } + if (ver>=46 && replace) { + gravityMode = ((c[3]>>2)&0x03);// | ((c[3]>>2)&0x01); + airMode = ((c[3]>>4)&0x07);// | ((c[3]>>4)&0x02) | ((c[3]>>4)&0x01); + } + if (ver>=49 && replace) { + tempGrav = ((c[3]>>7)&0x01); + } + } else { + if (c[3]==1||c[3]==0) { + legacy_enable = c[3]; + } else { + legacy_beta = 1; + } + } + } + + bw = c[6]; + bh = c[7]; + if (bx0+bw > XRES/CELL) + bx0 = XRES/CELL - bw; + if (by0+bh > YRES/CELL) + by0 = YRES/CELL - bh; + if (bx0 < 0) + bx0 = 0; + if (by0 < 0) + by0 = 0; + + if (c[5]!=CELL || bx0+bw>XRES/CELL || by0+bh>YRES/CELL) + return 3; + i = (unsigned)c[8]; + i |= ((unsigned)c[9])<<8; + i |= ((unsigned)c[10])<<16; + i |= ((unsigned)c[11])<<24; + d = malloc(i); + if (!d) + return 1; + + if (BZ2_bzBuffToBuffDecompress((char *)d, (unsigned *)&i, (char *)(c+12), size-12, 0, 0)) + return 1; + size = i; + + if (size < bw*bh) + return 1; + + // normalize coordinates + x0 = bx0*CELL; + y0 = by0*CELL; + w = bw *CELL; + h = bh *CELL; + + if (replace) + { + if (ver<46) { + gravityMode = 0; + airMode = 0; + } + clear_sim(); + } + parts_lastActiveIndex = NPART-1; + m = calloc(XRES*YRES, sizeof(int)); + + // make a catalog of free parts + //memset(pmap, 0, sizeof(pmap)); "Using sizeof for array given as function argument returns the size of pointer." + memset(pmap, 0, sizeof(unsigned)*(XRES*YRES)); + for (i=0; i<NPART; i++) + if (parts[i].type) + { + x = (int)(parts[i].x+0.5f); + y = (int)(parts[i].y+0.5f); + pmap[y][x] = (i<<8)|1; + } + else + fp[nf++] = i; + + // load the required air state + for (y=by0; y<by0+bh; y++) + for (x=bx0; x<bx0+bw; x++) + { + if (d[p]) + { + bmap[y][x] = d[p]; + if (bmap[y][x]==1) + bmap[y][x]=WL_WALL; + if (bmap[y][x]==2) + bmap[y][x]=WL_DESTROYALL; + if (bmap[y][x]==3) + bmap[y][x]=WL_ALLOWLIQUID; + if (bmap[y][x]==4) + bmap[y][x]=WL_FAN; + if (bmap[y][x]==5) + bmap[y][x]=WL_STREAM; + if (bmap[y][x]==6) + bmap[y][x]=WL_DETECT; + if (bmap[y][x]==7) + bmap[y][x]=WL_EWALL; + if (bmap[y][x]==8) + bmap[y][x]=WL_WALLELEC; + if (bmap[y][x]==9) + bmap[y][x]=WL_ALLOWAIR; + if (bmap[y][x]==10) + bmap[y][x]=WL_ALLOWSOLID; + if (bmap[y][x]==11) + bmap[y][x]=WL_ALLOWALLELEC; + if (bmap[y][x]==12) + bmap[y][x]=WL_EHOLE; + if (bmap[y][x]==13) + bmap[y][x]=WL_ALLOWGAS; + } + + p++; + } + for (y=by0; y<by0+bh; y++) + for (x=bx0; x<bx0+bw; x++) + if (d[(y-by0)*bw+(x-bx0)]==4||d[(y-by0)*bw+(x-bx0)]==WL_FAN) + { + if (p >= size) + goto corrupt; + fvx[y][x] = (d[p++]-127.0f)/64.0f; + } + for (y=by0; y<by0+bh; y++) + for (x=bx0; x<bx0+bw; x++) + if (d[(y-by0)*bw+(x-bx0)]==4||d[(y-by0)*bw+(x-bx0)]==WL_FAN) + { + if (p >= size) + goto corrupt; + fvy[y][x] = (d[p++]-127.0f)/64.0f; + } + + // load the particle map + i = 0; + pty = p; + for (y=y0; y<y0+h; y++) + for (x=x0; x<x0+w; x++) + { + if (p >= size) + goto corrupt; + j=d[p++]; + if (j >= PT_NUM) { + //TODO: Possibly some server side translation + j = PT_DUST;//goto corrupt; + } + gol[x][y]=0; + if (j) + { + if (pmap[y][x]) + { + k = pmap[y][x]>>8; + } + else if (i<nf) + { + k = fp[i]; + i++; + } + else + { + m[(x-x0)+(y-y0)*w] = NPART+1; + continue; + } + memset(parts+k, 0, sizeof(particle)); + parts[k].type = j; + if (j == PT_COAL) + parts[k].tmp = 50; + if (j == PT_FUSE) + parts[k].tmp = 50; + if (j == PT_PHOT) + parts[k].ctype = 0x3fffffff; + if (j == PT_SOAP) + parts[k].ctype = 0; + if (j==PT_BIZR || j==PT_BIZRG || j==PT_BIZRS) + parts[k].ctype = 0x47FFFF; + parts[k].x = (float)x; + parts[k].y = (float)y; + m[(x-x0)+(y-y0)*w] = k+1; + } + } + + // load particle properties + for (j=0; j<w*h; j++) + { + i = m[j]; + if (i) + { + i--; + if (p+1 >= size) + goto corrupt; + if (i < NPART) + { + parts[i].vx = (d[p++]-127.0f)/16.0f; + parts[i].vy = (d[p++]-127.0f)/16.0f; + } + else + p += 2; + } + } + for (j=0; j<w*h; j++) + { + i = m[j]; + if (i) + { + if (ver>=44) { + if (p >= size) { + goto corrupt; + } + if (i <= NPART) { + ttv = (d[p++])<<8; + ttv |= (d[p++]); + parts[i-1].life = ttv; + } else { + p+=2; + } + } else { + if (p >= size) + goto corrupt; + if (i <= NPART) + parts[i-1].life = d[p++]*4; + else + p++; + } + } + } + if (ver>=44) { + for (j=0; j<w*h; j++) + { + i = m[j]; + if (i) + { + if (p >= size) { + goto corrupt; + } + if (i <= NPART) { + ttv = (d[p++])<<8; + ttv |= (d[p++]); + parts[i-1].tmp = ttv; + if (ver<53 && !parts[i-1].tmp) + for (q = 1; q<=NGOLALT; q++) { + if (parts[i-1].type==goltype[q-1] && grule[q][9]==2) + parts[i-1].tmp = grule[q][9]-1; + } + if (ver>=51 && ver<53 && parts[i-1].type==PT_PBCN) + { + parts[i-1].tmp2 = parts[i-1].tmp; + parts[i-1].tmp = 0; + } + } else { + p+=2; + } + } + } + } + if (ver>=53) { + for (j=0; j<w*h; j++) + { + i = m[j]; + ty = d[pty+j]; + if (i && ty==PT_PBCN) + { + if (p >= size) + goto corrupt; + if (i <= NPART) + parts[i-1].tmp2 = d[p++]; + else + p++; + } + } + } + //Read ALPHA component + for (j=0; j<w*h; j++) + { + i = m[j]; + if (i) + { + if (ver>=49) { + if (p >= size) { + goto corrupt; + } + if (i <= NPART) { + parts[i-1].dcolour = d[p++]<<24; + } else { + p++; + } + } + } + } + //Read RED component + for (j=0; j<w*h; j++) + { + i = m[j]; + if (i) + { + if (ver>=49) { + if (p >= size) { + goto corrupt; + } + if (i <= NPART) { + parts[i-1].dcolour |= d[p++]<<16; + } else { + p++; + } + } + } + } + //Read GREEN component + for (j=0; j<w*h; j++) + { + i = m[j]; + if (i) + { + if (ver>=49) { + if (p >= size) { + goto corrupt; + } + if (i <= NPART) { + parts[i-1].dcolour |= d[p++]<<8; + } else { + p++; + } + } + } + } + //Read BLUE component + for (j=0; j<w*h; j++) + { + i = m[j]; + if (i) + { + if (ver>=49) { + if (p >= size) { + goto corrupt; + } + if (i <= NPART) { + parts[i-1].dcolour |= d[p++]; + } else { + p++; + } + } + } + } + for (j=0; j<w*h; j++) + { + i = m[j]; + ty = d[pty+j]; + if (i) + { + if (ver>=34&&legacy_beta==0) + { + if (p >= size) + { + goto corrupt; + } + if (i <= NPART) + { + if (ver>=42) { + if (new_format) { + ttv = (d[p++])<<8; + ttv |= (d[p++]); + if (parts[i-1].type==PT_PUMP) { + parts[i-1].temp = ttv + 0.15;//fix PUMP saved at 0, so that it loads at 0. + } else { + parts[i-1].temp = ttv; + } + } else { + parts[i-1].temp = (d[p++]*((MAX_TEMP+(-MIN_TEMP))/255))+MIN_TEMP; + } + } else { + parts[i-1].temp = ((d[p++]*((O_MAX_TEMP+(-O_MIN_TEMP))/255))+O_MIN_TEMP)+273; + } + } + else + { + p++; + if (new_format) { + p++; + } + } + } + else + { + parts[i-1].temp = ptypes[parts[i-1].type].heat; + } + } + } + for (j=0; j<w*h; j++) + { + int gnum = 0; + i = m[j]; + ty = d[pty+j]; + 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))) + { + if (p >= size) + goto corrupt; + if (i <= NPART) + parts[i-1].ctype = d[p++]; + else + p++; + } + // no more particle properties to load, so we can change type here without messing up loading + if (i && i<=NPART) + { + if ((player.spwn == 1 && ty==PT_STKM) || (player2.spwn == 1 && ty==PT_STKM2)) + { + parts[i-1].type = PT_NONE; + } + else if (parts[i-1].type == PT_STKM) + { + STKM_init_legs(&player, i-1); + player.spwn = 1; + player.elem = PT_DUST; + } + else if (parts[i-1].type == PT_STKM2) + { + STKM_init_legs(&player2, i-1); + player2.spwn = 1; + player2.elem = PT_DUST; + } + else if (parts[i-1].type == PT_FIGH) + { + unsigned char fcount = 0; + while (fcount < 100 && fcount < (fighcount+1) && fighters[fcount].spwn==1) fcount++; + if (fcount < 100 && fighters[fcount].spwn==0) + { + parts[i-1].tmp = fcount; + fighters[fcount].spwn = 1; + fighters[fcount].elem = PT_DUST; + fighcount++; + STKM_init_legs(&(fighters[fcount]), i-1); + } + } + + if (ver<48 && (ty==OLD_PT_WIND || (ty==PT_BRAY&&parts[i-1].life==0))) + { + // Replace invisible particles with something sensible and add decoration to hide it + x = (int)(parts[i-1].x+0.5f); + y = (int)(parts[i-1].y+0.5f); + parts[i-1].dcolour = 0xFF000000; + parts[i-1].type = PT_DMND; + } + if(ver<51 && ((ty>=78 && ty<=89) || (ty>=134 && ty<=146 && ty!=141))){ + //Replace old GOL + parts[i-1].type = PT_LIFE; + for (gnum = 0; gnum<NGOLALT; gnum++){ + if (ty==goltype[gnum]) + parts[i-1].ctype = gnum; + } + ty = PT_LIFE; + } + if(ver<52 && (ty==PT_CLNE || ty==PT_PCLN || ty==PT_BCLN)){ + //Replace old GOL ctypes in clone + for (gnum = 0; gnum<NGOLALT; gnum++){ + if (parts[i-1].ctype==goltype[gnum]) + { + parts[i-1].ctype = PT_LIFE; + parts[i-1].tmp = gnum; + } + } + } + if(ty==PT_LCRY){ + if(ver<67) + { + //New LCRY uses TMP not life + if(parts[i-1].life>=10) + { + parts[i-1].life = 10; + parts[i-1].tmp2 = 10; + parts[i-1].tmp = 3; + } + else if(parts[i-1].life<=0) + { + parts[i-1].life = 0; + parts[i-1].tmp2 = 0; + parts[i-1].tmp = 0; + } + else if(parts[i-1].life < 10 && parts[i-1].life > 0) + { + parts[i-1].tmp = 1; + } + } + else + { + parts[i-1].tmp2 = parts[i-1].life; + } + } + if (!ptypes[parts[i-1].type].enabled) + parts[i-1].type = PT_NONE; + } + } + + #ifndef RENDERER + //Change the gravity state + if(ngrav_enable != tempGrav && replace) + { + if(tempGrav) + start_grav_async(); + else + stop_grav_async(); + } + #endif + + gravity_mask(); + + if (p >= size) + goto version1; + j = d[p++]; + for (i=0; i<j; i++) + { + if (p+6 > size) + goto corrupt; + for (k=0; k<MAXSIGNS; k++) + if (!signs[k].text[0]) + break; + x = d[p++]; + x |= ((unsigned)d[p++])<<8; + if (k<MAXSIGNS) + signs[k].x = x+x0; + x = d[p++]; + x |= ((unsigned)d[p++])<<8; + if (k<MAXSIGNS) + signs[k].y = x+y0; + x = d[p++]; + if (k<MAXSIGNS) + signs[k].ju = x; + x = d[p++]; + if (p+x > size) + goto corrupt; + if (k<MAXSIGNS) + { + memcpy(signs[k].text, d+p, x); + signs[k].text[x] = 0; + clean_text(signs[k].text, 158-14 /* Current max sign length */); + } + p += x; + } + +version1: + if (m) free(m); + if (d) free(d); + if (fp) free(fp); + + return 0; + +corrupt: + if (m) free(m); + if (d) free(d); + if (fp) free(fp); + if (replace) + { + legacy_enable = 0; + clear_sim(); + } + return 1; +} + +void *build_thumb(int *size, int bzip2) +{ + unsigned char *d=calloc(1,XRES*YRES), *c; + int i,j,x,y; + for (i=0; i<NPART; i++) + if (parts[i].type) + { + x = (int)(parts[i].x+0.5f); + y = (int)(parts[i].y+0.5f); + if (x>=0 && x<XRES && y>=0 && y<YRES) + d[x+y*XRES] = parts[i].type; + } + for (y=0; y<YRES/CELL; y++) + for (x=0; x<XRES/CELL; x++) + if (bmap[y][x]) + for (j=0; j<CELL; j++) + for (i=0; i<CELL; i++) + d[x*CELL+i+(y*CELL+j)*XRES] = 0xFF; + j = XRES*YRES; + + if (bzip2) + { + i = (j*101+99)/100 + 608; + c = malloc(i); + + c[0] = 0x53; + c[1] = 0x68; + c[2] = 0x49; + c[3] = 0x74; + c[4] = PT_NUM; + c[5] = CELL; + c[6] = XRES/CELL; + c[7] = YRES/CELL; + + i -= 8; + + if (BZ2_bzBuffToBuffCompress((char *)(c+8), (unsigned *)&i, (char *)d, j, 9, 0, 0) != BZ_OK) + { + free(d); + free(c); + return NULL; + } + free(d); + *size = i+8; + return c; + } + + *size = j; + return d; +}
\ No newline at end of file |
