Index: AE/installer2/setup_win/AEI.iss
===================================================================
--- AE/installer2/setup_win/AEI.iss	(revision 701)
+++ AE/installer2/setup_win/AEI.iss	(revision 702)
@@ -1,4 +1,4 @@
 #define AppId "{{B67333BB-1CF9-4EFD-A40B-E25B5CB4C8A7}}"
-#define AppVersion "0.99p"
+#define AppVersion "0.99t"
 #define AppLongName "Anniversary Edition of Oni"
 #define AppShortName "AEInstaller"
@@ -47,17 +47,17 @@
 
 [Dirs]
-Name: "{app}\Edition"; Permissions: users-modify
+Name: "{app}\AE"; Permissions: users-modify
  
 [Files]
-Source: "AEInstaller2.jar"; DestDir: "{app}\Edition\AEInstaller"; Components: AEI
-Source: "JRE\*"; DestDir: "{app}\Edition\AEInstaller\JRE"; Excludes: ".svn"; Flags: createallsubdirs recursesubdirs onlyifdoesntexist; Components: JRE
-Source: "AElogo.ico"; DestDir: "{app}\Edition\AEInstaller"; Components: AEI
-Source: "..\locales\*"; DestDir: "{app}\Edition\AEInstaller\locales"; Excludes: ".svn"; Flags: createallsubdirs recursesubdirs onlyifdoesntexist; Components: AEI
+Source: "AEInstaller2.jar"; DestDir: "{app}\AE\AEInstaller"; Components: AEI
+Source: "JRE\*"; DestDir: "{app}\AE\AEInstaller\JRE"; Excludes: ".svn"; Flags: createallsubdirs recursesubdirs onlyifdoesntexist; Components: JRE
+Source: "AElogo.ico"; DestDir: "{app}\AE\AEInstaller"; Components: AEI
+Source: "..\locales\*"; DestDir: "{app}\AE\AEInstaller\locales"; Excludes: ".svn"; Flags: createallsubdirs recursesubdirs onlyifdoesntexist; Components: AEI
 
 [Icons]
-Name: "{group}\AEInstaller 2"; Filename: "{app}\Edition\AEInstaller\JRE\bin\javaw.exe"; WorkingDir: "{app}\Edition\AEInstaller"; IconFilename: "{app}\Edition\AEInstaller\AElogo.ico"; Parameters: "-Dsun.java2d.d3d=false -jar AEInstaller2.jar"; Check: not IsJavaInstalled
-Name: "{commondesktop}\AEInstaller 2"; Filename: "{app}\Edition\AEInstaller\JRE\bin\javaw.exe"; WorkingDir: "{app}\Edition\AEInstaller"; IconFilename: "{app}\Edition\AEInstaller\AElogo.ico"; Parameters: "-Dsun.java2d.d3d=false -jar AEInstaller2.jar"; Tasks: desktopicon; Check: not IsJavaInstalled
-Name: "{group}\AEInstaller 2"; Filename: "{code:GetJavaPath}\bin\javaw.exe"; WorkingDir: "{app}\Edition\AEInstaller"; IconFilename: "{app}\Edition\AEInstaller\AElogo.ico"; Parameters: "-Dsun.java2d.d3d=false -jar AEInstaller2.jar"; Check: IsJavaInstalled
-Name: "{commondesktop}\AEInstaller 2"; Filename: "{code:GetJavaPath}\bin\javaw.exe"; WorkingDir: "{app}\Edition\AEInstaller"; IconFilename: "{app}\Edition\AEInstaller\AElogo.ico"; Parameters: "-Dsun.java2d.d3d=false -jar AEInstaller2.jar"; Tasks: desktopicon; Check: IsJavaInstalled
+Name: "{group}\AEInstaller 2"; Filename: "{app}\AE\AEInstaller\JRE\bin\javaw.exe"; WorkingDir: "{app}\AE\AEInstaller"; IconFilename: "{app}\AE\AEInstaller\AElogo.ico"; Parameters: "-Dsun.java2d.d3d=false -jar AEInstaller2.jar"; Check: not IsJavaInstalled
+Name: "{commondesktop}\AEInstaller 2"; Filename: "{app}\AE\AEInstaller\JRE\bin\javaw.exe"; WorkingDir: "{app}\AE\AEInstaller"; IconFilename: "{app}\AE\AEInstaller\AElogo.ico"; Parameters: "-Dsun.java2d.d3d=false -jar AEInstaller2.jar"; Tasks: desktopicon; Check: not IsJavaInstalled
+Name: "{group}\AEInstaller 2"; Filename: "{code:GetJavaPath}\bin\javaw.exe"; WorkingDir: "{app}\AE\AEInstaller"; IconFilename: "{app}\AE\AEInstaller\AElogo.ico"; Parameters: "-Dsun.java2d.d3d=false -jar AEInstaller2.jar"; Check: IsJavaInstalled
+Name: "{commondesktop}\AEInstaller 2"; Filename: "{code:GetJavaPath}\bin\javaw.exe"; WorkingDir: "{app}\AE\AEInstaller"; IconFilename: "{app}\AE\AEInstaller\AElogo.ico"; Parameters: "-Dsun.java2d.d3d=false -jar AEInstaller2.jar"; Tasks: desktopicon; Check: IsJavaInstalled
 
 
Index: AE/installer2/setup_win/AEI_no_jre.iss
===================================================================
--- AE/installer2/setup_win/AEI_no_jre.iss	(revision 701)
+++ AE/installer2/setup_win/AEI_no_jre.iss	(revision 702)
@@ -1,4 +1,4 @@
 #define AppId "{{B67333BB-1CF9-4EFD-A40B-E25B5CB4C8A7}}"
