static int tap_cas_fill_wave( INT16 *buffer, int length, UINT8 *bytes ) { INT16 *p = buffer; int size = 0; while( size < length ) { int data_size = bytes[0] + ( bytes[1] << 8 ); int pilot_length = ( bytes[2] == 0x00 ) ? 8064 : 3220; /* TZX specification */ // int pilot_length = ( bytes[2] == 0x00 ) ? 8063 : 3223; /* worldofspectrum */ logerror( "tap_cas_fill_wave: Handling TAP block containing 0x%X bytes\n", data_size ); bytes += 2; size += tzx_cas_handle_block( &p, bytes, 1000, data_size, 2168, pilot_length, 667, 735, 855, 1710, 8 ); bytes += data_size; } return size; }
static int tap_cas_to_wav_size( const UINT8 *casdata, int caslen ) { int size = 0; const UINT8 *p = casdata; while( p < casdata + caslen ) { int data_size = p[0] + ( p[1] << 8 ); int pilot_length = ( p[2] == 0x00 ) ? 8064 : 3220; /* TZX specification */ // int pilot_length = ( p[2] == 0x00 ) ? 8063 : 3223; /* worldofspectrum */ logerror( "tap_cas_to_wav_size: Handling TAP block containing 0x%X bytes", data_size ); p += 2; size += tzx_cas_handle_block( NULL, p, 1000, data_size, 2168, pilot_length, 667, 735, 855, 1710, 8 ); logerror( ", total size is now: %d\n", size ); p += data_size; } return size; }
/* Will go through blocks and calculate number of samples needed. If buffer is not nullptr the sample data will also be written. */ static int tzx_cas_do_work( int16_t **buffer ) { int current_block = 0; int size = 0; wave_data = WAVE_LOW; int loopcount = 0, loopoffset = 0; while (current_block < block_count) { int pause_time; uint32_t data_size; int text_size, total_size, i; int pilot, pilot_length, sync1, sync2; int bit0, bit1, bits_in_last_byte; uint8_t *cur_block = blocks[current_block]; uint8_t block_type = cur_block[0]; uint16_t tstates = 0; /* Uncomment this to include into error.log a list of the types each block */ LOG_FORMATS("tzx_cas_fill_wave: block %d, block_type %02x\n", current_block, block_type); switch (block_type) { case 0x10: /* Standard Speed Data Block (.TAP block) */ pause_time = cur_block[1] + (cur_block[2] << 8); data_size = cur_block[3] + (cur_block[4] << 8); pilot_length = (cur_block[5] == 0x00) ? 8064 : 3220; size += tzx_cas_handle_block(buffer, &cur_block[5], pause_time, data_size, 2168, pilot_length, 667, 735, 855, 1710, 8); current_block++; break; case 0x11: /* Turbo Loading Data Block */ pilot = cur_block[1] + (cur_block[2] << 8); sync1 = cur_block[3] + (cur_block[4] << 8); sync2 = cur_block[5] + (cur_block[6] << 8); bit0 = cur_block[7] + (cur_block[8] << 8); bit1 = cur_block[9] + (cur_block[10] << 8); pilot_length = cur_block[11] + (cur_block[12] << 8); bits_in_last_byte = cur_block[13]; pause_time = cur_block[14] + (cur_block[15] << 8); data_size = cur_block[16] + (cur_block[17] << 8) + (cur_block[18] << 16); size += tzx_cas_handle_block(buffer, &cur_block[19], pause_time, data_size, pilot, pilot_length, sync1, sync2, bit0, bit1, bits_in_last_byte); current_block++; break; case 0x12: /* Pure Tone */ pilot = cur_block[1] + (cur_block[2] << 8); pilot_length = cur_block[3] + (cur_block[4] << 8); size += tzx_cas_handle_block(buffer, cur_block, 0, 0, pilot, pilot_length, 0, 0, 0, 0, 0); current_block++; break; case 0x13: /* Sequence of Pulses of Different Lengths */ for (data_size = 0; data_size < cur_block[1]; data_size++) { pilot = cur_block[2 + 2 * data_size] + (cur_block[3 + 2 * data_size] << 8); size += tzx_cas_handle_block(buffer, cur_block, 0, 0, pilot, 1, 0, 0, 0, 0, 0); } current_block++; break; case 0x14: /* Pure Data Block */ bit0 = cur_block[1] + (cur_block[2] << 8); bit1 = cur_block[3] + (cur_block[4] << 8); bits_in_last_byte = cur_block[5]; pause_time = cur_block[6] + (cur_block[7] << 8); data_size = cur_block[8] + (cur_block[9] << 8) + (cur_block[10] << 16); size += tzx_cas_handle_block(buffer, &cur_block[11], pause_time, data_size, 0, 0, 0, 0, bit0, bit1, bits_in_last_byte); current_block++; break; case 0x20: /* Pause (Silence) or 'Stop the Tape' Command */ pause_time = cur_block[1] + (cur_block[2] << 8); if (pause_time == 0) { /* pause = 0 is used to let an emulator automagically stop the tape in MESS we do not do that, so we insert a 5 second pause. */ pause_time = 5000; } size += tzx_cas_handle_block(buffer, cur_block, pause_time, 0, 0, 0, 0, 0, 0, 0, 0); current_block++; break; case 0x16: /* C64 ROM Type Data Block */ // Deprecated in TZX 1.20 case 0x17: /* C64 Turbo Tape Data Block */ // Deprecated in TZX 1.20 case 0x34: /* Emulation Info */ // Deprecated in TZX 1.20 case 0x40: /* Snapshot Block */ // Deprecated in TZX 1.20 LOG_FORMATS("Deprecated block type (%02x) encountered.\n", block_type); LOG_FORMATS("Please look for an updated .tzx file.\n"); current_block++; break; case 0x30: /* Text Description */ ascii_block_common_log("Text Description Block", block_type); for (data_size = 0; data_size < cur_block[1]; data_size++) LOG_FORMATS("%c", cur_block[2 + data_size]); LOG_FORMATS("\n"); current_block++; break; case 0x31: /* Message Block */ ascii_block_common_log("Message Block", block_type); LOG_FORMATS("Expected duration of the message display: %02x\n", cur_block[1]); LOG_FORMATS("Message: \n"); for (data_size = 0; data_size < cur_block[2]; data_size++) { LOG_FORMATS("%c", cur_block[3 + data_size]); if (cur_block[3 + data_size] == 0x0d) LOG_FORMATS("\n"); } LOG_FORMATS("\n"); current_block++; break; case 0x32: /* Archive Info */ ascii_block_common_log("Archive Info Block", block_type); total_size = cur_block[1] + (cur_block[2] << 8); text_size = 0; for (data_size = 0; data_size < cur_block[3]; data_size++) // data_size = number of text blocks, in this case { if (cur_block[4 + text_size] < 0x09) { LOG_FORMATS("%s: \n", archive_ident[cur_block[4 + text_size]]); } else { LOG_FORMATS("Comment(s): \n"); } for (i = 0; i < cur_block[4 + text_size + 1]; i++) { LOG_FORMATS("%c", cur_block[4 + text_size + 2 + i]); } text_size += 2 + i; } LOG_FORMATS("\n"); if (text_size != total_size) LOG_FORMATS("Malformed Archive Info Block (Text length different from the declared one).\n Please verify your tape image.\n"); current_block++; break; case 0x33: /* Hardware Type */ ascii_block_common_log("Hardware Type Block", block_type); for (data_size = 0; data_size < cur_block[1]; data_size++) // data_size = number of hardware blocks, in this case { LOG_FORMATS("Hardware Type %02x - Hardware ID %02x - ", cur_block[2 + data_size * 3], cur_block[2 + data_size * 3 + 1]); LOG_FORMATS("%s \n ", hw_info[cur_block[2 + data_size * 3 + 2]]); } current_block++; break; case 0x35: /* Custom Info Block */ ascii_block_common_log("Custom Info Block", block_type); for (data_size = 0; data_size < 10; data_size++) { LOG_FORMATS("%c", cur_block[1 + data_size]); } LOG_FORMATS(":\n"); text_size = cur_block[11] + (cur_block[12] << 8) + (cur_block[13] << 16) + (cur_block[14] << 24); for (data_size = 0; data_size < text_size; data_size++) LOG_FORMATS("%c", cur_block[15 + data_size]); LOG_FORMATS("\n"); current_block++; break; case 0x5A: /* "Glue" Block */ LOG_FORMATS("Glue Block (type %02x) encountered.\n", block_type); LOG_FORMATS("Please use a .tzx handling utility to split the merged tape files.\n"); current_block++; break; case 0x24: /* Loop Start */ loopcount = cur_block[1] + (cur_block[2] << 8); current_block++; loopoffset = current_block; LOG_FORMATS("loop start %d %d\n", loopcount, current_block); break; case 0x25: /* Loop End */ if (loopcount>0) { current_block = loopoffset; loopcount--; LOG_FORMATS("do loop\n"); } else { current_block++; } break; case 0x21: /* Group Start */ case 0x22: /* Group End */ case 0x23: /* Jump To Block */ case 0x26: /* Call Sequence */ case 0x27: /* Return From Sequence */ case 0x28: /* Select Block */ case 0x2A: /* Stop Tape if in 48K Mode */ case 0x2B: /* Set signal level */ default: LOG_FORMATS("Unsupported block type (%02x) encountered.\n", block_type); current_block++; break; case 0x15: /* Direct Recording */ // used on 'bombscar' in the cpc_cass list // having this missing is fatal tstates = cur_block[1] + (cur_block[2] << 8); pause_time= cur_block[3] + (cur_block[4] << 8); bits_in_last_byte = cur_block[5]; data_size = cur_block[6] + (cur_block[7] << 8) + (cur_block[8] << 16); size += tzx_handle_direct(buffer, &cur_block[9], pause_time, data_size, tstates, bits_in_last_byte); current_block++; break; case 0x18: /* CSW Recording */ // having this missing is fatal printf("Unsupported block type (0x15 - CSW Recording) encountered.\n"); current_block++; break; case 0x19: /* Generalized Data Block */ { // having this missing is fatal // used crudely by batmanc in spectrum_cass list (which is just a redundant encoding of batmane ?) data_size = cur_block[1] + (cur_block[2] << 8) + (cur_block[3] << 16) + (cur_block[4] << 24); pause_time= cur_block[5] + (cur_block[6] << 8); uint32_t totp = cur_block[7] + (cur_block[8] << 8) + (cur_block[9] << 16) + (cur_block[10] << 24); int npp = cur_block[11]; int asp = cur_block[12]; if (asp == 0 && totp > 0) asp = 256; uint32_t totd = cur_block[13] + (cur_block[14] << 8) + (cur_block[15] << 16) + (cur_block[16] << 24); int npd = cur_block[17]; int asd = cur_block[18]; if (asd == 0 && totd > 0) asd = 256; size += tzx_handle_generalized(buffer, &cur_block[19], pause_time, data_size, totp, npp, asp, totd, npd, asd); current_block++; } break; } } return size; }
/* Will go through blocks and calculate number of samples needed. If buffer is not NULL the sample data will also be written. */ static int tzx_cas_do_work( INT16 **buffer ) { int current_block = 0; int size = 0; wave_data = WAVE_LOW; while( current_block < block_count ) { int pause_time; int data_size; int pilot, pilot_length, sync1, sync2; int bit0, bit1, bits_in_last_byte; UINT8 *cur_block = blocks[current_block]; UINT8 block_type =cur_block[0]; logerror( "tzx_cas_fill_wave: block %d, block_type %02x\n", current_block, block_type ); switch( block_type ) { case 0x10: /* Standard Speed Data Block (.TAP block) */ pause_time = cur_block[1] + ( cur_block[2] << 8 ); data_size = cur_block[3] + ( cur_block[4] << 8 ); pilot_length = ( cur_block[5] == 0x00 ) ? 8064 : 3220; size += tzx_cas_handle_block( buffer, &cur_block[5], pause_time, data_size, 2168, pilot_length, 667, 735, 855, 1710, 8 ); current_block++; break; case 0x11: /* Turbo Loading Data Block */ pilot = cur_block[1] + ( cur_block[2] << 8 ); sync1 = cur_block[3] + ( cur_block[4] << 8 ); sync2 = cur_block[5] + ( cur_block[6] << 8 ); bit0 = cur_block[7] + ( cur_block[8] << 8 ); bit1 = cur_block[9] + ( cur_block[10] << 8 ); pilot_length = cur_block[11] + ( cur_block[12] << 8 ); bits_in_last_byte = cur_block[13]; pause_time = cur_block[14] + ( cur_block[15] << 8 ); data_size = cur_block[16] + ( cur_block[17] << 8 ) + ( cur_block[18] << 16 ); size += tzx_cas_handle_block( buffer, &cur_block[19], pause_time, data_size, pilot, pilot_length, sync1, sync2, bit0, bit1, bits_in_last_byte ); current_block++; break; case 0x12: /* Pure Tone */ pilot = cur_block[1] + ( cur_block[2] << 8 ); pilot_length = cur_block[3] + ( cur_block[4] << 8 ); size += tzx_cas_handle_block( buffer, cur_block, 0, 0, pilot, pilot_length, 0, 0, 0, 0, 0 ); current_block++; break; case 0x13: /* Sequence of Pulses of Different Lengths */ for( data_size = 0; data_size < cur_block[1]; data_size++ ) { pilot = cur_block[1 + 2*data_size] + ( cur_block[2 + 2*data_size] << 8 ); size += tzx_cas_handle_block( buffer, cur_block, 0, 0, pilot, 1, 0, 0, 0, 0, 0 ); } current_block++; break; case 0x14: /* Pure Data Block */ bit0 = cur_block[1] + ( cur_block[2] << 8 ); bit1 = cur_block[3] + ( cur_block[4] << 8 ); bits_in_last_byte = cur_block[5]; pause_time = cur_block[6] + ( cur_block[7] << 8 ); data_size = cur_block[8] + ( cur_block[9] << 8 ) + ( cur_block[10] << 16 ); size += tzx_cas_handle_block( buffer, &cur_block[11], pause_time, data_size, 0, 0, 0, 0, bit0, bit1, bits_in_last_byte ); current_block++; break; case 0x15: /* Direct Recording */ case 0x16: /* C64 ROM Type Data Block */ case 0x17: /* C64 Turbo Tape Data Block */ logerror( "Unsupported block type (%02x) encountered\n", block_type ); current_block++; break; case 0x20: /* Pause (Silence) or 'Stop the Tape' Command */ pause_time = cur_block[1] + ( cur_block[2] << 8 ); if ( pause_time == 0 ) { /* pause = 0 is used to let an emulator automagically stop the tape in MESS we do not do that, so we insert a 5 second pause. */ pause_time = 5000; } size += tzx_cas_handle_block( buffer, cur_block, pause_time, 0, 0, 0, 0, 0, 0, 0, 0 ); current_block++; break; case 0x21: /* Group Start */ case 0x22: /* Group End */ case 0x23: /* Jump To Block */ case 0x24: /* Loop Start */ case 0x25: /* Loop End */ case 0x26: /* Call Sequence */ case 0x27: /* Return From Sequence */ case 0x28: /* Select Block */ case 0x2A: /* Stop Tape if in 48K Mode */ case 0x30: /* Text Description */ case 0x31: /* Message Block */ case 0x32: /* Archive Info */ case 0x33: /* Hardware Type */ case 0x34: /* Emulation Info */ case 0x35: /* Custom Info Block */ case 0x40: /* Snapshot Block */ case 0x5A: /* Merge Block */ default: logerror( "Unsupported block type (%02x) encountered\n", block_type ); current_block++; break; } } return size; }