summaryrefslogtreecommitdiffstats
path: root/pokemod/Xml.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'pokemod/Xml.cpp')
-rw-r--r--pokemod/Xml.cpp492
1 files changed, 492 insertions, 0 deletions
diff --git a/pokemod/Xml.cpp b/pokemod/Xml.cpp
new file mode 100644
index 00000000..47d574e1
--- /dev/null
+++ b/pokemod/Xml.cpp
@@ -0,0 +1,492 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name: Xml.cpp
+// Purpose: An XML container for importing and exporting Pokémod files
+// Author: Ben Boeckel
+// Modified by: Ben Boeckel
+// Created: Sun Apr 8 16:07:46 2007
+// Copyright: ©2007 Ben Boeckel and Nerdy Productions
+// Licence:
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+/////////////////////////////////////////////////////////////////////////////
+
+#include "Xml.h"
+
+PokeMod::XmlElement::XmlElement(const String &p)
+{
+ std::ifstream f(p.c_str(), std::ios::in | std::ios::binary);
+ if (!f)
+ isValid = false;
+ else
+ {
+ isValid = Load(f);
+ f.close();
+ }
+ isTag = true;
+ pos = 0;
+}
+
+PokeMod::XmlElement::XmlElement(const String &n, int v)
+{
+ std::cout << "Making int value Element: " << v << "\n";
+ name = n;
+ value.printf("%d", v);
+ isTag = false;
+}
+
+PokeMod::XmlElement::XmlElement(const String &n, unsigned v, bool isId)
+{
+ std::cout << "Making unsigned value Element: " << v << "\n";
+ name = n;
+ isTag = isId;
+ pos = 0;
+ value.printf("%u", v);
+}
+
+PokeMod::XmlElement::XmlElement(const String &n, const String &v)
+{
+ std::cout << "Making string value Element: " << v << "\n";
+ name = n;
+ value = v;
+ isTag = false;
+}
+
+PokeMod::XmlElement::XmlElement(const String &n, bool v)
+{
+ std::cout << "Making bool value Element: " << v << "\n";
+ name = n;
+ value = v ? "true" : "false";
+ isTag = false;
+}
+
+PokeMod::XmlElement::~XmlElement()
+{
+ std::cout << "Destroying " << name << "\n";
+}
+
+void PokeMod::XmlElement::Export(std::ofstream &file, unsigned depth)
+{
+ file << std::string(depth, ' ');
+ if (isTag)
+ {
+ file << '<' << name << " " << value << ">\n";
+ for (std::vector<XmlElement>::iterator i = elements.begin(); i != elements.end(); ++i)
+ i->Export(file, depth + 3);
+ file << std::string(depth, ' ');
+ file << "</" << name << ">\n";
+ }
+ else
+ {
+ unsigned pos;
+ String v = value;
+ while ((pos = v.find("&")) != std::string::npos)
+ v.replace(pos, 1, "&amp;");
+ while ((pos = v.find("<")) != std::string::npos)
+ v.replace(pos, 1, "&lt;");
+ while ((pos = v.find(">")) != std::string::npos)
+ v.replace(pos, 1, "&gt;");
+ while ((pos = v.find("\"")) != std::string::npos)
+ v.replace(pos, 1, "&quot;");
+ while ((pos = v.find("\'")) != std::string::npos)
+ v.replace(pos, 1, "&apos;");
+ file << '<' << name << '>' << v << "</" << name << ">\n";
+ }
+}
+
+PokeMod::XmlElement *PokeMod::XmlElement::AddElement(const XmlElement &xml)
+{
+ if (!isTag)
+ return NULL;
+ elements.push_back(xml);
+ std::cout << "Adding an element...\n";
+ return &elements.back();
+}
+
+PokeMod::XmlElement *PokeMod::XmlElement::AddElement(const String &n, int v)
+{
+ if (!isTag)
+ return NULL;
+ elements.push_back(xml);
+ XmlElement add(n, v);
+ elements.push_back(add);
+ return &elements.back();
+}
+
+PokeMod::XmlElement *PokeMod::XmlElement::AddElement(const String &n, unsigned v, bool isId)
+{
+ if (!isTag)
+ return NULL;
+ elements.push_back(xml);
+ XmlElement add(n, v, isId);
+ elements.push_back(add);
+ return &elements.back();
+}
+
+PokeMod::XmlElement *PokeMod::XmlElement::AddElement(const String &n, const String &v)
+{
+ if (!isTag)
+ return NULL;
+ elements.push_back(xml);
+ XmlElement add(n, v);
+ elements.push_back(add);
+ return &elements.back();
+}
+
+PokeMod::XmlElement *PokeMod::XmlElement::AddElement(const String &n, bool v)
+{
+ if (!isTag)
+ return NULL;
+ elements.push_back(xml);
+ XmlElement add(n, v);
+ elements.push_back(add);
+ return &elements.back();
+}
+
+PokeMod::XmlElement *PokeMod::XmlElement::NextElement()
+{
+ if ((elements.size() <= pos) || !isTag)
+ return NULL;
+ std::cout << '\n' << &elements[pos] << '\n';
+ return &elements[pos++];
+}
+
+void PokeMod::XmlElement::ClearCounter()
+{
+ pos = 0;
+}
+
+PokeMod::String PokeMod::XmlElement::GetName()
+{
+ return name;
+}
+
+void PokeMod::XmlElement::GetValue(int &val, int def)
+{
+ if (!ExtractInt(val, value))
+ val = def;
+}
+
+void PokeMod::XmlElement::GetValue(unsigned &val, unsigned def)
+{
+ if (!ExtractUnsigned(val, value))
+ val = def;
+}
+
+void PokeMod::XmlElement::GetValue(String &val, const String &def)
+{
+ val = isTag ? def : value;
+}
+
+void PokeMod::XmlElement::GetValue(bool &val, bool def)
+{
+ val = isTag ? def : (value == "true");
+}
+
+bool PokeMod::XmlElement::IsValid()
+{
+ return isValid;
+}
+
+bool PokeMod::XmlElement::Save(const String &p)
+{
+ std::ofstream f(p.c_str(), std::ios::out | std::ios::binary);
+ if (!f)
+ return false;
+ else
+ {
+ f << "<?xml version=\"PokéGen\" encoding=\"UTF-8\" ?>\n";
+ Export(f);
+ f.close();
+ }
+ return true;
+}
+
+bool PokeMod::XmlElement::Load(std::ifstream &file)
+{
+ String curLine;
+ String curName;
+ String curValue;
+ std::stack<String> openElements;
+ std::stack<XmlElement *> prevElements;
+ XmlElement *curElement = this;
+ prevElements.push(this);
+ unsigned count = 0;
+ bool isClosing;
+ LinePos pos;
+ bool isId = false;
+ isValid = true;
+ // First line checks
+ std::getline(file, curLine);
+ if (curLine != "<?xml version=\"PokéGen\" encoding=\"UTF-8\" ?>")
+ return (isValid = false);
+ std::getline(file, curLine);
+ if (curLine[count] != '<')
+ isValid = false;
+ std::cout << (isValid ? "Valid\n" : "Not valid\n");
+ if (isValid)
+ {
+ ++count;
+ isValid = ReadValue(name, curLine, count, true);
+ openElements.push(name);
+ if ((curLine[count] != ' '))
+ isValid = false;
+ else
+ {
+ ++count;
+ isValid = ReadValue(curValue, curLine, count);
+ }
+ std::cout << (isValid ? "Valid\n" : "Not valid\n");
+ }
+ std::getline(file, curLine);
+ std::cout << (isValid ? "Valid\n" : "Not valid\n");
+ while (file.good() && isValid)
+ {
+ std::cout << "\nParsing line: \"" << curLine << '\"';
+ count = 0;
+ pos = POS_INDENT;
+ while ((count < curLine.size()) && isValid)
+ {
+ std::cout << "\nParsing char: \'" << curLine[count] << "\'\n";
+ switch (curLine[count])
+ {
+ case ' ':
+ case '\"':
+ break;
+ case '<':
+ if (pos == POS_INDENT)
+ {
+ if (curLine[++count] == '/')
+ {
+ isClosing = true;
+ ++count;
+ }
+ else
+ {
+ std::cout << "Finished indent...\n";
+ isClosing = false;
+ }
+ isValid = ReadValue(curName, curLine, count);
+ std::cout << "Found name: " << curName << '\n';
+ if (isClosing)
+ {
+ std::cout << "Found close of tag...\n";
+ if (curName == openElements.top())
+ {
+ curElement = prevElements.top();
+ prevElements.pop();
+ openElements.pop();
+ }
+ else
+ isValid = false;
+ }
+ else
+ {
+ std::cout << "Reading name...\n";
+ openElements.push(curName);
+ std::cout << "Next char: \'" << curLine[count] << "\'\n";
+ std::cout << "Reading type...\n";
+ pos = POS_VALUE;
+ if (curLine[count] == ' ')
+ {
+ isId = true;
+ ++count;
+ isValid = ReadValue(curValue, curLine, count);
+ }
+ }
+ }
+ else if (pos == POS_VALUE)
+ {
+ std::cout << "Found closing tag...\n";
+ pos = POS_END;
+ if (curLine[++count] != '/')
+ {
+ std::cout << "No slash!...\n";
+ isValid = false;
+ break;
+ }
+ isValid = ReadValue(curName, curLine, ++count);
+ std::cout << "Top element is: \"" << openElements.top() << "\"\n";
+ if (curName == openElements.top())
+ openElements.pop();
+ else
+ isValid = false;
+ }
+ else
+ isValid = false;
+ break;
+ case '>':
+ std::cout << "Closing bracket...\n";
+ if (pos == POS_VALUE)
+ {
+ ++count;
+ std::cout << "Reading value (\'" << curLine[count] << "\')...\n";
+ if (!isId)
+ isValid = ReadValue(curValue, curLine, count);
+ }
+ }
+ std::cout << (isValid ? "valid\n" : "not valid\n");
+ ++count;
+ }
+ if (isValid)
+ {
+ if (isId)
+ {
+ prevElements.push(curElement);
+ unsigned temp;
+ isValid = ExtractUnsigned(temp, curValue);
+ curElement = curElement->AddElement(curName, temp, true);
+ }
+ else
+ curElement->AddElement(curName, curValue);
+ }
+ else
+ break;
+ std::getline(file, curLine);
+ }
+ std::cout << (isValid ? "valid\n" : "not valid\n");
+ if (!openElements.size())
+ isValid = false;
+ return isValid;
+}
+
+bool PokeMod::XmlElement::ReadValue(String &v, const String &line, unsigned &pos, bool isId)
+{
+ unsigned start = pos;
+ bool notDone = true;
+ v.assign("");
+ while (notDone && (pos < line.length()))
+ {
+ std::cout << "parsing: " << line[pos] << '\n';
+ switch (line[pos])
+ {
+ case 'a' ... 'z':
+ case 'A' ... 'Z':
+ case '0' ... '9':
+ break;
+ case ' ':
+ if (isId)
+ {
+ --pos;
+ notDone = false;
+ }
+ break;
+ case '\n':
+ case '\r':
+ return false;
+ case '>':
+ if (isId)
+ {
+ --pos;
+ notDone = false;
+ }
+ else
+ isValid = false;
+ break;
+ case '&':
+ if (isId)
+ return false;
+ v.append(line.substr(start, pos - start));
+ if (line.substr(pos, 4) == "&amp;")
+ {
+ v += '&';
+ pos += 4;
+ }
+ else if (line.substr(pos, 4) == "&lt;")
+ {
+ v += '<';
+ pos += 3;
+ }
+ else if (line.substr(pos, 4) == "&gt;")
+ {
+ v += '>';
+ pos += 3;
+ }
+ else if (line.substr(pos, 6) == "&quot;")
+ {
+ v += '\"';
+ pos += 5;
+ }
+ else if (line.substr(pos, 6) == "&apos;")
+ {
+ v += '\'';
+ pos += 5;
+ }
+ start = pos + 1;
+ break;
+ default:
+ if (isId)
+ return false;
+ }
+ ++pos;
+ }
+ v.append(line.substr(start, pos - start));
+ std::cout << "Read string as " << v << '\n';
+ return true;
+}
+
+bool PokeMod::XmlElement::ExtractInt(int &v, const String &str)
+{
+ bool notDone = true;
+ unsigned pos = 0;
+ bool isNegative = (str[pos] == '-');
+ if (isNegative)
+ ++pos;
+ v = 0;
+ while (notDone && (pos < str.length()))
+ {
+ switch (str[pos])
+ {
+ case '0' ... '9':
+ v = 10 * v + (str[pos] - '0');
+ break;
+ case '<':
+ --pos;
+ notDone = false;
+ break;
+ default:
+ return false;
+ }
+ ++pos;
+ }
+ if (isNegative)
+ v = -v;
+ std::cout << "Read int as " << v << '\n';
+ return true;
+}
+
+bool PokeMod::XmlElement::ExtractUnsigned(unsigned &v, const String &str)
+{
+ bool notDone = true;
+ unsigned pos = 0;
+ v = 0;
+ while (notDone && (pos < str.length()))
+ {
+ switch (str[pos])
+ {
+ case '0' ... '9':
+ v = 10 * v + (str[pos] - '0');
+ break;
+ case '\"':
+ case '<':
+ --pos;
+ notDone = false;
+ break;
+ default:
+ return false;
+ }
+ ++pos;
+ }
+ std::cout << "Read unsigned as " << v << '\n';
+ return true;
+}