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

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

AEI2 0.99y:

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