Main Page | Class Hierarchy | Compound List | File List | Compound Members | File Members | Related Pages

1541fs.cpp

Go to the documentation of this file.
00001 /*
00002  *  1541fs.cpp - 1541 emulation in host file system
00003  *
00004  *  Frodo (C) 1994-1997 Christian Bauer
00005 
00006  *
00007  * Notes:
00008  * ------
00009  *
00010  *  - If the directory is opened (file name "$"), a temporary file
00011  *    with the structure of a 1541 directory file is created and
00012  *    opened. It can then be accessed in the same way as all other
00013  *    files.
00014  *
00015  * Incompatibilities:
00016  * ------------------
00017  *
00018  *  - No "raw" directory reading
00019  *  - No relative/sequential/user files
00020  *  - Only "I" and "UJ" commands implemented
00021  */
00022 
00023 #include "sysdeps.h"
00024 
00025 #include "1541fs.h"
00026 #include "IEC.h"
00027 #include "main.h"
00028 #include "Prefs.h"
00029 
00030 #ifdef __riscos__
00031 #include "ROlib.h"
00032 #endif
00033 
00034 
00035 // Access modes
00036 enum {
00037         FMODE_READ, FMODE_WRITE, FMODE_APPEND
00038 };
00039 
00040 // File types
00041 enum {
00042         FTYPE_PRG, FTYPE_SEQ
00043 };
00044 
00045 // Prototypes
00046 static bool match(char *p, char *n);
00047 
00048 
00049 /*
00050  *  Constructor: Prepare emulation
00051  */
00052 
00053 FSDrive::FSDrive(IEC *iec, char *path) : Drive(iec)
00054 {
00055         __CHECK_NULL(iec);
00056         __CHECK_NULL(path);
00057 
00058         strcpy(orig_dir_path, path);
00059         dir_path[0] = 0;
00060 
00061         if (change_dir(orig_dir_path)) {
00062                 for (int i=0; i<16; i++)
00063                         file[i] = NULL;
00064 
00065                 Reset();
00066 
00067                 Ready = true;
00068         }
00069 
00070         CTOR(FSDrive);
00071 
00072 }
00073 
00074 
00075 /*
00076  *  Destructor
00077  */
00078 
00079 FSDrive::~FSDrive()
00080 {
00081         if (Ready) {
00082                 close_all_channels();
00083                 Ready = false;
00084         }
00085 
00086         DTOR(FSDrive);
00087 }
00088 
00089 
00090 /*
00091  *  Change emulation directory
00092  */
00093 
00094 bool FSDrive::change_dir(char *dirpath)
00095 {
00096 #ifndef __riscos__
00097         DIR *dir;
00098 
00099         if ((dir = opendir(dirpath)) != NULL) {
00100                 closedir(dir);
00101                 strcpy(dir_path, dirpath);
00102                 strncpy(dir_title, dir_path, 16);
00103                 return true;
00104         } else
00105                 return false;
00106 #else
00107         int Info[4];
00108 
00109         if ((ReadCatalogueInfo(dirpath,Info) & 2) != 0) // Directory or image file
00110         {
00111           strcpy(dir_path, dirpath);
00112           strncpy(dir_title, dir_path, 16);
00113           return true;
00114         }
00115         else
00116         {
00117           return false;
00118         }
00119 #endif
00120 }
00121 
00122 
00123 /*
00124  *  Open channel
00125  */
00126 
00127 uint8 FSDrive::Open(int channel, char *filename)
00128 {
00129         set_error(ERR_OK);
00130 
00131         // Channel 15: Execute file name as command
00132         if (channel == 15) {
00133                 execute_command(filename);
00134                 return ST_OK;
00135         }
00136 
00137         // Close previous file if still open
00138         if (file[channel]) {
00139                 fclose(file[channel]);
00140                 file[channel] = NULL;
00141         }
00142 
00143         if (filename[0] == '$')
00144                 return open_directory(channel, filename+1);
00145 
00146         if (filename[0] == '#') {
00147                 set_error(ERR_NOCHANNEL);
00148                 return ST_OK;
00149         }
00150 
00151         return open_file(channel, filename);
00152 }
00153 
00154 
00155 /*
00156  *  Open file
00157  */
00158 
00159 uint8 FSDrive::open_file(int channel, char *filename)
00160 {
00161         char plainname[NAMEBUF_LENGTH];
00162         int filemode = FMODE_READ;
00163         int filetype = FTYPE_PRG;
00164         bool wildflag = false;
00165         char *mode = "rb";
00166         
00167         convert_filename(filename, plainname, &filemode, &filetype, &wildflag);
00168 
00169         // Channel 0 is READ PRG, channel 1 is WRITE PRG
00170         if (!channel) {
00171                 filemode = FMODE_READ;
00172                 filetype = FTYPE_PRG;
00173         }
00174         if (channel == 1) {
00175                 filemode = FMODE_WRITE;
00176                 filetype = FTYPE_PRG;
00177         }
00178 
00179         // Wildcards are only allowed on reading
00180         if (wildflag) {
00181                 if (filemode != FMODE_READ) {
00182                         set_error(ERR_SYNTAX33);
00183                         return ST_OK;
00184                 }
00185                 find_first_file(plainname);
00186         }
00187 
00188         // Select fopen() mode according to file mode
00189         switch (filemode) {
00190                 case FMODE_READ:
00191                         mode = "rb";
00192                         break;
00193                 case FMODE_WRITE:
00194                         mode = "wb";
00195                         break;
00196                 case FMODE_APPEND:
00197                         mode = "ab";
00198                         break;
00199         }
00200 
00201         // Open file
00202 #ifndef __riscos__
00203         if (chdir_cmd(dir_path)) //JoeF modified to use chdir_cmd
00204                 set_error(ERR_NOTREADY);
00205         else if ((file[channel] = fopen(plainname, mode)) != NULL) {
00206                 if (filemode == FMODE_READ)     // Read and buffer first byte
00207                         read_char[channel] = fgetc(file[channel]);
00208         } else
00209                 set_error(ERR_FILENOTFOUND);
00210         chdir_cmd(AppDirPath); //JoeF modified to use chdir_cmd 04.11.99
00211 #else
00212         {
00213           char fullname[NAMEBUF_LENGTH];
00214 
00215           // On RISC OS make a full filename
00216           sprintf(fullname,"%s.%s",dir_path,plainname);
00217           if ((file[channel] = fopen(fullname, mode)) != NULL)
00218           {
00219             if (filemode == FMODE_READ)
00220             {
00221               read_char[channel] = fgetc(file[channel]);
00222             }
00223           }
00224           else
00225           {
00226             set_error(ERR_FILENOTFOUND);
00227           }
00228         }
00229 #endif
00230 
00231         return ST_OK;
00232 }
00233 
00234 
00235 /*
00236  *  Analyze file name, get access mode and type
00237  */
00238 
00239 void FSDrive::convert_filename(char *srcname, char *destname, int *filemode, int *filetype, bool *wildflag)
00240 {
00241         char *p, *q;
00242         int i;
00243 
00244         // Search for ':', p points to first character after ':'
00245         if ((p = strchr(srcname, ':')) != NULL)
00246                 p++;
00247         else
00248                 p = srcname;
00249 
00250         // Convert char set of the remaining string -> destname
00251         q = destname;
00252         for (i=0; i<NAMEBUF_LENGTH && (*q++ = conv_from_64(*p++, true)); i++) ;
00253 
00254         // Look for mode parameters seperated by ','
00255         p = destname;
00256         while ((p = strchr(p, ',')) != NULL) {
00257 
00258                 // Cut string after the first ','
00259                 *p++ = 0;
00260 
00261                 switch (*p) {
00262                         case 'p':
00263                                 *filetype = FTYPE_PRG;
00264                                 break;
00265                         case 's':
00266                                 *filetype = FTYPE_SEQ;
00267                                 break;
00268                         case 'r':
00269                                 *filemode = FMODE_READ;
00270                                 break;
00271                         case 'w':
00272                                 *filemode = FMODE_WRITE;
00273                                 break;
00274                         case 'a':
00275                                 *filemode = FMODE_APPEND;
00276                                 break;
00277                 }
00278         }
00279 
00280         // Search for wildcards
00281         *wildflag = (strchr(destname, '?') != NULL) || (strchr(destname, '*') != NULL);
00282 }
00283 
00284 
00285 /*
00286  *  Find first file matching wildcard pattern and get its real name
00287  */
00288 
00289 // Return true if name 'n' matches pattern 'p'
00290 static bool match(char *p, char *n)
00291 {
00292         if (!*p)                // Null pattern matches everything
00293                 return true;
00294 
00295         do {
00296                 if (*p == '*')  // Wildcard '*' matches all following characters
00297                         return true;
00298                 if ((*p != *n) && (*p != '?'))  // Wildcard '?' matches single character
00299                         return false;
00300                 p++; n++;
00301         } while (*p);
00302 
00303         return !*n;
00304 }
00305 
00306 void FSDrive::find_first_file(char *name)
00307 {
00308 #ifndef __riscos__
00309         DIR *dir;
00310         struct dirent *de;
00311 
00312         // Open directory for reading and skip '.' and '..'
00313         if ((dir = opendir(dir_path)) == NULL)
00314                 return;
00315         de = readdir(dir);
00316         while (de && (0 == strcmp(".", de->d_name) || 0 == strcmp("..", de->d_name))) 
00317                 de = readdir(dir);
00318 
00319         while (de) {
00320 
00321                 // Match found? Then copy real file name
00322                 if (match(name, de->d_name)) {
00323                         strncpy(name, de->d_name, NAMEBUF_LENGTH);
00324                         closedir(dir);
00325                         return;
00326                 }
00327 
00328                 // Get next directory entry
00329                 de = readdir(dir);
00330         }
00331 
00332         closedir(dir);
00333 #else
00334         dir_env de;
00335         char Buffer[NAMEBUF_LENGTH];
00336 
00337         de.offset = 0; de.buffsize = NAMEBUF_LENGTH; de.match = name;
00338         do
00339         {
00340           de.readno = 1;
00341           if (ReadDirName(dir_path,Buffer,&de) != NULL) {de.offset = -1;}
00342           else if (de.offset != -1)
00343           {
00344             if (match(name,Buffer))
00345             {
00346               strncpy(name, Buffer, NAMEBUF_LENGTH);
00347               return;
00348             }
00349           }
00350         }
00351         while (de.offset != -1);
00352 #endif
00353 }
00354 
00355 
00356 /*
00357  *  Open directory, create temporary file
00358  */
00359 
00360 uint8 FSDrive::open_directory(int channel, char *filename)
00361 {
00362         char buf[] = "\001\004\001\001\0\0\022\042                \042 00 2A";
00363         char str[NAMEBUF_LENGTH];
00364         char pattern[NAMEBUF_LENGTH];
00365         char *p, *q;
00366         int i;
00367         int filemode;
00368         int filetype;
00369         bool wildflag;
00370 
00371 #ifndef __riscos__
00372         DIR *dir;
00373         struct dirent *de;
00374         struct stat statbuf;
00375 
00376         // Special treatment for "$0"
00377         if (filename[0] == '0' && filename[1] == 0)
00378                 filename += 1;
00379 
00380         // Convert filename ('$' already stripped), filemode/type are ignored
00381         convert_filename(filename, pattern, &filemode, &filetype, &wildflag);
00382 
00383         // Open directory for reading and skip '.' and '..'
00384         if ((dir = opendir(dir_path)) == NULL) {
00385                 set_error(ERR_NOTREADY);
00386                 return ST_OK;
00387         }
00388         de = readdir(dir);
00389         while (de && (0 == strcmp(".", de->d_name) || 0 == strcmp("..", de->d_name))) 
00390                 de = readdir(dir);
00391 
00392         // Create temporary file
00393         if ((file[channel] = tmpfile()) == NULL) {
00394                 closedir(dir);
00395                 return ST_OK;
00396         }
00397 
00398         // Create directory title
00399         p = &buf[8];
00400         for (i=0; i<16 && dir_title[i]; i++)
00401                 *p++ = conv_to_64(dir_title[i], false);
00402         fwrite(buf, 1, 32, file[channel]);
00403 
00404         // Create and write one line for every directory entry
00405         while (de) {
00406 
00407                 // Include only files matching the pattern
00408                 if (match(pattern, de->d_name)) {
00409 
00410                         // Get file statistics
00411                         chdir_cmd(dir_path); //JoeF modified to use chdir_cmd 04.11.99
00412                         stat(de->d_name, &statbuf);
00413                         chdir_cmd(AppDirPath); //JoeF modified to use chdir_cmd 04.11.99
00414 
00415                         // Clear line with spaces and terminate with null byte
00416                         memset(buf, ' ', 31);
00417                         buf[31] = 0;
00418 
00419                         p = buf;
00420                         *p++ = 0x01;    // Dummy line link
00421                         *p++ = 0x01;
00422 
00423                         // Calculate size in blocks (254 bytes each)
00424                         i = (statbuf.st_size + 254) / 254;
00425                         *p++ = i & 0xff;
00426                         *p++ = (i >> 8) & 0xff;
00427 
00428                         p++;
00429                         if (i < 10) p++;        // Less than 10: add one space
00430                         if (i < 100) p++;       // Less than 100: add another space
00431 
00432                         // Convert and insert file name
00433                         strcpy(str, de->d_name);
00434                         *p++ = '\"';
00435                         q = p;
00436                         for (i=0; i<16 && str[i]; i++)
00437                                 *q++ = conv_to_64(str[i], true);
00438                         *q++ = '\"';
00439                         p += 18;
00440 
00441                         // File type
00442                         if (S_ISDIR(statbuf.st_mode)) {
00443                                 *p++ = 'D';
00444                                 *p++ = 'I';
00445                                 *p++ = 'R';
00446                         } else {
00447                                 *p++ = 'P';
00448                                 *p++ = 'R';
00449                                 *p++ = 'G';
00450                         }
00451 
00452                         // Write line
00453                         fwrite(buf, 1, 32, file[channel]);
00454                 }
00455 
00456                 // Get next directory entry
00457                 de = readdir(dir);
00458         }
00459 #else
00460         dir_full_info di;
00461         dir_env de;
00462 
00463         // Much of this is very similar to the original
00464         if ((filename[0] == '0') && (filename[1] == 0)) {filename++;}
00465         // Concatenate dir_path and pattern in buffer pattern ==> read subdirs!
00466         strcpy(pattern,dir_path);
00467         convert_filename(filename, pattern + strlen(pattern), &filemode, &filetype, &wildflag);
00468 
00469         // We don't use tmpfile() -- problems involved!
00470         DeleteFile(RO_TEMPFILE);        // first delete it, if it exists
00471         if ((file[channel] = fopen(RO_TEMPFILE,"wb+")) == NULL)
00472         {
00473           return(ST_OK);
00474         }
00475         de.offset = 0; de.buffsize = NAMEBUF_LENGTH; de.match = filename;
00476 
00477         // Create directory title - copied from above
00478         p = &buf[8];
00479         for (i=0; i<16 && dir_title[i]; i++)
00480                 *p++ = conv_to_64(dir_title[i], false);
00481         fwrite(buf, 1, 32, file[channel]);
00482 
00483         do
00484         {
00485           de.readno = 1;
00486           if (ReadDirNameInfo(pattern,&di,&de) != NULL) {de.offset = -1;}
00487           else if (de.offset != -1)     // don't have to check for match here
00488           {
00489             memset(buf,' ',31); buf[31] = 0;    // most of this: see above
00490             p = buf; *p++ = 0x01; *p++ = 0x01;
00491             i = (di.length + 254) / 254; *p++ = i & 0xff; *p++ = (i>>8) & 0xff;
00492             p++;
00493             if (i < 10)  {*p++ = ' ';}
00494             if (i < 100) {*p++ = ' ';}
00495             strcpy(str, di.name);
00496             *p++ = '\"'; q = p;
00497             for (i=0; (i<16 && str[i]); i++)
00498             {
00499               *q++ = conv_to_64(str[i], true);
00500             }
00501             *q++ = '\"'; p += 18;
00502             if ((di.otype & 2) == 0)
00503             {
00504               *p++ = 'P'; *p++ = 'R'; *p++ = 'G';
00505             }
00506             else
00507             {
00508               *p++ = 'D'; *p++ = 'I'; *p++ = 'R';
00509             }
00510             fwrite(buf, 1, 32, file[channel]);
00511           }
00512         }
00513         while (de.offset != -1);
00514 #endif
00515 
00516         // Final line
00517         fwrite("\001\001\0\0BLOCKS FREE.             \0\0", 1, 32, file[channel]);
00518 
00519         // Rewind file for reading and read first byte
00520         rewind(file[channel]);
00521         read_char[channel] = fgetc(file[channel]);
00522 
00523 #ifndef __riscos
00524         // Close directory
00525         closedir(dir);
00526 #endif
00527 
00528         return ST_OK;
00529 }
00530 
00531 
00532 /*
00533  *  Close channel
00534  */
00535 
00536 uint8 FSDrive::Close(int channel)
00537 {
00538         if (channel == 15) {
00539                 close_all_channels();
00540                 return ST_OK;
00541         }
00542 
00543         if (file[channel]) {
00544                 fclose(file[channel]);
00545                 file[channel] = NULL;
00546         }
00547 
00548         return ST_OK;
00549 }
00550 
00551 
00552 /*
00553  *  Close all channels
00554  */
00555 
00556 void FSDrive::close_all_channels(void)
00557 {
00558         for (int i=0; i<15; i++)
00559                 Close(i);
00560 
00561         cmd_len = 0;
00562 }
00563 
00564 
00565 /*
00566  *  Read from channel
00567  */
00568 
00569 uint8 FSDrive::Read(int channel, uint8 *byte)
00570 {
00571         int c;
00572 
00573         // Channel 15: Error channel
00574         if (channel == 15) {
00575                 *byte = *error_ptr++;
00576 
00577                 if (*byte != '\r')
00578                         return ST_OK;
00579                 else {  // End of message
00580                         set_error(ERR_OK);
00581                         return ST_EOF;
00582                 }
00583         }
00584 
00585         if (!file[channel]) return ST_READ_TIMEOUT;
00586 
00587         // Read one byte
00588         *byte = read_char[channel];
00589         c = fgetc(file[channel]);
00590         if (c == EOF)
00591                 return ST_EOF;
00592         else {
00593                 read_char[channel] = c;
00594                 return ST_OK;
00595         }
00596 }
00597 
00598 
00599 /*
00600  *  Write to channel
00601  */
00602 
00603 uint8 FSDrive::Write(int channel, uint8 byte, bool eoi)
00604 {
00605         // Channel 15: Collect chars and execute command on EOI
00606         if (channel == 15) {
00607                 if (cmd_len >= 40)
00608                         return ST_TIMEOUT;
00609                 
00610                 cmd_buffer[cmd_len++] = byte;
00611 
00612                 if (eoi) {
00613                         cmd_buffer[cmd_len] = 0;
00614                         cmd_len = 0;
00615                         execute_command(cmd_buffer);
00616                 }
00617                 return ST_OK;
00618         }
00619 
00620         if (!file[channel]) {
00621                 set_error(ERR_FILENOTOPEN);
00622                 return ST_TIMEOUT;
00623         }
00624 
00625         if (fputc(byte, file[channel]) == EOF) {
00626                 set_error(ERR_WRITEERROR);
00627                 return ST_TIMEOUT;
00628         }
00629 
00630         return ST_OK;
00631 }
00632 
00633 
00634 /*
00635  *  Execute command string
00636  */
00637 
00638 void FSDrive::execute_command(char *command)
00639 {
00640         switch (command[0]) {
00641                 case 'I':
00642                         close_all_channels();
00643                         set_error(ERR_OK);
00644                         break;
00645 
00646                 case 'U':
00647                         if ((command[1] & 0x0f) == 0x0a) {
00648                                 Reset();
00649                         } else
00650                                 set_error(ERR_SYNTAX30);
00651                         break;
00652 
00653                 case 'G':
00654                         if (command[1] != ':')
00655                                 set_error(ERR_SYNTAX30);
00656                         else
00657                                 chdir_cmd(&command[2]);
00658                         break;
00659 
00660                 default:
00661                         set_error(ERR_SYNTAX30);
00662         }
00663 }
00664 
00665 
00666 /*
00667  *  Execute 'G' command
00668  */
00669 
00670 int FSDrive::chdir_cmd(char *dirpath)
00671 {
00672         char str[NAMEBUF_LENGTH];
00673         char *p = str;
00674 
00675         close_all_channels();
00676 
00677         // G:. resets the directory path to its original setting
00678         if (dirpath[0] == '.' && dirpath[1] == 0) {
00679                 change_dir(orig_dir_path);
00680         } else {
00681 
00682                 // Convert directory name
00683                 for (int i=0; i<NAMEBUF_LENGTH && (*p++ = conv_from_64(*dirpath++, false)); i++) ;
00684 
00685                 if (!change_dir(str))
00686                         {
00687                         set_error(ERR_NOTREADY);
00688                         return 1;                       //JoeF 04.11.99 ...hacked! this function was originally void!!!
00689                         }
00690         }
00691         return 0;
00692 }
00693 
00694 
00695 /*
00696  *  Reset drive
00697  */
00698 
00699 void FSDrive::Reset(void)
00700 {
00701         close_all_channels();
00702         cmd_len = 0;    
00703         set_error(ERR_STARTUP);
00704 
00705         ELOG1(_L8("FS drive reset\n"));
00706 }
00707 
00708 
00709 /*
00710  *  Conversion PETSCII->ASCII
00711  */
00712 
00713 uint8 FSDrive::conv_from_64(uint8 c, bool map_slash)
00714 {
00715         if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z'))
00716                 return c ^ 0x20;
00717         if ((c >= 0xc1) && (c <= 0xda))
00718                 return c ^ 0x80;
00719         if ((c == '/') && map_slash) 
00720 #ifdef __riscos__
00721                 return '.';     // directory separator is '.' in RO
00722         if (c == '.') {return('_');}    // convert dot to underscore
00723 #else
00724                 return '\\';
00725 #endif
00726         return c;
00727 }
00728 
00729 
00730 /*
00731  *  Conversion ASCII->PETSCII
00732  */
00733 
00734 uint8 FSDrive::conv_to_64(uint8 c, bool map_slash)
00735 {
00736         if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z'))
00737                 return c ^ 0x20;
00738 #ifdef __riscos__
00739         if ((c == '.') && map_slash && ThePrefs.MapSlash)
00740 #else
00741         if ((c == '\\') && map_slash) 
00742 #endif
00743                 return '/';
00744 #ifdef __riscos__
00745         if (c == '_') {return('.');}    // convert underscore to dot
00746 #endif
00747         return c;
00748 }

Generated on Tue Feb 8 04:07:12 2005 for E32frodo by doxygen 1.3.3