diff --git a/Frameworks/GME/GME.xcodeproj/project.pbxproj b/Frameworks/GME/GME.xcodeproj/project.pbxproj index c2e29024f..ea3e84b2b 100644 --- a/Frameworks/GME/GME.xcodeproj/project.pbxproj +++ b/Frameworks/GME/GME.xcodeproj/project.pbxproj @@ -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 */, diff --git a/Frameworks/GME/gme/Bml_Parser.cpp b/Frameworks/GME/gme/Bml_Parser.cpp index fc2463ffc..d2dddafc0 100644 --- a/Frameworks/GME/gme/Bml_Parser.cpp +++ b/Frameworks/GME/gme/Bml_Parser.cpp @@ -1,10 +1,16 @@ #include #include #include -#include #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::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::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::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 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 diff --git a/Frameworks/GME/gme/Bml_Parser.h b/Frameworks/GME/gme/Bml_Parser.h index c46c51146..b556640fa 100644 --- a/Frameworks/GME/gme/Bml_Parser.h +++ b/Frameworks/GME/gme/Bml_Parser.h @@ -2,6 +2,8 @@ #define BML_PARSER_H #include +#include +#include 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 diff --git a/Frameworks/GME/gme/Music_Emu.h b/Frameworks/GME/gme/Music_Emu.h index 32e32a9c6..a7194a714 100644 --- a/Frameworks/GME/gme/Music_Emu.h +++ b/Frameworks/GME/gme/Music_Emu.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 diff --git a/Frameworks/GME/gme/Spc_Sfm.cpp b/Frameworks/GME/gme/Spc_Sfm.cpp index 7f9c0794a..d21c32f99 100644 --- a/Frameworks/GME/gme/Spc_Sfm.cpp +++ b/Frameworks/GME/gme/Spc_Sfm.cpp @@ -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 ); diff --git a/Frameworks/GME/gme/Spc_Sfm.h b/Frameworks/GME/gme/Spc_Sfm.h index 2c27d78b3..63bc66d80 100644 --- a/Frameworks/GME/gme/Spc_Sfm.h +++ b/Frameworks/GME/gme/Spc_Sfm.h @@ -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 & 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 diff --git a/Frameworks/GME/gme/gme.cpp b/Frameworks/GME/gme/gme.cpp index 880b52653..1824d55ff 100644 --- a/Frameworks/GME/gme/gme.cpp +++ b/Frameworks/GME/gme/gme.cpp @@ -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 ) { diff --git a/Frameworks/GME/gme/gme.h b/Frameworks/GME/gme/gme.h index ffac6a7ee..a5c8e277d 100644 --- a/Frameworks/GME/gme/gme.h +++ b/Frameworks/GME/gme/gme.h @@ -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 ********/ diff --git a/Frameworks/GME/gme/higan/smp/smp.cpp b/Frameworks/GME/gme/higan/smp/smp.cpp index 110fa5d16..cc7fd1543 100755 --- a/Frameworks/GME/gme/higan/smp/smp.cpp +++ b/Frameworks/GME/gme/higan/smp/smp.cpp @@ -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(); + } } } diff --git a/Frameworks/GME/gme/higan/smp/smp.hpp b/Frameworks/GME/gme/higan/smp/smp.hpp index f5265be5a..d910705c2 100755 --- a/Frameworks/GME/gme/higan/smp/smp.hpp +++ b/Frameworks/GME/gme/higan/smp/smp.hpp @@ -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 diff --git a/Frameworks/GME/gme/higan/smp/timing.cpp b/Frameworks/GME/gme/higan/smp/timing.cpp index c65de6c7f..bdd27e406 100755 --- a/Frameworks/GME/gme/higan/smp/timing.cpp +++ b/Frameworks/GME/gme/higan/smp/timing.cpp @@ -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 } }