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:
Tim Allen 2011-12-30 17:41:29 +11:00
parent 0bd21185b8
commit f947d84309
11 changed files with 113051 additions and 79887 deletions

File diff suppressed because it is too large Load Diff

112549
bsnes/data/cheats.xml Executable file

File diff suppressed because it is too large Load Diff

View File

@ -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

265
bsnes/nall/string/xml-legacy.hpp Executable file
View File

@ -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, "&lt;")) { data[offset++] = '<'; source += 4; continue; }
if(strbegin(source, "&gt;")) { data[offset++] = '>'; source += 4; continue; }
if(strbegin(source, "&amp;")) { data[offset++] = '&'; source += 5; continue; }
if(strbegin(source, "&apos;")) { data[offset++] = '\''; source += 6; continue; }
if(strbegin(source, "&quot;")) { 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, "&lt;")) { data[offset++] = '<'; source += 4; continue; }
if(strbegin(source, "&gt;")) { data[offset++] = '>'; source += 4; continue; }
if(strbegin(source, "&amp;")) { data[offset++] = '&'; source += 5; continue; }
if(strbegin(source, "&apos;")) { data[offset++] = '\''; source += 6; continue; }
if(strbegin(source, "&quot;")) { 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

View File

@ -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, "&lt;")) { data[offset++] = '<'; source += 4; continue; }
if(strbegin(source, "&gt;")) { data[offset++] = '>'; source += 4; continue; }
if(strbegin(source, "&amp;")) { data[offset++] = '&'; source += 5; continue; }
if(strbegin(source, "&apos;")) { data[offset++] = '\''; source += 6; continue; }
if(strbegin(source, "&quot;")) { 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, "&lt;")) { data[offset++] = '<'; source += 4; continue; }
if(strbegin(source, "&gt;")) { data[offset++] = '>'; source += 4; continue; }
if(strbegin(source, "&amp;")) { data[offset++] = '&'; source += 5; continue; }
if(strbegin(source, "&apos;")) { data[offset++] = '\''; source += 6; continue; }
if(strbegin(source, "&quot;")) { 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, "&lt;", 4)) { *output++ = '<'; source += 4; length -= 4; continue; }
if(!memcmp(source, "&gt;", 4)) { *output++ = '>'; source += 4; length -= 4; continue; }
if(!memcmp(source, "&amp;", 5)) { *output++ = '&'; source += 5; length -= 5; continue; }
if(!memcmp(source, "&apos;", 6)) { *output++ = '\''; source += 6; length -= 6; continue; }
if(!memcmp(source, "&quot;", 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

View File

@ -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;

View File

@ -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);

View File

@ -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:

View File

@ -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;

View File

@ -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();

View File

@ -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;