source: AE/installer2/src/net/oni2/aeinstaller/backend/oni/Installer.java@ 624

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

AEI2 0.84:

  • Actually *do* update packages
  • Uninstall backend for tools
File size: 20.1 KB
Line 
1package net.oni2.aeinstaller.backend.oni;
2
3import java.io.File;
4import java.io.FileFilter;
5import java.io.FileInputStream;
6import java.io.FileNotFoundException;
7import java.io.FileOutputStream;
8import java.io.FilenameFilter;
9import java.io.IOException;
10import java.io.PrintWriter;
11import java.text.SimpleDateFormat;
12import java.util.Date;
13import java.util.HashMap;
14import java.util.HashSet;
15import java.util.List;
16import java.util.Scanner;
17import java.util.TreeMap;
18import java.util.TreeSet;
19import java.util.Vector;
20
21import net.oni2.aeinstaller.backend.Paths;
22import net.oni2.aeinstaller.backend.Settings;
23import net.oni2.aeinstaller.backend.Settings.Platform;
24import net.oni2.aeinstaller.backend.mods.EBSLInstallType;
25import net.oni2.aeinstaller.backend.mods.Mod;
26import net.oni2.aeinstaller.backend.mods.ModManager;
27
28import org.apache.commons.io.FileUtils;
29
30import com.thoughtworks.xstream.XStream;
31import com.thoughtworks.xstream.io.xml.StaxDriver;
32
33/**
34 * @author Christian Illy
35 */
36public class Installer {
37 private static FileFilter dirFileFilter = new FileFilter() {
38 @Override
39 public boolean accept(File pathname) {
40 return pathname.isDirectory();
41 }
42 };
43
44 /**
45 * @return Is Edition Core initialized
46 */
47 public static boolean isEditionInitialized() {
48 return Paths.getVanillaOnisPath().exists();
49 }
50
51 private static void createEmptyPath(File path) throws IOException {
52 if (path.exists())
53 FileUtils.deleteDirectory(path);
54 path.mkdirs();
55 }
56
57 /**
58 * @return list of currently installed mods
59 */
60 public static Vector<Integer> getInstalledMods() {
61 File installCfg = new File(Paths.getEditionGDF(), "installed_mods.xml");
62 return ModManager.getInstance().loadModSelection(installCfg);
63 }
64
65 /**
66 * @return Currently installed tools
67 */
68 @SuppressWarnings("unchecked")
69 public static TreeSet<Integer> getInstalledTools() {
70 File installCfg = new File(Paths.getInstallerPath(),
71 "installed_tools.xml");
72 TreeSet<Integer> res = new TreeSet<Integer>();
73 try {
74 if (installCfg.exists()) {
75 FileInputStream fis = new FileInputStream(installCfg);
76 XStream xs = new XStream(new StaxDriver());
77 Object obj = xs.fromXML(fis);
78 if (obj instanceof TreeSet<?>)
79 res = (TreeSet<Integer>) obj;
80 fis.close();
81 }
82 } catch (FileNotFoundException e) {
83 e.printStackTrace();
84 } catch (IOException e) {
85 e.printStackTrace();
86 }
87 return res;
88 }
89
90 private static void writeInstalledTools(TreeSet<Integer> tools) {
91 File installCfg = new File(Paths.getInstallerPath(),
92 "installed_tools.xml");
93 try {
94 FileOutputStream fos = new FileOutputStream(installCfg);
95 XStream xs = new XStream(new StaxDriver());
96 xs.toXML(tools, fos);
97 fos.close();
98 } catch (FileNotFoundException e) {
99 e.printStackTrace();
100 } catch (IOException e) {
101 e.printStackTrace();
102 }
103 }
104
105 /**
106 * @param tools
107 * Tools to install
108 */
109 public static void installTools(TreeSet<Mod> tools) {
110 TreeSet<Integer> installed = getInstalledTools();
111 for (Mod m : tools) {
112 File plain = new File(m.getLocalPath(), "plain");
113 if (plain.exists()) {
114 if (m.hasSeparatePlatformDirs()) {
115 File plainCommon = new File(plain, "common");
116 File plainMac = new File(plain, "mac_only");
117 File plainWin = new File(plain, "win_only");
118 if (plainCommon.exists())
119 copyToolsFiles(plainCommon);
120 if (Settings.getPlatform() == Platform.MACOS
121 && plainMac.exists())
122 copyToolsFiles(plainMac);
123 else if (plainWin.exists())
124 copyToolsFiles(plainWin);
125 } else {
126 copyToolsFiles(plain);
127 }
128 }
129 installed.add(m.getPackageNumber());
130 }
131 writeInstalledTools(installed);
132 }
133
134 /**
135 * @param tools
136 * Tools to uninstall
137 */
138 public static void uninstallTools(TreeSet<Mod> tools) {
139 TreeSet<Integer> installed = getInstalledTools();
140 for (Mod m : tools) {
141 if (installed.contains(m.getPackageNumber())) {
142 File plain = new File(m.getLocalPath(), "plain");
143 if (plain.exists()) {
144 if (m.hasSeparatePlatformDirs()) {
145 File plainCommon = new File(plain, "common");
146 File plainMac = new File(plain, "mac_only");
147 File plainWin = new File(plain, "win_only");
148 if (plainCommon.exists())
149 removeToolsFiles(plainCommon,
150 Paths.getEditionBasePath());
151 if (Settings.getPlatform() == Platform.MACOS
152 && plainMac.exists())
153 removeToolsFiles(plainMac,
154 Paths.getEditionBasePath());
155 else if (plainWin.exists())
156 removeToolsFiles(plainWin,
157 Paths.getEditionBasePath());
158 } else {
159 removeToolsFiles(plain, Paths.getEditionBasePath());
160 }
161 }
162 }
163 installed.remove(m.getPackageNumber());
164 }
165 writeInstalledTools(installed);
166 }
167
168 private static void copyToolsFiles(File srcFolder) {
169 for (File f : srcFolder.listFiles()) {
170 try {
171 if (f.isDirectory())
172 FileUtils.copyDirectoryToDirectory(f,
173 Paths.getEditionBasePath());
174 else
175 FileUtils
176 .copyFileToDirectory(f, Paths.getEditionBasePath());
177 } catch (IOException e) {
178 e.printStackTrace();
179 }
180 }
181 }
182
183 private static void removeToolsFiles(File srcFolder, File target) {
184 for (File f : srcFolder.listFiles()) {
185 if (f.isDirectory())
186 removeToolsFiles(f, new File(target, f.getName()));
187 else {
188 File targetFile = new File(target, f.getName());
189 if (targetFile.exists())
190 targetFile.delete();
191 }
192 }
193 if (target.list().length == 0)
194 target.delete();
195 }
196
197 /**
198 * Install the given set of mods
199 *
200 * @param mods
201 * Mods to install
202 * @param listener
203 * Listener for install progress updates
204 */
205 public static void install(TreeSet<Mod> mods,
206 InstallProgressListener listener) {
207 try {
208 createEmptyPath(Paths.getEditionGDF());
209 } catch (IOException e) {
210 e.printStackTrace();
211 }
212
213 File installCfg = new File(Paths.getEditionGDF(), "installed_mods.xml");
214 ModManager.getInstance().saveModSelection(installCfg, mods);
215
216 HashSet<Integer> unlockLevels = new HashSet<Integer>();
217
218 Vector<File> foldersOni = new Vector<File>();
219 foldersOni.add(Paths.getVanillaOnisPath());
220
221 for (Mod m : mods) {
222 for (int lev : m.getUnlockLevels())
223 unlockLevels.add(lev);
224
225 File oni = new File(m.getLocalPath(), "oni");
226 if (oni.exists()) {
227 if (m.hasSeparatePlatformDirs()) {
228 File oniCommon = new File(oni, "common");
229 File oniMac = new File(oni, "mac_only");
230 File oniWin = new File(oni, "win_only");
231 if (oniCommon.exists())
232 foldersOni.add(oniCommon);
233 if (Settings.getPlatform() == Platform.MACOS
234 && oniMac.exists())
235 foldersOni.add(oniMac);
236 else if (oniWin.exists())
237 foldersOni.add(oniWin);
238 } else {
239 foldersOni.add(oni);
240 }
241 }
242 }
243 combineBinaryFiles(foldersOni, listener);
244 combineBSLFolders(mods, listener);
245
246 if (unlockLevels.size() > 0) {
247 File dat = new File(Paths.getEditionBasePath(), "persist.dat");
248 if (dat.exists()) {
249 PersistDat save = new PersistDat(dat);
250 HashSet<Integer> currentlyUnlocked = save.getUnlockedLevels();
251 currentlyUnlocked.addAll(unlockLevels);
252 save.setUnlockedLevels(currentlyUnlocked);
253 save.close();
254 } else {
255 // TODO: what if persist.dat does not exist?
256 }
257 }
258 }
259
260 private static void combineBSLFolders(TreeSet<Mod> mods,
261 InstallProgressListener listener) {
262 listener.installProgressUpdate(95, 100, "Installing BSL files");
263
264 HashMap<EBSLInstallType, Vector<Mod>> modsToInclude = new HashMap<EBSLInstallType, Vector<Mod>>();
265 modsToInclude.put(EBSLInstallType.NORMAL, new Vector<Mod>());
266 modsToInclude.put(EBSLInstallType.ADDON, new Vector<Mod>());
267
268 for (Mod m : mods.descendingSet()) {
269 File bsl = new File(m.getLocalPath(), "bsl");
270 if (bsl.exists()) {
271 if (m.hasSeparatePlatformDirs()) {
272 File bslCommon = new File(bsl, "common");
273 File bslMac = new File(bsl, "mac_only");
274 File bslWin = new File(bsl, "win_only");
275 if ((Settings.getPlatform() == Platform.MACOS && bslMac
276 .exists())
277 || ((Settings.getPlatform() == Platform.WIN || Settings
278 .getPlatform() == Platform.LINUX) && bslWin
279 .exists()) || bslCommon.exists()) {
280 modsToInclude.get(m.getBSLInstallType()).add(m);
281 }
282 } else {
283 modsToInclude.get(m.getBSLInstallType()).add(m);
284 }
285 }
286 }
287
288 for (Mod m : modsToInclude.get(EBSLInstallType.NORMAL)) {
289 copyBSL(m, false);
290 }
291 Vector<Mod> addons = modsToInclude.get(EBSLInstallType.ADDON);
292 for (int i = addons.size() - 1; i >= 0; i--) {
293 copyBSL(addons.get(i), true);
294 }
295 }
296
297 private static void copyBSL(Mod sourceMod, boolean addon) {
298 File targetBaseFolder = new File(Paths.getEditionGDF(), "IGMD");
299 if (!targetBaseFolder.exists())
300 targetBaseFolder.mkdir();
301
302 Vector<File> sources = new Vector<File>();
303 File bsl = new File(sourceMod.getLocalPath(), "bsl");
304 if (sourceMod.hasSeparatePlatformDirs()) {
305 File bslCommon = new File(bsl, "common");
306 File bslMac = new File(bsl, "mac_only");
307 File bslWin = new File(bsl, "win_only");
308 if (Settings.getPlatform() == Platform.MACOS && bslMac.exists()) {
309 for (File f : bslMac.listFiles(dirFileFilter)) {
310 File targetBSL = new File(targetBaseFolder, f.getName());
311 if (addon || !targetBSL.exists())
312 sources.add(f);
313 }
314 }
315 if ((Settings.getPlatform() == Platform.WIN || Settings
316 .getPlatform() == Platform.LINUX) && bslWin.exists()) {
317 for (File f : bslWin.listFiles(dirFileFilter)) {
318 File targetBSL = new File(targetBaseFolder, f.getName());
319 if (addon || !targetBSL.exists())
320 sources.add(f);
321 }
322 }
323 if (bslCommon.exists()) {
324 for (File f : bslCommon.listFiles(dirFileFilter)) {
325 File targetBSL = new File(targetBaseFolder, f.getName());
326 if (addon || !targetBSL.exists())
327 sources.add(f);
328 }
329 }
330 } else {
331 for (File f : bsl.listFiles(dirFileFilter)) {
332 File targetBSL = new File(targetBaseFolder, f.getName());
333 if (addon || !targetBSL.exists())
334 sources.add(f);
335 }
336 }
337
338 System.out.println("For mod: " + sourceMod.getName()
339 + " install BSL folders: " + sources.toString());
340 for (File f : sources) {
341 File targetPath = new File(targetBaseFolder, f.getName());
342 if (!targetPath.exists())
343 targetPath.mkdir();
344 for (File fbsl : f.listFiles()) {
345 File targetFile = new File(targetPath, fbsl.getName());
346 if (addon || !targetFile.exists()) {
347 try {
348 FileUtils.copyFile(fbsl, targetFile);
349 } catch (IOException e) {
350 e.printStackTrace();
351 }
352 }
353 }
354 }
355 }
356
357 private static void combineBinaryFiles(List<File> srcFoldersFiles,
358 InstallProgressListener listener) {
359 TreeMap<String, Vector<File>> levels = new TreeMap<String, Vector<File>>();
360
361 for (File path : srcFoldersFiles) {
362 for (File levelF : path.listFiles()) {
363 String fn = levelF.getName().toLowerCase();
364 String levelN = null;
365 if (levelF.isDirectory()) {
366 levelN = fn;
367 } else if (fn.endsWith(".dat")) {
368 levelN = fn.substring(0, fn.lastIndexOf('.'));
369 }
370 if (levelN != null) {
371 if (!levels.containsKey(levelN))
372 levels.put(levelN, new Vector<File>());
373 levels.get(levelN).add(levelF);
374 }
375 }
376 }
377
378 int totalSteps = 0;
379 int stepsDone = 0;
380
381 for (@SuppressWarnings("unused")
382 String s : levels.keySet())
383 totalSteps++;
384 totalSteps++;
385
386 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
387
388 File logFile = new File(Paths.getInstallerPath(), "Installation.log");
389 PrintWriter log = null;
390 try {
391 log = new PrintWriter(logFile);
392 } catch (FileNotFoundException e) {
393 e.printStackTrace();
394 }
395
396 Date start = new Date();
397 log.println("Installation of mods started at " + sdf.format(start));
398
399 log.println("Importing levels");
400 for (String l : levels.keySet()) {
401 log.println("\tLevel " + l);
402 listener.installProgressUpdate(stepsDone, totalSteps,
403 "Installing level " + l);
404 for (File f : levels.get(l)) {
405 log.println("\t\t\t" + f.getPath());
406 }
407
408 Vector<String> res = OniSplit.packLevel(levels.get(l), new File(
409 Paths.getEditionGDF(), sanitizeLevelName(l) + ".dat"));
410 if (res != null && res.size() > 0) {
411 for (String s : res)
412 log.println("\t\t" + s);
413 }
414
415 log.println();
416 stepsDone++;
417 }
418
419 Date end = new Date();
420 log.println("Initialization ended at " + sdf.format(end));
421 log.println("Process took "
422 + ((end.getTime() - start.getTime()) / 1000) + " seconds");
423 log.close();
424 }
425
426 /**
427 * Initializes the Edition core
428 *
429 * @param listener
430 * Listener for status updates
431 */
432 public static void initializeEdition(InstallProgressListener listener) {
433 File init = new File(Paths.getTempPath(), "init");
434
435 int totalSteps = 0;
436 int stepsDone = 0;
437
438 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
439
440 for (@SuppressWarnings("unused")
441 File f : Paths.getVanillaGDF().listFiles(new FilenameFilter() {
442 @Override
443 public boolean accept(File dir, String name) {
444 return name.endsWith(".dat");
445 }
446 })) {
447 totalSteps++;
448 }
449 totalSteps = totalSteps * 2 + 2;
450
451 try {
452 File logFile = new File(Paths.getInstallerPath(),
453 "Initialization.log");
454 PrintWriter log = new PrintWriter(logFile);
455
456 Date start = new Date();
457 log.println("Initialization of Edition core started at "
458 + sdf.format(start));
459 log.println("Cleaning directories");
460
461 listener.installProgressUpdate(stepsDone, totalSteps,
462 "Cleaning up directories");
463 createEmptyPath(Paths.getVanillaOnisPath());
464 createEmptyPath(init);
465 File level0Folder = new File(init, "level0_Final");
466 createEmptyPath(level0Folder);
467
468 stepsDone++;
469
470 log.println("Exporting levels and moving files to level0");
471
472 for (File f : Paths.getVanillaGDF().listFiles(new FilenameFilter() {
473 @Override
474 public boolean accept(File dir, String name) {
475 return name.endsWith(".dat");
476 }
477 })) {
478 String levelName = f.getName().substring(0,
479 f.getName().indexOf('.'));
480 Scanner fi = new Scanner(levelName);
481 int levelNumber = Integer.parseInt(fi.findInLine("[0-9]+"));
482
483 log.println("\t" + levelName + ":");
484 log.println("\t\tExporting");
485 listener.installProgressUpdate(stepsDone, totalSteps,
486 "Exporting vanilla level " + levelNumber);
487
488 // Edition/GameDataFolder/level*_Final/
489 File tempLevelFolder = new File(init, levelName);
490
491 // Export Vanilla-Level-Dat -> Temp/Level
492 Vector<String> res = OniSplit.export(tempLevelFolder, f);
493 if (res != null && res.size() > 0) {
494 for (String s : res)
495 log.println("\t\t\t" + s);
496 }
497
498 log.println("\t\tMoving files");
499 handleFileGlobalisation(tempLevelFolder, level0Folder,
500 levelNumber);
501 stepsDone++;
502 log.println();
503 }
504
505 log.println("Reimporting levels");
506
507 for (File f : init.listFiles()) {
508 String levelName = f.getName();
509
510 log.println("\t" + levelName);
511 listener.installProgressUpdate(stepsDone, totalSteps,
512 "Creating globalized " + levelName);
513
514 Vector<File> folders = new Vector<File>();
515 folders.add(f);
516
517 Vector<String> res = OniSplit.importLevel(folders, new File(
518 Paths.getVanillaOnisPath(), levelName + ".dat"));
519 if (res != null && res.size() > 0) {
520 for (String s : res)
521 log.println("\t\t" + s);
522 }
523
524 log.println();
525 stepsDone++;
526 }
527
528 listener.installProgressUpdate(stepsDone, totalSteps,
529 "Copying basic files");
530 // Copy Oni-configs
531 File persistVanilla = new File(Paths.getOniBasePath(),
532 "persist.dat");
533 File persistEdition = new File(Paths.getEditionBasePath(),
534 "persist.dat");
535 File keyConfVanilla = new File(Paths.getOniBasePath(),
536 "key_config.txt");
537 File keyConfEdition = new File(Paths.getEditionBasePath(),
538 "key_config.txt");
539 if (persistVanilla.exists() && !persistEdition.exists())
540 FileUtils.copyFile(persistVanilla, persistEdition);
541 if (keyConfVanilla.exists() && !keyConfEdition.exists())
542 FileUtils.copyFile(keyConfVanilla, keyConfEdition);
543
544 FileUtils.deleteDirectory(init);
545
546 Date end = new Date();
547 log.println("Initialization ended at " + sdf.format(end));
548 log.println("Process took "
549 + ((end.getTime() - start.getTime()) / 1000) + " seconds");
550 log.close();
551 } catch (IOException e) {
552 e.printStackTrace();
553 }
554 }
555
556 private static void moveFileToTargetOrDelete(File source, File target) {
557 if (source.equals(target))
558 return;
559 if (!target.exists()) {
560 if (!source.renameTo(target)) {
561 System.err.println("File " + source.getPath() + " not moved!");
562 }
563 } else if (!source.delete()) {
564 System.err.println("File " + source.getPath() + " not deleted!");
565 }
566 }
567
568 private static void handleFileGlobalisation(File tempFolder,
569 File level0Folder, int levelNumber) {
570 // Move AKEV and related files to subfolder so they're not globalized:
571 if (levelNumber != 0) {
572 File akevFolder = new File(tempFolder, "AKEV");
573 akevFolder.mkdir();
574 OniSplit.move(akevFolder, tempFolder.getPath() + "/AKEV*.oni",
575 "overwrite");
576 }
577
578 for (File f : tempFolder.listFiles(new FileFilter() {
579 @Override
580 public boolean accept(File pathname) {
581 return pathname.isFile();
582 }
583 })) {
584 // Move matching files to subfolder NoGlobal:
585 if (f.getName().startsWith("TXMPfail")
586 || f.getName().startsWith("TXMPlevel")
587 || (f.getName().startsWith("TXMP") && f.getName().contains(
588 "intro"))
589 || f.getName().startsWith("TXMB")
590 || f.getName().equals("M3GMpowerup_lsi.oni")
591 || f.getName().equals("TXMPlsi_icon.oni")
592 || (f.getName().startsWith("TXMB") && f.getName().contains(
593 "splash_screen.oni"))) {
594 File noGlobal = new File(tempFolder, "NoGlobal");
595 noGlobal.mkdir();
596 File noGlobalFile = new File(noGlobal, f.getName());
597 moveFileToTargetOrDelete(f, noGlobalFile);
598 }
599 // Move matching files to level0_Animations/level0_TRAC
600 else if (f.getName().startsWith("TRAC")) {
601 File level0File = new File(level0Folder, f.getName());
602 moveFileToTargetOrDelete(f, level0File);
603 }
604 // Move matching files to level0_Animations/level0_TRAM
605 else if (f.getName().startsWith("TRAM")) {
606 File level0File = new File(level0Folder, f.getName());
607 moveFileToTargetOrDelete(f, level0File);
608 }
609 // Move matching files to level0_Textures
610 else if (f.getName().startsWith("ONSK")
611 || f.getName().startsWith("TXMP")) {
612 File level0File = new File(level0Folder, f.getName());
613 moveFileToTargetOrDelete(f, level0File);
614 }
615 // Move matching files to *VANILLA*/level0_Characters
616 else if (f.getName().startsWith("ONCC")
617 || f.getName().startsWith("TRBS")
618 || f.getName().startsWith("ONCV")
619 || f.getName().startsWith("ONVL")
620 || f.getName().startsWith("TRMA")
621 || f.getName().startsWith("TRSC")
622 || f.getName().startsWith("TRAS")) {
623 File level0File = new File(level0Folder, f.getName());
624 moveFileToTargetOrDelete(f, level0File);
625 }
626 // Move matching files to level0_Sounds
627 else if (f.getName().startsWith("OSBD")
628 || f.getName().startsWith("SNDD")) {
629 File level0File = new File(level0Folder, f.getName());
630 moveFileToTargetOrDelete(f, level0File);
631 }
632 // Move matching files to level0_Particles
633 else if (f.getName().startsWith("BINA3")
634 || f.getName().startsWith("M3GMdebris")
635 || f.getName().equals("M3GMtoxic_bubble.oni")
636 || f.getName().startsWith("M3GMelec")
637 || f.getName().startsWith("M3GMrat")
638 || f.getName().startsWith("M3GMjet")
639 || f.getName().startsWith("M3GMbomb_")
640 || f.getName().equals("M3GMbarab_swave.oni")
641 || f.getName().equals("M3GMbloodyfoot.oni")) {
642 File level0File = new File(level0Folder, f.getName());
643 moveFileToTargetOrDelete(f, level0File);
644 }
645 // Move matching files to Archive (aka delete them)
646 else if (f.getName().startsWith("AGDB")
647 || f.getName().startsWith("TRCM")) {
648 f.delete();
649 }
650 // Move matching files to /level0_Final/
651 else if (f.getName().startsWith("ONWC")) {
652 File level0File = new File(level0Folder, f.getName());
653 moveFileToTargetOrDelete(f, level0File);
654 }
655 }
656 }
657
658 private static String sanitizeLevelName(String ln) {
659 int ind = ln.indexOf("_");
660 String res = ln.substring(0, ind + 1);
661 res += ln.substring(ind + 1, ind + 2).toUpperCase();
662 res += ln.substring(ind + 2);
663 return res;
664 }
665
666 /**
667 * Verify that the Edition is within a subfolder to vanilla Oni
668 * (..../Oni/Edition/AEInstaller)
669 *
670 * @return true if GDF can be found in the parent's parent-path
671 */
672 public static boolean verifyRunningDirectory() {
673 return Paths.getVanillaGDF().exists()
674 && Paths.getVanillaGDF().isDirectory();
675 }
676}
Note: See TracBrowser for help on using the repository browser.