3 * Handle ArcFS format archives
5 * Author: Andrew Brooks, arb@comp.lancs.ac.uk
7 * $Header: arcfs.c 1.5 95/08/01 $
9 * Revision 1.5 95/08/01 xx:xx:xx BB
10 * Fixes for Borland C/C++
11 * Removed use of floating point routines.
13 * Revision 1.4 95/01/06 11:58:45 arb
16 * Revision 1.3 94/12/12 17:25:25 arb
17 * Fixes for writesize.
19 * Revision 1.2 94/10/26 15:06:35 arb
20 * Fixed date and time conversion.
22 * Revision 1.1 94/02/28 21:41:23 arb
23 * Fixed header, fixed include ordering, fixed directory check,
24 * added seek to start of compressed data, fixed maxbits, ...
25 * ie. got the thing working at last!
27 * Revision 1.0 93/08/20 12:40:15 arb
37 #include <sys/types.h>
40 #if defined(RISCOS) || defined(__MSDOS__)
43 #endif /* RISCOS || __MSDOS__ */
55 * Public flag to indicate whether the current archive is ArcFS format
61 * Public number of compression bits, used in compress.c
63 int arcfs_maxbits
= 0;
67 * Public size of file being extracted, used in io.c, crc.c
74 * ArcFS header list element
78 struct arcfs_header_s
*next
;
86 typedef struct arcfs_header_s
*arcfs_header
;
94 #define AFS_STORE 0x82
96 #define AFS_CRUNCH 0x88
97 #define AFS_COMPRESS 0xFF
103 static int arcfs_initialised
= 0;
104 static arcfs_header header_list
= NULL
;
105 static arcfs_header header_ptr
= NULL
;
109 * Convert RISC OS time to UNIX time.
110 * RISC OS time is five bytes of centiseconds since 1900.
111 * UNIX time is seconds since 1970.
112 * MSB of RISC OS time is LSB of `load' plus `exec'.
115 /* BB added extra prototype for Borland C/C++ */
118 rotm(Word load
, Word exec
)
123 high
= (load
& 0xff) - 0x33l
;
124 low
= exec
- 0x6e996a00l
;
129 /* BB changed constant in next line to long */
130 /* cast to Word, then time_t as date stamps will all be 32 bits and time_t
131 * might be 64 bits */
132 t
= (time_t)(Word
)(high
* 42949673L + low
/ 100L);
135 return (localtime(&t
));
140 * Convert RISC OS time to SPARK time
143 /* BB added extra prototype for Borland C/C++ */
146 arcfs_fixtime(Header
*hdr
)
148 /* BB next line commented out, variable ti never used */
152 /* Convert to UNIX time first (as it is easy) */
153 tim
= rotm(hdr
->load
, hdr
->exec
);
155 /* Convert UNIX time to SPARK time */
156 hdr
->date
= (tim
->tm_year
- 80) << 9;
157 hdr
->date
|= (tim
->tm_mon
+ 1) << 5;
158 hdr
->date
|= (tim
->tm_mday
);
159 hdr
->time
= (tim
->tm_hour
) << 11;
160 hdr
->time
|= (tim
->tm_min
) << 5;
161 hdr
->time
|= tim
->tm_sec
/ 2;
169 arcfs_read_header(FILE *ifp
)
171 static Header null_header
;
172 static Word data_start
;
173 Word header_length
= 0;
177 Byte info_byte
, name
[12];
178 Word length
, load
, exec
, attr
, complen
, info_word
;
179 arcfs_header header_prev
= NULL
;
182 /* Return next header from list */
183 if (arcfs_initialised
)
185 /* If end of list return an empty header structure to indicate EOF */
186 if (header_ptr
== NULL
)
187 return (&null_header
);
189 /* Return next header in list */
190 header
= header_ptr
->header
;
191 /* Seek to start of compressed data */
192 if ((!header_ptr
->is_dir
)
193 && (fseek(ifp
, (long) header_ptr
->seek
, SEEK_SET
)))
195 printf("Cannot seek compressed data in this file\n");
196 return (&null_header
);
198 /* Set up number of compression bits */
199 arcfs_maxbits
= header_ptr
->maxbits
;
200 /*if (header_ptr->is_dir) header = &null_header; */
201 header_ptr
= header_ptr
->next
;
205 /* Header list not constructed yet, so read all headers from file */
206 arcfs_initialised
= 1;
207 memset((char *) &null_header
, '\0', sizeof(null_header
));
208 null_header
.comptype
= 0;
209 header_length
= read_word(ifp
);
210 data_start
= read_word(ifp
);
211 if ((version
= read_word(ifp
)) > 40)
213 /* BB removed floating point routines from next line
214 This saves linking the floating point routines under DOS
215 which yields quite a reduction in executable size.
216 And it removes the need to have the FPE present under RISC OS. */
217 /* printf("Archive created by a newer version of ArcFS (%.2f)\n",((float)version)/100); */
218 printf("Archive created by a newer version of ArcFS (%d.%02d)\n",
219 version
/ 100, version
% 100);
220 return (&null_header
);
222 read_word(ifp
); /* read/write version */
223 if ((version
= read_word(ifp
)) > 0)
225 printf("Archive format %d not understood\n", version
);
226 return (&null_header
);
228 for (i
= 0; i
< 17; i
++)
229 read_word(ifp
); /* reserved */
231 /* Read list of headers */
232 for (i
= 0; i
< header_length
/ 36; i
++)
234 /* Create list item */
235 header
= (Header
*) malloc(sizeof(Header
));
236 header_ptr
= (arcfs_header
) malloc(sizeof(struct arcfs_header_s
));
237 if ((header
== NULL
) || (header_ptr
== NULL
))
239 printf("Out of memory\n");
240 return (&null_header
);
243 /* Read ArcFS file header */
244 info_byte
= read_byte(ifp
);
245 for (j
= 0; j
< 11; j
++)
247 name
[j
] = read_byte(ifp
);
248 if (name
[j
] == PATHSEP
)
250 if (name
[j
] < ' ' || name
[j
] > '~')
254 length
= read_word(ifp
);
255 load
= read_word(ifp
);
256 exec
= read_word(ifp
);
257 attr
= read_word(ifp
);
258 complen
= read_word(ifp
);
259 info_word
= read_word(ifp
);
261 /* Examine, and create nspark header */
262 if (info_byte
== AFS_DELETED
)
268 /* BB changed next line for Borland C/C++ 4 */
269 /* header_ptr->is_dir = (info_word >> 31); */
271 header_ptr
->is_dir
= (Halfword
) (info_word
>> 31);
273 header_ptr
->is_dir
= (info_word
>> 31);
275 header_ptr
->info_byte
= info_byte
;
276 header_ptr
->info_word
= info_word
;
277 /* BB changed next line for Borland C/C++ 4 */
278 /* header_ptr->maxbits = (attr & 0xff00) >> 8; */
280 header_ptr
->maxbits
= (Halfword
) (attr
& 0xff00) >> 8;
282 header_ptr
->maxbits
= (attr
& 0xff00) >> 8;
284 /* BB changed constant in next line to long. */
285 header_ptr
->seek
= (info_word
& 0x7fffffffL
) + data_start
;
286 header
->comptype
= info_byte
;
287 strcpy(header
->name
, (char *) name
);
288 header
->complen
= complen
;
291 header
->crc
= (Halfword
) (attr
>> 16);
292 header
->origlen
= length
;
295 header
->attr
= attr
& 0xff;
297 arcfs_fixtime(header
);
299 if (info_byte
== AFS_ENDDIR
)
301 /* Just return comptype == 0 */
302 *header
= null_header
;
303 header_ptr
->is_dir
= 0;
304 header_ptr
->seek
= 0;
307 /* If it is an ArcFS directory then convert to a Spark directory */
308 if (header_ptr
->is_dir
)
310 /* Make sure filetype is DDC */
311 header
->comptype
= CT_NOTCOMP2
;
312 /* BB changed constant in next line to long. */
313 header
->load
= 0xfffddcffL
;
316 /* Add list item to list */
317 /* Doing it here ensures that deleted items are not added */
318 header_ptr
->header
= header
;
319 if (header_list
== NULL
)
320 header_list
= header_ptr
;
322 header_prev
->next
= header_ptr
;
323 header_prev
= header_ptr
;
325 print_header(header
);
329 /* Return first element */
330 header_ptr
= header_list
;
331 header
= header_ptr
->header
;
332 /* Seek to start of data for first element */
333 if ((!header_ptr
->is_dir
)
334 && (fseek(ifp
, (long) header_ptr
->seek
, SEEK_SET
)))
336 printf("Cannot seek compressed data in this file\n");
337 return (&null_header
);
339 /* Set up number of compression bits */
340 arcfs_maxbits
= header_ptr
->maxbits
;
341 /*if (header_ptr->is_dir) header = &null_header; */
342 header_ptr
= header_ptr
->next
;