summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSimon 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)
commit86369670aaa11b8cd2ddcbd46413fb9482e4e58f (patch)
tree07be282e1e08003153ee5c000f3e50336cfa2eb0 /src
parentad2b000a8af84f299e7c7c43a6a272a60ffcd36d (diff)
downloadpowder-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')
-rw-r--r--src/BSON.c1122
-rw-r--r--src/interface.c19
-rw-r--r--src/main.c867
-rw-r--r--src/save.c1266
4 files changed, 2409 insertions, 865 deletions
diff --git a/src/BSON.c b/src/BSON.c
new file mode 100644
index 0000000..62dfc4f
--- /dev/null
+++ b/src/BSON.c
@@ -0,0 +1,1122 @@
+/* bson.c */
+
+/* Copyright 2009, 2010 10gen Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#include <limits.h>
+
+#include "bson.h"
+
+const int initialBufferSize = 128;
+
+/* only need one of these */
+static const int zero = 0;
+
+/* Custom standard function pointers. */
+void *( *bson_malloc_func )( size_t ) = malloc;
+void *( *bson_realloc_func )( void *, size_t ) = realloc;
+void ( *bson_free )( void * ) = free;
+bson_printf_func bson_printf = printf;
+bson_fprintf_func bson_fprintf = fprintf;
+bson_sprintf_func bson_sprintf = sprintf;
+
+static int _bson_errprintf( const char *, ... );
+bson_printf_func bson_errprintf = _bson_errprintf;
+
+/* ObjectId fuzz functions. */
+static int ( *oid_fuzz_func )( void ) = NULL;
+static int ( *oid_inc_func )( void ) = NULL;
+
+/* ----------------------------
+ READING
+ ------------------------------ */
+
+bson *bson_empty( bson *obj ) {
+ static char *data = "\005\0\0\0\0";
+ bson_init_data( obj, data );
+ obj->finished = 1;
+ obj->err = 0;
+ obj->stackPos = 0;
+ return obj;
+}
+
+int bson_copy( bson *out, const bson *in ) {
+ if ( !out ) return BSON_ERROR;
+ if ( !in->finished ) return BSON_ERROR;
+ bson_init_size( out, bson_size( in ) );
+ memcpy( out->data, in->data, bson_size( in ) );
+ out->finished = 1;
+
+ return BSON_OK;
+}
+
+int bson_init_data( bson *b, char *data ) {
+ b->data = data;
+ return BSON_OK;
+}
+
+int bson_init_finished_data( bson *b, char *data ) {
+ bson_init_data( b, data );
+ b->stackPos = 0;
+ b->finished = 1;
+ return BSON_OK;
+}
+
+static void _bson_reset( bson *b ) {
+ b->finished = 0;
+ b->stackPos = 0;
+ b->err = 0;
+ b->errstr = NULL;
+}
+
+int bson_size( const bson *b ) {
+ int i;
+ if ( ! b || ! b->data )
+ return 0;
+ bson_little_endian32( &i, b->data );
+ return i;
+}
+
+const char *bson_data( bson *b ) {
+ return (const char *)b->data;
+}
+
+static char hexbyte( char hex ) {
+ switch ( hex ) {
+ case '0':
+ return 0x0;
+ case '1':
+ return 0x1;
+ case '2':
+ return 0x2;
+ case '3':
+ return 0x3;
+ case '4':
+ return 0x4;
+ case '5':
+ return 0x5;
+ case '6':
+ return 0x6;
+ case '7':
+ return 0x7;
+ case '8':
+ return 0x8;
+ case '9':
+ return 0x9;
+ case 'a':
+ case 'A':
+ return 0xa;
+ case 'b':
+ case 'B':
+ return 0xb;
+ case 'c':
+ case 'C':
+ return 0xc;
+ case 'd':
+ case 'D':
+ return 0xd;
+ case 'e':
+ case 'E':
+ return 0xe;
+ case 'f':
+ case 'F':
+ return 0xf;
+ default:
+ return 0x0; /* something smarter? */
+ }
+}
+
+void bson_oid_from_string( bson_oid_t *oid, const char *str ) {
+ int i;
+ for ( i=0; i<12; i++ ) {
+ oid->bytes[i] = ( hexbyte( str[2*i] ) << 4 ) | hexbyte( str[2*i + 1] );
+ }
+}
+
+void bson_oid_to_string( const bson_oid_t *oid, char *str ) {
+ static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
+ int i;
+ for ( i=0; i<12; i++ ) {
+ str[2*i] = hex[( oid->bytes[i] & 0xf0 ) >> 4];
+ str[2*i + 1] = hex[ oid->bytes[i] & 0x0f ];
+ }
+ str[24] = '\0';
+}
+
+void bson_set_oid_fuzz( int ( *func )( void ) ) {
+ oid_fuzz_func = func;
+}
+
+void bson_set_oid_inc( int ( *func )( void ) ) {
+ oid_inc_func = func;
+}
+
+void bson_oid_gen( bson_oid_t *oid ) {
+ static int incr = 0;
+ static int fuzz = 0;
+ int i;
+ int t = time( NULL );
+
+ if( oid_inc_func )
+ i = oid_inc_func();
+ else
+ i = incr++;
+
+ if ( !fuzz ) {
+ if ( oid_fuzz_func )
+ fuzz = oid_fuzz_func();
+ else {
+ srand( t );
+ fuzz = rand();
+ }
+ }
+
+ bson_big_endian32( &oid->ints[0], &t );
+ oid->ints[1] = fuzz;
+ bson_big_endian32( &oid->ints[2], &i );
+}
+
+time_t bson_oid_generated_time( bson_oid_t *oid ) {
+ time_t out;
+ bson_big_endian32( &out, &oid->ints[0] );
+
+ return out;
+}
+
+void bson_print( bson *b ) {
+ bson_print_raw( b->data , 0 );
+}
+
+void bson_print_raw( const char *data , int depth ) {
+ bson_iterator i;
+ const char *key;
+ int temp;
+ bson_timestamp_t ts;
+ char oidhex[25];
+ bson scope;
+ bson_iterator_from_buffer( &i, data );
+
+ while ( bson_iterator_next( &i ) ) {
+ bson_type t = bson_iterator_type( &i );
+ if ( t == 0 )
+ break;
+ key = bson_iterator_key( &i );
+
+ for ( temp=0; temp<=depth; temp++ )
+ bson_printf( "\t" );
+ bson_printf( "%s : %d \t " , key , t );
+ switch ( t ) {
+ case BSON_DOUBLE:
+ bson_printf( "%f" , bson_iterator_double( &i ) );
+ break;
+ case BSON_STRING:
+ bson_printf( "%s" , bson_iterator_string( &i ) );
+ break;
+ case BSON_SYMBOL:
+ bson_printf( "SYMBOL: %s" , bson_iterator_string( &i ) );
+ break;
+ case BSON_OID:
+ bson_oid_to_string( bson_iterator_oid( &i ), oidhex );
+ bson_printf( "%s" , oidhex );
+ break;
+ case BSON_BOOL:
+ bson_printf( "%s" , bson_iterator_bool( &i ) ? "true" : "false" );
+ break;
+ case BSON_DATE:
+ bson_printf( "%ld" , ( long int )bson_iterator_date( &i ) );
+ break;
+ case BSON_BINDATA:
+ bson_printf( "BSON_BINDATA" );
+ break;
+ case BSON_UNDEFINED:
+ bson_printf( "BSON_UNDEFINED" );
+ break;
+ case BSON_NULL:
+ bson_printf( "BSON_NULL" );
+ break;
+ case BSON_REGEX:
+ bson_printf( "BSON_REGEX: %s", bson_iterator_regex( &i ) );
+ break;
+ case BSON_CODE:
+ bson_printf( "BSON_CODE: %s", bson_iterator_code( &i ) );
+ break;
+ case BSON_CODEWSCOPE:
+ bson_printf( "BSON_CODE_W_SCOPE: %s", bson_iterator_code( &i ) );
+ bson_init( &scope );
+ bson_iterator_code_scope( &i, &scope );
+ bson_printf( "\n\t SCOPE: " );
+ bson_print( &scope );
+ break;
+ case BSON_INT:
+ bson_printf( "%d" , bson_iterator_int( &i ) );
+ break;
+ case BSON_LONG:
+ bson_printf( "%lld" , ( uint64_t )bson_iterator_long( &i ) );
+ break;
+ case BSON_TIMESTAMP:
+ ts = bson_iterator_timestamp( &i );
+ bson_printf( "i: %d, t: %d", ts.i, ts.t );
+ break;
+ case BSON_OBJECT:
+ case BSON_ARRAY:
+ bson_printf( "\n" );
+ bson_print_raw( bson_iterator_value( &i ) , depth + 1 );
+ break;
+ default:
+ bson_errprintf( "can't print type : %d\n" , t );
+ }
+ bson_printf( "\n" );
+ }
+}
+
+/* ----------------------------
+ ITERATOR
+ ------------------------------ */
+
+void bson_iterator_init( bson_iterator *i, const bson *b ) {
+ i->cur = b->data + 4;
+ i->first = 1;
+}
+
+void bson_iterator_from_buffer( bson_iterator *i, const char *buffer ) {
+ i->cur = buffer + 4;
+ i->first = 1;
+}
+
+bson_type bson_find( bson_iterator *it, const bson *obj, const char *name ) {
+ bson_iterator_init( it, (bson *)obj );
+ while( bson_iterator_next( it ) ) {
+ if ( strcmp( name, bson_iterator_key( it ) ) == 0 )
+ break;
+ }
+ return bson_iterator_type( it );
+}
+
+bson_bool_t bson_iterator_more( const bson_iterator *i ) {
+ return *( i->cur );
+}
+
+bson_type bson_iterator_next( bson_iterator *i ) {
+ int ds;
+
+ if ( i->first ) {
+ i->first = 0;
+ return ( bson_type )( *i->cur );
+ }
+
+ switch ( bson_iterator_type( i ) ) {
+ case BSON_EOO:
+ return BSON_EOO; /* don't advance */
+ case BSON_UNDEFINED:
+ case BSON_NULL:
+ ds = 0;
+ break;
+ case BSON_BOOL:
+ ds = 1;
+ break;
+ case BSON_INT:
+ ds = 4;
+ break;
+ case BSON_LONG:
+ case BSON_DOUBLE:
+ case BSON_TIMESTAMP:
+ case BSON_DATE:
+ ds = 8;
+ break;
+ case BSON_OID:
+ ds = 12;
+ break;
+ case BSON_STRING:
+ case BSON_SYMBOL:
+ case BSON_CODE:
+ ds = 4 + bson_iterator_int_raw( i );
+ break;
+ case BSON_BINDATA:
+ ds = 5 + bson_iterator_int_raw( i );
+ break;
+ case BSON_OBJECT:
+ case BSON_ARRAY:
+ case BSON_CODEWSCOPE:
+ ds = bson_iterator_int_raw( i );
+ break;
+ case BSON_DBREF:
+ ds = 4+12 + bson_iterator_int_raw( i );
+ break;
+ case BSON_REGEX: {
+ const char *s = bson_iterator_value( i );
+ const char *p = s;
+ p += strlen( p )+1;
+ p += strlen( p )+1;
+ ds = p-s;
+ break;
+ }
+
+ default: {
+ char msg[] = "unknown type: 000000000000";
+ bson_numstr( msg+14, ( unsigned )( i->cur[0] ) );
+ bson_fatal_msg( 0, msg );
+ return 0;
+ }
+ }
+
+ i->cur += 1 + strlen( i->cur + 1 ) + 1 + ds;
+
+ return ( bson_type )( *i->cur );
+}
+
+bson_type bson_iterator_type( const bson_iterator *i ) {
+ return ( bson_type )i->cur[0];
+}
+
+const char *bson_iterator_key( const bson_iterator *i ) {
+ return i->cur + 1;
+}
+
+const char *bson_iterator_value( const bson_iterator *i ) {
+ const char *t = i->cur + 1;
+ t += strlen( t ) + 1;
+ return t;
+}
+
+/* types */
+
+int bson_iterator_int_raw( const bson_iterator *i ) {
+ int out;
+ bson_little_endian32( &out, bson_iterator_value( i ) );
+ return out;
+}
+
+double bson_iterator_double_raw( const bson_iterator *i ) {
+ double out;
+ bson_little_endian64( &out, bson_iterator_value( i ) );
+ return out;
+}
+
+int64_t bson_iterator_long_raw( const bson_iterator *i ) {
+ int64_t out;
+ bson_little_endian64( &out, bson_iterator_value( i ) );
+ return out;
+}
+
+bson_bool_t bson_iterator_bool_raw( const bson_iterator *i ) {
+ return bson_iterator_value( i )[0];
+}
+
+bson_oid_t *bson_iterator_oid( const bson_iterator *i ) {
+ return ( bson_oid_t * )bson_iterator_value( i );
+}
+
+int bson_iterator_int( const bson_iterator *i ) {
+ switch ( bson_iterator_type( i ) ) {
+ case BSON_INT:
+ return bson_iterator_int_raw( i );
+ case BSON_LONG:
+ return bson_iterator_long_raw( i );
+ case BSON_DOUBLE:
+ return bson_iterator_double_raw( i );
+ default:
+ return 0;
+ }
+}
+
+double bson_iterator_double( const bson_iterator *i ) {
+ switch ( bson_iterator_type( i ) ) {
+ case BSON_INT:
+ return bson_iterator_int_raw( i );
+ case BSON_LONG:
+ return bson_iterator_long_raw( i );
+ case BSON_DOUBLE:
+ return bson_iterator_double_raw( i );
+ default:
+ return 0;
+ }
+}
+
+int64_t bson_iterator_long( const bson_iterator *i ) {
+ switch ( bson_iterator_type( i ) ) {
+ case BSON_INT:
+ return bson_iterator_int_raw( i );
+ case BSON_LONG:
+ return bson_iterator_long_raw( i );
+ case BSON_DOUBLE:
+ return bson_iterator_double_raw( i );
+ default:
+ return 0;
+ }
+}
+
+bson_timestamp_t bson_iterator_timestamp( const bson_iterator *i ) {
+ bson_timestamp_t ts;
+ bson_little_endian32( &( ts.i ), bson_iterator_value( i ) );
+ bson_little_endian32( &( ts.t ), bson_iterator_value( i ) + 4 );
+ return ts;
+}
+
+bson_bool_t bson_iterator_bool( const bson_iterator *i ) {
+ switch ( bson_iterator_type( i ) ) {
+ case BSON_BOOL:
+ return bson_iterator_bool_raw( i );
+ case BSON_INT:
+ return bson_iterator_int_raw( i ) != 0;
+ case BSON_LONG:
+ return bson_iterator_long_raw( i ) != 0;
+ case BSON_DOUBLE:
+ return bson_iterator_double_raw( i ) != 0;
+ case BSON_EOO:
+ case BSON_NULL:
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+const char *bson_iterator_string( const bson_iterator *i ) {
+ return bson_iterator_value( i ) + 4;
+}
+
+int bson_iterator_string_len( const bson_iterator *i ) {
+ return bson_iterator_int_raw( i );
+}
+
+const char *bson_iterator_code( const bson_iterator *i ) {
+ switch ( bson_iterator_type( i ) ) {
+ case BSON_STRING:
+ case BSON_CODE:
+ return bson_iterator_value( i ) + 4;
+ case BSON_CODEWSCOPE:
+ return bson_iterator_value( i ) + 8;
+ default:
+ return NULL;
+ }
+}
+
+void bson_iterator_code_scope( const bson_iterator *i, bson *scope ) {
+ if ( bson_iterator_type( i ) == BSON_CODEWSCOPE ) {
+ int code_len;
+ bson_little_endian32( &code_len, bson_iterator_value( i )+4 );
+ bson_init_data( scope, ( void * )( bson_iterator_value( i )+8+code_len ) );
+ _bson_reset( scope );
+ scope->finished = 1;
+ } else {
+ bson_empty( scope );
+ }
+}
+
+bson_date_t bson_iterator_date( const bson_iterator *i ) {
+ return bson_iterator_long_raw( i );
+}
+
+time_t bson_iterator_time_t( const bson_iterator *i ) {
+ return bson_iterator_date( i ) / 1000;
+}
+
+int bson_iterator_bin_len( const bson_iterator *i ) {
+ return ( bson_iterator_bin_type( i ) == BSON_BIN_BINARY_OLD )
+ ? bson_iterator_int_raw( i ) - 4
+ : bson_iterator_int_raw( i );
+}
+
+char bson_iterator_bin_type( const bson_iterator *i ) {
+ return bson_iterator_value( i )[4];
+}
+
+const char *bson_iterator_bin_data( const bson_iterator *i ) {
+ return ( bson_iterator_bin_type( i ) == BSON_BIN_BINARY_OLD )
+ ? bson_iterator_value( i ) + 9
+ : bson_iterator_value( i ) + 5;
+}
+
+const char *bson_iterator_regex( const bson_iterator *i ) {
+ return bson_iterator_value( i );
+}
+
+const char *bson_iterator_regex_opts( const bson_iterator *i ) {
+ const char *p = bson_iterator_value( i );
+ return p + strlen( p ) + 1;
+
+}
+
+void bson_iterator_subobject( const bson_iterator *i, bson *sub ) {
+ bson_init_data( sub, ( char * )bson_iterator_value( i ) );
+ _bson_reset( sub );
+ sub->finished = 1;
+}
+
+void bson_iterator_subiterator( const bson_iterator *i, bson_iterator *sub ) {
+ bson_iterator_from_buffer( sub, bson_iterator_value( i ) );
+}
+
+/* ----------------------------
+ BUILDING
+ ------------------------------ */
+
+static void _bson_init_size( bson *b, int size ) {
+ if( size == 0 )
+ b->data = NULL;
+ else
+ b->data = ( char * )bson_malloc( size );
+ b->dataSize = size;
+ b->cur = b->data + 4;
+ _bson_reset( b );
+}
+
+void bson_init( bson *b ) {
+ _bson_init_size( b, initialBufferSize );
+}
+
+void bson_init_size( bson *b, int size ) {
+ _bson_init_size( b, size );
+}
+
+static void bson_append_byte( bson *b, char c ) {
+ b->cur[0] = c;
+ b->cur++;
+}
+
+static void bson_append( bson *b, const void *data, int len ) {
+ memcpy( b->cur , data , len );
+ b->cur += len;
+}
+
+static void bson_append32( bson *b, const void *data ) {
+ bson_little_endian32( b->cur, data );
+ b->cur += 4;
+}
+
+static void bson_append64( bson *b, const void *data ) {
+ bson_little_endian64( b->cur, data );
+ b->cur += 8;
+}
+
+int bson_ensure_space( bson *b, const int bytesNeeded ) {
+ int pos = b->cur - b->data;
+ char *orig = b->data;
+ int new_size;
+
+ if ( pos + bytesNeeded <= b->dataSize )
+ return BSON_OK;
+
+ new_size = 1.5 * ( b->dataSize + bytesNeeded );
+
+ if( new_size < b->dataSize ) {
+ if( ( b->dataSize + bytesNeeded ) < INT_MAX )
+ new_size = INT_MAX;
+ else {
+ b->err = BSON_SIZE_OVERFLOW;
+ return BSON_ERROR;
+ }
+ }
+
+ b->data = bson_realloc( b->data, new_size );
+ if ( !b->data )
+ bson_fatal_msg( !!b->data, "realloc() failed" );
+
+ b->dataSize = new_size;
+ b->cur += b->data - orig;
+
+ return BSON_OK;
+}
+
+int bson_finish( bson *b ) {
+ int i;
+
+ if( b->err & BSON_NOT_UTF8 )
+ return BSON_ERROR;
+
+ if ( ! b->finished ) {
+ if ( bson_ensure_space( b, 1 ) == BSON_ERROR ) return BSON_ERROR;
+ bson_append_byte( b, 0 );
+ i = b->cur - b->data;
+ bson_little_endian32( b->data, &i );
+ b->finished = 1;
+ }
+
+ return BSON_OK;
+}
+
+void bson_destroy( bson *b ) {
+ bson_free( b->data );
+ b->err = 0;
+ b->data = 0;
+ b->cur = 0;
+ b->finished = 1;
+}
+
+static int bson_append_estart( bson *b, int type, const char *name, const int dataSize ) {
+ const int len = strlen( name ) + 1;
+
+ if ( b->finished ) {
+ b->err |= BSON_ALREADY_FINISHED;
+ return BSON_ERROR;
+ }
+
+ if ( bson_ensure_space( b, 1 + len + dataSize ) == BSON_ERROR ) {
+ return BSON_ERROR;
+ }
+
+ if( bson_check_field_name( b, ( const char * )name, len - 1 ) == BSON_ERROR ) {
+ bson_builder_error( b );
+ return BSON_ERROR;
+ }
+
+ bson_append_byte( b, ( char )type );
+ bson_append( b, name, len );
+ return BSON_OK;
+}
+
+/* ----------------------------
+ BUILDING TYPES
+ ------------------------------ */
+
+int bson_append_int( bson *b, const char *name, const int i ) {
+ if ( bson_append_estart( b, BSON_INT, name, 4 ) == BSON_ERROR )
+ return BSON_ERROR;
+ bson_append32( b , &i );
+ return BSON_OK;
+}
+
+int bson_append_long( bson *b, const char *name, const int64_t i ) {
+ if ( bson_append_estart( b , BSON_LONG, name, 8 ) == BSON_ERROR )
+ return BSON_ERROR;
+ bson_append64( b , &i );
+ return BSON_OK;
+}
+
+int bson_append_double( bson *b, const char *name, const double d ) {
+ if ( bson_append_estart( b, BSON_DOUBLE, name, 8 ) == BSON_ERROR )
+ return BSON_ERROR;
+ bson_append64( b , &d );
+ return BSON_OK;
+}
+
+int bson_append_bool( bson *b, const char *name, const bson_bool_t i ) {
+ if ( bson_append_estart( b, BSON_BOOL, name, 1 ) == BSON_ERROR )
+ return BSON_ERROR;
+ bson_append_byte( b , i != 0 );
+ return BSON_OK;
+}
+
+int bson_append_null( bson *b, const char *name ) {
+ if ( bson_append_estart( b , BSON_NULL, name, 0 ) == BSON_ERROR )
+ return BSON_ERROR;
+ return BSON_OK;
+}
+
+int bson_append_undefined( bson *b, const char *name ) {
+ if ( bson_append_estart( b, BSON_UNDEFINED, name, 0 ) == BSON_ERROR )
+ return BSON_ERROR;
+ return BSON_OK;
+}
+
+static int bson_append_string_base( bson *b, const char *name,
+ const char *value, int len, bson_type type ) {
+
+ int sl = len + 1;
+ if ( bson_check_string( b, ( const char * )value, sl - 1 ) == BSON_ERROR )
+ return BSON_ERROR;
+ if ( bson_append_estart( b, type, name, 4 + sl ) == BSON_ERROR ) {
+ return BSON_ERROR;
+ }
+ bson_append32( b , &sl );
+ bson_append( b , value , sl - 1 );
+ bson_append( b , "\0" , 1 );
+ return BSON_OK;
+}
+
+int bson_append_string( bson *b, const char *name, const char *value ) {
+ return bson_append_string_base( b, name, value, strlen ( value ), BSON_STRING );
+}
+
+int bson_append_symbol( bson *b, const char *name, const char *value ) {
+ return bson_append_string_base( b, name, value, strlen ( value ), BSON_SYMBOL );
+}
+
+int bson_append_code( bson *b, const char *name, const char *value ) {
+ return bson_append_string_base( b, name, value, strlen ( value ), BSON_CODE );
+}
+
+int bson_append_string_n( bson *b, const char *name, const char *value, int len ) {
+ return bson_append_string_base( b, name, value, len, BSON_STRING );
+}
+
+int bson_append_symbol_n( bson *b, const char *name, const char *value, int len ) {
+ return bson_append_string_base( b, name, value, len, BSON_SYMBOL );
+}
+
+int bson_append_code_n( bson *b, const char *name, const char *value, int len ) {
+ return bson_append_string_base( b, name, value, len, BSON_CODE );
+}
+
+int bson_append_code_w_scope_n( bson *b, const char *name,
+ const char *code, int len, const bson *scope ) {
+
+ int sl = len + 1;
+ int size = 4 + 4 + sl + bson_size( scope );
+ if ( bson_append_estart( b, BSON_CODEWSCOPE, name, size ) == BSON_ERROR )
+ return BSON_ERROR;
+ bson_append32( b, &size );
+ bson_append32( b, &sl );
+ bson_append( b, code, sl );
+ bson_append( b, scope->data, bson_size( scope ) );
+ return BSON_OK;
+}
+
+int bson_append_code_w_scope( bson *b, const char *name, const char *code, const bson *scope ) {
+ return bson_append_code_w_scope_n( b, name, code, strlen ( code ), scope );
+}
+
+int bson_append_binary( bson *b, const char *name, char type, const char *str, int len ) {
+ if ( type == BSON_BIN_BINARY_OLD ) {
+ int subtwolen = len + 4;
+ if ( bson_append_estart( b, BSON_BINDATA, name, 4+1+4+len ) == BSON_ERROR )
+ return BSON_ERROR;
+ bson_append32( b, &subtwolen );
+ bson_append_byte( b, type );
+ bson_append32( b, &len );
+ bson_append( b, str, len );
+ } else {
+ if ( bson_append_estart( b, BSON_BINDATA, name, 4+1+len ) == BSON_ERROR )
+ return BSON_ERROR;
+ bson_append32( b, &len );
+ bson_append_byte( b, type );
+ bson_append( b, str, len );
+ }
+ return BSON_OK;
+}
+
+int bson_append_oid( bson *b, const char *name, const bson_oid_t *oid ) {
+ if ( bson_append_estart( b, BSON_OID, name, 12 ) == BSON_ERROR )
+ return BSON_ERROR;
+ bson_append( b , oid , 12 );
+ return BSON_OK;
+}
+
+int bson_append_new_oid( bson *b, const char *name ) {
+ bson_oid_t oid;
+ bson_oid_gen( &oid );
+ return bson_append_oid( b, name, &oid );
+}
+
+int bson_append_regex( bson *b, const char *name, const char *pattern, const char *opts ) {
+ const int plen = strlen( pattern )+1;
+ const int olen = strlen( opts )+1;
+ if ( bson_append_estart( b, BSON_REGEX, name, plen + olen ) == BSON_ERROR )
+ return BSON_ERROR;
+ if ( bson_check_string( b, pattern, plen - 1 ) == BSON_ERROR )
+ return BSON_ERROR;
+ bson_append( b , pattern , plen );
+ bson_append( b , opts , olen );
+ return BSON_OK;
+}
+
+int bson_append_bson( bson *b, const char *name, const bson *bson ) {
+ if ( bson_append_estart( b, BSON_OBJECT, name, bson_size( bson ) ) == BSON_ERROR )
+ return BSON_ERROR;
+ bson_append( b , bson->data , bson_size( bson ) );
+ return BSON_OK;
+}
+
+int bson_append_element( bson *b, const char *name_or_null, const bson_iterator *elem ) {
+ bson_iterator next = *elem;
+ int size;
+
+ bson_iterator_next( &next );
+ size = next.cur - elem->cur;
+
+ if ( name_or_null == NULL ) {
+ if( bson_ensure_space( b, size ) == BSON_ERROR )
+ return BSON_ERROR;
+ bson_append( b, elem->cur, size );
+ } else {
+ int data_size = size - 2 - strlen( bson_iterator_key( elem ) );
+ bson_append_estart( b, elem->cur[0], name_or_null, data_size );
+ bson_append( b, bson_iterator_value( elem ), data_size );
+ }
+
+ return BSON_OK;
+}
+
+int bson_append_timestamp( bson *b, const char *name, bson_timestamp_t *ts ) {
+ if ( bson_append_estart( b, BSON_TIMESTAMP, name, 8 ) == BSON_ERROR ) return BSON_ERROR;
+
+ bson_append32( b , &( ts->i ) );
+ bson_append32( b , &( ts->t ) );
+
+ return BSON_OK;
+}
+
+int bson_append_date( bson *b, const char *name, bson_date_t millis ) {
+ if ( bson_append_estart( b, BSON_DATE, name, 8 ) == BSON_ERROR ) return BSON_ERROR;
+ bson_append64( b , &millis );
+ return BSON_OK;
+}
+
+int bson_append_time_t( bson *b, const char *name, time_t secs ) {
+ return bson_append_date( b, name, ( bson_date_t )secs * 1000 );
+}
+
+int bson_append_start_object( bson *b, const char *name ) {
+ if ( bson_append_estart( b, BSON_OBJECT, name, 5 ) == BSON_ERROR ) return BSON_ERROR;
+ b->stack[ b->stackPos++ ] = b->cur - b->data;
+ bson_append32( b , &zero );
+ return BSON_OK;
+}
+
+int bson_append_start_array( bson *b, const char *name ) {
+ if ( bson_append_estart( b, BSON_ARRAY, name, 5 ) == BSON_ERROR ) return BSON_ERROR;
+ b->stack[ b->stackPos++ ] = b->cur - b->data;
+ bson_append32( b , &zero );
+ return BSON_OK;
+}
+
+int bson_append_finish_object( bson *b ) {
+ char *start;
+ int i;
+ if ( bson_ensure_space( b, 1 ) == BSON_ERROR ) return BSON_ERROR;
+ bson_append_byte( b , 0 );
+
+ start = b->data + b->stack[ --b->stackPos ];
+ i = b->cur - start;
+ bson_little_endian32( start, &i );
+
+ return BSON_OK;
+}
+
+int bson_append_finish_array( bson *b ) {
+ return bson_append_finish_object( b );
+}
+
+
+/* Error handling and allocators. */
+
+static bson_err_handler err_handler = NULL;
+
+bson_err_handler set_bson_err_handler( bson_err_handler func ) {
+ bson_err_handler old = err_handler;
+ err_handler = func;
+ return old;
+}
+
+void *bson_malloc( int size ) {
+ void *p;
+ p = bson_malloc_func( size );
+ bson_fatal_msg( !!p, "malloc() failed" );
+ return p;
+}
+
+void *bson_realloc( void *ptr, int size ) {
+ void *p;
+ p = bson_realloc_func( ptr, size );
+ bson_fatal_msg( !!p, "realloc() failed" );
+ return p;
+}
+
+int _bson_errprintf( const char *format, ... ) {
+ va_list ap;
+ int ret;
+ va_start( ap, format );
+ ret = vfprintf( stderr, format, ap );
+ va_end( ap );
+
+ return ret;
+}
+
+/**
+ * This method is invoked when a non-fatal bson error is encountered.
+ * Calls the error handler if available.
+ *
+ * @param
+ */
+void bson_builder_error( bson *b ) {
+ if( err_handler )
+ err_handler( "BSON error." );
+}
+
+void bson_fatal( int ok ) {
+ bson_fatal_msg( ok, "" );
+}
+
+void bson_fatal_msg( int ok , const char *msg ) {
+ if ( ok )
+ return;
+
+ if ( err_handler ) {
+ err_handler( msg );
+ }
+
+ bson_errprintf( "error: %s\n" , msg );
+ exit( -5 );
+}
+
+
+/* Efficiently copy an integer to a string. */
+
+void bson_numstr( char *str, int i ) {
+ if( i < 1000 )
+ memcpy( str, bson_numstrs[i], 4 );
+ else
+ bson_sprintf( str,"%d", i );
+}
+
+/* encoding.c */
+
+/*
+ * Copyright 2009-2011 10gen, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Portions Copyright 2001 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+/*
+ * Index into the table below with the first byte of a UTF-8 sequence to
+ * get the number of trailing bytes that are supposed to follow it.
+ */
+static const char trailingBytesForUTF8[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
+};
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Utility routine to tell whether a sequence of bytes is legal UTF-8.
+ * This must be called with the length pre-determined by the first byte.
+ * The length can be set by:
+ * length = trailingBytesForUTF8[*source]+1;
+ * and the sequence is illegal right away if there aren't that many bytes
+ * available.
+ * If presented with a length > 4, this returns 0. The Unicode
+ * definition of UTF-8 goes up to 4-byte sequences.
+ */
+static int isLegalUTF8( const unsigned char *source, int length ) {
+ unsigned char a;
+ const unsigned char *srcptr = source + length;
+ switch ( length ) {
+ default:
+ return 0;
+ /* Everything else falls through when "true"... */
+ case 4:
+ if ( ( a = ( *--srcptr ) ) < 0x80 || a > 0xBF ) return 0;
+ case 3:
+ if ( ( a = ( *--srcptr ) ) < 0x80 || a > 0xBF ) return 0;
+ case 2:
+ if ( ( a = ( *--srcptr ) ) > 0xBF ) return 0;
+ switch ( *source ) {
+ /* no fall-through in this inner switch */
+ case 0xE0:
+ if ( a < 0xA0 ) return 0;
+ break;
+ case 0xF0:
+ if ( a < 0x90 ) return 0;
+ break;
+ case 0xF4:
+ if ( a > 0x8F ) return 0;
+ break;
+ default:
+ if ( a < 0x80 ) return 0;
+ }
+ case 1:
+ if ( *source >= 0x80 && *source < 0xC2 ) return 0;
+ if ( *source > 0xF4 ) return 0;
+ }
+ return 1;
+}
+
+static int bson_validate_string( bson *b, const unsigned char *string,
+ const int length, const char check_utf8, const char check_dot,
+ const char check_dollar ) {
+
+ int position = 0;
+ int sequence_length = 1;
+
+ if( check_dollar && string[0] == '$' ) {
+ b->err |= BSON_FIELD_INIT_DOLLAR;
+ }
+
+ while ( position < length ) {
+ if ( check_dot && *( string + position ) == '.' ) {
+ b->err |= BSON_FIELD_HAS_DOT;
+ }
+
+ if ( check_utf8 ) {
+ sequence_length = trailingBytesForUTF8[*( string + position )] + 1;
+ if ( ( position + sequence_length ) > length ) {
+ b->err |= BSON_NOT_UTF8;
+ return BSON_ERROR;
+ }
+ if ( !isLegalUTF8( string + position, sequence_length ) ) {
+ b->err |= BSON_NOT_UTF8;
+ return BSON_ERROR;
+ }
+ }
+ position += sequence_length;
+ }
+
+ return BSON_OK;
+}
+
+
+int bson_check_string( bson *b, const char *string,
+ const int length ) {
+
+ return bson_validate_string( b, ( const unsigned char * )string, length, 1, 0, 0 );
+}
+
+int bson_check_field_name( bson *b, const char *string,
+ const int length ) {
+
+ return bson_validate_string( b, ( const unsigned char * )string, length, 1, 1, 1 );
+} \ No newline at end of file
diff --git a/src/interface.c b/src/interface.c
index 472fd9b..bb34c15 100644
--- a/src/interface.c
+++ b/src/interface.c
@@ -26,6 +26,7 @@
#include <pythonconsole.h>
#endif
#include <powdergraphics.h>
+#include "save.h"
SDLMod sdl_mod;
int sdl_key, sdl_rkey, sdl_wheel, sdl_caps=0, sdl_ascii, sdl_zoom_trig=0;
@@ -4265,7 +4266,7 @@ int open_ui(pixel *vid_buf, char *save_id, char *save_date)
if (queue_open) {
if (info_ready && data_ready) {
// Do Open!
- status = parse_save(data, data_size, 1, 0, 0, bmap, fvx, fvy, signs, parts, pmap);
+ status = parse_save(data, data_size, 1, 0, 0, bmap, vx, vy, pv, fvx, fvy, signs, parts, pmap);
if (!status) {
if(svf_last)
free(svf_last);
@@ -4827,7 +4828,7 @@ void execute_save(pixel *vid_buf)
plens[0] = strlen(svf_name);
uploadparts[1] = svf_description;
plens[1] = strlen(svf_description);
- uploadparts[2] = build_save(plens+2, 0, 0, XRES, YRES, bmap, fvx, fvy, signs, parts);
+ uploadparts[2] = build_save(plens+2, 0, 0, XRES, YRES, bmap, vx, vy, pv, fvx, fvy, signs, parts);
uploadparts[3] = build_thumb(plens+3, 1);
uploadparts[4] = (svf_publish==1)?"Public":"Private";
plens[4] = strlen((svf_publish==1)?"Public":"Private");
@@ -5945,9 +5946,12 @@ int save_filename_ui(pixel *vid_buf)
pixel *save = NULL;//calloc((XRES/3)*(YRES/3), PIXELSIZE);
ui_edit ed;
- save_data = build_save(&save_size, 0, 0, XRES, YRES, bmap, fvx, fvy, signs, parts);
+ save_data = build_save(&save_size, 0, 0, XRES, YRES, bmap, vx, vy, pv, fvx, fvy, signs, parts);
save_data_image = prerender_save(save_data, save_size, &imgw, &imgh);
- save = resample_img(save_data_image, imgw, imgh, XRES/3, YRES/3);
+ if(save_data_image!=NULL)
+ {
+ save = resample_img(save_data_image, imgw, imgh, XRES/3, YRES/3);
+ }
ed.x = x0+11;
ed.y = y0+25;
@@ -5993,7 +5997,10 @@ int save_filename_ui(pixel *vid_buf)
drawrect(vid_buf, x0, y0, xsize, ysize, 192, 192, 192, 255);
drawtext(vid_buf, x0+8, y0+8, "Filename:", 255, 255, 255, 255);
drawrect(vid_buf, x0+8, y0+20, xsize-16, 16, 255, 255, 255, 180);
- draw_image(vid_buf, save, x0+8, y0+40, XRES/3, YRES/3, 255);
+ if(save!=NULL)
+ {
+ draw_image(vid_buf, save, x0+8, y0+40, XRES/3, YRES/3, 255);
+ }
drawrect(vid_buf, x0+8, y0+40, XRES/3, YRES/3, 192, 192, 192, 255);
drawrect(vid_buf, x0, y0+ysize-16, xsize, 16, 192, 192, 192, 255);
@@ -6204,7 +6211,7 @@ void catalogue_ui(pixel * vid_buf)
void *data;
data = file_load(csave->filename, &size);
if(data){
- status = parse_save(data, size, 1, 0, 0, bmap, fvx, fvy, signs, parts, pmap);
+ status = parse_save(data, size, 1, 0, 0, bmap, vx, vy, pv, fvx, fvy, signs, parts, pmap);
if(!status)
{
//svf_filename[0] = 0;
diff --git a/src/main.c b/src/main.c
index fd4f34e..a835e3e 100644
--- a/src/main.c
+++ b/src/main.c
@@ -60,6 +60,7 @@
#ifdef LUACONSOLE
#include "luaconsole.h"
#endif
+#include "save.h"
pixel *vid_buf;
@@ -270,858 +271,6 @@ void dump_frame(pixel *src, int w, int h, int pitch)
* STATE MANAGEMENT *
***********************************************************/
-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;
-}
-
-//the saving function
-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 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(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 clear_sim(void)
{
int i, x, y;
@@ -1252,7 +401,7 @@ void stamp_save(int x, int y, int w, int h)
FILE *f;
int n;
char fn[64], sn[16];
- void *s=build_save(&n, x, y, w, h, bmap, fvx, fvy, signs, parts);
+ void *s=build_save(&n, x, y, w, h, bmap, vx, vy, pv, fvx, fvy, signs, parts);
#ifdef WIN32
_mkdir("stamps");
@@ -1489,7 +638,7 @@ int main(int argc, char *argv[])
if(load_data && load_size){
int parsestate = 0;
//parsestate = parse_save(load_data, load_size, 1, 0, 0);
- parsestate = parse_save(load_data, load_size, 1, 0, 0, bmap, fvx, fvy, signs, parts, pmap);
+ parsestate = parse_save(load_data, load_size, 1, 0, 0, bmap, vx, vy, pv, fvx, fvy, signs, parts, pmap);
for(i=0; i<30; i++){
memset(vid_buf, 0, (XRES+BARSIZE)*YRES*PIXELSIZE);
@@ -1632,7 +781,7 @@ int main(int argc, char *argv[])
{
svf_last = file_data;
svf_lsize = size;
- if(!parse_save(file_data, size, 1, 0, 0, bmap, fvx, fvy, signs, parts, pmap))
+ if(!parse_save(file_data, size, 1, 0, 0, bmap, fvx, fvy, vx, vy, pv, signs, parts, pmap))
{
it=0;
svf_filename[0] = 0;
@@ -2876,7 +2025,7 @@ int main(int argc, char *argv[])
if (load_y<0) load_y=0;
if (bq==1 && !b)
{
- parse_save(load_data, load_size, 0, load_x, load_y, bmap, fvx, fvy, signs, parts, pmap);
+ parse_save(load_data, load_size, 0, load_x, load_y, bmap, vx, vy, pv, fvx, fvy, signs, parts, pmap);
free(load_data);
free(load_img);
load_mode = 0;
@@ -2918,14 +2067,14 @@ int main(int argc, char *argv[])
{
if (copy_mode==1)//CTRL-C, copy
{
- clipboard_data=build_save(&clipboard_length, save_x, save_y, save_w, save_h, bmap, fvx, fvy, signs, parts);
+ clipboard_data=build_save(&clipboard_length, save_x, save_y, save_w, save_h, bmap, vx, vy, pv, fvx, fvy, signs, parts);
clipboard_ready = 1;
save_mode = 0;
copy_mode = 0;
}
else if (copy_mode==2)//CTRL-X, cut
{
- clipboard_data=build_save(&clipboard_length, save_x, save_y, save_w, save_h, bmap, fvx, fvy, signs, parts);
+ clipboard_data=build_save(&clipboard_length, save_x, save_y, save_w, save_h, bmap, vx, vy, pv, fvx, fvy, signs, parts);
clipboard_ready = 1;
save_mode = 0;
copy_mode = 0;
@@ -3061,7 +2210,7 @@ int main(int argc, char *argv[])
}
if (x>=19 && x<=35 && svf_last && (svf_open || svf_fileopen) && !bq) {
//int tpval = sys_pause;
- parse_save(svf_last, svf_lsize, 1, 0, 0, bmap, fvx, fvy, signs, parts, pmap);
+ parse_save(svf_last, svf_lsize, 1, 0, 0, bmap, vx, vy, pv, fvx, fvy, signs, parts, pmap);
//sys_pause = tpval;
}
if (x>=(XRES+BARSIZE-(510-476)) && x<=(XRES+BARSIZE-(510-491)) && !bq)
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