Index: AE/installer2/src/net/oni2/aeinstaller/backend/mods/ModManager.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/mods/ModManager.java	(revision 623)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/mods/ModManager.java	(revision 624)
@@ -286,5 +286,5 @@
 	 */
 	public HashMap<Mod, HashSet<Mod>> checkDependencies(TreeSet<Mod> mods) {
-		// TODO: Verify functionality
+		// TODO: Verify functionality (checkDependencies)
 		HashMap<Mod, HashSet<Mod>> res = new HashMap<Mod, HashSet<Mod>>();
 
@@ -311,5 +311,5 @@
 	 */
 	public HashMap<Mod, HashSet<Mod>> checkIncompabitilites(TreeSet<Mod> mods) {
-		// TODO: Verify functionality
+		// TODO: Verify functionality (checkIncompatibilities)
 		HashMap<Mod, HashSet<Mod>> res = new HashMap<Mod, HashSet<Mod>>();
 
Index: AE/installer2/src/net/oni2/aeinstaller/backend/mods/download/ModDownload.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/mods/download/ModDownload.java	(revision 623)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/mods/download/ModDownload.java	(revision 624)
@@ -72,6 +72,5 @@
 		zipFile = new File(Paths.getDownloadPath(),
 				mod.getPackageNumberString() + ".zip");
-		targetFolder = new File(Paths.getModsPath(),
-				mod.getPackageNumberString());
+		targetFolder = mod.getLocalPath();
 		try {
 			downloader = new FileDownloader(mod.getFile().getUri_full(),
Index: AE/installer2/src/net/oni2/aeinstaller/backend/oni/Installer.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/oni/Installer.java	(revision 623)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/oni/Installer.java	(revision 624)
@@ -3,5 +3,7 @@
 import java.io.File;
 import java.io.FileFilter;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.FilenameFilter;
 import java.io.IOException;
@@ -10,4 +12,5 @@
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Scanner;
@@ -24,4 +27,7 @@
 
 import org.apache.commons.io.FileUtils;
+
+import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.io.xml.StaxDriver;
 
 /**
@@ -58,8 +64,49 @@
 
 	/**
+	 * @return Currently installed tools
+	 */
+	@SuppressWarnings("unchecked")
+	public static TreeSet<Integer> getInstalledTools() {
+		File installCfg = new File(Paths.getInstallerPath(),
+				"installed_tools.xml");
+		TreeSet<Integer> res = new TreeSet<Integer>();
+		try {
+			if (installCfg.exists()) {
+				FileInputStream fis = new FileInputStream(installCfg);
+				XStream xs = new XStream(new StaxDriver());
+				Object obj = xs.fromXML(fis);
+				if (obj instanceof TreeSet<?>)
+					res = (TreeSet<Integer>) obj;
+				fis.close();
+			}
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return res;
+	}
+
+	private static void writeInstalledTools(TreeSet<Integer> tools) {
+		File installCfg = new File(Paths.getInstallerPath(),
+				"installed_tools.xml");
+		try {
+			FileOutputStream fos = new FileOutputStream(installCfg);
+			XStream xs = new XStream(new StaxDriver());
+			xs.toXML(tools, fos);
+			fos.close();
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
 	 * @param tools
 	 *            Tools to install
 	 */
 	public static void installTools(TreeSet<Mod> tools) {
+		TreeSet<Integer> installed = getInstalledTools();
 		for (Mod m : tools) {
 			File plain = new File(m.getLocalPath(), "plain");
@@ -80,5 +127,7 @@
 				}
 			}
-		}
+			installed.add(m.getPackageNumber());
+		}
+		writeInstalledTools(installed);
 	}
 
@@ -88,5 +137,31 @@
 	 */
 	public static void uninstallTools(TreeSet<Mod> tools) {
-		// TODO: implement this!
+		TreeSet<Integer> installed = getInstalledTools();
+		for (Mod m : tools) {
+			if (installed.contains(m.getPackageNumber())) {
+				File plain = new File(m.getLocalPath(), "plain");
+				if (plain.exists()) {
+					if (m.hasSeparatePlatformDirs()) {
+						File plainCommon = new File(plain, "common");
+						File plainMac = new File(plain, "mac_only");
+						File plainWin = new File(plain, "win_only");
+						if (plainCommon.exists())
+							removeToolsFiles(plainCommon,
+									Paths.getEditionBasePath());
+						if (Settings.getPlatform() == Platform.MACOS
+								&& plainMac.exists())
+							removeToolsFiles(plainMac,
+									Paths.getEditionBasePath());
+						else if (plainWin.exists())
+							removeToolsFiles(plainWin,
+									Paths.getEditionBasePath());
+					} else {
+						removeToolsFiles(plain, Paths.getEditionBasePath());
+					}
+				}
+			}
+			installed.remove(m.getPackageNumber());
+		}
+		writeInstalledTools(installed);
 	}
 
@@ -106,4 +181,18 @@
 	}
 
+	private static void removeToolsFiles(File srcFolder, File target) {
+		for (File f : srcFolder.listFiles()) {
+			if (f.isDirectory())
+				removeToolsFiles(f, new File(target, f.getName()));
+			else {
+				File targetFile = new File(target, f.getName());
+				if (targetFile.exists())
+					targetFile.delete();
+			}
+		}
+		if (target.list().length == 0)
+			target.delete();
+	}
+
 	/**
 	 * Install the given set of mods
@@ -125,8 +214,13 @@
 		ModManager.getInstance().saveModSelection(installCfg, mods);
 
+		HashSet<Integer> unlockLevels = new HashSet<Integer>();
+
 		Vector<File> foldersOni = new Vector<File>();
 		foldersOni.add(Paths.getVanillaOnisPath());
 
 		for (Mod m : mods) {
+			for (int lev : m.getUnlockLevels())
+				unlockLevels.add(lev);
+
 			File oni = new File(m.getLocalPath(), "oni");
 			if (oni.exists()) {
@@ -149,4 +243,17 @@
 		combineBinaryFiles(foldersOni, listener);
 		combineBSLFolders(mods, listener);
+
+		if (unlockLevels.size() > 0) {
+			File dat = new File(Paths.getEditionBasePath(), "persist.dat");
+			if (dat.exists()) {
+				PersistDat save = new PersistDat(dat);
+				HashSet<Integer> currentlyUnlocked = save.getUnlockedLevels();
+				currentlyUnlocked.addAll(unlockLevels);
+				save.setUnlockedLevels(currentlyUnlocked);
+				save.close();
+			} else {
+				// TODO: what if persist.dat does not exist?
+			}
+		}
 	}
 
Index: AE/installer2/src/net/oni2/aeinstaller/backend/oni/OniSplit.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/oni/OniSplit.java	(revision 623)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/oni/OniSplit.java	(revision 624)
@@ -8,5 +8,5 @@
 
 import net.oni2.aeinstaller.backend.Paths;
-import net.oni2.aeinstaller.backend.QuickAppExecution;
+import net.oni2.aeinstaller.backend.AppExecution;
 import net.oni2.aeinstaller.backend.Settings;
 import net.oni2.aeinstaller.backend.Settings.Architecture;
@@ -55,5 +55,5 @@
 				Vector<String> res = null;
 				try {
-					res = QuickAppExecution.execute(cmd);
+					res = AppExecution.executeAndWait(cmd);
 				} catch (IOException e) {
 					e.printStackTrace();
@@ -97,5 +97,5 @@
 		Vector<String> res = null;
 		try {
-			res = QuickAppExecution.execute(cmdLine);
+			res = AppExecution.executeAndWait(cmdLine);
 		} catch (IOException e) {
 			e.printStackTrace();
@@ -122,5 +122,5 @@
 		Vector<String> res = null;
 		try {
-			res = QuickAppExecution.execute(cmdLine);
+			res = AppExecution.executeAndWait(cmdLine);
 		} catch (IOException e) {
 			e.printStackTrace();
@@ -151,5 +151,5 @@
 		Vector<String> res = null;
 		try {
-			res = QuickAppExecution.execute(cmdLine);
+			res = AppExecution.executeAndWait(cmdLine);
 		} catch (IOException e) {
 			e.printStackTrace();
@@ -182,5 +182,5 @@
 		Vector<String> res = null;
 		try {
-			res = QuickAppExecution.execute(cmdLine);
+			res = AppExecution.executeAndWait(cmdLine);
 		} catch (IOException e) {
 			e.printStackTrace();
Index: AE/installer2/src/net/oni2/aeinstaller/backend/oni/PersistDat.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/oni/PersistDat.java	(revision 624)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/oni/PersistDat.java	(revision 624)
@@ -0,0 +1,90 @@
+package net.oni2.aeinstaller.backend.oni;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.HashSet;
+
+/**
+ * @author Christian Illy
+ */
+public class PersistDat {
+	private RandomAccessFile dat = null;
+
+	/**
+	 * Open the given persist.dat
+	 * 
+	 * @param dat
+	 *            Path to persist.dat
+	 */
+	public PersistDat(File dat) {
+		try {
+			this.dat = new RandomAccessFile(dat, "rw");
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * @return Currently unlocked levels in persist.dat
+	 */
+	public HashSet<Integer> getUnlockedLevels() {
+		HashSet<Integer> result = new HashSet<Integer>();
+		if (dat != null) {
+			try {
+				dat.seek(8);
+				for (int i = 0; i < 32; i++) {
+					int val = dat.read();
+					for (int j = 0; j < 8; j++) {
+						if ((val & (1 << j)) > 0) {
+							result.add(i * 8 + j);
+						}
+					}
+				}
+			} catch (IOException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+		}
+		return result;
+	}
+
+	/**
+	 * @param unlock
+	 *            Levels to be unlocked in persist.dat
+	 */
+	public void setUnlockedLevels(HashSet<Integer> unlock) {
+		if (dat != null) {
+			if (unlock != null) {
+				try {
+					dat.seek(8);
+					for (int i = 0; i < 32; i++) {
+						int val = 0;
+						for (int j = 0; j < 8; j++) {
+							if (unlock.contains(i * 8 + j))
+								val |= 1 << j;
+						}
+						dat.write(val);
+					}
+				} catch (IOException e) {
+					// TODO Auto-generated catch block
+					e.printStackTrace();
+				}
+			}
+		}
+	}
+
+	/**
+	 * Close file
+	 */
+	public void close() {
+		if (dat != null) {
+			try {
+				dat.close();
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+	}
+}