-#define AppVersion "0.99p"
+#define AppVersion "0.99t"
 #define AppLongName "Anniversary Edition of Oni"
 #define AppShortName "AEInstaller"
@@ -43,14 +43,14 @@
 
 [Dirs]
-Name: "{app}\Edition"; Permissions: users-modify
+Name: "{app}\AE"; Permissions: users-modify
  
 [Files]
-Source: "AEInstaller2.jar"; DestDir: "{app}\Edition\AEInstaller"
-Source: "AElogo.ico"; DestDir: "{app}\Edition\AEInstaller"
-Source: "..\locales\*"; DestDir: "{app}\Edition\AEInstaller\locales"; Excludes: ".svn"; Flags: createallsubdirs recursesubdirs onlyifdoesntexist
+Source: "AEInstaller2.jar"; DestDir: "{app}\AE\AEInstaller"
+Source: "AElogo.ico"; DestDir: "{app}\AE\AEInstaller"
+Source: "..\locales\*"; DestDir: "{app}\AE\AEInstaller\locales"; Excludes: ".svn"; Flags: createallsubdirs recursesubdirs onlyifdoesntexist
 
 [Icons]
-Name: "{group}\AEInstaller 2"; Filename: "{code:GetJavaPath}\bin\javaw.exe"; WorkingDir: "{app}\Edition\AEInstaller"; IconFilename: "{app}\Edition\AEInstaller\AElogo.ico"; Parameters: "-Dsun.java2d.d3d=false -jar AEInstaller2.jar"; Check: IsJavaInstalled
-Name: "{commondesktop}\AEInstaller 2"; Filename: "{code:GetJavaPath}\bin\javaw.exe"; WorkingDir: "{app}\Edition\AEInstaller"; IconFilename: "{app}\Edition\AEInstaller\AElogo.ico"; Parameters: "-Dsun.java2d.d3d=false -jar AEInstaller2.jar"; Tasks: desktopicon; Check: IsJavaInstalled
+Name: "{group}\AEInstaller 2"; Filename: "{code:GetJavaPath}\bin\javaw.exe"; WorkingDir: "{app}\AE\AEInstaller"; IconFilename: "{app}\AE\AEInstaller\AElogo.ico"; Parameters: "-Dsun.java2d.d3d=false -jar AEInstaller2.jar"; Check: IsJavaInstalled
+Name: "{commondesktop}\AEInstaller 2"; Filename: "{code:GetJavaPath}\bin\javaw.exe"; WorkingDir: "{app}\AE\AEInstaller"; IconFilename: "{app}\AE\AEInstaller\AElogo.ico"; Parameters: "-Dsun.java2d.d3d=false -jar AEInstaller2.jar"; Tasks: desktopicon; Check: IsJavaInstalled
 
 
Index: AE/installer2/src/net/oni2/aeinstaller/backend/AppExecution.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/AppExecution.java	(revision 701)
+++ 	(revision )
@@ -1,62 +1,0 @@
-package net.oni2.aeinstaller.backend;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.List;
-import java.util.Vector;
-
-/**
- * @author Christian Illy
- */
-public class AppExecution {
-
-	/**
-	 * Execute a short running application and wait for termination
-	 * 
-	 * @param cmdLine
-	 *            List of command and arguments
-	 * @return List of output lines
-	 * @throws IOException
-	 *             Exc
-	 */
-	public static Vector<String> executeAndWait(List<String> cmdLine)
-			throws IOException {
-		ProcessBuilder pb = new ProcessBuilder(cmdLine);
-		pb.redirectErrorStream(true);
-		Process proc = pb.start();
-
-		InputStream is = proc.getInputStream();
-		InputStreamReader isr = new InputStreamReader(is);
-		BufferedReader br = new BufferedReader(isr);
-
-		String line;
-		Vector<String> lines = new Vector<String>();
-
-		while ((line = br.readLine()) != null) {
-			lines.add(line);
-		}
-		return lines;
-	}
-
-	/**
-	 * Execute an app without waiting
-	 * 
-	 * @param cmd
-	 *            Command and parameters
-	 * @param workingDirectory
-	 *            Working directory of app
-	 */
-	public static void execute(List<String> cmd, File workingDirectory) {
-		try {
-			ProcessBuilder pb = new ProcessBuilder(cmd);
-			pb.directory(workingDirectory);
-			pb.start();
-		} catch (IOException e) {
-			e.printStackTrace();
-		}
-	}
-
-}
Index: AE/installer2/src/net/oni2/aeinstaller/backend/DotNet.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/DotNet.java	(revision 701)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/DotNet.java	(revision 702)
@@ -8,4 +8,6 @@
 import net.oni2.aeinstaller.backend.Settings.Architecture;
 import net.oni2.aeinstaller.backend.Settings.Platform;
