Index: AE/installer2/src/net/oni2/aeinstaller/AEInstaller.properties
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/AEInstaller.properties	(revision 647)
+++ AE/installer2/src/net/oni2/aeinstaller/AEInstaller.properties	(revision 648)
@@ -1,2 +1,2 @@
 appname=AE Installer 2
-appversion=0.99d
+appversion=0.99e
Index: AE/installer2/src/net/oni2/aeinstaller/AEInstaller2.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/AEInstaller2.java	(revision 647)
+++ AE/installer2/src/net/oni2/aeinstaller/AEInstaller2.java	(revision 648)
@@ -185,6 +185,12 @@
 		}
 
+		boolean forcedOffline = false;
+		for (String a : args)
+			if (a.equalsIgnoreCase("-offline"))
+				forcedOffline = true;
+
 		boolean offline = !DepotManager.getInstance().checkConnection();
-		if (offline) {
+
+		if (forcedOffline || offline) {
 			JOptionPane.showMessageDialog(null,
 					globalBundle.getString("offlineModeStartup.text"),
@@ -192,5 +198,5 @@
 					JOptionPane.INFORMATION_MESSAGE);
 		}
-		Settings.getInstance().setOfflineMode(offline);
+		Settings.getInstance().setOfflineMode(forcedOffline || offline);
 
 		SwingUtilities.invokeLater(new Runnable() {
Index: AE/installer2/src/net/oni2/aeinstaller/Images.properties
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/Images.properties	(revision 647)
+++ AE/installer2/src/net/oni2/aeinstaller/Images.properties	(revision 648)
@@ -20,3 +20,3 @@
 img.transparent=/net/oni2/aeinstaller/images/transparent.png
 
-img.mandatory=/net/oni2/aeinstaller/images/open_icon_library/edit-delete-6.png
+img.core=/net/oni2/aeinstaller/images/open_icon_library/edit-delete-6.png
Index: AE/installer2/src/net/oni2/aeinstaller/backend/depot/DepotConfig.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/depot/DepotConfig.java	(revision 647)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/depot/DepotConfig.java	(revision 648)
@@ -77,8 +77,8 @@
 
 	/**
-	 * @return First package number that's not a mandatory tool/mod. Everything
-	 *         below is considered mandatory
+	 * @return First package number that's not a core tool/mod. Everything
+	 *         below is considered a core package
 	 */
-	public static int getMandatoryLimit() {
+	public static int getCoreNumberLimit() {
 		return 8000;
 	}
Index: AE/installer2/src/net/oni2/aeinstaller/backend/depot/model/NodeMod.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/depot/model/NodeMod.java	(revision 647)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/depot/model/NodeMod.java	(revision 648)
@@ -7,5 +7,5 @@
 import net.oni2.aeinstaller.backend.depot.DepotConfig;
 import net.oni2.aeinstaller.backend.depot.DepotManager;
-import net.oni2.aeinstaller.backend.mods.ECompatiblePlatform;
+import net.oni2.aeinstaller.backend.packages.ECompatiblePlatform;
 
 import org.json.JSONArray;
Index: AE/installer2/src/net/oni2/aeinstaller/backend/oni/Installer.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/oni/Installer.java	(revision 647)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/oni/Installer.java	(revision 648)
@@ -24,7 +24,7 @@
 import net.oni2.aeinstaller.backend.Settings;
 import net.oni2.aeinstaller.backend.Settings.Platform;
-import net.oni2.aeinstaller.backend.mods.EBSLInstallType;
-import net.oni2.aeinstaller.backend.mods.Mod;
-import net.oni2.aeinstaller.backend.mods.ModManager;
+import net.oni2.aeinstaller.backend.packages.EBSLInstallType;
+import net.oni2.aeinstaller.backend.packages.Package;
+import net.oni2.aeinstaller.backend.packages.PackageManager;
 
 import org.apache.commons.io.FileUtils;
@@ -62,5 +62,5 @@
 	public static Vector<Integer> getInstalledMods() {
 		File installCfg = new File(Paths.getEditionGDF(), "installed_mods.xml");
-		return ModManager.getInstance().loadModSelection(installCfg);
+		return PackageManager.getInstance().loadModSelection(installCfg);
 	}
 
@@ -109,7 +109,7 @@
 	 *            Tools to install
 	 */
-	public static void installTools(TreeSet<Mod> tools) {
+	public static void installTools(TreeSet<Package> tools) {
 		TreeSet<Integer> installed = getInstalledTools();
-		for (Mod m : tools) {
+		for (Package m : tools) {
 			File plain = new File(m.getLocalPath(), "plain");
 			if (plain.exists()) {
@@ -138,7 +138,7 @@
 	 *            Tools to uninstall
 	 */
-	public static void uninstallTools(TreeSet<Mod> tools) {
+	public static void uninstallTools(TreeSet<Package> tools) {
 		TreeSet<Integer> installed = getInstalledTools();
-		for (Mod m : tools) {
+		for (Package m : tools) {
 			if (installed.contains(m.getPackageNumber())) {
 				File plain = new File(m.getLocalPath(), "plain");
@@ -205,5 +205,5 @@
 	 *            Listener for install progress updates
 	 */
-	public static void install(TreeSet<Mod> mods,
+	public static void install(TreeSet<Package> mods,
 			InstallProgressListener listener) {
 		try {
@@ -214,5 +214,5 @@
 
 		File installCfg = new File(Paths.getEditionGDF(), "installed_mods.xml");
-		ModManager.getInstance().saveModSelection(installCfg, mods);
+		PackageManager.getInstance().saveModSelection(installCfg, mods);
 
 		HashSet<Integer> unlockLevels = new HashSet<Integer>();
@@ -221,5 +221,5 @@
 		foldersOni.add(Paths.getVanillaOnisPath());
 
-		for (Mod m : mods) {
+		for (Package m : mods) {
 			for (int lev : m.getUnlockLevels())
 				unlockLevels.add(lev);
@@ -253,13 +253,13 @@
 	}
 
-	private static void combineBSLFolders(TreeSet<Mod> mods,
+	private static void combineBSLFolders(TreeSet<Package> mods,
 			InstallProgressListener listener) {
 		listener.installProgressUpdate(95, 100, "Installing BSL files");
 
-		HashMap<EBSLInstallType, Vector<Mod>> modsToInclude = new HashMap<EBSLInstallType, Vector<Mod>>();
-		modsToInclude.put(EBSLInstallType.NORMAL, new Vector<Mod>());
-		modsToInclude.put(EBSLInstallType.ADDON, new Vector<Mod>());
-
-		for (Mod m : mods.descendingSet()) {
+		HashMap<EBSLInstallType, Vector<Package>> modsToInclude = new HashMap<EBSLInstallType, Vector<Package>>();
+		modsToInclude.put(EBSLInstallType.NORMAL, new Vector<Package>());
+		modsToInclude.put(EBSLInstallType.ADDON, new Vector<Package>());
+
+		for (Package m : mods.descendingSet()) {
 			File bsl = new File(m.getLocalPath(), "bsl");
 			if (bsl.exists()) {
@@ -281,8 +281,8 @@
 		}
 
-		for (Mod m : modsToInclude.get(EBSLInstallType.NORMAL)) {
+		for (Package m : modsToInclude.get(EBSLInstallType.NORMAL)) {
 			copyBSL(m, false);
 		}
-		Vector<Mod> addons = modsToInclude.get(EBSLInstallType.ADDON);
+		Vector<Package> addons = modsToInclude.get(EBSLInstallType.ADDON);
 		for (int i = addons.size() - 1; i >= 0; i--) {
 			copyBSL(addons.get(i), true);
@@ -290,5 +290,5 @@
 	}
 
-	private static void copyBSL(Mod sourceMod, boolean addon) {
+	private static void copyBSL(Package sourceMod, boolean addon) {
 		File targetBaseFolder = new File(Paths.getEditionGDF(), "IGMD");
 		if (!targetBaseFolder.exists())
Index: AE/installer2/src/net/oni2/aeinstaller/backend/packages/EBSLInstallType.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/packages/EBSLInstallType.java	(revision 648)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/packages/EBSLInstallType.java	(revision 648)
@@ -0,0 +1,15 @@
+package net.oni2.aeinstaller.backend.packages;
+
+/**
+ * @author Christian Illy
+ */
+public enum EBSLInstallType {
+	/**
+	 * Normal BSL install mode
+	 */
+	NORMAL,
+	/**
+	 * BSL addon install mode
+	 */
+	ADDON
+}
Index: AE/installer2/src/net/oni2/aeinstaller/backend/packages/ECompatiblePlatform.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/packages/ECompatiblePlatform.java	(revision 648)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/packages/ECompatiblePlatform.java	(revision 648)
@@ -0,0 +1,19 @@
+package net.oni2.aeinstaller.backend.packages;
+
+/**
+ * @author Christian Illy
+ */
+public enum ECompatiblePlatform {
+	/**
+	 * Only for Win
+	 */
+	WIN,
+	/**
+	 * Only for MacOS
+	 */
+	MACOS,
+	/**
+	 * Usable with both platforms
+	 */
+	BOTH
+}
Index: AE/installer2/src/net/oni2/aeinstaller/backend/packages/Mod_Info.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/packages/Mod_Info.java	(revision 648)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/packages/Mod_Info.java	(revision 648)
@@ -0,0 +1,210 @@
+package net.oni2.aeinstaller.backend.packages;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.HashSet;
+
+import net.oni2.aeinstaller.backend.Paths;
+
+/**
+ * @author Christian Illy
+ */
+public class Mod_Info {
+	private double aeVersion = 0;
+	private String name = "";
+	private String creator = "";
+	private EBSLInstallType bslInstallType = EBSLInstallType.NORMAL;
+	private String version = "";
+	private String description = "";
+
+	private HashSet<Integer> incompatibilities = new HashSet<Integer>();
+	private HashSet<Integer> dependencies = new HashSet<Integer>();
+	private HashSet<Integer> unlockLevel = new HashSet<Integer>();
+
+	private File exeFile = null;
+	private File iconFile = null;
+	private String workingDir = "Base";
+
+	/**
+	 * @param f
+	 *            Mod_Info.cfg
+	 * @param packageNumber
+	 *            Package number this Mod_Info belongs to
+	 */
+	public Mod_Info(File f, int packageNumber) {
+		InputStreamReader isr = null;
+		try {
+			FileInputStream fstream = new FileInputStream(f);
+			isr = new InputStreamReader(fstream);
+			BufferedReader br = new BufferedReader(isr);
+			String strLine;
+			while ((strLine = br.readLine()) != null) {
+				if (strLine.indexOf("->") < 1)
+					continue;
+				if (strLine.indexOf("//") >= 0)
+					strLine = strLine.substring(0, strLine.indexOf("//"));
+				String[] split = strLine.split("->", 2);
+				String sName = split[0].trim();
+				String sVal = split[1].trim();
+				if (sName.equalsIgnoreCase("AEInstallVersion")) {
+					aeVersion = Double.parseDouble(sVal);
+				} else if (sName.equalsIgnoreCase("NameOfMod")) {
+					name = sVal;
+				} else if (sName.equalsIgnoreCase("Creator")) {
+					creator = sVal;
+				} else if (sName.equalsIgnoreCase("HasBsl")) {
+					if (sVal.equalsIgnoreCase("addon"))
+						bslInstallType = EBSLInstallType.ADDON;
+				} else if (sName.equalsIgnoreCase("ModVersion")) {
+					version = sVal;
+				} else if (sName.equalsIgnoreCase("Readme")) {
+					description = "<p>" + sVal.replaceAll("\\\\n", "<br>")
+							+ "</p>";
+				} else if (sName.equalsIgnoreCase("DependsOn")) {
+					String[] depsS = sVal.split(",");
+					for (String s : depsS) {
+						try {
+							int dep = Integer.parseInt(s);
+							dependencies.add(dep);
+						} catch (NumberFormatException e) {
+							System.err
+									.format("Mod_Info of %05d does contain a non-number dependency: '%s'\n",
+											packageNumber, s);
+						}
+					}
+				} else if (sName.equalsIgnoreCase("IncompatibleWith")) {
+					String[] confS = sVal.split(",");
+					for (String s : confS) {
+						try {
+							int conf = Integer.parseInt(s);
+							incompatibilities.add(conf);
+						} catch (NumberFormatException e) {
+							System.err
+									.format("Mod_Info of %05d does contain a non-number incompatibility: '%s'\n",
+											packageNumber, s);
+						}
+					}
+				} else if (sName.equalsIgnoreCase("UnlockLevel")) {
+					String[] levelsS = sVal.split(",");
+					for (String s : levelsS) {
+						try {
+							int level = Integer.parseInt(s);
+							unlockLevel.add(level);
+						} catch (NumberFormatException e) {
+							System.err
+									.format("Mod_Info of %05d does contain a non-number UnlockLevel value: '%s'\n",
+											packageNumber, s);
+						}
+					}
+				} else if (sName.equalsIgnoreCase("ExeName")) {
+					exeFile = new File(Paths.getEditionBasePath(), sVal);
+				} else if (sName.equalsIgnoreCase("WorkingDir")) {
+					workingDir = sVal;
+				} else if (sName.equalsIgnoreCase("IconName")) {
+					iconFile = new File(Paths.getEditionBasePath(), sVal);
+				}
+			}
+		} catch (FileNotFoundException e) {
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			if (isr != null) {
+				try {
+					isr.close();
+				} catch (IOException e) {
+					// TODO Auto-generated catch block
+					e.printStackTrace();
+				}
+			}
+		}
+	}
+
+	/**
+	 * @return the aeVersion
+	 */
+	public double getAeVersion() {
+		return aeVersion;
+	}
+
+	/**
+	 * @return the name
+	 */
+	public String getName() {
+		return name;
+	}
+
+	/**
+	 * @return the creator
+	 */
+	public String getCreator() {
+		return creator;
+	}
+
+	/**
+	 * @return the bslInstallType
+	 */
+	public EBSLInstallType getBslInstallType() {
+		return bslInstallType;
+	}
+
+	/**
+	 * @return the version
+	 */
+	public String getVersion() {
+		return version;
+	}
+
+	/**
+	 * @return the description
+	 */
+	public String getDescription() {
+		return description;
+	}
+
+	/**
+	 * @return the incompatibilities
+	 */
+	public HashSet<Integer> getIncompatibilities() {
+		return incompatibilities;
+	}
+
+	/**
+	 * @return the dependencies
+	 */
+	public HashSet<Integer> getDependencies() {
+		return dependencies;
+	}
+
+	/**
+	 * @return the unlockLevel
+	 */
+	public HashSet<Integer> getUnlockLevel() {
+		return unlockLevel;
+	}
+
+	/**
+	 * @return the exeFile
+	 */
+	public File getExeFile() {
+		return exeFile;
+	}
+
+	/**
+	 * @return the iconFile
+	 */
+	public File getIconFile() {
+		return iconFile;
+	}
+
+	/**
+	 * @return the workingDir
+	 */
+	public String getWorkingDir() {
+		return workingDir;
+	}
+
+}
Index: AE/installer2/src/net/oni2/aeinstaller/backend/packages/Package.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/packages/Package.java	(revision 648)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/packages/Package.java	(revision 648)
@@ -0,0 +1,405 @@
+package net.oni2.aeinstaller.backend.packages;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashSet;
+
+import net.oni2.aeinstaller.backend.Paths;
+import net.oni2.aeinstaller.backend.Settings;
+import net.oni2.aeinstaller.backend.Settings.Platform;
+import net.oni2.aeinstaller.backend.depot.DepotConfig;
+import net.oni2.aeinstaller.backend.depot.DepotManager;
+import net.oni2.aeinstaller.backend.depot.model.NodeMod;
+import net.oni2.aeinstaller.backend.depot.model.TaxonomyTerm;
+import net.oni2.aeinstaller.backend.oni.Installer;
+
+/**
+ * @author Christian Illy
+ */
+public class Package implements Comparable<Package> {
+	private String name = "";
+	private int packageNumber = 0;
+
+	private HashSet<Type> types = new HashSet<Type>();
+	private boolean tool = false;
+	private ECompatiblePlatform platform = null;
+	private String version = "";
+	private String submitter = "";
+	private String creator = "";
+	private EBSLInstallType bslInstallType = EBSLInstallType.NORMAL;
+	private String description = "";
+	private double aeVersion = 0;
+	private int zipSize = 0;
+	private NodeMod node = null;
+	private net.oni2.aeinstaller.backend.depot.model.File file = null;
+
+	private File exeFile = null;
+	private File iconFile = null;
+	private String workingDir = "Base";
+
+	private HashSet<Integer> incompatibilities = new HashSet<Integer>();
+	private HashSet<Integer> dependencies = new HashSet<Integer>();
+	private HashSet<Integer> unlockLevel = new HashSet<Integer>();
+
+	private long localTimestamp = 0;
+
+	/**
+	 * Create a new Package entry from a given Mod-Node
+	 * 
+	 * @param nm
+	 *            Mod-Node
+	 */
+	public Package(NodeMod nm) {
+		node = nm;
+		name = nm.getTitle();
+		packageNumber = nm.getPackageNumber();
+		platform = nm.getPlatform();
+		tool = nm.isTool();
+		for (TaxonomyTerm tt : nm.getTypes()) {
+			Type t = PackageManager.getInstance().getTypeByName(tt.getName());
+			types.add(t);
+			if (!tool && !isCorePackage() && isValidOnPlatform())
+				t.addEntry(this);
+		}
+		version = nm.getVersion();
+		submitter = nm.getName();
+		creator = nm.getCreator();
+		if (nm.getBody() != null)
+			description = nm.getBody().getSafe_value();
+		file = DepotManager.getInstance().getFile(
+				nm.getUploads().firstElement().getFid());
+		zipSize = file.getFilesize();
+
+		if (isLocalAvailable())
+			updateLocalData();
+	}
+
+	/**
+	 * Update information for local package existence
+	 */
+	public void updateLocalData() {
+		File config = new File(getLocalPath(), "Mod_Info.cfg");
+		File aeicfg = new File(getLocalPath(), "aei.cfg");
+		File plain = new File(getLocalPath(), "plain");
+		if (config.exists()) {
+			Mod_Info mi = new Mod_Info(config, packageNumber);
+
+			aeVersion = mi.getAeVersion();
+			bslInstallType = mi.getBslInstallType();
+			if (node == null) {
+				name = mi.getName();
+				creator = mi.getCreator();
+				version = mi.getVersion();
+				description = mi.getDescription();
+			}
+
+			dependencies = mi.getDependencies();
+			incompatibilities = mi.getIncompatibilities();
+			unlockLevel = mi.getUnlockLevel();
+
+			exeFile = mi.getExeFile();
+			workingDir = mi.getWorkingDir();
+			iconFile = mi.getIconFile();
+		} else {
+			System.err.println("No config found for mod folder: "
+					+ getLocalPath().getPath());
+		}
+		if (aeicfg.exists()) {
+			try {
+				FileInputStream fstream = new FileInputStream(aeicfg);
+				InputStreamReader isr = new InputStreamReader(fstream);
+				BufferedReader br = new BufferedReader(isr);
+				String strLine;
+				while ((strLine = br.readLine()) != null) {
+					if (strLine.indexOf("->") < 1)
+						continue;
+					if (strLine.indexOf("//") >= 0)
+						strLine = strLine.substring(0, strLine.indexOf("//"));
+					String[] split = strLine.split("->", 2);
+					String sName = split[0].trim();
+					String sVal = split[1].trim();
+					if (sName.equalsIgnoreCase("Timestamp")) {
+						localTimestamp = Long.parseLong(sVal);
+					}
+				}
+				isr.close();
+			} catch (FileNotFoundException e) {
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+		if (node == null)
+			tool = plain.exists();
+	}
+
+	/**
+	 * Create a new Mod entry from the given local mod folder
+	 * 
+	 * @param folder
+	 *            Mod folder with Mod_Info.cfg
+	 */
+	public Package(File folder) {
+		packageNumber = Integer.parseInt(folder.getName().substring(0, 5));
+		updateLocalData();
+
+		platform = ECompatiblePlatform.BOTH;
+	}
+
+	/**
+	 * @return has separate paths for win/mac/common or not
+	 */
+	public boolean hasSeparatePlatformDirs() {
+		return aeVersion >= 2;
+	}
+
+	private String getSanitizedPathName() {
+		return name.replaceAll("[^a-zA-Z0-9_.-]", "_");
+	}
+
+	/**
+	 * @return Path to local mod folder
+	 */
+	public File getLocalPath() {
+		final String folderStart = String.format("%05d", packageNumber);
+
+		if (Paths.getModsPath().exists()) {
+			for (File f : Paths.getModsPath().listFiles(new FilenameFilter() {
+				@Override
+				public boolean accept(File d, String fn) {
+					return fn.startsWith(folderStart);
+				}
+			})) {
+				return f;
+			}
+		}
+
+		return new File(Paths.getModsPath(), folderStart
+				+ getSanitizedPathName());
+	}
+
+	/**
+	 * @return Is there a newer version on the depot?
+	 */
+	public boolean isNewerAvailable() {
+		if (file != null)
+			return file.getTimestamp() > localTimestamp;
+		else
+			return false;
+	}
+
+	/**
+	 * @return Mod exists within mods folder
+	 */
+	public boolean isLocalAvailable() {
+		return getLocalPath().exists();
+	}
+
+	/**
+	 * @return Is mod installed?
+	 */
+	public boolean isInstalled() {
+		if (tool)
+			return Installer.getInstalledTools().contains(packageNumber);
+		else
+			return PackageManager.getInstance().isModInstalled(this);
+	}
+
+	/**
+	 * @return Name of mod
+	 */
+	public String getName() {
+		return name;
+	}
+
+	/**
+	 * @return the package number
+	 */
+	public int getPackageNumber() {
+		return packageNumber;
+	}
+
+	/**
+	 * @return the package number as 5 digit string
+	 */
+	public String getPackageNumberString() {
+		return String.format("%05d", packageNumber);
+	}
+
+	/**
+	 * @return Types of mod
+	 */
+	public HashSet<Type> getTypes() {
+		return types;
+	}
+
+	/**
+	 * @return Is this mod actually a tool?
+	 */
+	public boolean isTool() {
+		return tool;
+	}
+
+	/**
+	 * @return Compatible platforms
+	 */
+	public ECompatiblePlatform getPlatform() {
+		return platform;
+	}
+
+	/**
+	 * @return Version of mod
+	 */
+	public String getVersion() {
+		return version;
+	}
+
+	/**
+	 * @return Submitter of mod
+	 */
+	public String getSubmitter() {
+		return submitter;
+	}
+
+	/**
+	 * @return Creator of mod
+	 */
+	public String getCreator() {
+		return creator;
+	}
+
+	/**
+	 * @return Installation type of BSL files
+	 */
+	public EBSLInstallType getBSLInstallType() {
+		return bslInstallType;
+	}
+
+	/**
+	 * @return Description of mod
+	 */
+	public String getDescription() {
+		return description;
+	}
+
+	/**
+	 * @return Size of Zip file on Depot
+	 */
+	public int getZipSize() {
+		return zipSize;
+	}
+
+	/**
+	 * @return Is a package that is always installed?
+	 */
+	public boolean isCorePackage() {
+		return packageNumber < DepotConfig.getCoreNumberLimit();
+	}
+
+	/**
+	 * @return Get the depot file entry
+	 */
+	public net.oni2.aeinstaller.backend.depot.model.File getFile() {
+		return file;
+	}
+
+	/**
+	 * @return Depot page URI
+	 */
+	public URI getUrl() {
+		if (node == null)
+			return null;
+		if (node.getPath() == null)
+			return null;
+		URI res = null;
+		try {
+			res = new URI(node.getPath());
+		} catch (URISyntaxException e) {
+			e.printStackTrace();
+		}
+		return res;
+	}
+
+	@Override
+	public String toString() {
+		return name;
+	}
+
+	/**
+	 * @return the incompabitilities
+	 */
+	public HashSet<Integer> getIncompabitilities() {
+		return incompatibilities;
+	}
+
+	/**
+	 * @return the dependencies
+	 */
+	public HashSet<Integer> getDependencies() {
+		return dependencies;
+	}
+
+	/**
+	 * @return the levels this mod will unlock
+	 */
+	public HashSet<Integer> getUnlockLevels() {
+		return unlockLevel;
+	}
+
+	/**
+	 * @return Executable name of this tool
+	 */
+	public File getExeFile() {
+		return exeFile;
+	}
+
+	/**
+	 * @return Icon file of this tool
+	 */
+	public File getIconFile() {
+		return iconFile;
+	}
+
+	/**
+	 * @return Working directory of this tool
+	 */
+	public File getWorkingDir() {
+		if (workingDir.equalsIgnoreCase("Exe")) {
+			if (exeFile != null)
+				return exeFile.getParentFile();
+			else
+				return Paths.getEditionGDF();
+		} else if (workingDir.equalsIgnoreCase("GDF"))
+			return Paths.getEditionGDF();
+		else
+			return Paths.getEditionBasePath();
+	}
+
+	/**
+	 * @return Is this mod valid on the running platform?
+	 */
+	public boolean isValidOnPlatform() {
+		switch (platform) {
+			case BOTH:
+				return true;
+			case MACOS:
+				return (Settings.getPlatform() == Platform.MACOS);
+			case WIN:
+				return (Settings.getPlatform() == Platform.WIN)
+						|| (Settings.getPlatform() == Platform.LINUX);
+		}
+		return false;
+	}
+
+	@Override
+	public int compareTo(Package o) {
+		return getPackageNumber() - o.getPackageNumber();
+	}
+
+}
Index: AE/installer2/src/net/oni2/aeinstaller/backend/packages/PackageManager.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/packages/PackageManager.java	(revision 648)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/packages/PackageManager.java	(revision 648)
@@ -0,0 +1,345 @@
+package net.oni2.aeinstaller.backend.packages;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.Vector;
+
+import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.io.xml.StaxDriver;
+
+import net.oni2.aeinstaller.backend.Paths;
+import net.oni2.aeinstaller.backend.depot.DepotManager;
+import net.oni2.aeinstaller.backend.depot.model.NodeMod;
+import net.oni2.aeinstaller.backend.depot.model.TaxonomyTerm;
+import net.oni2.aeinstaller.backend.oni.Installer;
+
+/**
+ * @author Christian Illy
+ */
+public class PackageManager {
+	private static PackageManager instance = new PackageManager();
+
+	private HashMap<String, Type> types = new HashMap<String, Type>();
+	private HashMap<Integer, Package> mods = new HashMap<Integer, Package>();
+	private HashMap<Integer, Package> tools = new HashMap<Integer, Package>();
+
+	private Vector<Integer> currentlyInstalled = new Vector<Integer>();
+
+	/**
+	 * @param f
+	 *            Mod selection file
+	 * @return Mod selection
+	 */
+	@SuppressWarnings("unchecked")
+	public Vector<Integer> loadModSelection(File f) {
+		Vector<Integer> res = new Vector<Integer>();
+		try {
+			if (f.exists()) {
+				FileInputStream fis = new FileInputStream(f);
+				XStream xs = new XStream(new StaxDriver());
+				Object obj = xs.fromXML(fis);
+				if (obj instanceof Vector<?>)
+					res = (Vector<Integer>) obj;
+				fis.close();
+			}
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return res;
+	}
+
+	/**
+	 * @param f
+	 *            Mod selection file
+	 * @param mods
+	 *            Selected mods
+	 */
+	public void saveModSelection(File f, TreeSet<Package> mods) {
+		try {
+			Vector<Integer> installed = new Vector<Integer>();
+			for (Package m : mods) {
+				installed.add(m.getPackageNumber());
+			}
+			FileOutputStream fos = new FileOutputStream(f);
+			XStream xs = new XStream(new StaxDriver());
+			xs.toXML(installed, fos);
+			fos.close();
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * First initialization of ModManager
+	 */
+	public void init() {
+		types = new HashMap<String, Type>();
+		mods = new HashMap<Integer, Package>();
+
+		Type localType = new Type("-Local-", null);
+		types.put("-Local-", localType);
+
+		for (TaxonomyTerm tt : DepotManager.getInstance().getTypes()) {
+			types.put(tt.getName(), new Type(tt.getName(), tt));
+		}
+
+		HashMap<Integer, Package> modFolders = new HashMap<Integer, Package>();
+		if (Paths.getModsPath().exists()) {
+			for (File f : Paths.getModsPath().listFiles(new FileFilter() {
+				@Override
+				public boolean accept(File pathname) {
+					return pathname.isDirectory();
+				}
+			})) {
+				Package m = new Package(f);
+				modFolders.put(m.getPackageNumber(), m);
+			}
+		}
+
+		for (NodeMod nm : DepotManager.getInstance().getModPackageNodes()) {
+			if (nm.getUploads().size() == 1 && nm.getStatus() == 1) {
+				Package m = new Package(nm);
+				if (nm.isTool())
+					tools.put(m.getPackageNumber(), m);
+				else
+					mods.put(m.getPackageNumber(), m);
+				modFolders.remove(m.getPackageNumber());
+			}
+		}
+
+		for (Package m : modFolders.values()) {
+			if (!m.isCorePackage()) {
+				localType.addEntry(m);
+				m.getTypes().add(localType);
+			}
+			if (m.isTool())
+				tools.put(m.getPackageNumber(), m);
+			else
+				mods.put(m.getPackageNumber(), m);
+		}
+
+		updateInstalledMods();
+	}
+
+	/**
+	 * Update the list of currently installed mods
+	 */
+	public void updateInstalledMods() {
+		currentlyInstalled = Installer.getInstalledMods();
+		if (currentlyInstalled == null)
+			currentlyInstalled = new Vector<Integer>();
+	}
+
+	/**
+	 * @return Singleton instance
+	 */
+	public static PackageManager getInstance() {
+		return instance;
+	}
+
+	Type getTypeByName(String name) {
+		return types.get(name);
+	}
+
+	/**
+	 * @return Collection of types which do have mods associated
+	 */
+	public Collection<Type> getTypesWithContent() {
+		Vector<Type> res = new Vector<Type>();
+		for (Type t : types.values()) {
+			if (t.getEntries().size() > 0)
+				res.add(t);
+		}
+		return res;
+	}
+
+	/**
+	 * @return Collection of mods valid on this platform and not core package
+	 */
+	public Collection<Package> getModsValidAndNotCore() {
+		Vector<Package> res = new Vector<Package>();
+		for (Package m : mods.values())
+			if (m.isValidOnPlatform() && !m.isCorePackage())
+				res.add(m);
+		return res;
+	}
+
+	/**
+	 * @return Mods which are always installed and valid on this platform
+	 */
+	public TreeSet<Package> getCoreMods() {
+		TreeSet<Package> res = new TreeSet<Package>();
+		for (Package m : mods.values()) {
+			if (m.isValidOnPlatform() && m.isCorePackage())
+				res.add(m);
+		}
+		return res;
+	}
+
+	/**
+	 * @return Mods which are already locally available
+	 */
+	public TreeSet<Package> getLocalAvailableMods() {
+		TreeSet<Package> res = new TreeSet<Package>();
+		for (Package m : mods.values()) {
+			if (m.isLocalAvailable())
+				res.add(m);
+		}
+		return res;
+	}
+
+	/**
+	 * @return Mods which can be updated
+	 */
+	public TreeSet<Package> getUpdatableMods() {
+		TreeSet<Package> res = new TreeSet<Package>();
+		for (Package m : getLocalAvailableMods()) {
+			if (m.isNewerAvailable())
+				res.add(m);
+		}
+		return res;
+	}
+
+	/**
+	 * @return Collection of tools valid on this platform and not core
+	 */
+	public TreeMap<String, Package> getTools() {
+		TreeMap<String, Package> res = new TreeMap<String, Package>();
+		for (Package m : tools.values())
+			if (m.isValidOnPlatform() && !m.isCorePackage())
+				res.put(m.getName(), m);
+		return res;
+	}
+
+	/**
+	 * @return Tools which are always installed and valid on this platform
+	 */
+	public TreeSet<Package> getCoreTools() {
+		TreeSet<Package> res = new TreeSet<Package>();
+		for (Package m : tools.values()) {
+			if (m.isValidOnPlatform() && m.isCorePackage())
+				res.add(m);
+		}
+		return res;
+	}
+
+	/**
+	 * @return Tools which are already locally available
+	 */
+	public TreeSet<Package> getLocalAvailableTools() {
+		TreeSet<Package> res = new TreeSet<Package>();
+		for (Package m : tools.values()) {
+			if (m.isLocalAvailable())
+				res.add(m);
+		}
+		return res;
+	}
+
+	/**
+	 * @return Tools which can be updated
+	 */
+	public TreeSet<Package> getUpdatableTools() {
+		TreeSet<Package> res = new TreeSet<Package>();
+		for (Package m : getLocalAvailableTools()) {
+			if (m.isNewerAvailable())
+				res.add(m);
+		}
+		return res;
+	}
+
+	/**
+	 * @return Currently installed tools
+	 */
+	public TreeSet<Package> getInstalledTools() {
+		TreeSet<Package> res = new TreeSet<Package>();
+		for (int n : Installer.getInstalledTools()) {
+			res.add(getPackageByNumber(n));
+		}
+		return res;
+	}
+
+	/**
+	 * Get a mod/tool by its package number
+	 * 
+	 * @param number
+	 *            Package number
+	 * @return Mod/tool or null
+	 */
+	private Package getPackageByNumber(int number) {
+		if (mods.containsKey(number))
+			return mods.get(number);
+		if (tools.containsKey(number))
+			return tools.get(number);
+		return null;
+	}
+
+	/**
+	 * Check for unresolved dependencies within the given mods
+	 * 
+	 * @param mods
+	 *            Mods to check
+	 * @return Unmet dependencies
+	 */
+	public HashMap<Package, HashSet<Package>> checkDependencies(TreeSet<Package> mods) {
+		HashMap<Package, HashSet<Package>> res = new HashMap<Package, HashSet<Package>>();
+
+		for (Package m : mods) {
+			for (int depNum : m.getDependencies()) {
+				Package other = getPackageByNumber(depNum);
+				if (!mods.contains(other)) {
+					if (!res.containsKey(m))
+						res.put(m, new HashSet<Package>());
+					res.get(m).add(other);
+				}
+			}
+		}
+
+		return res;
+	}
+
+	/**
+	 * Check for incompabitilites between given mods
+	 * 
+	 * @param mods
+	 *            Mods to check
+	 * @return Incompatible mods
+	 */
+	public HashMap<Package, HashSet<Package>> checkIncompabitilites(TreeSet<Package> mods) {
+		HashMap<Package, HashSet<Package>> res = new HashMap<Package, HashSet<Package>>();
+
+		for (Package m : mods) {
+			for (int confNum : m.getIncompabitilities()) {
+				Package other = getPackageByNumber(confNum);
+				if (mods.contains(other)) {
+					if (!res.containsKey(m))
+						res.put(m, new HashSet<Package>());
+					res.get(m).add(other);
+				}
+			}
+		}
+
+		return res;
+	}
+
+	/**
+	 * @param m
+	 *            Mod to check
+	 * @return Is mod installed?
+	 */
+	boolean isModInstalled(Package m) {
+		return currentlyInstalled.contains(m.getPackageNumber());
+	}
+}
Index: AE/installer2/src/net/oni2/aeinstaller/backend/packages/Type.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/packages/Type.java	(revision 648)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/packages/Type.java	(revision 648)
@@ -0,0 +1,52 @@
+package net.oni2.aeinstaller.backend.packages;
+
+import java.util.HashSet;
+
+import net.oni2.aeinstaller.backend.depot.model.TaxonomyTerm;
+
+/**
+ * @author Christian Illy
+ */
+public class Type {
+	private String name;
+	@SuppressWarnings("unused")
+	private TaxonomyTerm depotTerm;
+
+	private HashSet<Package> entries = new HashSet<Package>();
+
+	/**
+	 * Create a new local type declaration
+	 * 
+	 * @param name
+	 *            Name of type
+	 * @param tt
+	 *            Optional TaxTerm link
+	 */
+	public Type(String name, TaxonomyTerm tt) {
+		this.name = name;
+		this.depotTerm = tt;
+	}
+
+	void addEntry(Package m) {
+		entries.add(m);
+	}
+
+	/**
+	 * @return Name of type
+	 */
+	public String getName() {
+		return name;
+	}
+
+	/**
+	 * @return Entries for type
+	 */
+	public HashSet<Package> getEntries() {
+		return entries;
+	}
+
+	@Override
+	public String toString() {
+		return String.format("%s (%d)", name, entries.size());
+	}
+}
Index: AE/installer2/src/net/oni2/aeinstaller/backend/packages/download/ModDownload.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/packages/download/ModDownload.java	(revision 648)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/packages/download/ModDownload.java	(revision 648)
@@ -0,0 +1,186 @@
+package net.oni2.aeinstaller.backend.packages.download;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import net.oni2.aeinstaller.backend.Paths;
+import net.oni2.aeinstaller.backend.packages.Package;
+import net.oni2.aeinstaller.backend.network.FileDownloadListener;
+import net.oni2.aeinstaller.backend.network.FileDownloader;
+import net.oni2.aeinstaller.backend.network.FileDownloader.EState;
+import net.oni2.aeinstaller.backend.packages.unpack.UnpackListener;
+import net.oni2.aeinstaller.backend.packages.unpack.Unpacker;
+
+/**
+ * @author Christian Illy
+ */
+public class ModDownload implements FileDownloadListener, UnpackListener {
+	/**
+	 * @author Christian Illy
+	 */
+	public enum ModDownloadState {
+		/**
+		 * Downloader initialized but not started
+		 */
+		INIT,
+		/**
+		 * Download running
+		 */
+		RUNNING,
+		/**
+		 * Aborted because of an error
+		 */
+		ERROR,
+		/**
+		 * Download interrupted
+		 */
+		INTERRUPTED,
+		/**
+		 * Download finished successfully
+		 */
+		DOWNLOADED,
+		/**
+		 * Package unzipped successfully
+		 */
+		UNPACKED
+	};
+
+	private Package mod;
+	private FileDownloader downloader;
+	private Unpacker unpacker;
+	private File zipFile;
+	private File targetFolder;
+	private ModDownloadListener listener;
+	private int size;
+
+	private ModDownloadState state = ModDownloadState.INIT;
+
+	/**
+	 * Create a mod download
+	 * 
+	 * @param mod
+	 *            Mod to download
+	 * @param listener
+	 *            Listener for progress
+	 */
+	public ModDownload(Package mod, ModDownloadListener listener) {
+		this.mod = mod;
+		this.listener = listener;
+
+		zipFile = new File(Paths.getDownloadPath(),
+				mod.getPackageNumberString() + ".zip");
+		targetFolder = mod.getLocalPath();
+		try {
+			downloader = new FileDownloader(mod.getFile().getUri_full(),
+					zipFile);
+			downloader.addListener(this);
+			unpacker = new Unpacker(zipFile, targetFolder, this);
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * @return Size of this download
+	 */
+	public int getSize() {
+		return mod.getZipSize();
+	}
+
+	/**
+	 * Start this download
+	 */
+	public void start() {
+		state = ModDownloadState.RUNNING;
+		downloader.start();
+	}
+
+	/**
+	 * Abort this download
+	 */
+	public void abort() {
+		switch (state) {
+			case UNPACKED:
+			case INIT:
+			case ERROR:
+			case INTERRUPTED:
+				break;
+			case RUNNING:
+				downloader.stop();
+				break;
+			case DOWNLOADED:
+				unpacker.stop();
+				break;
+		}
+		state = ModDownloadState.INTERRUPTED;
+	}
+
+	/**
+	 * @return the mod object handled by this download
+	 */
+	public Package getMod() {
+		return mod;
+	}
+
+	private void writeTimestamp() {
+		File logFile = new File(targetFolder, "aei.cfg");
+		PrintWriter log = null;
+		try {
+			log = new PrintWriter(logFile);
+			log.println("Timestamp -> " + mod.getFile().getTimestamp());
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		}
+		if (log != null)
+			log.close();
+	}
+
+	@Override
+	public void statusUpdate(FileDownloader source, EState state, int done,
+			int total) {
+		switch (state) {
+			case INIT:
+				break;
+			case RUNNING:
+				listener.modDownloadStatusUpdate(this, this.state, done, total);
+				break;
+			case PAUSED:
+				break;
+			case INTERRUPTED:
+				break;
+			case ERROR:
+				this.state = ModDownloadState.ERROR;
+				listener.modDownloadStatusUpdate(this, this.state, done, total);
+				break;
+			case FINISHED:
+				this.state = ModDownloadState.DOWNLOADED;
+				listener.modDownloadStatusUpdate(this, this.state, done, total);
+				this.size = done;
+				unpacker.start();
+				break;
+		}
+	}
+
+	@Override
+	public void statusUpdate(Unpacker source,
+			net.oni2.aeinstaller.backend.packages.unpack.Unpacker.EState state) {
+		switch (state) {
+			case INIT:
+				break;
+			case RUNNING:
+				break;
+			case INTERRUPTED:
+				this.state = ModDownloadState.INTERRUPTED;
+				listener.modDownloadStatusUpdate(this, this.state, size, size);
+				break;
+			case FINISHED:
+				this.state = ModDownloadState.UNPACKED;
+				writeTimestamp();
+				zipFile.delete();
+				listener.modDownloadStatusUpdate(this, this.state, size, size);
+				break;
+		}
+	}
+}
Index: AE/installer2/src/net/oni2/aeinstaller/backend/packages/download/ModDownloadListener.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/packages/download/ModDownloadListener.java	(revision 648)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/packages/download/ModDownloadListener.java	(revision 648)
@@ -0,0 +1,23 @@
+package net.oni2.aeinstaller.backend.packages.download;
+
+import net.oni2.aeinstaller.backend.packages.download.ModDownload.ModDownloadState;
+
+/**
+ * @author Christian Illy
+ */
+public interface ModDownloadListener {
+	/**
+	 * Called for progress changes within the mod download/unpack process
+	 * 
+	 * @param source
+	 *            Source of event
+	 * @param state
+	 *            Current state
+	 * @param done
+	 *            Bytes downloaded
+	 * @param total
+	 *            Bytes total
+	 */
+	public void modDownloadStatusUpdate(ModDownload source,
+			ModDownloadState state, int done, int total);
+}
Index: AE/installer2/src/net/oni2/aeinstaller/backend/packages/download/ModDownloader.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/packages/download/ModDownloader.java	(revision 648)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/packages/download/ModDownloader.java	(revision 648)
@@ -0,0 +1,172 @@
+package net.oni2.aeinstaller.backend.packages.download;
+
+import java.util.Date;
+import java.util.TreeSet;
+import java.util.Vector;
+
+import net.oni2.aeinstaller.backend.packages.Package;
+import net.oni2.aeinstaller.backend.packages.download.ModDownload.ModDownloadState;
+
+/**
+ * @author Christian Illy
+ */
+public class ModDownloader implements ModDownloadListener {
+	/**
+	 * @author Christian Illy
+	 */
+	public enum State {
+		/**
+		 * Downloads running
+		 */
+		RUNNING,
+		/**
+		 * Aborted because of an error
+		 */
+		ERROR,
+		/**
+		 * Downloads interrupted
+		 */
+		INTERRUPTED,
+		/**
+		 * When the last file was downloaded and only unpacking is left
+		 */
+		LAST_FILE_DOWNLOADED,
+		/**
+		 * Everything completed
+		 */
+		FINISHED
+	};
+
+	private int currentDownload = -1;
+	private int unpacked = 0;
+	private Vector<ModDownload> downloads = new Vector<ModDownload>();
+	private int totalSize = 0;
+	private int downloadedComplete = 0;
+	private int downloadedCurrent = 0;
+	private long startMS;
+	private State state = State.RUNNING;
+	private ModDownloaderListener listener;
+
+	/**
+	 * Create a mods-download-process
+	 * 
+	 * @param mods
+	 *            Mods to download
+	 * @param listener
+	 *            Listener for status updates
+	 */
+	public ModDownloader(TreeSet<Package> mods, ModDownloaderListener listener) {
+		this.listener = listener;
+		for (Package m : mods) {
+			downloads.add(new ModDownload(m, this));
+			totalSize += m.getZipSize();
+		}
+		startMS = new Date().getTime();
+		startNextDownload();
+	}
+
+	private void startNextDownload() {
+		if (currentDownload >= 0)
+			downloadedComplete += downloads.get(currentDownload).getSize();
+		currentDownload++;
+		downloadedCurrent = 0;
+		if ((state == State.RUNNING) && (currentDownload < downloads.size())) {
+			downloads.get(currentDownload).start();
+		} else if (state == State.RUNNING) {
+			state = State.LAST_FILE_DOWNLOADED;
+			notifyListener();
+		} else {
+			notifyListener();
+		}
+	}
+
+	private int getTimeElapsed() {
+		int total = (int) (new Date().getTime() - startMS) / 1000;
+		return total;
+	}
+
+	private int getDownloadSpeed() {
+		int elap = getTimeElapsed();
+		int down = downloadedComplete + downloadedCurrent;
+		if (elap > 0)
+			return down / elap;
+		else
+			return 1;
+	}
+
+	private int getTimeRemaining() {
+		int remainingSize = totalSize
+				- (downloadedComplete + downloadedCurrent);
+		return remainingSize / getDownloadSpeed();
+	}
+
+	private void notifyListener() {
+		if (currentDownload < downloads.size()) {
+			listener.updateStatus(this,
+					downloads.get(currentDownload).getMod(), state, unpacked,
+					downloads.size(), downloadedComplete + downloadedCurrent,
+					totalSize, getTimeElapsed(), getTimeRemaining(),
+					getDownloadSpeed());
+		} else {
+			listener.updateStatus(this, null, state, unpacked,
+					downloads.size(), downloadedComplete + downloadedCurrent,
+					totalSize, getTimeElapsed(), getTimeRemaining(),
+					getDownloadSpeed());
+		}
+	}
+
+	/**
+	 * @return total download size
+	 */
+	public int getTotalSize() {
+		return totalSize;
+	}
+
+	/**
+	 * @return Is this process finished
+	 */
+	public boolean isFinished() {
+		return state == State.FINISHED;
+	}
+
+	@Override
+	public void modDownloadStatusUpdate(ModDownload source,
+			ModDownloadState state, int done, int total) {
+		switch (state) {
+			case RUNNING:
+				downloadedCurrent = done;
+				notifyListener();
+				break;
+			case ERROR:
+				this.state = State.ERROR;
+				break;
+			case DOWNLOADED:
+				if (source == downloads.get(currentDownload))
+					startNextDownload();
+				break;
+			case UNPACKED:
+				source.getMod().updateLocalData();
+				unpacked++;
+				if (unpacked >= downloads.size())
+					this.state = State.FINISHED;
+				notifyListener();
+				break;
+			case INIT:
+				break;
+			case INTERRUPTED:
+				break;
+		}
+	}
+
+	/**
+	 * Abort download process
+	 */
+	public void abort() {
+		if (currentDownload < downloads.size()) {
+			state = State.INTERRUPTED;
+			ModDownload md = downloads.get(currentDownload);
+			md.abort();
+		}
+	}
+
+}
Index: AE/installer2/src/net/oni2/aeinstaller/backend/packages/download/ModDownloaderListener.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/packages/download/ModDownloaderListener.java	(revision 648)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/packages/download/ModDownloaderListener.java	(revision 648)
@@ -0,0 +1,37 @@
+package net.oni2.aeinstaller.backend.packages.download;
+
+import net.oni2.aeinstaller.backend.packages.Package;
+import net.oni2.aeinstaller.backend.packages.download.ModDownloader.State;
+
+/**
+ * @author Christian Illy
+ */
+public interface ModDownloaderListener {
+	/**
+	 * Callback for progress updates on mod downloads
+	 * 
+	 * @param source
+	 *            Event source
+	 * @param currentDownload
+	 *            Currently downloading mod
+	 * @param state
+	 *            Current state
+	 * @param filesDown
+	 *            Downloaded(+unpacked) files
+	 * @param filesTotal
+	 *            Files in total to handle
+	 * @param bytesDown
+	 *            Bytes downloaded
+	 * @param bytesTotal
+	 *            Bytes in total to handle
+	 * @param duration
+	 *            Duration of downloads in seconds
+	 * @param remaining
+	 *            Remaining time in seconds
+	 * @param speed
+	 *            Average download speed in B/s
+	 */
+	public void updateStatus(ModDownloader source, Package currentDownload,
+			State state, int filesDown, int filesTotal, int bytesDown,
+			int bytesTotal, int duration, int remaining, int speed);
+}
Index: AE/installer2/src/net/oni2/aeinstaller/backend/packages/unpack/UnpackListener.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/packages/unpack/UnpackListener.java	(revision 648)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/packages/unpack/UnpackListener.java	(revision 648)
@@ -0,0 +1,18 @@
+package net.oni2.aeinstaller.backend.packages.unpack;
+
+import net.oni2.aeinstaller.backend.packages.unpack.Unpacker.EState;
+
+/**
+ * @author Christian Illy
+ */
+public interface UnpackListener {
+	/**
+	 * Called for progress changes within the unpacker
+	 * 
+	 * @param source
+	 *            Source of event
+	 * @param state
+	 *            Current state
+	 */
+	public void statusUpdate(Unpacker source, EState state);
+}
Index: AE/installer2/src/net/oni2/aeinstaller/backend/packages/unpack/Unpacker.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/packages/unpack/Unpacker.java	(revision 648)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/packages/unpack/Unpacker.java	(revision 648)
@@ -0,0 +1,189 @@
+package net.oni2.aeinstaller.backend.packages.unpack;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+
+import org.apache.commons.io.FileUtils;
+
+/**
+ * @author Christian Illy
+ */
+public class Unpacker implements Runnable {
+	/**
+	 * @author Christian Illy
+	 */
+	public enum EState {
+		/**
+		 * Unpacker initialized but not started
+		 */
+		INIT,
+		/**
+		 * Unpack running
+		 */
+		RUNNING,
+		/**
+		 * Unpack interrupted
+		 */
+		INTERRUPTED,
+		/**
+		 * Unpack finished successfully
+		 */
+		FINISHED,
+	};
+
+	private UnpackListener listener;
+
+	private File zip;
+	private File target;
+
+	private Thread t = null;
+
+	private EState state = EState.INIT;
+
+	/**
+	 * Initialize a new AE package unpacker
+	 * 
+	 * @param zipFile
+	 *            AE zip package
+	 * @param targetFolder
+	 *            Target folder
+	 * @param listener
+	 *            Listener for progress updates
+	 */
+	public Unpacker(File zipFile, File targetFolder, UnpackListener listener) {
+		this.listener = listener;
+		zip = zipFile;
+		target = targetFolder;
+	}
+
+	/**
+	 * Start the unpack process
+	 */
+	public synchronized void start() {
+		if (t == null) {
+			t = new Thread(this);
+			t.start();
+			state = EState.RUNNING;
+			updateStatus();
+		}
+	}
+
+	/**
+	 * Stop (abort) the process
+	 */
+	public synchronized void stop() {
+		if (state != EState.FINISHED) {
+			state = EState.INTERRUPTED;
+			if (t != null) {
+				try {
+					t.join();
+				} catch (InterruptedException e) {
+					e.printStackTrace();
+				}
+				t = null;
+			}
+			updateStatus();
+			if (state != EState.FINISHED) {
+				if (target.exists()) {
+					try {
+						FileUtils.deleteDirectory(target);
+					} catch (IOException e) {
+						e.printStackTrace();
+					}
+				}
+			}
+		}
+	}
+
+	private synchronized void updateStatus() {
+		listener.statusUpdate(this, state);
+	}
+
+	@Override
+	public void run() {
+		try {
+			switch (state) {
+				case INTERRUPTED:
+					return;
+				case RUNNING:
+					ZipFile zf = null;
+					try {
+						int pathStart = 0;
+						String pathStartName = "";
+
+						zf = new ZipFile(zip);
+						
+						if (target.exists())
+							FileUtils.deleteDirectory(target);
+						target.mkdirs();
+
+						for (Enumeration<? extends ZipEntry> e = zf.entries(); e
+								.hasMoreElements();) {
+							ZipEntry ze = e.nextElement();
+							if (ze.getName().toLowerCase()
+									.endsWith("/mod_info.cfg")
+									|| ze.getName().toLowerCase()
+											.equals("mod_info.cfg")) {
+								pathStart = ze.getName().toLowerCase()
+										.indexOf("mod_info.cfg");
+								pathStartName = ze.getName().substring(0,
+										pathStart);
+							}
+						}
+
+						for (Enumeration<? extends ZipEntry> e = zf.entries(); e
+								.hasMoreElements();) {
+							if (state == EState.INTERRUPTED)
+								return;
+							ZipEntry ze = e.nextElement();
+							if (!ze.isDirectory()) {
+								if (ze.getName().startsWith(pathStartName)) {
+									File targetFile = new File(target, ze
+											.getName().substring(pathStart));
+									File parent = targetFile.getParentFile();
+									parent.mkdirs();
+
+									InputStream in = zf.getInputStream(ze);
+
+									int read = 0;
+									byte[] data = new byte[1024];
+									FileOutputStream fileOut = new FileOutputStream(
+											targetFile);
+									while ((read = in.read(data, 0, 1024)) != -1) {
+										fileOut.write(data, 0, read);
+									}
+									fileOut.close();
+								}
+							}
+						}
+					} catch (ZipException e) {
+						// TODO Auto-generated catch block
+						e.printStackTrace();
+					} catch (IOException e) {
+						// TODO Auto-generated catch block
+						e.printStackTrace();
+					} finally {
+						try {
+							if (zf != null)
+								zf.close();
+						} catch (IOException e) {
+							e.printStackTrace();
+						}
+					}
+					break;
+				default:
+					break;
+			}
+		} finally {
+		}
+
+		state = EState.FINISHED;
+		updateStatus();
+	}
+}
Index: AE/installer2/src/net/oni2/aeinstaller/gui/MainWin.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/gui/MainWin.java	(revision 647)
+++ AE/installer2/src/net/oni2/aeinstaller/gui/MainWin.java	(revision 648)
@@ -44,16 +44,16 @@
 import net.oni2.aeinstaller.backend.SizeFormatter;
 import net.oni2.aeinstaller.backend.depot.DepotManager;
-import net.oni2.aeinstaller.backend.mods.Mod;
-import net.oni2.aeinstaller.backend.mods.ModManager;
-import net.oni2.aeinstaller.backend.mods.Type;
-import net.oni2.aeinstaller.backend.mods.download.ModDownloader;
-import net.oni2.aeinstaller.backend.mods.download.ModDownloader.State;
-import net.oni2.aeinstaller.backend.mods.download.ModDownloaderListener;
+import net.oni2.aeinstaller.backend.packages.Package;
+import net.oni2.aeinstaller.backend.packages.PackageManager;
+import net.oni2.aeinstaller.backend.packages.Type;
 import net.oni2.aeinstaller.backend.oni.InstallProgressListener;
 import net.oni2.aeinstaller.backend.oni.Installer;
 import net.oni2.aeinstaller.backend.oni.OniSplit;
+import net.oni2.aeinstaller.backend.packages.download.ModDownloader;
+import net.oni2.aeinstaller.backend.packages.download.ModDownloaderListener;
+import net.oni2.aeinstaller.backend.packages.download.ModDownloader.State;
 import net.oni2.aeinstaller.gui.about.AboutDialog;
+import net.oni2.aeinstaller.gui.corepackages.CorePackagesDialog;
 import net.oni2.aeinstaller.gui.downloadwindow.Downloader;
-import net.oni2.aeinstaller.gui.mandatorypackages.MandatoryPackagesDialog;
 import net.oni2.aeinstaller.gui.modtable.DownloadSizeListener;
 import net.oni2.aeinstaller.gui.modtable.ModSelectionListener;
@@ -105,6 +105,6 @@
 	private JButton btnInstall;
 
-	private TreeSet<Mod> execMandatoryUpdates = new TreeSet<Mod>();
-	private TreeSet<Mod> execUpdates = null;
+	private TreeSet<Package> execCoreUpdates = new TreeSet<Package>();
+	private TreeSet<Package> execUpdates = null;
 
 	private enum EInstallState {
@@ -118,6 +118,6 @@
 
 	private EInstallState installState = EInstallState.DONE;
-	private TreeSet<Mod> installMods = null;
-	private TreeSet<Mod> installDeps = null;
+	private TreeSet<Package> installMods = null;
+	private TreeSet<Package> installDeps = null;
 
 	/**
@@ -153,5 +153,5 @@
 
 		TreeMap<String, Type> types = new TreeMap<String, Type>();
-		for (Type t : ModManager.getInstance().getTypesWithContent()) {
+		for (Type t : PackageManager.getInstance().getTypesWithContent()) {
 			types.put(t.getName(), t);
 		}
@@ -187,5 +187,5 @@
 		}
 
-		ModManager.getInstance().init();
+		PackageManager.getInstance().init();
 		tblMods.reloadData();
 		initModTypeBox();
@@ -206,13 +206,13 @@
 				}
 			} else {
-				TreeSet<Mod> mods = ModManager.getInstance().getUpdatableMods();
-				TreeSet<Mod> tools = ModManager.getInstance()
+				TreeSet<Package> mods = PackageManager.getInstance().getUpdatableMods();
+				TreeSet<Package> tools = PackageManager.getInstance()
 						.getUpdatableTools();
 				int size = 0;
 				JPanel panPackages = new JPanel(new GridLayout(0, 1));
-				execUpdates = new TreeSet<Mod>();
+				execUpdates = new TreeSet<Package>();
 				execUpdates.addAll(mods);
 				execUpdates.addAll(tools);
-				for (final Mod m : mods) {
+				for (final Package m : mods) {
 					size += m.getZipSize();
 					JCheckBox check = new JCheckBox("Mod: " + m.getName());
@@ -229,5 +229,5 @@
 					panPackages.add(check);
 				}
-				for (final Mod m : tools) {
+				for (final Package m : tools) {
 					size += m.getZipSize();
 					JCheckBox check = new JCheckBox("Tool: " + m.getName());
@@ -285,6 +285,6 @@
 				if (dl.isFinished()) {
 					TreeSet<Integer> installed = Installer.getInstalledTools();
-					TreeSet<Mod> tools = new TreeSet<Mod>();
-					for (Mod m : execUpdates)
+					TreeSet<Package> tools = new TreeSet<Package>();
+					for (Package m : execUpdates)
 						if (m.isTool()
 								&& installed.contains(m.getPackageNumber()))
@@ -315,6 +315,6 @@
 
 	@SuppressWarnings("unused")
-	private void showMandatoryPackagesDialog() {
-		new MandatoryPackagesDialog().setVisible(true);
+	private void showCorePackagesDialog() {
+		new CorePackagesDialog().setVisible(true);
 	}
 
@@ -369,5 +369,5 @@
 			if (!f.getName().endsWith(".xml"))
 				f = new File(f.getParentFile(), f.getName() + ".xml");
-			ModManager.getInstance().saveModSelection(f,
+			PackageManager.getInstance().saveModSelection(f,
 					tblMods.getSelectedMods());
 		}
@@ -397,5 +397,5 @@
 		}
 		toolsMenuItems.clear();
-		for (Mod m : ModManager.getInstance().getInstalledTools()) {
+		for (Package m : PackageManager.getInstance().getInstalledTools()) {
 			File exe = m.getExeFile();
 			if (exe != null && exe.exists()) {
@@ -441,23 +441,23 @@
 	}
 
-	@DoInBackground(progressMessage = "mandatoryFiles.title", cancelable = false, indeterminateProgress = false)
-	private void checkMandatoryFiles(final BackgroundEvent evt) {
+	@DoInBackground(progressMessage = "checkCorePackages.title", cancelable = false, indeterminateProgress = false)
+	private void checkCorePackages(final BackgroundEvent evt) {
 		if (!Settings.getInstance().isOfflineMode()) {
-			for (Mod m : ModManager.getInstance().getMandatoryTools()) {
+			for (Package m : PackageManager.getInstance().getCoreTools()) {
 				if (m.isNewerAvailable()) {
-					execMandatoryUpdates.add(m);
-				}
-			}
-			for (Mod m : ModManager.getInstance().getMandatoryMods()) {
+					execCoreUpdates.add(m);
+				}
+			}
+			for (Package m : PackageManager.getInstance().getCoreMods()) {
 				if (m.isNewerAvailable()) {
-					execMandatoryUpdates.add(m);
-				}
-			}
-			if (execMandatoryUpdates.size() > 0) {
-				ModDownloader m = new ModDownloader(execMandatoryUpdates,
+					execCoreUpdates.add(m);
+				}
+			}
+			if (execCoreUpdates.size() > 0) {
+				ModDownloader m = new ModDownloader(execCoreUpdates,
 						new ModDownloaderListener() {
 							@Override
 							public void updateStatus(ModDownloader source,
-									Mod currentDownload, State state,
+									Package currentDownload, State state,
 									int filesDown, int filesTotal,
 									int bytesDown, int bytesTotal,
@@ -476,21 +476,21 @@
 			}
 			evt.setProgressMessage(bundle
-					.getString("mandatoryToolsInstall.title"));
+					.getString("coreToolsInstall.title"));
 			Installer
-					.installTools(ModManager.getInstance().getMandatoryTools());
-		}
-	}
-
-	@SuppressWarnings("unused")
-	private void infoMandatoryFiles() {
-		if (execMandatoryUpdates.size() > 0) {
+					.installTools(PackageManager.getInstance().getCoreTools());
+		}
+	}
+
+	@SuppressWarnings("unused")
+	private void infoCorePackages() {
+		if (execCoreUpdates.size() > 0) {
 			String packages = "";
-			for (Mod m : execMandatoryUpdates) {
+			for (Package m : execCoreUpdates) {
 				packages += String.format("\n - %s (%s)", m.getName(),
 						m.getVersion());
 			}
 			JOptionPane.showMessageDialog(this, String.format(
-					bundle.getString("mandatoryFilesUpdated.text"), packages),
-					bundle.getString("mandatoryFilesUpdated.title"),
+					bundle.getString("corePackagesUpdated.text"), packages),
+					bundle.getString("corePackagesUpdated.title"),
 					JOptionPane.INFORMATION_MESSAGE);
 		}
@@ -499,15 +499,15 @@
 	@SuppressWarnings("unused")
 	private void install() {
-		TreeSet<Mod> mods = new TreeSet<Mod>();
-		mods.addAll(ModManager.getInstance().getMandatoryMods());
+		TreeSet<Package> mods = new TreeSet<Package>();
+		mods.addAll(PackageManager.getInstance().getCoreMods());
 		mods.addAll(tblMods.getSelectedMods());
 
-		installDeps = new TreeSet<Mod>();
+		installDeps = new TreeSet<Package>();
 
 		installState = EInstallState.CHECKING;
 
 		while (installState == EInstallState.CHECKING) {
-			TreeSet<Mod> toDownload = new TreeSet<Mod>();
-			for (Mod m : mods) {
+			TreeSet<Package> toDownload = new TreeSet<Package>();
+			for (Package m : mods) {
 				if (!m.isLocalAvailable())
 					toDownload.add(m);
@@ -529,13 +529,9 @@
 				}
 			}
-			HashMap<Mod, HashSet<Mod>> dependencies = ModManager.getInstance()
+			HashMap<Package, HashSet<Package>> dependencies = PackageManager.getInstance()
 					.checkDependencies(mods);
 			if (dependencies.size() > 0) {
-				for (HashSet<Mod> hm : dependencies.values()) {
+				for (HashSet<Package> hm : dependencies.values()) {
 					installDeps.addAll(hm);
-					for (Mod mDep : hm) {
-						if (!mods.contains(mDep))
-							mods.add(mDep);
-					}
 				}
 
@@ -545,6 +541,6 @@
 					String depsLocalString = "";
 					String depsDownloadString = "";
-					for (Mod m : dependencies.keySet()) {
-						for (Mod mDep : dependencies.get(m)) {
+					for (Package m : dependencies.keySet()) {
+						for (Package mDep : dependencies.get(m)) {
 							if (!mods.contains(mDep)) {
 								mods.add(mDep);
@@ -584,5 +580,5 @@
 				}
 			} else {
-				HashMap<Mod, HashSet<Mod>> incompatibilities = ModManager
+				HashMap<Package, HashSet<Package>> incompatibilities = PackageManager
 						.getInstance().checkIncompabitilites(mods);
 				if (incompatibilities.size() > 0) {
@@ -590,10 +586,10 @@
 
 					String incompatString = "";
-					for (Mod m : incompatibilities.keySet()) {
+					for (Package m : incompatibilities.keySet()) {
 						if (incompatString.length() > 0)
 							incompatString += "\n";
 						incompatString += m.getName() + ": ";
 						String confMods = "";
-						for (Mod mConf : incompatibilities.get(m)) {
+						for (Package mConf : incompatibilities.get(m)) {
 							if (confMods.length() > 0)
 								confMods += ", ";
@@ -616,8 +612,8 @@
 
 		if (installState == EInstallState.READY) {
-			installMods = new TreeSet<Mod>();
-			TreeSet<Mod> actuallyTools = new TreeSet<Mod>();
-
-			for (Mod m : mods) {
+			installMods = new TreeSet<Package>();
+			TreeSet<Package> actuallyTools = new TreeSet<Package>();
+
+			for (Package m : mods) {
 				if (m.isTool())
 					actuallyTools.add(m);
@@ -652,5 +648,5 @@
 	@SuppressWarnings("unused")
 	private void installDone() {
-		ModManager.getInstance().updateInstalledMods();
+		PackageManager.getInstance().updateInstalledMods();
 		switch (installState) {
 			case DONE:
@@ -660,5 +656,5 @@
 								false)) {
 					String installedDeps = "";
-					for (Mod m : installDeps) {
+					for (Package m : installDeps) {
 						if (installedDeps.length() > 0)
 							installedDeps += "\n";
@@ -690,5 +686,5 @@
 
 	@Override
-	public void modSelectionChanged(ModTable source, Mod m) {
+	public void modSelectionChanged(ModTable source, Package m) {
 		lblTitleVal.setText("");
 		lblCreatorVal.setText("");
Index: AE/installer2/src/net/oni2/aeinstaller/gui/MainWin.yml
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/gui/MainWin.yml	(revision 647)
+++ AE/installer2/src/net/oni2/aeinstaller/gui/MainWin.yml	(revision 648)
@@ -6,5 +6,5 @@
   locationRelativeTo: null
   defaultCloseOperation: doNothingOnClose
-  onWindowOpened: [execDepotUpdate,checkMandatoryFiles,infoMandatoryFiles,checkInitialize,initialize,checkUpdates,doUpdate,refreshToolsMenu,focus]
+  onWindowOpened: [execDepotUpdate,checkCorePackages,infoCorePackages,checkInitialize,initialize,checkUpdates,doUpdate,refreshToolsMenu,focus]
   onWindowClosing: [saveLocalData,exit]
   iconImage: img.ae
@@ -21,5 +21,5 @@
     - Action(name=tools, text=menu.manageTools, toolTipText=menu.manageToolsTooltip, icon=img.tools, onAction=[tools,refreshToolsMenu])
     - Action(name=update, text=menu.update, toolTipText=menu.updateTooltip, icon=img.update, onAction=[checkUpdates,doUpdate])
-    - Action(name=mandatoryPackages, text=menu.mandatoryPackages, toolTipText=menu.mandatoryPackagesTooltip, icon=img.mandatory, onAction=[showMandatoryPackagesDialog])
+    - Action(name=corePackages, text=menu.corePackages, toolTipText=menu.corePackagesTooltip, icon=img.core, onAction=[showCorePackagesDialog])
     - JMenuBar:
         - JMenu(name=mainMenu, text=menu.main):
@@ -38,5 +38,5 @@
             - JMenuItem(action=openEditionFolder)
             - JSeparator()
-            - JMenuItem(action=mandatoryPackages)
+            - JMenuItem(action=corePackages)
             - JMenuItem(action=reglobalize)
         - JMenu(name=toolsMenu, text=menu.tools):
Index: AE/installer2/src/net/oni2/aeinstaller/gui/corepackages/CorePackagesDialog.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/gui/corepackages/CorePackagesDialog.java	(revision 648)
+++ AE/installer2/src/net/oni2/aeinstaller/gui/corepackages/CorePackagesDialog.java	(revision 648)
@@ -0,0 +1,134 @@
+package net.oni2.aeinstaller.gui.corepackages;
+
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.ResourceBundle;
+import java.util.TreeSet;
+
+import javax.swing.AbstractAction;
+import javax.swing.DefaultListModel;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JSplitPane;
+import javax.swing.KeyStroke;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import net.oni2.aeinstaller.backend.SizeFormatter;
+import net.oni2.aeinstaller.backend.packages.Package;
+import net.oni2.aeinstaller.backend.packages.PackageManager;
+import net.oni2.aeinstaller.backend.packages.Type;
+import net.oni2.aeinstaller.gui.HTMLLinkLabel;
+
+import org.javabuilders.BuildResult;
+import org.javabuilders.swing.SwingJavaBuilder;
+
+/**
+ * @author Christian Illy
+ */
+public class CorePackagesDialog extends JDialog implements
+		ListSelectionListener {
+	private static final long serialVersionUID = -5444213842599816301L;
+
+	private ResourceBundle bundle = ResourceBundle
+			.getBundle("net.oni2.aeinstaller.localization."
+					+ getClass().getSimpleName());
+	@SuppressWarnings("unused")
+	private BuildResult result = SwingJavaBuilder.build(this, bundle);
+
+	private JSplitPane contents;
+
+	private JList lstTools;
+
+	private JLabel lblTitleVal;
+	private JLabel lblCreatorVal;
+	private JLabel lblTypesVal;
+	private JLabel lblPlatformVal;
+	private JLabel lblPackageNumberVal;
+	private JLabel lblVersionNumberVal;
+	private JLabel lblLastChangeVal;
+	private HTMLLinkLabel lblDescriptionVal;
+	private JLabel lblDownloadSizeVal;
+
+	private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+
+	/**
+	 * Open the dialog
+	 */
+	public CorePackagesDialog() {
+		setMinimumSize(new Dimension(getWidth() + 100, getHeight() + 100));
+
+		AbstractAction closeAction = new AbstractAction() {
+
+			private static final long serialVersionUID = 1L;
+
+			public void actionPerformed(ActionEvent arg0) {
+				dispose();
+			}
+		};
+		KeyStroke ksCtrlW = KeyStroke
+				.getKeyStroke('W', KeyEvent.CTRL_DOWN_MASK);
+		getRootPane()
+				.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
+				.put(ksCtrlW, "close");
+		getRootPane().getActionMap().put("close", closeAction);
+
+		contents.setDividerLocation(200);
+		contents.setResizeWeight(0.4);
+
+		lstTools.addListSelectionListener(this);
+		lstTools.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+
+		DefaultListModel dlm = new DefaultListModel();
+		TreeSet<Package> tools = PackageManager.getInstance().getCoreTools();
+		for (Package m : tools)
+			dlm.addElement(m);
+		TreeSet<Package> mods = PackageManager.getInstance().getCoreMods();
+		for (Package m : mods)
+			dlm.addElement(m);
+		lstTools.setModel(dlm);
+
+		setLocationRelativeTo(null);
+	}
+
+	@Override
+	public void valueChanged(ListSelectionEvent evt) {
+		lblTitleVal.setText("");
+		lblCreatorVal.setText("");
+		lblDescriptionVal.setText("");
+		lblTypesVal.setText("");
+		lblPlatformVal.setText("");
+		lblPackageNumberVal.setText("");
+		lblVersionNumberVal.setText("");
+		lblLastChangeVal.setText("");
+		lblDownloadSizeVal.setText("");
+
+		if (lstTools.getSelectedValue() instanceof Package) {
+			Package m = (Package) lstTools.getSelectedValue();
+			lblTitleVal.setText(m.getName());
+			lblCreatorVal.setText(m.getCreator());
+			lblDescriptionVal.setText(m.getDescription());
+
+			String types = "";
+			for (Type t : m.getTypes()) {
+				if (types.length() > 0)
+					types += ", ";
+				types += t.getName();
+			}
+			lblTypesVal.setText(types);
+			lblPlatformVal.setText(m.getPlatform().toString());
+			lblPackageNumberVal.setText(m.getPackageNumberString());
+			lblVersionNumberVal.setText(m.getVersion());
+			if (m.getFile() != null)
+				lblLastChangeVal.setText(sdf.format(new Date(m.getFile()
+						.getTimestamp() * 1000)));
+			lblDownloadSizeVal.setText(SizeFormatter.format(m.getZipSize(), 3));
+		}
+	}
+}
Index: AE/installer2/src/net/oni2/aeinstaller/gui/corepackages/CorePackagesDialog.yml
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/gui/corepackages/CorePackagesDialog.yml	(revision 648)
+++ AE/installer2/src/net/oni2/aeinstaller/gui/corepackages/CorePackagesDialog.yml	(revision 648)
@@ -0,0 +1,51 @@
+JDialog:
+  name: frame
+  title: frame.title
+  size: packed
+  locationRelativeTo: null
+  defaultCloseOperation: disposeOnClose
+  iconImage: img.ae
+  modalityType: applicationModal
+  content:
+    - JSplitPane(name=contents, orientation=horizontalSplit, continuousLayout=true):
+        - JPanel(name=panTools):
+            - JScrollPane(name=scrollTools, vScrollBar=always, hScrollBar=asNeeded):
+                JList(name=lstTools)
+            - MigLayout: |
+                 [grow]
+                 scrollTools   [grow]
+        - JPanel(name=panInfo):
+            - JLabel(name=lblTitle, text=lblTitle.text)
+            - JLabel(name=lblTitleVal)
+            - JLabel(name=lblCreator, text=lblCreator.text)
+            - JLabel(name=lblCreatorVal)
+            - JLabel(name=lblTypes, text=lblTypes.text)
+            - JLabel(name=lblTypesVal)
+            - JLabel(name=lblPlatform, text=lblPlatform.text)
+            - JLabel(name=lblPlatformVal)
+            - JLabel(name=lblPackageNumber, text=lblPackageNumber.text)
+            - JLabel(name=lblPackageNumberVal)
+            - JLabel(name=lblVersionNumber, text=lblVersionNumber.text)
+            - JLabel(name=lblVersionNumberVal)
+            - JLabel(name=lblLastChange, text=lblLastChange.text)
+            - JLabel(name=lblLastChangeVal)
+            - JLabel(name=lblDescription, text=lblDescription.text)
+            - JScrollPane(name=scrollDescription, vScrollBar=always, hScrollBar=asNeeded):
+                HTMLLinkLabel(name=lblDescriptionVal)
+            - JLabel(name=lblDownloadSize, text=lblDownloadSize.text)
+            - JLabel(name=lblDownloadSizeVal)
+            - MigLayout: |
+                 [min]             [grow]
+                 >lblTitle         lblTitleVal         [min]
+                 >lblCreator       lblCreatorVal       [min]
+                 >lblTypes         lblTypesVal         [min]
+                 >lblPlatform      lblPlatformVal      [min]
+                 >lblPackageNumber lblPackageNumberVal [min]
+                 >lblVersionNumber lblVersionNumberVal [min]
+                 >lblLastChange    lblLastChangeVal    [min]
+                 >^lblDescription  scrollDescription   [grow]
+                 >lblDownloadSize  lblDownloadSizeVal  [min]
+    - MigLayout: |
+         [grow]
+         contents [grow]
+  
Index: AE/installer2/src/net/oni2/aeinstaller/gui/downloadwindow/Downloader.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/gui/downloadwindow/Downloader.java	(revision 647)
+++ AE/installer2/src/net/oni2/aeinstaller/gui/downloadwindow/Downloader.java	(revision 648)
@@ -11,8 +11,8 @@
 
 import net.oni2.aeinstaller.backend.SizeFormatter;
-import net.oni2.aeinstaller.backend.mods.Mod;
-import net.oni2.aeinstaller.backend.mods.download.ModDownloader;
-import net.oni2.aeinstaller.backend.mods.download.ModDownloader.State;
-import net.oni2.aeinstaller.backend.mods.download.ModDownloaderListener;
+import net.oni2.aeinstaller.backend.packages.Package;
+import net.oni2.aeinstaller.backend.packages.download.ModDownloader;
+import net.oni2.aeinstaller.backend.packages.download.ModDownloaderListener;
+import net.oni2.aeinstaller.backend.packages.download.ModDownloader.State;
 
 import org.javabuilders.BuildResult;
@@ -43,5 +43,5 @@
 
 	private ModDownloader downloader;
-	private TreeSet<Mod> dependencies = new TreeSet<Mod>();
+	private TreeSet<Package> dependencies = new TreeSet<Package>();
 
 	/**
@@ -51,5 +51,5 @@
 	 *            List of mods that only are auto-resolved dependencies
 	 */
-	public Downloader(TreeSet<Mod> mods, TreeSet<Mod> dependencies) {
+	public Downloader(TreeSet<Package> mods, TreeSet<Package> dependencies) {
 		super();
 
@@ -92,5 +92,5 @@
 
 	@Override
-	public void updateStatus(ModDownloader source, Mod currentDownload,
+	public void updateStatus(ModDownloader source, Package currentDownload,
 			State state, int filesDown, int filesTotal, int bytesDown,
 			int bytesTotal, int duration, int remaining, int speed) {
Index: AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModSelectionListener.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModSelectionListener.java	(revision 647)
+++ AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModSelectionListener.java	(revision 648)
@@ -1,5 +1,5 @@
 package net.oni2.aeinstaller.gui.modtable;
 
-import net.oni2.aeinstaller.backend.mods.Mod;
+import net.oni2.aeinstaller.backend.packages.Package;
 
 /**
@@ -15,4 +15,4 @@
 	 *            New selected mod
 	 */
-	public void modSelectionChanged(ModTable source, Mod mod);
+	public void modSelectionChanged(ModTable source, Package mod);
 }
Index: AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModTable.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModTable.java	(revision 647)
+++ AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModTable.java	(revision 648)
@@ -30,6 +30,6 @@
 
 import net.oni2.aeinstaller.backend.Settings;
-import net.oni2.aeinstaller.backend.mods.Mod;
-import net.oni2.aeinstaller.backend.mods.Type;
+import net.oni2.aeinstaller.backend.packages.Package;
+import net.oni2.aeinstaller.backend.packages.Type;
 
 /**
@@ -90,5 +90,5 @@
 			int modelCol = convertColumnIndexToModel(c);
 			if (modelCol == 4) {
-				final Mod mod = (Mod) getValueAt(r, -1);
+				final Package mod = (Package) getValueAt(r, -1);
 
 				String tt = "<html>";
@@ -126,5 +126,5 @@
 	}
 
-	private void notifyModSelectionListeners(Mod m) {
+	private void notifyModSelectionListeners(Package m) {
 		for (ModSelectionListener l : modSelListeners) {
 			l.modSelectionChanged(this, m);
@@ -175,5 +175,5 @@
 	 * @return Mods selected for installation
 	 */
-	public TreeSet<Mod> getSelectedMods() {
+	public TreeSet<Package> getSelectedMods() {
 		return model.getSelectedMods();
 	}
@@ -212,5 +212,5 @@
 			notifyModSelectionListeners(null);
 		} else {
-			Mod mod = (Mod) getValueAt(viewRow, -1);
+			Package mod = (Package) getValueAt(viewRow, -1);
 			notifyModSelectionListeners(mod);
 		}
@@ -228,5 +228,5 @@
 			if (rowindex >= 0) {
 				if (e.isPopupTrigger() && e.getComponent() instanceof JTable) {
-					final Mod mod = (Mod) getValueAt(rowindex, -1);
+					final Package mod = (Package) getValueAt(rowindex, -1);
 
 					JPopupMenu popup = new JPopupMenu();
@@ -292,5 +292,5 @@
 				String key = String.valueOf(e.getKeyChar()).toLowerCase();
 				for (int i = 0; i < getRowCount(); i++) {
-					Mod m = (Mod) getValueAt(i, -1);
+					Package m = (Package) getValueAt(i, -1);
 					if (m.getName().toLowerCase().startsWith(key)) {
 						setRowSelectionInterval(i, i);
Index: AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModTableFilter.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModTableFilter.java	(revision 647)
+++ AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModTableFilter.java	(revision 648)
@@ -3,6 +3,6 @@
 import javax.swing.RowFilter;
 
-import net.oni2.aeinstaller.backend.mods.Mod;
-import net.oni2.aeinstaller.backend.mods.Type;
+import net.oni2.aeinstaller.backend.packages.Package;
+import net.oni2.aeinstaller.backend.packages.Type;
 
 /**
@@ -28,7 +28,7 @@
 	public boolean include(
 			RowFilter.Entry<? extends ModTableModel, ? extends Integer> entry) {
-		Mod mod = (Mod) entry.getModel().getValueAt(entry.getIdentifier(), -1);
+		Package mod = (Package) entry.getModel().getValueAt(entry.getIdentifier(), -1);
 		
-		if (mod.isMandatoryMod())
+		if (mod.isCorePackage())
 			return false;
 
Index: AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModTableModel.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModTableModel.java	(revision 647)
+++ AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModTableModel.java	(revision 648)
@@ -12,6 +12,6 @@
 import javax.swing.table.TableColumn;
 
-import net.oni2.aeinstaller.backend.mods.Mod;
-import net.oni2.aeinstaller.backend.mods.ModManager;
+import net.oni2.aeinstaller.backend.packages.Package;
+import net.oni2.aeinstaller.backend.packages.PackageManager;
 
 /**
@@ -25,5 +25,5 @@
 			.getBundle("net.oni2.aeinstaller.localization.ModTable");
 
-	private Vector<Mod> items = new Vector<Mod>();
+	private Vector<Package> items = new Vector<Package>();
 	private Vector<Boolean> install = new Vector<Boolean>();
 
@@ -40,5 +40,5 @@
 	@Override
 	public Object getValueAt(int row, int col) {
-		Mod mod = items.get(row);
+		Package mod = items.get(row);
 		switch (col) {
 			case -1:
@@ -54,5 +54,5 @@
 			case 4:
 				String res = "";
-				res += (install.get(row) ? "I" : "_");
+				res += (mod.isInstalled() ? "I" : "_");
 				res += (mod.isLocalAvailable() && mod.isNewerAvailable() ? "U"
 						: "_");
@@ -166,5 +166,5 @@
 	public void reloadData() {
 		items.clear();
-		items.addAll(ModManager.getInstance().getModsValidAndNotMandatory());
+		items.addAll(PackageManager.getInstance().getModsValidAndNotCore());
 		revertSelection();
 	}
@@ -189,6 +189,6 @@
 	 */
 	public void reloadSelection(File config) {
-		Vector<Integer> selected = ModManager.getInstance().loadModSelection(
-				config);
+		Vector<Integer> selected = PackageManager.getInstance()
+				.loadModSelection(config);
 		install.clear();
 		for (int i = 0; i < items.size(); i++) {
@@ -203,5 +203,5 @@
 	 * @return Items
 	 */
-	public Vector<Mod> getItems() {
+	public Vector<Package> getItems() {
 		return items;
 	}
@@ -210,6 +210,6 @@
 	 * @return Mods selected for installation
 	 */
-	public TreeSet<Mod> getSelectedMods() {
-		TreeSet<Mod> res = new TreeSet<Mod>();
+	public TreeSet<Package> getSelectedMods() {
+		TreeSet<Package> res = new TreeSet<Package>();
 		for (int i = 0; i < items.size(); i++) {
 			if (install.get(i))
@@ -223,5 +223,5 @@
 		return columnIndex == 0;
 	}
-	
+
 	private void notifyDownloadSize(int size) {
 		for (DownloadSizeListener dsl : listeners)
@@ -238,5 +238,5 @@
 			for (int i = 0; i < items.size(); i++) {
 				if (install.get(i)) {
-					Mod m = items.get(i);
+					Package m = items.get(i);
 					if (!m.isLocalAvailable())
 						size += m.getZipSize();
Index: AE/installer2/src/net/oni2/aeinstaller/gui/toolmanager/ToolManager.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/gui/toolmanager/ToolManager.java	(revision 647)
+++ AE/installer2/src/net/oni2/aeinstaller/gui/toolmanager/ToolManager.java	(revision 648)
@@ -28,6 +28,6 @@
 import net.oni2.aeinstaller.backend.Settings;
 import net.oni2.aeinstaller.backend.SizeFormatter;
-import net.oni2.aeinstaller.backend.mods.Mod;
-import net.oni2.aeinstaller.backend.mods.ModManager;
+import net.oni2.aeinstaller.backend.packages.Package;
+import net.oni2.aeinstaller.backend.packages.PackageManager;
 import net.oni2.aeinstaller.backend.oni.Installer;
 import net.oni2.aeinstaller.gui.HTMLLinkLabel;
@@ -97,5 +97,5 @@
 
 		DefaultListModel dlm = new DefaultListModel();
-		TreeMap<String, Mod> tools = ModManager.getInstance().getTools();
+		TreeMap<String, Package> tools = PackageManager.getInstance().getTools();
 		for (String name : tools.keySet())
 			dlm.addElement(tools.get(name));
@@ -113,9 +113,9 @@
 	private void install() {
 		Object o = lstTools.getSelectedValue();
-		if (o instanceof Mod) {
-			Mod theMod = (Mod) o;
+		if (o instanceof Package) {
+			Package theMod = (Package) o;
 
 			if (theMod.isInstalled()) {
-				TreeSet<Mod> tools = new TreeSet<Mod>();
+				TreeSet<Package> tools = new TreeSet<Package>();
 				tools.add(theMod);
 				Installer.uninstallTools(tools);
@@ -130,5 +130,5 @@
 					}
 
-					TreeSet<Mod> toDownload = new TreeSet<Mod>();
+					TreeSet<Package> toDownload = new TreeSet<Package>();
 					toDownload.add(theMod);
 
@@ -143,5 +143,5 @@
 				}
 
-				TreeSet<Mod> tools = new TreeSet<Mod>();
+				TreeSet<Package> tools = new TreeSet<Package>();
 				tools.add(theMod);
 				Installer.installTools(tools);
@@ -164,6 +164,6 @@
 		btnInstall.setIcon(icoInstall);
 
-		if (lstTools.getSelectedValue() instanceof Mod) {
-			Mod m = (Mod) lstTools.getSelectedValue();
+		if (lstTools.getSelectedValue() instanceof Package) {
+			Package m = (Package) lstTools.getSelectedValue();
 			lblTitleVal.setText(m.getName());
 			lblCreatorVal.setText(m.getCreator());
Index: AE/installer2/src/net/oni2/aeinstaller/localization/CorePackagesDialog.properties
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/localization/CorePackagesDialog.properties	(revision 648)
+++ AE/installer2/src/net/oni2/aeinstaller/localization/CorePackagesDialog.properties	(revision 648)
@@ -0,0 +1,11 @@
+frame.title=AE Installer: Core packages
+
+lblTitle.text=Name:
+lblCreator.text=Creator:
+lblTypes.text=Types:
+lblPlatform.text=Platform:
+lblPackageNumber.text=Package number:
+lblVersionNumber.text=Version number:
+lblLastChange.text=Last change:
+lblDescription.text=Description:
+lblDownloadSize.text=Download size of this package:
Index: AE/installer2/src/net/oni2/aeinstaller/localization/MainWin.properties
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/localization/MainWin.properties	(revision 647)
+++ AE/installer2/src/net/oni2/aeinstaller/localization/MainWin.properties	(revision 648)
@@ -22,6 +22,6 @@
 menu.saveConfigTooltip=Save current mod selection to a file
 
-menu.mandatoryPackages=&Show mandatory packages
-menu.mandatoryPackagesTooltip=View which mandatory packages are used
+menu.corePackages=&Show core packages
+menu.corePackagesTooltip=View which core packages are used
 menu.reglobalize=&Rebuild Core Data
 menu.reglobalizeTooltip=Rebuild Core Data
@@ -79,8 +79,8 @@
 initializingEdition.title=Initializing Edition core
 installing.title=Installing mods
-mandatoryFiles.title=Checking for mandatory packages
-mandatoryToolsInstall.title=Installing mandatory tools
-mandatoryFilesUpdated.title=Updated mandatory packages
-mandatoryFilesUpdated.text=The following mandatory packages have been updated:%s
+checkCorePackages.title=Checking for core packages
+coreToolsInstall.title=Installing core tools
+corePackagesUpdated.title=Updated core packages
+corePackagesUpdated.text=The following core packages have been updated:%s
 
 doUpdate.title=Updating packages
Index: AE/installer2/src/net/oni2/aeinstaller/localization/MandatoryPackagesDialog.properties
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/localization/MandatoryPackagesDialog.properties	(revision 647)
+++ 	(revision )
@@ -1,11 +1,0 @@
-frame.title=AE Installer: Mandatory packages
-
-lblTitle.text=Name:
-lblCreator.text=Creator:
-lblTypes.text=Types:
-lblPlatform.text=Platform:
-lblPackageNumber.text=Package number:
-lblVersionNumber.text=Version number:
-lblLastChange.text=Last change:
-lblDescription.text=Description:
-lblDownloadSize.text=Download size of this package:
