diff options
Diffstat (limited to 'src/libxineadec/nosefart/nsf.c')
-rw-r--r-- | src/libxineadec/nosefart/nsf.c | 1055 |
1 files changed, 0 insertions, 1055 deletions
diff --git a/src/libxineadec/nosefart/nsf.c b/src/libxineadec/nosefart/nsf.c deleted file mode 100644 index 69f77546b..000000000 --- a/src/libxineadec/nosefart/nsf.c +++ /dev/null @@ -1,1055 +0,0 @@ -/* -** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com) -** -** -** This program is free software; you can redistribute it and/or -** modify it under the terms of version 2 of the GNU Library General -** Public License as published by the Free Software Foundation. -** -** 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 -** Library General Public License for more details. To obtain a -** copy of the GNU Library General Public License, write to the Free -** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -** -** Any permitted reproduction of these routines, in whole or in part, -** must bear this legend. -** -** -** nsf.c -** -** 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> -#include "types.h" -#include "nsf.h" -#include "log.h" -#include "nes6502.h" -#include "nes_apu.h" -#include "vrcvisnd.h" -#include "vrc7_snd.h" -#include "mmc5_snd.h" -#include "fds_snd.h" - -/* TODO: bleh! should encapsulate in NSF */ -#define MAX_ADDRESS_HANDLERS 32 -static nes6502_memread nsf_readhandler[MAX_ADDRESS_HANDLERS]; -static nes6502_memwrite nsf_writehandler[MAX_ADDRESS_HANDLERS]; - -static nsf_t *cur_nsf = NULL; - -static void nsf_setcontext(nsf_t *nsf) -{ - ASSERT(nsf); - cur_nsf = nsf; -} - -static uint8 read_mirrored_ram(uint32 address) -{ - 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) -{ - 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; - 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); -} - -static nes6502_memread default_readhandler[] = -{ - { 0x0800, 0x1FFF, read_mirrored_ram }, - { 0x4000, 0x4017, apu_read }, - { -1, -1, NULL } -}; - -static nes6502_memwrite default_writehandler[] = -{ - { 0x0800, 0x1FFF, write_mirrored_ram }, - { 0x4000, 0x4017, apu_write }, - { 0x5FF6, 0x5FFF, nsf_bankswitch }, - { -1, -1, NULL} -}; - -static uint8 invalid_read(uint32 address) -{ -#ifdef NOFRENDO_DEBUG - log_printf("filthy NSF read from $%04X\n", address); -#endif /* NOFRENDO_DEBUG */ - - return 0xFF; -} - -static void invalid_write(uint32 address, uint8 value) -{ -#ifdef NOFRENDO_DEBUG - log_printf("filthy NSF tried to write $%02X to $%04X\n", value, address); -#endif /* NOFRENDO_DEBUG */ -} - -/* set up the address handlers that the CPU uses */ -static void build_address_handlers(nsf_t *nsf) -{ - int count, num_handlers; - - memset(nsf_readhandler, 0, sizeof(nsf_readhandler)); - memset(nsf_writehandler, 0, sizeof(nsf_writehandler)); - - num_handlers = 0; - for (count = 0; num_handlers < MAX_ADDRESS_HANDLERS; count++, num_handlers++) - { - if (NULL == default_readhandler[count].read_func) - break; - - memcpy(&nsf_readhandler[num_handlers], &default_readhandler[count], - sizeof(nes6502_memread)); - } - - if (nsf->apu->ext) - { - if (NULL != nsf->apu->ext->mem_read) - { - for (count = 0; num_handlers < MAX_ADDRESS_HANDLERS; count++, num_handlers++) - { - if (NULL == nsf->apu->ext->mem_read[count].read_func) - break; - - memcpy(&nsf_readhandler[num_handlers], &nsf->apu->ext->mem_read[count], - sizeof(nes6502_memread)); - } - } - } - - /* catch-all for bad reads */ - nsf_readhandler[num_handlers].min_range = 0x2000; /* min address */ - nsf_readhandler[num_handlers].max_range = 0x5BFF; /* max address */ - nsf_readhandler[num_handlers].read_func = invalid_read; /* handler */ - num_handlers++; - nsf_readhandler[num_handlers].min_range = -1; - nsf_readhandler[num_handlers].max_range = -1; - nsf_readhandler[num_handlers].read_func = NULL; - num_handlers++; - ASSERT(num_handlers <= MAX_ADDRESS_HANDLERS); - - num_handlers = 0; - for (count = 0; num_handlers < MAX_ADDRESS_HANDLERS; count++, num_handlers++) - { - if (NULL == default_writehandler[count].write_func) - break; - - memcpy(&nsf_writehandler[num_handlers], &default_writehandler[count], - sizeof(nes6502_memwrite)); - } - - if (nsf->apu->ext) - { - if (NULL != nsf->apu->ext->mem_write) - { - for (count = 0; num_handlers < MAX_ADDRESS_HANDLERS; count++, num_handlers++) - { - if (NULL == nsf->apu->ext->mem_write[count].write_func) - break; - - memcpy(&nsf_writehandler[num_handlers], &nsf->apu->ext->mem_write[count], - sizeof(nes6502_memwrite)); - } - } - } - - /* catch-all for bad writes */ - nsf_writehandler[num_handlers].min_range = 0x2000; /* min address */ - nsf_writehandler[num_handlers].max_range = 0x5BFF; /* max address */ - nsf_writehandler[num_handlers].write_func = invalid_write; /* handler */ - num_handlers++; - /* protect region at $8000-$FFFF */ - nsf_writehandler[num_handlers].min_range = 0x8000; /* min address */ - nsf_writehandler[num_handlers].max_range = 0xFFFF; /* max address */ - nsf_writehandler[num_handlers].write_func = invalid_write; /* handler */ - num_handlers++; - nsf_writehandler[num_handlers].min_range = -1; - nsf_writehandler[num_handlers].max_range = -1; - nsf_writehandler[num_handlers].write_func = NULL; - num_handlers++; - ASSERT(num_handlers <= MAX_ADDRESS_HANDLERS); -} - -#define NSF_ROUTINE_LOC 0x5000 - -/* sets up a simple loop that calls the desired routine and spins */ -static void nsf_setup_routine(uint32 address, uint8 a_reg, uint8 x_reg) -{ - uint8 *mem; - - nes6502_getcontext(cur_nsf->cpu); - mem = cur_nsf->cpu->mem_page[NSF_ROUTINE_LOC >> 12] + (NSF_ROUTINE_LOC & 0x0FFF); - - /* our lovely 4-byte 6502 NSF player */ - mem[0] = 0x20; /* JSR address */ - mem[1] = address & 0xFF; - mem[2] = address >> 8; - mem[3] = 0xF2; /* JAM (cpu kill op) */ - - cur_nsf->cpu->pc_reg = NSF_ROUTINE_LOC; - cur_nsf->cpu->a_reg = a_reg; - cur_nsf->cpu->x_reg = x_reg; - cur_nsf->cpu->y_reg = 0; - cur_nsf->cpu->s_reg = 0xFF; - - nes6502_setcontext(cur_nsf->cpu); -} - -/* retrieve any external soundchip driver */ -static apuext_t *nsf_getext(nsf_t *nsf) -{ - switch (nsf->ext_sound_type) - { - case EXT_SOUND_VRCVI: - return &vrcvi_ext; - - case EXT_SOUND_VRCVII: - return &vrc7_ext; - - case EXT_SOUND_FDS: - return &fds_ext; - - case EXT_SOUND_MMC5: - return &mmc5_ext; - - case EXT_SOUND_NAMCO106: - case EXT_SOUND_SUNSOFT_FME07: - case EXT_SOUND_NONE: - default: - return NULL; - } -} - -static void nsf_inittune(nsf_t *nsf) -{ - uint8 bank, x_reg; - uint8 start_bank, num_banks; - - memset(nsf->cpu->mem_page[0], 0, 0x800); - 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! */ - if (EXT_SOUND_FDS == nsf->ext_sound_type) - { - nsf_bankswitch(0x5FF6, nsf->bankswitch_info[6]); - nsf_bankswitch(0x5FF7, nsf->bankswitch_info[7]); - } - - for (bank = 0; bank < 8; bank++) - nsf_bankswitch(0x5FF8 + bank, nsf->bankswitch_info[bank]); - } - else - { - /* not bankswitched, just page in our standard stuff */ - ASSERT(nsf->load_addr + nsf->length <= 0x10000); - - /* avoid ripper filth */ - for (bank = 0; bank < 8; bank++) - nsf_bankswitch(0x5FF8 + bank, bank); - - start_bank = nsf->load_addr >> 12; - num_banks = ((nsf->load_addr + nsf->length - 1) >> 12) - start_bank + 1; - - for (bank = 0; bank < num_banks; bank++) - nsf_bankswitch(0x5FF0 + start_bank + bank, bank); - } - - /* determine PAL/NTSC compatibility shite */ - if (nsf->pal_ntsc_bits & NSF_DEDICATED_PAL) - x_reg = 1; - else - x_reg = 0; - - /* execute 1 frame or so; let init routine run free */ - nsf_setup_routine(nsf->init_addr, (uint8) (nsf->current_song - 1), x_reg); - nes6502_execute((int) NES_FRAME_CYCLES); -} - -void nsf_frame(nsf_t *nsf) -{ - // 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 */ -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]);/*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); - } -} - -int nsf_init(void) -{ - nes6502_init(); - return 0; -} - -/* Initialize NES CPU, hardware, etc. */ -static int nsf_cpuinit(nsf_t *nsf) -{ - int i; - - nsf->cpu = malloc(sizeof(nes6502_context)); - if (NULL == nsf->cpu) - return -1; - - memset(nsf->cpu, 0, sizeof(nes6502_context)); - - nsf->cpu->mem_page[0] = malloc(0x800); - if (NULL == nsf->cpu->mem_page[0]) - return -1; - - /* allocate some space for the NSF "player" MMC5 EXRAM, and WRAM */ - for (i = 5; i <= 7; i++) - { - nsf->cpu->mem_page[i] = malloc(0x1000); - if (NULL == nsf->cpu->mem_page[i]) - 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 unsigned int nsf_playback_rate(nsf_t *nsf) -{ - if (nsf->pal_ntsc_bits & NSF_DEDICATED_PAL) - { - if (nsf->pal_speed) - nsf->playback_rate = 1000000 / nsf->pal_speed; - else - nsf->playback_rate = 50; /* 50 Hz */ - } - else - { - if (nsf->ntsc_speed) - nsf->playback_rate = 1000000 / nsf->ntsc_speed; - 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]) - { - nsf->bankswitched = TRUE; - break; - } - } -} - -#ifdef HOST_LITTLE_ENDIAN -#define SWAP_16(x) (x) -#else /* !HOST_LITTLE_ENDIAN */ -#define SWAP_16(x) (((uint16) x >> 8) | (((uint16) x & 0xFF) << 8)) -#endif /* !HOST_LITTLE_ENDIAN */ - -/* $$$ 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; - } - /* 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; - } - - 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)) { - 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 **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); - 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); - - 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.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 -** -** Revision 1.13 2000/07/04 04:59:38 matt -** removed DOS-specific stuff, fixed bug in address handlers -** -** Revision 1.12 2000/07/03 02:19:36 matt -** dynamic address range handlers, cleaner and faster -** -** Revision 1.11 2000/06/23 03:27:58 matt -** cleaned up external sound inteface -** -** Revision 1.10 2000/06/20 20:42:47 matt -** accuracy changes -** -** Revision 1.9 2000/06/20 00:05:58 matt -** changed to driver-based external sound generation -** -** Revision 1.8 2000/06/13 03:51:54 matt -** update API to take freq/sample data on nsf_playtrack -** -** Revision 1.7 2000/06/12 03:57:14 matt -** more robust checking for winamp plugin -** -** Revision 1.6 2000/06/12 01:13:00 matt -** added CPU/APU as members of the nsf struct -** -** Revision 1.5 2000/06/11 16:09:21 matt -** nsf_free is more robust -** -** Revision 1.4 2000/06/09 15:12:26 matt -** initial revision -** -*/ |