ftp.nice.ch/pub/next/tools/emulators/vice.0.15.0.NeXT.sd.tgz#/vice-0.15.0/src/tape.c

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.