view src/vtx/vtxfile.c @ 2284:d19b53359b24

cleaned up the sndfile wav plugin, currently limiting it ONLY TO WAV PLAYBACK. if somebody is more experienced with it and wants to restore the other formats, go ahead (maybe change the name of the plugin too?).
author mf0102 <0102@gmx.at>
date Wed, 09 Jan 2008 15:41:22 +0100
parents 33ffac81f4c7
children 3134a0987162
line wrap: on
line source

#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <ctype.h>
#include <inttypes.h>

#include <audacious/plugin.h>

#include "ayemu.h"

/* defined in lh5dec.c */
extern void lh5_decode(unsigned char *inp,unsigned char *outp,unsigned long original_size, unsigned long packed_size);


/* Read 8-bit integer from file.
 * Return 1 if error occurs
 */
static int read_byte(VFSFile *fp, int *p)
{
  int c;
  if ((c = aud_vfs_getc(fp)) == EOF) {
    perror("libayemu: read_byte()");
    return 1;
  }
  *p = c;
  return 0;
}

/* Read 16-bit integer from file.
 * Return 1 if error occurs
 */
static int read_word16(VFSFile *fp, int *p)
{
  int c;
  if ((c = aud_vfs_getc(fp)) == EOF) {
    perror("libayemu: read_word16()");
    return 1;
  }
  *p = c;

  if ((c = aud_vfs_getc(fp)) == EOF) {
    perror("libayemu: read_word16()");
    return 1;
  }
  *p += c << 8;

  return 0;
}

/* read 32-bit integer from file.
 *  Returns 1 if error occurs 
 */
static int read_word32(VFSFile *fp, int32_t *p)
{
  int c;

  if ((c = aud_vfs_getc(fp)) == EOF) {
    perror("libayemu: read_word32()");
    return 1;
  }
  *p = c;

  if ((c = aud_vfs_getc(fp)) == EOF) {
    perror("libayemu: read_word32()");
    return 1;
  }
  *p += c << 8;

  if ((c = aud_vfs_getc(fp)) == EOF) {
    perror("libayemu: read_word32()");
    return 1;
  }
  *p += c << 16;

  if ((c = aud_vfs_getc(fp)) == EOF) {
    perror("libayemu: read_word32()");
    return 1;
  }
  *p += c << 24;

  return 0;
}

/* read_NTstring: reads null-terminated string from file.
 *  Return 1 if error occures.
 */
static int read_NTstring(VFSFile *fp, char s[])
{
  int i, c;
  for (i = 0 ; i < AYEMU_VTX_NTSTRING_MAX && (c = aud_vfs_getc(fp)) != EOF && c ; i++)
    s[i] = c;
  s[i] = '\0';
  if (c == EOF) {
    fprintf(stderr, "libayemu: read_NTstring(): uninspected end of file!\n");
    return 1;
  }
  return 0;
}

/** Open specified .vtx file and read vtx file header
 *
 *  Open specified .vtx file and read vtx file header in struct vtx
 *  Return value: true if success, else false
 */
int ayemu_vtx_open (ayemu_vtx_t *vtx, const char *filename)
{
  char buf[2];
  int error = 0;
  int32_t int_regdata_size;

  vtx->regdata = NULL;

  if ((vtx->fp = aud_vfs_fopen (filename, "rb")) == NULL) {
    fprintf(stderr, "ayemu_vtx_open: Cannot open file %s: %s\n", filename, strerror(errno));
    return 0;
  }

  if (aud_vfs_fread(buf, 2, 1, vtx->fp) != 1) {
    fprintf(stderr,"ayemu_vtx_open: Can't read from %s: %s\n", filename, strerror(errno));
    error = 1;
  }

  buf[0] = tolower(buf[0]);
  buf[1] = tolower(buf[1]);

  if (strncmp(buf, "ay", 2) == 0)
    vtx->hdr.chiptype = AYEMU_AY;
  else if (strncmp (buf, "ym", 2) == 0)
    vtx->hdr.chiptype = AYEMU_YM;
  else {
    fprintf (stderr, "File %s is _not_ VORTEX format!\nIt not begins from AY or YM.\n", filename);
    error = 1;
  }

  /* read VTX header info in order format specified, see http:// ..... */
  if (!error) error = read_byte(vtx->fp, &vtx->hdr.stereo);
  if (!error) error = read_word16(vtx->fp, &vtx->hdr.loop);
  if (!error) error = read_word32(vtx->fp, &vtx->hdr.chipFreq);
  if (!error) error = read_byte(vtx->fp, &vtx->hdr.playerFreq);
  if (!error) error = read_word16(vtx->fp, &vtx->hdr.year);
  if (!error) {
	error = read_word32(vtx->fp, &int_regdata_size);
	vtx->hdr.regdata_size = (size_t) int_regdata_size;
  }

  if (!error) error = read_NTstring(vtx->fp, vtx->hdr.title);
  if (!error) error = read_NTstring(vtx->fp, vtx->hdr.author);
  if (!error) error = read_NTstring(vtx->fp, vtx->hdr.from);
  if (!error) error = read_NTstring(vtx->fp, vtx->hdr.tracker);
  if (!error) error = read_NTstring (vtx->fp, vtx->hdr.comment);

  if (error) {
    aud_vfs_fclose(vtx->fp);
    vtx->fp = NULL;
  }
  return !error;
}

