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