From 9ddc690d686f6efc4b10f38749dd00b870cc7f5f Mon Sep 17 00:00:00 2001 From: twanvl Date: Mon, 18 Dec 2006 23:06:43 +0000 Subject: [PATCH] mtg editor import git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@118 0fc631ac-6414-0410-93d0-97cfa31319b6 --- src/data/format/formats.hpp | 12 ++ src/data/format/mse1.cpp | 2 +- src/data/format/mtg_editor.cpp | 241 ++++++++++++++++++++++++++++++++- src/script/scriptable.hpp | 2 +- src/util/defaultable.hpp | 2 +- src/util/string.cpp | 6 + src/util/string.hpp | 3 + 7 files changed, 264 insertions(+), 4 deletions(-) diff --git a/src/data/format/formats.hpp b/src/data/format/formats.hpp index a79e3b87..0d91a4c9 100644 --- a/src/data/format/formats.hpp +++ b/src/data/format/formats.hpp @@ -14,6 +14,7 @@ class Game; DECLARE_POINTER_TYPE(Set); +DECLARE_POINTER_TYPE(Card); DECLARE_POINTER_TYPE(FileFormat); // ----------------------------------------------------------------------------- : FileFormat @@ -78,5 +79,16 @@ FileFormatP mse1_file_format(); FileFormatP mse2_file_format(); FileFormatP mtg_editor_file_format(); +// ----------------------------------------------------------------------------- : Other ways to export + +/// Export images for each card in a set to a list of files +void export_images(Window* parent, const SetP& set); + +/// Export the image of a single card +void export_image(const SetP& set, const CardP& card, const String& filename); + +/// Generate a bitmap image of a card +Bitmap export_bitmap(const SetP& set, const CardP& card); + // ----------------------------------------------------------------------------- : EOF #endif diff --git a/src/data/format/mse1.cpp b/src/data/format/mse1.cpp index 5fb4b3e2..b1750b1e 100644 --- a/src/data/format/mse1.cpp +++ b/src/data/format/mse1.cpp @@ -75,7 +75,7 @@ SetP MSE1FileFormat::importSet(const String& filename) { if (stylesheet.substr(0,3) == _("old")) { try { set->stylesheet = StyleSheet::byGameAndName(*set->game, _("old")); - } catch (Error) { + } catch (const Error&) { // If old style doesn't work try the new one set->stylesheet = StyleSheet::byGameAndName(*set->game, _("new")); } diff --git a/src/data/format/mtg_editor.cpp b/src/data/format/mtg_editor.cpp index 15b2641c..ff34d703 100644 --- a/src/data/format/mtg_editor.cpp +++ b/src/data/format/mtg_editor.cpp @@ -7,6 +7,17 @@ // ----------------------------------------------------------------------------- : Includes #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_TYPEOF_COLLECTION(CardP); // ----------------------------------------------------------------------------- : MtgEditorFileFormat @@ -18,6 +29,17 @@ class MtgEditorFileFormat : public FileFormat { virtual bool canImport() { return true; } virtual bool canExport(const Game&) { return false; } virtual SetP importSet(const String& filename); + private: + // Filter: se filename -> image directory + // based on MtgEditor's: CardSet.getImageFolder + String filter1(const String& str); + // Filter: card name -> image name + // based on MtgEditor's: Tools::purgeSpecialChars + String filter2(const String& str); + // Remove mtg editor tags + void untag(const CardP& card, const String& field); + // Translate all tags, mana tags get converted to , other tags are removed + void translateTags(String& value); }; FileFormatP mtg_editor_file_format() { @@ -27,5 +49,222 @@ FileFormatP mtg_editor_file_format() { // ----------------------------------------------------------------------------- : Importing SetP MtgEditorFileFormat::importSet(const String& filename) { - return SetP();//TODO + wxFileInputStream f(filename); + #ifdef UNICODE + wxTextInputStream file(f, _('\n'), wxConvLibc); + #else + wxTextInputStream file(f); + #endif + // create set + SetP set(new Set(Game::byName(_("magic")))); + + // parsing state + CardP current_card; + Defaultable* target = nullptr; // value we are currently reading + String layout = _("8e"); + String set_date, card_date; + bool first = true; + + // read file + while (!f.Eof()) { + // read a line + if (!current_card) current_card = new_shared1(*set->game); + String line = file.ReadLine(); + if (line == _("#SET###########")) { // set.title + target = &set->value(_("title")).value; + } else if (line == _("#SETDATE#######")) { // date + // remember date for generation of illustration filename + target = nullptr; + set_date = file.ReadLine(); + } else if (line == _("#SHOWRARITY####") || + line == _("#FONTS#########") || line == _("#COUNT#########")) { // ignore + target = nullptr; + file.ReadLine(); + } else if (line == _("#LAYOUT########")) { // layout type + target = nullptr; + layout = file.ReadLine(); + } else if (line == _("#CARD##########") || line == _("#EOF###########")) { // begin/end of card + if (!first) { + // First [CARD##] indicates only the start of a card, subsequent ones also the end of the previous + // card. We only care about the latter + // remove all tags from all text values + untag(current_card, _("name")); + untag(current_card, _("super type")); + untag(current_card, _("sub type")); + untag(current_card, _("casting cost")); + untag(current_card, _("flavor text")); + untag(current_card, _("illustrator")); + untag(current_card, _("copyright")); + untag(current_card, _("power")); + untag(current_card, _("toughness")); + // translate mtg editor tags to mse2 tags + translateTags(current_card->value(_("rule text")).value.mutate()); + // add the card to the set + set->cards.push_back(current_card); + } + first = false; + current_card = new_shared1(*set->game); + target = ¤t_card->value(_("name")).value; + } else if (line == _("#DATE##########")) { // date + // remember date for generation of illustration filename + target = nullptr; + card_date = file.ReadLine(); + } else if (line == _("#TYPE##########")) { // super type + target = ¤t_card->value(_("super type")).value; + } else if (line == _("#SUBTYPE#######")) { // sub type + target = ¤t_card->value(_("sub type")).value; + } else if (line == _("#COST##########")) { // casting cost + target = ¤t_card->value(_("casting cost")).value; + } else if (line == _("#RARITY########") || line == _("#FREQUENCY#####")) { // rarity + target = 0; + line = file.ReadLine(); + if (line == _("0")) current_card->value(_("rarity")).value.assign(_("common")); + else if (line == _("1")) current_card->value(_("rarity")).value.assign(_("uncommon")); + else current_card->value(_("rarity")).value.assign(_("rare")); + } else if (line == _("#COLOR#########")) { // card color + target = 0; + line = file.ReadLine(); + current_card->value(_("card color")).value.assign(line); + } else if (line == _("#AUTOBG########")) { // card color.isDefault + target = 0; + line = file.ReadLine(); + if (line == _("TRUE")) { + current_card->value(_("card color")).value.makeDefault(); + } + } else if (line == _("#RULETEXT######")) { // rule text + target = ¤t_card->value(_("rule text")).value; + } else if (line == _("#FLAVORTEXT####")) { // flavor text + target = ¤t_card->value(_("flavor text")).value; + } else if (line == _("#ARTIST########")) { // illustrator + target = ¤t_card->value(_("illustrator")).value; + } else if (line == _("#COPYRIGHT#####")) { // copyright + target = ¤t_card->value(_("copyright")).value; + } else if (line == _("#POWER#########")) { // power + target = ¤t_card->value(_("power")).value; + } else if (line == _("#TOUGHNESS#####")) { // toughness + target = ¤t_card->value(_("toughness")).value; + } else if (line == _("#ILLUSTRATION##") || line == _("#ILLUSTRATION8#")) { // image + target = 0; + line = file.ReadLine(); + if (!wxFileExists(line)) { + // based on card name and date + line = filter1(filename) + set_date + _("/") + + filter2(current_card->value(_("name")).value()) + card_date + _(".jpg"); + } + // copy image into set + if (wxFileExists(line)) { + String image_file = set->newFileName(_("image"),_("")); + if (wxCopyFile(line, set->nameOut(image_file), true)) { + current_card->value(_("image")).filename = image_file; + } + } + } else if (line == _("#TOMBSTONE#####")) { // tombstone + target = 0; + line = file.ReadLine(); + current_card->value(_("card symbol")).value.assign( + line==_("TRUE") ? _("tombstone") : _("none") + ); + } else { + // normal text + if (target != 0) { // value of a text field + if (!target->isDefault()) target->mutate() += _("\n"); + target->mutate() += line; + } else { + throw ParseError(_("Error in Mtg Editor file, unexpected text:\n") + line); + } + } + } + + // set defaults for artist and copyright to that of the first card + if (!set->cards.empty()) { + String artist = set->cards[0]->value(_("illustrator")).value(); + String copyright = set->cards[0]->value(_("copyright")) .value(); + set->value(_("artist")) .value.assign(artist); + set->value(_("copyright")).value.assign(copyright); + // which cards have this value? + FOR_EACH(card, set->cards) { + Defaultable& card_artist = card->value(_("illustrator")).value; + Defaultable& card_copyright = card->value(_("copyright")) .value; + if (card_artist() == artist) card_artist.makeDefault(); + if (card_copyright() == copyright) card_copyright.makeDefault(); + } + } + + // Load stylesheet + if (layout != _("8e")) { + set->stylesheet = StyleSheet::byGameAndName(*set->game, _("old")); + } else { + set->stylesheet = StyleSheet::byGameAndName(*set->game, _("new")); + } + + return set; +} + +// ----------------------------------------------------------------------------- : Filtering + +String MtgEditorFileFormat::filter1(const String& str) { + String before, after, ret; + // split path name + size_t pos = str.find_last_of(_("/\\")); + if (pos == String::npos) { + before = _(""); after = str; + } else { + before = str.substr(0, pos + 1); + after = str.substr(pos + 1); + } + // filter + FOR_EACH(c, after) { + if (isAlnum(c)) ret += c; + else ret += _('_'); + } + return before + ret; +} + +String MtgEditorFileFormat::filter2(const String& str) { + String ret; + FOR_EACH_CONST(c, str) { + if (isAlnum(c)) ret += c; + else if (c==_(' ') || c==_('-')) ret += _('_'); + } + return ret; +} + +void MtgEditorFileFormat::untag(const CardP& card, const String& field) { + Defaultable& value = card->value(field).value; + value.assignDontChangeDefault(untag_no_escape(value())); +} + + +void MtgEditorFileFormat::translateTags(String& value) { + // Translate tags + String ret; + size_t pos = 0; + while (pos < value.size()) { + Char c = value.GetChar(pos); + ++pos; + if (c == _('<')) { + String tag; + while (pos < value.size()) { + c = value.GetChar(pos); + ++pos; + if (c == _('>')) break; + else tag += c; + } + tag.MakeUpper(); + unsigned long number; + if (tag==_("W") || tag==_("U") || tag==_("B") || tag==_("R") || tag==_("G") || tag==_("X") || tag==_("Y") || tag==_("Z")) { + ret += _("") + tag + _(""); + } else if (tag.ToULong(&number)) { + ret += _("") + tag + _(""); + } else if (tag==_("T") || tag==_("TAP")) { + ret += _("T"); + } else if (tag==_("THIS")) { + ret += _("~"); + } + } else { + ret += c; + } + } + // Join adjecent symbol sections + value = replace_all(ret, _(""), _("")); } diff --git a/src/script/scriptable.hpp b/src/script/scriptable.hpp index 9da8c39f..48fb764f 100644 --- a/src/script/scriptable.hpp +++ b/src/script/scriptable.hpp @@ -64,7 +64,7 @@ class OptionalScript { template bool invokeOnDefault(Context& ctx, Defaultable& value) const { if (value.isDefault() && invokeOn(ctx, value)) { - value.setDefault(); // restore defaultness + value.makeDefault(); // restore defaultness return true; } else { return false; diff --git a/src/util/defaultable.hpp b/src/util/defaultable.hpp index f5e2eb48..880fb5d1 100644 --- a/src/util/defaultable.hpp +++ b/src/util/defaultable.hpp @@ -49,7 +49,7 @@ class Defaultable { /// Is this value in the default state? inline bool isDefault() const { return is_default; } /// Set the defaultness to true - inline void setDefault() { is_default = true; } + inline void makeDefault() { is_default = true; } /// Set the defaultness to false inline void unsetDefault() { is_default = false; } diff --git a/src/util/string.cpp b/src/util/string.cpp index 48cba79e..398c5062 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -66,6 +66,12 @@ String substr_replace(const String& input, size_t start, size_t end, const Strin return input.substr(0,start) + replacement + input.substr(end); } +String replace_all(const String& heystack, const String& needle, const String& replacement) { + String ret = heystack; + ret.Replace(needle, replacement); + return ret; +} + // ----------------------------------------------------------------------------- : Words String last_word(const String& s) { diff --git a/src/util/string.hpp b/src/util/string.hpp index 7a602615..873b6cd3 100644 --- a/src/util/string.hpp +++ b/src/util/string.hpp @@ -84,6 +84,9 @@ String trim_left(const String&); /// Replace the substring [start...end) of 'input' with 'replacement' String substr_replace(const String& input, size_t start, size_t end, const String& replacement); +/// Replace all occurences of one needle with replacement +String replace_all(const String& heystack, const String& needle, const String& replacement); + // ----------------------------------------------------------------------------- : Words /// Returns the last word in a string