+import net.oni2.aeinstaller.backend.appexecution.AppExecution;
+import net.oni2.aeinstaller.backend.appexecution.AppExecutionResult;
 
 /**
@@ -48,5 +50,5 @@
 				cmd.add("which");
 				cmd.add("mono");
-				Vector<String> res = null;
+				AppExecutionResult res = null;
 				try {
 					res = AppExecution.executeAndWait(cmd);
@@ -55,6 +57,6 @@
 				}
 				if (res != null) {
-					if (res.get(0).startsWith("/")
-							&& res.get(0).endsWith("mono")) {
+					if (res.output.get(0).startsWith("/")
+							&& res.output.get(0).endsWith("mono")) {
 						return true;
 					}
Index: AE/installer2/src/net/oni2/aeinstaller/backend/Settings.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/Settings.java	(revision 701)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/Settings.java	(revision 702)
@@ -10,4 +10,7 @@
 import java.util.Vector;
 
+import net.oni2.aeinstaller.backend.appexecution.AppExecution;
+import net.oni2.aeinstaller.backend.appexecution.AppExecutionResult;
+
 import com.thoughtworks.xstream.XStream;
 import com.thoughtworks.xstream.io.xml.StaxDriver;
@@ -79,5 +82,5 @@
 		cmd.add("which");
 		cmd.add("wine");
-		Vector<String> res = null;
+		AppExecutionResult res = null;
 		try {
 			res = AppExecution.executeAndWait(cmd);
@@ -86,6 +89,6 @@
 		}
 		if (res != null) {
-			if (res.get(0).startsWith("/") && res.get(0).endsWith("wine")) {
-				return res.get(0);
+			if (res.output.get(0).startsWith("/") && res.output.get(0).endsWith("wine")) {
+				return res.output.get(0);
 			}
 		}
@@ -133,5 +136,5 @@
 				cmd.add("getconf");
 				cmd.add("LONG_BIT");
-				Vector<String> res = null;
+				AppExecutionResult res = null;
 				try {
 					res = AppExecution.executeAndWait(cmd);
@@ -140,5 +143,5 @@
 				}
 				if (res != null) {
-					if (res.get(0).equals("64"))
+					if (res.output.get(0).equals("64"))
 						return Architecture.AMD64;
 				}
Index: AE/installer2/src/net/oni2/aeinstaller/backend/ToolLauncher.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/ToolLauncher.java	(revision 701)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/ToolLauncher.java	(revision 702)
@@ -5,4 +5,5 @@
 
 import net.oni2.aeinstaller.backend.Settings.Platform;
+import net.oni2.aeinstaller.backend.appexecution.AppExecution;
 import net.oni2.aeinstaller.backend.packages.Package;
 
Index: AE/installer2/src/net/oni2/aeinstaller/backend/appexecution/AppExecution.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/appexecution/AppExecution.java	(revision 702)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/appexecution/AppExecution.java	(revision 702)
@@ -0,0 +1,67 @@
+package net.oni2.aeinstaller.backend.appexecution;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.List;
+import java.util.Vector;
+
+/**
+ * @author Christian Illy
+ */
+public class AppExecution {
+	/**
+	 * Execute a short running application and wait for termination
+	 * 
+	 * @param cmdLine
+	 *            List of command and arguments
+	 * @return Error code and list of output lines
+	 * @throws IOException
+	 *             Exc
+	 */
+	public static AppExecutionResult executeAndWait(Vector<String> cmdLine)
+			throws IOException {
+		ProcessBuilder pb = new ProcessBuilder(cmdLine);
+		pb.redirectErrorStream(true);
+		Process proc = pb.start();
+
+		InputStream is = proc.getInputStream();
+		InputStreamReader isr = new InputStreamReader(is);
+		BufferedReader br = new BufferedReader(isr);
+
+		String line;
+		Vector<String> lines = new Vector<String>();
+
+		while ((line = br.readLine()) != null) {
+			lines.add(line);
+		}
+		try {
+			proc.waitFor();
+		} catch (InterruptedException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		return new AppExecutionResult(proc.exitValue(), cmdLine, lines);
+	}
+
+	/**
+	 * Execute an app without waiting
+	 * 
+	 * @param cmd
+	 *            Command and parameters
+	 * @param workingDirectory
+	 *            Working directory of app
+	 */
+	public static void execute(List<String> cmd, File workingDirectory) {
+		try {
+			ProcessBuilder pb = new ProcessBuilder(cmd);
+			pb.directory(workingDirectory);
+			pb.start();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+
+}
Index: AE/installer2/src/net/oni2/aeinstaller/backend/appexecution/AppExecutionResult.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/appexecution/AppExecutionResult.java	(revision 702)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/appexecution/AppExecutionResult.java	(revision 702)
@@ -0,0 +1,36 @@
+package net.oni2.aeinstaller.backend.appexecution;
+
+import java.util.Vector;
+
+/**
+ * @author Christian Illy
+ */
+public class AppExecutionResult {
+	/**
+	 * Returned error code
+	 */
+	public int errorCode;
+	/**
+	 * Call command line
+	 */
+	public Vector<String> cmdLine;
+	/**
+	 * Combined StdOut+ErrOut lines
+	 */
+	public Vector<String> output;
+
+	/**
+	 * @param errorCode
+	 *            Error code from executed program
+	 * @param cmdLine
+	 *            Commandline of call
+	 * @param output
+	 *            Output of executed program
+	 */
+	public AppExecutionResult(int errorCode, Vector<String> cmdLine,
+			Vector<String> output) {
+		this.errorCode = errorCode;
+		this.cmdLine = cmdLine;
+		this.output = output;
+	}
+}
Index: AE/installer2/src/net/oni2/aeinstaller/backend/oni/Installer.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/oni/Installer.java	(revision 701)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/oni/Installer.java	(revision 702)
@@ -26,4 +26,5 @@
 import net.oni2.aeinstaller.backend.Settings;
 import net.oni2.aeinstaller.backend.Settings.Platform;
+import net.oni2.aeinstaller.backend.appexecution.AppExecutionResult;
 import net.oni2.aeinstaller.backend.packages.EBSLInstallType;
 import net.oni2.aeinstaller.backend.packages.Package;
@@ -164,6 +165,5 @@
 				else {
 					File targetFile = CaseInsensitiveFile
-							.getCaseInsensitiveFile(targetFolder,
-									f.getName());
+							.getCaseInsensitiveFile(targetFolder, f.getName());
 					if (remove) {
 						if (targetFile.exists())
@@ -464,11 +464,16 @@
 			List<File> patchFolders, InstallProgressListener listener,
 			PrintWriter log) {
+		log.println();
+		log.println("Applying XML patches");
+		listener.installProgressUpdate(0, 1, "Applying XML patches");
+
 		// TODO: REMOVE
 		long startMS = new Date().getTime();
-		// TODO: Implement this
+
 		String tmpFolderName = "installrun_temp-"
 				+ new SimpleDateFormat("yyyy_MM_dd-HH_mm_ss")
 						.format(new Date());
 		File tmpFolder = new File(Paths.getTempPath(), tmpFolderName);
+		tmpFolder.mkdir();
 
 		HashMap<String, Vector<File>> patches = new HashMap<String, Vector<File>>();
@@ -488,6 +493,9 @@
 			File levelFolder = new File(tmpFolder, level);
 			levelFolder.mkdir();
-
-			// Get files to be patched from vanilla.dat / packages
+			
+			log.println("\t\tPatches for " + level);
+
+			Vector<String> exportPatterns = new Vector<String>();
+			// Get files to be patched from vanilla.dat
 			for (File patch : patches.get(level)) {
 				String patternWildcard = patch.getName();
@@ -495,4 +503,26 @@
 						patternWildcard.indexOf(".oni-patch"));
 				patternWildcard = patternWildcard.replace('-', '*');
+				exportPatterns.add(patternWildcard);
+			}
+			for (File srcFolder : oniLevelFolders.get(level)) {
+				if (srcFolder.isFile()) {
+					if (srcFolder.getPath().toLowerCase().contains("vanilla")) {
+						// Extract from .dat
+						AppExecutionResult res = OniSplit.export(levelFolder,
+								srcFolder, exportPatterns);
+						logAppOutput(res, log);
+					}
+				}
+			}
+
+			// Get files to be patched from packages
+			for (File patch : patches.get(level)) {
+				String patternWildcard = patch.getName();
+				patternWildcard = patternWildcard.substring(0,
+						patternWildcard.indexOf(".oni-patch"));
+				patternWildcard = patternWildcard.replace('-', '*');
+				patternWildcard = patternWildcard + ".oni";
+				Vector<String> patterns = new Vector<String>();
+				patterns.add(patternWildcard);
 				final Pattern patternRegex = Pattern.compile(
 						patternWildcard.replaceAll("\\*", ".\\*"),
@@ -501,6 +531,11 @@
 				for (File srcFolder : oniLevelFolders.get(level)) {
 					if (srcFolder.isFile()) {
-						// Extract from .dat
-						OniSplit.export(levelFolder, srcFolder, patternWildcard);
+						if (!srcFolder.getPath().toLowerCase()
+								.contains("vanilla")) {
+							// Extract from .dat
+							AppExecutionResult res = OniSplit.export(
+									levelFolder, srcFolder, patterns);
+							logAppOutput(res, log);
+						}
 					} else {
 						// Copy from folder with overwrite
@@ -522,5 +557,7 @@
 			Vector<File> files = new Vector<File>();
 			files.add(new File(levelFolder, "*.oni"));
-			OniSplit.convertOniToXML(levelFolderXML, files);
+			AppExecutionResult res = OniSplit.convertOniToXML(levelFolderXML,
+					files);
+			logAppOutput(res, log);
 
 			// Apply patches in levelFolderXML
@@ -529,14 +566,10 @@
 				patternWildcard = patternWildcard.substring(0,
 						patternWildcard.indexOf(".oni-patch"));
+				patternWildcard = patternWildcard + ".xml";
 				patternWildcard = patternWildcard.replace('-', '*');
-				final Pattern patternRegex = Pattern.compile(
-						patternWildcard.replaceAll("\\*", ".\\*"),
-						Pattern.CASE_INSENSITIVE);
-
-				for (File toPatch : FileUtils.listFiles(levelFolderXML,
-						new RegexFileFilter(patternRegex), null)) {
-					// Apply patch "patch" to "toPatch"
-					XMLTools.patch(patch, toPatch);
-				}
+
+				res = XMLTools.patch(patch, new File(levelFolderXML,
+						patternWildcard));
+				logAppOutput(res, log);
 			}
 
@@ -544,14 +577,17 @@
 			files.clear();
 			files.add(new File(levelFolderXML, "*.xml"));
-			OniSplit.convertXMLtoOni(levelFolder, files);
+			res = OniSplit.convertXMLtoOni(levelFolder, files);
+			logAppOutput(res, log);
 
 			// Remove XML folder as import will only require .oni's
-			try {
-				FileUtils.deleteDirectory(levelFolderXML);
-			} catch (IOException e) {
-				e.printStackTrace();
-			}
+//			try {
+//				FileUtils.deleteDirectory(levelFolderXML);
+//			} catch (IOException e) {
+//				e.printStackTrace();
+//			}
 
 			oniLevelFolders.get(level).add(levelFolder);
+			
+			log.println();
 		}
 
@@ -571,4 +607,5 @@
 		totalSteps++;
 
+		log.println();
 		log.println("Importing levels");
 		for (String l : oniLevelFolders.keySet()) {
@@ -576,15 +613,9 @@
 			listener.installProgressUpdate(stepsDone, totalSteps,
 					"Installing level " + l);
-			for (File f : oniLevelFolders.get(l)) {
-				log.println("\t\t\t" + f.getPath());
-			}
-
-			Vector<String> res = OniSplit.packLevel(oniLevelFolders.get(l),
+
+			AppExecutionResult res = OniSplit.packLevel(oniLevelFolders.get(l),
 					new File(Paths.getEditionGDF(), sanitizeLevelName(l)
 							+ ".dat"));
-			if (res != null && res.size() > 0) {
-				for (String s : res)
-					log.println("\t\t" + s);
-			}
+			logAppOutput(res, log);
 
 			log.println();
@@ -604,4 +635,6 @@
 				}
 			}
+		} else {
+			log.println("NOT copying intro");
 		}
 		if (Settings.getInstance().get("copyoutro", true)) {
@@ -615,4 +648,6 @@
 				}
 			}
+		} else {
+			log.println("NOT copying outro");
 		}
 	}
@@ -704,9 +739,6 @@
 
 				// Export Vanilla-Level-Dat -> Temp/Level
-				Vector<String> res = OniSplit.export(tempLevelFolder, f);
-				if (res != null && res.size() > 0) {
-					for (String s : res)
-						log.println("\t\t\t" + s);
-				}
+				AppExecutionResult res = OniSplit.export(tempLevelFolder, f);
+				logAppOutput(res, log);
 
 				log.println("\t\tMoving files");
@@ -729,10 +761,9 @@
 				folders.add(f);
 
-				Vector<String> res = OniSplit.importLevel(folders, new File(
-						Paths.getVanillaOnisPath(), levelName + ".dat"));
-				if (res != null && res.size() > 0) {
-					for (String s : res)
-						log.println("\t\t" + s);
-				}
+				AppExecutionResult res = OniSplit
+						.importLevel(folders,
+								new File(Paths.getVanillaOnisPath(), levelName
+										+ ".dat"));
+				logAppOutput(res, log);
 
 				log.println();
@@ -888,3 +919,15 @@
 				&& Paths.getVanillaGDF().isDirectory();
 	}
+
+	private static void logAppOutput(AppExecutionResult result, PrintWriter log) {
+		if (result != null) {
+			log.println("\t\t\tCalled:");
+			for (String s : result.cmdLine)
+				log.println("\t\t\t\t" + s);
+			log.println("\t\t\tReturned: " + result.errorCode);
+			for (String s : result.output)
+				log.println("\t\t\t\t" + s);
+			log.println();
+		}
+	}
 }
