Added saving capability to GME, currently only supported by SFM
parent
1c9b19af74
commit
b8b93103bf
|
@ -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 */,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
|
|
|
@ -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 ********/
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue