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

1541t64.cpp

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

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