libfusefdc: reformatted UDI i/o code
[zymosis.git] / src / libfusefdc / disk_udi.c
bloba15ed58078f597299f7bb33885d26c524b14816c
1 /* disk.c: Routines for handling disk images
2 Copyright (c) 2007-2017 Gergely Szasz
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 Author contact information:
20 Philip: philip-fuse@shadowmagic.org.uk
23 #ifdef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION
24 //==========================================================================
26 // udi_read_compressed
28 //==========================================================================
29 static int udi_read_compressed (const uint8_t *buffer,
30 size_t compr_size, size_t uncompr_size,
31 uint8_t **data, size_t *data_size)
33 libspectrum_error error;
34 uint8_t *tmp;
35 size_t olength = uncompr_size;
37 tmp = NULL;
39 error = libspectrum_zlib_inflate(buffer, compr_size, &tmp, &olength);
40 if (error) return error;
42 if (*data_size < uncompr_size) {
43 *data = realloc(*data, uncompr_size);
44 *data_size = uncompr_size;
46 memcpy(*data, tmp, uncompr_size);
47 libspectrum_free(tmp);
49 return 0;
53 //==========================================================================
55 // udi_write_compressed
57 //==========================================================================
58 static int udi_write_compressed (const uint8_t *buffer,
59 size_t uncompr_size, size_t *compr_size,
60 uint8_t **data, size_t *data_size)
62 libspectrum_error error;
63 uint8_t *tmp;
65 tmp = NULL;
66 error = libspectrum_zlib_compress(buffer, uncompr_size, &tmp, compr_size);
67 if (error) return error;
69 if (*data_size < *compr_size) {
70 *data = realloc(*data, *compr_size );
71 *data_size = *compr_size;
73 memcpy(*data, tmp, *compr_size);
74 libspectrum_free(tmp);
76 return LIBSPECTRUM_ERROR_NONE;
78 #endif /* LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION */
81 //==========================================================================
83 // udi_pack_tracks
85 //==========================================================================
86 static void udi_pack_tracks (disk_t *d) {
87 int i, tlen, clen, ttyp;
88 uint8_t *tmp;
90 for (i = 0; i < d->sides*d->cylinders; ++i) {
91 DISK_SET_TRACK_IDX(d, i);
92 tmp = d->track;
93 ttyp = tmp[-1];
94 tlen = tmp[-3]+256*tmp[-2];
95 clen = DISK_CLEN(tlen);
96 tmp += tlen;
97 /* copy clock if needed */
98 if (tmp != d->clocks) memcpy(tmp, d->clocks, clen);
99 if (ttyp == 0x00 || ttyp == 0x01) continue;
100 tmp += clen;
101 if (ttyp&0x02) {
102 /* copy FM marks */
103 if (tmp != d->fm) memcpy(tmp, d->fm, clen);
104 tmp += clen;
106 if ((ttyp&0x80) == 0) continue;
107 /* copy WEAK marks*/
108 if (tmp != d->weak) memcpy(tmp, d->weak, clen);
113 //==========================================================================
115 // udi_unpack_tracks
117 //==========================================================================
118 static void udi_unpack_tracks (disk_t *d) {
119 int i, tlen, clen, ttyp;
120 uint8_t *tmp;
121 const uint8_t mask[] = { 0xff, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
122 for (i = 0; i < d->sides*d->cylinders; ++i) {
123 DISK_SET_TRACK_IDX(d, i);
124 tmp = d->track;
125 ttyp = tmp[-1];
126 tlen = tmp[-3]+256*tmp[-2];
127 clen = DISK_CLEN(tlen);
128 tmp += tlen;
129 if (ttyp&0x80) tmp += clen;
130 if (ttyp&0x02) tmp += clen;
131 if ((ttyp&0x80)) {
132 /* copy WEAK marks */
133 if (tmp != d->weak) memcpy(d->weak, tmp, clen);
134 tmp -= clen;
135 } else {
136 /* clear WEAK marks */
137 memset(d->weak, 0, clen);
139 if (ttyp&0x02) {
140 /* copy FM marks */
141 if (tmp != d->fm) memcpy(d->fm, tmp, clen);
142 tmp -= clen;
143 } else {
144 /* set/clear FM marks */
145 memset(d->fm, (ttyp&0x01 ? 0xff : 0), clen);
146 if (tlen%8) {
147 /* adjust last byte */
148 d->fm[clen-1] &= mask[tlen%8];
151 /* copy clock if needed */
152 if (tmp != d->clocks) memcpy(d->clocks, tmp, clen);
156 /* calculate track len from type, if type eq. 0x00/0x01/0x02/0x80/0x81/0x82
157 !!! not for 0x83 nor 0xf0 !!!
159 #define UDI_TLEN( type, bpt ) ((bpt)+DISK_CLEN(bpt)*(1+(type&0x02 ? 1 : 0)+(type&0x80 ? 1 : 0)))
162 //==========================================================================
164 // udi_uncompress_tracks
166 //==========================================================================
167 static int udi_uncompress_tracks (disk_t *d) {
168 int i;
169 uint8_t *data = NULL;
170 #ifdef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION
171 size_t data_size = 0;
172 int bpt, tlen, clen, ttyp;
173 #endif
175 for (i = 0; i < d->sides*d->cylinders; ++i) {
176 DISK_SET_TRACK_IDX(d, i);
177 if (d->track[-1] != 0xf0) continue; /* if not compressed */
179 #ifndef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION
180 /* if libspectrum cannot support */
181 return d->status = DISK_UNSUP;
182 #else
183 clen = d->track[-3]+256*d->track[-2]+1;
184 ttyp = d->track[0]; /* compressed track type */
185 bpt = d->track[1]+256*d->track[2]; /* compressed track len... */
186 tlen = UDI_TLEN(ttyp, bpt);
187 d->track[-1] = ttyp;
188 d->track[-3] = d->track[1];
189 d->track[-2] = d->track[2];
190 if (udi_read_compressed(d->track+3, clen, tlen, &data, &data_size)) {
191 if (data) free(data);
192 return (d->status = DISK_UNSUP);
194 memcpy(d->track, data, tlen); /* read track */
195 #endif
197 if (data) free(data);
198 return DISK_OK;
202 #ifdef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION
203 //==========================================================================
205 // udi_compress_tracks
207 //==========================================================================
208 static int udi_compress_tracks (disk_t *d) {
209 int i, tlen;
210 uint8_t *data = NULL;
211 size_t clen, data_size = 0;
213 for (i = 0; i < d->sides*d->cylinders; ++i) {
214 DISK_SET_TRACK_IDX(d, i);
215 if (d->track[-1] == 0xf0) continue; /* already compressed??? */
217 tlen = UDI_TLEN(d->track[-1], d->track[-3]+256*d->track[-2]);
218 /* if fail to compress, skip ... */
219 if (udi_write_compressed(d->track, tlen, &clen, &data, &data_size ) || clen < 1) continue;
220 /* if compression too large, skip... */
221 if (clen > 65535 || clen >= tlen) continue;
222 d->track[0] = d->track[-1]; /* track type... */
223 d->track[1] = d->track[-3]; /* compressed track len... */
224 d->track[2] = d->track[-2]; /* compressed track len... */
225 memcpy(d->track+3, data, clen); /* read track */
226 --clen;
227 d->track[-1] = 0xf0;
228 d->track[-3] = clen&0xff;
229 d->track[-2] = (clen>>8)&0xff;
231 if (data) free(data);
232 return DISK_OK;
234 #endif
237 //==========================================================================
239 // open_udi
241 //==========================================================================
242 static int open_udi (buffer_t *buffer, disk_t *d, int preindex_unused) {
243 int i, bpt, ttyp, tlen, error;
244 size_t eof;
245 uint32_t crc;
247 crc = ~(uint32_t)0;
249 /* check file length */
250 eof = buff[4]+256U*buff[5]+65536U*buff[6]+16777216U*buff[7];
251 if (eof != buffer->file.length-4) return (d->status = DISK_OPEN);
253 /* check CRC32 */
254 for (i = 0; i < eof; ++i) crc = crc_udi(crc, buff[i]);
255 if (crc != buff[eof]+256U*buff[eof+1]+65536U*buff[eof+2]+16777216U*buff[eof+3]) {
256 return (d->status = DISK_OPEN);
259 d->sides = buff[10]+1;
260 d->cylinders = buff[9]+1;
261 GEOM_CHECK;
262 d->density = DISK_DENS_AUTO;
263 buffer->index = 16;
264 d->bpt = 0;
266 /* scan file for the longest track */
267 for (i = 0; buffer->index < eof; ++i) {
268 if (buffavail(buffer) < 3) return (d->status = DISK_OPEN);
270 ttyp = buff[0];
271 if (ttyp != 0x00 && ttyp != 0x01 && ttyp != 0x02 && ttyp != 0x80 &&
272 ttyp != 0x81 && ttyp != 0x82 && ttyp != 0x83 && ttyp != 0xf0)
274 return d->status = DISK_UNSUP;
277 /* if libspectrum cannot suppot*/
278 #ifndef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION
279 if (ttyp == 0xf0) d->status = DISK_UNSUP;
280 #endif
281 if (ttyp == 0x83) {
282 /* multiple read */
283 if (i == 0) return (d->status = DISK_GEOM); /* cannot be first track */
284 /* not a real track */
285 --i;
286 bpt = 0;
287 tlen = buff[1]+256*buff[2]; /* current track len... */
288 tlen = ( tlen&0xfff8 )*(tlen&0x07);
289 } else if (ttyp == 0xf0) {
290 /* compressed track */
291 if (buffavail(buffer) < 7) return (d->status = DISK_OPEN);
292 bpt = buff[4]+256*buff[5];
293 tlen = 7+buff[1]+256*buff[2];
294 } else {
295 bpt = buff[1]+256*buff[2]; /* current track len... */
296 tlen = 3+UDI_TLEN(ttyp, bpt);
298 if (bpt > d->bpt) d->bpt = bpt;
299 if (buffseek(buffer, tlen, SEEK_CUR) == -1) return (d->status = DISK_OPEN);
302 if (d->bpt == 0) return (d->status = DISK_GEOM);
304 bpt = d->bpt; /* save the maximal value */
305 d->tlen = 3+bpt+3*DISK_CLEN(bpt);
306 d->bpt = 0; /* we know exactly the track len... */
308 if (disk_alloc(d) != DISK_OK) return d->status;
310 d->bpt = bpt; /* restore the maximal byte per track */
311 buffer->index = 16;
313 for (i = 0; buffer->index < eof; ++i) {
314 DISK_SET_TRACK_IDX(d, i);
315 ttyp = buff[0];
316 bpt = buff[1]+256*buff[2]; /* current track len... */
317 memset(d->track, 0x4e, d->bpt); /* fillup */
318 /* read track + clocks */
319 if (ttyp == 0x83) {
320 /* multiple read */
321 --i; /* not a real track */
322 DISK_SET_TRACK_IDX(d, i); /* back to previouse track */
323 d->weak += buff[3]+256*buff[4]; /* add offset to weak */
324 tlen = (buff[1]+256*buff[2])>>3; /* weak len in bytes */
325 for (--tlen; tlen >= 0; --tlen) d->weak[tlen] = 0xff;
326 tlen = buff[1]+256*buff[2]; /* current track len... */
327 tlen = (tlen&0xfff8)*(tlen&0x07);
328 buffseek(buffer, tlen, SEEK_CUR);
329 } else {
330 if (ttyp == 0xf0) {
331 /* compressed */
332 tlen = bpt+4;
333 } else {
334 tlen = UDI_TLEN(ttyp, bpt);
336 d->track[-1] = ttyp;
337 d->track[-3] = buff[1];
338 d->track[-2] = buff[2];
339 buffer->index += 3;
340 buffread(d->track, tlen, buffer); /* first read data */
344 error = udi_uncompress_tracks( d );
345 if (error) return error;
346 udi_unpack_tracks(d);
348 return (d->status = DISK_OK);
352 //==========================================================================
354 // write_udi
356 //==========================================================================
357 static int write_udi (FILE *file, disk_t *d) {
358 int i, j, error=error;
359 size_t len;
360 uint32_t crc;
361 uint8_t head[256];
363 udi_pack_tracks(d);
364 #ifdef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION
365 udi_compress_tracks(d);
366 #endif
368 crc = ~(uint32_t)0;
369 len = 16;
371 /* check tracks */
372 for (i = 0; i < d->sides*d->cylinders; ++i) {
373 DISK_SET_TRACK_IDX(d, i);
374 if (d->track[-1] == 0xf0) {
375 len += 7+d->track[-3]+256*d->track[-2];
376 } else {
377 len += 3+UDI_TLEN(d->track[-1], d->track[-3]+256*d->track[-2]);
381 head[0] = 'U';
382 head[1] = 'D';
383 head[2] = 'I';
384 head[3] = '!';
385 head[4] = len&0xff;
386 head[5] = (len>>8)&0xff;
387 head[6] = (len>>16)&0xff;
388 head[7] = (len>>24)&0xff;
389 head[8] = 0x00;
390 head[9] = d->cylinders-1;
391 head[10] = d->sides-1;
392 head[11] = head[12] = head[13] = head[14] = head[15] = 0;
393 if (fwrite(head, 16, 1, file) != 1) return (d->status = DISK_WRPART);
395 for (j = 0; j < 16; ++j) crc = crc_udi(crc, head[j]);
397 /* write tracks */
398 for (i = 0; i < d->sides*d->cylinders; ++i) {
399 DISK_SET_TRACK_IDX(d, i);
400 head[0] = d->track[-1]; /* track type */
401 head[1] = d->track[-3]; /* track len */
402 head[2] = d->track[-2]; /* track len2 */
403 if (fwrite(head, 3, 1, file) != 1) return (d->status = DISK_WRPART);
405 for (j = 0; j < 3; ++j) crc = crc_udi(crc, head[j]);
407 if (d->track[-1] == 0xf0) {
408 len = 4+d->track[-3]+256*d->track[-2];
409 } else {
410 len = UDI_TLEN(d->track[-1], d->track[-3]+256*d->track[-2]);
412 if (fwrite(d->track, len, 1, file) != 1) return (d->status = DISK_WRPART);
414 for (j = len; j > 0; --j) {
415 crc = crc_udi(crc, *d->track);
416 ++d->track;
420 head[0] = crc&0xff;
421 head[1] = (crc>>8)&0xff;
422 head[2] = (crc>>16)&0xff;
423 head[3] = (crc>>24)&0xff;
424 if (fwrite(head, 4, 1, file) != 1) fclose(file); /* CRC */
426 #ifdef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION
427 /* Keep tracks uncompressed in memory */
428 error = udi_uncompress_tracks(d);
429 if (error) return error;
430 #endif
432 udi_unpack_tracks(d);
433 return (d->status = DISK_OK);