diff --git a/decode.py b/decode.py
index e6a1516..c89e2c4 100755
--- a/decode.py
+++ b/decode.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
import sys
import os
+import zipfile
+import shutil
libdir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'lib')
sys.path.append(libdir)
@@ -14,7 +16,8 @@ def exclude_sets(cardset):
return cardset == 'Unglued' or cardset == 'Unhinged' or cardset == 'Celebration'
def main(fname, oname = None, verbose = True,
- gatherer = False, for_forum = False, creativity = False, norarity = False):
+ gatherer = False, for_forum = False, for_mse = False,
+ creativity = False, norarity = False):
cards = []
valid = 0
invalid = 0
@@ -107,9 +110,19 @@ def main(fname, oname = None, verbose = True,
namediff = Namediff()
def writecards(writer):
+ if for_mse:
+ # have to prepend a massive chunk of formatting info
+ writer.write(utils.mse_prepend)
for card in cards:
- writer.write((card.format(gatherer = gatherer, for_forum = for_forum)).encode('utf-8'))
+ if for_mse:
+ writer.write(card.to_mse().encode('utf-8'))
+ else:
+ writer.write(card.format(gatherer = gatherer,
+ for_forum = for_forum).encode('utf-8'))
+
if creativity:
+ if for_mse:
+ writer.write('\tnotes:\n\t\t'.encode('utf-8'))
writer.write('~~ closest cards ~~\n'.encode('utf-8'))
nearest = cbow.nearest(card)
for dist, cardname in nearest:
@@ -126,11 +139,31 @@ def main(fname, oname = None, verbose = True,
writer.write((cardname + ': ' + str(dist) + '\n').encode('utf-8'))
writer.write('\n'.encode('utf-8'))
+ if for_mse:
+ # more formatting info
+ writer.write('version control:\n\ttype: none\napprentice code: ')
+
if oname:
if verbose:
print 'Writing output to: ' + oname
with open(oname, 'w') as ofile:
writecards(ofile)
+ if for_mse:
+ # Copy whatever output file is produced, name the copy 'set' (yes, no extension).
+ if os.path.isfile('set'):
+ print 'ERROR: tried to overwrite existing file "set" - aborting.'
+ return
+ shutil.copyfile(oname, 'set')
+ # Use the freaky mse extension instead of zip.
+ with zipfile.ZipFile(oname+'.mse-set', mode='w') as zf:
+ try:
+ # Zip up the set file into oname.mse-set.
+ zf.write('set')
+ finally:
+ if verbose:
+ print 'Made an MSE set file called ' + oname + '.mse-set.'
+ # The set file is useless outside the .mse-set, delete it.
+ os.remove('set')
else:
writecards(sys.stdout)
sys.stdout.flush()
@@ -154,9 +187,10 @@ if __name__ == '__main__':
help='the card format has no rarity field; use for legacy input')
parser.add_argument('-v', '--verbose', action='store_true',
help='verbose output')
+ parser.add_argument('-mse', '--mse', action='store_true', help='use Magic Set Editor 2 encoding; will output as .mse-set file')
args = parser.parse_args()
main(args.infile, args.outfile, verbose = args.verbose,
- gatherer = args.gatherer, for_forum = args.forum, creativity = args.creativity,
- norarity = args.norarity)
+ gatherer = args.gatherer, for_forum = args.forum, for_mse = args.mse,
+ creativity = args.creativity, norarity = args.norarity)
exit(0)
diff --git a/lib/cardlib.py b/lib/cardlib.py
index 718155a..4b88cb7 100644
--- a/lib/cardlib.py
+++ b/lib/cardlib.py
@@ -17,6 +17,11 @@ try:
import textwrap
import nltk.data
sent_tokenizer = nltk.data.load('tokenizers/punkt/english.pickle')
+ # This could me made smarter - MSE will capitalize for us after :,
+ # but we still need to capitalize the first english component of an activation
+ # cost that starts with symbols, such as {2U}, *R*emove a +1/+1 counter from @: etc.
+ def cap(s):
+ return s[:1].capitalize() + s[1:]
# This crazy thing is actually invoked as an unpass, so newlines are still
# encoded.
def sentencecase(s):
@@ -26,11 +31,26 @@ try:
for line in lines:
if line:
sentences = sent_tokenizer.tokenize(line)
- clines += [' '.join([sent.capitalize() for sent in sentences])]
+ clines += [' '.join([cap(sent) for sent in sentences])]
return utils.newline.join(clines).replace(utils.reserved_marker, utils.x_marker)
except ImportError:
+ # non-nltk implementation provided by PAK90
+ def uppercaseNewLineAndFullstop(string):
+ # ok, let's capitalize every letter after a full stop and newline.
+ # first let's find all indices of '.' and '\n'
+ indices = [0] # initialise with 0, since we always want to capitalise the first letter.
+ newlineIndices = [0] # also need to keep track of pure newlines (for planeswalkers).
+ for i in range (len(string)):
+ if string[i] == '\\':
+ indices.append(i + 1) # we want the index of the letter after the \n, so add one.
+ newlineIndices.append(i + 1)
+ if string[i] == '.' or string[i] == "=": # also handle the choice bullets.
+ indices.append(i + 2) # we want the index of the letter after the ., so we need to count the space as well.
+ indexSet = set(indices) # convert it to a set for the next part; the capitalisation.
+ return "".join(c.upper() if i in indexSet else c for i, c in enumerate(string))
+
def sentencecase(s):
- return s
+ return uppercaseNewLineAndFullstop(s)
# These are used later to determine what the fields of the Card object are called.
# Define them here because they have nothing to do with the actual format.
@@ -109,6 +129,7 @@ def fields_check_valid(fields):
else:
return not field_pt in fields
+
# These functions take a bunch of source data in some format and turn
# it into nicely labeled fields that we know how to initialize a card from.
# Both return a dict that maps field names to lists of possible values,
@@ -141,7 +162,7 @@ def fields_check_valid(fields):
# layout - string
# rarity - string
# flavor - string
-# artis - string
+# artist - string
# number - string
# multiverseid - number
# variations - list
@@ -530,7 +551,7 @@ class Card:
return outstr
- def format(self, gatherer = False, for_forum = False):
+ def format(self, gatherer = False, for_forum = False, for_mse = False):
outstr = ''
if gatherer:
cardname = titlecase(self.__dict__[field_name])
@@ -663,6 +684,111 @@ class Card:
return outstr
+ def to_mse(self):
+ outstr = ''
+
+ # need a 'card' string first
+ outstr += 'card:\n'
+
+ cardname = titlecase(self.__dict__[field_name])
+ outstr += '\tname: ' + cardname + '\n'
+
+ if self.__dict__[field_rarity]:
+ if self.__dict__[field_rarity] in utils.json_rarity_unmap:
+ rarity = utils.json_rarity_unmap[self.__dict__[field_rarity]]
+ else:
+ rarity = self.__dict__[field_rarity]
+ outstr += '\trarity: ' + rarity.lower() + '\n'
+ #if not self.parsed:
+ # outstr += ' _UNPARSED_'
+ #if not self.valid:
+ # outstr += ' _INVALID_'
+
+ if not self.__dict__[field_cost].none:
+ outstr += '\tcasting cost: ' + self.__dict__[field_cost].format().replace('{','').replace('}','')
+ outstr += '\n'
+
+ outstr += '\tsuper type: ' + ' '.join(self.__dict__[field_supertypes]
+ + self.__dict__[field_types]).title() + '\n'
+ if self.__dict__[field_subtypes]:
+ outstr += '\tsub type: ' + ' '.join(self.__dict__[field_subtypes]).title() + '\n'
+
+ if self.__dict__[field_text].text:
+ mtext = self.__dict__[field_text].text
+ mtext = transforms.text_unpass_1_choice(mtext, delimit = False)
+ mtext = transforms.text_unpass_2_counters(mtext)
+ mtext = transforms.text_unpass_3_unary(mtext)
+ mtext = transforms.text_unpass_4_symbols(mtext, False)
+ mtext = sentencecase(mtext)
+ # I don't really want these MSE specific passes in transforms,
+ # but they could be pulled out separately somewhere else in here.
+ mtext = mtext.replace(utils.this_marker, ''
+ + utils.this_marker + '')
+ mtext = transforms.text_unpass_5_cardname(mtext, cardname)
+ mtext = transforms.text_unpass_6_newlines(mtext)
+ newtext = Manatext('')
+ newtext.text = mtext
+ newtext.costs = self.__dict__[field_text].costs
+ newtext = newtext.format()
+
+ #NOT NEEDED newtext = newtext.replace(utils.this_marker, cardname) # first let's put the cardname where all the @s are.
+
+
+ # newtext = newtext.replace(utils.counter_rename + ".", "countered.") # then replace any 'uncast' at the end of a sentence with 'countered'.
+ # newtext = newtext.replace(utils.dash_marker, u'\u2014') # also replace the ~ with a u2014 for choices.
+ # newtext = newtext.replace(utils.counter_rename, "counter") # then replace all the mid-sentence 'uncast' with 'counter'.
+ # newtext = newtext.replace('{','').replace('}','') # now we encase mana/tap symbols with the correct tags for mse.
+ # linecount = newtext.count('\n') + 1 # adding 1 because no newlines means 1 line, 1 newline means 2 lines etc.
+
+ # newtext = sentencecase(newtext) # make all the things uppercase!
+
+ # # done after uppercasing everything because string[i] == u2022 doesn't work apparently.
+ # newtext = newtext.replace(utils.bullet_marker, u'\u2022') # replace the = with a u2022.
+
+ # used later
+ linecount = newtext.count('\n') + 1 # adding 1 because no newlines means 1 line, 1 newline means 2 lines etc.
+
+ # actually really important
+ newtext = newtext.replace('{','').replace('}','') # now we encase mana/tap symbols with the correct tags for mse.
+
+ newlineIndices = [0] # also need to keep track of pure newlines (for planeswalkers).
+ for i in range (len(newtext)):
+ if newtext[i] == '\n':
+ newlineIndices.append(i + 1)
+
+ # need to do Special Things if it's a planeswalker.
+ if "planeswalker" in str(self.__dict__[field_types]): # for some reason this is in types, not supertypes...
+ outstr += '\tstylesheet: m15-planeswalker\n' # set the proper card style for a 3-line walker.
+
+ # set up the loyalty cost fields using regex to find how many there are.
+ i = 0
+ lcost_regex = r'[-+]?\d+: ' # 1+ figures, might be 0.
+ for costs in re.findall(lcost_regex, newtext):
+ i += 1
+ outstr += '\tloyalty cost ' + str(i) + ': ' + costs + '\n'
+ # sub out the loyalty costs.
+ newtext = re.sub(lcost_regex, '', newtext)
+
+ #newtext = sentencecase(newtext) # we need to uppercase again; previous uppercase call didn't work due to loyalty costs being there.
+
+ if self.__dict__[field_loyalty]:
+ outstr += '\tloyalty: ' + utils.from_unary(self.__dict__[field_loyalty]) + '\n'
+
+ newtext = newtext.replace('\n','\n\t\t')
+ outstr += '\trule text:\n\t\t' + newtext + '\n'
+
+ if self.__dict__[field_pt]:
+ ptstring = utils.from_unary(self.__dict__[field_pt]).split('/')
+ if (len(ptstring) > 1): #really don't want to be accessing anything nonexistent.
+ outstr += '\tpower: ' + ptstring[0] + '\n'
+ outstr += '\ttoughness: ' + ptstring[1] + '\n'
+ #outstr += '\n'
+
+ # now append all the other useless fields that the setfile expects.
+ outstr += '\thas styling: false\n\tnotes:\n\ttime created:2015-07-20 22:53:07\n\ttime modified:2015-07-20 22:53:08\n\textra data:\n\timage:\n\tcard code text:\n\tcopyright:\n\timage 2:\n\tcopyright 2: '
+
+ return outstr
+
def vectorize(self):
ld = '('
rd = ')'
diff --git a/lib/utils.py b/lib/utils.py
index bf51e6a..a2fc877 100644
--- a/lib/utils.py
+++ b/lib/utils.py
@@ -6,6 +6,9 @@ import re
import config
+# special chunk of text that Magic Set Editor 2 requires at the start of all set files.
+mse_prepend = 'mse version: 0.3.8\ngame: magic\nstylesheet: m15\nset info:\n\tsymbol:\nstyling:\n\tmagic-m15:\n\t\ttext box mana symbols: magic-mana-small.mse-symbol-font\n\t\toverlay:\n\tmagic-m15-clear:\n\t\ttext box mana symbols: magic-mana-small.mse-symbol-font\n\t\toverlay: \n\tmagic-m15-extra-improved:\n\t\ttext box mana symbols: magic-mana-small.mse-symbol-font\n\t\tpt box symbols: magic-pt-symbols-extra.mse-symbol-font\n\t\toverlay: \n\tmagic-m15-planeswalker:\n\t\ttext box mana symbols: magic-mana-small.mse-symbol-font\n\t\toverlay: \n\tmagic-m15-planeswalker-promo-black:\n\t\ttext box mana symbols: magic-mana-small.mse-symbol-font\n\t\toverlay: \n\tmagic-m15-promo-dka:\n\t\ttext box mana symbols: magic-mana-small.mse-symbol-font\n\t\toverlay: \n\tmagic-m15-token-clear:\n\t\ttext box mana symbols: magic-mana-small.mse-symbol-font\n\t\toverlay: \n\tmagic-new-planeswalker:\n\t\ttext box mana symbols: magic-mana-small.mse-symbol-font\n\t\toverlay: \n\tmagic-new-planeswalker-4abil:\n\t\ttext box mana symbols: magic-mana-small.mse-symbol-font\n\t\toverlay: \n\tmagic-new-planeswalker-clear:\n\t\ttext box mana symbols: magic-mana-small.mse-symbol-font\n\t\toverlay: \n\tmagic-new-planeswalker-promo-black:\n\t\ttext box mana symbols: magic-mana-small.mse-symbol-font\n\t\toverlay: \n'
+
# separators
cardsep = config.cardsep
fieldsep = config.fieldsep