Skip to content

Commit

Permalink
NSF and NSFe file loading
Browse files Browse the repository at this point in the history
implement a few LOCALPROPs now that file loading is in place
add NSFP_NOTEXT define in case we want to strip out all the text data
don't need finalize, there's nothing to allocate until starting a song anyway
missed some consts with a few error functions (now error_last_buffer is mutable)
rename current_song to song_current
add Shift-JIS option
make assume a parameter to nsfplay_load, add nsfplay_load_bin since we don't want to treat every possible file as bin, try to make it deliberate
  • Loading branch information
bbbradsmith committed Apr 24, 2024
1 parent 899dea1 commit 65a80fd
Show file tree
Hide file tree
Showing 12 changed files with 1,313 additions and 899 deletions.
13 changes: 13 additions & 0 deletions cmd/cmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,15 @@ int main(int argc, char** argv)
*/
NSFCore* core = nsfplay_create(TEST_INI);
nsfplay_set_init(core,TEST_INIT);
/*
//nsfplay_set_key_int(core,"TRI_ON",0);
//nsfplay_set_key_str(core,"TITLE_FORMAT","keyed");
nsfplay_set_ini_line(core,"SQU0_ON = off ");
nsfplay_set_ini_line(core,"SQU1_ON=$-1");
nsfplay_set_ini_line(core,"TRI_ON=$F");
nsfplay_set_ini_line(core,"NSE_ON=ON");
nsfplay_set_ini_line(core,"DPCM_ON=false");
*/

/*
// test info
Expand Down Expand Up @@ -137,6 +139,17 @@ int main(int argc, char** argv)
// test ini write
nsfplay_ini_write(core,stdout);

/*
FILE* f = platform_fopen("moon8.nsfe","rb");
fseek(f,0,SEEK_END);
int fs = ftell(f);
fseek(f,0,SEEK_SET);
void* fd = malloc(fs);
fread(fd,1,fs,f);
fclose(f);
nsfplay_load(core,fd,fs,false);
*/

nsfplay_destroy(core);

platform_shutdown();
Expand Down
117 changes: 81 additions & 36 deletions core/core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,18 +125,6 @@ void NSFCore::destroy(NSFCore* core)
nsfp::free(core);
}

void NSFCore::finalize()
{
ram8000 = memory_master;
ram0000 = ram8000 + 0x8000;
pad0 = ram0000 + 0x800;
pad1 = pad0 + 0x1000;

// TODO make any allocations needed based on the settings
// resample kernel, etc.
set_apply();
}

void NSFCore::release()
{
// release any allocations held
Expand All @@ -154,8 +142,9 @@ const char* NSFCore::last_error() const
return r;
}

void NSFCore::set_error(sint32 textenum,...)
void NSFCore::set_error(sint32 textenum,...) const
{
#if !(NSFP_NOTEXT)
const char* fmt = local_text(textenum);
// skip formatting if not needed
bool direct = true;
Expand All @@ -180,12 +169,17 @@ void NSFCore::set_error(sint32 textenum,...)
std::vsnprintf(error_last_buffer,sizeof(error_last_buffer),fmt,args);
error_last_buffer[sizeof(error_last_buffer)-1] = 0;
error_last = error_last_buffer;
#else
(void)textenum;
error_last = "";
#endif
if (nsfp::error_callback) nsfp::error_callback(error_last);
else { NSFP_DEBUG("ERROR: %s",error_last); }
}

void NSFCore::set_ini_error(int linenum, sint32 textenum,...)
void NSFCore::set_ini_error(int linenum, sint32 textenum,...) const
{
#if !(NSFP_NOTEXT)
const char* fmt = local_text(textenum);
// line number prefix if linenum < 0
if (linenum < 0) error_last_buffer[0] = 0;
Expand All @@ -201,11 +195,16 @@ void NSFCore::set_ini_error(int linenum, sint32 textenum,...)
std::vsnprintf(error_last_buffer+start,sizeof(error_last_buffer)-start,fmt,args);
error_last_buffer[sizeof(error_last_buffer)-1] = 0;
error_last = error_last_buffer;
#else
(void)linenum;
(void)textenum;
error_last = "";
#endif
if (nsfp::error_callback) nsfp::error_callback(error_last);
else { NSFP_DEBUG("ERROR: %s",error_last); }
}

