mirror of https://github.com/bsnes-emu/bsnes.git
Update to v084r07 release.
byuu says: Added the new super-fast XML parser. So far, the shaders, cheat files, and cheat database have been updated to allow XML mode once again. Which is sure to please Screwtape =) So it's down to just the cartridge mapping files now, which are always a major pain. I still think BML is better for parsing simplicity, memory usage, disk size, lack of red tape and speed (but horrendously bad for ease of creating files manually), but since the base API is identical, there's no reason not to support both. Especially since the pixel shaders have kind of taken on a life of their own.
This commit is contained in:
parent
0bd21185b8
commit
f947d84309
79635
bsnes/data/cheats.bml
79635
bsnes/data/cheats.bml
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -45,6 +45,7 @@
|
|||
#include <nall/string/wildcard.hpp>
|
||||
#include <nall/string/wrapper.hpp>
|
||||
#include <nall/string/xml.hpp>
|
||||
#include <nall/string/xml-legacy.hpp>
|
||||
#undef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,265 @@
|
|||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
//XML v1.0 subset parser
|
||||
//revision 0.05
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct xml_attribute {
|
||||
string name;
|
||||
string content;
|
||||
virtual string parse() const;
|
||||
};
|
||||
|
||||
struct xml_element : xml_attribute {
|
||||
string parse() const;
|
||||
linear_vector<xml_attribute> attribute;
|
||||
linear_vector<xml_element> element;
|
||||
|
||||
protected:
|
||||
void parse_doctype(const char *&data);
|
||||
bool parse_head(string data);
|
||||
bool parse_body(const char *&data);
|
||||
friend xml_element xml_parse(const char *data);
|
||||
};
|
||||
|
||||
inline string xml_attribute::parse() const {
|
||||
string data;
|
||||
unsigned offset = 0;
|
||||
|
||||
const char *source = content;
|
||||
while(*source) {
|
||||
if(*source == '&') {
|
||||
if(strbegin(source, "<")) { data[offset++] = '<'; source += 4; continue; }
|
||||
if(strbegin(source, ">")) { data[offset++] = '>'; source += 4; continue; }
|
||||
if(strbegin(source, "&")) { data[offset++] = '&'; source += 5; continue; }
|
||||
if(strbegin(source, "'")) { data[offset++] = '\''; source += 6; continue; }
|
||||
if(strbegin(source, """)) { data[offset++] = '"'; source += 6; continue; }
|
||||
}
|
||||
|
||||
//reject illegal characters
|
||||
if(*source == '&') return "";
|
||||
if(*source == '<') return "";
|
||||
if(*source == '>') return "";
|
||||
|
||||
data[offset++] = *source++;
|
||||
}
|
||||
|
||||
data[offset] = 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
inline string xml_element::parse() const {
|
||||
string data;
|
||||
unsigned offset = 0;
|
||||
|
||||
const char *source = content;
|
||||
while(*source) {
|
||||
if(*source == '&') {
|
||||
if(strbegin(source, "<")) { data[offset++] = '<'; source += 4; continue; }
|
||||
if(strbegin(source, ">")) { data[offset++] = '>'; source += 4; continue; }
|
||||
if(strbegin(source, "&")) { data[offset++] = '&'; source += 5; continue; }
|
||||
if(strbegin(source, "'")) { data[offset++] = '\''; source += 6; continue; }
|
||||
if(strbegin(source, """)) { data[offset++] = '"'; source += 6; continue; }
|
||||
}
|
||||
|
||||
if(strbegin(source, "<!--")) {
|
||||
if(auto pos = strpos(source, "-->")) {
|
||||
source += pos() + 3;
|
||||
continue;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
if(strbegin(source, "<![CDATA[")) {
|
||||
if(auto pos = strpos(source, "]]>")) {
|
||||
if(pos() - 9 > 0) {
|
||||
string cdata = substr(source, 9, pos() - 9);
|
||||
data.append(cdata);
|
||||
offset += strlen(cdata);
|
||||
}
|
||||
source += 9 + offset + 3;
|
||||
continue;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
//reject illegal characters
|
||||
if(*source == '&') return "";
|
||||
if(*source == '<') return "";
|
||||
if(*source == '>') return "";
|
||||
|
||||
data[offset++] = *source++;
|
||||
}
|
||||
|
||||
data[offset] = 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
inline void xml_element::parse_doctype(const char *&data) {
|
||||
name = "!DOCTYPE";
|
||||
const char *content_begin = data;
|
||||
|
||||
signed counter = 0;
|
||||
while(*data) {
|
||||
char value = *data++;
|
||||
if(value == '<') counter++;
|
||||
if(value == '>') counter--;
|
||||
if(counter < 0) {
|
||||
content = substr(content_begin, 0, data - content_begin - 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw "...";
|
||||
}
|
||||
|
||||
inline bool xml_element::parse_head(string data) {
|
||||
data.qreplace("\t", " ");
|
||||
data.qreplace("\r", " ");
|
||||
data.qreplace("\n", " ");
|
||||
while(qstrpos(data, " ")) data.qreplace(" ", " ");
|
||||
data.qreplace(" =", "=");
|
||||
data.qreplace("= ", "=");
|
||||
data.rtrim();
|
||||
|
||||
lstring part;
|
||||
part.qsplit(" ", data);
|
||||
|
||||
name = part[0];
|
||||
if(name == "") throw "...";
|
||||
|
||||
for(unsigned i = 1; i < part.size(); i++) {
|
||||
lstring side;
|
||||
side.qsplit("=", part[i]);
|
||||
if(side.size() != 2) throw "...";
|
||||
|
||||
xml_attribute attr;
|
||||
attr.name = side[0];
|
||||
attr.content = side[1];
|
||||
if(strbegin(attr.content, "\"") && strend(attr.content, "\"")) attr.content.trim<1>("\"");
|
||||
else if(strbegin(attr.content, "'") && strend(attr.content, "'")) attr.content.trim<1>("'");
|
||||
else throw "...";
|
||||
attribute.append(attr);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool xml_element::parse_body(const char *&data) {
|
||||
while(true) {
|
||||
if(!*data) return false;
|
||||
if(*data++ != '<') continue;
|
||||
if(*data == '/') return false;
|
||||
|
||||
if(strbegin(data, "!DOCTYPE") == true) {
|
||||
parse_doctype(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(strbegin(data, "!--")) {
|
||||
if(auto offset = strpos(data, "-->")) {
|
||||
data += offset() + 3;
|
||||
continue;
|
||||
} else {
|
||||
throw "...";
|
||||
}
|
||||
}
|
||||
|
||||
if(strbegin(data, "![CDATA[")) {
|
||||
if(auto offset = strpos(data, "]]>")) {
|
||||
data += offset() + 3;
|
||||
continue;
|
||||
} else {
|
||||
throw "...";
|
||||
}
|
||||
}
|
||||
|
||||
auto offset = strpos(data, ">");
|
||||
if(!offset) throw "...";
|
||||
|
||||
string tag = substr(data, 0, offset());
|
||||
data += offset() + 1;
|
||||
const char *content_begin = data;
|
||||
|
||||
bool self_terminating = false;
|
||||
|
||||
if(strend(tag, "?") == true) {
|
||||
self_terminating = true;
|
||||
tag.rtrim<1>("?");
|
||||
} else if(strend(tag, "/") == true) {
|
||||
self_terminating = true;
|
||||
tag.rtrim<1>("/");
|
||||
}
|
||||
|
||||
parse_head(tag);
|
||||
if(self_terminating) return true;
|
||||
|
||||
while(*data) {
|
||||
unsigned index = element.size();
|
||||
xml_element node;
|
||||
if(node.parse_body(data) == false) {
|
||||
if(*data == '/') {
|
||||
signed length = data - content_begin - 1;
|
||||
if(length > 0) content = substr(content_begin, 0, length);
|
||||
|
||||
data++;
|
||||
auto offset = strpos(data, ">");
|
||||
if(!offset) throw "...";
|
||||
|
||||
tag = substr(data, 0, offset());
|
||||
data += offset() + 1;
|
||||
|
||||
tag.replace("\t", " ");
|
||||
tag.replace("\r", " ");
|
||||
tag.replace("\n", " ");
|
||||
while(strpos(tag, " ")) tag.replace(" ", " ");
|
||||
tag.rtrim();
|
||||
|
||||
if(name != tag) throw "...";
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
element.append(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//ensure there is only one root element
|
||||
inline bool xml_validate(xml_element &document) {
|
||||
unsigned root_counter = 0;
|
||||
|
||||
for(unsigned i = 0; i < document.element.size(); i++) {
|
||||
string &name = document.element[i].name;
|
||||
if(strbegin(name, "?")) continue;
|
||||
if(strbegin(name, "!")) continue;
|
||||
if(++root_counter > 1) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline xml_element xml_parse(const char *data) {
|
||||
xml_element self;
|
||||
|
||||
try {
|
||||
while(*data) {
|
||||
xml_element node;
|
||||
if(node.parse_body(data) == false) {
|
||||
break;
|
||||
} else {
|
||||
self.element.append(node);
|
||||
}
|
||||
}
|
||||
|
||||
if(xml_validate(self) == false) throw "...";
|
||||
return self;
|
||||
} catch(const char*) {
|
||||
xml_element empty;
|
||||
return empty;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,265 +1,246 @@
|
|||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
//XML v1.0 subset parser
|
||||
//revision 0.05
|
||||
//revision 0.01
|
||||
|
||||
namespace nall {
|
||||
namespace XML {
|
||||
|
||||
struct xml_attribute {
|
||||
struct Node {
|
||||
string name;
|
||||
string content;
|
||||
virtual string parse() const;
|
||||
};
|
||||
|
||||
struct xml_element : xml_attribute {
|
||||
string parse() const;
|
||||
linear_vector<xml_attribute> attribute;
|
||||
linear_vector<xml_element> element;
|
||||
|
||||
protected:
|
||||
void parse_doctype(const char *&data);
|
||||
bool parse_head(string data);
|
||||
bool parse_body(const char *&data);
|
||||
friend xml_element xml_parse(const char *data);
|
||||
};
|
||||
|
||||
inline string xml_attribute::parse() const {
|
||||
string data;
|
||||
unsigned offset = 0;
|
||||
bool attribute;
|
||||
array<Node*> children;
|
||||
|
||||
const char *source = content;
|
||||
while(*source) {
|
||||
if(*source == '&') {
|
||||
if(strbegin(source, "<")) { data[offset++] = '<'; source += 4; continue; }
|
||||
if(strbegin(source, ">")) { data[offset++] = '>'; source += 4; continue; }
|
||||
if(strbegin(source, "&")) { data[offset++] = '&'; source += 5; continue; }
|
||||
if(strbegin(source, "'")) { data[offset++] = '\''; source += 6; continue; }
|
||||
if(strbegin(source, """)) { data[offset++] = '"'; source += 6; continue; }
|
||||
}
|
||||
|
||||
//reject illegal characters
|
||||
if(*source == '&') return "";
|
||||
if(*source == '<') return "";
|
||||
if(*source == '>') return "";
|
||||
|
||||
data[offset++] = *source++;
|
||||
inline bool isName(char c) const {
|
||||
if(c >= 'A' && c <= 'Z') return true;
|
||||
if(c >= 'a' && c <= 'z') return true;
|
||||
if(c >= '0' && c <= '9') return true;
|
||||
if(c == '.' || c == '_') return true;
|
||||
if(c == '?') return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
data[offset] = 0;
|
||||
return data;
|
||||
}
|
||||
inline bool isWhitespace(char c) const {
|
||||
if(c == ' ' || c == '\t') return true;
|
||||
if(c == '\r' || c == '\n') return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline string xml_element::parse() const {
|
||||
string data;
|
||||
unsigned offset = 0;
|
||||
//copy part of string from source document into target string; decode markup while copying
|
||||
inline void copy(string &target, const char *source, unsigned length) {
|
||||
target.reserve(length + 1);
|
||||
|
||||
const char *source = content;
|
||||
while(*source) {
|
||||
if(*source == '&') {
|
||||
if(strbegin(source, "<")) { data[offset++] = '<'; source += 4; continue; }
|
||||
if(strbegin(source, ">")) { data[offset++] = '>'; source += 4; continue; }
|
||||
if(strbegin(source, "&")) { data[offset++] = '&'; source += 5; continue; }
|
||||
if(strbegin(source, "'")) { data[offset++] = '\''; source += 6; continue; }
|
||||
if(strbegin(source, """)) { data[offset++] = '"'; source += 6; continue; }
|
||||
}
|
||||
#if defined(NALL_XML_LITERAL)
|
||||
memcpy(target(), source, length);
|
||||
target[length] = 0;
|
||||
return;
|
||||
#endif
|
||||
|
||||
if(strbegin(source, "<!--")) {
|
||||
if(auto pos = strpos(source, "-->")) {
|
||||
source += pos() + 3;
|
||||
continue;
|
||||
} else {
|
||||
return "";
|
||||
char *output = target();
|
||||
while(length) {
|
||||
if(*source == '&') {
|
||||
if(!memcmp(source, "<", 4)) { *output++ = '<'; source += 4; length -= 4; continue; }
|
||||
if(!memcmp(source, ">", 4)) { *output++ = '>'; source += 4; length -= 4; continue; }
|
||||
if(!memcmp(source, "&", 5)) { *output++ = '&'; source += 5; length -= 5; continue; }
|
||||
if(!memcmp(source, "'", 6)) { *output++ = '\''; source += 6; length -= 6; continue; }
|
||||
if(!memcmp(source, """, 6)) { *output++ = '\"'; source += 6; length -= 6; continue; }
|
||||
}
|
||||
}
|
||||
|
||||
if(strbegin(source, "<![CDATA[")) {
|
||||
if(auto pos = strpos(source, "]]>")) {
|
||||
if(pos() - 9 > 0) {
|
||||
string cdata = substr(source, 9, pos() - 9);
|
||||
data.append(cdata);
|
||||
offset += strlen(cdata);
|
||||
if(attribute == false && source[0] == '<' && source[1] == '!') {
|
||||
//comment
|
||||
if(!memcmp(source, "<!--", 4)) {
|
||||
source += 4, length -= 4;
|
||||
while(memcmp(source, "-->", 3)) source++, length--;
|
||||
source += 3, length -= 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
//CDATA
|
||||
if(!memcmp(source, "<![CDATA[", 9)) {
|
||||
source += 9, length -= 9;
|
||||
while(memcmp(source, "]]>", 3)) *output++ = *source++, length--;
|
||||
source += 3, length -= 3;
|
||||
continue;
|
||||
}
|
||||
source += 9 + offset + 3;
|
||||
continue;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
||||
*output++ = *source++, length--;
|
||||
}
|
||||
|
||||
//reject illegal characters
|
||||
if(*source == '&') return "";
|
||||
if(*source == '<') return "";
|
||||
if(*source == '>') return "";
|
||||
|
||||
data[offset++] = *source++;
|
||||
*output = 0;
|
||||
}
|
||||
|
||||
data[offset] = 0;
|
||||
return data;
|
||||
}
|
||||
inline bool parseExpression(const char *&p) {
|
||||
if(*(p + 1) != '!') return false;
|
||||
|
||||
inline void xml_element::parse_doctype(const char *&data) {
|
||||
name = "!DOCTYPE";
|
||||
const char *content_begin = data;
|
||||
|
||||
signed counter = 0;
|
||||
while(*data) {
|
||||
char value = *data++;
|
||||
if(value == '<') counter++;
|
||||
if(value == '>') counter--;
|
||||
if(counter < 0) {
|
||||
content = substr(content_begin, 0, data - content_begin - 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw "...";
|
||||
}
|
||||
|
||||
inline bool xml_element::parse_head(string data) {
|
||||
data.qreplace("\t", " ");
|
||||
data.qreplace("\r", " ");
|
||||
data.qreplace("\n", " ");
|
||||
while(qstrpos(data, " ")) data.qreplace(" ", " ");
|
||||
data.qreplace(" =", "=");
|
||||
data.qreplace("= ", "=");
|
||||
data.rtrim();
|
||||
|
||||
lstring part;
|
||||
part.qsplit(" ", data);
|
||||
|
||||
name = part[0];
|
||||
if(name == "") throw "...";
|
||||
|
||||
for(unsigned i = 1; i < part.size(); i++) {
|
||||
lstring side;
|
||||
side.qsplit("=", part[i]);
|
||||
if(side.size() != 2) throw "...";
|
||||
|
||||
xml_attribute attr;
|
||||
attr.name = side[0];
|
||||
attr.content = side[1];
|
||||
if(strbegin(attr.content, "\"") && strend(attr.content, "\"")) attr.content.trim<1>("\"");
|
||||
else if(strbegin(attr.content, "'") && strend(attr.content, "'")) attr.content.trim<1>("'");
|
||||
else throw "...";
|
||||
attribute.append(attr);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool xml_element::parse_body(const char *&data) {
|
||||
while(true) {
|
||||
if(!*data) return false;
|
||||
if(*data++ != '<') continue;
|
||||
if(*data == '/') return false;
|
||||
|
||||
if(strbegin(data, "!DOCTYPE") == true) {
|
||||
parse_doctype(data);
|
||||
//comment
|
||||
if(!memcmp(p, "<!--", 4)) {
|
||||
while(*p && memcmp(p, "-->", 3)) p++;
|
||||
if(!*p) throw "unclosed comment";
|
||||
p += 3;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(strbegin(data, "!--")) {
|
||||
if(auto offset = strpos(data, "-->")) {
|
||||
data += offset() + 3;
|
||||
continue;
|
||||
} else {
|
||||
throw "...";
|
||||
}
|
||||
//CDATA
|
||||
if(!memcmp(p, "<![CDATA[", 9)) {
|
||||
while(*p && memcmp(p, "]]>", 3)) p++;
|
||||
if(!*p) throw "unclosed CDATA";
|
||||
p += 3;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(strbegin(data, "![CDATA[")) {
|
||||
if(auto offset = strpos(data, "]]>")) {
|
||||
data += offset() + 3;
|
||||
continue;
|
||||
} else {
|
||||
throw "...";
|
||||
}
|
||||
//DOCTYPE
|
||||
if(!memcmp(p, "<!DOCTYPE", 9)) {
|
||||
unsigned counter = 0;
|
||||
do {
|
||||
char n = *p++;
|
||||
if(!n) throw "unclosed DOCTYPE";
|
||||
if(n == '<') counter++;
|
||||
if(n == '>') counter--;
|
||||
} while(counter);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto offset = strpos(data, ">");
|
||||
if(!offset) throw "...";
|
||||
|
||||
string tag = substr(data, 0, offset());
|
||||
data += offset() + 1;
|
||||
const char *content_begin = data;
|
||||
|
||||
bool self_terminating = false;
|
||||
|
||||
if(strend(tag, "?") == true) {
|
||||
self_terminating = true;
|
||||
tag.rtrim<1>("?");
|
||||
} else if(strend(tag, "/") == true) {
|
||||
self_terminating = true;
|
||||
tag.rtrim<1>("/");
|
||||
}
|
||||
|
||||
parse_head(tag);
|
||||
if(self_terminating) return true;
|
||||
|
||||
while(*data) {
|
||||
unsigned index = element.size();
|
||||
xml_element node;
|
||||
if(node.parse_body(data) == false) {
|
||||
if(*data == '/') {
|
||||
signed length = data - content_begin - 1;
|
||||
if(length > 0) content = substr(content_begin, 0, length);
|
||||
|
||||
data++;
|
||||
auto offset = strpos(data, ">");
|
||||
if(!offset) throw "...";
|
||||
|
||||
tag = substr(data, 0, offset());
|
||||
data += offset() + 1;
|
||||
|
||||
tag.replace("\t", " ");
|
||||
tag.replace("\r", " ");
|
||||
tag.replace("\n", " ");
|
||||
while(strpos(tag, " ")) tag.replace(" ", " ");
|
||||
tag.rtrim();
|
||||
|
||||
if(name != tag) throw "...";
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
element.append(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//ensure there is only one root element
|
||||
inline bool xml_validate(xml_element &document) {
|
||||
unsigned root_counter = 0;
|
||||
|
||||
for(unsigned i = 0; i < document.element.size(); i++) {
|
||||
string &name = document.element[i].name;
|
||||
if(strbegin(name, "?")) continue;
|
||||
if(strbegin(name, "!")) continue;
|
||||
if(++root_counter > 1) return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//returns true if tag closes itself (<tag/>); false if not (<tag>)
|
||||
inline bool parseHead(const char *&p) {
|
||||
//parse name
|
||||
const char *nameStart = ++p; //skip '<'
|
||||
while(isName(*p)) p++;
|
||||
const char *nameEnd = p;
|
||||
copy(name, nameStart, nameEnd - nameStart);
|
||||
if(name.empty()) throw "missing element name";
|
||||
|
||||
inline xml_element xml_parse(const char *data) {
|
||||
xml_element self;
|
||||
//parse attributes
|
||||
while(*p) {
|
||||
while(isWhitespace(*p)) p++;
|
||||
if(!*p) throw "unclosed attribute";
|
||||
if(*p == '?' || *p == '/' || *p == '>') break;
|
||||
|
||||
try {
|
||||
while(*data) {
|
||||
xml_element node;
|
||||
if(node.parse_body(data) == false) {
|
||||
break;
|
||||
} else {
|
||||
self.element.append(node);
|
||||
}
|
||||
//parse attribute name
|
||||
Node *attribute = new Node;
|
||||
children.append(attribute);
|
||||
attribute->attribute = true;
|
||||
|
||||
const char *nameStart = p;
|
||||
while(isName(*p)) p++;
|
||||
const char *nameEnd = p;
|
||||
copy(attribute->name, nameStart, nameEnd - nameStart);
|
||||
if(attribute->name.empty()) throw "missing attribute name";
|
||||
|
||||
//parse attribute data
|
||||
if(*p++ != '=') throw "missing attribute value";
|
||||
char terminal = *p++;
|
||||
if(terminal != '\'' && terminal != '\"') throw "attribute value not quoted";
|
||||
const char *dataStart = p;
|
||||
while(*p && *p != terminal) p++;
|
||||
if(!*p) throw "missing attribute data terminal";
|
||||
const char *dataEnd = p++; //skip closing terminal
|
||||
|
||||
copy(attribute->data, dataStart, dataEnd - dataStart);
|
||||
}
|
||||
|
||||
if(xml_validate(self) == false) throw "...";
|
||||
return self;
|
||||
} catch(const char*) {
|
||||
xml_element empty;
|
||||
return empty;
|
||||
//parse closure
|
||||
if(*p == '?' && *(p + 1) == '>') { p += 2; return true; }
|
||||
if(*p == '/' && *(p + 1) == '>') { p += 2; return true; }
|
||||
if(*p == '>') { p += 1; return false; }
|
||||
throw "invalid element tag";
|
||||
}
|
||||
}
|
||||
|
||||
//parse element and all of its child elements
|
||||
inline void parseElement(const char *&p) {
|
||||
Node *node = new Node;
|
||||
children.append(node);
|
||||
if(node->parseHead(p) == true) return;
|
||||
node->parse(p);
|
||||
}
|
||||
|
||||
//return true if </tag> matches this node's name
|
||||
inline bool parseClosureElement(const char *&p) {
|
||||
if(p[0] != '<' || p[1] != '/') return false;
|
||||
p += 2;
|
||||
const char *nameStart = p;
|
||||
while(*p && *p != '>') p++;
|
||||
if(*p != '>') throw "unclosed closure element";
|
||||
const char *nameEnd = p++;
|
||||
if(memcmp(name, nameStart, nameEnd - nameStart)) throw "closure element name mismatch";
|
||||
return true;
|
||||
}
|
||||
|
||||
//parse contents of an element
|
||||
inline void parse(const char *&p) {
|
||||
const char *dataStart = p, *dataEnd = p;
|
||||
|
||||
while(*p) {
|
||||
while(*p && *p != '<') p++;
|
||||
if(!*p) break;
|
||||
dataEnd = p;
|
||||
if(parseClosureElement(p) == true) break;
|
||||
if(parseExpression(p) == true) continue;
|
||||
parseElement(p);
|
||||
}
|
||||
|
||||
copy(data, dataStart, dataEnd - dataStart);
|
||||
}
|
||||
|
||||
inline void reset() {
|
||||
for(auto &child : children) delete child;
|
||||
children.reset();
|
||||
}
|
||||
|
||||
struct iterator {
|
||||
inline bool operator!=(const iterator &source) const { return index != source.index; }
|
||||
inline Node& operator*() { return *node.children[index]; }
|
||||
inline iterator& operator++() { index++; return *this; }
|
||||
inline iterator(const Node &node, unsigned index) : node(node), index(index) {}
|
||||
private:
|
||||
const Node &node;
|
||||
unsigned index;
|
||||
};
|
||||
|
||||
inline iterator begin() { return iterator(*this, 0); }
|
||||
inline iterator end() { return iterator(*this, children.size()); }
|
||||
inline const iterator begin() const { return iterator(*this, 0); }
|
||||
inline const iterator end() const { return iterator(*this, children.size()); }
|
||||
|
||||
inline Node& operator[](const char *name) {
|
||||
for(auto &node : *this) {
|
||||
if(node.name == name) return node;
|
||||
}
|
||||
static Node node;
|
||||
return node;
|
||||
}
|
||||
|
||||
inline Node() : attribute(false) {}
|
||||
inline ~Node() { reset(); }
|
||||
|
||||
Node(const Node&) = delete;
|
||||
Node& operator=(const Node&) = delete;
|
||||
};
|
||||
|
||||
struct Document : Node {
|
||||
string error;
|
||||
|
||||
inline bool load(const char *document) {
|
||||
if(document == nullptr) return false;
|
||||
reset();
|
||||
try {
|
||||
parse(document);
|
||||
} catch(const char *error) {
|
||||
reset();
|
||||
this->error = error;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline Document() {}
|
||||
inline Document(const char *document) { load(document); }
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -341,9 +341,9 @@ public:
|
|||
}
|
||||
shader_source_markup = source;
|
||||
|
||||
BML::Document document(shader_source_markup);
|
||||
bool is_hlsl = document["shader"]["language"].value == "HLSL";
|
||||
string shader_source = document["shader"].value;
|
||||
XML::Document document(shader_source_markup);
|
||||
bool is_hlsl = document["shader"]["language"].data == "HLSL";
|
||||
string shader_source = document["shader"]["source"].data;
|
||||
if(shader_source == "") return;
|
||||
|
||||
HMODULE d3dx;
|
||||
|
|
|
@ -136,11 +136,11 @@ public:
|
|||
}
|
||||
|
||||
if(source) {
|
||||
BML::Document document(source);
|
||||
bool is_glsl = document["shader"]["language"].value == "GLSL";
|
||||
fragmentfilter = document["shader"]["fragment"]["filter"].value == "linear" ? 1 : 0;
|
||||
string fragment_source = document["shader"]["fragment"].value;
|
||||
string vertex_source = document["shader"]["vertex"].value;
|
||||
XML::Document document(source);
|
||||
bool is_glsl = document["shader"]["language"].data == "GLSL";
|
||||
fragmentfilter = document["shader"]["fragment"]["filter"].data == "linear" ? 1 : 0;
|
||||
string fragment_source = document["shader"]["fragment"].data;
|
||||
string vertex_source = document["shader"]["vertex"].data;
|
||||
|
||||
if(is_glsl) {
|
||||
if(fragment_source != "") set_fragment_shader(fragment_source);
|
||||
|
|
|
@ -70,8 +70,8 @@ ifeq ($(platform),x)
|
|||
mkdir -p ~/.config/$(name)
|
||||
install -D -m 644 data/$(name).png $(DESTDIR)$(prefix)/share/pixmaps/$(name).png
|
||||
install -D -m 644 data/$(name).desktop $(DESTDIR)$(prefix)/share/applications/$(name).desktop
|
||||
cp data/cheats.bml ~/.config/$(name)/cheats.bml
|
||||
chmod 777 ~/.config/$(name) ~/.config/$(name)/cheats.bml
|
||||
cp data/cheats.xml ~/.config/$(name)/cheats.xml
|
||||
chmod 777 ~/.config/$(name) ~/.config/$(name)/cheats.xml
|
||||
endif
|
||||
|
||||
uninstall:
|
||||
|
|
|
@ -27,7 +27,7 @@ void Application::run() {
|
|||
}
|
||||
|
||||
Application::Application(int argc, char **argv) {
|
||||
title = "bsnes v084.06";
|
||||
title = "bsnes v084.07";
|
||||
|
||||
application = this;
|
||||
quit = false;
|
||||
|
|
|
@ -34,18 +34,18 @@ void CheatDatabase::findCodes() {
|
|||
cheatCode.reset();
|
||||
|
||||
string data;
|
||||
data.readfile(application->path("cheats.bml"));
|
||||
BML::Document document(data);
|
||||
for(auto &root : document) {
|
||||
data.readfile(application->path("cheats.xml"));
|
||||
XML::Document document(data);
|
||||
for(auto &root : document["database"]) {
|
||||
if(root.name != "cartridge") continue;
|
||||
if(root["sha256"].value != interface->sha256()) continue;
|
||||
if(root["sha256"].data != interface->sha256()) continue;
|
||||
|
||||
setTitle(root["title"].value);
|
||||
setTitle(root["title"].data);
|
||||
for(auto &cheat : root) {
|
||||
if(cheat.name != "cheat") continue;
|
||||
if(cheat["description"].exists() == false || cheat["code"].exists() == false) continue;
|
||||
cheatList.append(cheat["description"].value);
|
||||
cheatCode.append({ cheat["code"].value, "\t", cheat["description"].value });
|
||||
if(cheat["description"].name.empty() || cheat["code"].name.empty()) continue;
|
||||
cheatList.append(cheat["description"].data);
|
||||
cheatCode.append({ cheat["code"].data, "\t", cheat["description"].data });
|
||||
}
|
||||
|
||||
setVisible();
|
||||
|
|
|
@ -129,12 +129,12 @@ bool CheatEditor::load(const string &filename) {
|
|||
if(data.readfile(filename) == false) return false;
|
||||
|
||||
unsigned n = 0;
|
||||
BML::Document document(data);
|
||||
XML::Document document(data);
|
||||
for(auto &cheat : document["cartridge"]) {
|
||||
if(cheat.name != "cheat") continue;
|
||||
cheatList.setChecked(n, cheat["enable"].exists());
|
||||
cheatText[n][Code] = cheat["code"].value;
|
||||
cheatText[n][Desc] = cheat["description"].value;
|
||||
cheatList.setChecked(n, cheat["enable"].data == "true");
|
||||
cheatText[n][Code] = cheat["code"].data;
|
||||
cheatText[n][Desc] = cheat["description"].data;
|
||||
if(++n >= 128) break;
|
||||
}
|
||||
|
||||
|
@ -161,12 +161,15 @@ bool CheatEditor::save(const string &filename) {
|
|||
file fp;
|
||||
if(fp.open(filename, file::mode::write) == false) return false;
|
||||
|
||||
fp.print("cartridge sha256:", interface->sha256(), "\n");
|
||||
fp.print("<?xml version='1.0' encoding='UTF-8'?>\n");
|
||||
fp.print("<cartridge sha256='", interface->sha256(), "'>\n");
|
||||
for(unsigned n = 0; n <= lastSave; n++) {
|
||||
fp.print("\tcheat", cheatList.checked(n) ? " enable" : "", "\n");
|
||||
fp.print("\t\tdescription:", cheatText[n][Desc], "\n");
|
||||
fp.print("\t\tcode:", cheatText[n][Code], "\n");
|
||||
fp.print(" <cheat enable='", cheatList.checked(n) ? "true" : "false", "'>\n");
|
||||
fp.print(" <description><![CDATA[", cheatText[n][Desc], "]]></description>\n");
|
||||
fp.print(" <code><![CDATA[", cheatText[n][Code], "]]></code>\n");
|
||||
fp.print(" </cheat>\n");
|
||||
}
|
||||
fp.print("</cartridge>\n");
|
||||
|
||||
fp.close();
|
||||
return true;
|
||||
|
|
Loading…
Reference in New Issue