Added saving capability to GME, currently only supported by SFM

CQTexperiment
Chris Moeller 2013-10-26 09:00:59 -07:00
parent 1c9b19af74
commit b8b93103bf
11 changed files with 396 additions and 136 deletions

View File

@ -919,6 +919,7 @@
buildActionMask = 2147483647;
files = (
8370B76C17F615FE001A4D7A /* Nes_Cpu_run.h in Headers */,
8370B7B017F615FE001A4D7A /* Spc_Sfm.h in Headers */,
8370B75A17F615FE001A4D7A /* i_vrc7.h in Headers */,
8370B7A817F615FE001A4D7A /* Sgc_Emu.h in Headers */,
8370B74217F615FE001A4D7A /* divfix.h in Headers */,
@ -993,7 +994,6 @@
8370B79717F615FE001A4D7A /* s_logtbl.h in Headers */,
8370B77117F615FE001A4D7A /* Nes_Vrc7_Apu.h in Headers */,
8370B74517F615FE001A4D7A /* emuconfig.h in Headers */,
8370B7B017F615FE001A4D7A /* Spc_Sfm.h in Headers */,
17C8F2240CBED286008D969D /* Kss_Emu.h in Headers */,
17C8F2260CBED286008D969D /* Kss_Scc_Apu.h in Headers */,
17C8F2290CBED286008D969D /* M3u_Playlist.h in Headers */,

View File

@ -1,10 +1,16 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include "Bml_Parser.h"
const char * strchr_limited( const char * in, const char * end, char c )
{
while ( in < end && *in != c ) ++in;
if ( in < end ) return in;
else return 0;
}
Bml_Node Bml_Node::emptyNode;
Bml_Node::Bml_Node()
@ -13,6 +19,17 @@ Bml_Node::Bml_Node()
value = 0;
}
Bml_Node::Bml_Node(char const* name, size_t max_length)
{
size_t length = 0;
char const* ptr = name;
while (*ptr && length < max_length) ++ptr, ++length;
this->name = new char[ length + 1 ];
memcpy( this->name, name, length );
this->name[ length ] = '\0';
value = 0;
}
Bml_Node::Bml_Node(const Bml_Node &in)
{
size_t length;
@ -49,22 +66,25 @@ void Bml_Node::clear()
children.resize( 0 );
}
void Bml_Node::setLine(const char *line)
void Bml_Node::setLine(const char *line, size_t max_length)
{
delete [] name;
delete [] value;
name = 0;
value = 0;
size_t length = 0;
const char * end = line;
while (*end && length < max_length) ++end;
const char * line_end = strchr(line, '\n');
if ( !line_end ) line_end = line + strlen(line);
const char * line_end = strchr_limited(line, end, '\n');
if ( !line_end ) line_end = end;
const char * first_letter = line;
while ( first_letter < line_end && *first_letter <= 0x20 ) first_letter++;
const char * colon = strchr(first_letter, ':');
if (colon >= line_end) colon = 0;
const char * colon = strchr_limited(first_letter, line_end, ':');
const char * last_letter = line_end - 1;
if (colon)
@ -88,9 +108,10 @@ void Bml_Node::setLine(const char *line)
name[last_letter - first_letter + 1] = '\0';
}
void Bml_Node::addChild(const Bml_Node &child)
Bml_Node& Bml_Node::addChild(const Bml_Node &child)
{
children.push_back(child);
return *(children.end() - 1);
}
const char * Bml_Node::getName() const
@ -103,6 +124,14 @@ const char * Bml_Node::getValue() const
return value;
}
void Bml_Node::setValue(char const* value)
{
delete [] this->value;
size_t length = strlen( value ) + 1;
this->value = new char[ length ];
memcpy( this->value, value, length );
}
size_t Bml_Node::getChildCount() const
{
return children.size();
@ -113,26 +142,63 @@ Bml_Node const& Bml_Node::getChild(size_t index) const
return children[index];
}
Bml_Node & Bml_Node::walkToNode(const char *path)
Bml_Node & Bml_Node::walkToNode(const char *path, bool use_indexes)
{
Bml_Node * next_node;
Bml_Node * node = this;
while ( *path )
{
bool item_found = false;
size_t array_index = 0;
const char * array_index_start = strchr( path, '[' );
const char * next_separator = strchr( path, ':' );
if ( !next_separator ) next_separator = path + strlen(path);
for ( std::vector<Bml_Node>::iterator it = node->children.end(); it != node->children.begin(); )
if ( use_indexes && array_index_start && array_index_start < next_separator )
{
--it;
if ( next_separator - path == strlen(it->name) &&
strncmp( it->name, path, next_separator - path ) == 0 )
char * temp;
array_index = strtoul( array_index_start + 1, &temp, 10 );
}
else
{
array_index_start = next_separator;
}
if ( use_indexes )
{
for ( std::vector<Bml_Node>::iterator it = node->children.begin(); it != node->children.end(); ++it )
{
node = &(*it);
item_found = true;
break;
if ( array_index_start - path == strlen(it->name) &&
strncmp( it->name, path, array_index_start - path ) == 0 )
{
next_node = &(*it);
item_found = true;
if ( array_index == 0 ) break;
--array_index;
}
if (array_index)
item_found = false;
}
}
if ( !item_found ) return emptyNode;
else
{
for ( std::vector<Bml_Node>::iterator it = node->children.end(); it != node->children.begin(); )
{
--it;
if ( next_separator - path == strlen(it->name) &&
strncmp( it->name, path, next_separator - path ) == 0 )
{
next_node = &(*it);
item_found = true;
break;
}
}
}
if ( !item_found )
{
Bml_Node child( path, next_separator - path );
node = &(node->addChild( child ));
}
else
node = next_node;
if ( *next_separator )
{
path = next_separator + 1;
@ -184,7 +250,7 @@ Bml_Node const& Bml_Node::walkToNode(const char *path) const
return *node;
}
void Bml_Parser::parseDocument( const char * source )
void Bml_Parser::parseDocument( const char * source, size_t max_length )
{
std::vector<size_t> indents;
std::string last_name;
@ -195,15 +261,19 @@ void Bml_Parser::parseDocument( const char * source )
size_t last_indent = ~0;
Bml_Node node;
size_t length = 0;
const char * end = source;
while ( *end && length < max_length ) ++end, ++length;
while ( *source )
while ( source < end )
{
const char * line_end = strchr( source, '\n' );
if ( !line_end ) line_end = source + strlen( source );
const char * line_end = strchr_limited( source, end, '\n' );
if ( !line_end ) line_end = end;
if ( node.getName() ) last_name = node.getName();
node.setLine( source );
node.setLine( source, line_end - source );
size_t indent = 0;
while ( source < line_end && *source <= 0x20 )
@ -241,28 +311,45 @@ void Bml_Parser::parseDocument( const char * source )
}
}
const char * Bml_Parser::enumValue(const char *path) const
const char * Bml_Parser::enumValue(std::string const& path) const
{
return document.walkToNode(path).getValue();
return document.walkToNode(path.c_str()).getValue();
}
#if 0
void Bml_Parser::print(Bml_Node const* node, unsigned int indent) const
void Bml_Parser::setValue(std::string const& path, const char *value)
{
if (node == 0) node = &document;
document.walkToNode(path.c_str(), true).setValue(value);
}
for (unsigned i = 0; i < indent; ++i) printf(" ");
void Bml_Parser::setValue(std::string const& path, long value)
{
std::ostringstream str;
str << value;
setValue( path, str.str().c_str() );
}
printf("%s", node->getName());
if (node->getValue()) printf(":%s", node->getValue());
printf("\n");
void Bml_Parser::serialize(std::string & out)
{
std::ostringstream strOut;
serialize(strOut, &document, 0);
out = strOut.str();
}
indent++;
void Bml_Parser::serialize(std::ostringstream & out, Bml_Node const* node, unsigned int indent) const
{
for (unsigned i = 1; i < indent; ++i) out << " ";
if ( indent )
{
out << node->getName();
if (node->getValue()) out << ":" << node->getValue();
out << std::endl;
}
for (unsigned i = 0, j = node->getChildCount(); i < j; ++i)
{
Bml_Node const& child = node->getChild(i);
print( &child, indent );
serialize( out, &child, indent + 1 );
if ( indent == 0 ) out << std::endl;
}
}
#endif

View File

@ -2,6 +2,8 @@
#define BML_PARSER_H
#include <vector>
#include <string>
#include <sstream>
class Bml_Node
{
@ -14,22 +16,25 @@ class Bml_Node
public:
Bml_Node();
Bml_Node(char const* name, size_t max_length = ~0UL);
Bml_Node(Bml_Node const& in);
~Bml_Node();
void clear();
void setLine(const char * line);
void addChild(Bml_Node const& child);
void setLine(const char * line, size_t max_length = ~0UL);
Bml_Node& addChild(Bml_Node const& child);
const char * getName() const;
const char * getValue() const;
void setValue(char const* value);
size_t getChildCount() const;
Bml_Node const& getChild(size_t index) const;
Bml_Node & walkToNode( const char * path );
Bml_Node & walkToNode( const char * path, bool use_indexes = false );
Bml_Node const& walkToNode( const char * path ) const;
};
@ -40,13 +45,16 @@ class Bml_Parser
public:
Bml_Parser() { }
void parseDocument(const char * document);
void parseDocument(const char * document, size_t max_length = ~0UL);
const char * enumValue(const char * path) const;
const char * enumValue(std::string const& path) const;
void setValue(std::string const& path, long value);
void setValue(std::string const& path, const char * value);
#if 0
void print(Bml_Node const* node = 0, unsigned int indent = 0) const;
#endif
void serialize(std::string & out);
private:
void serialize(std::ostringstream & out, Bml_Node const* node, unsigned int indent) const;
};
#endif // BML_PARSER_H

View File

@ -47,6 +47,8 @@ public:
virtual void hash_( byte const* data, size_t size ) BLARGG_PURE( ; )
};
virtual blargg_err_t hash_( Hash_Function& ) const BLARGG_PURE( ; )
virtual blargg_err_t save( gme_writer_t, void* your_data) const { return "Not supported by this format"; }
// Track status/control