void NSFCore::set_error_raw(const char* fmt,...)
void NSFCore::set_error_raw(const char* fmt,...) const
{
va_list args;
va_start(args,fmt);
Expand Down Expand Up @@ -234,6 +233,7 @@ void NSFCore::set_default()

bool NSFCore::set_ini(const char* ini)
{
#if !(NSFP_NOTEXT)
if (ini == NULL) return true;
// skip UTF-8 BOM if present
ini = utf8_bom_skip(ini);
Expand All @@ -254,6 +254,10 @@ bool NSFCore::set_ini(const char* ini)
ini += (eol+1);
}
return result;
#else
(void)ini;
return false;
#endif
}

bool NSFCore::set_init(const NSFSetInit* init)
Expand Down Expand Up @@ -412,32 +416,33 @@ NSFSetGroupInfo NSFCore::group_info(sint32 group) const

const char* NSFCore::ini_line(sint32 setenum) const
{
#if !(NSFP_NOTEXT)
if (setenum < 0 || setenum >= NSFP_SET_COUNT) return "";
const NSFSetData& SD = NSFPD_SET[setenum];
if (SD.default_str == NULL)
{
switch (SD.display)
{
case NSFP_DISPLAY_LIST:
case NSFP_DISPLAY_LIST:
{
const char* list_key = local_text(NSFPD_LIST_TEXT[SD.list]+0);
for (int i=0; i<setting[setenum]; ++i)
{
const char* list_key = local_text(NSFPD_LIST_TEXT[SD.list]+0);
for (int i=0; i<setting[setenum]; ++i)
{
while(*list_key) ++list_key;
++list_key;
}
std::snprintf(temp_text,sizeof(temp_text),"%s=%s",SD.key,list_key);
} break;
case NSFP_DISPLAY_HEX8:
std::snprintf(temp_text,sizeof(temp_text),"%s=$%02X",SD.key,setting[setenum]); break;
case NSFP_DISPLAY_HEX16:
std::snprintf(temp_text,sizeof(temp_text),"%s=$%04X",SD.key,setting[setenum]); break;
case NSFP_DISPLAY_HEX32:
std::snprintf(temp_text,sizeof(temp_text),"%s=$%08X",SD.key,setting[setenum]); break;
case NSFP_DISPLAY_COLOR:
std::snprintf(temp_text,sizeof(temp_text),"%s=$%06X",SD.key,setting[setenum]); break;
default:
std::snprintf(temp_text,sizeof(temp_text),"%s=%d",SD.key,setting[setenum]); break;
while(*list_key) ++list_key;
++list_key;
}
std::snprintf(temp_text,sizeof(temp_text),"%s=%s",SD.key,list_key);
} break;
case NSFP_DISPLAY_HEX8:
std::snprintf(temp_text,sizeof(temp_text),"%s=$%02X",SD.key,setting[setenum]); break;
case NSFP_DISPLAY_HEX16:
std::snprintf(temp_text,sizeof(temp_text),"%s=$%04X",SD.key,setting[setenum]); break;
case NSFP_DISPLAY_HEX32:
std::snprintf(temp_text,sizeof(temp_text),"%s=$%08X",SD.key,setting[setenum]); break;
case NSFP_DISPLAY_COLOR:
std::snprintf(temp_text,sizeof(temp_text),"%s=$%06X",SD.key,setting[setenum]); break;
default:
std::snprintf(temp_text,sizeof(temp_text),"%s=%d", SD.key,setting[setenum]); break;
};
}
else
Expand All @@ -446,10 +451,15 @@ const char* NSFCore::ini_line(sint32 setenum) const
}
temp_text[sizeof(temp_text)-1] = 0;
return temp_text;
#else
(void)setenum;
return "";
#endif
}

void NSFCore::ini_write(FILE* f) const
{
#if !(NSFP_NOTEXT)
sint32 last_group = -1;
std::fprintf(f,"# NSFPlay INI settings file\n");
for (sint32 i=0; i<NSFP_SET_COUNT; ++i)
Expand All @@ -464,10 +474,14 @@ void NSFCore::ini_write(FILE* f) const
std::fprintf(f,"%s\n",ini_line(i));
}
std::fprintf(f,"# end of settings\n");
#else
(void)f;
#endif
}

