This is tape.c in view mode; [Download] [Up]
/* * tape.h - Cassette drive and P00. * * Written by: * Dan Fandrich (dan@fch.wimsey.bc.ca) * Jouko Valta (jopi@stekt.oulu.fi) * Martin Pottendorfer (Martin.Pottendorfer@autalcatel.at) * Ettore Perazzoli (ettore@comm2000.it) * * This file is part of VICE, the Versatile Commodore Emulator. * See README for copyright notice. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA. * */ /* FIXME: P00 should be moved from here. */ #include "vice.h" #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <unistd.h> #include "drive.h" #include "tape.h" #include "zfile.h" #include "utils.h" #include "charsets.h" #ifndef SEEK_SET #define SEEK_SET 0 typedef long fpos_t; #endif #ifdef __GNUC__ #define PACK __attribute__ ((packed)) /* pack structures on byte boundaries */ #else #define PACK /* pack using a compiler switch instead */ #endif typedef struct { BYTE Magic[8] PACK; char CbmName[17] PACK; BYTE RecordSize PACK; /* REL file record size */ } X00HDR; /* Global */ extern char *slot_type[]; static const BYTE MagicHeaderP00[8] = "C64File\0"; /*---------------------------------------------------------------------------*/ /* * Check if given filename has valid P00 extension. * Return the filetype or -1 if no match. */ int is_pc64name (const char *name) { int t = -1; char *p; if (name == NULL || (p = strrchr(name, '.')) == NULL || strlen(++p) != 3) return (-1); if (!isdigit((int)p[1]) || !isdigit((int)p[2])) return (-1); switch (toupper(*p)) { case 'D': t = FT_DEL; break; case 'S': t = FT_SEQ; break; case 'P': t = FT_PRG; break; case 'U': t = FT_USR; break; case 'R': t = FT_REL; break; } return (t); } /* * The read pointer must be positioned at the beginning of the * file before calling this routine. (stdin cannot be fseek(2)'ed) */ int read_pc64header (FILE *fd, char *name, int *reclen) { X00HDR hdr; if (fread (&hdr, sizeof(X00HDR), 1, fd) != 1) { fprintf (stderr, "\nP00 header read failed.\n"); return (FD_RDERR); } if (memcmp (hdr.Magic, MagicHeaderP00, 8) != 0) return (FD_BADIMAGE); strncpy (name, hdr.CbmName, 16); *reclen = hdr.RecordSize; /* REL file record size */ return (FD_OK); } int write_pc64header (FILE *fd, const char *name, int reclen) { X00HDR hdr; memset (&hdr, 0, sizeof(X00HDR)); strncpy ((char *)hdr.Magic, (char *)MagicHeaderP00, 8); strncpy (hdr.CbmName, name, 16); hdr.RecordSize = reclen; /* REL file record size */ if (fseek(fd, 0, SEEK_SET) != 0) return (FD_WRTERR); return (fwrite(&hdr, sizeof(X00HDR), 1, fd)); } /* * If the file is a PC64 file, read the actual filename from the header. * Otherwise, return the filename converted to appropriate PETSCII * representation. * * This function cannot be used on stdin. */ char *pc_get_cbmname (FILE *fd, char *fsname) { static char cbmname[24]; char *p; int type; int reclen; type = is_pc64name (fsname); fseek(fd, 0, SEEK_SET); if (type >= 0 && read_pc64header (fd, cbmname, &reclen) == FD_OK) { for (p = cbmname; *p && (BYTE)*p != 0xA0; ++p); sprintf (p, ",%s", slot_type[type]); } else { rewind (fd); /* There is no header */ /* name */ if ((p = strrchr(fsname, '/')) == NULL || !p++) p = fsname; if (!p || !*p) return (NULL); /* Get Basename and extension from FSname */ strncpy (cbmname, p, 16); if ((p = strrchr(fsname, '.')) != NULL && strlen(++p) == 3) { ; } if (type >= 0) { p = cbmname + strlen(cbmname); sprintf (p, ",%s", slot_type[type]); } else strcat (cbmname, ",prg"); /* Best default */ } /* !p00 */ return (cbmname); } /* ------------------------------------------------------------------------- */ /* * Read directory * * P00 is just a regular file with a simple header prepended, so just read the * header and return the name and addresses evaluated. * * This function cannot be used on stdin. */ int DirP00(FILE *fd, char *CbmName, int *start, int *end) { X00HDR hdr; unsigned int FileLength; int realtype = -1; char *FileExt; BYTE buf[2]; if (fseek(fd, 0, SEEK_SET) != 0) return (FD_RDERR); if (fread(&hdr, sizeof(hdr), 1, fd) != 1) { return (FD_RDERR); } /* If archive type is unknown, see if file is REL */ if (/*(realtype == X00) && */ (hdr.RecordSize > 0)) realtype = FT_REL; switch (realtype) { case FT_DEL: FileExt = "DEL"; break; case FT_SEQ: FileExt = "SEQ"; break; case FT_PRG: FileExt = "PRG"; break; case FT_USR: FileExt = "USR"; break; case FT_REL: FileExt = "REL"; break; default: FileExt = "???"; } strncpy(CbmName, (char *) hdr.CbmName, 16); CbmName[16] = 0; /* never need this on a good P00 file */ strcat (CbmName, ","); strcat (CbmName, FileExt); /* Start and End Addresses */ FileLength = file_length(fileno(fd)) - sizeof(hdr) -2; /* skip start addr */ if (fread(buf, 2, 1, fd) != 1) { return (FD_RDERR); } *start= *buf | (buf[1] << 8); *end = *start + FileLength; return (FD_OK); } /* ------------------------------------------------------------------------- */ /* * T64 Tape Image handling functions */ #define TAPE_HDR_SIZE 64 #define TAPE_DIR_SIZE 32 #define TAPE64_HEADERS 2 static const char *MagicHeaderT64[] = { "C64 tape image file", "C64S tape file", "C64S tape image file" /* EP */ }; /* ------------------------------------------------------------------------- */ /* * Check if given filename has valid .T64 extension. * Return the filetype or -1 if no match. */ int is_t64name (const char *name) { char *p; if (name == NULL || (p = strrchr(name, '.')) == NULL || strlen(++p) != 3) return (-1); if (toupper(*p) == 'T' && p[1] == '6' && p[2] == '4') return (1); return (-1); } /* * Check the image for a valid T64 header and return the * number of files it carries. */ int check_t64_header (FILE *fd) { BYTE buf[TAPE_HDR_SIZE]; int maxentries = 0; if (fseek(fd, 0, SEEK_SET) != 0) return (FD_RDERR); fread(buf, 1, TAPE_HDR_SIZE, fd); if (memcmp(buf, (BYTE *)MagicHeaderT64[0], strlen(MagicHeaderT64[0])) && memcmp(buf, (BYTE *)MagicHeaderT64[1], strlen(MagicHeaderT64[1])) && memcmp(buf, (BYTE *)MagicHeaderT64[2], strlen(MagicHeaderT64[2]))) return (FD_BADIMAGE); if (buf[32] != 0 || buf[33] != 1) fprintf (stderr, "Warning: Unknown tape version.\n"); maxentries = buf[34]; maxentries |= (buf[35] << 8); if (!maxentries) { printf("Cannot locate any files on the tape.\n"); return (FD_BADIMAGE); /* tapeimage, that is */ } printf("Tape version %d.%.2d with %2d out of %d files.\n", buf[33], buf[32], (buf[37] << 8) | buf[36], maxentries); return (maxentries); } /* ------------------------------------------------------------------------- */ /* Read the list of files in the T64 image and return it as a long malloc'ed ASCII string. */ char *read_tape_image_contents(const char *fname) { BYTE inbuf[TAPE_HDR_SIZE + 1]; char linebuf[512], *outbuf; int outbuf_size, max_outbuf_size; int no_entries, max_entries, res, fd, len, i; fd = zopen(fname, O_RDONLY, 0); if (fd < 0) return NULL; /* Check whether this really looks like a tape image. */ if (read (fd, inbuf, TAPE_HDR_SIZE) != TAPE_HDR_SIZE || ((memcmp(inbuf, (BYTE *)MagicHeaderT64[0], strlen(MagicHeaderT64[0])) && memcmp(inbuf, (BYTE *)MagicHeaderT64[1], strlen(MagicHeaderT64[1])) && memcmp(inbuf, (BYTE *)MagicHeaderT64[2], strlen(MagicHeaderT64[2]))))) { zclose(fd); return NULL; } max_entries = (unsigned int)inbuf[34] + (unsigned int)inbuf[35] * 256; no_entries = (unsigned int)inbuf[36] + (unsigned int)inbuf[37] * 256; /* Check a little bit... */ if (no_entries > max_entries) { fprintf(stderr, "Tape inconsistency, giving up!\n"); zclose(fd); return NULL; } /* Many T64 images are broken... */ if (!no_entries) no_entries = 1; /* Seek to start of file records. */ if (lseek (fd, 64, SEEK_SET) < 0) { perror("lseek to file records failed"); zclose(fd); return NULL; } /* Allocate the buffer. */ outbuf_size = 0; max_outbuf_size = 4096; outbuf = xmalloc(max_outbuf_size); inbuf[40 + 24] = '\0'; len = sprintf(linebuf, "0 .\"%s\"\n", &inbuf[40]); bufcat(outbuf, &outbuf_size, &max_outbuf_size, linebuf, len); for (i = 0; i < no_entries; i++) { char *ext; int start_addr, end_addr; int blocks; res = read(fd, inbuf, 32); if (res != 32) { perror("read of file record failed"); zclose(fd); free(outbuf); return NULL; } if (inbuf[0] == 0) /* Don't print free slots. */ continue; else if (inbuf[0] == 1) ext = "PRG"; else ext = "???"; /* Cut name. */ inbuf[32] = '\0'; start_addr = (unsigned int)inbuf[2] + (unsigned int)inbuf[3] * 256; end_addr = (unsigned int)inbuf[4] + (unsigned int)inbuf[5] * 256; blocks = (end_addr - start_addr) / 256 + 1; if (blocks <= 0) len = sprintf(linebuf, "??? \"%s\" %s\n", &inbuf[16], ext); else len = sprintf(linebuf, "%-6d\"%s\" %s\n", blocks, &inbuf[16], ext); bufcat(outbuf, &outbuf_size, &max_outbuf_size, linebuf, len); } len = sprintf(linebuf, "%d/%d ENTRIES USED.\n", no_entries, max_entries); bufcat(outbuf, &outbuf_size, &max_outbuf_size, linebuf, len); /* Add trailing zero. */ *(outbuf + outbuf_size) = '\0'; /* Convert from PETSCII to ASCII. */ petconvstring(outbuf, 1); zclose(fd); return outbuf; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.