ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/1541fs.cpp
Revision: 1.2
Committed: 2003-07-01T17:51:17Z (21 years, 3 months ago) by cebix
Branch: MAIN
Changes since 1.1: +1 -1 lines
Log Message:
updated copyright date

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * 1541fs.cpp - 1541 emulation in host file system
3     *
4 cebix 1.2 * Frodo (C) 1994-1997,2002-2003 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     * Notes:
23     * ------
24     *
25     * - If the directory is opened (file name "$"), a temporary file
26     * with the structure of a 1541 directory file is created and
27     * opened. It can then be accessed in the same way as all other
28     * files.
29     *
30     * Incompatibilities:
31     * ------------------
32     *
33     * - No "raw" directory reading
34     * - No relative/sequential/user files
35     * - Only "I" and "UJ" commands implemented
36     */
37    
38     #include "sysdeps.h"
39    
40     #include "1541fs.h"
41     #include "IEC.h"
42     #include "main.h"
43     #include "Prefs.h"
44    
45     #ifdef __riscos__
46     #include "ROlib.h"
47     #endif
48    
49    
50     // Access modes
51     enum {
52     FMODE_READ, FMODE_WRITE, FMODE_APPEND
53     };
54    
55     // File types
56     enum {
57     FTYPE_PRG, FTYPE_SEQ
58     };
59    
60     // Prototypes
61     static bool match(char *p, char *n);
62    
63    
64     /*
65     * Constructor: Prepare emulation
66     */
67    
68     FSDrive::FSDrive(IEC *iec, char *path) : Drive(iec)
69     {
70     strcpy(orig_dir_path, path);
71     dir_path[0] = 0;
72    
73     if (change_dir(orig_dir_path)) {
74     for (int i=0; i<16; i++)
75     file[i] = NULL;
76    
77     Reset();
78    
79     Ready = true;
80     }
81     }
82    
83    
84     /*
85     * Destructor
86     */
87    
88     FSDrive::~FSDrive()
89     {
90     if (Ready) {
91     close_all_channels();
92     Ready = false;
93     }
94     }
95    
96    
97     /*
98     * Change emulation directory
99     */
100    
101     bool FSDrive::change_dir(char *dirpath)
102     {
103     #ifndef __riscos__
104     DIR *dir;
105    
106     if ((dir = opendir(dirpath)) != NULL) {
107     closedir(dir);
108     strcpy(dir_path, dirpath);
109     strncpy(dir_title, dir_path, 16);
110     return true;
111     } else
112     return false;
113     #else
114     int Info[4];
115    
116     if ((ReadCatalogueInfo(dirpath,Info) & 2) != 0) // Directory or image file
117     {
118     strcpy(dir_path, dirpath);
119     strncpy(dir_title, dir_path, 16);
120     return true;
121     }
122     else
123     {
124     return false;
125     }
126     #endif
127     }
128    
129    
130     /*
131     * Open channel
132     */
133    
134     uint8 FSDrive::Open(int channel, char *filename)
135     {
136     set_error(ERR_OK);
137    
138     // Channel 15: Execute file name as command
139     if (channel == 15) {
140     execute_command(filename);
141     return ST_OK;
142     }
143    
144     // Close previous file if still open
145     if (file[channel]) {
146     fclose(file[channel]);
147     file[channel] = NULL;
148     }
149    
150     if (filename[0] == '$')
151     return open_directory(channel, filename+1);
152    
153     if (filename[0] == '#') {
154     set_error(ERR_NOCHANNEL);
155     return ST_OK;
156     }
157    
158     return open_file(channel, filename);
159     }
160    
161    
162     /*
163     * Open file
164     */
165    
166     uint8 FSDrive::open_file(int channel, char *filename)
167     {
168     char plainname[NAMEBUF_LENGTH];
169     int filemode = FMODE_READ;
170     int filetype = FTYPE_PRG;
171     bool wildflag = false;
172     char *mode = "rb";
173    
174     convert_filename(filename, plainname, &filemode, &filetype, &wildflag);
175    
176     // Channel 0 is READ PRG, channel 1 is WRITE PRG
177     if (!channel) {
178     filemode = FMODE_READ;
179     filetype = FTYPE_PRG;
180     }
181     if (channel == 1) {
182     filemode = FMODE_WRITE;
183     filetype = FTYPE_PRG;
184     }
185    
186     // Wildcards are only allowed on reading
187     if (wildflag) {
188     if (filemode != FMODE_READ) {
189     set_error(ERR_SYNTAX33);
190     return ST_OK;
191     }
192     find_first_file(plainname);
193     }
194    
195     // Select fopen() mode according to file mode
196     switch (filemode) {
197     case FMODE_READ:
198     mode = "rb";
199     break;
200     case FMODE_WRITE:
201     mode = "wb";
202     break;
203     case FMODE_APPEND:
204     mode = "ab";
205     break;
206     }
207    
208     // Open file
209     #ifndef __riscos__
210     if (chdir(dir_path))
211     set_error(ERR_NOTREADY);
212     else if ((file[channel] = fopen(plainname, mode)) != NULL) {
213     if (filemode == FMODE_READ) // Read and buffer first byte
214     read_char[channel] = fgetc(file[channel]);
215     } else
216     set_error(ERR_FILENOTFOUND);
217     chdir(AppDirPath);
218     #else
219     {
220     char fullname[NAMEBUF_LENGTH];
221    
222     // On RISC OS make a full filename
223     sprintf(fullname,"%s.%s",dir_path,plainname);
224     if ((file[channel] = fopen(fullname, mode)) != NULL)
225     {
226     if (filemode == FMODE_READ)
227     {
228     read_char[channel] = fgetc(file[channel]);
229     }
230     }
231     else
232     {
233     set_error(ERR_FILENOTFOUND);
234     }
235     }
236     #endif
237    
238     return ST_OK;
239     }
240    
241    
242     /*
243     * Analyze file name, get access mode and type
244     */
245    
246     void FSDrive::convert_filename(char *srcname, char *destname, int *filemode, int *filetype, bool *wildflag)
247     {
248     char *p, *q;
249     int i;
250    
251     // Search for ':', p points to first character after ':'
252     if ((p = strchr(srcname, ':')) != NULL)
253     p++;
254     else
255     p = srcname;
256    
257     // Convert char set of the remaining string -> destname
258     q = destname;
259     for (i=0; i<NAMEBUF_LENGTH && (*q++ = conv_from_64(*p++, true)); i++) ;
260    
261     // Look for mode parameters seperated by ','
262     p = destname;
263     while ((p = strchr(p, ',')) != NULL) {
264    
265     // Cut string after the first ','
266     *p++ = 0;
267    
268     switch (*p) {
269     case 'p':
270     *filetype = FTYPE_PRG;
271     break;
272     case 's':
273     *filetype = FTYPE_SEQ;
274     break;
275     case 'r':
276     *filemode = FMODE_READ;
277     break;
278     case 'w':
279     *filemode = FMODE_WRITE;
280     break;
281     case 'a':
282     *filemode = FMODE_APPEND;
283     break;
284     }
285     }
286    
287     // Search for wildcards
288     *wildflag = (strchr(destname, '?') != NULL) || (strchr(destname, '*') != NULL);
289     }
290    
291    
292     /*
293     * Find first file matching wildcard pattern and get its real name
294     */
295    
296     // Return true if name 'n' matches pattern 'p'
297     static bool match(char *p, char *n)
298     {
299     if (!*p) // Null pattern matches everything
300     return true;
301    
302     do {
303     if (*p == '*') // Wildcard '*' matches all following characters
304     return true;
305     if ((*p != *n) && (*p != '?')) // Wildcard '?' matches single character
306     return false;
307     p++; n++;
308     } while (*p);
309    
310     return !*n;
311     }
312    
313     void FSDrive::find_first_file(char *name)
314     {
315     #ifndef __riscos__
316     DIR *dir;
317     struct dirent *de;
318    
319     // Open directory for reading and skip '.' and '..'
320     if ((dir = opendir(dir_path)) == NULL)
321     return;
322     de = readdir(dir);
323     while (de && (0 == strcmp(".", de->d_name) || 0 == strcmp("..", de->d_name)))
324     de = readdir(dir);
325    
326     while (de) {
327    
328     // Match found? Then copy real file name
329     if (match(name, de->d_name)) {
330     strncpy(name, de->d_name, NAMEBUF_LENGTH);
331     closedir(dir);
332     return;
333     }
334    
335     // Get next directory entry
336     de = readdir(dir);
337     }
338    
339     closedir(dir);
340     #else
341     dir_env de;
342     char Buffer[NAMEBUF_LENGTH];
343    
344     de.offset = 0; de.buffsize = NAMEBUF_LENGTH; de.match = name;
345     do
346     {
347     de.readno = 1;
348     if (ReadDirName(dir_path,Buffer,&de) != NULL) {de.offset = -1;}
349     else if (de.offset != -1)
350     {
351     if (match(name,Buffer))
352     {
353     strncpy(name, Buffer, NAMEBUF_LENGTH);
354     return;
355     }
356     }
357     }
358     while (de.offset != -1);
359     #endif
360     }
361    
362    
363     /*
364     * Open directory, create temporary file
365     */
366    
367     uint8 FSDrive::open_directory(int channel, char *filename)
368     {
369     char buf[] = "\001\004\001\001\0\0\022\042 \042 00 2A";
370     char str[NAMEBUF_LENGTH];
371     char pattern[NAMEBUF_LENGTH];
372     char *p, *q;
373     int i;
374     int filemode;
375     int filetype;
376     bool wildflag;
377    
378     #ifndef __riscos__
379     DIR *dir;
380     struct dirent *de;
381     struct stat statbuf;
382    
383     // Special treatment for "$0"
384     if (filename[0] == '0' && filename[1] == 0)
385     filename += 1;
386    
387     // Convert filename ('$' already stripped), filemode/type are ignored
388     convert_filename(filename, pattern, &filemode, &filetype, &wildflag);
389    
390     // Open directory for reading and skip '.' and '..'
391     if ((dir = opendir(dir_path)) == NULL) {
392     set_error(ERR_NOTREADY);
393     return ST_OK;
394     }
395     de = readdir(dir);
396     while (de && (0 == strcmp(".", de->d_name) || 0 == strcmp("..", de->d_name)))
397     de = readdir(dir);
398    
399     // Create temporary file
400     if ((file[channel] = tmpfile()) == NULL) {
401     closedir(dir);
402     return ST_OK;
403     }
404    
405     // Create directory title
406     p = &buf[8];
407     for (i=0; i<16 && dir_title[i]; i++)
408     *p++ = conv_to_64(dir_title[i], false);
409     fwrite(buf, 1, 32, file[channel]);
410    
411     // Create and write one line for every directory entry
412     while (de) {
413    
414     // Include only files matching the pattern
415     if (match(pattern, de->d_name)) {
416    
417     // Get file statistics
418     chdir(dir_path);
419     stat(de->d_name, &statbuf);
420     chdir(AppDirPath);
421    
422     // Clear line with spaces and terminate with null byte
423     memset(buf, ' ', 31);
424     buf[31] = 0;
425    
426     p = buf;
427     *p++ = 0x01; // Dummy line link
428     *p++ = 0x01;
429    
430     // Calculate size in blocks (254 bytes each)
431     i = (statbuf.st_size + 254) / 254;
432     *p++ = i & 0xff;
433     *p++ = (i >> 8) & 0xff;
434    
435     p++;
436     if (i < 10) p++; // Less than 10: add one space
437     if (i < 100) p++; // Less than 100: add another space
438    
439     // Convert and insert file name
440     strcpy(str, de->d_name);
441     *p++ = '\"';
442     q = p;
443     for (i=0; i<16 && str[i]; i++)
444     *q++ = conv_to_64(str[i], true);
445     *q++ = '\"';
446     p += 18;
447    
448     // File type
449     if (S_ISDIR(statbuf.st_mode)) {
450     *p++ = 'D';
451     *p++ = 'I';
452     *p++ = 'R';
453     } else {
454     *p++ = 'P';
455     *p++ = 'R';
456     *p++ = 'G';
457     }
458    
459     // Write line
460     fwrite(buf, 1, 32, file[channel]);
461     }
462    
463     // Get next directory entry
464     de = readdir(dir);
465     }
466     #else
467     dir_full_info di;
468     dir_env de;
469    
470     // Much of this is very similar to the original
471     if ((filename[0] == '0') && (filename[1] == 0)) {filename++;}
472     // Concatenate dir_path and pattern in buffer pattern ==> read subdirs!
473     strcpy(pattern,dir_path);
474     convert_filename(filename, pattern + strlen(pattern), &filemode, &filetype, &wildflag);
475    
476     // We don't use tmpfile() -- problems involved!
477     DeleteFile(RO_TEMPFILE); // first delete it, if it exists
478     if ((file[channel] = fopen(RO_TEMPFILE,"wb+")) == NULL)
479     {
480     return(ST_OK);
481     }
482     de.offset = 0; de.buffsize = NAMEBUF_LENGTH; de.match = filename;
483    
484     // Create directory title - copied from above
485     p = &buf[8];
486     for (i=0; i<16 && dir_title[i]; i++)
487     *p++ = conv_to_64(dir_title[i], false);
488     fwrite(buf, 1, 32, file[channel]);
489    
490     do
491     {
492     de.readno = 1;
493     if (ReadDirNameInfo(pattern,&di,&de) != NULL) {de.offset = -1;}
494     else if (de.offset != -1) // don't have to check for match here
495     {
496     memset(buf,' ',31); buf[31] = 0; // most of this: see above
497     p = buf; *p++ = 0x01; *p++ = 0x01;
498     i = (di.length + 254) / 254; *p++ = i & 0xff; *p++ = (i>>8) & 0xff;
499     p++;
500     if (i < 10) {*p++ = ' ';}
501     if (i < 100) {*p++ = ' ';}
502     strcpy(str, di.name);
503     *p++ = '\"'; q = p;
504     for (i=0; (i<16 && str[i]); i++)
505     {
506     *q++ = conv_to_64(str[i], true);
507     }
508     *q++ = '\"'; p += 18;
509     if ((di.otype & 2) == 0)
510     {
511     *p++ = 'P'; *p++ = 'R'; *p++ = 'G';
512     }
513     else
514     {
515     *p++ = 'D'; *p++ = 'I'; *p++ = 'R';
516     }
517     fwrite(buf, 1, 32, file[channel]);
518     }
519     }
520     while (de.offset != -1);
521     #endif
522    
523     // Final line
524     fwrite("\001\001\0\0BLOCKS FREE. \0\0", 1, 32, file[channel]);
525    
526     // Rewind file for reading and read first byte
527     rewind(file[channel]);
528     read_char[channel] = fgetc(file[channel]);
529    
530     #ifndef __riscos
531     // Close directory
532     closedir(dir);
533     #endif
534    
535     return ST_OK;
536     }
537    
538    
539     /*
540     * Close channel
541     */
542    
543     uint8 FSDrive::Close(int channel)
544     {
545     if (channel == 15) {
546     close_all_channels();
547     return ST_OK;
548     }
549    
550     if (file[channel]) {
551     fclose(file[channel]);
552     file[channel] = NULL;
553     }
554    
555     return ST_OK;
556     }
557    
558    
559     /*
560     * Close all channels
561     */
562    
563     void FSDrive::close_all_channels(void)
564     {
565     for (int i=0; i<15; i++)
566     Close(i);
567    
568     cmd_len = 0;
569     }
570    
571    
572     /*
573     * Read from channel
574     */
575    
576     uint8 FSDrive::Read(int channel, uint8 *byte)
577     {
578     int c;
579    
580     // Channel 15: Error channel
581     if (channel == 15) {
582     *byte = *error_ptr++;
583    
584     if (*byte != '\r')
585     return ST_OK;
586     else { // End of message
587     set_error(ERR_OK);
588     return ST_EOF;
589     }
590     }
591    
592     if (!file[channel]) return ST_READ_TIMEOUT;
593    
594     // Read one byte
595     *byte = read_char[channel];
596     c = fgetc(file[channel]);
597     if (c == EOF)
598     return ST_EOF;
599     else {
600     read_char[channel] = c;
601     return ST_OK;
602     }
603     }
604    
605    
606     /*
607     * Write to channel
608     */
609    
610     uint8 FSDrive::Write(int channel, uint8 byte, bool eoi)
611     {
612     // Channel 15: Collect chars and execute command on EOI
613     if (channel == 15) {
614     if (cmd_len >= 40)
615     return ST_TIMEOUT;
616    
617     cmd_buffer[cmd_len++] = byte;
618    
619     if (eoi) {
620     cmd_buffer[cmd_len] = 0;
621     cmd_len = 0;
622     execute_command(cmd_buffer);
623     }
624     return ST_OK;
625     }
626    
627     if (!file[channel]) {
628     set_error(ERR_FILENOTOPEN);
629     return ST_TIMEOUT;
630     }
631    
632     if (fputc(byte, file[channel]) == EOF) {
633     set_error(ERR_WRITEERROR);
634     return ST_TIMEOUT;
635     }
636    
637     return ST_OK;
638     }
639    
640    
641     /*
642     * Execute command string
643     */
644    
645     void FSDrive::execute_command(char *command)
646     {
647     switch (command[0]) {
648     case 'I':
649     close_all_channels();
650     set_error(ERR_OK);
651     break;
652    
653     case 'U':
654     if ((command[1] & 0x0f) == 0x0a) {
655     Reset();
656     } else
657     set_error(ERR_SYNTAX30);
658     break;
659    
660     case 'G':
661     if (command[1] != ':')
662     set_error(ERR_SYNTAX30);
663     else
664     chdir_cmd(&command[2]);
665     break;
666    
667     default:
668     set_error(ERR_SYNTAX30);
669     }
670     }
671    
672    
673     /*
674     * Execute 'G' command
675     */
676    
677     void FSDrive::chdir_cmd(char *dirpath)
678     {
679     char str[NAMEBUF_LENGTH];
680     char *p = str;
681    
682     close_all_channels();
683    
684     // G:. resets the directory path to its original setting
685     if (dirpath[0] == '.' && dirpath[1] == 0) {
686     change_dir(orig_dir_path);
687     } else {
688    
689     // Convert directory name
690     for (int i=0; i<NAMEBUF_LENGTH && (*p++ = conv_from_64(*dirpath++, false)); i++) ;
691    
692     if (!change_dir(str))
693     set_error(ERR_NOTREADY);
694     }
695     }
696    
697    
698     /*
699     * Reset drive
700     */
701    
702     void FSDrive::Reset(void)
703     {
704     close_all_channels();
705     cmd_len = 0;
706     set_error(ERR_STARTUP);
707     }
708    
709    
710     /*
711     * Conversion PETSCII->ASCII
712     */
713    
714     uint8 FSDrive::conv_from_64(uint8 c, bool map_slash)
715     {
716     if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z'))
717     return c ^ 0x20;
718     if ((c >= 0xc1) && (c <= 0xda))
719     return c ^ 0x80;
720     if ((c == '/') && map_slash && ThePrefs.MapSlash)
721     #ifdef __riscos__
722     return '.'; // directory separator is '.' in RO
723     if (c == '.') {return('_');} // convert dot to underscore
724     #else
725     return '\\';
726     #endif
727     return c;
728     }
729    
730    
731     /*
732     * Conversion ASCII->PETSCII
733     */
734    
735     uint8 FSDrive::conv_to_64(uint8 c, bool map_slash)
736     {
737     if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z'))
738     return c ^ 0x20;
739     #ifdef __riscos__
740     if ((c == '.') && map_slash && ThePrefs.MapSlash)
741     #else
742     if ((c == '\\') && map_slash && ThePrefs.MapSlash)
743     #endif
744     return '/';
745     #ifdef __riscos__
746     if (c == '_') {return('.');} // convert underscore to dot
747     #endif
748     return c;
749     }