Commit 73d6577a authored by twanvl's avatar twanvl

mtg editor import

parent ef68b5a7
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
class Game; class Game;
DECLARE_POINTER_TYPE(Set); DECLARE_POINTER_TYPE(Set);
DECLARE_POINTER_TYPE(Card);
DECLARE_POINTER_TYPE(FileFormat); DECLARE_POINTER_TYPE(FileFormat);
// ----------------------------------------------------------------------------- : FileFormat // ----------------------------------------------------------------------------- : FileFormat
...@@ -78,5 +79,16 @@ FileFormatP mse1_file_format(); ...@@ -78,5 +79,16 @@ FileFormatP mse1_file_format();
FileFormatP mse2_file_format(); FileFormatP mse2_file_format();
FileFormatP mtg_editor_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 // ----------------------------------------------------------------------------- : EOF
#endif #endif
...@@ -75,7 +75,7 @@ SetP MSE1FileFormat::importSet(const String& filename) { ...@@ -75,7 +75,7 @@ SetP MSE1FileFormat::importSet(const String& filename) {
if (stylesheet.substr(0,3) == _("old")) { if (stylesheet.substr(0,3) == _("old")) {
try { try {
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("old")); set->stylesheet = StyleSheet::byGameAndName(*set->game, _("old"));
} catch (Error) { } catch (const Error&) {
// If old style doesn't work try the new one // If old style doesn't work try the new one
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("new")); set->stylesheet = StyleSheet::byGameAndName(*set->game, _("new"));
} }
......
...@@ -7,6 +7,17 @@ ...@@ -7,6 +7,17 @@
// ----------------------------------------------------------------------------- : Includes // ----------------------------------------------------------------------------- : Includes
#include <data/format/formats.hpp> #include <data/format/formats.hpp>
#include <data/set.hpp>
#include <data/game.hpp>
#include <data/stylesheet.hpp>
#include <data/card.hpp>
#include <data/field/text.hpp>
#include <data/field/choice.hpp>
#include <data/field/image.hpp>
#include <util/tagged_string.hpp>
#include <wx/wfstream.h>
DECLARE_TYPEOF_COLLECTION(CardP);
// ----------------------------------------------------------------------------- : MtgEditorFileFormat // ----------------------------------------------------------------------------- : MtgEditorFileFormat
...@@ -18,6 +29,17 @@ class MtgEditorFileFormat : public FileFormat { ...@@ -18,6 +29,17 @@ class MtgEditorFileFormat : public FileFormat {
virtual bool canImport() { return true; } virtual bool canImport() { return true; }
virtual bool canExport(const Game&) { return false; } virtual bool canExport(const Game&) { return false; }
virtual SetP importSet(const String& filename); 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 <sym>, other tags are removed
void translateTags(String& value);
}; };
FileFormatP mtg_editor_file_format() { FileFormatP mtg_editor_file_format() {
...@@ -27,5 +49,222 @@ FileFormatP mtg_editor_file_format() { ...@@ -27,5 +49,222 @@ FileFormatP mtg_editor_file_format() {
// ----------------------------------------------------------------------------- : Importing // ----------------------------------------------------------------------------- : Importing
SetP MtgEditorFileFormat::importSet(const String& filename) { 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<String>* 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<Card>(*set->game);
String line = file.ReadLine();
if (line == _("#SET###########")) { // set.title
target = &set->value<TextValue>(_("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<TextValue>(_("rule text")).value.mutate());
// add the card to the set
set->cards.push_back(current_card);
}
first = false;
current_card = new_shared1<Card>(*set->game);
target = &current_card->value<TextValue>(_("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 = &current_card->value<TextValue>(_("super type")).value;
} else if (line == _("#SUBTYPE#######")) { // sub type
target = &current_card->value<TextValue>(_("sub type")).value;
} else if (line == _("#COST##########")) { // casting cost
target = &current_card->value<TextValue>(_("casting cost")).value;
} else if (line == _("#RARITY########") || line == _("#FREQUENCY#####")) { // rarity
target = 0;
line = file.ReadLine();
if (line == _("0")) current_card->value<ChoiceValue>(_("rarity")).value.assign(_("common"));
else if (line == _("1")) current_card->value<ChoiceValue>(_("rarity")).value.assign(_("uncommon"));
else current_card->value<ChoiceValue>(_("rarity")).value.assign(_("rare"));
} else if (line == _("#COLOR#########")) { // card color
target = 0;
line = file.ReadLine();
current_card->value<ChoiceValue>(_("card color")).value.assign(line);
} else if (line == _("#AUTOBG########")) { // card color.isDefault
target = 0;
line = file.ReadLine();
if (line == _("TRUE")) {
current_card->value<ChoiceValue>(_("card color")).value.makeDefault();
}
} else if (line == _("#RULETEXT######")) { // rule text
target = &current_card->value<TextValue>(_("rule text")).value;
} else if (line == _("#FLAVORTEXT####")) { // flavor text
target = &current_card->value<TextValue>(_("flavor text")).value;
} else if (line == _("#ARTIST########")) { // illustrator
target = &current_card->value<TextValue>(_("illustrator")).value;
} else if (line == _("#COPYRIGHT#####")) { // copyright
target = &current_card->value<TextValue>(_("copyright")).value;
} else if (line == _("#POWER#########")) { // power
target = &current_card->value<TextValue>(_("power")).value;
} else if (line == _("#TOUGHNESS#####")) { // toughness
target = &current_card->value<TextValue>(_("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<TextValue>(_("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<ImageValue>(_("image")).filename = image_file;
}
}
} else if (line == _("#TOMBSTONE#####")) { // tombstone
target = 0;
line = file.ReadLine();
current_card->value<ChoiceValue>(_("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<TextValue>(_("illustrator")).value();
String copyright = set->cards[0]->value<TextValue>(_("copyright")) .value();
set->value<TextValue>(_("artist")) .value.assign(artist);
set->value<TextValue>(_("copyright")).value.assign(copyright);
// which cards have this value?
FOR_EACH(card, set->cards) {
Defaultable<String>& card_artist = card->value<TextValue>(_("illustrator")).value;
Defaultable<String>& card_copyright = card->value<TextValue>(_("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<String>& value = card->value<TextValue>(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 += _("<sym>") + tag + _("</sym>");
} else if (tag.ToULong(&number)) {
ret += _("<sym>") + tag + _("</sym>");
} else if (tag==_("T") || tag==_("TAP")) {
ret += _("<sym>T</sym>");
} else if (tag==_("THIS")) {
ret += _("~");
}
} else {
ret += c;
}
}
// Join adjecent symbol sections
value = replace_all(ret, _("</sym><sym>"), _(""));
} }
...@@ -64,7 +64,7 @@ class OptionalScript { ...@@ -64,7 +64,7 @@ class OptionalScript {
template <typename T> template <typename T>
bool invokeOnDefault(Context& ctx, Defaultable<T>& value) const { bool invokeOnDefault(Context& ctx, Defaultable<T>& value) const {
if (value.isDefault() && invokeOn(ctx, value)) { if (value.isDefault() && invokeOn(ctx, value)) {
value.setDefault(); // restore defaultness value.makeDefault(); // restore defaultness
return true; return true;
} else { } else {
return false; return false;
......
...@@ -49,7 +49,7 @@ class Defaultable { ...@@ -49,7 +49,7 @@ class Defaultable {
/// Is this value in the default state? /// Is this value in the default state?
inline bool isDefault() const { return is_default; } inline bool isDefault() const { return is_default; }
/// Set the defaultness to true /// Set the defaultness to true
inline void setDefault() { is_default = true; } inline void makeDefault() { is_default = true; }
/// Set the defaultness to false /// Set the defaultness to false
inline void unsetDefault() { is_default = false; } inline void unsetDefault() { is_default = false; }
......
...@@ -66,6 +66,12 @@ String substr_replace(const String& input, size_t start, size_t end, const Strin ...@@ -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); 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 // ----------------------------------------------------------------------------- : Words
String last_word(const String& s) { String last_word(const String& s) {
......
...@@ -84,6 +84,9 @@ String trim_left(const String&); ...@@ -84,6 +84,9 @@ String trim_left(const String&);
/// Replace the substring [start...end) of 'input' with 'replacement' /// Replace the substring [start...end) of 'input' with 'replacement'
String substr_replace(const String& input, size_t start, size_t end, const String& 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 // ----------------------------------------------------------------------------- : Words
/// Returns the last word in a string /// Returns the last word in a string
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment