ViewVC Help
View File | Revision Log | View Changeset | Root Listing
root/Oni2/java/installer2/src/net/oni2/aeinstaller/backend/oni/management/Installer.java
Revision: 924
Committed: Tue Feb 4 19:36:53 2014 UTC (11 years, 8 months ago) by alloc
Content type: text/x-java
File size: 19483 byte(s)
Log Message:
Updated to 2.17:
- Made the download dialog a bit wider for longer mod names
- Updated XmlTools invocation to XmlTools2

File Contents

# Content
1 package net.oni2.aeinstaller.backend.oni.management;
2
3 import java.io.File;
4 import java.io.FileFilter;
5 import java.io.FileNotFoundException;
6 import java.io.FilenameFilter;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.text.SimpleDateFormat;
10 import java.util.Date;
11 import java.util.HashMap;
12 import java.util.HashSet;
13 import java.util.List;
14 import java.util.TreeMap;
15 import java.util.TreeSet;
16 import java.util.Vector;
17 import java.util.regex.Pattern;
18
19 import net.oni2.SettingsManager;
20 import net.oni2.aeinstaller.AEInstaller2;
21 import net.oni2.aeinstaller.backend.CaseInsensitiveFile;
22 import net.oni2.aeinstaller.backend.Paths;
23 import net.oni2.aeinstaller.backend.oni.OniSplit;
24 import net.oni2.aeinstaller.backend.oni.PersistDat;
25 import net.oni2.aeinstaller.backend.oni.XMLTools;
26 import net.oni2.aeinstaller.backend.oni.management.tools.ToolInstallationList;
27 import net.oni2.aeinstaller.backend.packages.EBSLInstallType;
28 import net.oni2.aeinstaller.backend.packages.Package;
29 import net.oni2.aeinstaller.backend.packages.PackageManager;
30 import net.oni2.platformtools.PlatformInformation;
31 import net.oni2.platformtools.PlatformInformation.Platform;
32 import net.oni2.platformtools.applicationinvoker.ApplicationInvocationResult;
33
34 import org.apache.commons.io.FileUtils;
35 import org.apache.commons.io.filefilter.RegexFileFilter;
36 import org.apache.commons.io.filefilter.TrueFileFilter;
37 import org.javabuilders.swing.SwingJavaBuilder;
38
39 import com.paour.NaturalOrderComparator;
40
41 /**
42 * @author Christian Illy
43 */
44 public class Installer {
45 private static FileFilter dirFileFilter = new FileFilter() {
46 @Override
47 public boolean accept(File pathname) {
48 return pathname.isDirectory();
49 }
50 };
51
52 /**
53 * Verify that the Edition is within a subfolder to vanilla Oni
54 * (..../Oni/Edition/AEInstaller)
55 *
56 * @return true if GDF can be found in the parent's parent-path
57 */
58 public static boolean verifyRunningDirectory() {
59 return Paths.getVanillaGDF().exists()
60 && Paths.getVanillaGDF().isDirectory();
61 }
62
63 /**
64 * @return Is Edition Core initialized
65 */
66 public static boolean isEditionInitialized() {
67 return Paths.getVanillaOnisPath().exists();
68 }
69
70 /**
71 * Install the given set of mods
72 *
73 * @param mods
74 * Mods to install
75 * @param listener
76 * Listener for install progress updates
77 */
78 public static void install(TreeSet<Package> mods,
79 InstallProgressListener listener) {
80 File logFile = new File(Paths.getInstallerPath(), "Installation.log");
81 Logger log = null;
82 try {
83 log = new Logger(logFile);
84 } catch (FileNotFoundException e) {
85 e.printStackTrace();
86 }
87 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
88 Date start = new Date();
89 log.println("Installation of mods started at " + sdf.format(start));
90
91 log.println();
92 log.println("AEI2 version: "
93 + SwingJavaBuilder.getConfig().getResource("appversion"));
94
95 ToolInstallationList til = ToolInstallationList.getInstance();
96 log.println("Installed tools:");
97 for (Package t : PackageManager.getInstance().getInstalledTools()) {
98 log.println(String.format(" - %s (%s)", t.getName(), t.getVersion())
99 + (til.isModified(t.getPackageNumber()) ? " (! LOCALLY MODIFIED !)"
100 : ""));
101 }
102 log.println("Installing mods:");
103 for (Package m : mods) {
104 log.println(String.format(" - %s (%s)", m.getName(), m.getVersion()));
105 }
106 log.println();
107
108 HashSet<String> levelsAffectedBefore = null;
109 if (ModInstallationList.getInstance().isLoadedFromFile()) {
110 levelsAffectedBefore = ModInstallationList.getInstance()
111 .getAffectedLevels();
112 }
113 HashSet<String> levelsAffectedNow = new HashSet<String>();
114
115 File IGMD = new File(Paths.getEditionGDF(), "IGMD");
116 if (IGMD.exists()) {
117 for (File f : IGMD.listFiles(new FileFilter() {
118 @Override
119 public boolean accept(File pathname) {
120 return pathname.isDirectory();
121 }
122 })) {
123 File ignore = CaseInsensitiveFile.getCaseInsensitiveFile(f,
124 "ignore.txt");
125 if (!ignore.exists()) {
126 try {
127 FileUtils.deleteDirectory(f);
128 } catch (IOException e) {
129 e.printStackTrace();
130 }
131 }
132 }
133 }
134
135 TreeSet<Integer> unlockLevels = new TreeSet<Integer>();
136
137 Vector<File> foldersOni = new Vector<File>();
138 foldersOni.add(Paths.getVanillaOnisPath());
139
140 Vector<File> foldersPatches = new Vector<File>();
141
142 for (Package m : mods) {
143 for (int lev : m.getUnlockLevels())
144 unlockLevels.add(lev);
145
146 File oni = CaseInsensitiveFile.getCaseInsensitiveFile(
147 m.getLocalPath(), "oni");
148 if (oni.exists()) {
149 if (m.hasSeparatePlatformDirs()) {
150 File oniCommon = CaseInsensitiveFile
151 .getCaseInsensitiveFile(oni, "common");
152 File oniMac = CaseInsensitiveFile.getCaseInsensitiveFile(
153 oni, "mac_only");
154 File oniWin = CaseInsensitiveFile.getCaseInsensitiveFile(
155 oni, "win_only");
156 if (oniCommon.exists())
157 foldersOni.add(oniCommon);
158 if (PlatformInformation.getPlatform() == Platform.MACOS
159 && oniMac.exists())
160 foldersOni.add(oniMac);
161 else if (oniWin.exists())
162 foldersOni.add(oniWin);
163 } else {
164 foldersOni.add(oni);
165 }
166 }
167
168 File patches = CaseInsensitiveFile.getCaseInsensitiveFile(
169 m.getLocalPath(), "patches");
170 if (patches.exists()) {
171 if (m.hasSeparatePlatformDirs()) {
172 File patchesCommon = CaseInsensitiveFile
173 .getCaseInsensitiveFile(patches, "common");
174 File patchesMac = CaseInsensitiveFile
175 .getCaseInsensitiveFile(patches, "mac_only");
176 File patchesWin = CaseInsensitiveFile
177 .getCaseInsensitiveFile(patches, "win_only");
178 if (patchesCommon.exists())
179 foldersPatches.add(patchesCommon);
180 if (PlatformInformation.getPlatform() == Platform.MACOS
181 && patchesMac.exists())
182 foldersPatches.add(patchesMac);
183 else if (patchesWin.exists())
184 foldersPatches.add(patchesWin);
185 } else {
186 foldersPatches.add(patches);
187 }
188 }
189 }
190
191 TreeMap<String, Vector<File>> levels = new TreeMap<String, Vector<File>>(
192 new NaturalOrderComparator());
193 for (File path : foldersOni) {
194 for (File levelF : path.listFiles()) {
195 String fn = levelF.getName().toLowerCase();
196 String levelN = null;
197 if (levelF.isDirectory()) {
198 levelN = fn;
199 levelsAffectedNow.add(fn.toLowerCase());
200 } else if (fn.endsWith(".dat")) {
201 levelN = fn.substring(0, fn.lastIndexOf('.')).toLowerCase();
202 }
203 if (levelN != null) {
204 if (!levels.containsKey(levelN))
205 levels.put(levelN, new Vector<File>());
206 levels.get(levelN).add(levelF);
207 }
208 }
209 }
210
211 Paths.getEditionGDF().mkdirs();
212 for (File f : Paths.getEditionGDF().listFiles(new FilenameFilter() {
213 public boolean accept(File arg0, String arg1) {
214 String s = arg1.toLowerCase();
215 return s.endsWith(".dat")
216 || s.endsWith(".raw")
217 || s.endsWith(".sep")
218 || (s.equals("intro.bik") && !SettingsManager
219 .getInstance().get("copyintro", false))
220 || (s.equals("outro.bik") && !SettingsManager
221 .getInstance().get("copyoutro", false));
222 }
223 })) {
224 String l = f.getName().toLowerCase();
225 l = l.substring(0, l.length() - 4);
226 if ((levelsAffectedBefore == null)
227 || levelsAffectedBefore.contains(l)
228 || levelsAffectedNow.contains(l))
229 f.delete();
230 }
231
232 applyPatches(levels, foldersPatches, listener, log);
233
234 TreeSet<String> levelsAffectedBoth = null;
235 if (levelsAffectedBefore != null) {
236 levelsAffectedBoth = new TreeSet<String>();
237 levelsAffectedBoth.addAll(levelsAffectedBefore);
238 levelsAffectedBoth.addAll(levelsAffectedNow);
239 }
240
241 combineBinaryFiles(levels, levelsAffectedBoth, listener, log);
242 combineBSLFolders(mods, listener, log);
243
244 copyVideos(log);
245
246 if (unlockLevels.size() > 0) {
247 unlockLevels(unlockLevels, log);
248 }
249
250 ModInstallationList mil = ModInstallationList.getInstance();
251 mil.setAffectedLevels(levelsAffectedNow);
252 TreeSet<Integer> modsInstalled = new TreeSet<Integer>();
253 for (Package p : mods) {
254 modsInstalled.add(p.getPackageNumber());
255 }
256 mil.setInstalledMods(modsInstalled);
257 mil.saveList();
258
259 log.println();
260 Date end = new Date();
261 log.println("Installation ended at " + sdf.format(end));
262 log.println("Process took "
263 + ((end.getTime() - start.getTime()) / 1000) + " seconds");
264 log.close();
265 }
266
267 private static void combineBSLFolders(TreeSet<Package> mods,
268 InstallProgressListener listener, Logger log) {
269 listener.installProgressUpdate(95, 100, "Installing BSL files");
270 log.println();
271 log.println("Installing BSL files");
272
273 HashMap<EBSLInstallType, Vector<Package>> modsToInclude = new HashMap<EBSLInstallType, Vector<Package>>();
274 modsToInclude.put(EBSLInstallType.NORMAL, new Vector<Package>());
275 modsToInclude.put(EBSLInstallType.ADDON, new Vector<Package>());
276
277 for (Package m : mods.descendingSet()) {
278 File bsl = CaseInsensitiveFile.getCaseInsensitiveFile(
279 m.getLocalPath(), "bsl");
280 if (bsl.exists()) {
281 if (m.hasSeparatePlatformDirs()) {
282 File bslCommon = CaseInsensitiveFile
283 .getCaseInsensitiveFile(bsl, "common");
284 File bslMac = CaseInsensitiveFile.getCaseInsensitiveFile(
285 bsl, "mac_only");
286 File bslWin = CaseInsensitiveFile.getCaseInsensitiveFile(
287 bsl, "win_only");
288 if ((PlatformInformation.getPlatform() == Platform.MACOS && bslMac
289 .exists())
290 || ((PlatformInformation.getPlatform() == Platform.WIN || PlatformInformation
291 .getPlatform() == Platform.LINUX) && bslWin
292 .exists()) || bslCommon.exists()) {
293 modsToInclude.get(m.getBSLInstallType()).add(m);
294 }
295 } else {
296 modsToInclude.get(m.getBSLInstallType()).add(m);
297 }
298 }
299 }
300
301 for (Package m : modsToInclude.get(EBSLInstallType.NORMAL)) {
302 copyBSL(m, false, log);
303 }
304 Vector<Package> addons = modsToInclude.get(EBSLInstallType.ADDON);
305 for (int i = addons.size() - 1; i >= 0; i--) {
306 copyBSL(addons.get(i), true, log);
307 }
308 }
309
310 private static void copyBSL(Package sourceMod, boolean addon, Logger log) {
311 File targetBaseFolder = new File(Paths.getEditionGDF(), "IGMD");
312 if (!targetBaseFolder.exists())
313 targetBaseFolder.mkdir();
314
315 Vector<File> sources = new Vector<File>();
316 File bsl = CaseInsensitiveFile.getCaseInsensitiveFile(
317 sourceMod.getLocalPath(), "bsl");
318 if (sourceMod.hasSeparatePlatformDirs()) {
319 File bslCommon = CaseInsensitiveFile.getCaseInsensitiveFile(bsl,
320 "common");
321 File bslMac = CaseInsensitiveFile.getCaseInsensitiveFile(bsl,
322 "mac_only");
323 File bslWin = CaseInsensitiveFile.getCaseInsensitiveFile(bsl,
324 "win_only");
325 if (PlatformInformation.getPlatform() == Platform.MACOS
326 && bslMac.exists()) {
327 for (File f : bslMac.listFiles(dirFileFilter)) {
328 File targetBSL = new File(targetBaseFolder, f.getName());
329 if (addon || !targetBSL.exists())
330 sources.add(f);
331 }
332 }
333 if ((PlatformInformation.getPlatform() == Platform.WIN || PlatformInformation
334 .getPlatform() == Platform.LINUX) && bslWin.exists()) {
335 for (File f : bslWin.listFiles(dirFileFilter)) {
336 File targetBSL = new File(targetBaseFolder, f.getName());
337 if (addon || !targetBSL.exists())
338 sources.add(f);
339 }
340 }
341 if (bslCommon.exists()) {
342 for (File f : bslCommon.listFiles(dirFileFilter)) {
343 File targetBSL = new File(targetBaseFolder, f.getName());
344 if (addon || !targetBSL.exists())
345 sources.add(f);
346 }
347 }
348 } else {
349 for (File f : bsl.listFiles(dirFileFilter)) {
350 File targetBSL = new File(targetBaseFolder, f.getName());
351 if (addon || !targetBSL.exists())
352 sources.add(f);
353 }
354 }
355
356 log.println("\tMod \"" + sourceMod.getName() + "\"");
357 for (File f : sources) {
358 log.println("\t\t" + f.getName());
359 File targetPath = new File(targetBaseFolder, f.getName());
360 if (!targetPath.exists())
361 targetPath.mkdir();
362 if (!(CaseInsensitiveFile.getCaseInsensitiveFile(targetPath,
363 "ignore.txt").exists())) {
364 for (File fbsl : f.listFiles()) {
365 if (fbsl.getName().toLowerCase().endsWith(".bsl")) {
366 File targetFile = new File(targetPath, fbsl.getName());
367 if (addon || !targetFile.exists()) {
368 try {
369 FileUtils.copyFile(fbsl, targetFile);
370 } catch (IOException e) {
371 e.printStackTrace();
372 }
373 }
374 }
375 }
376 }
377 }
378 }
379
380 private static void applyPatches(
381 TreeMap<String, Vector<File>> oniLevelFolders,
382 List<File> patchFolders, InstallProgressListener listener,
383 Logger log) {
384 log.println();
385 log.println("Applying XML patches");
386 listener.installProgressUpdate(0, 1, "Applying XML patches");
387
388 long startMS = new Date().getTime();
389
390 String tmpFolderName = "installrun_temp-"
391 + new SimpleDateFormat("yyyy_MM_dd-HH_mm_ss")
392 .format(new Date());
393 File tmpFolder = new File(Paths.getTempPath(), tmpFolderName);
394 tmpFolder.mkdir();
395
396 TreeMap<String, Vector<File>> patches = new TreeMap<String, Vector<File>>(
397 new NaturalOrderComparator());
398 for (File patchFolder : patchFolders) {
399 for (File levelFolder : patchFolder.listFiles(dirFileFilter)) {
400 String lvlName = levelFolder.getName().toLowerCase();
401 for (File f : FileUtils.listFiles(levelFolder,
402 new String[] { "oni-patch" }, true)) {
403 if (!patches.containsKey(lvlName))
404 patches.put(lvlName, new Vector<File>());
405 patches.get(lvlName).add(f);
406 }
407 }
408 }
409
410 for (String level : patches.keySet()) {
411 File levelFolder = new File(tmpFolder, level);
412 levelFolder.mkdir();
413
414 log.println("\t\tPatches for " + level);
415
416 Vector<String> exportPatterns = new Vector<String>();
417 // Get files to be patched from vanilla.dat
418 for (File patch : patches.get(level)) {
419 String patternWildcard = patch.getName();
420 patternWildcard = patternWildcard.substring(0,
421 patternWildcard.indexOf(".oni-patch"));
422 patternWildcard = patternWildcard.replace('-', '*');
423 exportPatterns.add(patternWildcard);
424 }
425 for (File srcFolder : oniLevelFolders.get(level)) {
426 if (srcFolder.isFile()) {
427 if (srcFolder.getPath().toLowerCase().contains("vanilla")) {
428 // Extract from .dat
429 ApplicationInvocationResult res = OniSplit.export(
430 levelFolder, srcFolder, exportPatterns);
431 log.logAppOutput(res, true);
432 }
433 }
434 }
435
436 // Get files to be patched from packages
437 for (File patch : patches.get(level)) {
438 String patternWildcard = patch.getName();
439 patternWildcard = patternWildcard.substring(0,
440 patternWildcard.indexOf(".oni-patch"));
441 patternWildcard = patternWildcard.replace('-', '*');
442 patternWildcard = patternWildcard + ".oni";
443 Vector<String> patterns = new Vector<String>();
444 patterns.add(patternWildcard);
445 final Pattern patternRegex = Pattern.compile(
446 patternWildcard.replaceAll("\\*", ".\\*"),
447 Pattern.CASE_INSENSITIVE);
448
449 for (File srcFolder : oniLevelFolders.get(level)) {
450 if (srcFolder.isFile()) {
451 if (!srcFolder.getPath().toLowerCase()
452 .contains("vanilla")) {
453 // Extract from .dat
454 ApplicationInvocationResult res = OniSplit.export(
455 levelFolder, srcFolder, patterns);
456 log.logAppOutput(res, true);
457 }
458 } else {
459 // Copy from folder with overwrite
460 for (File f : FileUtils.listFiles(srcFolder,
461 new RegexFileFilter(patternRegex),
462 TrueFileFilter.TRUE)) {
463 try {
464 FileUtils.copyFileToDirectory(f, levelFolder);
465 } catch (IOException e) {
466 e.printStackTrace();
467 }
468 }
469 }
470 }
471 }
472
473 // Extract files to XML
474 File levelFolderXML = new File(levelFolder, "xml");
475 Vector<File> files = new Vector<File>();
476 files.add(new File(levelFolder, "*.oni"));
477 ApplicationInvocationResult res = OniSplit.convertOniToXML(
478 levelFolderXML, files);
479 log.logAppOutput(res, true);
480
481 // Apply patches in levelFolderXML
482 File lastFolder = null;
483 for (File f : patches.get(level)) {
484 File pathOfPatch = f.getParentFile();
485 if (!pathOfPatch.equals(lastFolder)) {
486 lastFolder = pathOfPatch;
487
488 res = XMLTools.patch(pathOfPatch, levelFolderXML);
489 log.logAppOutput(res, false);
490 }
491 }
492
493 // Create .oni files from XML
494 files.clear();
495 files.add(new File(levelFolderXML, "*.xml"));
496 res = OniSplit.convertXMLtoOni(levelFolder, files);
497 log.logAppOutput(res, true);
498
499 // Remove XML folder as import will only require .oni's
500 try {
501 FileUtils.deleteDirectory(levelFolderXML);
502 } catch (IOException e) {
503 e.printStackTrace();
504 }
505
506 oniLevelFolders.get(level).add(levelFolder);
507 }
508
509 log.println("Applying XML patches took "
510 + (new Date().getTime() - startMS) + " ms");
511 }
512
513 private static void combineBinaryFiles(
514 TreeMap<String, Vector<File>> oniLevelFolders,
515 TreeSet<String> levelsUpdated, InstallProgressListener listener,
516 Logger log) {
517 long startMS = new Date().getTime();
518
519 int totalSteps = oniLevelFolders.size() + 1;
520 int stepsDone = 0;
521
522 log.println();
523 log.println("Importing levels");
524 for (String l : oniLevelFolders.keySet()) {
525 log.println("\tLevel " + l);
526 listener.installProgressUpdate(stepsDone, totalSteps,
527 "Installing level " + l);
528
529 if ((levelsUpdated == null)
530 || levelsUpdated.contains(l.toLowerCase())) {
531 ApplicationInvocationResult res = OniSplit.packLevel(
532 oniLevelFolders.get(l), new File(Paths.getEditionGDF(),
533 sanitizeLevelName(l) + ".dat"));
534 log.logAppOutput(res, true);
535 } else {
536 log.println("\t\tLevel not affected by new mod selection");
537 log.println();
538 }
539
540 stepsDone++;
541 }
542
543 log.println("Importing levels took " + (new Date().getTime() - startMS)
544 + " ms");
545 log.println();
546 }
547
548 private static void copyVideos(Logger log) {
549 log.println();
550 if (SettingsManager.getInstance().get("copyintro", false)) {
551 File src = new File(Paths.getVanillaGDF(), "intro.bik");
552 File target = new File(Paths.getEditionGDF(), "intro.bik");
553 log.println("Copying intro");
554 if (src.exists() && !target.exists()) {
555 try {
556 FileUtils.copyFileToDirectory(src, Paths.getEditionGDF());
557 } catch (IOException e) {
558 e.printStackTrace();
559 }
560 }
561 } else {
562 log.println("NOT copying intro");
563 }
564 if (SettingsManager.getInstance().get("copyoutro", true)) {
565 File src = new File(Paths.getVanillaGDF(), "outro.bik");
566 File target = new File(Paths.getEditionGDF(), "outro.bik");
567 log.println("Copying outro");
568 if (src.exists() && !target.exists()) {
569 try {
570 FileUtils.copyFileToDirectory(src, Paths.getEditionGDF());
571 } catch (IOException e) {
572 e.printStackTrace();
573 }
574 }
575 } else {
576 log.println("NOT copying outro");
577 }
578 }
579
580 private static void unlockLevels(TreeSet<Integer> unlockLevels, Logger log) {
581 File dat = new File(Paths.getEditionBasePath(), "persist.dat");
582 log.println();
583 log.println("Unlocking levels: " + unlockLevels.toString());
584 if (!dat.exists()) {
585 InputStream is = AEInstaller2.class
586 .getResourceAsStream("/net/oni2/aeinstaller/resources/persist.dat");
587 try {
588 FileUtils.copyInputStreamToFile(is, dat);
589 } catch (IOException e) {
590 e.printStackTrace();
591 }
592 }
593 PersistDat save = new PersistDat(dat);
594 HashSet<Integer> currentlyUnlocked = save.getUnlockedLevels();
595 currentlyUnlocked.addAll(unlockLevels);
596 save.setUnlockedLevels(currentlyUnlocked);
597 save.close();
598 }
599
600 private static String sanitizeLevelName(String ln) {
601 int ind = ln.indexOf("_");
602 String res = ln.substring(0, ind + 1);
603 res += ln.substring(ind + 1, ind + 2).toUpperCase();
604 res += ln.substring(ind + 2);
605 return res;
606 }
607
608 }