static cassette_image::error sc3000_bit_load(cassette_image *cassette) { cassette_image::error err; UINT64 image_size = cassette_image_size(cassette); UINT64 image_pos = 0; double time_index = 0.0; double time_displacement; UINT8 data; while (image_pos < image_size) { cassette_image_read(cassette, &data, image_pos, 1); switch (data) { case '1': MODULATE(1); break; case '0': MODULATE(0); break; case ' ': err = cassette_put_sample( cassette, 0, time_index, 1/1200.0, 0); if (err != cassette_image::error::SUCCESS) return err; time_index += 1/1200.0; break; } image_pos++; } return cassette_image::error::SUCCESS; }
static cassette_image::error csw_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) { uint8_t header[22]; cassette_image_read(cassette, header, 0, sizeof(header)); if (memcmp(&header[0], CSW_HEADER, sizeof(CSW_HEADER) - 1)) { return cassette_image::error::INVALID_IMAGE; } return cassette_legacy_identify( cassette, opts, &csw_legacy_fill_wave ); }
static casserr_t mo5_k5_load( cassette_image *cass ) { size_t size = cassette_image_size( cass ), pos = 0; int i, sz, sz2, hbit = 0; UINT8 in, in2, in3, typ, block[264], sum; int invalid = 0, hbitsize = 0, dcmoto = 0; LOG (( "mo5_k5_load: start conversion, size=%li\n", (long)size )); PRINT (( "mo5_k5_load: open cassette, length: %li bytes\n", (long) size )); /* store a half-bit */ #define K5_PUT_HBIT \ do \ { \ cassette_put_sample ( cass, 0, hbitsize * MO5_HBIT_LENGTH, MO5_HBIT_LENGTH, (hbit ? 1 : -1) << 30 ); \ hbitsize++; \ } while ( 0 ) /* store one bit */ #define K5_PUT_BIT( BIT ) \ do \ { \ if ( BIT ) \ { \ K5_PUT_HBIT; \ hbit = !hbit; \ K5_PUT_HBIT; \ } \ else \ { \ K5_PUT_HBIT; \ K5_PUT_HBIT; \ } \ hbit = !hbit; \ } while (0) /* store one byte, no start / stop bit, converse order from TO7 */ #define K5_PUT_BYTE( BYTE ) \ do \ { \ UINT8 b = BYTE; \ int x; \ for ( x = 0; x < 8; x++ ) \ K5_PUT_BIT( (b >> (7 - x)) & 1 ); \ } while (0) /* store filler */ #define K5_FILL_0( SIZE ) \ do \ { \ if ( (SIZE) > 0 ) \ { \ int j; \ LOG (( "mo5_k5_load: 0-filler size=%i hbitstart=%i\n", (SIZE), hbitsize )); \ for ( j = 0; j < (SIZE); j++ ) \ K5_PUT_BIT( 0 ); \ } \ } while (0) #define K5_FILL_01( SIZE ) \ do \ { \ if ( (SIZE) > 0 ) \ { \ int j; \ LOG (( "mo5_k5_load: 0x01 filler size=%i bitstart=%i\n", (SIZE), hbitsize )); \ for ( j = 0; j < (SIZE); j++ ) \ K5_PUT_BYTE( 0x01 ); \ } \ } while (0) /* check format */ cassette_image_read( cass, block, 0, 64 ); for ( i = 3; ; i++ ) { if ( ( i >= size ) || ( i >= 64 ) ) { /* ? */ PRINT (( "to5_k5_load: WARNING: this does not look like a MO or TO cassette.\n" )); break; } else if ( ( block[i-3] == 0x01 ) && ( block[i-2] == 0x3c ) && ( block[i-1] == 0x5a ) && ! block[i] ) { /* MO */ break; } else if ( ( block[i-3] == 0xff ) && ( block[i-2] == 0x01 ) && ( block[i-1] == 0x3c ) && ! block[i] ) { /* TO */ PRINT (( "to5_k5_load: WARNING: this looks like a TO cassette, not a MO one.\n" )); break; } } cassette_image_read( cass, block, pos, 6 ); if ( ! memcmp( block, "DCMOTO", 6 ) || ! memcmp( block, "DCMO5", 5 ) || ! memcmp( block, "DCMO6", 5 ) ) dcmoto = 1; /* loop over regular blocks */ while ( pos < size ) { rebounce: /* skip DCMOTO header*/ if ( dcmoto ) { cassette_image_read( cass, block, pos, 6 ); if ( ! memcmp( block, "DCMOTO", 6 ) ) { LOG (( "mo5_k5_load: DCMOTO signature found at off=$%x\n", (int)pos )); pos += 6; } else if ( ! memcmp( block, "DCMO", 4 ) ) { LOG (( "mo5_k5_load: DCMO* signature found at off=$%x\n", (int)pos )); pos += 5; } } /* skip 0x01 filler */ for ( sz = 0; pos < size; pos++, sz++ ) { cassette_image_read( cass, &in, pos, 1 ); if ( in != 0x01 ) break; } /* get block header */ if ( pos + 4 > size ) { pos -= sz; break; } cassette_image_read( cass, block, pos, 4 ); typ = block[2]; sz2 = (UINT8) (block[3]-1); if ( block[0] != 0x3c || block[1] != 0x5a || ( typ != 0x00 && typ != 0x01 && typ != 0xff ) || pos+sz2 > size ) { pos -= sz; break; } pos += 4; /* get block */ cassette_image_read( cass, block+4, pos, sz2 ); pos += sz2; /* 0-fillers and 0x01-fillers */ if ( typ == 0 || typ == 0xff ) K5_FILL_0( 1200 ); else K5_FILL_0( 300 ); /* for MO6 */ K5_FILL_01( sz < 10 ? 10 : sz ); /* put block */ LOG (( "mo5_k5_load: block off=$%x type=$%02X size=%i hbitstart=%i\n", (int) pos-sz2-4, typ, sz2, hbitsize )); VLOG (( "mo5_k5_load: data:" )); for ( i = 0; i < sz2 + 4; i ++) { VLOG (( " $%02X", block[i] )); K5_PUT_BYTE( block[i] ); } VLOG (( "\n" )); /* checksum */ for ( i = 0, sum = 0; i < sz2; i++ ) sum += block[i+4]; if ( sum ) LOG(( "mo5_k5_load: invalid checksum $%02X (should be 0)\n", sum )); /* if it is a directory enty, says so to the user */ if ( typ == 0 ) { char name[] = "01234567.ABC"; UINT8 t = block[15]; UINT8 u = block[16]; int p = (hbitsize - sz2 - 4 - sz) * MO5_HBIT_LENGTH; memcpy( name, block+4, 8 ); memcpy( name+9, block+12, 3 ); for ( i = 0; name[i]; i++) { if ( name[i] < ' ' || name[i] >= 127 ) name[i] = '?'; } PRINT (( "mo5_k5_load: file \"%s\" type=%s,%s at %imn %is\n", name, (t==0) ? "bas" : (t==1) ? "dat" : (t==2) ? "bin" : "???", (u == 0) ? "a" : (u == 0xff) ? "b" : "?", p / 60, p % 60 )); } /* 0-fillers */ if ( typ == 0xff || typ == 0x00 ) K5_FILL_0( 1800 ); } /* dump trailing bytes, but also look for beginnings of blocks */ if ( pos < size ) { invalid++; K5_FILL_0( 1200 ); LOG (( "mo5_k5_load: trailing trash off=$%x size=%i hbitstart=%i\n", (int) pos, (int) (size-pos), hbitsize )); for ( ; pos < size; pos++ ) { cassette_image_read( cass, &in, pos, 1 ); if ( dcmoto && in=='D' ) { /* skip DCMOTO header*/ cassette_image_read( cass, block, pos, 6 ); if ( ! memcmp( block, "DCMOTO", 6 ) ) { LOG (( "mo5_k5_load: DCMOTO signature found at off=$%x\n", (int)pos )); pos += 6; cassette_image_read( cass, &in, pos, 1 ); } else if ( ! memcmp( block, "DCMO", 4 ) ) { LOG (( "mo5_k5_load: DCMO* signature found at off=$%x\n", (int)pos )); pos += 5; cassette_image_read( cass, &in, pos, 1 ); } } for ( sz = 0; pos < size && in == 0x01; sz++ ) { pos++; cassette_image_read( cass, &in, pos, 1 ); } if ( sz > 6 ) { cassette_image_read( cass, &in2, pos+1, 1 ); cassette_image_read( cass, &in3, pos+2, 1 ); if ( invalid < 10 && in == 0x3c && in2 == 0x5a && (in3 == 0x00 || in3 == 0x01 || in3 == 0xff ) ) { /* regular block found */ LOG (( "mo5_k5_load: hidden regular block found\n" )); pos -= sz; goto rebounce; } if ( invalid < 10 && sz > 6 && ( (in == 0x3c && in2 == 0x5a) || (in == 0xc3 && in2 == 0x5a) || (in == 0xc3 && in2 == 0x3c) || (in == 0x87 && in2 == 0x4a) ) ) { /* special block found */ K5_FILL_0( 1200 ); LOG (( "mo5_k5_load: special block $%02X $%02X found off=$%x hbitstart=%i\n", in, in2, (int) pos-sz, hbitsize )); } } VLOG (( "mo5_k5_load: special data:" )); for ( i = 0; i < sz; i++ ) { K5_PUT_BYTE( 0x01 ); VLOG (( " $01" )); } K5_PUT_BYTE( in ); VLOG (( " $%02X\n", in )); } } if ( invalid ) PRINT (( "mo5_k5_load: WARNING: the k5 has an unknown structure and may not work\n" )); sz = hbitsize * MO5_HBIT_LENGTH; PRINT (( "mo5_k5_load: cassette length: %imn %is (%i half-bits)\n", sz / 60, sz % 60, hbitsize )); return CASSETTE_ERROR_SUCCESS; }
static casserr_t to7_k7_load( cassette_image *cass ) { #if ! K7_SPEED_HACK static const INT8 square_wave[] = { -128, 127 }; double time = 0.; #endif size_t size = cassette_image_size( cass ), pos = 0; int i, sz, sz2, bitmax = 1024, invalid = 0; UINT8 in, typ, block[264]; LOG (( "to7_k7_load: start conversion, size=%li\n", (long)size )); PRINT (( "to7_k7_load: open cassette, length: %li bytes\n", (long) size )); if ( to7_k7_bits ) { free( to7_k7_bits ); to7_k7_bits = NULL; } to7_k7_bitsize = 0; to7_k7_bits = (UINT8*)malloc(bitmax ); /* store one period */ #if K7_SPEED_HACK #define K7_PUT( PERIOD ) #else #define K7_PUT( PERIOD ) \ do \ { \ casserr_t err; \ err = cassette_put_samples( cass, 0, time, (PERIOD), 2, 1, \ square_wave, CASSETTE_WAVEFORM_8BIT ); \ if ( err ) \ return err; \ time += (PERIOD); \ } while (0) #endif /* store one bit */ #define K7_PUT_BIT( BIT ) \ do \ { \ int b; \ if ( BIT ) \ { \ for ( b = 0; b < 7; b++ ) \ K7_PUT( TO7_PERIOD_CASS_1 ); \ } \ else \ { \ for ( b = 0; b < 5; b++ ) \ K7_PUT( TO7_PERIOD_CASS_0 ); \ } \ if ( to7_k7_bitsize + 1 >= bitmax ) \ { \ UINT8* a = (UINT8*)malloc(bitmax * 2); \ memcpy ( a, to7_k7_bits, bitmax ); \ bitmax *= 2; \ to7_k7_bits = a; \ } \ to7_k7_bits[ to7_k7_bitsize++ ] = (BIT); \ } while (0) /* store one byte, with start / stop bits */ #define K7_PUT_BYTE( BYTE ) \ do \ { \ UINT8 x; \ K7_PUT_BIT( 0 ); \ for ( x = 0; x < 8; x++ ) \ K7_PUT_BIT( ( (BYTE) >> x ) & 1 ); \ K7_PUT_BIT( 1 ); \ K7_PUT_BIT( 1 ); \ } while (0) #define K7_FILL_1( SIZE ) \ do \ { \ if ( (SIZE) > 0 ) { \ int ii; \ LOG (( "to7_k7_load: 1-filler size=%i bitstart=%i\n", \ (SIZE), to7_k7_bitsize )); \ for ( ii = 0; ii < (SIZE); ii++ ) K7_PUT_BIT( 1 ); \ } \ } while (0) #define K7_FILL_ff( SIZE ) \ do \ { \ if ( (SIZE) > 0 ) \ { \ int ii; \ LOG (( "to7_k7_load: 0xff filler size=%i bitstart=%i\n", (SIZE), to7_k7_bitsize )); \ for ( ii = 0; ii < (SIZE); ii++ ) \ K7_PUT_BYTE( 0xff ); \ } \ } while (0) /* check format */ cassette_image_read( cass, block, 0, 64 ); for ( i = 3; ; i++ ) { if ( ( i >= size ) || ( i >= 64 ) ) { /* ? */ PRINT (( "to7_k7_load: WARNING: this does not look like a MO or TO cassette.\n" )); break; } else if ( ( block[i-3] == 0x01 ) && ( block[i-2] == 0x3c ) && ( block[i-1] == 0x5a ) && ! block[i] ) { /* MO */ PRINT (( "to7_k7_load: WARNING: this looks like a MO cassette, not a TO one.\n" )); break; } else if ( ( block[i-3] == 0xff ) && ( block[i-2] == 0x01 ) && ( block[i-1] == 0x3c ) && ! block[i] ) { /* TO */ break; } } /* skip to first 0xff filler */ for ( sz = 0; pos < size; pos++, sz++ ) { cassette_image_read( cass, &in, pos, 1 ); if ( in == 0xff ) break; } if ( sz > 0 ) LOG (( "to7_k7_load: skip %i trash bytes\n", sz )); /* loop over regular blocks */ while ( pos < size ) { rebounce: /* skip 0xff filler */ for ( sz = 0; pos < size; pos++, sz++ ) { cassette_image_read( cass, &in, pos, 1 ); /* actually, we are bit laxist and treat as 0xff bytes with at least 5 bits out of 8 set to 1 */ for ( i = 0; in; in >>= 1 ) i += (in & 1); if ( i < 5 ) break; } /* get block header */ if ( pos + 4 > size ) { pos -= sz; break; } cassette_image_read( cass, block, pos, 4 ); typ = block[2]; sz2 = block[3]+1; if ( block[0] != 0x01 || block[1] != 0x3c || ( typ != 0x00 && typ != 0x01 && typ != 0xff ) ) { pos -= sz; break; } pos += 4; /* get block */ cassette_image_read( cass, block+4, pos, sz2 ); pos += sz2; /* 1-filler and 0xff-filler */ if ( typ == 0 || typ == 0xff ) K7_FILL_1( 1000 ); K7_FILL_ff( sz ); /* put block */ LOG (( "to7_k7_load: block off=$%x type=$%02X size=%i bitstart=%i\n", (int) pos-sz2-4, typ, sz2, to7_k7_bitsize )); VLOG (( "to7_k7_load: data:" )); for ( i = 0; i < sz2 + 4; i ++) { VLOG (( " $%02X", block[i] )); K7_PUT_BYTE( block[i] ); } VLOG (( "\n" )); /* if it is a directory enty, says so to the user */ if ( typ == 0 ) { char name[] = "01234567.ABC"; UINT8 t = block[15]; UINT8 u = block[16]; int p = (to7_k7_bitsize - sz2 - 4 - sz) * TO7_BIT_LENGTH; memcpy( name, block+4, 8 ); memcpy( name+9, block+12, 3 ); for ( i = 0; name[i]; i++) { if ( name[i] < ' ' || name[i] >= 127 ) name[i] = '?'; } PRINT (( "to7_k7_load: file \"%s\" type=%s,%s at %imn %is\n", name, (t==0) ? "bas" : (t==1) ? "dat" : (t==2) ? "bin" : "???", (u == 0) ? "a" : (u == 0xff) ? "b" : "?", p / 60, p % 60 )); } /* extra 1-fillers */ if ( typ == 0 || typ == 0xff ) K7_FILL_1( 1000 ); } /* trailing data with invalid block structure => dump it in a raw form, but stay alert for hidden block starts */ if ( pos < size ) { invalid++; LOG (( "to7_k7_load: trailing raw bytes off=$%x bitstart=%i\n", (int) pos, to7_k7_bitsize )); /* put block */ for (; pos < size; pos++ ) { cassette_image_read( cass, &in, pos, 1 ); for ( sz = 0; pos < size && in == 0xff; sz++ ) { pos++; cassette_image_read( cass, &in, pos, 1 ); } if ( invalid < 10 && sz > 4 && in == 0x01 && pos + 4 <= size ) { UINT8 in1,in2; cassette_image_read( cass, &in1, pos+1, 1 ); cassette_image_read( cass, &in2, pos+2, 1 ); if ( (in1 == 0x3c) && ((in2 == 0x00) || (in2 == 0x01) ) ) { /* seems we have a regular block hidden in raw data => rebounce */ LOG (( "to7_k7_load: hidden regular block found\n" )); pos -= sz; goto rebounce; } if ( ( ( in1 == 0x3d ) && ( in2 == 0 ) ) || ( ( in1 == 0x57 ) && ( in2 == 0x49 ) ) ) { /* special block (Infogrames) => just prepend filler */ K7_FILL_1 ( 500 ); LOG (( "to7_k7_load: special $%02X $%02X $%02X block found off=$%x bitstart=%i\n", in, in1, in2, (int) pos, to7_k7_bitsize )); } } for ( i = 0; i < sz; i++ ) K7_PUT_BYTE( 0xff ); K7_PUT_BYTE( in ); } } if ( invalid ) PRINT (( "to7_k7_load: WARNING: the k7 has an unknown structure and may not work\n" )); sz = to7_k7_bitsize * TO7_BIT_LENGTH; PRINT (( "to7_k7_load: cassette length: %imn %is (%i samples)\n", sz / 60, sz % 60, to7_k7_bitsize )); return CASSETTE_ERROR_SUCCESS; }
static casserr_t wavfile_process(cassette_image *cassette, struct CassetteOptions *opts, int read_waveform) { UINT8 file_header[12]; UINT8 tag_header[8]; UINT8 format_tag[16]; UINT32 stated_size; UINT64 file_size; UINT32 tag_size; UINT32 tag_samples; UINT64 offset; int format_specified = FALSE; UINT16 format_type = 0; UINT32 bytes_per_second = 0; // UINT16 block_align = 0; int waveform_flags = 0; /* read header */ cassette_image_read(cassette, file_header, 0, sizeof(file_header)); offset = sizeof(file_header); /* check magic numbers */ if (memcmp(&file_header[0], magic1, 4)) return CASSETTE_ERROR_INVALIDIMAGE; if (memcmp(&file_header[8], magic2, 4)) return CASSETTE_ERROR_INVALIDIMAGE; /* read and sanity check size */ stated_size = get_leuint32(&file_header[4]) + 8; file_size = cassette_image_size(cassette); if (stated_size > file_size) stated_size = (UINT32) file_size; while(offset < stated_size) { cassette_image_read(cassette, tag_header, offset, sizeof(tag_header)); tag_size = get_leuint32(&tag_header[4]); offset += sizeof(tag_header); if (!memcmp(tag_header, format_tag_id, 4)) { /* format tag */ if (format_specified || (tag_size < sizeof(format_tag))) return CASSETTE_ERROR_INVALIDIMAGE; format_specified = TRUE; cassette_image_read(cassette, format_tag, offset, sizeof(format_tag)); format_type = get_leuint16(&format_tag[0]); opts->channels = get_leuint16(&format_tag[2]); opts->sample_frequency = get_leuint32(&format_tag[4]); bytes_per_second = get_leuint32(&format_tag[8]); // block_align = get_leuint16(&format_tag[12]); opts->bits_per_sample = get_leuint16(&format_tag[14]); if (format_type != WAV_FORMAT_PCM) return CASSETTE_ERROR_INVALIDIMAGE; if (opts->sample_frequency * opts->bits_per_sample * opts->channels / 8 != bytes_per_second) return CASSETTE_ERROR_INVALIDIMAGE; switch(opts->bits_per_sample) { case 8: waveform_flags = CASSETTE_WAVEFORM_8BIT | CASSETTE_WAVEFORM_UNSIGNED; // 8-bits wav are stored unsigned break; case 16: waveform_flags = CASSETTE_WAVEFORM_16BITLE; break; case 32: waveform_flags = CASSETTE_WAVEFORM_32BITLE; break; default: return CASSETTE_ERROR_INVALIDIMAGE; } } else if (!memcmp(tag_header, data_tag_id, 4)) { /* data tag */ if (!format_specified) return CASSETTE_ERROR_INVALIDIMAGE; if (read_waveform) { tag_samples = tag_size / (opts->bits_per_sample / 8) / opts->channels; cassette_read_samples(cassette, opts->channels, 0.0, tag_samples / ((double) opts->sample_frequency), tag_samples, offset, waveform_flags); } } else { /* ignore other tags */ } offset += tag_size; } return CASSETTE_ERROR_SUCCESS; }
static int get_cas_block(cassette_image *cassette, UINT64 *offset, UINT8 *block, int *block_len) { UINT8 block_length = 0; UINT8 block_checksum = 0; UINT64 current_offset; UINT64 image_size; PAIR p; int i; int state = 0; int phase = 0; synccount = 0; p.w.l = 0; image_size = cassette_image_size(cassette); current_offset = *offset; while(current_offset < image_size) { cassette_image_read(cassette, &p.b.h, current_offset, 1); current_offset++; for (i = 0; i < 8; i++) { p.w.l >>= 1; if (state == 0) { /* searching for a block */ if (p.b.l == 0x3C) { /* found one! */ phase = i; state++; } else if (p.b.l == 0x55) { synccount++; } } else if (i == phase) { *(block++) = p.b.l; switch(state) { case 1: /* found file type */ block_checksum = p.b.l; state++; break; case 2: /* found file size */ block_length = p.b.l; *block_len = ((int) block_length) + 3; block_checksum += p.b.l; state++; break; case 3: /* data byte */ if (block_length) { block_length--; block_checksum += p.b.l; } else { /* end of block */ if (p.b.l != block_checksum) { /* checksum failure */ return FALSE; } else { /* checksum success */ *offset = current_offset; return TRUE; } } } } } } /* no more blocks */ return FALSE; }
static cassette_image::error coladam_ddp_load( cassette_image *cass ) { double time = 0.; int i, block, prev_sign=-1; uint8_t buffer[0x400]; cassette_image::error err = cassette_image::error::SUCCESS; // It would appear that data packs that originally had the type GW data layout and headers work fine when converted to type // HE. Thus we set all tapes to type HE. int layout_type = TYPE_HE; // Track 0 for ( i = 0; i < 2753; i++ ) // leading zero bytes { err = coladam_put_byte(cass, 0, &time, 0x00, &prev_sign); } for (block = 0; block < 128; block++) { cassette_image_read( cass, buffer, 0x20000+0x400*block, 0x400 ); err = coladam_put_block(cass, 0, &time, &prev_sign, block, buffer, layout_type); } for (block = 128; block < 131; block++) { cassette_image_read( cass, buffer, 0x3f400+0x400*(block-128), 0x400 ); err = coladam_put_block(cass, 0, &time, &prev_sign, block, buffer, layout_type); } // Track 1 time = 0.; for ( i = 0; i < 2753; i++ ) // leading zero bytes { err = coladam_put_byte(cass, 1, &time, 0x00, &prev_sign); } if (layout_type == TYPE_HE) { for (block = 0; block < 64; block++) { cassette_image_read( cass, buffer, 0x10000+0x400*block, 0x400 ); err = coladam_put_block(cass, 1, &time, &prev_sign, block, buffer, layout_type); } for (block = 64; block < 128; block++) { cassette_image_read( cass, buffer, 0x00000+0x400*(block-64), 0x400 ); err = coladam_put_block(cass, 1, &time, &prev_sign, block, buffer, layout_type); } for (block = 128; block < 131; block++) { cassette_image_read( cass, buffer, 0x0f400+0x400*(block-128), 0x400 ); err = coladam_put_block(cass, 1, &time, &prev_sign, block, buffer, layout_type); } } else { time = 0; for ( i = 0; i < 2753; i++ ) // leading zero bytes { err = coladam_put_byte(cass, 1, &time, 0x00, &prev_sign); } for (block = 0; block < 128; block++) { cassette_image_read( cass, buffer, 0x400*block, 0x400 ); err = coladam_put_block(cass, 1, &time, &prev_sign, block, buffer, layout_type); } for (block = 128; block < 131; block++) { cassette_image_read( cass, buffer, 0x1f400+0x400*(block-128), 0x400 ); err = coladam_put_block(cass, 1, &time, &prev_sign, block, buffer, layout_type); } } return err; }