Index: /AE/installer2/src/net/oni2/aeinstaller/AEInstaller.properties
===================================================================
--- /AE/installer2/src/net/oni2/aeinstaller/AEInstaller.properties	(revision 604)
+++ /AE/installer2/src/net/oni2/aeinstaller/AEInstaller.properties	(revision 605)
@@ -1,4 +1,4 @@
 appname=AE Installer 2
-appversion=0.62
+appversion=0.70
 
 invalidPath.title=Wrong directory
Index: /AE/installer2/src/net/oni2/aeinstaller/Images.properties
===================================================================
--- /AE/installer2/src/net/oni2/aeinstaller/Images.properties	(revision 604)
+++ /AE/installer2/src/net/oni2/aeinstaller/Images.properties	(revision 605)
@@ -26,5 +26,6 @@
 img.refresh=/net/oni2/aeinstaller/images/tango/view-refresh.png
 img.tools=/net/oni2/aeinstaller/images/open_icon_library/tools-hammer_and_nails.png
+img.install=/net/oni2/aeinstaller/images/open_icon_library/run-build-install-root.png
 
 img.ae=/net/oni2/aeinstaller/images/AElogo.png
-img.install=/net/oni2/aeinstaller/images/open_icon_library/run-build-install-root.png
+img.oni=/net/oni2/aeinstaller/images/oni.png
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();
 							}
 						}
Index: /AE/installer2/src/net/oni2/aeinstaller/gui/MainWin.java
===================================================================
--- /AE/installer2/src/net/oni2/aeinstaller/gui/MainWin.java	(revision 604)
+++ /AE/installer2/src/net/oni2/aeinstaller/gui/MainWin.java	(revision 605)
@@ -2,9 +2,13 @@
 
 import java.io.File;
+import java.io.IOException;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.ResourceBundle;
 import java.util.TreeMap;
 import java.util.TreeSet;
+import java.util.Vector;
 
 import javax.swing.JButton;
@@ -42,4 +46,5 @@
 import net.oni2.aeinstaller.backend.oni.Installer;
 import net.oni2.aeinstaller.gui.about.AboutDialog;
+import net.oni2.aeinstaller.gui.downloadwindow.Downloader;
 import net.oni2.aeinstaller.gui.modtable.DownloadSizeListener;
 import net.oni2.aeinstaller.gui.modtable.ModTableFilter;
@@ -97,4 +102,5 @@
 
 		getRootPane().setDefaultButton(btnInstall);
+		lblDownloadSizeVal.setText(SizeFormatter.format(0, 2));
 	}
 
@@ -160,12 +166,4 @@
 	}
 
-	private boolean askClose() {
-		int res = JOptionPane.showConfirmDialog(this,
-				bundle.getString("askClose.text"),
-				bundle.getString("askClose.title"), JOptionPane.YES_NO_OPTION,
-				JOptionPane.QUESTION_MESSAGE);
-		return res == JOptionPane.YES_OPTION;
-	}
-
 	private void exit() {
 		setVisible(false);
@@ -319,5 +317,6 @@
 						public void updateStatus(ModDownloader source,
 								State state, int filesDown, int filesTotal,
-								int bytesDown, int bytesTotal) {
+								int bytesDown, int bytesTotal, int duration,
+								int remaining, int speed) {
 							evt.setProgressEnd(filesTotal);
 							evt.setProgressValue(filesDown);
@@ -339,24 +338,62 @@
 	@DoInBackground(progressMessage = "installing.title", cancelable = false, indeterminateProgress = false)
 	private void install(final BackgroundEvent evt) {
-		// TODO: Conflicts/Dependencies
-
 		TreeSet<Mod> mods = new TreeSet<Mod>();
 		mods.addAll(ModManager.getInstance().getMandatoryMods());
 		mods.addAll(model.getSelectedMods());
 
-		System.out.println("Install mods:");
-		for (Mod m : mods) {
-			System.out.println("  " + m.getPackageNumberString() + ": "
-					+ m.getName());
-		}
-
-		Installer.install(mods, new InstallProgressListener() {
-			@Override
-			public void installProgressUpdate(int done, int total, String step) {
-				evt.setProgressEnd(total);
-				evt.setProgressValue(done);
-				evt.setProgressMessage(step);
-			}
-		});
+		boolean instReady = false;
+
+		while (!instReady) {
+			System.out.println("Checking downloads:");
+			TreeSet<Mod> toDownload = new TreeSet<Mod>();
+			for (Mod m : mods) {
+				if (!m.isLocalAvailable())
+					toDownload.add(m);
+			}
+			if (toDownload.size() > 0) {
+				System.out.println("Download files: " + toDownload.toString());
+				Downloader dl = new Downloader(toDownload);
+				dl.setVisible(true);
+				if (!dl.isFinished())
+					break;
+			}
+			HashMap<Mod, HashSet<Mod>> dependencies = ModManager.getInstance()
+					.checkDependencies(mods);
+			if (dependencies.size() > 0) {
+				System.out.println("Unmet dependencies: "
+						+ dependencies.toString());
+				for (Mod m : dependencies.keySet()) {
+					for (Mod mDep : dependencies.get(m))
+						mods.add(mDep);
+				}
+			} else {
+				HashMap<Mod, HashSet<Mod>> conflicts = ModManager.getInstance()
+						.checkConflicts(mods);
+				if (conflicts.size() > 0) {
+					System.err.println("Conflicting mods: "
+							+ conflicts.toString());
+					break;
+				} else {
+					instReady = true;
+				}
+			}
+		}
+
+		if (instReady) {
+			Installer.install(mods, new InstallProgressListener() {
+				@Override
+				public void installProgressUpdate(int done, int total,
+						String step) {
+					evt.setProgressEnd(total);
+					evt.setProgressValue(done);
+					evt.setProgressMessage(step);
+				}
+			});
+		}
+
+		JOptionPane.showMessageDialog(this,
+				bundle.getString("installDone.text"),
+				bundle.getString("installDone.title"),
+				JOptionPane.INFORMATION_MESSAGE);
 	}
 
@@ -409,4 +446,58 @@
 		}
 	}
