ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/1541t64.cpp
(Generate patch)

Comparing Frodo4/Src/1541t64.cpp (file contents):
Revision 1.5 by cebix, 2004-01-12T15:13:20Z vs.
Revision 1.7 by cebix, 2004-08-30T13:55:30Z

# Line 1 | Line 1
1   /*
2 < *  1541t64.cpp - 1541 emulation in .t64/LYNX file
2 > *  1541t64.cpp - 1541 emulation in archive-type files (.t64/LYNX/.p00)
3   *
4 < *  Frodo (C) 1994-1997,2002-2004 Christian Bauer
4 > *  Frodo (C) Copyright 1994-2001 Christian Bauer
5   *
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
# Line 19 | Line 19
19   */
20  
21   /*
22 < * Notes:
23 < * ------
22 > *  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   *
30 < *  - If any file is opened, the contents of the file in the
31 < *    .t64 file are copied into a temporary file which is used
32 < *    for reading. This is done to insert the load address.
33 < *  - C64 LYNX archives are also handled by these routines
34 < *
35 < * Incompatibilities:
31 < * ------------------
32 < *
33 < *  - Only read accesses possible
34 < *  - No "raw" directory reading
35 < *  - No relative/sequential/user files
36 < *  - Only "I" and "UJ" commands implemented
30 > *  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   */
37  
38   #include "sysdeps.h"
# Line 42 | Line 41
41   #include "IEC.h"
42   #include "Prefs.h"
43  
44 + #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  
57   /*
58   *  Constructor: Prepare emulation
59   */
60  
61 < T64Drive::T64Drive(IEC *iec, char *filepath) : Drive(iec)
61 > ArchDrive::ArchDrive(IEC *iec, const char *filepath) : Drive(iec), the_file(NULL)
62   {
52        the_file = NULL;
53        file_info = NULL;
54
55        Ready = false;
56        strcpy(orig_t64_name, filepath);
63          for (int i=0; i<16; i++)
64                  file[i] = NULL;
65 +        Reset();
66  
67 <        // Open .t64 file
68 <        open_close_t64_file(filepath);
62 <        if (the_file != NULL) {
63 <                Reset();
67 >        // Open archive file
68 >        if (change_arch(filepath))
69                  Ready = true;
65        }
70   }
71  
72  
# Line 70 | Line 74 | T64Drive::T64Drive(IEC *iec, char *filep
74   *  Destructor
75   */
76  
77 < T64Drive::~T64Drive()
77 > ArchDrive::~ArchDrive()
78   {
79 <        // Close .t64 file
80 <        open_close_t64_file("");
77 <
78 <        Ready = false;
79 < }
80 <
81 <
82 < /*
83 < *  Open/close the .t64/LYNX file
84 < */
85 <
86 < void T64Drive::open_close_t64_file(char *t64name)
87 < {
88 <        uint8 buf[64];
89 <        bool parsed_ok = false;
90 <
91 <        // Close old .t64, if open
92 <        if (the_file != NULL) {
79 >        // Close archive file
80 >        if (the_file) {
81                  close_all_channels();
82                  fclose(the_file);
95                the_file = NULL;
96                delete[] file_info;
97                file_info = NULL;
98        }
99
100        // Open new .t64 file
101        if (t64name[0]) {
102                if ((the_file = fopen(t64name, "rb")) != NULL) {
103
104                        // Check file ID
105                        fread(&buf, 64, 1, the_file);
106                        if (buf[0] == 0x43 && buf[1] == 0x36 && buf[2] == 0x34) {
107                                is_lynx = false;
108                                parsed_ok = parse_t64_file();
109                        } else if (buf[0x3c] == 0x4c && buf[0x3d] == 0x59 && buf[0x3e] == 0x4e && buf[0x3f] == 0x58) {
110                                is_lynx = true;
111                                parsed_ok = parse_lynx_file();
112                        }
113
114                        if (!parsed_ok) {
115                                fclose(the_file);
116                                the_file = NULL;
117                                delete[] file_info;
118                                file_info = NULL;
119                                return;
120                        }
121                }
83          }
84 +        Ready = false;
85   }
86  
87  
88   /*
89 < *  Parse .t64 file and construct FileInfo array
89 > *  Open the archive file
90   */
91  
92 < bool T64Drive::parse_t64_file(void)
92 > bool ArchDrive::change_arch(const char *path)
93   {
94 <        uint8 buf[32];
133 <        uint8 *buf2;
134 <        uint8 *p;
135 <        int max, i, j;
136 <
137 <        // Read header and get maximum number of files contained
138 <        fseek(the_file, 32, SEEK_SET);
139 <        fread(&buf, 32, 1, the_file);
140 <        max = (buf[3] << 8) | buf[2];
141 <
142 <        memcpy(dir_title, buf+8, 16);
143 <
144 <        // Allocate buffer for file records and read them
145 <        buf2 = new uint8[max*32];
146 <        fread(buf2, 32, max, the_file);
147 <
148 <        // Determine number of files contained
149 <        for (i=0, num_files=0; i<max; i++)
150 <                if (buf2[i*32] == 1)
151 <                        num_files++;
152 <
153 <        if (!num_files)
154 <                return false;
155 <
156 <        // Construct file information array
157 <        file_info = new FileInfo[num_files];
158 <        for (i=0, j=0; i<max; i++)
159 <                if (buf2[i*32] == 1) {
160 <                        memcpy(file_info[j].name, buf2+i*32+16, 16);
161 <
162 <                        // Strip trailing spaces
163 <                        file_info[j].name[16] = 0x20;
164 <                        p = file_info[j].name + 16;
165 <                        while (*p-- == 0x20) ;
166 <                        p[2] = 0;
94 >        FILE *new_file;
95  
96 <                        file_info[j].type = FTYPE_PRG;
97 <                        file_info[j].sa_lo = buf2[i*32+2];
98 <                        file_info[j].sa_hi = buf2[i*32+3];
99 <                        file_info[j].offset = (buf2[i*32+11] << 24) | (buf2[i*32+10] << 16) | (buf2[i*32+9] << 8) | buf2[i*32+8];
100 <                        file_info[j].length = ((buf2[i*32+5] << 8) | buf2[i*32+4]) - ((buf2[i*32+3] << 8) | buf2[i*32+2]);
101 <                        j++;
96 >        // 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  
116 <        delete[] buf2;
117 <        return true;
118 < }
119 <
120 <
121 < /*
122 < *  Parse LYNX file and construct FileInfo array
183 < */
184 <
185 < bool T64Drive::parse_lynx_file(void)
186 < {
187 <        uint8 *p;
188 <        int dir_blocks, cur_offset, num_blocks, last_block, i;
189 <        char type_char;
190 <
191 <        // Dummy directory title
192 <        strcpy(dir_title, "LYNX ARCHIVE    ");
193 <
194 <        // Read header and get number of directory blocks and files contained
195 <        fseek(the_file, 0x60, SEEK_SET);
196 <        fscanf(the_file, "%d", &dir_blocks);
197 <        while (fgetc(the_file) != 0x0d)
198 <                if (feof(the_file))
116 >                if (!parsed_ok) {
117 >                        fclose(new_file);
118 >                        if (the_file) {
119 >                                close_all_channels();
120 >                                fclose(the_file);
121 >                                the_file = NULL;
122 >                        }
123                          return false;
200        fscanf(the_file, "%d\015", &num_files);
201
202        // Construct file information array
203        file_info = new FileInfo[num_files];
204        cur_offset = dir_blocks * 254;
205        for (i=0; i<num_files; i++) {
206
207                // Read file name
208                fread(file_info[i].name, 16, 1, the_file);
209
210                // Strip trailing shift-spaces
211                file_info[i].name[16] = 0xa0;
212                p = (uint8 *)file_info[i].name + 16;
213                while (*p-- == 0xa0) ;
214                p[2] = 0;
215
216                // Read file length and type
217                fscanf(the_file, "\015%d\015%c\015%d\015", &num_blocks, &type_char, &last_block);
218
219                switch (type_char) {
220                        case 'S':
221                                file_info[i].type = FTYPE_SEQ;
222                                break;
223                        case 'U':
224                                file_info[i].type = FTYPE_USR;
225                                break;
226                        case 'R':
227                                file_info[i].type = FTYPE_REL;
228                                break;
229                        default:
230                                file_info[i].type = FTYPE_PRG;
231                                break;
124                  }
233                file_info[i].sa_lo = 0; // Only used for .t64 files
234                file_info[i].sa_hi = 0;
235                file_info[i].offset = cur_offset;
236                file_info[i].length = (num_blocks-1) * 254 + last_block;
125  
126 <                cur_offset += num_blocks * 254;
126 >                // 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 >                }
132 >                the_file = new_file;
133 >                return true;
134          }
135 <
241 <        return true;
135 >        return false;
136   }
137  
138  
# Line 246 | Line 140 | bool T64Drive::parse_lynx_file(void)
140   *  Open channel
141   */
142  
143 < uint8 T64Drive::Open(int channel, const uint8 *name, int name_len)
143 > uint8 ArchDrive::Open(int channel, const uint8 *name, int name_len)
144   {
145 +        D(bug("ArchDrive::Open channel %d, file %s\n", channel, name));
146 +
147          set_error(ERR_OK);
148  
149          // Channel 15: Execute file name as command
# Line 283 | Line 179 | uint8 T64Drive::Open(int channel, const
179   *  Open file
180   */
181  
182 < uint8 T64Drive::open_file(int channel, const uint8 *name, int name_len)
182 > uint8 ArchDrive::open_file(int channel, const uint8 *name, int name_len)
183   {
184          uint8 plain_name[NAMEBUF_LENGTH];
185          int plain_name_len;
186          int mode = FMODE_READ;
187 <        int type = FTYPE_PRG;
188 <        int rec_len;
187 >        int type = FTYPE_DEL;
188 >        int rec_len = 0;
189          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
# Line 327 | Line 223 | uint8 T64Drive::open_file(int channel, c
223                  if ((file[channel] = tmpfile()) != NULL) {
224  
225                          // Write load address (.t64 only)
226 <                        if (!is_lynx) {
226 >                        if (archive_type == TYPE_T64) {
227                                  fwrite(&file_info[num].sa_lo, 1, 1, file[channel]);
228                                  fwrite(&file_info[num].sa_hi, 1, 1, file[channel]);
229                          }
230  
231 <                        // Copy file contents from .t64 file to temp file
232 <                        uint8 *buf = new uint8[file_info[num].length];
231 >                        // Copy file contents from archive file to temp file
232 >                        uint8 *buf = new uint8[file_info[num].size];
233                          fseek(the_file, file_info[num].offset, SEEK_SET);
234 <                        fread(buf, file_info[num].length, 1, the_file);
235 <                        fwrite(buf, file_info[num].length, 1, file[channel]);
234 >                        fread(buf, file_info[num].size, 1, the_file);
235 >                        fwrite(buf, file_info[num].size, 1, file[channel]);
236                          rewind(file[channel]);
237                          delete[] buf;
238  
239                          if (mode == FMODE_READ) // Read and buffer first byte
240 <                                read_char[channel] = fgetc(file[channel]);
240 >                                read_char[channel] = getc(file[channel]);
241                  }
242          } else
243                  set_error(ERR_FILENOTFOUND);
# Line 368 | Line 264 | static bool match(const uint8 *p, int p_
264          return *n == 0;
265   }
266  
267 < bool T64Drive::find_first_file(const uint8 *pattern, int pattern_len, int &num)
267 > bool ArchDrive::find_first_file(const uint8 *pattern, int pattern_len, int &num)
268   {
269 <        for (int i=0; i<num_files; i++) {
270 <                if (match(pattern, pattern_len, file_info[i].name)) {
271 <                        num = i;
269 >        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                          return true;
377                }
273          }
274          return false;
275   }
# Line 384 | Line 279 | bool T64Drive::find_first_file(const uin
279   *  Open directory, create temporary file
280   */
281  
282 < uint8 T64Drive::open_directory(int channel, const uint8 *pattern, int pattern_len)
282 > uint8 ArchDrive::open_directory(int channel, const uint8 *pattern, int pattern_len)
283   {
284          // Special treatment for "$0"
285          if (pattern[0] == '0' && pattern_len == 1) {
# Line 411 | Line 306 | uint8 T64Drive::open_directory(int chann
306          fwrite(buf, 1, 32, file[channel]);
307  
308          // Create and write one line for every directory entry
309 <        for (int num=0; num<num_files; num++) {
309 >        vector<c64_dir_entry>::const_iterator i, end = file_info.end();
310 >        for (i = file_info.begin(); i != end; i++) {
311  
312                  // Include only files matching the pattern
313 <                if (pattern_len == 0 || match(pattern, pattern_len, file_info[num].name)) {
313 >                if (pattern_len == 0 || match(pattern, pattern_len, (uint8 *)i->name)) {
314  
315                          // Clear line with spaces and terminate with null byte
316                          memset(buf, ' ', 31);
317                          buf[31] = 0;
318  
319 <                        uint8 *p = buf;
319 >                        uint8 *p = (uint8 *)buf;
320                          *p++ = 0x01;    // Dummy line link
321                          *p++ = 0x01;
322  
323                          // Calculate size in blocks (254 bytes each)
324 <                        int n = (file_info[num].length + 254) / 254;
324 >                        int n = (i->size + 254) / 254;
325                          *p++ = n & 0xff;
326                          *p++ = (n >> 8) & 0xff;
327  
# Line 434 | Line 330 | uint8 T64Drive::open_directory(int chann
330                          if (n < 100) p++;       // Less than 100: add another space
331  
332                          // Convert and insert file name
437                        uint8 str[NAMEBUF_LENGTH];
438                        memcpy(str, file_info[num].name, 17);
333                          *p++ = '\"';
334                          uint8 *q = p;
335 <                        for (int i=0; i<16 && str[i]; i++)
336 <                                *q++ = str[i];
335 >                        for (int j=0; j<16 && i->name[j]; j++)
336 >                                *q++ = i->name[j];
337                          *q++ = '\"';
338                          p += 18;
339  
340                          // File type
341 <                        switch (file_info[num].type) {
342 <                                case FTYPE_PRG:
343 <                                        *p++ = 'P';
344 <                                        *p++ = 'R';
345 <                                        *p++ = 'G';
341 >                        switch (i->type) {
342 >                                case FTYPE_DEL:
343 >                                        *p++ = 'D';
344 >                                        *p++ = 'E';
345 >                                        *p++ = 'L';
346                                          break;
347                                  case FTYPE_SEQ:
348                                          *p++ = 'S';
349                                          *p++ = 'E';
350                                          *p++ = 'Q';
351                                          break;
352 +                                case FTYPE_PRG:
353 +                                        *p++ = 'P';
354 +                                        *p++ = 'R';
355 +                                        *p++ = 'G';
356 +                                        break;
357                                  case FTYPE_USR:
358                                          *p++ = 'U';
359                                          *p++ = 'S';
# Line 482 | Line 381 | uint8 T64Drive::open_directory(int chann
381  
382          // Rewind file for reading and read first byte
383          rewind(file[channel]);
384 <        read_char[channel] = fgetc(file[channel]);
384 >        read_char[channel] = getc(file[channel]);
385  
386          return ST_OK;
387   }
# Line 492 | Line 391 | uint8 T64Drive::open_directory(int chann
391   *  Close channel
392   */
393  
394 < uint8 T64Drive::Close(int channel)
394 > uint8 ArchDrive::Close(int channel)
395   {
396 +        D(bug("ArchDrive::Close channel %d\n", channel));
397 +
398          if (channel == 15) {
399                  close_all_channels();
400                  return ST_OK;
# Line 512 | Line 413 | uint8 T64Drive::Close(int channel)
413   *  Close all channels
414   */
415  
416 < void T64Drive::close_all_channels(void)
416 > void ArchDrive::close_all_channels(void)
417   {
418          for (int i=0; i<15; i++)
419                  Close(i);
# Line 525 | Line 426 | void T64Drive::close_all_channels(void)
426   *  Read from channel
427   */
428  
429 < uint8 T64Drive::Read(int channel, uint8 &byte)
429 > uint8 ArchDrive::Read(int channel, uint8 &byte)
430   {
431 <        int c;
431 >        D(bug("ArchDrive::Read channel %d\n", channel));
432  
433          // Channel 15: Error channel
434          if (channel == 15) {
435                  byte = *error_ptr++;
436  
437 <                if (byte != '\r')
437 >                if (byte != '\x0d')
438                          return ST_OK;
439                  else {  // End of message
440                          set_error(ERR_OK);
# Line 545 | Line 446 | uint8 T64Drive::Read(int channel, uint8
446  
447          // Get char from buffer and read next
448          byte = read_char[channel];
449 <        c = fgetc(file[channel]);
449 >        int c = getc(file[channel]);
450          if (c == EOF)
451                  return ST_EOF;
452          else {
# Line 559 | Line 460 | uint8 T64Drive::Read(int channel, uint8
460   *  Write to channel
461   */
462  
463 < uint8 T64Drive::Write(int channel, uint8 byte, bool eoi)
463 > uint8 ArchDrive::Write(int channel, uint8 byte, bool eoi)
464   {
465 +        D(bug("ArchDrive::Write channel %d, byte %02x, eoi %d\n", channel, byte, eoi));
466 +
467          // Channel 15: Collect chars and execute command on EOI
468          if (channel == 15) {
469 <                if (cmd_len >= 58)
469 >                if (cmd_len > 58) {
470 >                        set_error(ERR_SYNTAX32);
471                          return ST_TIMEOUT;
472 +                }
473                  
474                  cmd_buf[cmd_len++] = byte;
475  
# Line 591 | Line 496 | uint8 T64Drive::Write(int channel, uint8
496   // RENAME:new=old
497   //        ^   ^
498   // new_file   old_file
499 < void T64Drive::rename_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_file, int old_file_len)
499 > void ArchDrive::rename_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_file, int old_file_len)
500   {
501          // Check if destination file is already present
502          int num;
# Line 610 | Line 515 | void T64Drive::rename_cmd(const uint8 *n
515   }
516  
517   // INITIALIZE
518 < void T64Drive::initialize_cmd(void)
518 > void ArchDrive::initialize_cmd(void)
519   {
520          close_all_channels();
521   }
522  
523   // VALIDATE
524 < void T64Drive::validate_cmd(void)
524 > void ArchDrive::validate_cmd(void)
525   {
526   }
527  
# Line 625 | Line 530 | void T64Drive::validate_cmd(void)
530   *  Reset drive
531   */
532  
533 < void T64Drive::Reset(void)
533 > void ArchDrive::Reset(void)
534   {
535          close_all_channels();
536          cmd_len = 0;    
537          set_error(ERR_STARTUP);
538   }
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 +        if (memcmp(header, "C64S tape file", 14) == 0
549 +         || memcmp(header, "C64 tape image", 14) == 0
550 +         || memcmp(header, "C64S tape image", 15) == 0)
551 +                return true;
552 +        else
553 +                return false;
554 + }
555 +
556 + static bool is_lynx_header(const uint8 *header)
557 + {
558 +        return memcmp(header + 0x38, "USE LYNX", 8) == 0;
559 + }
560 +
561 + static bool is_p00_header(const uint8 *header)
562 + {
563 +        return memcmp(header, "C64File", 7) == 0;
564 + }
565 +
566 + bool IsArchFile(const char *path, const uint8 *header, long size)
567 + {
568 +        return is_t64_header(header) || is_lynx_header(header) || is_p00_header(header);
569 + }
570 +
571 +
572 + /*
573 + *  Read directory of archive file into (empty) c64_dir_entry vector,
574 + *  returns false on error
575 + */
576 +
577 + static bool parse_t64_file(FILE *f, vector<c64_dir_entry> &vec, char *dir_title)
578 + {
579 +        // Read header and get maximum number of files contained
580 +        fseek(f, 32, SEEK_SET);
581 +        uint8 buf[32];
582 +        fread(&buf, 32, 1, f);
583 +        int max = (buf[3] << 8) | buf[2];
584 +        if (max == 0)
585 +                max = 1;
586 +
587 +        memcpy(dir_title, buf+8, 16);
588 +
589 +        // Allocate buffer for file records and read them
590 +        uint8 *buf2 = new uint8[max * 32];
591 +        fread(buf2, 32, max, f);
592 +
593 +        // Determine number of files contained
594 +        int num_files = 0;
595 +        for (int i=0; i<max; i++)
596 +                if (buf2[i*32] == 1)
597 +                        num_files++;
598 +
599 +        if (!num_files) {
600 +                delete[] buf2;
601 +                return false;
602 +        }
603 +
604 +        // Construct file information array
605 +        vec.reserve(num_files);
606 +        uint8 *b = buf2;
607 +        for (int i=0; i<max; i++, b+=32) {
608 +                if (b[0] == 1) {
609 +
610 +                        // Convert file name (strip trailing spaces)
611 +                        uint8 name_buf[17];
612 +                        memcpy(name_buf, b + 16, 16);
613 +                        name_buf[16] = 0x20;
614 +                        uint8 *p = name_buf + 16;
615 +                        while (*p-- == 0x20) ;
616 +                        p[2] = 0;
617 +
618 +                        // Find file size and offset
619 +                        size_t size = ((b[5] << 8) | b[4]) - ((b[3] << 8) | b[2]);
620 +                        off_t offset = (b[11] << 24) | (b[10] << 16) | (b[9] << 8) | b[8];
621 +
622 +                        // Add entry
623 +                        vec.push_back(c64_dir_entry(name_buf, FTYPE_PRG, false, false, size, offset, b[2], b[3]));
624 +                }
625 +        }
626 +
627 +        delete[] buf2;
628 +        return true;
629 + }
630 +
631 + static bool parse_lynx_file(FILE *f, vector<c64_dir_entry> &vec, char *dir_title)
632 + {
633 +        // Dummy directory title
634 +        strcpy(dir_title, "LYNX ARCHIVE    ");
635 +
636 +        // Read header and get number of directory blocks and files contained
637 +        fseek(f, 0x60, SEEK_SET);
638 +        int dir_blocks;
639 +        fscanf(f, "%d", &dir_blocks);
640 +        while (getc(f) != 0x0d)
641 +                if (feof(f))
642 +                        return false;
643 +        int num_files;
644 +        fscanf(f, "%d\x0d", &num_files);
645 +
646 +        // Construct file information array
647 +        vec.reserve(num_files);
648 +        int cur_offset = dir_blocks * 254;
649 +        for (int i=0; i<num_files; i++) {
650 +
651 +                // Read and convert file name (strip trailing shift-spaces)
652 +                uint8 name_buf[17];
653 +                fread(name_buf, 16, 1, f);
654 +                name_buf[16] = 0xa0;
655 +                uint8 *p = name_buf + 16;
656 +                while (*p-- == 0xa0) ;
657 +                p[2] = 0;
658 +
659 +                // Read file length and type
660 +                int num_blocks, last_block;
661 +                char type_char;
662 +                fscanf(f, "\x0d%d\x0d%c\x0d%d\x0d", &num_blocks, &type_char, &last_block);
663 +                size_t size = (num_blocks - 1) * 254 + last_block - 1;
664 +
665 +                int type;
666 +                switch (type_char) {
667 +                        case 'S':
668 +                                type = FTYPE_SEQ;
669 +                                break;
670 +                        case 'U':
671 +                                type = FTYPE_USR;
672 +                                break;
673 +                        case 'R':
674 +                                type = FTYPE_REL;
675 +                                break;
676 +                        default:
677 +                                type = FTYPE_PRG;
678 +                                break;
679 +                }
680 +
681 +                // Read start address
682 +                long here = ftell(f);
683 +                uint8 sa_lo, sa_hi;
684 +                fseek(f, cur_offset, SEEK_SET);
685 +                fread(&sa_lo, 1, 1, f);
686 +                fread(&sa_hi, 1, 1, f);
687 +                fseek(f, here, SEEK_SET);
688 +
689 +                // Add entry
690 +                vec.push_back(c64_dir_entry(name_buf, type, false, false, size, cur_offset, sa_lo, sa_hi));
691 +
692 +                cur_offset += num_blocks * 254;
693 +        }
694 +
695 +        return true;
696 + }
697 +
698 + static bool parse_p00_file(FILE *f, vector<c64_dir_entry> &vec, char *dir_title)
699 + {
700 +        // Dummy directory title
701 +        strcpy(dir_title, ".P00 FILE       ");
702 +
703 +        // Contains only one file
704 +        vec.reserve(1);
705 +
706 +        // Read file name and start address
707 +        uint8 name_buf[17];
708 +        fseek(f, 8, SEEK_SET);
709 +        fread(name_buf, 17, 1, f);
710 +        name_buf[16] = 0;
711 +        uint8 sa_lo, sa_hi;
712 +        fseek(f, 26, SEEK_SET);
713 +        fread(&sa_lo, 1, 1, f);
714 +        fread(&sa_hi, 1, 1, f);
715 +
716 +        // Get file size
717 +        fseek(f, 0, SEEK_END);
718 +        size_t size = ftell(f) - 26;
719 +
720 +        // Add entry
721 +        vec.push_back(c64_dir_entry(name_buf, FTYPE_PRG, false, false, size, 26, sa_lo, sa_hi));
722 +        return true;
723 + }
724 +
725 + bool ReadArchDirectory(const char *path, vector<c64_dir_entry> &vec)
726 + {
727 +        // Open file
728 +        FILE *f = fopen(path, "rb");
729 +        if (f) {
730 +
731 +                // Read header
732 +                uint8 header[64];
733 +                fread(header, 1, sizeof(header), f);
734 +
735 +                // Determine archive type and parse archive
736 +                bool result = false;
737 +                char dir_title[16];
738 +                if (is_t64_header(header))
739 +                        result = parse_t64_file(f, vec, dir_title);
740 +                else if (is_lynx_header(header))
741 +                        result = parse_lynx_file(f, vec, dir_title);
742 +                else if (is_p00_header(header))
743 +                        result = parse_p00_file(f, vec, dir_title);
744 +
745 +                fclose(f);
746 +                return result;
747 +        } else
748 +                return false;
749 + }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines