home *** CD-ROM | disk | FTP | other *** search
- /*
- * 1541fs.c - 1541-Emulation im Amiga-Dateisystem
- *
- * Copyright (C) 1994-1995 by Christian Bauer
- */
-
- /*
- * Anmerkungen:
- * ------------
- *
- * Routinen:
- * - Die Schnittstelle zu den IEC-Routinen besteht in den Routinen
- * FS_Init, FS_Exit, FS_Open, FS_Close, FS_Read und FS_Write:
- * FS_Init bereitet die Emulation vor
- * FS_Exit beendet die Emulation
- * FS_Open öffnet einen Kanal
- * FS_Close schließt einen Kanal
- * FS_Read liest aus einem Kanal
- * FS_Write schreibt in einen Kanal
- *
- * DriveData:
- * - lock enthält den Lock des Verzeichnisses, in dem die Emulation
- * ablaufen soll
- *
- * Directory-Emulation:
- * - Wird das Directory geöffnet (Dateiname "$"), wird in T: eine
- * temporäre Datei angelegt, die vom Aufbau genau einem 1541-Directory
- * entspricht und diese Datei geöffnet. Sie kann dann mit den ganz
- * normalen Lesebefehlen verarbeitet werden.
- *
- * Inkompatibilitäten/Verbesserungen:
- * - Keine Wildcards beim Directory-Laden
- * - Kein "rohes" Directory-Lesen
- * - Keine relativen Dateien
- * - Nur 'I'- und 'UJ'-Befehle implementiert
- */
-
- #include <exec/types.h>
- #include <exec/memory.h>
- #include <clib/exec_protos.h>
- #include <clib/dos_protos.h>
- #include <string.h>
-
- #include "IEC.h"
- #include "1541fs.h"
- #include "Display.h"
- #define CATCOMP_NUMBERS 1
- #include "LocStrings.h"
-
-
- // Aus Main.asm
- extern void ResetC64(void);
-
-
- // Prototypes
- int open_file(DriveData *drive, int channel, const char *filename);
- void convert_filename(const char *filename, char *plainname, int *filemode, int *filetype, int *wildflag);
- void find_first_file(char *name);
- int open_directory(DriveData *drive, int channel, const char *filename);
- void close_all_channels(DriveData *drive);
- void execute_command(DriveData *drive, const char *command);
- char conv_from_64(char c);
- char conv_to_64(char c);
-
-
- /**
- ** Emulation vorbereiten, Lock auf Verzeichnis holen, prefs zeigt auf den
- ** Preferences-String
- **/
-
- void FS_Init(DriveData *drive, char *prefs)
- {
- int i;
-
- if (drive->lock = Lock(prefs,ACCESS_READ)) {
- for (i=0; i<16; i++) drive->handle[i] = NULL;
-
- drive->cmd_length = 0;
-
- SetError(drive, ERR_STARTUP);
- }
- }
-
-
- /**
- ** Emulation beenden, Lock auf Verzeichnis freigeben
- **/
-
- void FS_Exit(DriveData *drive)
- {
- if (drive->lock) {
- close_all_channels(drive);
-
- UnLock(drive->lock);
- drive->lock = NULL;
- }
- }
-
-
- /**
- ** Kanal öffnen, filename ist Null-terminiert
- **/
-
- int FS_Open(DriveData *drive, int channel, char *filename)
- {
- SetError(drive, ERR_OK);
-
- // Kanal 15: Dateiname als Befehl ausführen
- if (channel == 15) {
- execute_command(drive, filename);
- return ST_OK;
- }
-
- // Vorige Datei schließen, wenn noch offen
- if (drive->handle[channel]) {
- Close(drive->handle[channel]);
- drive->handle[channel] = NULL;
- }
-
- if (filename[0] == '$')
- return open_directory(drive, channel, filename);
-
- if (filename[0] == '#') {
- SetError(drive, ERR_NOCHANNEL);
- return ST_OK;
- }
-
- return open_file(drive, channel, filename);
- }
-
-
- /*
- * Datei wird geöffnet
- */
-
- // Zugriffsmodi
- enum {
- FMODE_READ, FMODE_WRITE, FMODE_APPEND
- };
-
- // Dateitypen
- enum {
- FTYPE_PRG, FTYPE_SEQ
- };
-
- int open_file(DriveData *drive, int channel, const char *filename)
- {
- BPTR olddir;
- BPTR fh = NULL;
- char plainname[256];
- int filemode = FMODE_READ;
- int filetype = FTYPE_PRG;
- int wildflag = 0;
-
- olddir = CurrentDir(drive->lock);
-
- convert_filename(filename, plainname, &filemode, &filetype, &wildflag);
-
- // Bei Kanal 0 immer PRG lesen, bei Kanal 1 immer PRG schreiben
- if (!channel) {
- filemode = FMODE_READ;
- filetype = FTYPE_PRG;
- }
- if (channel == 1) {
- filemode = FMODE_WRITE;
- filetype = FTYPE_PRG;
- }
-
- // Wildcards sind nur beim Lesen erlaubt
- if (wildflag) {
- if (filemode != FMODE_READ) {
- SetError(drive, ERR_SYNTAX33);
- CurrentDir(olddir);
- return ST_OK;
- }
- find_first_file(plainname);
- }
-
- if (fh = Open(plainname, filemode == FMODE_READ ? MODE_OLDFILE : MODE_NEWFILE)) {
- switch (filemode) {
-
- // Erstes Zeichen lesen, wenn zum Lesen geöffnet
- case FMODE_READ:
- drive->read_char = FGetC(fh);
- break;
-
- // Neue Datei anlegen: E-Bit bei sequentieller Datei löschen
- case FMODE_WRITE:
- if (filetype == FTYPE_SEQ) {
- Close(fh);
- SetProtection(plainname, FIBF_EXECUTE);
- fh = Open(plainname, filemode == FMODE_READ ? MODE_OLDFILE : MODE_NEWFILE);
- } else {
- Close(fh);
- SetProtection(plainname, 0);
- fh = Open(plainname, filemode == FMODE_READ ? MODE_OLDFILE : MODE_NEWFILE);
- }
- break;
-
- // Anhängen: Ans Ende der Datei gehen
- case FMODE_APPEND:
- Seek(fh, 0, OFFSET_END);
- break;
- }
- }
-
- if (!(drive->handle[channel] = fh))
- SetError(drive, ERR_FILENOTFOUND);
-
- CurrentDir(olddir);
- return ST_OK;
- }
-
-
- /*
- * Dateibezeichnung analysieren, Dateimodus und -typ ermitteln
- */
-
- void convert_filename(const char *srcname, char *destname, int *filemode, int *filetype, int *wildflag)
- {
- char *p, *q;
- int i;
-
- // Nach ':' suchen, p zeigt auf erstes Zeichen hinter dem ':'
- if (p = strchr(srcname, ':'))
- p++;
- else
- p = srcname;
-
- // Zeichensatz des Reststrings wandeln -> destname
- q = destname;
- for (i=0; i<NAMEBUF_LENGTH && (*q++ = conv_from_64(*p++)); i++);
-
- // Nach Modusparametern, getrennt durch ',' suchen
- p = destname;
- while (p = strchr(p, ',')) {
-
- // String hinter dem ersten ',' abschneiden
- *p++ = 0;
-
- switch (*p) {
- case 'p':
- *filetype = FTYPE_PRG;
- break;
- case 's':
- *filetype = FTYPE_SEQ;
- break;
- case 'r':
- *filemode = FMODE_READ;
- break;
- case 'w':
- *filemode = FMODE_WRITE;
- break;
- case 'a':
- *filemode = FMODE_APPEND;
- break;
- }
- }
-
- // Nach Wildcards suchen, '*' durch '#?' ersetzen und alles danach abschneiden
- *wildflag = (strchr(destname, '?') != NULL);
-
- if (p = strchr(destname, '*')) {
- *p++ = '#';
- *p++ = '?';
- *p++ = 0;
- *wildflag = TRUE;
- }
- }
-
-
- /*
- * Erste zum Muster passende Datei suchen den Dateinamen ermitteln
- */
-
- void find_first_file(char *name)
- {
- struct AnchorPath *a;
-
- if (a = AllocMem(sizeof(struct AnchorPath), MEMF_CLEAR|MEMF_PUBLIC)) {
- if (!MatchFirst(name, a)) {
- strncpy(name, a->ap_Info.fib_FileName, NAMEBUF_LENGTH);
- }
- MatchEnd(a);
- FreeMem(a, sizeof(struct AnchorPath));
- }
- }
-
-
- /*
- * Directory wird geöffnet, temporäre Datei erstellen
- */
-
- struct FileInfoBlock fib;
-
- int open_directory(DriveData *drive, int channel, const char *filename)
- {
- BPTR fh;
- char buf[] = "\001\004\001\001\0\0\022\042 \042 00 2A";
- char *p, *q;
- int i;
-
- if (!Examine(drive->lock, &fib)) return ST_OK;
-
- if (!(fh = Open("T:Frodo$File", MODE_NEWFILE))) return ST_OK;
-
- // Directory-Titel erzeugen und schreiben
- p = &buf[8];
- for (i=0; i<16 && fib.fib_FileName[i]; i++)
- *p++ = conv_to_64(fib.fib_FileName[i]);
- Write(fh, buf, 32);
-
- // Für jeden Verzeichniseintrag eine Zeile erzeugen und schreiben
- while (ExNext(drive->lock, &fib)) {
-
- // Zeile mit Leerzeichen löschen und mit Nullbyte abschließen
- memset(buf, ' ', 31);
- buf[31] = 0;
-
- p = buf;
- *p++ = 0x01; // Dummy-Verkettung
- *p++ = 0x01;
-
- // Größe in Blocks berechnen und eintragen
- i = (fib.fib_Size + 254) / 254;
- *p++ = i & 0xff;
- *p++ = (i >> 8) & 0xff;
-
- p++;
- if (i < 10) p++; // Kleiner als 10: Ein Leerzeichen dazunehmen
- if (i < 100) p++; // Kleiner als 100: Noch ein Leerzeichen dazunehmen
-
- // Dateiname wandeln und eintragen
- *p++ = '\"';
- q = p;
- for (i=0; i<16 && fib.fib_FileName[i]; i++)
- if (fib.fib_FileName[i])
- *q++ = conv_to_64(fib.fib_FileName[i]);
- *q++ = '\"';
- p += 18;
-
- // Typ ermitteln und eintragen
- if (fib.fib_DirEntryType >= 0) {
- *p++ = 'D';
- *p++ = 'I';
- *p++ = 'R';
- } else if (fib.fib_Protection & FIBF_EXECUTE) {
- *p++ = 'S';
- *p++ = 'E';
- *p++ = 'Q';
- } else {
- *p++ = 'P';
- *p++ = 'R';
- *p++ = 'G';
- }
-
- // Datei geschützt?
- if (fib.fib_Protection & FIBF_DELETE) *p++ = '<';
-
- // Zeile schreiben
- Write(fh, buf, 32);
- }
-
- // Abschlußzeile
- Write(fh, "\001\001\0\0BLOCKS FREE. \0\0", 32);
- Close(fh);
-
- // Datei zum Lesen öffnen und das erste Zeichen lesen
- if (drive->handle[channel] = Open("T:Frodo$File", MODE_OLDFILE))
- drive->read_char = FGetC(drive->handle[channel]);
-
- return ST_OK;
- }
-
-
- /**
- ** Kanal schließen
- **/
-
- int FS_Close(DriveData *drive, int channel)
- {
- if (channel==15) {
- close_all_channels(drive);
- return ST_OK;
- }
-
- if (drive->handle[channel]) {
- Close(drive->handle[channel]);
- drive->handle[channel] = NULL;
- }
-
- return ST_OK;
- }
-
-
- /*
- * Alle Kanäle schließen
- */
-
- void close_all_channels(DriveData *drive)
- {
- int i;
-
- for (i=0; i<15; i++) FS_Close(drive, i);
-
- drive->cmd_length = 0;
- }
-
-
- /**
- ** Ein Byte aus Kanal lesen
- **/
-
- int FS_Read(DriveData *drive, int channel, char *data)
- {
- LONG c;
-
- // Kanal 15: Fehlerkanal
- if (channel == 15) {
- *data = *drive->error_ptr++;
-
- if (*data != '\r')
- return ST_OK;
- else {
- SetError(drive, ERR_OK);
- return ST_EOF;
- }
- }
-
- if (!drive->handle[channel]) return ST_READ_TIMEOUT;
-
- // Zeichen aus dem Puffer holen und nächstes Zeichen lesen
- *data = drive->read_char;
- c = FGetC(drive->handle[channel]);
- drive->read_char = c;
-
- if (c == -1)
- return ST_EOF;
- else
- return ST_OK;
- }
-
-
- /**
- ** Ein Byte in Kanal schreiben
- **/
-
- int FS_Write(DriveData *drive, int channel, char data, char eof)
- {
- // Kanal 15: Zeichen sammeln und bei EOF Befehl ausführen
- if (channel == 15) {
- if (drive->cmd_length >= 40)
- return ST_TIMEOUT;
-
- drive->cmd_buffer[drive->cmd_length++] = data;
-
- if (eof < 0) {
- drive->cmd_buffer[drive->cmd_length++] = 0;
- drive->cmd_length = 0;
- execute_command(drive, drive->cmd_buffer);
- }
- return ST_OK;
- }
-
- if (!drive->handle[channel]) {
- SetError(drive, ERR_FILENOTOPEN);
- return ST_TIMEOUT;
- }
-
- if (FPutC(drive->handle[channel], data) < 0) {
- SetError(drive, ERR_WRITEERROR);
- return ST_TIMEOUT;
- }
-
- return ST_OK;
- }
-
-
- /*
- * Befehlsstring ausführen
- */
-
- void execute_command(DriveData *drive, const char *command)
- {
- APTR args;
-
- switch (command[0]) {
- case 'B':
- args = "B-?";
- if (ShowRequester(MSG_UNSUPFLOPPYCMD, MSG_REQGADS3, &args))
- ResetC64();
- SetError(drive, ERR_SYNTAX30);
- break;
-
- case 'M':
- args = "M-?";
- if (ShowRequester(MSG_UNSUPFLOPPYCMD, MSG_REQGADS3, &args))
- ResetC64();
- SetError(drive, ERR_SYNTAX30);
- break;
-
- case 'I':
- close_all_channels(drive);
- SetError(drive, ERR_OK);
- break;
-
- case 'U':
- if ((command[1] & 0x0f) == 0x0a) {
- close_all_channels(drive);
- SetError(drive, ERR_STARTUP);
- } else
- SetError(drive, ERR_SYNTAX30);
- break;
-
- default:
- SetError(drive, ERR_SYNTAX30);
- break;
- }
- }
-
-
- /*
- * Umwandlung PETSCII->ASCII
- */
-
- char conv_from_64(char c)
- {
- if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z'))
- return c ^ 0x20;
- if ((c == '/') && MapSlash)
- return '\\';
- return c;
- }
-
-
- /*
- * Umwandlung ASCII->PETSCII
- */
-
- char conv_to_64(char c)
- {
- if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z'))
- return c ^ 0x20;
- if ((c == '\\') && MapSlash)
- return '/';
- return c;
- }
-