// license:BSD-3-Clause // copyright-holders:Wilbert Pol /******************************************************************** Support for EACA Colour Genie .cas cassette images Current state: Not working. Only the sync signal and 0x66 byte get recognized. NOTE: There exist multiples type of .cas files for Colour Genie - the original one from Jurgen's emu, which starts with TAPE_HEADER below, followed by the sync signal, without the 255 leading 0xaa bytes (which are added at loading time) - a newer type from Genieous emu, which does not start with TAPE_HEADER but contains the 255 leading 0xaa bytes (which are now skipped below) - an alternative type (from Genieous as well?) without TAPE_HEADER and without the 255 leading 0xaa bytes We now support these three types below... ********************************************************************/ #include #include "formats/cgen_cas.h" #define TAPE_HEADER "Colour Genie - Virtual Tape File" #define SMPLO -32768 #define SMPHI 32767 static int cas_size; static int level; static int cgenie_output_byte(int16_t *buffer, int sample_count, uint8_t data) { int samples = 0; for (int i = 0; i < 8; i++) { // Output bit boundary level ^= 1; if (buffer) buffer[sample_count + samples] = level ? SMPHI : SMPLO; samples++; // Output bit if (data & 0x80) level ^= 1; if (buffer) buffer[sample_count + samples] = level ? SMPHI : SMPLO; samples++; data <<= 1; } return samples; } static int cgenie_handle_cas(int16_t *buffer, const uint8_t *casdata) { int data_pos, sample_count; data_pos = 0; sample_count = 0; level = 0; // Check for presence of optional header if (!memcmp(casdata, TAPE_HEADER, sizeof(TAPE_HEADER) - 1)) { // Search for 0x00 or end of file while (data_pos < cas_size && casdata[data_pos]) data_pos++; // If we're at the end of the file it's not a valid .cas file if (data_pos == cas_size) return -1; // Skip the 0x00 byte data_pos++; } // If we're at the end of the file it's not a valid .cas file if (data_pos == cas_size) return -1; // Check for beginning of tape file marker (possibly skipping the 0xaa header) if (casdata[data_pos] != 0x66 && casdata[data_pos + 0xff] != 0x66) return -1; // Create header, if not present in the file if (casdata[data_pos] == 0x66) for (int i = 0; i < 256; i++) sample_count += cgenie_output_byte(buffer, sample_count, 0xaa); // Start outputting data while (data_pos < cas_size) { sample_count += cgenie_output_byte(buffer, sample_count, casdata[data_pos]); data_pos++; } sample_count += cgenie_output_byte(buffer, sample_count, 0x00); return sample_count; } /******************************************************************* Generate samples for the tape image ********************************************************************/ static int cgenie_cas_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes) { return cgenie_handle_cas(buffer, bytes); } /******************************************************************* Calculate the number of samples needed for this tape image ********************************************************************/ static int cgenie_cas_to_wav_size(const uint8_t *casdata, int caslen) { cas_size = caslen; return cgenie_handle_cas(nullptr, casdata); } static const struct CassetteLegacyWaveFiller cgenie_cas_legacy_fill_wave = { cgenie_cas_fill_wave, /* fill_wave */ -1, /* chunk_size */ 0, /* chunk_samples */ cgenie_cas_to_wav_size, /* chunk_sample_calc */ 2400, /* sample_frequency */ 0, /* header_samples */ 0 /* trailer_samples */ }; static cassette_image::error cgenie_cas_identify(cassette_image *cassette, struct CassetteOptions *opts) { return cassette_legacy_identify(cassette, opts, &cgenie_cas_legacy_fill_wave); } static cassette_image::error cgenie_cas_load(cassette_image *cassette) { return cassette_legacy_construct(cassette, &cgenie_cas_legacy_fill_wave); } static const struct CassetteFormat cgenie_cas_format = { "cas", cgenie_cas_identify, cgenie_cas_load, nullptr }; CASSETTE_FORMATLIST_START(cgenie_cassette_formats) CASSETTE_FORMAT(cgenie_cas_format) CASSETTE_FORMATLIST_END