- Adding deck conversion tools that I'm using to convert MTGDecks.net deck lists and XMage cube definitions. Might be useful as a basis for converters from other deck formats too.

This commit is contained in:
Agetian
2017-01-23 15:16:57 +00:00
parent 5c6a184720
commit 4233e1dbc4
5 changed files with 647 additions and 0 deletions

4
.gitattributes vendored
View File

@@ -20746,6 +20746,10 @@ forge-gui/src/main/java/forge/util/package-info.java -text
forge-gui/src/main/resources/log4jConfig.config -text forge-gui/src/main/resources/log4jConfig.config -text
forge-gui/src/main/resources/proxy-template.ftl -text forge-gui/src/main/resources/proxy-template.ftl -text
forge-gui/src/site/apt/index.apt -text forge-gui/src/site/apt/index.apt -text
forge-gui/tools/DeckConversionTools/mtgdecksnet_convert.py -text
forge-gui/tools/DeckConversionTools/mtgdecksnet_decksort.py -text
forge-gui/tools/DeckConversionTools/mtgdecksnet_decksort_EDH.py -text
forge-gui/tools/DeckConversionTools/xmage_cube_convert.py -text
forge-gui/tools/EditionTracking.py -text forge-gui/tools/EditionTracking.py -text
forge-gui/tools/PerSetTracking.py svneol=native#text/x-python forge-gui/tools/PerSetTracking.py svneol=native#text/x-python
forge-gui/tools/RankingScraper.py -text forge-gui/tools/RankingScraper.py -text

View File

@@ -0,0 +1,250 @@
#!/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.3a\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")
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")
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")

View File

@@ -0,0 +1,159 @@
#!/usr/bin/env python
CARDSFOLDER = "../../res/cardsfolder"
EDITIONS = "../../res/editions"
DECKFOLDER = "."
import argparse, os, re, shutil
print("Agetian's MTG Forge Deck Sorter v1.4\n")
parser = argparse.ArgumentParser(description="Sort decks into folders (by edition).")
parser.add_argument("-d", action="store_true", help="physically delete original (unsorted) decks")
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) or os.access(os.path.join(EDITIONS,"Alara Reborn.txt"),os.F_OK)):
print("Fatal error:\n This utility requires the 'cardsfolder' folder with unpacked card files at " + CARDSFOLDER + ", 'editions' folder at " + EDITIONS + " and the 'decks' folder with .dck files at " + DECKFOLDER + " in order to operate. Exiting.")
exit(1)
# basic variables
editions = {}
edition_names = {}
cards_by_edition = {}
total_editions = 0
ignore_cards = ['Swamp', 'Plains', 'Mountain', 'Island', 'Forest']
# regexes
re_Code = '^Code=(.*)$'
re_Date = '^Date=([0-9]+-[0-9]+-[0-9]+)$'
re_Date2 = '^Date=([0-9]+-[0-9]+)$'
re_Type = '^Type=(.*)$'
re_Name = '^Name=(.*)$'
re_Card = '^[0-9]* *[A-Z] (.*)$'
# main algorithm
print("Loading editions...")
for root, dirs, files in os.walk(EDITIONS):
for name in files:
if name.find(".txt") != -1:
total_editions += 1
fullpath = os.path.join(root, name)
edition = open(fullpath).readlines()
foundCards = False
code = ""
date = ""
etype = ""
name = ""
for line in edition:
line = line.replace("\r\n","")
if not foundCards:
if line == "[cards]":
foundCards = True
else:
s_Code = re.search(re_Code, line)
if s_Code:
code = s_Code.groups()[0]
#print("Code found: " + code)
s_Date = re.search(re_Date, line)
if s_Date:
date = s_Date.groups()[0]
#print("Date found: " + date)
s_Date2 = re.search(re_Date2, line)
if s_Date2:
date = s_Date2.groups()[0] + "-01"
#print("Date found: " + date)
s_Type = re.search(re_Type, line)
if s_Type:
etype = s_Type.groups()[0]
#print("Type found: " + etype)
s_Name = re.search(re_Name, line)
if s_Name:
name = s_Name.groups()[0]
#print("Name found: " + name)
else:
if etype != "Expansion" and etype != "Core" and etype != "Starter":
#print("NOT LOADING: " + code)
continue
else:
if editions.keys().count(code) == 0:
editions[code] = date
edition_names[code] = name
#print(editions)
s_Card = re.search(re_Card, line)
if s_Card:
card = s_Card.groups()[0].strip()
#print("Card found: " + card)
if cards_by_edition.keys().count(card) == 0:
cards_by_edition[card] = []
cards_by_edition[card].append(code)
print("Loaded " + str(len(editions)) + " editions.")
def get_latest_set_for_card(card):
cdate = "0000-00-00"
edition = "XXX"
if ignore_cards.count(card) != 0:
return "LEA"
if cards_by_edition.keys().count(card) == 0:
#print("Warning: couldn't determine an edition for card: " + card)
return "LEA"
for code in cards_by_edition[card]:
if editions[code] > cdate:
cdate = editions[code]
edition = code
return edition
def get_earliest_set_for_card(card):
cdate = "9999-99-99"
edition = "XXX"
if cards_by_edition.keys().count(card) == 0:
#print("Warning: couldn't determine an edition for card: " + card)
return "LEA"
for code in cards_by_edition[card]:
if editions[code] < cdate:
cdate = editions[code]
edition = code
return edition
def get_latest_set_for_deck(deck):
edition = "LEA"
for line in deck.split('\n'):
#print("Line: " + line)
regexobj = re.search('^([0-9]+) +([^|]+)', line)
if regexobj:
cardname = regexobj.groups()[1].replace('\n','').replace('\r','').strip()
earliest_for_card = get_earliest_set_for_card(cardname)
#print("Card: " + cardname + ", latest for it: " + latest_for_card)
if editions[earliest_for_card] > editions[edition]:
edition = earliest_for_card
return edition
def get_event_for_deck(deck):
evline = deck.split('\n')[0].split("#EVENT:")
if len(evline) == 2:
return evline[1]
else:
return ""
print("Scanning decks...")
for root, dirs, files in os.walk(DECKFOLDER):
for name in files:
if name.find(".dck") != -1:
total_decks += 1
fullpath = os.path.join(root, name)
deckdata = open(fullpath).read()
set_for_deck = edition_names[get_latest_set_for_deck(deckdata)]
event_for_deck = get_event_for_deck(deckdata)
if event_for_deck[len(event_for_deck)-1] == ".":
event_for_deck = event_for_deck[0:len(event_for_deck)-1]
print("Deck: " + name + ", Set: " + set_for_deck + ", Event: " + event_for_deck)
if not os.access(os.path.join(root, set_for_deck, event_for_deck), os.F_OK):
os.makedirs(os.path.join(root, set_for_deck, event_for_deck))
shutil.copy(fullpath, os.path.join(root, set_for_deck, event_for_deck, name))
if args.d:
os.remove(fullpath)

