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
;
35 size_t olength
= uncompr_size
;
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
);
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
;
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 //==========================================================================
85 //==========================================================================
86 static void udi_pack_tracks (disk_t
*d
) {
87 int i
, tlen
, clen
, ttyp
;
90 for (i
= 0; i
< d
->sides
*d
->cylinders
; ++i
) {
91 DISK_SET_TRACK_IDX(d
, i
);
94 tlen
= tmp
[-3]+256*tmp
[-2];
95 clen
= DISK_CLEN(tlen
);
97 /* copy clock if needed */
98 if (tmp
!= d
->clocks
) memcpy(tmp
, d
->clocks
, clen
);
99 if (ttyp
== 0x00 || ttyp
== 0x01) continue;
103 if (tmp
!= d
->fm
) memcpy(tmp
, d
->fm
, clen
);
106 if ((ttyp
&0x80) == 0) continue;
108 if (tmp
!= d
->weak
) memcpy(tmp
, d
->weak
, clen
);
113 //==========================================================================
117 //==========================================================================
118 static void udi_unpack_tracks (disk_t
*d
) {
119 int i
, tlen
, clen
, ttyp
;
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
);
126 tlen
= tmp
[-3]+256*tmp
[-2];
127 clen
= DISK_CLEN(tlen
);
129 if (ttyp
&0x80) tmp
+= clen
;
130 if (ttyp
&0x02) tmp
+= clen
;
132 /* copy WEAK marks */
133 if (tmp
!= d
->weak
) memcpy(d
->weak
, tmp
, clen
);
136 /* clear WEAK marks */
137 memset(d
->weak
, 0, clen
);
141 if (tmp
!= d
->fm
) memcpy(d
->fm
, tmp
, clen
);
144 /* set/clear FM marks */
145 memset(d
->fm
, (ttyp
&0x01 ? 0xff : 0), clen
);
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
) {
169 uint8_t *data
= NULL
;
170 #ifdef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION
171 size_t data_size
= 0;
172 int bpt
, tlen
, clen
, ttyp
;
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
;
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
);
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 */
197 if (data
) free(data
);
202 #ifdef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION
203 //==========================================================================
205 // udi_compress_tracks
207 //==========================================================================
208 static int udi_compress_tracks (disk_t
*d
) {
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 */
228 d
->track
[-3] = clen
&0xff;
229 d
->track
[-2] = (clen
>>8)&0xff;
231 if (data
) free(data
);
237 //==========================================================================
241 //==========================================================================
242 static int open_udi (buffer_t
*buffer
, disk_t
*d
, int preindex_unused
) {
243 int i
, bpt
, ttyp
, tlen
, error
;
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
);
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;
262 d
->density
= DISK_DENS_AUTO
;
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
);
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
;
283 if (i
== 0) return (d
->status
= DISK_GEOM
); /* cannot be first track */
284 /* not a real track */
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];
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 */
313 for (i
= 0; buffer
->index
< eof
; ++i
) {
314 DISK_SET_TRACK_IDX(d
, i
);
316 bpt
= buff
[1]+256*buff
[2]; /* current track len... */
317 memset(d
->track
, 0x4e, d
->bpt
); /* fillup */
318 /* read track + clocks */
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
);
334 tlen
= UDI_TLEN(ttyp
, bpt
);
337 d
->track
[-3] = buff
[1];
338 d
->track
[-2] = buff
[2];
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 //==========================================================================
356 //==========================================================================
357 static int write_udi (FILE *file
, disk_t
*d
) {
358 int i
, j
, error
=error
;
364 #ifdef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION
365 udi_compress_tracks(d
);
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];
377 len
+= 3+UDI_TLEN(d
->track
[-1], d
->track
[-3]+256*d
->track
[-2]);
386 head
[5] = (len
>>8)&0xff;
387 head
[6] = (len
>>16)&0xff;
388 head
[7] = (len
>>24)&0xff;
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
]);
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];
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
);
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
;
432 udi_unpack_tracks(d
);
433 return (d
->status
= DISK_OK
);