43 |
|
#include "Prefs.h" |
44 |
|
|
45 |
|
|
46 |
– |
// Access modes |
47 |
– |
enum { |
48 |
– |
FMODE_READ, FMODE_WRITE, FMODE_APPEND |
49 |
– |
}; |
50 |
– |
|
51 |
– |
// File types |
52 |
– |
enum { |
53 |
– |
FTYPE_PRG, FTYPE_SEQ, FTYPE_USR, FTYPE_REL |
54 |
– |
}; |
55 |
– |
|
56 |
– |
// Prototypes |
57 |
– |
static bool match(char *p, char *n); |
58 |
– |
|
59 |
– |
|
46 |
|
/* |
47 |
|
* Constructor: Prepare emulation |
48 |
|
*/ |
131 |
|
{ |
132 |
|
uint8 buf[32]; |
133 |
|
uint8 *buf2; |
134 |
< |
char *p; |
134 |
> |
uint8 *p; |
135 |
|
int max, i, j; |
136 |
|
|
137 |
|
// Read header and get maximum number of files contained |
246 |
|
* Open channel |
247 |
|
*/ |
248 |
|
|
249 |
< |
uint8 T64Drive::Open(int channel, char *filename) |
249 |
> |
uint8 T64Drive::Open(int channel, const uint8 *name, int name_len) |
250 |
|
{ |
251 |
|
set_error(ERR_OK); |
252 |
|
|
253 |
|
// Channel 15: Execute file name as command |
254 |
|
if (channel == 15) { |
255 |
< |
execute_command(filename); |
255 |
> |
execute_cmd(name, name_len); |
256 |
|
return ST_OK; |
257 |
|
} |
258 |
|
|
262 |
|
file[channel] = NULL; |
263 |
|
} |
264 |
|
|
265 |
< |
if (filename[0] == '#') { |
265 |
> |
if (name[0] == '#') { |
266 |
|
set_error(ERR_NOCHANNEL); |
267 |
|
return ST_OK; |
268 |
|
} |
272 |
|
return ST_OK; |
273 |
|
} |
274 |
|
|
275 |
< |
if (filename[0] == '$') |
276 |
< |
return open_directory(channel, filename+1); |
275 |
> |
if (name[0] == '$') |
276 |
> |
return open_directory(channel, name + 1, name_len - 1); |
277 |
|
|
278 |
< |
return open_file(channel, filename); |
278 |
> |
return open_file(channel, name, name_len); |
279 |
|
} |
280 |
|
|
281 |
|
|
283 |
|
* Open file |
284 |
|
*/ |
285 |
|
|
286 |
< |
uint8 T64Drive::open_file(int channel, char *filename) |
286 |
> |
uint8 T64Drive::open_file(int channel, const uint8 *name, int name_len) |
287 |
|
{ |
288 |
< |
char plainname[NAMEBUF_LENGTH]; |
289 |
< |
int filemode = FMODE_READ; |
290 |
< |
int filetype = FTYPE_PRG; |
291 |
< |
int num; |
292 |
< |
|
293 |
< |
convert_filename(filename, plainname, &filemode, &filetype); |
294 |
< |
|
295 |
< |
// Channel 0 is READ PRG, channel 1 is WRITE PRG |
296 |
< |
if (!channel) { |
297 |
< |
filemode = FMODE_READ; |
298 |
< |
filetype = FTYPE_PRG; |
299 |
< |
} |
300 |
< |
if (channel == 1) { |
301 |
< |
filemode = FMODE_WRITE; |
302 |
< |
filetype = FTYPE_PRG; |
288 |
> |
uint8 plain_name[NAMEBUF_LENGTH]; |
289 |
> |
int plain_name_len; |
290 |
> |
int mode = FMODE_READ; |
291 |
> |
int type = FTYPE_PRG; |
292 |
> |
int rec_len; |
293 |
> |
parse_file_name(name, name_len, plain_name, plain_name_len, mode, type, rec_len); |
294 |
> |
|
295 |
> |
// Channel 0 is READ, channel 1 is WRITE |
296 |
> |
if (channel == 0 || channel == 1) { |
297 |
> |
mode = channel ? FMODE_WRITE : FMODE_READ; |
298 |
> |
if (type == FTYPE_DEL) |
299 |
> |
type = FTYPE_PRG; |
300 |
> |
} |
301 |
> |
|
302 |
> |
bool writing = (mode == FMODE_WRITE || mode == FMODE_APPEND); |
303 |
> |
|
304 |
> |
// Wildcards are only allowed on reading |
305 |
> |
if (writing && (strchr((const char *)plain_name, '*') || strchr((const char *)plain_name, '?'))) { |
306 |
> |
set_error(ERR_SYNTAX33); |
307 |
> |
return ST_OK; |
308 |
|
} |
309 |
|
|
310 |
|
// Allow only read accesses |
311 |
< |
if (filemode != FMODE_READ) { |
311 |
> |
if (writing) { |
312 |
|
set_error(ERR_WRITEPROTECT); |
313 |
|
return ST_OK; |
314 |
|
} |
315 |
|
|
316 |
+ |
// Relative files are not supported |
317 |
+ |
if (type == FTYPE_REL) { |
318 |
+ |
set_error(ERR_UNIMPLEMENTED); |
319 |
+ |
return ST_OK; |
320 |
+ |
} |
321 |
+ |
|
322 |
|
// Find file |
323 |
< |
if (find_first_file(plainname, filetype, &num)) { |
323 |
> |
int num; |
324 |
> |
if (find_first_file(plain_name, plain_name_len, num)) { |
325 |
|
|
326 |
|
// Open temporary file |
327 |
|
if ((file[channel] = tmpfile()) != NULL) { |
340 |
|
rewind(file[channel]); |
341 |
|
delete[] buf; |
342 |
|
|
343 |
< |
if (filemode == FMODE_READ) // Read and buffer first byte |
343 |
> |
if (mode == FMODE_READ) // Read and buffer first byte |
344 |
|
read_char[channel] = fgetc(file[channel]); |
345 |
|
} |
346 |
|
} else |
351 |
|
|
352 |
|
|
353 |
|
/* |
356 |
– |
* Analyze file name, get access mode and type |
357 |
– |
*/ |
358 |
– |
|
359 |
– |
void T64Drive::convert_filename(char *srcname, char *destname, int *filemode, int *filetype) |
360 |
– |
{ |
361 |
– |
char *p; |
362 |
– |
|
363 |
– |
// Search for ':', p points to first character after ':' |
364 |
– |
if ((p = strchr(srcname, ':')) != NULL) |
365 |
– |
p++; |
366 |
– |
else |
367 |
– |
p = srcname; |
368 |
– |
|
369 |
– |
// Remaining string -> destname |
370 |
– |
strncpy(destname, p, NAMEBUF_LENGTH); |
371 |
– |
|
372 |
– |
// Search for ',' |
373 |
– |
p = destname; |
374 |
– |
while (*p && (*p != ',')) p++; |
375 |
– |
|
376 |
– |
// Look for mode parameters seperated by ',' |
377 |
– |
p = destname; |
378 |
– |
while ((p = strchr(p, ',')) != NULL) { |
379 |
– |
|
380 |
– |
// Cut string after the first ',' |
381 |
– |
*p++ = 0; |
382 |
– |
|
383 |
– |
switch (*p) { |
384 |
– |
case 'P': |
385 |
– |
*filetype = FTYPE_PRG; |
386 |
– |
break; |
387 |
– |
case 'S': |
388 |
– |
*filetype = FTYPE_SEQ; |
389 |
– |
break; |
390 |
– |
case 'U': |
391 |
– |
*filetype = FTYPE_USR; |
392 |
– |
break; |
393 |
– |
case 'L': |
394 |
– |
*filetype = FTYPE_REL; |
395 |
– |
break; |
396 |
– |
case 'R': |
397 |
– |
*filemode = FMODE_READ; |
398 |
– |
break; |
399 |
– |
case 'W': |
400 |
– |
*filemode = FMODE_WRITE; |
401 |
– |
break; |
402 |
– |
case 'A': |
403 |
– |
*filemode = FMODE_APPEND; |
404 |
– |
break; |
405 |
– |
} |
406 |
– |
} |
407 |
– |
} |
408 |
– |
|
409 |
– |
|
410 |
– |
/* |
354 |
|
* Find first file matching wildcard pattern |
355 |
|
*/ |
356 |
|
|
357 |
|
// Return true if name 'n' matches pattern 'p' |
358 |
< |
static bool match(char *p, char *n) |
358 |
> |
static bool match(const uint8 *p, int p_len, const uint8 *n) |
359 |
|
{ |
360 |
< |
if (!*p) // Null pattern matches everything |
418 |
< |
return true; |
419 |
< |
|
420 |
< |
do { |
360 |
> |
while (p_len-- > 0) { |
361 |
|
if (*p == '*') // Wildcard '*' matches all following characters |
362 |
|
return true; |
363 |
|
if ((*p != *n) && (*p != '?')) // Wildcard '?' matches single character |
364 |
|
return false; |
365 |
|
p++; n++; |
366 |
< |
} while (*p); |
366 |
> |
} |
367 |
|
|
368 |
< |
return !(*n); |
368 |
> |
return *n == 0; |
369 |
|
} |
370 |
|
|
371 |
< |
bool T64Drive::find_first_file(char *name, int type, int *num) |
371 |
> |
bool T64Drive::find_first_file(const uint8 *pattern, int pattern_len, int &num) |
372 |
|
{ |
373 |
< |
for (int i=0; i<num_files; i++) |
374 |
< |
if (match(name, file_info[i].name) && type == file_info[i].type) { |
375 |
< |
*num = i; |
373 |
> |
for (int i=0; i<num_files; i++) { |
374 |
> |
if (match(pattern, pattern_len, file_info[i].name)) { |
375 |
> |
num = i; |
376 |
|
return true; |
377 |
|
} |
378 |
< |
|
378 |
> |
} |
379 |
|
return false; |
380 |
|
} |
381 |
|
|
384 |
|
* Open directory, create temporary file |
385 |
|
*/ |
386 |
|
|
387 |
< |
uint8 T64Drive::open_directory(int channel, char *filename) |
387 |
> |
uint8 T64Drive::open_directory(int channel, const uint8 *pattern, int pattern_len) |
388 |
|
{ |
449 |
– |
char buf[] = "\001\004\001\001\0\0\022\042 \042 00 2A"; |
450 |
– |
char str[NAMEBUF_LENGTH]; |
451 |
– |
char pattern[NAMEBUF_LENGTH]; |
452 |
– |
char *p, *q; |
453 |
– |
int i, num; |
454 |
– |
int filemode; |
455 |
– |
int filetype; |
456 |
– |
|
389 |
|
// Special treatment for "$0" |
390 |
< |
if (strlen(filename) == 1 && filename[0] == '0') |
391 |
< |
filename += 1; |
390 |
> |
if (pattern[0] == '0' && pattern_len == 1) { |
391 |
> |
pattern++; |
392 |
> |
pattern_len--; |
393 |
> |
} |
394 |
|
|
395 |
< |
// Convert filename ('$' already stripped), filemode/type are ignored |
396 |
< |
convert_filename(filename, pattern, &filemode, &filetype); |
395 |
> |
// Skip everything before the ':' in the pattern |
396 |
> |
uint8 *t = (uint8 *)memchr(pattern, ':', pattern_len); |
397 |
> |
if (t) { |
398 |
> |
t++; |
399 |
> |
pattern_len -= t - pattern; |
400 |
> |
pattern = t; |
401 |
> |
} |
402 |
|
|
403 |
|
// Create temporary file |
404 |
|
if ((file[channel] = tmpfile()) == NULL) |
405 |
|
return ST_OK; |
406 |
|
|
407 |
|
// Create directory title |
408 |
< |
p = &buf[8]; |
409 |
< |
for (i=0; i<16 && dir_title[i]; i++) |
410 |
< |
*p++ = dir_title[i]; |
408 |
> |
uint8 buf[] = "\001\004\001\001\0\0\022\042 \042 00 2A"; |
409 |
> |
for (int i=0; i<16 && dir_title[i]; i++) |
410 |
> |
buf[i + 8] = dir_title[i]; |
411 |
|
fwrite(buf, 1, 32, file[channel]); |
412 |
|
|
413 |
|
// Create and write one line for every directory entry |
414 |
< |
for (num=0; num<num_files; num++) { |
414 |
> |
for (int num=0; num<num_files; num++) { |
415 |
|
|
416 |
|
// Include only files matching the pattern |
417 |
< |
if (match(pattern, file_info[num].name)) { |
417 |
> |
if (pattern_len == 0 || match(pattern, pattern_len, file_info[num].name)) { |
418 |
|
|
419 |
|
// Clear line with spaces and terminate with null byte |
420 |
|
memset(buf, ' ', 31); |
421 |
|
buf[31] = 0; |
422 |
|
|
423 |
< |
p = buf; |
423 |
> |
uint8 *p = buf; |
424 |
|
*p++ = 0x01; // Dummy line link |
425 |
|
*p++ = 0x01; |
426 |
|
|
427 |
|
// Calculate size in blocks (254 bytes each) |
428 |
< |
i = (file_info[num].length + 254) / 254; |
429 |
< |
*p++ = i & 0xff; |
430 |
< |
*p++ = (i >> 8) & 0xff; |
428 |
> |
int n = (file_info[num].length + 254) / 254; |
429 |
> |
*p++ = n & 0xff; |
430 |
> |
*p++ = (n >> 8) & 0xff; |
431 |
|
|
432 |
|
p++; |
433 |
< |
if (i < 10) p++; // Less than 10: add one space |
434 |
< |
if (i < 100) p++; // Less than 100: add another space |
433 |
> |
if (n < 10) p++; // Less than 10: add one space |
434 |
> |
if (n < 100) p++; // Less than 100: add another space |
435 |
|
|
436 |
|
// Convert and insert file name |
437 |
< |
strcpy(str, file_info[num].name); |
437 |
> |
uint8 str[NAMEBUF_LENGTH]; |
438 |
> |
memcpy(str, file_info[num].name, 17); |
439 |
|
*p++ = '\"'; |
440 |
< |
q = p; |
441 |
< |
for (i=0; i<16 && str[i]; i++) |
440 |
> |
uint8 *q = p; |
441 |
> |
for (int i=0; i<16 && str[i]; i++) |
442 |
|
*q++ = str[i]; |
443 |
|
*q++ = '\"'; |
444 |
|
p += 18; |
563 |
|
{ |
564 |
|
// Channel 15: Collect chars and execute command on EOI |
565 |
|
if (channel == 15) { |
566 |
< |
if (cmd_len >= 40) |
566 |
> |
if (cmd_len >= 58) |
567 |
|
return ST_TIMEOUT; |
568 |
|
|
569 |
< |
cmd_buffer[cmd_len++] = byte; |
569 |
> |
cmd_buf[cmd_len++] = byte; |
570 |
|
|
571 |
|
if (eoi) { |
572 |
< |
cmd_buffer[cmd_len] = 0; |
572 |
> |
execute_cmd(cmd_buf, cmd_len); |
573 |
|
cmd_len = 0; |
634 |
– |
execute_command(cmd_buffer); |
574 |
|
} |
575 |
|
return ST_OK; |
576 |
|
} |
585 |
|
|
586 |
|
|
587 |
|
/* |
588 |
< |
* Execute command string |
588 |
> |
* Execute drive commands |
589 |
|
*/ |
590 |
|
|
591 |
< |
void T64Drive::execute_command(char *command) |
591 |
> |
// RENAME:new=old |
592 |
> |
// ^ ^ |
593 |
> |
// new_file old_file |
594 |
> |
void T64Drive::rename_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_file, int old_file_len) |
595 |
|
{ |
596 |
< |
switch (command[0]) { |
597 |
< |
case 'I': |
598 |
< |
close_all_channels(); |
599 |
< |
set_error(ERR_OK); |
600 |
< |
break; |
659 |
< |
|
660 |
< |
case 'U': |
661 |
< |
if ((command[1] & 0x0f) == 0x0a) { |
662 |
< |
Reset(); |
663 |
< |
} else |
664 |
< |
set_error(ERR_SYNTAX30); |
665 |
< |
break; |
666 |
< |
|
667 |
< |
case 'G': |
668 |
< |
if (command[1] != ':') |
669 |
< |
set_error(ERR_SYNTAX30); |
670 |
< |
else |
671 |
< |
cht64_cmd(&command[2]); |
672 |
< |
break; |
673 |
< |
|
674 |
< |
default: |
675 |
< |
set_error(ERR_SYNTAX30); |
596 |
> |
// Check if destination file is already present |
597 |
> |
int num; |
598 |
> |
if (find_first_file(new_file, new_file_len, num)) { |
599 |
> |
set_error(ERR_FILEEXISTS); |
600 |
> |
return; |
601 |
|
} |
677 |
– |
} |
602 |
|
|
603 |
+ |
// Check if source file is present |
604 |
+ |
if (!find_first_file(old_file, old_file_len, num)) { |
605 |
+ |
set_error(ERR_FILENOTFOUND); |
606 |
+ |
return; |
607 |
+ |
} |
608 |
|
|
609 |
< |
/* |
610 |
< |
* Execute 'G' command |
682 |
< |
*/ |
609 |
> |
set_error(ERR_WRITEPROTECT); |
610 |
> |
} |
611 |
|
|
612 |
< |
void T64Drive::cht64_cmd(char *t64name) |
612 |
> |
// INITIALIZE |
613 |
> |
void T64Drive::initialize_cmd(void) |
614 |
|
{ |
686 |
– |
char str[NAMEBUF_LENGTH]; |
687 |
– |
char *p = str; |
688 |
– |
|
689 |
– |
// Convert .t64 file name |
690 |
– |
for (int i=0; i<NAMEBUF_LENGTH && (*p++ = conv_from_64(*t64name++, false)); i++) ; |
691 |
– |
|
615 |
|
close_all_channels(); |
616 |
+ |
} |
617 |
|
|
618 |
< |
// G:. resets the .t64 file name to its original setting |
619 |
< |
if (str[0] == '.' && str[1] == 0) |
620 |
< |
open_close_t64_file(orig_t64_name); |
697 |
< |
else |
698 |
< |
open_close_t64_file(str); |
699 |
< |
|
700 |
< |
if (the_file == NULL) |
701 |
< |
set_error(ERR_NOTREADY); |
618 |
> |
// VALIDATE |
619 |
> |
void T64Drive::validate_cmd(void) |
620 |
> |
{ |
621 |
|
} |
622 |
|
|
623 |
|
|
631 |
|
cmd_len = 0; |
632 |
|
set_error(ERR_STARTUP); |
633 |
|
} |
715 |
– |
|
716 |
– |
|
717 |
– |
/* |
718 |
– |
* Conversion PETSCII->ASCII |
719 |
– |
*/ |
720 |
– |
|
721 |
– |
uint8 T64Drive::conv_from_64(uint8 c, bool map_slash) |
722 |
– |
{ |
723 |
– |
if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z')) |
724 |
– |
return c ^ 0x20; |
725 |
– |
if ((c >= 0xc1) && (c <= 0xda)) |
726 |
– |
return c ^ 0x80; |
727 |
– |
if ((c == '/') && map_slash && ThePrefs.MapSlash) |
728 |
– |
return '\\'; |
729 |
– |
return c; |
730 |
– |
} |