View File

@ -144,7 +144,7 @@ void Sfm_Emu::set_tempo_( double t )
// (n ? n : 256)
#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1)
#define META_ENUM_INT(n) (value = metadata.enumValue(n), value ? strtoul(value, &end, 10) : 0)
#define META_ENUM_INT(n,d) (value = metadata.enumValue(n), value ? strtol(value, &end, 10) : (d))
static const byte ipl_rom[0x40] =
{
@ -169,11 +169,7 @@ blargg_err_t Sfm_Emu::start_track_( int track )
int metadata_size = get_le32(ptr + 4);
if ( file_size() < metadata_size + Sfm_Emu::sfm_min_file_size )
return "SFM file too small";
char * temp = new char[metadata_size + 1];
temp[metadata_size] = '\0';
memcpy(temp, ptr + 8, metadata_size);
metadata.parseDocument(temp);
delete [] temp;
metadata.parseDocument((const char *) ptr + 8, metadata_size);
memcpy( smp.iplrom, ipl_rom, 64 );
@ -188,7 +184,7 @@ blargg_err_t Sfm_Emu::start_track_( int track )
char * end;
const char * value;
uint32_t test = META_ENUM_INT("smp:test");
uint32_t test = META_ENUM_INT("smp:test", 0);
smp.status.clock_speed = (test >> 6) & 3;
smp.status.timer_speed = (test >> 4) & 3;
smp.status.timers_enable = test & 0x08;
@ -196,117 +192,127 @@ blargg_err_t Sfm_Emu::start_track_( int track )
smp.status.ram_writable = test & 0x02;
smp.status.timers_disable = test & 0x01;
smp.status.iplrom_enable = META_ENUM_INT("smp:iplrom");
smp.status.dsp_addr = META_ENUM_INT("smp:dspaddr");
smp.status.iplrom_enable = META_ENUM_INT("smp:iplrom",1);
smp.status.dsp_addr = META_ENUM_INT("smp:dspaddr",0);
value = metadata.enumValue("smp:ram");
if (value)
{
smp.status.ram00f8 = strtoul(value, &end, 10);
smp.status.ram00f8 = strtol(value, &end, 10);
if (*end)
{
value = end + 1;
smp.status.ram00f9 = strtoul(value, &end, 10);
smp.status.ram00f9 = strtol(value, &end, 10);
}
}
char temp_path[256];
std::string name;
std::ostringstream oss;
name = "smp:regs:";
smp.regs.pc = META_ENUM_INT(name + "pc", 0xffc0);
smp.regs.a = META_ENUM_INT(name + "a", 0x00);
smp.regs.x = META_ENUM_INT(name + "x", 0x00);
smp.regs.y = META_ENUM_INT(name + "y", 0x00);
smp.regs.s = META_ENUM_INT(name + "s", 0xef);
smp.regs.p = META_ENUM_INT(name + "psw", 0x02);
for (int i = 0; i < 3; ++i)
{
SuperFamicom::SMP::Timer<192> &t = (i == 0 ? smp.timer0 : (i == 1 ? smp.timer1 : *(SuperFamicom::SMP::Timer<192>*)&smp.timer2));
sprintf(temp_path, "smp:timer[%u]:", i);
size_t length = strlen(temp_path);
strcpy(temp_path + length, "enable");
value = metadata.enumValue(temp_path);
oss.str("");
oss.clear();
oss << "smp:timer[" << i << "]:";
name = oss.str();
value = metadata.enumValue(name + "enable");
if (value)
{
t.enable = !!strtoul(value, &end, 10);
t.enable = !!strtol(value, &end, 10);
}
strcpy(temp_path + length, "target");
value = metadata.enumValue(temp_path);
value = metadata.enumValue(name + "target");
if (value)
{
t.target = strtoul(value, &end, 10);
t.target = strtol(value, &end, 10);
}
strcpy(temp_path + length, "stage");
value = metadata.enumValue(temp_path);
value = metadata.enumValue(name + "stage");
if (value)
{
t.stage0_ticks = strtoul(value, &end, 10);
t.stage0_ticks = strtol(value, &end, 10);
if (*end != ',') break;
value = end + 1;
t.stage1_ticks = strtoul(value, &end, 10);
t.stage1_ticks = strtol(value, &end, 10);
if (*end != ',') break;
value = end + 1;
t.stage2_ticks = strtoul(value, &end, 10);
t.stage2_ticks = strtol(value, &end, 10);
if (*end != ',') break;
value = end + 1;
t.stage3_ticks = strtoul(value, &end, 10);
t.stage3_ticks = strtol(value, &end, 10);
}
strcpy(temp_path + length, "line");
value = metadata.enumValue(temp_path);
value = metadata.enumValue(name + "line");
if (value)
{
t.current_line = !!strtoul(value, &end, 10);
t.current_line = !!strtol(value, &end, 10);
}
}
smp.dsp.spc_dsp.m.echo_hist_pos = &smp.dsp.spc_dsp.m.echo_hist[META_ENUM_INT("dsp:echohistaddr")];
smp.dsp.clock = META_ENUM_INT("dsp:clock", 0);
smp.dsp.spc_dsp.m.echo_hist_pos = &smp.dsp.spc_dsp.m.echo_hist[META_ENUM_INT("dsp:echohistaddr", 0)];
value = metadata.enumValue("dsp:echohistdata");
if (value)
{
for (int i = 0; i < 8; ++i)
{
smp.dsp.spc_dsp.m.echo_hist[i][0] = strtoul(value, &end, 10);
smp.dsp.spc_dsp.m.echo_hist[i][0] = strtol(value, &end, 10);
value = strchr(value, ',');
if (!value) break;
++value;
smp.dsp.spc_dsp.m.echo_hist[i][1] = strtoul(value, &end, 10);
smp.dsp.spc_dsp.m.echo_hist[i][1] = strtol(value, &end, 10);
value = strchr(value, ',');
if (!value) break;
++value;
}
}
smp.dsp.spc_dsp.m.phase = META_ENUM_INT("dsp:sample");
smp.dsp.spc_dsp.m.kon = META_ENUM_INT("dsp:kon");
smp.dsp.spc_dsp.m.noise = META_ENUM_INT("dsp:noise");
smp.dsp.spc_dsp.m.counter = META_ENUM_INT("dsp:counter");
smp.dsp.spc_dsp.m.echo_offset = META_ENUM_INT("dsp:echooffset");
smp.dsp.spc_dsp.m.echo_length = META_ENUM_INT("dsp:echolength");
smp.dsp.spc_dsp.m.new_kon = META_ENUM_INT("dsp:koncache");
smp.dsp.spc_dsp.m.endx_buf = META_ENUM_INT("dsp:endx");
smp.dsp.spc_dsp.m.envx_buf = META_ENUM_INT("dsp:envx");
smp.dsp.spc_dsp.m.outx_buf = META_ENUM_INT("dsp:outx");
smp.dsp.spc_dsp.m.t_pmon = META_ENUM_INT("dsp:pmon");
smp.dsp.spc_dsp.m.t_non = META_ENUM_INT("dsp:non");
smp.dsp.spc_dsp.m.t_eon = META_ENUM_INT("dsp:eon");
smp.dsp.spc_dsp.m.t_dir = META_ENUM_INT("dsp:dir");
smp.dsp.spc_dsp.m.t_koff = META_ENUM_INT("dsp:koff");
smp.dsp.spc_dsp.m.t_brr_next_addr = META_ENUM_INT("dsp:brrnext");
smp.dsp.spc_dsp.m.t_adsr0 = META_ENUM_INT("dsp:adsr0");
smp.dsp.spc_dsp.m.t_brr_header = META_ENUM_INT("dsp:brrheader");
smp.dsp.spc_dsp.m.t_brr_byte = META_ENUM_INT("dsp:brrdata");
smp.dsp.spc_dsp.m.t_srcn = META_ENUM_INT("dsp:srcn");
smp.dsp.spc_dsp.m.t_esa = META_ENUM_INT("dsp:esa");
smp.dsp.spc_dsp.m.t_echo_enabled = !META_ENUM_INT("dsp:echodisable");
smp.dsp.spc_dsp.m.t_dir_addr = META_ENUM_INT("dsp:diraddr");
smp.dsp.spc_dsp.m.t_pitch = META_ENUM_INT("dsp:pitch");
smp.dsp.spc_dsp.m.t_output = META_ENUM_INT("dsp:output");
smp.dsp.spc_dsp.m.t_looped = META_ENUM_INT("dsp:looped");
smp.dsp.spc_dsp.m.t_echo_ptr = META_ENUM_INT("dsp:echoaddr");
smp.dsp.spc_dsp.m.phase = META_ENUM_INT("dsp:sample", 0);
smp.dsp.spc_dsp.m.kon = META_ENUM_INT("dsp:kon", 0);
smp.dsp.spc_dsp.m.noise = META_ENUM_INT("dsp:noise", 0);
smp.dsp.spc_dsp.m.counter = META_ENUM_INT("dsp:counter", 0);
smp.dsp.spc_dsp.m.echo_offset = META_ENUM_INT("dsp:echooffset", 0);
smp.dsp.spc_dsp.m.echo_length = META_ENUM_INT("dsp:echolength", 0);
smp.dsp.spc_dsp.m.new_kon = META_ENUM_INT("dsp:koncache", 0);
smp.dsp.spc_dsp.m.endx_buf = META_ENUM_INT("dsp:endx", 0);
smp.dsp.spc_dsp.m.envx_buf = META_ENUM_INT("dsp:envx", 0);
smp.dsp.spc_dsp.m.outx_buf = META_ENUM_INT("dsp:outx", 0);
smp.dsp.spc_dsp.m.t_pmon = META_ENUM_INT("dsp:pmon", 0);
smp.dsp.spc_dsp.m.t_non = META_ENUM_INT("dsp:non", 0);
smp.dsp.spc_dsp.m.t_eon = META_ENUM_INT("dsp:eon", 0);
smp.dsp.spc_dsp.m.t_dir = META_ENUM_INT("dsp:dir", 0);
smp.dsp.spc_dsp.m.t_koff = META_ENUM_INT("dsp:koff", 0);
smp.dsp.spc_dsp.m.t_brr_next_addr = META_ENUM_INT("dsp:brrnext", 0);
smp.dsp.spc_dsp.m.t_adsr0 = META_ENUM_INT("dsp:adsr0", 0);
smp.dsp.spc_dsp.m.t_brr_header = META_ENUM_INT("dsp:brrheader", 0);
smp.dsp.spc_dsp.m.t_brr_byte = META_ENUM_INT("dsp:brrdata", 0);
smp.dsp.spc_dsp.m.t_srcn = META_ENUM_INT("dsp:srcn", 0);
smp.dsp.spc_dsp.m.t_esa = META_ENUM_INT("dsp:esa", 0);
smp.dsp.spc_dsp.m.t_echo_enabled = !META_ENUM_INT("dsp:echodisable", 0);
smp.dsp.spc_dsp.m.t_dir_addr = META_ENUM_INT("dsp:diraddr", 0);
smp.dsp.spc_dsp.m.t_pitch = META_ENUM_INT("dsp:pitch", 0);
smp.dsp.spc_dsp.m.t_output = META_ENUM_INT("dsp:output", 0);
smp.dsp.spc_dsp.m.t_looped = META_ENUM_INT("dsp:looped", 0);
smp.dsp.spc_dsp.m.t_echo_ptr = META_ENUM_INT("dsp:echoaddr", 0);
#define META_ENUM_LEVELS(n, o) \
value = metadata.enumValue(n); \
if (value) \
{ \
(o)[0] = strtoul(value, &end, 10); \
(o)[0] = strtol(value, &end, 10); \
if (*end) \
{ \
value = end + 1; \
(o)[1] = strtoul(value, &end, 10); \
(o)[1] = strtol(value, &end, 10); \
} \
}
@ -318,46 +324,36 @@ blargg_err_t Sfm_Emu::start_track_( int track )
for (int i = 0; i < 8; ++i)
{
sprintf(temp_path, "dsp:voice[%u]:", i);
size_t length = strlen(temp_path);
oss.str("");
oss.clear();
oss << "dsp:voice[" << i << "]:";
name = oss.str();
SuperFamicom::SPC_DSP::voice_t & voice = smp.dsp.spc_dsp.m.voices[i];
strcpy(temp_path + length, "brrhistaddr");
value = metadata.enumValue(temp_path);
value = metadata.enumValue(name + "brrhistaddr");
if (value)
{
voice.buf_pos = strtoul(value, &end, 10);
voice.buf_pos = strtol(value, &end, 10);
}
strcpy(temp_path + length, "brrhistdata");
value = metadata.enumValue(temp_path);
value = metadata.enumValue(name + "brrhistdata");
if (value)
{
for (int j = 0; j < SuperFamicom::SPC_DSP::brr_buf_size; ++j)
{
voice.buf[j] = voice.buf[j + SuperFamicom::SPC_DSP::brr_buf_size] = strtoul(value, &end, 10);
voice.buf[j] = voice.buf[j + SuperFamicom::SPC_DSP::brr_buf_size] = strtol(value, &end, 10);
if (!*end) break;
value = end + 1;
}
}
strcpy(temp_path + length, "interpaddr");
voice.interp_pos = META_ENUM_INT(temp_path);
strcpy(temp_path + length, "brraddr");
voice.brr_addr = META_ENUM_INT(temp_path);
strcpy(temp_path + length, "brroffset");
voice.brr_offset = META_ENUM_INT(temp_path);
strcpy(temp_path + length, "vbit");
voice.vbit = META_ENUM_INT(temp_path);
strcpy(temp_path + length, "vidx");
voice.regs = &smp.dsp.spc_dsp.m.regs[META_ENUM_INT(temp_path)];
strcpy(temp_path + length, "kondelay");
voice.kon_delay = META_ENUM_INT(temp_path);
strcpy(temp_path + length, "envmode");
voice.env_mode = (SuperFamicom::SPC_DSP::env_mode_t) META_ENUM_INT(temp_path);
strcpy(temp_path + length, "env");
voice.env = META_ENUM_INT(temp_path);
strcpy(temp_path + length, "envxout");
voice.t_envx_out = META_ENUM_INT(temp_path);
strcpy(temp_path + length, "envcache");
voice.hidden_env = META_ENUM_INT(temp_path);
voice.interp_pos = META_ENUM_INT(name + "interpaddr",0);
voice.brr_addr = META_ENUM_INT(name + "brraddr",0);
voice.brr_offset = META_ENUM_INT(name + "brroffset",0);
voice.vbit = META_ENUM_INT(name + "vbit",0);
voice.regs = &smp.dsp.spc_dsp.m.regs[META_ENUM_INT(name + "vidx",0)];
voice.kon_delay = META_ENUM_INT(name + "kondelay", 0);
voice.env_mode = (SuperFamicom::SPC_DSP::env_mode_t) META_ENUM_INT(name + "envmode", 0);
voice.env = META_ENUM_INT(name + "env", 0);
voice.t_envx_out = META_ENUM_INT(name + "envxout", 0);
voice.hidden_env = META_ENUM_INT(name + "envcache", 0);
}
filter.set_gain( (int) (gain() * Spc_Filter::gain_unit) );
@ -366,6 +362,153 @@ blargg_err_t Sfm_Emu::start_track_( int track )
#undef META_ENUM_INT
blargg_err_t Sfm_Emu::save( gme_writer_t writer, void* your_data ) const
{
std::string name;
std::ostringstream oss;
Bml_Parser metadata;
const byte * ptr = file_begin();
int metadata_size = get_le32(ptr + 4);
metadata.parseDocument((const char *) ptr + 8, metadata_size);
metadata.setValue( "smp:test", (smp.status.clock_speed << 6) | (smp.status.timer_speed << 4) | (smp.status.timers_enable << 3) | (smp.status.ram_disable << 2) | (smp.status.ram_writable << 1) | (smp.status.timers_disable << 0) );
metadata.setValue( "smp:iplrom", smp.status.iplrom_enable );
metadata.setValue( "smp:dspaddr", smp.status.dsp_addr );
oss.str("");
oss.clear();
oss << smp.status.ram00f8 << "," << smp.status.ram00f9;
metadata.setValue( "smp:ram", oss.str().c_str() );
name = "smp:regs:";
metadata.setValue( name + "pc", smp.regs.pc );
metadata.setValue( name + "a", smp.regs.a );
metadata.setValue( name + "x", smp.regs.x );
metadata.setValue( name + "y", smp.regs.y );
metadata.setValue( name + "s", smp.regs.s );
metadata.setValue( name + "psw", smp.regs.p );
for (int i = 0; i < 3; ++i)
{
SuperFamicom::SMP::Timer<192> const& t = (i == 0 ? smp.timer0 : (i == 1 ? smp.timer1 : *(SuperFamicom::SMP::Timer<192>*)&smp.timer2));
oss.str("");
oss.clear();
oss << "smp:timer[" << i << "]:";
name = oss.str();
metadata.setValue( name + "enable", t.enable );
metadata.setValue( name + "target", t.target );
oss.str("");
oss.clear();
oss << (unsigned long)t.stage0_ticks << "," << (unsigned long)t.stage1_ticks << ","
<< (unsigned long)t.stage2_ticks << "," << (unsigned long)t.stage3_ticks;
metadata.setValue( name + "stage", oss.str().c_str() );
metadata.setValue( name + "line", t.current_line );
}
metadata.setValue( "dsp:clock", smp.dsp.clock );
metadata.setValue( "dsp:echohistaddr", smp.dsp.spc_dsp.m.echo_hist_pos - smp.dsp.spc_dsp.m.echo_hist );
oss.str("");
oss.clear();
for (int i = 0; i < 8; ++i)
{
oss << smp.dsp.spc_dsp.m.echo_hist[i][0] << ","
<< smp.dsp.spc_dsp.m.echo_hist[i][1];
if ( i != 7 ) oss << ",";
}
metadata.setValue( "dsp:echohistdata", oss.str().c_str() );
metadata.setValue( "dsp:sample", smp.dsp.spc_dsp.m.phase );
metadata.setValue( "dsp:kon", smp.dsp.spc_dsp.m.kon );
metadata.setValue( "dsp:noise", smp.dsp.spc_dsp.m.noise );
metadata.setValue( "dsp:counter", smp.dsp.spc_dsp.m.counter );
metadata.setValue( "dsp:echooffset", smp.dsp.spc_dsp.m.echo_offset );
metadata.setValue( "dsp:echolength", smp.dsp.spc_dsp.m.echo_length );
metadata.setValue( "dsp:koncache", smp.dsp.spc_dsp.m.new_kon );
metadata.setValue( "dsp:endx", smp.dsp.spc_dsp.m.endx_buf );
metadata.setValue( "dsp:envx", smp.dsp.spc_dsp.m.envx_buf );
metadata.setValue( "dsp:outx", smp.dsp.spc_dsp.m.outx_buf );
metadata.setValue( "dsp:pmon", smp.dsp.spc_dsp.m.t_pmon );
metadata.setValue( "dsp:non", smp.dsp.spc_dsp.m.t_non );
metadata.setValue( "dsp:eon", smp.dsp.spc_dsp.m.t_eon );
metadata.setValue( "dsp:dir", smp.dsp.spc_dsp.m.t_dir );
metadata.setValue( "dsp:koff", smp.dsp.spc_dsp.m.t_koff );
metadata.setValue( "dsp:brrnext", smp.dsp.spc_dsp.m.t_brr_next_addr );
metadata.setValue( "dsp:adsr0", smp.dsp.spc_dsp.m.t_adsr0 );
metadata.setValue( "dsp:brrheader", smp.dsp.spc_dsp.m.t_brr_header );
metadata.setValue( "dsp:brrdata", smp.dsp.spc_dsp.m.t_brr_byte );
metadata.setValue( "dsp:srcn", smp.dsp.spc_dsp.m.t_srcn );
metadata.setValue( "dsp:esa", smp.dsp.spc_dsp.m.t_esa );
metadata.setValue( "dsp:echodisable", !smp.dsp.spc_dsp.m.t_echo_enabled );
metadata.setValue( "dsp:diraddr", smp.dsp.spc_dsp.m.t_dir_addr );
metadata.setValue( "dsp:pitch", smp.dsp.spc_dsp.m.t_pitch );
metadata.setValue( "dsp:output", smp.dsp.spc_dsp.m.t_output );
metadata.setValue( "dsp:looped", smp.dsp.spc_dsp.m.t_looped );
metadata.setValue( "dsp:echoaddr", smp.dsp.spc_dsp.m.t_echo_ptr );
#define META_WRITE_LEVELS(n, o) \
oss.str(""); \
oss.clear(); \
oss << (o)[0] << "," << (o)[1]; \
metadata.setValue((n), oss.str().c_str());
META_WRITE_LEVELS("dsp:mainout", smp.dsp.spc_dsp.m.t_main_out);
META_WRITE_LEVELS("dsp:echoout", smp.dsp.spc_dsp.m.t_echo_out);
META_WRITE_LEVELS("dsp:echoin", smp.dsp.spc_dsp.m.t_echo_in);
#undef META_WRITE_LEVELS
for (int i = 0; i < 8; ++i)
{
oss.str("");
oss.clear();
oss << "dsp:voice[" << i << "]:";
name = oss.str();
SuperFamicom::SPC_DSP::voice_t const& voice = smp.dsp.spc_dsp.m.voices[i];
metadata.setValue( name + "brrhistaddr", voice.buf_pos );
oss.str("");
oss.clear();
for (int j = 0; j < SuperFamicom::SPC_DSP::brr_buf_size; ++j)
{
oss << voice.buf[j];
if ( j != SuperFamicom::SPC_DSP::brr_buf_size - 1 )
oss << ",";
}
metadata.setValue( name + "brrhistdata", oss.str().c_str() );
metadata.setValue( name + "interpaddr", voice.interp_pos );
metadata.setValue( name + "brraddr", voice.brr_addr );
metadata.setValue( name + "brroffset", voice.brr_offset );
metadata.setValue( name + "vbit", voice.vbit );
metadata.setValue( name + "vidx", voice.regs - smp.dsp.spc_dsp.m.regs);
metadata.setValue( name + "kondelay", voice.kon_delay );
metadata.setValue( name + "envmode", voice.env_mode );
metadata.setValue( name + "env", voice.env );
metadata.setValue( name + "envxout", voice.t_envx_out );
metadata.setValue( name + "envcache", voice.hidden_env );
}
metadata.serialize( name );
RETURN_ERR( writer( your_data, "SFM1", 4 ) );
uint8_t temp[4];
uint32_t meta_length = (uint32_t) name.length();
set_le32( temp, meta_length );
RETURN_ERR( writer( your_data, temp, 4 ) );
RETURN_ERR( writer( your_data, name.c_str(), meta_length ) );
RETURN_ERR( writer( your_data, smp.apuram, 65536 ) );
RETURN_ERR( writer( your_data, smp.dsp.spc_dsp.m.regs, 128 ) );
if ( smp.get_sfm_queue_remain() )
RETURN_ERR( writer( your_data, smp.get_sfm_queue(), smp.get_sfm_queue_remain() ) );
return blargg_ok;
}
blargg_err_t Sfm_Emu::play_and_filter( int count, sample_t out [] )
{
smp.render( out, count );

View File

@ -26,6 +26,9 @@ public:
// The Super Nintendo hardware samples at 32kHz. Other sample rates are
// handled by resampling the 32kHz output; emulation accuracy is not affected.
enum { native_sample_rate = 32000 };
// This will serialize the current state of the emulator into a new SFM file
blargg_err_t serialize( std::vector<uint8_t> & out );
// Disables annoying pseudo-surround effect some music uses
void disable_surround( bool disable = true ) { smp.dsp.disable_surround( disable ); }
@ -33,12 +36,14 @@ public:
// Enables gaussian, cubic or sinc interpolation
void interpolation_level( int level = 0 ) { smp.dsp.spc_dsp.interpolation_level( level ); }
SuperFamicom::SMP const* get_smp() const;
SuperFamicom::SMP * get_smp();
SuperFamicom::SMP const* get_smp() const;
SuperFamicom::SMP * get_smp();
blargg_err_t hash_( Hash_Function& ) const;
static gme_type_t static_type() { return gme_sfm_type; }
virtual blargg_err_t save( gme_writer_t, void* ) const;
// Implementation
public:
@ -65,7 +70,7 @@ private:
blargg_err_t play_and_filter( int count, sample_t out [] );
};
inline SuperFamicom::SMP const* Sfm_Emu::get_smp() const { return &smp; }
inline SuperFamicom::SMP * Sfm_Emu::get_smp() { return &smp; }
inline SuperFamicom::SMP const* Sfm_Emu::get_smp() const { return &smp; }
inline SuperFamicom::SMP * Sfm_Emu::get_smp() { return &smp; }
#endif // SPC_SFM_H

View File

@ -324,6 +324,7 @@ void gme_mute_voices ( Music_Emu* gme, int mask ) { gme->m
void gme_set_equalizer ( Music_Emu* gme, gme_equalizer_t const* eq ) { gme->set_equalizer( *eq ); }
void gme_equalizer ( Music_Emu const* gme, gme_equalizer_t* o ) { *o = gme->equalizer(); }
const char* gme_voice_name ( Music_Emu const* gme, int i ) { return gme->voice_name( i ); }
gme_err_t gme_save ( Music_Emu const* gme, gme_writer_t writer, void* your_data ) { return gme->save( writer, your_data ); }
void gme_effects( Music_Emu const* gme, gme_effects_t* out )
{

View File

@ -230,6 +230,10 @@ gme_err_t gme_load_custom( gme_t*, gme_reader_t, long file_size, void* your_data
/* Loads m3u playlist file from memory (must be done after loading music) */
gme_err_t gme_load_m3u_data( gme_t*, void const* data, long size );
/******** Saving ********/
typedef gme_err_t (*gme_writer_t)( void* your_data, void const* in, long count );
gme_err_t gme_save( gme_t const*, gme_writer_t, void* your_data );
/******** User data ********/

View File

@ -21,6 +21,10 @@ void SMP::enter() {
while(status.clock_speed != 2 && sample_buffer < sample_buffer_end) op_step();
if (status.clock_speed == 2) {
synchronize_dsp();
if (sample_buffer < sample_buffer_end) {
dsp.clock -= 24 * 32 * (sample_buffer_end - sample_buffer) / 2;
synchronize_dsp();
}
}
}

View File

@ -36,6 +36,9 @@ private:
uint8_t const* sfm_queue_end;
public:
void set_sfm_queue(const uint8_t* queue, const uint8_t* queue_end);
const uint8_t* get_sfm_queue() const;
size_t get_sfm_queue_remain() const;
private:
int16_t * sample_buffer;
@ -114,6 +117,9 @@ public:
inline void SMP::set_sfm_queue(const uint8_t *queue, const uint8_t *queue_end) { sfm_queue = queue; sfm_queue_end = queue_end; sfm_last[0] = 0; sfm_last[1] = 0; sfm_last[2] = 0; sfm_last[3] = 0; }
inline const uint8_t* SMP::get_sfm_queue() const { return sfm_queue; }
inline size_t SMP::get_sfm_queue_remain() const { return sfm_queue_end - sfm_queue; }
};
#endif

View File

@ -15,7 +15,7 @@ void SMP::cycle_edge() {
switch(status.clock_speed) {
case 0: break; //100% speed
case 1: add_clocks(24); break; // 50% speed
case 2: clock = 0; break; // 0% speed -- locks S-SMP
case 2: break; // 0% speed -- locks S-SMP -- handled in outer loop
case 3: add_clocks(24 * 9); break; // 10% speed
}
}