/* sod2, a player for polychannel .csf music files.
 * Copyright (C) 1995 Russell Marks. See sod2.c for license details.
 *
 * tarfile.c - code to read files from a tar file.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "sod2.h"
#include "tarfile.h"


extern void die(char *str);

int tar_csf_file_size;



/* The tar record union is taken from the `tar' info file, which says:
 * "This chapter is based heavily on John Gilmore's tar(5) manual page for
 * the public domain `tar' that GNU `tar' is based on."
 * I've stripped it down to a minimum, i.e. removed the GNU extensions.
 */

#define RECORDSIZE  	512
#define NAMSIZ      	100
#define TUNMLEN      	32
#define TGNMLEN      	32

union {
  char charptr[RECORDSIZE];
  struct {
    char name[NAMSIZ];		/* This I want... */
    char mode[8];
    char uid[8];
    char gid[8];
    char size[12];		/* ...and this. The rest can go whistle. */
    char mtime[12];
    char chksum[8];
    char linkflag;
    char linkname[NAMSIZ];
    char magic[8];		/* not much use in practice :-( */
    char uname[TUNMLEN];
    char gname[TGNMLEN];
    char devmajor[8];
    char devminor[8];
    } header;
  } trec;


/* round up (a file size) to the length in 512-byte records. */
#define TAR_ROUND_UP(x)	(((x)+511)&~511)



/* Is this a tar file?
 * well, there isn't much to go on. Luckily, a csf file must start with
 * a '#' (comment) or a '*' (for "*tempo", "*bsize" or "*samples").
 * Since a filename is unlikely to start with those, it'll do.
 */
int is_tar_file(FILE *in)
{
int c;

c=fgetc(in); ungetc(c,in);
if(c=='#' || c=='*') return(0);
return(1);
}


/* If we find it's a tar file, the next thing to do is advance
 * the file pointer to read the first file in the archive, which must
 * be the csf file to play. We also need to find out how long the
 * csf file in the archive is, so that we know when to pretend we've
 * read EOF.
 */
void tar_csf_setup(FILE *in)
{
if(fread(trec.charptr,RECORDSIZE,1,in)!=1) die("read tar file record");
if(sscanf(trec.header.size,"%o",&tar_csf_file_size)!=1)
  die("understand csf file size in tar file");

/* now test if we can fseek() on it. */
if(fseek(in,ftell(in),SEEK_SET)==-1)
  die("fseek on tar file");
}


/* load a sample from an open tar file.
 * should exit with 'in' at the same point in the file as it entered with.
 */
void tar_load_sample(FILE *in,char *filename)
{
int oldpos=ftell(in);
int found=0;
int len;

rewind(in);

/* grind through the file until we find it (or EOF) */
while(fread(trec.charptr,RECORDSIZE,1,in)==1)
  {
  sscanf(trec.header.size,"%o",&len);
  if(strcmp(trec.header.name,filename)==0)
    {
    found=1;
    break;
    }
  
  /* skip it if not the right one */
  fseek(in,ftell(in)+TAR_ROUND_UP(len),SEEK_SET);
  }

if(found==0) die("find sample file in tar archive");

/* load the sample */

if((samples[next_sample].data=malloc(len))==NULL) die("malloc");
if(fread(samples[next_sample].data,1,len,in)!=len) die("fread");
fseek(in,oldpos,SEEK_SET);
samples[next_sample].len=len;

if(verbose)
  fprintf(stderr,"[tar file] %s %d\n",filename,len);
}
