Update to v093 release.

byuu says:

Changelog:
- added Cocoa target: higan can now be compiled for OS X Lion
  [Cydrak, byuu]
- SNES/accuracy profile hires color blending improvements - fixes
  Marvelous text [AWJ]
- fixed a slight bug in SNES/SA-1 VBR support caused by a typo
- added support for multi-pass shaders that can load external textures
  (requires OpenGL 3.2+)
- added game library path (used by ananke->Import Game) to
  Settings->Advanced
- system profiles, shaders and cheats database can be stored in "all
  users" shared folders now (eg /usr/share on Linux)
- all configuration files are in BML format now, instead of XML (much
  easier to read and edit this way)
- main window supports drag-and-drop of game folders (but not game files
  / ZIP archives)
- audio buffer clears when entering a modal loop on Windows (prevents
  audio repetition with DirectSound driver)
- a substantial amount of code clean-up (probably the biggest
  refactoring to date)

One highly desired target for this release was to default to the optimal
drivers instead of the safest drivers, but because AMD drivers don't
seem to like my OpenGL 3.2 driver, I've decided to postpone that. AMD
has too big a market share. Hopefully with v093 officially released, we
can get some public input on what AMD doesn't like.
This commit is contained in:
Tim Allen
2013-08-18 13:21:14 +10:00
parent c74865e171
commit 4e2eb23835
1928 changed files with 4834 additions and 84223 deletions

157
nall/string/markup/bml.hpp Normal file
View File

@@ -0,0 +1,157 @@
#ifdef NALL_STRING_INTERNAL_HPP
//BML v1.0 parser
//revision 0.03
namespace nall {
namespace BML {
struct Node : Markup::Node {
protected:
//test to verify if a valid character for a node name
bool valid(char p) const { //A-Z, a-z, 0-9, -.
return p - 'A' < 26u || p - 'a' < 26u || p - '0' < 10u || p - '-' < 2u;
}
//determine indentation level, without incrementing pointer
unsigned readDepth(const char* p) {
unsigned depth = 0;
while(p[depth] == '\t' || p[depth] == ' ') depth++;
return depth;
}
//determine indentation level
unsigned parseDepth(const char*& p) {
unsigned depth = readDepth(p);
p += depth;
return depth;
}
//read name
void parseName(const char*& p) {
unsigned length = 0;
while(valid(p[length])) length++;
if(length == 0) throw "Invalid node name";
name = substr(p, 0, length);
p += length;
}
void parseData(const char*& p) {
if(*p == '=' && *(p + 1) == '\"') {
unsigned length = 2;
while(p[length] && p[length] != '\n' && p[length] != '\"') length++;
if(p[length] != '\"') throw "Unescaped value";
data = {substr(p, 2, length - 2), "\n"};
p += length + 1;
} else if(*p == '=') {
unsigned length = 1;
while(p[length] && p[length] != '\n' && p[length] != '\"' && p[length] != ' ') length++;
if(p[length] == '\"') throw "Illegal character in value";
data = {substr(p, 1, length - 1), "\n"};
p += length;
} else if(*p == ':') {
unsigned length = 1;
while(p[length] && p[length] != '\n') length++;
data = {substr(p, 1, length - 1), "\n"};
p += length;
}
}
//read all attributes for a node
void parseAttributes(const char*& p) {
while(*p && *p != '\n') {
if(*p != ' ') throw "Invalid node name";
while(*p == ' ') p++; //skip excess spaces
if(*(p + 0) == '/' && *(p + 1) == '/') break; //skip comments
Node node;
node.attribute = true;
unsigned length = 0;
while(valid(p[length])) length++;
if(length == 0) throw "Invalid attribute name";
node.name = substr(p, 0, length);
node.parseData(p += length);
node.data.rtrim<1>("\n");
children.append(node);
}
}
//read a node and all of its child nodes
void parseNode(const lstring& text, unsigned& y) {
const char* p = text[y++];
level = parseDepth(p);
parseName(p);
parseData(p);
parseAttributes(p);
while(y < text.size()) {
unsigned depth = readDepth(text[y]);
if(depth <= level) break;
if(text[y][depth] == ':') {
data.append(substr(text[y++], depth + 1), "\n");
continue;
}
Node node;
node.parseNode(text, y);
children.append(node);
}
data.rtrim<1>("\n");
}
//read top-level nodes
void parse(const string& document) {
lstring text = string{document}.replace("\r", "").split("\n");
//remove empty lines and comment lines
for(unsigned y = 0; y < text.size();) {
unsigned x = 0;
bool empty = true;
while(x < text[y].size()) {
if(text[y][x] == ' ' || text[y][x] == '\t') { x++; continue; }
empty = (text[y][x + 0] == '/' && text[y][x + 1] == '/');
break;
}
if(empty) text.remove(y);
else y++;
}
unsigned y = 0;
while(y < text.size()) {
Node node;
node.parseNode(text, y);
if(node.level > 0) throw "Root nodes cannot be indented";
children.append(node);
}
}
friend class Document;
};
struct Document : Node {
string error;
bool load(const string& document) {
name = "", data = "";
try {
parse(document);
} catch(const char* error) {
this->error = error;
children.reset();
return false;
}
return true;
}
Document(const string& document = "") {
load(document);
}
};
}
}
#endif

View File

@@ -0,0 +1,14 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
namespace Markup {
inline Node Document(const string& markup) {
if(markup.beginswith("<")) return XML::Document(markup);
return BML::Document(markup);
}
}
}
#endif

147
nall/string/markup/node.hpp Normal file
View File

