blob: 9aa31f0df5ca8a24789030f83de41e2f40bfe7e0 [file] [log] [blame] [raw]
#include "device.h"
#include "iec.h"
#include "diskfs.h"
#include <string.h>
#include <ctype.h>
CIECFSDrive::CIECFSDrive(const char *path)
{
strcpy(orig_dir_path, path);
dir_path[0] = 0;
if (ChangeDir(orig_dir_path)) {
for (int i=0; i<16; i++)
file[i] = NULL;
Reset();
}
}
CIECFSDrive::~CIECFSDrive()
{
CloseAllChannels();
}
void CIECFSDrive::Reset(void)
{
CloseAllChannels();
cmd_len = 0;
name_length = 0;
SetError(ERR_STARTUP, 0, 0);
}
bool CIECFSDrive::ChangeDir(char *dirpath)
{
if ( dirpath[0] ) {
strcpy(dir_path, dirpath);
strncpy(dir_title, dir_path, 16);
return !!ad_set_curr_dir(dir_path);
} else
return false;
}
unsigned char CIECFSDrive::Open(int channel)
{
SetError(ERR_OK, 0, 0);
if (channel == 15) {
ExecuteCommand(name_buf);
return ST_OK;
}
if (file[channel]) {
fclose(file[channel]);
file[channel] = NULL;
}
switch ( name_buf[0] ) {
case '$':
return OpenDirectory(channel, name_buf+1);
case '#':
SetError(ERR_NOCHANNEL, 0, 0);
return ST_OK;
default:
return OpenFile(channel, name_buf);
}
}
unsigned char CIECFSDrive::Open(int channel, char *nameBuf)
{
if (nameBuf)
strcpy(name_buf, nameBuf);
return Open(channel);
}
unsigned char CIECFSDrive::OpenFile(int channel, char *filename)
{
char plainname[NAMEBUF_LENGTH];
int filemode;
int filetype;
bool wildflag = false;
char mode[4];
char extension[5]; /* File extension */
ParseFileName(filename, plainname, &filemode, &filetype, &wildflag);
/* Channel 0 is READ PRG, channel 1 is WRITE PRG */
switch (channel) {
case 1:
filemode = FMODE_WRITE;
filetype = FTYPE_PRG;
break;
default:
filemode = FMODE_READ;
filetype = FTYPE_PRG;
break;
}
if (wildflag) {
if (filemode != FMODE_READ) {
SetError(ERR_SYNTAX33, 0, 0);
return ST_OK;
}
FindFirstFile(plainname);
} else {
switch (filetype) {
case FTYPE_PRG:
strcpy( extension, ".prg"); // Add .prg extension
break;
/*case FTYPE_SEQ:
strcpy( extension, ".seq"); // Add .seq extension
break;*/
default:
strcpy( extension, ".prg");
}
strcat( plainname, extension); /* Add extension */
}
//fprintf(stderr,"Searching for: %s\n", plainname);
switch (filemode) {
default:
case FMODE_READ:
strcpy(mode, "rb");
break;
case FMODE_WRITE:
strcpy(mode, "wb");
break;
case FMODE_APPEND:
strcpy(mode, "ab");
break;
}
if ((file[channel] = fopen(plainname, mode)) != NULL) {
if (filemode == FMODE_READ) // Read and buffer first byte
read_char[channel] = fgetc(file[channel]);
} else {
SetError(ERR_FILENOTFOUND, 0, 0);
return ST_ERROR;
}
return ST_OK;
}
void CIECFSDrive::ParseFileName(char *srcname, char *destname, int *filemode, int *filetype, bool *wildflag)
{
char *p, *q;
int i;
if ((p = strchr(srcname, ':')) != NULL)
p++;
else
p = srcname;
q = destname;
for (i=0; i<NAMEBUF_LENGTH && (*q++ = ToPETSCII(*p++)); i++) ;
p = destname;
while ((p = strchr(p, ',')) != NULL) {
*p++ = 0;
switch (*p) {
case 'p':
*filetype = FTYPE_PRG;
break;
case 's':
*filetype = FTYPE_SEQ;
break;
case 'u':
*filetype = FTYPE_USR;
break;
case 'r':
*filemode = FMODE_READ;
break;
case 'w':
*filemode = FMODE_WRITE;
break;
case 'a':
*filemode = FMODE_APPEND;
break;
}
}
*wildflag = strpbrk(destname, "?*") != NULL;
}
void CIECFSDrive::FindFirstFile(char *name)
{
bool found = true;
char *filename, cmpname[NAMEBUF_LENGTH];
char fname[NAMEBUF_LENGTH];
// convert to uppercase
for ( filename = name; filename<name+strlen(name); filename++)
*filename=toupper(*filename);
if ( !(ad_set_curr_dir(dir_path)) )
return;
if (!ad_find_first_file("*.prg")) return;
found = ad_return_current_filename() != 0;
while (found) {
char *currfname = ad_return_current_filename();
if (currfname) {
char *ext;
strncpy( fname, currfname, NAMEBUF_LENGTH);
strcpy( cmpname, currfname);
ext = strrchr( cmpname, '.' );
// FIXME! filetype check!
if ( ext ) {
*ext++ = '\0';
}
// Match found? Then copy real file name
if (Match(name, cmpname)) {
strncpy(name, fname, strlen(fname)+1 );
return;
}
// Get next directory entry
}
found = !!ad_find_next_file();
}
ad_find_file_close();
}
unsigned char CIECFSDrive::OpenDirectory(int channel, char *filename)
{
char buf[] = "\001\004\001\001\0\0\022\042 \042 00 2A";
char str[NAMEBUF_LENGTH];
char pattern[NAMEBUF_LENGTH];
char *p, *q;
int i;
int filemode;
int filetype;
bool wildflag;
bool found = true;
char fname[NAMEBUF_LENGTH] = "";
if (filename[0] == '0' && filename[1] == 0)
filename += 1;
ParseFileName(filename, pattern, &filemode, &filetype, &wildflag);
if ( ad_find_first_file("*.prg"))
strcpy( fname, ad_return_current_filename());
if ( !fname[0] )
found = false;
if ((file[channel] = tmpfile()) == NULL) {
return ST_OK;
}
p = &buf[8];
for (i=0; i<16 && dir_title[i] ; i++)
*p++ = ToPETSCII(dir_title[i]);
fwrite(buf, 1, 32, file[channel]);
while (found) {
if (Match(pattern, fname)) {
memset(buf, ' ', 31);
buf[31] = 0;
p = buf;
*p++ = 0x01;
*p++ = 0x01;
int size = ad_get_current_filesize();
// Size in blocks
i = (size + 254) / 254;
*p++ = i & 0xff;
*p++ = (i >> 8) & 0xff;
p++;
if (i < 10) p++;
if (i < 100) p++;
strcpy(str, fname);
str[ strlen(str)-4 ] = '\0';
*p++ = '\"';
q = p;
for (i=0; i<16 && str[i]; i++)
*q++ = ToPETSCII(str[i]);
*q++ = '\"';
p += 18;
strncpy( p, "PRG", 3);
p += 3;
fwrite(buf, 1, 32, file[channel]);
}
// Take next directory entry
if ((found = !!ad_find_next_file())) {
strcpy( fname, ad_return_current_filename());
}
}
fwrite("\001\001\0\0BLOCKS FREE. \0\0", 1, 32, file[channel]);
rewind(file[channel]);
read_char[channel] = fgetc(file[channel]);
ad_find_file_close();
return ST_OK;
}
unsigned char CIECFSDrive::Close(int channel)
{
// Closing channel 15 closes all other channels
if (channel == 15) {
CloseAllChannels();
return ST_OK;
}
if (file[channel]) {
fclose(file[channel]);
file[channel] = NULL;
}
return ST_OK;
}
/*void CIECFSDrive::CloseAllChannels()
{
for (int i=0; i<15; i++)
Close(i);
cmd_len = 0;
}*/
unsigned char CIECFSDrive::Read(int channel, unsigned char *byte)
{
int c;
if (channel == 15) {
*byte = *errorPtr++;
if (*byte != '\r')
return ST_OK;
else { // End of message
SetError(ERR_OK, 0, 0);
return ST_EOF;
}
}
if (!file[channel]) return ST_ERROR;
// Read one byte
*byte = read_char[channel];
c = fgetc(file[channel]);
if (c == EOF)
return ST_EOF;
else {
read_char[channel] = c;
return ST_OK;
}
}
Uint8 CIECFSDrive::Write(int channel, Uint8 data, unsigned int cmd, bool eoi)
{
if (channel == 15) {
if (eoi) {
cmd_buffer[cmd_len] = 0;
cmd_len = 0;
ExecuteCommand(cmd_buffer);
return ST_OK;
}
if (cmd_len >= 40)
return ST_ERROR;
cmd_buffer[cmd_len++] = data;
return ST_OK;
}
switch (cmd) {
case CIECInterface::CMD_OPEN:
name_buf[name_length++] = data;
if (name_length>=16)
return ST_ERROR;
if (eoi) {
name_buf[name_length] = 0;
name_length = 0;
return ST_OK;
}
return ST_OK;
case CIECInterface::CMD_DATA:
if (!file[channel]) {
SetError(ERR_FILENOTOPEN, 0, 0);
return ST_ERROR;
}
if (!eoi && fputc(data, file[channel]) == EOF) {
SetError(ERR_WRITEERROR, 0, 0);
return ST_ERROR;
}
return ST_OK;
default:
return ST_ERROR;
}
}
void CIECFSDrive::ExecuteCommand(char *command)
{
unsigned short adr;
int len, i;
switch (command[0]) {
case 'B':
if (command[1] != '-')
SetError(ERR_SYNTAX30, 0, 0);
else
switch (command[2]) {
case 'E':
adr = ((unsigned char)command[4] << 8) | ((unsigned char)command[3]);
fprintf( stderr, "B-E ($%04X) : not supported with DOS level emulation.\n", adr);
break;
default:
SetError(ERR_SYNTAX30, 0, 0);
break;
}
break;
case 'I':
CloseAllChannels();
SetError(ERR_OK, 0, 0);
break;
case 'U':
if ((command[1] & 0x0f) == 0x0a) {
Reset();
} else
SetError(ERR_SYNTAX30, 0, 0);
break;
case 'G':
if (command[1] != ':')
SetError(ERR_SYNTAX30, 0, 0);
else
ChangeDirCmd(&command[2]);
break;
case 'M':
if (command[1] != '-')
SetError(ERR_SYNTAX30, 0, 0);
else
switch (command[2]) {
case 'R':
adr = (command[4] << 8) | (command[3]);
errorPtr = (char *)(ram + (adr & 0x07ff));
if (!(errorLength = command[5]))
errorLength = 1;
break;
case 'W':
adr = (command[4] << 8) | (command[3]);
len = command[5];
if (adr<0x1000)
for (i=0; i<len; i++)
ram[(adr+i)&0x0FFF] = command[i+6];
break;
case 'E':
adr = (command[4] << 8) | (command[3]);
fprintf( stderr, "M-E ($%04X) : not supported with DOS level emulation.\n", adr);
default:
SetError(ERR_SYNTAX30, 0, 0);
}
break;
default:
SetError(ERR_SYNTAX30, 0, 0);
}
}
void CIECFSDrive::ChangeDirCmd(char *dirpath)
{
char str[NAMEBUF_LENGTH];
char *p = str;
CloseAllChannels();
if (dirpath[0] == '.' && dirpath[1] == 0) {
ChangeDir(orig_dir_path);
} else {
// Convert directory name
for (int i=0; i<NAMEBUF_LENGTH && (*p++ = ToASCII(*dirpath++)); i++) ;
if (!ChangeDir(str))
SetError(ERR_NOTREADY, 0, 0);
}
}