Index: AE/installer2/src/net/oni2/aeinstaller/backend/Settings.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/Settings.java	(revision 604)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/Settings.java	(revision 605)
@@ -10,5 +10,4 @@
 import java.util.Vector;
 
-
 import com.thoughtworks.xstream.XStream;
 import com.thoughtworks.xstream.io.xml.StaxDriver;
@@ -66,5 +65,30 @@
 
 	private boolean printNamesNotInMap = false;
-	
+
+	/**
+	 * @return path to wine
+	 */
+	public static String getWinePath() {
+		if (getPlatform() != Platform.LINUX)
+			return null;
+
+		Vector<String> cmd = new Vector<String>();
+		cmd.add("which");
+		cmd.add("wine");
+		Vector<String> res = null;
+		try {
+			res = QuickAppExecution.execute(cmd);
+		} catch (IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		if (res != null) {
+			if (res.get(0).startsWith("/") && res.get(0).endsWith("wine")) {
+				return res.get(0);
+			}
+		}
+		return null;
+	}
+
 	/**
 	 * Get the singleton instance
Index: AE/installer2/src/net/oni2/aeinstaller/backend/depot/DepotManager.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/depot/DepotManager.java	(revision 604)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/depot/DepotManager.java	(revision 605)
@@ -92,5 +92,5 @@
 			page = 0;
 			do {
-				ja = DrupalJSONQuery.getIndex("taxonomy_vocabulary", page);
+				ja = DrupalJSONQuery.getIndex("taxonomy_vocabulary", page, 100);
 				for (int i = 0; i < ja.length(); i++) {
 					jo = ja.getJSONObject(i);
@@ -106,5 +106,5 @@
 			page = 0;
 			do {
-				ja = DrupalJSONQuery.getIndex("taxonomy_term", page);
+				ja = DrupalJSONQuery.getIndex("taxonomy_term", page, 100);
 				for (int i = 0; i < ja.length(); i++) {
 					jo = ja.getJSONObject(i);
@@ -122,5 +122,5 @@
 			page = 0;
 			do {
-				ja = DrupalJSONQuery.getIndex("node", page);
+				ja = DrupalJSONQuery.getIndex("node", page, 500);
 				for (int i = 0; i < ja.length(); i++) {
 					jo = ja.getJSONObject(i);
@@ -152,5 +152,5 @@
 			page = 0;
 			do {
-				ja = DrupalJSONQuery.getIndex("file", page);
+				ja = DrupalJSONQuery.getIndex("file", page, 500);
 				for (int i = 0; i < ja.length(); i++) {
 					jo = ja.getJSONObject(i);
Index: AE/installer2/src/net/oni2/aeinstaller/backend/mods/download/ModDownload.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/mods/download/ModDownload.java	(revision 604)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/mods/download/ModDownload.java	(revision 605)
@@ -96,4 +96,5 @@
 	 */
 	public void start() {
+		state = ModDownloadState.RUNNING;
 		downloader.start();
 	}
Index: AE/installer2/src/net/oni2/aeinstaller/backend/mods/download/ModDownloader.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/mods/download/ModDownloader.java	(revision 604)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/mods/download/ModDownloader.java	(revision 605)
@@ -67,5 +67,5 @@
 		currentDownload++;
 		downloadedCurrent = 0;
-		if (currentDownload < downloads.size()) {
+		if ((state == State.RUNNING) && (currentDownload < downloads.size())) {
 			downloads.get(currentDownload).start();
 		} else {
@@ -74,17 +74,20 @@
 	}
 
-	/**
-	 * @return Average download speed for up to now in B/s
-	 */
-	public int getDownloadSpeed() {
-		long duration = new Date().getTime() - startMS;
-		int down = downloadedComplete + downloadedCurrent;
-		return (int) (down * 1000 / duration);
+	private int getTimeElapsed() {
+		int total = (int) (new Date().getTime() - startMS)
+				/ 1000;
+		return total;
 	}
 
-	/**
-	 * @return Remaining time for all downloads by avg-speed in seconds
-	 */
-	public int getTimeRemaining() {
+	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);
@@ -94,5 +97,13 @@
 	private void notifyListener() {
 		listener.updateStatus(this, state, unpacked, downloads.size(),
-				downloadedComplete + downloadedCurrent, totalSize);
+				downloadedComplete + downloadedCurrent, totalSize,
+				getTimeElapsed(), getTimeRemaining(), getDownloadSpeed());
+	}
+
+	/**
+	 * @return total download size
+	 */
+	public int getTotalSize() {
+		return totalSize;
 	}
 
@@ -133,3 +144,14 @@
 	}
 
+	/**
+	 * 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/mods/download/ModDownloaderListener.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/mods/download/ModDownloaderListener.java	(revision 604)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/mods/download/ModDownloaderListener.java	(revision 605)
@@ -22,6 +22,13 @@
 	 * @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, State state, int filesDown,
-			int filesTotal, int bytesDown, int bytesTotal);
+			int filesTotal, int bytesDown, int bytesTotal, int duration,
+			int remaining, int speed);
 }
Index: AE/installer2/src/net/oni2/aeinstaller/backend/network/DrupalJSONQuery.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/network/DrupalJSONQuery.java	(revision 604)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/network/DrupalJSONQuery.java	(revision 605)
@@ -112,14 +112,20 @@
 	 *            Number of page to get (for limited results, e.g. nodes), -1 to
 	 *            ignore
+	 * @param pagesize
+	 *            Maximum number of elements to return
 	 * @return JSON structure of item
 	 * @throws Exception
 	 *             on HTTP error
 	 */
-	public static JSONArray getIndex(String resource, int page)
+	public static JSONArray getIndex(String resource, int page, int pagesize)
 			throws Exception {
 		String pageN = "";
 		if (page >= 0)
 			pageN = "&page=" + Integer.toString(page);
-		return executeQuery(getDepotUrl() + resource + ".json" + pageN, null);
+		String pagesizeN = "";
+		if (pagesize >= 0)
+			pagesizeN = "&pagesize=" + Integer.toString(pagesize);
+		return executeQuery(getDepotUrl() + resource + ".json" + pageN
+				+ pagesizeN, null);
 	}
 
Index: AE/installer2/src/net/oni2/aeinstaller/backend/network/FileDownloader.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/network/FileDownloader.java	(revision 604)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/network/FileDownloader.java	(revision 605)
@@ -212,5 +212,5 @@
 
 								i++;
-								if ((i % 10) == 0)
+								if ((i % 50) == 0)
 									updateStatus(downloaded, fileLength);
 							}
Index: AE/installer2/src/net/oni2/aeinstaller/backend/unpack/Unpacker.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/unpack/Unpacker.java	(revision 604)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/unpack/Unpacker.java	(revision 605)
@@ -117,4 +117,5 @@
 					try {
 						int pathStart = 0;
+						String pathStartName = "";
 
 						ZipFile zf = new ZipFile(zip);
@@ -124,7 +125,11 @@
 							ZipEntry ze = e.nextElement();
 							if (ze.getName().toLowerCase()
-									.endsWith("mod_info.cfg")) {
+									.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);
 							}
 						}
@@ -136,18 +141,21 @@
 							ZipEntry ze = e.nextElement();
 							if (!ze.isDirectory()) {
-								File targetFile = new File(target, ze.getName()
-										.substring(pathStart));
-								targetFile.getParentFile().mkdirs();
+								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);
+									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);
+									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();
 								}
-								fileOut.close();
 							}
 						}
