Diff of /src/libxineadec/nosefart/nsf.c [e466b8] .. [87e73d] Maximize Restore

  Switch to side-by-side view

--- a/src/libxineadec/nosefart/nsf.c
+++ b/src/libxineadec/nosefart/nsf.c
@@ -22,6 +22,7 @@
 ** NSF loading/saving related functions
 ** $Id: nsf.c,v 1.4 2006/09/26 00:52:17 dgp85 Exp $
 */
+
 
 #include <stdio.h>
 #include <string.h>
@@ -50,25 +51,34 @@
 
 static uint8 read_mirrored_ram(uint32 address)
 {
-   return cur_nsf->cpu->mem_page[0][address & 0x7FF];
+  nes6502_chk_mem_access(&cur_nsf->cpu->acc_mem_page[0][address & 0x7FF],
+			 NES6502_READ_ACCESS);
+  return cur_nsf->cpu->mem_page[0][address & 0x7FF];
 }
 
 static void write_mirrored_ram(uint32 address, uint8 value)
 {
-   cur_nsf->cpu->mem_page[0][address & 0x7FF] = value;
+  nes6502_chk_mem_access(&cur_nsf->cpu->acc_mem_page[0][address & 0x7FF],
+			 NES6502_WRITE_ACCESS);
+  cur_nsf->cpu->mem_page[0][address & 0x7FF] = value;
 }
 
 /* can be used for both banked and non-bankswitched NSFs */
 static void nsf_bankswitch(uint32 address, uint8 value)
 {
    int cpu_page;
+   int roffset;
    uint8 *offset;
 
    cpu_page = address & 0x0F;
-   offset = (cur_nsf->data - (cur_nsf->load_addr & 0x0FFF)) + (value << 12);
+   roffset = -(cur_nsf->load_addr & 0x0FFF) + ((int)value << 12);
+   offset = cur_nsf->data + roffset;
 
    nes6502_getcontext(cur_nsf->cpu);
    cur_nsf->cpu->mem_page[cpu_page] = offset;
+#ifdef NES6502_MEM_ACCESS_CTRL
+   cur_nsf->cpu->acc_mem_page[cpu_page] = offset + cur_nsf->length;
+#endif
    nes6502_setcontext(cur_nsf->cpu);
 }
 
@@ -248,6 +258,18 @@
    memset(nsf->cpu->mem_page[6], 0, 0x1000);
    memset(nsf->cpu->mem_page[7], 0, 0x1000);
 