+	
+	private Vector<String> getBasicOniLaunchParams() {
+		Vector<String> params = new Vector<String>();
+		switch (Settings.getPlatform()) {
+			case WIN:
+				params.add(new File(Paths.getEditionBasePath(), "Oni.exe")
+						.getPath());
+				break;
+			case MACOS:
+				params.add(new File(Paths.getEditionBasePath(), "Oni")
+						.getPath());
+				break;
+			case LINUX:
+				String wine = Settings.getWinePath();
+				if (wine != null) {
+					params.add(wine);
+					params.add(new File(Paths.getEditionBasePath(), "Oni.exe")
+							.getPath());
+				}
+				break;
+			default:
+		}
+		if (params.size() > 0) {
+			params.add("-debugfiles");
+		}
+		return params;
+	}
+
+	@SuppressWarnings("unused")
+	private void oniFull() {
+		Vector<String> params = getBasicOniLaunchParams();
+		if (params.size() > 0) {
+			try {
+				new ProcessBuilder(params).start();
+			} catch (IOException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+		}
+	}
+
+	@SuppressWarnings("unused")
+	private void oniWin() {
+		Vector<String> params = getBasicOniLaunchParams();
+		if (params.size() > 0) {
+			params.add("-noswitch");
+			try {
+				new ProcessBuilder(params).start();
+			} catch (IOException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+		}
+	}
 
 	@Override
@@ -435,11 +526,7 @@
 	@Override
 	public void handleQuit(ApplicationEvent event) {
-		if (askClose()) {
-			event.setHandled(true);
-			saveLocalData();
-			exit();
-		} else {
-			event.setHandled(false);
-		}
+		event.setHandled(true);
+		saveLocalData();
+		exit();
 	}
 
Index: /AE/installer2/src/net/oni2/aeinstaller/gui/MainWin.properties
===================================================================
--- /AE/installer2/src/net/oni2/aeinstaller/gui/MainWin.properties	(revision 604)
+++ /AE/installer2/src/net/oni2/aeinstaller/gui/MainWin.properties	(revision 605)
@@ -11,4 +11,8 @@
 
 menu.file=&File
+menu.runOniFull=Run &Oni (fullscreen)
+menu.runOniFullTooltip=Run Oni in fullscreen mode
+menu.runOniWin=Run Oni (&window)
+menu.runOniWinTooltip=Run Oni in windowed mode
 menu.loadConfig=&Load configuration...
 menu.loadConfigTooltip=Load configuration
@@ -35,9 +39,9 @@
 updateDepot.title=Updating Mod Depot cache
 
+installDone.title=Installation done
+installDone.text=You can now play AE Oni.
+
 updatesAvailable.title=Updates available
 updatesAvailable.text=Some mods have newer versions available.
-
-askClose.title=Confirm close request
-askClose.text=Are you sure you want to close the program?
 
 askInitialize.title=Initialize Edition
Index: /AE/installer2/src/net/oni2/aeinstaller/gui/MainWin.yml
===================================================================
--- /AE/installer2/src/net/oni2/aeinstaller/gui/MainWin.yml	(revision 604)
+++ /AE/installer2/src/net/oni2/aeinstaller/gui/MainWin.yml	(revision 605)
@@ -7,10 +7,12 @@
   defaultCloseOperation: doNothingOnClose
   onWindowOpened: [execDepotUpdate,checkMandatoryFiles,initialize,checkUpdates,focus]
-  onWindowClosing: [askClose,saveLocalData,exit]
+  onWindowClosing: [saveLocalData,exit]
   iconImage: img.ae
   content:
-    - Action(name=exitAction, text=menu.exit, toolTipText=menu.exitTooltip, icon=img.exit, onAction=[askClose,saveLocalData,exit])
+    - Action(name=exitAction, text=menu.exit, toolTipText=menu.exitTooltip, icon=img.exit, onAction=[saveLocalData,exit])
     - Action(name=settings, text=menu.settings, toolTipText=menu.settingsTooltip, icon=img.settings, onAction=[showSettings])
     - Action(name=about, text=menu.about, toolTipText=menu.aboutTooltip, icon=img.about, onAction=[showAbout])
+    - Action(name=runOniFull, text=menu.runOniFull, toolTipText=menu.runOniFullTooltip, icon=img.oni, onAction=[oniFull])
+    - Action(name=runOniWin, text=menu.runOniWin, toolTipText=menu.runOniWinTooltip, icon=img.oni, onAction=[oniWin])
     - Action(name=loadConfig, text=menu.loadConfig, toolTipText=menu.loadConfigTooltip, icon=img.openFile, onAction=[loadConfig])
     - Action(name=saveConfig, text=menu.saveConfig, toolTipText=menu.saveConfigTooltip, icon=img.saveFile, onAction=[saveConfig])
@@ -23,4 +25,7 @@
             - JMenuItem(action=exitAction)
         - JMenu(name=fileMenu, text=menu.file):
+            - JMenuItem(action=runOniFull)
+            - JMenuItem(action=runOniWin)
+            - JSeparator()
             - JMenuItem(action=loadConfig)
             - JMenuItem(action=saveConfig)
Index: /AE/installer2/src/net/oni2/aeinstaller/gui/downloadwindow/Downloader.java
===================================================================
--- /AE/installer2/src/net/oni2/aeinstaller/gui/downloadwindow/Downloader.java	(revision 605)
+++ /AE/installer2/src/net/oni2/aeinstaller/gui/downloadwindow/Downloader.java	(revision 605)
@@ -0,0 +1,109 @@
+package net.oni2.aeinstaller.gui.downloadwindow;
+
+import java.awt.Dimension;
+import java.util.ResourceBundle;
+import java.util.TreeSet;
+
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JProgressBar;
+
+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 org.javabuilders.BuildResult;
+import org.javabuilders.swing.SwingJavaBuilder;
+
+/**
+ * @author Christian Illy
+ */
+public class Downloader extends JDialog implements ModDownloaderListener {
+	private static final long serialVersionUID = 9097967828001263776L;
+
+	private ResourceBundle bundle = ResourceBundle.getBundle(getClass()
+			.getName());
+	@SuppressWarnings("unused")
+	private BuildResult result = SwingJavaBuilder.build(this, bundle);
+
+	private JLabel lblElapsedVal;
+	private JLabel lblRemainingVal;
+	private JLabel lblDownloadedVal;
+	private JLabel lblTotalVal;
+	private JLabel lblRateVal;
+	private JProgressBar progress;
+
+	private ModDownloader downloader;
+
+	/**
+	 * @param mods
+	 *            Mods to download
+	 */
+	public Downloader(TreeSet<Mod> mods) {
+		super();
+
+		setMinimumSize(new Dimension(350, (int) getSize().getHeight()));
+		setSize(450, (int) getSize().getHeight());
+
+		downloader = new ModDownloader(mods, this);
+		progress.setMaximum(downloader.getTotalSize());
+		progress.setStringPainted(true);
+		progress.setToolTipText(String.format("%d / %d files downloaded", 0,
+				mods.size()));
+
+		lblDownloadedVal.setText(SizeFormatter.format(0, 3));
+		lblTotalVal.setText(SizeFormatter.format(downloader.getTotalSize(), 3));
+	}
+
+	private void close() {
+		if (!downloader.isFinished())
+			downloader.abort();
+		setVisible(false);
+	}
+
+	@SuppressWarnings("unused")
+	private boolean confirm() {
+		int res = JOptionPane.showConfirmDialog(this,
+				bundle.getString("abort.text"),
+				bundle.getString("abort.title"), JOptionPane.YES_NO_OPTION,
+				JOptionPane.WARNING_MESSAGE);
+		return res == JOptionPane.YES_OPTION;
+	}
+
+	private String formatTime(int sec) {
+		int min = sec / 60;
+		sec = sec % 60;
+		return String.format("%02d:%02d", min, sec);
+	}
+
+	@Override
+	public void updateStatus(ModDownloader source, State state, int filesDown,
+			int filesTotal, int bytesDown, int bytesTotal, int duration,
+			int remaining, int speed) {
+		if (state == ModDownloader.State.FINISHED) {
+			close();
+		} else {
+			progress.setValue(bytesDown);
+			progress.setToolTipText(String.format("%d / %d files downloaded",
+					filesDown, filesTotal));
+
+			lblElapsedVal.setText(formatTime(duration));
+			lblRemainingVal.setText(formatTime(remaining));
+
+			lblDownloadedVal.setText(SizeFormatter.format(bytesDown, 3));
+
+			lblRateVal.setText(SizeFormatter.format(speed, 3) + "/s");
+		}
+	}
+
+	/**
+	 * @return were all downloads finished?
+	 */
+	public boolean isFinished() {
+		return downloader.isFinished();
+	}
+
+}
Index: /AE/installer2/src/net/oni2/aeinstaller/gui/downloadwindow/Downloader.properties
===================================================================
--- /AE/installer2/src/net/oni2/aeinstaller/gui/downloadwindow/Downloader.properties	(revision 605)
+++ /AE/installer2/src/net/oni2/aeinstaller/gui/downloadwindow/Downloader.properties	(revision 605)
@@ -0,0 +1,11 @@
+frame.title=AE Installer: Downloading mods
+
+btnAbort.title=Abort
+lblElapsed.title=Elpased:
+lblRemaining.title=Remaining:
+lblDownloaded.title=Downloaded:
+lblTotal.title=Total:
+lblRate.title=Rate:
+
+abort.text=If you abort the download now the installation of mods will also be aborted.\nDo you want to abort now?
+abort.title=Really abort?
Index: /AE/installer2/src/net/oni2/aeinstaller/gui/downloadwindow/Downloader.yml
===================================================================
--- /AE/installer2/src/net/oni2/aeinstaller/gui/downloadwindow/Downloader.yml	(revision 605)
+++ /AE/installer2/src/net/oni2/aeinstaller/gui/downloadwindow/Downloader.yml	(revision 605)
@@ -0,0 +1,29 @@
+JDialog:
+  name: frame
+  title: frame.title
+  size: packed
+  locationRelativeTo: null
+  defaultCloseOperation: doNothingOnClose
+  onWindowClosing: [confirm,close]
+  iconImage: img.ae
+  modalityType: applicationModal
+  content:
+    - JButton(name=btnAbort, text=btnAbort.title, icon=img.stop, onAction=[confirm,close])
+    - JLabel(name=lblElapsed, text=lblElapsed.title)
+    - JLabel(name=lblElapsedVal)
+    - JLabel(name=lblRemaining, text=lblRemaining.title)
+    - JLabel(name=lblRemainingVal)
+    - JLabel(name=lblRate, text=lblRate.title)
+    - JLabel(name=lblRateVal)
+    - JLabel(name=lblDownloaded, text=lblDownloaded.title)
+    - JLabel(name=lblDownloadedVal)
+    - JLabel(name=lblTotal, text=lblTotal.title)
+    - JLabel(name=lblTotalVal)
+    - JProgressBar(name=progress)
+    - MigLayout: |
+         [grow]
+         lblElapsed=1,lblElapsedVal=2,lblRemaining=1,lblRemainingVal=2   [min]
+         lblDownloaded=1,lblDownloadedVal=2,lblTotal=1,lblTotalVal=2     [min]
+         lblRate=1,lblRateVal=2                                          [min]
+         progress                                                        [min]
+         >btnAbort^                                                      [min]
Index: /AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModTableModel.java
===================================================================
--- /AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModTableModel.java	(revision 604)
+++ /AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModTableModel.java	(revision 605)
@@ -217,5 +217,5 @@
 				if (install.get(i)) {
 					Mod m = items.get(i);
-					if (!m.isLocalAvailable() || m.isNewerAvailable())
+					if (!m.isLocalAvailable())
 						size += m.getZipSize();
 				}
