mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 04:57:00 -04:00
Add to_json and from_json script functions
This commit is contained in:
@@ -7,7 +7,7 @@ Creates a new [[type:card]] object. The card is not automatically added to a set
|
||||
|
||||
The argument is a map from card field names to values, for example @new_card([name: "My Card"])@ creates a card with the name @"My Card"@, and all other fields at their default value.
|
||||
|
||||
The map can also contain the following built-in keys: notes, id, linked_card_1 to linked_card_4, linked_relation_1 to linked_relation_4, stylesheet, and styling_data. For styling_data, the value must itself be a map from styling field names to values. Be sure to define a stylesheet before styling_data.
|
||||
The map can also contain the following built-in keys: notes, id, linked_card_1 to linked_card_4, linked_relation_1 to linked_relation_4, stylesheet, styling_data, and extra_data. For styling_data and extra_data, the value must itself be a map from field names to values. Be sure to define a stylesheet before these.
|
||||
|
||||
NOTE: you should use underscores instead of spaces in field names.
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@ Field::~Field() {}
|
||||
|
||||
void Field::initDependencies(Context& ctx, const Dependency& dep) const {
|
||||
sort_script.initDependencies(ctx, dep);
|
||||
import_script.initDependencies(ctx, dep);
|
||||
}
|
||||
|
||||
IMPLEMENT_REFLECTION(Field) {
|
||||
|
||||
@@ -62,9 +62,9 @@ wxDataFormat CardsDataObject::format = _("application/x-mse-cards");
|
||||
|
||||
CardsDataObject::CardsDataObject(const SetP& set, const vector<CardP>& cards) {
|
||||
// set the stylesheet, so when deserializing we know whos style options we are reading
|
||||
bool* has_styling = new bool[cards.size()];
|
||||
vector<bool> has_styling;
|
||||
for (size_t i = 0 ; i < cards.size() ; ++i) {
|
||||
has_styling[i] = cards[i]->has_styling && !cards[i]->stylesheet;
|
||||
has_styling.push_back(cards[i]->has_styling && !cards[i]->stylesheet);
|
||||
if (has_styling[i]) {
|
||||
cards[i]->stylesheet = set->stylesheet;
|
||||
}
|
||||
@@ -78,7 +78,6 @@ CardsDataObject::CardsDataObject(const SetP& set, const vector<CardP>& cards) {
|
||||
}
|
||||
}
|
||||
SetFormat(format);
|
||||
delete [] has_styling;
|
||||
}
|
||||
|
||||
CardsDataObject::CardsDataObject() {
|
||||
|
||||
@@ -91,11 +91,11 @@ public:
|
||||
Keyword() : fixed(false), valid(false) {}
|
||||
|
||||
String keyword; ///< The keyword, only for human use
|
||||
String rules; ///< Rules/explanation
|
||||
String match; ///< String to match, <atom-param> tags are used for parameters
|
||||
vector<KeywordParamP> parameters; ///< The types of parameters
|
||||
StringScript reminder; ///< Reminder text of the keyword
|
||||
String mode; ///< Mode of use, can be used by scripts (only gives the name)
|
||||
String rules; ///< Rules/explanation
|
||||
String match; ///< String to match, <atom-param> tags are used for parameters
|
||||
vector<KeywordParamP> parameters; ///< The types of parameters
|
||||
StringScript reminder; ///< Reminder text of the keyword
|
||||
String mode; ///< Mode of use, can be used by scripts (only gives the name)
|
||||
/// Regular expression to match and split parameters, automatically generated.
|
||||
/** The regex has exactly 2 * parameters.size() + 1 captures (excluding the entire match, caputure 0),
|
||||
* captures 1,3,... capture the plain text of the match string
|
||||
|
||||
+1
-1
@@ -94,7 +94,7 @@ bool PackType::update(Context& ctx) {
|
||||
|
||||
bool PackItem::update(Context& ctx) {
|
||||
return amount.update(ctx)
|
||||
| weight.update(ctx);
|
||||
|| weight.update(ctx);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ public:
|
||||
/// The values on the fields of the set
|
||||
/** The indices should correspond to the set_fields in the Game */
|
||||
IndexMap<FieldP, ValueP> data;
|
||||
/// Extra values for specitic stylesheets, indexed by stylesheet name
|
||||
/// Extra values for specific stylesheets, indexed by stylesheet name
|
||||
DelayedIndexMaps<FieldP,ValueP> styling_data;
|
||||
vector<CardP> cards; ///< The cards in the set
|
||||
vector<KeywordP> keywords; ///< Additional keywords used in this set
|
||||
|
||||
@@ -19,10 +19,11 @@
|
||||
#include <data/stylesheet.hpp>
|
||||
#include <data/action/set.hpp>
|
||||
#include <gui/add_json_window.hpp>
|
||||
#include <script/functions/json.hpp>
|
||||
#include <script/functions/construction_helper.hpp>
|
||||
#include <wx/statline.h>
|
||||
#include <boost/json/src.hpp>
|
||||
#include <boost/json.hpp>
|
||||
#include <boost/json/src.hpp>
|
||||
|
||||
// ----------------------------------------------------------------------------- : AddJSON
|
||||
|
||||
@@ -63,54 +64,6 @@ AddJSONWindow::AddJSONWindow(Window* parent, const SetP& set, bool sizer)
|
||||
}
|
||||
}
|
||||
|
||||
static ScriptValueP json_to_script(boost::json::value& jv) {
|
||||
if (jv == nullptr) return script_nil;
|
||||
else if (jv.is_null()) return script_nil;
|
||||
else if (jv.is_bool()) return to_script(jv.get_bool());
|
||||
else if (jv.is_double()) return to_script(jv.get_double());
|
||||
else if (jv.is_int64()) {
|
||||
int integer = jv.get_int64();
|
||||
return to_script(integer);
|
||||
}
|
||||
else if (jv.is_uint64()) {
|
||||
int integer = jv.get_uint64();
|
||||
return to_script(integer);
|
||||
}
|
||||
else if (jv.is_string()) {
|
||||
std::string stdstring = boost::json::value_to<std::string>(jv);
|
||||
String wxstring(stdstring.c_str(), wxConvUTF8);
|
||||
return to_script(wxstring);
|
||||
}
|
||||
else if (jv.is_array()) {
|
||||
boost::json::array array = jv.get_array();
|
||||
ScriptCustomCollectionP result = make_intrusive<ScriptCustomCollection>();
|
||||
for (int i = 0; i < array.size(); ++i) {
|
||||
boost::json::value jvalue = array[i];
|
||||
ScriptValueP value = json_to_script(jvalue);
|
||||
result->value.push_back(value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else if (jv.is_object()) {
|
||||
boost::json::object object = jv.get_object();
|
||||
ScriptCustomCollectionP result = make_intrusive<ScriptCustomCollection>();
|
||||
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);
|
||||
boost::json::value jvalue = it->value();
|
||||
ScriptValueP value = json_to_script(jvalue);
|
||||
result->key_value[key] = value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
queue_message(MESSAGE_ERROR, _ERROR_("add card json unknown type"));
|
||||
return script_nil;
|
||||
}
|
||||
}
|
||||
|
||||
void AddJSONWindow::setJSONType() {
|
||||
int sel = json_type->GetSelection();
|
||||
if (sel == json_type->GetCount() - 1) { // Custom type
|
||||
@@ -210,7 +163,7 @@ void AddJSONWindow::onBrowseFiles(wxCommandEvent&) {
|
||||
auto& card = card_array[i].as_object();
|
||||
for (int h = 0; h < headers_out.size(); ++h) {
|
||||
auto& value = card[headers_out[h].ToStdString()];
|
||||
row.push_back(json_to_script(value));
|
||||
row.push_back(json_to_mse(value, set.get()));
|
||||
}
|
||||
table_out.push_back(row);
|
||||
}
|
||||
|
||||
+23
-27
@@ -102,36 +102,32 @@ CardsPanel::CardsPanel(Window* parent, int id)
|
||||
}
|
||||
|
||||
void CardsPanel::updateCardCounts() {
|
||||
if (counts) {
|
||||
if (card_list && set) {
|
||||
int selected = card_list->GetSelectedItemCount();
|
||||
int filtered = card_list->GetItemCount();
|
||||
int total = set->cards.size();
|
||||
if (counts && card_list && set) {
|
||||
int selected = card_list->GetSelectedItemCount();
|
||||
int filtered = card_list->GetItemCount();
|
||||
int total = set->cards.size();
|
||||
|
||||
if (
|
||||
selected_cards_count == selected
|
||||
&& filtered_cards_count == filtered
|
||||
&& total_cards_count == total
|
||||
) return;
|
||||
if (
|
||||
selected_cards_count == selected
|
||||
&& filtered_cards_count == filtered
|
||||
&& total_cards_count == total
|
||||
&& !counts->GetLabel().empty()
|
||||
) return;
|
||||
|
||||
selected_cards_count = selected;
|
||||
filtered_cards_count = filtered;
|
||||
total_cards_count = total;
|
||||
selected_cards_count = selected;
|
||||
filtered_cards_count = filtered;
|
||||
total_cards_count = total;
|
||||
|
||||
if (filtered == total) {
|
||||
counts->SetLabel(_TOOL_2_("card counts 2",
|
||||
wxString::Format(wxT("%i"), selected),
|
||||
wxString::Format(wxT("%i"), total)));
|
||||
}
|
||||
else {
|
||||
counts->SetLabel(_TOOL_3_("card counts 3",
|
||||
wxString::Format(wxT("%i"), selected),
|
||||
wxString::Format(wxT("%i"), filtered),
|
||||
wxString::Format(wxT("%i"), total)));
|
||||
}
|
||||
|
||||
} else {
|
||||
counts->SetLabel(_(""));
|
||||
if (filtered == total) {
|
||||
counts->SetLabel(_TOOL_2_("card counts 2",
|
||||
wxString::Format(wxT("%i"), selected),
|
||||
wxString::Format(wxT("%i"), total)));
|
||||
}
|
||||
else {
|
||||
counts->SetLabel(_TOOL_3_("card counts 3",
|
||||
wxString::Format(wxT("%i"), selected),
|
||||
wxString::Format(wxT("%i"), filtered),
|
||||
wxString::Format(wxT("%i"), total)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include <util/version.hpp>
|
||||
#include <script/functions/functions.hpp>
|
||||
#include <script/functions/util.hpp>
|
||||
#include <script/functions/construction_helper.hpp>
|
||||
#include <script/functions/json.hpp>
|
||||
#include <util/tagged_string.hpp>
|
||||
#include <util/spec_sort.hpp>
|
||||
#include <util/error.hpp>
|
||||
@@ -21,6 +23,8 @@
|
||||
#include <random>
|
||||
#include <wx/filename.h>
|
||||
#include <wx/stdpaths.h>
|
||||
#include <wx/wfstream.h>
|
||||
#include <boost/json.hpp>
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------- : Debugging
|
||||
@@ -257,6 +261,24 @@ SCRIPT_FUNCTION(to_code) {
|
||||
SCRIPT_RETURN(input->toCode());
|
||||
}
|
||||
|
||||
SCRIPT_FUNCTION(to_json) {
|
||||
SCRIPT_PARAM_C(ScriptValueP, input);
|
||||
SCRIPT_PARAM_C(Set*, set);
|
||||
SCRIPT_PARAM_DEFAULT(bool, pretty_print, true);
|
||||
boost::json::value jv = mse_to_json(input, set);
|
||||
|
||||
queue_message(MESSAGE_ERROR, json_pretty_print(jv));
|
||||
|
||||
if (pretty_print) return to_script(json_pretty_print(jv));
|
||||
else return to_script(json_ugly_print(jv));
|
||||
}
|
||||
|
||||
SCRIPT_FUNCTION(from_json) {
|
||||
SCRIPT_PARAM_C(ScriptValueP, input);
|
||||
SCRIPT_PARAM_C(Set*, set);
|
||||
return json_to_mse(input, set);
|
||||
}
|
||||
|
||||
SCRIPT_FUNCTION(type_name) {
|
||||
SCRIPT_PARAM_C(ScriptValueP, input);
|
||||
SCRIPT_RETURN(input->typeName());
|
||||
@@ -818,6 +840,8 @@ void init_script_basic_functions(Context& ctx) {
|
||||
ctx.setVariable(_("to_color"), script_to_color);
|
||||
ctx.setVariable(_("to_date"), script_to_date);
|
||||
ctx.setVariable(_("to_code"), script_to_code);
|
||||
ctx.setVariable(_("to_json"), script_to_json);
|
||||
ctx.setVariable(_("from_json"), script_from_json);
|
||||
ctx.setVariable(_("type_name"), script_type_name);
|
||||
ctx.setVariable(_("make_map"), script_make_map);
|
||||
ctx.setVariable(_("get_card_styling"), script_get_card_styling);
|
||||
|
||||
@@ -37,9 +37,9 @@ SCRIPT_FUNCTION(new_card) {
|
||||
if (key == script_nil) continue;
|
||||
String key_name = key->toString();
|
||||
// check if the given value is for a built-in field
|
||||
if (set_builtin_container(game, new_card, value, key_name, ignore_field_not_found)) continue;
|
||||
if (set_builtin_container(*game, new_card, value, key_name, ignore_field_not_found)) continue;
|
||||
// find the field value (container) that corresponds to the given value
|
||||
Value* container = get_container(game, new_card, key_name, ignore_field_not_found);
|
||||
Value* container = get_card_field_container(*game, new_card->data, key_name, ignore_field_not_found);
|
||||
if (container == nullptr) continue;
|
||||
FieldP field = container->fieldP;
|
||||
// if the field has a construction script, set the value and card context variables to be the given value and this card, run script
|
||||
@@ -59,9 +59,9 @@ SCRIPT_FUNCTION(new_card) {
|
||||
if (script_key == script_nil) continue;
|
||||
String script_key_name = script_key->toString();
|
||||
// check if the script value is for a built-in field
|
||||
if (set_builtin_container(game, new_card, script_value, script_key_name, ignore_field_not_found)) continue;
|
||||
if (set_builtin_container(*game, new_card, script_value, script_key_name, ignore_field_not_found)) continue;
|
||||
// find the field value that corresponds to the script value
|
||||
Value* script_container = get_container(game, new_card, script_key_name, ignore_field_not_found);
|
||||
Value* script_container = get_card_field_container(*game, new_card->data, script_key_name, ignore_field_not_found);
|
||||
if (script_container == nullptr) continue;
|
||||
// set the field value to the script value
|
||||
set_container(script_container, script_value, script_key_name);
|
||||
@@ -94,9 +94,9 @@ SCRIPT_FUNCTION(new_card) {
|
||||
if (script_key == script_nil) continue;
|
||||
String script_key_name = script_key->toString();
|
||||
// check if the script value is for a built-in field
|
||||
if (set_builtin_container(game, new_card, script_value, script_key_name, ignore_field_not_found)) continue;
|
||||
if (set_builtin_container(*game, new_card, script_value, script_key_name, ignore_field_not_found)) continue;
|
||||
// find the field value that corresponds to the script value
|
||||
Value* script_container = get_container(game, new_card, script_key_name, ignore_field_not_found);
|
||||
Value* script_container = get_card_field_container(*game, new_card->data, script_key_name, ignore_field_not_found);
|
||||
if (script_container == nullptr) continue;
|
||||
// set the field value to the script value
|
||||
set_container(script_container, script_value, script_key_name);
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
//| License: GNU General Public License 2 or later (see file COPYING) |
|
||||
//+----------------------------------------------------------------------------+
|
||||
|
||||
#pragma once
|
||||
|
||||
// ----------------------------------------------------------------------------- : Includes
|
||||
|
||||
#include <util/prec.hpp>
|
||||
@@ -12,33 +14,46 @@
|
||||
#include <data/field/package_choice.hpp>
|
||||
#include <data/field/color.hpp>
|
||||
#include <data/field/image.hpp>
|
||||
#include <data/field/symbol.hpp>
|
||||
#include <data/action/set.hpp>
|
||||
#include <data/game.hpp>
|
||||
#include <data/set.hpp>
|
||||
#include <data/stylesheet.hpp>
|
||||
#include <data/card.hpp>
|
||||
#include <util/error.hpp>
|
||||
|
||||
// ----------------------------------------------------------------------------- : Helper functions
|
||||
|
||||
static Value* get_container(GameP& game, CardP& card, String key_name, bool ignore_field_not_found) {
|
||||
inline static Value* get_card_field_container(Game& game, IndexMap<FieldP, ValueP>& map, String& key_name, bool ignore_field_not_found) {
|
||||
// find value container to update
|
||||
IndexMap<FieldP, ValueP>::const_iterator value_it = card->data.find(key_name);
|
||||
if (value_it == card->data.end()) {
|
||||
IndexMap<FieldP, ValueP>::const_iterator it = map.find(key_name);
|
||||
if (it == map.end()) {
|
||||
// look among alternate names
|
||||
map<String, String>::iterator alt_name_it = game->card_fields_alt_names.find(unified_form(key_name));
|
||||
if (alt_name_it != game->card_fields_alt_names.end()) {
|
||||
value_it = card->data.find(alt_name_it->second);
|
||||
std::map<String, String>::iterator alt_name_it = game.card_fields_alt_names.find(unified_form(key_name));
|
||||
if (alt_name_it != game.card_fields_alt_names.end()) {
|
||||
it = map.find(alt_name_it->second);
|
||||
}
|
||||
}
|
||||
if (value_it == card->data.end()) {
|
||||
if (it == map.end()) {
|
||||
if (ignore_field_not_found) return nullptr;
|
||||
throw ScriptError(_ERROR_1_("no field with name", key_name));
|
||||
throw ScriptError(_ERROR_2_("no field with name", _TYPE_("card"), key_name));
|
||||
}
|
||||
return value_it->get();
|
||||
return it->get();
|
||||
}
|
||||
|
||||
static void set_container(Value* container, ScriptValueP& value, String key_name) {
|
||||
inline static Value* get_container(IndexMap<FieldP, ValueP>& map, String& type, String& key_name, bool ignore_field_not_found) {
|
||||
// find value container to update
|
||||
IndexMap<FieldP, ValueP>::const_iterator it = map.find(key_name);
|
||||
if (it == map.end()) {
|
||||
it = map.find(key_name.Lower());
|
||||
if (it == map.end()) {
|
||||
if (ignore_field_not_found) return nullptr;
|
||||
throw ScriptError(_ERROR_2_("no field with name", _TYPE_V_(type), key_name));
|
||||
}
|
||||
}
|
||||
return it->get();
|
||||
}
|
||||
|
||||
inline static void set_container(Value* container, ScriptValueP& value, String key_name) {
|
||||
// set the given value into the container
|
||||
if (TextValue* tvalue = dynamic_cast<TextValue*>(container)) {
|
||||
tvalue->value = value->toString();
|
||||
@@ -53,23 +68,36 @@ static void set_container(Value* container, ScriptValueP& value, String key_name
|
||||
cvalue->value = value->toColor();
|
||||
}
|
||||
else if (ImageValue* ivalue = dynamic_cast<ImageValue*>(container)) {
|
||||
wxFileName fname(static_cast<ExternalImage*>(value.get())->toString());
|
||||
ivalue->filename = LocalFileName::fromReadString(fname.GetName(), "");
|
||||
if (ExternalImage* image = dynamic_cast<ExternalImage*>(value.get())) {
|
||||
wxFileName fname(image->toString());
|
||||
ivalue->filename = LocalFileName::fromReadString(fname.GetName(), "");
|
||||
} else if (value->type() == SCRIPT_STRING) {
|
||||
ivalue->filename = LocalFileName::fromReadString(value->toString(), "");
|
||||
} else {
|
||||
throw ScriptError(_ERROR_1_("cant set image value", key_name));
|
||||
}
|
||||
}
|
||||
else if (SymbolValue* svalue = dynamic_cast<SymbolValue*>(container)) {
|
||||
if (value->type() == SCRIPT_STRING) {
|
||||
svalue->filename = LocalFileName::fromReadString(value->toString(), "");
|
||||
} else {
|
||||
throw ScriptError(_ERROR_1_("cant set symbol value", key_name));
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw ScriptError(_ERROR_1_("can't set value", key_name));
|
||||
throw ScriptError(_ERROR_1_("cant set value", key_name));
|
||||
}
|
||||
}
|
||||
|
||||
static bool set_builtin_container(GameP& game, CardP& card, ScriptValueP& value, String key_name, bool ignore_field_not_found) {
|
||||
inline static bool set_builtin_container(const Game& game, CardP& card, ScriptValueP& value, String key_name, bool ignore_field_not_found) {
|
||||
// check if the given value is for a built-in field, if found set it and return true
|
||||
key_name = unified_form(key_name);
|
||||
if (key_name == _("notes") || key_name == _("note")) {
|
||||
card->notes = value->toString();
|
||||
return true;
|
||||
} else if (key_name == _("style") || key_name == _("stylesheet") || key_name == _("template")) {
|
||||
if (trim(value->toString()) != wxEmptyString) {
|
||||
card->stylesheet = StyleSheet::byGameAndName(*game, value->toString());
|
||||
if (!trim(value->toString()).empty()) {
|
||||
card->stylesheet = StyleSheet::byGameAndName(game, value->toString());
|
||||
if (card->stylesheet) card->styling_data.init(card->stylesheet->styling_fields);
|
||||
}
|
||||
return true;
|
||||
@@ -110,35 +138,34 @@ static bool set_builtin_container(GameP& game, CardP& card, ScriptValueP& value,
|
||||
// card->linked_relation_4 = value->toString();
|
||||
// return true;
|
||||
//}
|
||||
else if (key_name == _("styling_data") || key_name == _("style_data") || key_name == _("stylesheet_data") || key_name == _("template_data") || key_name == _("styling")
|
||||
|| key_name == _("styling_fields") || key_name == _("style_fields") || key_name == _("stylesheet_fields") || key_name == _("template_fields")) {
|
||||
else if (key_name == _("styling_data") || key_name == _("style_data") || key_name == _("stylesheet_data") || key_name == _("template_data") || key_name == _("styling")
|
||||
|| key_name == _("styling_fields") || key_name == _("style_fields") || key_name == _("stylesheet_fields") || key_name == _("template_fields")
|
||||
|| key_name == _("extra_data") || key_name == _("extra_fields") || key_name == _("extra_card_data") || key_name == _("extra_card_fields")) {
|
||||
bool is_extra = key_name == _("extra_data") || key_name == _("extra_fields") || key_name == _("extra_card_data") || key_name == _("extra_card_fields");
|
||||
String type = is_extra ? _("extra") : _("styling");
|
||||
if (value->type() != SCRIPT_COLLECTION) {
|
||||
throw ScriptError(_ERROR_("styling data not map"));
|
||||
throw ScriptError(_ERROR_1_("styling data not map", type));
|
||||
}
|
||||
ScriptValueP value_it = value->makeIterator();
|
||||
ScriptValueP value_key;
|
||||
while (ScriptValueP value_value = value_it->next(&value_key)) {
|
||||
assert(value_key);
|
||||
if (value_key == script_nil) continue;
|
||||
String value_key_name = value_key->toString();
|
||||
IndexMap<FieldP, ValueP>::const_iterator style_it = card->styling_data.find(value_key_name);
|
||||
if (style_it == card->styling_data.end()) {
|
||||
style_it = card->styling_data.find(value_key_name.Lower());
|
||||
if (style_it == card->styling_data.end()) {
|
||||
if (!ignore_field_not_found) throw ScriptError(_ERROR_1_("no style field with name", value_key_name));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Value* value_container = style_it->get();
|
||||
set_container(value_container, value_value, value_key_name);
|
||||
card->has_styling = true;
|
||||
if (!card->stylesheet) {
|
||||
throw ScriptError(_ERROR_1_("styling data without stylesheet", type));
|
||||
}
|
||||
IndexMap<FieldP, ValueP>& data = is_extra ? card->extraDataFor(*card->stylesheet) : card->styling_data;
|
||||
ScriptValueP it = value->makeIterator();
|
||||
ScriptValueP key;
|
||||
while (ScriptValueP value = it->next(&key)) {
|
||||
assert(key);
|
||||
if (key == script_nil) continue;
|
||||
String key_name = key->toString();
|
||||
Value* container = get_container(data, type, key_name, ignore_field_not_found);
|
||||
set_container(container, value, key_name);
|
||||
if (!is_extra) card->has_styling = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool check_table_headers(GameP& game, std::vector<String>& headers, const String& file_extension, String& missing_fields_out) {
|
||||
inline static bool check_table_headers(GameP& game, std::vector<String>& headers, const String& file_extension, String& missing_fields_out) {
|
||||
if (headers.empty()) {
|
||||
queue_message(MESSAGE_ERROR, _("Empty headers given"));
|
||||
return false;
|
||||
@@ -176,7 +203,7 @@ static bool check_table_headers(GameP& game, std::vector<String>& headers, const
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cards_from_table(SetP& set, vector<String>& headers, std::vector<std::vector<ScriptValueP>>& table, bool ignore_field_not_found, const String& file_extension, vector<CardP>& cards_out) {
|
||||
inline static bool cards_from_table(SetP& set, vector<String>& headers, std::vector<std::vector<ScriptValueP>>& table, bool ignore_field_not_found, const String& file_extension, vector<CardP>& cards_out) {
|
||||
// ensure table is square
|
||||
int count = headers.size();
|
||||
for (int y = 0; y < table.size(); ++y) {
|
||||
@@ -212,5 +239,3 @@ static bool cards_from_table(SetP& set, vector<String>& headers, std::vector<std
|
||||
if (ctx_ignore) ctx.setVariable("ignore_field_not_found", ctx_ignore);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,596 @@
|
||||
//+----------------------------------------------------------------------------+
|
||||
//| 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) |
|
||||
//+----------------------------------------------------------------------------+
|
||||
|
||||
#pragma once
|
||||
|
||||
// ----------------------------------------------------------------------------- : Includes
|
||||
|
||||
#include <util/prec.hpp>
|
||||
#include <data/set.hpp>
|
||||
#include <data/card.hpp>
|
||||
#include <data/pack.hpp>
|
||||
#include <data/format/clipboard.hpp>
|
||||
#include <script/functions/construction_helper.hpp>
|
||||
#include <boost/json.hpp>
|
||||
#include <sstream>
|
||||
#include <wx/sstream.h>
|
||||
|
||||
|
||||
// All this isn't great, but it will do for now. Idealy you would create JsonWriter and JsonReader classes
|
||||
// that inherit from Writer and Reader, and just have a switch to go from normal mode to JSON mode...
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------- : JSON to String
|
||||
|
||||
inline static void pretty_print(std::ostream& os, boost::json::value const& jv, std::string* indent = nullptr)
|
||||
{
|
||||
std::string indent_;
|
||||
if(! indent)
|
||||
indent = &indent_;
|
||||
switch(jv.kind())
|
||||
{
|
||||
case boost::json::kind::object:
|
||||
{
|
||||
os << "{\n";
|
||||
indent->append(4, ' ');
|
||||
auto const& obj = jv.get_object();
|
||||
if(! obj.empty())
|
||||
{
|
||||
auto it = obj.begin();
|
||||
for(;;)
|
||||
{
|
||||
os << *indent << boost::json::serialize(it->key()) << " : ";
|
||||
pretty_print(os, it->value(), indent);
|
||||
if(++it == obj.end())
|
||||
break;
|
||||
os << ",\n";
|
||||
}
|
||||
}
|
||||
os << "\n";
|
||||
indent->resize(indent->size() - 4);
|
||||
os << *indent << "}";
|
||||
break;
|
||||
}
|
||||
|
||||
case boost::json::kind::array:
|
||||
{
|
||||
os << "[\n";
|
||||
indent->append(4, ' ');
|
||||
auto const& arr = jv.get_array();
|
||||
if(! arr.empty())
|
||||
{
|
||||
auto it = arr.begin();
|
||||
for(;;)
|
||||
{
|
||||
os << *indent;
|
||||
pretty_print( os, *it, indent);
|
||||
if(++it == arr.end())
|
||||
break;
|
||||
os << ",\n";
|
||||
}
|
||||
}
|
||||
os << "\n";
|
||||
indent->resize(indent->size() - 4);
|
||||
os << *indent << "]";
|
||||
break;
|
||||
}
|
||||
|
||||
case boost::json::kind::string:
|
||||
{
|
||||
os << boost::json::serialize(jv.get_string());
|
||||
break;
|
||||
}
|
||||
|
||||
case boost::json::kind::uint64:
|
||||
case boost::json::kind::int64:
|
||||
case boost::json::kind::double_:
|
||||
os << jv;
|
||||
break;
|
||||
|
||||
case boost::json::kind::bool_:
|
||||
if(jv.get_bool())
|
||||
os << "true";
|
||||
else
|
||||
os << "false";
|
||||
break;
|
||||
|
||||
case boost::json::kind::null:
|
||||
os << "null";
|
||||
break;
|
||||
}
|
||||
|
||||
if(indent->empty())
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
inline static String json_pretty_print(boost::json::value const& jv, std::string* indent = nullptr) {
|
||||
std::ostringstream stream;
|
||||
pretty_print(stream, jv, indent);
|
||||
String string = wxString(stream.str().c_str());
|
||||
return string;
|
||||
}
|
||||
|
||||
inline static String json_ugly_print(boost::json::value const& jv) {
|
||||
String string = wxString(boost::json::serialize(jv).c_str());
|
||||
return string;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : JSON to MSE
|
||||
|
||||
inline static ScriptValueP json_to_mse(boost::json::value& jv, Set* set);
|
||||
|
||||
template <typename T>
|
||||
static 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);
|
||||
}
|
||||
}
|
||||
|
||||
// templates don't work with enums? are you kidding me with this language?
|
||||
static 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);
|
||||
}
|
||||
}
|
||||
|
||||
inline static PackItemP json_to_mse_pack_item(boost::json::object& jv) {
|
||||
PackItemP pack_item = make_intrusive<PackItem>();
|
||||
read(pack_item->name, jv, "name");
|
||||
read(pack_item->amount, jv, "amount");
|
||||
read(pack_item->weight, jv, "weight");
|
||||
return pack_item;
|
||||
}
|
||||
|
||||
inline static PackTypeP json_to_mse_pack_type(boost::json::object& jv) {
|
||||
PackTypeP pack_type = make_intrusive<PackType>();
|
||||
read(pack_type->name, jv, "name");
|
||||
read(pack_type->enabled, jv, "enabled");
|
||||
read(pack_type->selectable, jv, "selectable");
|
||||
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();
|
||||
for (int 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));
|
||||
}
|
||||
}
|
||||
return pack_type;
|
||||
}
|
||||
|
||||
inline static KeywordP json_to_mse_keyword(boost::json::object& jv) {
|
||||
KeywordP keyword = make_intrusive<Keyword>();
|
||||
read(keyword->keyword, jv, "keyword");
|
||||
read(keyword->match, jv, "match");
|
||||
read(keyword->reminder, jv, "reminder");
|
||||
read(keyword->rules, jv, "rules");
|
||||
read(keyword->mode, jv, "mode");
|
||||
return keyword;
|
||||
}
|
||||
|
||||
inline static CardP json_to_mse_card(boost::json::object& jv, Set* set) {
|
||||
CardP card = make_intrusive<Card>(*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");
|
||||
// card fields
|
||||
if (jv.contains("data") && jv["data"].is_object()) {
|
||||
boost::json::object datav = jv["data"].as_object();
|
||||
for (auto it = datav.begin(); it != datav.end(); ++it) {
|
||||
String key_name = wxString(it->key_c_str());
|
||||
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 (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();
|
||||
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);
|
||||
ScriptValueP value = json_to_mse(it->value(), set);
|
||||
set_container(container, value, key_name);
|
||||
card->has_styling = true;
|
||||
}
|
||||
}
|
||||
// extra card fields
|
||||
if (jv.contains("extra_data") && jv["extra_data"].is_object()) {
|
||||
boost::json::object datav = jv["extra_data"].as_object();
|
||||
for (auto it = datav.begin(); it != datav.end(); ++it) {
|
||||
StyleSheetP& stylesheet = StyleSheet::byGameAndName(*set->game, it->key_c_str());
|
||||
if (!stylesheet) continue;
|
||||
IndexMap<FieldP, ValueP>& 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);
|
||||
ScriptValueP value = json_to_mse(stylesheet_it->value(), set);
|
||||
set_container(container, value, key_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return card;
|
||||
}
|
||||
|
||||
inline static SetP json_to_mse_set(boost::json::object& jv) {
|
||||
if (!jv.contains("game")) {
|
||||
throw ScriptError(_ERROR_("json set without game"));
|
||||
}
|
||||
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()));
|
||||
SetP& set = make_intrusive<Set>(stylesheet);
|
||||
// set fields
|
||||
if (jv.contains("set_info") && jv["set_info"].is_object()) {
|
||||
boost::json::object datav = jv["set_info"].as_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);
|
||||
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();
|
||||
for (auto it = datav.begin(); it != datav.end(); ++it) {
|
||||
StyleSheetP& stylesheet = StyleSheet::byGameAndName(*set->game, it->key_c_str());
|
||||
if (!stylesheet) continue;
|
||||
IndexMap<FieldP, ValueP>& 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);
|
||||
ScriptValueP value = json_to_mse(stylesheet_it->value(), set.get());
|
||||
set_container(container, value, key_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
// cards
|
||||
if (jv.contains("cards") && jv["cards"].is_array()) {
|
||||
boost::json::array cardsv = jv["cards"].as_array();
|
||||
for (int 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()));
|
||||
}
|
||||
}
|
||||
// keywords
|
||||
if (jv.contains("keywords") && jv["keywords"].is_array()) {
|
||||
boost::json::array keywordsv = jv["keywords"].as_array();
|
||||
for (int i = 0; i < keywordsv.size(); i++) {
|
||||
boost::json::object keywordv = keywordsv[i].as_object();
|
||||
set->keywords.emplace_back(json_to_mse_keyword(keywordv));
|
||||
}
|
||||
}
|
||||
// pack types
|
||||
if (jv.contains("pack_types") && jv["pack_types"].is_array()) {
|
||||
boost::json::array pack_typesv = jv["pack_types"].as_array();
|
||||
for (int 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));
|
||||
}
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
inline static ScriptValueP json_to_mse(boost::json::value& jv, Set* set) {
|
||||
if (jv == nullptr) return script_nil;
|
||||
else if (jv.is_null()) return script_nil;
|
||||
else if (jv.is_bool()) return to_script(jv.get_bool());
|
||||
else if (jv.is_double()) return to_script(jv.get_double());
|
||||
else if (jv.is_int64()) {
|
||||
int integer = jv.get_int64();
|
||||
return to_script(integer);
|
||||
}
|
||||
else if (jv.is_uint64()) {
|
||||
int integer = jv.get_uint64();
|
||||
return to_script(integer);
|
||||
}
|
||||
else if (jv.is_string()) {
|
||||
std::string stdstring = boost::json::value_to<std::string>(jv);
|
||||
String wxstring(stdstring.c_str(), wxConvUTF8);
|
||||
return to_script(wxstring);
|
||||
}
|
||||
else if (jv.is_array()) {
|
||||
boost::json::array array = jv.get_array();
|
||||
ScriptCustomCollectionP result = make_intrusive<ScriptCustomCollection>();
|
||||
for (int i = 0; i < array.size(); ++i) {
|
||||
boost::json::value jvalue = array[i];
|
||||
ScriptValueP value = json_to_mse(jvalue, set);
|
||||
result->value.push_back(value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else if (jv.is_object()) {
|
||||
boost::json::object object = jv.get_object();
|
||||
if (object.contains("mse_object_type")) {
|
||||
boost::json::string mse_object_type = object["mse_object_type"].as_string();
|
||||
if (mse_object_type == "set") return make_intrusive<ScriptObject<SetP>> (json_to_mse_set(object));
|
||||
if (mse_object_type == "card") return make_intrusive<ScriptObject<CardP>> (json_to_mse_card(object, set));
|
||||
if (mse_object_type == "keyword") return make_intrusive<ScriptObject<KeywordP>> (json_to_mse_keyword(object));
|
||||
if (mse_object_type == "pack_type") return make_intrusive<ScriptObject<PackTypeP>>(json_to_mse_pack_type(object));
|
||||
if (mse_object_type == "pack_item") return make_intrusive<ScriptObject<PackItemP>>(json_to_mse_pack_item(object));
|
||||
queue_message(MESSAGE_ERROR, _ERROR_("json unknown type") + _("(") + wxString(mse_object_type.c_str()) + _(")"));
|
||||
return script_nil;
|
||||
}
|
||||
ScriptCustomCollectionP result = make_intrusive<ScriptCustomCollection>();
|
||||
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);
|
||||
boost::json::value jvalue = it->value();
|
||||
ScriptValueP value = json_to_mse(jvalue, set);
|
||||
result->key_value[key] = value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
queue_message(MESSAGE_ERROR, _ERROR_("json unknown type"));
|
||||
return script_nil;
|
||||
}
|
||||
}
|
||||
inline static ScriptValueP json_to_mse(String& string, Set* set) {
|
||||
try {
|
||||
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());
|
||||
return json_to_mse(jv, set);
|
||||
}
|
||||
catch (...) {
|
||||
queue_message(MESSAGE_ERROR, _ERROR_("json cant parse"));
|
||||
return script_nil;
|
||||
}
|
||||
}
|
||||
inline static ScriptValueP json_to_mse(ScriptValueP& sv, Set* set) {
|
||||
try {
|
||||
String string = sv->toString();
|
||||
return json_to_mse(string, set);
|
||||
}
|
||||
catch (...) {
|
||||
queue_message(MESSAGE_ERROR, _ERROR_("json cant convert"));
|
||||
return script_nil;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : MSE to JSON
|
||||
|
||||
template <typename T>
|
||||
static void write(boost::json::object& out, const String& name, T& value) {
|
||||
wxStringOutputStream stream;
|
||||
Writer writer(stream);
|
||||
writer.indentation = -1000;
|
||||
writer.handle(name, value);
|
||||
String string = stream.GetString();
|
||||
if (!string.empty()) {
|
||||
if (string.StartsWith(name + ":")) string = string.substr(name.length() + 1).Trim(false);
|
||||
if (string.EndsWith("\n")) string = string.substr(0, string.length() - 1);
|
||||
out.emplace(name.ToStdString(), string);
|
||||
}
|
||||
}
|
||||
|
||||
static void write(boost::json::object& out, const String& name, IndexMap<FieldP, ValueP>& map) {
|
||||
boost::json::object indexmapv;
|
||||
for (IndexMap<FieldP, ValueP>::iterator it = map.begin(); it != map.end(); ++it) {
|
||||
write(indexmapv, (*it)->fieldP->name, *it);
|
||||
}
|
||||
if (!indexmapv.empty()) out.emplace(name.ToStdString(), indexmapv);
|
||||
}
|
||||
|
||||
static void write(boost::json::object& out, const String& name, DelayedIndexMaps<FieldP,ValueP>& map) {
|
||||
boost::json::object delayedindexmapv;
|
||||
for (auto it = map.data.begin() ; it != map.data.end() ; ++it) {
|
||||
write(delayedindexmapv, it->first, it->second->read_data);
|
||||
}
|
||||
if (!delayedindexmapv.empty()) out.emplace(name.ToStdString(), delayedindexmapv);
|
||||
}
|
||||
|
||||
inline static boost::json::object mse_to_json(PackItemP& item) {
|
||||
boost::json::object itemv;
|
||||
itemv.emplace("mse_object_type", "pack_item");
|
||||
write(itemv, "name", item->name);
|
||||
write(itemv, "amount", item->amount);
|
||||
write(itemv, "weight", item->weight);
|
||||
return itemv;
|
||||
}
|
||||
|
||||
inline static boost::json::object mse_to_json(PackTypeP& pack) {
|
||||
boost::json::object packv;
|
||||
packv.emplace("mse_object_type", "pack_type");
|
||||
write(packv, "name", pack->name);
|
||||
write(packv, "enabled", pack->enabled);
|
||||
write(packv, "selectable", pack->selectable);
|
||||
write(packv, "summary", pack->summary);
|
||||
write(packv, "select", pack->select);
|
||||
write(packv, "filter", pack->filter);
|
||||
boost::json::array itemsv;
|
||||
for (auto item : pack->items) {
|
||||
itemsv.emplace_back(mse_to_json(item));
|
||||
}
|
||||
packv.emplace("items", itemsv);
|
||||
return packv;
|
||||
}
|
||||
|
||||
inline static boost::json::object mse_to_json(KeywordP& keyword) {
|
||||
boost::json::object keywordv;
|
||||
keywordv.emplace("mse_object_type", "keyword");
|
||||
write(keywordv, "keyword", keyword->keyword);
|
||||
write(keywordv, "match", keyword->match);
|
||||
write(keywordv, "reminder", keyword->reminder);
|
||||
write(keywordv, "rules", keyword->rules);
|
||||
write(keywordv, "mode", keyword->mode);
|
||||
return keywordv;
|
||||
}
|
||||
|
||||
inline static boost::json::object mse_to_json(CardP& card, Set* set) {
|
||||
boost::json::object cardv;
|
||||
cardv.emplace("mse_object_type", "card");
|
||||
// built-in values
|
||||
write(cardv, "time_created", card->time_created);
|
||||
write(cardv, "time_modified", card->time_modified);
|
||||
write(cardv, "notes", card->notes);
|
||||
//write(cardv, "uid", card->uid);
|
||||
//write(cardv, "linked_card_1", card->linked_card_1);
|
||||
//write(cardv, "linked_card_2", card->linked_card_2);
|
||||
//write(cardv, "linked_card_3", card->linked_card_3);
|
||||
//write(cardv, "linked_card_4", card->linked_card_4);
|
||||
//write(cardv, "linked_relation_1", card->linked_relation_1);
|
||||
//write(cardv, "linked_relation_2", card->linked_relation_2);
|
||||
//write(cardv, "linked_relation_3", card->linked_relation_3);
|
||||
//write(cardv, "linked_relation_4", card->linked_relation_4);
|
||||
// card fields
|
||||
write(cardv, "data", card->data);
|
||||
// stylesheet
|
||||
bool change_stylesheet = set && !card->stylesheet;
|
||||
if (change_stylesheet) {
|
||||
card->stylesheet = set->stylesheet;
|
||||
}
|
||||
if (card->stylesheet) {
|
||||
write(cardv, "stylesheet", card->stylesheet);
|
||||
write(cardv, "stylesheet_version", card->stylesheet->version);
|
||||
// extra card fields
|
||||
write(cardv, "extra_data", card->extra_data);
|
||||
}
|
||||
// style
|
||||
write(cardv, "has_styling", card->has_styling);
|
||||
if (card->has_styling) {
|
||||
write(cardv, "styling_data", card->styling_data);
|
||||
}
|
||||
// restore stylesheet
|
||||
if (change_stylesheet) {
|
||||
card->stylesheet = StyleSheetP();
|
||||
}
|
||||
// done
|
||||
return cardv;
|
||||
}
|
||||
|
||||
inline static boost::json::object mse_to_json(Set* set) {
|
||||
boost::json::object setv;
|
||||
setv.emplace("mse_object_type", "set");
|
||||
// built-in values
|
||||
write(setv, "mse_version", set->fileVersion());
|
||||
write(setv, "game", set->game);
|
||||
write(setv, "game_version", set->game->version);
|
||||
write(setv, "stylesheet", set->stylesheet);
|
||||
write(setv, "stylesheet_version", set->stylesheet->version);
|
||||
// set fields
|
||||
write(setv, "set_info", set->data);
|
||||
// styling
|
||||
write(setv, "styling", set->styling_data);
|
||||
// cards
|
||||
boost::json::array cardsv;
|
||||
for (auto card : set->cards) {
|
||||
cardsv.emplace_back(mse_to_json(card, set));
|
||||
}
|
||||
setv.emplace("cards", cardsv);
|
||||
// keywords
|
||||
boost::json::array keywordsv;
|
||||
for (auto keyword : set->keywords) {
|
||||
keywordsv.emplace_back(mse_to_json(keyword));
|
||||
}
|
||||
if (!keywordsv.empty()) setv.emplace("keywords", keywordsv);
|
||||
// pack types
|
||||
boost::json::array pack_typesv;
|
||||
for (auto pack_type : set->pack_types) {
|
||||
pack_typesv.emplace_back(mse_to_json(pack_type));
|
||||
}
|
||||
if (!pack_typesv.empty()) setv.emplace("pack_types", pack_typesv);
|
||||
// done
|
||||
return setv;
|
||||
}
|
||||
|
||||
inline static boost::json::value mse_to_json(ScriptValueP& sv, Set* set) {
|
||||
ScriptType type = sv->type();
|
||||
// special types
|
||||
if (ScriptObject<PackItemP>* i = dynamic_cast<ScriptObject<PackItemP>*>(sv.get())) return mse_to_json(i->getValue());
|
||||
if (ScriptObject<PackTypeP>* t = dynamic_cast<ScriptObject<PackTypeP>*>(sv.get())) return mse_to_json(t->getValue());
|
||||
if (ScriptObject<KeywordP>* k = dynamic_cast<ScriptObject<KeywordP>*> (sv.get())) return mse_to_json(k->getValue());
|
||||
if (ScriptObject<CardP>* c = dynamic_cast<ScriptObject<CardP>*> (sv.get())) return mse_to_json(c->getValue(), set);
|
||||
if (ScriptObject<SetP>* z = dynamic_cast<ScriptObject<SetP>*> (sv.get())) return mse_to_json(z->getValue().get());
|
||||
if (ScriptObject<Set*>* s = dynamic_cast<ScriptObject<Set*>*> (sv.get())) return mse_to_json(s->getValue());
|
||||
|
||||
// primitive types
|
||||
if (type == SCRIPT_NIL) return boost::json::value(nullptr);
|
||||
if (type == SCRIPT_INT) return boost::json::value(sv->toInt());
|
||||
if (type == SCRIPT_DOUBLE) return boost::json::value(sv->toDouble());
|
||||
if (type == SCRIPT_BOOL) return boost::json::value(sv->toBool());
|
||||
if (type == SCRIPT_STRING) return boost::json::value(sv->toString());
|
||||
if (type == SCRIPT_REGEX) return boost::json::value(sv->toString());
|
||||
if (type == SCRIPT_COLOR) return boost::json::value(format_color(sv->toColor()));
|
||||
if (type == SCRIPT_DATETIME) return boost::json::value(sv->toDateTime().FormatISOCombined(' '));
|
||||
if (type == SCRIPT_COLLECTION) {
|
||||
ScriptCustomCollection* custom = dynamic_cast<ScriptCustomCollection*>(sv.get());
|
||||
if (custom) {
|
||||
if (custom->value.size() > 0) {
|
||||
boost::json::array array;
|
||||
for (int i = 0; i < custom->value.size(); i++) {
|
||||
array.emplace_back(mse_to_json(custom->value[i], set));
|
||||
}
|
||||
return array;
|
||||
} else if (custom->key_value.size() > 0) {
|
||||
boost::json::object object;
|
||||
map<String, ScriptValueP>::iterator it;
|
||||
for (it = custom->key_value.begin(); it != custom->key_value.end(); it++) {
|
||||
object.emplace(it->first.ToStdString(), mse_to_json(it->second, set));
|
||||
}
|
||||
return object;
|
||||
}
|
||||
} else {
|
||||
ScriptConcatCollection* concat = dynamic_cast<ScriptConcatCollection*>(sv.get());
|
||||
if (concat) {
|
||||
boost::json::value a = mse_to_json(concat->getA(), set);
|
||||
boost::json::value b = mse_to_json(concat->getB(), set);
|
||||
if (a.is_array() && b.is_array()) {
|
||||
boost::json::array array_a = a.get_array();
|
||||
boost::json::array array_b = b.get_array();
|
||||
for (int i = 0; i < array_b.size(); i++) {
|
||||
array_a.emplace_back(array_b[i]);
|
||||
}
|
||||
return array_a;
|
||||
} else if (a.is_object() && b.is_object()) {
|
||||
boost::json::object object_a = a.get_object();
|
||||
boost::json::object object_b = b.get_object();
|
||||
for (auto it = object_b.begin(); it != object_b.end(); ++it) {
|
||||
object_a.emplace(it->key(), it->value());
|
||||
}
|
||||
return object_a;
|
||||
} else {
|
||||
queue_message(MESSAGE_ERROR, _ERROR_("json cant concat"));
|
||||
return boost::json::value(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
queue_message(MESSAGE_ERROR, _ERROR_1_("json unknown script type", sv->typeName()));
|
||||
return boost::json::value(nullptr);
|
||||
}
|
||||
@@ -223,7 +223,10 @@ public:
|
||||
inline ScriptConcatCollection(ScriptValueP a, ScriptValueP b) : a(a), b(b) {}
|
||||
ScriptValueP getMember(const String& name) const override;
|
||||
ScriptValueP getIndex(int index) const override;
|
||||
ScriptValueP makeIterator() const override;
|
||||
ScriptValueP makeIterator() const override;
|
||||
ScriptValueP getA() { return a; }
|
||||
ScriptValueP getB() { return b; }
|
||||
|
||||
int itemCount() const override { return a->itemCount() + b->itemCount(); }
|
||||
/// Collections can be compared by comparing pointers
|
||||
CompareWhat compareAs(String&, void const*& compare_ptr) const override {
|
||||
|
||||
@@ -149,8 +149,8 @@ public:
|
||||
IndexMap<Key,Value>& get(const String& name, const vector<Key>& init_with);
|
||||
/// Clear the delayed index map
|
||||
void clear();
|
||||
private:
|
||||
map<String, intrusive_ptr<DelayedIndexMapsData<Key,Value>>> data;
|
||||
private:
|
||||
friend class Reader;
|
||||
friend class Writer;
|
||||
friend class GetDefaultMember;
|
||||
|
||||
@@ -118,6 +118,9 @@ public:
|
||||
inline Packaged* getPackage() const { return package; }
|
||||
|
||||
String addLocale(String);
|
||||
|
||||
/// Set the value that will be returned by the next getValue() call (may mess up the state of the reader)
|
||||
inline void setValue(const String& value) { state = UNHANDLED; previous_value = value; };
|
||||
|
||||
private:
|
||||
// --------------------------------------------------- : Data
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
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)
|
||||
|
||||
@@ -25,8 +25,9 @@ DECLARE_POINTER_TYPE(StyleSheet);
|
||||
class Writer {
|
||||
public:
|
||||
/// Construct a writer that writes to the given output stream
|
||||
Writer(OutputStream& output);
|
||||
Writer(OutputStream& output, Version file_app_version);
|
||||
|
||||
|
||||
/// Tell the reflection code we are not reading
|
||||
static constexpr bool isReading = false;
|
||||
static constexpr bool isWriting = true;
|
||||
@@ -72,11 +73,11 @@ public:
|
||||
// special behaviour
|
||||
void handle(const GameP&);
|
||||
void handle(const StyleSheetP&);
|
||||
|
||||
private:
|
||||
// --------------------------------------------------- : Data
|
||||
|
||||
/// Indentation of the current block
|
||||
int indentation;
|
||||
private:
|
||||
// --------------------------------------------------- : Data
|
||||
/// Blocks opened to which nothing has been written
|
||||
vector<const Char*> pending_opened;
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ String tr(const String&, const String& subcat, const String& key, DefaultLocaleF
|
||||
#define _TITLE_(s) tr(LOCALE_CAT_TITLE, _(s))
|
||||
/// A localized string for type names in scripts
|
||||
#define _TYPE_(s) tr(LOCALE_CAT_TYPE, _(s))
|
||||
#define _TYPE_V_(s) tr(LOCALE_CAT_TYPE, s )
|
||||
/// A localized string for action names
|
||||
#define _ACTION_(s) tr(LOCALE_CAT_ACTION, _(s))
|
||||
/// A localized string for error messages
|
||||
|
||||
Reference in New Issue
Block a user