First pass idLib conversion from hex rays4.
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "../text/str.h"
|
||||
|
||||
class idXMLAttribute {
|
||||
public:
|
||||
idXMLAttribute(const char* attributeName = "", const char* attributeValue = "")
|
||||
: name(attributeName), value(attributeValue) {
|
||||
name.TrimWhitespaceRecovered();
|
||||
}
|
||||
|
||||
const char* GetName() const { return name.c_str(); }
|
||||
const char* GetValue() const { return value.c_str(); }
|
||||
void SetValue(const char* newValue) { value = newValue; }
|
||||
void FormatEntities() {
|
||||
name.ReplaceRecovered("&", "&");
|
||||
name.ReplaceRecovered("<", "<");
|
||||
name.ReplaceRecovered(">", ">");
|
||||
value.ReplaceRecovered("&", "&");
|
||||
value.ReplaceRecovered("<", "<");
|
||||
value.ReplaceRecovered(">", ">");
|
||||
}
|
||||
|
||||
private:
|
||||
idStr name;
|
||||
idStr value;
|
||||
};
|
||||
|
||||
static_assert(sizeof(idXMLAttribute) == 64,
|
||||
"Recovered idXMLAttribute ABI changed");
|
||||
@@ -0,0 +1,71 @@
|
||||
#include "xmlelement.h"
|
||||
|
||||
idXMLElement::idXMLElement(const char* const elementName)
|
||||
: name(elementName), value(), attributes(), children() {
|
||||
}
|
||||
|
||||
idXMLElement::idXMLElement(const char* const elementName,
|
||||
const char* const elementValue)
|
||||
: name(elementName), value(elementValue), attributes(), children() {
|
||||
}
|
||||
|
||||
idXMLElement::~idXMLElement() {
|
||||
for (int index = 0; index < children.Num(); ++index) {
|
||||
idXMLElement* const child = children[index];
|
||||
if (child != nullptr) {
|
||||
child->~idXMLElement();
|
||||
std::free(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
idXMLAttribute* idXMLElement::AddAttribute(const char* const attributeName,
|
||||
const char* const attributeValue) {
|
||||
return attributes.Append(idXMLAttribute(attributeName, attributeValue));
|
||||
}
|
||||
|
||||
idXMLElement* idXMLElement::AddChild(const char* const childName,
|
||||
const char* const childValue) {
|
||||
idXMLElement* const child = Create(childName, childValue);
|
||||
if (child == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if (children.Append(child) == nullptr) {
|
||||
Destroy(child);
|
||||
return nullptr;
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
idXMLElement* idXMLElement::Create(const char* const elementName,
|
||||
const char* const elementValue) {
|
||||
void* const storage = std::malloc(sizeof(idXMLElement));
|
||||
return storage == nullptr ? nullptr
|
||||
: new (storage) idXMLElement(elementName, elementValue);
|
||||
}
|
||||
|
||||
void idXMLElement::Destroy(idXMLElement* const element) {
|
||||
if (element != nullptr) {
|
||||
element->~idXMLElement();
|
||||
std::free(element);
|
||||
}
|
||||
}
|
||||
|
||||
void idXMLElement::FormatStrings() {
|
||||
FormatStrings_R(this);
|
||||
}
|
||||
|
||||
void idXMLElement::FormatStrings_R(idXMLElement* const element) {
|
||||
for (int index = 0; index < element->children.Num(); ++index) {
|
||||
FormatStrings_R(element->children[index]);
|
||||
}
|
||||
element->name.ReplaceRecovered("&", "&");
|
||||
element->name.ReplaceRecovered("<", "<");
|
||||
element->name.ReplaceRecovered(">", ">");
|
||||
element->value.ReplaceRecovered("&", "&");
|
||||
element->value.ReplaceRecovered("<", "<");
|
||||
element->value.ReplaceRecovered(">", ">");
|
||||
for (int index = 0; index < element->attributes.Num(); ++index) {
|
||||
element->attributes[index].FormatEntities();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
|
||||
#include "xmlattribute.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <new>
|
||||
|
||||
template<typename type>
|
||||
class idRecoveredList {
|
||||
public:
|
||||
explicit idRecoveredList(const std::uint8_t tag = 44,
|
||||
const std::int16_t initialGranularity = 16)
|
||||
: list(nullptr), num(0), size(0), granularity(initialGranularity),
|
||||
memTag(tag), listStatic(0) {
|
||||
}
|
||||
|
||||
~idRecoveredList() {
|
||||
Clear();
|
||||
}
|
||||
|
||||
idRecoveredList(const idRecoveredList&) = delete;
|
||||
idRecoveredList& operator=(const idRecoveredList&) = delete;
|
||||
|
||||
int Num() const { return num; }
|
||||
type& operator[](const int index) { return list[index]; }
|
||||
const type& operator[](const int index) const { return list[index]; }
|
||||
|
||||
type* Append(const type& value) {
|
||||
if (num == size && !Grow()) {
|
||||
return nullptr;
|
||||
}
|
||||
list[num] = value;
|
||||
return &list[num++];
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
if (list != nullptr) {
|
||||
for (int index = 0; index < size; ++index) {
|
||||
list[index].~type();
|
||||
}
|
||||
std::free(list);
|
||||
}
|
||||
list = nullptr;
|
||||
num = 0;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
type* list;
|
||||
int num;
|
||||
int size;
|
||||
std::int16_t granularity;
|
||||
std::uint8_t memTag;
|
||||
std::uint8_t listStatic;
|
||||
|
||||
bool Grow() {
|
||||
const int amount = granularity > 0 ? granularity : 16;
|
||||
const int newSize = size + amount;
|
||||
type* const replacement = static_cast<type*>(
|
||||
std::malloc(sizeof(type) * static_cast<std::size_t>(newSize))
|
||||
);
|
||||
if (replacement == nullptr) {
|
||||
return false;
|
||||
}
|
||||
for (int index = 0; index < newSize; ++index) {
|
||||
new (&replacement[index]) type();
|
||||
}
|
||||
for (int index = 0; index < num; ++index) {
|
||||
replacement[index] = list[index];
|
||||
}
|
||||
if (list != nullptr) {
|
||||
for (int index = 0; index < size; ++index) {
|
||||
list[index].~type();
|
||||
}
|
||||
std::free(list);
|
||||
}
|
||||
list = replacement;
|
||||
size = newSize;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(idRecoveredList<int>) == 16,
|
||||
"Recovered idList ABI changed");
|
||||
|
||||
class idXMLElement {
|
||||
public:
|
||||
explicit idXMLElement(const char* elementName = "");
|
||||
idXMLElement(const char* elementName, const char* elementValue);
|
||||
~idXMLElement();
|
||||
|
||||
idXMLElement(const idXMLElement&) = delete;
|
||||
idXMLElement& operator=(const idXMLElement&) = delete;
|
||||
|
||||
const char* GetName() const { return name.c_str(); }
|
||||
const char* GetValue() const { return value.c_str(); }
|
||||
void SetValue(const char* newValue) { value = newValue; }
|
||||
|
||||
idXMLAttribute* AddAttribute(const char* attributeName,
|
||||
const char* attributeValue);
|
||||
idXMLElement* AddChild(const char* childName, const char* childValue = "");
|
||||
bool AdoptChild(idXMLElement* child) { return children.Append(child) != nullptr; }
|
||||
void AppendValue(const char* text) { value.Append(text); }
|
||||
void AppendValue(char character) { value.Append(character); }
|
||||
int NumAttributes() const { return attributes.Num(); }
|
||||
int NumChildren() const { return children.Num(); }
|
||||
idXMLAttribute& GetAttribute(const int index) { return attributes[index]; }
|
||||
const idXMLAttribute& GetAttribute(const int index) const { return attributes[index]; }
|
||||
idXMLElement* GetChild(const int index) { return children[index]; }
|
||||
const idXMLElement* GetChild(const int index) const { return children[index]; }
|
||||
|
||||
void FormatStrings();
|
||||
|
||||
static idXMLElement* Create(const char* elementName,
|
||||
const char* elementValue = "");
|
||||
static void Destroy(idXMLElement* element);
|
||||
|
||||
private:
|
||||
idStr name;
|
||||
idStr value;
|
||||
idRecoveredList<idXMLAttribute> attributes;
|
||||
idRecoveredList<idXMLElement*> children;
|
||||
|
||||
void FormatStrings_R(idXMLElement* element);
|
||||
};
|
||||
|
||||
static_assert(sizeof(idXMLElement) == 96,
|
||||
"Recovered idXMLElement ABI changed");
|
||||
@@ -0,0 +1,201 @@
|
||||
#include "xmlreader.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
idXMLReader::idXMLReader(const char* const fileName)
|
||||
: sourceName(fileName), document(nullptr), cursor(nullptr), end(nullptr) {
|
||||
FILE* file = nullptr;
|
||||
if (fileName == nullptr || fopen_s(&file, fileName, "rb") != 0
|
||||
|| file == nullptr) {
|
||||
return;
|
||||
}
|
||||
std::fseek(file, 0, SEEK_END);
|
||||
const long length = std::ftell(file);
|
||||
std::fseek(file, 0, SEEK_SET);
|
||||
if (length >= 0) {
|
||||
document = static_cast<char*>(std::malloc(static_cast<std::size_t>(length) + 1));
|
||||
if (document != nullptr) {
|
||||
const std::size_t read = std::fread(document, 1,
|
||||
static_cast<std::size_t>(length), file);
|
||||
document[read] = '\0';
|
||||
cursor = document;
|
||||
end = document + read;
|
||||
}
|
||||
}
|
||||
std::fclose(file);
|
||||
}
|
||||
|
||||
idXMLReader::~idXMLReader() {
|
||||
std::free(document);
|
||||
}
|
||||
|
||||
void idXMLReader::SkipWhitespace() {
|
||||
while (cursor < end
|
||||
&& std::isspace(static_cast<unsigned char>(*cursor)) != 0) {
|
||||
++cursor;
|
||||
}
|
||||
}
|
||||
|
||||
bool idXMLReader::Consume(const char character) {
|
||||
if (cursor < end && *cursor == character) {
|
||||
++cursor;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool idXMLReader::Consume(const char* const text) {
|
||||
const std::size_t length = std::strlen(text);
|
||||
if (static_cast<std::size_t>(end - cursor) >= length
|
||||
&& std::memcmp(cursor, text, length) == 0) {
|
||||
cursor += length;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool idXMLReader::SkipMisc() {
|
||||
SkipWhitespace();
|
||||
if (Consume("<?")) {
|
||||
const char* close = std::strstr(cursor, "?>");
|
||||
if (close == nullptr || close > end) return false;
|
||||
cursor = close + 2;
|
||||
return true;
|
||||
}
|
||||
if (Consume("<!--")) {
|
||||
const char* close = std::strstr(cursor, "-->");
|
||||
if (close == nullptr || close > end) return false;
|
||||
cursor = close + 3;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
idStr idXMLReader::ReadName() {
|
||||
idStr result;
|
||||
while (cursor < end) {
|
||||
const unsigned char character = static_cast<unsigned char>(*cursor);
|
||||
if (std::isalnum(character) == 0 && character != '_'
|
||||
&& character != '-' && character != ':' && character != '.'
|
||||
&& character != '&' && character != ';' && character != '#') {
|
||||
break;
|
||||
}
|
||||
result.Append(*cursor++);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
idStr idXMLReader::ReadQuotedValue() {
|
||||
idStr result;
|
||||
if (cursor >= end || (*cursor != '"' && *cursor != '\'')) {
|
||||
return result;
|
||||
}
|
||||
const char quote = *cursor++;
|
||||
while (cursor < end && *cursor != quote) {
|
||||
result.Append(*cursor++);
|
||||
}
|
||||
Consume(quote);
|
||||
return result;
|
||||
}
|
||||
|
||||
idXMLElement* idXMLReader::ReadElement_R() {
|
||||
SkipWhitespace();
|
||||
while (SkipMisc()) {
|
||||
SkipWhitespace();
|
||||
}
|
||||
if (!Consume('<') || (cursor < end && *cursor == '/')) {
|
||||
return nullptr;
|
||||
}
|
||||
const idStr name = ReadName();
|
||||
if (name.Length() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
idXMLElement* const element = idXMLElement::Create(name.c_str());
|
||||
if (element == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
SkipWhitespace();
|
||||
if (Consume("/>")) {
|
||||
return element;
|
||||
}
|
||||
if (Consume('>')) {
|
||||
break;
|
||||
}
|
||||
const idStr attributeName = ReadName();
|
||||
SkipWhitespace();
|
||||
if (attributeName.Length() == 0 || !Consume('=')) {
|
||||
idXMLElement::Destroy(element);
|
||||
return nullptr;
|
||||
}
|
||||
SkipWhitespace();
|
||||
const idStr attributeValue = ReadQuotedValue();
|
||||
if (element->AddAttribute(attributeName.c_str(), attributeValue.c_str()) == nullptr) {
|
||||
idXMLElement::Destroy(element);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if (cursor >= end) {
|
||||
idXMLElement::Destroy(element);
|
||||
return nullptr;
|
||||
}
|
||||
if (Consume("<!--")) {
|
||||
const char* close = std::strstr(cursor, "-->");
|
||||
if (close == nullptr || close > end) {
|
||||
idXMLElement::Destroy(element);
|
||||
return nullptr;
|
||||
}
|
||||
cursor = close + 3;
|
||||
continue;
|
||||
}
|
||||
if (Consume("<![CDATA[")) {
|
||||
const char* close = std::strstr(cursor, "]]>");
|
||||
if (close == nullptr || close > end) {
|
||||
idXMLElement::Destroy(element);
|
||||
return nullptr;
|
||||
}
|
||||
while (cursor < close) element->AppendValue(*cursor++);
|
||||
cursor = close + 3;
|
||||
continue;
|
||||
}
|
||||
if (Consume("</")) {
|
||||
const idStr closeName = ReadName();
|
||||
SkipWhitespace();
|
||||
if (!Consume('>') || std::strcmp(name.c_str(), closeName.c_str()) != 0) {
|
||||
idXMLElement::Destroy(element);
|
||||
return nullptr;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
if (*cursor == '<') {
|
||||
idXMLElement* const child = ReadElement_R();
|
||||
if (child == nullptr || !element->AdoptChild(child)) {
|
||||
idXMLElement::Destroy(child);
|
||||
idXMLElement::Destroy(element);
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
element->AppendValue(*cursor++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
idXMLElement* idXMLReader::ReadDocument() {
|
||||
if (document == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
cursor = document;
|
||||
while (SkipMisc()) {
|
||||
}
|
||||
idXMLElement* const root = ReadElement_R();
|
||||
if (root != nullptr) {
|
||||
root->FormatStrings();
|
||||
}
|
||||
return root;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "xmlelement.h"
|
||||
|
||||
class idXMLReader {
|
||||
public:
|
||||
explicit idXMLReader(const char* fileName);
|
||||
~idXMLReader();
|
||||
|
||||
idXMLElement* ReadDocument();
|
||||
bool IsLoaded() const { return document != nullptr; }
|
||||
|
||||
private:
|
||||
idStr sourceName;
|
||||
char* document;
|
||||
const char* cursor;
|
||||
const char* end;
|
||||
|
||||
void SkipWhitespace();
|
||||
bool SkipMisc();
|
||||
bool Consume(char character);
|
||||
bool Consume(const char* text);
|
||||
idStr ReadName();
|
||||
idStr ReadQuotedValue();
|
||||
idXMLElement* ReadElement_R();
|
||||
};
|
||||
@@ -0,0 +1,218 @@
|
||||
#include "xmlwriter.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
|
||||
idXMLWriter::idXMLWriter()
|
||||
: xmlFile(nullptr), tabLevel(0), hasRoot(false),
|
||||
firstOpenTag(nullptr), lastOpenTag(nullptr) {
|
||||
}
|
||||
|
||||
idXMLWriter::~idXMLWriter() {
|
||||
CloseFile();
|
||||
}
|
||||
|
||||
bool idXMLWriter::OpenFile(const char* const fileName, const bool append,
|
||||
const bool writeDeclaration) {
|
||||
if (xmlFile != nullptr || fileName == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (fopen_s(&xmlFile, fileName, append ? "ab" : "wb") != 0
|
||||
|| xmlFile == nullptr) {
|
||||
xmlFile = nullptr;
|
||||
return false;
|
||||
}
|
||||
tabLevel = 0;
|
||||
hasRoot = append;
|
||||
if (!append && writeDeclaration) {
|
||||
std::fputs("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n", xmlFile);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool idXMLWriter::CloseFile() {
|
||||
if (xmlFile == nullptr) {
|
||||
return true;
|
||||
}
|
||||
while (firstOpenTag != nullptr) {
|
||||
CloseElement();
|
||||
}
|
||||
const bool success = std::fclose(xmlFile) == 0;
|
||||
xmlFile = nullptr;
|
||||
tabLevel = 0;
|
||||
hasRoot = false;
|
||||
return success;
|
||||
}
|
||||
|
||||
bool idXMLWriter::TestRoot(const char*) {
|
||||
if (xmlFile == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (tabLevel == 0 && hasRoot) {
|
||||
return false;
|
||||
}
|
||||
if (tabLevel == 0) {
|
||||
hasRoot = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool idXMLWriter::WriteIndent() {
|
||||
if (xmlFile == nullptr) {
|
||||
return false;
|
||||
}
|
||||
for (int index = 0; index < tabLevel; ++index) {
|
||||
if (std::fputs(" ", xmlFile) < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool idXMLWriter::WriteEscaped(const char* text) {
|
||||
if (xmlFile == nullptr) {
|
||||
return false;
|
||||
}
|
||||
const char* cursor = text == nullptr ? "" : text;
|
||||
while (*cursor != '\0') {
|
||||
const char* escaped = nullptr;
|
||||
switch (*cursor) {
|
||||
case '&': escaped = "&"; break;
|
||||
case '<': escaped = "<"; break;
|
||||
case '>': escaped = ">"; break;
|
||||
case '"': escaped = """; break;
|
||||
case '\'': escaped = "'"; break;
|
||||
default: break;
|
||||
}
|
||||
if (escaped != nullptr) {
|
||||
if (std::fputs(escaped, xmlFile) < 0) return false;
|
||||
} else if (std::fputc(*cursor, xmlFile) == EOF) {
|
||||
return false;
|
||||
}
|
||||
++cursor;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool idXMLWriter::WriteAttributes(
|
||||
const idRecoveredList<idXMLAttribute>& attributes) {
|
||||
for (int index = 0; index < attributes.Num(); ++index) {
|
||||
if (std::fputc(' ', xmlFile) == EOF
|
||||
|| !WriteEscaped(attributes[index].GetName())
|
||||
|| std::fputs("=\"", xmlFile) < 0
|
||||
|| !WriteEscaped(attributes[index].GetValue())
|
||||
|| std::fputc('"', xmlFile) == EOF) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool idXMLWriter::PushTag(const char* const name) {
|
||||
void* const storage = std::malloc(sizeof(stackXMLTag_t));
|
||||
if (storage == nullptr) {
|
||||
return false;
|
||||
}
|
||||
stackXMLTag_t* const tag = new (storage) stackXMLTag_t;
|
||||
tag->tagName = name;
|
||||
tag->next = firstOpenTag;
|
||||
firstOpenTag = tag;
|
||||
if (lastOpenTag == nullptr) {
|
||||
lastOpenTag = tag;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool idXMLWriter::OpenElement(const char* const name) {
|
||||
idRecoveredList<idXMLAttribute> noAttributes;
|
||||
return OpenElement(name, noAttributes);
|
||||
}
|
||||
|
||||
bool idXMLWriter::OpenElement(const char* const name,
|
||||
const idRecoveredList<idXMLAttribute>& attributes) {
|
||||
if (!TestRoot(name) || !WriteIndent() || std::fputc('<', xmlFile) == EOF
|
||||
|| !WriteEscaped(name) || !WriteAttributes(attributes)
|
||||
|| std::fputs(">\n", xmlFile) < 0 || !PushTag(name)) {
|
||||
return false;
|
||||
}
|
||||
++tabLevel;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool idXMLWriter::WriteElement(const char* const name, const char* const value) {
|
||||
idRecoveredList<idXMLAttribute> noAttributes;
|
||||
return WriteElement(name, value, noAttributes);
|
||||
}
|
||||
|
||||
bool idXMLWriter::WriteElement(const char* const name, const char* const value,
|
||||
const idRecoveredList<idXMLAttribute>& attributes) {
|
||||
if (!TestRoot(name) || !WriteIndent() || std::fputc('<', xmlFile) == EOF
|
||||
|| !WriteEscaped(name) || !WriteAttributes(attributes)) {
|
||||
return false;
|
||||
}
|
||||
if (value == nullptr || value[0] == '\0') {
|
||||
return std::fputs("/>\n", xmlFile) >= 0;
|
||||
}
|
||||
return std::fputc('>', xmlFile) != EOF && WriteEscaped(value)
|
||||
&& std::fputs("</", xmlFile) >= 0 && WriteEscaped(name)
|
||||
&& std::fputs(">\n", xmlFile) >= 0;
|
||||
}
|
||||
|
||||
bool idXMLWriter::CloseElement() {
|
||||
if (xmlFile == nullptr || firstOpenTag == nullptr) {
|
||||
return false;
|
||||
}
|
||||
stackXMLTag_t* const tag = firstOpenTag;
|
||||
firstOpenTag = tag->next;
|
||||
if (firstOpenTag == nullptr) {
|
||||
lastOpenTag = nullptr;
|
||||
}
|
||||
--tabLevel;
|
||||
const bool success = WriteIndent() && std::fputs("</", xmlFile) >= 0
|
||||
&& WriteEscaped(tag->tagName.c_str()) && std::fputs(">\n", xmlFile) >= 0;
|
||||
tag->~stackXMLTag_t();
|
||||
std::free(tag);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool idXMLWriter::CloseDocument() {
|
||||
while (firstOpenTag != nullptr) {
|
||||
if (!CloseElement()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return CloseFile();
|
||||
}
|
||||
|
||||
bool idXMLWriter::WriteElement_R(const idXMLElement* const element) {
|
||||
if (element == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (element->NumChildren() == 0) {
|
||||
idRecoveredList<idXMLAttribute> attributes;
|
||||
for (int index = 0; index < element->NumAttributes(); ++index) {
|
||||
attributes.Append(element->GetAttribute(index));
|
||||
}
|
||||
return WriteElement(element->GetName(), element->GetValue(), attributes);
|
||||
}
|
||||
idRecoveredList<idXMLAttribute> attributes;
|
||||
for (int index = 0; index < element->NumAttributes(); ++index) {
|
||||
attributes.Append(element->GetAttribute(index));
|
||||
}
|
||||
if (!OpenElement(element->GetName(), attributes)) {
|
||||
return false;
|
||||
}
|
||||
if (element->GetValue()[0] != '\0') {
|
||||
if (!WriteIndent() || !WriteEscaped(element->GetValue())
|
||||
|| std::fputc('\n', xmlFile) == EOF) return false;
|
||||
}
|
||||
for (int index = 0; index < element->NumChildren(); ++index) {
|
||||
if (!WriteElement_R(element->GetChild(index))) return false;
|
||||
}
|
||||
return CloseElement();
|
||||
}
|
||||
|
||||
bool idXMLWriter::WriteDocument(const idXMLElement* const root) {
|
||||
return xmlFile != nullptr && !hasRoot && WriteElement_R(root);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include "xmlelement.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
class idXMLWriter {
|
||||
public:
|
||||
idXMLWriter();
|
||||
~idXMLWriter();
|
||||
|
||||
bool OpenFile(const char* fileName, bool append = false,
|
||||
bool writeDeclaration = true);
|
||||
bool CloseFile();
|
||||
bool OpenElement(const char* name);
|
||||
bool OpenElement(const char* name,
|
||||
const idRecoveredList<idXMLAttribute>& attributes);
|
||||
bool WriteElement(const char* name, const char* value);
|
||||
bool WriteElement(const char* name, const char* value,
|
||||
const idRecoveredList<idXMLAttribute>& attributes);
|
||||
bool CloseElement();
|
||||
bool CloseDocument();
|
||||
bool WriteDocument(const idXMLElement* root);
|
||||
|
||||
private:
|
||||
struct stackXMLTag_t {
|
||||
idStr tagName;
|
||||
stackXMLTag_t* next;
|
||||
};
|
||||
|
||||
std::FILE* xmlFile;
|
||||
int tabLevel;
|
||||
bool hasRoot;
|
||||
stackXMLTag_t* firstOpenTag;
|
||||
stackXMLTag_t* lastOpenTag;
|
||||
|
||||
bool TestRoot(const char* name);
|
||||
bool WriteIndent();
|
||||
bool WriteEscaped(const char* text);
|
||||
bool WriteAttributes(const idRecoveredList<idXMLAttribute>& attributes);
|
||||
bool PushTag(const char* name);
|
||||
bool WriteElement_R(const idXMLElement* element);
|
||||
};
|
||||
|
||||
#if INTPTR_MAX == INT32_MAX
|
||||
static_assert(sizeof(idXMLWriter) == 20, "Recovered idXMLWriter ABI changed");
|
||||
#endif
|
||||
Reference in New Issue
Block a user