home *** CD-ROM | disk | FTP | other *** search
/ Amiga ISO Collection / AmigaUtilCD1.iso / Emulatoren / FRODO24.LZX / Frodo / Src / 1541fs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-11-30  |  12.3 KB  |  577 lines

  1. /*
  2.  *  1541fs.c - 1541-Emulation im Amiga-Dateisystem
  3.  *
  4.  *  Copyright (C) 1994-1996 by Christian Bauer
  5.  */
  6.  
  7. /*
  8.  *  Anmerkungen:
  9.  *  ------------
  10.  *
  11.  *  Routinen:
  12.  *   - Die Schnittstelle zu den IEC-Routinen besteht in den Routinen
  13.  *     FS_Init, FS_Exit, FS_Open, FS_Close, FS_Read und FS_Write:
  14.  *       FS_Init bereitet die Emulation vor
  15.  *       FS_Exit beendet die Emulation
  16.  *       FS_Open öffnet einen Kanal
  17.  *       FS_Close schließt einen Kanal
  18.  *       FS_Read liest aus einem Kanal
  19.  *       FS_Write schreibt in einen Kanal
  20.  *
  21.  *  DriveData:
  22.  *   - lock enthält den Lock des Verzeichnisses, in dem die Emulation
  23.  *     ablaufen soll
  24.  *
  25.  *  Directory-Emulation:
  26.  *   - Wird das Directory geöffnet (Dateiname "$"), wird in T: eine
  27.  *     temporäre Datei angelegt, die vom Aufbau genau einem 1541-Directory
  28.  *     entspricht und diese Datei geöffnet. Sie kann dann mit den ganz
  29.  *     normalen Lesebefehlen verarbeitet werden.
  30.  *
  31.  *  Inkompatibilitäten/Verbesserungen:
  32.  *   - Kein "rohes" Directory-Lesen
  33.  *   - Keine relativen Dateien
  34.  *   - Nur 'I'- und 'UJ'-Befehle implementiert
  35.  */
  36.  
  37. #include <exec/types.h>
  38. #include <exec/memory.h>
  39. #include <clib/exec_protos.h>
  40. #include <clib/dos_protos.h>
  41. #include <string.h>
  42.  
  43. #include "IEC.h"
  44. #include "1541fs.h"
  45. #include "Display.h"
  46. #define CATCOMP_NUMBERS 1
  47. #include "LocStrings.h"
  48.  
  49.  
  50. // Aus Main.asm
  51. extern void ResetC64(void);
  52.  
  53.  
  54. // Prototypes
  55. int open_file(DriveData *drive, int channel, const char *filename);
  56. void convert_filename(const char *filename, char *plainname, int *filemode, int *filetype, int *wildflag);
  57. void find_first_file(DriveData *drive, char *name);
  58. int open_directory(DriveData *drive, int channel, char *filename);
  59. void close_all_channels(DriveData *drive);
  60. void execute_command(DriveData *drive, const char *command);
  61. UBYTE conv_from_64(UBYTE c, int map_slash);
  62. UBYTE conv_to_64(UBYTE c, int map_slash);
  63.  
  64.  
  65. /**
  66.  **  Emulation vorbereiten, Lock auf Verzeichnis holen, prefs zeigt auf den
  67.  **    Preferences-String
  68.  **/
  69.  
  70. void FS_Init(DriveData *drive, char *prefs)
  71. {
  72.   int i;
  73.  
  74.   if (drive->lock = Lock(prefs,ACCESS_READ)) {
  75.     for (i=0; i<16; i++) drive->handle[i] = NULL;
  76.  
  77.     drive->cmd_length = 0;
  78.  
  79.     SetError(drive, ERR_STARTUP);
  80.   }
  81. }
  82.  
  83.  
  84. /**
  85.  **  Emulation beenden, Lock auf Verzeichnis freigeben
  86.  **/
  87.  
  88. void FS_Exit(DriveData *drive)
  89. {
  90.   if (drive->lock) {
  91.     close_all_channels(drive);
  92.  
  93.     UnLock(drive->lock);
  94.     drive->lock = NULL;
  95.   }
  96. }
  97.  
  98.  
  99. /**
  100.  **  Kanal öffnen, filename ist Null-terminiert
  101.  **/
  102.  
  103. int FS_Open(DriveData *drive, int channel, char *filename)
  104. {
  105.   SetError(drive, ERR_OK);
  106.  
  107.   // Kanal 15: Dateiname als Befehl ausführen
  108.   if (channel == 15) {
  109.     execute_command(drive, filename);
  110.     return ST_OK;
  111.   }
  112.  
  113.   // Vorige Datei schließen, wenn noch offen
  114.   if (drive->handle[channel]) {
  115.     Close(drive->handle[channel]);
  116.     drive->handle[channel] = NULL;
  117.   }
  118.  
  119.   if (filename[0] == '$')
  120.     return open_directory(drive, channel, filename+1);
  121.  
  122.   if (filename[0] == '#') {
  123.     SetError(drive, ERR_NOCHANNEL);
  124.     return ST_OK;
  125.   }
  126.  
  127.   return open_file(drive, channel, filename);
  128. }
  129.  
  130.  
  131. /*
  132.  *  Datei wird geöffnet
  133.  */
  134.  
  135. // Zugriffsmodi
  136. enum {
  137.   FMODE_READ, FMODE_WRITE, FMODE_APPEND
  138. };
  139.  
  140. // Dateitypen
  141. enum {
  142.   FTYPE_PRG, FTYPE_SEQ
  143. };
  144.  
  145. int open_file(DriveData *drive, int channel, const char *filename)
  146. {
  147.   BPTR olddir;
  148.   BPTR fh = NULL;
  149.   char plainname[256];
  150.   int filemode = FMODE_READ;
  151.   int filetype = FTYPE_PRG;
  152.   int wildflag = FALSE;
  153.  
  154.   olddir = CurrentDir(drive->lock);
  155.  
  156.   convert_filename(filename, plainname, &filemode, &filetype, &wildflag);
  157.  
  158.   // Bei Kanal 0 immer PRG lesen, bei Kanal 1 immer PRG schreiben
  159.   if (!channel) {
  160.     filemode = FMODE_READ;
  161.     filetype = FTYPE_PRG;
  162.   }
  163.   if (channel == 1) {
  164.     filemode = FMODE_WRITE;
  165.     filetype = FTYPE_PRG;
  166.   }
  167.  
  168.   // Wildcards sind nur beim Lesen erlaubt
  169.   if (wildflag) {
  170.     if (filemode != FMODE_READ) {
  171.       SetError(drive, ERR_SYNTAX33);
  172.       CurrentDir(olddir);
  173.       return ST_OK;
  174.     }
  175.     find_first_file(drive, plainname);
  176.   }
  177.  
  178.   if (fh = Open(plainname, filemode == FMODE_READ ? MODE_OLDFILE : MODE_NEWFILE)) {
  179.     switch (filemode) {
  180.  
  181.       // Erstes Zeichen lesen, wenn zum Lesen geöffnet
  182.       case FMODE_READ:
  183.         drive->read_char = FGetC(fh);
  184.         break;
  185.  
  186.       // Neue Datei anlegen: E-Bit bei sequentieller Datei löschen
  187.       case FMODE_WRITE:
  188.         if (filetype == FTYPE_SEQ) {
  189.           Close(fh);
  190.           SetProtection(plainname, FIBF_EXECUTE);
  191.           fh = Open(plainname, filemode == FMODE_READ ? MODE_OLDFILE : MODE_NEWFILE);
  192.         } else {
  193.           Close(fh);
  194.           SetProtection(plainname, 0);
  195.           fh = Open(plainname, filemode == FMODE_READ ? MODE_OLDFILE : MODE_NEWFILE);
  196.         }
  197.         break;
  198.  
  199.       // Anhängen: Ans Ende der Datei gehen
  200.       case FMODE_APPEND:
  201.         Seek(fh, 0, OFFSET_END);
  202.         break;
  203.     }
  204.   }
  205.  
  206.   if (!(drive->handle[channel] = fh))
  207.     SetError(drive, ERR_FILENOTFOUND);
  208.  
  209.   CurrentDir(olddir);
  210.   return ST_OK;
  211. }
  212.  
  213.  
  214. /*
  215.  *  Dateibezeichnung analysieren, Dateimodus und -typ ermitteln
  216.  */
  217.  
  218. void convert_filename(const char *srcname, char *destname, int *filemode, int *filetype, int *wildflag)
  219. {
  220.   char *p, *q;
  221.   int i;
  222.  
  223.   // Nach ':' suchen, p zeigt auf erstes Zeichen hinter dem ':'
  224.   if (p = strchr(srcname, ':'))
  225.     p++;
  226.   else
  227.     p = srcname;
  228.  
  229.   // Zeichensatz des Reststrings wandeln -> destname
  230.   q = destname;
  231.   for (i=0; i<NAMEBUF_LENGTH && (*q++ = conv_from_64(*p++, TRUE)); i++);
  232.  
  233.   // Nach Modusparametern, getrennt durch ',' suchen
  234.   p = destname;
  235.   while (p = strchr(p, ',')) {
  236.  
  237.     // String hinter dem ersten ',' abschneiden
  238.     *p++ = 0;
  239.  
  240.     switch (*p) {
  241.       case 'p':
  242.         *filetype = FTYPE_PRG;
  243.         break;
  244.       case 's':
  245.         *filetype = FTYPE_SEQ;
  246.         break;
  247.       case 'r':
  248.         *filemode = FMODE_READ;
  249.         break;
  250.       case 'w':
  251.         *filemode = FMODE_WRITE;
  252.         break;
  253.       case 'a':
  254.         *filemode = FMODE_APPEND;
  255.         break;
  256.     }
  257.   }
  258.  
  259.   // Nach Wildcards suchen
  260.   *wildflag = (strchr(destname, '?') != NULL) || (strchr(destname, '*') != NULL);
  261. }
  262.  
  263.  
  264. /*
  265.  *  Erste zum Muster passende Datei suchen den Dateinamen ermitteln
  266.  */
  267.  
  268. // FIB für find_first_file und open_directory
  269. struct FileInfoBlock fib;
  270.  
  271. // Gibt TRUE zurück, wenn Name n dem Pattern p entspricht
  272. static int match(UBYTE *p, UBYTE *n)
  273. {
  274.   if (!*p)        // Null-Pattern matcht mit allem
  275.     return TRUE;
  276.  
  277.   do {
  278.     if (*p == '*')
  279.       return TRUE;
  280.     if ((*p != *n) && (*p != '?'))
  281.       return FALSE;
  282.     p++; n++;
  283.   } while (*p);
  284.  
  285.   return !*n;
  286. }
  287.  
  288. void find_first_file(DriveData *drive, char *name)
  289. {
  290.   if (!Examine(drive->lock, &fib))
  291.     return;
  292.  
  293.   while (ExNext(drive->lock, &fib)) {
  294.  
  295.     // Passenden Namen gefunden? Dann wirklichen Dateinamen kopieren
  296.     if (match(name, fib.fib_FileName)) {
  297.       strncpy(name, fib.fib_FileName, NAMEBUF_LENGTH);
  298.       return;
  299.     }
  300.   }
  301. }
  302.  
  303.  
  304. /*
  305.  *  Directory wird geöffnet, temporäre Datei erstellen
  306.  */
  307.  
  308. int open_directory(DriveData *drive, int channel, char *filename)
  309. {
  310.   BPTR fh;
  311.   char buf[] = "\001\004\001\001\0\0\022\042                \042 00 2A";
  312.   char pattern[NAMEBUF_LENGTH];
  313.   char *p, *q;
  314.   int i;
  315.   int filemode, filetype, wildflag;
  316.  
  317.   // Spezialbehandlung für "$0"
  318.   if (filename[0] == '0' && filename[1] == 0)
  319.     filename += 1;
  320.  
  321.   // Dateinamen konvertieren ('$' bereits entfernt), filemode/type werden ignoriert
  322.   convert_filename(filename, pattern, &filemode, &filetype, &wildflag);
  323.  
  324.   if (!Examine(drive->lock, &fib))
  325.     return ST_OK;
  326.  
  327.   if (!(fh = Open("T:Frodo$File", MODE_NEWFILE)))
  328.     return ST_OK;
  329.  
  330.   // Directory-Titel erzeugen und schreiben
  331.   p = &buf[8];
  332.   for (i=0; i<16 && fib.fib_FileName[i]; i++)
  333.     *p++ = conv_to_64(fib.fib_FileName[i], FALSE);
  334.   Write(fh, buf, 32);
  335.  
  336.   // Für jeden Verzeichniseintrag eine Zeile erzeugen und schreiben
  337.   while (ExNext(drive->lock, &fib)) {
  338.  
  339.     // Nur Dateien, die zum Pattern passen, mit aufnehmen
  340.     if (match(pattern, fib.fib_FileName)) {
  341.  
  342.       // Zeile mit Leerzeichen löschen und mit Nullbyte abschließen
  343.       memset(buf, ' ', 31);
  344.       buf[31] = 0;
  345.  
  346.       p = buf;
  347.       *p++ = 0x01;    // Dummy-Verkettung
  348.       *p++ = 0x01;
  349.  
  350.       // Größe in Blocks berechnen und eintragen
  351.       i = (fib.fib_Size + 254) / 254;
  352.       *p++ = i & 0xff;
  353.       *p++ = (i >> 8) & 0xff;
  354.  
  355.       p++;
  356.       if (i < 10) p++;    // Kleiner als 10: Ein Leerzeichen dazunehmen
  357.       if (i < 100) p++;    // Kleiner als 100: Noch ein Leerzeichen dazunehmen
  358.  
  359.       // Dateiname wandeln und eintragen
  360.       *p++ = '\"';
  361.       q = p;
  362.       for (i=0; i<16 && fib.fib_FileName[i]; i++)
  363.         if (fib.fib_FileName[i])
  364.           *q++ = conv_to_64(fib.fib_FileName[i], TRUE);
  365.       *q++ = '\"';
  366.       p += 18;
  367.  
  368.       // Typ ermitteln und eintragen
  369.       if (fib.fib_DirEntryType >= 0) {
  370.         *p++ = 'D';
  371.         *p++ = 'I';
  372.         *p++ = 'R';
  373.       } else if (fib.fib_Protection & FIBF_EXECUTE) {
  374.           *p++ = 'S';
  375.           *p++ = 'E';
  376.           *p++ = 'Q';
  377.         } else {
  378.           *p++ = 'P';
  379.           *p++ = 'R';
  380.           *p++ = 'G';
  381.         }
  382.  
  383.       // Datei geschützt?
  384.       if (fib.fib_Protection & FIBF_DELETE) *p++ = '<';
  385.  
  386.       // Zeile schreiben
  387.       Write(fh, buf, 32);
  388.     }
  389.   }
  390.  
  391.   // Abschlußzeile
  392.   Write(fh, "\001\001\0\0BLOCKS FREE.             \0\0", 32);
  393.   Close(fh);
  394.  
  395.   // Datei zum Lesen öffnen und das erste Zeichen lesen
  396.   if (drive->handle[channel] = Open("T:Frodo$File", MODE_OLDFILE))
  397.     drive->read_char = FGetC(drive->handle[channel]);
  398.  
  399.   return ST_OK;
  400. }
  401.  
  402.  
  403. /**
  404.  **  Kanal schließen
  405.  **/
  406.  
  407. int FS_Close(DriveData *drive, int channel)
  408. {
  409.   if (channel==15) {
  410.     close_all_channels(drive);
  411.     return ST_OK;
  412.   }
  413.  
  414.   if (drive->handle[channel]) {
  415.     Close(drive->handle[channel]);
  416.     drive->handle[channel] = NULL;
  417.   }
  418.  
  419.   return ST_OK;
  420. }
  421.  
  422.  
  423. /*
  424.  *  Alle Kanäle schließen
  425.  */
  426.  
  427. void close_all_channels(DriveData *drive)
  428. {
  429.   int i;
  430.  
  431.   for (i=0; i<15; i++) FS_Close(drive, i);
  432.  
  433.   drive->cmd_length = 0;
  434. }
  435.  
  436.  
  437. /**
  438.  **  Ein Byte aus Kanal lesen
  439.  **/
  440.  
  441. int FS_Read(DriveData *drive, int channel, char *data)
  442. {
  443.   LONG c;
  444.  
  445.   // Kanal 15: Fehlerkanal
  446.   if (channel == 15) {
  447.     *data = *drive->error_ptr++;
  448.  
  449.     if (*data != '\r')
  450.       return ST_OK;
  451.     else {
  452.       SetError(drive, ERR_OK);
  453.       return ST_EOF;
  454.     }
  455.   }
  456.  
  457.   if (!drive->handle[channel]) return ST_READ_TIMEOUT;
  458.  
  459.   // Zeichen aus dem Puffer holen und nächstes Zeichen lesen
  460.   *data = drive->read_char;
  461.   c = FGetC(drive->handle[channel]);
  462.   drive->read_char = c;
  463.  
  464.   if (c == -1)
  465.     return ST_EOF;
  466.   else
  467.     return ST_OK;
  468. }
  469.  
  470.  
  471. /**
  472.  **  Ein Byte in Kanal schreiben
  473.  **/
  474.  
  475. int FS_Write(DriveData *drive, int channel, char data, char eof)
  476. {
  477.   // Kanal 15: Zeichen sammeln und bei EOF Befehl ausführen
  478.   if (channel == 15) {
  479.     if (drive->cmd_length >= 40)
  480.       return ST_TIMEOUT;
  481.  
  482.     drive->cmd_buffer[drive->cmd_length++] = data;
  483.  
  484.     if (eof < 0) {
  485.       drive->cmd_buffer[drive->cmd_length++] = 0;
  486.       drive->cmd_length = 0;
  487.       execute_command(drive, drive->cmd_buffer);
  488.     }
  489.     return ST_OK;
  490.   }
  491.  
  492.   if (!drive->handle[channel]) {
  493.     SetError(drive, ERR_FILENOTOPEN);
  494.     return ST_TIMEOUT;
  495.   }
  496.  
  497.   if (FPutC(drive->handle[channel], (unsigned char)data) < 0) {
  498.     SetError(drive, ERR_WRITEERROR);
  499.     return ST_TIMEOUT;
  500.   }
  501.  
  502.   return ST_OK;
  503. }
  504.  
  505.  
  506. /*
  507.  *  Befehlsstring ausführen
  508.  */
  509.  
  510. void execute_command(DriveData *drive, const char *command)
  511. {
  512.   APTR args;
  513.  
  514.   switch (command[0]) {
  515.     case 'B':
  516.       args = "B-?";
  517.       if (ShowRequester(MSG_UNSUPFLOPPYCMD, MSG_REQGADS3, &args))
  518.         ResetC64();
  519.       SetError(drive, ERR_SYNTAX30);
  520.       break;
  521.  
  522.     case 'M':
  523.       args = "M-?";
  524.       if (ShowRequester(MSG_UNSUPFLOPPYCMD, MSG_REQGADS3, &args))
  525.         ResetC64();
  526.       SetError(drive, ERR_SYNTAX30);
  527.       break;
  528.  
  529.     case 'I':
  530.       close_all_channels(drive);
  531.       SetError(drive, ERR_OK);
  532.       break;
  533.  
  534.     case 'U':
  535.       if ((command[1] & 0x0f) == 0x0a) {
  536.         close_all_channels(drive);
  537.         SetError(drive, ERR_STARTUP);
  538.       } else
  539.         SetError(drive, ERR_SYNTAX30);
  540.       break;
  541.  
  542.     default:
  543.       SetError(drive, ERR_SYNTAX30);
  544.       break;
  545.   }
  546. }
  547.  
  548.  
  549. /*
  550.  *  Umwandlung PETSCII->ASCII
  551.  */
  552.  
  553. UBYTE conv_from_64(UBYTE c, int map_slash)
  554. {
  555.   if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z'))
  556.     return c ^ 0x20;
  557.   if ((c >= 0xc1) && (c <= 0xda))
  558.     return c ^ 0x80;
  559.   if ((c == '/') && map_slash && MapSlash)
  560.     return '\\';
  561.   return c;
  562. }
  563.  
  564.  
  565. /*
  566.  *  Umwandlung ASCII->PETSCII
  567.  */
  568.  
  569. UBYTE conv_to_64(UBYTE c, int map_slash)
  570. {
  571.   if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z'))
  572.     return c ^ 0x20;
  573.   if ((c == '\\') && map_slash && MapSlash)
  574.     return '/';
  575.   return c;
  576. }
  577.