00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "sysdeps.h"
00025
00026 #include "1541t64.h"
00027 #include "IEC.h"
00028 #include "Prefs.h"
00029
00030
00031
00032 enum {
00033 FMODE_READ, FMODE_WRITE, FMODE_APPEND
00034 };
00035
00036
00037 enum {
00038 FTYPE_PRG, FTYPE_SEQ, FTYPE_USR, FTYPE_REL
00039 };
00040
00041
00042 static bool match(char *p, char *n);
00043
00044
00045
00046
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
00065 open_close_t64_file(filepath);
00066 if (the_file != NULL) {
00067 Reset();
00068 Ready = true;
00069 }
00070 }
00071
00072
00073
00074
00075
00076
00077 T64Drive::~T64Drive()
00078 {
00079
00080 open_close_t64_file("");
00081
00082 Ready = false;
00083
00084 DTOR(T64Drive);
00085 }
00086
00087
00088
00089
00090
00091
00092 void T64Drive::open_close_t64_file(char *t64name)
00093 {
00094 uint8 buf[64];
00095 bool parsed_ok = false;
00096
00097
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
00107 if (t64name[0]) {
00108 if ((the_file = fopen(t64name, "rb")) != NULL) {
00109
00110
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
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
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
00151 buf2 = new uint8[max*32];
00152 fread(buf2, 32, max, the_file);
00153
00154
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
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
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
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
00198 strcpy(dir_title, "LYNX ARCHIVE ");
00199
00200
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
00209 file_info = new FileInfo[num_files];
00210 cur_offset = dir_blocks * 254;
00211 for (i=0; i<num_files; i++) {
00212
00213
00214 fread(file_info[i].name, 16, 1, the_file);
00215
00216
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
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;
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
00253
00254
00255 uint8 T64Drive::Open(int channel, char *filename)
00256 {
00257 set_error(ERR_OK);
00258
00259
00260 if (channel == 15) {
00261 execute_command(filename);
00262 return ST_OK;
00263 }
00264
00265
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
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
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
00312 if (filemode != FMODE_READ) {
00313 set_error(ERR_WRITEPROTECT);
00314 return ST_OK;
00315 }
00316
00317
00318 if (find_first_file(plainname, filetype, &num)) {
00319
00320
00321 if ((file[channel] = tmpfile()) != NULL) {
00322
00323
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
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)
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
00349
00350
00351 void T64Drive::convert_filename(char *srcname, char *destname, int *filemode, int *filetype)
00352 {
00353 char *p;
00354
00355
00356 if ((p = strchr(srcname, ':')) != NULL)
00357 p++;
00358 else
00359 p = srcname;
00360
00361
00362 strncpy(destname, p, NAMEBUF_LENGTH);
00363
00364
00365 p = destname;
00366 while (*p && (*p != ',')) p++;
00367
00368
00369 p = destname;
00370 while ((p = strchr(p, ',')) != NULL) {
00371
00372
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
00404
00405
00406
00407 static bool match(char *p, char *n)
00408 {
00409 if (!*p)
00410 return true;
00411
00412 do {
00413 if (*p == '*')
00414 return true;
00415 if ((*p != *n) && (*p != '?'))
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
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
00450 if (strlen(filename) == 1 && filename[0] == '0')
00451 filename += 1;
00452
00453
00454 convert_filename(filename, pattern, &filemode, &filetype);
00455
00456
00457 if ((file[channel] = tmpfile()) == NULL)
00458 return ST_OK;
00459
00460
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
00467 for (num=0; num<num_files; num++) {
00468
00469
00470 if (match(pattern, file_info[num].name)) {
00471
00472
00473 memset(buf, ' ', 31);
00474 buf[31] = 0;
00475
00476 p = buf;
00477 *p++ = 0x01;
00478 *p++ = 0x01;
00479
00480
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++;
00487 if (i < 100) p++;
00488
00489
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
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
00528 fwrite(buf, 1, 32, file[channel]);
00529 }
00530 }
00531
00532
00533 fwrite("\001\001\0\0BLOCKS FREE. \0\0", 1, 32, file[channel]);
00534
00535
00536 rewind(file[channel]);
00537 read_char[channel] = fgetc(file[channel]);
00538
00539 return ST_OK;
00540 }
00541
00542
00543
00544
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
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
00578
00579
00580 uint8 T64Drive::Read(int channel, uint8 *byte)
00581 {
00582 int c;
00583
00584
00585 if (channel == 15) {
00586 *byte = *error_ptr++;
00587
00588 if (*byte != '\r')
00589 return ST_OK;
00590 else {
00591 set_error(ERR_OK);
00592 return ST_EOF;
00593 }
00594 }
00595
00596 if (!file[channel]) return ST_READ_TIMEOUT;
00597
00598
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
00612
00613
00614 uint8 T64Drive::Write(int channel, uint8 byte, bool eoi)
00615 {
00616
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
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
00674
00675
00676 void T64Drive::cht64_cmd(char *t64name)
00677 {
00678 char str[NAMEBUF_LENGTH];
00679 char *p = str;
00680
00681
00682 for (int i=0; i<NAMEBUF_LENGTH && (*p++ = conv_from_64(*t64name++, false)); i++) ;
00683
00684 close_all_channels();
00685
00686
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
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
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 }