/** Read and encode lha data from .vtx file
 *
 * Return value: pointer to unpacked data or NULL
 * Note: you must call ayemu_vtx_open() first.
 */
char *ayemu_vtx_load_data (ayemu_vtx_t *vtx)
{
  char *packed_data;
  size_t packed_size;
  size_t buf_alloc;
  int c;

  if (vtx->fp == NULL) {
    fprintf(stderr, "ayemu_vtx_load_data: tune file not open yet (do you call ayemu_vtx_open first?)\n");
    return NULL;
  }
  packed_size = 0; 
  buf_alloc = 4096;
  packed_data = (char *) malloc (buf_alloc);
  /* read packed AY register data to end of file. */
  while ((c = aud_vfs_getc (vtx->fp)) != EOF) {
    if (buf_alloc < packed_size) {              
      buf_alloc *= 2;
      packed_data = (char *) realloc (packed_data, buf_alloc);
      if (packed_data == NULL) {
	fprintf (stderr, "ayemu_vtx_load_data: Packed data out of memory!\n");
	aud_vfs_fclose (vtx->fp);
	return NULL;
      }
    }
    packed_data[packed_size++] = c;
  }  
  aud_vfs_fclose (vtx->fp);
  vtx->fp = NULL;
  if ((vtx->regdata = (char *) malloc (vtx->hdr.regdata_size)) == NULL) {
    fprintf (stderr, "ayemu_vtx_load_data: Can allocate %d bytes for unpack register data\n", (int)(vtx->hdr.regdata_size));
    free (packed_data);
    return NULL;
  }
  lh5_decode ((unsigned char *)packed_data, (unsigned char *)(vtx->regdata), vtx->hdr.regdata_size, packed_size);
  free (packed_data);
  vtx->pos = 0;
  return vtx->regdata;
}

/** Get next 14-bytes frame of AY register data.
 *
 * Return value: 1 if sucess, 0 if no enought data.
 */
int ayemu_vtx_get_next_frame (ayemu_vtx_t *vtx, char *regs)
{
  int numframes = vtx->hdr.regdata_size / 14;
  if (vtx->pos++ >= numframes)
    return 0;
  else {
    int n;
    char *p = vtx->regdata + vtx->pos;
    for(n = 0 ; n < 14 ; n++, p+=numframes)
      regs[n] = *p;
    return 1;
  }
}

static void append_string(char *buf, const int sz, const char *str)
{
  if (strlen(buf) + strlen(str) < sz - 1)
    strcat(buf, str);
}

static void append_number(char *buf, const int sz, const int num)
{
  char s[32];
  snprintf(s, sizeof(s), "%d", num);
  return append_string(buf, sz, s);
}

static void append_char(char *buf, const int sz, const char c)
{
  int pos = strlen(buf);
  if (pos < sz - 1)
    buf[pos++] = c;
  buf[pos] = '\0';
}

/** Print formated file name. If fmt is NULL the default format %a - %t will used
 *
 * %% the % sign
 * %a author of song
 * %t song title
 * %y year
 * %f song from
 * %T Tracker
 * %C Comment
 * %s stereo type (ABC, BCA, ...)
 * %l 'looped' or 'non-looped'
 * %c chip type: 'AY' or 'YM'
 * %F chip Freq
 * %P player freq
 */
void ayemu_vtx_sprintname (const ayemu_vtx_t *vtx, char *const buf, const int sz, const char *fmt)
{
  static char *stereo_types[] = { "MONO", "ABC", "ACB", "BAC", "BCA", "CAB", "CBA" };

  if (fmt == NULL)
    fmt = "%a - %t";

  buf[0] = '\0';

  while (*fmt != '\0') {
    if (*fmt == '%') {
      switch(*++fmt) {
      case 'a':
	append_string(buf, sz, vtx->hdr.author);
	break;
      case 't':
	append_string(buf, sz, vtx->hdr.title);
	break;
      case 'y':
	append_number(buf, sz, vtx->hdr.year);
	break;
      case 'f':
	append_string(buf, sz, vtx->hdr.from);
	break;
      case 'T':
	append_string(buf, sz, vtx->hdr.tracker);
	break;
      case 'C':
	append_string(buf, sz, vtx->hdr.comment);
	break;
      case 's':
	append_string(buf, sz, stereo_types[vtx->hdr.stereo]);
	break;
      case 'l':
	append_string(buf, sz, (vtx->hdr.loop)? "looped" : "non-looped" );
	break;
      case 'c':
	append_string(buf, sz, (vtx->hdr.chiptype == AYEMU_AY)? "AY" : "YM" );
	break;
      case 'F':
	append_number(buf, sz, vtx->hdr.chipFreq);
	break;
      case 'P':
	append_number(buf, sz, vtx->hdr.playerFreq);
	break;
      default:
	append_char(buf, sz, *fmt);
      }
      fmt++;
    } else {
      append_char(buf, sz, *fmt++);
    }
  }
}

/** Free all of allocaded resource for this file.
 *
 * Free the unpacket register data if is and close file.
 */
void ayemu_vtx_free (ayemu_vtx_t *vtx)
{
  if (vtx->fp) {
    aud_vfs_fclose(vtx->fp);
    vtx->fp = NULL;
  }

  if (vtx->regdata) {
    free(vtx->regdata);
    vtx->regdata = NULL;
  }
}