Files
MagicSetEditor2/src/util/io/writer.cpp
T
2025-08-07 18:45:12 +02:00

143 lines
4.4 KiB
C++

//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) Twan van Laarhoven and the other MSE developers |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include "writer.hpp"
#include <util/vector2d.hpp>
#include <util/error.hpp>
#include <util/version.hpp>
#include <util/io/package.hpp>
#include <boost/logic/tribool.hpp>
using boost::tribool;
// ----------------------------------------------------------------------------- : Writer
Writer::Writer(OutputStream& output)
: indentation(0)
, output(output)
, stream(output, wxEOL_UNIX, wxMBConvUTF8())
{}
Writer::Writer(OutputStream& output, Version file_app_version)
: indentation(0)
, output(output)
, stream(output, wxEOL_UNIX, wxMBConvUTF8())
{
stream.WriteString(BYTE_ORDER_MARK);
handle(_("mse_version"), file_app_version);
}
void Writer::enterBlock(const Char* name) {
// don't write the key yet
pending_opened.push_back(name);
}
void Writer::exitBlock() {
if (pending_opened.empty()) {
assert(indentation > 0);
indentation -= 1;
} else {
// this block was apparently empty, ignore it
pending_opened.pop_back();
}
}
void Writer::writePending() {
// In enterBlock we have delayed the actual writing of the keys until this point
// here we write all the pending keys, and increase indentation along the way.
for (size_t i = 0 ; i < pending_opened.size() ; ++i) {
if (i > 0) {
// before entering a sub-block, write a colon after the parent's name
stream.WriteString(_(":\n"));
}
indentation += 1;
writeIndentation();
writeUTF8(stream, pending_opened[i]);
}
pending_opened.clear();
}
void Writer::writeIndentation() {
for(int i = 1 ; i < indentation ; ++i) {
stream.PutChar(_('\t'));
}
}
// ----------------------------------------------------------------------------- : Handling basic types
void Writer::handle(const String& value) {
if (pending_opened.empty()) {
throw InternalError(_("Can only write a value in a key that was just opened"));
}
writePending();
// write indentation and key
if (value.find_first_of(_('\n')) != String::npos || (!value.empty() && isSpace(value.GetChar(0)))) {
// multiline string, or contains leading whitespace
stream.WriteString(_(":\n"));
indentation += 1;
// split lines, and write each line
size_t start = 0, end, size = value.size();
while (start < size) {
end = value.find_first_of(_("\n\r"), start); // until end of line
// write the line
writeIndentation();
writeUTF8(stream, value.substr(start, end - start));
// Skip \r and \n
if (end == String::npos) break;
stream.PutChar(_('\n'));
start = end + 1;
if (start < size) {
Char c1 = value.GetChar(start - 1);
Char c2 = value.GetChar(start);
// skip second character of \r\n or \n\r
if (c1 != c2 && (c2 == _('\r') || c2 == _('\n'))) start += 1;
}
}
indentation -= 1;
} else {
stream.WriteString(_(": "));
writeUTF8(stream, value);
}
stream.PutChar(_('\n'));
}
template <> void Writer::handle(const int& value) {
handle(String() << value);
}
template <> void Writer::handle(const unsigned int& value) {
handle(String() << value);
}
template <> void Writer::handle(const double& value) {
handle(String() << value);
}
template <> void Writer::handle(const bool& value) {
handle(value ? _("true") : _("false"));
}
template <> void Writer::handle(const tribool& value) {
if (!indeterminate(value)) {
handle(value ? _("true") : _("false"));
}
}
// ----------------------------------------------------------------------------- : Handling less basic util types
template <> void Writer::handle(const wxDateTime& date) {
if (date.IsValid()) {
handle(date.Format(_("%Y-%m-%d %H:%M:%S")));
}
}
template <> void Writer::handle(const Vector2D& vec) {
handle(String::Format(_("(%.10lf,%.10lf)"), vec.x, vec.y));
}
template <> void Writer::handle(const LocalFileName& value) {
handle(value.toStringForWriting());
}