snes9x/bml.cpp

272 lines
6.0 KiB
C++

#include <vector>
#include <iostream>
#include <fstream>
#include <stack>
#include <cstdio>
#include "port.h"
#include "bml.h"
bml_node::bml_node()
{
type = CHILD;
depth = -1;
}
static inline bool starts_with(const std::string_view &s, const std::string_view &prefix)
{
if (s.length() < prefix.length())
return false;
for (size_t i = 0; i < prefix.length(); i++)
{
if (s[i] != prefix[i])
return false;
}
return true;
}
static inline bool islf(const char c)
{
return (c == '\r' || c == '\n');
}
static inline bool isblank(const char c)
{
return (c == ' ' || c == '\t');
}
static inline bool isblankorlf(const char c)
{
return (islf(c) || isblank(c));
}
static inline bool isalnum(const char c)
{
return ((c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9'));
}
static inline bool bml_valid(char c)
{
return (isalnum(c) || c == '-');
}
static std::string trim(std::string_view sv)
{
while (!sv.empty() && isblank(sv.front()))
sv.remove_prefix(1);
while (!sv.empty() && isblankorlf(sv.back()))
sv.remove_suffix(1);
return std::string(sv);
}
static std::string trim_comments(std::string_view sv)
{
auto comment_location = sv.find("//");
if (comment_location != std::string::npos)
sv.remove_suffix(sv.length() - comment_location);
while (!sv.empty() && isblankorlf(sv.back()))
sv.remove_suffix(1);
return std::string(sv);
}
static inline int bml_read_depth(const std::string_view &str)
{
auto depth = str.find_first_not_of("\t ");
return depth == std::string::npos ? -1 : static_cast<int>(depth);
}
static void bml_parse_depth(bml_node &node, std::string_view &line)
{
auto depth = bml_read_depth(line);
line.remove_prefix(depth);
node.depth = depth;
}
static void bml_parse_name(bml_node &node, std::string_view &line)
{
int len;
for (len = 0; len < line.length() && bml_valid(line[len]); len++) {};
node.name = trim(line.substr(0, len));
line.remove_prefix(len);
}
static void bml_parse_data(bml_node &node, std::string_view &line)
{
if (!line.length()) {
return;
}
if (starts_with(line, "=\"")) {
line.remove_prefix(2);
auto quote_pos = line.find_first_of("\"\n\r");
if (quote_pos != std::string::npos && line[quote_pos] == '\"') {
node.data = line.substr(0, quote_pos);
line.remove_prefix(quote_pos + 1);
}
}
else if (line[0] == '=') {
line.remove_prefix(1);
auto end_pos = line.find_first_of("\n\r ");
node.data = trim(line.substr(0, end_pos));
if (end_pos != std::string::npos)
line.remove_prefix(end_pos);
}
else if (line[0] == ':') {
line.remove_prefix(1);
node.data = trim(line);
line = {};
}
}
static std::string bml_read_line(std::ifstream &fd)
{
std::string line;
while (fd)
{
std::getline(fd, line);
line = trim_comments(line);
if (!line.empty())
{
return line;
}
}
return {};
}
static void bml_parse_attr(bml_node &node, std::string_view &line)
{
while (!line.empty())
{
if (!isblank(line[0]))
return;
while (isblank(line[0]))
line.remove_prefix(1);
bml_node n;
int len = 0;
while (len < line.length() && bml_valid(line[len]))
len++;
if (len == 0)
return;
n.name = trim(line.substr(0, len));
line.remove_prefix(len);
bml_parse_data(n, line);
n.depth = node.depth + 1;
n.type = bml_node::ATTRIBUTE;
node.child.push_back(n);
}
}
static bool contains_space(const std::string &str)
{
for (int i = 0; str[i]; i++)
{
if (isblank(str[i]))
return true;
}
return false;
}
static void bml_print_node(bml_node &node, int depth)
{
for (int i = 0; i < depth * 2; i++)
{
printf(" ");
}
if (!node.name.empty())
printf("%s", node.name.c_str());
if (!node.data.empty())
{
if (contains_space(node.data))
printf("=\"%s\"", node.data.c_str());
else
printf(": %s", node.data.c_str());
}
for (size_t i = 0; i < node.child.size() && node.child[i].type == bml_node::ATTRIBUTE; i++)
{
if (!node.child[i].name.empty())
{
printf(" %s", node.child[i].name.c_str());
if (!node.child[i].data.empty())
{
if (contains_space(node.child[i].data))
printf("=\"%s\"", node.child[i].data.c_str());
else
printf("=%s", node.child[i].data.c_str());
}
}
}
if (depth >= 0)
printf("\n");
for (auto &child : node.child)
bml_print_node(child, depth + 1);
if (depth == 0)
printf("\n");
}
void bml_node::print()
{
bml_print_node(*this, -1);
}
void bml_node::parse(std::ifstream &fd)
{
std::stack<bml_node *> nodestack;
nodestack.push(this);
while (fd)
{
bml_node new_node;
std::string line = bml_read_line(fd);
std::string_view sv(line);
if (line.empty())
return;
int line_depth = bml_read_depth(line);
while (line_depth <= nodestack.top()->depth && nodestack.size() > 1)
nodestack.pop();
bml_parse_depth(new_node, sv);
bml_parse_name(new_node, sv);
bml_parse_data(new_node, sv);
bml_parse_attr(new_node, sv);
nodestack.top()->child.push_back(new_node);
nodestack.push(&nodestack.top()->child.back());
}
}
bml_node *bml_node::find_subnode(const std::string &name)
{
for (auto &i : child)
if (name == i.name)
return &i;
return nullptr;
}
bool bml_node::parse_file(const std::string &filename)
{
std::ifstream file(filename.c_str(), std::ios_base::binary);
if (!file)
return false;
parse(file);
return true;
}