@@ -0,0 +1,147 @@
#ifdef NALL_STRING_INTERNAL_HPP
//note: specific markups inherit from Markup::Node
//vector<Node> will slice any data; so derived nodes must not contain data nor virtual functions
//vector<Node*> would incur a large performance penalty and greatly increased complexity
namespace nall {
namespace Markup {
struct Node {
string name;
string data;
bool attribute;
bool exists() const {
return !name.empty();
}
string text() const {
return string{data}.strip();
}
intmax_t integer() const {
return numeral(text());
}
uintmax_t decimal() const {
return numeral(text());
}
void reset() {
children.reset();
}
bool evaluate(const string& query) const {
if(query.empty()) return true;
lstring rules = string{query}.replace(" ", "").split(",");
for(auto& rule : rules) {
enum class Comparator : unsigned { ID, EQ, NE, LT, LE, GT, GE };
auto comparator = Comparator::ID;
if(rule.match("*!=*")) comparator = Comparator::NE;
else if(rule.match("*<=*")) comparator = Comparator::LE;
else if(rule.match("*>=*")) comparator = Comparator::GE;
else if(rule.match ("*=*")) comparator = Comparator::EQ;
else if(rule.match ("*<*")) comparator = Comparator::LT;
else if(rule.match ("*>*")) comparator = Comparator::GT;
if(comparator == Comparator::ID) {
if(find(rule).size()) continue;
return false;
}
lstring side;
switch(comparator) {
case Comparator::EQ: side = rule.split<1> ("="); break;
case Comparator::NE: side = rule.split<1>("!="); break;
case Comparator::LT: side = rule.split<1> ("<"); break;
case Comparator::LE: side = rule.split<1>("<="); break;
case Comparator::GT: side = rule.split<1> (">"); break;
case Comparator::GE: side = rule.split<1>(">="); break;
}
string data = text();
if(side(0).empty() == false) {
auto result = find(side(0));
if(result.size() == 0) return false;
data = result(0).data;
}
switch(comparator) {
case Comparator::EQ: if(data.match(side(1)) == true) continue; break;
case Comparator::NE: if(data.match(side(1)) == false) continue; break;
case Comparator::LT: if(numeral(data) < numeral(side(1))) continue; break;
case Comparator::LE: if(numeral(data) <= numeral(side(1))) continue; break;
case Comparator::GT: if(numeral(data) > numeral(side(1))) continue; break;
case Comparator::GE: if(numeral(data) >= numeral(side(1))) continue; break;
}
return false;
}
return true;
}
vector<Node> find(const string& query) const {
vector<Node> result;
lstring path = query.split("/");
string name = path.take(0), rule;
unsigned lo = 0u, hi = ~0u;
if(name.match("*[*]")) {
lstring side = name.split<1>("[");
name = side(0);
side = side(1).rtrim<1>("]").split<1>("-");
lo = side(0).empty() ? 0u : numeral(side(0));
hi = side(1).empty() ? ~0u : numeral(side(1));
}
if(name.match("*(*)")) {
lstring side = name.split<1>("(");
name = side(0);
rule = side(1).rtrim<1>(")");
}
unsigned position = 0;
for(auto& node : children) {
if(node.name.match(name) == false) continue;
if(node.evaluate(rule) == false) continue;
bool inrange = position >= lo && position <= hi;
position++;
if(inrange == false) continue;
if(path.size() == 0) result.append(node);
else {
auto list = node.find(path.concatenate("/"));
for(auto& item : list) result.append(item);
}
}
return result;
}
Node operator[](const string& query) const {
auto result = find(query);
return result(0);
}
vector<Node>::iterator begin() { return children.begin(); }
vector<Node>::iterator end() { return children.end(); }
const vector<Node>::const_iterator begin() const { return children.begin(); }
const vector<Node>::const_iterator end() const { return children.end(); }
Node() : attribute(false), level(0) {}
protected:
unsigned level;
vector<Node> children;
};
}
}
#endif

219
nall/string/markup/xml.hpp Normal file
View File

@@ -0,0 +1,219 @@
#ifdef NALL_STRING_INTERNAL_HPP
//XML v1.0 subset parser
//revision 0.03
namespace nall {
namespace XML {
struct Node : Markup::Node {
protected:
inline string escape() const {
string result = data;
result.replace("&", "&amp;");
result.replace("<", "&lt;");
result.replace(">", "&gt;");
if(attribute == false) return result;
result.replace("\'", "&apos;");
result.replace("\"", "&quot;");
return result;
}
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;
}
inline bool isWhitespace(char c) const {
if(c == ' ' || c == '\t') return true;
if(c == '\r' || c == '\n') return true;
return false;
}
//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);
#if defined(NALL_XML_LITERAL)
memcpy(target(), source, length);
target[length] = 0;
return;
#endif
char* output = target.data();
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(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;
}
}
*output++ = *source++, length--;
}
*output = 0;
}
inline bool parseExpression(const char*& p) {
if(*(p + 1) != '!') return false;
//comment
if(!memcmp(p, "<!--", 4)) {
while(*p && memcmp(p, "-->", 3)) p++;
if(!*p) throw "unclosed comment";
p += 3;
return true;
}
//CDATA
if(!memcmp(p, "<![CDATA[", 9)) {
while(*p && memcmp(p, "]]>", 3)) p++;
if(!*p) throw "unclosed CDATA";
p += 3;
return true;
}
//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;
}
return false;
}
//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";
//parse attributes
while(*p) {
while(isWhitespace(*p)) p++;
if(!*p) throw "unclosed attribute";
if(*p == '?' || *p == '/' || *p == '>') break;
//parse attribute name
Node 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);
children.append(attribute);
}
//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;
if(node.parseHead(p) == false) node.parse(p);
children.append(node);
}
//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;
const char* 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);
}
};
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