+#ifdef NES6502_MEM_ACCESS_CTRL
+   memset(nsf->cpu->acc_mem_page[0], 0, 0x800);
+   memset(nsf->cpu->acc_mem_page[6], 0, 0x1000);
+   memset(nsf->cpu->acc_mem_page[7], 0, 0x1000);
+   memset(nsf->data+nsf->length, 0, nsf->length);
+#endif
+   nsf->cur_frame = 0;
+/*    nsf->last_access_frame = 0; */
+   nsf->cur_frame_end = !nsf->song_frames
+     ? 0
+     : nsf->song_frames[nsf->current_song];
+   
    if (nsf->bankswitched)
    {
       /* the first hack of the NSF spec! */
@@ -289,36 +311,81 @@
 
 void nsf_frame(nsf_t *nsf)
 {
-   //nsf_setcontext(nsf); /* future expansion =) */
+    // This is how Matthew Conte left it
+    //nsf_setcontext(nsf); /* future expansion =) */
+
+    // This was suggested by Arne Morten Kvarving, who says:
+/*	Also, I fixed a bug that prevented Nosefart to play multiple tunes at
+	one time (actually it was just a few missing setcontext calls in the
+	playback routine, it had a nice TODO commented beside it. You had to set
+	the cpu and apu contexts not just the nsf context).
+
+	it will affect any player that tries to use nosefart to play more than one
+	tune at a time.
+*/
+    nsf_setcontext(nsf);
+    apu_setcontext(nsf->apu);
+    nes6502_setcontext(nsf->cpu);
 
    /* one frame of NES processing */
    nsf_setup_routine(nsf->play_addr, 0, 0);
    nes6502_execute((int) NES_FRAME_CYCLES);
+
+   ++nsf->cur_frame;
+#if defined(NES6502_MEM_ACCESS_CTRL) && 0
+   if (nes6502_mem_access) {
+     uint32 sec =
+       (nsf->last_access_frame + nsf->playback_rate - 1) / nsf->playback_rate;
+     nsf->last_access_frame = nsf->cur_frame;
+     fprintf(stderr,"nsf : memory access [%x] at frame #%u [%u:%02u]\n",
+		nes6502_mem_access,
+		nsf->last_access_frame,
+		sec/60, sec%60);
+   }
+#endif
+
 }
 
 /* Deallocate memory */
-static void nes_shutdown(nsf_t *nsf)
+void nes_shutdown(nsf_t *nsf)
 {
    int i;
 
    ASSERT(nsf);
-   
+
    if (nsf->cpu)
    {
       if (nsf->cpu->mem_page[0])
-         free(nsf->cpu->mem_page[0]);
-      for (i = 5; i <= 7; i++)
-      {
-         if (nsf->cpu->mem_page[i])
-            free(nsf->cpu->mem_page[i]);
+	{
+	 free(nsf->cpu->mem_page[0]);/*tracks 1 and 2 of lifeforce hang here.*/
+	}
+      for (i = 5; i <= 7; i++) {
+	if (nsf->cpu->mem_page[i])
+	{
+	  free(nsf->cpu->mem_page[i]);
+	}
       }
+
+#ifdef NES6502_MEM_ACCESS_CTRL
+      if (nsf->cpu->acc_mem_page[0])
+	{
+ 	  free(nsf->cpu->acc_mem_page[0]);
+	}
+      for (i = 5; i <= 7; i++) {
+	if (nsf->cpu->acc_mem_page[i])
+	  {
+		free(nsf->cpu->acc_mem_page[i]);
+	  }
+      }
+#endif
       free(nsf->cpu);
    }
 }
 
-void nsf_init(void)
+int nsf_init(void)
 {
    nes6502_init();
+   return 0;
 }
 
 /* Initialize NES CPU, hardware, etc. */
@@ -344,18 +411,27 @@
          return -1;
    }
 
+#ifdef NES6502_MEM_ACCESS_CTRL
+   nsf->cpu->acc_mem_page[0] = malloc(0x800);
+   if (NULL == nsf->cpu->acc_mem_page[0])
+      return -1;
+   /* allocate some space for the NSF "player" MMC5 EXRAM, and WRAM */
+   for (i = 5; i <= 7; i++)
+   {
+      nsf->cpu->acc_mem_page[i] = malloc(0x1000);
+      if (NULL == nsf->cpu->acc_mem_page[i])
+         return -1;
+   }
+#endif
+
    nsf->cpu->read_handler = nsf_readhandler;
    nsf->cpu->write_handler = nsf_writehandler;
 
    return 0;
 }
 
