ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/1541t64.cpp
Revision: 1.6
Committed: 2004-01-14T16:54:46Z (20 years, 9 months ago) by cebix
Branch: MAIN
Changes since 1.5: +331 -220 lines
Log Message:
- backported T64 code from Frodo V5, handles .P00 files
- T64Drive renamed to ArchDrive
- PETSCII characters are uint8s, not chars

File Contents

# User Rev Content
1 cebix 1.1 /*
2 cebix 1.6 * 1541t64.cpp - 1541 emulation in archive-type files (.t64/LYNX/.p00)
3 cebix 1.1 *
4 cebix 1.6 * Frodo (C) Copyright 1994-2001 Christian Bauer
5 cebix 1.1 *
6     * This program is free software; you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation; either version 2 of the License, or
9     * (at your option) any later version.
10     *
11     * This program is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with this program; if not, write to the Free Software
18     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19     */
20    
21     /*
22 cebix 1.6 * NOTES:
23     * - This module handles access to files inside (uncompressed) archives
24     * and makes the archive look like a disk. It supports C64S tape images
25     * (.t64), C64 LYNX archives and .p00 files.
26     * - If any file is opened, the contents of the file in the archive file are
27     * copied into a temporary file which is used for reading. This is done
28     * to insert the load address.
29 cebix 1.1 *
30 cebix 1.6 * Incompatibilities:
31     * - Only read accesses possible
32     * - No "raw" directory reading
33     * - No relative/sequential/user files
34     * - Unimplemented commands: B-P, M-R, M-W, C, S, P, N
35     * - Impossible to implement: B-R, B-W, B-E, B-A, B-F, M-E
36 cebix 1.1 */
37    
38     #include "sysdeps.h"
39    
40     #include "1541t64.h"
41     #include "IEC.h"
42     #include "Prefs.h"
43    
44 cebix 1.6 #define DEBUG 0
45     #include "debug.h"
46    
47    
48     // Prototypes
49     static bool is_t64_header(const uint8 *header);
50     static bool is_lynx_header(const uint8 *header);
51     static bool is_p00_header(const uint8 *header);
52     static bool parse_t64_file(FILE *f, vector<c64_dir_entry> &vec, char *dir_title);
53     static bool parse_lynx_file(FILE *f, vector<c64_dir_entry> &vec, char *dir_title);
54     static bool parse_p00_file(FILE *f, vector<c64_dir_entry> &vec, char *dir_title);
55    
56 cebix 1.1
57     /*
58     * Constructor: Prepare emulation
59     */
60    
61 cebix 1.6 ArchDrive::ArchDrive(IEC *iec, const char *filepath) : Drive(iec), the_file(NULL)
62 cebix 1.1 {
63     for (int i=0; i<16; i++)
64     file[i] = NULL;
65 cebix 1.6 Reset();
66 cebix 1.1
67 cebix 1.6 // Open archive file
68     if (change_arch(filepath))
69 cebix 1.1 Ready = true;
70     }
71    
72    
73     /*
74     * Destructor
75     */
76    
77 cebix 1.6 ArchDrive::~ArchDrive()
78 cebix 1.1 {
79 cebix 1.6 // Close archive file
80     if (the_file) {
81     close_all_channels();
82     fclose(the_file);
83     }
84 cebix 1.1 Ready = false;
85     }
86    
87    
88     /*
89 cebix 1.6 * Open the archive file
90 cebix 1.1 */
91    
92 cebix 1.6 bool ArchDrive::change_arch(const char *path)
93 cebix 1.1 {
94 cebix 1.6 FILE *new_file;
95 cebix 1.1
96 cebix 1.6 // Open new archive file
97     if ((new_file = fopen(path, "rb")) != NULL) {
98    
99     file_info.clear();
100    
101     // Read header, determine archive type and parse archive contents
102     uint8 header[64];
103     fread(header, 1, 64, new_file);
104     bool parsed_ok = false;
105     if (is_t64_header(header)) {
106     archive_type = TYPE_T64;
107     parsed_ok = parse_t64_file(new_file, file_info, dir_title);
108     } else if (is_lynx_header(header)) {
109     archive_type = TYPE_LYNX;
110     parsed_ok = parse_lynx_file(new_file, file_info, dir_title);
111     } else if (is_p00_header(header)) {
112     archive_type = TYPE_P00;
113     parsed_ok = parse_p00_file(new_file, file_info, dir_title);
114     }
115 cebix 1.1
116 cebix 1.6 if (!parsed_ok) {
117     fclose(new_file);
118     if (the_file) {
119     close_all_channels();
120 cebix 1.1 fclose(the_file);
121     the_file = NULL;
122     }
123 cebix 1.6 return false;
124 cebix 1.1 }
125    
126 cebix 1.6 // Close old archive if open, and set new file
127     if (the_file) {
128     close_all_channels();
129     fclose(the_file);
130     the_file = NULL;
131 cebix 1.1 }
132 cebix 1.6 the_file = new_file;
133     return true;
134 cebix 1.1 }
135 cebix 1.6 return false;
136 cebix 1.1 }
137    
138    
139     /*
140     * Open channel
141     */
142    
143 cebix 1.6 uint8 ArchDrive::Open(int channel, const uint8 *name, int name_len)
144 cebix 1.1 {
145 cebix 1.6 D(bug("ArchDrive::Open channel %d, file %s\n", channel, name));
146    
147 cebix 1.1 set_error(ERR_OK);
148    
149     // Channel 15: Execute file name as command
150     if (channel == 15) {
151 cebix 1.3 execute_cmd(name, name_len);
152 cebix 1.1 return ST_OK;
153     }
154    
155     // Close previous file if still open
156     if (file[channel]) {
157     fclose(file[channel]);
158     file[channel] = NULL;
159     }
160    
161 cebix 1.3 if (name[0] == '#') {
162 cebix 1.1 set_error(ERR_NOCHANNEL);
163     return ST_OK;
164     }
165    
166     if (the_file == NULL) {
167     set_error(ERR_NOTREADY);
168     return ST_OK;
169     }
170    
171 cebix 1.3 if (name[0] == '$')
172     return open_directory(channel, name + 1, name_len - 1);
173 cebix 1.1
174 cebix 1.3 return open_file(channel, name, name_len);
175 cebix 1.1 }
176    
177    
178     /*
179     * Open file
180     */
181    
182 cebix 1.6 uint8 ArchDrive::open_file(int channel, const uint8 *name, int name_len)
183 cebix 1.1 {
184 cebix 1.3 uint8 plain_name[NAMEBUF_LENGTH];
185     int plain_name_len;
186     int mode = FMODE_READ;
187 cebix 1.6 int type = FTYPE_DEL;
188     int rec_len = 0;
189 cebix 1.3 parse_file_name(name, name_len, plain_name, plain_name_len, mode, type, rec_len);
190    
191     // Channel 0 is READ, channel 1 is WRITE
192     if (channel == 0 || channel == 1) {
193     mode = channel ? FMODE_WRITE : FMODE_READ;
194     if (type == FTYPE_DEL)
195     type = FTYPE_PRG;
196     }
197    
198     bool writing = (mode == FMODE_WRITE || mode == FMODE_APPEND);
199    
200     // Wildcards are only allowed on reading
201     if (writing && (strchr((const char *)plain_name, '*') || strchr((const char *)plain_name, '?'))) {
202     set_error(ERR_SYNTAX33);
203     return ST_OK;
204 cebix 1.1 }
205    
206     // Allow only read accesses
207 cebix 1.3 if (writing) {
208 cebix 1.1 set_error(ERR_WRITEPROTECT);
209     return ST_OK;
210     }
211    
212 cebix 1.3 // Relative files are not supported
213     if (type == FTYPE_REL) {
214     set_error(ERR_UNIMPLEMENTED);
215     return ST_OK;
216     }
217    
218 cebix 1.1 // Find file
219 cebix 1.3 int num;
220     if (find_first_file(plain_name, plain_name_len, num)) {
221 cebix 1.1
222     // Open temporary file
223     if ((file[channel] = tmpfile()) != NULL) {
224    
225     // Write load address (.t64 only)
226 cebix 1.6 if (archive_type == TYPE_T64) {
227 cebix 1.1 fwrite(&file_info[num].sa_lo, 1, 1, file[channel]);
228     fwrite(&file_info[num].sa_hi, 1, 1, file[channel]);
229     }
230    
231 cebix 1.6 // Copy file contents from archive file to temp file
232     uint8 *buf = new uint8[file_info[num].size];
233 cebix 1.1 fseek(the_file, file_info[num].offset, SEEK_SET);
234 cebix 1.6 fread(buf, file_info[num].size, 1, the_file);
235     fwrite(buf, file_info[num].size, 1, file[channel]);
236 cebix 1.1 rewind(file[channel]);
237     delete[] buf;
238    
239 cebix 1.3 if (mode == FMODE_READ) // Read and buffer first byte
240 cebix 1.6 read_char[channel] = getc(file[channel]);
241 cebix 1.1 }
242     } else
243     set_error(ERR_FILENOTFOUND);
244    
245     return ST_OK;
246     }
247    
248    
249     /*
250     * Find first file matching wildcard pattern
251     */
252    
253     // Return true if name 'n' matches pattern 'p'
254 cebix 1.3 static bool match(const uint8 *p, int p_len, const uint8 *n)
255 cebix 1.1 {
256 cebix 1.3 while (p_len-- > 0) {
257 cebix 1.1 if (*p == '*') // Wildcard '*' matches all following characters
258     return true;
259     if ((*p != *n) && (*p != '?')) // Wildcard '?' matches single character
260     return false;
261     p++; n++;
262 cebix 1.3 }
263 cebix 1.1
264 cebix 1.3 return *n == 0;
265 cebix 1.1 }
266    
267 cebix 1.6 bool ArchDrive::find_first_file(const uint8 *pattern, int pattern_len, int &num)
268 cebix 1.1 {
269 cebix 1.6 vector<c64_dir_entry>::const_iterator i, end = file_info.end();
270     for (i = file_info.begin(), num = 0; i != end; i++, num++) {
271     if (match(pattern, pattern_len, (uint8 *)i->name))
272 cebix 1.1 return true;
273 cebix 1.3 }
274 cebix 1.1 return false;
275     }
276    
277    
278     /*
279     * Open directory, create temporary file
280     */
281    
282 cebix 1.6 uint8 ArchDrive::open_directory(int channel, const uint8 *pattern, int pattern_len)
283 cebix 1.1 {
284     // Special treatment for "$0"
285 cebix 1.3 if (pattern[0] == '0' && pattern_len == 1) {
286     pattern++;
287     pattern_len--;
288     }
289 cebix 1.1
290 cebix 1.3 // Skip everything before the ':' in the pattern
291     uint8 *t = (uint8 *)memchr(pattern, ':', pattern_len);
292     if (t) {
293     t++;
294     pattern_len -= t - pattern;
295     pattern = t;
296     }
297 cebix 1.1
298     // Create temporary file
299     if ((file[channel] = tmpfile()) == NULL)
300     return ST_OK;
301    
302     // Create directory title
303 cebix 1.3 uint8 buf[] = "\001\004\001\001\0\0\022\042 \042 00 2A";
304     for (int i=0; i<16 && dir_title[i]; i++)
305     buf[i + 8] = dir_title[i];
306 cebix 1.1 fwrite(buf, 1, 32, file[channel]);
307    
308     // Create and write one line for every directory entry
309 cebix 1.6 vector<c64_dir_entry>::const_iterator i, end = file_info.end();
310     for (i = file_info.begin(); i != end; i++) {
311 cebix 1.1
312     // Include only files matching the pattern
313 cebix 1.6 if (pattern_len == 0 || match(pattern, pattern_len, (uint8 *)i->name)) {
314 cebix 1.1
315     // Clear line with spaces and terminate with null byte
316     memset(buf, ' ', 31);
317     buf[31] = 0;
318    
319 cebix 1.6 uint8 *p = (uint8 *)buf;
320 cebix 1.1 *p++ = 0x01; // Dummy line link
321     *p++ = 0x01;
322    
323     // Calculate size in blocks (254 bytes each)
324 cebix 1.6 int n = (i->size + 254) / 254;
325 cebix 1.3 *p++ = n & 0xff;
326     *p++ = (n >> 8) & 0xff;
327 cebix 1.1
328     p++;
329 cebix 1.3 if (n < 10) p++; // Less than 10: add one space
330     if (n < 100) p++; // Less than 100: add another space
331 cebix 1.1
332     // Convert and insert file name
333     *p++ = '\"';
334 cebix 1.3 uint8 *q = p;
335 cebix 1.6 for (int j=0; j<16 && i->name[j]; j++)
336     *q++ = i->name[j];
337 cebix 1.1 *q++ = '\"';
338     p += 18;
339    
340     // File type
341 cebix 1.6 switch (i->type) {
342     case FTYPE_DEL:
343     *p++ = 'D';
344     *p++ = 'E';
345     *p++ = 'L';
346 cebix 1.1 break;
347     case FTYPE_SEQ:
348     *p++ = 'S';
349     *p++ = 'E';
350     *p++ = 'Q';
351     break;
352 cebix 1.6 case FTYPE_PRG:
353     *p++ = 'P';
354     *p++ = 'R';
355     *p++ = 'G';
356     break;
357 cebix 1.1 case FTYPE_USR:
358     *p++ = 'U';
359     *p++ = 'S';
360     *p++ = 'R';
361     break;
362     case FTYPE_REL:
363     *p++ = 'R';
364     *p++ = 'E';
365     *p++ = 'L';
366     break;
367     default:
368     *p++ = '?';
369     *p++ = '?';
370     *p++ = '?';
371     break;
372     }
373    
374     // Write line
375     fwrite(buf, 1, 32, file[channel]);
376     }
377     }
378    
379     // Final line
380     fwrite("\001\001\0\0BLOCKS FREE. \0\0", 1, 32, file[channel]);
381    
382     // Rewind file for reading and read first byte
383     rewind(file[channel]);
384 cebix 1.6 read_char[channel] = getc(file[channel]);
385 cebix 1.1
386     return ST_OK;
387     }
388    
389    
390     /*
391     * Close channel
392     */
393    
394 cebix 1.6 uint8 ArchDrive::Close(int channel)
395 cebix 1.1 {
396 cebix 1.6 D(bug("ArchDrive::Close channel %d\n", channel));
397    
398 cebix 1.1 if (channel == 15) {
399     close_all_channels();
400     return ST_OK;
401     }
402    
403     if (file[channel]) {
404     fclose(file[channel]);
405     file[channel] = NULL;
406     }
407    
408     return ST_OK;
409     }
410    
411    
412     /*
413     * Close all channels
414     */
415    
416 cebix 1.6 void ArchDrive::close_all_channels(void)
417 cebix 1.1 {
418     for (int i=0; i<15; i++)
419     Close(i);
420    
421     cmd_len = 0;
422     }
423    
424    
425     /*
426     * Read from channel
427     */
428    
429 cebix 1.6 uint8 ArchDrive::Read(int channel, uint8 &byte)
430 cebix 1.1 {
431 cebix 1.6 D(bug("ArchDrive::Read channel %d\n", channel));
432 cebix 1.1
433     // Channel 15: Error channel
434     if (channel == 15) {
435 cebix 1.4 byte = *error_ptr++;
436 cebix 1.1
437 cebix 1.6 if (byte != '\x0d')
438 cebix 1.1 return ST_OK;
439     else { // End of message
440     set_error(ERR_OK);
441     return ST_EOF;
442     }
443     }
444    
445     if (!file[channel]) return ST_READ_TIMEOUT;
446    
447     // Get char from buffer and read next
448 cebix 1.4 byte = read_char[channel];
449 cebix 1.6 int c = getc(file[channel]);
450 cebix 1.1 if (c == EOF)
451     return ST_EOF;
452     else {
453     read_char[channel] = c;
454     return ST_OK;
455     }
456     }
457    
458    
459     /*
460     * Write to channel
461     */
462    
463 cebix 1.6 uint8 ArchDrive::Write(int channel, uint8 byte, bool eoi)
464 cebix 1.1 {
465 cebix 1.6 D(bug("ArchDrive::Write channel %d, byte %02x, eoi %d\n", channel, byte, eoi));
466    
467 cebix 1.1 // Channel 15: Collect chars and execute command on EOI
468     if (channel == 15) {
469 cebix 1.6 if (cmd_len > 58) {
470     set_error(ERR_SYNTAX32);
471 cebix 1.1 return ST_TIMEOUT;
472 cebix 1.6 }
473 cebix 1.1
474 cebix 1.3 cmd_buf[cmd_len++] = byte;
475 cebix 1.1
476     if (eoi) {
477 cebix 1.3 execute_cmd(cmd_buf, cmd_len);
478 cebix 1.1 cmd_len = 0;
479     }
480     return ST_OK;
481     }
482    
483     if (!file[channel])
484     set_error(ERR_FILENOTOPEN);
485     else
486     set_error(ERR_WRITEPROTECT);
487    
488     return ST_TIMEOUT;
489     }
490    
491    
492     /*
493 cebix 1.3 * Execute drive commands
494 cebix 1.1 */
495    
496 cebix 1.3 // RENAME:new=old
497     // ^ ^
498     // new_file old_file
499 cebix 1.6 void ArchDrive::rename_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_file, int old_file_len)
500 cebix 1.1 {
501 cebix 1.3 // Check if destination file is already present
502     int num;
503     if (find_first_file(new_file, new_file_len, num)) {
504     set_error(ERR_FILEEXISTS);
505     return;
506     }
507 cebix 1.1
508 cebix 1.3 // Check if source file is present
509     if (!find_first_file(old_file, old_file_len, num)) {
510     set_error(ERR_FILENOTFOUND);
511     return;
512     }
513 cebix 1.1
514 cebix 1.3 set_error(ERR_WRITEPROTECT);
515 cebix 1.1 }
516    
517 cebix 1.3 // INITIALIZE
518 cebix 1.6 void ArchDrive::initialize_cmd(void)
519 cebix 1.1 {
520     close_all_channels();
521 cebix 1.3 }
522 cebix 1.1
523 cebix 1.3 // VALIDATE
524 cebix 1.6 void ArchDrive::validate_cmd(void)
525 cebix 1.3 {
526 cebix 1.1 }
527    
528    
529     /*
530     * Reset drive
531     */
532    
533 cebix 1.6 void ArchDrive::Reset(void)
534 cebix 1.1 {
535     close_all_channels();
536     cmd_len = 0;
537     set_error(ERR_STARTUP);
538 cebix 1.6 }
539    
540    
541     /*
542     * Check whether file with given header (64 bytes) and size looks like one
543     * of the file types supported by this module
544     */
545    
546     static bool is_t64_header(const uint8 *header)
547     {
548     return memcmp(header, "C64S tape file", 14) == 0;
549     }
550    
551     static bool is_lynx_header(const uint8 *header)
552     {
553     return memcmp(header + 0x38, "USE LYNX", 8) == 0;
554     }
555    
556     static bool is_p00_header(const uint8 *header)
557     {
558     return memcmp(header, "C64File", 7) == 0;
559     }
560    
561     bool IsArchFile(const char *path, const uint8 *header, long size)
562     {
563     return is_t64_header(header) || is_lynx_header(header) || is_p00_header(header);
564     }
565    
566    
567     /*
568     * Read directory of archive file into (empty) c64_dir_entry vector,
569     * returns false on error
570     */
571    
572     static bool parse_t64_file(FILE *f, vector<c64_dir_entry> &vec, char *dir_title)
573     {
574     // Read header and get maximum number of files contained
575     fseek(f, 32, SEEK_SET);
576     uint8 buf[32];
577     fread(&buf, 32, 1, f);
578     int max = (buf[3] << 8) | buf[2];
579     if (max == 0)
580     max = 1;
581    
582     memcpy(dir_title, buf+8, 16);
583    
584     // Allocate buffer for file records and read them
585     uint8 *buf2 = new uint8[max * 32];
586     fread(buf2, 32, max, f);
587    
588     // Determine number of files contained
589     int num_files = 0;
590     for (int i=0; i<max; i++)
591     if (buf2[i*32] == 1)
592     num_files++;
593    
594     if (!num_files) {
595     delete[] buf2;
596     return false;
597     }
598    
599     // Construct file information array
600     vec.reserve(num_files);
601     uint8 *b = buf2;
602     for (int i=0; i<max; i++, b+=32) {
603     if (b[0] == 1) {
604    
605     // Convert file name (strip trailing spaces)
606     uint8 name_buf[17];
607     memcpy(name_buf, b + 16, 16);
608     name_buf[16] = 0x20;
609     uint8 *p = name_buf + 16;
610     while (*p-- == 0x20) ;
611     p[2] = 0;
612    
613     // Find file size and offset
614     size_t size = ((b[5] << 8) | b[4]) - ((b[3] << 8) | b[2]);
615     off_t offset = (b[11] << 24) | (b[10] << 16) | (b[9] << 8) | b[8];
616    
617     // Add entry
618     vec.push_back(c64_dir_entry(name_buf, FTYPE_PRG, false, false, size, offset, b[2], b[3]));
619     }
620     }
621    
622     delete[] buf2;
623     return true;
624     }
625    
626     static bool parse_lynx_file(FILE *f, vector<c64_dir_entry> &vec, char *dir_title)
627     {
628     // Dummy directory title
629     strcpy(dir_title, "LYNX ARCHIVE ");
630    
631     // Read header and get number of directory blocks and files contained
632     fseek(f, 0x60, SEEK_SET);
633     int dir_blocks;
634     fscanf(f, "%d", &dir_blocks);
635     while (getc(f) != 0x0d)
636     if (feof(f))
637     return false;
638     int num_files;
639     fscanf(f, "%d\x0d", &num_files);
640    
641     // Construct file information array
642     vec.reserve(num_files);
643     int cur_offset = dir_blocks * 254;
644     for (int i=0; i<num_files; i++) {
645    
646     // Read and convert file name (strip trailing shift-spaces)
647     uint8 name_buf[17];
648     fread(name_buf, 16, 1, f);
649     name_buf[16] = 0xa0;
650     uint8 *p = name_buf + 16;
651     while (*p-- == 0xa0) ;
652     p[2] = 0;
653    
654     // Read file length and type
655     int num_blocks, last_block;
656     char type_char;
657     fscanf(f, "\x0d%d\x0d%c\x0d%d\x0d", &num_blocks, &type_char, &last_block);
658     size_t size = (num_blocks - 1) * 254 + last_block - 1;
659    
660     int type;
661     switch (type_char) {
662     case 'S':
663     type = FTYPE_SEQ;
664     break;
665     case 'U':
666     type = FTYPE_USR;
667     break;
668     case 'R':
669     type = FTYPE_REL;
670     break;
671     default:
672     type = FTYPE_PRG;
673     break;
674     }
675    
676     // Read start address
677     long here = ftell(f);
678     uint8 sa_lo, sa_hi;
679     fseek(f, cur_offset, SEEK_SET);
680     fread(&sa_lo, 1, 1, f);
681     fread(&sa_hi, 1, 1, f);
682     fseek(f, here, SEEK_SET);
683    
684     // Add entry
685     vec.push_back(c64_dir_entry(name_buf, type, false, false, size, cur_offset, sa_lo, sa_hi));
686    
687     cur_offset += num_blocks * 254;
688     }
689    
690     return true;
691     }
692    
693     static bool parse_p00_file(FILE *f, vector<c64_dir_entry> &vec, char *dir_title)
694     {
695     // Dummy directory title
696     strcpy(dir_title, ".P00 FILE ");
697    
698     // Contains only one file
699     vec.reserve(1);
700    
701     // Read file name and start address
702     uint8 name_buf[17];
703     fseek(f, 8, SEEK_SET);
704     fread(name_buf, 17, 1, f);
705     name_buf[16] = 0;
706     uint8 sa_lo, sa_hi;
707     fseek(f, 26, SEEK_SET);
708     fread(&sa_lo, 1, 1, f);
709     fread(&sa_hi, 1, 1, f);
710    
711     // Get file size
712     fseek(f, 0, SEEK_END);
713     size_t size = ftell(f) - 26;
714    
715     // Add entry
716     vec.push_back(c64_dir_entry(name_buf, FTYPE_PRG, false, false, size, 26, sa_lo, sa_hi));
717     return true;
718     }
719    
720     bool ReadArchDirectory(const char *path, vector<c64_dir_entry> &vec)
721     {
722     // Open file
723     FILE *f = fopen(path, "rb");
724     if (f) {
725    
726     // Read header
727     uint8 header[64];
728     fread(header, 1, sizeof(header), f);
729    
730     // Determine archive type and parse archive
731     bool result = false;
732     char dir_title[16];
733     if (is_t64_header(header))
734     result = parse_t64_file(f, vec, dir_title);
735     else if (is_lynx_header(header))
736     result = parse_lynx_file(f, vec, dir_title);
737     else if (is_p00_header(header))
738     result = parse_p00_file(f, vec, dir_title);
739    
740     fclose(f);
741     return result;
742     } else
743     return false;
744 cebix 1.1 }