static int update_archive_file( char *archive_file, libspectrum_tape *tape ) { libspectrum_tape_block* info_block; libspectrum_error error; libspectrum_tape *tzx; libspectrum_tape_iterator iterator; if( read_tape( archive_file, &tzx ) ) return 1; /* Get the new archive block */ info_block = libspectrum_tape_iterator_init( &iterator, tzx ); while( info_block && libspectrum_tape_block_type( info_block ) != LIBSPECTRUM_TAPE_BLOCK_ARCHIVE_INFO ) { info_block = libspectrum_tape_iterator_next( &iterator ); } if( !info_block ) { libspectrum_tape_free( tzx ); return 1; } /* Remove any existing archive block */ error = remove_block_type( tape, LIBSPECTRUM_TAPE_BLOCK_ARCHIVE_INFO ); if( error ) { libspectrum_tape_free( tzx ); return error; } /* Finally, put the new info block at the beginning of the block list */ error = libspectrum_tape_insert_block( tape, info_block, 0 ); if( error ) { libspectrum_tape_free( tzx ); return error; } return 0; }
/* Free the memory used by a list of blocks, but not the object itself */ libspectrum_error libspectrum_tape_clear( libspectrum_tape *tape ) { g_slist_foreach( tape->blocks, block_free, NULL ); g_slist_free( tape->blocks ); tape->blocks = NULL; libspectrum_tape_iterator_init( &(tape->state.current_block), tape ); return LIBSPECTRUM_ERROR_NONE; }
static int remove_block_type( libspectrum_tape *tape, libspectrum_tape_type id ) { libspectrum_tape_block* block; libspectrum_tape_iterator iterator; for( block = libspectrum_tape_iterator_init( &iterator, tape ); block; block = libspectrum_tape_iterator_next( &iterator ) ) { if( libspectrum_tape_block_type( block ) == id ) { libspectrum_tape_remove_block( tape, iterator ); /* Iterator is invalidated by delete, so start again */ block = libspectrum_tape_iterator_init( &iterator, tape ); } } return 0; }
/* Call a user-supplied function for every block in the current tape */ int tape_foreach( void (*function)( libspectrum_tape_block *block, void *user_data), void *user_data ) { libspectrum_tape_block *block; libspectrum_tape_iterator iterator; for( block = libspectrum_tape_iterator_init( &iterator, tape ); block; block = libspectrum_tape_iterator_next( &iterator ) ) function( block, user_data ); return 0; }
/* Allocate a list of blocks */ libspectrum_error libspectrum_tape_alloc( libspectrum_tape **tape ) { (*tape) = (libspectrum_tape*)malloc( sizeof( libspectrum_tape ) ); if( !(*tape) ) { libspectrum_print_error( LIBSPECTRUM_ERROR_MEMORY, "libspectrum_tape_alloc: out of memory" ); return LIBSPECTRUM_ERROR_MEMORY; } (*tape)->blocks = NULL; libspectrum_tape_iterator_init( &((*tape)->state.current_block), *tape ); (*tape)->state.loop_block = NULL; return LIBSPECTRUM_ERROR_NONE; }
/* Cause the next block on the tape to be active, initialise it and return it */ libspectrum_tape_block* libspectrum_tape_select_next_block( libspectrum_tape *tape ) { libspectrum_tape_block *block; if( !tape->state.current_block ) return NULL; block = libspectrum_tape_iterator_next( &(tape->state.current_block) ); if( !block ) block = libspectrum_tape_iterator_init( &(tape->state.current_block), tape ); if( libspectrum_tape_block_init( block, &(tape->state) ) ) return NULL; return block; }
int tape_blocks_entries(char entries[][256],int length) { libspectrum_tape_block *block; libspectrum_tape_iterator iterator; int num_entries=0; for( block = libspectrum_tape_iterator_init( &iterator, tape ); block; block = libspectrum_tape_iterator_next( &iterator ),num_entries++) { char *p=entries[num_entries]; sprintf(p,"%4d: ",num_entries); tape_block_details(p+6,length-6,block ); } return num_entries; }
libspectrum_error libspectrum_tape_get_next_edge_internal( libspectrum_dword *tstates, int *flags, libspectrum_tape *tape, libspectrum_tape_block_state *it ) { int error; libspectrum_tape_block *block = libspectrum_tape_iterator_current( it->current_block ); /* 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; if( block ) { switch( block->type ) { case LIBSPECTRUM_TAPE_BLOCK_ROM: error = rom_edge( &(block->types.rom), &(it->block_state.rom), tstates, &end_of_block ); if( error ) return error; break; case LIBSPECTRUM_TAPE_BLOCK_TURBO: error = turbo_edge( &(block->types.turbo), &(it->block_state.turbo), tstates, &end_of_block ); if( error ) return error; break; case LIBSPECTRUM_TAPE_BLOCK_PURE_TONE: error = tone_edge( &(block->types.pure_tone), &(it->block_state.pure_tone), tstates, &end_of_block ); if( error ) return error; break; case LIBSPECTRUM_TAPE_BLOCK_PULSES: error = pulses_edge( &(block->types.pulses), &(it->block_state.pulses), tstates, &end_of_block ); if( error ) return error; break; case LIBSPECTRUM_TAPE_BLOCK_PURE_DATA: error = pure_data_edge( &(block->types.pure_data), &(it->block_state.pure_data), tstates, &end_of_block); if( error ) return error; break; case LIBSPECTRUM_TAPE_BLOCK_RAW_DATA: error = raw_data_edge( &(block->types.raw_data), &(it->block_state.raw_data), tstates, &end_of_block ); if( error ) return error; break; case LIBSPECTRUM_TAPE_BLOCK_GENERALISED_DATA: error = generalised_data_edge( &(block->types.generalised_data), &(it->block_state.generalised_data), tstates, &end_of_block, flags ); 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: if( it->current_block->next && block->types.loop_start.count ) { it->loop_block = it->current_block->next; it->loop_count = block->types.loop_start.count; } *tstates = 0; end_of_block = 1; break; case LIBSPECTRUM_TAPE_BLOCK_LOOP_END: if( it->loop_block ) { if( --(it->loop_count) ) { it->current_block = it->loop_block; no_advance = 1; } else { it->loop_block = NULL; } } *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; case LIBSPECTRUM_TAPE_BLOCK_RLE_PULSE: error = rle_pulse_edge( &(block->types.rle_pulse), &(it->block_state.rle_pulse), tstates, &end_of_block); if( error ) return error; break; default: *tstates = 0; libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "libspectrum_tape_get_next_edge: unknown block type 0x%02x", block->type ); return LIBSPECTRUM_ERROR_LOGIC; } } else { *tstates = 0; end_of_block = 1; } /* 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 ) { libspectrum_tape_iterator_next( &(it->current_block) ); /* If we've just hit the end of the tape, stop the tape (and then `rewind' to the start) */ if( libspectrum_tape_iterator_current( it->current_block ) == NULL ) { *flags |= LIBSPECTRUM_TAPE_FLAGS_STOP; libspectrum_tape_iterator_init( &(it->current_block), tape ); } } /* Initialise the new block */ error = libspectrum_tape_block_init( libspectrum_tape_iterator_current( it->current_block ), it ); if( error ) return error; } return LIBSPECTRUM_ERROR_NONE; }
static libspectrum_dword find_sample_rate( libspectrum_tape *tape ) { libspectrum_tape_iterator iterator; libspectrum_tape_block *block; libspectrum_dword sample_rate = 44100; int found = 0; /* FIXME: If tape has only one block that is a sampled type, just use it's rate and if it RLE, we should just zlib and write */ for( block = libspectrum_tape_iterator_init( &iterator, tape ); block; block = libspectrum_tape_iterator_next( &iterator ) ) { switch( libspectrum_tape_block_type( block ) ) { case LIBSPECTRUM_TAPE_BLOCK_RAW_DATA: case LIBSPECTRUM_TAPE_BLOCK_RLE_PULSE: { libspectrum_dword block_rate = 3500000 / libspectrum_tape_block_bit_length( block ); if( found ) { if( block_rate != sample_rate ) { libspectrum_print_error( LIBSPECTRUM_ERROR_WARNING, "find_sample_rate: converting tape with mixed sample rates; conversion may well not work" ); } } sample_rate = block_rate; found = 1; } break; case LIBSPECTRUM_TAPE_BLOCK_ROM: case LIBSPECTRUM_TAPE_BLOCK_TURBO: case LIBSPECTRUM_TAPE_BLOCK_PURE_TONE: case LIBSPECTRUM_TAPE_BLOCK_PULSES: case LIBSPECTRUM_TAPE_BLOCK_PURE_DATA: case LIBSPECTRUM_TAPE_BLOCK_GENERALISED_DATA: 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_COMMENT: case LIBSPECTRUM_TAPE_BLOCK_MESSAGE: case LIBSPECTRUM_TAPE_BLOCK_ARCHIVE_INFO: case LIBSPECTRUM_TAPE_BLOCK_HARDWARE: case LIBSPECTRUM_TAPE_BLOCK_CUSTOM: case LIBSPECTRUM_TAPE_BLOCK_CONCAT: break; default: libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "libspectrum_csw_write: unknown block type 0x%02x", libspectrum_tape_block_type( block ) ); } } return sample_rate; }
libspectrum_error internal_tzx_write( libspectrum_byte **buffer, size_t *length, libspectrum_tape *tape ) { libspectrum_error error; libspectrum_tape_iterator iterator; libspectrum_tape_block *block; libspectrum_byte *ptr = *buffer; size_t signature_length = strlen( libspectrum_tzx_signature ); /* First, write the .tzx signature and the version numbers */ libspectrum_make_room( buffer, signature_length + 2, &ptr, length ); memcpy( ptr, libspectrum_tzx_signature, signature_length ); ptr += signature_length; *ptr++ = 1; /* Major version number */ *ptr++ = 20; /* Minor version number */ for( block = libspectrum_tape_iterator_init( &iterator, tape ); block; block = libspectrum_tape_iterator_next( &iterator ) ) { switch( libspectrum_tape_block_type( block ) ) { case LIBSPECTRUM_TAPE_BLOCK_ROM: tzx_write_rom( block, buffer, &ptr, length ); break; case LIBSPECTRUM_TAPE_BLOCK_TURBO: tzx_write_turbo( block, buffer, &ptr, length ); break; case LIBSPECTRUM_TAPE_BLOCK_PURE_TONE: tzx_write_pure_tone( block, buffer, &ptr, length ); break; case LIBSPECTRUM_TAPE_BLOCK_PULSES: tzx_write_pulses( block, buffer, &ptr, length ); break; case LIBSPECTRUM_TAPE_BLOCK_PURE_DATA: tzx_write_data( block, buffer, &ptr, length ); break; case LIBSPECTRUM_TAPE_BLOCK_RAW_DATA: tzx_write_raw_data( block, buffer, &ptr, length ); break; case LIBSPECTRUM_TAPE_BLOCK_GENERALISED_DATA: error = tzx_write_generalised_data( block, buffer, &ptr, length ); if( error != LIBSPECTRUM_ERROR_NONE ) { libspectrum_free( *buffer ); return error; } break; case LIBSPECTRUM_TAPE_BLOCK_PAUSE: tzx_write_pause( block, buffer, &ptr, length ); break; case LIBSPECTRUM_TAPE_BLOCK_GROUP_START: tzx_write_group_start( block, buffer, &ptr, length ); break; case LIBSPECTRUM_TAPE_BLOCK_GROUP_END: tzx_write_empty_block( buffer, &ptr, length, libspectrum_tape_block_type( block ) ); break; case LIBSPECTRUM_TAPE_BLOCK_JUMP: tzx_write_jump( block, buffer, &ptr, length ); break; case LIBSPECTRUM_TAPE_BLOCK_LOOP_START: tzx_write_loop_start( block, buffer, &ptr, length ); break; case LIBSPECTRUM_TAPE_BLOCK_LOOP_END: tzx_write_empty_block( buffer, &ptr, length, libspectrum_tape_block_type( block ) ); break; case LIBSPECTRUM_TAPE_BLOCK_SELECT: tzx_write_select( block, buffer, &ptr, length ); break; case LIBSPECTRUM_TAPE_BLOCK_STOP48: tzx_write_stop( buffer, &ptr, length ); break; case LIBSPECTRUM_TAPE_BLOCK_COMMENT: tzx_write_comment( block, buffer, &ptr, length ); break; case LIBSPECTRUM_TAPE_BLOCK_MESSAGE: tzx_write_message( block, buffer, &ptr, length ); break; case LIBSPECTRUM_TAPE_BLOCK_ARCHIVE_INFO: tzx_write_archive_info( block, buffer, &ptr, length ); break; case LIBSPECTRUM_TAPE_BLOCK_HARDWARE: tzx_write_hardware( block, buffer, &ptr, length ); break; case LIBSPECTRUM_TAPE_BLOCK_CUSTOM: tzx_write_custom( block, buffer, &ptr, length ); break; case LIBSPECTRUM_TAPE_BLOCK_RLE_PULSE: error = tzx_write_rle( block, buffer, &ptr, length, tape, iterator ); if( error != LIBSPECTRUM_ERROR_NONE ) { libspectrum_free( *buffer ); return error; } break; default: libspectrum_free( *buffer ); libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "libspectrum_tzx_write: unknown block type 0x%02x", libspectrum_tape_block_type( block ) ); return LIBSPECTRUM_ERROR_LOGIC; } } (*length) = ptr - *buffer; return LIBSPECTRUM_ERROR_NONE; }