View File

@@ -0,0 +1,162 @@
#!/usr/bin/env python
CARDSFOLDER = "../../res/cardsfolder"
EDITIONS = "../../res/editions"
DECKFOLDER = "."
import argparse, os, re, shutil
print("Agetian's MTG Forge Deck Sorter v1.4\n")
parser = argparse.ArgumentParser(description="Sort decks into folders (by edition).")
parser.add_argument("-d", action="store_true", help="physically delete original (unsorted) decks")
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) or os.access(os.path.join(EDITIONS,"Alara Reborn.txt"),os.F_OK)):
print("Fatal error:\n This utility requires the 'cardsfolder' folder with unpacked card files at " + CARDSFOLDER + ", 'editions' folder at " + EDITIONS + " and the 'decks' folder with .dck files at " + DECKFOLDER + " in order to operate. Exiting.")
exit(1)
# basic variables
editions = {}
edition_names = {}
cards_by_edition = {}
total_editions = 0
ignore_cards = ['Swamp', 'Plains', 'Mountain', 'Island', 'Forest']
# regexes
re_Code = '^Code=(.*)$'
re_Date = '^Date=([0-9]+-[0-9]+-[0-9]+)$'
re_Date2 = '^Date=([0-9]+-[0-9]+)$'
re_Type = '^Type=(.*)$'
re_Name = '^Name=(.*)$'
re_Card = '^[0-9]* *[A-Z] (.*)$'
# main algorithm
print("Loading editions...")
for root, dirs, files in os.walk(EDITIONS):
for name in files:
if name.find(".txt") != -1:
total_editions += 1
fullpath = os.path.join(root, name)
edition = open(fullpath).readlines()
foundCards = False
code = ""
date = ""
etype = ""
name = ""
for line in edition:
line = line.replace("\r\n","")
if not foundCards:
if line == "[cards]":
foundCards = True
else:
s_Code = re.search(re_Code, line)
if s_Code:
code = s_Code.groups()[0]
#print("Code found: " + code)
s_Date = re.search(re_Date, line)
if s_Date:
date = s_Date.groups()[0]
#print("Date found: " + date)
s_Date2 = re.search(re_Date2, line)
if s_Date2:
date = s_Date2.groups()[0] + "-01"
#print("Date found: " + date)
s_Type = re.search(re_Type, line)
if s_Type:
etype = s_Type.groups()[0]
#print("Type found: " + etype)
s_Name = re.search(re_Name, line)
if s_Name:
name = s_Name.groups()[0]
#print("Name found: " + name)
else:
if etype != "Expansion" and etype != "Core" and etype != "Starter" and etype != "Other":
#print("NOT LOADING: " + code)
continue
if code == "EXP" or code == "MPS":
#print("NOT LOADING: " + code)
continue
else:
if editions.keys().count(code) == 0:
editions[code] = date
edition_names[code] = name
#print(editions)
s_Card = re.search(re_Card, line)
if s_Card:
card = s_Card.groups()[0].strip()
#print("Card found: " + card)
if cards_by_edition.keys().count(card) == 0:
cards_by_edition[card] = []
cards_by_edition[card].append(code)
print("Loaded " + str(len(editions)) + " editions.")
def get_latest_set_for_card(card):
cdate = "0000-00-00"
edition = "XXX"
if ignore_cards.count(card) != 0:
return "LEA"
if cards_by_edition.keys().count(card) == 0:
#print("Warning: couldn't determine an edition for card: " + card)
return "LEA"
for code in cards_by_edition[card]:
if editions[code] > cdate:
cdate = editions[code]
edition = code
return edition
def get_earliest_set_for_card(card):
cdate = "9999-99-99"
edition = "XXX"
if cards_by_edition.keys().count(card) == 0:
#print("Warning: couldn't determine an edition for card: " + card)
return "LEA"
for code in cards_by_edition[card]:
if editions[code] < cdate:
cdate = editions[code]
edition = code
return edition
def get_latest_set_for_deck(deck):
edition = "LEA"
for line in deck.split('\n'):
#print("Line: " + line)
regexobj = re.search('^([0-9]+) +([^|]+)', line)
if regexobj:
cardname = regexobj.groups()[1].replace('\n','').replace('\r','').strip()
earliest_for_card = get_earliest_set_for_card(cardname)
#print("Card: " + cardname + ", latest for it: " + latest_for_card)
if editions[earliest_for_card] > editions[edition]:
edition = earliest_for_card
return edition
def get_event_for_deck(deck):
evline = deck.split('\n')[0].split("#EVENT:")
if len(evline) == 2:
return evline[1]
else:
return ""
print("Scanning decks...")
for root, dirs, files in os.walk(DECKFOLDER):
for name in files:
if name.find(".dck") != -1:
total_decks += 1
fullpath = os.path.join(root, name)
deckdata = open(fullpath).read()
set_for_deck = edition_names[get_latest_set_for_deck(deckdata)]
event_for_deck = get_event_for_deck(deckdata)
if event_for_deck[len(event_for_deck)-1] == ".":
event_for_deck = event_for_deck[0:len(event_for_deck)-1]
print("Deck: " + name + ", Set: " + set_for_deck + ", Event: " + event_for_deck)
if not os.access(os.path.join(root, set_for_deck, event_for_deck), os.F_OK):
os.makedirs(os.path.join(root, set_for_deck, event_for_deck))
shutil.copy(fullpath, os.path.join(root, set_for_deck, event_for_deck, name))
if args.d:
os.remove(fullpath)

