static floperr_t g64_read_track(floppy_image_legacy *floppy, int head, int track, UINT64 offset, void *buffer, size_t buflen) { /* The following is written into the buffer: n bytes of track data 1982 bytes of speed zone data get_track_size() returns n (size of track data) */ struct g64dsk_tag *tag = get_tag(floppy); floperr_t err; UINT64 track_offset; UINT16 track_length = tag->track_size[head][track]; // get track offset err = get_track_offset(floppy, head, track, &track_offset); if (err) return err; if ((head <= tag->heads) && track_offset) { if (buflen < track_length) { printf("G64 track buffer too small: %u < %u!", (UINT32)buflen, track_length); exit(-1); } // read track data floppy_image_read(floppy, ((UINT8*)buffer), track_offset + 2, track_length); // skip the 2 track length bytes in the beginning of track data if (tag->speed_zone_offset[head][track] > 3) { // read speed block floppy_image_read(floppy, ((UINT8*)buffer) + track_length, tag->speed_zone_offset[head][track], G64_SPEED_BLOCK_SIZE); } else { // create a speed block with the same speed zone for the whole track UINT8 speed = tag->speed_zone_offset[head][track] & 0x03; UINT8 speed_byte = (speed << 6) | (speed << 4) | (speed << 2) | speed; memset(((UINT8*)buffer) + track_length, speed_byte, G64_SPEED_BLOCK_SIZE); } LOG_FORMATS("G64 side %u track %.1f length %u\n", head, get_dos_track(track), track_length); } else { // no data for given track, or tried to read side 1 memset(((UINT8*)buffer), 0, buflen); LOG_FORMATS("G64 side %u track %.1f\n", head, get_dos_track(track)); } return FLOPPY_ERROR_SUCCESS; }
static uint32_t d88_get_sector_offset(floppy_image_legacy* floppy, int head, int track, int sector) { struct d88_tag* tag = get_d88_tag(floppy); uint32_t offset = 0; uint8_t sector_hdr[16]; uint32_t len; uint32_t secs; int count; // get offset of the beginning of the track offset = tag->trackoffset[(track*tag->heads)+head]; floppy_image_read(floppy,sector_hdr,offset,16); secs = sector_hdr[4]; for(count=0;count<secs;count++) { floppy_image_read(floppy,sector_hdr,offset,16); if(sector == sector_hdr[2]) { LOG_FORMATS("d88_get_sector_offset - track %i, side %i, sector %02x, returns %08x\n",track,head,sector,offset+16); return offset + 16; } len = (sector_hdr[15] << 8) | sector_hdr[14]; len += 16; offset += len; } LOG_FORMATS("d88_get_sector_offset - track %i, side %i, sector %02x, not found\n",track,head,sector); return 0; }
bool apd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image) { uint64_t size = io_generic_size(io); std::vector<uint8_t> img(size); io_generic_read(io, &img[0], 0, size); int err; std::vector<uint8_t> gz_ptr; z_stream d_stream; int inflate_size = (img[size - 1] << 24) | (img[size - 2] << 16) | (img[size - 3] << 8) | img[size - 4]; uint8_t *in_ptr = &img[0]; if (!memcmp(&img[0], GZ_HEADER, sizeof(GZ_HEADER))) { gz_ptr.resize(inflate_size); d_stream.zalloc = nullptr; d_stream.zfree = nullptr; d_stream.opaque = nullptr; d_stream.next_in = in_ptr; d_stream.avail_in = size; d_stream.next_out = &gz_ptr[0]; d_stream.avail_out = inflate_size; err = inflateInit2(&d_stream, MAX_WBITS | 16); if (err != Z_OK) { LOG_FORMATS("inflateInit2 error: %d\n", err); return false; } err = inflate(&d_stream, Z_FINISH); if (err != Z_STREAM_END && err != Z_OK) { LOG_FORMATS("inflate error: %d\n", err); return false; } err = inflateEnd(&d_stream); if (err != Z_OK) { LOG_FORMATS("inflateEnd error: %d\n", err); return false; } size = inflate_size; img = gz_ptr; } int data = 0x7d0; for (int track = 0; track < 166; track++) { uint32_t sdlen = little_endianize_int32(*(uint32_t *)(&img[(track * 12) + 8 + 0x0])); uint32_t ddlen = little_endianize_int32(*(uint32_t *)(&img[(track * 12) + 8 + 0x4])); uint32_t qdlen = little_endianize_int32(*(uint32_t *)(&img[(track * 12) + 8 + 0x8])); if (sdlen > 0) { generate_track_from_bitstream(track / 2, track % 2, &img[data], sdlen, image); data += (sdlen + 7) >> 3; } if (ddlen > 0) { generate_track_from_bitstream(track / 2, track % 2, &img[data], ddlen, image); data += (ddlen + 7) >> 3; }
static floperr_t g64_read_track(floppy_image *floppy, int head, int track, UINT64 offset, void *buffer, size_t buflen) { struct g64dsk_tag *tag = get_tag(floppy); floperr_t err; UINT64 track_offset; UINT16 track_length = 0; /* get track offset */ err = get_track_offset(floppy, head, track, &track_offset); if (err) return err; if (!head && track_offset) { if (buflen < tag->track_size) { printf("G64 track buffer too small: %u!", (UINT32)buflen); exit(-1); } /* read track */ floppy_image_read(floppy, buffer, track_offset + 2, tag->track_size); } else { /* set track length to 0 */ memset(buffer, 0, buflen); } if (LOG) LOG_FORMATS("G64 track %.1f length %u\n", get_dos_track(track), track_length); return FLOPPY_ERROR_SUCCESS; }
bool g64_format::save(io_generic *io, floppy_image *image) { int tracks, heads; image->get_actual_geometry(tracks, heads); tracks = TRACK_COUNT * heads; // write header UINT8 header[] = { 'G', 'C', 'R', '-', '1', '5', '4', '1', 0x00, tracks, TRACK_LENGTH & 0xff, TRACK_LENGTH >> 8 }; io_generic_write(io, header, POS_SIGNATURE, sizeof(header)); // write tracks for (int head = 0; head < heads; head++) { int tracks_written = 0; dynamic_buffer trackbuf(TRACK_LENGTH-2); for (int track = 0; track < TRACK_COUNT; track++) { UINT32 tpos = POS_TRACK_OFFSET + (track * 4); UINT32 spos = tpos + (tracks * 4); UINT32 dpos = POS_TRACK_OFFSET + (tracks * 4 * 2) + (tracks_written * TRACK_LENGTH); io_generic_write_filler(io, 0x00, tpos, 4); io_generic_write_filler(io, 0x00, spos, 4); if (image->get_buffer(track, head).size() <= 1) continue; int track_size; int speed_zone; // figure out the cell size and speed zone from the track data if ((speed_zone = generate_bitstream(track, head, 3, &trackbuf[0], track_size, image)) == -1) if ((speed_zone = generate_bitstream(track, head, 2, &trackbuf[0], track_size, image)) == -1) if ((speed_zone = generate_bitstream(track, head, 1, &trackbuf[0], track_size, image)) == -1) if ((speed_zone = generate_bitstream(track, head, 0, &trackbuf[0], track_size, image)) == -1) throw emu_fatalerror("g64_format: Cannot determine speed zone for track %u", track); LOG_FORMATS("head %u track %u size %u cell %u\n", head, track, track_size, c1541_cell_size[speed_zone]); UINT8 track_offset[4]; UINT8 speed_offset[4]; UINT8 track_length[2]; place_integer_le(track_offset, 0, 4, dpos); place_integer_le(speed_offset, 0, 4, speed_zone); place_integer_le(track_length, 0, 2, track_size/8); io_generic_write(io, track_offset, tpos, 4); io_generic_write(io, speed_offset, spos, 4); io_generic_write_filler(io, 0xff, dpos, TRACK_LENGTH); io_generic_write(io, track_length, dpos, 2); io_generic_write(io, &trackbuf[0], dpos + 2, track_size); tracks_written++; } } return true; }
static int tap_cas_to_wav_size( const uint8_t *casdata, int caslen ) { int size = 0; const uint8_t *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 */ LOG_FORMATS("tap_cas_to_wav_size: Handling TAP block containing 0x%X bytes", data_size); p += 2; size += tzx_cas_handle_block(nullptr, p, 1000, data_size, 2168, pilot_length, 667, 735, 855, 1710, 8); LOG_FORMATS(", total size is now: %d\n", size); p += data_size; } return size; }
static int tzx_cas_to_wav_size( const uint8_t *casdata, int caslen ) { int size = 0; /* Header size plus major and minor version number */ if (caslen < 10) { LOG_FORMATS("tzx_cas_to_wav_size: cassette image too small\n"); goto cleanup; } /* Check for correct header */ if (memcmp(casdata, TZX_HEADER, sizeof(TZX_HEADER))) { LOG_FORMATS("tzx_cas_to_wav_size: cassette image has incompatible header\n"); goto cleanup; } /* Check major version number in header */ if (casdata[0x08] > SUPPORTED_VERSION_MAJOR) { LOG_FORMATS("tzx_cas_to_wav_size: unsupported version\n"); goto cleanup; } tzx_cas_get_blocks(casdata, caslen); LOG_FORMATS("tzx_cas_to_wav_size: %d blocks found\n", block_count); if (block_count == 0) { LOG_FORMATS("tzx_cas_to_wav_size: no blocks found!\n"); goto cleanup; } size = tzx_cas_do_work(nullptr); return size; cleanup: return -1; }
int fsd_format::identify(io_generic *io, UINT32 form_factor) { UINT8 h[3]; io_generic_read(io, h, 0, 3); if (memcmp(h, "FSD", 3) == 0) { return 100; } LOG_FORMATS("fsd: no match\n"); return 0; }
bool g64_format::save(io_generic *io, floppy_image *image) { UINT8 header[] = { 'G', 'C', 'R', '-', '1', '5', '4', '1', 0x00, 0x54, TRACK_LENGTH & 0xff, TRACK_LENGTH >> 8 }; io_generic_write(io, header, SIGNATURE, sizeof(header)); int head = 0; int tracks_written = 0; for (int track = 0; track < 84; track++) { offs_t tpos = TRACK_OFFSET + track * 4; offs_t spos = SPEED_ZONE + track * 4; offs_t dpos = TRACK_DATA + tracks_written * TRACK_LENGTH; io_generic_write_filler(io, 0x00, tpos, 4); io_generic_write_filler(io, 0x00, spos, 4); if (image->get_track_size(track, head) <= 1) continue; UINT8 *trackbuf = global_alloc_array(UINT8, TRACK_LENGTH-2); int track_size; int speed_zone; // figure out the cell size and speed zone from the track data if ((speed_zone = generate_bitstream(track, head, 3, trackbuf, track_size, image)) == -1) if ((speed_zone = generate_bitstream(track, head, 2, trackbuf, track_size, image)) == -1) if ((speed_zone = generate_bitstream(track, head, 1, trackbuf, track_size, image)) == -1) if ((speed_zone = generate_bitstream(track, head, 0, trackbuf, track_size, image)) == -1) throw emu_fatalerror("g64_format: Cannot determine speed zone for track %u", track); LOG_FORMATS("track %u size %u cell %u\n", track, track_size, c1541_cell_size[speed_zone]); UINT8 track_offset[4]; UINT8 speed_offset[4]; UINT8 track_length[2]; place_integer_le(track_offset, 0, 4, dpos); place_integer_le(speed_offset, 0, 4, speed_zone); place_integer_le(track_length, 0, 2, track_size/8); io_generic_write(io, track_offset, tpos, 4); io_generic_write(io, speed_offset, spos, 4); io_generic_write_filler(io, 0xff, dpos, TRACK_LENGTH); io_generic_write(io, track_length, dpos, 2); io_generic_write(io, trackbuf, dpos + 2, track_size); tracks_written++; global_free(trackbuf); } return true; }
bool g64_format::load(io_generic *io, UINT32 form_factor, floppy_image *image) { UINT64 size = io_generic_size(io); dynamic_buffer img(size); io_generic_read(io, &img[0], 0, size); if (img[POS_VERSION]) { throw emu_fatalerror("g64_format: Unsupported version %u", img[POS_VERSION]); } int track_count = img[POS_TRACK_COUNT]; int head = 0; for (int track = 0; track < track_count; track++) { int cylinder = track % TRACK_COUNT; if (track == TRACK_COUNT) head = 1; UINT32 tpos = POS_TRACK_OFFSET + (track * 4); UINT32 spos = tpos + (track_count * 4); UINT32 dpos = pick_integer_le(&img[0], tpos, 4); if (!dpos) continue; if (dpos > size) throw emu_fatalerror("g64_format: Track %u offset %06x out of bounds", track, dpos); UINT32 speed_zone = pick_integer_le(&img[0], spos, 4); if (speed_zone > 3) throw emu_fatalerror("g64_format: Unsupported variable speed zones on track %d", track); UINT16 track_bytes = pick_integer_le(&img[0], dpos, 2); int track_size = track_bytes * 8; LOG_FORMATS("head %u track %u offs %u size %u cell %ld\n", head, cylinder, dpos, track_bytes, 200000000L/track_size); generate_track_from_bitstream(cylinder, head, &img[dpos+2], track_size, image); } if (!head) image->set_variant(floppy_image::SSSD); else image->set_variant(floppy_image::DSSD); return true; }
static int x1_handle_tap(INT16* buffer, const UINT8* casdata) { int sample_count = 0; int data_pos = 0x28; if(memcmp(casdata, "TAPE",4)) // header check { LOG_FORMATS("TAP: image is not a 'new' format TAP image\n"); return -1; } if(samplerate != 8000) { LOG_FORMATS("TAP: images that are not 8000Hz are not yet supported\n"); return -1; } while(sample_count < cas_size) { sample_count += x1_fill_wave(buffer,casdata[data_pos],sample_count); data_pos++; } return sample_count; }
static int tap_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) { int16_t *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 */ LOG_FORMATS("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 x1_handle_tap(INT16* buffer, const UINT8* casdata) { int sample_count = 0; int data_pos = new_format ? 0x28 : 0x04; if (samplerate != 8000) { LOG_FORMATS("TAP: images that are not 8000Hz are not yet supported\n"); return -1; } while (sample_count < cas_size) { sample_count += x1_fill_wave(buffer, casdata[data_pos], sample_count); data_pos++; } return sample_count; }
bool g64_format::load(io_generic *io, UINT32 form_factor, floppy_image *image) { UINT64 size = io_generic_size(io); UINT8 *img = global_alloc_array(UINT8, size); io_generic_read(io, img, 0, size); if (img[VERSION]) { throw emu_fatalerror("g64_format: Unsupported version %u", img[VERSION]); } int track_count = img[TRACK_COUNT]; int head = 0; for (int track = 0; track < track_count; track++) { offs_t track_offset = pick_integer_le(img, TRACK_OFFSET + (track * 4), 4); if (!track_offset) continue; if (track_offset > size) throw emu_fatalerror("g64_format: Track %u offset %06x out of bounds", track, track_offset); offs_t speed_zone = pick_integer_le(img, SPEED_ZONE + (track * 4), 4); if (speed_zone > 3) throw emu_fatalerror("g64_format: Unsupported variable speed zones on track %d", track); UINT16 track_bytes = pick_integer_le(img, track_offset, 2); int track_size = track_bytes * 8; LOG_FORMATS("track %u size %u cell %ld\n", track, track_size, 200000000L/track_size); generate_track_from_bitstream(track, head, &img[track_offset+2], track_size, image); } global_free(img); image->set_variant(floppy_image::SSSD); return true; }
/* length is length of sample buffer to fill! */ static int oric_cassette_fill_wave(INT16 *buffer, int length, UINT8 *bytes) { unsigned char header[9]; UINT8 *data_ptr; INT16 *p; int i; UINT8 data; p = buffer; /* header and trailer act as pauses */ /* the trailer is required so that the via sees the last bit of the last byte */ if (bytes == CODE_HEADER) { for (i = 0; i < ORIC_WAVESAMPLES_HEADER; i++) *(p++) = WAVEENTRY_NULL; } else if (bytes == CODE_TRAILER) { for (i = 0; i < ORIC_WAVESAMPLES_TRAILER; i++) *(p++) = WAVEENTRY_NULL; } else { /* the length is the number of samples left in the buffer and NOT the number of bytes for the input file */ length = length - ORIC_WAVESAMPLES_TRAILER; oric.cassette_state = ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE; data_ptr = bytes; while ((data_ptr<(bytes + oric.tap_size)) && (p < (buffer+length)) ) { data = data_ptr[0]; data_ptr++; switch (oric.cassette_state) { case ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE: { if (data==ORIC_SYNC_BYTE) { LOG_FORMATS("found sync byte!\n"); /* found first sync byte */ oric.cassette_state = ORIC_CASSETTE_GOT_SYNC_BYTE; } } break; case ORIC_CASSETTE_GOT_SYNC_BYTE: { if (data!=ORIC_SYNC_BYTE) { /* 0.25 second pause */ p = oric_fill_pause(p, oric_seconds_to_samples(0.25)); LOG_FORMATS("found end of sync bytes!\n"); /* found end of sync bytes */ for (i=0; i<ORIC_LEADER_LENGTH; i++) { p = oric_output_byte(p,0x016); } if (data==0x024) { //LOG_FORMATS("reading header!\n"); p = oric_output_byte(p,data); oric.cassette_state = ORIC_CASSETTE_READ_HEADER; oric.data_count = 0; oric.data_length = 9; } } } break; case ORIC_CASSETTE_READ_HEADER: { header[oric.data_count] = data; p = oric_output_byte(p, data); oric.data_count++; if (oric.data_count==oric.data_length) { //LOG_FORMATS("finished reading header!\n"); oric.cassette_state = ORIC_CASSETTE_READ_FILENAME; } } break; case ORIC_CASSETTE_READ_FILENAME: { p = oric_output_byte(p, data); /* got end of filename? */ if (data==0) { UINT16 end, start; LOG_FORMATS("got end of filename\n"); /* oric includes a small delay, but I don't see it being 1 bits */ for (i=0; i<100; i++) { p = oric_output_bit(p,1); } oric.cassette_state = ORIC_CASSETTE_WRITE_DATA; oric.data_count = 0; end = (((header[4] & 0x0ff)<<8) | (header[5] & 0x0ff)); start = (((header[6] & 0x0ff)<<8) | (header[7] & 0x0ff)); LOG(("start (from header): %02x\n",start)); LOG(("end (from header): %02x\n",end)); oric.data_length = end - start + 1; } } break; case ORIC_CASSETTE_WRITE_DATA: { p = oric_output_byte(p, data); oric.data_count++; if (oric.data_count==oric.data_length) { LOG_FORMATS("finished writing data!\n"); oric.cassette_state = ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE; } } break; } } } return p - buffer; }
static void read_g64_header(floppy_image_legacy *floppy, struct g64dsk_tag *tag, int head, UINT64 &pos) { UINT8 header[HEADER_LENGTH]; UINT64 start_pos = pos; // read header floppy_image_read(floppy, header, pos, HEADER_LENGTH); // get version tag->version = header[8]; if (!head) LOG_FORMATS("G64 version: %u\n", tag->version); // get number of tracks tag->tracks = header[9]; LOG_FORMATS("G64 side %u tracks: %u\n", head, tag->tracks); // get track size UINT16 track_size = (header[0xb] << 8) | header[0xa]; // get data offsets pos = 0xc; for (int i = 0; i < tag->tracks; i++) { tag->track_offset[head][i] = pick_integer_le(header, pos, 4); if (tag->track_offset[head][i]) { tag->track_offset[head][i] += start_pos; } pos += 4; } // get speed zone information UINT32 track_offs = 0; for (int i = 0; i < tag->tracks; i++) { tag->speed_zone_offset[head][i] = pick_integer_le(header, pos, 4); if (tag->speed_zone_offset[head][i] >= 4) { tag->speed_zone_offset[head][i] += start_pos; } pos += 4; tag->track_size[head][i] = g64_get_track_size(floppy, head, i); if (tag->track_offset[head][i] != 0) { LOG_FORMATS("G64 side %u track %.1f offset %05x length %04x ", head, get_dos_track(i), tag->track_offset[head][i], tag->track_size[head][i]); track_offs = tag->track_offset[head][i]; if (tag->speed_zone_offset[head][i] < 4) { LOG_FORMATS("speed %u\n", tag->speed_zone_offset[head][i]); } else { LOG_FORMATS("speed offset %04x\n", tag->speed_zone_offset[head][i]); } } } pos = track_offs + 2 + track_size; }
static int csw_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) { uint32_t SampleRate; uint32_t NumberOfPulses; uint8_t CompressionType; uint8_t Flags; uint8_t HeaderExtensionLength; int8_t Bit; std::vector<uint8_t> gz_ptr; int total_size; z_stream d_stream; int err; uint8_t *in_ptr; int bsize=0; int i; LOG_FORMATS("Length %d\n",length); SampleRate=get_leuint32(bytes+0x19); LOG_FORMATS("Sample rate %u\n",SampleRate); NumberOfPulses=get_leuint32(bytes+0x1d); LOG_FORMATS("Number Of Pulses %u\n",NumberOfPulses); CompressionType=bytes[0x21]; Flags=bytes[0x22]; HeaderExtensionLength=bytes[0x23]; if ((Flags&0)==0) { Bit=-100; } else { Bit=100; } LOG_FORMATS("CompressionType %u Flast %u HeaderExtensionLength %u\n",CompressionType,Flags,HeaderExtensionLength); //from here on down for now I am assuming it is compressed csw file. in_ptr = (uint8_t*) bytes+0x34+HeaderExtensionLength; gz_ptr.resize( 8 ); d_stream.next_in = (unsigned char *)in_ptr; d_stream.avail_in = mycaslen - ( in_ptr - bytes ); d_stream.total_in=0; d_stream.next_out = &gz_ptr[0]; d_stream.avail_out = 1; d_stream.total_out=0; d_stream.zalloc = nullptr; d_stream.zfree = nullptr; d_stream.opaque = nullptr; d_stream.data_type=0; err = inflateInit( &d_stream ); if ( err != Z_OK ) { LOG_FORMATS( "inflateInit2 error: %d\n", err ); goto cleanup; } total_size=0; do { d_stream.next_out = &gz_ptr[0]; d_stream.avail_out=1; err=inflate( &d_stream, Z_SYNC_FLUSH ); if (err==Z_OK) { bsize=gz_ptr[0]; if (bsize==0) { d_stream.avail_out=4; d_stream.next_out = &gz_ptr[0]; err=inflate( &d_stream, Z_SYNC_FLUSH ); bsize=get_leuint32(&gz_ptr[0]); } for (i=0;i<bsize;i++) { buffer[total_size++]=Bit; } Bit=-Bit; } } while (err==Z_OK); if ( err != Z_STREAM_END ) { LOG_FORMATS( "inflate error: %d\n", err ); goto cleanup; } err = inflateEnd( &d_stream ); if ( err != Z_OK ) { LOG_FORMATS( "inflateEnd error: %d\n", err ); goto cleanup; } return length; cleanup: return -1; }
static int csw_cas_to_wav_size( const uint8_t *casdata, int caslen ) { uint32_t SampleRate; uint32_t NumberOfPulses; uint8_t MajorRevision; uint8_t MinorRevision; uint8_t CompressionType; uint8_t Flags; uint8_t HeaderExtensionLength; std::vector<uint8_t> gz_ptr; int total_size; z_stream d_stream; int err; uint8_t *in_ptr; int bsize=0; if ( memcmp( casdata, CSW_HEADER, sizeof(CSW_HEADER)-1 ) ) { LOG_FORMATS( "csw_cas_to_wav_size: cassette image has incompatible header\n" ); goto cleanup; } if (casdata[0x16]!=0x1a) { LOG_FORMATS( "csw_cas_to_wav_size: Terminator Code Not Found\n" ); goto cleanup; } MajorRevision=casdata[0x17]; MinorRevision=casdata[0x18]; LOG_FORMATS("Version %d : %d\n",MajorRevision,MinorRevision); if (casdata[0x17]!=2) { LOG_FORMATS( "csw_cas_to_wav_size: Unsuported Major Version\n" ); goto cleanup; } SampleRate=get_leuint32(casdata+0x19); LOG_FORMATS("Sample rate %u\n",SampleRate); NumberOfPulses=get_leuint32(casdata+0x1d); LOG_FORMATS("Number Of Pulses %u\n",NumberOfPulses); CompressionType=casdata[0x21]; Flags=casdata[0x22]; HeaderExtensionLength=casdata[0x23]; LOG_FORMATS("CompressionType %u Flast %u HeaderExtensionLength %u\n",CompressionType,Flags,HeaderExtensionLength); mycaslen=caslen; //from here on down for now I am assuming it is compressed csw file. in_ptr = (uint8_t*) casdata+0x34+HeaderExtensionLength; gz_ptr.resize( 8 ); d_stream.next_in = (unsigned char *)in_ptr; d_stream.avail_in = caslen - ( in_ptr - casdata ); d_stream.total_in=0; d_stream.next_out = &gz_ptr[0]; d_stream.avail_out = 1; d_stream.total_out=0; d_stream.zalloc = nullptr; d_stream.zfree = nullptr; d_stream.opaque = nullptr; d_stream.data_type=0; err = inflateInit( &d_stream ); if ( err != Z_OK ) { LOG_FORMATS( "inflateInit2 error: %d\n", err ); goto cleanup; } total_size=1; do { d_stream.next_out = &gz_ptr[0]; d_stream.avail_out=1; err=inflate( &d_stream, Z_SYNC_FLUSH ); if (err==Z_OK) { bsize=gz_ptr[0]; if (bsize==0) { d_stream.avail_out=4; d_stream.next_out = &gz_ptr[0]; err=inflate( &d_stream, Z_SYNC_FLUSH ); bsize=get_leuint32(&gz_ptr[0]); } total_size=total_size+bsize; } } while (err==Z_OK); if ( err != Z_STREAM_END ) { LOG_FORMATS( "inflate error: %d\n", err ); goto cleanup; } err = inflateEnd( &d_stream ); if ( err != Z_OK ) { LOG_FORMATS( "inflateEnd error: %d\n", err ); goto cleanup; } return total_size; cleanup: return -1; }
bool fsd_format::load(io_generic *io, UINT32 form_factor, floppy_image *image) { UINT64 size = io_generic_size(io); dynamic_buffer img(size); io_generic_read(io, &img[0], 0, size); UINT64 pos; std::string title; for(pos=8; pos < size && img[pos] != '\0'; pos++) title += char(img[pos]); pos++; if(pos >= size) return false; //popmessage("Loading image of '%s'\n", title); desc_pc_sector sects[256]; UINT8 total_tracks = img[pos++]; UINT8 tnum, hnum, snum, ssize, error; hnum = 0; LOG_FORMATS("%02d Tracks\n", total_tracks+1); LOG_FORMATS("Tr.# No.S Sec.# Tr.ID Head# SecID IDsiz REsiz Error\n"); for(int curr_track=0; curr_track <= total_tracks; curr_track++) { UINT8 track = img[pos++]; UINT8 spt = img[pos++]; LOG_FORMATS("%02X %02X\n", track, spt); if (spt > 0) // formatted { UINT8 readable = img[pos++]; for (int i = 0; i < spt; i++) { tnum = img[pos++]; // logical track hnum = img[pos++]; // head number snum = img[pos++]; // logical sector ssize = img[pos++]; // reported size sects[i].track = tnum; sects[i].head = hnum; sects[i].sector = snum; sects[i].size = ssize; if (readable == 0xff) { sects[i].actual_size = 1 << (img[pos++] + 7); error = img[pos++]; sects[i].deleted = (error & 0x20) == 0x20; sects[i].bad_crc = (error & 0x0e) == 0x0e; sects[i].data = &img[pos]; pos += sects[i].actual_size; LOG_FORMATS("Read %02X %02X %02X %02X %02X %02X %02X\n", i, sects[i].track, sects[i].head, sects[i].sector, sects[i].size, sects[i].actual_size, error); } else { LOG_FORMATS("Unreadable sector on track %02d sector %02X head %02d", track, i, hnum); sects[i].actual_size = 0; sects[i].deleted = false; sects[i].bad_crc = false; sects[i].data = nullptr; LOG_FORMATS("Unread %02X %02X %02X %02X %02X %02X %02X\n", i, sects[i].track, sects[i].head, sects[i].sector, sects[i].size, sects[i].actual_size, 0); } } } else // unformatted { sects[0].track = curr_track; sects[0].head = hnum; sects[0].sector = 0; sects[0].size = 0; LOG_FORMATS("Unform %02X %02X %02X %02X %02X %02X %02X\n", 0, sects[0].track, sects[0].head, sects[0].sector, sects[0].size, sects[0].actual_size, 0); } build_wd_track_fm(curr_track, hnum, image, 50000, spt, sects, 10, 40, 10); } return true; }
/* length is length of .tap file! */ static int oric_cassette_calculate_size_in_samples(const UINT8 *bytes, int length) { unsigned char header[9]; int count; const UINT8 *data_ptr; int i; UINT8 data; oric.tap_size = length; oric.cassette_state = ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE; count = 0; data_ptr = bytes; while (data_ptr<(bytes+length)) { data = data_ptr[0]; data_ptr++; switch (oric.cassette_state) { case ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE: { if (data==ORIC_SYNC_BYTE) { LOG_FORMATS("found sync byte!\n"); /* found first sync byte */ oric.cassette_state = ORIC_CASSETTE_GOT_SYNC_BYTE; } } break; case ORIC_CASSETTE_GOT_SYNC_BYTE: { if (data!=ORIC_SYNC_BYTE) { /* 0.25 second pause */ count += oric_seconds_to_samples(0.25); LOG_FORMATS("found end of sync bytes!\n"); /* oric writes approx 512 bytes */ /* found end of sync bytes */ for (i=0; i<ORIC_LEADER_LENGTH; i++) { count+=oric_calculate_byte_size_in_samples(0x016); } if (data==0x024) { //LOG_FORMATS("reading header!\n"); count+=oric_calculate_byte_size_in_samples(0x024); oric.cassette_state = ORIC_CASSETTE_READ_HEADER; oric.data_count = 0; oric.data_length = 9; } } } break; case ORIC_CASSETTE_READ_HEADER: { header[oric.data_count] = data; count+=oric_calculate_byte_size_in_samples(data); oric.data_count++; if (oric.data_count==oric.data_length) { //LOG_FORMATS("finished reading header!\n"); oric.cassette_state = ORIC_CASSETTE_READ_FILENAME; } } break; case ORIC_CASSETTE_READ_FILENAME: { count+=oric_calculate_byte_size_in_samples(data); /* got end of filename? */ if (data==0) { UINT16 end, start; LOG_FORMATS("got end of filename\n"); /* 100 1 bits to separate header from data */ for (i=0; i<100; i++) { count+=oric_get_bit_size_in_samples(1); } oric.cassette_state = ORIC_CASSETTE_WRITE_DATA; oric.data_count = 0; end = (((header[4] & 0x0ff)<<8) | (header[5] & 0x0ff)); start = (((header[6] & 0x0ff)<<8) | (header[7] & 0x0ff)); LOG(("start (from header): %02x\n",start)); LOG(("end (from header): %02x\n",end)); oric.data_length = end - start + 1; } } break; case ORIC_CASSETTE_WRITE_DATA: { count+=oric_calculate_byte_size_in_samples(data); oric.data_count++; if (oric.data_count==oric.data_length) { LOG_FORMATS("finished writing data!\n"); oric.cassette_state = ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE; } } break; } } return count; }
/* 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; }
static void ascii_block_common_log( const char *block_type_string, uint8_t block_type ) { LOG_FORMATS("%s (type %02x) encountered:\n", block_type_string, block_type); }
static int cbm_tap_do_work( INT16 **buffer, int length, const UINT8 *data ) { int i, j = 0; int size = 0; int version = data[0x0c]; int system = data[0x0d]; int video_standard = data[0x0e]; int tap_frequency = 0; int byte_samples = 0; UINT8 over_pulse_bytes[3] = {0 , 0, 0 }; int over_pulse_length = 0; /* These waveamp_* values are currently stored but not used. Further investigations are needed to find real pulse amplitude in Commodore tapes. Implementation here would follow */ /* int waveamp_high, waveamp_low; */ /* Log .TAP info but only once */ if (!(buffer == NULL)) { LOG_FORMATS("TAP version : %d\n", version); LOG_FORMATS("Machine type : %d\n", system); LOG_FORMATS("Video standard : %d\n", video_standard); LOG_FORMATS("Tape frequency : %d\n", (tap_frequency) << 3); } /* is this a supported version? */ if ((version < 0) || (version > 2)) { LOG_FORMATS("Unsupported .tap version: %d \n", version); return -1; } /* is the .tap file corrupted? */ if ((data == NULL) || (length <= CBM_HEADER_SIZE)) return -1; /* read the frequency from the .tap header */ switch (system) { case VIC20: tap_frequency = (video_standard == NTSC) ? VIC20_NTSC : VIC20_PAL; break; case C16: tap_frequency = (video_standard == NTSC) ? C16_NTSC : C16_PAL; break; case C64: default: tap_frequency = (video_standard == NTSC) ? C64_NTSC : C64_PAL; break; } for (i = CBM_HEADER_SIZE; i < length; i++) { UINT8 byte = data[i]; /* .TAP v0 */ /* Here is simple: if byte is != 0 -> length = byte otherwise -> length = 0x100 (i.e. 0xff + 1) */ if (!version) { if (byte != 0x00) { byte_samples = tap_data_to_samplecount(byte, tap_frequency); /* waveamp_high = WAVE_HIGH; */ } else { byte_samples = tap_data_to_samplecount(PAUSE, tap_frequency); // tap2wav value // byte_samples = tap_data_to_samplecount(0x100, tap_frequency); // vice value /* waveamp_high = WAVE_PAUSE; */ } /* waveamp_low = WAVE_LOW; */ } /* .TAP v1 & v2 */ /* Here is a bit more complicate: if byte is != 0 -> length = byte otherwise -> the length of the pulse is stored as a 24bit value in the 3 bytes after the 0. See below for comments on the implementation of this mechanism */ if (version) { if ((byte != 0x00) && !j) { byte_samples = tap_data_to_samplecount(byte, tap_frequency); /* waveamp_high = WAVE_HIGH; */ } else { /* If we have a long pulse close to the end of the .TAP, check that bytes still to be read are enough to complete it. */ if (length - i + j >= 4) { /* Here we read the 3 following bytes, using an index j j = 0 -> The 0x00 byte: we simply skip everything and go on j = 1,2 -> The 1st and 2nd bytes after 0x00: we store them and go on j = 3 -> The final byte of the pulse length: we store it, and then we pass to finally output the wave */ if (j > 0) { over_pulse_bytes[j-1] = byte; j += 1; if (j >= 4) { over_pulse_length = ((over_pulse_bytes[2] << 16) | (over_pulse_bytes[1] << 8) | over_pulse_bytes[0]) >> 3; byte_samples = tap_data_to_samplecount(over_pulse_length, tap_frequency); /* waveamp_high = WAVE_PAUSE; */ j = 0; } } else { j += 1; LOG_FORMATS("Found a 00 byte close to the end of the .tap file.\n"); LOG_FORMATS("This is not allowed by the format specs. \n"); LOG_FORMATS("Check if your .tap file got corrupted when you created it!\n"); } } else j = 1;
static int csw_cas_fill_wave( INT16 *buffer, int length, UINT8 *bytes ) { UINT32 SampleRate; UINT32 NumberOfPulses; UINT8 CompressionType; UINT8 Flags; UINT8 HeaderExtensionLength; INT8 Bit; UINT8 *gz_ptr = NULL; int total_size; z_stream d_stream; int err; UINT8 *in_ptr; int bsize=0; int i; LOG_FORMATS("Length %d\n",length); SampleRate=get_leuint32(bytes+0x19); LOG_FORMATS("Sample rate %d\n",SampleRate); NumberOfPulses=get_leuint32(bytes+0x1d); LOG_FORMATS("Number Of Pulses %d\n",NumberOfPulses); CompressionType=bytes[0x21]; Flags=bytes[0x22]; HeaderExtensionLength=bytes[0x23]; if ((Flags&0)==0) { Bit=-100; } else { Bit=100; } LOG_FORMATS("CompressionType %d Flast %d HeaderExtensionLength %d\n",CompressionType,Flags,HeaderExtensionLength); //from here on down for now I am assuming it is compressed csw file. in_ptr = (UINT8*) bytes+0x34+HeaderExtensionLength; gz_ptr = (UINT8*)malloc( 8 ); d_stream.next_in = (unsigned char *)in_ptr; d_stream.avail_in = mycaslen - ( in_ptr - bytes ); d_stream.total_in=0; d_stream.next_out = gz_ptr; d_stream.avail_out = 1; d_stream.total_out=0; d_stream.zalloc = 0; d_stream.zfree = 0; d_stream.opaque = 0; d_stream.data_type=0; err = inflateInit( &d_stream ); if ( err != Z_OK ) { LOG_FORMATS( "inflateInit2 error: %d\n", err ); goto cleanup; } total_size=0; do { d_stream.next_out = gz_ptr; d_stream.avail_out=1; err=inflate( &d_stream, Z_SYNC_FLUSH ); if (err==Z_OK) { bsize=gz_ptr[0]; if (bsize==0) { d_stream.avail_out=4; d_stream.next_out = gz_ptr; err=inflate( &d_stream, Z_SYNC_FLUSH ); bsize=get_leuint32(gz_ptr); } for (i=0;i<bsize;i++) { buffer[total_size++]=Bit; } Bit=-Bit; } } while (err==Z_OK); if ( err != Z_STREAM_END ) { LOG_FORMATS( "inflate error: %d\n", err ); goto cleanup; } err = inflateEnd( &d_stream ); if ( err != Z_OK ) { LOG_FORMATS( "inflateEnd error: %d\n", err ); goto cleanup; } if ( gz_ptr ) { free( gz_ptr ); gz_ptr = NULL; } return length; cleanup: if ( gz_ptr ) { free( gz_ptr ); gz_ptr = NULL; } return -1; }