source: java/installer2/src/net/oni2/aeinstaller/backend/oni/management/Installer.java@ 809

Last change on this file since 809 was 809, checked in by alloc, 12 years ago

AEI2.03:

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