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

Last change on this file since 1001 was 988, checked in by alloc, 11 years ago

AEI: Minor code changes

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