-static void nsf_setup(nsf_t *nsf)
-{
-   int i;
-
-   nsf->current_song = nsf->start_song;
-
+static unsigned int nsf_playback_rate(nsf_t *nsf)
+{
    if (nsf->pal_ntsc_bits & NSF_DEDICATED_PAL)
    {
       if (nsf->pal_speed)
@@ -370,9 +446,17 @@
       else
          nsf->playback_rate = 60; /* 60 Hz */
    }
+   return 0;
+}
+
+static void nsf_setup(nsf_t *nsf)
+{
+   int i;
+
+   nsf->current_song = nsf->start_song;
+   nsf_playback_rate(nsf);
 
    nsf->bankswitched = FALSE;
-
    for (i = 0; i < 8; i++)
    {
       if (nsf->bankswitch_info[i])
@@ -389,212 +473,551 @@
 #define  SWAP_16(x)  (((uint16) x >> 8) | (((uint16) x & 0xFF) << 8))
 #endif /* !HOST_LITTLE_ENDIAN */
 
-/* Load a ROM image into memory */
-nsf_t *nsf_load(char *filename, void *source, int length)
-{
-   FILE *fp = NULL;
-   char *new_fn = NULL;
-   nsf_t *temp_nsf;
-
-   if (NULL == filename && NULL == source)
-      return NULL;
- 
-   if (NULL == source)
-   {
-      fp = fopen(filename, "rb");
-
-      /* Didn't find the file?  Maybe the .NSF extension was omitted */
-      if (NULL == fp)
-      {
-         new_fn = malloc(strlen(filename) + 5);
-         if (NULL == new_fn)
-            return NULL;
-         strcpy(new_fn, filename);
-
-         if (NULL == strrchr(new_fn, '.'))
-            strcat(new_fn, ".nsf");
-
-         fp = fopen(new_fn, "rb");
-
-         if (NULL == fp)
-         {
-            log_printf("could not find file '%s'\n", new_fn);
-            free(new_fn);
-            return NULL;
-         }
+/* $$$ ben : find extension. Should be OK with DOS, but not with some
+ * OS like RiscOS ... */
+static char * find_ext(char *fn)
+{
+  char * a, * b, * c;
+  a = strrchr(fn,'.');
+  b = strrchr(fn,'/');
+  c = strrchr(fn,'\\');
+  if (a <= b || a <= c) {
+    a = 0;
+  }
+  return a;
+}
+
+/* $$$ ben : FILE loader */
+struct nsf_file_loader_t {
+  struct nsf_loader_t loader;
+  FILE *fp;
+  char * fname;
+  int name_allocated;
+};
+
+static int nfs_open_file(struct nsf_loader_t *loader)
+{
+  struct nsf_file_loader_t * floader = (struct nsf_file_loader_t *)loader;
+
+  floader->name_allocated = 0;
+  floader->fp = 0;
+  if (!floader->fname) {
+    return -1;
+  }
+  floader->fp = fopen(floader->fname,"rb");
+  if (!floader->fp) {
+    char * fname, * ext;
+    ext = find_ext(floader->fname);
+    if (ext) {
+      /* There was an extension, so we do not change it */
+      return -1;
+    }
+    fname = malloc(strlen(floader->fname) + 5);
+    if (!fname) {
+      return -1;
+    }
+    /* try with .nsf extension. */
+    strcpy(fname, floader->fname);
+    strcat(fname, ".nsf");
+    floader->fp = fopen(fname,"rb");
+    if (!floader->fp) {
+      free(fname);
+      return -1;
+    }
+    floader->fname = fname;
+    floader->name_allocated = 1;
+  }
+  return 0;
+}
+
+static void nfs_close_file(struct nsf_loader_t *loader)
+{
+  struct nsf_file_loader_t * floader = (struct nsf_file_loader_t *)loader;
+  if (floader->fp) {
+    fclose(floader->fp);
+    floader->fp = 0;
+  }
+  if (floader->fname && floader->name_allocated) {
+    free(floader->fname);
+    floader->fname = 0;
+    floader->name_allocated = 0;
+  }
+}
+
+static int nfs_read_file(struct nsf_loader_t *loader, void *data, int n)
+{
+  struct nsf_file_loader_t * floader = (struct nsf_file_loader_t *)loader;
+  int r = fread(data, 1, n, floader->fp);
+  if (r >= 0) {
+    r = n-r;
+  }
+  return r;
+}
+
+static int nfs_length_file(struct nsf_loader_t *loader)
+{
+  struct nsf_file_loader_t * floader = (struct nsf_file_loader_t *)loader;
+  long save, pos;
+  save = ftell(floader->fp);
+  fseek(floader->fp, 0, SEEK_END);
+  pos = ftell(floader->fp);
+  fseek(floader->fp, save, SEEK_SET);
+  return pos;
+}
+
+static int nfs_skip_file(struct nsf_loader_t *loader, int n)
+{
+  struct nsf_file_loader_t * floader = (struct nsf_file_loader_t *)loader;
+  int r;
+  r = fseek(floader->fp, n, SEEK_CUR);
+  return r;
+}
+
+static const char * nfs_fname_file(struct nsf_loader_t *loader)
+{
+  struct nsf_file_loader_t * floader = (struct nsf_file_loader_t *)loader;
+  return floader->fname ? floader->fname : "<null>";
+}
+
+static struct nsf_file_loader_t nsf_file_loader = {
+  {
+    nfs_open_file,
+    nfs_close_file,
+    nfs_read_file,
+    nfs_length_file,
+    nfs_skip_file,
+    nfs_fname_file
+  },
+  0,0,0
+};
+
+struct nsf_mem_loader_t {
+  struct nsf_loader_t loader;
+  uint8 *data;
+  unsigned long cur;
+  unsigned long len;
+  char fname[32];
+};
+
+static int nfs_open_mem(struct nsf_loader_t *loader)
+{
+  struct nsf_mem_loader_t * mloader = (struct nsf_mem_loader_t *)loader;
+  if (!mloader->data) {
+    return -1;
+  }
+  mloader->cur = 0;
+  sprintf(mloader->fname,"<mem(%p,%u)>",
+	  mloader->data, (unsigned int)mloader->len);
+  return 0;
+}
+
+static void nfs_close_mem(struct nsf_loader_t *loader)
+{
+  struct nsf_mem_loader_t * mloader = (struct nsf_mem_loader_t *)loader;
+  mloader->data = 0;
+  mloader->cur = 0;
+  mloader->len = 0;
+}
+
+static int nfs_read_mem(struct nsf_loader_t *loader, void *data, int n)
+{
+  struct nsf_mem_loader_t * mloader = (struct nsf_mem_loader_t *)loader;
+  int rem;
+  if (n <= 0) {
+    return n;
+  }
+  if (!mloader->data) {
+    return -1;
+  }
+  rem = mloader->len - mloader->cur;
+  if (rem > n) {
+    rem = n;
+  }
+  memcpy(data, mloader->data + mloader->cur, rem);
+  mloader->cur += rem;
+  return n - rem;
+}
+
+static int nfs_length_mem(struct nsf_loader_t *loader)
+{
+  struct nsf_mem_loader_t * mloader = (struct nsf_mem_loader_t *)loader;
+  return mloader->len;
+}
+
+static int nfs_skip_mem(struct nsf_loader_t *loader, int n)
+{
+  struct nsf_mem_loader_t * mloader = (struct nsf_mem_loader_t *)loader;
+  unsigned long goal = mloader->cur + n;
+  mloader->cur = (goal > mloader->len) ? mloader->len : goal;
+  return goal - mloader->cur;
+}
+
+static const char * nfs_fname_mem(struct nsf_loader_t *loader)
+{
+  struct nsf_mem_loader_t * mloader = (struct nsf_mem_loader_t *)loader;
+  return mloader->fname;
+}
+
+static struct nsf_mem_loader_t nsf_mem_loader = {
+  { nfs_open_mem, nfs_close_mem, nfs_read_mem, nfs_length_mem, nfs_skip_mem },
+  0,0,0
+};
+
+nsf_t * nsf_load_extended(struct nsf_loader_t * loader)
+{
+  nsf_t *temp_nsf = 0;
+  int length;
+  char id[6];
+
+  struct {
+    uint8 magic[4]; /* always "NESM" */
+    uint8 type[4];  /* defines extension type */
+    uint8 size[4];  /* extension data size (this struct include) */
+  } nsf_file_ext;
+
+  /* no loader ! */ 
+  if (!loader) {
+    return NULL;
+  }
+  
+  /* Open the "file" */
+  if (loader->open(loader) < 0) {
+    return NULL;
+  }
+
+  /* Get file size, and exit if there is not enough data for NSF header
+   * and more since it does not make sens to have header without data.
+   */
+  length = loader->length(loader);
+  /* For version 2, we do not need file length. just check error later. */
+#if 0
+  if (length <= NSF_HEADER_SIZE) {
+    log_printf("nsf : [%s] not an NSF format file\n",
+	       loader->fname);
+    goto error;
+  }
+#endif
+
+  /* Read magic */
+  if (loader->read(loader, id, 5)) {
+    log_printf("nsf : [%s] error reading magic number\n",
+	       loader->fname);
+    goto error;
+  }
+
+  /* Check magic */
+  if (memcmp(id, NSF_MAGIC, 5)) {
+    log_printf("nsf : [%s] is not an NSF format file\n",
+	       loader->fname);
+    goto error;
+  }
+
+  /* $$$ ben : Now the file should be an NSF, we can start allocating.
+   * first : the nsf struct
+   */
+  temp_nsf = malloc(sizeof(nsf_t));
+  
+  if (NULL == temp_nsf) {
+    log_printf("nsf : [%s] error allocating nsf header\n",
+	       loader->fname);
+    goto error;
+  }
+  /* $$$ ben : safety net */
+  memset(temp_nsf,0,sizeof(nsf_t));
+  /* Copy magic ID */
+  memcpy(temp_nsf,id,5);
+
+  /* Read header (without MAGIC) */
+  if (loader->read(loader, (int8 *)temp_nsf+5, NSF_HEADER_SIZE - 5)) {
+    log_printf("nsf : [%s] error reading nsf header\n",
+	       loader->fname);
+    goto error;
+  }
+
+  /* fixup endianness */
+  temp_nsf->load_addr = SWAP_16(temp_nsf->load_addr);
+  temp_nsf->init_addr = SWAP_16(temp_nsf->init_addr);
+  temp_nsf->play_addr = SWAP_16(temp_nsf->play_addr);
+  temp_nsf->ntsc_speed = SWAP_16(temp_nsf->ntsc_speed);
+  temp_nsf->pal_speed = SWAP_16(temp_nsf->pal_speed);
+
+  /* we're now at position 80h */
+
+
+  /* Here comes the specific codes for spec version 2 */
+
+  temp_nsf->length = 0;
+
+  if (temp_nsf->version > 1) {
+    /* Get specified data size in reserved field (3 bytes). */
+    temp_nsf->length = 0
+      + temp_nsf->reserved[0]
+      + (temp_nsf->reserved[1]<<8)
+      + (temp_nsf->reserved[2]<<16);
+
+  }
+  /* no specified size : try to guess with file length. */
+  if (!temp_nsf->length) {
+    temp_nsf->length = length - NSF_HEADER_SIZE;
+  }
+
+  if (temp_nsf->length <= 0) {
+    log_printf("nsf : [%s] not an NSF format file (missing data)\n",
+	       loader->fname);
+    goto error;
+  }
+
+  /* Allocate NSF space, and load it up! */
+  {
+    int len = temp_nsf->length;
+#ifdef NES6502_MEM_ACCESS_CTRL
+    /* $$$ twice memory for access control shadow mem. */
+    len <<= 1;
+#endif
+   temp_nsf->data = malloc(len);
+  }
+  if (NULL == temp_nsf->data) {
+    log_printf("nsf : [%s] error allocating nsf data\n",
+	       loader->fname);
+    goto error;
+  }
+
+  /* Read data */
+  if (loader->read(loader, temp_nsf->data, temp_nsf->length)) {
+    log_printf("nsf : [%s] error reading NSF data\n",
+	       loader->fname);
+    goto error;
+  }
+
+  /* Here comes the second part of spec > 1 : get extension */
+  while (!loader->read(loader, &nsf_file_ext, sizeof(nsf_file_ext))
+	 && !memcmp(nsf_file_ext.magic,id,4)) {
+    /* Got a NESM extension here. Checks for known extension type :
+     * right now, the only extension is "TIME" which give songs length.
+     * in frames.
+     */
+    int size;
+    size = 0
+      + nsf_file_ext.size[0]
+      + (nsf_file_ext.size[1] << 8)
+      + (nsf_file_ext.size[2] << 16)
+      + (nsf_file_ext.size[3] << 24);
+
+    if (size < sizeof(nsf_file_ext)) {
+      log_printf("nsf : [%s] corrupt extension size (%d)\n",
+		 loader->fname, size);
+      /* Not a fatal error here. Just skip extension loading. */
+      break;
+    }
+    size -= sizeof(nsf_file_ext);
+
+    if (!temp_nsf->song_frames
+	&& !memcmp(nsf_file_ext.type,"TIME", 4)
+	&& !(size & 3)
+	&& (size >= 2*4)
+	&& (size <= 256*4)) {
+
+      uint8 tmp_time[256][4];
+      int tsongs = size >> 2;
+      int i;
+      int songs = temp_nsf->num_songs;
+
+      /* Add 1 for 0 which contains total time for all songs. */
+      ++songs;
+
+      if (loader->read(loader, tmp_time, size)) {
+	log_printf("nsf : [%s] missing extension data\n",
+		   loader->fname);
+	/* Not a fatal error here. Just skip extension loading. */
+	break;
       }
-   }
-
-   temp_nsf = malloc(sizeof(nsf_t));
-   if (NULL == temp_nsf) {
-     fclose(fp);
-     free(new_fn);
-     return NULL;
-   }
-
-   /* Read in the header */
-   if (NULL == source)
-      fread(temp_nsf, 1, NSF_HEADER_SIZE, fp);
-   else
-      memcpy(temp_nsf, source, NSF_HEADER_SIZE);
-
-   if (memcmp(temp_nsf->id, NSF_MAGIC, 5))
-   {
-      if (NULL == source)
-      {
-         log_printf("%s is not an NSF format file\n", new_fn);
-         fclose(fp);
-         free(new_fn);
+      /* Alloc song_frames for songs (not tsongs). */
+      temp_nsf->song_frames = malloc(sizeof(*temp_nsf->song_frames) * songs);
+      if (!temp_nsf->song_frames) {
+	log_printf("nsf : [%s] extension alloc failed\n",
+		   loader->fname);
+	/* Not a fatal error here. Just skip extension loading. */
+	break;
       }
-      nsf_free(&temp_nsf);
-      return NULL;
-   }
-
-   /* fixup endianness */
-   temp_nsf->load_addr = SWAP_16(temp_nsf->load_addr);
-   temp_nsf->init_addr = SWAP_16(temp_nsf->init_addr);
-   temp_nsf->play_addr = SWAP_16(temp_nsf->play_addr);
-   temp_nsf->ntsc_speed = SWAP_16(temp_nsf->ntsc_speed);
-   temp_nsf->pal_speed = SWAP_16(temp_nsf->pal_speed);
-
-   /* we're now at position 80h */
-   if (NULL == source)
-   {
-      fseek(fp, 0, SEEK_END);
-      temp_nsf->length = ftell(fp) - NSF_HEADER_SIZE;
-   }
-   else
-   {
-      temp_nsf->length = length - NSF_HEADER_SIZE;
-   }
-
-   /* Allocate NSF space, and load it up! */
-   temp_nsf->data = malloc(temp_nsf->length);
-   if (NULL == temp_nsf->data)
-   {
-      log_printf("error allocating memory for NSF data\n");
-      nsf_free(&temp_nsf);
-      return NULL;
-   }
-
-   /* seek to end of header, read in data */
-   if (NULL == source)
-   {
-      fseek(fp, NSF_HEADER_SIZE, SEEK_SET);
-      fread(temp_nsf->data, temp_nsf->length, 1, fp);
-
-      fclose(fp);
-
-      if (new_fn)
-         free(new_fn);
-   }
-   else
-      memcpy(temp_nsf->data, (uint8 *) source + NSF_HEADER_SIZE, length - NSF_HEADER_SIZE);
+
+      if (tsongs > songs) {
+	tsongs = songs;
+      }
+      
+      /* Copy time info. */
+      for (i=0; i<tsongs; ++i) {
+	temp_nsf->song_frames[i] = 0
+	  | tmp_time[i][0]
+	  | (tmp_time[i][1] << 8)
+	  | (tmp_time[i][2] << 16)
+	  | (tmp_time[i][2] << 24);
+      }
+      /* Clear missing (safety net). */
+      for (; i<songs; ++i) {
+	temp_nsf->song_frames[i] = 0;
+      }
+    } else if (loader->skip(loader, size)) {
+	log_printf("nsf : [%s] extension skip failed\n",
+		   loader->fname);
+	/* Not a fatal error here. Just skip extension loading. */
+	break;
+    }
+  }
+  
+
+  /* Close "file" */
+  loader->close(loader);
+  loader = 0;
 
    /* Set up some variables */
    nsf_setup(temp_nsf);
-
    temp_nsf->apu = NULL; /* just make sure */
 
-   if (nsf_cpuinit(temp_nsf))
-   {
-      nsf_free(&temp_nsf);
-      return NULL;
-   }
-
+   if (nsf_cpuinit(temp_nsf)) {
+     log_printf("nsf : error cpu init\n");
+     goto error;
+   }
    return temp_nsf;
+
+   /* $$$ ben : some people tell that goto are not clean. I am not agree with
+    * them. In most case, it allow to avoid code duplications, which are as
+    * most people know a source of error... Here we are sure of being clean
+    */
+ error:
+   if (loader) {
+     loader->close(loader);
+   }
+   if (temp_nsf) {
+	nsf_free(&temp_nsf);
+   }
+   return 0;
+}
+
+/* Load a ROM image into memory */
+nsf_t *nsf_load(const char *filename, void *source, int length)
+{
+  struct nsf_loader_t * loader = 0;
+
+  /* $$$ ben : new loader */
+  if (filename) {
+    nsf_file_loader.fname = (char *)filename;
+    loader = &nsf_file_loader.loader;
+  } else {
+    nsf_mem_loader.data = source;
+    nsf_mem_loader.len = length;
+    nsf_mem_loader.fname[0] = 0;
+    loader = &nsf_mem_loader.loader;
+  }
+  return nsf_load_extended(loader);
 }
 
 /* Free an NSF */
-void nsf_free(nsf_t **nsf)
-{
-   if (*nsf)
-   {
-      if ((*nsf)->apu)
-         apu_destroy((*nsf)->apu);
-
-      nes_shutdown(*nsf);
-
-      if ((*nsf)->data)
-         free((*nsf)->data);
-
-      free(*nsf);
-   }
-}
-
-void nsf_setchan(nsf_t *nsf, int chan, boolean enabled)
-{
-   if (nsf)
-   {
-      nsf_setcontext(nsf);
-      apu_setchan(chan, enabled);
-   }
-}
-
-void nsf_playtrack(nsf_t *nsf, int track, int sample_rate, int sample_bits, boolean stereo)
-{
-   ASSERT(nsf);
-
-   /* make this NSF the current context */
+void nsf_free(nsf_t **pnsf)
+{
+  nsf_t *nsf;
+
+  if (!pnsf) {
+    return;
+  }
+
+  nsf = *pnsf;
+  /* $$$ ben : Don't see why passing a pointer to pointer
+   *  is not to clear it :) */
+  *pnsf = 0;
+
+  if (nsf) {
+    if (nsf->apu)
+      apu_destroy(nsf->apu);
+
+    nes_shutdown(nsf);
+    
+    if (nsf->data)
+      free(nsf->data);
+
+    if (nsf->song_frames)
+      free (nsf->song_frames);
+
+    free(nsf);
+  }
+}
+
+int nsf_setchan(nsf_t *nsf, int chan, boolean enabled)
+{
+   if (!nsf)
+     return -1;
+
    nsf_setcontext(nsf);
-
-   /* create the APU */
-   if (nsf->apu)
-      apu_destroy(nsf->apu);
-
-   nsf->apu = apu_create(sample_rate, nsf->playback_rate, sample_bits, stereo);
-   if (NULL == nsf->apu)
-   {
-      nsf_free(&nsf);
-      return;
-   }
-
-   apu_setext(nsf->apu, nsf_getext(nsf));
-
-   /* go ahead and init all the read/write handlers */
-   build_address_handlers(nsf);
-
-   /* convenience? */
-   nsf->process = nsf->apu->process;
-
-   nes6502_setcontext(nsf->cpu);
-
-   if (track > nsf->num_songs)
-      track = nsf->num_songs;
-   else if (track < 1)
-      track = 1;
-
-   nsf->current_song = track;
+   return apu_setchan(chan, enabled);
+}
+
+int nsf_playtrack(nsf_t *nsf, int track, int sample_rate, int sample_bits,
+		  boolean stereo)
+{
+  if (!nsf) {
+    return -1;
+  }
+
+  /* make this NSF the current context */
+  nsf_setcontext(nsf);
+
+  /* create the APU */
+  if (nsf->apu) {
+    apu_destroy(nsf->apu);
+  }
+
+  nsf->apu = apu_create(sample_rate, nsf->playback_rate, sample_bits, stereo);
+  if (NULL == nsf->apu)
+    {
+      /* $$$ ben : from my point of view this is not clean. Function should
+       *	never destroy object it has not created...
+       */
+      /*       nsf_free(&nsf); */
+      return -1;
+    }
+
+  apu_setext(nsf->apu, nsf_getext(nsf));
+
+  /* go ahead and init all the read/write handlers */
+  build_address_handlers(nsf);
+  
+  /* convenience? */
+  nsf->process = nsf->apu->process;
+  
+  nes6502_setcontext(nsf->cpu);
+
+  if (track > nsf->num_songs)
+    track = nsf->num_songs;
+  else if (track < 1)
+    track = 1;
+
+  nsf->current_song = track;
    
-   apu_reset();
-
-   nsf_inittune(nsf);
-}
-
-void nsf_setfilter(nsf_t *nsf, int filter_type)
-{
-   if (nsf)
-   {
-      nsf_setcontext(nsf);
-      apu_setfilter(filter_type);
-   }
+  apu_reset();
+
+  nsf_inittune(nsf);
+
+  return nsf->current_song;
+}
+
+int nsf_setfilter(nsf_t *nsf, int filter_type)
+{
+  if (!nsf) {
+    return -1;
+  }
+  nsf_setcontext(nsf);
+  return apu_setfilter(filter_type);
 }
 
 /*
 ** $Log: nsf.c,v $
-** Revision 1.4  2006/09/26 00:52:17  dgp85
-** Free the filename string and close the file pointer when returning.
-**
-** Found by Coverity Scan.
-**
-** Revision 1.3  2003/08/25 21:51:43  f1rmb
-** Reduce GCC verbosity (various prototype declaration fixes). ffmpeg, wine and fft*post are untouched (fft: for now).
-**
-** Revision 1.2  2003/01/09 18:36:40  jkeil
-** memcpy copies too much, corrupts malloc heap
-**
-** Revision 1.1  2003/01/08 07:04:35  tmmm
-** initial import of Nosefart sources
+** Revision 1.3  2003/05/01 22:34:20  benjihan
+** New NSF plugin
+**
+** Revision 1.2  2003/04/09 14:50:32  ben
+** Clean NSF api.
+**
+** Revision 1.1  2003/04/08 20:53:00  ben
+** Adding more files...
 **
 ** Revision 1.14  2000/07/05 14:54:45  matt
 ** fix for naughty Crystalis rip