bool NSFCore::parse_ini_line(const char* line, int len, int linenum)
{
#if !(NSFP_NOTEXT)
// trim leading whitespace
while (line[0]==' ' || line[0] == '\t') { ++line; --len; }
// truncate for comments
Expand Down Expand Up @@ -554,15 +568,34 @@ bool NSFCore::parse_ini_line(const char* line, int len, int linenum)
NSFP_DEBUG("Unexpected set_int error at INI line %d: %s",linenum,error_last);
}
return true;
#else
(void)line;
(void)len;
(void)linenum;
return false;
#endif
}

bool NSFCore::load(const uint8* data, uint32 size, bool assume)
bool NSFCore::load(const uint8* data, uint32 size, bool assume, bool bin)
{
NSFP_DEBUG("NSFCore::load(%s,%d,%d,%d)",data?"*":"NULL",size,assume,bin);
if (nsf_free)
nsfp::free(const_cast<uint8*>(nsf));
nsf = NULL;
nsf_size = 0;
nsf_free = false;

// drop all potential references to the former nsf
rom = NULL;
pad0 = NULL;
pad1 = NULL;
for (int i=0; i<16; ++i)
{
rpage[i] = 0;
wpage[i] = 0;
}
bank_last = 0;

if (data != NULL)
{
if (assume)
Expand All @@ -573,9 +606,10 @@ bool NSFCore::load(const uint8* data, uint32 size, bool assume)
std::memcpy(const_cast<uint8*>(nsf),data,size);
nsf_free = true;
}
nsf_size = size;
}

bool result = nsf_parse();
bool result = nsf_parse(bin);
// TODO play song (reset/rebuilds audio stacks) which may & with result
return result;
}
Expand Down Expand Up @@ -608,16 +642,27 @@ NSFPropInfo NSFCore::prop_info(sint32 prop, bool song) const

const char* NSFCore::local_text(sint32 textenum) const
{
#if !(NSFP_NOTEXT)
return NSFCore::local_text(textenum,setting[NSFP_SET_LOCALE]);
#else
(void)textenum;
return (const char*)NSFPD_NOTEXT_LIST_KEY;
#endif
}

const char* NSFCore::local_text(sint32 textenum, sint32 locale)
{
#if !(NSFP_NOTEXT)
if (locale < 0 || locale >= NSFP_LOCALE_COUNT || textenum < 0 || textenum >= NSFP_TEXT_COUNT)
{
// text 0 is a default <MISSING TEXT> value
locale = 0;
textenum = 0;
}
return (const char*)(NSFPD_LOCAL_TEXT_DATA + NSFPD_LOCAL_TEXT[locale][textenum]);
#else
(void)textenum;
(void)locale;
return (const char*)NSFPD_NOTEXT_LIST_KEY;
#endif
}
56 changes: 36 additions & 20 deletions core/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ typedef uint64_t uint64;
#define DEBUG_ALLOC 0
#endif

#ifndef NSFP_NOTEXT
// define NSFP_NOTEXT=1 to strip all unnecessary text from the build
// this disables ini parsing, error messages will become blank strings, and list keys will also become blank
#define NSFP_NOTEXT 0
#endif

// NSFCore structure, code members defined in core.cpp unless otherwised marked

typedef struct NSFCore_
Expand All @@ -51,7 +57,7 @@ typedef struct NSFCore_

// text and error output buffers
mutable const char* error_last;
char error_last_buffer[256]; // error_last may point to this for formatted errors
mutable char error_last_buffer[256]; // error_last may point to this for formatted errors
mutable char temp_text[1024]; // used for returned text information
mutable const char* prop_lines;

Expand All @@ -62,15 +68,21 @@ typedef struct NSFCore_

// emulation
const uint8* nsf;
uint32 nsf_size;
bool nsf_free; // true if nsf was allocated (load rather than assume)
uint8 memory_master[(32+2+4+4)*1024]; // master allocation divided into blocks below
uint8* ram8000; // 32k of RAM at $8000
uint8* ram0000; // 2k of on-board RAM
uint8* pad0; // 4k first bank, if it needs padding due to LOAD address
uint8* pad1; // 4k last bank, if it needs padding due to not filling a complete bank
int padbank0; // the bank assigned to pad0
int padbank1; // the bank assigned to pad1
uint8 current_song;
bool nsf_bin; // binary file at $6000 instead of NSF

