libspectrum_tape_block* libspectrum_tape_block_internal_init( libspectrum_tape_block_state *it, libspectrum_tape *tape ) { if( !tape || !tape->blocks ) return NULL; it->current_block = tape->blocks; if( libspectrum_tape_block_init( it->current_block->data, it ) ) return NULL; return libspectrum_tape_iterator_current( it->current_block ); }
libspectrum_error libspectrum_tape_append_block( libspectrum_tape *tape, libspectrum_tape_block *block ) { tape->blocks = g_slist_append( tape->blocks, (gpointer)block ); /* If we previously didn't have a tape loaded ( implied by tape->current_block == NULL ), set up so that we point to the start of the tape */ if( !tape->state.current_block ) { tape->state.current_block = tape->blocks; libspectrum_tape_block_init( tape->blocks->data, &(tape->state) ); } 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; }
/* 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_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; }
/* Convert RLE block to a TZX DRB as TZX CSW block support is limited :/ */ static libspectrum_error tzx_write_rle( libspectrum_tape_block *block, libspectrum_byte **buffer, libspectrum_byte **ptr, size_t *length, libspectrum_tape *tape, libspectrum_tape_iterator iterator ) { libspectrum_error error; libspectrum_tape_block_state it; libspectrum_dword scale = libspectrum_tape_block_scale( block ); libspectrum_dword pulse_tstates = 0; libspectrum_dword balance_tstates = 0; int flags = 0; libspectrum_tape_block *raw_block = libspectrum_tape_block_alloc( LIBSPECTRUM_TAPE_BLOCK_RAW_DATA ); libspectrum_tape_block_set_bit_length( raw_block, scale ); libspectrum_tape_block_set_pause ( raw_block, 0 ); rle_state.bits_used = 0; rle_state.level = 0; rle_state.length = 0; rle_state.tape_length = 8192; rle_state.tape_buffer = libspectrum_malloc( rle_state.tape_length ); *rle_state.tape_buffer = 0; it.current_block = iterator; error = libspectrum_tape_block_init( block, &it ); if( error != LIBSPECTRUM_ERROR_NONE ) { libspectrum_free( rle_state.tape_buffer ); libspectrum_tape_block_free( raw_block ); return error; } while( !(flags & LIBSPECTRUM_TAPE_FLAGS_BLOCK) ) { libspectrum_dword pulse_length = 0; /* Use internal version of this that doesn't bugger up the external tape status */ error = libspectrum_tape_get_next_edge_internal( &pulse_tstates, &flags, tape, &it ); if( error != LIBSPECTRUM_ERROR_NONE ) { libspectrum_free( rle_state.tape_buffer ); libspectrum_tape_block_free( raw_block ); return error; } balance_tstates += pulse_tstates; /* next set of pulses is: balance_tstates / scale; */ pulse_length = balance_tstates / scale; balance_tstates = balance_tstates % scale; /* write pulse_length bits of the current level into the buffer */ write_pulse( pulse_length ); } if( rle_state.length || rle_state.bits_used ) { if( rle_state.bits_used ) { rle_state.length++; } else { rle_state.bits_used = 8; } error = libspectrum_tape_block_set_bits_in_last_byte( raw_block, rle_state.bits_used ); if( error != LIBSPECTRUM_ERROR_NONE ) { libspectrum_free( rle_state.tape_buffer ); libspectrum_tape_block_free( raw_block ); return error; } error = libspectrum_tape_block_set_data_length( raw_block, rle_state.length ); if( error != LIBSPECTRUM_ERROR_NONE ) { libspectrum_free( rle_state.tape_buffer ); libspectrum_tape_block_free( raw_block ); return error; } error = libspectrum_tape_block_set_data( raw_block, rle_state.tape_buffer ); if( error != LIBSPECTRUM_ERROR_NONE ) { libspectrum_free( rle_state.tape_buffer ); libspectrum_tape_block_free( raw_block ); return error; } /* now have tzx_write_raw_data finish the job */ tzx_write_raw_data( raw_block, buffer, ptr, length ); } else { libspectrum_free( rle_state.tape_buffer ); } return libspectrum_tape_block_free( raw_block ); }