1 // license:BSD-3-Clause
2 // copyright-holders:Nigel Barnes
3 /*
4   CSW format
5   ----------
6   Header Description
7 
8   Offset  Value   Type    Description
9   0x00    (note)  ASCII   22 bytes "Compressed Square Wave" signature
10   0x16    0x1A    BYTE    Terminator code
11   0x17    0x02    BYTE    CSW major revision number
12   0x18    0x00    BYTE    CSW minor revision number
13   0x19            DWORD   Sample rate
14   0x1D            DWORD   Total number of pulses (after decompression)
15   0x21            BYTE    Compression type  0x01: RLE    0x02: Z-RLE
16   0x22            BYTE    Flags   b0: initial polarity; if set, the signal starts at logical high
17   0x23    HDR     BYTE    Header extension length in bytes (0x00)
18   0x24            ASCII   Encoding application description
19   0x34            BYTE    Header extension data (if present HDR>0)
20   0x34+HDR                CSW data
21 */
22 
23 #include "csw_cas.h"
24 #include "uef_cas.h"
25 
26 #include <zlib.h>
27 
28 #include <cassert>
29 #include <cstring>
30 
31 
32 static const uint8_t CSW_HEADER[] = { "Compressed Square Wave" };
33 
34 
35 /*-------------------------------------------------
36     csw_cassette_identify - identify cassette
37 -------------------------------------------------*/
38 
csw_cassette_identify(cassette_image * cassette,cassette_image::Options * opts)39 static cassette_image::error csw_cassette_identify(cassette_image *cassette, cassette_image::Options *opts)
40 {
41 	uint8_t header[0x34];
42 
43 	cassette->image_read(header, 0, sizeof(header));
44 	if (memcmp(&header[0], CSW_HEADER, sizeof(CSW_HEADER) - 1)) {
45 		return cassette_image::error::INVALID_IMAGE;
46 	}
47 
48 	opts->bits_per_sample = 8;
49 	opts->channels = 1;
50 	opts->sample_frequency = little_endianize_int16(*(uint32_t*)(header + 0x19));
51 	return cassette_image::error::SUCCESS;
52 }
53 
54 
55 /*-------------------------------------------------
56     csw_cassette_load - load cassette
57 -------------------------------------------------*/
58 
csw_cassette_load(cassette_image * cassette)59 static cassette_image::error csw_cassette_load(cassette_image *cassette)
60 {
61 	uint8_t  header[0x34];
62 	uint64_t image_size = cassette->image_size();
63 	std::vector<uint8_t> image_data(image_size);
64 
65 	int8_t bit;
66 	uint8_t compression;
67 	int bsize = 0;
68 	size_t csw_data = 0;
69 	size_t sample_count = 0;
70 	uint32_t sample_rate;
71 	std::vector<int8_t> samples;
72 
73 	/* csw header */
74 	cassette->image_read(header, 0, sizeof(header));
75 
76 	if (header[0x16] != 0x1a)
77 	{
78 		LOG_FORMATS("csw_cassette_load: Terminator Code Not Found\n");
79 		return cassette_image::error::INVALID_IMAGE;
80 	}
81 
82 	LOG_FORMATS("CSW Version %d.%d\n", header[0x17], header[0x18]);
83 	switch (header[0x17])
84 	{
85 	case 1:
86 		sample_rate = little_endianize_int16(*(uint32_t*)(header + 0x19));
87 		compression = header[0x1b];
88 		bit = (header[0x1c] & 1) ? 127 : -128;
89 		csw_data = 0x20;
90 
91 		LOG_FORMATS("Sample Rate: %u\n", sample_rate);
92 		LOG_FORMATS("CompressionType: %u   Flags: %u\n", header[0x1b], header[0x1c]);
93 		break;
94 
95 	case 2:
96 		sample_rate = little_endianize_int32(*(uint32_t*)(header + 0x19));
97 		compression = header[0x21];
98 		bit = (header[0x22] & 1) ? 127 : -128;
99 		csw_data = (size_t) header[0x23] + 0x34;
100 
101 		LOG_FORMATS("Sample Rate: %u\n", sample_rate);
102 		LOG_FORMATS("Number of Pulses: %u\n", little_endianize_int32(*(uint32_t *)(header + 0x1d)));
103 		LOG_FORMATS("CompressionType: %u   Flags: %u\n", header[0x21], header[0x22]);
104 		LOG_FORMATS("Encoder: ");
105 		for (int i = 0; i < 16; i++)
106 			LOG_FORMATS("%c", header[0x24 + i]);
107 		LOG_FORMATS("\n");
108 		break;
109 
110 	default:
111 		LOG_FORMATS("Unsupported Major Version\n");
112 		return cassette_image::error::INVALID_IMAGE;
113 	}
114 
115 	/* csw data */
116 	switch (compression)
117 	{
118 	case 0x01:
119 		/* RLE (Run Length Encoding) */
120 		for (size_t pos = csw_data; pos < image_size; pos++)
121 		{
122 			bsize = image_data[pos];
123 			if (bsize == 0)
124 			{
125 				bsize = little_endianize_int32(*(uint32_t *)(&image_data[pos + 1]));
126 				pos += 4;
127 			}
128 			for (int i = 0; i < bsize; i++)
129 			{
130 				samples.resize(sample_count + 1);
131 				samples[sample_count++] = bit;
132 			}
133 			bit ^= 0xff;
134 		}
135 		break;
136 
137 	case 0x02:
138 		/* Z-RLE (CSW v2.xx only) */
139 		cassette->image_read(&image_data[0], 0, image_size);
140 
141 		std::vector<uint8_t> gz_ptr;
142 		z_stream    d_stream;
143 		int         err;
144 
145 		gz_ptr.resize(8);
146 
147 		d_stream.next_in = (unsigned char *) &image_data[csw_data];
148 		d_stream.avail_in = image_size - 0x34 - header[0x23];
149 		d_stream.total_in = 0;
150 
151 		d_stream.next_out = &gz_ptr[0];
152 		d_stream.avail_out = 1;
153 		d_stream.total_out = 0;
154 
155 		d_stream.zalloc = nullptr;
156 		d_stream.zfree = nullptr;
157 		d_stream.opaque = nullptr;
158 		d_stream.data_type = 0;
159 
160 		err = inflateInit(&d_stream);
161 		if (err != Z_OK)
162 		{
163 			LOG_FORMATS("inflateInit error: %d\n", err);
164 			return cassette_image::error::INVALID_IMAGE;
165 		}
166 
167 		do
168 		{
169 			d_stream.next_out = &gz_ptr[0];
170 			d_stream.avail_out = 1;
171 			err = inflate(&d_stream, Z_SYNC_FLUSH);
172 			if (err == Z_OK)
173 			{
174 				bsize = gz_ptr[0];
175 				if (bsize == 0)
176 				{
177 					d_stream.avail_out = 4;
178 					d_stream.next_out = &gz_ptr[0];
179 					err = inflate(&d_stream, Z_SYNC_FLUSH);
180 					bsize = little_endianize_int32(*(uint32_t *)(&gz_ptr[0]));
181 				}
182 				for (int i = 0; i < bsize; i++)
183 				{
184 					samples.resize(sample_count + 1);
185 					samples[sample_count++] = bit;
186 				}
187 				bit ^= 0xff;
188 			}
189 		}
190 		while (err == Z_OK);
191 
192 		if (err != Z_STREAM_END)
193 		{
194 			LOG_FORMATS("inflate error: %d\n", err);
195 			return cassette_image::error::INVALID_IMAGE;
196 		}
197 
198 		err = inflateEnd(&d_stream);
199 		if (err != Z_OK)
200 		{
201 			LOG_FORMATS("inflateEnd error: %d\n", err);
202 			return cassette_image::error::INVALID_IMAGE;
203 		}
204 		break;
205 	}
206 
207 	return cassette->put_samples(0, 0.0, (double) sample_count / sample_rate, sample_count, 1, &samples[0], cassette_image::WAVEFORM_8BIT);
208 }
209 
210 
211 /*-------------------------------------------------
212     CassetteFormat csw_cassette_format
213 -------------------------------------------------*/
214 
215 const cassette_image::Format csw_cassette_format = {
216 	"csw",
217 	csw_cassette_identify,
218 	csw_cassette_load,
219 	nullptr
220 };
221 
222 CASSETTE_FORMATLIST_START(csw_cassette_formats)
223 	CASSETTE_FORMAT(csw_cassette_format)
224 CASSETTE_FORMATLIST_END
225 
226 CASSETTE_FORMATLIST_START(bbc_cassette_formats)
227 	CASSETTE_FORMAT(csw_cassette_format)
228 	CASSETTE_FORMAT(uef_cassette_format)
229 CASSETTE_FORMATLIST_END
230