/* * 1541t64.cpp - 1541 emulation in .t64/LYNX file * * Frodo (C) 1994-1997,2002 Christian Bauer * * Notes: * ------ * * - If any file is opened, the contents of the file in the * .t64 file are copied into a temporary file which is used * for reading. This is done to insert the load address. * - C64 LYNX archives are also handled by these routines * * Incompatibilities: * ------------------ * * - Only read accesses possible * - No "raw" directory reading * - No relative/sequential/user files * - Only "I" and "UJ" commands implemented */ #include "sysdeps.h" #include "1541t64.h" #include "IEC.h" #include "Prefs.h" // Access modes enum { FMODE_READ, FMODE_WRITE, FMODE_APPEND }; // File types enum { FTYPE_PRG, FTYPE_SEQ, FTYPE_USR, FTYPE_REL }; // Prototypes static bool match(char *p, char *n); /* * Constructor: Prepare emulation */ T64Drive::T64Drive(IEC *iec, char *filepath) : Drive(iec) { the_file = NULL; file_info = NULL; Ready = false; strcpy(orig_t64_name, filepath); for (int i=0; i<16; i++) file[i] = NULL; // Open .t64 file open_close_t64_file(filepath); if (the_file != NULL) { Reset(); Ready = true; } } /* * Destructor */ T64Drive::~T64Drive() { // Close .t64 file open_close_t64_file(""); Ready = false; } /* * Open/close the .t64/LYNX file */ void T64Drive::open_close_t64_file(char *t64name) { uint8 buf[64]; bool parsed_ok = false; // Close old .t64, if open if (the_file != NULL) { close_all_channels(); fclose(the_file); the_file = NULL; delete[] file_info; file_info = NULL; } // Open new .t64 file if (t64name[0]) { if ((the_file = fopen(t64name, "rb")) != NULL) { // Check file ID fread(&buf, 64, 1, the_file); if (buf[0] == 0x43 && buf[1] == 0x36 && buf[2] == 0x34) { is_lynx = false; parsed_ok = parse_t64_file(); } else if (buf[0x3c] == 0x4c && buf[0x3d] == 0x59 && buf[0x3e] == 0x4e && buf[0x3f] == 0x58) { is_lynx = true; parsed_ok = parse_lynx_file(); } if (!parsed_ok) { fclose(the_file); the_file = NULL; delete[] file_info; file_info = NULL; return; } } } } /* * Parse .t64 file and construct FileInfo array */ bool T64Drive::parse_t64_file(void) { uint8 buf[32]; uint8 *buf2; char *p; int max, i, j; // Read header and get maximum number of files contained fseek(the_file, 32, SEEK_SET); fread(&buf, 32, 1, the_file); max = (buf[3] << 8) | buf[2]; memcpy(dir_title, buf+8, 16); // Allocate buffer for file records and read them buf2 = new uint8[max*32]; fread(buf2, 32, max, the_file); // Determine number of files contained for (i=0, num_files=0; i destname strncpy(destname, p, NAMEBUF_LENGTH); // Search for ',' p = destname; while (*p && (*p != ',')) p++; // Look for mode parameters seperated by ',' p = destname; while ((p = strchr(p, ',')) != NULL) { // Cut string after the first ',' *p++ = 0; switch (*p) { case 'P': *filetype = FTYPE_PRG; break; case 'S': *filetype = FTYPE_SEQ; break; case 'U': *filetype = FTYPE_USR; break; case 'L': *filetype = FTYPE_REL; break; case 'R': *filemode = FMODE_READ; break; case 'W': *filemode = FMODE_WRITE; break; case 'A': *filemode = FMODE_APPEND; break; } } } /* * Find first file matching wildcard pattern */ // Return true if name 'n' matches pattern 'p' static bool match(char *p, char *n) { if (!*p) // Null pattern matches everything return true; do { if (*p == '*') // Wildcard '*' matches all following characters return true; if ((*p != *n) && (*p != '?')) // Wildcard '?' matches single character return false; p++; n++; } while (*p); return !(*n); } bool T64Drive::find_first_file(char *name, int type, int *num) { for (int i=0; i> 8) & 0xff; p++; if (i < 10) p++; // Less than 10: add one space if (i < 100) p++; // Less than 100: add another space // Convert and insert file name strcpy(str, file_info[num].name); *p++ = '\"'; q = p; for (i=0; i<16 && str[i]; i++) *q++ = str[i]; *q++ = '\"'; p += 18; // File type switch (file_info[num].type) { case FTYPE_PRG: *p++ = 'P'; *p++ = 'R'; *p++ = 'G'; break; case FTYPE_SEQ: *p++ = 'S'; *p++ = 'E'; *p++ = 'Q'; break; case FTYPE_USR: *p++ = 'U'; *p++ = 'S'; *p++ = 'R'; break; case FTYPE_REL: *p++ = 'R'; *p++ = 'E'; *p++ = 'L'; break; default: *p++ = '?'; *p++ = '?'; *p++ = '?'; break; } // Write line fwrite(buf, 1, 32, file[channel]); } } // Final line fwrite("\001\001\0\0BLOCKS FREE. \0\0", 1, 32, file[channel]); // Rewind file for reading and read first byte rewind(file[channel]); read_char[channel] = fgetc(file[channel]); return ST_OK; } /* * Close channel */ uint8 T64Drive::Close(int channel) { if (channel == 15) { close_all_channels(); return ST_OK; } if (file[channel]) { fclose(file[channel]); file[channel] = NULL; } return ST_OK; } /* * Close all channels */ void T64Drive::close_all_channels(void) { for (int i=0; i<15; i++) Close(i); cmd_len = 0; } /* * Read from channel */ uint8 T64Drive::Read(int channel, uint8 *byte) { int c; // Channel 15: Error channel if (channel == 15) { *byte = *error_ptr++; if (*byte != '\r') return ST_OK; else { // End of message set_error(ERR_OK); return ST_EOF; } } if (!file[channel]) return ST_READ_TIMEOUT; // Get char from buffer and read next *byte = read_char[channel]; c = fgetc(file[channel]); if (c == EOF) return ST_EOF; else { read_char[channel] = c; return ST_OK; } } /* * Write to channel */ uint8 T64Drive::Write(int channel, uint8 byte, bool eoi) { // Channel 15: Collect chars and execute command on EOI if (channel == 15) { if (cmd_len >= 40) return ST_TIMEOUT; cmd_buffer[cmd_len++] = byte; if (eoi) { cmd_buffer[cmd_len] = 0; cmd_len = 0; execute_command(cmd_buffer); } return ST_OK; } if (!file[channel]) set_error(ERR_FILENOTOPEN); else set_error(ERR_WRITEPROTECT); return ST_TIMEOUT; } /* * Execute command string */ void T64Drive::execute_command(char *command) { switch (command[0]) { case 'I': close_all_channels(); set_error(ERR_OK); break; case 'U': if ((command[1] & 0x0f) == 0x0a) { Reset(); } else set_error(ERR_SYNTAX30); break; case 'G': if (command[1] != ':') set_error(ERR_SYNTAX30); else cht64_cmd(&command[2]); break; default: set_error(ERR_SYNTAX30); } } /* * Execute 'G' command */ void T64Drive::cht64_cmd(char *t64name) { char str[NAMEBUF_LENGTH]; char *p = str; // Convert .t64 file name for (int i=0; iASCII */ uint8 T64Drive::conv_from_64(uint8 c, bool map_slash) { if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z')) return c ^ 0x20; if ((c >= 0xc1) && (c <= 0xda)) return c ^ 0x80; if ((c == '/') && map_slash && ThePrefs.MapSlash) return '\\'; return c; }