/* Append to the current tape file in memory; returns 0 if a block was saved or non-zero if there was an error at the emulator level, or tape traps are not active */ int tape_save_trap( void ) { libspectrum_tape_block *block; libspectrum_byte parity, *data; size_t length; int i; /* Do nothing if tape traps aren't active */ if( !settings_current.tape_traps || tape_recording ) return 2; /* Check we're in the right ROM */ if( ! trap_check_rom() ) return 3; block = libspectrum_tape_block_alloc( LIBSPECTRUM_TAPE_BLOCK_ROM ); /* The +2 here is for the flag and parity bytes */ length = DE + 2; libspectrum_tape_block_set_data_length( block, length ); data = malloc( length * sizeof(libspectrum_byte) ); if( !data ) { free( block ); return 1; } libspectrum_tape_block_set_data( block, data ); /* First, store the flag byte (and initialise the parity counter) */ data[0] = parity = A; /* then the main body of the data, counting parity along the way */ for( i=0; i<DE; i++) { libspectrum_byte b = readbyte_internal( IX+i ); parity ^= b; data[i+1] = b; } /* And finally the parity byte */ data[ DE+1 ] = parity; /* Give a 1 second pause after this block */ libspectrum_tape_block_set_pause( block, 1000 ); libspectrum_tape_append_block( tape, block ); tape_modified = 1; ui_tape_browser_update( UI_TAPE_BROWSER_NEW_BLOCK, block ); /* And then return via the RET at #053E, except on Timex 2068 at #00E4 */ if ( machine_current->machine == LIBSPECTRUM_MACHINE_TC2068 || machine_current->machine == LIBSPECTRUM_MACHINE_TS2068 ) { PC = 0x00e4; } else { PC = 0x053e; } return 0; }
static int append_inlay_file( char *inlay_file, libspectrum_tape *tape ) { libspectrum_tape_block* block; char *description; libspectrum_byte* jpg_data; size_t jpg_length; libspectrum_byte* custom_block_data; size_t custom_block_length; block = libspectrum_tape_block_alloc( LIBSPECTRUM_TAPE_BLOCK_CUSTOM ); /* Get the description */ description = malloc( DESCRIPTION_BUFFER_LEN ); memcpy( description, "Picture ", DESCRIPTION_BUFFER_LEN ); libspectrum_tape_block_set_text( block, description ); /* Read in the data */ if( read_file( inlay_file, &jpg_data, &jpg_length ) ) { free( description ); free( block ); return 1; } custom_block_length = jpg_length + 2; custom_block_data = malloc( custom_block_length ); /* Picture format */ if( is_filetype( inlay_file, ".jpg" ) ) { custom_block_data[0] = 1; } else if( is_filetype( inlay_file, ".gif" ) ) { custom_block_data[0] = 0; } else { free( custom_block_data ); return 1; } /* Picture description length 0 == "Inlay Card" */ custom_block_data[1] = 0; /* and the JPG itself */ memcpy( custom_block_data + 2, jpg_data, jpg_length ); libspectrum_tape_block_set_data_length( block, custom_block_length ); libspectrum_tape_block_set_data( block, custom_block_data ); /* Finally, put the block into the block list */ libspectrum_tape_append_block( tape, block ); return 0; }
static int append_scr_file( char *scr_file, libspectrum_tape *tape ) { libspectrum_tape_block* block; char *description; libspectrum_byte* scr_data; size_t scr_length; libspectrum_byte* custom_block_data; size_t custom_block_length; block = libspectrum_tape_block_alloc( LIBSPECTRUM_TAPE_BLOCK_CUSTOM ); /* Get the description */ description = malloc( DESCRIPTION_BUFFER_LEN ); memcpy( description, "Spectrum Screen ", DESCRIPTION_BUFFER_LEN ); libspectrum_tape_block_set_text( block, description ); /* Read in the data */ if( read_file( scr_file, &scr_data, &scr_length ) ) { free( description ); free( block ); return 1; } custom_block_length = scr_length + 2; custom_block_data = malloc( custom_block_length ); /* Picture description length 0 == "Loading Screen" */ custom_block_data[0] = 0; /* Border colour 0 == black */ custom_block_data[1] = 0; /* and the SCR itself */ memcpy( custom_block_data + 2, scr_data, scr_length ); libspectrum_tape_block_set_data_length( block, custom_block_length ); libspectrum_tape_block_set_data( block, custom_block_data ); /* Finally, put the block into the block list */ libspectrum_tape_append_block( tape, block ); return 0; }
int tape_record_stop( void ) { libspectrum_tape_block* block; /* put last sample into the recording buffer */ rec_state.tape_buffer_used = write_rec_buffer( rec_state.tape_buffer, rec_state.tape_buffer_used, rec_state.last_level_count ); /* stop scheduling events and turn buffer into a block and pop into the current tape */ event_remove_type( record_event ); block = libspectrum_tape_block_alloc( LIBSPECTRUM_TAPE_BLOCK_RLE_PULSE ); libspectrum_tape_block_set_scale( block, rec_state.tstates_per_sample ); libspectrum_tape_block_set_data_length( block, rec_state.tape_buffer_used ); libspectrum_tape_block_set_data( block, rec_state.tape_buffer ); libspectrum_tape_append_block( tape, block ); rec_state.tape_buffer = NULL; rec_state.tape_buffer_size = 0; rec_state.tape_buffer_used = 0; tape_modified = 1; ui_tape_browser_update( UI_TAPE_BROWSER_NEW_BLOCK, block ); tape_recording = 0; /* Also want to reenable other tape actions */ ui_menu_activate( UI_MENU_ITEM_TAPE_RECORDING, 0 ); return 0; }
int main(int argc, char *argv[]) { int opt, i, quiet = 0; char *output = "output.tzx", *p; int turbo_params[6] = { 2168, 3323, 667, 735, 855, 1710 }; FILE *fd; size_t data_len; libspectrum_tape *tape; libspectrum_byte *data; libspectrum_tape_block *block; libspectrum_dword pause, pause_tstates; while ((opt = getopt(argc, argv, "vhqt:o:")) != -1) { switch(opt) { case 'q': quiet = 1; break; case 't': if (sscanf(optarg, "%i:%i:%i:%i:%i:%i", &turbo_params[0], &turbo_params[1], &turbo_params[2], &turbo_params[3], &turbo_params[4], &turbo_params[5]) != 6) fatal(argv[0], "Failed to parse turbo params"); break; case 'o': output = strdup(optarg); break; case 'h': help(argv[0]); exit(0); case 'v': fprintf(stderr, VERSION "\n"); exit(0); default: fprintf(stderr, "\n"); help(argv[0]); exit(1); } } if (optind >= argc) fatal(argv[0], "Expected input filename to process"); if (libspectrum_init()) fatal(argv[0], "Failed to init libspectrum"); fd = fopen(argv[optind], "rb"); if (!fd) fatal(argv[0], "Failed to open input file"); fseek(fd, 0, SEEK_END); data_len = ftell(fd); fseek(fd, 0, SEEK_SET); data = (libspectrum_byte *) malloc(data_len); if (!data) { fclose(fd); fatal(argv[0], "Failed to allocate memory"); } if (fread(data, sizeof(libspectrum_byte), data_len, fd) != data_len) { fclose(fd); free(data); fatal(argv[0], "Failed to read input file"); } fclose(fd); tape = libspectrum_tape_alloc(); if (!tape) { fclose(fd); free(data); fatal(argv[0], "Failed to allocate tape memory"); } if (libspectrum_tape_read(tape, data, data_len, LIBSPECTRUM_ID_UNKNOWN, argv[optind])) { fclose(fd); free(data); free(tape); fatal(argv[0], "Failed to read tape"); } free(data); for (i = optind + 1; i < argc; i++) { for (p = argv[i]; *p && isdigit(*p); p++); if (*p) { fprintf(stderr, "WARNING: Parameter is not a valid block number, skipping\n"); continue; } if (libspectrum_tape_nth_block(tape, atoi(argv[i]))) continue; block = libspectrum_tape_current_block(tape); if (!block) continue; if (!quiet) printf("%s: Setting block #%i (type 0x%02x): ", argv[0], atoi(argv[i]), libspectrum_tape_block_type(block)); data = libspectrum_tape_block_data(block); data_len = libspectrum_tape_block_data_length(block); pause = libspectrum_tape_block_pause(block); pause_tstates = libspectrum_tape_block_pause_tstates(block); if (libspectrum_tape_block_set_type(block, LIBSPECTRUM_TAPE_BLOCK_TURBO) || libspectrum_tape_block_set_pilot_pulses(block, turbo_params[1]) || libspectrum_tape_block_set_pilot_length(block, turbo_params[0]) || libspectrum_tape_block_set_sync1_length(block, turbo_params[2]) || libspectrum_tape_block_set_sync2_length(block, turbo_params[3]) || libspectrum_tape_block_set_bit0_length(block, turbo_params[4]) || libspectrum_tape_block_set_bit1_length(block, turbo_params[5]) || libspectrum_tape_block_set_data(block, data) || libspectrum_tape_block_set_data_length(block, data_len) || libspectrum_tape_block_set_pause(block, pause) || libspectrum_tape_block_set_pause_tstates(block, pause_tstates) || libspectrum_tape_block_set_bits_in_last_byte(block, 8) ) { if (!quiet) printf("failed!\n"); fprintf(stderr, "WARNING: Failed to set block %i\n", atoi(argv[i])); continue; } if (!quiet) printf("OK\n"); } data = NULL; data_len = 0; if (libspectrum_tape_write(&data, &data_len, tape, LIBSPECTRUM_ID_TAPE_TZX)) { free(tape); fatal(argv[0], "Failed to write the output file"); } fd = fopen(output, "wb"); if (!fd) { free(data); free(tape); fatal(argv[0], "Failed to open output file"); } if (fwrite(data, sizeof(libspectrum_byte), data_len, fd) != data_len) { fclose(fd); free(data); free(tape); fatal(argv[0], "Failed to write output file"); } fclose(fd); free(data); free(tape); exit(0); }
libspectrum_error libspectrum_wav_read( libspectrum_tape *tape, const char *filename ) { libspectrum_byte *buffer; size_t length; libspectrum_byte *tape_buffer; size_t tape_length; size_t data_length; libspectrum_tape_block *block = NULL; int frames; /* Our filehandle from libaudiofile */ AFfilehandle handle; /* The track we're using in the file */ int track = AF_DEFAULT_TRACK; if( !filename ) { libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "libspectrum_wav_read: no filename provided - wav files can only be loaded from a file" ); return LIBSPECTRUM_ERROR_LOGIC; } handle = afOpenFile( filename, "r", NULL ); if( handle == AF_NULL_FILEHANDLE ) { libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "libspectrum_wav_read: audiofile failed to open file:%s", filename ); return LIBSPECTRUM_ERROR_LOGIC; } if( afSetVirtualSampleFormat( handle, track, AF_SAMPFMT_UNSIGNED, 8 ) ) { afCloseFile( handle ); libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "libspectrum_wav_read: audiofile failed to set virtual sample format" ); return LIBSPECTRUM_ERROR_LOGIC; } if( afSetVirtualChannels( handle, track, 1 ) ) { afCloseFile( handle ); libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "libspectrum_wav_read: audiofile failed to set virtual channel count" ); return LIBSPECTRUM_ERROR_LOGIC; } length = afGetFrameCount( handle, track ); tape_length = length; if( tape_length%8 ) tape_length += 8 - (tape_length%8); buffer = libspectrum_new0( libspectrum_byte, tape_length * afGetChannels(handle, track) ); frames = afReadFrames( handle, track, buffer, length ); if( frames == -1 ) { libspectrum_free( buffer ); afCloseFile( handle ); libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "libspectrum_wav_read: can't calculate number of frames in audio file" ); return LIBSPECTRUM_ERROR_CORRUPT; } if( !length ) { libspectrum_free( buffer ); afCloseFile( handle ); libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "libspectrum_wav_read: empty audio file, nothing to load" ); return LIBSPECTRUM_ERROR_CORRUPT; } if( frames != length ) { libspectrum_free( buffer ); afCloseFile( handle ); libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "libspectrum_wav_read: read %d frames, but expected %lu\n", frames, (unsigned long)length ); return LIBSPECTRUM_ERROR_CORRUPT; } block = libspectrum_tape_block_alloc( LIBSPECTRUM_TAPE_BLOCK_RAW_DATA ); /* 44100 Hz 79 t-states 22050 Hz 158 t-states */ libspectrum_tape_block_set_bit_length( block, 3500000/afGetRate( handle, track ) ); libspectrum_set_pause_ms( block, 0 ); libspectrum_tape_block_set_bits_in_last_byte( block, length % LIBSPECTRUM_BITS_IN_BYTE ? length % LIBSPECTRUM_BITS_IN_BYTE : LIBSPECTRUM_BITS_IN_BYTE ); data_length = tape_length / LIBSPECTRUM_BITS_IN_BYTE; libspectrum_tape_block_set_data_length( block, data_length ); tape_buffer = libspectrum_new0( libspectrum_byte, data_length ); libspectrum_byte *from = buffer; libspectrum_byte *to = tape_buffer; length = tape_length; do { libspectrum_byte val = 0; int i; for( i = 7; i >= 0; i-- ) { if( *from++ > 127 ) val |= 1 << i; } *to++ = val; } while ((length -= 8) > 0); libspectrum_tape_block_set_data( block, tape_buffer ); libspectrum_tape_append_block( tape, block ); if( afCloseFile( handle ) ) { libspectrum_free( buffer ); libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "libspectrum_wav_read: failed to close audio file" ); return LIBSPECTRUM_ERROR_UNKNOWN; } libspectrum_free( buffer ); /* Successful completion */ 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 ); }