mirror of
https://github.com/Relintai/mtg-forge-ios.git
synced 2024-12-25 21:17:12 +01:00
29260c205d
git-svn-id: http://svn.slightlymagic.net/forge/trunk@35700 269b9781-a132-4a9b-9d4e-f004f1b56b58
256 lines
9.4 KiB
Python
Executable File
256 lines
9.4 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# Modify key directories here
|
|
# Recommended parameters: -i -w (will add ! to all unsupported decks) OR -i -f (will only convert supported decks)
|
|
CARDSFOLDER = "../../res/cardsfolder"
|
|
DECKFOLDER = "."
|
|
OUT_DECKFOLDER = "./ForgeDecks"
|
|
|
|
import argparse, os, re
|
|
|
|
print("Agetian's MtgDecks.net DEC to MTG Forge Deck Converter v3.5\n")
|
|
|
|
parser = argparse.ArgumentParser(description="Convert MtgDecks.net DEC to Forge DCK.")
|
|
|
|
parser.add_argument("-f", action="store_true", help="only convert decks that have 100% card support in Forge")
|
|
parser.add_argument("-i", action="store_true", help="add MtgDecksNet deck ID to the output deck name for uniqueness")
|
|
parser.add_argument("-t", help="only convert decks that belong to the specified format")
|
|
parser.add_argument("-w", action="store_true", help="add [!] to the name of decks that have unsupported cards")
|
|
parser.add_argument("-U", action="store_true", help="preserve UTF-8 characters in file names")
|
|
parser.add_argument("-D", action="store_true", help="sort converted decks into folders according to format")
|
|
parser.add_argument("-P", action="store_true", help="convert period (.) to underscore (_) in deck names")
|
|
|
|
args = parser.parse_args()
|
|
|
|
# simple structural self-test (can this tool work?)
|
|
if not (os.access(os.path.join(CARDSFOLDER,"a","abu_jafar.txt"),os.F_OK) or os.access(os.path.join("decks"),os.F_OK)):
|
|
print("Fatal error:\n This utility requires the 'cardsfolder' folder with unpacked card files at " + CARDSFOLDER + " and the 'decks' folder with .dck files at " + DECKFOLDER + " in order to operate. Exiting.")
|
|
exit(1)
|
|
|
|
# basic variables
|
|
cardlist = {}
|
|
total_cards = 0
|
|
ai_playable_cards = 0
|
|
total_decks = 0
|
|
playable_decks = 0
|
|
nonplayable_in_deck = 0
|
|
|
|
# main algorithm
|
|
print("Loading cards...")
|
|
for root, dirs, files in os.walk(CARDSFOLDER):
|
|
for name in files:
|
|
if name.find(".txt") != -1:
|
|
total_cards += 1
|
|
fullpath = os.path.join(root, name)
|
|
cardtext = open(fullpath).read()
|
|
cardtext_lower = cardtext.lower()
|
|
cardname_literal = cardtext.replace('\r','').split('\n')[0].split(':')
|
|
cardname = ":".join(cardname_literal[1:]).strip()
|
|
if (cardtext_lower.find("alternatemode:split") != -1) or (cardtext_lower.find("alternatemode: split") != -1):
|
|
# split card, special handling needed
|
|
cardsplittext = cardtext.replace('\r','').split('\n')
|
|
cardnames = []
|
|
for line in cardsplittext:
|
|
if line.lower().find("name:") != -1:
|
|
cardnames.extend([line.split('\n')[0].split(':')[1]])
|
|
cardname = " // ".join(cardnames)
|
|
if cardtext.lower().find("remaideck") != -1:
|
|
cardlist[cardname] = 0
|
|
else:
|
|
cardlist[cardname] = 1
|
|
ai_playable_cards += 1
|
|
|
|
perc_playable = (float(ai_playable_cards) / total_cards) * 100
|
|
perc_unplayable = ((float(total_cards) - ai_playable_cards) / total_cards) * 100
|
|
|
|
print("Loaded %d cards, among them %d playable by the AI (%d%%), %d unplayable by the AI (%d%%).\n" % (total_cards, ai_playable_cards, perc_playable, total_cards - ai_playable_cards, perc_unplayable))
|
|
|
|
re_Metadata = '^//(.*) a (.*) deck by (.*)$'
|
|
re_Metadata2 = '^//(.*) a ([A-Za-z]+) MTG deck played by (.*) in (.*) - MTGDECKS.NET.*$'
|
|
re_DeckID = '^([0-9]+)\.dec$'
|
|
re_Maindeck = '^([0-9]+) (.*)$'
|
|
re_Sideboard = '^SB:[ \t]+([0-9]+) (.*)$'
|
|
re_Timeinfo = '<!--.*>$'
|
|
|
|
unsupportedList = []
|
|
badChars = ['/', '\\', '*']
|
|
|
|
print("Converting decks...")
|
|
for root, dirs, files in os.walk(DECKFOLDER):
|
|
for name in files:
|
|
if name.find(".dec") != -1:
|
|
print("Converting deck: " + name + "...")
|
|
deck_id = -1
|
|
s_DeckID = re.search(re_DeckID, name)
|
|
if s_DeckID:
|
|
deck_id = s_DeckID.groups()[0]
|
|
fullpath = os.path.join(root, name)
|
|
deckdata = open(fullpath).readlines()
|
|
name = ""
|
|
creator = ""
|
|
format = ""
|
|
maindeck = []
|
|
maindeck_cards = 0
|
|
sideboard = []
|
|
supported = True
|
|
deckHasUnsupportedCards = False
|
|
|
|
for line in deckdata:
|
|
#line = line.replace("\xE1", "a")
|
|
#line = line.replace("\xFB", "u")
|
|
#line = line.replace("\xE9", "e")
|
|
line = line.replace("\xC3\x86", "AE")
|
|
line = line.replace("\xC3\xA9", "e")
|
|
line = line.replace("\xC3\xBB", "u")
|
|
line = line.replace("\xC3\xA1", "a")
|
|
line = line.replace("\xC3\xAD", "i")
|
|
#line = line.replace("Unravel the Aether", "Unravel the AEther")
|
|
#line = line.replace("Aether", "AEther")
|
|
line = line.replace("Chandra, Roaring Flame", "Chandra, Fire of Kaladesh")
|
|
line = line.replace("Nissa, Sage Animist", "Nissa, Vastwood Seer")
|
|
line = line.replace("Neck Breaker", "Breakneck Rider")
|
|
line = line.replace("\xC3\xB6", "o")
|
|
line = line.replace("\x97", "-")
|
|
line = line.replace("\x91", "'")
|
|
line = line.replace("\xFB", "u")
|
|
line = line.replace("\xFC", "u")
|
|
line = line.replace("\xC4", "A")
|
|
if line[0] != "/" and line.find(" // ") == -1:
|
|
line = line.replace("/"," // ")
|
|
timepos = line.find("<!")
|
|
if timepos > -1:
|
|
line = line[0:timepos]
|
|
isCardSupported = True
|
|
mode = 0
|
|
s_Metadata = re.search(re_Metadata, line)
|
|
if not s_Metadata:
|
|
s_Metadata = re.search(re_Metadata2, line)
|
|
mode = 1
|
|
if s_Metadata:
|
|
name = s_Metadata.groups()[0].strip()
|
|
format = s_Metadata.groups()[1].strip()
|
|
creator = s_Metadata.groups()[2].strip()
|
|
event = ""
|
|
if mode == 1:
|
|
event = s_Metadata.groups()[3].strip()
|
|
for badChar in badChars:
|
|
event = event.replace(badChar, "-")
|
|
if args.t and args.t != format:
|
|
print("Skipping an off-format deck " + name + " (format = " + format + ")")
|
|
supported = False
|
|
continue
|
|
s_Maindeck = re.search(re_Maindeck, line)
|
|
if s_Maindeck:
|
|
cardAmount = s_Maindeck.groups()[0].strip()
|
|
cardName = s_Maindeck.groups()[1].strip()
|
|
if not cardlist.has_key(cardName) and not cardlist.has_key(cardName.replace("Aether", "AEther")) and not cardlist.has_key(cardName.replace("AEther", "Aether")):
|
|
print("Unsupported card (MAIN): " + cardName)
|
|
if args.f:
|
|
supported = False
|
|
else:
|
|
isCardSupported = False
|
|
deckHasUnsupportedCards = True
|
|
if not cardName in unsupportedList:
|
|
unsupportedList.extend([cardName])
|
|
if cardlist.has_key(cardName):
|
|
mdline = cardAmount + " " + cardName
|
|
elif cardlist.has_key(cardName.replace("Aether", "AEther")):
|
|
mdline = cardAmount + " " + cardName.replace("Aether", "AEther")
|
|
elif cardlist.has_key(cardName.replace("AEther", "Aether")):
|
|
mdline = cardAmount + " " + cardName.replace("AEther", "Aether")
|
|
else:
|
|
mdline = cardAmount + " " + cardName # for the purposes of unsupported cards
|
|
if isCardSupported:
|
|
maindeck.extend([mdline])
|
|
else:
|
|
maindeck.extend(["#"+mdline])
|
|
maindeck_cards += int(cardAmount)
|
|
continue
|
|
s_Sideboard = re.search(re_Sideboard, line)
|
|
if s_Sideboard:
|
|
cardAmount = s_Sideboard.groups()[0].strip()
|
|
cardName = s_Sideboard.groups()[1].strip()
|
|
if not cardlist.has_key(cardName) and not cardlist.has_key(cardName.replace("Aether", "AEther")) and not cardlist.has_key(cardName.replace("AEther", "Aether")):
|
|
print("Unsupported card (SIDE): " + cardName)
|
|
if args.f:
|
|
supported = False
|
|
else:
|
|
isCardSupported = False
|
|
deckHasUnsupportedCards = True
|
|
if not cardName in unsupportedList:
|
|
unsupportedList.extend([cardName])
|
|
if cardlist.has_key(cardName):
|
|
sdline = cardAmount + " " + cardName
|
|
elif cardlist.has_key(cardName.replace("Aether", "AEther")):
|
|
sdline = cardAmount + " " + cardName.replace("Aether", "AEther")
|
|
elif cardlist.has_key(cardName.replace("AEther", "Aether")):
|
|
sdline = cardAmount + " " + cardName.replace("AEther", "Aether")
|
|
else:
|
|
sdline = cardAmount + " " + cardName # for the purposes of unsupported cards
|
|
if isCardSupported:
|
|
sideboard.extend([sdline])
|
|
else:
|
|
sideboard.extend(["#"+sdline])
|
|
continue
|
|
|
|
# convert here
|
|
if supported and len(maindeck) > 0:
|
|
if creator != "":
|
|
deckname = creator + " - " + name + " ("
|
|
else:
|
|
deckname = name + " ("
|
|
deckname += format
|
|
if args.i and deck_id > -1:
|
|
if format != "":
|
|
deckname += ", #" + deck_id
|
|
else:
|
|
deckname += "#" + deck_id
|
|
deckname += ")"
|
|
#deckname = (c for c in deckname if ord(c) < 128)
|
|
if not args.U:
|
|
deckname = re.sub(r'[^\x00-\x7F]', '@', deckname)
|
|
deckname = re.sub(r'[/\\]', '-', deckname)
|
|
deckname = re.sub(r'[?*]', '_', deckname)
|
|
if args.w and deckHasUnsupportedCards:
|
|
deckname += " [!]"
|
|
if args.D:
|
|
if deckHasUnsupportedCards:
|
|
outname = "Unsupported" + "/" + format + "/"
|
|
pathToUnsupported = "./" + OUT_DECKFOLDER + "/Unsupported/" + format
|
|
if not os.path.isdir(pathToUnsupported):
|
|
os.makedirs(pathToUnsupported)
|
|
else:
|
|
outname = format + "/"
|
|
if not os.path.isdir("./" + OUT_DECKFOLDER + "/" + format):
|
|
os.makedirs("./" + OUT_DECKFOLDER + "/" + format)
|
|
else:
|
|
outname = ""
|
|
outname += deckname + ".dck"
|
|
print ("Writing converted deck: " + outname)
|
|
dck = open(OUT_DECKFOLDER + "/" + outname, "w")
|
|
|
|
if event:
|
|
dck.write("#EVENT:"+event+"\n")
|
|
|
|
dck.write("[metadata]\n")
|
|
dck.write("Name="+deckname+"\n")
|
|
dck.write("[general]\n")
|
|
dck.write("Constructed\n")
|
|
dck.write("[Main]\n")
|
|
for m in maindeck:
|
|
dck.write(m+"\n")
|
|
if not (format == "Commander" and len(sideboard) == 1):
|
|
dck.write("[Sideboard]\n")
|
|
else:
|
|
dck.write("[Commander]\n")
|
|
for s in sideboard:
|
|
dck.write(s+"\n")
|
|
|
|
# write out unsupported cards
|
|
log = open("dec2forge.log", "w")
|
|
log.write("Unsupported cards:\n")
|
|
for uns in unsupportedList:
|
|
log.write(uns+"\n")
|
|
|