Change tabs to two spaces.

This commit is contained in:
Lymia Aluysia
2017-01-18 08:43:21 -06:00
parent d7f5f0dc3b
commit d2c635f739
329 changed files with 41307 additions and 41496 deletions
File diff suppressed because it is too large Load Diff
+76 -76
View File
@@ -23,131 +23,131 @@
/// Serialize an object to a string, clipboard_package will be set to the given package.
template <typename T>
String serialize_for_clipboard(Package& package, T& object) {
shared_ptr<wxStringOutputStream> stream( new wxStringOutputStream );
Writer writer(stream, file_version_clipboard);
WITH_DYNAMIC_ARG(clipboard_package, &package);
writer.handle(object);
return stream->GetString();
shared_ptr<wxStringOutputStream> stream( new wxStringOutputStream );
Writer writer(stream, file_version_clipboard);
WITH_DYNAMIC_ARG(clipboard_package, &package);
writer.handle(object);
return stream->GetString();
}
template <typename T>
void deserialize_from_clipboard(T& object, Package& package, const String& data) {
shared_ptr<wxStringInputStream> stream( new wxStringInputStream(data) );
Reader reader(stream, nullptr, _("clipboard"));
WITH_DYNAMIC_ARG(clipboard_package, &package);
reader.handle_greedy(object);
shared_ptr<wxStringInputStream> stream( new wxStringInputStream(data) );
Reader reader(stream, nullptr, _("clipboard"));
WITH_DYNAMIC_ARG(clipboard_package, &package);
reader.handle_greedy(object);
}
// ----------------------------------------------------------------------------- : CardDataObject
/// A wrapped cards for storing on the clipboard
struct WrappedCards {
Game* expected_game;
String game_name;
vector<CardP> cards;
DECLARE_REFLECTION();
Game* expected_game;
String game_name;
vector<CardP> cards;
DECLARE_REFLECTION();
};
IMPLEMENT_REFLECTION(WrappedCards) {
REFLECT(game_name);
if (game_name == expected_game->name()) {
WITH_DYNAMIC_ARG(game_for_reading, expected_game);
REFLECT(cards);
}
REFLECT(game_name);
if (game_name == expected_game->name()) {
WITH_DYNAMIC_ARG(game_for_reading, expected_game);
REFLECT(cards);
}
}
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()];
for (size_t i = 0 ; i < cards.size() ; ++i) {
has_styling[i] = cards[i]->has_styling && !cards[i]->stylesheet;
if (has_styling[i]) {
cards[i]->stylesheet = set->stylesheet;
}
}
WrappedCards data = { set->game.get(), set->game->name(), cards };
SetText(serialize_for_clipboard(*set, data));
// restore cards
for (size_t i = 0 ; i < cards.size() ; ++i) {
if (has_styling[i]) {
cards[i]->stylesheet = StyleSheetP();
}
}
SetFormat(format);
delete [] has_styling;
// set the stylesheet, so when deserializing we know whos style options we are reading
bool* has_styling = new bool[cards.size()];
for (size_t i = 0 ; i < cards.size() ; ++i) {
has_styling[i] = cards[i]->has_styling && !cards[i]->stylesheet;
if (has_styling[i]) {
cards[i]->stylesheet = set->stylesheet;
}
}
WrappedCards data = { set->game.get(), set->game->name(), cards };
SetText(serialize_for_clipboard(*set, data));
// restore cards
for (size_t i = 0 ; i < cards.size() ; ++i) {
if (has_styling[i]) {
cards[i]->stylesheet = StyleSheetP();
}
}
SetFormat(format);
delete [] has_styling;
}
CardsDataObject::CardsDataObject() {
SetFormat(format);
SetFormat(format);
}
bool CardsDataObject::getCards(const SetP& set, vector<CardP>& out) {
WrappedCards data = { set->game.get(), set->game->name() };
deserialize_from_clipboard(data, *set, GetText());
if (data.cards.empty()) return false;
if (data.game_name == set->game->name()) {
// Cards are from the same game
out = data.cards;
return true;
} else {
return false;
}
WrappedCards data = { set->game.get(), set->game->name() };
deserialize_from_clipboard(data, *set, GetText());
if (data.cards.empty()) return false;
if (data.game_name == set->game->name()) {
// Cards are from the same game
out = data.cards;
return true;
} else {
return false;
}
}
// ----------------------------------------------------------------------------- : KeywordDataObject
/// A wrapped keyword for storing on the clipboard
struct WrappedKeyword {
Game* expected_game;
String game_name;
KeywordP keyword;
DECLARE_REFLECTION();
Game* expected_game;
String game_name;
KeywordP keyword;
DECLARE_REFLECTION();
};
IMPLEMENT_REFLECTION(WrappedKeyword) {
REFLECT(game_name);
if (game_name == expected_game->name()) {
WITH_DYNAMIC_ARG(game_for_reading, expected_game);
REFLECT(keyword);
}
REFLECT(game_name);
if (game_name == expected_game->name()) {
WITH_DYNAMIC_ARG(game_for_reading, expected_game);
REFLECT(keyword);
}
}
wxDataFormat KeywordDataObject::format = _("application/x-mse-keyword");
KeywordDataObject::KeywordDataObject(const SetP& set, const KeywordP& keyword) {
WrappedKeyword data = { set->game.get(), set->game->name(), keyword };
SetText(serialize_for_clipboard(*set, data));
SetFormat(format);
WrappedKeyword data = { set->game.get(), set->game->name(), keyword };
SetText(serialize_for_clipboard(*set, data));
SetFormat(format);
}
KeywordDataObject::KeywordDataObject() {
SetFormat(format);
SetFormat(format);
}
KeywordP KeywordDataObject::getKeyword(const SetP& set) {
KeywordP keyword(new Keyword());
WrappedKeyword data = { set->game.get(), set->game->name(), keyword};
deserialize_from_clipboard(data, *set, GetText());
if (data.game_name != set->game->name()) return KeywordP(); // Keyword is from a different game
else return keyword;
KeywordP keyword(new Keyword());
WrappedKeyword data = { set->game.get(), set->game->name(), keyword};
deserialize_from_clipboard(data, *set, GetText());
if (data.game_name != set->game->name()) return KeywordP(); // Keyword is from a different game
else return keyword;
}
// ----------------------------------------------------------------------------- : Card on clipboard
CardsOnClipboard::CardsOnClipboard(const SetP& set, const vector<CardP>& cards) {
// Conversion to text format
// TODO
//Add( new TextDataObject(_("card")))
// Conversion to bitmap format
if (cards.size() == 1) {
Add(new wxBitmapDataObject(export_bitmap(set, cards[0])));
}
// Conversion to serialized card format
Add(new CardsDataObject(set, cards), true);
// Conversion to text format
// TODO
//Add( new TextDataObject(_("card")))
// Conversion to bitmap format
if (cards.size() == 1) {
Add(new wxBitmapDataObject(export_bitmap(set, cards[0])));
}
// Conversion to serialized card format
Add(new CardsDataObject(set, cards), true);
}
+20 -20
View File
@@ -21,16 +21,16 @@ DECLARE_POINTER_TYPE(Keyword);
/// The data format for cards on the clipboard
class CardsDataObject : public wxTextDataObject {
public:
/// Name of the format of MSE cards
static wxDataFormat format;
CardsDataObject();
/// Store a card
CardsDataObject(const SetP& set, const vector<CardP>& cards);
/// Retrieve the cards, only if it is made with the same game as set
/** Return true if the cards are correctly retrieved, and there is at least one card */
bool getCards(const SetP& set, vector<CardP>& out);
/// Name of the format of MSE cards
static wxDataFormat format;
CardsDataObject();
/// Store a card
CardsDataObject(const SetP& set, const vector<CardP>& cards);
/// Retrieve the cards, only if it is made with the same game as set
/** Return true if the cards are correctly retrieved, and there is at least one card */
bool getCards(const SetP& set, vector<CardP>& out);
};
// ----------------------------------------------------------------------------- : KeywordDataObject
@@ -38,15 +38,15 @@ class CardsDataObject : public wxTextDataObject {
/// The data format for keywords on the clipboard
class KeywordDataObject : public wxTextDataObject {
public:
/// Name of the format of MSE keywords
static wxDataFormat format;
KeywordDataObject();
/// Store a keyword
KeywordDataObject(const SetP& set, const KeywordP& card);
/// Retrieve a keyword, only if it is made with the same game as set
KeywordP getKeyword(const SetP& set);
/// Name of the format of MSE keywords
static wxDataFormat format;
KeywordDataObject();
/// Store a keyword
KeywordDataObject(const SetP& set, const KeywordP& card);
/// Retrieve a keyword, only if it is made with the same game as set
KeywordP getKeyword(const SetP& set);
};
// ----------------------------------------------------------------------------- : Card on clipboard
@@ -54,7 +54,7 @@ class KeywordDataObject : public wxTextDataObject {
/// A DataObject for putting one or more cards on the clipboard, in multiple formats
class CardsOnClipboard : public wxDataObjectComposite {
public:
CardsOnClipboard(const SetP& set, const vector<CardP>& cards);
CardsOnClipboard(const SetP& set, const vector<CardP>& cards);
};
// ----------------------------------------------------------------------------- : EOF
+37 -37
View File
@@ -19,53 +19,53 @@ DECLARE_TYPEOF_COLLECTION(FileFormatP);
vector<FileFormatP> file_formats;
void init_file_formats() {
file_formats.push_back(mse2_file_format());
file_formats.push_back(mse1_file_format());
file_formats.push_back(mtg_editor_file_format());
file_formats.push_back(mse2_file_format());
file_formats.push_back(mse1_file_format());
file_formats.push_back(mtg_editor_file_format());
}
String import_formats() {
String all_extensions; // type1;type2
String type_strings; // |name1|type1|name2|type2
FOR_EACH(f, file_formats) {
if (f->canImport()) {
if (!all_extensions.empty()) all_extensions += _(";");
all_extensions += f->matches();
type_strings += _("|") + f->name() + _("|") + f->matches();
}
}
return _("Set files|") + all_extensions + type_strings + _("|All files (*.*)|*");
String all_extensions; // type1;type2
String type_strings; // |name1|type1|name2|type2
FOR_EACH(f, file_formats) {
if (f->canImport()) {
if (!all_extensions.empty()) all_extensions += _(";");
all_extensions += f->matches();
type_strings += _("|") + f->name() + _("|") + f->matches();
}
}
return _("Set files|") + all_extensions + type_strings + _("|All files (*.*)|*");
}
String export_formats(const Game& game) {
String type_strings; // name1|type1|name2|type2
FOR_EACH(f, file_formats) {
if (f->canExport(game)) {
if (!type_strings.empty()) type_strings += _("|");
type_strings += f->name() + _("|") + f->matches();
}
}
return type_strings;
String type_strings; // name1|type1|name2|type2
FOR_EACH(f, file_formats) {
if (f->canExport(game)) {
if (!type_strings.empty()) type_strings += _("|");
type_strings += f->name() + _("|") + f->matches();
}
}
return type_strings;
}
void export_set(Set& set, const String& filename, size_t format_index, bool is_copy) {
FileFormatP format = file_formats.at(format_index);
if (!format->canExport(*set.game)) {
throw InternalError(_("File format doesn't apply to set"));
}
format->exportSet(set, filename, is_copy);
FileFormatP format = file_formats.at(format_index);
if (!format->canExport(*set.game)) {
throw InternalError(_("File format doesn't apply to set"));
}
format->exportSet(set, filename, is_copy);
}
SetP import_set(String name) {
size_t pos = name.find_last_of(_('.'));
String extension = pos==String::npos ? _("") : name.substr(pos + 1);
// determine format
FOR_EACH(f, file_formats) {
if (f->extension() == extension) {
return f->importSet(name);
}
}
// default: use first format = MSE2 format
assert(!file_formats.empty() && file_formats[0]->canImport());
return file_formats[0]->importSet(name);
size_t pos = name.find_last_of(_('.'));
String extension = pos==String::npos ? _("") : name.substr(pos + 1);
// determine format
FOR_EACH(f, file_formats) {
if (f->extension() == extension) {
return f->importSet(name);
}
}
// default: use first format = MSE2 format
assert(!file_formats.empty() && file_formats[0]->canImport());
return file_formats[0]->importSet(name);
}
+22 -22
View File
@@ -23,28 +23,28 @@ DECLARE_POINTER_TYPE(FileFormat);
/// A filter for a specific file format
class FileFormat : public IntrusivePtrVirtualBase {
public:
virtual ~FileFormat() {}
/// File extension used by this file format
virtual String extension() = 0;
/// What to match against
virtual String matches() {
return _("*.") + extension();
}
/// Name of the filter
virtual String name() = 0;
/// Can it be used for importing sets?
virtual bool canImport() = 0;
/// Can it be used for exporting sets for a particular game?
virtual bool canExport(const Game&) = 0;
/// Import using this filter
virtual SetP importSet(const String& filename) {
throw InternalError(_("Import not supported by this file format"));
}
/// Export using this filter
/** If is_copy, then the set should not be modified */
virtual void exportSet(Set& set, const String& filename, bool is_copy = false) {
throw InternalError(_("Export not supported by this file format"));
}
virtual ~FileFormat() {}
/// File extension used by this file format
virtual String extension() = 0;
/// What to match against
virtual String matches() {
return _("*.") + extension();
}
/// Name of the filter
virtual String name() = 0;
/// Can it be used for importing sets?
virtual bool canImport() = 0;
/// Can it be used for exporting sets for a particular game?
virtual bool canExport(const Game&) = 0;
/// Import using this filter
virtual SetP importSet(const String& filename) {
throw InternalError(_("Import not supported by this file format"));
}
/// Export using this filter
/** If is_copy, then the set should not be modified */
virtual void exportSet(Set& set, const String& filename, bool is_copy = false) {
throw InternalError(_("Export not supported by this file format"));
}
};
// ----------------------------------------------------------------------------- : Formats
+51 -51
View File
@@ -21,46 +21,46 @@ DECLARE_TYPEOF_COLLECTION(CardP);
// ----------------------------------------------------------------------------- : Single card export
void export_image(const SetP& set, const CardP& card, const String& filename) {
Image img = export_bitmap(set, card).ConvertToImage();
img.SaveFile(filename); // can't use Bitmap::saveFile, it wants to know the file type
// but image.saveFile determines it automagicly
Image img = export_bitmap(set, card).ConvertToImage();
img.SaveFile(filename); // can't use Bitmap::saveFile, it wants to know the file type
// but image.saveFile determines it automagicly
}
class UnzoomedDataViewer : public DataViewer {
public:
UnzoomedDataViewer(bool use_zoom_settings)
: use_zoom_settings(use_zoom_settings)
{}
virtual Rotation getRotation() const;
UnzoomedDataViewer(bool use_zoom_settings)
: use_zoom_settings(use_zoom_settings)
{}
virtual Rotation getRotation() const;
private:
bool use_zoom_settings;
bool use_zoom_settings;
};
Rotation UnzoomedDataViewer::getRotation() const {
if (use_zoom_settings) {
return DataViewer::getRotation();
} else {
if (!stylesheet) stylesheet = set->stylesheet;
return Rotation(0, stylesheet->getCardRect(), 1.0, 1.0, ROTATION_ATTACH_TOP_LEFT);
}
if (use_zoom_settings) {
return DataViewer::getRotation();
} else {
if (!stylesheet) stylesheet = set->stylesheet;
return Rotation(0, stylesheet->getCardRect(), 1.0, 1.0, ROTATION_ATTACH_TOP_LEFT);
}
}
Bitmap export_bitmap(const SetP& set, const CardP& card) {
if (!set) throw Error(_("no set"));
// create viewer
UnzoomedDataViewer viewer(!settings.stylesheetSettingsFor(set->stylesheetFor(card)).card_normal_export());
viewer.setSet(set);
viewer.setCard(card);
// size of cards
RealSize size = viewer.getRotation().getExternalSize();
// create bitmap & dc
Bitmap bitmap((int) size.width, (int) size.height);
if (!bitmap.Ok()) throw InternalError(_("Unable to create bitmap"));
wxMemoryDC dc;
dc.SelectObject(bitmap);
// draw
viewer.draw(dc);
dc.SelectObject(wxNullBitmap);
return bitmap;
if (!set) throw Error(_("no set"));
// create viewer
UnzoomedDataViewer viewer(!settings.stylesheetSettingsFor(set->stylesheetFor(card)).card_normal_export());
viewer.setSet(set);
viewer.setCard(card);
// size of cards
RealSize size = viewer.getRotation().getExternalSize();
// create bitmap & dc
Bitmap bitmap((int) size.width, (int) size.height);
if (!bitmap.Ok()) throw InternalError(_("Unable to create bitmap"));
wxMemoryDC dc;
dc.SelectObject(bitmap);
// draw
viewer.draw(dc);
dc.SelectObject(wxNullBitmap);
return bitmap;
}
// ----------------------------------------------------------------------------- : Multiple card export
@@ -69,25 +69,25 @@ Bitmap export_bitmap(const SetP& set, const CardP& card) {
void export_images(const SetP& set, const vector<CardP>& cards,
const String& path, const String& filename_template, FilenameConflicts conflicts)
{
wxBusyCursor busy;
// Script
ScriptP filename_script = parse(filename_template, nullptr, true);
// Path
wxFileName fn(path);
// Export
std::set<String> used; // for CONFLICT_NUMBER_OVERWRITE
FOR_EACH_CONST(card, cards) {
// filename for this card
Context& ctx = set->getContext(card);
String filename = clean_filename(untag(ctx.eval(*filename_script)->toString()));
if (!filename) continue; // no filename -> no saving
// full path
fn.SetFullName(filename);
// does the file exist?
if (!resolve_filename_conflicts(fn, conflicts, used)) continue;
// write image
filename = fn.GetFullPath();
used.insert(filename);
export_image(set, card, filename);
}
wxBusyCursor busy;
// Script
ScriptP filename_script = parse(filename_template, nullptr, true);
// Path
wxFileName fn(path);
// Export
std::set<String> used; // for CONFLICT_NUMBER_OVERWRITE
FOR_EACH_CONST(card, cards) {
// filename for this card
Context& ctx = set->getContext(card);
String filename = clean_filename(untag(ctx.eval(*filename_script)->toString()));
if (!filename) continue; // no filename -> no saving
// full path
fn.SetFullName(filename);
// does the file exist?
if (!resolve_filename_conflicts(fn, conflicts, used)) continue;
// write image
filename = fn.GetFullPath();
used.insert(filename);
export_image(set, card, filename);
}
}
+350 -350
View File
@@ -7,10 +7,10 @@
// ----------------------------------------------------------------------------- : Includes
#if defined(_MSC_VER) && _MSC_VER >= 1400
// VS 8 has the audacity to give a warning about fill_n
// That is both STUPID and WRONG, so disable that warning
// This has to be done before includes, because the warning is reported in standard headers!
#pragma warning(disable:4996)
// VS 8 has the audacity to give a warning about fill_n
// That is both STUPID and WRONG, so disable that warning
// This has to be done before includes, because the warning is reported in standard headers!
#pragma warning(disable:4996)
#endif
#include <util/prec.hpp>
@@ -25,9 +25,9 @@ DECLARE_TYPEOF_COLLECTION(SymbolPartP);
// ----------------------------------------------------------------------------- : Image preprocessing
enum ImageMarker
{ EMPTY = 0 // This cell is empty
, FULL = 1 // This cell is full
, MARKED = 2 // This cell is full, but it has been used as a starting point for finding symbols
{ EMPTY = 0 // This cell is empty
, FULL = 1 // This cell is full
, MARKED = 2 // This cell is full, but it has been used as a starting point for finding symbols
};
@@ -37,13 +37,13 @@ enum ImageMarker
* is no longer an actual image.
*/
void greyscale(Image& img) {
UInt size = img.GetWidth() * img.GetHeight();
Byte* data = img.GetData();
Byte* out = data;
for (UInt i = 0 ; i < size ; ++i) {
*out++ = (data[0] + data[1] + data[2]) / 3;
data += 3;
}
UInt size = img.GetWidth() * img.GetHeight();
Byte* data = img.GetData();
Byte* out = data;
for (UInt i = 0 ; i < size ; ++i) {
*out++ = (data[0] + data[1] + data[2]) / 3;
data += 3;
}
}
/// Thresholds an image, giving a black & white result
@@ -52,192 +52,192 @@ void greyscale(Image& img) {
* EMPTY for the 'border' color, FULL for the interior
*/
void threshold(Byte* data, int w, int h) {
size_t size = w * h;
// make histogram of data
size_t hist[256];
fill_n(hist,256,0);
for (size_t i = 0 ; i < size ; ++i) {
hist[data[i]]++;
}
// find threshold
size_t threshold_pos = size / 2;
int threshold = 255;
size_t below = 0;
for (int i = 0 ; i < 255 ; ++i) {
if (below + hist[i]/2 > threshold_pos) {
threshold = i;
break;
}
below += hist[i];
if (below >= threshold_pos) {
threshold = i + 1;
break;
}
}
// threshold data
for (size_t i = 0 ; i < size ; ++i) {
data[i] = data[i] >= threshold ? FULL : EMPTY;
}
// should the colors be inverted?
int border_count = 0;
for (int x = 0 ; x < w ; ++x) {
border_count += data[x] + data[x+(h-1)*w];
}
for (int y = 0 ; y < h ; ++y) {
border_count += data[y*w] + data[w-1+y*w];
}
if (border_count > w + h) {
// more then half the border if FULL, invert
for (size_t i = 0 ; i < size ; ++i) {
data[i] = data[i] == FULL ? EMPTY : FULL;
}
}
size_t size = w * h;
// make histogram of data
size_t hist[256];
fill_n(hist,256,0);
for (size_t i = 0 ; i < size ; ++i) {
hist[data[i]]++;
}
// find threshold
size_t threshold_pos = size / 2;
int threshold = 255;
size_t below = 0;
for (int i = 0 ; i < 255 ; ++i) {
if (below + hist[i]/2 > threshold_pos) {
threshold = i;
break;
}
below += hist[i];
if (below >= threshold_pos) {
threshold = i + 1;
break;
}
}
// threshold data
for (size_t i = 0 ; i < size ; ++i) {
data[i] = data[i] >= threshold ? FULL : EMPTY;
}
// should the colors be inverted?
int border_count = 0;
for (int x = 0 ; x < w ; ++x) {
border_count += data[x] + data[x+(h-1)*w];
}
for (int y = 0 ; y < h ; ++y) {
border_count += data[y*w] + data[w-1+y*w];
}
if (border_count > w + h) {
// more then half the border if FULL, invert
for (size_t i = 0 ; i < size ; ++i) {
data[i] = data[i] == FULL ? EMPTY : FULL;
}
}
}
// ----------------------------------------------------------------------------- : Image to symbol
bool is_mse1_symbol(const Image& img) {
// mse1 symbols are 60x80
if (img.GetWidth() != 60 || img.GetHeight() != 80) return false;
// the right side is black & white
int delta = 0;
for (int y = 0 ; y < 80 ; ++y) {
Byte* d = img.GetData() + 3 * (y * 60 + 20);
for (int x = 20 ; x < 60 ; ++x) {
int r = *d++;
int g = *d++;
int b = *d++;
delta += abs(r - b) + abs(r - g) + abs(b - g);
}
}
if (delta > 5000) return false; // not black & white enough
// TODO : more checks?
return true;
// mse1 symbols are 60x80
if (img.GetWidth() != 60 || img.GetHeight() != 80) return false;
// the right side is black & white
int delta = 0;
for (int y = 0 ; y < 80 ; ++y) {
Byte* d = img.GetData() + 3 * (y * 60 + 20);
for (int x = 20 ; x < 60 ; ++x) {
int r = *d++;
int g = *d++;
int b = *d++;
delta += abs(r - b) + abs(r - g) + abs(b - g);
}
}
if (delta > 5000) return false; // not black & white enough
// TODO : more checks?
return true;
}
struct ImageData {
int width, height;
Byte* data;
mutable Byte dummy;
inline Byte& operator () (int x, int y) const {
if (x < 0 || x >= width || y < 0 || y >= height) {
return (dummy = EMPTY); // outside, return empty
} else {
return data[x + y*width];
}
}
int width, height;
Byte* data;
mutable Byte dummy;
inline Byte& operator () (int x, int y) const {
if (x < 0 || x >= width || y < 0 || y >= height) {
return (dummy = EMPTY); // outside, return empty
} else {
return data[x + y*width];
}
}
};
bool find_symbol_shape_start(const ImageData& data, int& x_out, int& y_out) {
for (int x = 0 ; x < data.width ; ++x) {
for (int y = 0 ; y < data.height ; ++y) {
if (data(x, y) == FULL && data(x, y-1) == EMPTY) {
// the point above must be clear, we don't want to start in the 'ground'
// also, we don't want to find things we found before
x_out = x;
y_out = y;
return true;
}
}
}
return false;
for (int x = 0 ; x < data.width ; ++x) {
for (int y = 0 ; y < data.height ; ++y) {
if (data(x, y) == FULL && data(x, y-1) == EMPTY) {
// the point above must be clear, we don't want to start in the 'ground'
// also, we don't want to find things we found before
x_out = x;
y_out = y;
return true;
}
}
}
return false;
}
SymbolShapeP read_symbol_shape(const ImageData& data) {
// find start point
int xs, ys;
if (!find_symbol_shape_start(data, xs, ys)) return SymbolShapeP();
data(xs, ys) |= MARKED;
SymbolShapeP shape(new SymbolShape);
// walk around, clockwise
xs += 1; // start right of the found point, otherwise last_move might think we came from above
int x = xs, y = ys;
int old_x = x, old_y = y;
int last_move = 1; // 1 = right or down, (as in x|y += 1)
do {
// the cursor (x,y) is between four pounts:
// a b
// .
// c d
bool a = data(x-1, y-1) & FULL;
bool b = data(x, y-1) & FULL;
bool c = data(x-1, y ) & FULL;
bool d = data(x, y ) & FULL;
UInt pack = (a << 12) + (b << 8) + (c << 4) + d; // 0xabcd
switch (pack) {
case 0x0001 : x += 1; break;
case 0x0010 : y += 1; break;
case 0x0011 : x += 1; break;
case 0x0100 : y -= 1; break;
case 0x0101 : y -= 1; break;
case 0x0110 : y -= last_move; break; // diagonal, we can come here from two sides, from left and right
case 0x0111 : y -= 1; break; // last_move indicates which of {b,c} we are 'attached' to
case 0x1000 : x -= 1; break;
case 0x1001 : x += last_move; break;
case 0x1010 : y += 1; break;
case 0x1011 : x += 1; break;
case 0x1100 : x -= 1; break;
case 0x1101 : x -= 1; break;
case 0x1110 : y += 1; break;
default:
throw InternalError(_("in the ground/air"));
}
// add to shape and place a mark
shape->points.push_back(intrusive(new ControlPoint(
double(x) / data.width,
double(y) / data.height
)));
if (x > old_x) data(old_x, y) |= MARKED; // mark when moving right -> only mark the top of the shape
last_move = (x + y) - (old_x + old_y);
old_x = x;
old_y = y;
} while (x != xs || y != ys); // we will end up in the start point
// are we on the inside or the outside?
if (data(x-2,y-1) & FULL) {
shape->combine = SYMBOL_COMBINE_SUBTRACT;
} else {
shape->combine = SYMBOL_COMBINE_MERGE;
}
return shape;
// find start point
int xs, ys;
if (!find_symbol_shape_start(data, xs, ys)) return SymbolShapeP();
data(xs, ys) |= MARKED;
SymbolShapeP shape(new SymbolShape);
// walk around, clockwise
xs += 1; // start right of the found point, otherwise last_move might think we came from above
int x = xs, y = ys;
int old_x = x, old_y = y;
int last_move = 1; // 1 = right or down, (as in x|y += 1)
do {
// the cursor (x,y) is between four pounts:
// a b
// .
// c d
bool a = data(x-1, y-1) & FULL;
bool b = data(x, y-1) & FULL;
bool c = data(x-1, y ) & FULL;
bool d = data(x, y ) & FULL;
UInt pack = (a << 12) + (b << 8) + (c << 4) + d; // 0xabcd
switch (pack) {
case 0x0001 : x += 1; break;
case 0x0010 : y += 1; break;
case 0x0011 : x += 1; break;
case 0x0100 : y -= 1; break;
case 0x0101 : y -= 1; break;
case 0x0110 : y -= last_move; break; // diagonal, we can come here from two sides, from left and right
case 0x0111 : y -= 1; break; // last_move indicates which of {b,c} we are 'attached' to
case 0x1000 : x -= 1; break;
case 0x1001 : x += last_move; break;
case 0x1010 : y += 1; break;
case 0x1011 : x += 1; break;
case 0x1100 : x -= 1; break;
case 0x1101 : x -= 1; break;
case 0x1110 : y += 1; break;
default:
throw InternalError(_("in the ground/air"));
}
// add to shape and place a mark
shape->points.push_back(intrusive(new ControlPoint(
double(x) / data.width,
double(y) / data.height
)));
if (x > old_x) data(old_x, y) |= MARKED; // mark when moving right -> only mark the top of the shape
last_move = (x + y) - (old_x + old_y);
old_x = x;
old_y = y;
} while (x != xs || y != ys); // we will end up in the start point
// are we on the inside or the outside?
if (data(x-2,y-1) & FULL) {
shape->combine = SYMBOL_COMBINE_SUBTRACT;
} else {
shape->combine = SYMBOL_COMBINE_MERGE;
}
return shape;
}
SymbolP image_to_symbol(Image& img) {
int w = img.GetWidth(), h = img.GetHeight();
// 1. threshold the image
greyscale(img);
threshold(img.GetData(), w, h);
// 2. read as many symbol shapes as we can
ImageData data = {w,h,img.GetData()};
SymbolP symbol(new Symbol);
while (true) {
SymbolShapeP shape = read_symbol_shape(data);
if (!shape) break;
symbol->parts.push_back(shape);
}
reverse(symbol->parts.begin(), symbol->parts.end());
return symbol;
int w = img.GetWidth(), h = img.GetHeight();
// 1. threshold the image
greyscale(img);
threshold(img.GetData(), w, h);
// 2. read as many symbol shapes as we can
ImageData data = {w,h,img.GetData()};
SymbolP symbol(new Symbol);
while (true) {
SymbolShapeP shape = read_symbol_shape(data);
if (!shape) break;
symbol->parts.push_back(shape);
}
reverse(symbol->parts.begin(), symbol->parts.end());
return symbol;
}
SymbolP import_symbol(Image& img) {
SymbolP symbol;
if (is_mse1_symbol(img)) {
Image img2 = img.GetSubImage(wxRect(20,0,40,40));
symbol = image_to_symbol(img2);
} else if (img.GetWidth() > 100 || img.GetHeight() > 100) {
// 100x100 ought to be enough, we trow out most afterwards data anyway
Image resampled = img.Rescale(100,100);
symbol = image_to_symbol(resampled);
} else {
symbol = image_to_symbol(img);
}
simplify_symbol(*symbol);
return symbol;
SymbolP symbol;
if (is_mse1_symbol(img)) {
Image img2 = img.GetSubImage(wxRect(20,0,40,40));
symbol = image_to_symbol(img2);
} else if (img.GetWidth() > 100 || img.GetHeight() > 100) {
// 100x100 ought to be enough, we trow out most afterwards data anyway
Image resampled = img.Rescale(100,100);
symbol = image_to_symbol(resampled);
} else {
symbol = image_to_symbol(img);
}
simplify_symbol(*symbol);
return symbol;
}
@@ -247,19 +247,19 @@ SymbolP import_symbol(Image& img) {
/** A corner is a point that has an angle between tangent greater then a treshold
*/
void mark_corners(SymbolShape& shape) {
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
ControlPoint& current = *shape.getPoint(i);
Vector2D before = .6 * shape.getPoint(i-1)->pos + .2 * shape.getPoint(i-2)->pos + .1 * shape.getPoint(i-3)->pos + .1 * shape.getPoint(i-4)->pos;
Vector2D after = .6 * shape.getPoint(i+1)->pos + .2 * shape.getPoint(i+2)->pos + .1 * shape.getPoint(i+3)->pos + .1 * shape.getPoint(i+4)->pos;
before = (before - current.pos).normalized();
after = (after - current.pos).normalized();
if (dot(before,after) >= -0.25f) {
// corner
current.lock = LOCK_FREE;
} else {
current.lock = LOCK_DIR;
}
}
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
ControlPoint& current = *shape.getPoint(i);
Vector2D before = .6 * shape.getPoint(i-1)->pos + .2 * shape.getPoint(i-2)->pos + .1 * shape.getPoint(i-3)->pos + .1 * shape.getPoint(i-4)->pos;
Vector2D after = .6 * shape.getPoint(i+1)->pos + .2 * shape.getPoint(i+2)->pos + .1 * shape.getPoint(i+3)->pos + .1 * shape.getPoint(i+4)->pos;
before = (before - current.pos).normalized();
after = (after - current.pos).normalized();
if (dot(before,after) >= -0.25f) {
// corner
current.lock = LOCK_FREE;
} else {
current.lock = LOCK_DIR;
}
}
}
/// Merge adjacent corners
@@ -280,116 +280,116 @@ void mark_corners(SymbolShape& shape) {
* is the merged corner. If it is too far away, don't merge
*/
void merge_corners(SymbolShape& shape) {
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
ControlPoint& cur = *shape.getPoint(i);
ControlPoint& prev = *shape.getPoint(i - 1);
if (prev.lock != LOCK_FREE || cur.lock != LOCK_FREE) continue;
// step 1. find tangent lines: try tangent lines to the first point, the second, etc.
// and take the one that has the largest angle with ab, i.e. the smallest dot,
// where ab is the line between the two corners
Vector2D ab = cur.pos - prev.pos;
double min_a_dot = 1e100, min_b_dot = 1e100;
Vector2D a, b;
for (int j = 0 ; j < 4 ; ++j) {
Vector2D a_ = (shape.getPoint(i-j-1)->pos - prev.pos).normalized();
Vector2D b_ = (shape.getPoint(i+j)->pos - cur.pos).normalized();
double a_dot = dot(a_, ab);
double b_dot = -dot(b_, ab);
if (a_dot < min_a_dot) {
min_a_dot = a_dot;
a = a_;
}
if (b_dot < min_b_dot) {
min_b_dot = b_dot;
b = b_;
}
}
// step 2. find intersection point, to solve:
// t a + ab = u b, solve for t,u
// Gives us:
// t = ab cross b / b cross a
double tden = max(0.00000001, cross(b,a));
double t = cross(ab,b) / tden;
// do these tangent lines intersect, and not too far away?
// if so, then the intersection point is the merged point
if (t >= 0 && t < 20.0) {
prev.pos += a * -t;
shape.points.erase(shape.points.begin() + i);
i -= 1;
}
}
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
ControlPoint& cur = *shape.getPoint(i);
ControlPoint& prev = *shape.getPoint(i - 1);
if (prev.lock != LOCK_FREE || cur.lock != LOCK_FREE) continue;
// step 1. find tangent lines: try tangent lines to the first point, the second, etc.
// and take the one that has the largest angle with ab, i.e. the smallest dot,
// where ab is the line between the two corners
Vector2D ab = cur.pos - prev.pos;
double min_a_dot = 1e100, min_b_dot = 1e100;
Vector2D a, b;
for (int j = 0 ; j < 4 ; ++j) {
Vector2D a_ = (shape.getPoint(i-j-1)->pos - prev.pos).normalized();
Vector2D b_ = (shape.getPoint(i+j)->pos - cur.pos).normalized();
double a_dot = dot(a_, ab);
double b_dot = -dot(b_, ab);
if (a_dot < min_a_dot) {
min_a_dot = a_dot;
a = a_;
}
if (b_dot < min_b_dot) {
min_b_dot = b_dot;
b = b_;
}
}
// step 2. find intersection point, to solve:
// t a + ab = u b, solve for t,u
// Gives us:
// t = ab cross b / b cross a
double tden = max(0.00000001, cross(b,a));
double t = cross(ab,b) / tden;
// do these tangent lines intersect, and not too far away?
// if so, then the intersection point is the merged point
if (t >= 0 && t < 20.0) {
prev.pos += a * -t;
shape.points.erase(shape.points.begin() + i);
i -= 1;
}
}
}
/// Avarage/'blur' a symbol shape
void avarage(SymbolShape& shape) {
// create a copy of the points
vector<Vector2D> old_points;
FOR_EACH(p, shape.points) {
old_points.push_back(p->pos);
}
// avarage points
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
ControlPoint& p = *shape.getPoint(i);
if (p.lock == LOCK_DIR) {
p.pos = .25 * old_points[mod(i-1, old_points.size())]
+ .50 * p.pos
+ .25 * old_points[mod(i+1, old_points.size())];
}
}
// create a copy of the points
vector<Vector2D> old_points;
FOR_EACH(p, shape.points) {
old_points.push_back(p->pos);
}
// avarage points
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
ControlPoint& p = *shape.getPoint(i);
if (p.lock == LOCK_DIR) {
p.pos = .25 * old_points[mod(i-1, old_points.size())]
+ .50 * p.pos
+ .25 * old_points[mod(i+1, old_points.size())];
}
}
}
/// Convert a symbol shape to curves
void convert_to_curves(SymbolShape& shape) {
// mark all segments as curves
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
ControlPoint& cur = *shape.getPoint(i);
ControlPoint& next = *shape.getPoint(i + 1);
cur.segment_after = SEGMENT_CURVE;
cur.segment_before = SEGMENT_CURVE;
cur.delta_after = (next.pos - cur.pos) / 3.0;
next.delta_before = (cur.pos - next.pos) / 3.0;
}
// make the curves smooth by enforcing direction constraints
FOR_EACH(p, shape.points) {
p->onUpdateLock();
}
// mark all segments as curves
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
ControlPoint& cur = *shape.getPoint(i);
ControlPoint& next = *shape.getPoint(i + 1);
cur.segment_after = SEGMENT_CURVE;
cur.segment_before = SEGMENT_CURVE;
cur.delta_after = (next.pos - cur.pos) / 3.0;
next.delta_before = (cur.pos - next.pos) / 3.0;
}
// make the curves smooth by enforcing direction constraints
FOR_EACH(p, shape.points) {
p->onUpdateLock();
}
}
/// Convert almost straight curves in a symbol shape to lines
void straighten(SymbolShape& shape) {
const double treshold = 0.2;
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
ControlPoint& cur = *shape.getPoint(i);
ControlPoint& next = *shape.getPoint(i + 1);
Vector2D ab = (next.pos - cur.pos).normalized();
Vector2D aa = cur.delta_after.normalized();
Vector2D bb = next.delta_before.normalized();
// if the area beneath the polygon formed by the handles is small
// then it is a straight line
double cpDot = abs(cross(aa,ab)) + abs(cross(bb,ab));
if (cpDot < treshold) {
cur.segment_after = next.segment_before = SEGMENT_LINE;
cur.delta_after = next.delta_before = Vector2D();
cur.lock = next.lock = LOCK_FREE;
}
}
const double treshold = 0.2;
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
ControlPoint& cur = *shape.getPoint(i);
ControlPoint& next = *shape.getPoint(i + 1);
Vector2D ab = (next.pos - cur.pos).normalized();
Vector2D aa = cur.delta_after.normalized();
Vector2D bb = next.delta_before.normalized();
// if the area beneath the polygon formed by the handles is small
// then it is a straight line
double cpDot = abs(cross(aa,ab)) + abs(cross(bb,ab));
if (cpDot < treshold) {
cur.segment_after = next.segment_before = SEGMENT_LINE;
cur.delta_after = next.delta_before = Vector2D();
cur.lock = next.lock = LOCK_FREE;
}
}
}
/// Remove unneeded points between straight lines
void merge_lines(SymbolShape& shape) {
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
ControlPoint& cur = *shape.getPoint(i);
if (cur.segment_before != cur.segment_after) continue;
Vector2D a = shape.getPoint(i-1)->pos, b = cur.pos, c = shape.getPoint(i+1)->pos;
Vector2D ab = (a-b).normalized();
Vector2D bc = (b-c).normalized();
double angle_len = abs( atan2(ab.x,ab.y) - atan2(bc.x,bc.y)) * (a-c).lengthSqr();
bool keep = angle_len >= .0001;
if (!keep) {
shape.points.erase(shape.points.begin() + i);
i -= 1;
}
}
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
ControlPoint& cur = *shape.getPoint(i);
if (cur.segment_before != cur.segment_after) continue;
Vector2D a = shape.getPoint(i-1)->pos, b = cur.pos, c = shape.getPoint(i+1)->pos;
Vector2D ab = (a-b).normalized();
Vector2D bc = (b-c).normalized();
double angle_len = abs( atan2(ab.x,ab.y) - atan2(bc.x,bc.y)) * (a-c).lengthSqr();
bool keep = angle_len >= .0001;
if (!keep) {
shape.points.erase(shape.points.begin() + i);
i -= 1;
}
}
}
double cost_of_point_removal(SymbolShape& shape, int i);
@@ -400,83 +400,83 @@ void remove_point(SymbolShape& shape, int i);
* stop when the cost becomes too high
*/
void remove_points(SymbolShape& shape) {
const double treshold = 0.0002; // maximum cost
while (true) {
// Find the point with the lowest cost of removal
int best = -1;
double best_cost = 1e100;
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
double cost = cost_of_point_removal(shape, i);
if (cost < best_cost) {
best_cost = cost;
best = i;
}
}
if (best_cost > treshold) break;
// ... and remove it
remove_point(shape, best);
}
const double treshold = 0.0002; // maximum cost
while (true) {
// Find the point with the lowest cost of removal
int best = -1;
double best_cost = 1e100;
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
double cost = cost_of_point_removal(shape, i);
if (cost < best_cost) {
best_cost = cost;
best = i;
}
}
if (best_cost > treshold) break;
// ... and remove it
remove_point(shape, best);
}
}
/// Cost of removing point i from a symbol shape
double cost_of_point_removal(SymbolShape& shape, int i) {
ControlPoint& cur = *shape.getPoint(i);
ControlPoint& prev = *shape.getPoint(i-1);
ControlPoint& next = *shape.getPoint(i+1);
if (cur.lock != LOCK_DIR) return 1e100; // don't remove corners
Vector2D before = cur.delta_before;
Vector2D after = cur.delta_after;
Vector2D ac = prev.pos - next.pos;
// Based on SinglePointRemoveAction
double bl = before.length() + 0.00001; // prevent division by 0
double al = after.length() + 0.00001;
double totl = bl + al;
// set new handle sizes
Vector2D after0 = prev.delta_after * totl / bl;
Vector2D before2 = next.delta_before * totl / al;
// determine closest point on the merged curve
BezierCurve c(prev.pos, prev.pos + after0, next.pos + before2, next.pos);
double t = bl/totl;
Vector2D np = cur.pos - c.pointAt(t);
// cost is distance to new point * length of line ~= area added/removed from shape
return np.length() * ac.length();
ControlPoint& cur = *shape.getPoint(i);
ControlPoint& prev = *shape.getPoint(i-1);
ControlPoint& next = *shape.getPoint(i+1);
if (cur.lock != LOCK_DIR) return 1e100; // don't remove corners
Vector2D before = cur.delta_before;
Vector2D after = cur.delta_after;
Vector2D ac = prev.pos - next.pos;
// Based on SinglePointRemoveAction
double bl = before.length() + 0.00001; // prevent division by 0
double al = after.length() + 0.00001;
double totl = bl + al;
// set new handle sizes
Vector2D after0 = prev.delta_after * totl / bl;
Vector2D before2 = next.delta_before * totl / al;
// determine closest point on the merged curve
BezierCurve c(prev.pos, prev.pos + after0, next.pos + before2, next.pos);
double t = bl/totl;
Vector2D np = cur.pos - c.pointAt(t);
// cost is distance to new point * length of line ~= area added/removed from shape
return np.length() * ac.length();
}
/// Remove a point from a bezier curve
/** See SinglePointRemoveAction for algorithm */
void remove_point(SymbolShape& shape, int i) {
ControlPoint& cur = *shape.getPoint(i);
ControlPoint& prev = *shape.getPoint(i-1);
ControlPoint& next = *shape.getPoint(i+1);
Vector2D before = cur.delta_before;
Vector2D after = cur.delta_after;
// Based on SinglePointRemoveAction
double bl = before.length() + 0.00001; // prevent division by 0
double al = after.length() + 0.00001;
double totl = bl + al;
// set new handle sizes
prev.delta_after *= totl / bl;
next.delta_before *= totl / al;
// remove
shape.points.erase(shape.points.begin() + i);
ControlPoint& cur = *shape.getPoint(i);
ControlPoint& prev = *shape.getPoint(i-1);
ControlPoint& next = *shape.getPoint(i+1);
Vector2D before = cur.delta_before;
Vector2D after = cur.delta_after;
// Based on SinglePointRemoveAction
double bl = before.length() + 0.00001; // prevent division by 0
double al = after.length() + 0.00001;
double totl = bl + al;
// set new handle sizes
prev.delta_after *= totl / bl;
next.delta_before *= totl / al;
// remove
shape.points.erase(shape.points.begin() + i);
}
void simplify_symbol_shape(SymbolShape& shape) {
mark_corners(shape);
merge_corners(shape);
for (int i = 0 ; i < 3 ; ++i) {
avarage(shape);
}
convert_to_curves(shape);
remove_points(shape);
straighten(shape);
merge_lines(shape);
mark_corners(shape);
merge_corners(shape);
for (int i = 0 ; i < 3 ; ++i) {
avarage(shape);
}
convert_to_curves(shape);
remove_points(shape);
straighten(shape);
merge_lines(shape);
}
void simplify_symbol(Symbol& symbol) {
FOR_EACH(pb, symbol.parts) {
if (SymbolShape* p = pb->isSymbolShape()) {
simplify_symbol_shape(*p);
}
}
FOR_EACH(pb, symbol.parts) {
if (SymbolShape* p = pb->isSymbolShape()) {
simplify_symbol_shape(*p);
}
}
}
+133 -133
View File
@@ -22,15 +22,15 @@
/// The file format of MSE1 files
class MSE1FileFormat : public FileFormat {
public:
virtual String extension() { return _("mse"); }
virtual String name() { return _("Magic Set Editor version 1 files (*.mse)"); }
virtual bool canImport() { return true; }
virtual bool canExport(const Game&) { return false; }
virtual SetP importSet(const String& filename);
virtual String extension() { return _("mse"); }
virtual String name() { return _("Magic Set Editor version 1 files (*.mse)"); }
virtual bool canImport() { return true; }
virtual bool canExport(const Game&) { return false; }
virtual SetP importSet(const String& filename);
};
FileFormatP mse1_file_format() {
return intrusive(new MSE1FileFormat());
return intrusive(new MSE1FileFormat());
}
// ----------------------------------------------------------------------------- : Importing
@@ -39,134 +39,134 @@ FileFormatP mse1_file_format() {
void read_mse1_card(Set& set, wxFileInputStream& f, wxTextInputStream& file);
SetP MSE1FileFormat::importSet(const String& filename) {
wxFileInputStream f(filename);
#ifdef UNICODE
wxTextInputStream file(f, _('\n'), wxConvLibc);
#else
wxTextInputStream file(f);
#endif
// create set
SetP set(new Set(Game::byName(_("magic"))));
// file version check
String format = file.ReadLine();
if (format.substr(0,8) != _("MTG Set8")) {
throw ParseError(_("Expected MSE format version 8\nTo convert files made with older versions of Magic Set Editor:\n 1. Download the latest version 1 from http:;//magicsetedtitor.sourceforge.net\n 2. Open the set, then save the set\n 3. Try to open them again in this program."));
}
// read general info
set->value<TextValue>(_("title")) .value = file.ReadLine();
set->value<TextValue>(_("artist")) .value = file.ReadLine();
set->value<TextValue>(_("copyright")).value = file.ReadLine();
file.ReadLine(); // border color, ignored
String stylesheet = file.ReadLine();
set->apprentice_code = file.ReadLine(); // apprentice prefix
file.ReadLine(); // 'formatN'?, not even used by MSE1 :S, ignored
file.ReadLine(); // 'formatS'?, same, ignored
file.ReadLine(); // symbol filename, ignored
file.ReadLine(); // use black symbol for all rarities, ignored
String desc, line;
while (!f.Eof()) {
line = file.ReadLine();
if (line == _("\xFF")) break;
desc += line;
}
set->value<TextValue>(_("description")).value = desc;
// load stylesheet
if (stylesheet.substr(0,3) == _("old")) {
try {
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("old"));
} catch (const Error&) {
// If old style doesn't work try the new one
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("new"));
}
} else {
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("new"));
}
// read cards
CardP current_card;
while (!f.Eof()) {
read_mse1_card(*set, f, file);
}
// done
set->validate();
return set;
wxFileInputStream f(filename);
#ifdef UNICODE
wxTextInputStream file(f, _('\n'), wxConvLibc);
#else
wxTextInputStream file(f);
#endif
// create set
SetP set(new Set(Game::byName(_("magic"))));
// file version check
String format = file.ReadLine();
if (format.substr(0,8) != _("MTG Set8")) {
throw ParseError(_("Expected MSE format version 8\nTo convert files made with older versions of Magic Set Editor:\n 1. Download the latest version 1 from http:;//magicsetedtitor.sourceforge.net\n 2. Open the set, then save the set\n 3. Try to open them again in this program."));
}
// read general info
set->value<TextValue>(_("title")) .value = file.ReadLine();
set->value<TextValue>(_("artist")) .value = file.ReadLine();
set->value<TextValue>(_("copyright")).value = file.ReadLine();
file.ReadLine(); // border color, ignored
String stylesheet = file.ReadLine();
set->apprentice_code = file.ReadLine(); // apprentice prefix
file.ReadLine(); // 'formatN'?, not even used by MSE1 :S, ignored
file.ReadLine(); // 'formatS'?, same, ignored
file.ReadLine(); // symbol filename, ignored
file.ReadLine(); // use black symbol for all rarities, ignored
String desc, line;
while (!f.Eof()) {
line = file.ReadLine();
if (line == _("\xFF")) break;
desc += line;
}
set->value<TextValue>(_("description")).value = desc;
// load stylesheet
if (stylesheet.substr(0,3) == _("old")) {
try {
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("old"));
} catch (const Error&) {
// If old style doesn't work try the new one
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("new"));
}
} else {
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("new"));
}
// read cards
CardP current_card;
while (!f.Eof()) {
read_mse1_card(*set, f, file);
}
// done
set->validate();
return set;
}
void read_mse1_card(Set& set, wxFileInputStream& f, wxTextInputStream& file) {
CardP card(new Card(*set.game));
while (!f.Eof()) {
// read a line
String line = file.ReadLine();
if (line.empty()) continue;
Char type = line.GetChar(0);
line = line.substr(1);
// interpret this line
switch (type) {
case 'A': { // done
set.cards.push_back(card);
return;
} case 'B': { // name
card->value<TextValue>(_("name")) .value.assign(line);
break;
} case 'C': case 'D': { // image filename
String image_file = set.newFileName(_("image"),_("")); // a new unique name in the package
if (wxCopyFile(line, set.nameOut(image_file), true)) {
card->value<ImageValue>(_("image")) .filename = image_file;
}
break;
} case 'E': { // super type
card->value<TextValue>(_("super type")) .value.assign(line);
break;
} case 'F': { // sub type
card->value<TextValue>(_("sub type")) .value.assign(line);
break;
} case 'G': { // casting cost
card->value<TextValue>(_("casting cost")).value.assign(line);
break;
} case 'H': { // rarity
String rarity;
if (line == _("(U)")) rarity = _("uncommon");
else if (line == _("(R)")) rarity = _("rare");
else rarity = _("common");
card->value<ChoiceValue>(_("rarity")) .value.assign(rarity);
break;
} case 'I': { // power/thoughness
size_t pos = line.find_first_of(_('/'));
if (pos != String::npos) {
card->value<TextValue>(_("power")) .value.assign(line.substr(0, pos));
card->value<TextValue>(_("toughness")) .value.assign(line.substr(pos+1));
}
break;
} case 'J': { // rule text or part of text
Defaultable<String>& text = card->value<TextValue>(_("rule text")).value;
if (!text().empty()) text.mutate() += _('\n');
text.mutate() += line;
break;
} case 'K': { // flavor text or part of text
Defaultable<String>& text = card->value<TextValue>(_("flavor text")).value;
if (!text().empty()) text.mutate() += _('\n');
text.mutate() += line;
break;
} case 'L': { // card color (if not default)
// decode color
String color;
if (line == _("1")) color = _("white");
else if (line == _("2")) color = _("blue");
else if (line == _("3")) color = _("black");
else if (line == _("4")) color = _("red");
else if (line == _("5")) color = _("green");
else if (line == _("6")) color = _("colorless");
else if (line == _("7")) color = _("land");
else if (line == _("9")) color = _("multicolor");
else color = _("colorless");
card->value<ChoiceValue>(_("card color")).value.assign(color);
break;
} default: {
throw ParseError(_("Not a valid MSE1 file"));
}
}
}
CardP card(new Card(*set.game));
while (!f.Eof()) {
// read a line
String line = file.ReadLine();
if (line.empty()) continue;
Char type = line.GetChar(0);
line = line.substr(1);
// interpret this line
switch (type) {
case 'A': { // done
set.cards.push_back(card);
return;
} case 'B': { // name
card->value<TextValue>(_("name")) .value.assign(line);
break;
} case 'C': case 'D': { // image filename
String image_file = set.newFileName(_("image"),_("")); // a new unique name in the package
if (wxCopyFile(line, set.nameOut(image_file), true)) {
card->value<ImageValue>(_("image")) .filename = image_file;
}
break;
} case 'E': { // super type
card->value<TextValue>(_("super type")) .value.assign(line);
break;
} case 'F': { // sub type
card->value<TextValue>(_("sub type")) .value.assign(line);
break;
} case 'G': { // casting cost
card->value<TextValue>(_("casting cost")).value.assign(line);
break;
} case 'H': { // rarity
String rarity;
if (line == _("(U)")) rarity = _("uncommon");
else if (line == _("(R)")) rarity = _("rare");
else rarity = _("common");
card->value<ChoiceValue>(_("rarity")) .value.assign(rarity);
break;
} case 'I': { // power/thoughness
size_t pos = line.find_first_of(_('/'));
if (pos != String::npos) {
card->value<TextValue>(_("power")) .value.assign(line.substr(0, pos));
card->value<TextValue>(_("toughness")) .value.assign(line.substr(pos+1));
}
break;
} case 'J': { // rule text or part of text
Defaultable<String>& text = card->value<TextValue>(_("rule text")).value;
if (!text().empty()) text.mutate() += _('\n');
text.mutate() += line;
break;
} case 'K': { // flavor text or part of text
Defaultable<String>& text = card->value<TextValue>(_("flavor text")).value;
if (!text().empty()) text.mutate() += _('\n');
text.mutate() += line;
break;
} case 'L': { // card color (if not default)
// decode color
String color;
if (line == _("1")) color = _("white");
else if (line == _("2")) color = _("blue");
else if (line == _("3")) color = _("black");
else if (line == _("4")) color = _("red");
else if (line == _("5")) color = _("green");
else if (line == _("6")) color = _("colorless");
else if (line == _("7")) color = _("land");
else if (line == _("9")) color = _("multicolor");
else color = _("colorless");
card->value<ChoiceValue>(_("card color")).value.assign(color);
break;
} default: {
throw ParseError(_("Not a valid MSE1 file"));
}
}
}
}
+27 -27
View File
@@ -16,34 +16,34 @@
/// The file format of MSE2 files
class MSE2FileFormat : public FileFormat {
public:
virtual String extension() { return _("mse-set"); }
virtual String matches() { return _("*.mse-set;set"); }
virtual String name() { return _("Magic Set Editor sets (*.mse-set)"); }
virtual bool canImport() { return true; }
virtual bool canExport(const Game&) { return true; }
virtual SetP importSet(const String& filename) {
wxString set_name = filename;
// Strip "/set" from the end, newer wx versions have a function for this:
// filename.EndsWith(_("/set"), &set_name);
if (filename.size() > 4 && filename.substr(filename.size()-4) == _("/set")) {
set_name = filename.substr(0, filename.size()-4);
}
SetP set(new Set);
set->open(set_name);
settings.addRecentFile(set_name);
return set;
}
virtual void exportSet(Set& set, const String& filename, bool is_copy) {
if (is_copy) {
set.saveCopy(filename);
} else {
set.saveAs(filename);
settings.addRecentFile(filename);
set.actions.setSavePoint();
}
}
virtual String extension() { return _("mse-set"); }
virtual String matches() { return _("*.mse-set;set"); }
virtual String name() { return _("Magic Set Editor sets (*.mse-set)"); }
virtual bool canImport() { return true; }
virtual bool canExport(const Game&) { return true; }
virtual SetP importSet(const String& filename) {
wxString set_name = filename;
// Strip "/set" from the end, newer wx versions have a function for this:
// filename.EndsWith(_("/set"), &set_name);
if (filename.size() > 4 && filename.substr(filename.size()-4) == _("/set")) {
set_name = filename.substr(0, filename.size()-4);
}
SetP set(new Set);
set->open(set_name);
settings.addRecentFile(set_name);
return set;
}
virtual void exportSet(Set& set, const String& filename, bool is_copy) {
if (is_copy) {
set.saveCopy(filename);
} else {
set.saveAs(filename);
settings.addRecentFile(filename);
set.actions.setSavePoint();
}
}
};
FileFormatP mse2_file_format() {
return intrusive(new MSE2FileFormat());
return intrusive(new MSE2FileFormat());
}
+225 -225
View File
@@ -25,253 +25,253 @@ DECLARE_TYPEOF_COLLECTION(CardP);
/// The file format of Mtg Editor files
class MtgEditorFileFormat : public FileFormat {
public:
virtual String extension() { return _("set"); }
virtual String name() { return _("Mtg Editor files (*.set)"); }
virtual bool canImport() { return true; }
virtual bool canExport(const Game&) { return false; }
virtual SetP importSet(const String& filename);
virtual String extension() { return _("set"); }
virtual String name() { return _("Mtg Editor files (*.set)"); }
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 <sym>, other tags are removed
void translateTags(String& value);
// 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() {
return intrusive(new MtgEditorFileFormat());
return intrusive(new MtgEditorFileFormat());
}
// ----------------------------------------------------------------------------- : Importing
SetP MtgEditorFileFormat::importSet(const String& filename) {
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 = intrusive(new 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 = intrusive(new 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")) {
try {
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("old"));
} catch (const Error&) {
// If old style doesn't work try the new one
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("new"));
}
} else {
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("new"));
}
set->validate();
return set;
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 = intrusive(new 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 = intrusive(new 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")) {
try {
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("old"));
} catch (const Error&) {
// If old style doesn't work try the new one
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("new"));
}
} else {
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("new"));
}
set->validate();
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 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;
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));
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>"), _(""));
// 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>"), _(""));
}
+70 -70
View File
@@ -23,89 +23,89 @@ DECLARE_TYPEOF_COLLECTION(CardP);
/// Convert a tagged string to MWS format: \t\t before each line beyond the first
String untag_mws(const String& str) {
// TODO : em dashes?
return replace_all(untag(curly_quotes(str,false)), _("\n"), _("\n\t\t") );
// TODO : em dashes?
return replace_all(untag(curly_quotes(str,false)), _("\n"), _("\n\t\t") );
}
//String untag_mws(const Defaultable<String>& str) {
// str.
// str.
//}
/// Code for card color in MWS format
String card_color_mws(const String& col) {
if (col == _("white")) return _("W");
if (col == _("blue")) return _("U");
if (col == _("black")) return _("B");
if (col == _("red")) return _("R");
if (col == _("green")) return _("G");
if (col == _("artifact")) return _("Art");
if (col == _("colorless")) return _("Art");
if (col.find(_("land")) != String::npos) {
return _("Lnd"); // land
} else {
return _("Gld"); // multicolor
}
if (col == _("white")) return _("W");
if (col == _("blue")) return _("U");
if (col == _("black")) return _("B");
if (col == _("red")) return _("R");
if (col == _("green")) return _("G");
if (col == _("artifact")) return _("Art");
if (col == _("colorless")) return _("Art");
if (col.find(_("land")) != String::npos) {
return _("Lnd"); // land
} else {
return _("Gld"); // multicolor
}
}
/// Code for card rarity, used for MWS and Apprentice
String card_rarity_code(const String& rarity) {
if (rarity == _("rare")) return _("R");
if (rarity == _("uncommon")) return _("U");
else return _("C");
if (rarity == _("rare")) return _("R");
if (rarity == _("uncommon")) return _("U");
else return _("C");
}
// ----------------------------------------------------------------------------- : export_mws
void export_mws(Window* parent, const SetP& set) {
if (!set->game->isMagic()) {
throw Error(_("Can only export Magic sets to Magic Workstation"));
}
// Select filename
String name = wxFileSelector(_("Export to file"),settings.default_export_dir,_(""),_(""),
_("Text files (*.txt)|*.txt|All Files|*"),
wxFD_SAVE | wxFD_OVERWRITE_PROMPT, parent);
if (name.empty()) return;
settings.default_export_dir = wxPathOnly(name);
wxBusyCursor busy;
// Open file
wxFileOutputStream f(name);
wxTextOutputStream file(f, wxEOL_DOS);
// Write header
file.WriteString(set->value<TextValue>(_("title")).value + _(" Spoiler List\n"));
file.WriteString(_("Set exported using Magic Set Editor 2, version ") + app_version.toString() + _("\n\n"));
wxDateTime now = wxDateTime::Now();
file.WriteString(_("Spoiler List created on ") + now.FormatISODate() + _(" ") + now.FormatISOTime());
file.WriteString(_("\n\n"));
// Write cards
FOR_EACH(card, set->cards) {
file.WriteString(_("Card Name:\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("name")).value));
file.WriteString(_("\nCard Color:\t"));
file.WriteString(card_color_mws(card->value<ChoiceValue>(_("card color")).value));
file.WriteString(_("\nMana Cost:\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("casting cost")).value));
file.WriteString(_("\nType & Class:\t"));
String sup_type = untag_mws(card->value<TextValue>(_("super type")).value);
String sub_type = untag_mws(card->value<TextValue>(_("sub type")).value);
if (sub_type.empty()) {
file.WriteString(sup_type);
} else {
file.WriteString(sup_type + _(" - ") + sub_type);
}
file.WriteString(_("\nPow/Tou:\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("pt")).value));
file.WriteString(_("\nCard Text:\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("rule text")).value));
file.WriteString(_("\nFlavor Text:\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("flavor text")).value));
file.WriteString(_("\nArtist:\t\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("illustrator")).value));
file.WriteString(_("\nRarity:\t\t"));
file.WriteString(card_rarity_code(card->value<ChoiceValue>(_("rarity")).value));
file.WriteString(_("\nCard #:\t\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("card number")).value));
file.WriteString(_("\n\n"));
}
if (!set->game->isMagic()) {
throw Error(_("Can only export Magic sets to Magic Workstation"));
}
// Select filename
String name = wxFileSelector(_("Export to file"),settings.default_export_dir,_(""),_(""),
_("Text files (*.txt)|*.txt|All Files|*"),
wxFD_SAVE | wxFD_OVERWRITE_PROMPT, parent);
if (name.empty()) return;
settings.default_export_dir = wxPathOnly(name);
wxBusyCursor busy;
// Open file
wxFileOutputStream f(name);
wxTextOutputStream file(f, wxEOL_DOS);
// Write header
file.WriteString(set->value<TextValue>(_("title")).value + _(" Spoiler List\n"));
file.WriteString(_("Set exported using Magic Set Editor 2, version ") + app_version.toString() + _("\n\n"));
wxDateTime now = wxDateTime::Now();
file.WriteString(_("Spoiler List created on ") + now.FormatISODate() + _(" ") + now.FormatISOTime());
file.WriteString(_("\n\n"));
// Write cards
FOR_EACH(card, set->cards) {
file.WriteString(_("Card Name:\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("name")).value));
file.WriteString(_("\nCard Color:\t"));
file.WriteString(card_color_mws(card->value<ChoiceValue>(_("card color")).value));
file.WriteString(_("\nMana Cost:\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("casting cost")).value));
file.WriteString(_("\nType & Class:\t"));
String sup_type = untag_mws(card->value<TextValue>(_("super type")).value);
String sub_type = untag_mws(card->value<TextValue>(_("sub type")).value);
if (sub_type.empty()) {
file.WriteString(sup_type);
} else {
file.WriteString(sup_type + _(" - ") + sub_type);
}
file.WriteString(_("\nPow/Tou:\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("pt")).value));
file.WriteString(_("\nCard Text:\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("rule text")).value));
file.WriteString(_("\nFlavor Text:\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("flavor text")).value));
file.WriteString(_("\nArtist:\t\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("illustrator")).value));
file.WriteString(_("\nRarity:\t\t"));
file.WriteString(card_rarity_code(card->value<ChoiceValue>(_("rarity")).value));
file.WriteString(_("\nCard #:\t\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("card number")).value));
file.WriteString(_("\n\n"));
}
}