Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deckbox functionality #5

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.idea
out/
target/
*.iml
45 changes: 44 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Magic Duels Deck Exporter
A tool to allow importing/exporting decks between Magic Duels and Magic Assistant, to ease the pain of deckbuilding



## Installation Instructions
1. Download and install [Magic Assistant](https://sourceforge.net/projects/mtgbrowser/). Run it and let it update. Keep track of where you save the workspace.
2. In Magic Assistant, open the card filter dialog _(looks like three arrows point right)_. Click _'Set filter'_, then make sure the following sets all have icons:
Expand All @@ -17,13 +19,38 @@ A tool to allow importing/exporting decks between Magic Duels and Magic Assistan
3. Download [the latest `Magic.Duels.Deck.Exporter.zip` file](https://github.com/BlueRaja/Magic-Duels-Deck-Exporter/releases/latest) and extract it somewhere.
4. Open `Settings.bat` in a text editor and update the values to match your machine.

## Usage instructions
## Magic Assistant - Usage instructions
1. _(Optional)_ Open Magic Duels and create a new deck. Decks can only be edited within Magic Assistant, not created.
2. _(Optional)_ Backup your Magic Duels profile before running.
3. Run `Import - Duels to Assist.bat`
4. Open Magic Assistant and edit your decks however you'd like them. The cards you have available are under the "Magic Duels collection" collection.
5. When finished, close Magic Assistant and run `Export - Assist to Duels.bat`

## Low level instructions

It is possible to directly run the jar file with one of the following commands:

java -jar magic-duels-deck-exporter.jar BROWSE <path-to-magic-duels-profile>
java -jar magic-duels-deck-exporter.jar ASSIST_TO_DUELS <path-to-magic-assist-workspace> <path-to-magic-duels-profile>
java -jar magic-duels-deck-exporter.jar DUELS_TO_ASSIST <path-to-magic-assist-workspace> <path-to-magic-duels-profile>
java -jar magic-duels-deck-exporter.jar DUELS_TO_DECKBOX <path-to-deckbox-out-file> <path-to-magic-duels-profile>
java -jar magic-duels-deck-exporter.jar DUELS_TO_DECKBOX <path-to-deckbox-deck-file> <path-to-magic-duels-profile>

**BROWSE** Allow to inspect the Magic Duels profile.

**path-to-magic-assist-workspace**: Path to Magic Assist workspace.

**path-to-magic-duels-profile**: Usually under C:\Program Files (x86)\Steam\userdata\<your-id>\316010\remote\<your-id>.profile

**path-to-deckbox-out-file**: File that will be generated to import into [deckbox](https://deckbox.org)
from Inventory -> Add Cards -> Add From Card List -> <Paste> -> Import
Note: it will overwrite an existing path-to-deckbox-out-file file.

**path-to-deckbox-deck-file**: File exported from [deckbox](https://deckbox.org) with
Deck To Export -> Tools -> Export to Text
The name of the file will be used as name of the deck created in Magic Duels.
If there's already a deck in Magic Duels with the same name then the deck will be overwritten.

## FAQ

### When I run the .bat files, I get 'java not found'
Expand All @@ -42,6 +69,22 @@ This is a bug some users have reported when their deck names contain non-English

I've been unable to reproduce this error, so I can't fix it. If you encounter this bug, I'd appreciate if you could export your decks from Magic Assistant and post them to the issues-tracker.


## Development

The project uses [Maven](https://maven.apache.org/)
and developed using [IntelliJ](https://www.jetbrains.com/idea/)

### Run tests with mvn

mvn test

### Build runnable jar with mvn

mvn clean compile assembly:single
# jar will be under target


---

_Thanks to [spirolone](http://www.slightlymagic.net/forum/viewtopic.php?f=99&t=17931) for his work reverse-engineering the Magic Duels file format_
60 changes: 60 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.codehaus.mojo</groupId>
<artifactId>magic-duels-deck-exporter</artifactId>
<version>3.1-beta</version>

<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.6.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.shazam</groupId>
<artifactId>shazamcrest</artifactId>
<version>0.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.blueraja.magicduelsimporter.Main</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
</project>
83 changes: 83 additions & 0 deletions src/main/java/com/blueraja/magicduelsimporter/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.blueraja.magicduelsimporter;

import com.blueraja.magicduelsimporter.export.*;
import com.blueraja.magicduelsimporter.export.browser.ExporterBrowser;

import java.util.Arrays;

import static com.blueraja.magicduelsimporter.Main.Modality.*;

public class Main {

public enum Modality {
BROWSE,
ASSIST_TO_DUELS,
DUELS_TO_ASSIST,
DUELS_TO_DECKBOX,
DECKBOX_TO_DUELS
}


public static void main(String[] args) throws Exception {
if (args.length == 0) {
printUsage();
}

if (args.length >= 1) {
try {
Modality.valueOf(args[0]);
} catch (IllegalArgumentException e) {
error(
"MODALITY " + args[0] + " Not found.\n"+
"Available modalities are: " + Arrays.asList(Modality.values())
);
}
}

String[] otherArguments = Arrays.copyOfRange(args, 1, args.length);
switch (Modality.valueOf(args[0])) {
case BROWSE:
ExporterBrowser.main(otherArguments);
break;

case ASSIST_TO_DUELS:
ExporterAssistToDuels.main(otherArguments);
break;

case DUELS_TO_ASSIST:
ExporterDuelsToAssist.main(otherArguments);
break;

case DUELS_TO_DECKBOX:
ExporterDuelsToDeckbox.main(otherArguments);
break;

case DECKBOX_TO_DUELS:
ExporterDeckboxToDuels.main(otherArguments);
break;
}

}

private static void printUsage() {
System.out.println(
"\n" +
"magic-duels-deck-exporter.jar MODALITY options" + "\n" +
"\n" +
"Available modalities are: " + Arrays.asList(Modality.values()) + "\n" +
"\n" +
BROWSE + " <path-to-magic-duels-profile>" + "\n" +
ASSIST_TO_DUELS + " <path-to-magic-assist-workspace> <path-to-magic-duels-profile>" + "\n" +
DUELS_TO_ASSIST + " <path-to-magic-assist-workspace> <path-to-magic-duels-profile>" + "\n" +
DUELS_TO_DECKBOX + " <path-to-deckbox-out-file> <path-to-magic-duels-profile>" + "\n" +
DECKBOX_TO_DUELS + " <path-to-deckbox-deck-file> <path-to-magic-duels-profile>" + "\n" +
"\n"
);
}

private static void error(String message) {
System.err.println("\nERROR: " + message + "\n");
System.exit(-1);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,8 @@ public int hashCode() {
result = 31 * result + idMagicAssist;
return result;
}

public String getDisplayName() {
return displayName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -59,13 +60,11 @@ private static void LoadMagicAssistCardsForFile(File file)
}
}

private static void LoadMagicDuelsCardPool(String cardpoolPath)
private static void LoadMagicDuelsCardPool()
throws Exception {
if(!new File(cardpoolPath).isFile()) {
throw new IOException("Cardpool.xml cannot be found at " + cardpoolPath);
}

Document doc = FileUtils.getFileAsXMLDocument(cardpoolPath);
InputStream cardPoolInput = ClassLoader.getSystemResourceAsStream("CardData.xml");
Document doc = FileUtils.getFileAsXMLDocument(cardPoolInput);
NodeList cards = doc.getElementsByTagName("card");
for(int i = 0; i < cards.getLength(); i++) {
Element element = (Element)cards.item(i);
Expand Down Expand Up @@ -103,16 +102,15 @@ private static String normalizeName(String name) {

public static void main(String[] args)
throws Exception {
if(args.length != 2) {
System.out.println("Format: CardDataGenerator <path-to-magic-assist-workspace> <path-to-CardPool.xml>");
if(args.length != 1) {
System.out.println("Format: CardDataGenerator <path-to-magic-assist-workspace>");
return;
}

String assistWorkspacePath = args[0];
String cardpoolPath = args[1];

LoadAllMagicAssistCards(assistWorkspacePath);
LoadMagicDuelsCardPool(cardpoolPath);
LoadMagicDuelsCardPool();
_cardDataManager.writeXml();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@

public class CardDataManager {
public static abstract class Lands {

public static final CardData FOREST = new CardData("Forest", -1, 383241);
public static final CardData ISLAND = new CardData("Island", -2, 383281);
public static final CardData MOUNTAIN = new CardData("Mountain", -3, 383315);
public static final CardData PLAINS = new CardData("Plains", -4, 383346);
public static final CardData SWAMP = new CardData("Swamp", -5, 383408);
}

public List<CardData> _cardEntries = new ArrayList<>();

public void addEntry(String name, int idMagicDuels, int idMagicAssist) {
Expand All @@ -38,6 +38,22 @@ public List<CardData> getAllCards() {
return _cardEntries;
}

public List<CardData> getAllCardsWithBasicLands() {
List<CardData> allCardsAndLands = new ArrayList<>();
allCardsAndLands.addAll(getAllLands());
allCardsAndLands.addAll(getAllCards());
return allCardsAndLands;
}

public CardData getCard(String cardName) {
for (CardData cardEntry : getAllCardsWithBasicLands()) {
if (cardEntry.getDisplayName().equals(cardName)) {
return cardEntry;
}
}
throw new RuntimeException("Card " + cardName + " not found.");
}

public List<CardData> getAllLands() {
return Arrays.asList(new CardData[] {Lands.FOREST, Lands.ISLAND, Lands.MOUNTAIN, Lands.PLAINS, Lands.SWAMP});
}
Expand Down Expand Up @@ -80,7 +96,8 @@ public void writeXml() throws IOException, ParserConfigurationException, Transfo
public void readXml() throws ParserConfigurationException, IOException, SAXException {
this.clear();

Document doc = FileUtils.getFileAsXMLDocument("CardData.xml");
InputStream cardDataInput = ClassLoader.getSystemResourceAsStream("CardData.xml");
Document doc = FileUtils.getFileAsXMLDocument(cardDataInput);
NodeList cards = doc.getElementsByTagName("card");
for(int i = 0; i < cards.getLength(); i++) {
Element element = (Element)cards.item(i);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.blueraja.magicduelsimporter.deckbox;

import com.blueraja.magicduelsimporter.carddata.CardData;
import com.blueraja.magicduelsimporter.carddata.CardDataManager;
import com.blueraja.magicduelsimporter.magicassist.Deck;
import com.blueraja.magicduelsimporter.utils.FileUtils;

import java.io.FileNotFoundException;
import java.io.IOException;

public class DeckboxDeckManager {

private final CardDataManager cardDataManager;

public DeckboxDeckManager(CardDataManager cardDataManager) {
this.cardDataManager = cardDataManager;
}

public void writeCardsToFile(Deck ownedCards, String deckboxOutFilePath) throws FileNotFoundException {
FileUtils.writeToFile(deckboxOutFilePath, asString(ownedCards));
}

public Deck loadDeck(String deckboxDeckFilePath) throws IOException {
String deckName = FileUtils.getBaseName(deckboxDeckFilePath);
String fileAsString = FileUtils.getFileAsString(deckboxDeckFilePath);
return fromString(deckName, FileUtils.toUnix(fileAsString));
}

String asString(Deck ownedCards) {
StringBuilder stringBuilder = new StringBuilder();

for (CardData cardData : ownedCards.getCards()) {
stringBuilder.append(ownedCards.getCardCount(cardData)).append(" ").append(cardData.getDisplayName()).append("\n");
}

return stringBuilder.toString();
}

Deck fromString(String deckName, String deckContent) {
Deck deck = new Deck(deckName);

String[] lines = deckContent.split("\n");

for (String line : lines) {
if (!line.trim().isEmpty()) {
int firstSpaceIndex = line.indexOf(" ");
int cardsCount = Integer.parseInt(line.substring(0, firstSpaceIndex));
String cardName = line.substring(firstSpaceIndex + 1);

CardData cardData = cardDataManager.getCard(cardName);
deck.addCard(cardData, cardsCount);
}
}

return deck;
}
}