static libspectrum_error rzx_read_sign_start( libspectrum_rzx *rzx, const libspectrum_byte **ptr, const libspectrum_byte *end ) { rzx_block_t *block; libspectrum_dword length; /* Check we've got enough data for the length */ if( end - (*ptr) < 4 ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "rzx_read_sign_start: not enough data in buffer" ); return LIBSPECTRUM_ERROR_CORRUPT; } length = libspectrum_read_dword( ptr ); /* Check the length is at least the expected 13 bytes */ if( length < 13 ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "rzx_read_sign_start: block length %lu less than the minimum 13 bytes", (unsigned long)length ); return LIBSPECTRUM_ERROR_CORRUPT; } /* Check there's still enough data (the -5 is because we've already read the length and the block ID) */ if( end - (*ptr) < (ptrdiff_t)length - 5 ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "rzx_read_sign_start: not enough data in buffer" ); return LIBSPECTRUM_ERROR_CORRUPT; } block_alloc( &block, LIBSPECTRUM_RZX_SIGN_START_BLOCK ); block->types.keyid = libspectrum_read_dword( ptr ); /* Skip the week code */ *ptr += 4; /* Skip anything we don't know about */ *ptr += length - 13; rzx->blocks = g_slist_append( rzx->blocks, block ); return LIBSPECTRUM_ERROR_NONE; }
static libspectrum_error rzx_read_creator( const libspectrum_byte **ptr, const libspectrum_byte *end ) { size_t length; /* Check we've got enough data for the block */ if( end - (*ptr) < 28 ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "rzx_read_creator: not enough data in buffer" ); return LIBSPECTRUM_ERROR_CORRUPT; } /* Get the length */ length = libspectrum_read_dword( ptr ); /* Check there's still enough data (the -5 is because we've already read the block ID and the length) */ if( end - (*ptr) < (ptrdiff_t)length - 5 ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "rzx_read_creator: not enough data in buffer" ); return LIBSPECTRUM_ERROR_CORRUPT; } (*ptr) += length - 5; return LIBSPECTRUM_ERROR_NONE; }
static libspectrum_error rzx_read_header( const libspectrum_byte **ptr, const libspectrum_byte *end ) { libspectrum_dword flags; /* Check the header exists */ if( end - (*ptr) < (ptrdiff_t)strlen( rzx_signature ) + 6 ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "rzx_read_header: not enough data in buffer" ); return LIBSPECTRUM_ERROR_CORRUPT; } /* Check the RZX signature exists */ if( memcmp( *ptr, rzx_signature, strlen( rzx_signature ) ) ) { libspectrum_print_error( LIBSPECTRUM_ERROR_SIGNATURE, "rzx_read_header: RZX signature not found" ); return LIBSPECTRUM_ERROR_SIGNATURE; } /* Skip over the signature and the version numbers */ (*ptr) += strlen( rzx_signature ) + 2; flags = libspectrum_read_dword( ptr ); /* FIXME: how to handle signatures */ /* This is where the signed data starts (if it's signed at all) */ /* if( signature && ( flags & 0x01 ) ) signature->start = *ptr; */ return LIBSPECTRUM_ERROR_NONE; }
libspectrum_error libspectrum_tape_block_read_symbol_table_parameters( libspectrum_tape_block *block, int pilot, const libspectrum_byte **ptr ) { libspectrum_tape_generalised_data_block *generalised = &block->types.generalised_data; libspectrum_tape_generalised_data_symbol_table *table = pilot ? &generalised->pilot_table : &generalised->data_table; table->symbols_in_block = libspectrum_read_dword( ptr ); table->max_pulses = (*ptr)[0]; table->symbols_in_table = (*ptr)[1]; if( !table->symbols_in_table ) table->symbols_in_table = 256; (*ptr) += 2; return LIBSPECTRUM_ERROR_NONE; }
static libspectrum_error inflate_block( libspectrum_byte **uncompressed, size_t *uncompressed_length, const libspectrum_byte **compressed, size_t compressed_length ) { #ifdef HAVE_ZLIB_H libspectrum_dword header_length, expected_crc32, actual_crc32; libspectrum_byte *zlib_buffer; unsigned long actual_length; int error; /* First, look at the compression header */ header_length = libspectrum_read_dword( compressed ); if( header_length != 12 ) { libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "zxs_inflate_block: unknown header length %lu", (unsigned long)header_length ); return LIBSPECTRUM_ERROR_UNKNOWN; } compressed_length -= 12; expected_crc32 = libspectrum_read_dword( compressed ); *uncompressed_length = libspectrum_read_dword( compressed ); /* Some space to put the zlib header for decompression */ zlib_buffer = libspectrum_malloc( ( compressed_length + 6 ) * sizeof( *zlib_buffer ) ); /* zlib's header */ zlib_buffer[0] = 0x78; zlib_buffer[1] = 0xda; memcpy( &zlib_buffer[2], *compressed, compressed_length ); *compressed += compressed_length; *uncompressed = libspectrum_malloc( *uncompressed_length * sizeof( **uncompressed ) ); actual_length = *uncompressed_length; error = uncompress( *uncompressed, &actual_length, zlib_buffer, compressed_length + 6 ); /* At this point, we expect to get a Z_DATA_ERROR, as we don't have the Adler-32 checksum of the data. There is a 1 in 65521 chance that the random bytes will match, so we might (very rarely) get Z_OK */ if( error != Z_DATA_ERROR && error != Z_OK ) { libspectrum_free( *uncompressed ); libspectrum_free( zlib_buffer ); libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "zxs_inflate_block: unexpected zlib error" ); return LIBSPECTRUM_ERROR_CORRUPT; } if( *uncompressed_length != actual_length ) { libspectrum_free( *uncompressed ); libspectrum_free( zlib_buffer ); libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "zxs_inflate_block: block expanded to 0x%04lx, not the expected 0x%04lx bytes", actual_length, (unsigned long)*uncompressed_length ); return LIBSPECTRUM_ERROR_CORRUPT; } libspectrum_free( zlib_buffer ); actual_crc32 = crc32( 0, Z_NULL, 0 ); actual_crc32 = crc32( actual_crc32, *uncompressed, *uncompressed_length ); if( actual_crc32 != expected_crc32 ) { libspectrum_free( *uncompressed ); libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "zxs_inflate_block: crc 0x%08x does not match expected 0x%08x", actual_crc32, expected_crc32 ); return LIBSPECTRUM_ERROR_CORRUPT; } return LIBSPECTRUM_ERROR_NONE; #else /* #ifdef HAVE_ZLIB_H */ /* No zlib, so can't inflate the block */ return LIBSPECTRUM_ERROR_UNKNOWN; #endif /* #ifdef HAVE_ZLIB_H */ }
static libspectrum_error rzx_read_input( libspectrum_rzx *rzx, const libspectrum_byte **ptr, const libspectrum_byte *end ) { size_t blocklength; libspectrum_dword flags; int compressed; libspectrum_error error; rzx_block_t *rzx_block; input_block_t *block; /* Check we've got enough data for the block */ if( end - (*ptr) < 18 ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "rzx_read_input: not enough data in buffer" ); return LIBSPECTRUM_ERROR_CORRUPT; } block_alloc( &rzx_block, LIBSPECTRUM_RZX_INPUT_BLOCK ); block = &( rzx_block->types.input ); /* Get the length and number of frames */ blocklength = libspectrum_read_dword( ptr ); block->count = libspectrum_read_dword( ptr ); /* Frame size is undefined, so just skip it */ (*ptr)++; /* Allocate memory for the frames */ block->frames = libspectrum_malloc( block->count * sizeof( *block->frames ) ); block->allocated = block->count; /* Fetch the T-state counter and the flags */ block->tstates = libspectrum_read_dword( ptr ); flags = libspectrum_read_dword( ptr ); compressed = flags & 0x02; if( compressed ) { #ifdef HAVE_ZLIB_H libspectrum_byte *data; const libspectrum_byte *data_ptr; size_t data_length = 0; /* Discount the block intro */ blocklength -= 18; /* Check that we've got enough compressed data */ if( end - (*ptr) < (ptrdiff_t)blocklength ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "rzx_read_input: not enough data in buffer" ); libspectrum_free( rzx_block ); return LIBSPECTRUM_ERROR_CORRUPT; } error = libspectrum_zlib_inflate( *ptr, blocklength, &data, &data_length ); if( error != LIBSPECTRUM_ERROR_NONE ) { block_free( rzx_block ); return error; } *ptr += blocklength; data_ptr = data; error = rzx_read_frames( block, &data_ptr, data + data_length ); if( error ) { libspectrum_free( rzx_block ); libspectrum_free( data ); return error; } libspectrum_free( data ); #else /* #ifdef HAVE_ZLIB_H */ libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "rzx_read_input: zlib needed for decompression" ); libspectrum_free( rzx_block ); return LIBSPECTRUM_ERROR_UNKNOWN; #endif /* #ifdef HAVE_ZLIB_H */ } else { /* Data not compressed */ error = rzx_read_frames( block, ptr, end ); if( error ) { libspectrum_free( rzx_block ); return error; } } rzx->blocks = g_slist_append( rzx->blocks, rzx_block ); return LIBSPECTRUM_ERROR_NONE; }
static libspectrum_error rzx_read_snapshot( libspectrum_rzx *rzx, const libspectrum_byte **ptr, const libspectrum_byte *end ) { rzx_block_t *block; libspectrum_snap *snap; size_t blocklength, snaplength; libspectrum_error error = LIBSPECTRUM_ERROR_NONE; libspectrum_dword flags; const libspectrum_byte *snap_ptr; int done; snapshot_string_t *type; /* For deflated snapshot data: */ int compressed; libspectrum_byte *gzsnap = NULL; size_t uncompressed_length = 0; if( end - (*ptr) < 16 ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "rzx_read_snapshot: not enough data in buffer" ); return LIBSPECTRUM_ERROR_CORRUPT; } blocklength = libspectrum_read_dword( ptr ); if( end - (*ptr) < (ptrdiff_t)blocklength - 5 ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "rzx_read_snapshot: not enough data in buffer" ); return LIBSPECTRUM_ERROR_CORRUPT; } /* See if we want a compressed snap */ flags = libspectrum_read_dword( ptr ); /* We don't handle 'links' to external snapshots. I really think these are just more trouble than they're worth */ if( flags & 0x01 ) { libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "rzx_read_snapshot: skipping external snapshot" ); (*ptr) += blocklength - 9; return LIBSPECTRUM_ERROR_NONE; } /* Do we have a compressed snapshot? */ compressed = flags & 0x02; /* How long is the (uncompressed) snap? */ (*ptr) += 4; snaplength = libspectrum_read_dword( ptr ); (*ptr) -= 8; /* If compressed, uncompress the data */ if( compressed ) { #ifdef HAVE_ZLIB_H error = libspectrum_zlib_inflate( (*ptr) + 8, blocklength - 17, &gzsnap, &uncompressed_length ); if( error != LIBSPECTRUM_ERROR_NONE ) return error; if( uncompressed_length != snaplength ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "rzx_read_snapshot: compressed snapshot has wrong length" ); libspectrum_free( gzsnap ); return LIBSPECTRUM_ERROR_CORRUPT; } snap_ptr = gzsnap; #else /* #ifdef HAVE_ZLIB_H */ libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "rzx_read_snapshot: zlib needed for decompression\n" ); return LIBSPECTRUM_ERROR_UNKNOWN; #endif /* #ifdef HAVE_ZLIB_H */ } else { /* If not compressed, check things are consistent */ if( blocklength != snaplength + 17 ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "rzx_read_snapshot: inconsistent snapshot lengths" ); return LIBSPECTRUM_ERROR_CORRUPT; } snap_ptr = (*ptr) + 8; uncompressed_length = snaplength; } block_alloc( &block, LIBSPECTRUM_RZX_SNAPSHOT_BLOCK ); block->types.snap.snap = libspectrum_snap_alloc(); block->types.snap.automatic = 0; snap = block->types.snap.snap; for( done = 0, type = snapshot_strings; type->format; type++ ) { if( !strncasecmp( (char*)*ptr, type->string, 4 ) ) { error = libspectrum_snap_read( snap, snap_ptr, uncompressed_length, type->format, NULL ); done = 1; } } if( !done ) { libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "%s:rzx_read_snapshot: unrecognised snapshot format", __FILE__ ); if( compressed ) libspectrum_free( gzsnap ); block_free( block ); return LIBSPECTRUM_ERROR_UNKNOWN; } if( error != LIBSPECTRUM_ERROR_NONE ) { if( compressed ) libspectrum_free( gzsnap ); block_free( block ); return error; } /* Free the decompressed data (if we created it) */ if( compressed ) libspectrum_free( gzsnap ); /* Skip over the data */ (*ptr) += blocklength - 9; rzx->blocks = g_slist_append( rzx->blocks, block ); return LIBSPECTRUM_ERROR_NONE; }
static libspectrum_error rzx_read_sign_end( libspectrum_rzx *rzx, const libspectrum_byte **ptr, const libspectrum_byte *end ) { rzx_block_t *block; signature_block_t *signature; size_t length; /* Check we've got enough data for the length */ if( end - (*ptr) < 4 ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "rzx_read_sign_end: not enough data in buffer" ); return LIBSPECTRUM_ERROR_CORRUPT; } /* Get the length; the -5 is because we've read the block ID and the length bytes */ length = libspectrum_read_dword( ptr ) - 5; /* Check there's still enough data */ if( end - (*ptr) < (ptrdiff_t)length ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "rzx_read_sign_end: not enough data in buffer" ); return LIBSPECTRUM_ERROR_CORRUPT; } block_alloc( &block, LIBSPECTRUM_RZX_SIGN_END_BLOCK ); signature = &( block->types.signature ); /* - 5 as we don't sign the block ID and length of this block */ signature->length = ( *ptr - rzx->signed_start ) - 5; #ifdef HAVE_GCRYPT_H { gcry_error_t error; size_t mpi_length; error = gcry_mpi_scan( &signature->r, GCRYMPI_FMT_PGP, *ptr, length, &mpi_length ); if( error ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "error reading 'r': %s", gcry_strerror( error ) ); libspectrum_free( block ); return LIBSPECTRUM_ERROR_CORRUPT; } (*ptr) += mpi_length; length -= mpi_length; error = gcry_mpi_scan( &signature->s, GCRYMPI_FMT_PGP, *ptr, length, &mpi_length ); if( error ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "error reading 's': %s", gcry_strerror( error ) ); gcry_mpi_release( signature->r ); libspectrum_free( block ); return LIBSPECTRUM_ERROR_CORRUPT; } (*ptr) += mpi_length; length -= mpi_length; } #endif /* #ifdef HAVE_GCRYPT_H */ (*ptr) += length; rzx->blocks = g_slist_append( rzx->blocks, block ); return LIBSPECTRUM_ERROR_NONE; }