static libspectrum_error rzx_write_signed_start( libspectrum_byte **buffer, libspectrum_byte **ptr, size_t *length, libspectrum_rzx_dsa_key *key, libspectrum_creator *creator ) { #ifdef HAVE_GCRYPT_H libspectrum_make_room( buffer, 13, ptr, length ); /* Block ID */ *(*ptr)++ = LIBSPECTRUM_RZX_SIGN_START_BLOCK; /* Block length */ libspectrum_write_dword( ptr, 13 ); /* Key ID */ if( !key || !key->y || strlen( key->y ) < 8 ) { libspectrum_print_error( LIBSPECTRUM_ERROR_INVALID, "rzx_write_signed_start: invalid key" ); return LIBSPECTRUM_ERROR_INVALID; } libspectrum_write_dword( ptr, strtoul( &key->y[ strlen( key->y ) - 8 ], NULL, 16 ) ); /* Week code */ if( creator ) { libspectrum_write_dword( ptr, libspectrum_creator_competition_code( creator ) ); } else { libspectrum_write_dword( ptr, 0 ); } #endif /* #ifdef HAVE_GCRYPT_H */ return LIBSPECTRUM_ERROR_NONE; }
libspectrum_error libspectrum_rzx_rollback_to( libspectrum_rzx *rzx, libspectrum_snap **snap, size_t which ) { GSList *previous = NULL, *list; rzx_block_t *block; size_t i; /* Find the nth snapshot block in the file */ for( i = 0, list = rzx->blocks; i <= which; i++, list = list->next ) { list = g_slist_find_custom( list, GINT_TO_POINTER( LIBSPECTRUM_RZX_SNAPSHOT_BLOCK ), find_block ); if( !list ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "snapshot block %lu not found in recording", (unsigned long)which ); return LIBSPECTRUM_ERROR_CORRUPT; } previous = list; } if( rzx->current_input ) { libspectrum_error error; error = libspectrum_rzx_stop_input( rzx ); if( error ) return error; } /* Delete all blocks after the snapshot */ g_slist_foreach( previous->next, block_free_wrapper, NULL ); previous->next = NULL; block = previous->data; *snap = block->types.snap.snap; return LIBSPECTRUM_ERROR_NONE; }
static libspectrum_error pure_data_edge( libspectrum_tape_pure_data_block *block, libspectrum_dword *tstates, int *end_of_block ) { int error; switch( block->state ) { case LIBSPECTRUM_TAPE_STATE_DATA1: /* The first edge for a bit of data */ *tstates = block->bit_tstates; /* Followed by the second edge */ block->state = LIBSPECTRUM_TAPE_STATE_DATA2; break; case LIBSPECTRUM_TAPE_STATE_DATA2: /* The second edge for a bit of data */ *tstates = block->bit_tstates; /* Followed by the next bit of data (or the end of data) */ error = pure_data_next_bit( block ); if( error ) return error; break; case LIBSPECTRUM_TAPE_STATE_PAUSE: /* The pause at the end of the block */ *tstates = (block->pause * 69888)/20; /* FIXME: should vary with tstates per frame */ *end_of_block = 1; break; default: libspectrum_print_error( "pure_data_edge: unknown state %d\n", block->state ); return LIBSPECTRUM_ERROR_LOGIC; } return LIBSPECTRUM_ERROR_NONE; }
/* Select the nth block on the tape */ libspectrum_error libspectrum_tape_nth_block( libspectrum_tape *tape, int n ) { GSList *new_block; libspectrum_error error; new_block = g_slist_nth( tape->blocks, n ); if( !new_block ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "libspectrum_tape_nth_block: tape does not have block %d", n ); return LIBSPECTRUM_ERROR_CORRUPT; } tape->state.current_block = new_block; error = libspectrum_tape_block_init( tape->state.current_block->data, &(tape->state) ); if( error ) return error; return LIBSPECTRUM_ERROR_NONE; }
libspectrum_error libspectrum_tape_block_read_symbol_table( libspectrum_tape_generalised_data_symbol_table *table, const libspectrum_byte **ptr, size_t length ) { if( table->symbols_in_block ) { libspectrum_tape_generalised_data_symbol *symbol; size_t i, j; /* Sanity check */ if( length < ( 2 * table->max_pulses + 1 ) * table->symbols_in_table ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "%s: not enough data in buffer", __func__ ); return LIBSPECTRUM_ERROR_CORRUPT; } table->symbols = libspectrum_new( libspectrum_tape_generalised_data_symbol, table->symbols_in_table ); for( i = 0, symbol = table->symbols; i < table->symbols_in_table; i++, symbol++ ) { symbol->edge_type = **ptr; (*ptr)++; symbol->lengths = libspectrum_new( libspectrum_word, table->max_pulses ); for( j = 0; j < table->max_pulses; j++ ) { symbol->lengths[ j ] = (*ptr)[0] + (*ptr)[1] * 0x100; (*ptr) += 2; } } } return LIBSPECTRUM_ERROR_NONE; }
static libspectrum_error write_48k_sna( libspectrum_byte **buffer, libspectrum_byte **ptr, size_t *length, libspectrum_snap *snap ) { libspectrum_error error; libspectrum_byte *stack, *sp; /* Must have somewhere in RAM to store PC */ if( libspectrum_snap_sp( snap ) < 0x4002 ) { libspectrum_print_error( LIBSPECTRUM_ERROR_INVALID, "SP is too low (0x%04x) to stack PC", libspectrum_snap_sp( snap ) ); return LIBSPECTRUM_ERROR_INVALID; } libspectrum_make_room( buffer, 0xc000, ptr, length ); error = write_page( &( (*ptr)[ 0x0000 ] ), snap, 5 ); if( error ) return error; error = write_page( &( (*ptr)[ 0x4000 ] ), snap, 2 ); if( error ) return error; error = write_page( &( (*ptr)[ 0x8000 ] ), snap, 0 ); if( error ) return error; /* Place PC on the stack */ stack = &( (*ptr)[ libspectrum_snap_sp( snap ) - 0x4000 - 2 ] ); libspectrum_write_word( &stack, libspectrum_snap_pc( snap ) ); *ptr += 0xc000; /* Store the new value of SP */ sp = *buffer + SNA_OFFSET_SP; libspectrum_write_word( &sp, libspectrum_snap_sp( snap ) - 2 ); return LIBSPECTRUM_ERROR_NONE; }
static libspectrum_error get_signature( gcry_mpi_t *r, gcry_mpi_t *s, libspectrum_byte *data, size_t data_length, libspectrum_rzx_dsa_key *key ) { libspectrum_error error; gcry_error_t gcrypt_error; gcry_sexp_t hash, s_key, s_signature; error = get_hash( &hash, data, data_length ); if( error ) return error; error = create_key( &s_key, key, 1 ); if( error ) { gcry_sexp_release( hash ); return error; } gcrypt_error = gcry_pk_sign( &s_signature, hash, s_key ); if( gcrypt_error ) { libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "get_signature: error signing data: %s", gcry_strerror( gcrypt_error ) ); gcry_sexp_release( s_key ); gcry_sexp_release( hash ); return LIBSPECTRUM_ERROR_LOGIC; } gcry_sexp_release( s_key ); gcry_sexp_release( hash ); error = get_mpi( r, s_signature, "r" ); if( error ) { gcry_sexp_release( s_signature ); return error; } error = get_mpi( s, s_signature, "s" ); if( error ) { gcry_sexp_release( s_signature ); gcry_mpi_release( *r ); return error; } gcry_sexp_release( s_signature ); return LIBSPECTRUM_ERROR_NONE; }
libspectrum_error libspectrum_bzip2_inflate( const libspectrum_byte *bzptr, size_t bzlength, libspectrum_byte **outptr, size_t *outlength ) { int error; unsigned int length2; /* Known length, so we can use the easy method */ if( *outlength ) { *outptr = libspectrum_new( libspectrum_byte, *outlength ); length2 = *outlength; error = BZ2_bzBuffToBuffDecompress( (char*)*outptr, &length2, (char*)bzptr, bzlength, 0, 0 ); if( error != BZ_OK ) { libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "error decompressing bzip data" ); return LIBSPECTRUM_ERROR_LOGIC; } *outlength = length2; return LIBSPECTRUM_ERROR_NONE; } else { /* Unknown length, have to stream */ bz_stream stream; libspectrum_byte *ptr; size_t length; length = bzlength; *outptr = libspectrum_new( libspectrum_byte, length ); /* Use standard memory allocation/free routines */ stream.bzalloc = NULL; stream.bzfree = NULL; stream.opaque = NULL; error = BZ2_bzDecompressInit( &stream, 0, 0 ); if( error != BZ_OK ) { switch( error ) { case BZ_MEM_ERROR: libspectrum_print_error( LIBSPECTRUM_ERROR_MEMORY, "out of memory at %s:%d", __FILE__, __LINE__ ); libspectrum_free( *outptr ); return LIBSPECTRUM_ERROR_MEMORY; default: libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "bzip2_inflate: serious error from BZ2_bzDecompressInit: %d", error ); libspectrum_free( *outptr ); return LIBSPECTRUM_ERROR_LOGIC; } } stream.next_in = (char*)bzptr; stream.avail_in = bzlength; stream.next_out = (char*)*outptr; stream.avail_out = bzlength; while( 1 ) { error = BZ2_bzDecompress( &stream ); switch( error ) { case BZ_STREAM_END: /* Finished decompression */ error = BZ2_bzDecompressEnd( &stream ); if( error ) { libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "bzip2_inflate: error from BZ2_bzDecompressEnd: %d", error ); libspectrum_free( *outptr ); return LIBSPECTRUM_ERROR_LOGIC; } *outlength = stream.total_out_lo32; *outptr = libspectrum_renew( libspectrum_byte, *outptr, *outlength ); return LIBSPECTRUM_ERROR_NONE; case BZ_OK: /* More output space required */ length += bzlength; ptr = libspectrum_renew( libspectrum_byte, *outptr, length ); *outptr = ptr; stream.next_out = (char*)*outptr + stream.total_out_lo32; stream.avail_out += bzlength; break; default: libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "bzip2_inflate: serious error from BZ2_bzDecompress: %d", error ); BZ2_bzDecompressEnd( &stream ); libspectrum_free( *outptr ); return LIBSPECTRUM_ERROR_LOGIC; } } /* Matches while( 1 ) { ... } */ } /* Matches if( *outlength ) { ... } */ }
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; }
libspectrum_error libspectrum_sna_write( libspectrum_byte **buffer, size_t *length, int *out_flags, libspectrum_snap *snap, int in_flags GCC_UNUSED ) { libspectrum_error error = LIBSPECTRUM_ERROR_NONE; libspectrum_byte *ptr; /* Minor info loss already due to things like tstate count, halted state, etc which are not stored in .sna format */ *out_flags = LIBSPECTRUM_FLAG_SNAPSHOT_MINOR_INFO_LOSS; /* We don't store +D info at all */ if( libspectrum_snap_plusd_active( snap ) ) *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; /* We don't store Beta info at all */ if( libspectrum_snap_beta_active( snap ) ) *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; /* We don't store Opus info at all */ if( libspectrum_snap_opus_active( snap ) ) *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; /* We don't save IDE interface info at all */ if( libspectrum_snap_zxatasp_active( snap ) ) *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; if( libspectrum_snap_zxcf_active( snap ) ) *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; if( libspectrum_snap_simpleide_active( snap ) ) *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; if( libspectrum_snap_divide_active( snap ) ) *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; /* We don't save the Interface 2 ROM at all */ if( libspectrum_snap_interface2_active( snap ) ) *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; /* We don't save the Timex Dock at all */ if( libspectrum_snap_dock_active( snap ) ) *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; /* We don't save custom ROMs at all */ if( libspectrum_snap_custom_rom( snap ) ) *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; /* We don't save AY interfaces at all */ if( libspectrum_snap_fuller_box_active( snap ) || libspectrum_snap_melodik_active( snap ) ) *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; /* We don't save the Specdrum state at all */ if( libspectrum_snap_specdrum_active( snap ) ) *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; /* We don't save the Spectranet state at all */ if( libspectrum_snap_spectranet_active( snap ) ) *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; /* We don't save the uSource state at all */ if( libspectrum_snap_usource_active( snap ) ) *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; /* We don't save the DISCiPLE state at all */ if( libspectrum_snap_disciple_active( snap ) ) *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; /* We don't save the Didaktik80 state at all */ if( libspectrum_snap_didaktik80_active( snap ) ) *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; ptr = *buffer; write_header( buffer, &ptr, length, snap ); switch( libspectrum_snap_machine( snap ) ) { case LIBSPECTRUM_MACHINE_48_NTSC: case LIBSPECTRUM_MACHINE_TC2048: case LIBSPECTRUM_MACHINE_TC2068: case LIBSPECTRUM_MACHINE_TS2068: *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; /* Fall through */ case LIBSPECTRUM_MACHINE_16: case LIBSPECTRUM_MACHINE_48: error = write_48k_sna( buffer, &ptr, length, snap ); break; case LIBSPECTRUM_MACHINE_128: case LIBSPECTRUM_MACHINE_128E: case LIBSPECTRUM_MACHINE_PENT512: case LIBSPECTRUM_MACHINE_PENT1024: case LIBSPECTRUM_MACHINE_PLUS2: case LIBSPECTRUM_MACHINE_PLUS2A: case LIBSPECTRUM_MACHINE_PLUS3: case LIBSPECTRUM_MACHINE_PLUS3E: case LIBSPECTRUM_MACHINE_SCORP: case LIBSPECTRUM_MACHINE_SE: *out_flags |= LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS; /* Fall through */ case LIBSPECTRUM_MACHINE_PENT: error = write_128k_sna( buffer, &ptr, length, snap ); break; case LIBSPECTRUM_MACHINE_UNKNOWN: libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "Emulated machine type is set to 'unknown'!" ); return LIBSPECTRUM_ERROR_LOGIC; } if( error ) return error; /* Set length to be actual length, not allocated length */ *length = ptr - *buffer; return LIBSPECTRUM_ERROR_NONE; }
libspectrum_error libspectrum_rzx_store_frame( libspectrum_rzx *rzx, size_t instructions, size_t count, libspectrum_byte *in_bytes ) { input_block_t *input; libspectrum_rzx_frame_t *frame; libspectrum_error error; input = rzx->current_input; /* Check we've got an IRB to record to */ if( !input ) { libspectrum_print_error( LIBSPECTRUM_ERROR_INVALID, "libspectrum_rzx_store_frame called with no active input block" ); return LIBSPECTRUM_ERROR_INVALID; } /* Get more space if we need it */ if( input->allocated == input->count ) { error = input_block_resize( input, input->count + 1 ); if( error ) return error; } frame = &input->frames[ input->count ]; frame->instructions = instructions; /* Check for repeated frames */ if( input->count != 0 && count != 0 && count == input->frames[ input->non_repeat ].count && !memcmp( in_bytes, input->frames[ input->non_repeat ].in_bytes, count ) ) { frame->repeat_last = 1; frame->count = 0; frame->in_bytes = NULL; } else { frame->repeat_last = 0; frame->count = count; /* Note this as the last non-repeated frame */ input->non_repeat = input->count; if( count ) { frame->in_bytes = libspectrum_new( libspectrum_byte, count ); memcpy( frame->in_bytes, in_bytes, count * sizeof( *( frame->in_bytes ) ) ); } else { frame->in_bytes = NULL; } } /* Move along to the next frame */ input->count++; return 0; }
libspectrum_error libspectrum_rzx_read( libspectrum_rzx *rzx, const libspectrum_byte *buffer, size_t length ) { libspectrum_error error; const libspectrum_byte *ptr, *end; int uncompressed; libspectrum_byte *new_buffer; libspectrum_id_t raw_type; libspectrum_class_t class; /* Find out if this file needs decompression */ uncompressed = 0; new_buffer = NULL; error = libspectrum_identify_file_raw( &raw_type, NULL, buffer, length ); if( error ) return error; error = libspectrum_identify_class( &class, raw_type ); if( error ) return error; if( class == LIBSPECTRUM_CLASS_COMPRESSED ) { size_t new_length; error = libspectrum_uncompress_file( &new_buffer, &new_length, NULL, raw_type, buffer, length, NULL ); buffer = new_buffer; length = new_length; uncompressed = 1; } ptr = buffer; end = buffer + length; error = rzx_read_header( &ptr, end ); if( error != LIBSPECTRUM_ERROR_NONE ) { libspectrum_free( new_buffer ); return error; } rzx->signed_start = ptr; while( ptr < end ) { libspectrum_byte id; id = *ptr++; switch( id ) { case LIBSPECTRUM_RZX_CREATOR_BLOCK: error = rzx_read_creator( &ptr, end ); if( error != LIBSPECTRUM_ERROR_NONE ) { libspectrum_free( new_buffer ); return error; } break; case LIBSPECTRUM_RZX_SNAPSHOT_BLOCK: error = rzx_read_snapshot( rzx, &ptr, end ); if( error != LIBSPECTRUM_ERROR_NONE ) { libspectrum_free( new_buffer ); return error; } break; case LIBSPECTRUM_RZX_INPUT_BLOCK: error = rzx_read_input( rzx, &ptr, end ); if( error != LIBSPECTRUM_ERROR_NONE ) { libspectrum_free( new_buffer ); return error; } break; case LIBSPECTRUM_RZX_SIGN_START_BLOCK: error = rzx_read_sign_start( rzx, &ptr, end ); if( error != LIBSPECTRUM_ERROR_NONE ) { libspectrum_free( new_buffer ); return error; } break; case LIBSPECTRUM_RZX_SIGN_END_BLOCK: error = rzx_read_sign_end( rzx, &ptr, end ); if( error != LIBSPECTRUM_ERROR_NONE ) { libspectrum_free( new_buffer ); return error; } break; default: libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "libspectrum_rzx_read: unknown RZX block ID 0x%02x", id ); libspectrum_free( new_buffer ); return LIBSPECTRUM_ERROR_UNKNOWN; } } libspectrum_free( new_buffer ); return LIBSPECTRUM_ERROR_NONE; }
/* Read in a tape file, optionally guessing what sort of file it is */ libspectrum_error libspectrum_tape_read( libspectrum_tape *tape, const libspectrum_byte *buffer, size_t length, libspectrum_id_t type, const char *filename ) { libspectrum_id_t raw_type; libspectrum_class_t class; libspectrum_byte *new_buffer; libspectrum_error error; int uncompressed; /* If we don't know what sort of file this is, make a best guess */ if( type == LIBSPECTRUM_ID_UNKNOWN ) { error = libspectrum_identify_file( &type, filename, buffer, length ); if( error ) return error; /* If we still can't identify it, give up */ if( type == LIBSPECTRUM_ID_UNKNOWN ) { libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "libspectrum_tape_read: couldn't identify file" ); return LIBSPECTRUM_ERROR_UNKNOWN; } } /* Find out if this file needs decompression */ uncompressed = 0; new_buffer = NULL; error = libspectrum_identify_file_raw( &raw_type, filename, buffer, length ); if( error ) return error; error = libspectrum_identify_class( &class, raw_type ); if( error ) return error; if( class == LIBSPECTRUM_CLASS_COMPRESSED ) { size_t new_length; error = libspectrum_uncompress_file( &new_buffer, &new_length, NULL, raw_type, buffer, length, NULL ); buffer = new_buffer; length = new_length; uncompressed = 1; } switch( type ) { case LIBSPECTRUM_ID_TAPE_TAP: case LIBSPECTRUM_ID_TAPE_SPC: case LIBSPECTRUM_ID_TAPE_STA: case LIBSPECTRUM_ID_TAPE_LTP: error = internal_tap_read( tape, buffer, length, type ); break; case LIBSPECTRUM_ID_TAPE_TZX: error = internal_tzx_read( tape, buffer, length ); break; /* case LIBSPECTRUM_ID_TAPE_WARAJEVO: error = internal_warajevo_read( tape, buffer, length ); break; case LIBSPECTRUM_ID_TAPE_Z80EM: error = libspectrum_z80em_read( tape, buffer, length ); break; case LIBSPECTRUM_ID_TAPE_CSW: error = libspectrum_csw_read( tape, buffer, length ); break; */ case LIBSPECTRUM_ID_TAPE_WAV: #ifdef HAVE_LIB_AUDIOFILE error = libspectrum_wav_read( tape, filename ); break; #else /* #ifdef HAVE_LIB_AUDIOFILE */ error = LIBSPECTRUM_ERROR_LOGIC; libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "libspectrum_tape_read: format not supported without libaudiofile" ); break; #endif /* #ifdef HAVE_LIB_AUDIOFILE */ default: libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "libspectrum_tape_read: not a tape file" ); free( new_buffer ); return LIBSPECTRUM_ERROR_CORRUPT; } free( new_buffer ); return error; }
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; }
libspectrum_error libspectrum_rzx_store_frame( libspectrum_rzx *rzx, size_t instructions, size_t count, libspectrum_byte *in_bytes ) { input_block_t *input; libspectrum_rzx_frame_t *frame; input = rzx->current_input; /* Check we've got an IRB to record to */ if( !input ) { libspectrum_print_error( LIBSPECTRUM_ERROR_INVALID, "libspectrum_rzx_store_frame called with no active input block" ); return LIBSPECTRUM_ERROR_INVALID; } /* Get more space if we need it; allocate twice as much as we currently have, with a minimum of 50 */ if( input->count == input->allocated ) { libspectrum_rzx_frame_t *ptr; size_t new_allocated; new_allocated = input->allocated >= 25 ? 2 * input->allocated : 50; ptr = realloc( input->frames, new_allocated * sizeof( *ptr ) ); if( !ptr ) return LIBSPECTRUM_ERROR_MEMORY; input->frames = ptr; input->allocated = new_allocated; } frame = &input->frames[ input->count ]; frame->instructions = instructions; /* Check for repeated frames */ if( input->count != 0 && count != 0 && count == input->frames[ input->non_repeat ].count && !memcmp( in_bytes, input->frames[ input->non_repeat ].in_bytes, count ) ) { frame->repeat_last = 1; } else { frame->repeat_last = 0; frame->count = count; /* Note this as the last non-repeated frame */ input->non_repeat = input->count; if( count ) { frame->in_bytes = libspectrum_malloc( count * sizeof( *( frame->in_bytes ) ) ); memcpy( frame->in_bytes, in_bytes, count * sizeof( *( frame->in_bytes ) ) ); } else { frame->in_bytes = NULL; } } /* Move along to the next frame */ input->count++; return 0; }
/* Identify a file without attempting to decompress it */ libspectrum_error libspectrum_identify_file_raw( libspectrum_id_t *type, const char *filename, const unsigned char *buffer, size_t length ) { struct type { int type; const char *extension; int extension_score; const char *signature; size_t offset, length; int sig_score; }; struct type *ptr, types[] = { { LIBSPECTRUM_ID_RECORDING_RZX, "rzx", 3, "RZX!", 0, 4, 4 }, { LIBSPECTRUM_ID_SNAPSHOT_SNA, "sna", 3, NULL, 0, 0, 0 }, /* Peter McGavin's Spectrum Emulator on the Amiga used .snapshot for sna snaps */ { LIBSPECTRUM_ID_SNAPSHOT_SNA, "snapshot", 3, NULL, 0, 0, 0 }, { LIBSPECTRUM_ID_SNAPSHOT_SNP, "snp", 3, NULL, 0, 0, 0 }, { LIBSPECTRUM_ID_SNAPSHOT_SP, "sp", 3, "\x53\x50\0", 0, 3, 1 }, { LIBSPECTRUM_ID_SNAPSHOT_SZX, "szx", 3, "ZXST", 0, 4, 4 }, { LIBSPECTRUM_ID_SNAPSHOT_Z80, "z80", 3, "\0\0", 6, 2, 1 }, /* .slt files also dealt with by the .z80 loading code */ { LIBSPECTRUM_ID_SNAPSHOT_Z80, "slt", 3, "\0\0", 6, 2, 1 }, { LIBSPECTRUM_ID_SNAPSHOT_ZXS, "zxs", 3, "SNAP", 8, 4, 4 }, { LIBSPECTRUM_ID_SNAPSHOT_PLUSD,"mgtsnp", 3, NULL, 0, 0, 0 }, { LIBSPECTRUM_ID_CARTRIDGE_DCK, "dck", 3, NULL, 0, 0, 0 }, { LIBSPECTRUM_ID_CARTRIDGE_IF2, "rom", 3, NULL, 0, 0, 0 }, { LIBSPECTRUM_ID_MICRODRIVE_MDR, "mdr", 3, NULL, 0, 0, 0 }, { LIBSPECTRUM_ID_TAPE_TAP, "tap", 3, "\x13\0\0", 0, 3, 1 }, { LIBSPECTRUM_ID_TAPE_SPC, "spc", 3, "\x11\0\0", 0, 3, 1 }, { LIBSPECTRUM_ID_TAPE_STA, "sta", 3, "\x11\0\0", 0, 3, 1 }, { LIBSPECTRUM_ID_TAPE_LTP, "ltp", 3, "\x11\0\0", 0, 3, 1 }, { LIBSPECTRUM_ID_TAPE_TZX, "tzx", 3, "ZXTape!", 0, 7, 4 }, { LIBSPECTRUM_ID_TAPE_WARAJEVO, "tap", 2, "\xff\xff\xff\xff", 8, 4, 2 }, { LIBSPECTRUM_ID_DISK_DSK, "dsk", 3, NULL, 0, 0, 0 }, { LIBSPECTRUM_ID_DISK_SCL, "scl", 3, "SINCLAIR", 0, 8, 4 }, { LIBSPECTRUM_ID_DISK_TRD, "trd", 3, NULL, 0, 0, 0 }, { LIBSPECTRUM_ID_HARDDISK_HDF, "hdf", 3, "RS-IDE\x1a", 0, 7, 4 }, { LIBSPECTRUM_ID_COMPRESSED_BZ2,"bz2", 3, "BZh", 0, 3, 4 }, { LIBSPECTRUM_ID_COMPRESSED_GZ, "gz", 3, "\x1f\x8b", 0, 2, 4 }, { LIBSPECTRUM_ID_TAPE_Z80EM, "raw", 1, "\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\0Raw tape sample", 0, 64, 0 }, { LIBSPECTRUM_ID_TAPE_CSW, "csw", 2, "Compressed Square Wave\x1a", 0, 23, 4 }, { LIBSPECTRUM_ID_TAPE_WAV, "wav", 3, NULL, 0, 0, 0 }, { LIBSPECTRUM_ID_DISK_MGT, "mgt", 3, NULL, 0, 0, 0 }, { LIBSPECTRUM_ID_DISK_IMG, "img", 3, NULL, 0, 0, 0 }, { -1, NULL, 0, NULL, 0, 0, 0 }, /* End marker */ }; const char *extension = NULL; int score, best_score, best_guess, duplicate_best; /* Get the filename extension, if it exists */ if( filename ) { extension = strrchr( filename, '.' ); if( extension ) extension++; } best_guess = LIBSPECTRUM_ID_UNKNOWN; best_score = 0; duplicate_best = 0; /* Compare against known extensions and signatures */ for( ptr = types; ptr->type != -1; ptr++ ) { score = 0; if( extension && ptr->extension && !strcasecmp( extension, ptr->extension ) ) score += ptr->extension_score; if( ptr->signature && length >= ptr->offset + ptr->length && !memcmp( &buffer[ ptr->offset ], ptr->signature, ptr->length ) ) score += ptr->sig_score; if( score > best_score ) { best_guess = ptr->type; best_score = score; duplicate_best = 0; } else if( score == best_score && ptr->type != best_guess ) { duplicate_best = 1; } } #if defined AMIGA || defined __MORPHOS__ /* Compressed file check through xfdmaster.library */ /* this prevents most good matches and gz/bz2 from being run through xfd (xfd supports gz, but we want to use the internal code) */ if( best_score <= 3 ) { #ifndef __MORPHOS__ struct ExecIFace *IExec = (struct ExecIFace *)(*(struct ExecBase **)4)->MainInterface; #endif /* #ifndef __MORPHOS__ */ struct xfdBufferInfo *xfdobj; #ifndef __MORPHOS__ if( xfdMasterBase = IExec->OpenLibrary("xfdmaster.library",38 ) ) { if( IxfdMaster = (struct xfdMasterIFace *)IExec->GetInterface(xfdMasterBase,"main",1,NULL)){ if( xfdobj = (struct xfdBufferInfo *)IxfdMaster->xfdAllocObject(XFDOBJ_BUFFERINFO) ) { #else /* #ifndef __MORPHOS__ */ if( xfdMasterBase = OpenLibrary("xfdmaster.library",38 ) ) { if( xfdobj = (struct xfdBufferInfo *)xfdAllocObject(XFDOBJ_BUFFERINFO) ) { #endif /* #ifndef __MORPHOS__ */ xfdobj->xfdbi_SourceBuffer = buffer; xfdobj->xfdbi_SourceBufLen = length; xfdobj->xfdbi_Flags = XFDFB_RECOGTARGETLEN | XFDFB_RECOGEXTERN; #ifndef __MORPHOS__ if( IxfdMaster->xfdRecogBuffer( xfdobj ) ) { #else /* #ifndef __MORPHOS__ */ if( xfdRecogBuffer( xfdobj ) ) { #endif /* #ifndef __MORPHOS__ */ best_guess = LIBSPECTRUM_ID_COMPRESSED_XFD; duplicate_best=0; } #ifndef __MORPHOS__ IxfdMaster->xfdFreeObject( (APTR)xfdobj ); #else /* #ifndef __MORPHOS__ */ xfdFreeObject( (APTR)xfdobj ); #endif /* #ifndef __MORPHOS__ */ } #ifndef __MORPHOS__ IExec->DropInterface( (struct Interface *)IxfdMaster ); } IExec->CloseLibrary( xfdMasterBase ); #else /* #ifndef __MORPHOS__ */ CloseLibrary( xfdMasterBase ); #endif /* #ifndef __MORPHOS__ */ } } #endif /* #ifdef AMIGA */ /* If two things were equally good, we can't identify this. Otherwise, return our best guess */ if( duplicate_best ) { *type = LIBSPECTRUM_ID_UNKNOWN; } else { *type = best_guess; } return LIBSPECTRUM_ERROR_NONE; } /* What generic 'class' of file is this file */ libspectrum_error libspectrum_identify_class( libspectrum_class_t *libspectrum_class, libspectrum_id_t type ) { switch( type ) { case LIBSPECTRUM_ID_UNKNOWN: *libspectrum_class = LIBSPECTRUM_CLASS_UNKNOWN; return 0; case LIBSPECTRUM_ID_CARTRIDGE_DCK: *libspectrum_class = LIBSPECTRUM_CLASS_CARTRIDGE_TIMEX; return 0; case LIBSPECTRUM_ID_COMPRESSED_BZ2: case LIBSPECTRUM_ID_COMPRESSED_GZ: case LIBSPECTRUM_ID_COMPRESSED_XFD: *libspectrum_class = LIBSPECTRUM_CLASS_COMPRESSED; return 0; case LIBSPECTRUM_ID_DISK_DSK: *libspectrum_class = LIBSPECTRUM_CLASS_DISK_PLUS3; return 0; case LIBSPECTRUM_ID_DISK_IMG: case LIBSPECTRUM_ID_DISK_MGT: *libspectrum_class = LIBSPECTRUM_CLASS_DISK_PLUSD; return 0; case LIBSPECTRUM_ID_DISK_SCL: case LIBSPECTRUM_ID_DISK_TRD: *libspectrum_class = LIBSPECTRUM_CLASS_DISK_TRDOS; return 0; case LIBSPECTRUM_ID_HARDDISK_HDF: *libspectrum_class = LIBSPECTRUM_CLASS_HARDDISK; return 0; case LIBSPECTRUM_ID_MICRODRIVE_MDR: *libspectrum_class = LIBSPECTRUM_CLASS_MICRODRIVE; return 0; case LIBSPECTRUM_ID_RECORDING_RZX: *libspectrum_class = LIBSPECTRUM_CLASS_RECORDING; return 0; case LIBSPECTRUM_ID_SNAPSHOT_PLUSD: case LIBSPECTRUM_ID_SNAPSHOT_SNA: case LIBSPECTRUM_ID_SNAPSHOT_SNP: case LIBSPECTRUM_ID_SNAPSHOT_SP: case LIBSPECTRUM_ID_SNAPSHOT_SZX: case LIBSPECTRUM_ID_SNAPSHOT_Z80: case LIBSPECTRUM_ID_SNAPSHOT_ZXS: *libspectrum_class = LIBSPECTRUM_CLASS_SNAPSHOT; return 0; case LIBSPECTRUM_ID_TAPE_TAP: case LIBSPECTRUM_ID_TAPE_SPC: case LIBSPECTRUM_ID_TAPE_STA: case LIBSPECTRUM_ID_TAPE_LTP: case LIBSPECTRUM_ID_TAPE_TZX: case LIBSPECTRUM_ID_TAPE_WARAJEVO: case LIBSPECTRUM_ID_TAPE_Z80EM: case LIBSPECTRUM_ID_TAPE_CSW: case LIBSPECTRUM_ID_TAPE_WAV: *libspectrum_class = LIBSPECTRUM_CLASS_TAPE; return 0; case LIBSPECTRUM_ID_CARTRIDGE_IF2: *libspectrum_class = LIBSPECTRUM_CLASS_CARTRIDGE_IF2; return 0; } libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "Unknown file type %d", type ); return LIBSPECTRUM_ERROR_UNKNOWN; } libspectrum_error libspectrum_uncompress_file( unsigned char **new_buffer, size_t *new_length, char **new_filename, libspectrum_id_t type, const unsigned char *old_buffer, size_t old_length, const char *old_filename ) { libspectrum_class_t class; libspectrum_error error; error = libspectrum_identify_class( &class, type ); if( error ) return error; if( class != LIBSPECTRUM_CLASS_COMPRESSED ) { libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "file type %d is not a compressed type", type ); return LIBSPECTRUM_ERROR_LOGIC; } if( new_filename && old_filename ) { *new_filename = strdup( old_filename ); if( !*new_filename ) { libspectrum_print_error( LIBSPECTRUM_ERROR_MEMORY, "out of memory at %s:%d", __FILE__, __LINE__ ); return LIBSPECTRUM_ERROR_MEMORY; } } /* Tells the inflation routines to allocate memory for us */ *new_length = 0; switch( type ) { case LIBSPECTRUM_ID_COMPRESSED_BZ2: #ifdef HAVE_LIBBZ2 if( new_filename && *new_filename ) { if( strlen( *new_filename ) >= 4 && !strcasecmp( &(*new_filename)[ strlen( *new_filename ) - 4 ], ".bz2" ) ) (*new_filename)[ strlen( *new_filename ) - 4 ] = '\0'; } error = libspectrum_bzip2_inflate( old_buffer, old_length, new_buffer, new_length ); if( error ) { if( new_filename ) free( *new_filename ); return error; } #else /* #ifdef HAVE_LIBBZ2 */ libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "libbz2 not available to decompress bzipped file" ); if( new_filename ) free( *new_filename ); return LIBSPECTRUM_ERROR_UNKNOWN; #endif /* #ifdef HAVE_LIBBZ2 */ break; case LIBSPECTRUM_ID_COMPRESSED_GZ: #ifdef HAVE_ZLIB_H if( new_filename && *new_filename ) { if( strlen( *new_filename ) >= 3 && !strcasecmp( &(*new_filename)[ strlen( *new_filename ) - 3 ], ".gz" ) ) (*new_filename)[ strlen( *new_filename ) - 3 ] = '\0'; } error = libspectrum_gzip_inflate( old_buffer, old_length, new_buffer, new_length ); if( error ) { if( new_filename ) free( *new_filename ); return error; } #else /* #ifdef HAVE_ZLIB_H */ libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "zlib not available to decompress gzipped file" ); if( new_filename ) free( *new_filename ); return LIBSPECTRUM_ERROR_UNKNOWN; #endif /* #ifdef HAVE_ZLIB_H */ break; #if defined AMIGA || defined __MORPHOS__ case LIBSPECTRUM_ID_COMPRESSED_XFD: { #ifndef __MORPHOS__ struct ExecIFace *IExec = (struct ExecIFace *)(*(struct ExecBase **)4)->MainInterface; #endif /* #ifndef __MORPHOS__ */ struct xfdBufferInfo *xfdobj; #ifndef __MORPHOS__ if( xfdMasterBase = IExec->OpenLibrary( "xfdmaster.library", 38 ) ) { if( IxfdMaster = (struct xfdMasterIFace *)IExec->GetInterface(xfdMasterBase,"main",1,NULL)) { if( xfdobj = (struct xfdBufferInfo *)IxfdMaster->xfdAllocObject(XFDOBJ_BUFFERINFO) ) { #else /* #ifndef __MORPHOS__ */ if( xfdMasterBase = OpenLibrary( "xfdmaster.library", 38 ) ) { if( xfdobj = (struct xfdBufferInfo *)xfdAllocObject(XFDOBJ_BUFFERINFO) ) { #endif /* #ifndef __MORPHOS__ */ xfdobj->xfdbi_SourceBufLen = old_length; xfdobj->xfdbi_SourceBuffer = old_buffer; xfdobj->xfdbi_Flags = XFDFB_RECOGEXTERN | XFDFB_RECOGTARGETLEN; xfdobj->xfdbi_PackerFlags = XFDPFB_RECOGLEN; #ifndef __MORPHOS__ if( IxfdMaster->xfdRecogBuffer( xfdobj ) ) { #else /* #ifndef __MORPHOS__ */ if( xfdRecogBuffer( xfdobj ) ) { #endif /* #ifndef __MORPHOS__ */ xfdobj->xfdbi_TargetBufMemType = MEMF_ANY; #ifndef __MORPHOS__ if( IxfdMaster->xfdDecrunchBuffer( xfdobj ) ) { #else /* #ifndef __MORPHOS__ */ if( xfdDecrunchBuffer( xfdobj ) ) { #endif /* #ifndef __MORPHOS__ */ *new_buffer = malloc( xfdobj->xfdbi_TargetBufSaveLen ); *new_length = xfdobj->xfdbi_TargetBufSaveLen; memcpy( *new_buffer, xfdobj->xfdbi_TargetBuffer, *new_length ); #ifndef __MORPHOS__ IExec->FreeMem( xfdobj->xfdbi_TargetBuffer,xfdobj->xfdbi_TargetBufLen ); #else /* #ifndef __MORPHOS__ */ FreeMem( xfdobj->xfdbi_TargetBuffer,xfdobj->xfdbi_TargetBufLen ); #endif /* #ifndef __MORPHOS__ */ } else { libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "xfdmaster.library not able to decrunch %s file", xfdobj->xfdbi_PackerName ); return LIBSPECTRUM_ERROR_UNKNOWN; } } else { libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "xfdmaster.library does not recognise file" ); return LIBSPECTRUM_ERROR_UNKNOWN; } #ifndef __MORPHOS__ IxfdMaster->xfdFreeObject( xfdobj ); #else /* #ifndef __MORPHOS__ */ xfdFreeObject( xfdobj ); #endif /* #ifndef __MORPHOS__ */ } #ifndef __MORPHOS__ IExec->DropInterface( (struct Interface *)IxfdMaster ); } IExec->CloseLibrary( xfdMasterBase ); #else /* #ifndef __MORPHOS__ */ CloseLibrary( xfdMasterBase ); #endif /* #ifndef __MORPHOS__ */ } } break; #endif /* #ifdef AMIGA */ default: libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "unknown compressed type %d", type ); if( new_filename ) free( *new_filename ); return LIBSPECTRUM_ERROR_LOGIC; } return LIBSPECTRUM_ERROR_NONE; } /* Ensure there is room for `requested' characters after the current position `ptr' in `buffer'. If not, realloc() and update the pointers as necessary */ int libspectrum_make_room( libspectrum_byte **dest, size_t requested, libspectrum_byte **ptr, size_t *allocated ) { size_t current_length; current_length = *ptr - *dest; if( *allocated == 0 ) { (*allocated) = requested; *dest = (libspectrum_byte*)malloc( requested * sizeof(libspectrum_byte) ); if( *dest == NULL ) return 1; } else { /* If there's already enough room here, just return */ if( current_length + requested <= (*allocated) ) return 0; /* Make the new size the maximum of the new needed size and the old allocated size * 2 */ (*allocated) = current_length + requested > 2 * (*allocated) ? current_length + requested : 2 * (*allocated); *dest = (libspectrum_byte*) realloc( *dest, (*allocated) * sizeof( libspectrum_byte ) ); if( *dest == NULL ) return 1; } /* Update the secondary pointer to the block */ *ptr = *dest + current_length; return 0; } /* Read an LSB word from 'buffer' */ libspectrum_word libspectrum_read_word( const libspectrum_byte **buffer ) { libspectrum_word value; value = (*buffer)[0] + (*buffer)[1] * 0x100; (*buffer) += 2; return value; } /* Read an LSB dword from buffer */ libspectrum_dword libspectrum_read_dword( const libspectrum_byte **buffer ) { libspectrum_dword value; value = (*buffer)[0] + (*buffer)[1] * 0x100 + (*buffer)[2] * 0x10000 + (*buffer)[3] * 0x1000000 ; (*buffer) += 4; return value; } /* Write an (LSB) word to buffer */ int libspectrum_write_word( libspectrum_byte **buffer, libspectrum_word w ) { *(*buffer)++ = w & 0xff; *(*buffer)++ = w >> 8; return LIBSPECTRUM_ERROR_NONE; } /* Write an LSB dword to buffer */ int libspectrum_write_dword( libspectrum_byte **buffer, libspectrum_dword d ) { *(*buffer)++ = ( d & 0x000000ff ) ; *(*buffer)++ = ( d & 0x0000ff00 ) >> 8; *(*buffer)++ = ( d & 0x00ff0000 ) >> 16; *(*buffer)++ = ( d & 0xff000000 ) >> 24; return LIBSPECTRUM_ERROR_NONE; }
/* The main function: called with a tape object and returns the number of t-states until the next edge, and a marker if this was the last edge on the tape */ libspectrum_error libspectrum_tape_get_next_edge( libspectrum_tape *tape, libspectrum_dword *tstates, int *flags ) { int error; libspectrum_tape_block *block = (libspectrum_tape_block*)tape->current_block->data; /* Has this edge ended the block? */ int end_of_block = 0; /* After getting a new block, do we want to advance to the next one? */ int no_advance = 0; /* Assume no special flags by default */ *flags = 0; switch( block->type ) { case LIBSPECTRUM_TAPE_BLOCK_ROM: error = rom_edge( &(block->types.rom), tstates, &end_of_block ); if( error ) return error; break; case LIBSPECTRUM_TAPE_BLOCK_TURBO: error = turbo_edge( &(block->types.turbo), tstates, &end_of_block ); if( error ) return error; break; case LIBSPECTRUM_TAPE_BLOCK_PURE_TONE: error = tone_edge( &(block->types.pure_tone), tstates, &end_of_block ); if( error ) return error; break; case LIBSPECTRUM_TAPE_BLOCK_PULSES: error = pulses_edge( &(block->types.pulses), tstates, &end_of_block ); if( error ) return error; break; case LIBSPECTRUM_TAPE_BLOCK_PURE_DATA: error = pure_data_edge( &(block->types.pure_data), tstates, &end_of_block); if( error ) return error; break; case LIBSPECTRUM_TAPE_BLOCK_PAUSE: *tstates = ( block->types.pause.length * 69888 ) / 20; end_of_block = 1; /* 0 ms pause => stop tape */ if( *tstates == 0 ) { *flags |= LIBSPECTRUM_TAPE_FLAGS_STOP; } break; case LIBSPECTRUM_TAPE_BLOCK_JUMP: error = jump_blocks( tape, block->types.jump.offset ); if( error ) return error; *tstates = 0; end_of_block = 1; no_advance = 1; break; case LIBSPECTRUM_TAPE_BLOCK_LOOP_START: tape->loop_block = tape->current_block->next; tape->loop_count = block->types.loop_start.count; *tstates = 0; end_of_block = 1; break; case LIBSPECTRUM_TAPE_BLOCK_LOOP_END: if( --tape->loop_count ) { tape->current_block = tape->loop_block; no_advance = 1; } *tstates = 0; end_of_block = 1; break; case LIBSPECTRUM_TAPE_BLOCK_STOP48: *tstates = 0; *flags |= LIBSPECTRUM_TAPE_FLAGS_STOP48; end_of_block = 1; break; /* For blocks which contain no Spectrum-readable data, return zero tstates and set end of block set so we instantly get the next block */ case LIBSPECTRUM_TAPE_BLOCK_GROUP_START: case LIBSPECTRUM_TAPE_BLOCK_GROUP_END: case LIBSPECTRUM_TAPE_BLOCK_SELECT: case LIBSPECTRUM_TAPE_BLOCK_COMMENT: case LIBSPECTRUM_TAPE_BLOCK_MESSAGE: case LIBSPECTRUM_TAPE_BLOCK_ARCHIVE_INFO: case LIBSPECTRUM_TAPE_BLOCK_HARDWARE: case LIBSPECTRUM_TAPE_BLOCK_CUSTOM: *tstates = 0; end_of_block = 1; break; default: *tstates = 0; libspectrum_print_error( "libspectrum_tape_get_next_edge: unknown block type 0x%02x\n", block->type ); return LIBSPECTRUM_ERROR_LOGIC; } /* If that ended the block, move onto the next block */ if( end_of_block ) { *flags |= LIBSPECTRUM_TAPE_FLAGS_BLOCK; /* Advance to the next block, unless we've been told not to */ if( !no_advance ) { tape->current_block = tape->current_block->next; /* If we've just hit the end of the tape, stop the tape (and then `rewind' to the start) */ if( tape->current_block == NULL ) { *flags |= LIBSPECTRUM_TAPE_FLAGS_STOP; tape->current_block = tape->blocks; } } /* Initialise the new block */ block = (libspectrum_tape_block*)tape->current_block->data; error = libspectrum_tape_init_block( block ); if( error ) return error; } return LIBSPECTRUM_ERROR_NONE; }
static libspectrum_error generalised_data_edge( libspectrum_tape_generalised_data_block *block, libspectrum_tape_generalised_data_block_state *state, libspectrum_dword *tstates, int *end_of_block, int *flags ) { libspectrum_tape_generalised_data_symbol_table *table; libspectrum_tape_generalised_data_symbol *symbol; size_t current_symbol; switch( state->state ) { case LIBSPECTRUM_TAPE_STATE_PILOT: table = &( block->pilot_table ); current_symbol = block->pilot_symbols[ state->run ]; symbol = &( table->symbols[ current_symbol ] ); set_tstates_and_flags( symbol, state->edges_through_symbol, tstates, flags ); state->edges_through_symbol++; if( state->edges_through_symbol == table->max_pulses || symbol->lengths[ state->edges_through_symbol ] == 0 ) { state->edges_through_symbol = 0; if( ++state->symbols_through_run == block->pilot_repeats[ state->run ] ) { state->symbols_through_run = 0; if( ++state->run == table->symbols_in_block ) { state->state = LIBSPECTRUM_TAPE_STATE_DATA1; state->bits_through_byte = 0; state->bytes_through_stream = 0; state->symbols_through_stream = 0; state->current_byte = block->data[ 0 ]; state->current_symbol = get_generalised_data_symbol( block, state ); } } } break; case LIBSPECTRUM_TAPE_STATE_DATA1: table = &( block->data_table ); symbol = &( table->symbols[ state->current_symbol ] ); set_tstates_and_flags( symbol, state->edges_through_symbol, tstates, flags ); state->edges_through_symbol++; if( state->edges_through_symbol == table->max_pulses || symbol->lengths[ state->edges_through_symbol ] == 0 ) { if( ++state->symbols_through_stream == table->symbols_in_block ) { state->state = LIBSPECTRUM_TAPE_STATE_PAUSE; } else { state->edges_through_symbol = 0; state->current_symbol = get_generalised_data_symbol( block, state ); } } break; case LIBSPECTRUM_TAPE_STATE_PAUSE: /* The pause at the end of the block */ *tstates = ( block->pause * 69888 )/20; /* FIXME: should vary with tstates per frame */ *end_of_block = 1; break; default: libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "%s: unknown state %d", __func__, state->state ); return LIBSPECTRUM_ERROR_LOGIC; } return LIBSPECTRUM_ERROR_NONE; }
static libspectrum_error turbo_edge( libspectrum_tape_turbo_block *block, libspectrum_tape_turbo_block_state *state, libspectrum_dword *tstates, int *end_of_block ) { int error; switch( state->state ) { case LIBSPECTRUM_TAPE_STATE_PILOT: /* Check we actually have some edges */ if( state->edge_count-- != 0 ) { *tstates = block->pilot_length; break; } /* Fall through */ case LIBSPECTRUM_TAPE_STATE_SYNC1: /* The first short sync pulse */ *tstates = block->sync1_length; /* Followed by the second sync pulse */ state->state = LIBSPECTRUM_TAPE_STATE_SYNC2; break; case LIBSPECTRUM_TAPE_STATE_SYNC2: /* The second short sync pulse */ *tstates = block->sync2_length; /* Followed by the first bit of data */ error = turbo_next_bit( block, state ); if( error ) return error; break; case LIBSPECTRUM_TAPE_STATE_DATA1: /* The first edge for a bit of data */ *tstates = state->bit_tstates; /* Followed by the second edge */ state->state = LIBSPECTRUM_TAPE_STATE_DATA2; break; case LIBSPECTRUM_TAPE_STATE_DATA2: /* The second edge for a bit of data */ *tstates = state->bit_tstates; /* Followed by the next bit of data (or the end of data) */ error = turbo_next_bit( block, state ); if( error ) return error; break; case LIBSPECTRUM_TAPE_STATE_PAUSE: /* The pause at the end of the block */ *tstates = (block->pause * 69888)/20; /* FIXME: should vary with tstates per frame */ *end_of_block = 1; break; default: libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "turbo_edge: unknown state %d", state->state ); return LIBSPECTRUM_ERROR_LOGIC; } return LIBSPECTRUM_ERROR_NONE; }
static libspectrum_error rom_edge( libspectrum_tape_rom_block *block, libspectrum_tape_rom_block_state *state, libspectrum_dword *tstates, int *end_of_block ) { int error; switch( state->state ) { case LIBSPECTRUM_TAPE_STATE_PILOT: /* The next edge occurs in one pilot edge timing */ *tstates = LIBSPECTRUM_TAPE_TIMING_PILOT; /* If that was the last pilot edge, change state */ if( --(state->edge_count) == 0 ) state->state = LIBSPECTRUM_TAPE_STATE_SYNC1; break; case LIBSPECTRUM_TAPE_STATE_SYNC1: /* The first short sync pulse */ *tstates = LIBSPECTRUM_TAPE_TIMING_SYNC1; /* Followed by the second sync pulse */ state->state = LIBSPECTRUM_TAPE_STATE_SYNC2; break; case LIBSPECTRUM_TAPE_STATE_SYNC2: /* The second short sync pulse */ *tstates = LIBSPECTRUM_TAPE_TIMING_SYNC2; /* Followed by the first bit of data */ error = rom_next_bit( block, state ); if( error ) return error; break; case LIBSPECTRUM_TAPE_STATE_DATA1: /* The first edge for a bit of data */ *tstates = state->bit_tstates; /* Followed by the second edge */ state->state = LIBSPECTRUM_TAPE_STATE_DATA2; break; case LIBSPECTRUM_TAPE_STATE_DATA2: /* The second edge for a bit of data */ *tstates = state->bit_tstates; /* Followed by the next bit of data (or the end of data) */ error = rom_next_bit( block, state ); if( error ) return error; break; case LIBSPECTRUM_TAPE_STATE_PAUSE: /* The pause at the end of the block */ *tstates = (block->pause * 69888)/20; /* FIXME: should vary with tstates per frame */ *end_of_block = 1; break; default: libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "rom_edge: unknown state %d", state->state ); return LIBSPECTRUM_ERROR_LOGIC; } return LIBSPECTRUM_ERROR_NONE; }
static libspectrum_error rzx_write_snapshot( libspectrum_byte **buffer, libspectrum_byte **ptr, size_t *length, libspectrum_snap *snap, libspectrum_id_t snap_format, libspectrum_creator *creator, int compress ) { libspectrum_error error; libspectrum_byte *snap_buffer = NULL; size_t snap_length; libspectrum_byte *gzsnap = NULL; size_t gzlength = 0; int flags, done; snapshot_string_t *type; snap_length = 0; if( snap_format == LIBSPECTRUM_ID_UNKNOWN ) { /* If not given a snap format, try using .z80. If that would result in major information loss, use .szx instead */ snap_format = LIBSPECTRUM_ID_SNAPSHOT_Z80; error = libspectrum_snap_write( &snap_buffer, &snap_length, &flags, snap, snap_format, creator, 0 ); if( error ) return error; if( flags & LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS ) { libspectrum_free( snap_buffer ); snap_length = 0; snap_format = LIBSPECTRUM_ID_SNAPSHOT_SZX; error = libspectrum_snap_write( &snap_buffer, &snap_length, &flags, snap, snap_format, creator, 0 ); if( error ) return error; } } else { error = libspectrum_snap_write( &snap_buffer, &snap_length, &flags, snap, snap_format, creator, 0 ); if( error ) return error; } if( flags & LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS ) { libspectrum_print_error( LIBSPECTRUM_ERROR_WARNING, "%s:rzx_write_snapshot: embedded snapshot has lost a significant amount of information", __FILE__ ); } if( compress ) { #ifdef HAVE_ZLIB_H error = libspectrum_zlib_compress( snap_buffer, snap_length, &gzsnap, &gzlength ); if( error != LIBSPECTRUM_ERROR_NONE ) { libspectrum_free( snap_buffer ); return error; } libspectrum_make_room( buffer, 17 + gzlength, ptr, length ); #else /* #ifdef HAVE_ZLIB_H */ libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "rzx_write_snapshot: compression needs zlib" ); return LIBSPECTRUM_ERROR_UNKNOWN; #endif /* #ifdef HAVE_ZLIB_H */ } else { libspectrum_make_room( buffer, 17 + snap_length, ptr, length ); } *(*ptr)++ = LIBSPECTRUM_RZX_SNAPSHOT_BLOCK; if( compress ) { /* Block length and flags */ libspectrum_write_dword( ptr, 17 + gzlength ); libspectrum_write_dword( ptr, 2 ); } else { libspectrum_write_dword( ptr, 17 + snap_length ); libspectrum_write_dword( ptr, 0 ); } for( type = snapshot_strings, done = 0; type->format; type++ ) { if( type->format == snap_format ) { memcpy( *ptr, type->string, 4 ); *ptr += 4; done = 1; break; } } if( !done ) { libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "%s:rzx_write_snapshot: unexpected snap type %d", __FILE__, snap_format ); return LIBSPECTRUM_ERROR_UNKNOWN; } libspectrum_write_dword( ptr, snap_length ); /* Snapshot length */ if( compress ) { memcpy( *ptr, gzsnap, gzlength ); (*ptr) += gzlength; libspectrum_free( gzsnap ); } else { memcpy( *ptr, snap_buffer, snap_length ); (*ptr) += snap_length; } libspectrum_free( snap_buffer ); return LIBSPECTRUM_ERROR_NONE; }
static int libspectrum_sna_read_data( const libspectrum_byte *buffer, size_t buffer_length, libspectrum_snap *snap ) { int error, page, i; libspectrum_word sp, offset; if( buffer_length < 0xc000 ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "libspectrum_sna_read_data: not enough data in buffer" ); return LIBSPECTRUM_ERROR_CORRUPT; } switch( libspectrum_snap_machine( snap ) ) { case LIBSPECTRUM_MACHINE_48: sp = libspectrum_snap_sp( snap ); if( sp < 0x4000 || sp == 0xffff ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "libspectrum_sna_read_data: SP invalid (0x%04x)", sp ); return LIBSPECTRUM_ERROR_CORRUPT; } /* Rescue PC from the stack */ offset = sp - 0x4000; libspectrum_snap_set_pc( snap, buffer[offset] + 0x100 * buffer[offset+1] ); /* Increase SP as PC has been unstacked */ libspectrum_snap_set_sp( snap, libspectrum_snap_sp( snap ) + 2 ); /* And split the pages up */ error = libspectrum_split_to_48k_pages( snap, buffer ); if( error != LIBSPECTRUM_ERROR_NONE ) return error; break; case LIBSPECTRUM_MACHINE_PENT: for( i=0; i<8; i++ ) { libspectrum_byte *ram = libspectrum_new( libspectrum_byte, 0x4000 ); libspectrum_snap_set_pages( snap, i, ram ); } memcpy( libspectrum_snap_pages( snap, 5 ), &buffer[0x0000], 0x4000 ); memcpy( libspectrum_snap_pages( snap, 2 ), &buffer[0x4000], 0x4000 ); error = libspectrum_sna_read_128_header( buffer + 0xc000, buffer_length - 0xc000, snap ); if( error != LIBSPECTRUM_ERROR_NONE ) return error; page = libspectrum_snap_out_128_memoryport( snap ) & 0x07; if( page == 5 || page == 2 ) { if( memcmp( libspectrum_snap_pages( snap, page ), &buffer[0x8000], 0x4000 ) ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "libspectrum_sna_read_data: duplicated page not identical" ); return LIBSPECTRUM_ERROR_CORRUPT; } } else { memcpy( libspectrum_snap_pages( snap, page ), &buffer[0x8000], 0x4000 ); } buffer += 0xc000 + LIBSPECTRUM_SNA_128_HEADER_LENGTH; buffer_length -= 0xc000 + LIBSPECTRUM_SNA_128_HEADER_LENGTH; error = libspectrum_sna_read_128_data( buffer, buffer_length, snap ); if( error != LIBSPECTRUM_ERROR_NONE ) return error; break; default: libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "libspectrum_sna_read_data: unknown machine" ); return LIBSPECTRUM_ERROR_LOGIC; } return LIBSPECTRUM_ERROR_NONE; }
static libspectrum_error rzx_write_input( input_block_t *block, libspectrum_byte **buffer, libspectrum_byte **ptr, size_t *length, int compress ) { libspectrum_error error; size_t i, size; size_t length_offset, data_offset, flags_offset; libspectrum_byte *length_ptr; libspectrum_make_room( buffer, 18, ptr, length ); *(*ptr)++ = LIBSPECTRUM_RZX_INPUT_BLOCK; /* The length bytes: for uncompressed data, 18 for the block introduction and 4 per frame; the number of bytes in every frame is added in below. If compression is requested (and makes the data shorter), this will be overwritten with the compressed length */ size = 18 + 4 * block->count; /* Store where the length will be written, and skip over those bytes */ length_offset = *ptr - *buffer; (*ptr) += 4; /* How many frames? */ libspectrum_write_dword( ptr, block->count ); /* Each frame has an undefined length, so write a zero */ *(*ptr)++ = 0; /* T-state counter */ libspectrum_write_dword( ptr, block->tstates ); /* Flags */ flags_offset = *ptr - *buffer; libspectrum_write_dword( ptr, compress ? 0x02 : 0 ); /* Write the frames */ data_offset = *ptr - *buffer; for( i = 0; i < block->count; i++ ) { libspectrum_rzx_frame_t *frame = &block->frames[i]; libspectrum_make_room( buffer, 4, ptr, length ); libspectrum_write_word( ptr, frame->instructions ); if( frame->repeat_last ) { libspectrum_write_word( ptr, libspectrum_rzx_repeat_frame ); } else { size += frame->count; /* Keep track of the size */ libspectrum_write_word( ptr, frame->count ); libspectrum_make_room( buffer, frame->count, ptr, length ); memcpy( *ptr, frame->in_bytes, frame->count ); (*ptr) += frame->count; } } /* Write the length in */ length_ptr = *buffer + length_offset; libspectrum_write_dword( &length_ptr, size ); length_ptr -= 4; if( compress ) { #ifdef HAVE_ZLIB_H /* Compress the data the simple way. Really, we should stream the data */ libspectrum_byte *gzsnap = NULL; size_t gzlength; libspectrum_byte *data_ptr = *buffer + data_offset; error = libspectrum_zlib_compress( data_ptr, *ptr - data_ptr, &gzsnap, &gzlength ); if( error != LIBSPECTRUM_ERROR_NONE ) return error; if( (ptrdiff_t)gzlength >= *ptr - data_ptr ) { /* Compression made it bigger :-( */ *(*buffer + flags_offset) &= ~0x02; /* Clear `compressed' bit */ } else { /* Write the compressed data in */ memcpy( data_ptr, gzsnap, gzlength ); /* Correct the length word and the buffer length */ libspectrum_write_dword( &length_ptr, 18 + gzlength ); *ptr = *buffer + data_offset + gzlength; } libspectrum_free( gzsnap ); #else /* #ifdef HAVE_ZLIB_H */ libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "rzx_write_input: compression needs zlib" ); return LIBSPECTRUM_ERROR_UNKNOWN; #endif /* #ifdef HAVE_ZLIB_H */ } return LIBSPECTRUM_ERROR_NONE; }
/* Read in a snapshot, optionally guessing what type it is */ libspectrum_error libspectrum_snap_read( libspectrum_snap *snap, const libspectrum_byte *buffer, size_t length, libspectrum_id_t type, const char *filename ) { libspectrum_id_t raw_type; libspectrum_class_t class; libspectrum_byte *new_buffer; libspectrum_error error; /* If we don't know what sort of file this is, make a best guess */ if( type == LIBSPECTRUM_ID_UNKNOWN ) { error = libspectrum_identify_file( &type, filename, buffer, length ); if( error ) return error; /* If we still can't identify it, give up */ if( type == LIBSPECTRUM_ID_UNKNOWN ) { libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "libspectrum_snap_read: couldn't identify file" ); return LIBSPECTRUM_ERROR_UNKNOWN; } } error = libspectrum_identify_class( &class, type ); if( error ) return error; if( class != LIBSPECTRUM_CLASS_SNAPSHOT ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "libspectrum_snap_read: not a snapshot file" ); return LIBSPECTRUM_ERROR_CORRUPT; } /* Find out if this file needs decompression */ new_buffer = NULL; error = libspectrum_identify_file_raw( &raw_type, filename, buffer, length ); if( error ) return error; error = libspectrum_identify_class( &class, raw_type ); if( error ) return error; if( class == LIBSPECTRUM_CLASS_COMPRESSED ) { size_t new_length; error = libspectrum_uncompress_file( &new_buffer, &new_length, NULL, raw_type, buffer, length, NULL ); if( error ) return error; buffer = new_buffer; length = new_length; } switch( type ) { case LIBSPECTRUM_ID_SNAPSHOT_PLUSD: error = libspectrum_plusd_read( snap, buffer, length ); break; case LIBSPECTRUM_ID_SNAPSHOT_SNA: error = internal_sna_read( snap, buffer, length ); break; case LIBSPECTRUM_ID_SNAPSHOT_SNP: error = libspectrum_snp_read( snap, buffer, length ); break; case LIBSPECTRUM_ID_SNAPSHOT_SP: error = libspectrum_sp_read( snap, buffer, length ); break; case LIBSPECTRUM_ID_SNAPSHOT_SZX: error = libspectrum_szx_read( snap, buffer, length ); break; case LIBSPECTRUM_ID_SNAPSHOT_Z80: error = internal_z80_read( snap, buffer, length ); break; case LIBSPECTRUM_ID_SNAPSHOT_ZXS: error = libspectrum_zxs_read( snap, buffer, length ); break; default: libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "libspectrum_snap_read: unknown snapshot type %d", type ); libspectrum_free( new_buffer ); return LIBSPECTRUM_ERROR_LOGIC; } libspectrum_free( new_buffer ); return error; }
libspectrum_error libspectrum_rzx_playback_frame( libspectrum_rzx *rzx, int *finished, libspectrum_snap **snap ) { *snap = NULL; *finished = 0; /* Check we read the correct number of INs during this frame */ if( rzx->in_count != rzx->data_frame->count ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "libspectrum_rzx_playback_frame: wrong number of INs in frame %lu: expected %lu, got %lu", (unsigned long)rzx->current_frame, (unsigned long)rzx->data_frame->count, (unsigned long)rzx->in_count ); return LIBSPECTRUM_ERROR_CORRUPT; } /* Increment the frame count and see if we've finished with this file */ if( ++rzx->current_frame >= rzx->current_input->count ) { GSList *it = rzx->current_block->next; rzx->current_block = NULL; for( ; it; it = it->next ) { rzx_block_t *block = it->data; if( block->type == LIBSPECTRUM_RZX_INPUT_BLOCK ) { rzx->current_block = it; break; } else if( block->type == LIBSPECTRUM_RZX_SNAPSHOT_BLOCK ) { *snap = block->types.snap.snap; } } if( rzx->current_block ) { rzx_block_t *block = rzx->current_block->data; rzx->current_input = &( block->types.input ); rzx->current_frame = 0; rzx->in_count = 0; rzx->data_frame = rzx->current_input->frames; } else { *finished = 1; } return LIBSPECTRUM_ERROR_NONE; } /* Move the data frame pointer along, unless we're supposed to be repeating the last frame */ if( !rzx->current_input->frames[ rzx->current_frame ].repeat_last ) rzx->data_frame = &rzx->current_input->frames[ rzx->current_frame ]; /* And start with the first byte of the new frame */ rzx->in_count = 0; return LIBSPECTRUM_ERROR_NONE; }
/* Called when a new block is started to initialise its internal state */ libspectrum_error libspectrum_tape_block_init( libspectrum_tape_block *block, libspectrum_tape_block_state *state ) { if( !block ) return LIBSPECTRUM_ERROR_NONE; switch( libspectrum_tape_block_type( block ) ) { case LIBSPECTRUM_TAPE_BLOCK_ROM: return rom_init( &(block->types.rom), &(state->block_state.rom) ); case LIBSPECTRUM_TAPE_BLOCK_TURBO: return turbo_init( &(block->types.turbo), &(state->block_state.turbo) ); case LIBSPECTRUM_TAPE_BLOCK_PURE_TONE: state->block_state.pure_tone.edge_count = block->types.pure_tone.pulses; break; case LIBSPECTRUM_TAPE_BLOCK_PULSES: state->block_state.pulses.edge_count = 0; break; case LIBSPECTRUM_TAPE_BLOCK_PURE_DATA: return pure_data_init( &(block->types.pure_data), &(state->block_state.pure_data) ); case LIBSPECTRUM_TAPE_BLOCK_RAW_DATA: raw_data_init( &(block->types.raw_data), &(state->block_state.raw_data) ); return LIBSPECTRUM_ERROR_NONE; case LIBSPECTRUM_TAPE_BLOCK_GENERALISED_DATA: return generalised_data_init( &(block->types.generalised_data), &(state->block_state.generalised_data) ); case LIBSPECTRUM_TAPE_BLOCK_RLE_PULSE: state->block_state.rle_pulse.index = 0; return LIBSPECTRUM_ERROR_NONE; case LIBSPECTRUM_TAPE_BLOCK_PULSE_SEQUENCE: state->block_state.pulse_sequence.index = 0; state->block_state.pulse_sequence.pulse_count = 0; state->block_state.pulse_sequence.level = -1; return LIBSPECTRUM_ERROR_NONE; case LIBSPECTRUM_TAPE_BLOCK_DATA_BLOCK: return data_block_init( &(block->types.data_block), &(state->block_state.data_block) ); /* These blocks need no initialisation */ case LIBSPECTRUM_TAPE_BLOCK_PAUSE: case LIBSPECTRUM_TAPE_BLOCK_GROUP_START: case LIBSPECTRUM_TAPE_BLOCK_GROUP_END: case LIBSPECTRUM_TAPE_BLOCK_JUMP: case LIBSPECTRUM_TAPE_BLOCK_LOOP_START: case LIBSPECTRUM_TAPE_BLOCK_LOOP_END: case LIBSPECTRUM_TAPE_BLOCK_SELECT: case LIBSPECTRUM_TAPE_BLOCK_STOP48: case LIBSPECTRUM_TAPE_BLOCK_SET_SIGNAL_LEVEL: case LIBSPECTRUM_TAPE_BLOCK_COMMENT: case LIBSPECTRUM_TAPE_BLOCK_MESSAGE: case LIBSPECTRUM_TAPE_BLOCK_ARCHIVE_INFO: case LIBSPECTRUM_TAPE_BLOCK_HARDWARE: case LIBSPECTRUM_TAPE_BLOCK_CUSTOM: return LIBSPECTRUM_ERROR_NONE; default: libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "libspectrum_tape_init_block: unknown block type 0x%02x", block->type ); return LIBSPECTRUM_ERROR_LOGIC; } 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; }
/* Free the memory used by one block */ libspectrum_error libspectrum_tape_block_free( libspectrum_tape_block *block ) { size_t i; switch( block->type ) { case LIBSPECTRUM_TAPE_BLOCK_ROM: libspectrum_free( block->types.rom.data ); break; case LIBSPECTRUM_TAPE_BLOCK_TURBO: libspectrum_free( block->types.turbo.data ); break; case LIBSPECTRUM_TAPE_BLOCK_PURE_TONE: break; case LIBSPECTRUM_TAPE_BLOCK_PULSES: libspectrum_free( block->types.pulses.lengths ); break; case LIBSPECTRUM_TAPE_BLOCK_PURE_DATA: libspectrum_free( block->types.pure_data.data ); break; case LIBSPECTRUM_TAPE_BLOCK_RAW_DATA: libspectrum_free( block->types.raw_data.data ); break; case LIBSPECTRUM_TAPE_BLOCK_GENERALISED_DATA: free_symbol_table( &block->types.generalised_data.pilot_table ); free_symbol_table( &block->types.generalised_data.data_table ); libspectrum_free( block->types.generalised_data.pilot_symbols ); libspectrum_free( block->types.generalised_data.pilot_repeats ); libspectrum_free( block->types.generalised_data.data ); break; case LIBSPECTRUM_TAPE_BLOCK_PAUSE: break; case LIBSPECTRUM_TAPE_BLOCK_GROUP_START: libspectrum_free( block->types.group_start.name ); break; case LIBSPECTRUM_TAPE_BLOCK_GROUP_END: break; case LIBSPECTRUM_TAPE_BLOCK_JUMP: break; case LIBSPECTRUM_TAPE_BLOCK_LOOP_START: break; case LIBSPECTRUM_TAPE_BLOCK_LOOP_END: break; case LIBSPECTRUM_TAPE_BLOCK_SELECT: for( i=0; i<block->types.select.count; i++ ) { libspectrum_free( block->types.select.descriptions[i] ); } libspectrum_free( block->types.select.descriptions ); libspectrum_free( block->types.select.offsets ); break; case LIBSPECTRUM_TAPE_BLOCK_STOP48: break; case LIBSPECTRUM_TAPE_BLOCK_SET_SIGNAL_LEVEL: break; case LIBSPECTRUM_TAPE_BLOCK_COMMENT: libspectrum_free( block->types.comment.text ); break; case LIBSPECTRUM_TAPE_BLOCK_MESSAGE: libspectrum_free( block->types.message.text ); break; case LIBSPECTRUM_TAPE_BLOCK_ARCHIVE_INFO: for( i=0; i<block->types.archive_info.count; i++ ) { libspectrum_free( block->types.archive_info.strings[i] ); } libspectrum_free( block->types.archive_info.ids ); libspectrum_free( block->types.archive_info.strings ); break; case LIBSPECTRUM_TAPE_BLOCK_HARDWARE: libspectrum_free( block->types.hardware.types ); libspectrum_free( block->types.hardware.ids ); libspectrum_free( block->types.hardware.values ); break; case LIBSPECTRUM_TAPE_BLOCK_CUSTOM: libspectrum_free( block->types.custom.description ); libspectrum_free( block->types.custom.data ); break; /* Block types not present in .tzx follow here */ case LIBSPECTRUM_TAPE_BLOCK_RLE_PULSE: libspectrum_free( block->types.rle_pulse.data ); break; case LIBSPECTRUM_TAPE_BLOCK_PULSE_SEQUENCE: libspectrum_free( block->types.pulse_sequence.lengths ); libspectrum_free( block->types.pulse_sequence.pulse_repeats ); break; case LIBSPECTRUM_TAPE_BLOCK_DATA_BLOCK: libspectrum_free( block->types.data_block.data ); libspectrum_free( block->types.data_block.bit0_pulses ); libspectrum_free( block->types.data_block.bit1_pulses ); break; case LIBSPECTRUM_TAPE_BLOCK_CONCAT: /* This should never occur */ default: libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "%s: unknown block type %d", __func__, block->type ); return LIBSPECTRUM_ERROR_LOGIC; } libspectrum_free( block ); 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 */ }
libspectrum_error libspectrum_tape_block_description( libspectrum_tape_block *block, char *buffer, size_t length ) { switch( block->type ) { case LIBSPECTRUM_TAPE_BLOCK_ROM: strncpy( buffer, "Standard Speed Data Block", length ); break; case LIBSPECTRUM_TAPE_BLOCK_TURBO: strncpy( buffer, "Turbo Speed Data Block", length ); break; case LIBSPECTRUM_TAPE_BLOCK_PURE_TONE: strncpy( buffer, "Pure Tone Block", length ); break; case LIBSPECTRUM_TAPE_BLOCK_PULSES: strncpy( buffer, "List of Pulses", length ); break; case LIBSPECTRUM_TAPE_BLOCK_PURE_DATA: strncpy( buffer, "Pure Data Block", length ); break; case LIBSPECTRUM_TAPE_BLOCK_PAUSE: strncpy( buffer, "Pause Block", length ); break; case LIBSPECTRUM_TAPE_BLOCK_GROUP_START: strncpy( buffer, "Group Start Block", length ); break; case LIBSPECTRUM_TAPE_BLOCK_GROUP_END: strncpy( buffer, "Group End Block", length ); break; case LIBSPECTRUM_TAPE_BLOCK_JUMP: strncpy( buffer, "Jump Block", length ); break; case LIBSPECTRUM_TAPE_BLOCK_LOOP_START: strncpy( buffer, "Loop Start Block", length ); break; case LIBSPECTRUM_TAPE_BLOCK_LOOP_END: strncpy( buffer, "Loop End Block", length ); break; case LIBSPECTRUM_TAPE_BLOCK_SELECT: strncpy( buffer, "Select Block", length ); break; case LIBSPECTRUM_TAPE_BLOCK_STOP48: strncpy( buffer, "Stop Tape If In 48K Mode Block", length ); break; case LIBSPECTRUM_TAPE_BLOCK_COMMENT: strncpy( buffer, "Comment Block", length ); break; case LIBSPECTRUM_TAPE_BLOCK_MESSAGE: strncpy( buffer, "Message Block", length ); break; case LIBSPECTRUM_TAPE_BLOCK_ARCHIVE_INFO: strncpy( buffer, "Archive Info Block", length ); break; case LIBSPECTRUM_TAPE_BLOCK_HARDWARE: strncpy( buffer, "Hardware Information Block", length ); break; case LIBSPECTRUM_TAPE_BLOCK_CUSTOM: strncpy( buffer, "Custom Info Block", length ); break; default: libspectrum_print_error( "libspectrum_tape_block_description: unknown block type 0x%02x\n", block->type ); return LIBSPECTRUM_ERROR_LOGIC; } buffer[ length-1 ] = '\0'; return LIBSPECTRUM_ERROR_NONE; }