uint8 ram0000[0x00800-0x0000]; // $0000-07FF 2k of on-board RAM
uint8 ram6000[0x10000-0x6000]; // $6000-FFFF 40k of RAM or other workspace
const uint8* rom; // pointer to read only NSF data, aligned for padding (first/last banks may be masked by pad0/pad1)
uint8* pad0; // 4k first bank, if it needs padding due to LOAD address (may reuse ram6000 at E000)
uint8* pad1; // 4k last bank, if it needs padding due to not filling a complete bank (may reuse ram6000 at F000)
unsigned int bank_last; // last bank in data (pad1 if not NULL)
const uint8* rpage[16]; // currently assigned 4k bank pages for reading (null for open bus)
uint8* wpage[16]; // currently assigned 4k bank pages for writing

// playback
uint8 song_current;

// audio units
// TODO these should be static POD structures, each with a pointer to the core
Expand Down Expand Up @@ -98,22 +110,21 @@ typedef struct NSFCore_

// interface

static NSFCore* create(); // After create: ->set_... then ->finalize.
static NSFCore* create();
static void destroy(NSFCore* core); // Calls ->release before freeing the core.
void finalize(); // finishes creation after create and initial settings
void release(); // called by destroy, releases all owned allocations
void release(); // internal: called by destroy, releases all owned allocations

const char* last_error() const; // returns last error message, NULL if none since last check
void set_error(sint32 textenum,...); // sets last error and generates error callback
void set_ini_error(int linenum, sint32 textenum,...); // for ini files (-1 if no ini)
void set_error_raw(const char* fmt,...); // set_error with localized errors is preferred, but this can send raw text errors for debug purposes
void set_error(sint32 textenum,...) const; // sets last error and generates error callback
void set_ini_error(int linenum, sint32 textenum,...) const; // for ini files (-1 if no ini)
void set_error_raw(const char* fmt,...) const; // set_error with localized errors is preferred, but this can send raw text errors for debug purposes

void set_default(); // restore default settings
bool set_ini(const char* ini);
bool set_init(const NSFSetInit* init);
bool set_int(sint32 setenum, sint32 value); // integer setting (sets error if false)
bool set_str(sint32 setenum, const char* value, sint32 len=-1); // string setting, len truncates, len<0 will strlen (sets error if false)
void set_apply(); // call to apply changed settings now (as much as possible)
void set_apply(); // call to apply changed settings now
sint32 get_int(sint32 setenum) const;
const char* get_str(sint32 setenum) const; // use this instead of manually de-indexing setting_str

Expand All @@ -126,15 +137,20 @@ typedef struct NSFCore_
void ini_write(FILE* f) const;
bool parse_ini_line(const char* line, int len, int linenum); // linenum=-1 to parse a line with no INI file context

bool load(const uint8* data, uint32 size, bool assume);
bool load(const uint8* data, uint32 size, bool assume, bool bin=false);
NSFPropInfo prop_info(sint32 prop, bool song=false) const; // if song, prop is a SONGPROP

const char* local_text(sint32 textenum) const; // NSFP_TEXT_x for curent locale (local_text(0) is a default error string)
static const char* local_text(sint32 textenum, sint32 locale); // NSFP_TEXT_x for specific locale

// nsf.cpp
bool nsf_parse();
const void* nsf_chunk(const char* fourcc, uint32* chunk_size) const;
// nsf.cpp internal
sint32 nsf_type() const; // returns NSFP_LK_FILETYPE enum
uint32 nsfe_data() const; // returns offset to NSFe data if it exists, 0 otherwise
bool nsf_header_present() const; // returns true if NSF or NSF2
// nsf.cpp external
bool nsf_parse(bool bin);
const uint8* nsfe_chunk(uint32 fourcc, uint32* chunk_size) const; // fourcc is packed little-endian into uint32
const uint8* nsfe_chunk(const char* fourcc, uint32* chunk_size) const;
bool nsf_prop_exists(sint32 prop, sint32 song=-1) const; // if song>=0 prop is a SONGPROP
sint32 nsf_prop_int(sint32 prop, sint32 song=-1) const;
sint64 nsf_prop_long(sint32 prop, sint32 song=-1) const;
Expand Down
Loading

0 comments on commit 65a80fd

Please sign in to comment.