diff options
Diffstat (limited to 'src/lib/formats/tzx_cas.cpp')
-rw-r--r-- | src/lib/formats/tzx_cas.cpp | 247 |
1 files changed, 149 insertions, 98 deletions
diff --git a/src/lib/formats/tzx_cas.cpp b/src/lib/formats/tzx_cas.cpp index 28d363df433..f758c1b31e6 100644 --- a/src/lib/formats/tzx_cas.cpp +++ b/src/lib/formats/tzx_cas.cpp @@ -27,11 +27,13 @@ TODO: */ -#include <assert.h> - #include "tzx_cas.h" -#include "formats/imageutl.h" -#include "emu.h" +#include "imageutl.h" + +#include "multibyte.h" + +#include <cmath> + #define TZX_WAV_FREQUENCY 44100 #define WAVE_LOW -0x5a9e @@ -51,7 +53,7 @@ static const uint8_t TZX_HEADER[8] = { 'Z','X','T','a','p','e','!',0x1a }; Initialized by tzx_cas_get_wave_size, used (and cleaned up) by tzx_cas_fill_wave */ -static int16_t wave_data = 0; +static int16_t wave_data = 0; // FIXME: global variables prevent multiple instances static int block_count = 0; static uint8_t** blocks = nullptr; static float t_scale = 1; /* for scaling T-states to the 4MHz CPC */ @@ -70,7 +72,7 @@ static void toggle_wave_data(void) static void tzx_cas_get_blocks( const uint8_t *casdata, int caslen ) { - int pos = sizeof(TZX_HEADER) + 2; + uint32_t pos = sizeof(TZX_HEADER) + 2; int max_block_count = INITIAL_MAX_BLOCK_COUNT; int loopcount = 0, loopoffset = 0; blocks = (uint8_t**)malloc(max_block_count * sizeof(uint8_t*)); @@ -87,7 +89,7 @@ static void tzx_cas_get_blocks( const uint8_t *casdata, int caslen ) void *old_blocks = blocks; int old_max_block_count = max_block_count; max_block_count = max_block_count + BLOCK_COUNT_INCREMENTS; - blocks = (uint8_t**)malloc(max_block_count * sizeof(uint8_t*)); // SHOULD NOT BE USING auto_alloc_array() + blocks = (uint8_t**)malloc(max_block_count * sizeof(uint8_t*)); memset(blocks, 0, max_block_count); memcpy(blocks, old_blocks, old_max_block_count * sizeof(uint8_t*)); free(old_blocks); @@ -101,12 +103,12 @@ static void tzx_cas_get_blocks( const uint8_t *casdata, int caslen ) { case 0x10: pos += 2; - datasize = casdata[pos] + (casdata[pos + 1] << 8); + datasize = get_u16le(&casdata[pos]); pos += 2 + datasize; break; case 0x11: pos += 0x0f; - datasize = casdata[pos] + (casdata[pos + 1] << 8) + (casdata[pos + 2] << 16); + datasize = get_u24le(&casdata[pos]); pos += 3 + datasize; break; case 0x12: @@ -118,12 +120,12 @@ static void tzx_cas_get_blocks( const uint8_t *casdata, int caslen ) break; case 0x14: pos += 7; - datasize = casdata[pos] + (casdata[pos + 1] << 8) + (casdata[pos + 2] << 16); + datasize = get_u24le(&casdata[pos]); pos += 3 + datasize; break; case 0x15: pos += 5; - datasize = casdata[pos] + (casdata[pos + 1] << 8) + (casdata[pos + 2] << 16); + datasize = get_u24le(&casdata[pos]); pos += 3 + datasize; break; case 0x20: case 0x23: @@ -131,7 +133,7 @@ static void tzx_cas_get_blocks( const uint8_t *casdata, int caslen ) break; case 0x24: - loopcount = casdata[pos] + (casdata[pos + 1] << 8); + loopcount = get_u16le(&casdata[pos]); pos +=2; loopoffset = pos; break; @@ -152,11 +154,11 @@ static void tzx_cas_get_blocks( const uint8_t *casdata, int caslen ) break; case 0x26: - datasize = casdata[pos] + (casdata[pos + 1] << 8); + datasize = get_u16le(&casdata[pos]); pos += 2 + 2 * datasize; break; case 0x28: case 0x32: - datasize = casdata[pos] + (casdata[pos + 1] << 8); + datasize = get_u16le(&casdata[pos]); pos += 2 + datasize; break; case 0x31: @@ -173,24 +175,31 @@ static void tzx_cas_get_blocks( const uint8_t *casdata, int caslen ) break; case 0x35: pos += 0x10; - datasize = casdata[pos] + (casdata[pos + 1] << 8) + (casdata[pos + 2] << 16) + (casdata[pos + 3] << 24); + datasize = get_u32le(&casdata[pos]); pos += 4 + datasize; break; case 0x40: pos += 1; - datasize = casdata[pos] + (casdata[pos + 1] << 8) + (casdata[pos + 2] << 16); + datasize = get_u24le(&casdata[pos]); pos += 3 + datasize; break; case 0x5A: pos += 9; break; default: - datasize = casdata[pos] + (casdata[pos + 1] << 8) + (casdata[pos + 2] << 16) + (casdata[pos + 3] << 24); + datasize = get_u32le(&casdata[pos]); pos += 4 + datasize; break; } - block_count++; + if (pos > caslen) + { + LOG_FORMATS("Block %d(ID=%d) with wrong length discarded\n", block_count, blocktype); + } + else + { + block_count++; + } } } @@ -360,7 +369,7 @@ static inline int tzx_handle_symbol(int16_t **buffer, const uint8_t *symtable, u for (int i = 0; i < maxp; i++) { - uint16_t pulse_length = cursymb[1 + (i*2)] | (cursymb[2 + (i*2)] << 8); + uint16_t pulse_length = get_u16le(&cursymb[1 + (i*2)]); // shorter lists can be terminated with a pulse_length of 0 if (pulse_length != 0) @@ -415,7 +424,7 @@ static int tzx_handle_generalized(int16_t **buffer, const uint8_t *bytes, int pa for (int i = 0; i < totp*3; i+=3) { uint8_t symbol = table2[i + 0]; - uint16_t repetitions = table2[i + 1] + (table2[i + 2] << 8); + uint16_t repetitions = get_u16le(&table2[i + 1]); //printf("symbol %02x repetitions %04x\n", symbol, repetitions); // does 1 mean repeat once, or that it only occurs once? for (int j = 0; j < repetitions; j++) @@ -435,7 +444,7 @@ static int tzx_handle_generalized(int16_t **buffer, const uint8_t *bytes, int pa const uint8_t *symtable = bytes; const uint8_t *table2 = bytes + (2 * npd + 1)*asd; - int NB = ceil(compute_log2(asd)); // number of bits needed to represent each symbol + int NB = std::ceil(compute_log2(asd)); // number of bits needed to represent each symbol uint8_t stream_bit = 0; uint32_t stream_byte = 0; @@ -523,28 +532,28 @@ static int tzx_cas_do_work( int16_t **buffer ) 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); + pause_time = get_u16le(&cur_block[1]); + data_size = get_u16le(&cur_block[3]); pilot_length = (cur_block[5] < 128) ? 8063 : 3223; 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); + pilot = get_u16le(&cur_block[1]); + sync1 = get_u16le(&cur_block[3]); + sync2 = get_u16le(&cur_block[5]); + bit0 = get_u16le(&cur_block[7]); + bit1 = get_u16le(&cur_block[9]); + pilot_length = get_u16le(&cur_block[11]); 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); + pause_time = get_u16le(&cur_block[14]); + data_size = get_u24le(&cur_block[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); + pilot = get_u16le(&cur_block[1]); + pilot_length = get_u16le(&cur_block[3]); size += tzx_cas_handle_block(buffer, cur_block, 0, 0, pilot, pilot_length, 0, 0, 0, 0, 0); current_block++; break; @@ -557,20 +566,20 @@ static int tzx_cas_do_work( int16_t **buffer ) current_block++; break; case 0x14: /* Pure Data Block */ - bit0 = cur_block[1] + (cur_block[2] << 8); - bit1 = cur_block[3] + (cur_block[4] << 8); + bit0 = get_u16le(&cur_block[1]); + bit1 = get_u16le(&cur_block[3]); 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); + pause_time = get_u16le(&cur_block[6]); + data_size = get_u24le(&cur_block[8]); 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); + pause_time = get_u16le(&cur_block[1]); 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. */ + in MAME 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); @@ -606,7 +615,7 @@ static int tzx_cas_do_work( int16_t **buffer ) break; case 0x32: /* Archive Info */ ascii_block_common_log("Archive Info Block", block_type); - total_size = cur_block[1] + (cur_block[2] << 8); + total_size = get_u16le(&cur_block[1]); text_size = 0; for (data_size = 0; data_size < cur_block[3]; data_size++) // data_size = number of text blocks, in this case { @@ -644,7 +653,7 @@ static int tzx_cas_do_work( int16_t **buffer ) 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); + text_size = get_u32le(&cur_block[11]); for (data_size = 0; data_size < text_size; data_size++) LOG_FORMATS("%c", cur_block[15 + data_size]); LOG_FORMATS("\n"); @@ -656,7 +665,7 @@ static int tzx_cas_do_work( int16_t **buffer ) current_block++; break; case 0x24: /* Loop Start */ - loopcount = cur_block[1] + (cur_block[2] << 8); + loopcount = get_u16le(&cur_block[1]); current_block++; loopoffset = current_block; @@ -690,10 +699,10 @@ static int tzx_cas_do_work( int16_t **buffer ) 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); + tstates = get_u16le(&cur_block[1]); + pause_time = get_u16le(&cur_block[3]); bits_in_last_byte = cur_block[5]; - data_size = cur_block[6] + (cur_block[7] << 8) + (cur_block[8] << 16); + data_size = get_u24le(&cur_block[6]); size += tzx_handle_direct(buffer, &cur_block[9], pause_time, data_size, tstates, bits_in_last_byte); current_block++; break; @@ -708,15 +717,15 @@ static int tzx_cas_do_work( int16_t **buffer ) { // 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); + data_size = get_u32le(&cur_block[1]); + pause_time = get_u16le(&cur_block[5]); - uint32_t totp = cur_block[7] + (cur_block[8] << 8) + (cur_block[9] << 16) + (cur_block[10] << 24); + uint32_t totp = get_u32le(&cur_block[7]); 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); + uint32_t totd = get_u32le(&cur_block[13]); int npd = cur_block[17]; int asd = cur_block[18]; if (asd == 0 && totd > 0) asd = 256; @@ -777,7 +786,7 @@ cleanup: return -1; } -static int tzx_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) +static int tzx_cas_fill_wave( int16_t *buffer, int length, const uint8_t *bytes, int ) { int16_t *p = buffer; int size = 0; @@ -786,7 +795,7 @@ static int tzx_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) return size; } -static int cdt_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) +static int cdt_cas_fill_wave( int16_t *buffer, int length, const uint8_t *bytes, int ) { int16_t *p = buffer; int size = 0; @@ -795,92 +804,134 @@ static int cdt_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) return size; } -static int tap_cas_to_wav_size( const uint8_t *casdata, int caslen ) +static int tap_cas_to_wav_size(const uint8_t *casdata, int caslen) { + /* + TAP Header: + | 0 | W | Length of Data Block (N) | -+ + | 2 | B | Flag Byte | -+ + | 3 | B*(N-2) | Data | | Data Block + | N+1 | B | XOR Checksum of [2..N] | -+ + */ + int size = 0; const uint8_t *p = casdata; - while (p < casdata + caslen) + while (caslen > 2) { - int data_size = p[0] + (p[1] << 8); - int pilot_length = (p[2] == 0x00) ? 8063 : 3223; - LOG_FORMATS("tap_cas_to_wav_size: Handling TAP block containing 0x%X bytes", data_size); + int data_size = get_u16le(&p[0]); p += 2; + caslen -= 2; + if (caslen < data_size) + { + LOG_FORMATS("tap_cas_to_wav_size: Requested 0x%X bytes but only 0x%X available.\n", data_size, caslen); + data_size = caslen; + } + caslen -= data_size; + + // Data Block + LOG_FORMATS("tap_cas_to_wav_size: Handling TAP block containing 0x%X bytes", data_size); + const int pilot_length = (p[0] == 0x00) ? 8063 : 3223; 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); + LOG_FORMATS(", total size is now: %d", size); + + // Validate Checksum + const uint8_t checksum = p[data_size - 1]; + LOG_FORMATS(", checksum 0x%X\n", checksum); + uint8_t check = 0x00; + for(int i = 0; i < (data_size - 1); i++) + { + check ^= p[i]; + } + if (check != checksum) + { + osd_printf_warning("tap_cas_to_wav_size: wrong checksum 0x%X\n", check); + } + p += data_size; } return size; } -static int tap_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) +static int tap_cas_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int bytes_length) { int16_t *p = buffer; int size = 0; - while (size < length) + int block = 0; + while (bytes_length > 2) { - int data_size = bytes[0] + (bytes[1] << 8); - int pilot_length = (bytes[2] == 0x00) ? 8063 : 3223; - LOG_FORMATS("tap_cas_fill_wave: Handling TAP block containing 0x%X bytes\n", data_size); + int data_size = get_u16le(&bytes[0]); bytes += 2; + bytes_length -= 2; + + LOG_FORMATS("tap_cas_fill_wave: Handling TAP block containing 0x%X bytes\n", data_size); + if (bytes_length < data_size) + { + osd_printf_warning("BAD Image: block #%d required %d byte(s), but only %d available\n", block, data_size, bytes_length); + data_size = bytes_length; // Take as much as we can. + } + bytes_length -= data_size; + + int pilot_length = (bytes[0] == 0x00) ? 8063 : 3223; size += tzx_cas_handle_block(&p, bytes, 1000, data_size, 2168, pilot_length, 667, 735, 855, 1710, 8); bytes += data_size; + ++block; } return size; } -static const struct CassetteLegacyWaveFiller tzx_legacy_fill_wave = +static const cassette_image::LegacyWaveFiller tzx_legacy_fill_wave = { - tzx_cas_fill_wave, /* fill_wave */ - -1, /* chunk_size */ - 0, /* chunk_samples */ - tzx_cas_to_wav_size, /* chunk_sample_calc */ - TZX_WAV_FREQUENCY, /* sample_frequency */ - 0, /* header_samples */ - 0 /* trailer_samples */ + tzx_cas_fill_wave, // fill_wave + -1, // chunk_size + 0, // chunk_samples + tzx_cas_to_wav_size, // chunk_sample_calc + TZX_WAV_FREQUENCY, // sample_frequency + 0, // header_samples + 0, // trailer_samples }; -static const struct CassetteLegacyWaveFiller tap_legacy_fill_wave = +static const cassette_image::LegacyWaveFiller tap_legacy_fill_wave = { - tap_cas_fill_wave, /* fill_wave */ - -1, /* chunk_size */ - 0, /* chunk_samples */ - tap_cas_to_wav_size, /* chunk_sample_calc */ - TZX_WAV_FREQUENCY, /* sample_frequency */ - 0, /* header_samples */ - 0 /* trailer_samples */ + tap_cas_fill_wave, // fill_wave + -1, // chunk_size + 0, // chunk_samples + tap_cas_to_wav_size, // chunk_sample_calc + TZX_WAV_FREQUENCY, // sample_frequency + 0, // header_samples + 0 // trailer_samples }; -static const struct CassetteLegacyWaveFiller cdt_legacy_fill_wave = +static const cassette_image::LegacyWaveFiller cdt_legacy_fill_wave = { - cdt_cas_fill_wave, /* fill_wave */ - -1, /* chunk_size */ - 0, /* chunk_samples */ - tzx_cas_to_wav_size, /* chunk_sample_calc */ - TZX_WAV_FREQUENCY, /* sample_frequency */ - 0, /* header_samples */ - 0 /* trailer_samples */ + cdt_cas_fill_wave, // fill_wave + -1, // chunk_size + 0, // chunk_samples + tzx_cas_to_wav_size, // chunk_sample_calc + TZX_WAV_FREQUENCY, // sample_frequency + 0, // header_samples + 0 // trailer_samples }; -static cassette_image::error tzx_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) +static cassette_image::error tzx_cassette_identify( cassette_image *cassette, cassette_image::Options *opts ) { - return cassette_legacy_identify(cassette, opts, &tzx_legacy_fill_wave); + return cassette->legacy_identify(opts, &tzx_legacy_fill_wave); } -static cassette_image::error tap_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) +static cassette_image::error tap_cassette_identify( cassette_image *cassette, cassette_image::Options *opts ) { - return cassette_legacy_identify(cassette, opts, &tap_legacy_fill_wave); + return cassette->legacy_identify(opts, &tap_legacy_fill_wave); } -static cassette_image::error cdt_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) +static cassette_image::error cdt_cassette_identify( cassette_image *cassette, cassette_image::Options *opts ) { - return cassette_legacy_identify(cassette, opts, &cdt_legacy_fill_wave); + return cassette->legacy_identify(opts, &cdt_legacy_fill_wave); } static cassette_image::error tzx_cassette_load( cassette_image *cassette ) { - cassette_image::error err = cassette_legacy_construct(cassette, &tzx_legacy_fill_wave); + cassette_image::error err = cassette->legacy_construct(&tzx_legacy_fill_wave); free(blocks); blocks = nullptr; return err; @@ -888,18 +939,18 @@ static cassette_image::error tzx_cassette_load( cassette_image *cassette ) static cassette_image::error tap_cassette_load( cassette_image *cassette ) { - return cassette_legacy_construct(cassette, &tap_legacy_fill_wave); + return cassette->legacy_construct(&tap_legacy_fill_wave); } static cassette_image::error cdt_cassette_load( cassette_image *cassette ) { - cassette_image::error err = cassette_legacy_construct(cassette, &cdt_legacy_fill_wave); + cassette_image::error err = cassette->legacy_construct(&cdt_legacy_fill_wave); free(blocks); blocks = nullptr; return err; } -const struct CassetteFormat tzx_cassette_format = +const cassette_image::Format tzx_cassette_format = { "tzx", tzx_cassette_identify, @@ -907,7 +958,7 @@ const struct CassetteFormat tzx_cassette_format = nullptr }; -static const struct CassetteFormat tap_cassette_format = +static const cassette_image::Format tap_cassette_format = { "tap,blk", tap_cassette_identify, @@ -915,7 +966,7 @@ static const struct CassetteFormat tap_cassette_format = nullptr }; -static const struct CassetteFormat cdt_cassette_format = +static const cassette_image::Format cdt_cassette_format = { "cdt", cdt_cassette_identify, |