From f4fe9ab6b012b5ba42b156b4599fd922bcaecff9 Mon Sep 17 00:00:00 2001 From: GenevensiS <66968533+G-e-n-e-v-e-n-s-i-S@users.noreply.github.com> Date: Mon, 11 May 2026 13:51:43 +0200 Subject: [PATCH] Strengthen paste logic --- src/data/format/image_encoding.hpp | 102 +++--- src/gfx/generated_image.cpp | 9 +- src/gui/control/card_list.cpp | 143 +++++--- src/gui/set/cards_panel.cpp | 2 +- src/gui/web_request_window.cpp | 43 ++- src/gui/web_request_window.hpp | 4 +- src/script/functions/json.cpp | 557 +++++++++++++++-------------- src/util/io/reader.cpp | 6 +- src/util/io/reader.hpp | 6 +- 9 files changed, 497 insertions(+), 375 deletions(-) diff --git a/src/data/format/image_encoding.hpp b/src/data/format/image_encoding.hpp index 14af23bb..09c0b2cf 100644 --- a/src/data/format/image_encoding.hpp +++ b/src/data/format/image_encoding.hpp @@ -13,6 +13,7 @@ #include #include #include +#include // ----------------------------------------------------------------------------- : Crop Rect Encoding @@ -111,67 +112,70 @@ inline static String transformAllEncodedRects(const String& rectString, RectTran // ----------------------------------------------------------------------------- : File to UTF8 Encoding +inline static const char Base64Alphabet[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +inline static const std::vector Base64ReverseAlphabet = [] { + std::vector table(256, -1); + for (int i = 0; i < 64; i++) table[(uint8_t)Base64Alphabet[i]] = i; + return table; +}(); + /// Encode a file in a string inline static std::string fileToUTF8(const std::string& filepath) { - // File to char + // Load file std::ifstream file(filepath, std::ios::binary); - file.unsetf(std::ios::skipws); - std::vector buffer = std::vector(std::istreambuf_iterator(file), std::istreambuf_iterator()); - int size = buffer.size(); - if (size < 2) { - queue_message(MESSAGE_WARNING, _("File too small to encode")); + if (!file) { + queue_message(MESSAGE_WARNING, _("Could not find file: ") + String(filepath)); return ""; } - // All bytes that have a highest bit of 0 are valid UTF8 characters, so: - // Reset the highest bit of each byte, store these bits in additional bytes at the end - const unsigned char highest_bit = 1 << 7; - unsigned char added_byte = 0; - for (int i = 0, b = 0 ; i < size ; ++i, ++b) { - if (b == 7) { // Never set the highest bit of the added byte - buffer.push_back(added_byte); - b = 0; - } - unsigned char bit = 1 << b; - if ((buffer[i] & highest_bit) != 0) { // The highest bit of the buffer is set - buffer[i] &= ~highest_bit; // Reset the highest bit of the buffer - added_byte |= bit; // Set the bit of the added byte - } else { - added_byte &= ~bit; // Reset the bit of the added byte + size_t size = std::filesystem::file_size(filepath); + std::vector data(size); + file.read(reinterpret_cast(data.data()), size); + // Base64 encode + std::string out; + out.reserve(((size + 2) / 3) * 4); + int val = 0; + int valb = -6; + for (uint8_t c : data) { + val = (val << 8) | c; + valb += 8; + while (valb >= 0) { + out.push_back(Base64Alphabet[(val >> valb) & 0x3F]); + valb -= 6; } } - buffer.push_back(added_byte); - // Char to string - return std::string(buffer.begin(), buffer.end()); + if (valb > -6) { + out.push_back(Base64Alphabet[((val << 8) >> (valb + 8)) & 0x3F]); + } + // Pad + while (out.size() % 4) { + out.push_back('='); + } + return out; } /// Retreive a file encoded in a string, return true if successful -inline static bool UTF8ToFile(const std::string& filepath, std::string& string) { - // String to char - std::vector buffer(string.begin(), string.end()); - int size = buffer.size(); - if (size < 2) { - queue_message(MESSAGE_WARNING, _("File too small to decode")); - return false; - } - // Restore the highest bit of each byte - size = (size * 7) / 8; - const unsigned char highest_bit = 1 << 7; - unsigned char added_byte = buffer[size]; - for (int i = 0, j = size, b = 0 ; i < size ; ++i, ++b) { - if (b == 7) { - ++j; - added_byte = buffer[j]; - b = 0; - } - unsigned char bit = 1 << b; - if ((added_byte & bit) != 0) { // The bit of the added byte is set - buffer[i] |= highest_bit; // Set the highest bit of the buffer +inline static bool UTF8ToFile(const std::string& filepath, std::string& data) { + // Base64 decode + std::string out; + out.reserve(data.size() * 3 / 4); + int val = 0; + int valb = -8; + for (uint8_t c : data) { + if (c == '=') break; // padding, we're done + val = (val << 6) | Base64ReverseAlphabet[c]; + valb += 6; + if (valb >= 0) { + out.push_back(static_cast((val >> valb) & 0xFF)); + valb -= 8; } } - buffer.resize(size); - // Char to file - std::ofstream file(filepath, std::ios::out|std::ios::binary); - std::copy(buffer.cbegin(), buffer.cend(), std::ostream_iterator(file)); + // Save file + std::ofstream file(filepath, std::ios::binary); + file.write(out.data(), out.size()); return true; } diff --git a/src/gfx/generated_image.cpp b/src/gfx/generated_image.cpp index fcc5b198..2e5d193f 100644 --- a/src/gfx/generated_image.cpp +++ b/src/gfx/generated_image.cpp @@ -850,14 +850,11 @@ DownloadedImage::DownloadedImage(Set* set, const String& url) } // is the data an image? - const String& content_type = wnd.out.GetContentType(); - if (!content_type.StartsWith(_("image"))) throw ScriptError(_ERROR_1_("download not image", loadpath)); - Image img(*wnd.out.GetStream()); - if (!img.IsOk()) throw ScriptError(_ERROR_("web request corrupted")); - + if (!wnd.content_type.StartsWith(_("image/"))) throw ScriptError(_ERROR_1_("download not image", loadpath)); + // add the file to the set (or overwrite it if pre-existing), save set auto outStream = set->openOut(savename); - img.SaveFile(*outStream, wxBITMAP_TYPE_PNG); + wnd.image_out.SaveFile(*outStream, wxBITMAP_TYPE_PNG); if (!outStream->IsOk()) throw ScriptError(_ERROR_1_("can't write image to set", loadpath)); outStream->Close(); set->save(false); diff --git a/src/gui/control/card_list.cpp b/src/gui/control/card_list.cpp index 15fc6f31..82da0726 100644 --- a/src/gui/control/card_list.cpp +++ b/src/gui/control/card_list.cpp @@ -259,7 +259,7 @@ void CardListBase::parseImageMetadata(CardP& card, const Image& image) { int degrees = 0; if (decodeRectFromString(value->filename.toStringForKey(), rect, degrees)) { rect = rect.intersect(RealRect(0.0, 0.0, image.GetWidth(), image.GetHeight())); - if (rect.width > 0.0 && rect.height > 0.0) { + if (rect.width > 0.0 && rect.height > 0.0) { Image img = image.GetSubImage(rect); img = rotate_image(img, deg_to_rad(360 - degrees)); LocalFileName filename = set->newFileName(_("cropped_image"), _(".png")); // a new unique name in the package @@ -275,13 +275,13 @@ void CardListBase::parseImageMetadata(CardP& card, const Image& image) { } } -void CardListBase::parseImageMetadata(CardP& card) { +void CardListBase::parseImageMetadata(CardP& card) { for (IndexMap::iterator it = card->data.begin(); it != card->data.end(); it++) { ImageValue* value = dynamic_cast(it->get()); if (value && !value->filename.empty()) { Image img; - if (decodeImageFromString(value->filename.toStringForKey(), img)) { - if (img.IsOk()) { + if (decodeImageFromString(value->filename.toStringForKey(), img)) { + if (img.IsOk()) { LocalFileName filename = set->newFileName(_("decoded_image"), _(".png")); // a new unique name in the package img.SaveFile(set->nameOut(filename), wxBITMAP_TYPE_PNG); value->filename = filename; @@ -295,28 +295,21 @@ void CardListBase::parseImageMetadata(CardP& card) { } } -bool CardListBase::parseUrl(String& url, vector& out) { +bool CardListBase::parseUrl(String& url, vector& out) { size_t j = out.size(); size_t pos = url.find("URL="); if (pos != std::string::npos) { url = url.substr(pos+4); } - if (!url.StartsWith(_("http"))) return false; - + if (!url.StartsWith(_("http"))) return false; + WebRequestWindow wnd(url); - if (wnd.ShowModal() == wxID_OK) { - const String& content_type = wnd.out.GetContentType(); - if (content_type.StartsWith(_("image"))) { - Image img(*wnd.out.GetStream()); - if (img.IsOk()) { - parseImage(img, out); - } - else { - queue_message(MESSAGE_ERROR, _ERROR_("web request corrupted")); - } + if (wnd.ShowModal() == wxID_OK) { + if (wnd.content_type.StartsWith(_("image/"))) { + parseImage(wnd.image_out, out); } - else if (content_type.StartsWith(_("text"))) { - String text = wnd.out.AsString(); + else if (wnd.content_type.StartsWith(_("text/"))) { + String text = String(wnd.text_out.data(), wnd.text_out.size()); parseText(text, out); } else { @@ -326,10 +319,10 @@ bool CardListBase::parseUrl(String& url, vector& out) { return j < out.size(); } -bool CardListBase::parseFiles(wxArrayString& filenames, vector& out) { +bool CardListBase::parseFiles(wxArrayString& filenames, vector& out) { size_t j = out.size(); for (size_t i = 0; i < filenames.size(); i++) { - if (wxImage::CanRead(filenames[i])) { + if (wxImage::CanRead(filenames[i])) { // if it's an image file, try to get meta_data Image image_file; image_file.SetLoadFlags(image_file.GetLoadFlags() & ~wxImage::Load_Verbose); @@ -340,12 +333,12 @@ bool CardListBase::parseFiles(wxArrayString& filenames, vector& out) { } else { // if it's an url, request the data std::ifstream ifs(filenames[i].ToStdString()); - if (ifs.bad() || ifs.fail() || !ifs.good() || !ifs.is_open()) continue; + if (ifs.bad() || ifs.fail() || !ifs.good() || !ifs.is_open()) continue; std::string content((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); bool looks_like_text = std::all_of(content.begin(), content.end(), [](char c) { return isprint(c) || isspace(c); }); - if (!looks_like_text) continue; + if (!looks_like_text) continue; wxString text(content); if (!parseUrl(text, out)) parseText(text, out); } @@ -353,7 +346,7 @@ bool CardListBase::parseFiles(wxArrayString& filenames, vector& out) { return j < out.size(); } -bool CardListBase::parseImage(Image& image, vector& out) { +bool CardListBase::parseImage(Image& image, vector& out) { size_t j = out.size(); if (image.HasOption(wxIMAGE_OPTION_PNG_DESCRIPTION)) { auto text = image.GetOption(wxIMAGE_OPTION_PNG_DESCRIPTION); @@ -368,7 +361,10 @@ bool CardListBase::parseImage(Image& image, vector& out) { return j < out.size(); } -bool CardListBase::parseText(String& text, vector& out) { +bool CardListBase::parseText(String& text, vector& out) { + if (text.size() == 0) { + return false; + } size_t j = out.size(); size_t pos = text.find(""); if (pos != wxString::npos) { @@ -376,7 +372,7 @@ bool CardListBase::parseText(String& text, vector& out) { } try { ScriptValueP sv = json_to_mse(text, set.get()); - if (sv->type() == SCRIPT_COLLECTION) { + if (sv->type() == SCRIPT_COLLECTION) { if (ScriptCustomCollection* custom = dynamic_cast(sv.get())) { for (size_t i = 0; i < custom->value.size(); i++) { if (ScriptObject* c = dynamic_cast*>(custom->value[i].get())) { @@ -384,11 +380,11 @@ bool CardListBase::parseText(String& text, vector& out) { } } } - } else if (ScriptObject* c = dynamic_cast*>(sv.get())) { + } else if (ScriptObject* c = dynamic_cast*>(sv.get())) { out.push_back(make_intrusive(*c->getValue())); } - } catch (...) {} - + } catch (...) {} + // decode images to populate image fields for (int k = j; k < out.size(); k++) { CardP& card = out[k]; @@ -398,27 +394,33 @@ bool CardListBase::parseText(String& text, vector& out) { return j < out.size(); } -bool CardListBase::parseData(bool ignore_cards_from_own_card_list) { +bool CardListBase::parseData(bool ignore_cards_from_own_card_list) { wxBusyCursor wait; wxDataObjectComposite* composite = drop_target->data_object; wxDataFormat format = composite->GetReceivedFormat(); vector new_cards; - if (format == CardsDataObject::format) { + if (format == CardsDataObject::format) { String id = ignore_cards_from_own_card_list ? drop_target->ignored_id : _(""); size_t size = composite->GetDataSize(format); - if (size > 0) { - std::vector buffer(size); - if (composite->GetDataHere(format, buffer.data())) { - CardsDataObject card_data; - card_data.SetData(size, buffer.data()); - card_data.getCards(set, id, new_cards); - } + if (size < 1) { + queue_message(MESSAGE_ERROR, _("DEBUG: CardsDataObject corrupted")); + return false; + } + if (size > 10000000) { // 10Mb + queue_message(MESSAGE_ERROR, _("Too much card data, paste less cards!")); + return false; + } + std::vector buffer(size); + if (composite->GetDataHere(format, buffer.data())) { + CardsDataObject card_data; + card_data.SetData(size, buffer.data()); + card_data.getCards(set, id, new_cards); } } else { wxDataObject *data = composite->GetObject(format); - + switch (format.GetType()) { case wxDF_FILENAME: @@ -432,17 +434,59 @@ bool CardListBase::parseData(bool ignore_cards_from_own_card_list) { case wxDF_PNG: { wxImageDataObject* image_data = static_cast(data); - Image image = image_data->GetImage(); - parseImage(image, new_cards); + size_t size = image_data->GetDataSize(); + if (size < 1) { + queue_message(MESSAGE_ERROR, _("DEBUG: ImageDataObject corrupted")); + return false; + } + if (size > 50000000) { // 50Mb + queue_message(MESSAGE_ERROR, _("Image data too large or corrupted")); + return false; + } + try { + Image image = image_data->GetImage(); + if (!image.IsOk() || image.GetWidth() > 20000 || image.GetHeight() > 20000) { + queue_message(MESSAGE_ERROR, _("Image too large or corrupted")); + return false; + } + parseImage(image, new_cards); + } catch (const std::bad_alloc&) { + //queue_message(MESSAGE_ERROR, _("Image couldn't be allocated")); + return false; + } catch (...) { + queue_message(MESSAGE_ERROR, _("Image couldn't be processed")); + return false; + } } break; case wxDF_BITMAP: { wxBitmapDataObject* bitmap_data = static_cast(data); - wxBitmap bitmap = bitmap_data->GetBitmap(); - Image image = bitmap.ConvertToImage(); - parseImage(image, new_cards); + size_t size = bitmap_data->GetDataSize(); + if (size < 1) { + queue_message(MESSAGE_ERROR, _("DEBUG: BitmapDataObject corrupted")); + return false; + } + if (size > 50000000) { // 50Mb + queue_message(MESSAGE_ERROR, _("Bitmap data too large or corrupted")); + return false; + } + try { + wxBitmap bitmap = bitmap_data->GetBitmap(); + if (!bitmap.IsOk() || bitmap.GetWidth() > 20000 || bitmap.GetHeight() > 20000) { + queue_message(MESSAGE_ERROR, _("Bitmap too large or corrupted")); + return false; + } + Image image = bitmap.ConvertToImage(); + parseImage(image, new_cards); + } catch (const std::bad_alloc&) { + //queue_message(MESSAGE_ERROR, _("Bitmap or Image couldn't be allocated")); + return false; + } catch (...) { + queue_message(MESSAGE_ERROR, _("Bitmap or Image couldn't be processed")); + return false; + } } break; @@ -451,6 +495,15 @@ bool CardListBase::parseData(bool ignore_cards_from_own_card_list) { case wxDF_HTML: { wxTextDataObject* text_data = static_cast(data); + size_t size = text_data->GetDataSize(); + if (size < 1) { + queue_message(MESSAGE_ERROR, _("DEBUG: TextDataObject corrupted")); + return false; + } + if (size > 30000000) { // 30Mb + queue_message(MESSAGE_ERROR, _("Text too large or corrupted")); + return false; + } String text = text_data->GetText(); if (!parseUrl(text, new_cards)) parseText(text, new_cards); } @@ -774,7 +827,7 @@ CardListDropTarget::CardListDropTarget(CardListBase* card_list) CardListDropTarget::~CardListDropTarget() {} -wxDragResult CardListDropTarget::OnData(wxCoord x, wxCoord y, wxDragResult defaultDragResult) { +wxDragResult CardListDropTarget::OnData(wxCoord x, wxCoord y, wxDragResult defaultDragResult) { if (!GetData()) return wxDragNone; if (!card_list->parseData(true)) return wxDragError; return wxDragCopy; diff --git a/src/gui/set/cards_panel.cpp b/src/gui/set/cards_panel.cpp index 8dc3a756..31270599 100644 --- a/src/gui/set/cards_panel.cpp +++ b/src/gui/set/cards_panel.cpp @@ -579,7 +579,7 @@ bool CardsPanel::canPaste() const { else return false; } void CardsPanel::doPaste() { - if (card_list->doPaste()) return; + if (card_list->doPaste()) return; int id = focused_control(this); if (id == ID_EDITOR) editor->doPaste(); diff --git a/src/gui/web_request_window.cpp b/src/gui/web_request_window.cpp index ad40421e..ec0d1d6e 100644 --- a/src/gui/web_request_window.cpp +++ b/src/gui/web_request_window.cpp @@ -9,6 +9,7 @@ #include #include #include +#include // ----------------------------------------------------------------------------- : WebRequestWindow @@ -76,16 +77,50 @@ void WebRequestWindow::onUpdate(wxWebRequestEvent& evt) { } void WebRequestWindow::onComplete(wxWebRequestEvent& evt) { - out = evt.GetResponse(); - if (out.IsOk()) { - EndModal(wxID_OK); + wxWebResponse response = evt.GetResponse(); + if (!response.IsOk()) { + onFail(_ERROR_("web request corrupted")); + return; + } + wxInputStream* stream = response.GetStream(); + if (!stream || !stream->IsOk()) { + onFail(_ERROR_("web request corrupted")); + return; + } + + content_type = response.GetContentType(); + if (content_type.StartsWith("image/")) { + image_out = Image(*stream); + if (!image_out.IsOk()) { + onFail(_ERROR_("web request corrupted")); + return; + } + } + else if (content_type.StartsWith("text/") || content_type.Contains("json")) { + wxMemoryOutputStream mem; + char buffer[8192]; + while (true) { + stream->Read(buffer, sizeof(buffer)); + size_t read = stream->LastRead(); + if (read > 0) mem.Write(buffer, read); + if (stream->Eof()) break; + if (stream->GetLastError() != wxSTREAM_NO_ERROR) { + onFail(_ERROR_("web request corrupted")); + return; + } + } + text_out.resize(mem.GetSize()); + mem.CopyTo(text_out.data(), mem.GetSize()); } else { - onFail(_ERROR_("web request corrupted")); + onFail(_ERROR_("web request unsupported format")); + return; } + EndModal(wxID_OK); } void WebRequestWindow::onFail(const String& message) { + content_type.Clear(); info->SetLabel(_ERROR_("web request failed")); address->SetLabel(message); } diff --git a/src/gui/web_request_window.hpp b/src/gui/web_request_window.hpp index 68cf2534..d7733bdd 100644 --- a/src/gui/web_request_window.hpp +++ b/src/gui/web_request_window.hpp @@ -19,7 +19,9 @@ class WebRequestWindow : public wxDialog { public: WebRequestWindow(const String& url, bool sizer=true); - wxWebResponse out; + String content_type; + std::string text_out; + Image image_out; protected: DECLARE_EVENT_TABLE(); diff --git a/src/script/functions/json.cpp b/src/script/functions/json.cpp index 47ae0030..45d4cd20 100644 --- a/src/script/functions/json.cpp +++ b/src/script/functions/json.cpp @@ -112,13 +112,25 @@ void pretty_print(std::ostream& os, const boost::json::value& jv, std::string* i String json_pretty_print(const boost::json::value& jv, std::string* indent) { std::ostringstream stream; pretty_print(stream, jv, indent); - String string = wxString(stream.str().c_str()); - return string; + std::string stdstring = stream.str(); + const char* data = stdstring.data(); + size_t size = stdstring.size(); + String wxstring = String::FromUTF8(data, size); + if (wxstring.empty() && size > 0) { + wxstring = String(data, wxConvWhateverWorks, size); + } + return wxstring; } String json_ugly_print(const boost::json::value& jv) { - String string = wxString(boost::json::serialize(jv).c_str()); - return string; + std::string stdstring = boost::json::serialize(jv); + const char* data = stdstring.data(); + size_t size = stdstring.size(); + String wxstring = String::FromUTF8(data, size); + if (wxstring.empty() && size > 0) { + wxstring = String(data, wxConvWhateverWorks, size); + } + return wxstring; } // ----------------------------------------------------------------------------- : JSON to MSE @@ -128,23 +140,33 @@ ScriptValueP json_to_mse(const boost::json::value& jv, Set* set); template void read(T& out, boost::json::object& jv, const char value_name[]) { if (!jv.contains(value_name)) return; - else { - wxStringInputStream stream = {_("")}; - Reader reader(stream, nullptr, _("")); - reader.setValue(wxString(jv[value_name].as_string().c_str())); - reader.handle(out); + boost::json::string jstring = jv[value_name].as_string(); + const char* data = jstring.data(); + size_t size = jstring.size(); + String wxstring = String::FromUTF8(data, size); + if (wxstring.empty() && size > 0) { + wxstring = String(data, wxConvWhateverWorks, size); } + wxStringInputStream stream(wxstring); + Reader reader(stream, nullptr, _(""), false, true); + reader.setValue(wxstring); + reader.handle(out); } // templates don't work with enums? are you kidding me with this language? void read(PackSelectType& out, boost::json::object& jv, const char value_name[]) { if (!jv.contains(value_name)) return; - else { - wxStringInputStream stream = {_("")}; - Reader reader(stream, nullptr, _("")); - reader.setValue(wxString(jv[value_name].as_string().c_str())); - reader.handle(out); + boost::json::string jstring = jv[value_name].as_string(); + const char* data = jstring.data(); + size_t size = jstring.size(); + String wxstring = String::FromUTF8(data, size); + if (wxstring.empty() && size > 0) { + wxstring = String(data, wxConvWhateverWorks, size); } + wxStringInputStream stream(wxstring); + Reader reader(stream, nullptr, _(""), false, true); + reader.setValue(wxstring); + reader.handle(out); } PackItemP json_to_mse_pack_item(boost::json::object& jv) { @@ -163,7 +185,7 @@ PackTypeP json_to_mse_pack_type(boost::json::object& jv) { read(pack_type->summary, jv, "summary"); read(pack_type->select, jv, "select"); if (jv.contains("items") && jv["items"].is_array()) { - boost::json::array pack_itemsv = jv["items"].as_array(); + boost::json::array pack_itemsv = jv["items"].get_array(); for (size_t i = 0; i < pack_itemsv.size(); i++) { boost::json::object pack_itemv = pack_itemsv[i].as_object(); pack_type->items.emplace_back(json_to_mse_pack_item(pack_itemv)); @@ -184,38 +206,43 @@ KeywordP json_to_mse_keyword(boost::json::object& jv) { CardP json_to_mse_card(boost::json::object& jv, Set* set) { CardP card = make_intrusive(*set->game); - read(card->time_created, jv, "time_created"); - read(card->time_modified, jv, "time_modified"); - read(card->notes, jv, "notes"); - read(card->uid, jv, "uid"); - read(card->linked_card_1, jv, "linked_card_1"); - read(card->linked_card_2, jv, "linked_card_2"); - read(card->linked_card_3, jv, "linked_card_3"); - read(card->linked_card_4, jv, "linked_card_4"); - read(card->linked_relation_1, jv, "linked_relation_1"); - read(card->linked_relation_2, jv, "linked_relation_2"); - read(card->linked_relation_3, jv, "linked_relation_3"); - read(card->linked_relation_4, jv, "linked_relation_4"); + read(card->time_created, jv, "time_created"); + read(card->time_modified, jv, "time_modified"); + read(card->notes, jv, "notes"); + read(card->uid, jv, "uid"); + read(card->linked_card_1, jv, "linked_card_1"); + read(card->linked_card_2, jv, "linked_card_2"); + read(card->linked_card_3, jv, "linked_card_3"); + read(card->linked_card_4, jv, "linked_card_4"); + read(card->linked_relation_1, jv, "linked_relation_1"); + read(card->linked_relation_2, jv, "linked_relation_2"); + read(card->linked_relation_3, jv, "linked_relation_3"); + read(card->linked_relation_4, jv, "linked_relation_4"); // card fields if (jv.contains("data") && jv["data"].is_object()) { - boost::json::object datav = jv["data"].as_object(); + boost::json::object datav = jv["data"].get_object(); for (auto it = datav.begin(); it != datav.end(); ++it) { - String key_name = wxString(it->key_c_str()); + boost::json::string_view key_view = it->key(); + String key_name = String::FromUTF8(key_view.data(), key_view.size()); Value* container = get_card_field_container(*set->game, card->data, key_name, false); ScriptValueP value = json_to_mse(it->value(), set); set_container(container, value, key_name); } } // stylesheet - if (jv.contains("stylesheet")) card->stylesheet = StyleSheet::byGameAndName(*set->game, wxString(jv["stylesheet"].as_string().c_str())); + if (jv.contains("stylesheet")) { + boost::json::string stylesheet_name = jv["stylesheet"].as_string(); + card->stylesheet = StyleSheet::byGameAndName(*set->game, String::FromUTF8(stylesheet_name.data(), stylesheet_name.size())); + } if (card->stylesheet) { // styling fields card->styling_data.init(card->stylesheet->styling_fields); if (jv.contains("styling_data") && jv["styling_data"].is_object()) { - boost::json::object datav = jv["styling_data"].as_object(); + boost::json::object datav = jv["styling_data"].get_object(); for (auto it = datav.begin(); it != datav.end(); ++it) { - String key_name = wxString(it->key_c_str()); - Value* container = get_container(card->styling_data, wxString("styling"), key_name, false); + boost::json::string_view key_view = it->key(); + String key_name = String::FromUTF8(key_view.data(), key_view.size()); + Value* container = get_container(card->styling_data, String("styling"), key_name, false); ScriptValueP value = json_to_mse(it->value(), set); set_container(container, value, key_name); card->has_styling = true; @@ -223,15 +250,17 @@ CardP json_to_mse_card(boost::json::object& jv, Set* set) { } // extra card fields if (jv.contains("extra_data") && jv["extra_data"].is_object()) { - boost::json::object datav = jv["extra_data"].as_object(); + boost::json::object datav = jv["extra_data"].get_object(); for (auto it = datav.begin(); it != datav.end(); ++it) { - StyleSheetP stylesheet = StyleSheet::byGameAndName(*set->game, it->key_c_str()); + boost::json::string_view stylesheet_view = it->key(); + StyleSheetP stylesheet = StyleSheet::byGameAndName(*set->game, String::FromUTF8(stylesheet_view.data(), stylesheet_view.size())); if (!stylesheet) continue; IndexMap& stylesheet_data = card->extraDataFor(*stylesheet); boost::json::object stylesheet_datav = it->value().as_object(); for (auto stylesheet_it = stylesheet_datav.begin(); stylesheet_it != stylesheet_datav.end(); ++stylesheet_it) { - String key_name = wxString(stylesheet_it->key_c_str()); - Value* container = get_container(stylesheet_data, wxString("extra card"), key_name, false); + boost::json::string_view key_view = stylesheet_it->key(); + String key_name = String::FromUTF8(key_view.data(), key_view.size()); + Value* container = get_container(stylesheet_data, String("extra card"), key_name, false); ScriptValueP value = json_to_mse(stylesheet_it->value(), set); set_container(container, value, key_name); } @@ -248,30 +277,35 @@ SetP json_to_mse_set(boost::json::object& jv) { if (!jv.contains("stylesheet")) { throw ScriptError(_ERROR_("json set without stylesheet")); } - GameP game = Game::byName(wxString(jv["game"].as_string().c_str())); - StyleSheetP stylesheet = StyleSheet::byGameAndName(*game, wxString(jv["stylesheet"].as_string().c_str())); + boost::json::string game_name = jv["game"].as_string(); + GameP game = Game::byName(String::FromUTF8(game_name.data(), game_name.size())); + boost::json::string stylesheet_name = jv["stylesheet"].as_string(); + StyleSheetP stylesheet = StyleSheet::byGameAndName(*game, String::FromUTF8(stylesheet_name.data(), stylesheet_name.size())); SetP set = make_intrusive(stylesheet); // set fields if (jv.contains("set_info") && jv["set_info"].is_object()) { - boost::json::object datav = jv["set_info"].as_object(); + boost::json::object datav = jv["set_info"].get_object(); for (auto it = datav.begin(); it != datav.end(); ++it) { - String key_name = wxString(it->key_c_str()); - Value* container = get_container(set->data, wxString("set"), key_name, false); + boost::json::string_view key_view = it->key(); + String key_name = String::FromUTF8(key_view.data(), key_view.size()); + Value* container = get_container(set->data, String("set"), key_name, false); ScriptValueP value = json_to_mse(it->value(), set.get()); set_container(container, value, key_name); } } // styling if (jv.contains("styling") && jv["styling"].is_object()) { - boost::json::object datav = jv["styling"].as_object(); + boost::json::object datav = jv["styling"].get_object(); for (auto it = datav.begin(); it != datav.end(); ++it) { - StyleSheetP stylesheet = StyleSheet::byGameAndName(*set->game, it->key_c_str()); + boost::json::string_view stylesheet_view = it->key(); + StyleSheetP stylesheet = StyleSheet::byGameAndName(*set->game, String::FromUTF8(stylesheet_view.data(), stylesheet_view.size())); if (!stylesheet) continue; IndexMap& stylesheet_data = set->stylingDataFor(*stylesheet); boost::json::object stylesheet_datav = it->value().as_object(); for (auto stylesheet_it = stylesheet_datav.begin(); stylesheet_it != stylesheet_datav.end(); ++stylesheet_it) { - String key_name = wxString(stylesheet_it->key_c_str()); - Value* container = get_container(stylesheet_data, wxString("styling"), key_name, false); + boost::json::string_view key_view = stylesheet_it->key(); + String key_name = String::FromUTF8(key_view.data(), key_view.size()); + Value* container = get_container(stylesheet_data, String("styling"), key_name, false); ScriptValueP value = json_to_mse(stylesheet_it->value(), set.get()); set_container(container, value, key_name); } @@ -279,7 +313,7 @@ SetP json_to_mse_set(boost::json::object& jv) { } // cards if (jv.contains("cards") && jv["cards"].is_array()) { - boost::json::array cardsv = jv["cards"].as_array(); + boost::json::array cardsv = jv["cards"].get_array(); for (size_t i = 0; i < cardsv.size(); i++) { boost::json::object cardv = cardsv[i].as_object(); set->cards.emplace_back(json_to_mse_card(cardv, set.get())); @@ -287,7 +321,7 @@ SetP json_to_mse_set(boost::json::object& jv) { } // keywords if (jv.contains("keywords") && jv["keywords"].is_array()) { - boost::json::array keywordsv = jv["keywords"].as_array(); + boost::json::array keywordsv = jv["keywords"].get_array(); for (size_t i = 0; i < keywordsv.size(); i++) { boost::json::object keywordv = keywordsv[i].as_object(); set->keywords.emplace_back(json_to_mse_keyword(keywordv)); @@ -295,7 +329,7 @@ SetP json_to_mse_set(boost::json::object& jv) { } // pack types if (jv.contains("pack_types") && jv["pack_types"].is_array()) { - boost::json::array pack_typesv = jv["pack_types"].as_array(); + boost::json::array pack_typesv = jv["pack_types"].get_array(); for (size_t i = 0; i < pack_typesv.size(); i++) { boost::json::object pack_typev = pack_typesv[i].as_object(); set->pack_types.emplace_back(json_to_mse_pack_type(pack_typev)); @@ -318,17 +352,13 @@ ScriptValueP json_to_mse(const boost::json::value& jv, Set* set) { return to_script(integer); } else if (jv.is_string()) { - if (jv.as_string().empty()) return to_script(String()); - std::string string = boost::json::value_to(jv); - const char* cstring = string.c_str(); - size_t nulpos = strlen(cstring); - // if the string contains nul bytes, we have to use the std::string constructor, even though we can't specify the encoding - if (nulpos < string.size()) return to_script(String(string)); - // if the string doesn't contain nul bytes, we can use the constructor that allows to specify the encoding - String wxstring(cstring, wxConvUTF8); + boost::json::string jstring = jv.get_string(); + if (jstring.empty()) return to_script(String()); + const char* data = jstring.data(); + size_t size = jstring.size(); + String wxstring = String::FromUTF8(data, size); if (!wxstring.empty()) return to_script(wxstring); - // if all else fails, use "Whatever Works" - return to_script(String(cstring, wxConvWhateverWorks)); + return to_script(String(data, wxConvWhateverWorks, size)); } else if (jv.is_array()) { boost::json::array array = jv.get_array(); @@ -349,18 +379,16 @@ ScriptValueP json_to_mse(const boost::json::value& jv, Set* set) { if (mse_object_type == "keyword") return make_intrusive> (json_to_mse_keyword(object)); if (mse_object_type == "pack_type") return make_intrusive>(json_to_mse_pack_type(object)); if (mse_object_type == "pack_item") return make_intrusive>(json_to_mse_pack_item(object)); - queue_message(MESSAGE_ERROR, _ERROR_("json unknown type") + _("(") + wxString(mse_object_type.c_str()) + _(")")); + queue_message(MESSAGE_ERROR, _ERROR_("json unknown type") + _("(") + String(mse_object_type.c_str()) + _(")")); return script_nil; } ScriptCustomCollectionP result = make_intrusive(); for (auto it = object.begin(); it != object.end(); ++it) { boost::json::string_view jview = it->key(); - std::string_view stdview = std::string_view(jview.data(), jview.size()); - std::string stdstring = { stdview.begin(), stdview.end() }; - String key(stdstring.c_str(), wxConvUTF8); + String key_name = String::FromUTF8(jview.data(), jview.size()); boost::json::value jvalue = it->value(); ScriptValueP value = json_to_mse(jvalue, set); - result->key_value[key] = value; + result->key_value[key_name] = value; } return result; } @@ -374,8 +402,9 @@ ScriptValueP json_to_mse(const String& string, Set* set) { boost::system::error_code ec; boost::json::parse_options options; options.allow_invalid_utf8 = true; - boost::json::value jv = boost::json::parse(string.ToStdString(), ec, {}, options); - //if(ec) queue_message(MESSAGE_ERROR, _ERROR_("json cant parse") + _("\n\n") + ec.message()); + wxScopedCharBuffer buffer = string.ToUTF8(); + boost::json::value jv = boost::json::parse(boost::json::string_view(buffer.data(), buffer.length()), ec, {}, options); + if(ec && buffer.length() > 0) queue_message(MESSAGE_ERROR, _ERROR_("json cant parse") + _("\n\n") + ec.message()); if(ec) return script_nil; return json_to_mse(jv, set); } @@ -512,92 +541,92 @@ boost::json::object mse_to_json(const StyleP& style) { boost::json::object stylev; stylev.emplace("mse_object_type", "style"); - stylev.emplace("z_index", wxString::Format(wxT("%i"), style->z_index)); - stylev.emplace("tab_index", wxString::Format(wxT("%i"), style->tab_index)); - stylev.emplace("left", wxString::Format(wxT("%.2f"), style->left())); - stylev.emplace("top", wxString::Format(wxT("%.2f"), style->top())); - stylev.emplace("right", wxString::Format(wxT("%.2f"), style->right())); - stylev.emplace("bottom", wxString::Format(wxT("%.2f"), style->bottom())); - stylev.emplace("width", wxString::Format(wxT("%.2f"), style->width())); - stylev.emplace("height", wxString::Format(wxT("%.2f"), style->height())); - stylev.emplace("angle", wxString::Format(wxT("%.2f"), style->angle())); - stylev.emplace("visible", style->visible()); - stylev.emplace("mask", style->mask.toScriptString()); + stylev.emplace("z_index", String::Format(wxT("%i"), style->z_index)); + stylev.emplace("tab_index", String::Format(wxT("%i"), style->tab_index)); + stylev.emplace("left", String::Format(wxT("%.2f"), style->left())); + stylev.emplace("top", String::Format(wxT("%.2f"), style->top())); + stylev.emplace("right", String::Format(wxT("%.2f"), style->right())); + stylev.emplace("bottom", String::Format(wxT("%.2f"), style->bottom())); + stylev.emplace("width", String::Format(wxT("%.2f"), style->width())); + stylev.emplace("height", String::Format(wxT("%.2f"), style->height())); + stylev.emplace("angle", String::Format(wxT("%.2f"), style->angle())); + stylev.emplace("visible", style->visible()); + stylev.emplace("mask", style->mask.toScriptString()); if (TextStyle* s = dynamic_cast(style.get())) { stylev.emplace("field_type", "text"); boost::json::object fontv; - fontv.emplace("name", s->font.name()); - fontv.emplace("italic_name", s->font.italic_name()); - fontv.emplace("size", wxString::Format(wxT("%.2f"), s->font.size())); - fontv.emplace("weight", s->font.weight()); - fontv.emplace("style", s->font.style()); - fontv.emplace("underline", s->font.underline()); - fontv.emplace("strikethrough", s->font.strikethrough()); - fontv.emplace("scale_down_to", wxString::Format(wxT("%.2f"), s->font.scale_down_to)); - fontv.emplace("max_stretch", wxString::Format(wxT("%.2f"), s->font.max_stretch)); - fontv.emplace("color", format_color( s->font.color())); - fontv.emplace("shadow_color", format_color( s->font.shadow_color())); - fontv.emplace("shadow_displacement_x", wxString::Format(wxT("%.2f"), s->font.shadow_displacement_x())); - fontv.emplace("shadow_displacement_y", wxString::Format(wxT("%.2f"), s->font.shadow_displacement_y())); - fontv.emplace("shadow_blur", wxString::Format(wxT("%.2f"), s->font.shadow_blur())); - fontv.emplace("stroke_color", format_color( s->font.stroke_color())); - fontv.emplace("stroke_radius", wxString::Format(wxT("%.2f"), s->font.stroke_radius())); - fontv.emplace("stroke_blur", wxString::Format(wxT("%.2f"), s->font.stroke_blur())); - fontv.emplace("separator_color", format_color( s->font.separator_color)); - fontv.emplace("flags", wxString::Format(wxT("%i"), s->font.flags)); - stylev.emplace("font", fontv); + fontv.emplace("name", s->font.name()); + fontv.emplace("italic_name", s->font.italic_name()); + fontv.emplace("size", String::Format(wxT("%.2f"), s->font.size())); + fontv.emplace("weight", s->font.weight()); + fontv.emplace("style", s->font.style()); + fontv.emplace("underline", s->font.underline()); + fontv.emplace("strikethrough", s->font.strikethrough()); + fontv.emplace("scale_down_to", String::Format(wxT("%.2f"), s->font.scale_down_to)); + fontv.emplace("max_stretch", String::Format(wxT("%.2f"), s->font.max_stretch)); + fontv.emplace("color", format_color( s->font.color())); + fontv.emplace("shadow_color", format_color( s->font.shadow_color())); + fontv.emplace("shadow_displacement_x", String::Format(wxT("%.2f"), s->font.shadow_displacement_x())); + fontv.emplace("shadow_displacement_y", String::Format(wxT("%.2f"), s->font.shadow_displacement_y())); + fontv.emplace("shadow_blur", String::Format(wxT("%.2f"), s->font.shadow_blur())); + fontv.emplace("stroke_color", format_color( s->font.stroke_color())); + fontv.emplace("stroke_radius", String::Format(wxT("%.2f"), s->font.stroke_radius())); + fontv.emplace("stroke_blur", String::Format(wxT("%.2f"), s->font.stroke_blur())); + fontv.emplace("separator_color", format_color( s->font.separator_color)); + fontv.emplace("flags", String::Format(wxT("%i"), s->font.flags)); + stylev.emplace("font", fontv); boost::json::object symbolfontv; - symbolfontv.emplace("name", s->symbol_font.name()); - symbolfontv.emplace("size", wxString::Format(wxT("%.2f"), s->symbol_font.size())); - symbolfontv.emplace("underline", s->symbol_font.underline()); - symbolfontv.emplace("strikethrough", s->symbol_font.strikethrough()); - symbolfontv.emplace("scale_down_to", wxString::Format(wxT("%.2f"), s->symbol_font.scale_down_to)); - symbolfontv.emplace("shadow_color", format_color( s->symbol_font.shadow_color())); - symbolfontv.emplace("shadow_displacement_x", wxString::Format(wxT("%.2f"), s->symbol_font.shadow_displacement_x())); - symbolfontv.emplace("shadow_displacement_y", wxString::Format(wxT("%.2f"), s->symbol_font.shadow_displacement_y())); - symbolfontv.emplace("shadow_blur", wxString::Format(wxT("%.2f"), s->symbol_font.shadow_blur())); - symbolfontv.emplace("stroke_color", format_color( s->symbol_font.stroke_color())); - symbolfontv.emplace("stroke_radius", wxString::Format(wxT("%.2f"), s->symbol_font.stroke_radius())); - symbolfontv.emplace("stroke_blur", wxString::Format(wxT("%.2f"), s->symbol_font.stroke_blur())); - stylev.emplace("symbol_font", symbolfontv); + symbolfontv.emplace("name", s->symbol_font.name()); + symbolfontv.emplace("size", String::Format(wxT("%.2f"), s->symbol_font.size())); + symbolfontv.emplace("underline", s->symbol_font.underline()); + symbolfontv.emplace("strikethrough", s->symbol_font.strikethrough()); + symbolfontv.emplace("scale_down_to", String::Format(wxT("%.2f"), s->symbol_font.scale_down_to)); + symbolfontv.emplace("shadow_color", format_color( s->symbol_font.shadow_color())); + symbolfontv.emplace("shadow_displacement_x", String::Format(wxT("%.2f"), s->symbol_font.shadow_displacement_x())); + symbolfontv.emplace("shadow_displacement_y", String::Format(wxT("%.2f"), s->symbol_font.shadow_displacement_y())); + symbolfontv.emplace("shadow_blur", String::Format(wxT("%.2f"), s->symbol_font.shadow_blur())); + symbolfontv.emplace("stroke_color", format_color( s->symbol_font.stroke_color())); + symbolfontv.emplace("stroke_radius", String::Format(wxT("%.2f"), s->symbol_font.stroke_radius())); + symbolfontv.emplace("stroke_blur", String::Format(wxT("%.2f"), s->symbol_font.stroke_blur())); + stylev.emplace("symbol_font", symbolfontv); - stylev.emplace("always_symbol", s->always_symbol); - stylev.emplace("allow_formating", s->allow_formating); - stylev.emplace("alignment", alignment_to_string( s->alignment())); - stylev.emplace("direction", direction_to_string( s->direction)); - stylev.emplace("padding_left", wxString::Format(wxT("%.2f"), s->padding_left())); - stylev.emplace("padding_right", wxString::Format(wxT("%.2f"), s->padding_right())); - stylev.emplace("padding_top", wxString::Format(wxT("%.2f"), s->padding_top())); - stylev.emplace("padding_bottom", wxString::Format(wxT("%.2f"), s->padding_bottom())); - stylev.emplace("padding_left_min", wxString::Format(wxT("%.2f"), s->padding_left_min())); - stylev.emplace("padding_right_min", wxString::Format(wxT("%.2f"), s->padding_right_min())); - stylev.emplace("padding_top_min", wxString::Format(wxT("%.2f"), s->padding_top_min())); - stylev.emplace("padding_bottom_min", wxString::Format(wxT("%.2f"), s->padding_bottom_min())); - stylev.emplace("line_height_soft", wxString::Format(wxT("%.2f"), s->line_height_soft())); - stylev.emplace("line_height_hard", wxString::Format(wxT("%.2f"), s->line_height_hard())); - stylev.emplace("line_height_line", wxString::Format(wxT("%.2f"), s->line_height_line())); - stylev.emplace("line_height_soft_max", wxString::Format(wxT("%.2f"), s->line_height_soft_max())); - stylev.emplace("line_height_hard_max", wxString::Format(wxT("%.2f"), s->line_height_hard_max())); - stylev.emplace("line_height_line_max", wxString::Format(wxT("%.2f"), s->line_height_line_max())); - stylev.emplace("paragraph_height", wxString::Format(wxT("%.2f"), s->paragraph_height())); + stylev.emplace("always_symbol", s->always_symbol); + stylev.emplace("allow_formating", s->allow_formating); + stylev.emplace("alignment", alignment_to_string( s->alignment())); + stylev.emplace("direction", direction_to_string( s->direction)); + stylev.emplace("padding_left", String::Format(wxT("%.2f"), s->padding_left())); + stylev.emplace("padding_right", String::Format(wxT("%.2f"), s->padding_right())); + stylev.emplace("padding_top", String::Format(wxT("%.2f"), s->padding_top())); + stylev.emplace("padding_bottom", String::Format(wxT("%.2f"), s->padding_bottom())); + stylev.emplace("padding_left_min", String::Format(wxT("%.2f"), s->padding_left_min())); + stylev.emplace("padding_right_min", String::Format(wxT("%.2f"), s->padding_right_min())); + stylev.emplace("padding_top_min", String::Format(wxT("%.2f"), s->padding_top_min())); + stylev.emplace("padding_bottom_min", String::Format(wxT("%.2f"), s->padding_bottom_min())); + stylev.emplace("line_height_soft", String::Format(wxT("%.2f"), s->line_height_soft())); + stylev.emplace("line_height_hard", String::Format(wxT("%.2f"), s->line_height_hard())); + stylev.emplace("line_height_line", String::Format(wxT("%.2f"), s->line_height_line())); + stylev.emplace("line_height_soft_max", String::Format(wxT("%.2f"), s->line_height_soft_max())); + stylev.emplace("line_height_hard_max", String::Format(wxT("%.2f"), s->line_height_hard_max())); + stylev.emplace("line_height_line_max", String::Format(wxT("%.2f"), s->line_height_line_max())); + stylev.emplace("paragraph_height", String::Format(wxT("%.2f"), s->paragraph_height())); boost::json::object layoutv; - layoutv.emplace("content_top", wxString::Format(wxT("%.2f"), s->layout->top)); - layoutv.emplace("content_middle", wxString::Format(wxT("%.2f"), s->layout->middle())); - layoutv.emplace("content_bottom", wxString::Format(wxT("%.2f"), s->layout->bottom())); - layoutv.emplace("content_width", wxString::Format(wxT("%.2f"), s->layout->width)); - layoutv.emplace("content_height", wxString::Format(wxT("%.2f"), s->layout->height)); - layoutv.emplace("content_lines", wxString::Format(wxT("%i"), s->layout->lines.size())); - layoutv.emplace("content_clauses", wxString::Format(wxT("%i"), s->layout->clauses.size())); - layoutv.emplace("content_paragraphs", wxString::Format(wxT("%i"), s->layout->paragraphs.size())); - layoutv.emplace("content_blocks", wxString::Format(wxT("%i"), s->layout->blocks.size())); + layoutv.emplace("content_top", String::Format(wxT("%.2f"), s->layout->top)); + layoutv.emplace("content_middle", String::Format(wxT("%.2f"), s->layout->middle())); + layoutv.emplace("content_bottom", String::Format(wxT("%.2f"), s->layout->bottom())); + layoutv.emplace("content_width", String::Format(wxT("%.2f"), s->layout->width)); + layoutv.emplace("content_height", String::Format(wxT("%.2f"), s->layout->height)); + layoutv.emplace("content_lines", String::Format(wxT("%i"), s->layout->lines.size())); + layoutv.emplace("content_clauses", String::Format(wxT("%i"), s->layout->clauses.size())); + layoutv.emplace("content_paragraphs", String::Format(wxT("%i"), s->layout->paragraphs.size())); + layoutv.emplace("content_blocks", String::Format(wxT("%i"), s->layout->blocks.size())); boost::json::array separatorsv; int size = s->layout->separators.size(); for (int i = 0; i < size; i++) { - separatorsv.emplace_back(wxString::Format(wxT("%.2f"), s->layout->separators[i])); + separatorsv.emplace_back(String::Format(wxT("%.2f"), s->layout->separators[i])); } if (size > 0) layoutv.emplace("content_separators", separatorsv); @@ -612,35 +641,35 @@ boost::json::object mse_to_json(const StyleP& style) { else if (MultipleChoiceStyle* s = dynamic_cast(style.get())) { stylev.emplace("field_type", "multiple_choice"); - stylev.emplace("popup_style", popup_style_to_string( s->popup_style)); - stylev.emplace("render_style", render_style_to_string( s->render_style)); - stylev.emplace("image", s->image.toScriptString()); - stylev.emplace("combine", combine_to_string( s->combine)); - stylev.emplace("alignment", alignment_to_string( s->alignment)); - stylev.emplace("direction", direction_to_string( s->direction())); - stylev.emplace("spacing", wxString::Format(wxT("%.2f"), s->spacing())); + stylev.emplace("popup_style", popup_style_to_string( s->popup_style)); + stylev.emplace("render_style", render_style_to_string( s->render_style)); + stylev.emplace("image", s->image.toScriptString()); + stylev.emplace("combine", combine_to_string( s->combine)); + stylev.emplace("alignment", alignment_to_string( s->alignment)); + stylev.emplace("direction", direction_to_string( s->direction())); + stylev.emplace("spacing", String::Format(wxT("%.2f"), s->spacing())); boost::json::object fontv; - fontv.emplace("name", s->font.name()); - fontv.emplace("italic_name", s->font.italic_name()); - fontv.emplace("size", wxString::Format(wxT("%.2f"), s->font.size())); - fontv.emplace("weight", s->font.weight()); - fontv.emplace("style", s->font.style()); - fontv.emplace("underline", s->font.underline()); - fontv.emplace("strikethrough", s->font.strikethrough()); - fontv.emplace("scale_down_to", wxString::Format(wxT("%.2f"), s->font.scale_down_to)); - fontv.emplace("max_stretch", wxString::Format(wxT("%.2f"), s->font.max_stretch)); - fontv.emplace("color", format_color( s->font.color())); - fontv.emplace("shadow_color", format_color( s->font.shadow_color())); - fontv.emplace("shadow_displacement_x", wxString::Format(wxT("%.2f"), s->font.shadow_displacement_x())); - fontv.emplace("shadow_displacement_y", wxString::Format(wxT("%.2f"), s->font.shadow_displacement_y())); - fontv.emplace("shadow_blur", wxString::Format(wxT("%.2f"), s->font.shadow_blur())); - fontv.emplace("stroke_color", format_color( s->font.stroke_color())); - fontv.emplace("stroke_radius", wxString::Format(wxT("%.2f"), s->font.stroke_radius())); - fontv.emplace("stroke_blur", wxString::Format(wxT("%.2f"), s->font.stroke_blur())); - fontv.emplace("separator_color", format_color( s->font.separator_color)); - fontv.emplace("flags", wxString::Format(wxT("%i"), s->font.flags)); - stylev.emplace("font", fontv); + fontv.emplace("name", s->font.name()); + fontv.emplace("italic_name", s->font.italic_name()); + fontv.emplace("size", String::Format(wxT("%.2f"), s->font.size())); + fontv.emplace("weight", s->font.weight()); + fontv.emplace("style", s->font.style()); + fontv.emplace("underline", s->font.underline()); + fontv.emplace("strikethrough", s->font.strikethrough()); + fontv.emplace("scale_down_to", String::Format(wxT("%.2f"), s->font.scale_down_to)); + fontv.emplace("max_stretch", String::Format(wxT("%.2f"), s->font.max_stretch)); + fontv.emplace("color", format_color( s->font.color())); + fontv.emplace("shadow_color", format_color( s->font.shadow_color())); + fontv.emplace("shadow_displacement_x", String::Format(wxT("%.2f"), s->font.shadow_displacement_x())); + fontv.emplace("shadow_displacement_y", String::Format(wxT("%.2f"), s->font.shadow_displacement_y())); + fontv.emplace("shadow_blur", String::Format(wxT("%.2f"), s->font.shadow_blur())); + fontv.emplace("stroke_color", format_color( s->font.stroke_color())); + fontv.emplace("stroke_radius", String::Format(wxT("%.2f"), s->font.stroke_radius())); + fontv.emplace("stroke_blur", String::Format(wxT("%.2f"), s->font.stroke_blur())); + fontv.emplace("separator_color", format_color( s->font.separator_color)); + fontv.emplace("flags", String::Format(wxT("%i"), s->font.flags)); + stylev.emplace("font", fontv); boost::json::object choiceimagesv; for (auto choice_image : s->choice_images) { @@ -652,33 +681,33 @@ boost::json::object mse_to_json(const StyleP& style) { else if (ChoiceStyle* s = dynamic_cast(style.get())) { stylev.emplace("field_type", dynamic_cast(style.get()) ? "boolean" : "choice"); - stylev.emplace("popup_style", popup_style_to_string( s->popup_style)); - stylev.emplace("render_style", render_style_to_string( s->render_style)); - stylev.emplace("image", s->image.toScriptString()); - stylev.emplace("combine", combine_to_string( s->combine)); - stylev.emplace("alignment", alignment_to_string( s->alignment)); + stylev.emplace("popup_style", popup_style_to_string( s->popup_style)); + stylev.emplace("render_style", render_style_to_string( s->render_style)); + stylev.emplace("image", s->image.toScriptString()); + stylev.emplace("combine", combine_to_string( s->combine)); + stylev.emplace("alignment", alignment_to_string( s->alignment)); boost::json::object fontv; - fontv.emplace("name", s->font.name()); - fontv.emplace("italic_name", s->font.italic_name()); - fontv.emplace("size", wxString::Format(wxT("%.2f"), s->font.size())); - fontv.emplace("weight", s->font.weight()); - fontv.emplace("style", s->font.style()); - fontv.emplace("underline", s->font.underline()); - fontv.emplace("strikethrough", s->font.strikethrough()); - fontv.emplace("scale_down_to", wxString::Format(wxT("%.2f"), s->font.scale_down_to)); - fontv.emplace("max_stretch", wxString::Format(wxT("%.2f"), s->font.max_stretch)); - fontv.emplace("color", format_color( s->font.color())); - fontv.emplace("shadow_color", format_color( s->font.shadow_color())); - fontv.emplace("shadow_displacement_x", wxString::Format(wxT("%.2f"), s->font.shadow_displacement_x())); - fontv.emplace("shadow_displacement_y", wxString::Format(wxT("%.2f"), s->font.shadow_displacement_y())); - fontv.emplace("shadow_blur", wxString::Format(wxT("%.2f"), s->font.shadow_blur())); - fontv.emplace("stroke_color", format_color( s->font.stroke_color())); - fontv.emplace("stroke_radius", wxString::Format(wxT("%.2f"), s->font.stroke_radius())); - fontv.emplace("stroke_blur", wxString::Format(wxT("%.2f"), s->font.stroke_blur())); - fontv.emplace("separator_color", format_color( s->font.separator_color)); - fontv.emplace("flags", wxString::Format(wxT("%i"), s->font.flags)); - stylev.emplace("font", fontv); + fontv.emplace("name", s->font.name()); + fontv.emplace("italic_name", s->font.italic_name()); + fontv.emplace("size", String::Format(wxT("%.2f"), s->font.size())); + fontv.emplace("weight", s->font.weight()); + fontv.emplace("style", s->font.style()); + fontv.emplace("underline", s->font.underline()); + fontv.emplace("strikethrough", s->font.strikethrough()); + fontv.emplace("scale_down_to", String::Format(wxT("%.2f"), s->font.scale_down_to)); + fontv.emplace("max_stretch", String::Format(wxT("%.2f"), s->font.max_stretch)); + fontv.emplace("color", format_color( s->font.color())); + fontv.emplace("shadow_color", format_color( s->font.shadow_color())); + fontv.emplace("shadow_displacement_x", String::Format(wxT("%.2f"), s->font.shadow_displacement_x())); + fontv.emplace("shadow_displacement_y", String::Format(wxT("%.2f"), s->font.shadow_displacement_y())); + fontv.emplace("shadow_blur", String::Format(wxT("%.2f"), s->font.shadow_blur())); + fontv.emplace("stroke_color", format_color( s->font.stroke_color())); + fontv.emplace("stroke_radius", String::Format(wxT("%.2f"), s->font.stroke_radius())); + fontv.emplace("stroke_blur", String::Format(wxT("%.2f"), s->font.stroke_blur())); + fontv.emplace("separator_color", format_color( s->font.separator_color)); + fontv.emplace("flags", String::Format(wxT("%i"), s->font.flags)); + stylev.emplace("font", fontv); boost::json::object choiceimagesv; for (auto choice_image : s->choice_images) { @@ -692,71 +721,71 @@ boost::json::object mse_to_json(const StyleP& style) { stylev.emplace("field_type", "package_choice"); boost::json::object fontv; - fontv.emplace("name", s->font.name()); - fontv.emplace("italic_name", s->font.italic_name()); - fontv.emplace("size", wxString::Format(wxT("%.2f"), s->font.size())); - fontv.emplace("weight", s->font.weight()); - fontv.emplace("style", s->font.style()); - fontv.emplace("underline", s->font.underline()); - fontv.emplace("strikethrough", s->font.strikethrough()); - fontv.emplace("scale_down_to", wxString::Format(wxT("%.2f"), s->font.scale_down_to)); - fontv.emplace("max_stretch", wxString::Format(wxT("%.2f"), s->font.max_stretch)); - fontv.emplace("color", format_color( s->font.color())); - fontv.emplace("shadow_color", format_color( s->font.shadow_color())); - fontv.emplace("shadow_displacement_x", wxString::Format(wxT("%.2f"), s->font.shadow_displacement_x())); - fontv.emplace("shadow_displacement_y", wxString::Format(wxT("%.2f"), s->font.shadow_displacement_y())); - fontv.emplace("shadow_blur", wxString::Format(wxT("%.2f"), s->font.shadow_blur())); - fontv.emplace("stroke_color", format_color( s->font.stroke_color())); - fontv.emplace("stroke_radius", wxString::Format(wxT("%.2f"), s->font.stroke_radius())); - fontv.emplace("stroke_blur", wxString::Format(wxT("%.2f"), s->font.stroke_blur())); - fontv.emplace("separator_color", format_color( s->font.separator_color)); - fontv.emplace("flags", wxString::Format(wxT("%i"), s->font.flags)); - stylev.emplace("font", fontv); + fontv.emplace("name", s->font.name()); + fontv.emplace("italic_name", s->font.italic_name()); + fontv.emplace("size", String::Format(wxT("%.2f"), s->font.size())); + fontv.emplace("weight", s->font.weight()); + fontv.emplace("style", s->font.style()); + fontv.emplace("underline", s->font.underline()); + fontv.emplace("strikethrough", s->font.strikethrough()); + fontv.emplace("scale_down_to", String::Format(wxT("%.2f"), s->font.scale_down_to)); + fontv.emplace("max_stretch", String::Format(wxT("%.2f"), s->font.max_stretch)); + fontv.emplace("color", format_color( s->font.color())); + fontv.emplace("shadow_color", format_color( s->font.shadow_color())); + fontv.emplace("shadow_displacement_x", String::Format(wxT("%.2f"), s->font.shadow_displacement_x())); + fontv.emplace("shadow_displacement_y", String::Format(wxT("%.2f"), s->font.shadow_displacement_y())); + fontv.emplace("shadow_blur", String::Format(wxT("%.2f"), s->font.shadow_blur())); + fontv.emplace("stroke_color", format_color( s->font.stroke_color())); + fontv.emplace("stroke_radius", String::Format(wxT("%.2f"), s->font.stroke_radius())); + fontv.emplace("stroke_blur", String::Format(wxT("%.2f"), s->font.stroke_blur())); + fontv.emplace("separator_color", format_color( s->font.separator_color)); + fontv.emplace("flags", String::Format(wxT("%i"), s->font.flags)); + stylev.emplace("font", fontv); } else if (ColorStyle* s = dynamic_cast(style.get())) { stylev.emplace("field_type", "color"); - stylev.emplace("radius", wxString::Format(wxT("%.2f"), s->radius())); - stylev.emplace("left_width", wxString::Format(wxT("%.2f"), s->left_width())); - stylev.emplace("right_width", wxString::Format(wxT("%.2f"), s->right_width())); - stylev.emplace("top_width", wxString::Format(wxT("%.2f"), s->top_width())); - stylev.emplace("bottom_width", wxString::Format(wxT("%.2f"), s->bottom_width())); - stylev.emplace("combine", combine_to_string( s->combine)); + stylev.emplace("radius", String::Format(wxT("%.2f"), s->radius())); + stylev.emplace("left_width", String::Format(wxT("%.2f"), s->left_width())); + stylev.emplace("right_width", String::Format(wxT("%.2f"), s->right_width())); + stylev.emplace("top_width", String::Format(wxT("%.2f"), s->top_width())); + stylev.emplace("bottom_width", String::Format(wxT("%.2f"), s->bottom_width())); + stylev.emplace("combine", combine_to_string( s->combine)); } else if (SymbolStyle* s = dynamic_cast(style.get())) { stylev.emplace("field_type", "symbol"); - stylev.emplace("min_aspect_ratio", wxString::Format(wxT("%.2f"), s->min_aspect_ratio)); - stylev.emplace("max_aspect_ratio", wxString::Format(wxT("%.2f"), s->max_aspect_ratio)); + stylev.emplace("min_aspect_ratio", String::Format(wxT("%.2f"), s->min_aspect_ratio)); + stylev.emplace("max_aspect_ratio", String::Format(wxT("%.2f"), s->max_aspect_ratio)); boost::json::array variationsv; int size = s->variations.size(); for (int i = 0; i < size; i++) { boost::json::object variationv; - variationv.emplace("name", s->variations[i]->name); - variationv.emplace("border_radius", wxString::Format(wxT("%.2f"), s->variations[i]->border_radius)); + variationv.emplace("name", s->variations[i]->name); + variationv.emplace("border_radius", String::Format(wxT("%.2f"), s->variations[i]->border_radius)); SymbolFilterP filter = s->variations[i]->filter; if (SolidFillSymbolFilter* f = dynamic_cast(filter.get())) { - variationv.emplace("fill_type", f->fillType()); - variationv.emplace("fill_color", format_color( f->fill_color)); - variationv.emplace("border_color", format_color( f->border_color)); + variationv.emplace("fill_type", f->fillType()); + variationv.emplace("fill_color", format_color( f->fill_color)); + variationv.emplace("border_color", format_color( f->border_color)); } else if (RadialGradientSymbolFilter* f = dynamic_cast(filter.get())) { - variationv.emplace("fill_type", f->fillType()); - variationv.emplace("fill_color_1", format_color( f->fill_color_1)); - variationv.emplace("fill_color_2", format_color( f->fill_color_2)); - variationv.emplace("border_color_1", format_color( f->border_color_1)); - variationv.emplace("border_color_2", format_color( f->border_color_2)); + variationv.emplace("fill_type", f->fillType()); + variationv.emplace("fill_color_1", format_color( f->fill_color_1)); + variationv.emplace("fill_color_2", format_color( f->fill_color_2)); + variationv.emplace("border_color_1", format_color( f->border_color_1)); + variationv.emplace("border_color_2", format_color( f->border_color_2)); } else if (LinearGradientSymbolFilter* f = dynamic_cast(filter.get())) { - variationv.emplace("fill_type", f->fillType()); - variationv.emplace("fill_color_1", format_color( f->fill_color_1)); - variationv.emplace("fill_color_2", format_color( f->fill_color_2)); - variationv.emplace("border_color_1", format_color( f->border_color_1)); - variationv.emplace("border_color_2", format_color( f->border_color_2)); - variationv.emplace("center_x", wxString::Format(wxT("%.2f"), f->center_x)); - variationv.emplace("center_y", wxString::Format(wxT("%.2f"), f->center_y)); - variationv.emplace("end_x", wxString::Format(wxT("%.2f"), f->end_x)); - variationv.emplace("end_y", wxString::Format(wxT("%.2f"), f->end_y)); + variationv.emplace("fill_type", f->fillType()); + variationv.emplace("fill_color_1", format_color( f->fill_color_1)); + variationv.emplace("fill_color_2", format_color( f->fill_color_2)); + variationv.emplace("border_color_1", format_color( f->border_color_1)); + variationv.emplace("border_color_2", format_color( f->border_color_2)); + variationv.emplace("center_x", String::Format(wxT("%.2f"), f->center_x)); + variationv.emplace("center_y", String::Format(wxT("%.2f"), f->center_y)); + variationv.emplace("end_x", String::Format(wxT("%.2f"), f->end_x)); + variationv.emplace("end_y", String::Format(wxT("%.2f"), f->end_y)); } variationsv.emplace_back(variationv); } @@ -765,34 +794,34 @@ boost::json::object mse_to_json(const StyleP& style) { else if (InfoStyle* s = dynamic_cast(style.get())) { stylev.emplace("field_type", "info"); - stylev.emplace("alignment", alignment_to_string( s->alignment)); - stylev.emplace("padding_left", wxString::Format(wxT("%.2f"), s->padding_left)); - stylev.emplace("padding_right", wxString::Format(wxT("%.2f"), s->padding_right)); - stylev.emplace("padding_top", wxString::Format(wxT("%.2f"), s->padding_top)); - stylev.emplace("padding_bottom", wxString::Format(wxT("%.2f"), s->padding_bottom)); - stylev.emplace("background_color", format_color( s->background_color)); + stylev.emplace("alignment", alignment_to_string( s->alignment)); + stylev.emplace("padding_left", String::Format(wxT("%.2f"), s->padding_left)); + stylev.emplace("padding_right", String::Format(wxT("%.2f"), s->padding_right)); + stylev.emplace("padding_top", String::Format(wxT("%.2f"), s->padding_top)); + stylev.emplace("padding_bottom", String::Format(wxT("%.2f"), s->padding_bottom)); + stylev.emplace("background_color", format_color( s->background_color)); boost::json::object fontv; - fontv.emplace("name", s->font.name()); - fontv.emplace("italic_name", s->font.italic_name()); - fontv.emplace("size", wxString::Format(wxT("%.2f"), s->font.size())); - fontv.emplace("weight", s->font.weight()); - fontv.emplace("style", s->font.style()); - fontv.emplace("underline", s->font.underline()); - fontv.emplace("strikethrough", s->font.strikethrough()); - fontv.emplace("scale_down_to", wxString::Format(wxT("%.2f"), s->font.scale_down_to)); - fontv.emplace("max_stretch", wxString::Format(wxT("%.2f"), s->font.max_stretch)); - fontv.emplace("color", format_color( s->font.color())); - fontv.emplace("shadow_color", format_color( s->font.shadow_color())); - fontv.emplace("shadow_displacement_x", wxString::Format(wxT("%.2f"), s->font.shadow_displacement_x())); - fontv.emplace("shadow_displacement_y", wxString::Format(wxT("%.2f"), s->font.shadow_displacement_y())); - fontv.emplace("shadow_blur", wxString::Format(wxT("%.2f"), s->font.shadow_blur())); - fontv.emplace("stroke_color", format_color( s->font.stroke_color())); - fontv.emplace("stroke_radius", wxString::Format(wxT("%.2f"), s->font.stroke_radius())); - fontv.emplace("stroke_blur", wxString::Format(wxT("%.2f"), s->font.stroke_blur())); - fontv.emplace("separator_color", format_color( s->font.separator_color)); - fontv.emplace("flags", wxString::Format(wxT("%i"), s->font.flags)); - stylev.emplace("font", fontv); + fontv.emplace("name", s->font.name()); + fontv.emplace("italic_name", s->font.italic_name()); + fontv.emplace("size", String::Format(wxT("%.2f"), s->font.size())); + fontv.emplace("weight", s->font.weight()); + fontv.emplace("style", s->font.style()); + fontv.emplace("underline", s->font.underline()); + fontv.emplace("strikethrough", s->font.strikethrough()); + fontv.emplace("scale_down_to", String::Format(wxT("%.2f"), s->font.scale_down_to)); + fontv.emplace("max_stretch", String::Format(wxT("%.2f"), s->font.max_stretch)); + fontv.emplace("color", format_color( s->font.color())); + fontv.emplace("shadow_color", format_color( s->font.shadow_color())); + fontv.emplace("shadow_displacement_x", String::Format(wxT("%.2f"), s->font.shadow_displacement_x())); + fontv.emplace("shadow_displacement_y", String::Format(wxT("%.2f"), s->font.shadow_displacement_y())); + fontv.emplace("shadow_blur", String::Format(wxT("%.2f"), s->font.shadow_blur())); + fontv.emplace("stroke_color", format_color( s->font.stroke_color())); + fontv.emplace("stroke_radius", String::Format(wxT("%.2f"), s->font.stroke_radius())); + fontv.emplace("stroke_blur", String::Format(wxT("%.2f"), s->font.stroke_blur())); + fontv.emplace("separator_color", format_color( s->font.separator_color)); + fontv.emplace("flags", String::Format(wxT("%i"), s->font.flags)); + stylev.emplace("font", fontv); } return stylev; diff --git a/src/util/io/reader.cpp b/src/util/io/reader.cpp index 5b8c868b..26dcd202 100644 --- a/src/util/io/reader.cpp +++ b/src/util/io/reader.cpp @@ -18,9 +18,9 @@ using boost::tribool; // ----------------------------------------------------------------------------- : Reader -Reader::Reader(wxInputStream& input, Packaged* package, const String& filename, bool ignore_invalid) +Reader::Reader(wxInputStream& input, Packaged* package, const String& filename, bool ignore_invalid, bool suppress_warnings) : indent(0), expected_indent(0), state(OUTSIDE) - , ignore_invalid(ignore_invalid) + , ignore_invalid(ignore_invalid), suppress_warnings(suppress_warnings) , filename(filename), package(package), line_number(0), previous_line_number(0) , input(input) { @@ -53,7 +53,7 @@ void Reader::warning(const String& msg, int line_number_delta, bool warn_on_prev } void Reader::showWarnings() { - if (!warnings.empty()) { + if (!suppress_warnings && !warnings.empty()) { queue_message(MESSAGE_WARNING, _("Warnings while reading file:\n") + filename + _("\n") + warnings); warnings.clear(); } diff --git a/src/util/io/reader.hpp b/src/util/io/reader.hpp index 6d5dc789..0412f851 100644 --- a/src/util/io/reader.hpp +++ b/src/util/io/reader.hpp @@ -38,7 +38,7 @@ public: /** filename is used only for error messages * package is used for looking up included files. */ - Reader(wxInputStream& input, Packaged* package = nullptr, const String& filename = _(""), bool ignore_invalid = false); + Reader(wxInputStream& input, Packaged* package = nullptr, const String& filename = _(""), bool ignore_invalid = false, bool suppress_warnings = false); ~Reader() { showWarnings(); } @@ -148,7 +148,9 @@ private: } state; /// Should all invalid keys be ignored? bool ignore_invalid; - + /// Should warnings be emitted? + bool suppress_warnings; + /// Filename for error messages String filename; /// Package this file is from, if any