View File

@@ -0,0 +1,72 @@
import os
# basic variables
CARDSFOLDER = "../../res/cardsfolder"
cardlist = []
total_cards = 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)
cardlist.extend([cardname])
print("Loaded %d cards.\n" % total_cards)
filewalker = os.walk(".")
for elem in filewalker:
for filename in elem[2]:
if filename.endswith(".java"):
fully_supported = True
f = open(filename, "r")
cubename = filename.replace(".java", "")
cards = []
for line in f.readlines():
if line.find("super") != -1:
cubename = line[line.find('"')+1:]
cubename = cubename[0:cubename.find('"')]
# print(cubename)
if line.find("cubeCards.add") != -1:
line = line.strip()
cardname = line[line.find('"')+1:]
cardname = cardname[0:cardname.find('"')].replace("'Sleeping Dragon'", '"Sleeping Dragon"').replace("Mardu Woe Reaper","Mardu Woe-Reaper").strip()
if cardname not in cardlist:
print("Unsupported card in '" + cubename +"': " + cardname)
fully_supported = False
else:
cards.extend([cardname])
# print(cardname)
if fully_supported:
print("'" + cubename + "' is FULLY SUPPORTED")
print("")
out = open("cube/" + cubename + ".dck", "w")
out.write("[metadata]\n")
out.write("Name="+cubename+"\n")
out.write("[main]\n")
for card in cards:
out.write("1 " + card + "\n")
out.close()
out = open("draft/" + cubename + ".draft", "w")
out.write("Name:"+cubename+"\n")
out.write("DeckFile:"+cubename+"\n")
out.write("Singleton:True\n")
out.write("\n")
out.write("Booster: 15 Any\n")
out.write("NumPacks: 3\n")
out.close()