diff options
Diffstat (limited to 'src/lib/formats/csw_cas.cpp')
-rw-r--r-- | src/lib/formats/csw_cas.cpp | 420 |
1 files changed, 172 insertions, 248 deletions
diff --git a/src/lib/formats/csw_cas.cpp b/src/lib/formats/csw_cas.cpp index 9edea5c3ff7..3db3eb70c34 100644 --- a/src/lib/formats/csw_cas.cpp +++ b/src/lib/formats/csw_cas.cpp @@ -1,294 +1,218 @@ // license:BSD-3-Clause -// copyright-holders:Gordon Jefferyes +// copyright-holders:Nigel Barnes /* -CSW format ----------- -Header Description - -Offset Value Type Description -0x00 (note) ASCII 22 bytes "Compressed Square Wave" signature -0x16 0x1A BYTE Terminator code -0x17 0x02 BYTE CSW major revision number -0x18 0x00 BYTE CSW minor revision number -0x19 DWORD Sample rate -0x1D DWORD Total number of pulses (after decompression) -0x21 BYTE Compression type 0x01: RLE 0x02: Z-RLE -0x22 BYTE Flags b0: initial polarity: if set, the signal starts at logical high -0x23 HDR BYTE Header extension length in bytes (0x00) -0x24 ASCII 16 bytes free use -0x34 BYTE Start of Header is HDR>0 - - - + CSW format + ---------- + Header Description + + Offset Value Type Description + 0x00 (note) ASCII 22 bytes "Compressed Square Wave" signature + 0x16 0x1A BYTE Terminator code + 0x17 0x02 BYTE CSW major revision number + 0x18 0x00 BYTE CSW minor revision number + 0x19 DWORD Sample rate + 0x1D DWORD Total number of pulses (after decompression) + 0x21 BYTE Compression type 0x01: RLE 0x02: Z-RLE + 0x22 BYTE Flags b0: initial polarity; if set, the signal starts at logical high + 0x23 HDR BYTE Header extension length in bytes (0x00) + 0x24 ASCII Encoding application description + 0x34 BYTE Header extension data (if present HDR>0) + 0x34+HDR CSW data */ -#include <cstring> +#include "csw_cas.h" +#include "uef_cas.h" #include <zlib.h> -#include <cassert> -#include "uef_cas.h" -#include "csw_cas.h" +#include <cassert> +#include <cstring> -#define CSW_WAV_FREQUENCY 44100 static const uint8_t CSW_HEADER[] = { "Compressed Square Wave" }; -static uint32_t get_leuint32(const void *ptr) -{ - uint32_t value; - memcpy(&value, ptr, sizeof(value)); - return little_endianize_int32(value); -} -static int mycaslen; +/*------------------------------------------------- + csw_cassette_identify - identify cassette +-------------------------------------------------*/ -static int csw_cas_to_wav_size( const uint8_t *casdata, int caslen ) +static cassette_image::error csw_cassette_identify(cassette_image *cassette, cassette_image::Options *opts) { - uint8_t MajorRevision; - uint8_t MinorRevision; - 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; - } + uint8_t header[0x34]; - if (casdata[0x16]!=0x1a) - { - LOG_FORMATS( "csw_cas_to_wav_size: Terminator Code Not Found\n" ); - goto cleanup; + cassette->image_read(header, 0, sizeof(header)); + if (memcmp(&header[0], CSW_HEADER, sizeof(CSW_HEADER) - 1)) { + return cassette_image::error::INVALID_IMAGE; } - MajorRevision=casdata[0x17]; - MinorRevision=casdata[0x18]; + opts->bits_per_sample = 8; + opts->channels = 1; + opts->sample_frequency = little_endianize_int16(*(uint32_t*)(header + 0x19)); + return cassette_image::error::SUCCESS; +} + - LOG_FORMATS("Version %d : %d\n",MajorRevision,MinorRevision); +/*------------------------------------------------- + csw_cassette_load - load cassette +-------------------------------------------------*/ - if (casdata[0x17]!=2) +static cassette_image::error csw_cassette_load(cassette_image *cassette) +{ + uint8_t header[0x34]; + uint64_t image_size = cassette->image_size(); + std::vector<uint8_t> image_data(image_size); + + int8_t bit; + uint8_t compression; + int bsize = 0; + size_t csw_data = 0; + size_t sample_count = 0; + uint32_t sample_rate; + std::vector<int8_t> samples; + + /* csw header */ + cassette->image_read(header, 0, sizeof(header)); + + if (header[0x16] != 0x1a) { - LOG_FORMATS( "csw_cas_to_wav_size: Unsuported Major Version\n" ); - goto cleanup; + LOG_FORMATS("csw_cassette_load: Terminator Code Not Found\n"); + return cassette_image::error::INVALID_IMAGE; } - HeaderExtensionLength=casdata[0x23]; - - 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("CSW Version %d.%d\n", header[0x17], header[0x18]); + switch (header[0x17]) { - LOG_FORMATS( "inflateInit2 error: %d\n", err ); - goto cleanup; + case 1: + sample_rate = little_endianize_int16(*(uint32_t*)(header + 0x19)); + compression = header[0x1b]; + bit = (header[0x1c] & 1) ? 127 : -128; + csw_data = 0x20; + + LOG_FORMATS("Sample Rate: %u\n", sample_rate); + LOG_FORMATS("CompressionType: %u Flags: %u\n", header[0x1b], header[0x1c]); + break; + + case 2: + sample_rate = little_endianize_int32(*(uint32_t*)(header + 0x19)); + compression = header[0x21]; + bit = (header[0x22] & 1) ? 127 : -128; + csw_data = (size_t) header[0x23] + 0x34; + + LOG_FORMATS("Sample Rate: %u\n", sample_rate); + LOG_FORMATS("Number of Pulses: %u\n", little_endianize_int32(*(uint32_t *)(header + 0x1d))); + LOG_FORMATS("CompressionType: %u Flags: %u\n", header[0x21], header[0x22]); + LOG_FORMATS("Encoder: "); + for (int i = 0; i < 16; i++) + LOG_FORMATS("%c", header[0x24 + i]); + LOG_FORMATS("\n"); + break; + + default: + LOG_FORMATS("Unsupported Major Version\n"); + return cassette_image::error::INVALID_IMAGE; } - - - total_size=1; - do + + /* csw data */ + switch (compression) { - d_stream.next_out = &gz_ptr[0]; - d_stream.avail_out=1; - err=inflate( &d_stream, Z_SYNC_FLUSH ); - if (err==Z_OK) + case 0x01: + /* RLE (Run Length Encoding) */ + for (size_t pos = csw_data; pos < image_size; pos++) { - bsize=gz_ptr[0]; - if (bsize==0) + bsize = image_data[pos]; + if (bsize == 0) + { + bsize = little_endianize_int32(*(uint32_t *)(&image_data[pos + 1])); + pos += 4; + } + for (int i = 0; i < bsize; i++) { - 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]); + samples.resize(sample_count + 1); + samples[sample_count++] = bit; } - total_size=total_size+bsize; + bit ^= 0xff; } - } - 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; -} - -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]; - - Bit = (Flags & 1) ? 100 : -100; - - LOG_FORMATS("CompressionType %u Flags %u HeaderExtensionLength %u\n",CompressionType,Flags,HeaderExtensionLength); - - LOG_FORMATS("Encoder: "); - for (i = 0; i < 16; i++) - LOG_FORMATS("%c", bytes[0x24 + i]); - LOG_FORMATS("\n"); - - LOG_FORMATS("Header: "); - for (i = 0; i < HeaderExtensionLength; i++) - LOG_FORMATS("%c", bytes[0x34 + i]); - LOG_FORMATS("\n"); - - //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; + break; + + case 0x02: + /* Z-RLE (CSW v2.xx only) */ + cassette->image_read(&image_data[0], 0, image_size); + + std::vector<uint8_t> gz_ptr; + z_stream d_stream; + int err; + + gz_ptr.resize(8); + + d_stream.next_in = (unsigned char *) &image_data[csw_data]; + d_stream.avail_in = image_size - 0x34 - header[0x23]; + d_stream.total_in = 0; + + d_stream.next_out = &gz_ptr[0]; + d_stream.avail_out = 1; + d_stream.total_out = 0; - err = inflateInit( &d_stream ); - if ( err != Z_OK ) - { - LOG_FORMATS( "inflateInit2 error: %d\n", err ); - goto cleanup; - } + d_stream.zalloc = nullptr; + d_stream.zfree = nullptr; + d_stream.opaque = nullptr; + d_stream.data_type = 0; - total_size=0; + err = inflateInit(&d_stream); + if (err != Z_OK) + { + LOG_FORMATS("inflateInit error: %d\n", err); + return cassette_image::error::INVALID_IMAGE; + } - do - { - d_stream.next_out = &gz_ptr[0]; - d_stream.avail_out=1; - err=inflate( &d_stream, Z_SYNC_FLUSH ); - if (err==Z_OK) + do { - bsize=gz_ptr[0]; - if (bsize==0) + d_stream.next_out = &gz_ptr[0]; + d_stream.avail_out = 1; + err = inflate(&d_stream, Z_SYNC_FLUSH); + if (err == Z_OK) { - 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]); + 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 = little_endianize_int32(*(uint32_t *)(&gz_ptr[0])); + } + for (int i = 0; i < bsize; i++) + { + samples.resize(sample_count + 1); + samples[sample_count++] = bit; + } + bit ^= 0xff; } - for (i=0;i<bsize;i++) - { - buffer[total_size++]=Bit; - } - Bit=-Bit; - } - } - while (err==Z_OK); + } + while (err == Z_OK); - if ( err != Z_STREAM_END ) - { - LOG_FORMATS( "inflate error: %d\n", err ); - goto cleanup; - } + if (err != Z_STREAM_END) + { + LOG_FORMATS("inflate error: %d\n", err); + return cassette_image::error::INVALID_IMAGE; + } - err = inflateEnd( &d_stream ); - if ( err != Z_OK ) - { - LOG_FORMATS( "inflateEnd error: %d\n", err ); - goto cleanup; + err = inflateEnd(&d_stream); + if (err != Z_OK) + { + LOG_FORMATS("inflateEnd error: %d\n", err); + return cassette_image::error::INVALID_IMAGE; + } + break; } - return length; - -cleanup: - return -1; + return cassette->put_samples(0, 0.0, (double) sample_count / sample_rate, sample_count, 1, &samples[0], cassette_image::WAVEFORM_8BIT); } -static const struct CassetteLegacyWaveFiller csw_legacy_fill_wave = { - csw_cas_fill_wave, /* fill_wave */ - -1, /* chunk_size */ - 0, /* chunk_samples */ - csw_cas_to_wav_size, /* chunk_sample_calc */ - CSW_WAV_FREQUENCY, /* sample_frequency */ - 0, /* header_samples */ - 0 /* trailer_samples */ -}; - -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 cassette_image::error csw_cassette_load( cassette_image *cassette ) -{ - return cassette_legacy_construct( cassette, &csw_legacy_fill_wave ); -} +/*------------------------------------------------- + CassetteFormat csw_cassette_format +-------------------------------------------------*/ -const struct CassetteFormat csw_cassette_format = { +const cassette_image::Format csw_cassette_format = { "csw", csw_cassette_identify, csw_cassette_load, |