1 // license:BSD-3-Clause
2 // copyright-holders:Wilbert Pol
3 /********************************************************************
4 
5 Support for EACA Colour Genie .cas cassette images
6 
7 Current state: Not working. Only the sync signal and 0x66 byte get
8                recognized.
9 
10 NOTE: There exist multiples type of .cas files for Colour Genie
11  - the original one from Jurgen's emu, which starts with TAPE_HEADER
12    below, followed by the sync signal, without the 255 leading 0xaa
13    bytes (which are added at loading time)
14  - a newer type from Genieous emu, which does not start with TAPE_HEADER
15    but contains the 255 leading 0xaa bytes (which are now skipped below)
16  - an alternative type (from Genieous as well?) without TAPE_HEADER
17    and without the 255 leading 0xaa bytes
18 We now support these three types below...
19 
20 ********************************************************************/
21 #include "formats/cgen_cas.h"
22 
23 #include <cassert>
24 
25 
26 #define TAPE_HEADER "Colour Genie - Virtual Tape File"
27 
28 #define SMPLO   -32768
29 #define SMPHI   32767
30 
31 
32 static int cas_size;
33 static int level;
34 
35 
cgenie_output_byte(int16_t * buffer,int sample_count,uint8_t data)36 static int cgenie_output_byte(int16_t *buffer, int sample_count, uint8_t data)
37 {
38 	int samples = 0;
39 
40 	for (int i = 0; i < 8; i++)
41 	{
42 		// Output bit boundary
43 		level ^= 1;
44 		if (buffer)
45 			buffer[sample_count + samples] = level ? SMPHI : SMPLO;
46 		samples++;
47 
48 		// Output bit
49 		if (data & 0x80)
50 			level ^= 1;
51 		if (buffer)
52 			buffer[sample_count + samples] = level ? SMPHI : SMPLO;
53 		samples++;
54 
55 		data <<= 1;
56 	}
57 	return samples;
58 }
59 
60 
cgenie_handle_cas(int16_t * buffer,const uint8_t * casdata)61 static int cgenie_handle_cas(int16_t *buffer, const uint8_t *casdata)
62 {
63 	int data_pos, sample_count;
64 
65 	data_pos = 0;
66 	sample_count = 0;
67 	level = 0;
68 
69 	// Check for presence of optional header
70 	if (!memcmp(casdata, TAPE_HEADER, sizeof(TAPE_HEADER) - 1))
71 	{
72 		// Search for 0x00 or end of file
73 		while (data_pos < cas_size && casdata[data_pos])
74 			data_pos++;
75 
76 		// If we're at the end of the file it's not a valid .cas file
77 		if (data_pos == cas_size)
78 			return -1;
79 
80 		// Skip the 0x00 byte
81 		data_pos++;
82 	}
83 
84 	// If we're at the end of the file it's not a valid .cas file
85 	if (data_pos == cas_size)
86 		return -1;
87 
88 	// Check for beginning of tape file marker (possibly skipping the 0xaa header)
89 	if (casdata[data_pos] != 0x66 && casdata[data_pos + 0xff] != 0x66)
90 		return -1;
91 
92 	// Create header, if not present in the file
93 	if (casdata[data_pos] == 0x66)
94 		for (int i = 0; i < 256; i++)
95 			sample_count += cgenie_output_byte(buffer, sample_count, 0xaa);
96 
97 	// Start outputting data
98 	while (data_pos < cas_size)
99 	{
100 		sample_count += cgenie_output_byte(buffer, sample_count, casdata[data_pos]);
101 		data_pos++;
102 	}
103 	sample_count += cgenie_output_byte(buffer, sample_count, 0x00);
104 
105 	return sample_count;
106 }
107 
108 /*******************************************************************
109    Generate samples for the tape image
110 ********************************************************************/
cgenie_cas_fill_wave(int16_t * buffer,int sample_count,uint8_t * bytes)111 static int cgenie_cas_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes)
112 {
113 	return cgenie_handle_cas(buffer, bytes);
114 }
115 
116 
117 /*******************************************************************
118    Calculate the number of samples needed for this tape image
119 ********************************************************************/
cgenie_cas_to_wav_size(const uint8_t * casdata,int caslen)120 static int cgenie_cas_to_wav_size(const uint8_t *casdata, int caslen)
121 {
122 	cas_size = caslen;
123 
124 	return cgenie_handle_cas(nullptr, casdata);
125 }
126 
127 static const cassette_image::LegacyWaveFiller cgenie_cas_legacy_fill_wave =
128 {
129 	cgenie_cas_fill_wave,                   /* fill_wave */
130 	-1,                                     /* chunk_size */
131 	0,                                      /* chunk_samples */
132 	cgenie_cas_to_wav_size,                 /* chunk_sample_calc */
133 	2400,                                   /* sample_frequency */
134 	0,                                      /* header_samples */
135 	0                                       /* trailer_samples */
136 };
137 
138 
cgenie_cas_identify(cassette_image * cassette,cassette_image::Options * opts)139 static cassette_image::error cgenie_cas_identify(cassette_image *cassette, cassette_image::Options *opts)
140 {
141 	return cassette->legacy_identify(opts, &cgenie_cas_legacy_fill_wave);
142 }
143 
144 
cgenie_cas_load(cassette_image * cassette)145 static cassette_image::error cgenie_cas_load(cassette_image *cassette)
146 {
147 	return cassette->legacy_construct(&cgenie_cas_legacy_fill_wave);
148 }
149 
150 
151 static const cassette_image::Format cgenie_cas_format =
152 {
153 	"cas",
154 	cgenie_cas_identify,
155 	cgenie_cas_load,
156 	nullptr
157 };
158 
159 
160 CASSETTE_FORMATLIST_START(cgenie_cassette_formats)
161 	CASSETTE_FORMAT(cgenie_cas_format)
162 CASSETTE_FORMATLIST_END
163