Index: AE/installer2/src/net/oni2/aeinstaller/backend/oni/OniSplit.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/oni/OniSplit.java	(revision 701)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/oni/OniSplit.java	(revision 702)
@@ -5,5 +5,4 @@
 import java.util.Vector;
 
-import net.oni2.aeinstaller.backend.AppExecution;
 import net.oni2.aeinstaller.backend.CaseInsensitiveFile;
 import net.oni2.aeinstaller.backend.DotNet;
@@ -11,4 +10,6 @@
 import net.oni2.aeinstaller.backend.Settings;
 import net.oni2.aeinstaller.backend.Settings.Platform;
+import net.oni2.aeinstaller.backend.appexecution.AppExecution;
+import net.oni2.aeinstaller.backend.appexecution.AppExecutionResult;
 
 /**
@@ -33,5 +34,5 @@
 	 * @return OniSplit output
 	 */
-	public static Vector<String> export(File targetFolder, File input) {
+	public static AppExecutionResult export(File targetFolder, File input) {
 		return export(targetFolder, input, null);
 	}
@@ -44,21 +45,23 @@
 	 * @param input
 	 *            Dat file
-	 * @param pattern
-	 *            Filename pattern for files to export
-	 * @return OniSplit output
-	 */
-	public static Vector<String> export(File targetFolder, File input,
-			String pattern) {
+	 * @param patterns
+	 *            Filename patterns for files to export
+	 * @return OniSplit output
+	 */
+	public static AppExecutionResult export(File targetFolder, File input,
+			Vector<String> patterns) {
 		if (!targetFolder.exists())
 			targetFolder.mkdir();
 
 		Vector<String> cmdLine = getProgramInvocation();
-		if (pattern == null)
+		if (patterns == null)
 			cmdLine.add("-export");
-		else
-			cmdLine.add("-export:" + pattern);
+		else {
+			for (String p : patterns)
+				cmdLine.add("-export:" + p);
+		}
 		cmdLine.add(targetFolder.getPath());
 		cmdLine.add(input.getPath());
-		Vector<String> res = null;
+		AppExecutionResult res = null;
 		try {
 			res = AppExecution.executeAndWait(cmdLine);
@@ -78,5 +81,5 @@
 	 * @return OniSplit output
 	 */
-	public static Vector<String> importLevel(Vector<File> sourceFolders,
+	public static AppExecutionResult importLevel(Vector<File> sourceFolders,
 			File targetFile) {
 		Vector<String> cmdLine = getProgramInvocation();
@@ -85,5 +88,5 @@
 			cmdLine.add(f.getPath());
 		cmdLine.add(targetFile.getPath());
-		Vector<String> res = null;
+		AppExecutionResult res = null;
 		try {
 			res = AppExecution.executeAndWait(cmdLine);
@@ -105,5 +108,5 @@
 	 * @return OniSplit output
 	 */
-	public static Vector<String> packLevel(Vector<File> sourceFoldersFiles,
+	public static AppExecutionResult packLevel(Vector<File> sourceFoldersFiles,
 			File targetFile) {
 		Vector<String> cmdLine = getProgramInvocation();
@@ -114,5 +117,5 @@
 		for (File f : sourceFoldersFiles)
 			cmdLine.add(f.getPath());
-		Vector<String> res = null;
+		AppExecutionResult res = null;
 		try {
 			res = AppExecution.executeAndWait(cmdLine);
@@ -135,5 +138,5 @@
 	 * @return OniSplit output
 	 */
-	public static Vector<String> move(File targetFolder, String input,
+	public static AppExecutionResult move(File targetFolder, String input,
 			String moveParameter) {
 		if (!targetFolder.exists())
@@ -145,5 +148,5 @@
 		cmdLine.add(targetFolder.getPath());
 		cmdLine.add(input);
-		Vector<String> res = null;
+		AppExecutionResult res = null;
 		try {
 			res = AppExecution.executeAndWait(cmdLine);
@@ -163,5 +166,5 @@
 	 * @return OniSplit output
 	 */
-	public static Vector<String> convertOniToXML(File targetFolder,
+	public static AppExecutionResult convertOniToXML(File targetFolder,
 			Vector<File> inputFiles) {
 		if (!targetFolder.exists())
@@ -174,5 +177,5 @@
 			cmdLine.add(f.getPath());
 		}
-		Vector<String> res = null;
+		AppExecutionResult res = null;
 		try {
 			res = AppExecution.executeAndWait(cmdLine);
@@ -192,5 +195,5 @@
 	 * @return OniSplit output
 	 */
-	public static Vector<String> convertXMLtoOni(File targetFolder,
+	public static AppExecutionResult convertXMLtoOni(File targetFolder,
 			Vector<File> inputFiles) {
 		if (!targetFolder.exists())
@@ -203,5 +206,5 @@
 			cmdLine.add(f.getPath());
 		}
-		Vector<String> res = null;
+		AppExecutionResult res = null;
 		try {
 			res = AppExecution.executeAndWait(cmdLine);
@@ -239,6 +242,8 @@
 
 	private static File getProgramFile() {
-		File toolsPath = CaseInsensitiveFile.getCaseInsensitiveFile(Paths.getEditionBasePath(), "Tools");
-		return CaseInsensitiveFile.getCaseInsensitiveFile(toolsPath, "Onisplit.exe");
+		File toolsPath = CaseInsensitiveFile.getCaseInsensitiveFile(
+				Paths.getEditionBasePath(), "Tools");
+		return CaseInsensitiveFile.getCaseInsensitiveFile(toolsPath,
+				"Onisplit.exe");
 	}
 }
Index: AE/installer2/src/net/oni2/aeinstaller/backend/oni/XMLTools.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/backend/oni/XMLTools.java	(revision 701)
+++ AE/installer2/src/net/oni2/aeinstaller/backend/oni/XMLTools.java	(revision 702)
@@ -5,8 +5,9 @@
 import java.util.Vector;
 
-import net.oni2.aeinstaller.backend.AppExecution;
 import net.oni2.aeinstaller.backend.CaseInsensitiveFile;
 import net.oni2.aeinstaller.backend.DotNet;
 import net.oni2.aeinstaller.backend.Paths;
+import net.oni2.aeinstaller.backend.appexecution.AppExecution;
+import net.oni2.aeinstaller.backend.appexecution.AppExecutionResult;
 
 /**
@@ -25,5 +26,5 @@
 	 * @return XMLTools output
 	 */
-	public static Vector<String> patch(File patch, File source) {
+	public static AppExecutionResult patch(File patch, File source) {
 		Vector<String> cmdLine = getProgramInvocation();
 		// xmlTools.exe patchfile -filename:PATCH -forceinfiles:TOPATCH
@@ -31,5 +32,5 @@
 		cmdLine.add("-filename:" + patch.getPath());
 		cmdLine.add("-forceinfiles:" + source.getPath());
-		Vector<String> res = null;
+		AppExecutionResult res = null;
 		try {
 			res = AppExecution.executeAndWait(cmdLine);
Index: AE/installer2/src/net/oni2/aeinstaller/gui/MainWin.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/gui/MainWin.java	(revision 701)
+++ AE/installer2/src/net/oni2/aeinstaller/gui/MainWin.java	(revision 702)
@@ -40,5 +40,4 @@
 
 import net.oni2.aeinstaller.AEInstaller2;
-import net.oni2.aeinstaller.backend.AppExecution;
 import net.oni2.aeinstaller.backend.ImageResizer;
 import net.oni2.aeinstaller.backend.Paths;
@@ -47,4 +46,5 @@
 import net.oni2.aeinstaller.backend.Settings.Platform;
 import net.oni2.aeinstaller.backend.SizeFormatter;
+import net.oni2.aeinstaller.backend.appexecution.AppExecution;
 import net.oni2.aeinstaller.backend.depot.DepotManager;
 import net.oni2.aeinstaller.backend.oni.InstallProgressListener;
