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.2 by cebix, 2003-07-01T17:51:17Z vs.
Revision 1.6 by cebix, 2004-01-14T16:54:46Z

# 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-2003 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  
46 // Access modes
47 enum {
48        FMODE_READ, FMODE_WRITE, FMODE_APPEND
49 };
50
51 // File types
52 enum {
53        FTYPE_PRG, FTYPE_SEQ, FTYPE_USR, FTYPE_REL
54 };
47  
48   // Prototypes
49 < static bool match(char *p, char *n);
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   {
66        the_file = NULL;
67        file_info = NULL;
68
69        Ready = false;
70        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);
76 <        if (the_file != NULL) {
77 <                Reset();
67 >        // Open archive file
68 >        if (change_arch(filepath))
69                  Ready = true;
79        }
70   }
71  
72  
# Line 84 | 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("");
91 <
92 <        Ready = false;
93 < }
94 <
95 <
96 < /*
97 < *  Open/close the .t64/LYNX file
98 < */
99 <
100 < void T64Drive::open_close_t64_file(char *t64name)
101 < {
102 <        uint8 buf[64];
103 <        bool parsed_ok = false;
104 <
105 <        // Close old .t64, if open
106 <        if (the_file != NULL) {
79 >        // Close archive file
80 >        if (the_file) {
81                  close_all_channels();
82                  fclose(the_file);
109                the_file = NULL;
110                delete[] file_info;
111                file_info = NULL;
112        }
113
114        // Open new .t64 file
115        if (t64name[0]) {
116                if ((the_file = fopen(t64name, "rb")) != NULL) {
117
118                        // Check file ID
119                        fread(&buf, 64, 1, the_file);
120                        if (buf[0] == 0x43 && buf[1] == 0x36 && buf[2] == 0x34) {
121                                is_lynx = false;
122                                parsed_ok = parse_t64_file();
123                        } else if (buf[0x3c] == 0x4c && buf[0x3d] == 0x59 && buf[0x3e] == 0x4e && buf[0x3f] == 0x58) {
124                                is_lynx = true;
125                                parsed_ok = parse_lynx_file();
126                        }
127
128                        if (!parsed_ok) {
129                                fclose(the_file);
130                                the_file = NULL;
131                                delete[] file_info;
132                                file_info = NULL;
133                                return;
134                        }
135                }
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];
147 <        uint8 *buf2;
148 <        char *p;
149 <        int max, i, j;
150 <
151 <        // Read header and get maximum number of files contained
152 <        fseek(the_file, 32, SEEK_SET);
153 <        fread(&buf, 32, 1, the_file);
154 <        max = (buf[3] << 8) | buf[2];
155 <
156 <        memcpy(dir_title, buf+8, 16);
157 <
158 <        // Allocate buffer for file records and read them
159 <        buf2 = new uint8[max*32];
160 <        fread(buf2, 32, max, the_file);
161 <
162 <        // Determine number of files contained
163 <        for (i=0, num_files=0; i<max; i++)
164 <                if (buf2[i*32] == 1)
165 <                        num_files++;
166 <
167 <        if (!num_files)
168 <                return false;
94 >        FILE *new_file;
95  
96 <        // Construct file information array
97 <        file_info = new FileInfo[num_files];
98 <        for (i=0, j=0; i<max; i++)
99 <                if (buf2[i*32] == 1) {
100 <                        memcpy(file_info[j].name, buf2+i*32+16, 16);
101 <
102 <                        // Strip trailing spaces
103 <                        file_info[j].name[16] = 0x20;
104 <                        p = file_info[j].name + 16;
105 <                        while (*p-- == 0x20) ;
106 <                        p[2] = 0;
107 <
108 <                        file_info[j].type = FTYPE_PRG;
109 <                        file_info[j].sa_lo = buf2[i*32+2];
110 <                        file_info[j].sa_hi = buf2[i*32+3];
111 <                        file_info[j].offset = (buf2[i*32+11] << 24) | (buf2[i*32+10] << 16) | (buf2[i*32+9] << 8) | buf2[i*32+8];
112 <                        file_info[j].length = ((buf2[i*32+5] << 8) | buf2[i*32+4]) - ((buf2[i*32+3] << 8) | buf2[i*32+2]);
113 <                        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
197 < */
198 <
199 < bool T64Drive::parse_lynx_file(void)
200 < {
201 <        uint8 *p;
202 <        int dir_blocks, cur_offset, num_blocks, last_block, i;
203 <        char type_char;
204 <
205 <        // Dummy directory title
206 <        strcpy(dir_title, "LYNX ARCHIVE    ");
207 <
208 <        // Read header and get number of directory blocks and files contained
209 <        fseek(the_file, 0x60, SEEK_SET);
210 <        fscanf(the_file, "%d", &dir_blocks);
211 <        while (fgetc(the_file) != 0x0d)
212 <                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;
214        fscanf(the_file, "%d\015", &num_files);
215
216        // Construct file information array
217        file_info = new FileInfo[num_files];
218        cur_offset = dir_blocks * 254;
219        for (i=0; i<num_files; i++) {
220
221                // Read file name
222                fread(file_info[i].name, 16, 1, the_file);
223
224                // Strip trailing shift-spaces
225                file_info[i].name[16] = 0xa0;
226                p = (uint8 *)file_info[i].name + 16;
227                while (*p-- == 0xa0) ;
228                p[2] = 0;
229
230                // Read file length and type
231                fscanf(the_file, "\015%d\015%c\015%d\015", &num_blocks, &type_char, &last_block);
232
233                switch (type_char) {
234                        case 'S':
235                                file_info[i].type = FTYPE_SEQ;
236                                break;
237                        case 'U':
238                                file_info[i].type = FTYPE_USR;
239                                break;
240                        case 'R':
241                                file_info[i].type = FTYPE_REL;
242                                break;
243                        default:
244                                file_info[i].type = FTYPE_PRG;
245                                break;
124                  }
247                file_info[i].sa_lo = 0; // Only used for .t64 files
248                file_info[i].sa_hi = 0;
249                file_info[i].offset = cur_offset;
250                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 <
255 <        return true;
135 >        return false;
136   }
137  
138  
# Line 260 | Line 140 | bool T64Drive::parse_lynx_file(void)
140   *  Open channel
141   */
142  
143 < uint8 T64Drive::Open(int channel, char *filename)
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
150          if (channel == 15) {
151 <                execute_command(filename);
151 >                execute_cmd(name, name_len);
152                  return ST_OK;
153          }
154  
# Line 276 | Line 158 | uint8 T64Drive::Open(int channel, char *
158                  file[channel] = NULL;
159          }
160  
161 <        if (filename[0] == '#') {
161 >        if (name[0] == '#') {
162                  set_error(ERR_NOCHANNEL);
163                  return ST_OK;
164          }
# Line 286 | Line 168 | uint8 T64Drive::Open(int channel, char *
168                  return ST_OK;
169          }
170  
171 <        if (filename[0] == '$')
172 <                return open_directory(channel, filename+1);
171 >        if (name[0] == '$')
172 >                return open_directory(channel, name + 1, name_len - 1);
173  
174 <        return open_file(channel, filename);
174 >        return open_file(channel, name, name_len);
175   }
176  
177  
# Line 297 | Line 179 | uint8 T64Drive::Open(int channel, char *
179   *  Open file
180   */
181  
182 < uint8 T64Drive::open_file(int channel, char *filename)
182 > uint8 ArchDrive::open_file(int channel, const uint8 *name, int name_len)
183   {
184 <        char plainname[NAMEBUF_LENGTH];
185 <        int filemode = FMODE_READ;
186 <        int filetype = FTYPE_PRG;
187 <        int num;
188 <
189 <        convert_filename(filename, plainname, &filemode, &filetype);
190 <
191 <        // Channel 0 is READ PRG, channel 1 is WRITE PRG
192 <        if (!channel) {
193 <                filemode = FMODE_READ;
194 <                filetype = FTYPE_PRG;
195 <        }
196 <        if (channel == 1) {
197 <                filemode = FMODE_WRITE;
198 <                filetype = FTYPE_PRG;
184 >        uint8 plain_name[NAMEBUF_LENGTH];
185 >        int plain_name_len;
186 >        int mode = FMODE_READ;
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
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          }
205  
206          // Allow only read accesses
207 <        if (filemode != FMODE_READ) {
207 >        if (writing) {
208                  set_error(ERR_WRITEPROTECT);
209                  return ST_OK;
210          }
211  
212 +        // Relative files are not supported
213 +        if (type == FTYPE_REL) {
214 +                set_error(ERR_UNIMPLEMENTED);
215 +                return ST_OK;
216 +        }
217 +
218          // Find file
219 <        if (find_first_file(plainname, filetype, &num)) {
219 >        int num;
220 >        if (find_first_file(plain_name, plain_name_len, num)) {
221  
222                  // Open temporary file
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 (filemode == FMODE_READ)     // Read and buffer first byte
240 <                                read_char[channel] = fgetc(file[channel]);
239 >                        if (mode == FMODE_READ) // Read and buffer first byte
240 >                                read_char[channel] = getc(file[channel]);
241                  }
242          } else
243                  set_error(ERR_FILENOTFOUND);
# Line 353 | Line 247 | uint8 T64Drive::open_file(int channel, c
247  
248  
249   /*
356 *  Analyze file name, get access mode and type
357 */
358
359 void T64Drive::convert_filename(char *srcname, char *destname, int *filemode, int *filetype)
360 {
361        char *p;
362
363        // Search for ':', p points to first character after ':'
364        if ((p = strchr(srcname, ':')) != NULL)
365                p++;
366        else
367                p = srcname;
368
369        // Remaining string -> destname
370        strncpy(destname, p, NAMEBUF_LENGTH);
371
372        // Search for ','
373        p = destname;
374        while (*p && (*p != ',')) p++;
375
376        // Look for mode parameters seperated by ','
377        p = destname;
378        while ((p = strchr(p, ',')) != NULL) {
379
380                // Cut string after the first ','
381                *p++ = 0;
382
383                switch (*p) {
384                        case 'P':
385                                *filetype = FTYPE_PRG;
386                                break;
387                        case 'S':
388                                *filetype = FTYPE_SEQ;
389                                break;
390                        case 'U':
391                                *filetype = FTYPE_USR;
392                                break;
393                        case 'L':
394                                *filetype = FTYPE_REL;
395                                break;
396                        case 'R':
397                                *filemode = FMODE_READ;
398                                break;
399                        case 'W':
400                                *filemode = FMODE_WRITE;
401                                break;
402                        case 'A':
403                                *filemode = FMODE_APPEND;
404                                break;
405                }
406        }
407 }
408
409
410 /*
250   *  Find first file matching wildcard pattern
251   */
252  
253   // Return true if name 'n' matches pattern 'p'
254 < static bool match(char *p, char *n)
254 > static bool match(const uint8 *p, int p_len, const uint8 *n)
255   {
256 <        if (!*p)                // Null pattern matches everything
418 <                return true;
419 <
420 <        do {
256 >        while (p_len-- > 0) {
257                  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 <        } while (*p);
262 >        }
263  
264 <        return !(*n);
264 >        return *n == 0;
265   }
266  
267 < bool T64Drive::find_first_file(char *name, int type, 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(name, file_info[i].name) && type == file_info[i].type) {
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;
273 <                }
438 <
273 >        }
274          return false;
275   }
276  
# Line 444 | Line 279 | bool T64Drive::find_first_file(char *nam
279   *  Open directory, create temporary file
280   */
281  
282 < uint8 T64Drive::open_directory(int channel, char *filename)
282 > uint8 ArchDrive::open_directory(int channel, const uint8 *pattern, int pattern_len)
283   {
449        char buf[] = "\001\004\001\001\0\0\022\042                \042 00 2A";
450        char str[NAMEBUF_LENGTH];
451        char pattern[NAMEBUF_LENGTH];
452        char *p, *q;
453        int i, num;
454        int filemode;
455        int filetype;
456
284          // Special treatment for "$0"
285 <        if (strlen(filename) == 1 && filename[0] == '0')
286 <                filename += 1;
285 >        if (pattern[0] == '0' && pattern_len == 1) {
286 >                pattern++;
287 >                pattern_len--;
288 >        }
289  
290 <        // Convert filename ('$' already stripped), filemode/type are ignored
291 <        convert_filename(filename, pattern, &filemode, &filetype);
290 >        // 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  
298          // Create temporary file
299          if ((file[channel] = tmpfile()) == NULL)
300                  return ST_OK;
301  
302          // Create directory title
303 <        p = &buf[8];
304 <        for (i=0; i<16 && dir_title[i]; i++)
305 <                *p++ = dir_title[i];
303 >        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          fwrite(buf, 1, 32, file[channel]);
307  
308          // Create and write one line for every directory entry
309 <        for (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 (match(pattern, 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 <                        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 <                        i = (file_info[num].length + 254) / 254;
325 <                        *p++ = i & 0xff;
326 <                        *p++ = (i >> 8) & 0xff;
324 >                        int n = (i->size + 254) / 254;
325 >                        *p++ = n & 0xff;
326 >                        *p++ = (n >> 8) & 0xff;
327  
328                          p++;
329 <                        if (i < 10) p++;        // Less than 10: add one space
330 <                        if (i < 100) p++;       // Less than 100: add another space
329 >                        if (n < 10) p++;        // Less than 10: add one space
330 >                        if (n < 100) p++;       // Less than 100: add another space
331  
332                          // Convert and insert file name
498                        strcpy(str, file_info[num].name);
333                          *p++ = '\"';
334 <                        q = p;
335 <                        for (i=0; i<16 && str[i]; i++)
336 <                                *q++ = str[i];
334 >                        uint8 *q = p;
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 542 | 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 552 | 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 572 | 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 585 | 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++;
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 604 | Line 445 | uint8 T64Drive::Read(int channel, uint8
445          if (!file[channel]) return ST_READ_TIMEOUT;
446  
447          // Get char from buffer and read next
448 <        *byte = read_char[channel];
449 <        c = fgetc(file[channel]);
448 >        byte = read_char[channel];
449 >        int c = getc(file[channel]);
450          if (c == EOF)
451                  return ST_EOF;
452          else {
# Line 619 | 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 >= 40)
469 >                if (cmd_len > 58) {
470 >                        set_error(ERR_SYNTAX32);
471                          return ST_TIMEOUT;
472 +                }
473                  
474 <                cmd_buffer[cmd_len++] = byte;
474 >                cmd_buf[cmd_len++] = byte;
475  
476                  if (eoi) {
477 <                        cmd_buffer[cmd_len] = 0;
477 >                        execute_cmd(cmd_buf, cmd_len);
478                          cmd_len = 0;
634                        execute_command(cmd_buffer);
479                  }
480                  return ST_OK;
481          }
# Line 646 | Line 490 | uint8 T64Drive::Write(int channel, uint8
490  
491  
492   /*
493 < *  Execute command string
493 > *  Execute drive commands
494   */
495  
496 < void T64Drive::execute_command(char *command)
496 > // RENAME:new=old
497 > //        ^   ^
498 > // new_file   old_file
499 > void ArchDrive::rename_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_file, int old_file_len)
500   {
501 <        switch (command[0]) {
502 <                case 'I':
503 <                        close_all_channels();
504 <                        set_error(ERR_OK);
505 <                        break;
501 >        // 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  
508 <                case 'U':
509 <                        if ((command[1] & 0x0f) == 0x0a) {
510 <                                Reset();
511 <                        } else
512 <                                set_error(ERR_SYNTAX30);
665 <                        break;
508 >        // 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  
514 <                case 'G':
515 <                        if (command[1] != ':')
669 <                                set_error(ERR_SYNTAX30);
670 <                        else
671 <                                cht64_cmd(&command[2]);
672 <                        break;
514 >        set_error(ERR_WRITEPROTECT);
515 > }
516  
517 <                default:
518 <                        set_error(ERR_SYNTAX30);
519 <        }
517 > // INITIALIZE
518 > void ArchDrive::initialize_cmd(void)
519 > {
520 >        close_all_channels();
521 > }
522 >
523 > // VALIDATE
524 > void ArchDrive::validate_cmd(void)
525 > {
526   }
527  
528  
529   /*
530 < *  Execute 'G' command
530 > *  Reset drive
531   */
532  
533 < void T64Drive::cht64_cmd(char *t64name)
533 > void ArchDrive::Reset(void)
534   {
535 <        char str[NAMEBUF_LENGTH];
536 <        char *p = str;
535 >        close_all_channels();
536 >        cmd_len = 0;    
537 >        set_error(ERR_STARTUP);
538 > }
539  
689        // Convert .t64 file name
690        for (int i=0; i<NAMEBUF_LENGTH && (*p++ = conv_from_64(*t64name++, false)); i++) ;
540  
541 <        close_all_channels();
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 <        // G:. resets the .t64 file name to its original setting
547 <        if (str[0] == '.' && str[1] == 0)
548 <                open_close_t64_file(orig_t64_name);
549 <        else
698 <                open_close_t64_file(str);
546 > static bool is_t64_header(const uint8 *header)
547 > {
548 >        return memcmp(header, "C64S tape file", 14) == 0;
549 > }
550  
551 <        if (the_file == NULL)
552 <                set_error(ERR_NOTREADY);
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 < *  Reset drive
568 > *  Read directory of archive file into (empty) c64_dir_entry vector,
569 > *  returns false on error
570   */
571  
572 < void T64Drive::Reset(void)
572 > static bool parse_t64_file(FILE *f, vector<c64_dir_entry> &vec, char *dir_title)
573   {
574 <        close_all_channels();
575 <        cmd_len = 0;    
576 <        set_error(ERR_STARTUP);
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 < /*
632 < *  Conversion PETSCII->ASCII
633 < */
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 < uint8 T64Drive::conv_from_64(uint8 c, bool map_slash)
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 <        if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z'))
696 <                return c ^ 0x20;
697 <        if ((c >= 0xc1) && (c <= 0xda))
698 <                return c ^ 0x80;
699 <        if ((c == '/') && map_slash && ThePrefs.MapSlash)
700 <                return '\\';
701 <        return c;
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   }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines