diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java
index d2952767fa..55fa58d1d2 100644
--- a/Mage.Client/src/main/java/mage/client/MageFrame.java
+++ b/Mage.Client/src/main/java/mage/client/MageFrame.java
@@ -39,8 +39,10 @@ import de.schlichtherle.truezip.file.TConfig;
 import de.schlichtherle.truezip.fs.FsOutputOption;
 import mage.cards.Card;
 import mage.cards.decks.Deck;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
+import mage.cards.repository.CardScanner;
 import mage.client.cards.BigCard;
-import mage.client.cards.CardsStorage;
 import mage.client.chat.ChatPanel;
 import mage.client.components.*;
 import mage.client.components.ext.dlg.DialogManager;
@@ -74,6 +76,7 @@ import mage.server.Main;
 import mage.utils.MageVersion;
 import org.apache.log4j.Logger;
 import org.mage.card.arcane.ManaSymbols;
+import org.mage.plugins.card.images.DownloadPictures;
 import org.mage.plugins.card.utils.impl.ImageManagerImpl;
 
 import javax.imageio.ImageIO;
@@ -81,14 +84,23 @@ import javax.swing.*;
 import javax.swing.JToolBar.Separator;
 import javax.swing.event.PopupMenuEvent;
 import javax.swing.event.PopupMenuListener;
-import java.awt.*;
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.Rectangle;
+import java.awt.SplashScreen;
 import java.awt.event.*;
 import java.awt.image.BufferedImage;
 import java.beans.PropertyVetoException;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.UUID;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
@@ -468,22 +480,36 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
         menu.show(component, 0, component.getHeight());
     }
 
+    private List<Card> getAllCards() {
+        List<Card> cards = new ArrayList<Card>();
+        List<CardInfo> allCards = CardRepository.instance.getAllCards();
+        for (CardInfo cardInfo : allCards) {
+            cards.add(cardInfo.getCard());
+        }
+
+        return cards;
+    }
+
     private void checkForNewImages() {
-        HashSet<Card> cards = new HashSet<Card>(CardsStorage.getAllCards());
-        List<Card> notImplemented = CardsStorage.getNotImplementedCards();
-        cards.addAll(notImplemented);
-        if (Plugins.getInstance().newImage(cards)) {
+        List<Card> cards = getAllCards();
+
+        String useDefault = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_USE_DEFAULT, "true");
+        String path = useDefault.equals("true") ? null : PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PATH, null);
+
+        if (DownloadPictures.checkForNewCards(cards, path)) {
             if (JOptionPane.showConfirmDialog(null, "New cards are available.  Do you want to download the images?", "New images available", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
-                Plugins.getInstance().downloadImage(cards);
+                DownloadPictures.startDownload(null, cards, path);
             }
         }
     }
 
     public void btnImagesActionPerformed(java.awt.event.ActionEvent evt) {
-        HashSet<Card> cards = new HashSet<Card>(CardsStorage.getAllCards());
-        List<Card> notImplemented = CardsStorage.getNotImplementedCards();
-        cards.addAll(notImplemented);
-        Plugins.getInstance().downloadImage(cards);
+        List<Card> cards = getAllCards();
+
+        String useDefault = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_USE_DEFAULT, "true");
+        String path = useDefault.equals("true") ? null : PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PATH, null);
+
+        DownloadPictures.startDownload(null, cards, path);
     }
 
     public void btnSymbolsActionPerformed(java.awt.event.ActionEvent evt) {
@@ -974,6 +1000,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
         });
         SwingUtilities.invokeLater(new Runnable() {
             public void run() {
+                CardScanner.scan();
                 for (String arg : args) {
                     if (arg.startsWith(liteModeArg)) {
                         liteMode = true;
@@ -1147,4 +1174,4 @@ class MagePaneMenuItem extends JCheckBoxMenuItem {
     public MagePane getFrame() {
         return frame;
     }
-}
\ No newline at end of file
+}
diff --git a/Mage.Client/src/main/java/mage/client/cards/Card.java b/Mage.Client/src/main/java/mage/client/cards/Card.java
index c81ed5f1b3..7028e4845d 100644
--- a/Mage.Client/src/main/java/mage/client/cards/Card.java
+++ b/Mage.Client/src/main/java/mage/client/cards/Card.java
@@ -46,7 +46,7 @@ import mage.client.util.DefaultActionCallback;
 import mage.client.util.ImageHelper;
 import mage.client.util.gui.ArrowBuilder;
 import mage.remote.Session;
-import mage.sets.Sets;
+import mage.cards.Sets;
 import mage.view.AbilityView;
 import mage.view.CardView;
 import mage.view.PermanentView;
diff --git a/Mage.Client/src/main/java/mage/client/cards/CardEventSource.java b/Mage.Client/src/main/java/mage/client/cards/CardEventSource.java
index c512a42794..966c38fdef 100644
--- a/Mage.Client/src/main/java/mage/client/cards/CardEventSource.java
+++ b/Mage.Client/src/main/java/mage/client/cards/CardEventSource.java
@@ -32,9 +32,9 @@ import mage.client.util.Event;
 import mage.client.util.EventDispatcher;
 import mage.client.util.EventSource;
 import mage.client.util.Listener;
+import mage.view.SimpleCardView;
 
 import java.io.Serializable;
-import java.util.UUID;
 
 /**
  *
@@ -49,12 +49,12 @@ public class CardEventSource implements EventSource<Event>, Serializable {
         dispatcher.addListener(listener);
     }
 
-    public void doubleClick(UUID cardId, String message) {
-        dispatcher.fireEvent(new Event(cardId, message));
+    public void doubleClick(SimpleCardView card, String message) {
+        dispatcher.fireEvent(new Event(card, message));
     }
 
-    public void shiftDoubleClick(UUID cardId, String message) {
-        dispatcher.fireEvent(new Event(cardId, message));
+    public void shiftDoubleClick(SimpleCardView card, String message) {
+        dispatcher.fireEvent(new Event(card, message));
     }
 
     public void removeFromMainEvent(String message) {
diff --git a/Mage.Client/src/main/java/mage/client/cards/CardGrid.java b/Mage.Client/src/main/java/mage/client/cards/CardGrid.java
index 45c014aded..ebd412156d 100644
--- a/Mage.Client/src/main/java/mage/client/cards/CardGrid.java
+++ b/Mage.Client/src/main/java/mage/client/cards/CardGrid.java
@@ -253,9 +253,9 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener,
             e.consume();
             Object obj = e.getSource();
             if (obj instanceof Card) {
-                cardEventSource.doubleClick(((Card)obj).getCardId(), "double-click");
+                cardEventSource.doubleClick(((Card) obj).getOriginal(), "double-click");
             } else if (obj instanceof MageCard) {
-                cardEventSource.doubleClick(((MageCard)obj).getOriginal().getId(), "double-click");
+                cardEventSource.doubleClick(((MageCard) obj).getOriginal(), "double-click");
             }
         }
     }
diff --git a/Mage.Client/src/main/java/mage/client/cards/CardsList.java b/Mage.Client/src/main/java/mage/client/cards/CardsList.java
index 3018553fc9..cfdce3fd71 100644
--- a/Mage.Client/src/main/java/mage/client/cards/CardsList.java
+++ b/Mage.Client/src/main/java/mage/client/cards/CardsList.java
@@ -462,14 +462,14 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar
             Object obj = e.getSource();
             if (obj instanceof Card) {
                 if (e.isShiftDown())
-                    cardEventSource.shiftDoubleClick(((Card)obj).getCardId(), "shift-double-click");
+                    cardEventSource.shiftDoubleClick(((Card)obj).getOriginal(), "shift-double-click");
                 else
-                    cardEventSource.doubleClick(((Card)obj).getCardId(), "double-click");
+                    cardEventSource.doubleClick(((Card)obj).getOriginal(), "double-click");
             } else if (obj instanceof MageCard) {
                 if (e.isShiftDown())
-                    cardEventSource.shiftDoubleClick(((MageCard)obj).getOriginal().getId(), "shift-double-click");
+                    cardEventSource.shiftDoubleClick(((MageCard)obj).getOriginal(), "shift-double-click");
                 else
-                    cardEventSource.doubleClick(((MageCard)obj).getOriginal().getId(), "double-click");
+                    cardEventSource.doubleClick(((MageCard)obj).getOriginal(), "double-click");
             }
         }
     }
diff --git a/Mage.Client/src/main/java/mage/client/cards/CardsStorage.java b/Mage.Client/src/main/java/mage/client/cards/CardsStorage.java
index 058314ff28..94e70cdfda 100644
--- a/Mage.Client/src/main/java/mage/client/cards/CardsStorage.java
+++ b/Mage.Client/src/main/java/mage/client/cards/CardsStorage.java
@@ -1,15 +1,11 @@
 package mage.client.cards;
 
-import mage.Constants;
 import mage.cards.Card;
-import mage.cards.CardImpl;
-import mage.cards.ExpansionSet;
-import mage.sets.Sets;
-import mage.utils.CardUtil;
-import org.apache.log4j.Logger;
 
 import java.io.InputStream;
-import java.util.*;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Scanner;
 
 /**
  * Stores all implemented cards on client side.
@@ -18,16 +14,9 @@ import java.util.*;
  * @author nantuko
  */
 public class CardsStorage {
-    private static final Logger log = Logger.getLogger(CardsStorage.class);
 
-    private static final List<Card> allCards;
-    private static final Set<Card> nonBasicLandCards;
-    private static final List<String> setCodes;
     private static Map<String, Integer> ratings;
     private static Integer min = Integer.MAX_VALUE, max = 0;
-    private static List<Card> notImplementedCards;
-    
-    private static boolean loaded;
 
     /**
      * Rating that is given for new cards.
@@ -35,99 +24,6 @@ public class CardsStorage {
      */
     private static final int DEFAULT_NOT_RATED_CARD_RATING = 6;
 
-    static {
-        allCards = new ArrayList<Card>();
-        nonBasicLandCards = new LinkedHashSet<Card>();
-        setCodes = new ArrayList<String>();
-
-        List<ExpansionSet> sets = new ArrayList<ExpansionSet>(Sets.getInstance().values());
-        Collections.sort(sets, new SetComparator());
-        for (ExpansionSet set : sets) {
-            setCodes.add(set.getCode());
-        }
-
-    }
-
-    private static void loadLazily() {
-        if (!loaded) {
-            synchronized (CardsStorage.class) {
-                if (!loaded) {
-                    List<ExpansionSet> sets = new ArrayList<ExpansionSet>(Sets.getInstance().values());
-                    for (ExpansionSet set : sets) {
-                        List<Card> cards = set.getCards();
-                        Collections.sort(cards, new CardComparator());
-                        allCards.addAll(cards);
-                        for (Card card : cards) {
-                            if (CardUtil.isLand(card) && !CardUtil.isBasicLand(card)) {
-                                nonBasicLandCards.add(card);
-                            }
-                        }
-                    }
-                    loaded = true;
-                }
-            }
-        }
-    }
-
-    public static List<Card> getAllCards() {
-        if (!loaded) {
-            loadLazily();
-        }
-        return allCards;
-    }
-
-    /**
-     * Get cards from card pool starting from start index and ending with end index.
-     * Can filter cards by set (if parameter is not null).
-     *
-     * @param start
-     * @param end
-     * @param set             Cards set code. Can be null.
-     * @param onlyImplemented return only implemented cards
-     * @return
-     */
-    public static List<Card> getAllCards(int start, int end, String set, boolean onlyImplemented) {
-        List<Card> cards = new ArrayList<Card>();
-        List<Card> pool;
-        if (set == null) {
-            pool = getAllCards();
-        } else {
-            pool = new ArrayList<Card>();
-            for (Card card : getAllCards()) {
-                if (card.getExpansionSetCode().equals(set)) {
-                    pool.add(card);
-                }
-            }
-        }
-        if (!onlyImplemented) {
-            for (Card card : getNotImplementedCards()) {
-                if (card.getExpansionSetCode().equals(set)) {
-                    pool.add(card);
-                }
-            }
-            Collections.sort(pool, new CardComparator());
-        }
-        for (int i = start; i < Math.min(end + 1, pool.size()); i++) {
-            cards.add(pool.get(i));
-        }
-        return cards;
-    }
-
-    public static int getCardsCount() {
-        return getAllCards().size();
-    }
-
-    public static List<String> getSetCodes() {
-        return setCodes;
-    }
-
-    public static Set<Card> getNonBasicLandCards() {
-        if (!loaded) {
-            loadLazily();
-        }
-        return nonBasicLandCards;
-    }
-
     /**
      * Return rating of a card: 1-10.
      *
@@ -175,157 +71,4 @@ public class CardsStorage {
             }
         }
     }
-
-    /**
-     * Get list of not implemented cards.
-     * Used in collection viewer to show what cards need to be done for the latest set.
-     *
-     * @return
-     */
-    public static List<Card> getNotImplementedCards() {
-        List<Card> cards = new ArrayList<Card>();
-        if (notImplementedCards == null) {
-            if (getAllCards().isEmpty()) {
-                return cards;
-            }
-
-            Set<String> names = new HashSet<String>();
-            for (Card card : getAllCards()) {
-                names.add(card.getExpansionSetCode() + card.getName());
-            }
-
-            readUnimplemented("ZEN", "/zen.txt", names, cards);
-            readUnimplemented("WWK", "/wwk.txt", names, cards);
-
-            names.clear();
-        }
-        return cards;
-    }
-
-    private static final class UnimplementedCardImpl extends CardImpl {
-
-        public UnimplementedCardImpl(CardImpl card) {
-            super(card);
-        }
-
-        @Override
-        public UnimplementedCardImpl copy() {
-            return new UnimplementedCardImpl(this);
-        }
-
-        public void setCanTransform(boolean canTransform) {
-            this.canTransform = canTransform;
-        }
-
-        public void setNightCard(boolean nightCard) {
-            this.nightCard = nightCard;
-        }
-
-        public void setSecondSideCard(Card secondSideCard) {
-            this.secondSideCard = secondSideCard;
-        }
-    }
-
-    private static void readUnimplemented(String set, String filename, Set<String> names, List<Card> cards) {
-        try {
-            CardImpl tmp = (CardImpl) allCards.get(0);
-            InputStream is = CardsStorage.class.getResourceAsStream(filename);
-            if (is == null) {
-                log.error("Couldn't find: " + filename);
-                return;
-            }
-            Scanner scanner = new Scanner(is);
-            UnimplementedCardImpl cardToAdd = null;
-            boolean addCard = false;
-            while (scanner.hasNextLine()) {
-                String line = scanner.nextLine();
-                String[] s = line.split("\\|");
-                if (s.length == 2) {
-                    String name = s[1].trim();
-                    if (!names.contains(set + name)) {
-                        UnimplementedCardImpl card = new UnimplementedCardImpl(tmp);
-                        Integer cid;
-                        boolean secondFace = false;
-                        if (s[0].endsWith("a")) {
-                            cid = Integer.parseInt(s[0].replace("a", ""));
-                        } else if (s[0].endsWith("b")) {
-                            cid = Integer.parseInt(s[0].replace("b", ""));
-                            secondFace = true;
-                            if (cardToAdd != null) {
-                                addCard = true;
-                            }
-                        } else {
-                            cid = Integer.parseInt(s[0]);
-                            addCard = true;
-                        }
-                        card.setName(name);
-                        card.setExpansionSetCode(set);
-                        card.setCardNumber(cid);
-                        card.setRarity(Constants.Rarity.NA); // mark as not implemented
-                        card.getCardType().clear();
-                        if (secondFace) {
-                            if (cardToAdd != null) {
-                                cardToAdd.setCanTransform(true);
-                                cardToAdd.setSecondSideCard(card);
-                                card.setCanTransform(true);
-                                card.setNightCard(true);
-                            }
-                        } else {
-                            cardToAdd = card;
-                        }
-                        if (addCard) {
-                            cards.add(cardToAdd);
-                            cardToAdd = null;
-                            addCard = false;
-                        }
-                    }
-                }
-            }
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-
-    public static void main(String[] argv) {
-        for (Card card : getAllCards()) {
-            String name = card.getName();
-            if (name.equals("Baneslayer Angel") || name.equals("Lightning Bolt") || name.equals("Zombie Outlander")
-                    || name.equals("Naturalize") || name.equals("Kraken's Eye") || name.equals("Serra Angel")) {
-                System.out.println(name + " : " + rateCard(card));
-            }
-        }
-    }
-
-    /**
-     * Card comparator.
-     * First compares collector ids and then card names.
-     * <p/>
-     * Show latest set cards on top.
-     *
-     * @author nantuko
-     */
-    private static class CardComparator implements Comparator<Card> {
-
-        @Override
-        public int compare(Card o1, Card o2) {
-            Integer cid1 = o1.getCardNumber();
-            Integer cid2 = o2.getCardNumber();
-            if (cid1 == cid2) {
-                return o1.getName().compareTo(o2.getName());
-            } else {
-                return cid1.compareTo(cid2);
-            }
-        }
-    }
-
-    /**
-     * Set comparator. Puts latest set on top.
-     */
-    private static class SetComparator implements Comparator<ExpansionSet> {
-
-        @Override
-        public int compare(ExpansionSet set1, ExpansionSet set2) {
-            return set2.getReleaseDate().compareTo(set1.getReleaseDate());
-        }
-    }
 }
diff --git a/Mage.Client/src/main/java/mage/client/cards/DraftGrid.java b/Mage.Client/src/main/java/mage/client/cards/DraftGrid.java
index ed944cff9d..aae69bdfa3 100644
--- a/Mage.Client/src/main/java/mage/client/cards/DraftGrid.java
+++ b/Mage.Client/src/main/java/mage/client/cards/DraftGrid.java
@@ -132,7 +132,7 @@ public class DraftGrid extends javax.swing.JPanel implements MouseListener {
     public void mousePressed(MouseEvent e) {
         Object obj = e.getSource();
         if (obj instanceof MageCard) {
-            this.cardEventSource.doubleClick(((MageCard)obj).getOriginal().getId(), "pick-a-card");
+            this.cardEventSource.doubleClick(((MageCard)obj).getOriginal(), "pick-a-card");
         }
     }
 
diff --git a/Mage.Client/src/main/java/mage/client/cards/Permanent.java b/Mage.Client/src/main/java/mage/client/cards/Permanent.java
index 89aa3f3176..921d5d1ab6 100644
--- a/Mage.Client/src/main/java/mage/client/cards/Permanent.java
+++ b/Mage.Client/src/main/java/mage/client/cards/Permanent.java
@@ -56,7 +56,7 @@ import mage.cards.CardDimensions;
 import mage.cards.MagePermanent;
 import mage.client.util.Config;
 import mage.client.util.ImageHelper;
-import mage.sets.Sets;
+import mage.cards.Sets;
 import mage.view.CounterView;
 import mage.view.PermanentView;
 
diff --git a/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java b/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java
index 93e054ac59..381faacae4 100644
--- a/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java
+++ b/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java
@@ -1,51 +1,53 @@
 package mage.client.deck.generator;
- 
+
 import mage.Constants.CardType;
 import mage.Constants.ColoredManaSymbol;
 import mage.Mana;
 import mage.cards.Card;
 import mage.cards.decks.Deck;
+import mage.cards.repository.CardCriteria;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
 import mage.client.MageFrame;
 import mage.client.cards.CardsStorage;
 import mage.client.util.gui.ColorsChooser;
 import mage.client.util.sets.ConstructedFormats;
 import mage.interfaces.rate.RateCallback;
-import mage.sets.Sets;
-import mage.utils.CardUtil;
+import mage.cards.Sets;
 import mage.utils.DeckBuilder;
- 
+
 import javax.swing.*;
-import java.awt.*;
+import java.awt.Component;
+import java.awt.Dimension;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.io.File;
-import java.util.*;
+import java.util.ArrayList;
 import java.util.List;
- 
+import java.util.Random;
+import java.util.UUID;
+
 /**
  * Generates random card pool and builds a deck.
  *
  * @author nantuko
  */
 public class DeckGenerator {
- 
+
     private static JDialog dlg;
     private static String selectedColors;
     private static JComboBox formats;
- 
+
     private static final int SPELL_CARD_POOL_SIZE = 180;
- 
+
     private static final int DECK_LANDS = 16;
     private static final int MAX_NON_BASIC_SOURCE = DECK_LANDS / 2;
- 
-    private static final boolean GENERATE_RANDOM_BASIC_LAND = true;
+
     private static final int MAX_TRIES = 4096;
- 
+
     private static Deck deck = new Deck();
     private static final int ADDITIONAL_CARDS_FOR_3_COLOR_DECKS = 20;
- 
-    private static String colors = "GWUBR";
- 
+
     /**
      * Opens color chooser dialog. Generates deck.
      * Saves generated deck and use it as selected deck to play.
@@ -55,21 +57,21 @@ public class DeckGenerator {
     public static String generateDeck() {
         JPanel p0 = new JPanel();
         p0.setLayout(new BoxLayout(p0, BoxLayout.Y_AXIS));
- 
+
         JLabel text = new JLabel("Choose color for your deck: ");
         text.setAlignmentX(Component.CENTER_ALIGNMENT);
         p0.add(text);
- 
+
         p0.add(Box.createVerticalStrut(5));
         String chosen = MageFrame.getPreferences().get("genDeckColor", "u");
         final ColorsChooser colorsChooser = new ColorsChooser(chosen);
         p0.add(colorsChooser);
- 
+
         p0.add(Box.createVerticalStrut(5));
         JLabel text2 = new JLabel("(X - random color)");
         text2.setAlignmentX(Component.CENTER_ALIGNMENT);
         p0.add(text2);
- 
+
         p0.add(Box.createVerticalStrut(5));
         JPanel jPanel = new JPanel();
         JLabel text3 = new JLabel("Choose format:");
@@ -81,9 +83,10 @@ public class DeckGenerator {
         jPanel.add(text3);
         jPanel.add(formats);
         p0.add(jPanel);
- 
+
         final JButton btnGenerate = new JButton("Ok");
         btnGenerate.addActionListener(new ActionListener() {
+            @Override
             public void actionPerformed(ActionEvent e) {
                 btnGenerate.setEnabled(false);
                 colorsChooser.setEnabled(false);
@@ -94,6 +97,7 @@ public class DeckGenerator {
         });
         final JButton btnCancel = new JButton("Cancel");
         btnCancel.addActionListener(new ActionListener() {
+            @Override
             public void actionPerformed(ActionEvent e) {
                 dlg.setVisible(false);
                 selectedColors = null;
@@ -104,7 +108,7 @@ public class DeckGenerator {
         dlg = optionPane.createDialog("Generating deck");
         dlg.setVisible(true);
         dlg.dispose();
- 
+
         if (selectedColors != null) {
             buildDeck();
             try {
@@ -115,53 +119,53 @@ public class DeckGenerator {
                 //JOptionPane.showMessageDialog(null, "Deck has been generated.");
                 return tmp.getAbsolutePath();
             } catch (Exception e) {
-                e.printStackTrace();
                 JOptionPane.showMessageDialog(null, "Couldn't generate deck. Try once again.");
             }
         }
- 
+
         return selectedColors;
     }
- 
+
     /**
      * Generates card pool
      */
     protected static void buildDeck() {
         List<ColoredManaSymbol> allowedColors = new ArrayList<ColoredManaSymbol>();
-        selectedColors = selectedColors.toUpperCase();
- 
-        String format = (String)formats.getSelectedItem();
+        selectedColors = selectedColors != null ? selectedColors.toUpperCase() : getRandomColors("X");
+
+        String format = (String) formats.getSelectedItem();
         List<String> setsToUse = ConstructedFormats.getSetsByFormat(format);
         if (setsToUse.isEmpty()) {
             // use all
-            setsToUse = CardsStorage.getSetCodes();
+            setsToUse = CardRepository.instance.getSetCodes();
         }
- 
+
         if (selectedColors.contains("X")) {
             selectedColors = getRandomColors(selectedColors);
         }
- 
+
         for (int i = 0; i < selectedColors.length(); i++) {
             char c = selectedColors.charAt(i);
             allowedColors.add(ColoredManaSymbol.lookup(c));
         }
- 
+
         int cardPoolSize = SPELL_CARD_POOL_SIZE;
         if (selectedColors.length() > 2) {
             cardPoolSize += ADDITIONAL_CARDS_FOR_3_COLOR_DECKS;
         }
         List<Card> spellCardPool = generateSpellCardPool(cardPoolSize, allowedColors, setsToUse);
         List<Card> landCardPool = generateNonBasicLandCardPool(MAX_NON_BASIC_SOURCE, allowedColors, setsToUse);
- 
+
         System.out.println("deck generator card pool: spells=" + spellCardPool.size() + ", lands=" + landCardPool.size());
- 
+
         final List<String> setsToUseFinal = setsToUse;
- 
+
         deck = DeckBuilder.buildDeck(spellCardPool, allowedColors, landCardPool, new RateCallback() {
             @Override
             public int rateCard(Card card) {
                 return CardsStorage.rateCard(card);
             }
+
             @Override
             public Card getBestBasicLand(ColoredManaSymbol color) {
                 int tries = 100;
@@ -175,29 +179,36 @@ public class DeckGenerator {
             }
         });
     }
- 
+
     private static String getRandomColors(String _selectedColors) {
+        Random random = new Random();
+        List<Character> availableColors = new ArrayList();
+        availableColors.add('R');
+        availableColors.add('G');
+        availableColors.add('B');
+        availableColors.add('U');
+        availableColors.add('W');
+
         StringBuilder generatedColors = new StringBuilder();
-        Set<String> colors = new HashSet<String>();
+        int randomColors = 0;
         for (int i = 0; i < _selectedColors.length(); i++) {
-            String color = getRandomColor() + "";
-            int retry = 100;
-            while (colors.contains(color)) {
-                color = getRandomColor() + "";
-                retry--;
-                if (retry <= 0) break;
+            char currentColor = _selectedColors.charAt(i);
+            if (currentColor != 'X') {
+                generatedColors.append(currentColor);
+                availableColors.remove(new Character(currentColor));
+            } else {
+                randomColors++;
             }
-            generatedColors.append(color);
-            colors.add(color);
         }
+
+        for (int i = 0; i < randomColors && !availableColors.isEmpty(); i++) {
+            int index = random.nextInt(availableColors.size());
+            generatedColors.append(availableColors.remove(index));
+        }
+
         return generatedColors.toString();
     }
- 
-    private static char getRandomColor() {
-        Random r = new Random();
-        return colors.charAt(r.nextInt(colors.length()));
-    }
- 
+
     /**
      * Generates card pool of cardsCount cards that have manacost of allowed colors.
      *
@@ -207,25 +218,22 @@ public class DeckGenerator {
      */
     private static List<Card> generateSpellCardPool(int cardsCount, List<ColoredManaSymbol> allowedColors, List<String> setsToUse) {
         List<Card> spellCardPool = new ArrayList<Card>();
- 
-        int count = 0;
-        List<Card> cardPool = new ArrayList<Card>();
-        for (Card card : CardsStorage.getAllCards()) {
-            if (setsToUse.contains(card.getExpansionSetCode())) {
-                cardPool.add(card);
-            }
-        }
+
+        CardCriteria spellCriteria = new CardCriteria();
+        spellCriteria.setCodes(setsToUse.toArray(new String[0]));
+        spellCriteria.notTypes(CardType.LAND);
+
+        List<CardInfo> cardPool = CardRepository.instance.findCards(spellCriteria);
         int cardPoolCount = cardPool.size();
         Random random = new Random();
         if (cardPoolCount > 0) {
             int tries = 0;
+            int count = 0;
             while (count < cardsCount) {
-                Card card = cardPool.get(random.nextInt(cardPoolCount));
-                if (!card.getCardType().contains(CardType.LAND)) {
-                    if (cardFitsChosenColors(card, allowedColors)) {
-                        spellCardPool.add(card);
-                        count++;
-                    }
+                Card card = cardPool.get(random.nextInt(cardPoolCount)).getCard();
+                if (cardFitsChosenColors(card, allowedColors)) {
+                    spellCardPool.add(card);
+                    count++;
                 }
                 tries++;
                 if (tries > MAX_TRIES) { // to avoid infinite loop
@@ -235,10 +243,10 @@ public class DeckGenerator {
         } else {
             throw new IllegalStateException("Not enough cards to generate deck.");
         }
- 
+
         return spellCardPool;
     }
- 
+
     /**
      * Check that card can be played using chosen (allowed) colors.
      *
@@ -264,7 +272,7 @@ public class DeckGenerator {
         }
         return true;
     }
- 
+
     /**
      * Generates card pool of land cards that can produce allowed colors.
      *
@@ -274,25 +282,23 @@ public class DeckGenerator {
      */
     private static List<Card> generateNonBasicLandCardPool(int landsCount, List<ColoredManaSymbol> allowedColors, List<String> setsToUse) {
         List<Card> nonBasicLandCardPool = new ArrayList<Card>();
- 
-        int count = 0;
-        List<Card> landCards = new ArrayList<Card>();
-        for (Card land : CardsStorage.getNonBasicLandCards()) {
-            if (setsToUse.contains(land.getExpansionSetCode())) {
-                landCards.add(land);
-            }
-        }
+
+        CardCriteria landCriteria = new CardCriteria();
+        landCriteria.setCodes(setsToUse.toArray(new String[0]));
+        landCriteria.types(CardType.LAND);
+        landCriteria.notSupertypes("Basic");
+        List<CardInfo> landCards = CardRepository.instance.findCards(landCriteria);
+
         int allCount = landCards.size();
         Random random = new Random();
         if (allCount > 0) {
             int tries = 0;
+            int count = 0;
             while (count < landsCount) {
-                Card card = landCards.get(random.nextInt(allCount));
-                if (!CardUtil.isBasicLand(card)) {
-                    if (cardCardProduceChosenColors(card, allowedColors)) {
-                        nonBasicLandCardPool.add(card);
-                        count++;
-                    }
+                Card card = landCards.get(random.nextInt(allCount)).getCard();
+                if (cardCardProduceChosenColors(card, allowedColors)) {
+                    nonBasicLandCardPool.add(card);
+                    count++;
                 }
                 tries++;
                 if (tries > MAX_TRIES) { // to avoid infinite loop
@@ -301,10 +307,10 @@ public class DeckGenerator {
                 }
             }
         }
- 
+
         return nonBasicLandCardPool;
     }
- 
+
     /**
      * Checks that chosen card can produce mana of specific color.
      *
@@ -324,7 +330,7 @@ public class DeckGenerator {
         }
         return false;
     }
- 
+
     /**
      * Get random basic land that can produce specified color mana.
      * Random here means random set and collector id for the same mana producing land.
@@ -334,52 +340,30 @@ public class DeckGenerator {
      */
     private static Card getBestBasicLand(ColoredManaSymbol color) {
         if (color.equals(ColoredManaSymbol.G)) {
-            return Sets.findCard("Forest", GENERATE_RANDOM_BASIC_LAND);
+            CardInfo cardInfo = CardRepository.instance.findCard("Forest");
+            return cardInfo != null ? cardInfo.getCard() : null;
         }
         if (color.equals(ColoredManaSymbol.R)) {
-            return Sets.findCard("Mountain", GENERATE_RANDOM_BASIC_LAND);
+            CardInfo cardInfo = CardRepository.instance.findCard("Mountain");
+            return cardInfo != null ? cardInfo.getCard() : null;
         }
         if (color.equals(ColoredManaSymbol.B)) {
-            return Sets.findCard("Swamp", GENERATE_RANDOM_BASIC_LAND);
+            CardInfo cardInfo = CardRepository.instance.findCard("Swamp");
+            return cardInfo != null ? cardInfo.getCard() : null;
         }
         if (color.equals(ColoredManaSymbol.U)) {
-            return Sets.findCard("Island", GENERATE_RANDOM_BASIC_LAND);
+            CardInfo cardInfo = CardRepository.instance.findCard("Island");
+            return cardInfo != null ? cardInfo.getCard() : null;
         }
         if (color.equals(ColoredManaSymbol.W)) {
-            return Sets.findCard("Plains", GENERATE_RANDOM_BASIC_LAND);
+            CardInfo cardInfo = CardRepository.instance.findCard("Plains");
+            return cardInfo != null ? cardInfo.getCard() : null;
         }
- 
+
         return null;
     }
- 
+
     protected static boolean isColoredMana(String symbol) {
         return symbol.equals("W") || symbol.equals("G") || symbol.equals("U") || symbol.equals("B") || symbol.equals("R") || symbol.contains("/");
     }
- 
-    public static void main(String[] args) {
-        Card selesnyaGuildMage = null;
-        for (Card card : CardsStorage.getAllCards()) {
-            if (card.getName().equals("Selesnya Guildmage")) {
-                selesnyaGuildMage = card;
-                break;
-            }
-        }
-        if (selesnyaGuildMage == null) {
-            throw new RuntimeException("Couldn't find card: Selesnya Guildmage");
-        }
-        List<ColoredManaSymbol> allowedColors = new ArrayList<ColoredManaSymbol>();
-        allowedColors.add(ColoredManaSymbol.lookup('B'));
-        allowedColors.add(ColoredManaSymbol.lookup('R'));
-        System.out.println(DeckGenerator.cardFitsChosenColors(selesnyaGuildMage, allowedColors));
- 
-        allowedColors.clear();
-        allowedColors = new ArrayList<ColoredManaSymbol>();
-        allowedColors.add(ColoredManaSymbol.lookup('G'));
-        System.out.println(DeckGenerator.cardFitsChosenColors(selesnyaGuildMage, allowedColors));
- 
-        allowedColors.clear();
-        allowedColors = new ArrayList<ColoredManaSymbol>();
-        allowedColors.add(ColoredManaSymbol.lookup('W'));
-        System.out.println(DeckGenerator.cardFitsChosenColors(selesnyaGuildMage, allowedColors));
-    }
-}
\ No newline at end of file
+}
diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java
index d1fd82612a..c55389acce 100644
--- a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java
+++ b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java
@@ -39,9 +39,12 @@ import mage.MageObject;
 import mage.ObjectColor;
 import mage.cards.Card;
 import mage.cards.ExpansionSet;
+import mage.cards.Sets;
+import mage.cards.repository.CardCriteria;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
 import mage.client.cards.BigCard;
 import mage.client.cards.CardGrid;
-import mage.client.cards.CardsStorage;
 import mage.client.cards.ICardGrid;
 import mage.client.constants.Constants.SortBy;
 import mage.client.deckeditor.table.TableModel;
@@ -54,15 +57,14 @@ import mage.filter.predicate.mageobject.ColorPredicate;
 import mage.filter.predicate.mageobject.ColorlessPredicate;
 import mage.filter.predicate.other.CardTextPredicate;
 import mage.filter.predicate.other.ExpansionSetPredicate;
-import mage.sets.Sets;
 import mage.view.CardsView;
 
 import javax.swing.*;
 import javax.swing.table.DefaultTableCellRenderer;
-import java.awt.*;
+import java.awt.Color;
+import java.awt.Cursor;
 import java.awt.event.*;
 import java.util.*;
-import java.util.List;
 
 /**
  *
@@ -71,7 +73,6 @@ import java.util.List;
 public class CardSelector extends javax.swing.JPanel implements ComponentListener {
 
     private final List<Card> cards = new ArrayList<Card>();
-    private FilterCard filter = new FilterCard();
     private BigCard bigCard;
     private boolean limited = false;
 
@@ -163,8 +164,8 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
         filterCards();
     }
 
-    private void buildFilter() {
-        filter = new FilterCard();
+    private FilterCard buildFilter() {
+        FilterCard filter = new FilterCard();
         ArrayList<Predicate<MageObject>> predicates = new ArrayList<Predicate<MageObject>>();
 
         if (this.rdoGreen.isSelected()) {
@@ -224,10 +225,59 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
                 filter.add(Predicates.or(expansionPredicates));
             }
         }
+
+        return filter;
+    }
+
+    private CardCriteria buildCriteria() {
+        CardCriteria criteria = new CardCriteria();
+        criteria.black(this.rdoBlack.isSelected());
+        criteria.blue(this.rdoBlue.isSelected());
+        criteria.green(this.rdoGreen.isSelected());
+        criteria.red(this.rdoRed.isSelected());
+        criteria.white(this.rdoWhite.isSelected());
+        criteria.colorless(this.rdoColorless.isSelected());
+
+        if (this.rdoLand.isSelected()) {
+            criteria.types(CardType.LAND);
+        }
+        if (this.rdoArtifacts.isSelected()) {
+            criteria.types(CardType.ARTIFACT);
+        }
+        if (this.rdoCreatures.isSelected()) {
+            criteria.types(CardType.CREATURE);
+        }
+        if (this.rdoEnchantments.isSelected()) {
+            criteria.types(CardType.ENCHANTMENT);
+        }
+        if (this.rdoInstants.isSelected()) {
+            criteria.types(CardType.INSTANT);
+        }
+        if (this.rdoSorceries.isSelected()) {
+            criteria.types(CardType.SORCERY);
+        }
+        if (this.rdoPlaneswalkers.isSelected()) {
+            criteria.types(CardType.PLANESWALKER);
+        }
+
+        String text = jTextFieldSearch.getText().trim();
+        if (!text.isEmpty()) {
+            // criteria.rules(text);
+        }
+
+        if (this.cbExpansionSet.isVisible()) {
+            String expansionSelection = this.cbExpansionSet.getSelectedItem().toString();
+            if (!expansionSelection.equals("- All Sets")) {
+                List<String> setCodes = ConstructedFormats.getSetsByFormat(expansionSelection);
+                criteria.setCodes(setCodes.toArray(new String[0]));
+            }
+        }
+
+        return criteria;
     }
 
     private void filterCards() {
-        buildFilter();
+        FilterCard filter = buildFilter();
         try {
             List<Card> filteredCards = new ArrayList<Card>();
             setCursor(new Cursor(Cursor.WAIT_CURSOR));
@@ -239,7 +289,9 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
                 }
             }
             else {
-                for (Card card: CardsStorage.getAllCards()) {
+                List<CardInfo> foundCards = CardRepository.instance.findCards(buildCriteria());
+                for (CardInfo cardInfo : foundCards) {
+                    Card card = cardInfo.getCard();
                     if (filter.match(card, null)) {
                         filteredCards.add(card);
                     }
@@ -275,22 +327,6 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
         }
     }
 
-    public Card getCard(UUID cardId) {
-        if (!cards.isEmpty()) {
-            for (Card card: cards) {
-                if (card.getId().equals(cardId))
-                    return card;
-            }
-        }
-        else {
-            for (Card card: CardsStorage.getAllCards()) {
-                if (card.getId().equals(cardId))
-                    return card;
-            }
-        }
-        return null;
-    }
-
     /** This method is called from within the constructor to
      * initialize the form.
      * WARNING: Do NOT modify this code. The content of this method is
@@ -748,34 +784,36 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
     }//GEN-LAST:event_rdoPlaneswalkersActionPerformed
 
     private void cbExpansionSetActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbExpansionSetActionPerformed
-        if (cbExpansionSet.getSelectedItem() instanceof ExpansionSet) {
+        if (this.cbExpansionSet.getSelectedItem().equals("-- Standard")) {
             filterCards();
         } else {
-            if (this.cbExpansionSet.getSelectedItem().equals("-- Standard")) {
-                filterCards();
-            } else {
-                // auto switch for ListView for "All sets" (too many cards to load)
-                jToggleListView.doClick();
-                jToggleListView.setSelected(true);
-            }
+            // auto switch for ListView for "All sets" (too many cards to load)
+            jToggleListView.doClick();
+            jToggleListView.setSelected(true);
         }
-
     }//GEN-LAST:event_cbExpansionSetActionPerformed
 
     private void btnClearActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnClearActionPerformed
         cards.clear();
+        this.limited = false;
         filterCards();
     }//GEN-LAST:event_btnClearActionPerformed
 
     private void btnBoosterActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnBoosterActionPerformed
-        if (cbExpansionSet.getSelectedItem() instanceof ExpansionSet) {
-            List<Card> booster = ((ExpansionSet)this.cbExpansionSet.getSelectedItem()).createBooster();
-            for (Card card: booster) {
-                cards.add(card);
+        List<String> sets = ConstructedFormats.getSetsByFormat(this.cbExpansionSet.getSelectedItem().toString());
+        if (sets.size() == 1) {
+            if (!this.limited) {
+                this.limited = true;
+                cards.clear();
+            }
+            ExpansionSet expansionSet = Sets.getInstance().get(sets.get(0));
+            if (expansionSet != null) {
+                List<Card> booster = expansionSet.createBooster();
+                cards.addAll(booster);
+                filterCards();
             }
-            filterCards();
         } else {
-            JOptionPane.showMessageDialog(null, "It's not possible to generate booster for not Expansion Set \nChoose Expansion Set first.");
+            JOptionPane.showMessageDialog(null, "An expansion set must be selected to be able to generate a booster.");
         }
     }//GEN-LAST:event_btnBoosterActionPerformed
 
diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java
index 83048cb6ec..018f1adffd 100644
--- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java
+++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java
@@ -28,9 +28,12 @@
 package mage.client.deckeditor;
 
 import mage.cards.Card;
+import mage.cards.CardImpl;
 import mage.cards.decks.Deck;
 import mage.cards.decks.importer.DeckImporter;
 import mage.cards.decks.importer.DeckImporterUtil;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
 import mage.client.MageFrame;
 import mage.client.cards.BigCard;
 import mage.client.cards.ICardGrid;
@@ -42,13 +45,16 @@ import mage.client.util.Listener;
 import mage.components.CardInfoPane;
 import mage.game.GameException;
 import mage.remote.Session;
-import mage.sets.Sets;
+import mage.cards.Sets;
 import mage.view.CardView;
+import mage.view.SimpleCardView;
 import org.apache.log4j.Logger;
 
 import javax.swing.*;
 import javax.swing.filechooser.FileFilter;
-import java.awt.*;
+import java.awt.Component;
+import java.awt.Cursor;
+import java.awt.Dimension;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.io.File;
@@ -163,9 +169,11 @@ public class DeckEditorPanel extends javax.swing.JPanel {
                     @Override
                     public void event(Event event) {
                         if (event.getEventName().equals("double-click")) {
-                            Card card = cardSelector.getCard((UUID) event.getSource());
+                            SimpleCardView cardView = (SimpleCardView) event.getSource();
+                            CardInfo cardInfo = CardRepository.instance.findCard(cardView.getExpansionSetCode(), cardView.getCardNumber());
+                            Card card = cardInfo != null ? cardInfo.getCard() : null;
                             if (card != null) {
-                                deck.getCards().add(Sets.createCard(card.getClass()));
+                                deck.getCards().add(card);
                                 if (mode == DeckEditorMode.Sideboard || mode == DeckEditorMode.Limited) {
                                     deck.getSideboard().remove(card);
                                     cardSelector.removeCard(card.getId());
@@ -179,8 +187,12 @@ public class DeckEditorPanel extends javax.swing.JPanel {
                             }
 
                         } else if (event.getEventName().equals("shift-double-click") && mode == DeckEditorMode.Constructed) {
-                            Card card = cardSelector.getCard((UUID) event.getSource());
-                            deck.getSideboard().add(Sets.createCard(card.getClass()));
+                            SimpleCardView cardView = (SimpleCardView) event.getSource();
+                            CardInfo cardInfo = CardRepository.instance.findCard(cardView.getExpansionSetCode(), cardView.getCardNumber());
+                            Card card = cardInfo != null ? cardInfo.getCard() : null;
+                            if (card != null) {
+                                deck.getSideboard().add(CardImpl.createCard(card.getClass()));
+                            }
                             if (cardInfoPane instanceof  CardInfoPane)  {
                                 ((CardInfoPane)cardInfoPane).setCard(new CardView(card));
                             }
@@ -201,8 +213,9 @@ public class DeckEditorPanel extends javax.swing.JPanel {
                 @Override
                 public void event(Event event) {
                     if (event.getEventName().equals("double-click")) {
+                        SimpleCardView cardView = (SimpleCardView) event.getSource();
                         for (Card card: deck.getCards()) {
-                            if (card.getId().equals(event.getSource())) {
+                            if (card.getId().equals(cardView.getId())) {
                                 deck.getCards().remove(card);
                                 if (mode == DeckEditorMode.Limited || mode == DeckEditorMode.Sideboard) {
                                     deck.getSideboard().add(card);
@@ -215,8 +228,9 @@ public class DeckEditorPanel extends javax.swing.JPanel {
                         refreshDeck();
                     }
                     else if (event.getEventName().equals("shift-double-click") && mode == DeckEditorMode.Constructed) {
+                        SimpleCardView cardView = (SimpleCardView) event.getSource();
                         for (Card card: deck.getCards()) {
-                            if (card.getId().equals(event.getSource())) {
+                            if (card.getId().equals(cardView.getId())) {
                                 deck.getCards().remove(card);
                                 deck.getSideboard().add(card);
                                 break;
@@ -233,8 +247,9 @@ public class DeckEditorPanel extends javax.swing.JPanel {
                 @Override
                 public void event(Event event) {
                     if (event.getEventName().equals("double-click")) {
+                        SimpleCardView cardView = (SimpleCardView) event.getSource();
                         for (Card card: deck.getSideboard()) {
-                            if (card.getId().equals(event.getSource())) {
+                            if (card.getId().equals(cardView.getId())) {
                                 deck.getSideboard().remove(card);
                                 deck.getCards().add(card);
                                 break;
diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java
index 432daf5d0a..72f0f83d5f 100644
--- a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java
+++ b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java
@@ -32,8 +32,11 @@ import mage.cards.Card;
 import mage.cards.CardDimensions;
 import mage.cards.MageCard;
 import mage.client.MageFrame;
+import mage.cards.CardImpl;
+import mage.cards.repository.CardCriteria;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
 import mage.client.cards.BigCard;
-import mage.client.cards.CardsStorage;
 import mage.client.components.HoverButton;
 import mage.client.plugins.impl.Plugins;
 import mage.client.util.AudioManager;
@@ -49,11 +52,16 @@ import org.mage.card.arcane.ManaSymbols;
 
 import javax.imageio.ImageIO;
 import javax.swing.*;
-import java.awt.*;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Image;
+import java.awt.Rectangle;
 import java.awt.image.BufferedImage;
 import java.io.FileNotFoundException;
 import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.UUID;
 
 /**
@@ -209,13 +217,14 @@ public class MageBook extends JComponent {
         jLayeredPane.removeAll();
         addLeftRightPageButtons();
 
-        java.util.List<Card> cards = getCards(currentPage, currentSet);
+        List<CardInfo> cards = getCards(currentPage, currentSet);
         int size = cards.size();
 
         Rectangle rectangle = new Rectangle();
         rectangle.translate(OFFSET_X, OFFSET_Y);
         for (int i = 0; i < Math.min(conf.CARDS_PER_PAGE / 2, size); i++) {
-            addCard(new CardView(cards.get(i)), bigCard, null, rectangle);
+            Card card = CardImpl.createCard(cards.get(i).getClassName());
+            addCard(new CardView(card), bigCard, null, rectangle);
             rectangle = CardPosition.translatePosition(i, rectangle, conf);
         }
 
@@ -225,7 +234,8 @@ public class MageBook extends JComponent {
 
         rectangle.setLocation(second_page_x, OFFSET_Y);
         for (int i = conf.CARDS_PER_PAGE / 2; i < Math.min(conf.CARDS_PER_PAGE, size); i++) {
-            addCard(new CardView(cards.get(i)), bigCard, null, rectangle);
+            Card card = CardImpl.createCard(cards.get(i).getClassName());
+            addCard(new CardView(card), bigCard, null, rectangle);
             rectangle = CardPosition.translatePosition(i - conf.CARDS_PER_PAGE / 2, rectangle, conf);
         }
 
@@ -252,10 +262,10 @@ public class MageBook extends JComponent {
         jLayeredPane.add(label);
     }
 
-    private java.util.List<Card> getCards(int page, String set) {
-        int start = page * conf.CARDS_PER_PAGE;
-        int end = (page + 1) * conf.CARDS_PER_PAGE;
-        java.util.List<Card> cards = CardsStorage.getAllCards(start, end, currentSet, false);
+    private List<CardInfo> getCards(int page, String set) {
+        CardCriteria criteria = new CardCriteria();
+        criteria.setCodes(set).start((long) page * conf.CARDS_PER_PAGE).count((long) conf.CARDS_PER_PAGE + 1);
+        List<CardInfo> cards = CardRepository.instance.findCards(criteria);
         if (cards.size() > conf.CARDS_PER_PAGE) {
             pageRight.setVisible(true);
         }
@@ -288,7 +298,7 @@ public class MageBook extends JComponent {
         this.setsToDisplay = ConstructedFormats.getSetsByFormat(format);
         if (this.setsToDisplay.isEmpty()) {
             // display all
-            this.setsToDisplay = CardsStorage.getSetCodes();
+            this.setsToDisplay = CardRepository.instance.getSetCodes();
         }
         addSetTabs();
         tabs.get(0).execute();
diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/TestMageBook.java b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/TestMageBook.java
index 1307c175c1..91d75c815b 100644
--- a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/TestMageBook.java
+++ b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/TestMageBook.java
@@ -3,6 +3,7 @@ package mage.client.deckeditor.collection.viewer;
 import mage.client.plugins.impl.Plugins;
 
 import javax.swing.*;
+import mage.cards.repository.CardScanner;
 import org.mage.card.arcane.ManaSymbols;
 
 /**
@@ -12,6 +13,7 @@ public class TestMageBook extends JFrame {
     public static void main(String[] args) {
         Plugins.getInstance().loadPlugins();
         ManaSymbols.loadImages();
+        CardScanner.scan();
         JFrame frame = new TestMageBook();
         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
         frame.add(new MageBook(null));
diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/table/CardTableSelector.java b/Mage.Client/src/main/java/mage/client/deckeditor/table/CardTableSelector.java
deleted file mode 100644
index 6e38a9ef39..0000000000
--- a/Mage.Client/src/main/java/mage/client/deckeditor/table/CardTableSelector.java
+++ /dev/null
@@ -1,770 +0,0 @@
-/*
-* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
-*
-* Redistribution and use in source and binary forms, with or without modification, are
-* permitted provided that the following conditions are met:
-*
-*    1. Redistributions of source code must retain the above copyright notice, this list of
-*       conditions and the following disclaimer.
-*
-*    2. Redistributions in binary form must reproduce the above copyright notice, this list
-*       of conditions and the following disclaimer in the documentation and/or other materials
-*       provided with the distribution.
-*
-* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
-* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
-* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
-* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*
-* The views and conclusions contained in the software and documentation are those of the
-* authors and should not be interpreted as representing official policies, either expressed
-* or implied, of BetaSteward_at_googlemail.com.
-*/
-
-package mage.client.deckeditor.table;
-
-import mage.Constants.CardType;
-import mage.MageObject;
-import mage.ObjectColor;
-import mage.cards.Card;
-import mage.cards.ExpansionSet;
-import mage.client.cards.BigCard;
-import mage.client.cards.CardEventSource;
-import mage.client.cards.CardsStorage;
-import mage.client.cards.ICardGrid;
-import mage.client.constants.Constants.DeckEditorMode;
-import mage.client.constants.Constants.SortBy;
-import mage.client.util.sets.ConstructedFormats;
-import mage.filter.FilterCard;
-import mage.filter.predicate.Predicate;
-import mage.filter.predicate.Predicates;
-import mage.filter.predicate.mageobject.CardTypePredicate;
-import mage.filter.predicate.mageobject.ColorPredicate;
-import mage.filter.predicate.mageobject.ColorlessPredicate;
-import mage.filter.predicate.other.CardTextPredicate;
-import mage.filter.predicate.other.ExpansionSetPredicate;
-import mage.sets.Sets;
-import mage.view.CardsView;
-
-import javax.swing.*;
-import javax.swing.table.DefaultTableCellRenderer;
-import java.awt.*;
-import java.awt.event.ComponentEvent;
-import java.awt.event.ComponentListener;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.util.*;
-import java.util.List;
-
-/**
- *
- * @author BetaSteward_at_googlemail.com, nantuko
- */
-public class CardTableSelector extends javax.swing.JPanel implements ComponentListener {
-
-    private final List<Card> cards = new ArrayList<Card>();
-    private FilterCard filter = new FilterCard();
-    private BigCard bigCard;
-    protected CardEventSource cardEventSource = new CardEventSource();
-    private DeckEditorMode mode = DeckEditorMode.Constructed;
-
-    public CardTableSelector() {
-        initComponents();
-        this.addComponentListener(this);
-        setOpaque(false);
-        mainTable.setOpaque(false);
-        jScrollPane1.setOpaque(false);
-        jScrollPane1.getViewport().setOpaque(false);
-        cbSortBy.setModel(new DefaultComboBoxModel(SortBy.values()));
-        cbSortBy.setVisible(false);
-        chkPiles.setVisible(false);
-
-        mainTable.addMouseListener(new MouseAdapter() {
-            public void mousePressed(MouseEvent e) {
-                if (e.getClickCount() == 2 && !e.isConsumed()) {
-                    e.consume();
-                    jButtonAddToMainActionPerformed(null);
-                }
-            }
-        });
-
-    }
-
-    private void buildFilter() {
-        filter = new FilterCard();
-        ArrayList<Predicate<MageObject>> predicates = new ArrayList<Predicate<MageObject>>();
-
-        if (this.rdoGreen.isSelected()) {
-            predicates.add(new ColorPredicate(ObjectColor.GREEN));
-        }
-        if (this.rdoRed.isSelected()) {
-            predicates.add(new ColorPredicate(ObjectColor.RED));
-        }
-        if (this.rdoBlack.isSelected()) {
-            predicates.add(new ColorPredicate(ObjectColor.BLACK));
-        }
-        if (this.rdoBlue.isSelected()) {
-            predicates.add(new ColorPredicate(ObjectColor.BLUE));
-        }
-        if (this.rdoWhite.isSelected()) {
-            predicates.add(new ColorPredicate(ObjectColor.WHITE));
-        }
-        if (this.rdoColorless.isSelected()) {
-            predicates.add(new ColorlessPredicate());
-        }
-        filter.add(Predicates.or(predicates));
-
-        predicates.clear();
-        if (this.rdoLand.isSelected()) {
-            predicates.add(new CardTypePredicate(CardType.LAND));
-        }
-        if (this.rdoArtifacts.isSelected()) {
-            predicates.add(new CardTypePredicate(CardType.ARTIFACT));
-        }
-        if (this.rdoCreatures.isSelected()) {
-            predicates.add(new CardTypePredicate(CardType.CREATURE));
-        }
-        if (this.rdoEnchantments.isSelected()) {
-            predicates.add(new CardTypePredicate(CardType.ENCHANTMENT));
-        }
-        if (this.rdoInstants.isSelected()) {
-            predicates.add(new CardTypePredicate(CardType.INSTANT));
-        }
-        if (this.rdoSorceries.isSelected()) {
-            predicates.add(new CardTypePredicate(CardType.SORCERY));
-        }
-        if (this.rdoPlaneswalkers.isSelected()) {
-            predicates.add(new CardTypePredicate(CardType.PLANESWALKER));
-        }
-        filter.add(Predicates.or(predicates));
-
-        String name = jTextFieldSearch.getText().trim();
-        filter.add(new CardTextPredicate(name));
-
-        if (this.cbExpansionSet.getSelectedItem() instanceof ExpansionSet) {
-            filter.add(new ExpansionSetPredicate(((ExpansionSet) this.cbExpansionSet.getSelectedItem()).getCode()));
-        } else if (this.cbExpansionSet.getSelectedItem().equals("-- Standard")) {
-            ArrayList<Predicate<Card>> expansionPredicates = new ArrayList<Predicate<Card>>();
-            for(String setCode : ConstructedFormats.getSetsByFormat("Standard")) {
-                expansionPredicates.add(new ExpansionSetPredicate(setCode));
-            }
-            filter.add(Predicates.or(expansionPredicates));
-        }
-    }
-
-    public void loadCards(List<Card> sideboard, BigCard bigCard, boolean construct) {
-        this.bigCard = bigCard;
-        this.btnBooster.setVisible(false);
-        this.btnClear.setVisible(false);
-        this.cbExpansionSet.setVisible(false);
-        this.cards.clear();
-        for (Card card: sideboard) {
-            this.cards.add(card);
-        }
-
-        filterCards();
-    }
-
-    public void loadCards(BigCard bigCard) {
-        this.bigCard = bigCard;
-        this.btnBooster.setVisible(true);
-        this.btnClear.setVisible(true);
-        this.cbExpansionSet.setVisible(true);
-        Object[] l = Sets.getInstance().values().toArray();
-        Arrays.sort(l, new Comparator<Object>() {
-            @Override
-            public int compare(Object o1, Object o2) {
-                return ((ExpansionSet)o1).getName().compareTo(((ExpansionSet)o2).getName());
-            }
-        });
-        cbExpansionSet.setModel(new DefaultComboBoxModel(l));
-        cbExpansionSet.insertItemAt("-- All sets -- ", 0);
-        cbExpansionSet.setSelectedIndex(0);
-
-        filterCards();
-    }
-
-    private void filterCards() {
-        buildFilter();
-        try {
-            List<Card> filteredCards = new ArrayList<Card>();
-            setCursor(new Cursor(Cursor.WAIT_CURSOR));
-            if (!cards.isEmpty()) {
-                for (Card card: cards) {
-                    if (filter.match(card, null))
-                        filteredCards.add(card);
-                }
-            }
-            else {
-                for (Card card: CardsStorage.getAllCards()) {
-                    if (filter.match(card, null))
-                        filteredCards.add(card);
-                }
-            }
-            this.mainModel.loadCards(new CardsView(filteredCards), (SortBy) cbSortBy.getSelectedItem(), chkPiles.isSelected(), bigCard, null);
-        }
-        finally {
-            setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
-        }
-    }
-
-    public ICardGrid getCardsList() {
-        return this.mainModel;
-    }
-
-    public void removeCard(UUID cardId) {
-        this.mainModel.removeCard(cardId);
-        for (Card card: cards) {
-            if (card.getId().equals(cardId)) {
-                cards.remove(card);
-                break;
-            }
-        }
-    }
-
-    public Card getCard(UUID cardId) {
-        if (!cards.isEmpty()) {
-            for (Card card: cards) {
-                if (card.getId().equals(cardId))
-                    return card;
-            }
-        }
-        else {
-            for (Card card: CardsStorage.getAllCards()) {
-                if (card.getId().equals(cardId))
-                    return card;
-            }
-        }
-        return null;
-    }
-
-    private void initComponents() {
-
-        tbColor = new javax.swing.JToolBar();
-        rdoRed = new javax.swing.JRadioButton();
-        rdoGreen = new javax.swing.JRadioButton();
-        rdoBlue = new javax.swing.JRadioButton();
-        rdoBlack = new javax.swing.JRadioButton();
-        rdoWhite = new javax.swing.JRadioButton();
-        rdoColorless = new javax.swing.JRadioButton();
-        cbExpansionSet = new javax.swing.JComboBox();
-        btnBooster = new javax.swing.JButton();
-        btnClear = new javax.swing.JButton();
-        jScrollPane1 = new javax.swing.JScrollPane();
-        mainTable = new JTable();
-        tbTypes = new javax.swing.JToolBar();
-        rdoLand = new javax.swing.JRadioButton();
-        rdoCreatures = new javax.swing.JRadioButton();
-        rdoArtifacts = new javax.swing.JRadioButton();
-        rdoEnchantments = new javax.swing.JRadioButton();
-        rdoInstants = new javax.swing.JRadioButton();
-        rdoSorceries = new javax.swing.JRadioButton();
-        rdoPlaneswalkers = new javax.swing.JRadioButton();
-        chkPiles = new javax.swing.JCheckBox();
-        cbSortBy = new javax.swing.JComboBox();
-        jTextFieldSearch = new javax.swing.JTextField();
-        jLabel1 = new javax.swing.JLabel();
-        jButtonSearch = new javax.swing.JButton();
-        jButtonClean = new javax.swing.JButton();
-        jSeparator1 = new javax.swing.JSeparator();
-        jButtonAddToMain = new javax.swing.JButton();
-        jButtonAddToSideboard = new javax.swing.JButton();
-
-        tbColor.setFloatable(false);
-        tbColor.setRollover(true);
-
-        rdoRed.setSelected(true);
-        rdoRed.setText("Red ");
-        rdoRed.setFocusable(false);
-        rdoRed.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
-        rdoRed.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
-        rdoRed.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                rdoRedActionPerformed(evt);
-            }
-        });
-        tbColor.add(rdoRed);
-
-        rdoGreen.setSelected(true);
-        rdoGreen.setText("Green ");
-        rdoGreen.setFocusable(false);
-        rdoGreen.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
-        rdoGreen.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
-        rdoGreen.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                rdoGreenActionPerformed(evt);
-            }
-        });
-        tbColor.add(rdoGreen);
-
-        rdoBlue.setSelected(true);
-        rdoBlue.setText("Blue ");
-        rdoBlue.setFocusable(false);
-        rdoBlue.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
-        rdoBlue.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
-        rdoBlue.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                rdoBlueActionPerformed(evt);
-            }
-        });
-        tbColor.add(rdoBlue);
-
-        rdoBlack.setSelected(true);
-        rdoBlack.setText("Black ");
-        rdoBlack.setFocusable(false);
-        rdoBlack.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
-        rdoBlack.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
-        rdoBlack.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                rdoBlackActionPerformed(evt);
-            }
-        });
-        tbColor.add(rdoBlack);
-
-        rdoWhite.setSelected(true);
-        rdoWhite.setText("White ");
-        rdoWhite.setFocusable(false);
-        rdoWhite.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
-        rdoWhite.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
-        rdoWhite.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                rdoWhiteActionPerformed(evt);
-            }
-        });
-        tbColor.add(rdoWhite);
-
-        rdoColorless.setSelected(true);
-        rdoColorless.setText("Colorless ");
-        rdoColorless.setFocusable(false);
-        rdoColorless.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
-        rdoColorless.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
-        rdoColorless.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                rdoColorlessActionPerformed(evt);
-            }
-        });
-        tbColor.add(rdoColorless);
-
-        cbExpansionSet.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
-        cbExpansionSet.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                cbExpansionSetActionPerformed(evt);
-            }
-        });
-        tbColor.add(cbExpansionSet);
-
-        btnBooster.setText("Open Booster");
-        btnBooster.setFocusable(false);
-        btnBooster.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
-        btnBooster.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
-        btnBooster.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                btnBoosterActionPerformed(evt);
-            }
-        });
-        tbColor.add(btnBooster);
-
-        btnClear.setText("Clear");
-        btnClear.setFocusable(false);
-        btnClear.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
-        btnClear.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
-        btnClear.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                btnClearActionPerformed(evt);
-            }
-        });
-        tbColor.add(btnClear);
-
-        mainModel = new TableModel();
-        mainModel.addListeners(mainTable);
-
-        mainTable.setModel(mainModel);
-        mainTable.setForeground(Color.white);
-        DefaultTableCellRenderer myRenderer = (DefaultTableCellRenderer) mainTable.getDefaultRenderer(String.class);
-        myRenderer.setBackground(new Color(0, 0, 0, 100));
-        mainTable.getColumnModel().getColumn(0).setMaxWidth(0);
-        mainTable.getColumnModel().getColumn(0).setPreferredWidth(10);
-        mainTable.getColumnModel().getColumn(1).setPreferredWidth(110);
-        mainTable.getColumnModel().getColumn(2).setPreferredWidth(90);
-        mainTable.getColumnModel().getColumn(3).setPreferredWidth(50);
-        mainTable.getColumnModel().getColumn(4).setPreferredWidth(170);
-        mainTable.getColumnModel().getColumn(5).setPreferredWidth(30);
-        mainTable.getColumnModel().getColumn(6).setPreferredWidth(15);
-        mainTable.getColumnModel().getColumn(7).setPreferredWidth(15);
-
-        jScrollPane1.setViewportView(mainTable);
-
-        tbTypes.setFloatable(false);
-        tbTypes.setRollover(true);
-
-        rdoLand.setSelected(true);
-        rdoLand.setFocusable(false);
-        rdoLand.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
-        rdoLand.setLabel("Land ");
-        rdoLand.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
-        rdoLand.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                rdoLandActionPerformed(evt);
-            }
-        });
-        tbTypes.add(rdoLand);
-
-        rdoCreatures.setSelected(true);
-        rdoCreatures.setFocusable(false);
-        rdoCreatures.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
-        rdoCreatures.setLabel("Creatures ");
-        rdoCreatures.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
-        rdoCreatures.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                rdoCreaturesActionPerformed(evt);
-            }
-        });
-        tbTypes.add(rdoCreatures);
-
-        rdoArtifacts.setSelected(true);
-        rdoArtifacts.setText("Artifacts ");
-        rdoArtifacts.setFocusable(false);
-        rdoArtifacts.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
-        rdoArtifacts.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
-        rdoArtifacts.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                rdoArtifactsActionPerformed(evt);
-            }
-        });
-        tbTypes.add(rdoArtifacts);
-
-        rdoEnchantments.setSelected(true);
-        rdoEnchantments.setText("Enchantments ");
-        rdoEnchantments.setFocusable(false);
-        rdoEnchantments.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
-        rdoEnchantments.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
-        rdoEnchantments.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                rdoEnchantmentsActionPerformed(evt);
-            }
-        });
-        tbTypes.add(rdoEnchantments);
-
-        rdoInstants.setSelected(true);
-        rdoInstants.setText("Instants ");
-        rdoInstants.setFocusable(false);
-        rdoInstants.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
-        rdoInstants.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
-        rdoInstants.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                rdoInstantsActionPerformed(evt);
-            }
-        });
-        tbTypes.add(rdoInstants);
-
-        rdoSorceries.setSelected(true);
-        rdoSorceries.setText("Sorceries ");
-        rdoSorceries.setFocusable(false);
-        rdoSorceries.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
-        rdoSorceries.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
-        rdoSorceries.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                rdoSorceriesActionPerformed(evt);
-            }
-        });
-        tbTypes.add(rdoSorceries);
-
-        rdoPlaneswalkers.setSelected(true);
-        rdoPlaneswalkers.setText("Planeswalkers ");
-        rdoPlaneswalkers.setFocusable(false);
-        rdoPlaneswalkers.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
-        rdoPlaneswalkers.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
-        rdoPlaneswalkers.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                rdoPlaneswalkersActionPerformed(evt);
-            }
-        });
-        tbTypes.add(rdoPlaneswalkers);
-
-        chkPiles.setText("Piles");
-        chkPiles.setFocusable(false);
-        chkPiles.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
-        chkPiles.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
-        chkPiles.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                chkPilesActionPerformed(evt);
-            }
-        });
-        tbTypes.add(chkPiles);
-
-        cbSortBy.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
-        cbSortBy.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                cbSortByActionPerformed(evt);
-            }
-        });
-        tbTypes.add(cbSortBy);
-
-        jLabel1.setText("Search (by name,in rules):");
-
-        jButtonSearch.setLabel("Search");
-        jButtonSearch.setMaximumSize(new java.awt.Dimension(85, 23));
-        jButtonSearch.setMinimumSize(new java.awt.Dimension(85, 23));
-        jButtonSearch.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                jButtonSearchActionPerformed(evt);
-            }
-        });
-
-        jButtonClean.setText("Clear");
-        jButtonClean.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                jButtonCleanActionPerformed(evt);
-            }
-        });
-
-        jSeparator1.setOrientation(javax.swing.SwingConstants.VERTICAL);
-
-        jButtonAddToMain.setLabel("Add to Main");
-        jButtonAddToMain.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                jButtonAddToMainActionPerformed(evt);
-            }
-        });
-
-        jButtonAddToSideboard.setLabel("Add to Sideboard");
-        jButtonAddToSideboard.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                jButtonAddToSideboardActionPerformed(evt);
-            }
-        });
-
-        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
-        this.setLayout(layout);
-        layout.setHorizontalGroup(
-            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                .addComponent(tbColor, javax.swing.GroupLayout.DEFAULT_SIZE, 938, Short.MAX_VALUE)
-                .addComponent(tbTypes, javax.swing.GroupLayout.DEFAULT_SIZE, 938, Short.MAX_VALUE)
-                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 938, Short.MAX_VALUE)
-                .addGroup(layout.createSequentialGroup()
-                        .addContainerGap()
-                        .addComponent(jButtonAddToMain, javax.swing.GroupLayout.PREFERRED_SIZE, 109, javax.swing.GroupLayout.PREFERRED_SIZE)
-                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
-                        .addComponent(jButtonAddToSideboard)
-                        .addGap(18, 18, 18)
-                        .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE)
-                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                        .addComponent(jLabel1)
-                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                        .addComponent(jTextFieldSearch, javax.swing.GroupLayout.PREFERRED_SIZE, 87, javax.swing.GroupLayout.PREFERRED_SIZE)
-                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                        .addComponent(jButtonSearch, javax.swing.GroupLayout.PREFERRED_SIZE, 95, javax.swing.GroupLayout.PREFERRED_SIZE)
-                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                        .addComponent(jButtonClean)
-                        .addContainerGap(322, Short.MAX_VALUE))
-        );
-        layout.setVerticalGroup(
-            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-            .addGroup(layout.createSequentialGroup()
-                .addComponent(tbColor, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)
-                .addGap(0, 0, 0)
-                .addComponent(tbTypes, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)
-                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 284, Short.MAX_VALUE)
-                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
-                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
-                        .addComponent(jButtonAddToMain)
-                        .addComponent(jButtonAddToSideboard))
-                    .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)
-                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
-                        .addComponent(jLabel1)
-                        .addComponent(jButtonSearch, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
-                        .addComponent(jButtonClean)
-                        .addComponent(jTextFieldSearch, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
-                .addContainerGap())
-        );
-    }
-
-    private void rdoGreenActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rdoGreenActionPerformed
-        filterCards();
-    }//GEN-LAST:event_rdoGreenActionPerformed
-
-    private void rdoBlackActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rdoBlackActionPerformed
-        filterCards();
-    }//GEN-LAST:event_rdoBlackActionPerformed
-
-    private void rdoWhiteActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rdoWhiteActionPerformed
-        filterCards();
-    }//GEN-LAST:event_rdoWhiteActionPerformed
-
-    private void rdoRedActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rdoRedActionPerformed
-        filterCards();
-    }//GEN-LAST:event_rdoRedActionPerformed
-
-    private void rdoBlueActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rdoBlueActionPerformed
-        filterCards();
-    }//GEN-LAST:event_rdoBlueActionPerformed
-
-    private void rdoColorlessActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rdoColorlessActionPerformed
-        filterCards();
-    }//GEN-LAST:event_rdoColorlessActionPerformed
-
-    private void rdoLandActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rdoLandActionPerformed
-        filterCards();
-    }//GEN-LAST:event_rdoLandActionPerformed
-
-    private void rdoCreaturesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rdoCreaturesActionPerformed
-        filterCards();
-    }//GEN-LAST:event_rdoCreaturesActionPerformed
-
-    private void rdoArtifactsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rdoArtifactsActionPerformed
-        filterCards();
-    }//GEN-LAST:event_rdoArtifactsActionPerformed
-
-    private void rdoEnchantmentsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rdoEnchantmentsActionPerformed
-        filterCards();
-    }//GEN-LAST:event_rdoEnchantmentsActionPerformed
-
-    private void rdoInstantsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rdoInstantsActionPerformed
-        filterCards();
-    }//GEN-LAST:event_rdoInstantsActionPerformed
-
-    private void rdoSorceriesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rdoSorceriesActionPerformed
-        filterCards();
-    }//GEN-LAST:event_rdoSorceriesActionPerformed
-
-    private void rdoPlaneswalkersActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rdoPlaneswalkersActionPerformed
-        filterCards();
-    }//GEN-LAST:event_rdoPlaneswalkersActionPerformed
-
-    private void cbExpansionSetActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbExpansionSetActionPerformed
-        filterCards();
-    }//GEN-LAST:event_cbExpansionSetActionPerformed
-
-    private void btnClearActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnClearActionPerformed
-        cards.clear();
-        filterCards();
-    }//GEN-LAST:event_btnClearActionPerformed
-
-    private void btnBoosterActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnBoosterActionPerformed
-        if (this.cbExpansionSet.getSelectedItem() instanceof ExpansionSet) {
-            List<Card> booster = ((ExpansionSet)this.cbExpansionSet.getSelectedItem()).createBooster();
-            for (Card card: booster) {
-                cards.add(card);
-            }
-            filterCards();
-        }
-    }//GEN-LAST:event_btnBoosterActionPerformed
-
-    private void cbSortByActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbSortByActionPerformed
-        if (cbSortBy.getSelectedItem() instanceof SortBy)
-            this.mainModel.drawCards((SortBy) cbSortBy.getSelectedItem(), chkPiles.isSelected());
-    }//GEN-LAST:event_cbSortByActionPerformed
-
-    private void chkPilesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chkPilesActionPerformed
-        if (cbSortBy.getSelectedItem() instanceof SortBy)
-            this.mainModel.drawCards((SortBy) cbSortBy.getSelectedItem(), chkPiles.isSelected());
-    }//GEN-LAST:event_chkPilesActionPerformed
-
-    private void jButtonAddToMainActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed
-        if (mainTable.getSelectedRowCount() > 0) {
-            int[] n = mainTable.getSelectedRows();
-            List<Integer> indexes = asList(n);
-            Collections.reverse(indexes);
-            for (Integer index : indexes) {
-                mainModel.doubleClick(index);
-            }
-            if (!mode.equals(DeckEditorMode.Constructed))
-                mainModel.fireTableDataChanged();
-        }
-    }//GEN-LAST:event_jButton3ActionPerformed
-
-    private void jButtonAddToSideboardActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton4ActionPerformed
-        if (mainTable.getSelectedRowCount() > 0) {
-            int[] n = mainTable.getSelectedRows();
-            List<Integer> indexes = asList(n);
-            Collections.reverse(indexes);
-            for (Integer index : indexes) {
-                mainModel.shiftDoubleClick(index);
-            }
-            if (!mode.equals(DeckEditorMode.Constructed))
-                mainModel.fireTableDataChanged();
-        }
-    }//GEN-LAST:event_jButton4ActionPerformed
-
-    public List<Integer> asList(final int[] is) {
-        List<Integer> list = new ArrayList<Integer>();
-        for (int i : is) list.add(i);
-        return list;
-    }
-
-    private void jButtonSearchActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
-        filterCards();
-    }//GEN-LAST:event_jButton1ActionPerformed
-
-    private void jButtonCleanActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed
-        jTextFieldSearch.setText("");
-        filterCards();
-    }//GEN-LAST:event_jButton2ActionPerformed
-
-    private TableModel mainModel;
-
-    private javax.swing.JButton btnBooster;
-    private javax.swing.JButton btnClear;
-    private JTable mainTable = new JTable();
-    private javax.swing.JComboBox cbExpansionSet;
-    private javax.swing.JComboBox cbSortBy;
-    private javax.swing.JCheckBox chkPiles;
-    private javax.swing.JButton jButtonSearch;
-    private javax.swing.JButton jButtonClean;
-    private javax.swing.JButton jButtonAddToMain;
-    private javax.swing.JButton jButtonAddToSideboard;
-    private javax.swing.JLabel jLabel1;
-    private javax.swing.JScrollPane jScrollPane1;
-    private javax.swing.JSeparator jSeparator1;
-    private javax.swing.JTextField jTextFieldSearch;
-    private javax.swing.JRadioButton rdoArtifacts;
-    private javax.swing.JRadioButton rdoBlack;
-    private javax.swing.JRadioButton rdoBlue;
-    private javax.swing.JRadioButton rdoColorless;
-    private javax.swing.JRadioButton rdoCreatures;
-    private javax.swing.JRadioButton rdoEnchantments;
-    private javax.swing.JRadioButton rdoGreen;
-    private javax.swing.JRadioButton rdoInstants;
-    private javax.swing.JRadioButton rdoLand;
-    private javax.swing.JRadioButton rdoPlaneswalkers;
-    private javax.swing.JRadioButton rdoRed;
-    private javax.swing.JRadioButton rdoSorceries;
-    private javax.swing.JRadioButton rdoWhite;
-    private javax.swing.JToolBar tbColor;
-    private javax.swing.JToolBar tbTypes;
-
-    @Override
-    public void componentResized(ComponentEvent e) {
-        if (cbSortBy.getSelectedItem() instanceof SortBy)
-            this.mainModel.drawCards((SortBy) cbSortBy.getSelectedItem(), chkPiles.isSelected());
-    }
-
-    @Override
-    public void componentMoved(ComponentEvent e) {
-        if (cbSortBy.getSelectedItem() instanceof SortBy)
-            this.mainModel.drawCards((SortBy) cbSortBy.getSelectedItem(), chkPiles.isSelected());
-    }
-
-    @Override
-    public void componentShown(ComponentEvent e) {
-        if (cbSortBy.getSelectedItem() instanceof SortBy)
-            this.mainModel.drawCards((SortBy) cbSortBy.getSelectedItem(), chkPiles.isSelected());
-    }
-
-    @Override
-    public void componentHidden(ComponentEvent e) {
-        if (cbSortBy.getSelectedItem() instanceof SortBy)
-            this.mainModel.drawCards((SortBy) cbSortBy.getSelectedItem(), chkPiles.isSelected());
-    }
-
-    public void setMode(DeckEditorMode mode) {
-        this.mode = mode;
-    }
-}
diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/table/TableModel.java b/Mage.Client/src/main/java/mage/client/deckeditor/table/TableModel.java
index 2e4c07a5ac..a7093bf3ec 100644
--- a/Mage.Client/src/main/java/mage/client/deckeditor/table/TableModel.java
+++ b/Mage.Client/src/main/java/mage/client/deckeditor/table/TableModel.java
@@ -269,12 +269,12 @@ public class TableModel extends AbstractTableModel implements ICardGrid {
 
     public void doubleClick(int index) {
         CardView card = view.get(index);
-        cardEventSource.doubleClick(card.getId(), "double-click");
+        cardEventSource.doubleClick(card, "double-click");
     }
 
     public void shiftDoubleClick(int index) {
         CardView card = view.get(index);
-        cardEventSource.shiftDoubleClick(card.getId(), "shift-double-click");
+        cardEventSource.shiftDoubleClick(card, "shift-double-click");
     }
 
     public void removeFromMainEvent(int index) {
diff --git a/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java b/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java
index d8694959a9..f4df539250 100644
--- a/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java
+++ b/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java
@@ -34,10 +34,14 @@
 
 package mage.client.dialog;
 
+import java.util.List;
+import java.util.Random;
 import javax.swing.JLayeredPane;
+import mage.cards.Card;
 import mage.cards.decks.Deck;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
 import mage.client.MageFrame;
-import mage.sets.Sets;
 
 /**
  *
@@ -59,6 +63,19 @@ public class AddLandDialog extends MageDialog {
         this.setVisible(true);
     }
 
+    private void addLands(String landName, int number) {
+        Random random = new Random();
+        List<CardInfo> cards = CardRepository.instance.findCards(landName);
+        if (cards.isEmpty()) {
+            return;
+        }
+
+        for (int i = 0; i < number; i++) {
+            Card land = cards.get(random.nextInt(cards.size())).getCard();
+            deck.getCards().add(land);
+        }
+    }
+
     /** This method is called from within the constructor to
      * initialize the form.
      * WARNING: Do NOT modify this code. The content of this method is
@@ -194,25 +211,16 @@ public class AddLandDialog extends MageDialog {
 
     private void btnAddActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnAddActionPerformed
         int nForest = ((Number)spnForest.getValue()).intValue();
-        for (int i = 0; i < nForest; i++) {
-            deck.getCards().add(Sets.findCard("Forest", true));
-        }
         int nIsland = ((Number)spnIsland.getValue()).intValue();
-        for (int i = 0; i < nIsland; i++) {
-            deck.getCards().add(Sets.findCard("Island", true));
-        }
         int nMountain = ((Number)spnMountain.getValue()).intValue();
-        for (int i = 0; i < nMountain; i++) {
-            deck.getCards().add(Sets.findCard("Mountain", true));
-        }
         int nPlains = ((Number)spnPlains.getValue()).intValue();
-        for (int i = 0; i < nPlains; i++) {
-            deck.getCards().add(Sets.findCard("Plains", true));
-        }
         int nSwamp = ((Number)spnSwamp.getValue()).intValue();
-        for (int i = 0; i < nSwamp; i++) {
-            deck.getCards().add(Sets.findCard("Swamp", true));
-        }
+
+        addLands("Forest", nForest);
+        addLands("Island", nIsland);
+        addLands("Mountain", nMountain);
+        addLands("Plains", nPlains);
+        addLands("Swamp", nSwamp);
         this.hideDialog();
     }//GEN-LAST:event_btnAddActionPerformed
 
diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java
index 838f95f7e2..2c71cfbc97 100644
--- a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java
+++ b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java
@@ -44,7 +44,7 @@ import mage.game.draft.DraftOptions.TimingOption;
 import mage.game.tournament.LimitedOptions;
 import mage.game.tournament.TournamentOptions;
 import mage.remote.Session;
-import mage.sets.Sets;
+import mage.cards.Sets;
 import mage.view.TableView;
 import mage.view.TournamentTypeView;
 import org.apache.log4j.Logger;
diff --git a/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java b/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java
index 3414b1ac56..0871a31155 100644
--- a/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java
+++ b/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java
@@ -37,6 +37,7 @@ package mage.client.game;
 import mage.cards.MageCard;
 import mage.cards.action.ActionCallback;
 import mage.cards.decks.importer.DckDeckImporter;
+import mage.cards.repository.CardRepository;
 import mage.client.MageFrame;
 import mage.client.cards.BigCard;
 import mage.client.components.HoverButton;
@@ -50,7 +51,6 @@ import mage.client.util.ImageHelper;
 import mage.client.util.gui.BufferedImageBuilder;
 import mage.components.ImagePanel;
 import mage.remote.Session;
-import mage.sets.Sets;
 import mage.view.CardView;
 import mage.view.ManaPoolView;
 import mage.view.PlayerView;
@@ -202,7 +202,7 @@ public class PlayerPanelExt extends javax.swing.JPanel {
         // Avatar
         Image image = ImageHelper.getImageFromResources("/avatars/unknown.jpg");
 
-        topCardPanel = Plugins.getInstance().getMageCard(new CardView(Sets.findCard("Forest")), bigCard, topCardDimension, gameId, true);
+        topCardPanel = Plugins.getInstance().getMageCard(new CardView(CardRepository.instance.findCard("Forest").getCard()), bigCard, topCardDimension, gameId, true);
         topCardPanel.setVisible(false);
         panelBackground.add(topCardPanel);
 
diff --git a/Mage.Client/src/main/java/mage/client/plugins/MagePlugins.java b/Mage.Client/src/main/java/mage/client/plugins/MagePlugins.java
index 6816e63972..b0b896fec1 100644
--- a/Mage.Client/src/main/java/mage/client/plugins/MagePlugins.java
+++ b/Mage.Client/src/main/java/mage/client/plugins/MagePlugins.java
@@ -1,6 +1,5 @@
 package mage.client.plugins;
 
-import mage.cards.Card;
 import mage.cards.MageCard;
 import mage.cards.MagePermanent;
 import mage.cards.action.ActionCallback;
@@ -13,7 +12,6 @@ import java.awt.*;
 import java.awt.image.BufferedImage;
 import java.util.Collection;
 import java.util.Map;
-import java.util.Set;
 import java.util.UUID;
 
 public interface MagePlugins {
@@ -27,8 +25,6 @@ public interface MagePlugins {
     boolean isCardPluginLoaded();
     boolean isCounterPluginLoaded();
     int sortPermanents(Map<String, JComponent> ui, Collection<MagePermanent> permanents);
-    boolean newImage(Set<Card> allCards);
-    void downloadImage(Set<Card> allCards);
     void downloadSymbols();
     int getGamesPlayed();
     void addGamesPlayed();
diff --git a/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java b/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java
index 36fbdb98a4..f239140582 100644
--- a/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java
+++ b/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java
@@ -119,22 +119,6 @@ public class Plugins implements MagePlugins {
         return -1;
     }
 
-    @Override
-    public boolean newImage(Set<mage.cards.Card> allCards) {
-        String useDefault = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_USE_DEFAULT, "true");
-        String path = useDefault.equals("true") ? null : PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PATH, null);
-        return this.cardPlugin.newImages(allCards, path);
-    }
-
-    @Override
-    public void downloadImage(Set<mage.cards.Card> allCards) {
-        String useDefault = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_USE_DEFAULT, "true");
-        String path = useDefault.equals("true") ? null : PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PATH, null);
-        if (this.cardPlugin != null) {
-            this.cardPlugin.downloadImages(allCards, path);
-        }
-    }
-
     @Override
     public void downloadSymbols() {
         String useDefault = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_USE_DEFAULT, "true");
diff --git a/Mage.Client/src/main/java/mage/client/util/CardsViewUtil.java b/Mage.Client/src/main/java/mage/client/util/CardsViewUtil.java
index 07d61ffdfe..2dc69ae457 100644
--- a/Mage.Client/src/main/java/mage/client/util/CardsViewUtil.java
+++ b/Mage.Client/src/main/java/mage/client/util/CardsViewUtil.java
@@ -28,7 +28,9 @@
 
 package mage.client.util;
 
-import mage.sets.Sets;
+import mage.cards.Card;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
 import mage.view.CardView;
 import mage.view.CardsView;
 import mage.view.SimpleCardView;
@@ -44,9 +46,9 @@ public class CardsViewUtil {
         CardsView cards = new CardsView();
 
         for (SimpleCardView simple: view.values()) {
-            mage.cards.Card card = Sets.findCard(simple.getExpansionSetCode(), simple.getCardNumber());
+            CardInfo cardInfo = CardRepository.instance.findCard(simple.getExpansionSetCode(), simple.getCardNumber());
+            Card card = cardInfo != null ? cardInfo.getCard() : null;
             if (card != null) {
-
                 cards.put(simple.getId(), new CardView(card, simple.getId()));
             }
         }
diff --git a/Mage.Client/src/main/java/mage/client/util/DeckUtil.java b/Mage.Client/src/main/java/mage/client/util/DeckUtil.java
index f4608bf780..8be31a46fc 100644
--- a/Mage.Client/src/main/java/mage/client/util/DeckUtil.java
+++ b/Mage.Client/src/main/java/mage/client/util/DeckUtil.java
@@ -29,9 +29,9 @@
 package mage.client.util;
 
 import mage.cards.Card;
-import mage.cards.ExpansionSet;
 import mage.cards.decks.Deck;
-import mage.sets.Sets;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
 import mage.view.DeckView;
 import mage.view.SimpleCardView;
 import org.apache.log4j.Logger;
@@ -51,25 +51,21 @@ public class DeckUtil {
     public static Deck construct(DeckView view) {
         Deck deck = new Deck();
         for (SimpleCardView cardView : view.getCards().values()) {
-            ExpansionSet set = Sets.findSet(cardView.getExpansionSetCode());
-            if (set != null) {
-                Card card = set.findCard(cardView.getCardNumber());
-                if (card != null) {
-                    deck.getCards().add(card);
-                } else {
-                    log.fatal("(Deck constructing) Couldn't find card: set=" + cardView.getExpansionSetCode() + ", cid=" + Integer.valueOf(cardView.getCardNumber()));
-                }
+            CardInfo cardInfo = CardRepository.instance.findCard(cardView.getExpansionSetCode(), cardView.getCardNumber());
+            Card card = cardInfo != null ? cardInfo.getCard() : null;
+            if (card != null) {
+                deck.getCards().add(card);
+            } else {
+                log.fatal("(Deck constructing) Couldn't find card: set=" + cardView.getExpansionSetCode() + ", cid=" + Integer.valueOf(cardView.getCardNumber()));
             }
         }
         for (SimpleCardView cardView : view.getSideboard().values()) {
-            ExpansionSet set = Sets.findSet(cardView.getExpansionSetCode());
-            if (set != null) {
-                Card card = set.findCard(cardView.getCardNumber());
-                if (card != null) {
-                    deck.getSideboard().add(card);
-                } else {
-                    log.fatal("(Deck constructing) Couldn't find card: set=" + cardView.getExpansionSetCode() + ", cid=" + Integer.valueOf(cardView.getCardNumber()));
-                }
+            CardInfo cardInfo = CardRepository.instance.findCard(cardView.getExpansionSetCode(), cardView.getCardNumber());
+            Card card = cardInfo != null ? cardInfo.getCard() : null;
+            if (card != null) {
+                deck.getSideboard().add(card);
+            } else {
+                log.fatal("(Deck constructing) Couldn't find card: set=" + cardView.getExpansionSetCode() + ", cid=" + Integer.valueOf(cardView.getCardNumber()));
             }
         }
         return deck;
diff --git a/Mage.Client/src/main/java/mage/client/util/sets/ConstructedFormats.java b/Mage.Client/src/main/java/mage/client/util/sets/ConstructedFormats.java
index 4a801cecf5..db337be4f4 100644
--- a/Mage.Client/src/main/java/mage/client/util/sets/ConstructedFormats.java
+++ b/Mage.Client/src/main/java/mage/client/util/sets/ConstructedFormats.java
@@ -6,8 +6,8 @@ import java.util.Date;
 import java.util.GregorianCalendar;
 import java.util.List;
 import mage.cards.ExpansionSet;
-import mage.client.cards.CardsStorage;
-import mage.sets.Sets;
+import mage.cards.repository.CardRepository;
+import mage.cards.Sets;
 
 /**
  * Utility class for constructed formats.
@@ -344,10 +344,9 @@ public class ConstructedFormats {
     }
 
     private static void buildLists() {
-        
-        for (String setCode : CardsStorage.getSetCodes()) {
+        for (String setCode : CardRepository.instance.getSetCodes()) {
             ExpansionSet set = Sets.findSet(setCode);
-        
+
             if (set.getReleaseDate().after(standardDate)) {
                 standard.add(set.getCode());
             }
@@ -359,7 +358,7 @@ public class ConstructedFormats {
             }
         }
     }
-    
+
     private static final List<String> standard = new ArrayList<String>();
     private static final Date standardDate = new GregorianCalendar(2011, 9, 29).getTime();
 
diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java
index b5c6f8210c..363daebc43 100644
--- a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java
+++ b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java
@@ -1,6 +1,6 @@
 package org.mage.card.arcane;
 
-import mage.client.cards.CardsStorage;
+import mage.cards.repository.CardRepository;
 import mage.client.util.ImageHelper;
 import mage.client.util.gui.BufferedImageBuilder;
 import org.apache.log4j.Logger;
@@ -47,7 +47,8 @@ public class ManaSymbols {
             } catch (Exception e) {
             }
         }
-        for (String set : CardsStorage.getSetCodes()) {
+        List<String> setCodes = CardRepository.instance.getSetCodes();
+        for (String set : setCodes) {
             String _set = set.equals("CON") ? "CFX" : set;
             File file = new File(Constants.RESOURCE_PATH_SET + _set + "-C.jpg");
             try {
@@ -104,7 +105,7 @@ public class ManaSymbols {
         }
 
         File file;
-        for (String set : CardsStorage.getSetCodes()) {
+        for (String set : CardRepository.instance.getSetCodes()) {
             file = new File(Constants.RESOURCE_PATH_SET_SMALL);
             if (!file.exists()) {
                 break;
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java b/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java
index 5f54909f5e..9deac148ea 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java
@@ -1,6 +1,5 @@
 package org.mage.plugins.card;
 
-import mage.cards.Card;
 import mage.cards.MagePermanent;
 import mage.cards.action.ActionCallback;
 import mage.interfaces.plugin.CardPlugin;
@@ -21,7 +20,6 @@ import org.mage.plugins.card.dl.DownloadJob;
 import org.mage.plugins.card.dl.Downloader;
 import org.mage.plugins.card.dl.sources.GathererSets;
 import org.mage.plugins.card.dl.sources.GathererSymbols;
-import org.mage.plugins.card.images.DownloadPictures;
 import org.mage.plugins.card.images.ImageCache;
 import org.mage.plugins.card.info.CardInfoPaneImpl;
 
@@ -421,22 +419,6 @@ public class CardPluginImpl implements CardPlugin {
         }
     }
 
-    @Override
-    public boolean newImages(Set<Card> allCards, String imagesPath) {
-        return DownloadPictures.checkForNewCards(allCards, imagesPath);
-    }
-
-    /**
-     * Download images.
-     *
-     * @param allCards Set of cards to download images for.
-     * @param imagesPath Path to check in and store images to. Can be null, in such case default path should be used.
-     */
-    @Override
-    public void downloadImages(Set<Card> allCards, String imagesPath) {
-        DownloadPictures.startDownload(null, allCards, imagesPath);
-    }
-
     /**
      * Download various symbols (mana, tap, set).
      *
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPictures.java b/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPictures.java
index 1c38b658ea..c282d199e8 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPictures.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPictures.java
@@ -21,7 +21,9 @@ import javax.imageio.ImageWriteParam;
 import javax.imageio.ImageWriter;
 import javax.imageio.stream.FileImageOutputStream;
 import javax.swing.*;
-import java.awt.*;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Graphics2D;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.image.BufferedImage;
@@ -32,7 +34,7 @@ import java.net.URL;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.Set;
+import java.util.List;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.regex.Pattern;
@@ -67,7 +69,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
         startDownload(null, null, null);
     }
 
-    public static void startDownload(JFrame frame, Set<Card> allCards, String imagesPath) {
+    public static void startDownload(JFrame frame, List<Card> allCards, String imagesPath) {
         ArrayList<CardInfo> cards = getNeededCards(allCards, imagesPath);
 
         /*
@@ -207,7 +209,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
         dlg = new JOptionPane(p0, JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[1]);
     }
 
-    public static boolean checkForNewCards(Set<Card> allCards, String imagesPath) {
+    public static boolean checkForNewCards(List<Card> allCards, String imagesPath) {
         TFile file;
         for (Card card : allCards) {
             if (card.getCardNumber() > 0 && !card.getExpansionSetCode().isEmpty()) {
@@ -221,7 +223,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
         return false;
     }
 
-    private static ArrayList<CardInfo> getNeededCards(Set<Card> allCards, String imagesPath) {
+    private static ArrayList<CardInfo> getNeededCards(List<Card> allCards, String imagesPath) {
 
         ArrayList<CardInfo> cardsToDownload = new ArrayList<CardInfo>();
 
diff --git a/Mage.Common/src/mage/interfaces/plugin/CardPlugin.java b/Mage.Common/src/mage/interfaces/plugin/CardPlugin.java
index 0bb4140532..45be448079 100644
--- a/Mage.Common/src/mage/interfaces/plugin/CardPlugin.java
+++ b/Mage.Common/src/mage/interfaces/plugin/CardPlugin.java
@@ -1,6 +1,5 @@
 package mage.interfaces.plugin;
 
-import mage.cards.Card;
 import mage.cards.MagePermanent;
 import mage.cards.action.ActionCallback;
 import mage.view.CardView;
@@ -12,7 +11,6 @@ import java.awt.*;
 import java.awt.image.BufferedImage;
 import java.util.Collection;
 import java.util.Map;
-import java.util.Set;
 import java.util.UUID;
 
 /**
@@ -29,22 +27,6 @@ public interface CardPlugin extends Plugin {
     MagePermanent getMageCard(CardView permanent, Dimension dimension, UUID gameId, ActionCallback callback, boolean canBeFoil, boolean loadImage);
     int sortPermanents(Map<String, JComponent> ui, Collection<MagePermanent> cards, Map<String, String> options);
 
-    /**
-     * Check for new images.
-     *
-     * @param allCards Set of cards to check images for.
-     * @param imagesPath Path to check in. Can be null, in such case default path should be used.
-     */
-    public boolean newImages(Set<Card> allCards, String imagesPath);
-
-    /**
-     * Download images.
-     *
-     * @param allCards Set of cards to download images for.
-     * @param imagesPath Path to check in and store images to. Can be null, in such case default path should be used.
-     */
-    void downloadImages(Set<Card> allCards, String imagesPath);
-
     /**
      * Download various symbols (mana, tap, set).
      *
diff --git a/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/RateFrame.java b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/RateFrame.java
index 1a5141c032..a0745d3783 100644
--- a/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/RateFrame.java
+++ b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/RateFrame.java
@@ -1,7 +1,6 @@
 package org.mage.plugins.rating;
 
 import org.apache.log4j.Logger;
-import org.mage.plugins.rating.cards.CardsStorage;
 import org.mage.plugins.rating.results.ResultHandler;
 import org.mage.plugins.rating.ui.BigCard;
 
@@ -78,7 +77,6 @@ public class RateFrame extends JFrame {
     }
 
     public void startRating() {
-        CardsStorage.getAllCards();
         label.setText("The results are stored automatically for every 10 compare.");
         RateThread.getInstance().start(this, this.bigCard);
     }
diff --git a/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/RateThread.java b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/RateThread.java
index 824fad88d2..435c697033 100644
--- a/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/RateThread.java
+++ b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/RateThread.java
@@ -12,10 +12,13 @@ import mage.Constants.CardType;
 import mage.cards.Card;
 import mage.cards.CardDimensions;
 import mage.cards.MageCard;
+import mage.cards.repository.CardCriteria;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
+import mage.cards.repository.CardScanner;
 import mage.view.CardView;
 
 import org.mage.plugins.card.CardPluginImpl;
-import org.mage.plugins.rating.cards.CardsStorage;
 import org.mage.plugins.rating.results.Rating;
 import org.mage.plugins.rating.results.ResultHandler;
 import org.mage.plugins.rating.ui.BigCard;
@@ -35,6 +38,21 @@ public class RateThread extends Thread {
     private Random random = new Random();
 
     private static List<Rating> results = new ArrayList<Rating>();
+    private static final List<CardInfo> cards = new ArrayList<CardInfo>();
+
+    static {
+        CardScanner.scan();
+        CardCriteria criteria = new CardCriteria();
+        criteria.notTypes(CardType.LAND);
+        List<CardInfo> allCards = CardRepository.instance.findCards(criteria);
+        List<String> names = new ArrayList<String>();
+        for (CardInfo card : allCards) {
+            if (!names.contains(card.getName())) {
+                names.add(card.getName());
+                cards.add(card);
+            }
+        }
+    }
 
     public RateThread() {
         setDaemon(true);
@@ -70,14 +88,12 @@ public class RateThread extends Thread {
     }
 
     protected Card getRandomUniqueNonLandCard(Card previousCard) {
-        int count = CardsStorage.getUniqueCards().size();
-        int index = random.nextInt(count);
-        Card card1 = CardsStorage.getUniqueCards().get(index);
-        while (card1.getCardType().contains(CardType.LAND) || card1.getName().equals(previousCard)) {
-            index = random.nextInt(count);
-            card1 = CardsStorage.getUniqueCards().get(index);
+        int count = cards.size();
+        Card card = cards.get(random.nextInt(count)).getCard();
+        while (previousCard != null && card.getName().equals(previousCard.getName())) {
+            card = cards.get(random.nextInt(count)).getCard();
         }
-        return card1;
+        return card;
     }
 
     public void start(JFrame frame, BigCard bigCard) {
diff --git a/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/cards/CardsStorage.java b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/cards/CardsStorage.java
deleted file mode 100644
index a4e5625aed..0000000000
--- a/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/cards/CardsStorage.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package org.mage.plugins.rating.cards;
-
-import mage.cards.Card;
-import mage.cards.ExpansionSet;
-import mage.sets.Sets;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-public class CardsStorage {
-    private static List<Card> allCards = new ArrayList<Card>();
-    private static List<Card> uniqueCards = new ArrayList<Card>();
-
-    static {
-        for (ExpansionSet set: Sets.getInstance().values()) {
-            if (set.getName().equals("Magic 2013")) {
-                allCards.addAll(set.getCards());
-            }
-        }
-        Set<String> names = new HashSet<String>();
-        for (Card card : allCards) {
-            if (!names.contains(card.getName())) {
-                uniqueCards.add(card);
-                names.add(card.getName());
-            }
-        }
-        System.out.println("cards=" + allCards.size() + ", unique cards=" + uniqueCards.size());
-    }
-
-    public static List<Card> getAllCards() {
-        return allCards;
-    }
-
-    public static List<Card> getUniqueCards() {
-        return uniqueCards;
-    }
-}
diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Extended.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Extended.java
index 6ec14ee49e..45def4ee99 100644
--- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Extended.java
+++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Extended.java
@@ -33,7 +33,7 @@ import java.util.GregorianCalendar;
 import mage.Constants.SetType;
 import mage.cards.ExpansionSet;
 import mage.cards.decks.Constructed;
-import mage.sets.Sets;
+import mage.cards.Sets;
 
 /**
  *
diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Standard.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Standard.java
index deda018651..e38979af4f 100644
--- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Standard.java
+++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Standard.java
@@ -33,7 +33,7 @@ import java.util.GregorianCalendar;
 import mage.Constants.SetType;
 import mage.cards.ExpansionSet;
 import mage.cards.decks.Constructed;
-import mage.sets.Sets;
+import mage.cards.Sets;
 
 /**
  *
diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java
index 6284274666..765ffc7f04 100644
--- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java
+++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java
@@ -51,6 +51,8 @@ import mage.abilities.mana.ManaOptions;
 import mage.cards.Card;
 import mage.cards.Cards;
 import mage.cards.decks.Deck;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
 import mage.choices.Choice;
 import mage.filter.FilterPermanent;
 import mage.filter.common.*;
@@ -72,7 +74,6 @@ import mage.players.Player;
 import mage.players.PlayerImpl;
 import mage.players.net.UserData;
 import mage.players.net.UserGroup;
-import mage.sets.Sets;
 import mage.target.*;
 import mage.target.common.*;
 import mage.util.Copier;
@@ -1096,6 +1097,19 @@ public class ComputerPlayer<T extends ComputerPlayer<T>> extends PlayerImpl<T> i
         match.submitDeck(playerId, deck);
     }
 
+    private static void addBasicLands(Deck deck, String landName, int number) {
+        Random random = new Random();
+        List<CardInfo> cards = CardRepository.instance.findCards(landName);
+        if (cards.isEmpty()) {
+            return;
+        }
+
+        for (int i = 0; i < number; i++) {
+            Card land = cards.get(random.nextInt(cards.size())).getCard();
+            deck.getCards().add(land);
+        }
+    }
+
     public static Deck buildDeck(List<Card> cardPool, final List<Constants.ColoredManaSymbol> colors) {
         Deck deck = new Deck();
         List<Card> sortedCards = new ArrayList<Card>(cardPool);
@@ -1124,46 +1138,29 @@ public class ComputerPlayer<T extends ComputerPlayer<T>> extends PlayerImpl<T> i
         }
         double total = mana.getBlack() + mana.getBlue() + mana.getGreen() + mana.getRed() + mana.getWhite();
         if (mana.getGreen() > 0) {
-            int numGreen = (int) Math.round(mana.getGreen() / total * 17);
-            for (int i = 0; i < numGreen; i++) {
-                Card land = Sets.findCard("Forest", true);
-                deck.getCards().add(land);
-            }
+            int number = (int) Math.round(mana.getGreen() / total * 17);
+            addBasicLands(deck, "Forest", number);
         }
         if (mana.getBlack() > 0) {
-            int numBlack = (int) Math.round(mana.getBlack() / total * 17);
-            for (int i = 0; i < numBlack; i++) {
-                Card land = Sets.findCard("Swamp", true);
-                deck.getCards().add(land);
-            }
+            int number = (int) Math.round(mana.getBlack() / total * 17);
+            addBasicLands(deck, "Swamp", number);
         }
         if (mana.getBlue() > 0) {
-            int numBlue = (int) Math.round(mana.getBlue() / total * 17);
-            for (int i = 0; i < numBlue; i++) {
-                Card land = Sets.findCard("Island", true);
-                deck.getCards().add(land);
-            }
+            int number = (int) Math.round(mana.getBlue() / total * 17);
+            addBasicLands(deck, "Island", number);
         }
         if (mana.getWhite() > 0) {
-            int numWhite = (int) Math.round(mana.getWhite() / total * 17);
-            for (int i = 0; i < numWhite; i++) {
-                Card land = Sets.findCard("Plains", true);
-                deck.getCards().add(land);
-            }
+            int number = (int) Math.round(mana.getWhite() / total * 17);
+            addBasicLands(deck, "Plains", number);
         }
         if (mana.getRed() > 0) {
-            int numRed = (int) Math.round(mana.getRed() / total * 17);
-            for (int i = 0; i < numRed; i++) {
-                Card land = Sets.findCard("Mountain", true);
-                deck.getCards().add(land);
-            }
-        }
-        while (deck.getCards().size() < 40) {
-            //TODO: improve this
-            Card land = Sets.findCard("Forest", true);
-            deck.getCards().add(land);
+            int number = (int) Math.round(mana.getRed() / total * 17);
+            addBasicLands(deck, "Mountain", number);
         }
 
+        //TODO: improve this
+        addBasicLands(deck, "Forest", 40 - deck.getCards().size());
+
         return deck;
     }
 
diff --git a/Mage.Server/src/main/java/mage/server/Main.java b/Mage.Server/src/main/java/mage/server/Main.java
index be595752bc..8f9d6693bd 100644
--- a/Mage.Server/src/main/java/mage/server/Main.java
+++ b/Mage.Server/src/main/java/mage/server/Main.java
@@ -28,6 +28,7 @@
 
 package mage.server;
 
+import mage.cards.repository.CardScanner;
 import mage.game.match.MatchType;
 import mage.game.tournament.TournamentType;
 import mage.interfaces.MageServer;
@@ -83,6 +84,7 @@ public class Main {
 
         logger.info("Starting MAGE server version " + version);
         logger.info("Logging level: " + logger.getEffectiveLevel());
+        CardScanner.scan();
         deleteSavedGames();
         ConfigSettings config = ConfigSettings.getInstance();
         for (GamePlugin plugin: config.getGameTypes()) {
diff --git a/Mage.Server/src/main/java/mage/server/game/GameController.java b/Mage.Server/src/main/java/mage/server/game/GameController.java
index df42f4bbe9..dc3f64042e 100644
--- a/Mage.Server/src/main/java/mage/server/game/GameController.java
+++ b/Mage.Server/src/main/java/mage/server/game/GameController.java
@@ -35,6 +35,8 @@ import mage.cards.Card;
 import mage.cards.Cards;
 import mage.cards.decks.Deck;
 import mage.cards.decks.DeckCardLists;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
 import mage.game.Game;
 import mage.game.GameException;
 import mage.game.events.Listener;
@@ -46,7 +48,6 @@ import mage.server.*;
 import mage.server.util.Splitter;
 import mage.server.util.SystemUtil;
 import mage.server.util.ThreadExecutor;
-import mage.sets.Sets;
 import mage.view.AbilityPickerView;
 import mage.view.CardsView;
 import mage.view.ChatMessage.MessageColor;
@@ -273,7 +274,8 @@ public class GameController implements GameCallback {
     }
 
     public boolean cheat(UUID userId, UUID playerId, String cardName) {
-        Card card = Sets.findCard(cardName, true);
+        CardInfo cardInfo = CardRepository.instance.findCard(cardName);
+        Card card = cardInfo != null ? cardInfo.getCard() : null;
         if (card != null) {
             Set<Card> cards = new HashSet<Card>();
             cards.add(card);
diff --git a/Mage.Server/src/main/java/mage/server/tournament/TournamentFactory.java b/Mage.Server/src/main/java/mage/server/tournament/TournamentFactory.java
index 26b919dda2..a0018bf1a3 100644
--- a/Mage.Server/src/main/java/mage/server/tournament/TournamentFactory.java
+++ b/Mage.Server/src/main/java/mage/server/tournament/TournamentFactory.java
@@ -36,7 +36,7 @@ import java.util.Map;
 import mage.game.tournament.Tournament;
 import mage.game.tournament.TournamentOptions;
 import mage.game.tournament.TournamentType;
-import mage.sets.Sets;
+import mage.cards.Sets;
 import mage.view.TournamentTypeView;
 import org.apache.log4j.Logger;
 
diff --git a/Mage.Server/src/main/java/mage/server/util/SystemUtil.java b/Mage.Server/src/main/java/mage/server/util/SystemUtil.java
index 121a23e29c..43dbcac95f 100644
--- a/Mage.Server/src/main/java/mage/server/util/SystemUtil.java
+++ b/Mage.Server/src/main/java/mage/server/util/SystemUtil.java
@@ -2,12 +2,15 @@ package mage.server.util;
 
 import mage.Constants;
 import mage.cards.Card;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
 import mage.game.Game;
 import mage.players.Player;
-import mage.sets.Sets;
 
 import java.io.File;
 import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
 import java.util.Scanner;
 import java.util.Set;
 import java.util.regex.Matcher;
@@ -48,46 +51,59 @@ public class SystemUtil {
             try {
                 while (scanner.hasNextLine()) {
                     String line = scanner.nextLine().trim();
-                    if (line.trim().length() == 0 || line.startsWith("#")) continue;
+                    if (line.trim().length() == 0 || line.startsWith("#")) {
+                        continue;
+                    }
+
                     Matcher m = pattern.matcher(line);
-                    if (m.matches()) {
-
-                        String zone = m.group(1);
-                        String nickname = m.group(2);
-
-                        Player player = findPlayer(game, nickname);
-                        if (player != null) {
-                            Constants.Zone gameZone;
-                            if ("hand".equalsIgnoreCase(zone)) {
-                                gameZone = Constants.Zone.HAND;
-                            } else if ("battlefield".equalsIgnoreCase(zone)) {
-                                gameZone = Constants.Zone.BATTLEFIELD;
-                            } else if ("graveyard".equalsIgnoreCase(zone)) {
-                                gameZone = Constants.Zone.GRAVEYARD;
-                            } else if ("library".equalsIgnoreCase(zone)) {
-                                gameZone = Constants.Zone.LIBRARY;
-                            } else {
-                                continue; // go parse next line
-                            }
-
-                            String cardName = m.group(3);
-                            Integer amount = Integer.parseInt(m.group(4));
-                            for (int i = 0; i < amount; i++) {
-                                Card card = Sets.findCard(cardName, true);
-                                if (card != null) {
-                                    Set<Card> cards = new HashSet<Card>();
-                                    cards.add(card);
-                                    game.loadCards(cards, player.getId());
-                                    swapWithAnyCard(game, player, card, gameZone);
-                                } else {
-                                    logger.fatal("Couldn't find a card: " + cardName);
-                                }
-                            }
-                        } else {
-                            logger.warn("Was skipped: " + line);
-                        }
-                    } else {
+                    if (!m.matches()) {
                         logger.warn("Init string wasn't parsed: " + line);
+                        continue;
+                    }
+
+                    String zone = m.group(1);
+                    String nickname = m.group(2);
+
+                    Player player = findPlayer(game, nickname);
+                    if (player == null) {
+                        logger.warn("Was skipped: " + line);
+                        continue;
+                    }
+
+                    Constants.Zone gameZone;
+                    if ("hand".equalsIgnoreCase(zone)) {
+                        gameZone = Constants.Zone.HAND;
+                    } else if ("battlefield".equalsIgnoreCase(zone)) {
+                        gameZone = Constants.Zone.BATTLEFIELD;
+                    } else if ("graveyard".equalsIgnoreCase(zone)) {
+                        gameZone = Constants.Zone.GRAVEYARD;
+                    } else if ("library".equalsIgnoreCase(zone)) {
+                        gameZone = Constants.Zone.LIBRARY;
+                    } else {
+                        continue; // go parse next line
+                    }
+
+                    String cardName = m.group(3);
+                    Integer amount = Integer.parseInt(m.group(4));
+
+                    List<CardInfo> cards = CardRepository.instance.findCards(cardName);
+                    if (cards.isEmpty()) {
+                        logger.warn("Couldn't find a card: " + cardName);
+                        continue;
+                    }
+
+                    Random random = new Random();
+                    Set<Card> cardsToLoad = new HashSet<Card>();
+                    for (int i = 0; i < amount; i++) {
+                        CardInfo cardInfo = cards.get(random.nextInt(cards.size()));
+                        Card card = cardInfo != null ? cardInfo.getCard() : null;
+                        if (card != null) {
+                            cardsToLoad.add(card);
+                        }
+                    }
+                    game.loadCards(cardsToLoad, player.getId());
+                    for (Card card : cardsToLoad) {
+                        swapWithAnyCard(game, player, card, gameZone);
                     }
                 }
             }
@@ -126,8 +142,9 @@ public class SystemUtil {
      */
     private static Player findPlayer(Game game, String name) {
         for (Player player: game.getPlayers().values()) {
-            if (player.getName().equals(name))
+            if (player.getName().equals(name)) {
                 return player;
+            }
         }
         return null;
     }
diff --git a/Mage.Sets/src/mage/cache/Cache.java b/Mage.Sets/src/mage/cache/Cache.java
deleted file mode 100644
index eb9c207b44..0000000000
--- a/Mage.Sets/src/mage/cache/Cache.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package mage.cache;
-
-import java.io.Serializable;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- *  Cache model
- *
- *  @author noxx
- */
-public class Cache implements Serializable {
-    
-    private int version;
-    private String name;
-    private Map<String, Object> cacheObjects = new HashMap<String, Object>();
-   
-    public Cache(String name, int version) {
-        this.name = name;
-        this.version = version;
-    }
-    
-    public int getVersion() {
-        return version;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public Map<String, Object> getCacheObjects() {
-        return cacheObjects;
-    }
-
-    private static final long serialVersionUID = 1L;
-}
diff --git a/Mage.Sets/src/mage/cache/CacheDataHelper.java b/Mage.Sets/src/mage/cache/CacheDataHelper.java
deleted file mode 100644
index ff5145ab5a..0000000000
--- a/Mage.Sets/src/mage/cache/CacheDataHelper.java
+++ /dev/null
@@ -1,116 +0,0 @@
-package mage.cache;
-
-import org.apache.log4j.Logger;
-
-import java.io.*;
-
-/**
- * @author noxx
- */
-public class CacheDataHelper {
-
-    private static final Logger log = Logger.getLogger(CacheDataHelper.class);
-
-    /**
-     * Save object on disk.
-     *
-     * @param cache Cache object to save.
-     * @param name Part of name that will be used to form original filename to save object to.
-     */
-    public static void cacheObject(Cache cache, String name) {
-        ObjectOutputStream oos = null;
-        try {
-            File dir = new File("cache");
-            if (!dir.exists() || dir.exists() && dir.isFile()) {
-                boolean bCreated = dir.mkdir();
-                if (!bCreated) {
-                    log.error("Couldn't create directory for cache.");
-                    return;
-                }
-            }
-            File f = new File("cache" + File.separator + name + ".obj");
-            if (!f.exists()) {
-                f.createNewFile();
-            }
-            oos = new ObjectOutputStream(new FileOutputStream(f));
-            oos.writeObject(cache);
-            oos.close();
-
-        } catch (FileNotFoundException e) {
-            log.error("Error while caching data: ", e);
-            return;
-        } catch (IOException io) {
-            log.error("Error while caching data: ", io);
-            return;
-        }
-    }
-
-    /**
-     * Gets Cache object from cache folder.
-     *
-     * @param name
-     * @return
-     */
-    public static Cache getCachedObject(String name) {
-        ObjectInputStream ois = null;
-        try {
-            File dir = new File("cache");
-            if (!dir.exists() || dir.exists() && dir.isFile()) {
-                return null;
-            }
-            File f = new File("cache" + File.separator + name + ".obj");
-            if (!f.exists()) {
-                log.warn("Couldn't find cache for name: " + name);
-                return null;
-            }
-            ois = new ObjectInputStream(new FileInputStream(f));
-            Object object = ois.readObject();
-
-            if (!(object instanceof Cache)) {
-                log.error("Cached object has wrong type: " + object.getClass().getName());
-                return null;
-            }
-
-            return (Cache)object;
-
-        } catch (FileNotFoundException e) {
-            log.error("Error while reading cached data: ", e);
-            return null;
-        } catch (IOException io) {
-            log.error("Error while reading cached data: ", io);
-            return null;
-        } catch (ClassNotFoundException e) {
-            log.error("Error while reading cached data: ", e);
-            return null;
-        } finally {
-            try {
-                if (ois != null) {
-                    ois.close();
-                }
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
-        }
-    }
-
-    /**
-     * Validates cache for being consistent.
-     *
-     * @param cache
-     * @return
-     */
-    public static boolean validateCache(Cache cache, int cacheVersion, int cardCount, String countKey) {
-        if (cache == null || cache.getVersion() != cacheVersion) {
-            return false;
-        }
-        Object object = cache.getCacheObjects().get(countKey);
-        if (object == null || !(object instanceof Integer)) {
-            return false;
-        }
-        Integer count = (Integer) object;
-        if (!count.equals(cardCount)) {
-            return false;
-        }
-        return true;
-    }
-}
diff --git a/Mage.Sets/src/mage/cache/CacheService.java b/Mage.Sets/src/mage/cache/CacheService.java
deleted file mode 100644
index bc7dbad988..0000000000
--- a/Mage.Sets/src/mage/cache/CacheService.java
+++ /dev/null
@@ -1,114 +0,0 @@
-package mage.cache;
-
-import mage.Constants;
-import mage.cards.Card;
-import mage.cards.ExpansionSet;
-import org.apache.log4j.Logger;
-
-import java.util.*;
-
-/**
- * @author noxx
- */
-public class CacheService {
-
-    private static final Logger log = Logger.getLogger(CacheService.class);
-
-    private static final String CARDS_CACHE_OBJECT_NAME = "cards";
-    private static final String CARDS_KEY = "cards_key";
-    private static final String NAMES_CACHE_OBJECT_NAME = "card_names";
-    private static final String NAMES_KEY = "card_names_key";
-    private static final String CARD_COUNT_KEY = "card_count_key";
-    private static final String CREATURE_TYPES_CACHE_OBJECT_NAME = "creature_types";
-    private static final String CREATURE_TYPES_KEY = "creature_types_key";
-    private static final String NONLAND_NAMES_CACHE_OBJECT_NAME = "nonland_names";
-    private static final String NONLAND_NAMES_KEY = "nonland_names_key";
-
-    private static final int CACHE_VERSION = 1;
-
-    public static List<Card> loadCards(Collection<ExpansionSet> sets) {
-        Cache cache = CacheDataHelper.getCachedObject(CARDS_CACHE_OBJECT_NAME);
-        List<Card> cards =  new ArrayList<Card>();
-        if (cache == null || cache.getVersion() != CACHE_VERSION) {
-            for (ExpansionSet set : sets) {
-                cards.addAll(set.getCards());
-            }
-            cache = new Cache(CARDS_CACHE_OBJECT_NAME, CACHE_VERSION);
-            cache.getCacheObjects().put(CARDS_KEY, cards);
-            cache.getCacheObjects().put(CARD_COUNT_KEY, cards.size());
-            CacheDataHelper.cacheObject(cache, CARDS_CACHE_OBJECT_NAME);
-        } else {
-            cards = (List<Card>) cache.getCacheObjects().get(CARDS_KEY);
-            log.debug("Loaded cards from cache.");
-        }
-
-        return cards;
-    }
-
-    public static Set<String> loadCardNames(List<Card> cards) {
-        Cache cache = CacheDataHelper.getCachedObject(NAMES_CACHE_OBJECT_NAME);
-        Set<String> names = new TreeSet<String>();
-        if (!CacheDataHelper.validateCache(cache, CACHE_VERSION, cards.size(), CARD_COUNT_KEY)) {
-            for (Card card : cards) {
-                names.add(card.getName());
-            }
-            cache = new Cache(NAMES_CACHE_OBJECT_NAME, CACHE_VERSION);
-            cache.getCacheObjects().put(NAMES_KEY, names);
-            cache.getCacheObjects().put(CARD_COUNT_KEY, cards.size());
-            CacheDataHelper.cacheObject(cache, NAMES_CACHE_OBJECT_NAME);
-        } else {
-            Set<String> cachedNames = (Set<String>) cache.getCacheObjects().get(NAMES_KEY);
-            names.addAll(cachedNames);
-            log.debug("Loaded card names from cache.");
-        }
-
-        return names;
-    }
-
-    public static Set<String> loadCreatureTypes(List<Card> cards) {
-        Set<String> creatureTypes = new TreeSet<String>();
-        Cache cache = CacheDataHelper.getCachedObject(CREATURE_TYPES_CACHE_OBJECT_NAME);
-        if (!CacheDataHelper.validateCache(cache, CACHE_VERSION, cards.size(), CARD_COUNT_KEY)) {
-            for (Card card : cards) {
-                if (card.getCardType().contains(Constants.CardType.CREATURE)) {
-                    for (String type : card.getSubtype()) {
-                        creatureTypes.add(type);
-                        if (type.equals("")) {
-                            throw new IllegalStateException("Card with empty subtype: " + card.getName());
-                        }
-                    }
-                }
-            }
-            cache = new Cache(CREATURE_TYPES_CACHE_OBJECT_NAME, CACHE_VERSION);
-            cache.getCacheObjects().put(CREATURE_TYPES_KEY, creatureTypes);
-            cache.getCacheObjects().put(CARD_COUNT_KEY, cards.size());
-            CacheDataHelper.cacheObject(cache, CREATURE_TYPES_CACHE_OBJECT_NAME);
-        } else {
-            Set<String> cachedCreatureTypes = (Set<String>) cache.getCacheObjects().get(CREATURE_TYPES_KEY);
-            creatureTypes.addAll(cachedCreatureTypes);
-            log.debug("Loaded creature types from cache.");
-        }
-
-        return creatureTypes;
-    }
-
-    public static Set<String> loadNonLandNames(List<Card> cards) {
-        Set<String> nonLandNames = new TreeSet<String>();
-        Cache cache = CacheDataHelper.getCachedObject(NONLAND_NAMES_CACHE_OBJECT_NAME);
-        if (!CacheDataHelper.validateCache(cache, CACHE_VERSION, cards.size(), CARD_COUNT_KEY)) {
-            for (Card card : cards) {
-                if (!card.getCardType().contains(Constants.CardType.LAND)) nonLandNames.add(card.getName());
-            }
-            cache = new Cache(NONLAND_NAMES_CACHE_OBJECT_NAME, CACHE_VERSION);
-            cache.getCacheObjects().put(NONLAND_NAMES_KEY, nonLandNames);
-            cache.getCacheObjects().put(CARD_COUNT_KEY, cards.size());
-            CacheDataHelper.cacheObject(cache, NONLAND_NAMES_CACHE_OBJECT_NAME);
-        } else {
-            Set<String> cachedNonLandNames = (Set<String>) cache.getCacheObjects().get(NONLAND_NAMES_KEY);
-            nonLandNames.addAll(cachedNonLandNames);
-            log.debug("Loaded non land names from cache.");
-        }
-
-        return nonLandNames;
-    }
-}
diff --git a/Mage.Sets/src/mage/cache/CacheTest.java b/Mage.Sets/src/mage/cache/CacheTest.java
deleted file mode 100644
index 28fef6f118..0000000000
--- a/Mage.Sets/src/mage/cache/CacheTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package mage.cache;
-
-import mage.Constants;
-import mage.cards.Card;
-import mage.cards.ExpansionSet;
-import mage.sets.Sets;
-import org.junit.Assert;
-import org.junit.Test;
-
-import java.util.Set;
-
-/**
- * @author noxx
- */
-public class CacheTest {
-
-    /**
-     * In case this test fails, it does mean that you need to update cache version in Sets.java
-     */
-    @Test
-    public void testCacheConsistency() {
-        Set<String> names = Sets.getCardNames();
-        Set<String> nonLandNames = Sets.getNonLandCardNames();
-        Set<String> creatureTypes = Sets.getCreatureTypes();
-
-        for (ExpansionSet set : Sets.getInstance().values()) {
-            for (Card card : set.getCards()) {
-                if (card.getCardType().contains(Constants.CardType.CREATURE)) {
-                    for (String type : card.getSubtype()) {
-                        if (!creatureTypes.contains(type)) {
-                            Assert.assertTrue("Couldn't find a creature type in the cache: " + type, false);
-                        }
-                    }
-                }
-                if (!names.contains(card.getName())) {
-                    Assert.assertTrue("Couldn't find a card name in the cache: " + card.getName(), false);
-                }
-                if (!card.getCardType().contains(Constants.CardType.LAND)) {
-                    if (!nonLandNames.contains(card.getName())) {
-                        Assert.assertTrue("Couldn't find a non-land card name in the cache: " + card.getName(), false);
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/Mage.Sets/src/mage/sets/Sets.java b/Mage.Sets/src/mage/sets/Sets.java
deleted file mode 100644
index 48c0ab17bc..0000000000
--- a/Mage.Sets/src/mage/sets/Sets.java
+++ /dev/null
@@ -1,397 +0,0 @@
-/*
- *  Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
- * 
- *  Redistribution and use in source and binary forms, with or without modification, are
- *  permitted provided that the following conditions are met:
- * 
- *     1. Redistributions of source code must retain the above copyright notice, this list of
- *        conditions and the following disclaimer.
- * 
- *     2. Redistributions in binary form must reproduce the above copyright notice, this list
- *        of conditions and the following disclaimer in the documentation and/or other materials
- *        provided with the distribution.
- * 
- *  THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
- *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- *  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
- *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- *  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- * 
- *  The views and conclusions contained in the software and documentation are those of the
- *  authors and should not be interpreted as representing official policies, either expressed
- *  or implied, of BetaSteward_at_googlemail.com.
- */
-
-package mage.sets;
-
-import mage.Constants.CardType;
-import mage.Constants.ColoredManaSymbol;
-import mage.Mana;
-import mage.cache.CacheService;
-import mage.cards.Card;
-import mage.cards.CardImpl;
-import mage.cards.ExpansionSet;
-import mage.cards.decks.Deck;
-import mage.cards.decks.DeckCardLists;
-import org.apache.log4j.Logger;
-
-import java.io.FileNotFoundException;
-import java.io.PrintWriter;
-import java.lang.reflect.Constructor;
-import java.util.*;
-
-/**
- *
- * @author BetaSteward_at_googlemail.com
- */
-public class Sets extends HashMap<String, ExpansionSet> {
-
-    private final static Logger logger = Logger.getLogger(Sets.class);
-    private static final Sets fINSTANCE =  new Sets();
-    private static Set<String> names;
-    private static Set<String> nonLandNames;
-    private static Set<String> creatureTypes;
-    private static List<Card> cards;
-    private static Map<String, Card> cardMap;
-    protected static Random rnd = new Random();
-
-    private static boolean loaded;
-
-    public static Sets getInstance() {
-        return fINSTANCE;
-    }
-
-    private Sets() {
-        names = new TreeSet<String>();
-        nonLandNames = new TreeSet<String>();
-        cards = new ArrayList<Card>();
-        cardMap = new HashMap<String, Card>();
-        creatureTypes = new TreeSet<String>();
-        this.addSet(AlaraReborn.getInstance());
-        this.addSet(Alliances.getInstance());
-        this.addSet(Antiquities.getInstance());
-        this.addSet(Apocalypse.getInstance());
-        this.addSet(ArabianNights.getInstance());
-        this.addSet(AvacynRestored.getInstance());
-        this.addSet(BetrayersOfKamigawa.getInstance());
-        this.addSet(ChampionsOfKamigawa.getInstance());
-        this.addSet(Coldsnap.getInstance());
-        this.addSet(Conflux.getInstance());
-        this.addSet(DarkAscension.getInstance());
-        this.addSet(Darksteel.getInstance());
-        this.addSet(Dissension.getInstance());
-        this.addSet(EighthEdition.getInstance());
-        this.addSet(ElspethvsTezzeret.getInstance());
-        this.addSet(Eventide.getInstance());
-        this.addSet(Exodus.getInstance());
-        this.addSet(FifthDawn.getInstance());
-        this.addSet(FifthEdition.getInstance());
-        this.addSet(FallenEmpires.getInstance());
-        this.addSet(FourthEdition.getInstance());
-        this.addSet(FutureSight.getInstance());
-        this.addSet(Guildpact.getInstance());
-        this.addSet(Guru.getInstance());
-        this.addSet(Homelands.getInstance());
-        this.addSet(IceAge.getInstance());
-        this.addSet(Innistrad.getInstance());
-        this.addSet(Invasion.getInstance());
-        this.addSet(Judgment.getInstance());
-        this.addSet(Legends.getInstance());
-        this.addSet(Legions.getInstance());
-        this.addSet(Lorwyn.getInstance());
-        this.addSet(Magic2010.getInstance());
-        this.addSet(Magic2011.getInstance());
-        this.addSet(Magic2012.getInstance());
-        this.addSet(Magic2013.getInstance());
-        this.addSet(MagicPlayerRewards.getInstance());
-        this.addSet(MercadianMasques.getInstance());
-        this.addSet(Mirage.getInstance());
-        this.addSet(Mirrodin.getInstance());
-        this.addSet(MirrodinBesieged.getInstance());
-        this.addSet(Morningtide.getInstance());
-        this.addSet(Nemesis.getInstance());
-        this.addSet(NewPhyrexia.getInstance());
-        this.addSet(NinthEdition.getInstance());
-        this.addSet(Odyssey.getInstance());
-        this.addSet(Onslaught.getInstance());
-        this.addSet(PlanarChaos.getInstance());
-        this.addSet(Planechase.getInstance());
-        this.addSet(Planeshift.getInstance());
-        this.addSet(Prophecy.getInstance());
-        this.addSet(RavnicaCityOfGuilds.getInstance());
-        this.addSet(ReturnToRavnica.getInstance());
-        this.addSet(RiseOfTheEldrazi.getInstance());
-        this.addSet(SaviorsOfKamigawa.getInstance());
-        this.addSet(ScarsOfMirrodin.getInstance());
-        this.addSet(Scourge.getInstance());
-        this.addSet(SeventhEdition.getInstance());
-        this.addSet(ShardsOfAlara.getInstance());
-        this.addSet(Shadowmoor.getInstance());
-        this.addSet(SixthEdition.getInstance());
-        this.addSet(Stronghold.getInstance());
-        this.addSet(Tenth.getInstance());
-        this.addSet(Tempest.getInstance());
-        this.addSet(TheDark.getInstance());
-        this.addSet(TimeSpiral.getInstance());
-        this.addSet(TimeSpiralTimeshifted.getInstance());
-        this.addSet(Torment.getInstance());
-        this.addSet(UrzasSaga.getInstance());
-        this.addSet(UrzasLegacy.getInstance());
-        this.addSet(UrzasDestiny.getInstance());
-        this.addSet(Visions.getInstance());
-        this.addSet(Weatherlight.getInstance());
-        this.addSet(Worldwake.getInstance());
-        this.addSet(Zendikar.getInstance());
-    }
-
-	private void addSet(ExpansionSet set) {
-		this.put(set.getCode(), set);
-        //cards.addAll(set.getCards());
-	}
-
-    private static void loadCards() {
-        if (!loaded) {
-            synchronized (Sets.class) {
-                if (!loaded) {
-                    for (ExpansionSet set : getInstance().values()) {
-                        cards.addAll(set.getCards());
-                    }
-                    names = CacheService.loadCardNames(cards);
-                    creatureTypes = CacheService.loadCreatureTypes(cards);
-                    nonLandNames = CacheService.loadNonLandNames(cards);
-                    loaded = true;
-                }
-            }
-        }
-    }
-
-	public static Set<String> getCardNames() {
-        loadCards();
-		return names;
-	}
-
-	public static Set<String> getNonLandCardNames() {
-        loadCards();
-		return nonLandNames;
-	}
-
-	public static Set<String> getCreatureTypes() {
-        loadCards();
-		return creatureTypes;
-	}
-
-	public static Card getRandomCard() {
-        loadCards();
-		return cards.get(rnd.nextInt(cards.size()));
-	}
-
-    /**
-     * Generates card pool of cardsCount cards that have manacost of allowed colors.
-     *
-     * @param cardsCount
-     * @param allowedColors
-     * @return
-     */
-    public static List<Card> generateRandomCardPool(int cardsCount, List<ColoredManaSymbol> allowedColors) {
-        List<Card> cardPool = new ArrayList<Card>();
-
-        int count = 0;
-        int tries = 0;
-        while (count < cardsCount) {
-            Card card = getRandomCard();
-            if (!card.getCardType().contains(CardType.LAND)) {
-                if (cardFitsChosenColors(card, allowedColors)) {
-                    cardPool.add(card);
-                    count++;
-                }
-            }
-            tries++;
-            if (tries > 4096) { // to avoid infinite loop
-                throw new IllegalStateException("Not enough cards for chosen colors to generate deck: " + allowedColors);
-            }
-        }
-
-        return cardPool;
-    }
-
-    /**
-     * Check that card can be played using chosen (allowed) colors.
-     *
-     * @param card
-     * @param allowedColors
-     * @return
-     */
-    private static boolean cardFitsChosenColors(Card card, List<ColoredManaSymbol> allowedColors) {
-        if (card.getCardType().contains(CardType.LAND))  {
-            if (!card.getSupertype().contains("Basic")) {
-                int score = 0;
-                for (Mana mana : card.getMana()) {
-                    for (ColoredManaSymbol color : allowedColors) {
-                        score += mana.getColor(color);
-                    }
-                }
-                if (score > 1) {
-                    return true;
-                }
-            }
-        }
-        else {
-            for (String symbol : card.getManaCost().getSymbols()) {
-                boolean found = false;
-                symbol = symbol.replace("{", "").replace("}", "");
-                if (isColoredMana(symbol)) {
-                    for (ColoredManaSymbol allowed : allowedColors) {
-                        if (allowed.toString().equals(symbol)) {
-                            found = true;
-                            break;
-                        }
-                    }
-                    if (!found) {
-                        return false;
-                    }
-                }
-            }
-            return true;
-        }
-        return false;
-    }
-
-    protected static boolean isColoredMana(String symbol) {
-        return symbol.equals("W") || symbol.equals("G") || symbol.equals("U") || symbol.equals("B") || symbol.equals("R");
-    }
-
-    public static Deck generateDeck() {
-        List<ColoredManaSymbol> allowedColors = new ArrayList<ColoredManaSymbol>();
-        int numColors = rnd.nextInt(2) + 1;
-        int cardPoolSize = 60;
-        if (numColors > 2) {
-            cardPoolSize += 20;
-        }
-        Deck deck = new Deck();
-
-        return deck;
-    }
-
-    public static Card findCard(String name) {
-        for (ExpansionSet set: fINSTANCE.values()) {
-            Card card = set.findCard(name);
-            if (card != null)
-                return card;
-        }
-        return null;
-    }
-
-    public static Card findCard(String name, boolean random) {
-        if (!random) {
-            return findCard(name);
-        } else {
-            List<Card> cardsFound = new ArrayList<Card>();
-            for (ExpansionSet set: fINSTANCE.values()) {
-                Card card = set.findCard(name, true);
-                if (card != null) {
-                    cardsFound.add(card);
-                }
-            }
-            if (cardsFound.size() > 0) {
-                Card card = cardsFound.get(rnd.nextInt(cardsFound.size()));
-                String cardClassName = card.getClass().getName();
-                return CardImpl.createCard(cardClassName);
-            }
-        }
-        return null;
-    }
-
-    public static Card findCard(String expansionsetCode, int cardNum) {
-        if (cardMap.containsKey(expansionsetCode + Integer.toString(cardNum))) {
-            return cardMap.get(expansionsetCode + Integer.toString(cardNum));
-        }
-        if (fINSTANCE.containsKey(expansionsetCode)) {
-            ExpansionSet set = fINSTANCE.get(expansionsetCode);
-            Card card = set.findCard(cardNum);
-            if (card != null) {
-                cardMap.put(expansionsetCode + Integer.toString(cardNum), card);
-                return card;
-            }
-        }
-        logger.warn("Could not find card: set=" + expansionsetCode + "cardNum=" + Integer.toString(cardNum));
-        return null;
-
-    }
-
-    public static Card createCard(Class clazz) {
-        try {
-            Constructor<?> con = clazz.getConstructor(new Class[]{UUID.class});
-            Card card = (Card) con.newInstance(new Object[] {null});
-            card.build();
-            return card;
-        } catch (Exception ex) {
-            logger.fatal("Error creating card:" + clazz.getName(), ex);
-            return null;
-        }
-    }
-
-
-    public static ExpansionSet findSet(String code) {
-        if (fINSTANCE.containsKey(code))
-            return fINSTANCE.get(code);
-        return null;
-    }
-
-    public static void saveDeck(String file, DeckCardLists deck) throws FileNotFoundException {
-        PrintWriter out = new PrintWriter(file);
-        Map<String, Integer> deckCards = new HashMap<String, Integer>();
-        Map<String, Integer> sideboard = new HashMap<String, Integer>();
-        try {
-            if (deck.getName() != null && deck.getName().length() > 0)
-                out.println("NAME:" + deck.getName());
-            if (deck.getAuthor() != null && deck.getAuthor().length() > 0)
-                out.println("AUTHOR:" + deck.getAuthor());
-            for (String cardClass: deck.getCards()) {
-                if (deckCards.containsKey(cardClass)) {
-                    deckCards.put(cardClass, deckCards.get(cardClass) + 1);
-                }
-                else {
-                    deckCards.put(cardClass, 1);
-                }
-            }
-            for (String cardClass: deck.getSideboard()) {
-                if (sideboard.containsKey(cardClass)) {
-                    sideboard.put(cardClass, sideboard.get(cardClass) + 1);
-                }
-                else {
-                    sideboard.put(cardClass, 1);
-                }
-            }
-            for (Map.Entry<String, Integer> entry: deckCards.entrySet()) {
-                Card card = CardImpl.createCard(entry.getKey());
-                if (card != null) {
-                    out.printf("%d [%s:%d] %s%n", entry.getValue(), card.getExpansionSetCode(), card.getCardNumber(), card.getName());
-                }
-            }
-            for (Map.Entry<String, Integer> entry: sideboard.entrySet()) {
-                Card card = CardImpl.createCard(entry.getKey());
-                if (card != null) {
-                    out.printf("SB: %d [%s:%d] %s%n", entry.getValue(), card.getExpansionSetCode(), card.getCardNumber(), card.getName());
-                }
-            }
-        }
-        finally {
-            out.close();
-        }
-    }
-
-    public ExpansionSet[] getSortedByReleaseDate() {
-        ExpansionSet[] sets = Sets.getInstance().values().toArray(new ExpansionSet[0]);
-        Arrays.sort(sets, new Comparator<ExpansionSet>() {
-            @Override
-            public int compare(ExpansionSet o1, ExpansionSet o2) {
-                return o2.getReleaseDate().compareTo(o1.getReleaseDate());
-            }
-        });
-        return sets;
-    }
-}
diff --git a/Mage.Sets/src/mage/sets/avacynrestored/CavernOfSouls.java b/Mage.Sets/src/mage/sets/avacynrestored/CavernOfSouls.java
index f586d48510..2906477f43 100644
--- a/Mage.Sets/src/mage/sets/avacynrestored/CavernOfSouls.java
+++ b/Mage.Sets/src/mage/sets/avacynrestored/CavernOfSouls.java
@@ -43,6 +43,7 @@ import mage.abilities.mana.ConditionalAnyColorManaAbility;
 import mage.abilities.mana.builder.ConditionalManaBuilder;
 import mage.abilities.mana.conditional.CreatureCastManaCondition;
 import mage.cards.CardImpl;
+import mage.cards.repository.CardRepository;
 import mage.choices.Choice;
 import mage.choices.ChoiceImpl;
 import mage.game.Game;
@@ -50,7 +51,6 @@ import mage.game.events.GameEvent;
 import mage.game.permanent.Permanent;
 import mage.game.stack.Spell;
 import mage.players.Player;
-import mage.sets.Sets;
 import mage.watchers.WatcherImpl;
 
 import java.util.ArrayList;
@@ -109,7 +109,7 @@ class CavernOfSoulsEffect extends OneShotEffect<CavernOfSoulsEffect> {
         if (player != null && permanent != null) {
             Choice typeChoice = new ChoiceImpl(true);
             typeChoice.setMessage("Choose creature type");
-            typeChoice.setChoices(Sets.getCreatureTypes());
+            typeChoice.setChoices(CardRepository.instance.getCreatureTypes());
             while (!player.choose(Constants.Outcome.Benefit, typeChoice, game)) {
                 game.debugMessage("player canceled choosing type. retrying.");
             }
diff --git a/Mage.Sets/src/mage/sets/avacynrestored/RidersOfGavony.java b/Mage.Sets/src/mage/sets/avacynrestored/RidersOfGavony.java
index 4ff140ba41..9f3b452a7b 100644
--- a/Mage.Sets/src/mage/sets/avacynrestored/RidersOfGavony.java
+++ b/Mage.Sets/src/mage/sets/avacynrestored/RidersOfGavony.java
@@ -39,6 +39,7 @@ import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.ProtectionAbility;
 import mage.abilities.keyword.VigilanceAbility;
 import mage.cards.CardImpl;
+import mage.cards.repository.CardRepository;
 import mage.choices.Choice;
 import mage.choices.ChoiceImpl;
 import mage.filter.FilterPermanent;
@@ -47,7 +48,6 @@ import mage.filter.predicate.mageobject.SubtypePredicate;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
-import mage.sets.Sets;
 
 import java.util.UUID;
 
@@ -103,7 +103,7 @@ class RidersOfGavonyEffect extends OneShotEffect<RidersOfGavonyEffect> {
         if (player != null && permanent != null) {
             Choice typeChoice = new ChoiceImpl(true);
             typeChoice.setMessage("Choose creature type");
-            typeChoice.setChoices(Sets.getCreatureTypes());
+            typeChoice.setChoices(CardRepository.instance.getCreatureTypes());
             while (!player.choose(Constants.Outcome.BoostCreature, typeChoice, game)) {
                 game.debugMessage("player canceled choosing type. retrying.");
             }
diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/CranialExtraction.java b/Mage.Sets/src/mage/sets/championsofkamigawa/CranialExtraction.java
index 1a46b1a913..cf9c3b2c37 100644
--- a/Mage.Sets/src/mage/sets/championsofkamigawa/CranialExtraction.java
+++ b/Mage.Sets/src/mage/sets/championsofkamigawa/CranialExtraction.java
@@ -37,11 +37,11 @@ import mage.abilities.effects.OneShotEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardsImpl;
+import mage.cards.repository.CardRepository;
 import mage.choices.Choice;
 import mage.choices.ChoiceImpl;
 import mage.game.Game;
 import mage.players.Player;
-import mage.sets.Sets;
 import mage.target.TargetPlayer;
 
 /**
@@ -90,7 +90,7 @@ class CranialExtractionEffect extends OneShotEffect<CranialExtractionEffect> {
         Player controller = game.getPlayer(source.getControllerId());
         if (player != null && controller != null) {
             Choice cardChoice = new ChoiceImpl();
-            cardChoice.setChoices(Sets.getNonLandCardNames());
+            cardChoice.setChoices(CardRepository.instance.getNonLandNames());
             cardChoice.clearChoice();
 
             while (!controller.choose(Outcome.Exile, cardChoice, game)) {
diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/Mindblaze.java b/Mage.Sets/src/mage/sets/championsofkamigawa/Mindblaze.java
index 52b3d43b5c..f7aac6bba6 100644
--- a/Mage.Sets/src/mage/sets/championsofkamigawa/Mindblaze.java
+++ b/Mage.Sets/src/mage/sets/championsofkamigawa/Mindblaze.java
@@ -39,13 +39,13 @@ import mage.abilities.effects.OneShotEffect;
 import mage.cards.CardImpl;
 import mage.cards.Cards;
 import mage.cards.CardsImpl;
+import mage.cards.repository.CardRepository;
 import mage.choices.Choice;
 import mage.choices.ChoiceImpl;
 import mage.filter.FilterCard;
 import mage.filter.predicate.mageobject.NamePredicate;
 import mage.game.Game;
 import mage.players.Player;
-import mage.sets.Sets;
 import mage.target.TargetPlayer;
 
 
@@ -95,7 +95,7 @@ class MindblazeEffect extends OneShotEffect<MindblazeEffect> {
         Player playerControls = game.getPlayer(source.getControllerId());
         if (player != null && playerControls != null) {
             Choice cardChoice = new ChoiceImpl();
-            cardChoice.setChoices(Sets.getNonLandCardNames());
+            cardChoice.setChoices(CardRepository.instance.getNonLandNames());
             cardChoice.clearChoice();
             Choice numberChoice = new ChoiceImpl();
             numberChoice.setMessage("Choose a number greater than 0");
diff --git a/Mage.Sets/src/mage/sets/innistrad/Nevermore.java b/Mage.Sets/src/mage/sets/innistrad/Nevermore.java
index 0c26cc5431..28b5603987 100644
--- a/Mage.Sets/src/mage/sets/innistrad/Nevermore.java
+++ b/Mage.Sets/src/mage/sets/innistrad/Nevermore.java
@@ -40,13 +40,13 @@ import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.ReplacementEffectImpl;
 import mage.cards.CardImpl;
+import mage.cards.repository.CardRepository;
 import mage.choices.Choice;
 import mage.choices.ChoiceImpl;
 import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.game.events.GameEvent.EventType;
 import mage.players.Player;
-import mage.sets.Sets;
 
 /**
  *
@@ -93,7 +93,7 @@ class NevermoreEffect1 extends OneShotEffect<NevermoreEffect1> {
         Player controller = game.getPlayer(source.getControllerId());
         if (controller != null) {
             Choice cardChoice = new ChoiceImpl();
-            cardChoice.setChoices(Sets.getNonLandCardNames());
+            cardChoice.setChoices(CardRepository.instance.getNonLandNames());
             cardChoice.clearChoice();
             while (!controller.choose(Outcome.Detriment, cardChoice, game)) {
                 game.debugMessage("player canceled choosing name. retrying.");
diff --git a/Mage.Sets/src/mage/sets/judgment/CabalTherapy.java b/Mage.Sets/src/mage/sets/judgment/CabalTherapy.java
index 8c6b6627e3..762ff96ce0 100644
--- a/Mage.Sets/src/mage/sets/judgment/CabalTherapy.java
+++ b/Mage.Sets/src/mage/sets/judgment/CabalTherapy.java
@@ -39,11 +39,11 @@ import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.FlashbackAbility;
 import mage.cards.Card;
 import mage.cards.CardImpl;
+import mage.cards.repository.CardRepository;
 import mage.choices.Choice;
 import mage.choices.ChoiceImpl;
 import mage.game.Game;
 import mage.players.Player;
-import mage.sets.Sets;
 import mage.target.TargetPlayer;
 import mage.target.common.TargetControlledCreaturePermanent;
 
@@ -93,7 +93,7 @@ class CabalTherapyEffect extends OneShotEffect<CabalTherapyEffect> {
         Player controller = game.getPlayer(source.getControllerId());
         if (player != null && controller != null) {
             Choice cardChoice = new ChoiceImpl();
-            cardChoice.setChoices(Sets.getNonLandCardNames());
+            cardChoice.setChoices(CardRepository.instance.getNonLandNames());
             cardChoice.clearChoice();
 
             while (!controller.choose(Outcome.Discard, cardChoice, game)) {
diff --git a/Mage.Sets/src/mage/sets/magic2011/ConundrumSphinx.java b/Mage.Sets/src/mage/sets/magic2011/ConundrumSphinx.java
index 4af7682eac..3327f03529 100644
--- a/Mage.Sets/src/mage/sets/magic2011/ConundrumSphinx.java
+++ b/Mage.Sets/src/mage/sets/magic2011/ConundrumSphinx.java
@@ -42,11 +42,11 @@ import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.Cards;
 import mage.cards.CardsImpl;
+import mage.cards.repository.CardRepository;
 import mage.choices.Choice;
 import mage.choices.ChoiceImpl;
 import mage.game.Game;
 import mage.players.Player;
-import mage.sets.Sets;
 
 /**
  *
@@ -91,7 +91,7 @@ class ConundrumSphinxEffect extends OneShotEffect<ConundrumSphinxEffect> {
     @Override
     public boolean apply(Game game, Ability source) {
         Choice cardChoice = new ChoiceImpl();
-        cardChoice.setChoices(Sets.getCardNames());
+        cardChoice.setChoices(CardRepository.instance.getNames());
         for (Player player: game.getPlayers().values()) {
             if(player.getLibrary().size() > 0){
                 cardChoice.clearChoice();
diff --git a/Mage.Sets/src/mage/sets/magic2012/AdaptiveAutomaton.java b/Mage.Sets/src/mage/sets/magic2012/AdaptiveAutomaton.java
index 366a4df198..d63678941c 100644
--- a/Mage.Sets/src/mage/sets/magic2012/AdaptiveAutomaton.java
+++ b/Mage.Sets/src/mage/sets/magic2012/AdaptiveAutomaton.java
@@ -38,13 +38,13 @@ import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.ContinuousEffectImpl;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.CardImpl;
+import mage.cards.repository.CardRepository;
 import mage.choices.Choice;
 import mage.choices.ChoiceImpl;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
-import mage.sets.Sets;
 
 import java.util.UUID;
 
@@ -96,7 +96,7 @@ class AdaptiveAutomatonEffect extends OneShotEffect<AdaptiveAutomatonEffect> {
         Permanent permanent = game.getPermanent(source.getSourceId());
         if (player != null && permanent != null) {
             Choice typeChoice = new ChoiceImpl(true);
-            typeChoice.setChoices(Sets.getCreatureTypes());
+            typeChoice.setChoices(CardRepository.instance.getCreatureTypes());
             while (!player.choose(Constants.Outcome.BoostCreature, typeChoice, game)) {
                 game.debugMessage("player canceled choosing type. retrying.");
             }
diff --git a/Mage.Sets/src/mage/sets/mirrodinbesieged/PhyrexianRevoker.java b/Mage.Sets/src/mage/sets/mirrodinbesieged/PhyrexianRevoker.java
index a201d979b1..ce6ae25703 100644
--- a/Mage.Sets/src/mage/sets/mirrodinbesieged/PhyrexianRevoker.java
+++ b/Mage.Sets/src/mage/sets/mirrodinbesieged/PhyrexianRevoker.java
@@ -42,13 +42,13 @@ import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.ReplacementEffectImpl;
 import mage.cards.CardImpl;
+import mage.cards.repository.CardRepository;
 import mage.choices.Choice;
 import mage.choices.ChoiceImpl;
 import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.game.events.GameEvent.EventType;
 import mage.players.Player;
-import mage.sets.Sets;
 
 /**
  *
@@ -97,7 +97,7 @@ class PhyrexianRevokerEffect1 extends OneShotEffect<PhyrexianRevokerEffect1> {
         Player controller = game.getPlayer(source.getControllerId());
         if (controller != null) {
             Choice cardChoice = new ChoiceImpl();
-            cardChoice.setChoices(Sets.getNonLandCardNames());
+            cardChoice.setChoices(CardRepository.instance.getNonLandNames());
             cardChoice.clearChoice();
             while (!controller.choose(Outcome.Detriment, cardChoice, game)) {
                 game.debugMessage("player canceled choosing name. retrying.");
diff --git a/Mage.Sets/src/mage/sets/newphyrexia/Xenograft.java b/Mage.Sets/src/mage/sets/newphyrexia/Xenograft.java
index c84ce54ed7..bf24a1f64f 100644
--- a/Mage.Sets/src/mage/sets/newphyrexia/Xenograft.java
+++ b/Mage.Sets/src/mage/sets/newphyrexia/Xenograft.java
@@ -42,13 +42,13 @@ import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.ContinuousEffectImpl;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.CardImpl;
+import mage.cards.repository.CardRepository;
 import mage.choices.Choice;
 import mage.choices.ChoiceImpl;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
-import mage.sets.Sets;
 
 /**
  *
@@ -95,7 +95,7 @@ class XenograftEffect extends OneShotEffect<XenograftEffect> {
         Permanent permanent = game.getPermanent(source.getSourceId());
         if (player != null && permanent != null) {
             Choice typeChoice = new ChoiceImpl(true);
-            typeChoice.setChoices(Sets.getCreatureTypes());
+            typeChoice.setChoices(CardRepository.instance.getCreatureTypes());
             while (!player.choose(Outcome.BoostCreature, typeChoice, game)) {
                 game.debugMessage("player canceled choosing type. retrying.");
             }
diff --git a/Mage.Sets/src/mage/sets/returntoravnica/SlaughterGames.java b/Mage.Sets/src/mage/sets/returntoravnica/SlaughterGames.java
index ed8c97cda4..0d02d9e3cf 100644
--- a/Mage.Sets/src/mage/sets/returntoravnica/SlaughterGames.java
+++ b/Mage.Sets/src/mage/sets/returntoravnica/SlaughterGames.java
@@ -38,11 +38,11 @@ import mage.abilities.effects.common.CantCounterSourceEffect;
 import mage.abilities.effects.common.search.SearchTargetGraveyardHandLibraryForCardNameAndExileEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
+import mage.cards.repository.CardRepository;
 import mage.choices.Choice;
 import mage.choices.ChoiceImpl;
 import mage.game.Game;
 import mage.players.Player;
-import mage.sets.Sets;
 import mage.target.common.TargetOpponent;
 
 /**
@@ -93,7 +93,7 @@ class SlaughterGamesEffect extends SearchTargetGraveyardHandLibraryForCardNameAn
         Player controller = game.getPlayer(source.getControllerId());
         if (player != null && controller != null) {
             Choice cardChoice = new ChoiceImpl();
-            cardChoice.setChoices(Sets.getNonLandCardNames());
+            cardChoice.setChoices(CardRepository.instance.getNonLandNames());
             cardChoice.clearChoice();
             cardChoice.setMessage("Name a nonland card");
 
diff --git a/Mage.Sets/src/mage/sets/saviorsofkamigawa/PithingNeedle.java b/Mage.Sets/src/mage/sets/saviorsofkamigawa/PithingNeedle.java
index 71628965cf..bbeca7d2f1 100644
--- a/Mage.Sets/src/mage/sets/saviorsofkamigawa/PithingNeedle.java
+++ b/Mage.Sets/src/mage/sets/saviorsofkamigawa/PithingNeedle.java
@@ -38,12 +38,12 @@ import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.ReplacementEffectImpl;
 import mage.cards.CardImpl;
+import mage.cards.repository.CardRepository;
 import mage.choices.Choice;
 import mage.choices.ChoiceImpl;
 import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.players.Player;
-import mage.sets.Sets;
 
 /**
  *
@@ -88,7 +88,7 @@ class NameCard extends OneShotEffect<NameCard> {
         Player controller = game.getPlayer(source.getControllerId());
         if (controller != null) {
             Choice cardChoice = new ChoiceImpl();
-            cardChoice.setChoices(Sets.getCardNames());
+            cardChoice.setChoices(CardRepository.instance.getNames());
             cardChoice.clearChoice();
             while (!controller.choose(Constants.Outcome.Detriment, cardChoice, game)) {
                 game.debugMessage("player canceled choosing name. retrying.");
diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/Memoricide.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/Memoricide.java
index 1645cabd5e..cea75b67f1 100644
--- a/Mage.Sets/src/mage/sets/scarsofmirrodin/Memoricide.java
+++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/Memoricide.java
@@ -37,11 +37,11 @@ import mage.abilities.effects.OneShotEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardsImpl;
+import mage.cards.repository.CardRepository;
 import mage.choices.Choice;
 import mage.choices.ChoiceImpl;
 import mage.game.Game;
 import mage.players.Player;
-import mage.sets.Sets;
 import mage.target.TargetPlayer;
 
 /**
@@ -89,7 +89,7 @@ class MemoricideEffect extends OneShotEffect<MemoricideEffect> {
         Player controller = game.getPlayer(source.getControllerId());
         if (player != null && controller != null) {
             Choice cardChoice = new ChoiceImpl();
-            cardChoice.setChoices(Sets.getNonLandCardNames());
+            cardChoice.setChoices(CardRepository.instance.getNonLandNames());
             cardChoice.clearChoice();
 
             while (!controller.choose(Outcome.Exile, cardChoice, game)) {
diff --git a/Mage.Sets/src/mage/sets/tempest/CursedScroll.java b/Mage.Sets/src/mage/sets/tempest/CursedScroll.java
index 1214a4cac1..704c8f0652 100644
--- a/Mage.Sets/src/mage/sets/tempest/CursedScroll.java
+++ b/Mage.Sets/src/mage/sets/tempest/CursedScroll.java
@@ -40,12 +40,12 @@ import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.Cards;
 import mage.cards.CardsImpl;
+import mage.cards.repository.CardRepository;
 import mage.choices.Choice;
 import mage.choices.ChoiceImpl;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
-import mage.sets.Sets;
 import mage.target.common.TargetCreatureOrPlayer;
 
 /**
@@ -91,7 +91,7 @@ class CursedScrollEffect extends OneShotEffect<CursedScrollEffect> {
         Player you = game.getPlayer(source.getControllerId());
         if (you != null) {
             Choice cardChoice = new ChoiceImpl();
-            cardChoice.setChoices(Sets.getCardNames());
+            cardChoice.setChoices(CardRepository.instance.getNames());
             cardChoice.clearChoice();
             while (!you.choose(Constants.Outcome.Damage, cardChoice, game)) {
                 game.debugMessage("player canceled choosing name. retrying.");
diff --git a/Mage.Sets/src/mage/sets/urzaslegacy/EngineeredPlague.java b/Mage.Sets/src/mage/sets/urzaslegacy/EngineeredPlague.java
index ed3a276014..059fd4f2be 100644
--- a/Mage.Sets/src/mage/sets/urzaslegacy/EngineeredPlague.java
+++ b/Mage.Sets/src/mage/sets/urzaslegacy/EngineeredPlague.java
@@ -32,12 +32,12 @@ import mage.Constants;
 import mage.Constants.CardType;
 import mage.Constants.Rarity;
 import mage.abilities.Ability;
-import mage.abilities.StaticAbility;
 import mage.abilities.common.AsEntersBattlefieldAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.continious.BoostAllEffect;
 import mage.cards.CardImpl;
+import mage.cards.repository.CardRepository;
 import mage.choices.Choice;
 import mage.choices.ChoiceImpl;
 import mage.filter.common.FilterCreaturePermanent;
@@ -45,7 +45,6 @@ import mage.filter.predicate.mageobject.SubtypePredicate;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
-import mage.sets.Sets;
 
 /**
  *
@@ -96,7 +95,7 @@ public class EngineeredPlague extends CardImpl<EngineeredPlague> {
             if (player != null && permanent != null) {
                 Choice typeChoice = new ChoiceImpl(true);
                 typeChoice.setMessage("Choose creature type");
-                typeChoice.setChoices(Sets.getCreatureTypes());
+                typeChoice.setChoices(CardRepository.instance.getCreatureTypes());
                 while (!player.choose(Constants.Outcome.Detriment, typeChoice, game)) {
                     game.debugMessage("player canceled choosing type. retrying.");
                 }
diff --git a/Mage.Tests/src/test/java/org/mage/test/load/LoadTest.java b/Mage.Tests/src/test/java/org/mage/test/load/LoadTest.java
index bf90f6a483..70ddc440b3 100644
--- a/Mage.Tests/src/test/java/org/mage/test/load/LoadTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/load/LoadTest.java
@@ -2,15 +2,16 @@ package org.mage.test.load;
 
 import mage.Constants;
 import mage.cards.Card;
-import mage.cards.ExpansionSet;
 import mage.cards.decks.Deck;
 import mage.cards.decks.DeckCardLists;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
 import mage.game.match.MatchOptions;
 import mage.player.ai.ComputerPlayer;
 import mage.remote.Connection;
 import mage.remote.Session;
 import mage.remote.SessionImpl;
-import mage.sets.Sets;
+import mage.cards.Sets;
 import mage.view.GameTypeView;
 import mage.view.TableView;
 import org.apache.log4j.Logger;
@@ -247,9 +248,10 @@ public class LoadTest {
         DeckCardLists deckList = new DeckCardLists();
         Deck deck = generateRandomDeck();
         for (Card card : deck.getCards()) {
-            ExpansionSet set = Sets.findSet(card.getExpansionSetCode());
-            String cardName = set.findCardName(card.getCardNumber());
-            deckList.getCards().add(cardName);
+            CardInfo cardInfo = CardRepository.instance.findCard(card.getExpansionSetCode(), card.getCardNumber());
+            if (cardInfo != null) {
+                deckList.getCards().add(cardInfo.getClassName());
+            }
         }
         return deckList;
     }
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/PlayGameTest.java b/Mage.Tests/src/test/java/org/mage/test/serverside/PlayGameTest.java
index f0272dd1bb..03e76eee30 100644
--- a/Mage.Tests/src/test/java/org/mage/test/serverside/PlayGameTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/PlayGameTest.java
@@ -10,7 +10,7 @@ import mage.game.GameOptions;
 import mage.game.TwoPlayerDuel;
 import mage.player.ai.ComputerPlayer;
 import mage.players.Player;
-import mage.sets.Sets;
+import mage.cards.Sets;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.mage.test.serverside.base.MageTestBase;
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/TestPlayRandomGame.java b/Mage.Tests/src/test/java/org/mage/test/serverside/TestPlayRandomGame.java
index 2b3723edbe..8842ee1353 100644
--- a/Mage.Tests/src/test/java/org/mage/test/serverside/TestPlayRandomGame.java
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/TestPlayRandomGame.java
@@ -10,7 +10,7 @@ import mage.game.GameOptions;
 import mage.game.TwoPlayerDuel;
 import mage.player.ai.ComputerPlayer;
 import mage.players.Player;
-import mage.sets.Sets;
+import mage.cards.Sets;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.mage.test.serverside.base.MageTestBase;
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java
index b5106ac217..7690500e24 100644
--- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java
@@ -3,6 +3,8 @@ package org.mage.test.serverside.base;
 import mage.Constants;
 import mage.Constants.PhaseStep;
 import mage.cards.Card;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
 import mage.game.Game;
 import mage.game.match.MatchType;
 import mage.game.permanent.PermanentCard;
@@ -15,7 +17,6 @@ import mage.server.util.ConfigSettings;
 import mage.server.util.PluginClassLoader;
 import mage.server.util.config.GamePlugin;
 import mage.server.util.config.Plugin;
-import mage.sets.Sets;
 import mage.util.Copier;
 import org.apache.log4j.Level;
 import org.apache.log4j.Logger;
@@ -253,7 +254,8 @@ public abstract class MageTestBase {
                     }
                 } else {
                     for (int i = 0; i < amount; i++) {
-                        Card card = Sets.findCard(cardName, true);
+                        CardInfo cardInfo = CardRepository.instance.findCard(cardName);
+                        Card card = cardInfo != null ? cardInfo.getCard() : null;
                         if (card != null) {
                             if (gameZone.equals(Constants.Zone.BATTLEFIELD)) {
                                 PermanentCard p = new PermanentCard(card, null);
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java
index 946d1ca539..7ddd90bd45 100644
--- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java
@@ -3,6 +3,8 @@ package org.mage.test.serverside.base;
 import mage.Constants;
 import mage.Constants.PhaseStep;
 import mage.cards.Card;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
 import mage.game.Game;
 import mage.game.match.MatchType;
 import mage.game.permanent.PermanentCard;
@@ -13,7 +15,6 @@ import mage.server.util.ConfigSettings;
 import mage.server.util.PluginClassLoader;
 import mage.server.util.config.GamePlugin;
 import mage.server.util.config.Plugin;
-import mage.sets.Sets;
 import mage.util.Copier;
 import org.apache.log4j.Level;
 import org.apache.log4j.Logger;
@@ -230,7 +231,8 @@ public abstract class MageTestPlayerBase {
                     getCommands(getPlayer(nickname)).put(gameZone, "clear");
                 } else {
                     for (int i = 0; i < amount; i++) {
-                        Card card = Sets.findCard(cardName, true);
+                        CardInfo cardInfo = CardRepository.instance.findCard(cardName);
+                        Card card = cardInfo != null ? cardInfo.getCard() : null;
                         if (card != null) {
                             if (gameZone.equals(Constants.Zone.BATTLEFIELD)) {
                                 PermanentCard p = new PermanentCard(card, null);
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestAPIImpl.java
index 35fd26f078..4d597decc1 100644
--- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestAPIImpl.java
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestAPIImpl.java
@@ -4,11 +4,12 @@ import mage.Constants;
 import mage.Constants.PhaseStep;
 import mage.abilities.Ability;
 import mage.cards.Card;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
 import mage.filter.Filter;
 import mage.game.permanent.Permanent;
 import mage.game.permanent.PermanentCard;
 import mage.players.Player;
-import mage.sets.Sets;
 import org.junit.Assert;
 import org.mage.test.player.TestPlayer;
 import org.mage.test.serverside.base.CardTestAPI;
@@ -118,7 +119,8 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
 
         if (gameZone.equals(Constants.Zone.BATTLEFIELD)) {
             for (int i = 0; i < count; i++) {
-                Card card = Sets.findCard(cardName, true);
+                CardInfo cardInfo = CardRepository.instance.findCard(cardName);
+                Card card = cardInfo != null ? cardInfo.getCard() : null;
                 if (card == null) {
                     throw new IllegalArgumentException("[TEST] Couldn't find a card: " + cardName);
                 }
@@ -136,7 +138,8 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP
             }
             List<Card> cards = getCardList(gameZone, player);
             for (int i = 0; i < count; i++) {
-                Card card = Sets.findCard(cardName, true);
+                CardInfo cardInfo = CardRepository.instance.findCard(cardName);
+                Card card = cardInfo != null ? cardInfo.getCard() : null;
                 cards.add(card);
             }
         }
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
index ef1f6730cc..5383e4ffc9 100644
--- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
@@ -7,6 +7,9 @@ import mage.abilities.Ability;
 import mage.cards.Card;
 import mage.cards.decks.Deck;
 import mage.cards.decks.importer.DeckImporterUtil;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
+import mage.cards.repository.CardScanner;
 import mage.counters.CounterType;
 import mage.filter.Filter;
 import mage.game.ExileZone;
@@ -16,7 +19,6 @@ import mage.game.command.CommandObject;
 import mage.game.permanent.Permanent;
 import mage.game.permanent.PermanentCard;
 import mage.players.Player;
-import mage.sets.Sets;
 import org.junit.Assert;
 import org.mage.test.player.TestPlayer;
 import org.mage.test.serverside.base.CardTestAPI;
@@ -32,6 +34,10 @@ import java.util.UUID;
  */
 public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implements CardTestAPI {
 
+    static {
+        CardScanner.scan();
+    }
+
     /**
      * Default game initialization params for red player (that plays with Mountains)
      */
@@ -150,7 +156,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
 
         if (gameZone.equals(Constants.Zone.BATTLEFIELD)) {
             for (int i = 0; i < count; i++) {
-                Card card = Sets.findCard(cardName, true);
+                CardInfo cardInfo = CardRepository.instance.findCard(cardName);
+                Card card = cardInfo != null ? cardInfo.getCard() : null;
                 if (card == null) {
                     throw new IllegalArgumentException("[TEST] Couldn't find a card: " + cardName);
                 }
@@ -164,7 +171,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
             }
             List<Card> cards = getCardList(gameZone, player);
             for (int i = 0; i < count; i++) {
-                Card card = Sets.findCard(cardName, true);
+                CardInfo cardInfo = CardRepository.instance.findCard(cardName);
+                Card card = cardInfo != null ? cardInfo.getCard() : null;
                 if (card == null) {
                     throw new AssertionError("Couldn't find a card: " + cardName);
                 }
diff --git a/Mage/pom.xml b/Mage/pom.xml
index 7c2a94ec62..7e216a5e08 100644
--- a/Mage/pom.xml
+++ b/Mage/pom.xml
@@ -21,6 +21,22 @@
             <version>1.2.14</version>
             <type>jar</type>
         </dependency>
+
+        <dependency>
+            <groupId>org.xerial</groupId>
+            <artifactId>sqlite-jdbc</artifactId>
+            <version>3.7.2</version>
+        </dependency>
+        <dependency>
+            <groupId>com.j256.ormlite</groupId>
+            <artifactId>ormlite-core</artifactId>
+            <version>4.42</version>
+        </dependency>
+        <dependency>
+            <groupId>com.j256.ormlite</groupId>
+            <artifactId>ormlite-jdbc</artifactId>
+            <version>4.42</version>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/Mage/src/mage/cards/CardImpl.java b/Mage/src/mage/cards/CardImpl.java
index d8ec8ea9f0..569f1da5f9 100644
--- a/Mage/src/mage/cards/CardImpl.java
+++ b/Mage/src/mage/cards/CardImpl.java
@@ -121,13 +121,21 @@ public abstract class CardImpl<T extends CardImpl<T>> extends MageObjectImpl<T>
 
     public static Card createCard(String name) {
         try {
-            Class<?> theClass = Class.forName(name);
-            Constructor<?> con = theClass.getConstructor(new Class[]{UUID.class});
+            return createCard(Class.forName(name));
+        } catch (ClassNotFoundException ex) {
+            logger.fatal("Error loading card: " + name, ex);
+            return null;
+        }
+    }
+
+    public static Card createCard(Class<?> clazz) {
+        try {
+            Constructor<?> con = clazz.getConstructor(new Class[]{UUID.class});
             Card card = (Card) con.newInstance(new Object[]{null});
             card.build();
             return card;
         } catch (Exception e) {
-            logger.fatal("Error loading card: " + name, e);
+            logger.fatal("Error loading card: " + clazz.getCanonicalName(), e);
             return null;
         }
     }
diff --git a/Mage/src/mage/cards/ExpansionSet.java b/Mage/src/mage/cards/ExpansionSet.java
index 47370aa9fd..1a56cdc682 100644
--- a/Mage/src/mage/cards/ExpansionSet.java
+++ b/Mage/src/mage/cards/ExpansionSet.java
@@ -30,16 +30,13 @@ package mage.cards;
 
 import mage.Constants.Rarity;
 import mage.Constants.SetType;
+import mage.cards.repository.CardCriteria;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
 import org.apache.log4j.Logger;
 
 import java.io.*;
-import java.lang.reflect.Constructor;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.net.URLDecoder;
 import java.util.*;
-import java.util.jar.JarEntry;
-import java.util.jar.JarInputStream;
 
 /**
  * @author BetaSteward_at_googlemail.com
@@ -77,26 +74,6 @@ public abstract class ExpansionSet implements Serializable {
         this.releaseDate = releaseDate;
         this.setType = setType;
         this.packageName = packageName;
-        //this.cards = getCardClassesForPackage(packageName);
-        //this.rarities = getCardsByRarity();
-    }
-
-    public List<Card> getCards() {
-        if (cards == null) {
-            loadLazily();
-        }
-        return cards;
-    }
-
-    private void loadLazily() {
-        synchronized (this) {
-            if (cards == null) {
-                this.cards = getCardClassesForPackage(packageName);
-            }
-            if (rarities == null) {
-                this.rarities = getCardsByRarity();
-            }
-        }
     }
 
     public String getName() {
@@ -119,16 +96,8 @@ public abstract class ExpansionSet implements Serializable {
         return setType;
     }
 
-    private Card createCard(Class clazz) {
-        try {
-            Constructor<?> con = clazz.getConstructor(new Class[]{UUID.class});
-            Card card = (Card) con.newInstance(new Object[]{null});
-            card.build();
-            return card;
-        } catch (Exception ex) {
-            logger.fatal("Error creating card:" + clazz.getName(), ex);
-            return null;
-        }
+    public String getPackageName() {
+        return packageName;
     }
 
     @Override
@@ -136,335 +105,68 @@ public abstract class ExpansionSet implements Serializable {
         return name;
     }
 
-    public Card findCard(String name) {
-        for (Card card : getCards()) {
-            if (name.equalsIgnoreCase(card.getName())) {
-                Card newCard = card.copy();
-                newCard.assignNewId();
-                return newCard;
-            }
-        }
-        return null;
-    }
-
-    public Card findCard(int cardNum) {
-        for (Card card : getCards()) {
-            if (cardNum == card.getCardNumber()) {
-                Card newCard = card.copy();
-                newCard.assignNewId();
-                return newCard;
-            }
-        }
-        return null;
-    }
-
-    public Card findCard(String name, boolean random) {
-        List<Card> foundCards = new ArrayList<Card>();
-        for (Card card : getCards()) {
-            if (name.equalsIgnoreCase(card.getName())) {
-                foundCards.add(card);
-            }
-        }
-        if (foundCards.size() > 0) {
-            Card newCard = foundCards.get(rnd.nextInt(foundCards.size())).copy();
-            newCard.assignNewId();
-            return newCard;
-        }
-        return null;
-    }
-
-    public String findCardName(int cardNum) {
-        for (Card card : getCards()) {
-            if (card.getCardNumber() == cardNum)
-                return card.getClass().getCanonicalName();
-        }
-        return null;
-    }
-
-    private List<Card> getCardClassesForPackage(String packageName) {
-        ClassLoader classLoader = this.getClass().getClassLoader();
-        assert classLoader != null;
-        String path = packageName.replace(".", "/");
-        Enumeration<URL> resources = null;
-        try {
-            resources = classLoader.getResources(path);
-        } catch (IOException e) {
-            logger.fatal("Error loading resource - " + path, e);
-        }
-        List<File> dirs = new ArrayList<File>();
-        boolean isLoadingFromJar = false;
-        String jarPath = null;
-        while (resources.hasMoreElements()) {
-            URL resource = resources.nextElement();
-            if (resource.toString().startsWith("jar:")) {
-                isLoadingFromJar = true;
-                jarPath = resource.getFile();
-                break;
-            }
-            try {
-                dirs.add(new File(URLDecoder.decode(resource.getFile(), "UTF-8")));
-            } catch (UnsupportedEncodingException e) {
-                logger.fatal("Error decoding director - " + resource.getFile(), e);
-            }
-        }
-        List<Class> classes = new ArrayList<Class>();
-        if (isLoadingFromJar) {
-            if (jarPath.contains("!")) {
-                jarPath = jarPath.substring(0, jarPath.lastIndexOf('!'));
-            }
-            String filePathElement = "file:";
-            if (jarPath.startsWith(filePathElement)) {
-                try {
-                    jarPath = URLDecoder.decode(jarPath.substring(jarPath.indexOf(filePathElement) + filePathElement.length()), "UTF-8");
-                } catch (UnsupportedEncodingException e) {
-                    logger.fatal("Error decoding file - " + jarPath, e);
-                }
-            }
-            try {
-                classes.addAll(findClassesInJar(new File(jarPath), path));
-            } catch (ClassNotFoundException e) {
-                logger.fatal("Error loading classes - " + jarPath, e);
-            }
-        } else { // faster but doesn't work for jars
-            for (File directory : dirs) {
-                try {
-                    classes.addAll(findClasses(directory, packageName));
-                } catch (ClassNotFoundException e) {
-                    logger.fatal("Error loading classes - " + jarPath, e);
-                }
-            }
-        }
-        List<Card> newCards = new ArrayList<Card>();
-        for (Class clazz : classes) {
-            if (clazz.getPackage().getName().equals(packageName)) {
-                Card card = createCard(clazz);
-                if (card.isNightCard()) {
-                    // skip second face of double-faced cards
-                    continue;
-                }
-                newCards.add(card);
-            }
-        }
-        return newCards;
-    }
-
-    private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException {
-        List<Class> classes = new ArrayList<Class>();
-        if (!directory.exists()) {
-            return classes;
-        }
-        File[] files = directory.listFiles();
-        if (files == null) {
-            return classes;
-        }
-        for (File file : files) {
-            if (file.isDirectory()) {
-                assert !file.getName().contains(".");
-                classes.addAll(findClasses(file, packageName + "." + file.getName()));
-            } else if (file.getName().endsWith(".class")) {
-                Class c = Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6));
-                if (CardImpl.class.isAssignableFrom(c)) {
-                    classes.add(c);
-                }
-            }
-        }
-        return classes;
-    }
-
-    private static List<Class> findClassesInJar(File file, String packageName) throws ClassNotFoundException {
-        List<Class> classes = new ArrayList<Class>();
-
-        if (!file.exists()) {
-            return classes;
-        }
-
-        try {
-            URL url = file.toURL();
-            URL[] urls = new URL[]{url};
-            ClassLoader cl = new URLClassLoader(urls);
-
-            JarInputStream jarFile = new JarInputStream(new FileInputStream(file));
-            JarEntry jarEntry;
-
-            while (true) {
-                jarEntry = jarFile.getNextJarEntry();
-                if (jarEntry == null) {
-                    break;
-                }
-                if ((jarEntry.getName().startsWith(packageName)) && (jarEntry.getName().endsWith(".class"))) {
-                    String clazz = jarEntry.getName().replaceAll("/", "\\.").replace(".class", "");
-                    Class c = cl.loadClass(clazz);
-                    if (CardImpl.class.isAssignableFrom(c)) {
-                        classes.add(c);
-                    }
-                }
-            }
-        } catch (Exception e) {
-            logger.fatal("Error loading classes - " + file, e);
-        }
-
-        return classes;
-    }
-
-    private Map<Rarity, List<Card>> getCardsByRarity() {
-        Map<Rarity, List<Card>> cardsByRarity = new HashMap<Rarity, List<Card>>();
-
-        for (Card card : getCards()) {
-            if (!cardsByRarity.containsKey(card.getRarity()))
-                cardsByRarity.put(card.getRarity(), new ArrayList<Card>());
-            cardsByRarity.get(card.getRarity()).add(card);
-        }
-
-        return cardsByRarity;
-    }
-
     public List<Card> createBooster() {
         List<Card> booster = new ArrayList<Card>();
-
-        if (!hasBoosters)
+        if (!hasBoosters) {
             return booster;
+        }
 
-        if (parentSet != null) {
-            for (int i = 0; i < numBoosterLands; i++) {
-                addToBooster(booster, parentSet, Rarity.LAND);
-            }
-        } else {
-            for (int i = 0; i < numBoosterLands; i++) {
-                addToBooster(booster, this, Rarity.LAND);
-            }
+        CardCriteria criteria = new CardCriteria();
+        criteria.setCodes(parentSet != null ? parentSet.code : this.code).rarities(Rarity.LAND).doubleFaced(false);
+        List<CardInfo> basicLand = CardRepository.instance.findCards(criteria);
+
+        criteria = new CardCriteria();
+        criteria.setCodes(this.code).rarities(Rarity.COMMON).doubleFaced(false);
+        List<CardInfo> common = CardRepository.instance.findCards(criteria);
+
+        criteria = new CardCriteria();
+        criteria.setCodes(this.code).rarities(Rarity.UNCOMMON).doubleFaced(false);
+        List<CardInfo> uncommon = CardRepository.instance.findCards(criteria);
+
+        criteria = new CardCriteria();
+        criteria.setCodes(this.code).rarities(Rarity.RARE).doubleFaced(false);
+        List<CardInfo> rare = CardRepository.instance.findCards(criteria);
+
+        criteria = new CardCriteria();
+        criteria.setCodes(this.code).rarities(Rarity.MYTHIC).doubleFaced(false);
+        List<CardInfo> mythic = CardRepository.instance.findCards(criteria);
+
+        criteria = new CardCriteria();
+        criteria.setCodes(this.code).doubleFaced(true);
+        List<CardInfo> doubleFaced = CardRepository.instance.findCards(criteria);
+
+        for (int i = 0; i < numBoosterLands; i++) {
+            addToBooster(booster, basicLand);
         }
         for (int i = 0; i < numBoosterCommon; i++) {
-            addToBooster(booster, this, Rarity.COMMON);
+            addToBooster(booster, common);
         }
         for (int i = 0; i < numBoosterUncommon; i++) {
-            addToBooster(booster, this, Rarity.UNCOMMON);
+            addToBooster(booster, uncommon);
         }
         for (int i = 0; i < numBoosterRare; i++) {
             if (ratioBoosterMythic > 0 && rnd.nextInt(ratioBoosterMythic) == 1) {
-                addToBooster(booster, this, Rarity.MYTHIC);
+                addToBooster(booster, mythic);
             } else {
-                addToBooster(booster, this, Rarity.RARE);
+                addToBooster(booster, rare);
             }
         }
         for (int i = 0; i < numBoosterDoubleFaced; i++) {
-            addToBoosterDoubleFaced(booster, this);
+            addToBooster(booster, doubleFaced);
         }
 
         return booster;
     }
 
-    protected void addToBooster(List<Card> booster, ExpansionSet set, Rarity rarity) {
-        Card card = set.getRandom(rarity);
-        if (card != null) {
-            card = checkNotDoubleFaced(card, set, rarity);
-            card = checkNotDuplicate(card, booster, set, rarity);
-            Card newCard = card.copy();
-            newCard.assignNewId();
-            booster.add(newCard);
-        }
-    }
-
-    protected void addToBoosterDoubleFaced(List<Card> booster, ExpansionSet set) {
-        Card card = set.getRandomDoubleFaced();
-        if (card != null) {
-            Card newCard = card.copy();
-            newCard.assignNewId();
-            booster.add(newCard);
-        }
-    }
-
-    /**
-     * Checks that card doesn't already exist in the booster. If so, tries to generate new one several times.
-     *
-     * @param cardToCheck
-     * @param booster
-     * @param set
-     * @param rarity
-     * @return
-     */
-    private Card checkNotDuplicate(Card cardToCheck, List<Card> booster, ExpansionSet set, Rarity rarity) {
-        Card card = cardToCheck;
-        boolean duplicate = true;
-        int retryCount = 5;
-        while (duplicate && retryCount > 0) {
-            if (!rarity.equals(Rarity.LAND)) {
-                // check for duplicates
-                if (hasCardByName(booster, card.getName())) {
-                    card = set.getRandom(rarity);
-                } else {
-                    duplicate = false; // no such card yet
-                }
-            } else {
-                duplicate = false;
-            }
-            retryCount--;
-        }
-        return card;
-    }
-
-    /**
-     * Checks that card is not double faced. If so, tries to generate new one several times.
-     *
-     * @param cardToCheck
-     * @param set
-     * @param rarity
-     * @return
-     */
-    private Card checkNotDoubleFaced(Card cardToCheck, ExpansionSet set, Rarity rarity) {
-        int retryCount = 100;
-        Card card = cardToCheck;
-        while (card.canTransform()) {
-            card = set.getRandom(rarity);
-            retryCount--;
-            if (retryCount <= 0) {
-                logger.warn("Couldn't find non double faced card");
-                break;
-            }
-        }
-        return card;
-    }
-
-    protected boolean hasCardByName(List<Card> booster, String name) {
-        for (Card card : booster) {
-            if (card.getName().equals(name)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    protected Card getRandom(Rarity rarity) {
-        if (!getRarities().containsKey(rarity))
-            return null;
-        int size = getRarities().get(rarity).size();
-        if (size > 0) {
-            return getRarities().get(rarity).get(rnd.nextInt(size)).copy();
-        }
-        return null;
-    }
-
-    protected Card getRandomDoubleFaced() {
-        int size = getCards().size();
-        if (size > 0) {
-            Card card = cards.get(rnd.nextInt(size));
-            int retryCount = 1000;
-            while (!card.canTransform()) {
-                card = cards.get(rnd.nextInt(size));
-                retryCount--;
-                if (retryCount <= 0) {
-                    logger.warn("Couldn't find double-faced card.");
-                    break;
+    private void addToBooster(List<Card> booster, List<CardInfo> cards) {
+        if (!cards.isEmpty()) {
+            CardInfo cardInfo = cards.remove(rnd.nextInt(cards.size()));
+            if (cardInfo != null) {
+                Card card = cardInfo.getCard();
+                if (card != null) {
+                    booster.add(card);
                 }
             }
-            return card;
         }
-        return null;
-    }
-
-    public Map<Rarity, List<Card>> getRarities() {
-        if (rarities == null) {
-            loadLazily();
-        }
-        return rarities;
     }
 }
diff --git a/Mage/src/mage/cards/Sets.java b/Mage/src/mage/cards/Sets.java
new file mode 100644
index 0000000000..20254e1119
--- /dev/null
+++ b/Mage/src/mage/cards/Sets.java
@@ -0,0 +1,185 @@
+/*
+ *  Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
+ * 
+ *  Redistribution and use in source and binary forms, with or without modification, are
+ *  permitted provided that the following conditions are met:
+ * 
+ *     1. Redistributions of source code must retain the above copyright notice, this list of
+ *        conditions and the following disclaimer.
+ * 
+ *     2. Redistributions in binary form must reproduce the above copyright notice, this list
+ *        of conditions and the following disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ * 
+ *  THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ *  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
+ *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ *  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ *  The views and conclusions contained in the software and documentation are those of the
+ *  authors and should not be interpreted as representing official policies, either expressed
+ *  or implied, of BetaSteward_at_googlemail.com.
+ */
+
+package mage.cards;
+
+import mage.Constants.CardType;
+import mage.Constants.ColoredManaSymbol;
+import mage.cards.decks.DeckCardLists;
+import mage.cards.repository.CardCriteria;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
+import mage.util.ClassScanner;
+import org.apache.log4j.Logger;
+
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+import java.util.*;
+
+/**
+ *
+ * @author BetaSteward_at_googlemail.com
+ */
+public class Sets extends HashMap<String, ExpansionSet> {
+
+    private final static Logger logger = Logger.getLogger(Sets.class);
+    private static final Sets fINSTANCE =  new Sets();
+    protected static Random rnd = new Random();
+
+    public static Sets getInstance() {
+        return fINSTANCE;
+    }
+
+    private Sets() {
+        ArrayList<String> packages = new ArrayList<String>();
+        packages.add("mage.sets");
+        for (Class c : ClassScanner.findClasses(packages, ExpansionSet.class)) {
+            try {
+                addSet((ExpansionSet) c.getMethod("getInstance").invoke(null));
+            } catch (Exception ex) {
+            }
+        }
+    }
+
+    private void addSet(ExpansionSet set) {
+        this.put(set.getCode(), set);
+    }
+
+    /**
+     * Generates card pool of cardsCount cards that have manacost of allowed colors.
+     *
+     * @param cardsCount
+     * @param allowedColors
+     * @return
+     */
+    public static List<Card> generateRandomCardPool(int cardsCount, List<ColoredManaSymbol> allowedColors) {
+        CardCriteria criteria = new CardCriteria();
+        criteria.notTypes(CardType.LAND);
+        for (ColoredManaSymbol color : allowedColors) {
+            switch (color) {
+                case W:
+                    criteria.white(true);
+                    break;
+                case U:
+                    criteria.blue(true);
+                    break;
+                case B:
+                    criteria.black(true);
+                    break;
+                case R:
+                    criteria.red(true);
+                    break;
+                case G:
+                    criteria.green(true);
+                    break;
+            }
+        }
+        List<CardInfo> cards = CardRepository.instance.findCards(criteria);
+
+        int count = 0;
+        int tries = 0;
+        List<Card> cardPool = new ArrayList<Card>();
+        while (count < cardsCount) {
+            CardInfo cardInfo = cards.get(rnd.nextInt(cards.size()));
+            Card card = cardInfo != null ? cardInfo.getCard() : null;
+            if (card != null) {
+                cardPool.add(card);
+                count++;
+            }
+            tries++;
+            if (tries > 4096) { // to avoid infinite loop
+                throw new IllegalStateException("Not enough cards for chosen colors to generate deck: " + allowedColors);
+            }
+        }
+
+        return cardPool;
+    }
+
+    public static ExpansionSet findSet(String code) {
+        if (fINSTANCE.containsKey(code)) {
+            return fINSTANCE.get(code);
+        }
+        return null;
+    }
+
+    public static void saveDeck(String file, DeckCardLists deck) throws FileNotFoundException {
+        PrintWriter out = new PrintWriter(file);
+        Map<String, Integer> deckCards = new HashMap<String, Integer>();
+        Map<String, Integer> sideboard = new HashMap<String, Integer>();
+        try {
+            if (deck.getName() != null && deck.getName().length() > 0) {
+                out.println("NAME:" + deck.getName());
+            }
+            if (deck.getAuthor() != null && deck.getAuthor().length() > 0) {
+                out.println("AUTHOR:" + deck.getAuthor());
+            }
+            for (String cardClass: deck.getCards()) {
+                if (deckCards.containsKey(cardClass)) {
+                    deckCards.put(cardClass, deckCards.get(cardClass) + 1);
+                }
+                else {
+                    deckCards.put(cardClass, 1);
+                }
+            }
+            for (String cardClass: deck.getSideboard()) {
+                if (sideboard.containsKey(cardClass)) {
+                    sideboard.put(cardClass, sideboard.get(cardClass) + 1);
+                }
+                else {
+                    sideboard.put(cardClass, 1);
+                }
+            }
+            for (Map.Entry<String, Integer> entry: deckCards.entrySet()) {
+                Card card = CardImpl.createCard(entry.getKey());
+                if (card != null) {
+                    out.printf("%d [%s:%d] %s%n", entry.getValue(), card.getExpansionSetCode(), card.getCardNumber(), card.getName());
+                }
+            }
+            for (Map.Entry<String, Integer> entry: sideboard.entrySet()) {
+                Card card = CardImpl.createCard(entry.getKey());
+                if (card != null) {
+                    out.printf("SB: %d [%s:%d] %s%n", entry.getValue(), card.getExpansionSetCode(), card.getCardNumber(), card.getName());
+                }
+            }
+        }
+        finally {
+            out.close();
+        }
+    }
+
+    public ExpansionSet[] getSortedByReleaseDate() {
+        ExpansionSet[] sets = Sets.getInstance().values().toArray(new ExpansionSet[0]);
+        Arrays.sort(sets, new Comparator<ExpansionSet>() {
+            @Override
+            public int compare(ExpansionSet o1, ExpansionSet o2) {
+                return o2.getReleaseDate().compareTo(o1.getReleaseDate());
+            }
+        });
+        return sets;
+    }
+}
diff --git a/Mage.Sets/src/mage/cards/decks/importer/DckDeckImporter.java b/Mage/src/mage/cards/decks/importer/DckDeckImporter.java
similarity index 83%
rename from Mage.Sets/src/mage/cards/decks/importer/DckDeckImporter.java
rename to Mage/src/mage/cards/decks/importer/DckDeckImporter.java
index d1cfb2b674..755e119cbc 100644
--- a/Mage.Sets/src/mage/cards/decks/importer/DckDeckImporter.java
+++ b/Mage/src/mage/cards/decks/importer/DckDeckImporter.java
@@ -29,15 +29,15 @@ package mage.cards.decks.importer;
 
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import mage.cards.ExpansionSet;
 import mage.cards.decks.DeckCardLists;
-import mage.sets.Sets;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
 
 /**
  *
  * @author North
  */
-public class DckDeckImporter extends DeckImporterImpl {
+public class DckDeckImporter extends DeckImporter {
 
     private static final Pattern pattern = Pattern.compile("(SB:)?\\s*(\\d*)\\s*\\[([a-zA-Z0-9]{3}):(\\d*)\\].*");
 
@@ -57,17 +57,18 @@ public class DckDeckImporter extends DeckImporterImpl {
             int count = Integer.parseInt(m.group(2));
             String setCode = m.group(3);
             int cardNum = Integer.parseInt(m.group(4));
-            ExpansionSet set = Sets.findSet(setCode);
-            String card = null;
-            if (set != null) {
-                card = set.findCardName(cardNum);
+
+            String className = null;
+            CardInfo cardInfo = CardRepository.instance.findCard(setCode, cardNum);
+            if (cardInfo != null) {
+                className = cardInfo.getClassName();
             }
-            if (card != null) {
+            if (className != null) {
                 for (int i = 0; i < count; i++) {
                     if (!sideboard) {
-                        deckList.getCards().add(card);
+                        deckList.getCards().add(className);
                     } else {
-                        deckList.getSideboard().add(card);
+                        deckList.getSideboard().add(className);
                     }
                 }
             } else {
diff --git a/Mage.Sets/src/mage/cards/decks/importer/DecDeckImporter.java b/Mage/src/mage/cards/decks/importer/DecDeckImporter.java
similarity index 76%
rename from Mage.Sets/src/mage/cards/decks/importer/DecDeckImporter.java
rename to Mage/src/mage/cards/decks/importer/DecDeckImporter.java
index 8c3a34359a..013eee32bf 100644
--- a/Mage.Sets/src/mage/cards/decks/importer/DecDeckImporter.java
+++ b/Mage/src/mage/cards/decks/importer/DecDeckImporter.java
@@ -28,43 +28,50 @@
 
 package mage.cards.decks.importer;
 
-import mage.cards.Card;
+import java.util.List;
+import java.util.Random;
 import mage.cards.decks.DeckCardLists;
-import mage.sets.Sets;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
 
 /**
  *
  * @author BetaSteward_at_googlemail.com
  */
-public class DecDeckImporter extends DeckImporterImpl {
+public class DecDeckImporter extends DeckImporter {
 
     @Override
     protected void readLine(String line, DeckCardLists deckList) {
-        if (line.length() == 0 || line.startsWith("//")) return;
+        if (line.length() == 0 || line.startsWith("//")) {
+            return;
+        }
+
         boolean sideboard = false;
         if (line.startsWith("SB:")) {
             line = line.substring(3).trim();
             sideboard = true;
         }
+
         int delim = line.indexOf(' ');
         String lineNum = line.substring(0, delim).trim();
         String lineName = line.substring(delim).trim();
         try {
             int num = Integer.parseInt(lineNum);
-            Card card = Sets.findCard(lineName);
-            if (card == null)
+            List<CardInfo> cards = CardRepository.instance.findCards(lineName);
+            if (cards.isEmpty()) {
                 sbMessage.append("Could not find card: '").append(lineName).append("' at line ").append(lineCount).append("\n");
-            else {
-                String cardName = card.getClass().getCanonicalName();
+            } else {
+                Random random = new Random();
                 for (int i = 0; i < num; i++) {
-                    if (!sideboard)
-                        deckList.getCards().add(cardName);
-                    else
-                        deckList.getSideboard().add(cardName);
+                    String className = cards.get(random.nextInt(cards.size())).getClassName();
+                    if (!sideboard) {
+                        deckList.getCards().add(className);
+                    } else {
+                        deckList.getSideboard().add(className);
+                    }
                 }
             }
-        }
-        catch (NumberFormatException nfe) {
+        } catch (NumberFormatException nfe) {
             sbMessage.append("Invalid number: ").append(lineNum).append(" at line ").append(lineCount).append("\n");
         }
     }
diff --git a/Mage.Sets/src/mage/cards/decks/importer/DeckImporterImpl.java b/Mage/src/mage/cards/decks/importer/DeckImporter.java
similarity index 96%
rename from Mage.Sets/src/mage/cards/decks/importer/DeckImporterImpl.java
rename to Mage/src/mage/cards/decks/importer/DeckImporter.java
index 0cc0926cfd..132fa75378 100644
--- a/Mage.Sets/src/mage/cards/decks/importer/DeckImporterImpl.java
+++ b/Mage/src/mage/cards/decks/importer/DeckImporter.java
@@ -37,13 +37,12 @@ import org.apache.log4j.Logger;
  *
  * @author BetaSteward_at_googlemail.com
  */
-public abstract class DeckImporterImpl implements DeckImporter {
+public abstract class DeckImporter {
 
-    private final static Logger logger = Logger.getLogger(DeckImporterImpl.class);
+    private final static Logger logger = Logger.getLogger(DeckImporter.class);
     protected StringBuilder sbMessage = new StringBuilder();
     protected int lineCount;
 
-    @Override
     public DeckCardLists importDeck(String file) {
         File f = new File(file);
         DeckCardLists deckList = new DeckCardLists();
@@ -73,7 +72,6 @@ public abstract class DeckImporterImpl implements DeckImporter {
         return deckList;
     }
 
-    @Override
     public String getErrors(){
         return sbMessage.toString();
     }
diff --git a/Mage.Sets/src/mage/cards/decks/importer/DeckImporterUtil.java b/Mage/src/mage/cards/decks/importer/DeckImporterUtil.java
similarity index 100%
rename from Mage.Sets/src/mage/cards/decks/importer/DeckImporterUtil.java
rename to Mage/src/mage/cards/decks/importer/DeckImporterUtil.java
diff --git a/Mage.Sets/src/mage/cards/decks/importer/MWSDeckImporter.java b/Mage/src/mage/cards/decks/importer/MWSDeckImporter.java
similarity index 70%
rename from Mage.Sets/src/mage/cards/decks/importer/MWSDeckImporter.java
rename to Mage/src/mage/cards/decks/importer/MWSDeckImporter.java
index fd8f368612..e684a0e087 100644
--- a/Mage.Sets/src/mage/cards/decks/importer/MWSDeckImporter.java
+++ b/Mage/src/mage/cards/decks/importer/MWSDeckImporter.java
@@ -25,23 +25,26 @@
  *  authors and should not be interpreted as representing official policies, either expressed
  *  or implied, of BetaSteward_at_googlemail.com.
  */
-
 package mage.cards.decks.importer;
 
-import mage.cards.Card;
-import mage.cards.ExpansionSet;
+import java.util.List;
+import java.util.Random;
 import mage.cards.decks.DeckCardLists;
-import mage.sets.Sets;
+import mage.cards.repository.CardCriteria;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
 
 /**
  *
  * @author BetaSteward_at_googlemail.com
  */
-public class MWSDeckImporter extends DeckImporterImpl {
+public class MWSDeckImporter extends DeckImporter {
 
     @Override
     protected void readLine(String line, DeckCardLists deckList) {
-        if (line.length() == 0 || line.startsWith("//")) return;
+        if (line.length() == 0 || line.startsWith("//")) {
+            return;
+        }
         boolean sideboard = false;
         if (line.startsWith("SB:")) {
             line = line.substring(3).trim();
@@ -50,7 +53,7 @@ public class MWSDeckImporter extends DeckImporterImpl {
         int delim = line.indexOf(' ');
         String lineNum = line.substring(0, delim).trim();
         String setCode = "";
-        if (line.indexOf('[') != -1 ) {
+        if (line.indexOf('[') != -1) {
             int setStart = line.indexOf('[') + 1;
             int setEnd = line.indexOf(']');
             setCode = line.substring(setStart, setEnd).trim();
@@ -59,31 +62,32 @@ public class MWSDeckImporter extends DeckImporterImpl {
         String lineName = line.substring(delim + 1).trim();
         try {
             int num = Integer.parseInt(lineNum);
-            ExpansionSet set = null;
-            if (setCode.length() > 0)
-                set = Sets.findSet(setCode);
-            Card card;
-            if (set != null) {
-                card = set.findCard(lineName);
+
+            CardCriteria criteria = new CardCriteria();
+            criteria.name(lineName);
+            criteria.setCodes(setCode);
+            List<CardInfo> cards = CardRepository.instance.findCards(criteria);
+            if (cards.isEmpty()) {
+                criteria = new CardCriteria();
+                criteria.name(lineName);
+                cards = CardRepository.instance.findCards(criteria);
             }
-            else {
-                card = Sets.findCard(lineName);
-            }
-            if (card == null)
+
+            if (cards.isEmpty()) {
                 sbMessage.append("Could not find card: '").append(lineName).append("' at line ").append(lineCount).append("\n");
-            else {
-                String cardName = card.getClass().getCanonicalName();
+            } else {
+                Random random = new Random();
                 for (int i = 0; i < num; i++) {
-                    if (!sideboard)
-                        deckList.getCards().add(cardName);
-                    else
-                        deckList.getSideboard().add(cardName);
+                    String className = cards.get(random.nextInt(cards.size())).getClassName();
+                    if (!sideboard) {
+                        deckList.getCards().add(className);
+                    } else {
+                        deckList.getSideboard().add(className);
+                    }
                 }
             }
-        }
-        catch (NumberFormatException nfe) {
+        } catch (NumberFormatException nfe) {
             sbMessage.append("Invalid number: ").append(lineNum).append(" at line ").append(lineCount).append("\n");
         }
     }
-
 }
diff --git a/Mage.Sets/src/mage/cards/decks/importer/TxtDeckImporter.java b/Mage/src/mage/cards/decks/importer/TxtDeckImporter.java
similarity index 76%
rename from Mage.Sets/src/mage/cards/decks/importer/TxtDeckImporter.java
rename to Mage/src/mage/cards/decks/importer/TxtDeckImporter.java
index 2ee8999262..0aa8008697 100644
--- a/Mage.Sets/src/mage/cards/decks/importer/TxtDeckImporter.java
+++ b/Mage/src/mage/cards/decks/importer/TxtDeckImporter.java
@@ -28,44 +28,51 @@
 
 package mage.cards.decks.importer;
 
-import mage.cards.Card;
+import java.util.List;
+import java.util.Random;
 import mage.cards.decks.DeckCardLists;
-import mage.sets.Sets;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
 
 /**
  *
  * @author BetaSteward_at_googlemail.com
  */
-public class TxtDeckImporter extends DeckImporterImpl {
+public class TxtDeckImporter extends DeckImporter {
 
     private boolean sideboard = false;
 
     @Override
     protected void readLine(String line, DeckCardLists deckList) {
-        if (line.length() == 0 || line.startsWith("//")) return;
+        if (line.length() == 0 || line.startsWith("//")) {
+            return;
+        }
+
         if (line.startsWith("Sideboard")) {
             sideboard = true;
             return;
         }
+
         int delim = line.indexOf(' ');
         String lineNum = line.substring(0, delim).trim();
         String lineName = line.substring(delim).trim();
         try {
             int num = Integer.parseInt(lineNum);
-            Card card = Sets.findCard(lineName);
-            if (card == null)
+            List<CardInfo> cards = CardRepository.instance.findCards(lineName);
+            if (cards.isEmpty()) {
                 sbMessage.append("Could not find card: '").append(lineName).append("' at line ").append(lineCount).append("\n");
-            else {
-                String cardName = card.getClass().getCanonicalName();
+            } else {
+                Random random = new Random();
                 for (int i = 0; i < num; i++) {
-                    if (!sideboard)
-                        deckList.getCards().add(cardName);
-                    else
-                        deckList.getSideboard().add(cardName);
+                    String className = cards.get(random.nextInt(cards.size())).getClassName();
+                    if (!sideboard) {
+                        deckList.getCards().add(className);
+                    } else {
+                        deckList.getSideboard().add(className);
+                    }
                 }
             }
-        }
-        catch (NumberFormatException nfe) {
+        } catch (NumberFormatException nfe) {
             sbMessage.append("Invalid number: ").append(lineNum).append(" at line ").append(lineCount).append("\n");
         }
     }
diff --git a/Mage/src/mage/cards/repository/CardCriteria.java b/Mage/src/mage/cards/repository/CardCriteria.java
new file mode 100644
index 0000000000..11d2d2ac4d
--- /dev/null
+++ b/Mage/src/mage/cards/repository/CardCriteria.java
@@ -0,0 +1,274 @@
+/*
+ *  Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without modification, are
+ *  permitted provided that the following conditions are met:
+ *
+ *     1. Redistributions of source code must retain the above copyright notice, this list of
+ *        conditions and the following disclaimer.
+ *
+ *     2. Redistributions in binary form must reproduce the above copyright notice, this list
+ *        of conditions and the following disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ *  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
+ *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ *  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  The views and conclusions contained in the software and documentation are those of the
+ *  authors and should not be interpreted as representing official policies, either expressed
+ *  or implied, of BetaSteward_at_googlemail.com.
+ */
+package mage.cards.repository;
+
+import com.j256.ormlite.stmt.QueryBuilder;
+import com.j256.ormlite.stmt.SelectArg;
+import com.j256.ormlite.stmt.Where;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import mage.Constants.CardType;
+import mage.Constants.Rarity;
+
+/**
+ *
+ * @author North
+ */
+public class CardCriteria {
+
+    private String name;
+    private String rules;
+    private List<String> setCodes;
+    private List<CardType> types;
+    private List<CardType> notTypes;
+    private List<String> supertypes;
+    private List<String> notSupertypes;
+    private List<String> subtypes;
+    private List<Rarity> rarities;
+    private Boolean doubleFaced;
+    private boolean black;
+    private boolean blue;
+    private boolean green;
+    private boolean red;
+    private boolean white;
+    private boolean colorless;
+    private Long start;
+    private Long count;
+
+    public CardCriteria() {
+        this.setCodes = new ArrayList<String>();
+        this.rarities = new ArrayList<Rarity>();
+        this.types = new ArrayList<CardType>();
+        this.notTypes = new ArrayList<CardType>();
+        this.supertypes = new ArrayList<String>();
+        this.notSupertypes = new ArrayList<String>();
+        this.subtypes = new ArrayList<String>();
+
+        this.black = true;
+        this.blue = true;
+        this.green = true;
+        this.red = true;
+        this.white = true;
+        this.colorless = true;
+    }
+
+    public CardCriteria black(boolean black) {
+        this.black = black;
+        return this;
+    }
+
+    public CardCriteria blue(boolean blue) {
+        this.blue = blue;
+        return this;
+    }
+
+    public CardCriteria green(boolean green) {
+        this.green = green;
+        return this;
+    }
+
+    public CardCriteria red(boolean red) {
+        this.red = red;
+        return this;
+    }
+
+    public CardCriteria white(boolean white) {
+        this.white = white;
+        return this;
+    }
+
+    public CardCriteria colorless(boolean colorless) {
+        this.colorless = colorless;
+        return this;
+    }
+
+    public CardCriteria doubleFaced(boolean doubleFaced) {
+        this.doubleFaced = doubleFaced;
+        return this;
+    }
+
+    public CardCriteria name(String name) {
+        this.name = name;
+        return this;
+    }
+
+    public CardCriteria rules(String rules) {
+        this.rules = rules;
+        return this;
+    }
+
+    public CardCriteria start(Long start) {
+        this.start = start;
+        return this;
+    }
+
+    public CardCriteria count(Long count) {
+        this.count = count;
+        return this;
+    }
+
+    public CardCriteria rarities(Rarity... rarities) {
+        this.rarities.addAll(Arrays.asList(rarities));
+        return this;
+    }
+
+    public CardCriteria setCodes(String... setCodes) {
+        this.setCodes.addAll(Arrays.asList(setCodes));
+        return this;
+    }
+
+    public CardCriteria types(CardType... types) {
+        this.types.addAll(Arrays.asList(types));
+        return this;
+    }
+
+    public CardCriteria notTypes(CardType... types) {
+        this.notTypes.addAll(Arrays.asList(types));
+        return this;
+    }
+
+    public CardCriteria supertypes(String... supertypes) {
+        this.supertypes.addAll(Arrays.asList(supertypes));
+        return this;
+    }
+
+    public CardCriteria notSupertypes(String... supertypes) {
+        this.notSupertypes.addAll(Arrays.asList(supertypes));
+        return this;
+    }
+
+    public CardCriteria subtypes(String... subtypes) {
+        this.subtypes.addAll(Arrays.asList(subtypes));
+        return this;
+    }
+
+    public void buildQuery(QueryBuilder qb) throws SQLException {
+        Where where = qb.where();
+        int clausesCount = 0;
+        if (name != null) {
+            where.like("name", new SelectArg('%' + name + '%'));
+            clausesCount++;
+        }
+        if (rules != null) {
+            where.like("rules", new SelectArg('%' + rules + '%'));
+            clausesCount++;
+        }
+
+        if (doubleFaced != null) {
+            where.eq("doubleFaced", doubleFaced);
+            clausesCount++;
+        }
+
+        for (Rarity rarity : rarities) {
+            where.eq("rarity", rarity);
+        }
+        if (!rarities.isEmpty()) {
+            where.or(rarities.size());
+            clausesCount++;
+        }
+
+        for (String setCode : setCodes) {
+            where.eq("setCode", setCode);
+        }
+        if (!setCodes.isEmpty()) {
+            where.or(setCodes.size());
+            clausesCount++;
+        }
+
+        for (CardType type : types) {
+            where.like("types", new SelectArg('%' + type.name() + '%'));
+        }
+        if (!types.isEmpty()) {
+            where.or(types.size());
+            clausesCount++;
+        }
+
+        for (CardType type : notTypes) {
+            where.not().like("types", new SelectArg('%' + type.name() + '%'));
+            clausesCount++;
+        }
+
+        for (String superType : supertypes) {
+            where.like("supertypes", new SelectArg('%' + superType + '%'));
+            clausesCount++;
+        }
+        for (String subType : notSupertypes) {
+            where.not().like("supertypes", new SelectArg('%' + subType + '%'));
+            clausesCount++;
+        }
+
+        for (String subType : subtypes) {
+            where.like("subtypes", new SelectArg('%' + subType + '%'));
+            clausesCount++;
+        }
+
+        if (!black || !blue || !green || !red || !white || !colorless) {
+            int colorClauses = 0;
+            if (black) {
+                where.eq("black", true);
+                colorClauses++;
+            }
+            if (blue) {
+                where.eq("blue", true);
+                colorClauses++;
+            }
+            if (green) {
+                where.eq("green", true);
+                colorClauses++;
+            }
+            if (red) {
+                where.eq("red", true);
+                colorClauses++;
+            }
+            if (white) {
+                where.eq("white", true);
+                colorClauses++;
+            }
+            if (colorless) {
+                where.eq("black", false).eq("blue", false).eq("green", false).eq("red", false).eq("white", false);
+                where.and(5);
+                colorClauses++;
+            }
+            where.or(colorClauses);
+            clausesCount++;
+        }
+
+        where.and(clausesCount);
+
+        if (start != null) {
+            qb.offset(start);
+        }
+        if (count != null) {
+            qb.limit(count);
+        }
+
+        qb.orderBy("cardNumber", true);
+    }
+}
diff --git a/Mage/src/mage/cards/repository/CardInfo.java b/Mage/src/mage/cards/repository/CardInfo.java
new file mode 100644
index 0000000000..b11a698093
--- /dev/null
+++ b/Mage/src/mage/cards/repository/CardInfo.java
@@ -0,0 +1,220 @@
+/*
+ *  Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without modification, are
+ *  permitted provided that the following conditions are met:
+ *
+ *     1. Redistributions of source code must retain the above copyright notice, this list of
+ *        conditions and the following disclaimer.
+ *
+ *     2. Redistributions in binary form must reproduce the above copyright notice, this list
+ *        of conditions and the following disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ *  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
+ *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ *  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  The views and conclusions contained in the software and documentation are those of the
+ *  authors and should not be interpreted as representing official policies, either expressed
+ *  or implied, of BetaSteward_at_googlemail.com.
+ */
+package mage.cards.repository;
+
+import com.j256.ormlite.field.DataType;
+import com.j256.ormlite.field.DatabaseField;
+import com.j256.ormlite.table.DatabaseTable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import mage.Constants.CardType;
+import mage.Constants.Rarity;
+import mage.ObjectColor;
+import mage.cards.Card;
+import mage.cards.CardImpl;
+
+/**
+ *
+ * @author North
+ */
+@DatabaseTable(tableName = "card")
+public class CardInfo {
+
+    private static final String SEPARATOR = "@@@";
+    @DatabaseField
+    protected String name;
+    @DatabaseField
+    protected int cardNumber;
+    @DatabaseField
+    protected String setCode;
+    @DatabaseField
+    protected String className;
+    @DatabaseField
+    protected String power;
+    @DatabaseField
+    protected String toughness;
+    @DatabaseField
+    protected int convertedManaCost;
+    @DatabaseField(dataType = DataType.ENUM_STRING)
+    protected Rarity rarity;
+    @DatabaseField
+    protected String types;
+    @DatabaseField
+    protected String subtypes;
+    @DatabaseField
+    protected String supertypes;
+    @DatabaseField
+    protected String manaCosts;
+    @DatabaseField
+    protected String rules;
+    @DatabaseField
+    protected boolean black;
+    @DatabaseField
+    protected boolean blue;
+    @DatabaseField
+    protected boolean green;
+    @DatabaseField
+    protected boolean red;
+    @DatabaseField
+    protected boolean white;
+    @DatabaseField
+    protected boolean doubleFaced;
+
+    public CardInfo() {
+    }
+
+    public CardInfo(Card card) {
+        this.name = card.getName();
+        this.cardNumber = card.getCardNumber();
+        this.setCode = card.getExpansionSetCode();
+        this.className = card.getClass().getCanonicalName();
+        this.power = card.getPower().toString();
+        this.toughness = card.getToughness().toString();
+        this.convertedManaCost = card.getManaCost().convertedManaCost();
+        this.rarity = card.getRarity();
+        this.doubleFaced = card.canTransform();
+
+        this.blue = card.getColor().isBlue();
+        this.black = card.getColor().isBlack();
+        this.green = card.getColor().isGreen();
+        this.red = card.getColor().isRed();
+        this.white = card.getColor().isWhite();
+
+        this.setTypes(card.getCardType());
+        this.setSubtypes(card.getSubtype());
+        this.setSuperTypes(card.getSupertype());
+        this.setManaCosts(card.getManaCost().getSymbols());
+        this.setRules(card.getRules());
+    }
+
+    public Card getCard() {
+        return CardImpl.createCard(className);
+    }
+
+    public ObjectColor getColor() {
+        ObjectColor color = new ObjectColor();
+        color.setBlack(black);
+        color.setBlue(blue);
+        color.setGreen(green);
+        color.setRed(red);
+        color.setWhite(white);
+        return color;
+    }
+
+    private String joinList(List items) {
+        StringBuilder sb = new StringBuilder();
+        for (Object item : items) {
+            sb.append(item.toString()).append(SEPARATOR);
+        }
+        return sb.toString();
+    }
+
+    private List<String> parseList(String list) {
+        return Arrays.asList(list.split(SEPARATOR));
+    }
+
+    public final List<CardType> getTypes() {
+        ArrayList<CardType> list = new ArrayList<CardType>();
+        for (String type : this.types.split(SEPARATOR)) {
+            try {
+                list.add(CardType.valueOf(type));
+            } catch (Exception e) {
+            }
+        }
+        return list;
+    }
+
+    public final void setTypes(List<CardType> types) {
+        StringBuilder sb = new StringBuilder();
+        for (CardType item : types) {
+            sb.append(item.name()).append(SEPARATOR);
+        }
+        this.types = sb.toString();
+    }
+
+    public int getConvertedManaCost() {
+        return convertedManaCost;
+    }
+
+    public final List<String> getManaCosts() {
+        return parseList(manaCosts);
+    }
+
+    public final void setManaCosts(List<String> manaCosts) {
+        this.manaCosts = joinList(manaCosts);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getPower() {
+        return power;
+    }
+
+    public Rarity getRarity() {
+        return rarity;
+    }
+
+    public final List<String> getRules() {
+        return parseList(rules);
+    }
+
+    public final void setRules(List<String> rules) {
+        this.rules = joinList(rules);
+    }
+
+    public final List<String> getSubTypes() {
+        return parseList(subtypes);
+    }
+
+    public final void setSubtypes(List<String> subtypes) {
+        this.subtypes = joinList(subtypes);
+    }
+
+    public final List<String> getSupertypes() {
+        return parseList(supertypes);
+    }
+
+    public final void setSuperTypes(List<String> superTypes) {
+        this.supertypes = joinList(superTypes);
+    }
+
+    public String getToughness() {
+        return toughness;
+    }
+
+    public String getSetCode() {
+        return setCode;
+    }
+
+    public String getClassName() {
+        return className;
+    }
+}
diff --git a/Mage/src/mage/cards/repository/CardRepository.java b/Mage/src/mage/cards/repository/CardRepository.java
new file mode 100644
index 0000000000..6902be403b
--- /dev/null
+++ b/Mage/src/mage/cards/repository/CardRepository.java
@@ -0,0 +1,230 @@
+/*
+ *  Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without modification, are
+ *  permitted provided that the following conditions are met:
+ *
+ *     1. Redistributions of source code must retain the above copyright notice, this list of
+ *        conditions and the following disclaimer.
+ *
+ *     2. Redistributions in binary form must reproduce the above copyright notice, this list
+ *        of conditions and the following disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ *  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
+ *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ *  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  The views and conclusions contained in the software and documentation are those of the
+ *  authors and should not be interpreted as representing official policies, either expressed
+ *  or implied, of BetaSteward_at_googlemail.com.
+ */
+package mage.cards.repository;
+
+import com.j256.ormlite.dao.Dao;
+import com.j256.ormlite.dao.DaoManager;
+import com.j256.ormlite.jdbc.JdbcConnectionSource;
+import com.j256.ormlite.stmt.QueryBuilder;
+import com.j256.ormlite.stmt.SelectArg;
+import com.j256.ormlite.support.ConnectionSource;
+import com.j256.ormlite.table.TableUtils;
+import java.io.File;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.Callable;
+import mage.Constants.CardType;
+
+/**
+ *
+ * @author North
+ */
+public enum CardRepository {
+
+    instance;
+    private static final String JDBC_URL = "jdbc:sqlite:db/cards.db";
+    private Random random = new Random();
+    private Dao<CardInfo, Object> cardDao;
+    private Set<String> classNames;
+
+    private CardRepository() {
+        File file = new File("db");
+        if (!file.exists()) {
+            file.mkdirs();
+        }
+        try {
+            ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL);
+
+            TableUtils.createTableIfNotExists(connectionSource, CardInfo.class);
+            cardDao = DaoManager.createDao(connectionSource, CardInfo.class);
+        } catch (SQLException ex) {
+        }
+    }
+
+    public void addCards(final List<CardInfo> cards) {
+        try {
+            cardDao.callBatchTasks(new Callable<Object>() {
+                @Override
+                public Object call() throws Exception {
+                    try {
+                        for (CardInfo card : cards) {
+                            cardDao.create(card);
+                            if (classNames != null) {
+                                classNames.add(card.getClassName());
+                            }
+                        }
+                    } catch (SQLException ex) {
+                    }
+                    return null;
+                }
+            });
+        } catch (Exception ex) {
+        }
+    }
+
+    public boolean cardExists(String className) {
+        try {
+            if (classNames == null) {
+                QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
+                qb.distinct().selectColumns("className").where().isNotNull("className");
+                List<CardInfo> results = cardDao.query(qb.prepare());
+                classNames = new TreeSet<String>();
+                for (CardInfo card : results) {
+                    classNames.add(card.getClassName());
+                }
+            }
+            return classNames.contains(className);
+        } catch (SQLException ex) {
+        }
+        return false;
+    }
+
+    public List<String> getSetCodes() {
+        List<String> setCodes = new ArrayList<String>();
+        try {
+            QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
+            qb.distinct().selectColumns("setCode");
+            List<CardInfo> results = cardDao.query(qb.prepare());
+            for (CardInfo card : results) {
+                setCodes.add(card.getSetCode());
+            }
+        } catch (SQLException ex) {
+        } finally {
+            return setCodes;
+        }
+    }
+
+    public Set<String> getNames() {
+        Set<String> names = new TreeSet<String>();
+        try {
+            QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
+            qb.distinct().selectColumns("name");
+            List<CardInfo> results = cardDao.query(qb.prepare());
+            for (CardInfo card : results) {
+                names.add(card.getName());
+            }
+        } catch (SQLException ex) {
+        } finally {
+            return names;
+        }
+    }
+
+    public Set<String> getNonLandNames() {
+        Set<String> names = new TreeSet<String>();
+        try {
+            QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
+            qb.distinct().selectColumns("name");
+            qb.where().not().like("types", new SelectArg('%' + CardType.LAND.name() + '%'));
+            List<CardInfo> results = cardDao.query(qb.prepare());
+            for (CardInfo card : results) {
+                names.add(card.getName());
+            }
+        } catch (SQLException ex) {
+        } finally {
+            return names;
+        }
+    }
+
+    public Set<String> getCreatureTypes() {
+        TreeSet<String> subtypes = new TreeSet<String>();
+        try {
+            QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
+            qb.distinct().selectColumns("subtypes");
+            qb.where().like("types", new SelectArg('%' + CardType.CREATURE.name() + '%'));
+            List<CardInfo> results = cardDao.query(qb.prepare());
+            for (CardInfo card : results) {
+                subtypes.addAll(card.getSubTypes());
+            }
+        } catch (SQLException ex) {
+        } finally {
+            return subtypes;
+        }
+    }
+
+    public CardInfo findCard(String setCode, int cardNumber) {
+        try {
+            QueryBuilder<CardInfo, Object> queryBuilder = cardDao.queryBuilder();
+            queryBuilder.where().eq("setCode", new SelectArg(setCode)).and().eq("cardNumber", cardNumber);
+            List<CardInfo> result = cardDao.query(queryBuilder.prepare());
+            if (!result.isEmpty()) {
+                return result.get(0);
+            }
+        } catch (SQLException ex) {
+        }
+        return null;
+    }
+
+    /**
+     *
+     * @param name
+     * @return random card with the provided name or null if none is found
+     */
+    public CardInfo findCard(String name) {
+        List<CardInfo> cards = findCards(name);
+        if (!cards.isEmpty()) {
+            return cards.get(random.nextInt(cards.size()));
+        }
+        return null;
+    }
+
+    public List<CardInfo> findCards(String name) {
+        try {
+            QueryBuilder<CardInfo, Object> queryBuilder = cardDao.queryBuilder();
+            queryBuilder.where().eq("name", new SelectArg(name));
+
+            return cardDao.query(queryBuilder.prepare());
+        } catch (SQLException ex) {
+        }
+        return new ArrayList<CardInfo>();
+    }
+
+    public List<CardInfo> findCards(CardCriteria criteria) {
+        try {
+            QueryBuilder<CardInfo, Object> queryBuilder = cardDao.queryBuilder();
+            criteria.buildQuery(queryBuilder);
+
+            return cardDao.query(queryBuilder.prepare());
+        } catch (SQLException ex) {
+        }
+        return new ArrayList<CardInfo>();
+    }
+
+    public List<CardInfo> getAllCards() {
+        try {
+            QueryBuilder<CardInfo, Object> queryBuilder = cardDao.queryBuilder();
+
+            return cardDao.query(queryBuilder.prepare());
+        } catch (SQLException ex) {
+        }
+        return new ArrayList<CardInfo>();
+    }
+}
diff --git a/Mage.Sets/src/mage/cards/decks/importer/DeckImporter.java b/Mage/src/mage/cards/repository/CardScanner.java
similarity index 57%
rename from Mage.Sets/src/mage/cards/decks/importer/DeckImporter.java
rename to Mage/src/mage/cards/repository/CardScanner.java
index bd68a2771b..e8b5472d39 100644
--- a/Mage.Sets/src/mage/cards/decks/importer/DeckImporter.java
+++ b/Mage/src/mage/cards/repository/CardScanner.java
@@ -1,42 +1,70 @@
-/*
- *  Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
- * 
- *  Redistribution and use in source and binary forms, with or without modification, are
- *  permitted provided that the following conditions are met:
- * 
- *     1. Redistributions of source code must retain the above copyright notice, this list of
- *        conditions and the following disclaimer.
- * 
- *     2. Redistributions in binary form must reproduce the above copyright notice, this list
- *        of conditions and the following disclaimer in the documentation and/or other materials
- *        provided with the distribution.
- * 
- *  THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
- *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- *  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
- *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- *  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- * 
- *  The views and conclusions contained in the software and documentation are those of the
- *  authors and should not be interpreted as representing official policies, either expressed
- *  or implied, of BetaSteward_at_googlemail.com.
- */
-
-package mage.cards.decks.importer;
-
-import mage.cards.decks.DeckCardLists;
-
-/**
- *
- * @author BetaSteward_at_googlemail.com
- */
-public interface DeckImporter {
-
-    public DeckCardLists importDeck(String file);
-    public String getErrors();
-
-}
+/*
+ *  Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without modification, are
+ *  permitted provided that the following conditions are met:
+ *
+ *     1. Redistributions of source code must retain the above copyright notice, this list of
+ *        conditions and the following disclaimer.
+ *
+ *     2. Redistributions in binary form must reproduce the above copyright notice, this list
+ *        of conditions and the following disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ *  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
+ *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ *  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  The views and conclusions contained in the software and documentation are those of the
+ *  authors and should not be interpreted as representing official policies, either expressed
+ *  or implied, of BetaSteward_at_googlemail.com.
+ */
+package mage.cards.repository;
+
+import java.util.ArrayList;
+import java.util.List;
+import mage.cards.Card;
+import mage.cards.CardImpl;
+import mage.cards.ExpansionSet;
+import mage.cards.Sets;
+import mage.util.ClassScanner;
+
+/**
+ *
+ * @author North
+ */
+public class CardScanner {
+
+    private static boolean scanned = false;
+
+    public static void scan() {
+        if (scanned) {
+            return;
+        }
+        scanned = true;
+
+        List<CardInfo> cardsToAdd = new ArrayList<CardInfo>();
+        List<String> packages = new ArrayList<String>();
+        for (ExpansionSet set : Sets.getInstance().values()) {
+            packages.add(set.getPackageName());
+        }
+
+        for (Class c : ClassScanner.findClasses(packages, CardImpl.class)) {
+            if (!CardRepository.instance.cardExists(c.getCanonicalName())) {
+                Card card = CardImpl.createCard(c);
+                if (card != null && !card.isNightCard()) {
+                    cardsToAdd.add(new CardInfo(card));
+                }
+            }
+        }
+        if (!cardsToAdd.isEmpty()) {
+            CardRepository.instance.addCards(cardsToAdd);
+        }
+    }
+}
diff --git a/Mage/src/mage/util/ClassScanner.java b/Mage/src/mage/util/ClassScanner.java
new file mode 100644
index 0000000000..434258be2e
--- /dev/null
+++ b/Mage/src/mage/util/ClassScanner.java
@@ -0,0 +1,144 @@
+/*
+ *  Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without modification, are
+ *  permitted provided that the following conditions are met:
+ *
+ *     1. Redistributions of source code must retain the above copyright notice, this list of
+ *        conditions and the following disclaimer.
+ *
+ *     2. Redistributions in binary form must reproduce the above copyright notice, this list
+ *        of conditions and the following disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ *  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
+ *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ *  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  The views and conclusions contained in the software and documentation are those of the
+ *  authors and should not be interpreted as representing official policies, either expressed
+ *  or implied, of BetaSteward_at_googlemail.com.
+ */
+package mage.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.TreeSet;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+
+/**
+ *
+ * @author North
+ */
+public class ClassScanner {
+
+    public static List<Class> findClasses(List<String> packages, Class<?> type) {
+        List<Class> cards = new ArrayList<Class>();
+        try {
+            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+            assert classLoader != null;
+
+            HashMap<String, String> dirs = new HashMap<String, String>();
+            TreeSet<String> jars = new TreeSet<String>();
+            for (String packageName : packages) {
+                String path = packageName.replace('.', '/');
+                Enumeration<URL> resources = classLoader.getResources(path);
+
+                while (resources.hasMoreElements()) {
+                    URL resource = resources.nextElement();
+                    String filePath = resource.getFile();
+                    if (filePath.startsWith("file:")) {
+                        filePath = filePath.substring("file:".length(), filePath.lastIndexOf("!"));
+                        jars.add(filePath);
+                    } else {
+                        dirs.put(filePath, packageName);
+                    }
+                }
+            }
+
+            for (String filePath : dirs.keySet()) {
+                cards.addAll(findClasses(new File(filePath), dirs.get(filePath), type));
+            }
+
+            for (String filePath : jars) {
+                File file = new File(URLDecoder.decode(filePath, "UTF-8"));
+                cards.addAll(findClassesInJar(file, packages, type));
+            }
+        } catch (IOException ex) {
+        }
+        return cards;
+    }
+
+    private static List<Class> findClasses(File directory, String packageName, Class<?> type) {
+        List<Class> cards = new ArrayList<Class>();
+        if (!directory.exists()) {
+            return cards;
+        }
+
+        for (File file : directory.listFiles()) {
+            if (file.getName().endsWith(".class")) {
+                try {
+                    Class<?> clazz = Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6));
+                    if (type.isAssignableFrom(clazz)) {
+                        cards.add(clazz);
+                    }
+                } catch (ClassNotFoundException ex) {
+                }
+            }
+        }
+        return cards;
+    }
+
+    private static List<Class> findClassesInJar(File file, List<String> packages, Class<?> type) {
+        List<Class> cards = new ArrayList<Class>();
+        if (!file.exists()) {
+            return cards;
+        }
+        JarInputStream jarFile = null;
+        try {
+            jarFile = new JarInputStream(new FileInputStream(file));
+            while (true) {
+                JarEntry jarEntry = jarFile.getNextJarEntry();
+                if (jarEntry == null) {
+                    break;
+                }
+                if (jarEntry.getName().endsWith(".class")) {
+                    String className = jarEntry.getName().replace('/', '.').replace(".class", "");
+                    int packageNameEnd = className.lastIndexOf('.');
+                    String packageName = packageNameEnd != -1 ? className.substring(0, packageNameEnd) : "";
+                    if (packages.contains(packageName)) {
+                        Class<?> clazz;
+                        try {
+                            clazz = Class.forName(className);
+                            if (type.isAssignableFrom(clazz)) {
+                                cards.add(clazz);
+                            }
+                        } catch (ClassNotFoundException ex) {
+                        }
+                    }
+                }
+            }
+        } catch (IOException ex) {
+        } finally {
+            try {
+                jarFile.close();
+            } catch (IOException ex) {
+            }
+        }
+        return cards;
+    }
+}